[PATCH] Proxy remote server SSL certificate verification

Maxim Dounin mdounin at mdounin.ru
Wed Aug 28 00:41:43 UTC 2013


Hello!

On Tue, Aug 27, 2013 at 11:47:38AM +0300, Aviram Cohen wrote:

> Added a new version, with all the required fixes.

This looks better, modulo various style problems.  It also looks 
like verification code isn't complete.

See below for comments.

> 
> diff -Nrpu nginx-1.4.1/src/http/modules/ngx_http_proxy_module.c
> nginx-1.4.1-proxy-ssl-verification/src/http/modules/ngx_http_proxy_module.c
> --- nginx-1.4.1/src/http/modules/ngx_http_proxy_module.c 2013-05-06
> 13:26:50.000000000 +0300
> +++ nginx-1.4.1-proxy-ssl-verification/src/http/modules/ngx_http_proxy_module.c
> 2013-08-26 10:43:15.639557701 +0300
> @@ -511,6 +511,27 @@ static ngx_command_t  ngx_http_proxy_com
>        offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_session_reuse),
>        NULL },
> 
> +

Style: extra empty line.

> +    { ngx_string("proxy_ssl_verify"),
> +      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
> +      ngx_conf_set_flag_slot,
> +      NGX_HTTP_LOC_CONF_OFFSET,
> +      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_verify),
> +      NULL },
> +
> +    { ngx_string("proxy_ssl_verify_depth"),
> +      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
> +      ngx_conf_set_num_slot,
> +      NGX_HTTP_LOC_CONF_OFFSET,
> +      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_verify_depth),
> +      NULL },
> +
> +    { ngx_string("proxy_ssl_trusted_certificate"),
> +      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
> +      ngx_conf_set_str_slot,
> +      NGX_HTTP_LOC_CONF_OFFSET,
> +      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_certificate),
> +      NULL },
>  #endif

Style: missing empty line before "#endif".

It also might make sense to name the field ssl_trusted_certificate 
to match directive name.

It probably also a good idea to move both certificate and 
verify_depth to ngx_http_proxy_loc_conf_t as they aren't needed in 
upstream configuration.  (At upstream level, only upstream.verify 
is needed/used.)

> 
>        ngx_null_command
> @@ -2419,8 +2440,11 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
>      conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
> 
>      conf->upstream.intercept_errors = NGX_CONF_UNSET;
> +
>  #if (NGX_HTTP_SSL)
>      conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;
> +    conf->upstream.ssl_verify = NGX_CONF_UNSET;
> +    conf->upstream.ssl_verify_depth = NGX_CONF_UNSET_UINT;
>  #endif
> 
>      /* "proxy_cyclic_temp_file" is disabled */

Style: please add conf->upstream.ssl_certificate (or whatever) to 
a comment "set by ngx_pcalloc()".

> @@ -2697,6 +2721,30 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
>  #if (NGX_HTTP_SSL)
>      ngx_conf_merge_value(conf->upstream.ssl_session_reuse,
>                                prev->upstream.ssl_session_reuse, 1);

Style: if you add empty line before "#endif", please add another 
one after "#if".

> +    ngx_conf_merge_value(conf->upstream.ssl_verify,
> +                              prev->upstream.ssl_verify, 0);
> +    ngx_conf_merge_uint_value(conf->upstream.ssl_verify_depth,
> +                              prev->upstream.ssl_verify_depth, 1);
> +    ngx_conf_merge_str_value(conf->upstream.ssl_certificate,
> +                              prev->upstream.ssl_certificate, "");
> +
> +    if (conf->upstream.ssl_verify) {
> +        if (conf->upstream.ssl_certificate.len == 0) {
> +            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> +                               "no \"proxy_ssl_trusted_certificate\" is defined for "
> +                               "the \"proxy_ssl_verify\" directive");

Style: no lines longer than 80 chars, please.

It might also be a good idea to only complain if there is 
conf->upstream.ssl.

> +
> +            return NGX_CONF_ERROR;
> +        }
> +    }
> +
> +    if (conf->upstream.ssl &&
> +        ngx_ssl_trusted_certificate(cf, conf->upstream.ssl,
> +          &conf->upstream.ssl_certificate, conf->upstream.ssl_verify_depth) != NGX_OK)
> +    {

Style:

- no lines longer than 80 chars, please.
- if you wrap long conditions, please put operators a the start of 
  a continuation line.

That is,

    if (conf->upstream.ssl
        && ngx_ssl_trusted_certificate(cf, conf->upstream.ssl,
                                       &conf->upstream.ssl_certificate
                                       conf->upstream.ssl_verify_depth)
           != NGX_OK)
    {
        ...
    }

Additional question is what happens in a configuration like

    location / {
        proxy_pass https://example.com;
        proxy_ssl_verify on;
        proxy_ssl_trusted_ceritifcate example.crt;

        if ($foo) {
            # do nothing
        }
    }

or the same with a nested location instead of "if".  Quick look 
suggest it will result in trusted certs loaded twice (and stale 
alerts later due to how OpenSSL handles this).

> +        return NGX_CONF_ERROR;
> +    }
> +
>  #endif
> 
>      ngx_conf_merge_value(conf->redirect, prev->redirect, 1);
> diff -Nrpu nginx-1.4.1/src/http/ngx_http_upstream.c
> nginx-1.4.1-proxy-ssl-verification/src/http/ngx_http_upstream.c
> --- nginx-1.4.1/src/http/ngx_http_upstream.c 2013-05-06 13:26:50.000000000 +0300
> +++ nginx-1.4.1-proxy-ssl-verification/src/http/ngx_http_upstream.c
> 2013-08-26 10:44:35.323558884 +0300
> @@ -1324,7 +1324,13 @@ ngx_http_upstream_ssl_handshake(ngx_conn
>      u = r->upstream;
> 
>      if (c->ssl->handshaked) {
> -
> +        if (u->conf->ssl_verify && SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
> +            ngx_log_error(NGX_LOG_ERR, c->log, 0, "upstream ssl certificate validation failed");
> +            c = r->connection;
> +            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
> +            goto fail;
> +        }
> +

Style: 80 chars.

Please also note that SSL_get_verify_result() will return 
X509_V_OK if there is no certificate at all, quote from 
SSL_get_verify_result manpage:

       If no peer certificate was presented, the returned result code is
       X509_V_OK. This is because no verification error occurred, it does
       however not indicate success. SSL_get_verify_result() is only useful in
       connection with SSL_get_peer_certificate(3).

Please take a look at relevant code at ngx_http_process_request().  
It also seems to have better error reporting.

>          if (u->conf->ssl_session_reuse) {
>              u->peer.save_session(&u->peer, u->peer.data);
>          }
> @@ -1332,13 +1338,21 @@ ngx_http_upstream_ssl_handshake(ngx_conn
>          c->write->handler = ngx_http_upstream_handler;
>          c->read->handler = ngx_http_upstream_handler;
> 
> +        c = r->connection;
> +
>          ngx_http_upstream_send_request(r, u);
> 
> +fail:
> +        ngx_http_run_posted_requests(c);
> +
>          return;
>      }

Probably just adding

    ngx_http_run_posted_requests(c);
    return;

instead of "goto" above would be more readable...

> 
> +    c = r->connection;
> +
>      ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
> 
> +    ngx_http_run_posted_requests(c);
>  }

... or, alternatively, "fail:" may be moved before these lines 
(and "c = ..." and "ngx_http_upstream_next(...)" lines before 
"goto" removed), resulting in less code duplication.

[...]

> On Thu, Aug 22, 2013 at 5:00 PM, Aviram Cohen <aviram at adallom.com> wrote:
> > Hello!
> >
> > I have a couple of questions regarding the two last comments:

[...]

Sorry, I somehow missed this message.  Glad to see you've 
succcessfully found answers.

-- 
Maxim Dounin
http://nginx.org/en/donation.html



More information about the nginx-devel mailing list