#1744 closed defect (invalid)
Cannot use custom variable in ssl_certificate directive.
Reported by: | Owned by: | ||
---|---|---|---|
Priority: | minor | Milestone: | |
Component: | other | Version: | 1.15.x |
Keywords: | Cc: | ||
uname -a: | Linux a371d0a1c462 4.15.0-43-generic #46-Ubuntu SMP Thu Dec 6 14:45:28 UTC 2018 x86_64 Linux | ||
nginx -V: |
nginx version: nginx/1.15.9
built by gcc 8.2.0 (Alpine 8.2.0) built with OpenSSL 1.1.1a 20 Nov 2018 TLS SNI support enabled configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-threads --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-stream_realip_module --with-stream_geoip_module=dynamic --with-http_slice_module --with-mail --with-mail_ssl_module --with-compat --with-file-aio --with-http_v2_module |
Description
Nginx: nginx:1.15.9-alpine
Part of the config:
server {
...
set $cert_name my.example.com;
ssl_certificate "letsencrypt/live/${cert_name}/fullchain.pem";
...
}
In logs:
"2019/03/12 19:51:20 [error] 6#6: *25 cannot load certificate \"/etc/nginx/letsencrypt/livefullchain.pem\": BIO_new_file() failed (SSL: error:0200100D:system library:fopen:Permission denied:fopen('/etc/nginx/letsencrypt/live//fullchain.pem','r') error:2006D002:BIO routines:BIO_new_file:system lib) while SSL handshaking, client: 10.0.1.135, server: 0.0.0.0:443"
- Permission denied is because I haven't run nginx as root in docker container and letsencrypt files were readable only by 0 uid.
When using built-in $ssl_server_name variable - all is ok and path is set properly.
Change History (7)
comment:2 by , 6 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
Variables set with the set
directive of the rewrite module are only available after rewrite instructions were evaluated when processing a request, see rewrite module documentation. As such, these variables won't have any meaningful value during an SSL handshake. When loading certificates you have to use builtin connection-related variables, or custom variables which are always available - such as provided with map, geo, perl_set, or js_set.
comment:3 by , 6 years ago
connection-related variables, or custom variables which are always available
All of the sites I host follow a subdomain.domain.tld
pattern (where subdomain
is optional.) Each separate domain has its own wildcard certificate provided by LetsEncrypt.
I've tried every variation of the following with no success:
http { ... map $host $ssl_domain_name { volatile; hostnames; default 'example.org'; ~^((?<subdomain>.*)\.)(?<domain>[^.]+)\.(?<tld>[^.]+)$ $domain.$tld; } server { ... location { ... ssl_certificate /etc/letsencrypt/live/$ssl_domain_name/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$ssl_domain_name/privkey.pem; } } }
In my above map
example, I have confirmed that the value of $ssl_domain_name
correctly maps to only the domain & TLD.
(I've done this by using add_header X-Debug $ssl_domain_name;
and confirming the value.)
I've also tried mapping $ssl_server_name
instead of $host
, or simply using the $ssl_server_name
variable directly while symlinking the directories.
I've tried with and without the volatile;
and hostnames;
flags.
In my experience, even with the guidance you've provided above, it appears like variables do not work as expected.
Can you offer up a practical example, or a bit more guidance? I must be missing something, but I can't find what it is in the documentation or the source itself.
comment:4 by , 6 years ago
Using the $host
variable hardly make sense - before an HTTP request is received it is expected to map to server_name
and will be empty by default.
The $ssl_server_name
variable is expected to work fine, and you can create derivative variables from it, for example:
map $ssl_server_name $cert { default /tmp/default; example.com /tmp/example.com; } server { listen 8443 ssl; ssl_certificate $cert.crt; ssl_certificate_key $cert.key; }
Testing with
$ openssl s_client -connect 127.0.0.1:8443 -servername example.com
produces the following error (expected, as there are no corresponding certificate available):
2019/04/30 16:24:09 [error] 28941#100101: *1 cannot load certificate "/tmp/example.com.crt": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/tmp/example.com.crt','r') error:2006D080:BIO routines:BIO_new_file:no such file) while SSL handshaking, client: 127.0.0.1, server: 0.0.0.0:8443
Clearly everything works as expected.
comment:5 by , 6 years ago
Using the
$host
variable hardly make sense
Only trying to follow your guidance. $host
is listed in the Nginx documentation's variables listing as always being available, while $ssl_server_name
is not explicitly documented as such.
No matter what I do, I do not get the results that you do.
Having any variable in either ssl_certificate
or ssl_certificate_key
causes this:
openssl s_client -connect 127.0.0.1:443 -servername example.org CONNECTED(00000003) 1996304384:error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:../ssl/record/rec_layer_s3.c:1407:SSL alert number 80 --- no peer certificate available --- No client certificate CA names sent --- SSL handshake has read 7 bytes and written 197 bytes Verification: OK --- New, (NONE), Cipher is (NONE) Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : 0000 Session-ID: Session-ID-ctx: Master-Key: PSK identity: None PSK identity hint: None SRP username: None Start Time: 1556668430 Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no ---
I'm using:
- Nginx 1.16.0 (compiled from official source)
- OpenSSL 1.1.0j
I originally tried all of this on Nginx 1.15..
Is there some specific configuration or module necessary? When I hardcode the path, it works perfectly. When any variable is in the path, it does not work.
comment:6 by , 6 years ago
Only trying to follow your guidance. $host is listed in the Nginx documentation's variables listing as always being available, while $ssl_server_name is not explicitly documented as such.
The $host is explicitly documented as:
in this order of precedence: host name from the request line, or host name from the “Host” request header field, or the server name matching a request
without a request (and hence without the request line and the "Host" header) it is going to be identical to what is used in the server_name
directive in the relevant server block. While it certainly can be used, there is no real difference to writing the same string right in the ssl_certificate directive.
No matter what I do, I do not get the results that you do.
Having any variable in either ssl_certificate or ssl_certificate_key causes this:
openssl s_client -connect 127.0.0.1:443 -servername example.org CONNECTED(00000003) 1996304384:error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:../ssl/record/rec_layer_s3.c:1407:SSL alert number 80 --- no peer certificate available ...
The error shown by openssl
indicate that nginx wasn't able to load a certificate. Check nginx error log, most likely it have corresponding details.
Note well that for nginx worker processes to be able to load certificates and keys, they have to be readable by nginx worker processes user. Usually this is not the case, unless you've specifically configured your certificate paths to allow this.
Note well that dynamic loading of certificates is generally to be avoided, unless you know why you need it and understand possible implications, such as security ones.
Note well that Trac is to report bugs. As demonstrated above, what you observe is clearly not a bug. If you need further help with configuring nginx, consider using support options available.
comment:7 by , 6 years ago
The error shown by openssl indicate that nginx wasn't able to load a certificate. Check nginx error log, most likely it have corresponding details.
I'd forgotten to compile using --with-debug
, so my logs were pretty useless. Recompiling got me the same Permission denied
response, which was helpful.
Note well that for nginx worker processes to be able to load certificates and keys, they have to be readable by nginx worker processes user. Usually this is not the case, unless you've specifically configured your certificate paths to allow this.
This was a good hint, actually. Thank you.
Note well that dynamic loading of certificates is generally to be avoided, unless you know why you need it and understand possible implications, such as security ones.
I appreciate the warning. Thank you.
Note well that Trac is to report bugs.
I'm aware. I legitimately believed I'd encountered the same bug that the original reported had here. Your additional direction in these replies was extremely helpful. Thank you again, and sorry to muck this issue up here.
Cannot edit ticket - don't know why. The path to the cert in logs is "/etc/nginx/letsencrypt/livefullchain.pem"