Set permanent public IP for VM instance in Google Cloud Compute:
- VM details page / network interface: default / External IP address menu / + Reserve static address
- (or attach previously reserved static IP to the VM instance)
Allow Firewall rules:
- "default-allow-http" firewall rule is required for Let's encrypt renewal process
- "default-allow-https" is required for secure connection to the API from mobile
sudo apt-get update
sudo apt-get upgrade
We need to point a purchased domain to the VM's static IP address in order to have a secure https connection to this API later... Login to the domain registrar's admin page. In my case it's under Cpanel / DNS zone editor.
Create a new "A" record:
- name: skatebudapest.libertyskate.hu
- IP: 35.185.80.180
sudo add-apt-repository "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -sc)-pgdg main"
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install postgresql-9.6
sudo -s
sudo -u postgres psql postgres
\password postgres
\q
exit
sudo nano /etc/postgresql/9.6/main/pg_hba.conf
Add:
host all all 0.0.0.0/0 md5
Whitelist all IP address, I'll limit the list of incoming IP addresses with GCP Firewall rules in the next step.
sudo nano /etc/postgresql/9.6/main/postgresql.conf
Modify to:
listen_addresses = '*'
sudo service postgresql restart
Google Cloud Console / VPC network / Firewall rules / + Create Firewall rule
Name: default-allow-postgres
Type: Ingress
Targets: Apply to all
IP ranges: my-ip-here/32
Ports: tcp:5432
Action: Allow
Install TablePlus / pgAdmin database manager on dev Macintosh:
brew install --cask tableplus
brew install --cask pgadmin4
- Add new server connection: connect with default user/pass/database: postgres/postgres/postgres
- Create a new user and database for production. Use the config from the
.env.production
dotenv file. - Execute seed data/migration sql scripts into the production database from the
/Resources
folder.
sudo apt-get install nginx
systemctl status nginx
sudo ufw allow 'Nginx HTTP'
sudo ufw allow 'Nginx HTTPS'
sudo nano /etc/nginx/snippets/ssl-params.conf
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
sudo rm /etc/nginx/sites-available/default
sudo rm /etc/nginx/sites-enabled/default
sudo touch /etc/nginx/sites-enabled/skate-budapest-vapor
sudo nano /etc/nginx/sites-enabled/skate-budapest-vapor
server {
server_name skatebudapest.libertyskate.hu;
listen 80 default_server;
listen [::]:80 default_server;listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include snippets/ssl-params.conf;try_files $uri @proxy;
location @proxy {
proxy_pass http://localhost:8080;
proxy_pass_header Server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Server;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
}
Raise max body size in the "http" section - tipically for multipart requests (default is 1MB)
sudo nano /etc/nginx/nginx.conf
client_max_body_size 20M;
Verify:
sudo nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo reboot
Test connection (nginx 502 should appear): https://skatebudapest.libertyskate.hu
Self-signed certificates are not publicly trusted, it has problems with iOS App Transport Security (ATS). So we need a Certificate Authority (CA) for this: Let's encrypt. Previously we pointed the domain to the VM's static public IP.
sudo add-apt-repository ppa:certbot/certbot
sudo apt install python-certbot-nginx
sudo certbot --nginx -d skatebudapest.libertyskate.hu
Enter an email address and agree to the terms of service. Select 1: No redirect - Make no further changes to the webserver configuration.
Verify that the "ssl_certificate" and the "ssl_certificate_key" is added to the configuration:
sudo nano /etc/nginx/sites-enabled/skate-budapest-vapor
List certificates, expiry dates, verify no errors:
sudo certbot certificates
The certificate auto-renewal script is saved here as a cron job automatically:
sudo nano /etc/cron.d/certbot
Check if the auto-renewal works:
sudo certbot renew --dry-run
Verify browser's security indicator. (nginx 502 should appear with a lock sign): https://skatebudapest.libertyskate.hu
Install Swift Dependencies:
sudo apt-get update
sudo apt-get install clang libicu-dev libatomic1 build-essential pkg-config
Install Vapor's system dependencies:
sudo apt-get install openssl libssl-dev zlib1g-dev libsqlite3-dev
Take a look at Linux install notes on Swift's Downloads page
Download and decompress the Swift toolchain:
wget https://swift.org/builds/swift-5.2.4-release/ubuntu1804/swift-5.2.4-RELEASE/swift-5.2.4-RELEASE-ubuntu18.04.tar.gz
tar xzf swift-5.2.4-RELEASE-ubuntu18.04.tar.gz
Move Swift somewhere easy to acess:
sudo mkdir /swift
sudo mv swift-5.2.4-RELEASE-ubuntu18.04 /swift/5.2.4
Add Swift to /usr/bin so it can be executed by vapor and root:
sudo ln -s /swift/5.2.4/usr/bin/swift /usr/bin/swift
Verify:
swift --version
git config --global user.email "balazs630@icloud.com"
git config --global user.name "Horváth Balázs"
git clone https://github.com/balazs630/Skate-Budapest-Vapor.git
Copy .env.production
to the projects root folder, into: ~/development/Skate-Budapest-Vapor/
This file contains API keys, Postgres & Mailgun secrets and therefore it's not checked into source control.
cd ~/development/Skate-Budapest-Vapor/ ; git pull
sudo rm -R ~/development/Skate-Budapest-Vapor/.build
In case of any GCP service outage or maintanance, we can fire up our API automatically after a reboot.
Create a startup script for the Vapor API:
sudo touch ~/development/startup.sh
sudo nano ~/development/startup.sh
Add:
#! /bin/bash
cd ~/development/Skate-Budapest-Vapor/
swift build --enable-test-discovery --configuration releasecp -R .env.production .build/release/
cd .build/release/
./Run serve --env production
sudo chmod +x ~/development/startup.sh
Configure startup script for the Compute Engine instance with GCP Console.
Select the instance > Edit
, Custom metadada + Add item
:
Key:
startup-script
Value:
#! /bin/bash
echo "User: $(whoami)"
echo "Working directory: $(pwd)"
sudo -H -u balazs630uk tmux new-session -d -s skate-budapest
sudo -H -u balazs630uk tmux send -t skate-budapest /home/balazs630uk/development/startup.sh ENTER
We need to switch to our user account balazs630uk
because startup scripts are executed as root
by defualt - which is managed by GCP.
Inspect startup-script logs:
sudo journalctl -u google-startup-scripts.service
Try out startup script:
sudo reboot
Attach:
tmux a
or
tmux a -t skate-budapest
Close tmux:
Ctrl+B, followed by D