1

I'm looking for a setup where I'd like to have SSL client certificates for all but one source IP.

My idea is to set

 ssl_verify_client optional;

and to add an elaborate if statement to the locations. However I don't know how to write such an if statement.

  # this requires ssl client certificates for all locations
  location / {
    if ($ssl_client_verify != "SUCCESS") { 
       return 403; 
    ...
  }

  # now what to write to require ssl certs except if source IP is e.g. 1.2.3.4
  location /two {
    if (?????) { 
       return 403; 
    ...
  }

Edit: Additional information

The switch ssl_verify_client with the value optional tells clients, that they can but don't have to send client certificates.

So by checking the variable $var_ssl_client_verify I can see whether a client certificate was presented and valid (SUCCESS) or not. This rule shall be applied for all clients, that do not have a given source IP. For one specific source IP I do not want to verify client certificates.

What I need is something like

    if ($ssl_client_verify != "SUCCESS" and source_ip != 1.2.3.4 ) { 
       return 403; 
    }

Edit 2: I changed the title from setup nginx to require client certs for all but a given source IP to setup nginx to require certain conditions of a location for all but a given source IP as what I am really struggling with has nothing to do with client certificates, but with combining if statements and filtering conditionally on the source IP address.

2

1 Answer 1

1

Multiple conditions aren't supported in Nginx for if statement. So, as a workaround and as client IP is evaluated in our usecase, the solution could be achieved similar to how it was achieved at https://www.nginx.com/blog/rate-limiting-nginx/#Advanced-Configuration-Examples . Basically, the solution goes like this...

geo $limit {
    default 1;
    1.2.3.4 0; #please replace the example IP with the actual IP.
}

ssl_verify_client optional;
ssl_client_certificate /path/to/cert.pem;

map $limit $limit_key {
    0 "SUCCESS";
    1 $ssl_client_verify;
}

server {
    # ... other directives
    location / {
        if ( $limit_key != 'SUCCESS' ) { return 403; }
        # ... other directives
    }
}

Basically, we assign the value "SUCCESS" to the variable for a specific IP. For every other IP, we assign the actual value of $ssl_client_verify.

Alternative Answer

ssl_verify_client optional;
ssl_client_certificate /path/to/cert.pem;
server {
    # ... other directives
    
    # initial value of $variable is the actual value of $ssl_client_verify
    set $variable $ssl_client_verify;

    # here we re-assign $variable with "SUCCESS" for our specific IP
    # please replace 1.2.3.4 with the actual IP
    if ( $remote_addr = "1.2.3.4" ) {
        set $variable "SUCCESS";
    }

    location / {
        if ( $variable != 'SUCCESS' ) { return 403; }
        # ... other directives
    }
}
5
  • @gelonida Apologies for misunderstanding your question initially. Commented Jun 30, 2021 at 9:19
  • Thanks for your answer. This should do the job. So the idea is to use geo for source ip adress mapping, map statements to create one variable, which can finally be used in an if ( $variable != "SUCCESS") { return 403; } Concerning the initial misunderstanding. It made me rewrite my question in (hopefully) a less ambiguous and more generic way. Will mark question as OK as soon as I have the opportunity to test in my environment. In reality my scenario is a little more complicated as the ip address will be determined via the proxy protocol.
    – gelonida
    Commented Jun 30, 2021 at 12:10
  • @gelonida You are right. Please see the updated answer that includes an alternative solution. Commented Jun 30, 2021 at 12:42
  • 1
    Alternative Answer is working for me. Didn't try so far the first one.
    – gelonida
    Commented Jun 30, 2021 at 18:22
  • Glad to know it worked. Commented Jul 1, 2021 at 0:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .