nginx, Meteor and Docker: Proxy SSL redirection do

2019-05-05 17:28发布

I am trying to set up nginx as a proxy server in front of a Meteor app. These will run in a Docker Container. What I would like to do is have every request to / be redirected as a as SSL call to the Meteor server (on port 8080). However, when I do this all that happens is that in the browser it comes back and says https://localhost and nothing happens, the Meteor app is not displayed. It is noted that I have created a self-signed SSL certificate where the server name is "localhost". However, if I remove the SSL part then the redirection works perfectly and a call to / results in a successful call Meteor on port 8080. So, how do I get this working correctly with SSL? Are the self-signed localhost certificates the problem? Below shows the config that works, and the config that does not work (with SSL). Thanks :)

This works

server_tokens off; # for security-by-obscurity: stop displaying nginx version

# This section is needed to proxy web-socket connections
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# HTTP
server {
    # If this is not a default server, remove "default_server"
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    # These are irrelevant
    root /usr/share/nginx/html;
    index index.html index.htm;

    # The domain on which we want to host the application. Since we set "default_server"
    # previously, nginx will answer all hosts anyway.
    server_name localhost;

     # The redirection.
     location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection ‘upgrade’;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

This does not work

server_tokens off; # for security-by-obscurity: stop displaying nginx version

# This section is needed to proxy web-socket connections
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# HTTP
server {
    # If this is not a default server, remove "default_server"
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    # These are irrelevant
    root /usr/share/nginx/html;
    index index.html index.htm;

    # The domain on which we want to host the application. Since we set "default_server"
    # previously, nginx will answer all hosts anyway.
    server_name localhost;

    # Redirect non-SSL to SSL
    location / {
        rewrite     ^ https://$server_name$request_uri? permanent;
    }
}

# HTTPS server
server {
    # We enable SPDY here
    listen 443 ssl spdy;

    # This domain must match Common Name (CN) in the SSL certificate
    server_name localhost;

    # Irrelevant
    root html;
    index index.html;

    # Full path to SSL certificate and CA certificate concatenated together
    ssl_certificate /etc/nginx/ssl/server.crt;

    # Full path to SSL key
    ssl_certificate_key /etc/nginx/ssl/server.key;

    # Performance enhancement for SSL
    ssl_stapling on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;

    # Safety enhancement to SSL: make sure we actually use a safe cipher
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-    SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384: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-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK';

    # Config to enable HSTS(HTTP Strict Transport Security)     https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security

    # To avoid ssl stripping     https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
    add_header Strict-Transport-Security "max-age=31536000;";

    # If your application is not compatible with IE <= 10, this will redirect visitors to a page advising a browser update
    # This works because IE 11 does not present itself as MSIE anymore
    if ($http_user_agent ~ "MSIE" ) {
        return 303 https://browser-update.org/update.html;
    }

    # Pass all requests to Meteor
    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade; # allow websockets
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header X-Forwarded-For $remote_addr; # preserve client IP

        # This setting allows the browser to cache the application in a way compatible with Meteor
        # on every application update the name of CSS and JS file is different, so they can be cache
        # infinitely (here: 30 days). The root path (/) MUST NOT be cached
        if ($uri != '/') {
            expires 30d;
        }
    }
}

2条回答
趁早两清
2楼-- · 2019-05-05 18:25

I could not get it to work on "localhost" but here is a working production version with lots of description to help people out:

# This configuration provides strong SSL security on the nginx webserver. We do
# this by disabling SSL Compression to mitigate the CRIME attack, disable SSLv3
# and because of vulnerabilities in the protocol and we will set up a strong
# ciphersuite that enables Forward Secrecy when possible. We also enable HSTS and
# HPKP. This way we have a strong and future proof ssl configuration and we get
# an A on the Qually Labs SSL Test.
#
# This configuration passes all the requests to a Meteor server.  In order for
# this to work you have to ensure that Meteor does NOT implement force-ssl.

# Enables or disables emitting nginx version in error messages and in the Server
# response header field.
server_tokens off;

# This turns a connection between a client and server from HTTP/1.1 into a WebSocket,
# the protocol switch mechanism available in HTTP/1.1 is used.  In this implementation
# the Connection header field in a request to the proxied server depends on the
# presence of the Upgrade field in the client request header.
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# Nginx upstream services.  Nginx connects to nodejs on the IPv6 loopback [::1] and so
# you must specify the IP address here or nodejs will just listen on IPv4.
upstream meteor-server {
    server 127.0.0.1:8080;
}

# HTTP.  This is not the default server and will redirect all the calls
# to the https server.
server {
    # Listen on port 80 for ipv4 traffic and on [::]:80 for ipv6 traffic
    listen 80;
    listen [::]:80 ipv6only=on;

    # File paths - these are ignored.  The path is specific for nginx on Ubuntu.
    root /usr/share/nginx/html/;
    index index.html index.htm;

    # The domain on which we want to host the application.
    server_name <your server>;

    # Redirect non-SSL to SSL
    location / {
        rewrite     ^ https://$server_name$request_uri? permanent;
    }
}

# HTTPS.  This is the default server.
server {
    # Listen on port 443 for ipv4 traffic and on [::]:443 for ipv6 traffic.
    # This is the default server
    listen 443 ssl spdy;
    listen [::]:443 ssl spdy ipv6only=on;

    # File paths - these are ignored.  The path is specific for nginx on Ubuntu.
    root /usr/share/nginx/html/;
    index index.html index.htm;

    # The domain on which we want to host the application.
    server_name <your server>;

    # Full path to SSL Certificate and CA Certificate concatenated together
    ssl_certificate /etc/nginx/ssl/server.crt;

    # Full path to SSL Key
    ssl_certificate_key /etc/nginx/ssl/server.key;

    # When choosing a cipher during an SSLv3 or TLSv1 handshake, normally the
    # client's preference is used. If this directive is enabled, the server's
    # preference will be used instead.
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    # The ciphers
    ssl_ciphers 'AES128+EECDH:AES128+EDH:!aNULL';

    # This is for backwards compatibility with IE6/WinXP
    #ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384: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-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK';

    # SSL Protocols
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    # Online Certificate Status Protocol (OCSP) stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # Enforce the use of the Google DNS servers to resolve addresses
    resolver 8.8.4.4 8.8.8.8 valid=300s;
    resolver_timeout 10s;

    # Forward Secrecy & Diffie Hellman Ephemeral Parameters
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    # HTTP Strict-Transport-Security (HSTS) enforces secure (HTTP over SSL/TLS)
    # connections to the server. This reduces impact of bugs in web applications
    # leaking session data through cookies and external links and  defends against
    # Man-in-the-middle attacks. HSTS also disables the ability for user's to ignore
    # SSL negotiation warnings (ssl stripping). See: https://developer.mozilla.org/en-US/docs/Security/
    # and https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
    add_header Strict-Transport-Security max-age=63072000;

    # Provides for Clickjacking protection by denying the ability of the browser to
    # render a page in a <frame>, <iframe> or <object>.
    add_header X-Frame-Options DENY;

    # This prevents Internet Explorer and Google Chrome from MIME-sniffing a response
    # away from the declared content-type.
    add_header X-Content-Type-Options nosniff;

    # This header enables the Cross-site scripting (XSS) filter built into most
    # recent web browsers. It's usually enabled by default anyway.
    add_header X-XSS-Protection 1;

    # If your application is not compatible with IE <= 10, this will redirect visitors to
    # a page advising a browser update. This works because IE 11 does not present itself as
    # MSIE anymore
    if ($http_user_agent ~ "MSIE" ) {
        return 303 https://browser-update.org/update.html;
    }

    # Handle the root route.  This will pass everything onto the Meteor
    # server.
    location / {
        # Pass upstream
        proxy_pass http://meteor-server;

        # Socket.IO Support (WebSockets)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_upgrade;

        # This ensure that the Host header that the client sent nginx is
        # sent on to the backend
        proxy_set_header Host $host;

        # Defines conditions under which the response will not be taken from a cache.
        proxy_cache_bypass $http_upgrade;

        # This provides the real client IP rather than the one from the nginx proxy system.
        # This is very useful for logging etc.
        proxy_set_header X-Real-IP $remote_addr;

        # This is similar to X-Real-IP, but provides added connection source entries
        # for the entire chain of proxies the connection's passed through.
        proxy_set_header X-Forwarded-For $remote_addr;

        # A de facto standard for identifying the originating protocol of an HTTP request,
        # since a reverse proxy may communicate with a web server using HTTP even if the
        # request to the reverse proxy is HTTPS.
        proxy_set_header X-Forwarded-Proto $scheme;

        # This simply acts as as a marker that the proxy is used.  It not really needed.
        proxy_set_header X-NginX-Proxy true;

        # Sets the text that should be changed in the Location and Refresh header
        # fields of a proxied server response.
        proxy_redirect off;

        # This setting allows the browser to cache the application in a way compatible with Meteor
        # on every application update the name of CSS and JS file is different, so they can be cache
        # infinitely (here: 30 days). The root path (/) MUST NOT be cached
        if ($uri != '/') {
            expires 30d;
        }
    }
}
查看更多
贼婆χ
3楼-- · 2019-05-05 18:28

I had a similar issue with a Meteor 0.8 app, and I fixed it by adding the X-Real-IP and Host headers to the proxied request. The app was using the force-ssl package, which (I think) issues a redirect based on the Host header.

Here's the location block from my config:

# pass all requests to Meteor
location / {
    proxy_pass http://localhost:8080;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;  # http://wiki.nginx.org/HttpProxyModule
    proxy_set_header Host $host;  # pass the host header - http://wiki.nginx.org/HttpProxyModule#proxy_pass
    proxy_set_header Upgrade $http_upgrade; # allow websockets
    proxy_set_header Connection $connection_upgrade;

    # Browser can cache everything except the root
    if ($uri != '/') {
        expires 30d;
    }
}
查看更多
登录 后发表回答