Nginx

Nginx 在 docker 中找不到 ssl_certificate,即使它在那裡

  • October 28, 2018

我在 AWS EB 上執行 multidocker。設置如下所示:

Dockerrun.aws.json(xxx 作為域的佔位符。該應用程序不安全)

{
 "AWSEBDockerrunVersion": 2,
 "volumes": [
   {
     "name": "nginx-conf",
     "host": {
       "sourcePath": "/var/app/current/nginx.conf"
     }
   },
   {
     "name": "nginx-cert-conf",
     "host": {
       "sourcePath": "/var/app/current/nginx.cert.conf"
     }
   },
   {
     "name": "cert-bot-lib",
     "host": {
       "sourcePath": "/var/app/current/django_project/certbot"
     }
   },
   {
     "name": "lets-encrypt",
     "host": {
       "sourcePath": "/var/app/current/django_project/certs"
     }
   },
   {
     "name": "cert-challenge",
     "host": {
       "sourcePath": "/var/app/current/django_project/cert_challenge"
     }
   }
 ],
 "containerDefinitions": [{
     "name": "web-app",
     "image": "xxx",
     "command": [
       "…"
     ],
     "essential": true,
     "memory": 1000,
     "mountPoints": [
       …
     ]
   },
   {
     "name": "channels-app",
     "image": "xxx",
     "command": [
       "…"
     ],
     "essential": true,
     "memory": 1000,
     "mountPoints": [
       …
     ]
   },
   {
     "name": "nginx-proxy",
     "image": "nginx",
     "essential": false,
     "memory": 500,
     "portMappings": [{
       "hostPort": 443,
       "containerPort": 443
     }],
     "links": ["web-app", "channels-app"],
     "mountPoints": [{
         "sourceVolume": "nginx-conf",
         "containerPath": "/etc/nginx/conf.d/default.conf",
         "readOnly": true
       },
       {
         "sourceVolume": "lets-encrypt",
         "containerPath": "/etc/ssl/certs",
         "readOnly": true
       }
     ]
   },
   {
     "name": "nginx-cert-proxy",
     "image": "nginx",
     "essential": true,
     "memory": 250,
     "portMappings": [{
       "hostPort": 80,
       "containerPort": 80
     }],
     "mountPoints": [{
         "sourceVolume": "nginx-cert-conf",
         "containerPath": "/etc/nginx/conf.d/default.conf",
         "readOnly": true
       },
       {
         "sourceVolume": "cert-challenge",
         "containerPath": "/cert_challenge",
         "readOnly": true
       }
     ]
   },
   {
     "name": "certbot",
     "image": "yspreen/certbot-bash",
     "command": [
       "-c",
       "(([ -d /etc/letsencrypt/live ] && certbot renew --staging --webroot --register-unsafely-without-email --agree-tos --no-eff-email --webroot-path=/cert_challenge) || certbot certonly --staging --webroot --register-unsafely-without-email --agree-tos --no-eff-email --webroot-path=/cert_challenge -d xxx) && cp -Lr /etc/letsencrypt/live /etc/letsencrypt/live_cp"
     ],
     "essential": false,
     "memory": 250,
     "mountPoints": [{
         "sourceVolume": "lets-encrypt",
         "containerPath": "/etc/letsencrypt",
         "readOnly": false
       },
       {
         "sourceVolume": "cert-challenge",
         "containerPath": "/cert_challenge",
         "readOnly": false
       },
       {
         "sourceVolume": "cert-bot-lib",
         "containerPath": "/var/lib/letsencrypt",
         "readOnly": false
       }
     ]
   }
 ]
}

nginx.conf

upstream djangomain {
   server web-app:8000;
}

upstream djangochannels {
   server channels-app:8001;
}

server {
   server_name xxx;
   listen 443 ssl http2;
   listen [::]:443 ssl http2;

   server_tokens off;
   disable_symlinks off;

   ssl on;

   ssl_buffer_size 8k;
   ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;

   ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
   ssl_prefer_server_ciphers on;
   ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;

   ssl_ecdh_curve secp384r1;
   ssl_session_tickets off;

   ## OCSP stapling
   ssl_stapling on;
   ssl_stapling_verify on;
   resolver 1.1.1.1 1.0.0.1;

   ssl_certificate /etc/ssl/certs/live_cp/xxx/fullchain.pem;
   ssl_certificate_key /etc/ssl/certs/live_cp/xxx/privkey.pem;


   location /ws/ {
       proxy_pass         http://djangochannels/;
       proxy_http_version 1.1;
       proxy_set_header   Upgrade $http_upgrade;
       proxy_set_header   Connection "upgrade";
       proxy_redirect     off;
   }
   location /static/ {
       alias /static_root/;
       autoindex on;
   }
   location / {
       proxy_pass         http://djangomain/;
       proxy_redirect     off;
       proxy_set_header   Host $host;
       proxy_set_header   X-Real-IP $remote_addr;
       proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header   X-Forwarded-Host $server_name;
   }
}

nginx.cert.conf

server {
   listen      80;
   listen [::]:80;
   server_name  localhost;

   location ~ /.well-known/acme-challenge {
       allow all;
       root /cert_challenge;
   }
   location / {
       rewrite ^ https://$host$request_uri? permanent;
   }
}

解釋:

密鑰文件需要由certbot容器創建。為此,挑戰被放置在cert-challenge體積中並由nginx-cert-proxy容器暴露。這行得通。

不起作用的是將證書應用於nginx-proxy容器。它失敗並出現以下錯誤:

nginx: [warn] the "ssl" directive is deprecated, use the "listen ... ssl" directive instead in /etc/nginx/conf.d/default.conf:17
nginx: [emerg] BIO_new_file("/etc/ssl/certs/live_cp/xxx/fullchain.pem") failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/etc/ssl/certs/live_cp/xxx/fullchain.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)

現在簡單的解釋是有一些拼寫錯誤,或者 nginx 由於來自 certbot 的符號連結而搞砸了。

但我已將 bash 更改nginx.conf為預設值,在容器內啟動 bash,然後 ls 顯示:

root@a58f8e87ca:/# ls /etc/ssl/certs/live_cp/xxx/fullchain.pem -lAh
-rw-r--r-- 1 root root 3.8K Oct 25 21:43 /etc/ssl/certs/live_cp/xxx/fullchain.pem

所以文件存在。關於符號連結,它絕對不是連結,而是一個實際的文件。修改後的 certbot 映像允許添加複制命令,因為入口點是bash.

我完全迷路了。為什麼nginx無法讀取文件?

原來這是一個時間問題。

當所有容器同時啟動時,不會在 nginx 查找之前生成證書。當我手動進入容器進行檢查時,它們已同時生成並且可以正常工作。所以這是一個添加一個問題

until [ -f $cert ]; do sleep 1; done

在 nginx 執行命令之前。

我沒有考慮這一點,因為我忘記了每次執行設置時都會刪除證書。

我對此進行測試的方式是通過 EB CLI 的部署命令。這每次都會創建一個新版本的應用程序,導致儲存被刷新。

引用自:https://serverfault.com/questions/937274