首页 > 解决方案 > Nginx 在一台主机上具有多个客户端证书

问题描述

我想使用 nginx 1.15.12 作为 tls 终止和身份验证的代理。如果显示有效的客户端证书,则 nginx 服务器将转发到相应的后端系统(在本例中为 localhost:8080)当前配置为每个请求执行此操作。

遗憾的是,无法为每个 location{} 块配置一个证书。可以创建多个服务器块,每个块都检查另一个证书,但我还需要只通过一个端口接收请求。

nginx.conf: |
    events {
      worker_connections  1024;  ## Default: 1024
    }

    http{
      # password file to be moved to seperate folder?
      ssl_password_file /etc/nginx/certs/global.pass;
      server {
        listen 8443;
        ssl on;
        server_name *.blabla.domain.com;
        error_log stderr debug;

        # server certificate
        ssl_certificate     /etc/nginx/certs/server.crt;
        ssl_certificate_key /etc/nginx/certs/server.key;

        # CA certificate for mutual TLS
        ssl_client_certificate /etc/nginx/certs/ca.crt;
        proxy_ssl_trusted_certificate /etc/nginx/certs/ca.crt;

        # need to validate client certificate(if this flag optional it won't
        # validate client certificates)
        ssl_verify_client on;

        location / {
          # remote ip and forwarding ip
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

          # certificate verification information
          # if the client certificate verified against CA, the header VERIFIED
          # will have the value of 'SUCCESS' and 'NONE' otherwise
          proxy_set_header VERIFIED $ssl_client_verify;

          # client certificate information(DN)
          proxy_set_header DN $ssl_client_s_dn;
          proxy_pass http://localhost:8080/;
        }
      }
    }

理想情况下,我想实现这样的目标:(对除“/blabla”之外的任何路径“/”的请求应使用 cert1 检查,如果“/blabla”匹配,则应使用另一个密钥来检查客户端证书。

nginx.conf: |
    events {
      worker_connections  1024;  ## Default: 1024
    }

    http{
      # password file to be moved to seperate folder?
      ssl_password_file /etc/nginx/certs/global.pass;
      server {
        listen 8443;
        ssl on;
        server_name *.blabla.domain.com;
        error_log stderr debug;

        # server certificate
        ssl_certificate     /etc/nginx/certs/server.crt;
        ssl_certificate_key /etc/nginx/certs/server.key;

        # CA certificate for mutual TLS
        ssl_client_certificate /etc/nginx/certs/ca.crt;
        proxy_ssl_trusted_certificate /etc/nginx/certs/ca.crt;

        # need to validate client certificate(if this flag optional it won't
        # validate client certificates)
        ssl_verify_client on;

        location / {
          # remote ip and forwarding ip
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

          # certificate verification information
          # if the client certificate verified against CA, the header VERIFIED
          # will have the value of 'SUCCESS' and 'NONE' otherwise
          proxy_set_header VERIFIED $ssl_client_verify;

          # client certificate information(DN)
          proxy_set_header DN $ssl_client_s_dn;
          proxy_pass http://localhost:8080/;
        }

        location /blabla {
          # Basically do the same like above, but use another ca.crt for checking the client cert.
        }
      }
    }

我在 kubernetes 集群上,但出于某种原因,这里没有选择使用入口身份验证机制。理想的结果将是一种配置不同路径的方法,在 nginx 中为同一服务器块使用不同的证书。

谢谢!

编辑:以下 nginx.conf 可用于检查 nginx 中的不同证书。因此server{}需要 2 个具有不同server_name. URI /blabla 现在只能通过 blabla-api.blabla.domain.com 访问。

events {
      worker_connections  1024;  ## Default: 1024
    }
    http{
      server_names_hash_bucket_size 128;
      server {
        listen 8443;
        ssl on;
        server_name *.blabla.domain.com;
        error_log stderr debug;
        # password file (passphrase) for secret keys
        ssl_password_file /etc/nginx/certs/global.pass;
        # server certificate
        ssl_certificate     /etc/nginx/certs/server.crt;
        ssl_certificate_key /etc/nginx/certs/server.key;
        # CA certificate for mutual TLS
        ssl_client_certificate /etc/nginx/certs/ca.crt;
        proxy_ssl_trusted_certificate /etc/nginx/certs/ca.crt;
        # need to validate client certificate(if this flag optional it won't
        # validate client certificates)
        ssl_verify_client on;

        location / {
          # remote ip and forwarding ip
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          # certificate verification information
          # if the client certificate verified against CA, the header VERIFIED
          # will have the value of 'SUCCESS' and 'NONE' otherwise
          proxy_set_header VERIFIED $ssl_client_verify;
          # client certificate information(DN)
          proxy_set_header DN $ssl_client_s_dn;
          proxy_pass http://localhost:8080/;
        }
        location /blabla {
          return 403 "authorized user is not allowed to access /blabla";
        }

      }
      server {
        listen 8443;
        ssl on;
        server_name blabla-api.blabla.domain.com;
        error_log stderr debug;
        # password file (passphrase) for secret keys
        ssl_password_file /etc/nginx/certs/global-support.pass;
        # server certificate
        ssl_certificate     /etc/nginx/certs/server-support.crt;
        ssl_certificate_key /etc/nginx/certs/server-support.key;
        # CA certificate for mutual TLS
        ssl_client_certificate /etc/nginx/certs/ca-support.crt;
        proxy_ssl_trusted_certificate /etc/nginx/certs/ca-support.crt;
        # need to validate client certificate(if this flag optional it won't
        # validate client certificates)
        ssl_verify_client on;

        location /blabla {
          # remote ip and forwarding ip
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          # certificate verification information
          # if the client certificate verified against CA, the header VERIFIED
          # will have the value of 'SUCCESS' and 'NONE' otherwise
          proxy_set_header VERIFIED $ssl_client_verify;
          # client certificate information(DN)
          proxy_set_header DN $ssl_client_s_dn;
          proxy_pass http://localhost:8080/blabla;
        }
      }
    }

标签: authenticationnginxkubernetes

解决方案


我想 SNI 就是答案。在 ssl 握手中,一个 IP 和一个端口的服务器可以提供多个证书

但据我了解server_name,两台服务器的属性必须不同。不确定顶级域和二级域是否需要这样做,或者您是否可以简单地使用路径来完成。

SNI 扩展了 TLS 的握手协议。这样,在 ssl 握手期间建立连接之前,服务器就可以知道要使用什么证书

较新的 nginx 版本应该默认启用 SNI。可以检查:nginx -V

看看这个如何构造 nginx.conf


推荐阅读