Setting up a game server using Nakama on Google Compute Engine, part 4

· by Steve · Read in about 6 min · (1197 Words)

This is the fourth instalment of a blog series I’m writing about Nakama, which I’ve used for leaderboards in our first game Washed Up!.

  • Part 1 covers what Nakama is, and why I chose it over other options
  • Part 2 ran you through setting up a basic service you can use for development & testing
  • Part 3 showed you how to run Cockroach in secure mode

This part deals with how to set up SSL on the Nakama server.

A note on Nakama versions

You’ll need to be running Nakama version 1.4.1 or later for these steps. Details of how to upgrade are in Part 2.

Self-signed SSL certificates

To cut down costs, I used a self-signed certificate. The only thing connecting to my server is my own game code, I know I can trust my own certificate (so long as I check the fingerprint) and don’t need to convince anyone else that it’s valid, so really it doesn’t need to be signed by a CA. I could have used Lets Encrypt, but why make things more complex than they need to be?

Here’s how to generate a self-signed cert:

openssl req -x509 -newkey rsa:2048 -keyout nakamassl_key.pem -out nakamassl_cert.pem -days 3650 -nodes

This gives you a 10-year self-signed cert. You want to extract the fingerprint, so that you can use this to manually validate the certificate on the client end:

openssl x509 -fingerprint -noout -inform pem -in nakamassl_cert.pem

This will give you output something like:

SHA1 Fingerprint=9E:10:AC:EB:4D:D1:3F:0F:D3:E1:87:C3:CB:C2:70:60:5E:A6:50:31

Keep that fingerprint string, you’ll need it later.

Choose your SSL route

There are actually 2 ways to set up SSL for your Nakama server:

  1. Set up a Load Balancer to be the front-end to your service, or
  2. Serve SSL from your single server directly

The first option is the one that’s recommended by the Nakama authors, and is the most resilient. A load balancer can not only front multiple Nakama servers, improving your availability (although note that unless you run the Enterprise edition, certain functions like Chat are limited to players on a single server), it’s also where your response to traffic spikes and DDoS attacks can be managed more efficiently.

However, running a load balancer means a certain minimum running cost. Load balancers on all hosting platforms tend to be more expensive than the smaller hosts (ballpark you can expect is about $20pm), and obviously unless you’re running 2 actual hosts you’re wasting the load balancer’s potential for availability.

If you have modest traffic and can live with the potential downtime of a single point of failure, but you still want to encrypt your traffic (you really do), then you can host SSL directly on the Nakama server. The authors want me to make it clear that they do not recommend this, but it’s what I’ve been doing.

The Load Balancer Option

If you want to spend the money to set up the recommended load-balancing version, here’s how you do it. If you don’t (like me), skip to the Direct SSL Option below.

  1. First create an instance group at Menu > Compute Engine > Instance groups
    • Create a new Unmanaged Instance group
    • Put it in the same zone as your server(s)
    • Add your server(s) to the group
  2. Create a health check at Menu > Compute Engine > Health checks
    • Create a new healthcheck
    • Protocol HTTP, Port 7350, Request path “/”
    • This is testing that the underlying Nakama server is responding
  3. Open the Load Balancing section in GCE (Menu > Network Services > Load Balancing)
    • Click “Create load balancer”
    • Select HTTP(S) Load Balancing (WebSockets are fully supported by this)
    • Call it whatever you like, say ‘nakama-lb’
    • Click Backend configuration
    • Create a new Backend service
    • Target the Instance Group you created earlier
    • Port: 7350
    • Select the Health Check you created earlier
    • Click the “Edit” (Pencil icon) button near the top of the page, next to the Timeout
      • Change the Timeout from 30 to 86400, this is to prevent Websockets getting terminated
    • Add a new front-end service on port 443 on a static IP
    • Add the certificate and key you created earlier (chain is not needed)
    • Accept the warning about a self-signed certificate
    • Disable the HTTP front end, leaving just the HTTPS
  4. Change firewall rules
    • Instead of allowing connections from all IPs to the Nakama server, we now want to only allow the load balancer
    • Go to Menu > VPC Network > Firewall rules
    • Edit the “allow-nakama” firewall rule you created in Part 2
    • Change the source IP range from 0.0.0.0/0 to 130.211.0.0/22 and 35.191.0.0/16 (these are the GCE load balancer IP ranges)

The Direct SSL Option

If you’re cheap like me, and accept the reduced resilience, you can go the direct SSL route. Of course this means a single point of failure, and less DDoS protection (although since Go 1.8 it’s not bad), but we’re adults and we can make those trade-offs if we want. 😉

In the Nakama config file, you want to add 2 extra lines:

socket:
  ...
  ssl_certificate: /etc/nakama/nakamassl_cert.pem
  ssl_private_key: /etc/nakama/nakamassl_key.pem

Obviously transfer those 2 files into /etc/nakama if you haven’t already. If your firewall is still set up like Part 2 you’re all set, since we’re still using port 7350 but adding SSL to it.

Again, make sure you’re using Nakama 1.4.1 or later, this configuration wasn’t supported before then - I know, my PR added it 😎.

Client changes

The most obvious change is to change the port to 443 and enable SSL in your Nakama client, and change the IP address if you need to (if you changed to the load balancer and aren’t abstracting it via DNS).

Assuming you used a self-signed certificate, you also need to validate the certificate in your client by checking the fingerprint.

Now, I don’t know the status of other Nakama clients, but the Nakama Unity client has a flaw - it accepts all SSL certificates. Yep, literally all of them. Now, obviously this will work immediately with your self-signed cert, but wow, this is not secure.

I’ve addressed this in a fork, and have submitted a pull request which allows you to disable this laissez-faire approach and pass in a list of fingerprints that you consider to be acceptable, above and beyond CA-approved certs. Unfortunately this PR hasn’t been merged yet, but I highly recommend that you use it in order to avoid man-in-the-middle attacks.

The easiest way to do this is to use my fork instead of the main version, which has the benefit of both this SSL validation and some improved HTTP error detection as well (another PR waiting to be merged).

If you do this, you can use code like this when initialising the Nakama client to validate your self-signed certificate:

var client = new NClient.Builder(sb.ToString())
    .Host("your.host.com")
    .Port(443)
    .SSL(true)
    .SSLAcceptAllCertificates(false)
    .SSLValidKeyFingerprints(new string[] { "9E10ACEB4DD13F0FD3E187C3CBC270605EA65031" })
    .Build();

The argument to SSLValidKeyFingerprints is the fingerprint you retrieved after creating the certificate, but with the : characters removed.

Fin

That’s it for now! I had planned to cover backups and accessing the Nakama and Cockroach dashboards via SSH tunnelling, but I’m lacking time a bit these days. If you really want to hear about these, let me know via Twitter.

I hope this series has been useful!