title: Nginx conditional client certificate authentication
body: <p>I am attempting to proxy traffic using Nginx. I have a server block in the configuration listening on HTTPS, and it is both supplying a valid certificate and checking the client certificate (2-way ssl). This setup works, but I want to be able to allow our developers to hit the server <em>without</em> the client certificate verification (since they obviously wont have it). The config I was working with before was:</p>
  
  <pre><code>server {
      listen       443 ssl http2 default_server;
      server_name  api.website.com;
      root         /usr/share/nginx/html;
      underscores_in_headers on;
  
      ssl_certificate "/etc/pki/nginx/private/ServerCert.pem";
      ssl_certificate_key "/etc/pki/nginx/private/ServerCert.pem";
      ssl_client_certificate "/etc/pki/nginx/ClientCert.pem";
      ssl_verify_client on;
      ssl_verify_depth 2;
      ssl_dhparam "/etc/pki/nginx/dhparams.pem";
      ssl_session_cache shared:SSL:1m;
      ssl_session_timeout  1m;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_ciphers HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP;
      ssl_prefer_server_ciphers on;
      include /etc/nginx/default.d/*.conf;
  
      location /tomcat/endpoint {
          proxy_pass http://localhost:8080/tomcat-war;
          proxy_pass_request_headers      on;
      }
  }
  </code></pre>
  
  <p>Again, this works, but effectively locks access except to certs that I add to the client certificate store. One solution would be to make an internally signed certificate and add that to the store, then distribute to our devs, but I would rather have the devs excluded from such checks based on IP address. I managed to do that by changing ssl_client_verify to optional and the location block to the following:</p>
  
  <pre><code>location /tomcat/endpoint {
    set $allowed 9;
    if ($ssl_client_verify != "SUCCESS") { set $allowed 1;}
    if ($not_internal_ip) { set $allowed  "${allowed}1"; }
    if ($allowed = 11) {
        return 403;
    }
    proxy_pass http://localhost:8080/tomcat-war;
    proxy_pass_request_headers      on;
  }
  </code></pre>
  
  <p>where $not_internal_ip is set as a geo block like so:</p>
  
  <pre><code>geo $not_internal_ip {
    default 0;
    10.0.0.0/8 1;
    172.16.0.0/12 1;
    192.168.0.0/16 1;
  }
  </code></pre>
  
  <p>Even though this works, I am concerned that the logic depends on so many if statements, which are <a href="https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/" rel="nofollow noreferrer">notoriously dangerous</a> in Nginx config files.</p>
  
  <p>If this way of using if statements in the location block safe? Is there a better way to do this?</p>