Discourse setup with multisite and SSO

subtopic

#1

This topic is a linked part of a larger work: “Edgeryders Communities Platform Manual

Content

1. Making a Discourse multisite installation

2. Adding a Discourse site to the multisite setup

3. Configuring a Discourse site to be the default site

4. Configuring a Discourse site to be the SSO provider

5. Configuring a Discourse site to be a federated forum

6. Discourse multisite administration


1. Making a Discourse multisite installation

Discourse supports multisite installations out of the box. However, we made some changes and fixes and provide our code in the multisite branch of edgeryders/discourse.

By installing from that codebase, you will get the same multisite setup that we use. We will regularly update this branch to the most recent state (HEAD) from discourse/discourse, but only do this about every 3-12 months.

We did not follow the default Discourse installation instructions (which are about a Docker image and Nginx) but rather use a Discourse + Apache 2 + SSL setup without Docker. Good hints to achieve that are:

[TODO: Detailed installation instructions.]

2. Adding a Discourse site to the multisite setup

These are generic instructions for adding one Discourse platform to the multisite setup – it does not matter whether you later configure it to be your SSO provider or to be a federated forum.

The following instructions assume you use Apache 2 as your webserver.

  1. Point the domain to the server with your Discourse installation.

  2. Create a new website record in your Apache 2 configuration, and include the following into the VirtualHost config. It provides the proxy server configuration for Apache 2 to forward to Puma (the Rails server providing Discourse on an internal port), with exceptions for static files etc. that should be served by Apache directly.

    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [NE]
    
    DocumentRoot /home/discourse/production_multisite/current/public
    
    <Directory /home/discourse/production_multisite/current/public>
      Options +FollowSymLinks
      AllowOverride All
      Require all granted
    </Directory>
    
    <location /assets>
      ProxyPass !
    </location>
    
    <location /system>
     ProxyPass !
    </location>
    
    <location /images>
      ProxyPass !
    </location>
    
    <location /javascripts>
      ProxyPass !
    </location>
    
    # IMPORTANT IMPORTANT IMPORTANT
    # Replace "sitename" with the folder containing this 
    # multisite's uploads. Excepting all of /uploads is impossible 
    # as some URLs inside must be served by Discourse.
    <location /uploads/sitename>
      ProxyPass !
    </location>
    
    # Do not proxy the Alias URL needed for LetsEncrypt 
    # certificate generation.
    ProxyPass /.well-known/acme-challenge !
    
    # Proxying Discourse requests to Puma.
    ProxyPass / http://127.0.0.1:9294/
    ProxyPassReverse / http://127.0.0.1:9294/
    
    # Assure Discourse it is on HTTPS.
    # Else the Discourse setting "force https" fails.
    RequestHeader set X-Forwarded-Proto "https"
    
    
  3. Enable a redirection from the www subdomain to the naked domain.

  4. Add the multisite to /home/discourse/production_multisite/shared/config/multisite.yml

  5. Create the database (supply your database name suffix for sitename):
    createdb discourse_production_sitename -U discourse

  6. Migrate the database (supply the correct database name for discourse_production_sitename):
    cd /home/discourse/production_multisite/current; RAILS_DB=*** bundle exec rails db:migrate['multisite']

  7. Restart puma:
    sudo monit restart puma_discourse_multisite_production

3. Configuring a Discourse site to be the default site

Each Discourse multisite installation has one “default” site. This is only relevant for Sidekiq: unlike in “normal” Discourse installations, the Sidekiq queue in a multisite installation processes the tasks of all sites in the multisite group.

Due to this, it should only be accessible to the server administrator and not to any Discourse administrator of any one Discourse site of the multisite group. To achieve this, Sidekiq looks at the servername of the Discourse server process and only grants a Discourse administator access to /sidekiq if that is the same as the servername used to access Discourse. This servername is that of the “default” site of a Discourse multisite set.

We propose to configure one Discourse multisite site with the sole purpose of providing access to this /sidekiq URL, as follows:

  1. Mark this site of the multisite set as the default one in the Discourse configuration. [TODO: How to do that.]

  2. In the site’s Apache Virtualhost configuration file provided above, change the proxy exception for uploaded images to be as follows:

    <location /uploads/default>
      ProxyPass !
    </location>
    
  3. In the site’s Apache Virtualhost configuration file provided above, change the proxy server forwarding to use the domain name (example.com) of the default site instead of 127.0.0.1, as follows:

    ProxyPass / http://example.com:9294/
    ProxyPassReverse / http://example.com:9294/
    

    This type of forwarding is obviously slower as it goes via the Internet rather than the local loop interface for 127.0.0.1, but as the default site is only used to access the Sidelkiq interface it does not matter here.

4. Configuring a Discourse site to be the SSO provider

The Discourse multisite system just means “one codebase, one process set, multiple databases”. All Discourse platforms in a multisite set are completely independent and can use their own login and signup mechanism. Using SSO (single-sign-on) is completely optional when using a Discourse multisite setup – we chose to do so as part of our federated Discourse hosting.

Out of the box, Discourse can be configured to be either the SSO provider or an SSO client for the single-sign-on mechanism. In our setup, one of the Discourse multisite platforms is the SSO provider and all others are SSO clients.

We configured communities.edgeryders.eu to be the SSO provider and called it the “Edgeryders Communities Login Site”. Putting it on a subdomain of our main platform edgeryders.eu is meant to create the least amount of confusion for the bulk of users compared to a process where they would all be forwarded to another domain for login.

We made the following adaptations to let Discourse work as a SSO provider without much friction:

  1. Configure Discourse as SSO provider in the Discourse admin interface under “☰ → Admin → Settings”:

    • Login → enable sso: false
      (:warning: This setting must only be enabled if you want Discourse to act as the SSO client.)
    • Login → enable sso provider: true
    • Login → sso secret: (put in a random passphrase string)
  2. Hide everything of the Discourse user interface for non-staff users (using theme CSS overwrites) except the following:

    • user menu → Edit Profile
    • user menu → Logout
  3. Put in prominent links to the manual for help with signup, login and site usage. In our case we have a top navbar link (with yellow background) to signup help and a link in the hamburger menu to the whole manual.

  4. Create a topic that users will see at the end of the signup process, with the following information:

    • A message “You have successfully activated your account and logged in.”
    • A call to action: “Return to the community you want to join”
    • A list of the federated Discourse websites where users can go back to, with icons and links leading to https://example.com/login . Using that link target instead of https://example.com/ will automatically log in users when they return to the site where they started the signup process.
    • An explanation of what the login site is for.
  5. Set the topic that should be visible at the end of the signup process as a “global banner topic”. This will make it visible, given that you have hidden all Discourse content by CSS.

  6. Hide the “close” button of the banner topic by CSS, to make sure it will always be visible to all logged-in users.

5. Configuring a Discourse site to be a federated forum

These tasks are mostly specific to the Edgeryders Communities platforms, a hosting offer of federated Discourse platforms by Edgeryders OÜ.

5.1. Set the SSO secret

  1. Get the current value of the SSO secret from an existing Discourse website that you configured as a federated Discourse platform (so not from your SSO provider), by looking at: https://example.com/admin/site_settings/category/all_results?filter=sso

  2. Set the SSO secret with the following commands (supply your database name for discourse_production_sitename and the SSO secret for sso_secret)

    cd /home/discourse/production_multisite/current; 
    RAILS_DB=discourse_production_sitename RAILS_ENV=production bundle exec rails c
    SiteSetting.create!(name: 'sso_secret', data_type: 1, value: "sso_secret")
    

5.2. Make the basic configuration settings

If you use our codebase, most of these settings will already be done (and in addition, useful defaults will be provided for other Discourse settings) via our plugin EdgerydersMultisite.

Otherwise, you can make the settings manually:

  1. Run the setup wizard by visiting https://example.com/wizard

  2. Upload logos and favicons, either as part of the setup wizard or by visiting at any later time https://example.com/wizard/steps/logos

  3. Make the following settings in the Discourse admin interface under “☰ → Admin → Settings”. (These are essential for the login mechanism and external user profile mechanism to work properly.)

    • Users → Email editable: false
    • Security → Force Https: true
    • Login → enable_sso: true
    • Login → sso overrides email: true
    • Login → sso overrides username: true
    • Login → sso overrides avatar: true
    • Login → sso overrides profile background: true
    • Login → sso overrides card background: true

5.3. Add the site to the federation menu

We created a custom plugin EdgerydersCommunitiesNavigation that provides the top-right “Communities” menu, linking all our federated Discourse platforms together. To add the new site:

  1. Add a new menu entry in file edgeryders_communities_navigation.hbs.

  2. Deploy the code. This will change the top-right federation menu on all sites of the Discourse multisite group at once.

5.4. Creating a site contact user

In the recent versions of Discourse, the system user can no longer be renamed, but you can (and should) configure in setting “site contact username” that a different user account is used by the software to send messages etc…

We have a convention that this new user should be named after the platform – for example, user @edgeryders in the case of this platform. That makes it a natural choice for the author of the Terms of Service, Privacy Policy, various menu pages and other official content of a Discourse platform. Even though this user is not meant to be used for login, content can be transferred to it using the admin feature “Change Ownership …”.

Here is how to create and maintain such a user account for one specific Edgeryders Communities platform (the “target platform”):

  1. Create a new user account on communities.edgeryders.eu using:

    • the target platform’s name as the username
    • a fictive e-mail address that belongs to the target platform’s domain – for example, noreply@edgeryders.eu in the case of this platform
    • a password that you won’t remember. as it will never be needed
  2. Log in as an admin user on communities.edgeryders.eu.

  3. Go to “Admin → Users → New”, select the user account you just created, and click Activate Account. This verifies the non-existing e-mail address.

  4. Click Impersonate.

  5. Upload the platform logo as the user’s profile image.

  6. Go to the target platform and click “Log in”, to create an account with the same username there and sync the profile image.

  7. Log in with an admin account on the target platform and make the new user a moderator. This will indicate it is an “official” account. (Do not make it admin, as that is not shown in the frontend anyway but would be more dangerous in case the account is ever compromised or somebody gets access to the “non-existing e-mail address”.)

  8. Set the Discourse setting “site contact username” to that user, which makes it take over most roles of the system user.

If you ever have to adapt the username or profile picture of this special user, do so by logging in as admin on communities.edgeryders.eu and impersonating that user. (Re-)log in to the target platform as that user afterwards to sync the changes to that platform. Since we use SSO, username, e-mail address and profile picture cannot be changed at all on Edgeryders Communities platforms directly, not even by admins.

5.5. Provide Terms of Service, Privacy Policy, FAQ

The FAQ is a stub document that will grow to be different for each Edgeryders Communities platform. The others are legal agreements that are identical for all sites. Until we have an automated process, use the versions of the TeaM2021 website as templates: Terms of Service, Privacy Policy, FAQ. You have to adapt the organization name, other organization information, and the website URL. Provide them under the same (Discourse) default URLs on the client’s platform, which means /tos, /privacy and /faq.

5.6. Disable the Discourse Dark theme

Because it’s not yet ready to work with our custom modifications in Discourse.

  1. Go to /admin/customize/themes/1.
  2. Disable “Theme can be selected by users”.

6. Discourse multisite administration

[TODO: Content of this section.]

6.1. Rake tasks

Some multisite specific Rake tasks for Discourse are available.