SSL Termination on NGINX - ssl

I have purchased an SSL cert and bundled it up correctly in so much as when I verify the modulus (i.e. https://kb.wisc.edu/middleware/4064) then the hashes are the same.
I have moved the cert and key to my server # /etc/ssl and ensured that the folder permissions are 700 and each file is 600.
I have then the following nginx config:
server {
listen 80;
listen 443 ;
server_name escapehatch.chrisjowen.uk;
ssl on;
ssl_certificate /etc/ssl/ssl-bundle.crt;
ssl_certificate_key /etc/ssl/secret.txt;
access_log /var/log/nginx/nginx.vhost.access.log;
error_log /var/log/nginx/nginx.vhost.error.log;
location / {
proxy_pass http://localhost:8080;
}
}
Finally, to test this, I have a Python SimpleHTTPServer running on port 8080. When I hit the URL on HTTPS, I receive an error
This site can’t provide a secure connection
Looking at the logs from the Python server, I see:
218.186.183.142 - - [21/Aug/2019 04:45:53] code 400, message Bad HTTP/0.9 request type ('\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03\x01a\x96\x061LE\x88I\xf1i\x7f\xc3\xdc%d\x18r\xbbzq9q<\xeb\x1dD\xa3\x8b\x01\x10\x7f')
218.186.183.142 - - [21/Aug/2019 04:45:53] "�a�1LE�I�i��%dr�zq9q<�D�� n��Z���΀��SN�F���j;X.Zw�s^�"**�+�/�,�0̨̩����/5" 400 -
218.186.183.142 - - [21/Aug/2019 04:45:53] code 400, message Bad request version ('\x0fb\x03g\x8d\x04\x8b\xbe!\xad\x98W\x9bV\xd2\x8e\x1e\xc6\xf3\xaa\xff\xce\x0f\x1b\xc9\x0f\xebY\xae\xc4\x00"\xfa\xfa\x13\x01\x13\x02\x13\x03\xc0+\xc0/\xc0,\xc00\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00/\x005\x00')
So, it seems like nginx is not decrypting the request and terminating the SSL connection, instead it's passing it to the upstream server, which I do not want.
Checking the nginx logs /var/log/nginx/nginx.vhost.access.log shows nothing.
So, now I am stumped what to do to debug the issue, it appears that either nginx config is wrong or there is something wrong with the cert, but as mentioned I checked this with the following method https://kb.wisc.edu/middleware/4064

listen 80;
listen 443 ;
If you want it to listen for plain http on port 80 and https on port 443 the second line should be listen 443 ssl;.
ssl on;
From the documentation:
This directive was made obsolete in version 1.15.0. The ssl parameter of the listen directive should be used instead.
Also you have the following in the logs of your Python server:
218.186.183.142 - - [21/Aug/2019 04:45:53] code 400, ....
This Python server is clearly visited directly by an external IP address. If the request would be forwarded by the local nginx then the source IP should be 127.0.0.1 instead. This shows, that you don't hit nginx at all but somehow make a direct request to the Python server.

Related

How do I fix an infinite redirect loop on a self-hosted nginx server?

I'm learning how to build and host my own website using Python and Flask, but I'm unable to make my website work as I keep getting an infinite redirect loop when I try to access my website through my domain name.
I've made my website using Python, Flask, and Flask-Flatpages. I uploaded the code to GitHub and pulled it onto a Raspberry Pi 4 that I have at my house. I installed gunicorn on the RasPi to serve the website and set up two workers to listen for requests. I've also set up nginx to act as a reverse proxy and listen to requests from outside. Here is my nginx configuration:
server {
if ($host = <redacted>.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
# listen on port 80 (http)
listen 80;
server_name <redacted>.com www.<redacted>.com;
location ~ /.well-known {
root /home/pi/<redacted>.com/certs;
}
location / {
# redirect any requests to the same URL but on https
return 301 https://$host$request_uri;
}
}
server {
# listen on port 443 (https)
listen 443;
ssl on;
server_name <redacted>.com www.<redacted>.com;
# location of the SSL certificate
ssl_certificate /etc/letsencrypt/live/<redacted>.com/fullchain.pem; # m$
ssl_certificate_key /etc/letsencrypt/live/<redacted>.com/privkey.pem; #$
# write access and error logs to /var/log
access_log /var/log/blog_access.log;
error_log /var/log/blog_error.log;
location / {
# forward application requests to the gunicorn server
proxy_pass http://localhost:8000;
proxy_redirect off;
proxy_set_header X_Forwarded_Proto $scheme;
proxy_set_header Host $host;
location /static {
# handle static files directly, without forwarding to the application
alias /home/pi/<redacted>.com/blog/static;
expires 30d;
}
}
When I access the website by typing in the local IP of the RasPi (I've set up a static IP address in /etc/dhcpcd.conf), the website is served just fine, although it seems like my browser won't recognize the SSL certificate even though Chrome says the certificate is valid when I click on Not Secure > Certificate next to the .
To make the website public, I've forwarded port 80 on my router to the RasPi and set up ufw to allow requests only from ports 80, 443, and 22. I purchased a domain name using GoDaddy, then added the domain to CloudFlare by changing the nameservers in GoDaddy (I'm planning to set up cloudflare-ddns later, which is why I added the domain to CloudFlare in the first place). As a temporary solution, I've added the current IP of my router to the A Record in the CloudFlare DNS settings, which I'm hoping will be the same for the next few days.
My problem arises when I try to access my website via my public domain name. When I do so, I get ERR_TOO_MANY_REDIRECTS, and I suspect this is due to some problem with my nginx configuration. I've already read this post and tried changing my CloudFlare SSL/TLS setting from Flexible to Full (strict). However, this leads to a different problem, where I get a CloudFlare error 522: connection timed out. None of the solutions in the CloudFlare help page seem to apply to my situation, as I've confirmed that:
I haven't blocked any CloudFlare IPs in ufw
The server isn't overloaded (I'm the only one accessing it right now)
Keepalive is enabled (I haven't changed anything from the default, although I'm unsure whether it is enabled by default)
The IP address in the A Record of the DNS Table matches the Public IP of my router (found through searching "What is my IP" on google)
Apologies if there is a lot in here for a single question, but any help would be appreciated!
I only see one obvious problem with your config, which is that this block that was automatically added by certbot should probably be removed:
if ($host = <redacted>.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
Because that behavior is already specified in the location / {} block, and I think the Certbot rule may take effect before the location ~ /.well-known block and break that functionality. I'm not certain about that, and I don't think that would cause the redirects, but you can test the well-known functionality yourself by trying to access http://yourhost.com/.well-known and seeing if it redirects to HTTPS or not.
On that note, the immediate answer to your question is, get more information about what's happening! My next step would be to see what the redirect loop is - your browser may show this in its network requests log, or you can use a command-line tool like curl or httpie or similar to try to access your site via the hostname and see what requests are being made. Is it simply trying to access the same URL over and over, or is it looping through multiple URLs? What are they? What does that point at?
And as a side note, it makes sense that Chrome wouldn't like your certificate when accessing it via IP - certificates are tied to one or more hostnames, so when you're accessing it over an IP address, the hostname doesn't match, so Chrome is probably (correctly) pointing that out and warning you that you're not at the hostname the certificate says you should be at.

How to make nginx passthrough on 443 and redirect 80 to 443?

I have a winstone server (Jenkins) listening on 8443.
Jenkins has a valid cert, and Jenkins is doing the cert termination successfully:
JENKINS_ARGS="--httpPort=-1 --httpsKeyStore=/secure/jenkins.keystore --httpsKeyStorePassword=MY_PASSWORD --httpsPort=8443"
The only problem is that users now have to go:
https://example.com:8443
I don't want that port number in the URL.
I want:
https://example.com:8443 -> https://example.com
https://example.com -> https://example.com
http://example.com -> https://example.com
So I figure I'll run nginx on the same instance that is running Jenkins.
So my question is:
Do I have to reconfigure jenkins to NOT do cert termination so that nginx does it only?
Can nginx redirect 80 and 443 to localhost:8443 without a cert (Since Jenkins is doing cert termination)?
Do BOTH nginx AND Jenkins need to do cert termination?
Sorry for those similar questions.
I'm pretty sure an AWS ELB cannot replace what nginx is doing here, but I thought I'd throw it out there, in case an ELB can solve this for me too.
1) No, you can have Nginx Stream the connection directly to the Jenkins using the Stream Module.
Do note this was added in 1.9.0 but is not part of the default build so you might have to build it yourself.
It works a lot like an http server block but you have to set it up outside of the http block.
stream {
upstream jenkins_server {
server jenkins:443;
}
server {
listen 443;
proxy_pass jenkins_server;
}
}
2) You do not need a cert on nginx but you should have a http server block for port 80 that does a 301 to the 443 stream talked about in answer part 1.
server {
listen 80;
server_name your_server_name_here;
return 301 https://$host$request_uri;
}
3) No, you don't as you can use the nginx stream to passthru the ssl from the client to the Jenkins server.

nginx returns SSL certificate for subdomain that should have no certificate

I have a nginx server that manages a few domains and subdomains. There are some subdomains that have an own SSL certificate and they work fine. The problem is that if I try to open blablabla.mydomain.com (this subdomain is not configured in nginx) then firefox shows me an error "Connection is not secure" and "The certificate is only valid for xyz.mydomain.com" (this domain is configured with SSL and works well)
The same happens when I open the root domain mydomain.com. Then the server also returns the certificate for xyz.mydomain.com which is rejected by firefox.
I only want nginx to return the SSL certificate for domains/subdomains I explicitly have configured HTTPS. For what I understand, my configuration should be doing exactly this.
I configured all my https-subdomains like this:
server {
listen 443 ssl;
server_name xyz.mydomain.com;
root /var/www/xyz.mydomain/;
ssl on;
ssl_certificate /etc/letsencrypt/live/xyz.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xyz.mydomain.com/privkey.pem;
...
}
The root domain (which should have no SSL) is configured like this:
server {
listen 80;
server_name mydomain.com;
return http://some-redirection.com;
}
There is no SSL server block for this domain. Nor is for the other subdomains that do not exist. So why does nginx in these cases return the certificate for xyz.mydomain.com?
I don't use any wildcards in my server config. Is there some way to debug this? I mean, there must be a reason why nginx always returns the certificate for xyz.mydomain.com for every non-configured domain/subdomain. Why not another configured and working certificate?
I use an Ubuntu 14.04 server with nginx 1.4.6
If you need more info on my config, let me know
EDIT: I think I know why my config does not work. When using https the client encrypts also the domain name and this causes nginx to try all available server defintions. When it doesn't find one it returns the last one? And xyz.mydomain.com seems to be the last one (alphabetically)
So is there a way to avoid this? Would I have to create a ssl cert for every other subdomain?

Nginx serves different website (on the same sever) when using HTTPS

I have several websites hosted on the same sever. To simplify I have just 2 (http-only.com and https.com) and using nginx to handle requests.
One has SSL enabled. And another doesn't. I noticed links like this in Google Search Console http-only.com/https_server_path and when accessing an http-only.com server with https protocol I get requests served by an https.com server instead.
https.com:
server {
listen 443 ssl;
server_name https.com;
ssl on;
}
only-http.com:
server {
listen 80;
server_name only-http.com;
}
I think I should define something like a default ssl server to handle ssl for http.com, but don't know how to do it properly. I guess nginx should redirect https request to an http url if corresponding server doesn't handle https. Or maybe there is a better solution?

Nginx self-signed certificate isn't working on a Vagrant VM

We're running Vagrant VMs here. On a VM, I installed nginx. I then created a self-signed certificate. When I look at the certificate's innards, I see:
subject= /C=US/ST=IN/L=myCity/O=My Company/OU=MyProduct/CN=silly.com/emailAddress=info#silly.com
This is obviously sanitized. I believe this certificate is supposed to work for silly.com. Do I interpret this correctly?
On my laptop, I added a hostfile entry to map silly.com to the appropriate IP address.
On the VM, I added the following configuration to /etc/nginx/conf.d/default.conf
# HTTPS server server {
listen 443;
server_name silly.com;
ssl on;
ssl_certificate /etc/nginx/ssl/silly.crt;
ssl_certificate_key /etc/nginx/ssl/silly.key; }
When I browse the site, the port 80 http screen is displayed properly. When I browse with https://silly.com, however, the https portion is rejected and the non-SSL screen is displayed. (I think I clicked 'proceed' while experimenting...)
I commented from the nginx.conf file all lines relating to port 80. Then I restarted nginx. I will get the same success on port 80 and failure on port 443 as I did before.
I tested the config file with nginx -t. It reported no errors.
Would someone offer a debugging tip, please?
It might have something to do with the fact that your opening server { block is effectively commented out because of the # HTTPS server comment. You should actually be getting an error on this config because the closing } is there. Perhaps this is preventing you from restarting Nginx.