I have clients connecting to my WebSockets game server for an online browser game. Game servers are created and destroyed when players start games, and so each server can have a different IP address that I can’t control.
I need the WebSockets to be secure (WSS) so I have an nginx proxy with an SSL certificate. The client receives the IP of the game server but instead of connecting directly (insecure) it goes via my nginx server with the game server IP as a query param.
Here’s the problem: anyone can now use my nginx server to proxy to any IP they choose. What I need is a way to ensure nginx is only proxying to my game servers.
I don’t have control of the game server IPs since it’s an external host, but I own the game server code. My nginx proxy is hosted by me but the game servers are hosted by a provider.
My plan was to have a shared secret key on the game server and nginx and encrypt all traffic with that, but I’m struggling to find out how this could be done.
Here's what I've got done so far (based on this gist):
I created my own self-signed CA certificate:
openssl genrsa -des3 -out rootCA.key 4096
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt
I created a certificate for the game server:
openssl genrsa -out gameserver.key 2048
openssl req -new -key gameserver.key -out gameserver.csr
openssl x509 -req -in gameserver.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out gameserver.crt -days 500 -sha256
I did the same for the nginx server (is this needed? note: not needed):
openssl genrsa -out nginx.key 2048
openssl req -new -key nginx.key -out nginx.csr
openssl x509 -req -in nginx.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out nginx.crt -days 500 -sha256
My nginx config is then something like this:
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
# these are for game client
ssl_certificate /etc/letsencrypt/live/mywebsite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mywebsite.com/privkey.pem;
location / {
if ( $arg_host != "" ) {
proxy_pass https://$arg_host:$arg_port;
}
proxy_ssl_certificate nginx.crt;
proxy_ssl_certificate_key nginx.key;
proxy_ssl_trusted_certificate rootCA.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}
}
My game server is a WebSockets server using gameserver.key
and gameserver.csr
.
However when I try it the nginx error logs show:
upstream SSL certificate verify error: (18:self signed certificate) while SSL handshaking to upstream
I'm not sure if this can work, and where I went wrong? The only article I found mentioning this error suggests the game server certificate isn't trusted but I can't figure out why.
I'm also not sure what value I should be putting for the Common Name
when creating certificates (since each game server it on its own IP) and if this is a problem or not.