Need to compare client certificate CN with an entry in /etc/hosts

I am a newbie to Nginx. We plan to use nginx as a reverse proxy to
tomcat and node js on our systems. We plan to use MTLS to secure server
to server communication (between nginx on different servers). An
additional requirement is that we have to match the client certificate
CN with an existing entry in /etc/hosts. What would be the simplest
mechanism to do this? HttpPerlModule? Uwsgi?
Below is the config we have used to prototype nginx as reverse proxy
with MTLS.

server {

    listen       443 ssl;

    server_name  localhost;



    error_page   500 502 503 504  /50x.html;

    location = /50x.html {

        root   /usr/share/nginx/html;

    }



    #SSL Certs

    #SSL Certs

    ssl_certificate      /etc/nginx/locations.d/b7k-vma170.crt;

    ssl_certificate_key  /etc/nginx/locations.d/b7k-vma170.key;

    ssl_protocols       SSLv3 TLSv1 TLSv1.1 TLSv1.2;

    ssl_ciphers 

RC4:HIGH:!aNULL:!MD5:AES128-SHA:AES256-SHA:RC4-SHA:@STRENGTH;

    ssl_client_certificate  /etc/nginx/locations.d/root-ca.crt;

    ssl_verify_client   on;

    ssl_session_cache   shared:SSL:10m;

    ssl_session_timeout 10m;



    keepalive_timeout    70;



    include /etc/nginx/locations.d/*.conf;

    include /var/nginx/locations.d/*.conf;

    deny all;
}

ip-allow.conf contents

allow 10.94.12.148;

allow 10.94.12.165;

deny all;

webapps.conf contents

location / {

root /var/lib/tomcat/webapps;

proxy_pass http://127.0.0.1:8082;



proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header Host $http_host;

proxy_set_header X-Forwarded-Proto https;

proxy_redirect off;



proxy_connect_timeout      1200;

proxy_send_timeout         1200;

proxy_read_timeout         1200;

}

On 26 November 2013 19:19, Radha Venkatesh (radvenka)
[email protected] wrote:

we have to match the client certificate CN with an
existing entry in /etc/hosts.

Please could you specify exactly what you need to ensure matches?
It’s not obvious (to me!), given what you wrote and given the minimal
information available in /etc/hosts.

Jonathan

Jonathan,

The requirement is that we match an existing hostname entry in
/etc/hosts with the Client certificate CN (CN has to be the hostname of
the client).

Thanks,
Radha.

Well, this has absolutely nothing to do with nginx, but

openssl x509 -in -text -noout

will tell you which domain ( or hostname ) the certificate is for.

Steve

On Tue, 2013-11-26 at 22:48 +0000, Radha Venkatesh (radvenka) wrote:

To: [email protected]

nginx Info Page

Steve H. BSc(Hons) MIITP
http://www.greengecko.co.nz
Linkedin: http://www.linkedin.com/in/steveholdoway
Skype: sholdowa

On 26 November 2013 22:48, Radha Venkatesh (radvenka)
[email protected] wrote:

Jonathan,

The requirement is that we match an existing hostname entry in /etc/hosts with
the Client certificate CN (CN has to be the hostname of the client).

That’s not really saying anything /new/, is it? :wink:

Here are some examples of different things that your requirement could
mean:

  1. Do you want to ensure that the CN that is presented merely exists
    in /etc/hosts?
  2. Do you want to ensure that the connection came from an IP that the
    CN’s entry in /etc/hosts matches?
  3. Both of #1 and #2 combined?

Please give some representative examples of CNs being presented,
/etc/hosts contents, and the allow/deny behaviour you want to see
based on those combinations. Your requirement, whilst obvious and
clear to yourself, is not clear to some people (well, me at least!) as
they don’t have their head deep inside your project.

Regards,
Jonathan

I found the below snippet which could provide me the cn from the
certificate. What would be the easiest way to compare this with an entry
in /etc/hosts? Do we need an external module to do this?

The “map” directive with regex can be used instead of “if”,
something like this:

  map  $ssl_client_s_dn  $ssl_client_s_dn_cn {
       default           "";
       ~/CN=(?<CN>[^/]+) $CN;
  };

On Tue, Nov 26, 2013 at 07:19:55PM +0000, Radha Venkatesh (radvenka)
wrote:

Hi there,

An additional requirement is that we have to match the client certificate
CN with an existing entry in /etc/hosts. What would be the simplest
mechanism to do this? HttpPerlModule? Uwsgi?

In nginx terms, you have $remote_addr as the client IP address, and you
have the variables described in

http://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables

as “things from the certificate”.

I don’t see CN listed there, so I suspect that whatever you do is going
to involve some extra parsing of the certificate, which probably means
something external or dynamic within nginx.conf.

The “simplest” mechanism is probably whichever one you are most familiar
with already.

Whether you use an embedded language or something external, you can make
sure to send the appropriate raw information to it, and let it decide
whether this is good or not.

You may be interested in trying Module ngx_http_auth_request_module as one
possibly way of communicating the success or failure state of your check
back to nginx, but it all depends on the extra code that you must write.

Good luck with it,

f

Francis D. [email protected]

On Wed, Nov 27, 2013 at 12:01:16AM +0000, Radha Venkatesh (radvenka)
wrote:

Hi there,

I found the below snippet which could provide me the cn from the certificate.

Great, now you have a variable to hold the CN that you want to do
something with.

What would be the easiest way to compare this with an entry in /etc/hosts? Do we
need an external module to do this?

I think you need some form of programming, if you want to read
/etc/hosts
“live” each time – you can try whatever language you have compiled in
to your nginx, or you can use any one of the *_pass directives to talk
to whatever you write in the language of your choice.

If you are happy to statically write the contents of /etc/hosts into
your nginx.conf, so that it is only read on startup, you could probably
do it all in config: use another “map” to check that $ssl_client_s_dn_cn
is one of your expected values:

map $ssl_client_s_dn_cn $is_cn_in_etc_hosts {
default “no”;
hostname1 “yes”;
host2.example.com “yes”;
}

Or you could check that the matching ip address is the same as
$remote_addr, if that is what you want:

map $ssl_client_s_dn_cn $what_ip_should_cn_have {
default “”;
hostname1 “127.0.0.3”;
host2.example.com “127.0.0.4”;
}

and then compare $what_ip_should_cn_have with $remote_addr.

f

Francis D. [email protected]