Blogging in Swift (Part 2)

Introduction

This post follows where the first part left off.

Now that you've written a blog, or whatever you'd like as part of server-side Swift, the next step is to get it hosted somewhere and running all the time. Personally, I use DigitalOcean for my hosting running an Ubuntu VPS. Most of the instructions here are fairly generic, but depending on the distribution of Linux (or Unix) you're running, you may need to switch some things up.

Setting Up Swift

The first thing you'll want to do after getting your VPS is getting Swift 5 (the latest version as of writing this post) setup and running on it.

As a pre-requisite you'll need clang installed.

sudo apt-get install clang

You'll then want to download the latest version from swift.org.

Note: As of this writing, packages are only pre-built for Ubuntu, so if you're using something else, you'll need to compile your own version.

You'll want to unpack the downloaded Swift package, and then add it to your $PATH:

tar xzf swift-<VERSION>-<PLATFORM>.tar.gz
echo 'export PATH=/path/to/swift-<VERSION>-<PLATFORM>/usr/bin:"${PATH}"' >> ~/.bashrc

After that, you should be able to run Swift from the commandline.

> swift
Welcome to Swift version 5.0 (swift-5.0-RELEASE).
Type :help for assistance.
  1> var a = 1 + 1
a: Int = 2

Not Running Swift as Root

A really bad thing™ to do would be to run your Swift server as root… if someone manages to hack your server, they'll be able to wreck havoc as root. You'll want to configure your server to run as an unprivileged user.

sudo adduser swiftserver
sudo usermod -aG sudo swiftserver

This'll create a new user called swiftserver that can run privileged commands with sudo if needed (ps: don't start your server with sudo).

You'll want to logout and then log back in as the swiftserver user.

Serving Server-Side Swift

To serve the server, you can actually just run it directly via swift run and it'll be served (by default) on port 8080. However, this doesn't support SSL or any of the nice things that come with web servers (security, disabling access to folders, etc.). Whilst I'm sure you could do all of this by hand, I wanted to focus mostly on serving blog content and not the intricacies of web servers. To serve the Swift app, I setup NGINX and setup a reverse-proxy to my Swift app running. This way, the Swift app would run on a different port and NGINX would communicate with it (instead of it being the whole webserver).

It looks something like this:

[browser: https://blog.mysite.com/blog/entry] -> [NGINX] -> [swift-server] -> [NGINX] -> [browser]

Setting up NGINX is pretty easy:

sudo apt-get install nginx
systemctl status nginx

# DigitalOcean needs to have the firewall configured via ufw
# This opens both port 80 and 443, but you may want to use just Nginx HTTPS to be more secure after you've setup HTTPS
sudo ufw allow 'Nginx Full' 

Next you'll want to configure NGINX.

Create a file at /etc/nginx/sites-available/swift-blog (replace swift-blog with whatever your binary is called) and fill it out with something like this:

server {
  server_name blog.mysite.com;

  location / {
    proxy_set_header        Host $host:$server_port;
    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_pass          http://127.0.0.1:8888;
    proxy_read_timeout  90;

    proxy_redirect      http://127.0.0.1:8888 http://mysite.com;

    # Required for new HTTP-based CLI
    proxy_http_version 1.1;
    proxy_request_buffering off;
  }
}

Lastly, you'll want to activate this configuration by symlinking it:

sudo ln -s /etc/nginx/sites-available/swift-blog /etc/nginx/sites-enabled/swift-blog

# Reload NGINX
sudo systemctl restart nginx

You'll want to set the server_name to use the subdomain of where you're hosting this server, and proxy_pass and proxy_redirect accordingly (this is where you supply the port your server will be running on). Now whenever you visit blog.mysite.com you'll be sending web requests to your Swift app via NGINX.

There's a lot of configuration options available here, and if you'd like to setup a separate webserver alongside this app, you can do so in this file as well. For more information on that check out DigitalOcean's tutorial here.

Securing the Server

The next thing you'll want to do is serve your content securely using SSL. Whilst this is technically optional, I think it's much more future proof and safer to setup for people consuming your content. There's countless ways to set this up, but the easiest way I found was done by setting up Cloudflare (thanks @_inside!). When you setup your domain and verify it through Cloudflare you'll be given a certificate and a private key. You'll want to put those on your server somewhere (I used /etc/cloudflare/). You'll also want to download the Origin Pull certificate here and save that into the same directory as your Cloudflare certificates.

The last thing you'll need is to generate a dhparam.pem and save that into the same directory as well. You can do that via this command:

sudo openssl dhparam -out /etc/cloudflare/dhparam.pem 4096

After you've got all those files, you'll want to edit the top of the server area of your swift-blog config with the following:

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  ssl on;
  
  ssl_certificate               /etc/cloudflare/cloudflare_origin_cert.pem;
  ssl_certificate_key           /etc/cloudflare/cloudflare_origin_privatekey.pem;
  ssl_dhparam                   /etc/cloudflare/dhparam.pem;
  ssl_client_certificate        /etc/cloudflare/origin-pull-ca.pem;
  ssl_verify_client on;

  ...

This change will enable serving content with NGINX using HTTPS / SSL.

After making these changes you'll also want to change your firewall settings and reload NGINX.

sudo ufw allow 'Nginx HTTPS' 
sudo systemctl restart nginx

Keeping Your Server Alive

Sometimes apps crash or servers fail, so you need to ensure that your Swift app can restart itself in case it fails in any way; that way it can still communicate with NGINX and the web. Setting this up with Ubuntu is pretty easy, you just need to create and register a service for it.

Simply create a file at /etc/systemd/system/swift-blog.service with the following contents:


[Unit]
Description=swift-blog
After=network.target
[Service]
Type=simple
Environment="PORT=8888"
WorkingDirectory=/path/to/your/swift-blog-folder
ExecStart=/path/to/your/swift-blog-folder/swift-blog
Restart=always
StandardOutput=file:/var/log/swift-blog.log
[Install]
WantedBy=multi-user.target

You'll then want to activate your server service using the following:

sudo systemctl start swift-blog
sudo systemctl enable swift-blog

After that, you should be able to visit https://blog.mysite.com/ and your Swift server will be running!

Conclusion

Writing my whole blog in Swift and then getting it running as a fully fledged web app was a really neat endeavour. It was fun starting from square one and getting everything running from the ground up; I definitely learned a bunch and I hope in reading all of this you. It's still wild as a concept to me, being able to use all of my Cocoa knowledge I've built up over the years to use for a Linux server box. I'm still learning how to make it better, caching things, etc. so I'm always open to suggestions on how to improve.

Let me know how your own server-side Swift projects go and if you have any questions feel free to ask!