## Install nginx (Homebrew) `brew install nginx` Configuration file for nginx will be at /usr/local/etc/nginx/nginx.conf Web apps can be stored at /usr/local/var/www ## Commands Start: `nginx` Stop: `nginx -s stop` Reload config: `nginx -s reload` Lint: `nginx -t` ## Firewall - Firewall was already disabled. - Port forward 80 & 443 in router settings. ## Basic Reverse Proxy with Nginx The full nginx.conf file will be at the end - this here is a basic proxy example. In the block shown below - we created a web server: 1- 80 is the web server port. 2- localhost is where the web server is listening. 3- proxy_pass is the location we would like to proxy to. Should be the app server. Therefore, this block is saying: proxy all requests at http://localhost:80 to http://localhost:3000. ```nginx # nginx.conf server { listen 80; server_name localhost; location / { proxy_pass http://192.168.100.190:3000; } } ``` ## Issues ### 1. Dynamic IP The webserver server_name will usually point to a domain name - which DNS records should be set up for so it resolves to our public IP address. However, if that IP is dynamic its a problem as it eventually changes. In my case, I had a domain with namecheap and they allow you to update the IP address that a special DNS record points to with a [GET request](https://www.namecheap.com/support/knowledgebase/article.aspx/29/11/how-do-i-use-a-browser-to-dynamically-update-the-hosts-ip/). Therefore, we can automate that process with a bash script and a cronjob: ```sh #!/usr/bin/env sh IP4=$(dig @resolver1.opendns.com ANY myip.opendns.com +short) echo "$IP4" URL="https://dynamicdns.park-your-domain.com/update?host=%40&domain=mydomain.com&password=mypassword&ip=""${IP4}" echo "$URL" curl --request GET \ --url $URL ``` Crontab: - List cronjobs: `crontab -l` - Add cronjob (useful: https://crontab.guru) `crontab -e` ### 2. SSL Using Certbot (Let's Encrypt client) guide it is straightforward: Install certbot: `brew install certbot` To only generate certificate: `sudo certbot certonly --standalone -d mydomain.com` To generate certificate and update nginx.conf file automatically: `sudo certbot --nginx` The location of the files generated will be at /etc/letsencrypt/archive. It also generates symbolic links to those files at /etc/letsencrypt/live. To change our initial basic setup to use SSL and our domain name, this is how it would look like: ```nginx # nginx.conf server { listen 443 ssl; server_name mydomain.com; ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; # managed by Certbot location / { proxy_pass http://localhost:3000; } } ``` - "Managed by certbot" is a line added by certbot automatically after adjusting your nginx.conf file if you ran the automatic command. Otherwise you can amend your nginx.conf manually to include your SSL certificates. - One issue faced here is that the user process running nginx did not have the required permissions to read the certificates (error when running nginx -t). Grant the necessary permissions using chmod on the real SSL files at /etc/letsencrypt/archive/mydomain.com/ and the symbolic ones at /etc/letsencrypt/live/mydomain.com/. Perform a comprehensive SSL test using [SSL Labs](https://www.ssllabs.com/ssltest/). ## Full Setup - 2 Apps on localhost 3000 and 5000. - Each with own subdomain and SSL certificates. - HTTP redirects to HTTPS. ```nginx #user nobody; # Number of processes should not exceed number of cores # worker_processes 1; # MINIMUM (probably too low): worker_connections * 2 file descriptors = 512 # # No need to multiply by worker_prcocesses as the limit is applied to each worker # # 1 descriptor for client connection, 1 for proxied server # # Could be more based on conf. Could be limited by system (ulimit -n) # worker_rlimit_nofile 1024; events { # Default 1024 # worker_connections 256; } # Error Log # error_log logs/error.log; error_log logs/error.log notice; error_log logs/error.log info; # Process ID Log # pid logs/nginx.pid; http { include mime.types; default_type application/octet-stream; # Access Logs # map $request_uri $loggable { default 1; ~*\.(ico|css|js|gif|jpg|jpeg|png|svg|woff|ttf|eot)$ 0; } log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$request_body_file"'; access_log logs/access.log main buffer=32k flush=30m if=$loggable; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; # Max user upload size # client_max_body_size 20M; # Uploaded file RAM buffer instead of temp file # client_body_buffer_size 20M; # store request body in temp file for debugging # # client_body_in_file_only on; # Root domain HTTP # server { listen 80; server_name mydomain.com; return 301 https://$server_name$request_uri; } server { listen 80; server_name app1.mydomain.com; return 301 https://$server_name$request_uri; } server { listen 80; server_name app2.mydomain.com; return 301 https://$server_name$request_uri; } # Root domain HTTPS # server { listen 443 ssl; ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { return 403; } } # App 1 # server { listen 443 ssl; server_name app1.mydomain.com; ssl_certificate /etc/letsencrypt/live/app1.mydomain.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/app1.mydomain.com/privkey.pem; # managed by Certbot location / { proxy_pass http://localhost:3000; } } # App 2 # server { listen 443 ssl; server_name app2.mydomain.com; ssl_certificate /etc/letsencrypt/live/app2.mydomain.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/app2.mydomain.com/privkey.pem; # managed by Certbot location / { proxy_pass http://localhost:5000; } } } ``` ### Suggestions - Regex based HTTP redirects to HTTPS subdomains instead of multiple HTTP server blocks. - Wildcard SSL certificate instead of different SSL certificates for each subdomain. - The SSL certificate generated here achieves a rating of B on [SSL labs](https://www.ssllabs.com/ssltest/). Todo for A+?