The platform for SUTD's community discussion. Free, open, simple.
This README will document the instructions necessary to setup the SUTD Discourse Forum from a sysadmin perspective or point of view, as well as list off the plugins that we are using in the forum. Meanwhile, the repository will host the files used for the custom SUTD theme style used for the forum.
For now, we will be using Namecheap, DigitalOcean and Mailgun. Once we have gone official, we will be migrating to the Microsoft Outlook MX Mail Server officially utilized by SUTD so as to smoothly support and integrate into the SUTD emailing system and its current Active Directory. Currently, we are using the official subdomain URL of discourse.sutd.edu.sg
. We will also consider utilizing an internal SUTD server to provide hosting services, thereby slowly migrating away from external service providers such as Namecheap and DigitalOcean over time. The usage of Let's Encrypt would be reconsidered in the future, but it is likely to stay (unless we migrate from DV to OV or even EV certificates). The system environment setup (such as using Ubuntu 18.04.3 LTS x64 and Docker) should be similar between our current setup and the eventual official setup. Any instructions for migration and system update/upgrade would be documented here as well.
NOTE: We would need to switch to-and-fro between Namecheap, DigitalOcean and Mailgun as we need relevant information from one another to be inputted to each other's settings panels for a complete setup.
Ensure that you have access to your primary email that you will be using throughout this setup process, your registered mobile phone for any required authentication and verification processes, as well as your respective credentials of your selected payment card option.
The resources that we would need are:
- Domain Name (Namecheap)
- Hosting (DigitalOcean)
- Mail Server (Mailgun)
Firstly, obtain a domain name from Namecheap. Enable WhoisGuard
(it's free) with a 3-days refresh rate and you could optionally enable Namecheap PremiumDNS
protection (for a fee). Verify your email credentials so that the domain is activated. Auto-renewal is optional. Enable DNSSEC
under the Advanced DNS
section (we use Namecheap's nameservers since DigitalOcean does not support DNSSEC
at the moment). For subsequent steps, you could either use the root domain that you have obtained or a discourse
subdomain (for the latter, you would then need to manage it in the Advanced DNS Host Records section).
While DigitalOcean does not provide a robust protection against DDoS attacks at the Droplet level (read more about it here), Cloudflare CDN and Discourse have not historically played well together (read more about it here and here). Currently, there are no Caddy module implementations here for Fastly support. We are also not using Caddy since the default
discourse-setup
now already provides auto-renewed SSL (through a daily cron job) + HTTP2 + QUIC out of the box. If we were to use a CDN in the future for mitigation against DDoS attacks, we would elaborate more here (might need to use the corresponding API keys or scoped tokens for HTTPS support with CDN). For future reference, refer to here or here to setup Caddy for Discourse. The latter requires manual installation of Go (latest version) and Caddy on the Droplet to facilitate the execution of Caddy without a container.
Create a DigitalOcean Droplet
(Cloud Virtual Machine). You could park it under a project named SUTDiscourse
. These are the settings for the Droplet
:
- Image: Ubuntu 18.04.3 (LTS) x64
- Sufficient CPU Plan (see recommended minimum hardware requirements here)
- Datacenter Region: Singapore
- VPC Network:
default-sgp1
- IPv6 Enabled
- Monitoring Enabled
- SSH Key Authentication Method
- 1 Droplet (Hostname:
sutdiscourse
or your respective domain/subdomain name) - OPTIONAL: Enable backups (recommended)
After that, create a DigitalOcean Cloud Firewall (Manage
> Networking
> Firewalls
> Create Firewall
) with these settings:
-
An appropriate name of your choice (
sutdiscourse-rules
,sutdiscourse-firewall
, etc.) -
Inbound Rules:
Type Protocol Port Range Sources SSH TCP 22 All IPv4
All IPv6
HTTP TCP 80 All IPv4
All IPv6
HTTPS TCP 443 All IPv4
All IPv6
Custom (for Git) TCP 9418 All IPv4
All IPv6
-
Outbound Rules:
Type Protocol Port Range Destinations ICMP ICMP All IPv4
All IPv6
All TCP TCP All ports All IPv4
All IPv6
All UDP UDP All ports All IPv4
All IPv6
-
Apply to the
sutdiscourse
Droplet (or your corresponding hostname) ONLY after you are done with Step 4 (after rebuilding the container).
The specified inbound firewall rules would lead to no response for any
ping
commands to the DigitalOcean Droplet (no ICMP traffic allowed). No need to panic if there are no responses! As long as you can SSH into the Droplet, it is working properly.
Register for a free trial account (Flex Tier Plan) and activate the account by verifying through your email and mobile phone. After that, go to Sending
> Domains
and click Add New Domain
. Use a mg
subdomain for the domain name (mg.discourse.sutd.edu.sg
). Either US
or EU
is fine (we chose EU
since Europe is closer to Singapore). Enable the Create DKIM Authority
checkbox and select 2048
. Turn on Click tracking
, Open tracking
and Unsubscribes
under Sending
> Domain settings
> Domain settings
> Tracking
.
Take note that we prefer to have our SSL Certificates from Let's Encrypt instead of from Comodo, GoDaddy or Symantec, in the light of certain security breaches that shall not be named. We also prefer SSL Certificates from Let's Encrypt instead of Cloudflare due to the implementations of DNS hijacking, shared SSL Certificates and no end-to-end encryption by Cloudflare (Free Plan).
We do not need to modify the nameservers, so leave those settings to their default options/values.
In Namecheap > Domain List
, select your domain and click Manage
. Under the Advanced DNS
> Host Records
section, add 2 new records with the following details:
Type | Host | Value | TTL |
---|---|---|---|
A Record | @ |
<DigitalOcean Droplet's IPv4 Address> |
60 min |
A Record | www |
<DigitalOcean Droplet's IPv4 Address> |
60 min |
If you are pointing the
discourse
subdomain to the Droplet instead of the main root domain, usediscourse
andwww.discourse
as the values underHost
respectively.
To improve the security of the issued SSL certificates, we can restrict the certificate issuance. Since we are using Let's Encrypt, add another record with the following details:
Type | Host | Flag | Tag | Value (CAA Identifying Domain) | TTL |
---|---|---|---|---|---|
CAA Record | @ |
0 |
issue |
letsencrypt.org |
60 min |
If you are pointing the
discourse
subdomain to the Droplet instead of the main root domain, usediscourse
as the value underHost
.
If you are pointing the
discourse
subdomain to the Droplet instead of the main root domain, replace anymg
values withmg.discourse
under theHost
column for this sub-section.
Check your specific records in Mailgun under Sending
> Domain settings
.
In Namecheap > Domain List
, select your domain and click Manage
. Under the Advanced DNS
> Host Records
section, add 3 new records with the following details:
Type | Host | Value | TTL |
---|---|---|---|
TXT Record | mg |
v=spf1 include:eu.mailgun.org ~all |
60 min |
TXT Record | <xxx>._domainkey.mg |
k=rsa; p=... |
60 min |
CNAME Record | email.mg |
eu.mailgun.org |
Automatic |
<xxx>
andp=...
can be anything that Mailgun specifies (both of them vary from domain to domain).
The first entry is for SPF, while the second entry is for DKIM. Both are required for our Discourse forum. The second TXT record contains the DKIM public key that Mailgun would automatically generate on their side. The CNAME Record
is optional for tracking purposes.
In Namecheap > Domain List
, select your domain and click Manage
. Under the Advanced DNS
> Mail Settings
section, choose Custom MX
and add 2 new records with the following details:
Type | Host | Mail Server | Priority | TTL |
---|---|---|---|---|
MX Record | mg |
mxa.eu.mailgun.org |
10 |
Automatic |
MX Record | mg |
mxb.eu.mailgun.org |
10 |
Automatic |
If you chose
US
, replace anyeu.mailgun.org
withmailgun.org
.
After entering the records for the first time, you should verify the DNS records in Mailgun to ensure that your entries are correct by going to Sending
> Domain settings
> DNS records
and clicking Verify DNS Settings
(if first-time) or Check DNS Records Now
(subsequent attempts).
Access your DigitalOcean's cloud server via SSH. Follow the instructions here first, without setting up UFW
(since we are already using DigitalOcean Cloud Firewall).
Reasoning behind our choice of DigitalOcean Cloud Firewall instead of
UFW
is explained here. Please only choose one to avoid redundancy or even conflicting firewall rules.
Remember to add a 2 GB swapfile by following the instructions here (in Step 3, change 1G
to 2G
). Do not worry too much about the SSD warning since this swap space will only serve as an "insurance policy" during heavy work load times.
After that, follow the instructions here (only Step 1) to install Docker.
Follow the instructions here to clone the official Discourse Docker image, launch the discourse-setup
tool and initiate bootstrapping to build the Discourse forum. Use your SMTP credentials that you have obtained from Mailgun.
Do follow some of the post-install maintenance instructions such as running these commands:
# Enable automatic updates
$ sudo dpkg-reconfigure -plow unattended-upgrades
# Enforce strong VM user password (modify /etc/pam.d/common-password accordingly)
$ sudo apt install libpam-cracklib
# Install and enable fail2ban (no conflict with iptables)
$ sudo apt install fail2ban
$ sudo service fail2ban start
# Tell Linux to relax and perform the Redis fork in a more optimistic allocation fashion
$ echo vm.overcommit_memory=1 | tee -a /etc/sysctl.conf
If you would like to redirect the www
subdomain to the root domain directory, set up Let's Encrypt for the www
subdomain by following the instructions here and add the redirection rules by following the instructions here.
Reboot the Droplet after running the aforementioned commands and rebuild the Discourse forum to restart the Docker container.
Remember to activate and apply the DigitalOcean Cloud Firewall to the Droplet after rebuilding the forum.
For regular maintenance, whereby Discourse would need to be regularly updated via the /admin/upgrade
subcategory directory on the website, we would also need to routinely run these commands to reclaim enough space so as to ensure that there would be minimal lag and prevent any 502 Bad Gateway
timeout errors:
# Elevate your permissions as super user (root@sutdiscourse)
$ sudo -s
# Navigate to the SUTDiscourse installation folder
$ cd /var/discourse
# Fetch and download the latest version of the Discourse launcher
$ git pull
# Rebuild the app
$ ./launcher rebuild app
# Start the pruning process only AFTER the app is rebuilt and relaunched (with its current status being turned on and running)
$ yes | ./launcher cleanup
This is to remove any stopped Docker containers, delete any build cache, expunge any unused networks (if any) and prune any old dangling Docker images which might slowly pile up in the server for some reason.
A user-level cron(tab) job (on the root
user) could also be set up to routinely conduct and execute this maintenance process at a specific time interval:
$ sudo apt update
$ sudo apt install cron
$ sudo systemctl enable cron
$ sudo systemctl start cron
$ crontab -e
Add this line to the /var/spool/cron/crontabs/root
file using your preferred selected file/text editor (with the syntax @monthly
being a replacement shorthand for 0 0 1 * *
):
@monthly cd /var/discourse && ./launcher rebuild app && (yes | ./launcher cleanup)
Note that not all
cron
daemons can parse this relatively new shortcut syntax of@monthly
(particularly older versions) so double-check that it works and thatcrontab
does not throw any errors before you rely on it and saving the job file. Alternatively, you could also specify a different custom time interval, instead of@monthly
. Also, a reminder that you do not need to specify the user account to be used to execute the commands if we are using thecrontab -e
method (instead of directly editing the system-wide/etc/crontab
file) since all commands will be run as the owner of the file.
If you need to migrate the Discourse forum's domain at any time for any reason, you can follow the guide here.
We will list down the features that we enabled and plugins that we are using here!
Every time before rebuilding the Discourse forum after adding a
git clone
plugin entry in theapp.yml
file, ensure that you temporarily disable the DigitalOcean Cloud Firewall rules. Else, it will fail to properly rebuild and restart the app.
More content coming soon!
- https://github.com/discourse/docker_manager.git (default built-in)
- https://github.com/discourse/discourse-spoiler-alert.git
- https://github.com/discourse/discourse-solved.git
- https://github.com/discourse/discourse-data-explorer.git
- https://github.com/discourse/discourse-calendar.git
- https://github.com/discourse/discourse-cakeday.git
- https://github.com/discourse/discourse-assign.git
- https://github.com/discourse/discourse-math.git
To add a plugin, simply add the plugin's repo URL to your container's app.yml
file (exec
section under the after_code
hook), and then re-build the container.
More instructions coming soon!
Credits to the OpenSUTD team, the DiscoverSUTD 2020 team and the relevant respective SUTD Offices in supporting this special project.