Easily Secure your Perfect Server Side Swift code with HTTPS

Jonathan Guthrie
Server Side Swift and More
4 min readApr 3, 2017

One of the most critical things we should be doing as software developers is to make sure the data we’re managing is safe and secure, and the first step should always be to enforce SSL encryption.

This means that anyone accessing your API, or, to be honest, your website, should be accessing it over port 443 using HTTPS, and getting a nice closed lock symbol in their browser.

So in this article I’m going to show you how simple it is to fire up a server that works for both unencrypted traffic on port 80, and encrypted traffic on port 443, with a LetsEncrypt certificate.

First, I’m starting with a brand new AWS instance created by the Perfect Assistant, so I know it’s got all the right things on there.

Next, I’m going to hit up https://certbot.eff.org/#ubuntuxenial-other to get the LetsEncrypt Certbot installed. This will make it really easy for us to create and validate a certificate.

Following the instructions, I do the following:

$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install certbot

While that’s installing, I’m heading back to the Perfect Assistant, and making a new clone of the Perfect Template. This will give me a one-file starting point with all the things I need.

Once that’s cloned & an Xcode project built, we’re going to do a bit of surgery to the template’s main.swift file. The template as it stands has 1 HTML route, and 2 “servers” but we want to replace the “handler” function with 2 slightly different ones.

// This ‘handler’ function serves port 80.
func handler80(data: [String:Any]) throws -> RequestHandler {
return {
request, response in
response.appendBody(string: "<html><title>Hello, world!</title><body>Hello, world! Port 80</body></html>")
response.completed()
}
}
// This ‘handler’ function serves port 443.
func handler443(data: [String:Any]) throws -> RequestHandler {
return {
request, response in
response.appendBody(string: "<html><title>Hello, world!</title><body>Hello, world! Port 443</body></html>")
response.completed()
}
}

Next, lets replace the “confData” which is the configuration for the HTTP Servers:

let confData = [
"servers": [
// Serves Port 80
[
"name":"localhost",
"port":80,
"routes":[
["method":"get", "uri":"/", "handler":handler80],
["method":"get", "uri":"/**",
"handler":PerfectHTTPServer.HTTPHandler.staticFiles,
"documentRoot":"./webroot",
"allowResponseFilters":true]
],
"filters":[
[
"type":"response",
"priority":"high",
"name":PerfectHTTPServer.HTTPFilter.contentCompression,
]
]
]
]

Now, all this is doing is creating a server on port 80 — we need this so that the Certbot can get the SSL cert for us. Certbot also needs us to create a “webroot” directory at the surface of the project.

Now, use Perfect Assistant to “Deploy” the project to the AWS instance. Once it’s successful, note the IP address. We need to create a DNS A-record entry for this. It should look something like this “test.mydomain.com A 123.123.123.123”, where the test.mydomain.com is the domain/subdomain, and the IP address is the IP address of the server.

Once the DNS is done (and available to the world), http://test.mydomain.com should yield “Hello, world! Port 80”… We’re ready to go to the next stage and create the certificate.

Back in the terminal session on the server, enter:

sudo certbot certonly --webroot -w /perfect-deployed/httpstest/webroot/ -d test.mydomain.com

Where test.mydomain.com is the domain name and “/perfect-deployed/httpstest/webroot/” is the all path to the deployed application using Perfect Assistant.

This gives the following:

Starting new HTTPS connection (1): supporters.eff.org
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for test.mydomain.com
Using the webroot path /perfect-deployed/httpstest/webroot for all unmatched domains.
Waiting for verification…
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/test.mydomain.com/fullchain.pem. Your cert
will expire on 2017–07–02. To obtain a new or tweaked version of
this certificate in the future, simply run certbot again. To
non-interactively renew *all* of your certificates, run “certbot
renew”

This gives us 2 pieces of vital information:

  • the certificate file is at “/etc/letsencrypt/live/test.mydomain.com/fullchain.pem”
  • the key file is at “/etc/letsencrypt/keys/0000_key-certbot.pem”

Back in Xcode, I’m going to change the port, the handler, and add a “tlsConfig” dictionary for the configuration. The server section now looks like this:

// Serves Port 443.
[
"name":"localhost",
"port":443,
"routes":[
["method":"get", "uri":"/", "handler":handler443],
["method":"get", "uri":"/**",
"handler":PerfectHTTPServer.HTTPHandler.staticFiles,
"documentRoot":"./webroot",
"allowResponseFilters":true]
],
"filters":[
[
"type":"response",
"priority":"high",
"name":PerfectHTTPServer.HTTPFilter.contentCompression,
]
],
"tlsConfig":[
"certPath": "/etc/letsencrypt/live/test1.iamjono.io/fullchain.pem",
"verifyMode": "peer",
"keyPath": "/etc/letsencrypt/keys/0000_key-certbot.pem"
]
]

Now, deploying this should give me a server only on 443. So, I deploy, and once done, hit https://testmydomain.com and I see what I expect!

“Hello, world! Port 443”, and the browser has a nice closed lock!

The “finished” example is available at https://github.com/PerfectExamples/HTTPS-Configuration. Note that you’ll need to make sure the right paths are there for your use case.

For examples visit GitHub.com/perfectexamples — there is an ever growing library of examples and demos there.

And of course if you’re looking for live help from our awesome community, join our Slack channel via www.perfect.ly

Thanks for being with us today, don’t forget to say Hi on Slack!

--

--

Jonathan Guthrie
Server Side Swift and More

Developer Evangelist, Musician, and Active Activist... Newmarket ON, Canada