Trouble getting SSL to work with NGINX in Docker - docker

I am trying to use NGINX as a proxy with a next.js frontend and FastAPI backend, each running in their own container.
I got everything working fine with HTTP, but having some issues getting things to work with HTTPS.
All containers start running without any issues, and things seem to be working, but when I try to communicate with the proxy, I get the following errors:
From host:
lafton#lafton-platform:~$ curl localhost -L
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:443
Form inside NGINX container using localhost:
root#6016e75698cf:/# curl localhost -L
curl: (60) SSL: no alternative certificate subject name matches target host name 'localhost'
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
From inside NGINX container using lafton.io:
root#6016e75698cf:/# curl https://lafton.io
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to lafton.io:443
I tried to install NGINX locally instead of inside Docker and it works as expected. I tried to enable the SSL configuration which is commented out in the default configuration, and it worked perfectly with SSL locally.
I then tried to use the default SSL configuration with my setup, but it does not work.
This is the NGINX config I am running inside /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
load_module /etc/nginx/modules/ngx_http_js_module.so;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name lafton.io;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name lafton.io;
ssl_certificate /etc/certs/fullchain1.pem;
ssl_certificate_key /etc/certs/privkey1.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://lafton-website:3000;
}
location /api/albums {
proxy_pass http://lafton-albums:8000;
}
}
}
The port 80 part is just a redirect to https. It is the exact same without it.
The ciphers is from Mozillas recommendations. I tried to change this from the default as some of the troubleshooting I did seemed to indicate no matching ciphers.
I am really lost here and not sure where to look for further troubleshooting. Any help would be really appreciated!

Timo Stark's comment solved the issue.
It didn't work inside the container because the certificates CN was lafton.io, so I had to use the -k flag in the curl command.
When that worked, I saw a typo in my docker-compose file, so the container had exposed port 433, not 443.

Related

Enable Docker port access only with Nginx reverse proxy

I have a Docker container on port 8081 running on Centos7, and a reverse proxy with Nginx.
My domain have a LetsEncrypt SSl installed and it works good when i access "https://my.example.com", it redirects me to my 8081 Docker.
But i when i access "http://my.example.com:8081", i still can reach my Docker application...i don't want to enable this...don't want to enable any http access.
I want to reach 8081 only through Nginx reverse proxy (that forces me to https)...i think it may be some configuration on my iptables, but i don't have experience with it.
Can someone help me?
Thanks!
This is my conf.d file in Nginx
server{
server_name my.example.com;
location / {
proxy_pass http://localhost:8081;}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/my.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/my.example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server{
if ($host = my.example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name my.example.com;
return 404; # managed by Certbot
}
iptables does not understand the difference between HTTP or HTTPS, it understands only ip; ports and mac levels, if you try to block port 8081 with iptables even your https connection will be dropped or rejected depending on your choice.
If your docker container is accessible from the outside without passing through the reverse proxy, it is a container configuration issue, or if your nginx reverse proxy lets through http packets, then it is an nginx configuration issue, I think we need more details from your side.
I have resolved this issue using the firewall application from my hosting provider(Vultr).
There, i left 8081 only for local access, so now it's not possible to access this without passing through Nginx reverse proxy!

nginx config reverse proxy + docker + http to https redirect

Context
I have a nginx container in front of several other containers. One of them running node.js front end that is presenting on 10001:3000
I've managed to piece together a nginx config that partially works, allowing SSL termination on 443 to the container on 10001.
However, I now need to re-direct all traffic to HTTPS and ideally prevent port 10001 from working, some sort of http catch all?
Here is my localhost config for this.
user www-data;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
worker_processes 2;
events {
worker_connections 1024;
multi_accept off;
}
stream {
upstream stream_backend {
server 172.17.0.1:10001;
# server backend2.example.com:12345;
#server backend3.example.com:12345;
}
server {
listen 443 ssl;
proxy_pass stream_backend;
ssl_certificate /etc/letsencrypt/live/localhost/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/localhost/fullchain.pem;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 4h;
ssl_handshake_timeout 30s;
#...
}
}
Beyond this everything I try I get a syntax error or some other error. Can anyone offer some plain advice?
If you are using docker-compose and adding your API and nginx to the same bridge network, you can expose the port to your api container, and remove the ports directive. This will allow nginx to communicate with the api container, but there will be no open port to the api that is publically available.
ports:
- "8000:80"
expose:
- "8000"
Above, the ports directive opens port 8000 to the public. Expose makes 8000 available only over the local subnet or bridge network. So, in this scenario, my suggestion is to remove the ports section. Expose also works with the run command but you will need to create a bridge network manually in that case.
I had the same issue recently. Actually, I had several issues, but this was one of them. See my question and answer here for more detail.
NGINX reverse proxy not working for .NET core webAPI running in Docker

nginx responds to HTTPS but not HTTP

I am using the dockerized Nextcloud as shown here: https://github.com/nextcloud/docker/tree/master/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm
I set this up with port 80 mapped to 12345 and port 443 mapped to 12346. When I go to https://mycloud.example.com:12346, I get the self-signed certificate prompt, but otherwise everything is fine and I see the NextCloud web UI. But when I go to http://mycloud.example.com:12345 nginx (the proxy container) gives error "503 Service Temporarily Unavailable". The error also shows up in proxy's logs.
How can I diagnose the issue? Why is HTTPS working but not HTTP?
Can you provide your docker command starting nextcloud, or docker-compose file ?
Diagnosis is as usual with docker stuff : get the id for the currently running container
docker ps
Then check the logs
docker logs [id or name of your container]
docker-compose logs [name of your service]
Connect in the container
docker exec -ti [id or name of your container] [bash or ash if alpine based container]
There read the nginx conf files involved. In your case I'ld check the redirection being made from http to https, most likely it's something like below with no specific port specified for https, hence port 443, hence not working
server {
listen 80;
server_name my.domain.com;
return 301 https://$server_name$request_uri; <======== no port = 443
}
server {
listen 443 ssl;
server_name my.domain.com;
# add Strict-Transport-Security to prevent man in the middle attacks
add_header Strict-Transport-Security "max-age=31536000" always;
[....]
}

is authentication possible with Onlyoffice?

I run onlyoffice with docker docker run -i -t -d -p 80:80 onlyoffice/documentserver and a nginx load balancer which provide ssl encryption.
My question is, how can i provide a authentication? without to touch the load balancer.
The Problem is, everybody can use the server.
The Problem is, everybody can use the server.
We would recommend to enable JWT on the Document Server.
It is supported by the NC connector
http basic auth works, tested with nextcloud integration:
root#e54c225ab8aa:/# cat /etc/nginx/conf.d/onlyoffice-documentserver.conf
include /etc/nginx/includes/onlyoffice-http.conf;
server {
listen 0.0.0.0:80;
listen [::]:80 default_server;
server_tokens off;
include /etc/nginx/includes/onlyoffice-documentserver-*.conf;
}root#e54c225ab8aa:/#
insert eg.:
auth_basic “Administrator’s Area”;
auth_basic_user_file /etc/nginx/.htpasswd;
and restart nginx /etc/init.d/nginx restart

LetsEncrypt in a Docker (docker-compose) app container not working

I'm using docker-compose for a rails app to have an app and db container. In order to test some app functionality I need SSL...so I'm going with LetsEncrypt vs self-signed.
The app uses nginx, and the server is ubuntu 14.04 lts, with the phusion passenger docker image as a base image (lightweight debian)
Normally with LetsEncrypt, I run the usual ./certbot-auto certonly --webroot -w /path/to/app/public -d www.example.com
My server runs nginx (proxy passing the app to the container), so I've hopped into the container to run the certbot command without issue.
However, when I try to go to https://test-app.example.com it doesn't work. I can't figure out why.
Error on site (Chrome):
This site can’t be reached
The connection was reset.
Curl gives a bit better error:
curl: (35) Unknown SSL protocol error in connection to test-app.example.com
Server nginx app.conf
upstream test_app { server localhost:4200; }
server {
listen 80;
listen 443 default ssl;
server_name test-app.example.com;
# for SSL
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_dhparam /etc/ssl/dhparam.pem;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-blahblahblah-SHA';
location / {
proxy_set_header Host $http_host;
proxy_pass http://test_app;
}
}
Container's nginx app.conf
server {
server_name _;
root /home/app/test/public;
ssl_certificate /etc/letsencrypt/live/test-app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/test-app.example.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_dhparam /etc/ssl/dhparam.pem;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-blahblah-SHA';
passenger_enabled on;
passenger_user app;
passenger_ruby /usr/bin/ruby2.3;
passenger_app_env staging;
location /app_test/assets/ {
passenger_enabled off;
alias /home/app/test/public/assets/;
gzip_static on;
expires +7d;
add_header Cache-Control public;
break;
}
}
In my Dockerfile, I have:
# expose port
EXPOSE 80
EXPOSE 443
In my docker-compose.yml file I have:
test_app_app:
build: "."
env_file: config/test_app-application.env
links:
- test_app_db:postgres
environment:
app_url: https://test-app.example.com
ports:
- 4200:80
And with docker ps it shows up as:
Up About an hour 443/tcp, 0.0.0.0:4200->80/tcp
I am now suspecting it's because the server's nginx - the "front-facing" server - doesn't have the certs, but I can't run the LetsEncrypt command without an app location.
I tried running the manual LetsEncrypt command on the server, but because I presumably have port 80 exposed, I get this: socket.error: [Errno 98] Address already in use Did I miss something here?
What do I do?
Fun one.
I would tend to agree that it's likely due to not getting the certs.
First and foremost read my disclaimer at the end. I would try to use DNS authentication., IMHO it's a better method for something like Docker. A few ideas come to mind. Easiest that answers your question would be a docker entrypoint script that gets the certs first and then starts nginx:
#!/bin/bash
set -ea
#get cert
./certbot-auto certonly --webroot -w /path/to/app/public -d www.example.com
#start nginx
nginx
This is "okay" solution, IMHO, but is not really "automated" (which is part of the lets encrypt goals). It doesn't really address renewing the certificate down the road. If that's not a concern of yours, then there you go.
You could get really involved and create an entrypoint script that detects when the cert expires and then rerun the command to renew it and then reloads nginx.
A much more complicated (but also more scalable solution) would be to create a docker image that's sole purpose in life is to handle lets_encrypt certificates and renewals and then provide a way of distributing those certificates to other containers, eg: nfs (or shared docker volumes if you are really careful).
For anyone in the future reading this: this was written before compose hooks was an available feature, which would be by far the best way of handling something like this.
Please read this disclaimer:
Docker is not really the best solution for this, IMHO. Docker images should be static data. Because lets encrypt certificates expire after 3 months, that means your container should have a shelf-life of three months or less (or, like I said above, account for renewing). "Thats fine!" I hear you say. But that would also mean you are constantly getting a new certificate issued each time you start the container (with the entrypoint method). At the very least, that means that the previous certificate gets revoked every time. I don't know what the ramifications are for doing this with Lets Encrypt. They may only give you so many revokes before they think something fishy is going on.
What I tend to do most often is actually use configuration management and use nginx as the "front" on the host system. Or rely on some other mechanism to handle SSL termination. But that doesn't answer your question of how to get Lets Encrypt to work with docker. :-)
I hope that helps or points you in a better direction. :-)
I knew I was missing one small thing. As stated in the question, since the nginx on the server is the 'front-facing' nginx, with the container's nginx specifically for the app, the server's nginx needed to know about the SSL.
The answer was super simple. Copy the certs over! (Kudos to my client's ops lead)
I cat the fullchain.pem and privkey.pem in the docker container and created the associated files in /etc/ssl on the server.
On the server's /etc/nginx/sites-enabled/app.conf I added:
ssl_certificate /etc/ssl/test-app-fullchain.pem;
ssl_certificate_key /etc/ssl/test-app-privkey.pem;
Checked configuration and restarted nginx. Boom! Worked like a charm. :)

Resources