I am currently working on a Nginx config to group some of my docker containers into subdomains. Some of these containers are not running permanently and prevent nginx from starting (with error host not found in upstream "somecontainer:5000" in /etc/nginx/conf.d/default.conf:48) because the host defined in the upstream is not reachable. Is there a way to set a fallback upstream server in case the first one is not running?
The config currently looks like that:
upstream somecontainer {
server somecontainer:5000;
# here i need something like: if host is unreachable
# server fallbackserver:5000
}
server {
listen 443 ssl http2;
server_name some.subdomain.com;
root /public_html/;
client_max_body_size 16384m;
ssl on;
server_tokens off;
ssl_certificate sslstuff;
ssl_certificate_key sslstuff;
ssl_buffer_size 8k;
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;
location / {
proxy_pass http://somecontainer;
proxy_set_header Host $http_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-Proto $scheme;
proxy_read_timeout 900;
}
}
Unfortunately, it's because of Nginx design.
You can use variable in proxy_pass which will be resolved at runtime, so there is no such error on nginx load:
set $destination_host somecontainer;
proxy_pass http://$destination_host:5000;
But the disadvantage of the solution above is that you can not leverage nginx upstream such as specify load balancing algorithm or weighted balancing...
Additionally, You have to patch nginx if both upstream and dynamic service initialization is a need. I have a patch which change that Nginx design and was discussed here and I'm using it on production environment for a while. You can check it if patching is not a problem to you https://github.com/ZigzagAK/ngx_dynamic_upstream/issues/8#issuecomment-814702336
Related
I am trying to setup Nginx Docker reverse proxy with a single container and am getting connection refused. See attachment for error.Results of browsing to https://IPAddressofServer
I am getting the following error in the logs:
2021/10/21 17:41:00 [warn] 19199#19199: conflicting server name "" on 0.0.0.0:443, ignored
Can anyone assist with this issue?
OS is Ubuntu 20 running on an Azure VM
Here is my .conf configuration within /etc/nginx/sites-enabled
`server {
listen 443 ssl;
server_name DNSNameofServer;
ssl_certificate /etc/ssl/certs/ChainedCertName.pem;
ssl_certificate_key /etc/ssl/certs/KeyFileName.key-plain.key;
ssl_protocols TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://DNSNameofServer:8088;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}`
I am trying to set up a home GitLab + GitLab container registry(VM ubuntu omnibus install) behind an Nginx reverse proxy(win10):
I took the Nginx config from here: https://gitlab.com/gitlab-org/gitlab-recipes/-/tree/master/web-server/nginx
server {
listen 443 ssl;
listen 5050 ssl;
#listen [::]:443 ipv6only=on ssl;
server_name my-gitlab.org; ## Replace this with something like gitlab.example.com
server_tokens off; ## Don't show the nginx version number, a security best practice
root /opt/gitlab/embedded/service/gitlab-rails/public;
ssl_certificate /nginx-1.18.0/my-gitlab.crt;
ssl_certificate_key /nginx-1.18.0/my-gitlab.key;
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
location / {
client_max_body_size 0;
gzip off;
proxy_cache off;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass https://my-gitlab-IP;
}
}
Opening GitLab on 433 works so does Docker login on 5050, but when I try to push or pull from the container registry :
Error response from daemon: error parsing HTTP 404 response body: unexpected end of JSON input: ""
tried many suggestions changing the GitLab.rb file, but non-work without the Nginx-revseproxing it Docker push/pull works so I am pretty sure it's something in my Nginx config that is missing, but not sure what.
Can anyone help?
it appears I needed a separate server block for the registry
server
{
listen 5005 ssl;
server_name my-ip:5005;
ssl_certificate /nginx-1.18.0/my-ip.crt;
ssl_certificate_key /nginx-1.18.0/my-ip.key;
location /
{
client_max_body_size 0;
proxy_pass https://my-ip:5005;
}
}
reverted back to default GitLab registry port
Is there a "proper" structure for the directives of an NGINX Reverse Proxy? I have seen 2 main differences when looking for examples of an NGINX reverse proxy.
http directive is used to house all server directives. Servers with data are listed in a pool within the upstream directive.
server directives are listed directly within the main directive.
Is there any reason for this or is this just a syntactical sugar difference?
Example of #1 within ./nginx.conf file:
upstream docker-registry {
server registry:5000;
}
http {
server {
listen 80;
listen [::]:80;
return 301 https://$host#request_uri;
}
server {
listen 443 default_server;
ssl on;
ssl_certificate external/cert.pem;
ssl_certificate_key external/key.pem;
# set HSTS-Header because we only allow https traffic
add_header Strict-Transport-Security "max-age=31536000;";
proxy_set_header Host $http_host; # required for Docker client sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client IP
location / {
auth_basic "Restricted"
auth_basic_user_file external/docker-registry.htpasswd;
proxy_pass http://docker-registry; # the docker container is the domain name
}
location /v1/_ping {
auth_basic off;
proxy_pass http://docker-registry;
}
}
}
Example of #2 within ./nginx.conf file:
server {
listen 80;
listen [::]:80;
return 301 https://$host#request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
error_log /var/log/nginx/error.log info;
access_log /var/log/nginx/access.log main;
ssl_certificate /etc/ssl/private/{SSL_CERT_FILENAME};
ssl_certificate_key /etc/ssl/private/{SSL_CERT_KEY_FILENAME};
location / {
proxy_pass http://app1
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr; # could also be `$proxy_add_x_forwarded_for`
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Request-Start $msec;
}
}
I dont quite understand your question, but it seems to me that the second example is missing the http {}, I dont think that nginx will start without it.
unless your example2 file is included somehow in the nginx.conf that has the http{}
I had a Shopify-like application. So, my customer get sub-domain when they create store(i.e customer1.myShopify.com).
to handle this case of dynamic sub-domains with nginx:
server {
listen 443 ssl;
server_name admin.myapp.com;
ssl_certificate /etc/letsencrypt/live/myapp/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp/privkey.pem;
location / {
proxy_pass http://admin-front-end:80/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
}
}
server {
listen 443 ssl;
server_name *.myapp.com;
ssl_certificate /etc/letsencrypt/live/myapp/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp/privkey.pem;
location / {
proxy_pass http://app-front-end:80/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
}
}
this works great so if you visit admin.myapp.com you'll see the admin application and if you visit any xxx.myapp.com you'll see the shop-front-end application.
The Problem
I want to allow my customer to connect their own domain. so I told them to connect with CNAME and A Record.
A Record => # => 12.12.12.3(my root nginx ip)
CNAME => WWW => thier.myapp.com
not each request to customer.com will resolved by my nginx.
so I added this configuration to my nginx, to catch all other server_name request:
server {
listen 80;
server_name server_name ~^.*$;
location / {
proxy_pass http://app-front-end:80/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
}
}
and it works fine.
but how can I handle SSL for this case? because it could be any domain name.I don't know what the customer domain name will be.
how i can give them the ability to add SSL certificate automatically and without create manually ?
This server block should work since variable names are supported for ssl_certificate and ssl_certificate_key directives.
http {
map "$ssl_server_name" $domain_name { ~(.*)\.(.*)\.(.*)$ $2.$3; }
server {
listen 443;
server_name server_name ~^.*$;
ssl_certificate /path/to/cert/files/$domain_name.crt;
ssl_certificate_key /path/to/cert/keys/$domain_name.key;
location / {
proxy_pass http://app-front-end:80/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
}
}
}
P.S. Using variable names would compromise performance because now nginx would load the files on each ssl handshake.
Ref: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate
In my view instead of compromising the nginx performance, we should use one cron job, say letsencrypt bot, which will fetch certificate based on user requested domain, and you can add the certificate in nginx conf, and restart server.
Bonus:
I have used traefik which is kubernetes based solutions, they load configs on the fly without the restart.
In VM you can use the certbot for managing the SSL/TLS certificate.
Now if you are using the HTTP-01 method to verify your domain you won't be able to get the Wild card domain name.
i would suggest to use the DNS-01 method in cert-bot for domain verification and you can get the wild card certificate and use it.
Adding the certificate to Nginx config using :
ssl_certificate /path/to/cert/files/tls.crt;
ssl_certificate_key /path/to/cert/keys/tls.key;
If you are using the certbot it will also auto inject and add the SSL config above lines to the configuration file.
For different domains also you can run the job or certbot with HTTP-01 method and you will get the certificate.
If you are on Kubernetes you can use the cert-manager, which will be managing the SSL/TLS certificate.
I suggest using separete block with different ssh certificates thats the only solution that worked for me
server {
listen 80;
root /var/www/html/example1.com;
index index.html;
server_name example1.com;
ssl_certificate /etc/letsencrypt/live/example1.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example1.com/privkey.pem;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
root /var/www/html/example2.com;
index index.html;
server_name example2.com;
ssl_certificate /etc/letsencrypt/live/example2.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example2.com/privkey.pem;
location / {
try_files $uri $uri/ =404;
}
}
I'm a bit out of my element trying to deploy a Laravel (php) application via docker. Everything works great until I try to use SSL certs via Lets Encrypt, which triggers a redirect loop I'm unable to resolve.
upstream app {
server app1520925178:80;
}
server {
listen 80 default_server;
server_name app.example.com;
# handle future LE refreshes
location /.well-known {
root /var/www/html;
try_files $uri $uri/ =404;
}
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl default_server;
server_name app.example.com;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
keepalive_timeout 300s;
ssl on;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
charset utf-8;
location / {
#include proxy_params;
proxy_pass http://app;
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;
# Handle Web Socket connections
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Any guidance is greatly appreciated.
EDIT: It "randomly" started working a few minutes after this post was created. Still not 100% sure why it would take time to "propagate", if anyone has insight into that, I'd appreciate it.
If you use Cloudflare as your DNS service (not registering domains over it, but managing your DNS records with it) and have Cloudflare's protection enabled (orange cloud symbol), this can occur.
Note the following paragraph in this support article by Cloudflare:
If the origin server happens to be configured to redirect HTTP requests to HTTPS, server responses back to Cloudflare are encrypted and since Cloudflare is expecting HTTP traffic, it keeps resending the same request, resulting in a redirect loop. This causes browsers to display "The page isn’t redirecting properly" or "ERR_TOO_MANY_REDIRECTS" errors.
So, the flexible SSL is most likely your issue. You can turn that off by going to the crypto page in your Cloudflare control panel and setting the SSL mode to "Full (strict)":
This resolved my issues on an Apache system, but I'm very confident it's the same source of problems with nginx.
Changing the crypto setting to "Full (strict)" may help: