nox.im · All Snippets · All in Ubuntu · All in Server

Ubuntu Expose Services Behind an Nginx Reverse Proxy

Install Nginx for our reverse proxy setup:

apt-get install -y nginx

Start the service and ensure it starts on reboot:

systemctl start nginx
systemctl enable nginx

We can test this by hitting our box now with a web browser. If it works we’ll setup basic auth first on the /etc/nginx/sites-available/default site which is located under:

root /var/www/html;

Create an htpasswd file with OpenSSL

echo "someuser:$(openssl passwd -crypt somepass)" > /etc/nginx/conf.d/.htpasswd

And reference it in the config:

server {
    # ...
    location / {
        root /var/www/html;
        error_page 404 =200 /index.html;

        # ...
        auth_basic           "closed site";
        auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
    }
}

NOTE: The error_page 404 =200 /index.html; is generally recommended when *working with single page apps (SPA), “modern web” sites to always serve the *index on paths that the app will route for us.

Check if the config is good, and if so reload it:

sudo nginx -t
sudo nginx -s reload

Nginx with basic authentication should now be working. We’ll reuse this setup in the reverse proxy section below.

Firewall

By default our services would be exposed on their native ports, not just through the reverse proxy. For extra security we’ll want to lock down the server to just Nginx. Setup ufw and ensure the SSH port is open BEFORE enabling it.

sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

We can view rules:

ufw status numbered
Status: active

     To                         Action      From
     --                         ------      ----
[ 1] 22/tcp                     ALLOW IN    Anywhere
[ 2] 80/tcp                     ALLOW IN    Anywhere
[ 3] 8080/tcp                   ALLOW IN    Anywhere
[ 4] 22/tcp (v6)                ALLOW IN    Anywhere (v6)
[ 5] 80/tcp (v6)                ALLOW IN    Anywhere (v6)
[ 6] 8080/tcp (v6)              ALLOW IN    Anywhere (v6)

and delete a rule with:

sudo ufw delete 6

Setup our Service

Create a systemd service file

touch /etc/systemd/system/solana-test-validator.service
chmod 664 /etc/systemd/system/solana-test-validator.service

point the service file to your binary, here it’s the Solana test validator from my Raspberry Pi post:

[Unit]
Description=Solana Test Validator

[Service]
Restart=always
ExecStart=/usr/local/bin/solana-test-validator -q --ledger /var/db/solana/test-ledger

[Install]
WantedBy=multi-user.target

Reload the service files and start our new service

systemctl daemon-reload
systemctl start solana-test-validator
systemctl status solana-test-validator

if ok, enable so it survives a reboot

systemctl enable solana-test-validator

Logrotate

Our service may produce log files. In order to not run out of disk space, we’ll add a logrotate onto the file:

vim /etc/logrotate.d/solana

With zero days retention, running hourly if the file is not empty.

/var/local/solana-test-ledger/validator-*.log {
	hourly
	missingok
	rotate 1
    copytruncate
	notifempty
}

In this case, the log files look as follows:

ls -lh /var/local/solana-test-ledger/*.log
-rw-r--r-- 1 root staff 550M Mar 10 08:37 /var/local/solana-test-ledger/validator-1646890477927.log
-rw-r--r-- 1 root staff 278M Mar 10 10:51 /var/local/solana-test-ledger/validator-1646903838680.log
-rw-r--r-- 1 root staff 543M Mar 10 14:01 /var/local/solana-test-ledger/validator-1646909768151.log
lrwxrwxrwx 1 root staff   27 Mar 10 10:56 /var/local/solana-test-ledger/validator.log -> validator-1646909768151.log

If we deal with a service that doesn’t close their log files on SIGHUB and doesn’t rotate the files by itself as it is the case here, we can use the copytruncate option.

You can test this by running logrotate manually:

logrotate --force /etc/logrotate.d/solana

Note that usually logrotate is configured to be run by cron daily. To run logrotate hourly copy the file /etc/cron.daily/logrotate into the /etc/cron.hourly/ directory.

As part of systemd, we can setup journald to vaccuum by time and size to only retain 1 day and max 300MB.

journalctl --vacuum-time=1d
journalctl --vacuum-size=300M

Expose the service through a Reverse Proxy

Create and enable a new Nginx “site” called “reverse-proxy”:

touch /etc/nginx/sites-available/reverse-proxy
ln -s /etc/nginx/sites-available/reverse-proxy /etc/nginx/sites-enabled/reverse-proxy
vim /etc/nginx/sites-available/reverse-proxy

Link an upstream service, the one we want to expose through the reverse proxy and a server section with the proxy_pass and the basic authentication configuration from above:

upstream myservice {
        server 127.0.0.1:8081;
}


server {
  listen 8080;
  server_name _;

  location / {
        proxy_pass http://myservice;

        auth_basic           "closed site";
        auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
    }
}

Note that you can have multiple upstream and multiple server blocks.

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Everything seems to be good, let’s reload nginx:

sudo systemctl reload nginx

We’re done, enjoy!

If you need a service from a web frontend, you may want to look at my follow up on Cross-Origin Resource Sharing (CORS) headers.


Last modified on Saturday, Mar 12, 2022.
Go back