postfix http://tech.oeru.org/ en Creating your own OER Foundation-style Libre Self-hosting Infrastructure with Docker Compose and Ubuntu LTS http://tech.oeru.org/creating-your-own-oer-foundation-style-libre-self-hosting-infrastructure-docker-compose-and-ubuntu <span class="field field--name-title field--type-string field--label-hidden">Creating your own OER Foundation-style Libre Self-hosting Infrastructure with Docker Compose and Ubuntu LTS</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> <div class="field__item field__item--nginx"> <span class="field__item-wrapper"><a href="/taxonomy/term/30" hreflang="en">nginx</a></span> </div> <div class="field__item field__item--lets-encrypt"> <span class="field__item-wrapper"><a href="/taxonomy/term/17" hreflang="en">let&#039;s encrypt</a></span> </div> <div class="field__item field__item--postfix"> <span class="field__item-wrapper"><a href="/taxonomy/term/66" hreflang="en">postfix</a></span> </div> <div class="field__item field__item--docker-compose"> <span class="field__item-wrapper"><a href="/taxonomy/term/25" hreflang="en">docker compose</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/1" class="username">dave</a></span> <span class="field field--name-created field--type-created field--label-hidden">Thu 12/10/2023 - 13:12</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><ul class="table-of-contents"><li> <p><a href="#tips-for-this-tutorial">Tips for this tutorial</a></p> </li> <li> <p><a href="#create-a-virtual-private-server">Create a Virtual Private Server</a></p> <ul><li> <p><a href="#vps-properties">VPS Properties:</a></p> </li> </ul></li> <li> <p><a href="#key-variables-for-you-vps">Key variables for you VPS</a></p> <ul><li> <p><a href="#get-your-domain-lined-up">Get your Domain lined up</a></p> </li> </ul></li> <li> <p><a href="#editing-files">Editing files</a></p> <ul><li> <p><a href="#set-up-an-unprivileged-user-for-yourself">Set up an unprivileged user for yourself</a></p> </li> </ul></li> <li> <p><a href="#configure-the-vps">Configure the VPS</a></p> <ul><li> <p><a href="#configuring-your-firewall">Configuring your firewall</a></p> </li> <li> <p><a href="#install-the-nginx">Install the Nginx</a></p> </li> </ul></li> <li> <p><a href="#outgoing-vps-email-optional">Outgoing VPS Email (optional)</a></p> </li> <li> <p><a href="#installing-the-docker-engine-docker-compose-and-lets-encrypt">Installing the Docker Engine, Docker Compose, and Let's Encrypt</a></p> <ul><li> <p><a href="#backwards-compatibility-for-docker-compose">Backwards compatibility for Docker Compose</a></p> </li> <li> <p><a href="#docker-use-by-non-root-user">Docker use by non-root user</a></p> </li> </ul></li> <li> <p><a href="#setting-up-places-for-your-docker-configurations-and-persistent-data">Setting up places for your Docker configurations and persistent Data</a></p> </li> <li> <p><a href="#configuring-nginx-reverse-proxy-for-your-service">Configuring Nginx reverse proxy for your service</a></p> </li> <li> <p><a href="#lets-encrypt-setup">Let's Encrypt setup</a></p> <ul><li> <p><a href="#requesting-lets-encrypt-certificates">Requesting Let's Encrypt certificates</a></p> </li> </ul></li> </ul><h2><a id="user-content-tips-for-this-tutorial" href="#tips-for-this-tutorial" name="tips-for-this-tutorial" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Tips for this tutorial</h2> <p>This tutorial is aimed at adventuresome would-be system administrators. I try not to assume any specialised knowledge on your part, and try to provide useful tips and explanation along the way to help you build a valid mental model of what you're doing. At the same, this is not a trivial process. Luckily, if you try it out, and decide not to follow through, so long as you <em>delete your VPS</em>, you should not be out-of-pocket by more than a few cents.</p> <p>With this tutorial, I'm assuming you've got a computer with an Internet connection, that can run SSH (all modern systems should do that) and you can copy-and-paste stuff from this tutorial (in your browser) into either a terminal window (in which you're SSH'd into your VPS) or into a text editor. Note, if you find it difficult to paste into a terminal window, try using CTRL+SHIFT+V (CTRL+V is already used as a short-cut for something else in UNIX terminals since long before the Windows world started using CTRL+C and CTRL+V).</p> <p>When I provide files you need to copy, look for the 'tokens' or placeholders with values you need to substitute (search-and-replace) in square brackets like this, [token name], with your own values. Any text editor worth using should let you do that!</p> <h2><a id="user-content-create-a-virtual-private-server" href="#create-a-virtual-private-server" name="create-a-virtual-private-server" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Create a Virtual Private Server</h2> <p>The first step is to create a place to host instances of your own <a href="/node/58">LibreSoftware</a> services.</p> <p>You can use a local piece of computing hardware of sufficient capacity in your own home or organisation (e.g. a redundant depreciated desktop should be sufficiently powerful), recognising that it needs to be reliable because anyone using your solution will not be able to access it if it's <em>not running</em>. But, doing that is no joke - making reliable systems is <em>hard</em>.</p> <p>The more cost-effective self-hosting approach in our experience is to lease a low cost commodity Linux Virtual Private Server (aka a VPS) running Ubuntu Linux 22.04 (or the latest "Long Term Support" version - the next one, 24.04, will be released in April 2024). That's what we assume you're running for all our recent tutorials.</p> <p>We have used quite a few Linux VPSs commodity providers. Known good options include Digital Ocean, Linode, Vultr, Hetzner, and TurnkeyLinux. There are many (hundreds of) other credible options. We recommend you find one hosted in the network epicentre (which isn't necessarily the same as the 'geographic' epicentre) of your audience. In the past year, we shifted almost all of our hosting to Hetzner as they've got the benefit of not being US-owned (They're German, and are therefore less likely to expose us to the egregiously over-reaching US <a href="https://en.wikipedia.org/wiki/CLOUD_Act">Cloud</a> and <a href="https://proprivacy.com/guides/what-is-the-partiot-act">Patriot</a> Acts). Shifting to Hetzner also reduced our infrastructure costs by about 50% compared to Digital Ocean. It also represents a <strong>95% savings</strong> over Amazon AWS or Microsoft Azure hosting (can't imagine how anyone could justify hosting with either of them).</p> <p>If you have trouble getting a VPS, you might find <a href="https://vimeo.com/684028258">this video</a> I created for provisioning a VPS, using Digital Ocean as an example, useful. In my experience, the process for provisioning VPSs on other platforms is very similar. You'll find this process <em>much</em> easier than using either Microsoft Azure or Amazon AWS, which we strongly recommend against using.</p> <h3><a id="user-content-vps-properties" href="#vps-properties" name="vps-properties" class="heading-permalink" aria-hidden="true" title="Permalink"></a>VPS Properties:</h3> <p>We recommend that you provision a VPS with the following spec - these will suffice capacity-wise for all the tutorials we provide, although depending on your load, you might want to beef them up, which you can do as the need arises - ou should be able to upgrade those specs in real time if required, except for your disk space. You can, however, provision a secondary storage space (you can start small and increase it as you need to). I will cover setting this up, as it'll make your life far far easier in the medium-long term.</p> <ul><li>4-8 GB RAM - system volatile memory for running applications</li> <li>2-4 Virtual CPUs - processing capacity</li> <li>80-160 GB Disk space (NVMe storage is faster than SSD which is faster than spinning disk space) - long term storage for data</li> <li>running Ubuntu Linux 22.04 (the current Long Term Support version) - the operating system</li> <li>extra storage - 20-40GB extra space (can be expanded on fairly short notice) - a long term storage option separate to your operating system drive - this is very very useful in the event that your app inadvertently fills your hard drive. You'll thank yourself for doing this (I an assure you from bitter experience). It's always a good idea to learn from others' past mistakes rather than repeat them!</li> </ul><p>You'll need to create an account for yourself on your chosen hosting provider (it's a good idea to use Two Factor Authentication, aka 2FA, on your hosting account so that no one can log in as you and, say, delete your server unexpectedly - you'll find instructions on how to set up 2FA on your hosting provider's site) and create an Ubuntu 22.04 (or the most recent 'Long Term Support' (LTS) version) in the 'zone' nearest to you (or your primary audience, if that's different).</p> <p>If you don't already have an SSH key on your computer, I encourage you to <a href="https://helpdeskgeek.com/how-to/how-to-generate-ssh-keys-on-windows-mac-and-linux/">create one</a> and specify the <strong>public key</strong> in the process of creating your server - specifying the 'public key' of your SSH identity during the server creation process that should allow you to log in without needing a password!</p> <p>You'll need to note the server's <strong>IPv4</strong> address (it'll be a series of 4 numbers, 0-254, separated by full stops, e.g. 103.99.72.244), and you should also be aware that your server will have a newer <strong>IPv6</strong> address, which will be a set of 8 four <em>hex character</em> values (each hex character can have one of 16 values: 0-9,A-F) separated by colons, e.g. 2604:A880:0002:00D0:0000:0000:20DE:9001. With one or the other of those IPs, you should be able to <a href="https://www.digitalocean.com/community/tutorials/how-to-use-ssh-to-connect-to-a-remote-server-in-ubuntu">log into your new server via SSH</a>. If you're on a UNIX command line (e.g. a Linux or MacOS desktop), do this in a terminal (On Windows, I understand people use a tool called Putty for SSH, in which case follow the app's instructions - or you'll find many tutorials on using it with a quick web search):</p> <p><code>ssh [your server IPv4 or IPv6]</code></p> <p>followed by the ENTER key (that'll be true for any line of commands I provide).</p> <p>In some cases, depending on your hosting provider, you'll have a password to enter, or if you've specified your pre-existing public SSH key, you shouldn't need to enter a password at all, you should be logged in. To check what user you care, you can type</p> <p><code>whoami</code></p> <p>If it returns <code>root</code> (there's also a convention of using a '#' as the command prompt), you're the root or super-admin of the server. If not, you're a normal user (some hosting providers have a convention of giving you a default user called "ubuntu" or perhaps "debian") with a prompt that is, by convention, a '$'.</p> <p>Now that you're logged in, it's worth doing an upgrade of your server's Ubuntu system! Do that as follows (this works regardless of whether your a root user or an unprivileged user with 'sudo' ability):</p> <p><code>sudo apt update &amp;&amp; sudo apt dist-upgrade</code></p> <p>Usually the user, even if it's not the root user, will have the ability to use the <code>sudo</code> command modifier - that means "<em>do</em> this action as the root (aka the 'Super User', thus '<em>su</em>' in 'sudo' for short) user" - if you're a non-root user, you'll likely be asked to enter your password as a security precaution the first time you run a command prefaced by <code>sudo</code>. Enter it, and it should run the command. Plus, the system shouldn't bother you for it again unless you leave your terminal unused for a while (usually 5 minutes) and come back to it.</p> <p>At this point, I also like to install a cool software package called 'etckeeper' which records configuration changes on your VPS for future reference (it can be life-saving if trying to recover from an administrative mess-up!):</p> <p><code>sudo apt install etckeeper git</code></p> <p>which will also install some dependencies, including the very important (and relevant later on) 'git' version control system.</p> <h2><a id="user-content-key-variables-for-you-vps" href="#key-variables-for-you-vps" name="key-variables-for-you-vps" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Key variables for you VPS</h2> <p>To set up your services, you'll need a few crucial bits of information related to your system's identity and external systems you'll need it to interact with. For example, as mentioned before, you'll need a domain name. For the rest of this tutorial, we'll use the convention of representing those variables as a name inside [], for example, the domain name you've picked, [domain name].</p> <p>Here's a list of variables you'll need to know to complete the rest of this tutorial:</p> <ul><li> <strong>[ipv4]</strong> and <strong>[ipv6]</strong> - your VPS' IPv4 and IPv6 addresses (the latter can be ignored if your cloud provider doesn't support IPv6 addresses) as described above.</li> <li> <strong>[domain name]</strong> - the fully qualified domain names or subdomains of a base <strong>[domain name]</strong> by which you want your services to be accessed. You must have full domain management ability on this domain. Example: nextcloud.oeru.org - that's the nextcloud subdomain of the oeru.org domain.</li> <li>Authenticating SMTP details - this is required so your services can send emails to users - crucial things like email address validation and password recovery emails... <ul><li> <strong>[smtp server]</strong> - the domain name or IPv4 or IPv6 address of an SMTP server</li> <li> <strong>[smtp port]</strong> - the port number on the server that is listening for your connection. By convention it's likely to be 465 or 587, or possibly 25.</li> <li> <strong>[smtp reply-to-email]</strong> - a monitored email to which people can send email related to this WordPress site, e.g. notifications@<strong>[domain name]</strong> </li> <li> <strong>[smtp user]</strong> - the username (often an email address) used to authenticate against your SMTP server, provided by your email provider.</li> <li> <strong>[smtp password]</strong> - the accompanying password, provided by your email provider.</li> </ul></li> <li> <strong>[your email]</strong> - an email address to which system-related emails can be sent to you, perhaps something like webmaster@[domain name].</li> <li> <strong>[vps username]</strong> - the username you use on your server (by convention, these are one word, and all lower case).</li> </ul><h3><a id="user-content-get-your-domain-lined-up" href="#get-your-domain-lined-up" name="get-your-domain-lined-up" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Get your Domain lined up</h3> <p>You will want to have a domain to point at your server, so you don't have to remember the IP number. There're are thousands of domain "registrars" in the world who'll help you do that... You just need to "register" a name, and you pay yearly fee (usually between USD10-30 depending on the country and the "TLD" (Top Level Domain. There're national ones like .nz, .au, .uk, .tv, .sa, .za, etc., or international domains (mostly associated with the US) like .com, .org, .net, and a myriad of others. Countries decide on how much their domains wholesale for and registrars add a margin for the registration service).</p> <p>Here in NZ, I use the services of Metaname (they're local to me in Christchurch, and I know them personally and trust their technical capabilities). If you're not sure who to use, ask your friends. Someone's bound to have recommendations (either positive or negative, in which case you'll know who to avoid).</p> <p>Once you have selected and registered your domain, you can 'manage your Zone' to set up (usually through a web interface provided by the registrar) an <strong>A Record</strong> which associates your website's name to the <strong>IPv4</strong> address of your server. So you should just be able to enter your server's IPv4 address, the domain name (or sub-domain) you want to use for the web service you want to set up.</p> <p>Nowadays, <em>if your Domain Name host offers it (some don't, meaning you might be better off with a different one),</em> it's also important to define an <strong>IPv6</strong> record, which is called an <strong>AAAA Record</strong>... you put in your IPv6 address instead of your IPv4 one.</p> <p>You might be asked to set a "Time-to-live" (which has to do with the length of time Domain Name Servers are asked to "cache" the association that the A Record specifies) in which case you can put in 3600 seconds or an hour depending on the time units your registrar's interface requests... but in most cases that'll be set to a default of an hour automatically.</p> <h2><a id="user-content-editing-files" href="#editing-files" name="editing-files" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Editing files</h2> <p>In the rest of this tutorial, we're going to be editing quite a few files via the command line. If you're new to this, I recommend using the 'nano' text editor which is installed by default on Ubuntu Linux systems. It's fairly simple, and all of its options are visible in the text-based interface. I tend to use a far more powerful but far less beginner-friendly editor called 'vim'. There're other editors people might choose, too. To use your preferred editor for the rest of the tutorial, enter the following to set an environment variable EDIT, specifying your preferred editor, e.g.:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>EDIT</span>=$<span>(</span><span>which</span> <span>nano</span><span>)</span></pre></div></div> <p>or, if you're like me</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>EDIT</span>=$<span>(</span><span>which</span> <span>vim</span><span>)</span></pre></div></div> <p>so that subsequent references to $EDIT will invoke your preferred editor. Note the command <code>$(which nano)</code> is a script which finds the full path to the named command, in this case 'nano'. Putting a command inside the $() means 'replace with the value the script returns', so it sets the value of EDIT to the path of the nano command in this case. On my current machine, the value it returns is <code>/usr/bin/nano</code>, which is pretty typical.</p> <p>To test (at any time) whether you session still knows your $EDIT command, run</p> <p><code>echo $EDIT</code></p> <p>if it returns the path to your preferred editor, you're good to go. If not, just reassert (run again) the EDIT= line from above!</p> <p><em>Note: if you log out and back in again, change users, or create a new terminal tab/session, you'll need to reassert the EDIT value.</em></p> <h3><a id="user-content-set-up-an-unprivileged-user-for-yourself" href="#set-up-an-unprivileged-user-for-yourself" name="set-up-an-unprivileged-user-for-yourself" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Set up an unprivileged user for yourself</h3> <p>You should be able to test that your A and AAAA Records have been set correctly by logging into your server via SSH using your domain name rather than the IPv4 or IPv6 address you used previously. It should (after you accept the SSH warning that the server's name has a new name) work the same way your original SSH login did.</p> <p>This will log you into your server as it did the first time, either as 'root' or the default unprivileged user. It's not considered good practice to access your server as root (it's too easy to completely screw it up by accident). It's a good idea to create your own separate 'non-root' user who has 'sudo' privileges and the ability to log in via SSH. If you are <em>currently logged in as 'root'</em>, you can create a normal user for yourself via (replace [vps username] with your chosen username - in my case, I'd use <code>U=dave</code>):</p> <p><code>U=[vps username]</code><br /><code>adduser $U</code><br /><code>adduser $U ssh</code><br /><code>adduser $U admin</code><br /><code>adduser $U sudo</code></p> <p>You'll also want to a set a password for user [vps username] (we have a tutorial on <a href="/node/43">creating good passwords</a>):</p> <p><code>passwd $U</code></p> <p>then become that user temporarily (note, the root user can 'become' another user without needing to enter a password) and create an SSH key and, in the process, the <code>.ssh</code> directory (directories starting with a '.' are normally 'hidden' - you can show them in a directory listing via <code>ls -a</code>) for the file into which to put your public SSH key:</p> <p><code>su $U</code></p> <p>after which you need to re-run your EDIT command: <code>EDIT=$(which nano)</code></p> <p>and then run <code>ssh-keygen -t rsa -b 2048</code><br /><code>$EDIT ~/.ssh/authorized_keys</code></p> <p>and in that file, copy and paste (without spaces on either end) your <em>current computer's</em> <strong>public</strong> ssh key (<em>never publish</em> your private key anywhere!), save and close the file.</p> <p>and then leave the 'su' state, back to the superuser:</p> <p><code>CTRL+D</code> or type <code>exit</code></p> <p>From that point, you should be able to SSH to your server via <code>ssh [vps username]@[domain name]</code> without needing to enter a password.</p> <p>These instructions use 'sudo' in front of commands because I assume you're using a non-root user. The instructions will still work fine even if you're logged in as 'root' (the 'sudo' will be ignored as it's unnecessary).</p> <h2><a id="user-content-configure-the-vps" href="#configure-the-vps" name="configure-the-vps" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Configure the VPS</h2> <p>First things first. Let's make sure you've got the time zone set appropriately for your instance. It'll probably default to 'UTC' (Greenwich Mean Time). For our servers, I tend to pick 'Pacific/Auckland' which is our time zone. Run this</p> <p><code>sudo dpkg-reconfigure tzdata</code></p> <p>and pick the appropriate timezone. You can just leave it running UTC, but you might find it tricky down the track if, for example, you're looking at logs and having to constantly convert the times into your timezone.</p> <h3><a id="user-content-configuring-your-firewall" href="#configuring-your-firewall" name="configuring-your-firewall" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Configuring your firewall</h3> <p>In the name of safety from the get-go, let's configure our firewall. We work on the basis of explicitly allowing in <em>only</em> what we want to let in (i.e. a 'default deny' policy).</p> <p>First we'll enable the use of SSH through the firewall (<em>not doing this could lock us out of your machine!</em>)</p> <p><code>sudo ufw allow ssh</code><br /></p> <p>while we're here, we'll also enable data transfer from the internal (to the VPS) Docker virtual network and the IP range it uses for Docker containers:</p> <p><code>sudo ufw allow in on docker0</code><br /><code>sudo ufw allow from 172.0.0.0/8 to any</code></p> <p>Then we'll enable forwarding from internal network interfaces as required for Docker containers to be able to talk to the outside world:</p> <p><code>sudo $EDIT /etc/default/ufw</code></p> <p>and copy the line <code>DEFAULT_FORWARD_POLICY="DROP"</code> tweak it to look like this (commenting out the default, but leaving it there for future reference!):</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>#DEFAULT_FORWARD_POLICY="DROP"</span> <span>DEFAULT_FORWARD_POLICY</span>=<span>"ACCEPT"</span></pre></div></div> <p>and then save and exit the file (CTRL-X and then 'Y' if your editor is nano).</p> <p>You also have to edit <code>/etc/ufw/sysctl.conf</code></p> <p><code>sudo $EDIT /etc/ufw/sysctl.conf</code></p> <p>and remove the "#" at the start of the following lines, so they look like this:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># Uncomment this to allow this host to route packets between interfaces</span> net<span>/</span>ipv4<span>/</span><span>ip_forward</span>=<span>1</span> net<span>/</span>ipv6<span>/</span>conf<span>/</span>default<span>/</span><span>forwarding</span>=<span>1</span> net<span>/</span>ipv6<span>/</span>conf<span>/</span>all<span>/</span><span>forwarding</span>=<span>1</span></pre></div></div> <p>Then we need to restart the network stack to apply that configuration change</p> <p><code>sudo systemctl restart systemd-networkd</code></p> <p>(on older Ubuntu systems this would have been done via <code>sudo service networking restart</code>...)</p> <p>Next we have to enable the (default on Ubuntu) UFW firewall to start at boot time to keep your machine relatively safe.</p> <p><code>sudo $EDIT /etc/ufw/ufw.conf</code></p> <p>And set the ENABLED variable near the top:</p> <p><code>ENABLED=yes</code></p> <p>Now you can formally start UFW now:</p> <p><code>sudo ufw enable</code></p> <h3><a id="user-content-install-the-nginx" href="#install-the-nginx" name="install-the-nginx" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Install the Nginx</h3> <p>Next we need to install the Nginx web server and reverse-proxy, as well as the Let's Encrypt SSL certificate generator, both of which are crucial for any secure web services you might want to host. Nginx is a more efficient and flexible alternative to the older Apache web server you might've seen elsewhere (Nginx recently surpassed Apache as the most widely used web server on the Internet).</p> <p><code>sudo apt install nginx-full letsencrypt ssl-cert</code></p> <p>You'll get a couple pop-up windows in your terminal, just hit ENTER to accept the defaults. Having installed it, we need to create firewall rules to allow external services to see it:</p> <p><code>sudo ufw allow 'Nginx Full'</code></p> <p>You can check if the firewall rules you requested have been enabled:</p> <p><code>sudo ufw status</code></p> <h2><a id="user-content-outgoing-vps-email-optional" href="#outgoing-vps-email-optional" name="outgoing-vps-email-optional" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Outgoing VPS Email (optional)</h2> <p>Although it's not absolutely necessary (you can do this section later if you're in a big hurry), it's very useful for your server to be able to send out emails, like status emails to administrators (perhaps you) about things requiring their attention, e.g. the status of backups, pending security updates, expiring SSL certificates, etc.</p> <p>To do this, we'll set up the industrial strength Postfix SMTP server, which is pretty quick and easy. First we install Postfix and a command line mail client for testing purposes.</p> <p><code>sudo apt install postfix bsd-mailx</code></p> <p>During the install, you'll be asked to select a bunch of configuration parameters. Select the defaults except:</p> <ul><li>Select "Internet Site with Smarthost",</li> <li>fill in the domain name for your server [domain name],</li> <li>the [smtp server] name and [smtp port] (in the form [smtp server]:[smtp port], e.g. smtp.oeru.org:587 ) of your "smarthost" who'll be doing the authenticating SMTP for you, and</li> <li>the email address to which you want to receive system-related messages, [your email].</li> </ul><p>After that's done, we set a default address for the server to mail to, to [your email] selected above. First</p> <p><code>sudo $EDIT /etc/aliases</code></p> <p>We need to make sure the "root" user points to a real email address. Add a line at the bottom which says (replacing [your email] with your email :) )</p> <p><code>root: [your email]</code></p> <p>After which you'll need to convert the aliases file into a form that postfix can process, simply by running this:</p> <p><code>sudo newaliases</code></p> <p>Then we have to define the authentication credentials required to convince your mail server that you're you!</p> <p><code>sudo $EDIT /etc/postfix/relay_password</code></p> <p>and enter a single line in this format:</p> <p><code>[smtp server] [smtp user]:[smtp password]</code></p> <p>as an example, this is more or less what I've got for my system. Note that the [smtp user] in my case is an email address (this is common with many smtp system - the user is the same as the email address):</p> <p><code>smtp.oeru.org smtp-work@fossdle.org:YourObscurePassw0rd</code></p> <p>then save the file and, like the aliases file, run the conversion process (which uses a slightly different mechanism):</p> <p><code>sudo postmap /etc/postfix/relay_password</code></p> <p>Finally, we'll edit the main configuration file for Postfix to tell it about all this stuff:</p> <p>sudo $EDIT /etc/postfix/main.cf</p> <p>If your SMTP server uses port 25 (the default for unencrypted SMTP) you don't have to change anything, although most people nowadays prefer to use StartTLS or otherwise encrypted transport to at least ensure that your SMTP authentication details (at least) are transferred encrypted. That means using port 587 or 465. If you're using either of those ports, find the "relayhost = [your server name]" line... and add your port number after a colon, like this</p> <p><code>relayhost = [smtp server]:[smtp port]</code></p> <p>or, for example:</p> <p><code>relayhost = smtp.oerfoundation.org:465</code></p> <p>Then we have to update the configuration for Postfix to ensure that it knows about the details we've just defined (this command will automatically back up the original default configuration so you can start from scratch with the template below):</p> <p><code>sudo mv /etc/postfix/main.cf /etc/postfix/main.cf.orig &amp;&amp; sudo $EDIT /etc/postfix/main.cf</code></p> <p>You can just copy-and-paste the following into it, substituting your specific values for the [tokens]. Note: the IPv6 designations in the line <code>mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128</code> are <em>not</em> tokens - you can leave those unchanged.</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># See /usr/share/postfix/main.cf.dist for a commented, more complete version</span>   <span># Debian specific: Specifying a file name will cause the first</span> <span># line of that file to be used as the name. The Debian default</span> <span># is /etc/mailname.</span> <span>#myorigin = /etc/mailname</span>   smtpd_banner = <span>$myhostname</span> ESMTP <span>$mail_name</span> <span>(</span>Ubuntu<span>)</span> biff = no   <span># appending .domain is the MUA's job.</span> append_dot_mydomain = no   <span># Uncomment the next line to generate "delayed mail" warnings</span> <span>#delay_warning_time = 4h</span> readme_directory = no   <span># See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on</span> <span># fresh installs.</span> compatibility_level = <span>3.6</span>   <span># TLS parameters</span> <span>smtpd_tls_cert_file</span>=<span>/</span>etc<span>/</span>ssl<span>/</span>certs<span>/</span>ssl-cert-snakeoil.pem <span>smtpd_tls_key_file</span>=<span>/</span>etc<span>/</span>ssl<span>/</span>private<span>/</span>ssl-cert-snakeoil.key <span>smtpd_tls_security_level</span>=may   <span>smtp_tls_CApath</span>=<span>/</span>etc<span>/</span>ssl<span>/</span>certs <span>#smtp_tls_security_level=may</span> smtp_tls_session_cache_database = btree:<span>${data_directory}</span><span>/</span>smtp_scache   smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination myhostname = <span>[</span>domain name<span>]</span> alias_maps = hash:<span>/</span>etc<span>/</span>aliases alias_database = hash:<span>/</span>etc<span>/</span>aliases myorigin = <span>/</span>etc<span>/</span>mailname mydestination = <span>$myhostname</span>, localhost relayhost = <span>[</span>smtp server<span>]</span>:<span>[</span>smtp port<span>]</span> mynetworks = 127.0.0.0<span>/</span><span>8</span> <span>[</span>::ffff:127.0.0.0<span>]</span><span>/</span><span>104</span> <span>[</span>::<span>1</span><span>]</span><span>/</span><span>128</span> mailbox_size_limit = <span>0</span> recipient_delimiter = + inet_interfaces = all inet_protocols = all   <span># added to configure accessing the relay host via authenticating SMTP</span> smtp_sasl_auth_enable = <span>yes</span> smtp_sasl_password_maps = hash:<span>/</span>etc<span>/</span>postfix<span>/</span>relay_password smtp_sasl_security_options = noanonymous smtp_tls_security_level = encrypt   <span># if you're using Ubuntu prior to 20.04, uncomment (remove the #) the</span> <span># earlier line smtp_tls_security_level = may to save errors in 'postfix check'</span> <span># and comment this line (by adding a # at the start)</span> smtp_tls_wrappermode = <span>yes</span></pre></div></div> <p>Once you've created that <code>main.cf</code> file, you can double check that your config is valid:</p> <p><code>sudo postfix check</code></p> <p>and if it's all ok, you can get Postfix to re-read its configuration:</p> <p><code>sudo postfix reload</code></p> <p>You can then try sending an email so see if it works!</p> <p>By default, a command line application called "mail" is installed as part of the bsd-mailx package we installed alongside postfix. You can use it to send test email from the command line on your host to verify you've got things working correctly! The stuff in &lt;&gt; are the keys to hit at the end of the line...</p> <p><code>mail you@email.domain&lt;ENTER&gt;</code></p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>Subject: Testing from your.relay.server.domain<span>&lt;</span>ENTER<span>&gt;</span> Testing postfix remote host<span>&lt;</span>ENTER<span>&gt;</span> <span>&lt;</span>CTRL-D<span>&gt;</span> Cc:<span>&lt;</span>ENTER<span>&gt;</span></pre></div></div> <p>Typing (hold down the Control or Ctrl key on your keyboard and press the "d" key) will finish your message, showing you a "CC:" field, in which you can type in other email addresses if you want to test sending to multiple addresses. When you then hit , it will attempt to send this email. It might take a few minutes to work its way through to the receiving email system (having to run the gauntlet of spam and virus filters on the way).</p> <p>You can also always check the postfix system logs to see what postfix thinks about it using the command:</p> <p><code>sudo less +G /var/log/mail.log</code></p> <p>if your system doesn't have a <code>/var/log/mail.log</code>, never fear! Try this instead:</p> <p><code>sudo less +G /var/log/syslog</code></p> <p>In either case, hit to have the log update in real time.</p> <h2><a id="user-content-installing-the-docker-engine-docker-compose-and-lets-encrypt" href="#installing-the-docker-engine-docker-compose-and-lets-encrypt" name="installing-the-docker-engine-docker-compose-and-lets-encrypt" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Installing the Docker Engine, Docker Compose, and Let's Encrypt</h2> <p>First let's install the Docker Engine, which (these days) comes with Docker Compose and the <a href="http://letsencrypt.org/">Let's Encrypt</a> scripts that let you procure no-cost Secure Sockets Layer certificates to secure access to your server. You can follow the <a href="https://docs.docker.com/engine/install/ubuntu/">official Docker Engine install instructions</a>, but I've summarised them here (If the following doesn't work for you, go back to the official instructions, because something might've changed since I wrote this).</p> <p>First we want to make sure no old Docker engines are installed on this server (this probably won't do anything, but no harm in running it):</p> <p><code>for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done</code></p> <p>Second, we want to set up Docker's 'APT' repository, so you can keep Docker up-to-date with their latest versions (usually more up-to-date than those shipped with Ubuntu):</p> <p>First we Add Docker's official GPG key (copy and paste all of this at your command line):</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>sudo</span> <span>apt-get update</span> <span>sudo</span> <span>apt-get install</span> ca-certificates curl gnupg <span>sudo</span> <span>install</span> <span>-m</span> 0755 <span>-d</span> <span>/</span>etc<span>/</span>apt<span>/</span>keyrings curl <span>-fsSL</span> https:<span>//</span>download.docker.com<span>/</span>linux<span>/</span>ubuntu<span>/</span>gpg <span>|</span> <span>sudo</span> gpg <span>--dearmor</span> <span>-o</span> <span>/</span>etc<span>/</span>apt<span>/</span>keyrings<span>/</span>docker.gpg <span>sudo</span> <span>chmod</span> a+r <span>/</span>etc<span>/</span>apt<span>/</span>keyrings<span>/</span>docker.gpg</pre></div></div> <p>Then we have to add the repository to Apt our system's sources and install the Docker Engine:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>echo</span> \ <span>"deb [arch="</span>$<span>(</span><span>dpkg</span> --print-architecture<span>)</span><span>" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu <span>\ </span> "</span>$<span>(</span>. <span>/</span>etc<span>/</span>os-release <span>&amp;&amp;</span> <span>echo</span> <span>"<span>$VERSION_CODENAME</span>"</span><span>)</span><span>" stable"</span> <span>|</span> \ <span>sudo</span> <span>tee</span> <span>/</span>etc<span>/</span>apt<span>/</span>sources.list.d<span>/</span>docker.list <span>&gt;</span> <span>/</span>dev<span>/</span>null <span>sudo</span> <span>apt-get update</span> <span>sudo</span> <span>apt-get install</span> docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin</pre></div></div> <p>With this approach, any updates made by the Docker community will be installed as part of your regular server upgrades.</p> <p>Having installed the most recent release of the Docker engine, you should find that you've now got Docker Compose built in. Test that by running</p> <p><code>docker compose version</code></p> <p>which should return something like</p> <p><code>Docker Compose version v2.18.1</code></p> <p>If that's the case, superb, everything's great.</p> <h3><a id="user-content-backwards-compatibility-for-docker-compose" href="#backwards-compatibility-for-docker-compose" name="backwards-compatibility-for-docker-compose" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Backwards compatibility for Docker Compose</h3> <p>Since version 2 of the Docker Engine, the 'Docker Compose' capability has been incorporated into the base system. Prior to version 2, using Docker Compose required installing a separate app, and it was run by typing <code>docker-compose</code> rather than <code>docker compose</code>... so something I do now, to accommodate my muscle memory of typing <code>docker-compose</code> is to create a tiny script that lets me keep using that command, but have it call, instead, the new <code>docker compose</code> functionality. I do this via</p> <p><code>sudo $EDIT /usr/local/bin/docker-compose</code></p> <p>into which I put the following:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>#!/bin/bash</span> <span>D</span>=<span>`</span><span>which</span> docker<span>`</span> <span>$D</span> compose <span>"$@"</span></pre></div></div> <p>After saving that, we have to make the script 'executable' via</p> <p><code>sudo chmod a+x /usr/local/bin/docker-compose</code></p> <p>So you should now be able to run</p> <p><code>docker-compose version</code></p> <p>and get the same result that you did above for <code>docker compose version</code>...</p> <h3><a id="user-content-docker-use-by-non-root-user" href="#docker-use-by-non-root-user" name="docker-use-by-non-root-user" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Docker use by non-root user</h3> <p>If the above docker commands didn't work for you... and if you want to run Docker commands without being the root user or using <code>sudo</code>, as we usually do, you need to do a few more steps...</p> <ol><li> <p>create a 'docker' group on your system (this might already exist, but doing this again won't hurt): <code> sudo groupadd docker</code></p> </li> <li> <p>add your user to it: <code>sudo usermod -aG docker $USER</code></p> </li> <li> <p>refresh your shell so that it recognises your user's membership in the docker group: <code>newgrp docker</code></p> </li> </ol><p>You should now be to run a test as a non-root user <code>docker run hello-world</code></p> <h2><a id="user-content-setting-up-places-for-your-docker-configurations-and-persistent-data" href="#setting-up-places-for-your-docker-configurations-and-persistent-data" name="setting-up-places-for-your-docker-configurations-and-persistent-data" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Setting up places for your Docker configurations and persistent Data</h2> <p>The next step is to set up the file structure for holding your Docker configurations and the data your Docker containers will access. This is my convention, so you're welcome to do things different, but this is a 'known good' approach.</p> <p>Now we create the set of directories I use for holding Docker Compose configurations (<code>/home/docker</code>) and the persistent data the Docker containers create (<code>/home/data</code>)</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>D</span>=<span>[</span>domain<span>]</span> <span>sudo</span> <span>mkdir</span> <span>-p</span> <span>/</span>home<span>/</span>data<span>/</span><span>$D</span> <span>sudo</span> <span>mkdir</span> <span>-p</span> <span>/</span>home<span>/</span>docker<span>/</span><span>$D</span></pre></div></div> <p>It's helpful to make sure that your non-root user can also read and write files in these directories:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>U</span>=<span>[</span>vps username<span>]</span> <span>sudo</span> <span>chown</span> <span>-R</span> <span>$U</span> <span>/</span>home<span>/</span>docker <span>sudo</span> <span>chown</span> <span>-R</span> <span>$U</span> <span>/</span>home<span>/</span>data</pre></div></div> <h2><a id="user-content-configuring-nginx-reverse-proxy-for-your-service" href="#configuring-nginx-reverse-proxy-for-your-service" name="configuring-nginx-reverse-proxy-for-your-service" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Configuring Nginx reverse proxy for your service</h2> <p>Above, we installed Nginx as well as the Let's Encrypt scripts. Now we'll configure them as it's useful to have them working <em>before</em> you set up your services.</p> <p>In order for you, outside of your server, to see your specific LibreSoftware services, you will need to set up a secure external 'reverse proxy' on your host VPS which will accept requests for each of those services from the Internet and pass those requests securely to the two sets of Docker containers providing the services. These will answer to <code>https://[domain name]</code>, noting that a given VPS can answer on behalf of more than one domain name or subdomain name, where each references a different instance of a service or altogether different services. For example, a given VPS could host multiple services, like, say, a password manager, a WordPress blog, a Mautic email automation system, an Authentik Single-Sign On service, and a Discourse forum, where each has a separate domain name (or sub domain name) and each has an Nginx reverse proxy configuration that directs requests to the appropriate domain to the appropriate set of Docker containers.</p> <p>We use Let's Encrypt to provide the SSL certificates (each is a file with a specially generated, very long string) which we use to limit access to our services to encrypted (secure) connections (protecting both our users and ourselves from external enemies), usually one for each service domain name and corresponding Nginx configuration file. Some Nginx configurations will have multiple domains names for which a given service accepts connections, but it consolidates them to the 'canonical' (official) domain name. For example, the OER Foundation's WordPress website will respond to any of the following:</p> <ul><li> <a href="http://www.oerfoundation.org">http://www.oerfoundation.org</a>,</li> <li> <a href="http://oerfoundation.org">http://oerfoundation.org</a>,</li> <li> <a href="https://www.oerfoundation.org">https://www.oerfoundation.org</a>, and</li> <li> <a href="https://oerfoundation.org">https://oerfoundation.org</a> </li> </ul><p>but all requests to any of those 4 options (note the difference between http:// and http<strong>s</strong>:// where 's' refers to 'Secure' or encrypted) will all be redirected - transparently by the Nginx configuration, which will be reflected in your browser's address bar - to the canonical web address (or URL), <a href="https://oerfoundation.org">https://oerfoundation.org</a>, that we've chosen because it's short and secure.</p> <p>Nginx will not run unless the SSL certificates you reference in your configurations are valid. Given that we need to request them with a working Nginx <em>prior</em> to them being created puts us in an awkward position. We use a trick to get around it: we <em>temporarily</em> reference the default 'self-signed' SSL certificates (sometimes called 'Snakeoil certs' because that's the placeholder name they're given) that every new Linux system generates when it's installed, that are <em>valid certificates</em> (and thus acceptable to Nginx) but *they won't work with our domains, as they're generic and not 'signed' by an external party, like Let's Encrypt, meaning that your browser won't like them. But that's ok, as you browser will never need to see them, and Let's Encrypt's systems won't look at them either. We'll swap the Snakeoil certs out as soon as we've successfully created the Let's Encrypt ones, and your browser will be happy, and all will be well with the world.</p> <p>This should happen automatically when you install the Let's Encrypt packages, but just to be sure, run this (it won't harm anything if you run it more than once):</p> <p><code>sudo make-ssl-cert generate-default-snakeoil</code></p> <p>which creates your default 'Snakeoil' certificates. They are technically <em>valid</em> certificates, but they aren't signed by any</p> <h2><a id="user-content-lets-encrypt-setup" href="#lets-encrypt-setup" name="lets-encrypt-setup" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Let's Encrypt setup</h2> <p>Step one of using Let's Encrypt is to make sure the Let's Encrypt scripts are installed (note: sometimes they're referred to as 'certbot' - it's the same code):</p> <p><code>sudo apt install letsencrypt</code></p> <p>Let's Encrypt and Nginx need to work together. Nginx stores all of its configuration in the directory <code>/etc/nginx</code>. The first thing we'll do is create a place for Let's Encrypt Nginx-specific configuration details:</p> <p><code>sudo mkdir /etc/nginx/includes</code></p> <p>Then we create that configuration file itself:</p> <p><code>sudo $EDIT /etc/nginx/includes/letsencrypt.conf</code></p> <p>into which we copy-and-paste the following (no [tokens] to replace in this one!)</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># Rule for legitimate ACME Challenge requests</span> location ^~ <span>/</span>.well-known<span>/</span>acme-challenge<span>/</span> <span>{</span> default_type <span>"text/plain"</span>; <span># this can be any directory, but this name keeps it clear</span> root <span>/</span>var<span>/</span>www<span>/</span>letsencrypt; <span>}</span>   <span># Hide /acme-challenge subdirectory and return 404 on all requests.</span> <span># It is somewhat more secure than letting Nginx return 403.</span> <span># Ending slash is important!</span> location = <span>/</span>.well-known<span>/</span>acme-challenge<span>/</span> <span>{</span> <span>return</span> <span>404</span>; <span>}</span></pre></div></div> <p>As described in the file we've just created, Let's Encrypt will look for a secret code we create to verify that we own the domain we're requesting an SSL certificate for, so we have to make sure it exists:</p> <p><code>sudo mkdir /var/www/letsencrypt</code></p> <p>You will need to install an Nginx reverse proxy configuration file for each web-based service you run on your VPS, but those will usually be service-specific in their make up, so I won't discuss them here.</p> <p>But, for the record (to make your lives a bit easier), this is what you'll do <em>after</em> you've got a suitable Nginx reverse proxy configuration in place and ready to run to acquire a Let's Encrypt SSL certificate.</p> <h3><a id="user-content-requesting-lets-encrypt-certificates" href="#requesting-lets-encrypt-certificates" name="requesting-lets-encrypt-certificates" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Requesting Let's Encrypt certificates</h3> <p>To request Let's Encrypt SSL certificates for your service, run the following, replacing the [domain name] reference with the address you've selected for your service. Note that 'certbot' is the script provided by the Let's Encrypt package - historically, it could also be called via 'letsencrypt', although apparently the latter is now deprecated:</p> <p><code>sudo certbot --webroot -w /var/www/letsencrypt -d [domain name]</code></p> <p>Note - if you want to address your instance from multiple domains, use one (or more) <code>-d [another domain]</code> - just make sure that</p> <ul><li>all those domains already point to your VPS, and</li> <li>those domains are included in the Nginx proxy configuration above.</li> </ul><p>otherwise the Let's Encrypt certbot request will fail!</p> <p>Here's what you're likely to see as output from the first run of the letsencrypt script - note that it will ask you for an email address (so it can send you warnings if your certificate is going to expire, e.g. due to a problem with renewal (like if you make a configuration change that breaks the renewal process)).</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>Saving debug log to <span>/</span>var<span>/</span>log<span>/</span>letsencrypt<span>/</span>letsencrypt.log Enter email address <span>(</span>used <span>for</span> urgent renewal and security notices<span>)</span> <span>(</span>Enter <span>'c'</span> to cancel<span>)</span>: webmaster<span>@</span>fossdle.org   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please <span>read</span> the Terms of Service at https:<span>//</span>letsencrypt.org<span>/</span>documents<span>/</span>LE-SA-v1.3-September-<span>21</span>-<span>2022</span>.pdf. You must agree <span>in</span> order to register with the ACME server. Do you agree? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <span>(</span>Y<span>)</span>es<span>/</span><span>(</span>N<span>)</span>o: y   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would you be willing, once your first certificate is successfully issued, to share your email address with the Electronic Frontier Foundation, a founding partner of the Let<span>'s Encrypt project and the non-profit organization that develops Certbot? We'</span>d like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <span>(</span>Y<span>)</span>es<span>/</span><span>(</span>N<span>)</span>o: y Account registered. Requesting a certificate <span>for</span> <span>[</span>domain name<span>]</span>   Successfully received certificate. Certificate is saved at: <span>/</span>etc<span>/</span>letsencrypt<span>/</span>live<span>/</span><span>[</span>domain name<span>]</span><span>/</span>fullchain.pem Key is saved at: <span>/</span>etc<span>/</span>letsencrypt<span>/</span>live<span>/</span><span>[</span>domain name<span>]</span><span>/</span>privkey.pem This certificate expires on <span>(</span>some future <span>date</span><span>)</span>. These files will be updated when the certificate renews. Certbot has <span>set</span> up a scheduled task to automatically renew this certificate <span>in</span> the background.   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - If you like Certbot, please consider supporting our work by: <span>*</span> Donating to ISRG <span>/</span> Let<span>'s Encrypt: https://letsencrypt.org/donate * Donating to EFF: https://eff.org/donate-le - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -</span></pre></div></div> <p>Ideally, you'll see a message like the above. If not, and there's an error, the error messages they provide are usually very useful and accurate. Fix the problem and try again. Note, your SSL certificate will have the name of your [domain name], even if it also provide support for [second domain name] (or third, fourth, etc.).</p> <p>Once you have a Let's Encrypt certificate, you can update our Nginx configuration:</p> <p><code>sudo $EDIT /etc/nginx/sites-available/[domain name]</code></p> <p>and swap all occurrences of</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre> ssl_certificate <span>/</span>etc<span>/</span>ssl<span>/</span>certs<span>/</span>ssl-cert-snakeoil.pem; ssl_certificate_key <span>/</span>etc<span>/</span>ssl<span>/</span>private<span>/</span>ssl-cert-snakeoil.key; <span># ssl_certificate /etc/letsencrypt/live/[domain name]/fullchain.pem;</span> <span># ssl_certificate_key /etc/letsencrypt/live/[domain name]/privkey.pem;</span></pre></div></div> <p>to</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;</span> <span># ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;</span> ssl_certificate <span>/</span>etc<span>/</span>letsencrypt<span>/</span>live<span>/</span><span>[</span>domain name<span>]</span><span>/</span>fullchain.pem; ssl_certificate_key <span>/</span>etc<span>/</span>letsencrypt<span>/</span>live<span>/</span><span>[</span>domain name<span>]</span><span>/</span>privkey.pem;</pre></div></div> <p>which enables your new domain-specific SSL certificate. Check that Nginx is happy with your change:</p> <p><code>sudo nginx -t</code></p> <p>and if so,</p> <p><code>sudo service nginx reload</code></p> <p>You domain should now be enabled for <code>https://</code> access. Note that going to <code>http://[domain name]</code> should automatically redirect you to <code>https://[domain name]</code> because you care about your user's security!</p> <p>And that's it - your server is now ready to receive specific configurations for a myriad of Libre web services! Have a look around this site and check out some of the other tutorials - for many of them, you'll now be able to skip to near the end, as you've done most of the groundwork already!</p></div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=59&amp;2=field_blog_comments&amp;3=comment" token="_ja99mlVJRPvJ8nwyG98TWz55iVgOEcN5jO1aOJbqu0"></drupal-render-placeholder> </div> </section> Thu, 12 Oct 2023 00:12:25 +0000 dave 59 at http://tech.oeru.org Installing BigBlueButton on an OERu Docker Server http://tech.oeru.org/installing-bigbluebutton-oeru-docker-server <span class="field field--name-title field--type-string field--label-hidden">Installing BigBlueButton on an OERu Docker Server</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--bigbluebutton"> <span class="field__item-wrapper"><a href="/taxonomy/term/84" hreflang="en">BigBlueButton</a></span> </div> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> <div class="field__item field__item--_004"> <span class="field__item-wrapper"><a href="/taxonomy/term/75" hreflang="en">20.04</a></span> </div> <div class="field__item field__item--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</a></span> </div> <div class="field__item field__item--docker-compose"> <span class="field__item-wrapper"><a href="/taxonomy/term/49" hreflang="en">docker-compose</a></span> </div> <div class="field__item field__item--postgresql"> <span class="field__item-wrapper"><a href="/taxonomy/term/20" hreflang="en">postgresql</a></span> </div> <div class="field__item field__item--postfix"> <span class="field__item-wrapper"><a href="/taxonomy/term/66" hreflang="en">postfix</a></span> </div> <div class="field__item field__item--nginx"> <span class="field__item-wrapper"><a href="/taxonomy/term/30" hreflang="en">nginx</a></span> </div> <div class="field__item field__item--lets-encrypt"> <span class="field__item-wrapper"><a href="/taxonomy/term/17" hreflang="en">let&#039;s encrypt</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/1" class="username">dave</a></span> <span class="field field--name-created field--type-created field--label-hidden">Wed 10/11/2021 - 15:42</span> <div class="field field-node--field-image field-name-field-image field-type-image field-label-hidden has-multiple"> <figure class="field-type-image__figure image-count-1"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-26%20at%2015-03-34%20The%20OERu%20BigBlueButton%20-%20Home%20Room-withModeratorMenu.png?itok=wkyM4MSu" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;BigBlueButton conversation window in a browser, with the moderator tools open, showing link to breakout rooms and other moderation functions.&quot;}" role="button" title="BigBlueButton conversation window in a browser, with the moderator tools open, showing link to breakout rooms and other moderation functions." data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;BigBlueButton conversation window in a browser, with the moderator tools open, showing link to breakout rooms and other moderation functions.&quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-26%20at%2015-03-34%20The%20OERu%20BigBlueButton%20-%20Home%20Room-withModeratorMenu.png?itok=tsVPmMgZ" width="220" height="143" alt="BigBlueButton conversation window in a browser, with the moderator tools open, showing link to breakout rooms and other moderation functions." loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-2"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-26%20at%2012-02-53%20Create%20Droplets%20-%20DigitalOcean.png?itok=RFCxKkbH" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Digital Ocean &#039;Droplet&#039; creation screen showing options selected for a BBB server&quot;}" role="button" title="Digital Ocean &#039;Droplet&#039; creation screen showing options selected for a BBB server" data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Digital Ocean &#039;Droplet&#039; creation screen showing options selected for a BBB server&quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-26%20at%2012-02-53%20Create%20Droplets%20-%20DigitalOcean.png?itok=qpszFHwz" width="106" height="220" alt="Digital Ocean &#039;Droplet&#039; creation screen showing options selected for a BBB server" loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-3"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-22%20at%2012-42-19%20bbbtest%20milll%20ws%20-%20DigitalOcean.png?itok=9s5Cj-8F" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Performance graphs for a Digital Ocean &#039;Droplet&#039; configured to run BBB.&quot;}" role="button" title="Performance graphs for a Digital Ocean &#039;Droplet&#039; configured to run BBB." data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Performance graphs for a Digital Ocean &#039;Droplet&#039; configured to run BBB.&quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-22%20at%2012-42-19%20bbbtest%20milll%20ws%20-%20DigitalOcean.png?itok=Sy3JvvuB" width="220" height="166" alt="Performance graphs for a Digital Ocean &#039;Droplet&#039; configured to run BBB." loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-4"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-22%20at%2012-42-52%20milll%20ws%20-%20Metaname.png?itok=eILrt0_H" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;The Metaname DNS configuration for the milll.ws domain.&quot;}" role="button" title="The Metaname DNS configuration for the milll.ws domain." data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;The Metaname DNS configuration for the milll.ws domain.&quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-22%20at%2012-42-52%20milll%20ws%20-%20Metaname.png?itok=8iNSxMhC" width="220" height="166" alt="The Metaname DNS configuration for the milll.ws domain." loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-5"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-23%20at%2014-20-22%20BigBlueButton.png?itok=vRbjT27h" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;The Greenlight front-end (providing user management and &#039;room&#039; configuration) for BigBlueButton.&quot;}" role="button" title="The Greenlight front-end (providing user management and &#039;room&#039; configuration) for BigBlueButton." data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;The Greenlight front-end (providing user management and &#039;room&#039; configuration) for BigBlueButton.&quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-23%20at%2014-20-22%20BigBlueButton.png?itok=4W4E__KP" width="220" height="144" alt="The Greenlight front-end (providing user management and &#039;room&#039; configuration) for BigBlueButton." loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-6"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-30%20at%2023-04-07%20The%20OERu%20BigBlueButton%20-%20Home%20Room.png?itok=ofBw87DD" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Joining a BBB session, you get the option of using a microphone to participate or being a &#039;listen-only&#039; participant.&quot;}" role="button" title="Joining a BBB session, you get the option of using a microphone to participate or being a &#039;listen-only&#039; participant." data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Joining a BBB session, you get the option of using a microphone to participate or being a &#039;listen-only&#039; participant.&quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-30%20at%2023-04-07%20The%20OERu%20BigBlueButton%20-%20Home%20Room.png?itok=RsOTQSIE" width="220" height="133" alt="Joining a BBB session, you get the option of using a microphone to participate or being a &#039;listen-only&#039; participant." loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-7"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-30%20at%2023-04-22%20The%20OERu%20BigBlueButton%20-%20Home%20Room%20-%20echo%20test.png?itok=H8WlXV4f" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;If you select &quot;microphone&quot;, you will next be asked to speak into it and do an &#039;echo test&#039; to calibrate BBB&#039;s echo cancellation algorithms.&quot;}" role="button" title="If you select &quot;microphone&quot;, you will next be asked to speak into it and do an &#039;echo test&#039; to calibrate BBB&#039;s echo cancellation algorithms." data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;If you select &quot;microphone&quot;, you will next be asked to speak into it and do an &#039;echo test&#039; to calibrate BBB&#039;s echo cancellation algorithms.&quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-30%20at%2023-04-22%20The%20OERu%20BigBlueButton%20-%20Home%20Room%20-%20echo%20test.png?itok=KvueJqpL" width="220" height="133" alt="If you select &quot;microphone&quot;, you will next be asked to speak into it and do an &#039;echo test&#039; to calibrate BBB&#039;s echo cancellation algorithms." loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-8"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-30%20at%2023-04-49%20The%20OERu%20BigBlueButton%20-%20Home%20Room-webcam.png?itok=pEnZWxzO" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;And you can share your webcam feed, too. &quot;}" role="button" title="And you can share your webcam feed, too. " data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;And you can share your webcam feed, too. &quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-30%20at%2023-04-49%20The%20OERu%20BigBlueButton%20-%20Home%20Room-webcam.png?itok=tXwJ7Cpf" width="220" height="133" alt="And you can share your webcam feed, too. " loading="lazy" class="image-style-medium" /> </a> </div> </figure> <figure class="field-type-image__figure image-count-9"> <div class="field-type-image__item"> <a href="http://tech.oeru.org/sites/default/files/styles/max_1300x1300/public/2021-11/Screenshot%202021-11-26%20at%2015-00-54%20The%20OERu%20BigBlueButton%20-%20Home%20Room.png?itok=55K_wxG1" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;BigBlueButton in effect with 3 participants (all the author from different devices). Note the public chat.&quot;}" role="button" title="BigBlueButton in effect with 3 participants (all the author from different devices). Note the public chat." data-colorbox-gallery="gallery-field_image-Ty9W6WjDqVs" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;BigBlueButton in effect with 3 participants (all the author from different devices). Note the public chat.&quot;}"><img src="/sites/default/files/styles/medium/public/2021-11/Screenshot%202021-11-26%20at%2015-00-54%20The%20OERu%20BigBlueButton%20-%20Home%20Room.png?itok=j1O8G0e-" width="220" height="143" alt="BigBlueButton in effect with 3 participants (all the author from different devices). Note the public chat." loading="lazy" class="image-style-medium" /> </a> </div> </figure> </div> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>This tutorial was developed for the <a href="https://samoaksi.ws/">Samoan Knowledge Society Initiative</a>, in which the OER Foundation is very pleased to be involved. (Update 2022-02-10: we have also created a <a href="https://vimeo.com/663520867">video tutorial</a> based on this written tutorial)</p> <p><a href="https://bigbluebutton.org">BigBlueButton</a> (BBB) is a full-featured video conferencing system designed for large scale remote learning at the university level. In some ways, BBB goes beyond feature parity with widely used proprietary video conferencing systems like Zoom or Microsoft Teams, Webex, or Google Hangouts. It's main differences are that BBB</p> <ul><li>was designed from the gound up for educational use, and</li> <li>is Free and Open Source Software (FOSS) and anyone can install it (as we describe in this tutorial) at no cost other than whatever cost you pay for hosting the application and a bit of your time.</li> </ul><p>You can read all about BigBlueButton and how it works using the <a href="https://docs.bigbluebutton.org">excellent documentation</a> provided by its development community.</p> <p>There are no per-user license fees (or <em>any</em> fees), and your users don't need to install special software, vastly simplifying its adoption: BBB works brilliantly on any desktop computer, laptop, or mobile device (tablet or phone) with a modern web browser that implements the <a href="https://webrtc.org">WebRTC</a> open web standard (which is all modern browsers).</p> <p>The <a href="https://github.com/bigbluebutton/">source code for BBB's internal components, and its many ancillary tools</a> is available for anyone to learn from or improve - that's the fundamental benefit of FOSS. Since the outbreak of COVID19, the team at a small Ottowa, Canada-based company, Blindside Networks, has been working tirelessly with the BBB community to improve the BBB code, which is being used all over the world by millions of people every day.</p> <p>This tutorial walks you through provisioning and building your own BigBlueButton instance on a server running <a href="https://ubuntu.com/download/server">Ubuntu GNU/Linux</a> - version 20.04 is the Long Term Support (LTS) version as of this writing - using <a href="https://www.docker.com/docker-community">Docker</a> containers for the various software services and components, with the containers managed by <a href="https://docs.docker.com/compose/">Docker Compose</a>.</p> <p><em>Table of contents</em></p> <ul class="table-of-contents"><li> <p><a href="#introduction">Introduction</a></p> <ul><li> <p><a href="#references">References</a></p> </li> <li> <p><a href="#copying-and-pasting">Copying and pasting</a></p> </li> </ul></li> <li> <p><a href="#information-you-need-to-know">Information you need to know</a></p> </li> <li> <p><a href="#preferences">Preferences</a></p> </li> <li> <p><a href="#set-up-your-virtual-server">Set up your virtual server</a></p> </li> <li> <p><a href="#configure-your-domain-to-point-at-your-server">Configure your Domain to point at your Server</a></p> <ul><li> <p><a href="#best-practice-access-your-server-as-a-non-root-user">Best Practice: access your server as a non-root user</a></p> </li> </ul></li> <li> <p><a href="#a-few-post-install-updates">A few post-install updates</a></p> <ul><li> <p><a href="#update-the-servers-hosts-file">Update the server's hosts file</a></p> </li> </ul></li> <li> <p><a href="#firewall-configuration">Firewall Configuration</a></p> </li> <li> <p><a href="#install-postfix-so-the-server-can-send-email">Install Postfix so the server can send email</a></p> <ul><li> <p><a href="#email-test">Email test</a></p> </li> </ul></li> <li> <p><a href="#install-nginx-webserver-and-lets-encrypt-tools">Install Nginx webserver and Let's Encrypt tools</a></p> </li> <li> <p><a href="#set-up-coturn-certificates">Set up COTURN certificates</a></p> </li> <li> <p><a href="#set-up-nginx-reverse-proxy-configuration">Set up Nginx Reverse Proxy Configuration</a></p> <ul><li> <p><a href="#extra-security-with-dhparampem">Extra security with dhparam.pem</a></p> </li> </ul></li> <li> <p><a href="#install-docker">Install Docker</a></p> </li> <li> <p><a href="#install-docker-compose">Install Docker-compose</a></p> </li> <li> <p><a href="#git-clone-bbb-docker-repository">Git-Clone BBB Docker repository</a></p> </li> <li> <p><a href="#configure-your-bbb">Configure your BBB</a></p> <ul><li> <p><a href="#fix-minor-configuration-error-that-blocks-jodconverter">Fix minor configuration error that blocks JODConverter</a></p> </li> </ul></li> <li> <p><a href="#build-your-bbb">Build your BBB</a></p> </li> <li> <p><a href="#visit-your-new-bbb">Visit your new BBB</a></p> </li> <li> <p><a href="#create-an-admin-user">Create an admin user:</a></p> </li> <li> <p><a href="#run-your-first-conference">Run your first conference</a></p> </li> <li> <p><a href="#next-steps">Next Steps</a></p> </li> </ul><h2><a id="user-content-introduction" href="#introduction" name="introduction" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Introduction</h2> <h3><a id="user-content-references" href="#references" name="references" class="heading-permalink" aria-hidden="true" title="Permalink"></a>References</h3> <p>For this tutorial, we are using a <a href="https://digitalocean.com">Digital Ocean</a> 'Droplet' as our cloud server. You can use an almost identical process to provision a server from any one of a thousand other commodity GNU/Linux cloud hosting providers.</p> <p>To demonstrate the process of defining a sub domain and its IPv4 and IPv6 addresses (via A and AAAA records respectively) I'm using my preferred local domain registrar <a href="https://metaname.net">Metaname</a> (based here in Christchurch, New Zealand, as am I. I know the developer/owner of the service and I trust him). You can use any domain registrar that gives you the ability to configure your 'DNS zone file'. Some of might have your own DNS server or institutional name servers, in which case you might have to request these changes be made on your behalf.</p> <h3><a id="user-content-copying-and-pasting" href="#copying-and-pasting" name="copying-and-pasting" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Copying and pasting</h3> <p>We will be providing a bunch of command lines that you can copy and paste into a terminal on the computer you're using to access your virtual server. Links like</p> <p><code>this one</code></p> <p>are intended for you to copy and paste into the command line. On Linux, please note that in some cases if "CTRL-V" doesn't work to paste, try using "CTRL+SHIFT+V" (that's because in some terminals, "CTRL+V" had a pre-existing purpose as terminals have been around since long before desktops and 'copy-paste" were invented and the arbitrary CTRL-C, CTRL-X, CTRL-V key combos were chosen by Microsoft without any consideration for prior art). Similarly, if you want to copy <em>from</em> a terminal, you might need to use "CTRL+SHIFT+C". Try it.</p> <p>When we ask you edit a file (using the editor you choose to assign to the EDIT shell variable below), we expect you to complete the recommended changes, and then <em>save the file</em> and exit back to the command line.</p> <p>We'll aim to show you what to change using a 'code' box:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>Like this one <span>which</span> is suitable <span>for</span> multi-line content <span>(</span>like what you <span>find</span> <span>in</span> a <span>file</span><span>)</span></pre></div></div> <h2><a id="user-content-information-you-need-to-know" href="#information-you-need-to-know" name="information-you-need-to-know" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Information you need to know</h2> <p>Info you need to have handy to complete this tutorial:</p> <ul><li>You need to know the IPv4 address of your server. Token: [IPv4] - it'll be in the form of nnn.nnn.nnn.nnn where nnn = 0-254, example value 143.110.228.88</li> <li>If your server has one, you need to know its IPv6 address. Token: [IPv6] - it'll be in the form of hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh where h = 0-9a-f, i.e. a hex value, example 2604:a880:4:1d0::402:b000</li> <li>You need to have a domain name (or a sub domain name) to point at your server's IPv4 and IPv6. Token: [Domain], example domain bbbtest.milll.ws (in this case, bbbtest is the subdomain of the milll.ws domain)</li> <li>The email address your server should send status or admin-related email to. Token [Admin email], example webmaster@[Domain]</li> <li>You'll need a set of authenticating SMTP account settings including - these can be used both on the host and in the BBB installation: <ul><li>[SMTP server] - the domain name or IP address of an existing SMTP server, e.g. about.oerfoundation.org (our one), or smtp.googlemail.com,</li> <li>[SMTP port] - the port number for the service you're using. It'll usually be one of 587 or 465. Older, unauthenticated SMTP servers used to use port 25, but that's now blocked by most ISPs due to its abuse by spammers.</li> <li>[SMTP security] - usually this'll be something like 'SSL' (usually associated with port 465) or 'StartTLS' (usually associated with port 587).</li> <li>[SMTP username] - the username - often an email address - for the authenticating SMTP service, and lastly,</li> <li>[SMTP password] - the password accompanying the username.</li> </ul></li> <li>You'll want an email address that you can check to send test emails to: token [Test email], e.g. <a href="mailto:you@youremailprovider.tld">you@youremailprovider.tld</a>, and finally</li> <li>You'll want a 'from' email address that users of your system will see when they get emails from you. Token: [Outgoing email], e.g. <a href="mailto:notifications@milll.ws">notifications@milll.ws</a>.</li> </ul><h2><a id="user-content-preferences" href="#preferences" name="preferences" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Preferences</h2> <p>You'll need to copy and paste the following lines into any terminal you're using to complete this tutorial, so that that session is aware of your preferred values:</p> <p><code>EDIT=$(which nano)</code></p> <p><code>SITE=bbbtest.milll.ws</code></p> <p>We're going to reference these periodically in the following process, so it's important you set these correctly. To verify them, you can run this in any terminal at any time to verify that the values are still defined and current:</p> <p><code>echo "Our variables = $EDIT and $SITE"</code></p> <h2><a id="user-content-set-up-your-virtual-server" href="#set-up-your-virtual-server" name="set-up-your-virtual-server" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Set up your virtual server</h2> <p>You'll need to log into your cloud service provider's dashboard - in our case, we've used DigitalOcean. That means pointing our browser to <a href="https://digitalocean.com">https://digitalocean.com</a> and either using "Log In" (if we already have an account) or "Sign Up" (if we don't).</p> <p>Once you're logged in, you'll want to go to 'Create' and select 'Droplets' (Digital Ocean calls each of their virtual servers a "Droplet").</p> <p>We've included a screenshot of the Create Droplet page, with the relevant options selected. They are as follows:</p> <ul><li>Choose an image: we choose 'Ubuntu 20.04 LTS'</li> <li>Choose a plan: we choose 'Basic' <ul><li>for CPU options: we choose 'Premium AMD with NVMe SSD'</li> <li>for the size, we choose either the '$24/mo' (choose the '$48/mo' option if you want to be able to support large groups, like 100+ simultaneous participants).</li> </ul></li> <li>Choose a datacenter region: we choose 'San Franciso, zone 3' (which we've determined to be in the 'centre of the internet', i.e. the most central point to give the best website performance to a global audience. You might want to try a different place depending on your location in the globe and your audience).</li> <li>VPC Network - we just leave the default.</li> <li>Select additional options: we check 'IPv6' and 'Monitoring' but <em>not</em> User data.</li> <li>Authentication: if we have added one or more SSH keys to our DigitalOcean profile select 'SSH keys'. If not, you can select 'Password'. It's far more efficient and secure to use SSH whereever possible.</li> <li>We're ony creating 1 Droplet and we Choose a hostname of [Domain]</li> <li>We don't need to add any tags</li> <li>We <em>won't</em> Add backups.</li> </ul><p>Then we can <code>Create Droplet</code>...</p> <p>When the Droplet is finished, we can get the [IPv4] and [IPv6] addresses.</p> <p>We can also either log in via the command line from a local GNU/Linux or UNIX (e.g. MacOS) terminal or via a graphical SSH client (on Microsoft Windows, most people use Putty) via <code>ssh root@[IPv4]</code> or <code>ssh root@[IPv6]</code> (replacing those tokens with their actual IPv4 or IPv6 addresses). If we've set a key, we should get logged in without the need to enter any passwords. If not, we have to enter a password provided by DigitalOcean.</p> <h2><a id="user-content-configure-your-domain-to-point-at-your-server" href="#configure-your-domain-to-point-at-your-server" name="configure-your-domain-to-point-at-your-server" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Configure your Domain to point at your Server</h2> <p>Once you have selected and registered your domain with a domain registrar - we are using Metaname - you can set up (usually through a web interface provided by the registrar)</p> <ul><li>an "A Record" which associates your website's name (your [Domain], e.g. milll.ws, or a subdomain of that domain, e.g. bbb.milll.ws, where 'bbb' is the subdomain part) to the IPv4 address of your server. You should just be able to enter your server's IPv4 address, the domain name (or sub-domain) you want to use for your server.</li> <li>if your registrar offers it it's also important to define an IPv6 record, which is called an "AAAA Record"... you put in the same domain name or subdomain and then your IPv6 address instead of your IPv4 one.</li> </ul><p>I've attached an screenshot of the Metaname interface for configuring these DNS zone records.</p> <p>You might be asked to set a "Time-to-live" (which has to do with the length of time Domain Name Servers are asked to "cache" the association that the A Record specifies) in which case you can put in 3600 seconds or an hour depending on the time units your interface requests... but in most cases that'll be set to a default of an hour automatically.</p> <p>Once your domain A and AAAA records are configured, you should be able to log into your server via <code>ssh root@[Domain]</code>, or, for example, <code>ssh root@bbb.milll.ws</code>. Although it should be instant, depending on your registrar, it might take as long as a few hours (even 24) for your domain name assignments to propogate through the DNS network and be available on your computer.</p> <h3><a id="user-content-best-practice-access-your-server-as-a-non-root-user" href="#best-practice-access-your-server-as-a-non-root-user" name="best-practice-access-your-server-as-a-non-root-user" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Best Practice: access your server as a non-root user</h3> <p>You should be able to test that your A and AAAA Records have been set correctly by logging into your server via SSH using your domain name rather than the IPv4 or IPv6 address you used previously. It should (after you accept the SSH warning that the server's name has a new name) work the same way your original SSH login did. On Linux, you'd SSH via a terminal and enter <code>ssh root@[domain name]</code>. I think you can do similar on MacOS and on Windows, I believe people typically use software called <a href="https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html">Putty</a>...</p> <p>But this will log you into your server as the 'root' user.</p> <p>It's not considered good practice to access your server as root (it's too easy to completely screw it up). Best practice is to create a separate 'non-root' user who has 'sudo' privileges and the ability to log in via SSH. If you are <em>currently logged in as 'root'</em>, you can create a normal user for yourself via (replace [username] with your chosen username):</p> <p><code>U=[username]</code><br /><code>adduser $U</code></p> <p>You will be required to set a password for the user here. Then you can add the new user to several relevant groups that confer the required administrative capabilities.</p> <p><code>adduser $U ssh</code><br /><code>adduser $U admin</code><br /><code>adduser $U sudo</code></p> <p>If you want to change the password for user [username] you can run:</p> <p><code>passwd $U</code></p> <p>then become that user temporarily (note, the root user can 'become' another user without needing to enter a password) and create an SSH key and, in the process, the <code>.ssh</code> directory (directories starting with a '.' are normally 'hidden') for the file into which to put your public SSH key:</p> <p><code>su $U</code><br /><code>ssh-keygen -t rsa -b 2048</code><br /><code>nano ~/.ssh/authorized_keys</code></p> <p>and in that file, copy and paste (without spaces on either end) your <em>current computer's</em> <strong>public</strong> ssh key (<em>never publish</em> your private key anywhere!), save and close the file.</p> <p>From that point, you should be able to SSH to your server via <code>ssh [username]@[domain name]</code> without needing to enter a password.</p> <p>These instructions use 'sudo' in front of commands because I assume you're using a non-root user. The instructions will still work fine even if you're logged in as 'root'.</p> <p>The rest of the tutorial can be run as your 'sudo-capable' non-root user.</p> <h2><a id="user-content-a-few-post-install-updates" href="#a-few-post-install-updates" name="a-few-post-install-updates" class="heading-permalink" aria-hidden="true" title="Permalink"></a>A few post-install updates</h2> <h3><a id="user-content-update-the-servers-hosts-file" href="#update-the-servers-hosts-file" name="update-the-servers-hosts-file" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Update the server's hosts file</h3> <p>This is required due to some quirks of the auto-detection of your domain name used by BigBlueButton - you'll need it later.</p> <p><code>sudo $EDIT /etc/hosts</code></p> <p>and add (or make sure it already has) the following:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>127.0.1.1 <span>[</span>First part of domain<span>]</span> 127.0.0.1 localhost <span>[</span>IPv4<span>]</span> <span>[</span>Domain<span>]</span> <span>[</span>IPv6<span>]</span> <span>[</span>Domain<span>]</span></pre></div></div> <p>For example - my test system has these details:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>127.0.1.1 bbbtest 127.0.0.1 localhost 143.110.228.88 bbbtest.milll.ws <span>2604</span>:a880:<span>4</span>:1d0::<span>402</span>:b000 bbbtest.milll.ws</pre></div></div> <p>Do an initial post-install update to the latest software versions:</p> <p><code>sudo apt-get update &amp;&amp; sudo apt-get dist-upgrade</code></p> <p>and add a couple useful apps that, for example, track changes to our system configuration for future reference (etckeeper) and allow us to do network troubleshooting (net-tools):</p> <p><code>sudo apt-get install -y etckeeper net-tools</code></p> <p>We also want to enable console logins in the event we have trouble using SSH:</p> <p><code>wget -qO- https://repos-droplet.digitalocean.com/install.sh | sudo bash</code></p> <h2><a id="user-content-firewall-configuration" href="#firewall-configuration" name="firewall-configuration" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Firewall Configuration</h2> <p>We need to configure firewall for admin access via SSH and access to internet for Docker containers</p> <p><code>sudo ufw allow OpenSSH</code></p> <p><code>sudo ufw allow in on docker0</code></p> <p><code>sudo ufw allow from 172.0.0.0/8 to any</code></p> <p>Then we need to adjust the default firewall policy:</p> <p><code>sudo $EDIT /etc/default/ufw</code></p> <p>You'll find this:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># Set the default forward policy to ACCEPT, DROP or REJECT. Please note that</span> <span># if you change this you will most likely want to adjust your rules</span> <span>DEFAULT_FORWARD_POLICY</span>=<span>"DROP"</span></pre></div></div> <p>which you need to change to</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># Set the default forward policy to ACCEPT, DROP or REJECT. Please note that</span> <span># if you change this you will most likely want to adjust your rules</span> <span>DEFAULT_FORWARD_POLICY</span>=<span>"ACCEPT"</span></pre></div></div> <p>Next you need to edit this file</p> <p><code>sudo $EDIT /etc/ufw/sysctl.conf</code></p> <p>And change (near the top of the file):</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># Uncomment this to allow this host to route packets between interfaces</span> <span>#net/ipv4/ip_forward=1</span> <span>#net/ipv6/conf/default/forwarding=1</span> <span>#net/ipv6/conf/all/forwarding=1</span></pre></div></div> <p>to</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># Uncomment this to allow this host to route packets between interfaces</span> net<span>/</span>ipv4<span>/</span><span>ip_forward</span>=<span>1</span> net<span>/</span>ipv6<span>/</span>conf<span>/</span>default<span>/</span><span>forwarding</span>=<span>1</span> net<span>/</span>ipv6<span>/</span>conf<span>/</span>all<span>/</span><span>forwarding</span>=<span>1</span></pre></div></div> <p>(removing the "#" uncomments those lines)</p> <p>Next, you have to restart the server's networking stack to apply the changes you've made:</p> <p><code>sudo systemctl restart systemd-networkd</code></p> <p>Luckily, this is generally instantaneous, so your connection to your server shouldn't be interrupted.</p> <p>Then you either start or (if it's already running for some reason) restart the firewall</p> <p><code>sudo ufw enable</code></p> <p>or</p> <p><code>sudo service ufw restart</code></p> <p>(running both won't do any harm)</p> <p>And we also update the firewall configuration to tell it to automatically start at boot time.</p> <p><code>sudo $EDIT /etc/ufw/ufw.conf</code></p> <p>Change <code>ENABLED=no</code> to <code>ENABLED=yes</code>.</p> <p>Next we need to set up some specialised rules for the "COTURN" functionality of the BigBlueButton system. <a href="https://coturn.github.io/">COTURN</a> is a FOSS application that provides both <a href="https://www.youtube.com/watch?v=4dLJmZOcWFc">TURN and STUN (video)</a> (<a href="https://blog.ivrpowers.com/post/technologies/what-is-stun-turn-server/">alternative non-video reference</a>) functionality which are core to the WebRTC protocol on which modern video conferencing applications are built, and are necessary for your users to connect their audio and video to a session, particularly if they're behind an institutional firewall.</p> <p><em>Note</em>: in some cases, <em>institutions will have implemented a rather over-the-top-paranoid 'default deny' for video conference hosts</em> which will block access to your BBB instance unless you can get them to add your site's domain name to their institutional whitelist. Sadly, there's no easy way around this.</p> <p><code>sudo ufw allow 7443/tcp</code></p> <p><code>sudo ufw allow 16384:32768/udp</code></p> <p><code>sudo ufw allow 3478/udp</code></p> <p><code>sudo ufw allow 5349/tcp</code></p> <p><code>sudo ufw allow from 10.7.7.0/24</code></p> <p>Ok, that's it for the firewall shenanigans.</p> <h2><a id="user-content-install-postfix-so-the-server-can-send-email" href="#install-postfix-so-the-server-can-send-email" name="install-postfix-so-the-server-can-send-email" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Install Postfix so the server can send email</h2> <p>It's generally a good idea to make sure any server you build can send email, and has a sensible default email address to send stuff to: stuff like administrative messages alerting you to failed tasks, or other system problems. We recommend that you designate an email address for whoever's responsible for this server. We've got a more <a href="/node/28">comprehsensive howto</a> for setting this up if you're wanting extra details.</p> <p>We achieve the outgoing mail functionality using the industrial strength Postfix SMTP server (it's widely used by ISPs for large-scale email services) along with a command line SMTP client that allows us to test our solution from the command line:</p> <p><code>sudo apt install postfix bsd-mailx</code></p> <p>During this installation, you'll be asked by the installer to make some decisions to do preliminary configuration of the Postfix installation. Select the default asnwers except as follows:</p> <ul><li>Select "Internet Site with Smarthost",</li> <li>fill in the [Domain] name you've chosen for your server,</li> <li>the domain name or IP address and port (in the form [SMTP server]:[port], examples might be smtp.oeru.org:465, or 10.11.143.22:465) of your "smarthost" who'll be doing the authenticating SMTP for you - note: <strong>our configuration works with port 465</strong>, and</li> <li>the email address to which you want to receive system-related messages, namely [Admin email].</li> </ul><p>Once that's installed, we need to set up our default email address for this server:</p> <p><code>sudo $EDIT /etc/aliases</code></p> <p>This file will containerd</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># See man 5 aliases for format</span> postmaster: root</pre></div></div> <p>update it to include an email address to send stuff intended for the system admin (aka 'root'):</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># See man 5 aliases for format</span> postmaster: root   root: <span>[</span>Admin email<span>]</span></pre></div></div> <p>after writing that, we have to convert that file into a form understood by Postix:</p> <p><code>sudo newaliases</code></p> <p>Next, we need to create a new file with the SMTP 'relay host' aka 'smart host' details:</p> <p><code>sudo $EDIT /etc/postfix/relay_password</code></p> <p>In it you need to put the following:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>[</span>SMTP Server<span>]</span> <span>[</span>SMTP username<span>]</span>:<span>[</span>SMTP password<span>]</span></pre></div></div> <p>Here's an example:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>about.oerfoundation.org demosmtp<span>@</span>milll.ws:7TLM6qGoqZXHfkDmkh6</pre></div></div> <p>Once you've set that up, we have to again prepare that file for use by Postfix:</p> <p><code>sudo postmap /etc/postfix/relay_password</code></p> <p>Finally, we need to update Postfix's main configuration to tell it to use our authenticating SMTP details:</p> <p><code>sudo $EDIT /etc/postfix/main.cf</code></p> <p>You'll need to make main.cf look like this - specifically commenting out <code>smtp_tls_security_level=may</code> by adding a '#' at the start of the line, and then adding the details below <code># added to configure accessing the relay host via authenticating SMTP</code>. You should also take this opportunity to confirm that your [Domain], [SMTP server] and [SMTP port] are set correctly.</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>smtpd_banner = <span>$myhostname</span> ESMTP <span>$mail_name</span> <span>(</span>Ubuntu<span>)</span> biff = no   <span># appending .domain is the MUA's job.</span> append_dot_mydomain = no   <span># Uncomment the next line to generate "delayed mail" warnings</span> <span>#delay_warning_time = 4h</span>   readme_directory = no   <span># See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on</span> <span># fresh installs.</span> compatibility_level = <span>2</span>   <span># TLS parameters</span> <span>smtpd_tls_cert_file</span>=<span>/</span>etc<span>/</span>ssl<span>/</span>certs<span>/</span>ssl-cert-snakeoil.pem <span>smtpd_tls_key_file</span>=<span>/</span>etc<span>/</span>ssl<span>/</span>private<span>/</span>ssl-cert-snakeoil.key <span>smtpd_tls_security_level</span>=may   <span>smtp_tls_CApath</span>=<span>/</span>etc<span>/</span>ssl<span>/</span>certs <span>## Commented out for SmartHost configuration</span> <span>#smtp_tls_security_level=may</span> smtp_tls_session_cache_database = btree:<span>${data_directory}</span><span>/</span>smtp_scache     smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination myhostname = <span>[</span>Domain<span>]</span> alias_maps = hash:<span>/</span>etc<span>/</span>aliases alias_database = hash:<span>/</span>etc<span>/</span>aliases myorigin = <span>/</span>etc<span>/</span>mailname mydestination = <span>$myhostname</span>, bbbtest.milll.ws, localhost.milll.ws, localhost relayhost = <span>[</span>SMTP server<span>]</span>:<span>465</span> mynetworks = 127.0.0.0<span>/</span><span>8</span> <span>[</span>::ffff:127.0.0.0<span>]</span><span>/</span><span>104</span> <span>[</span>::<span>1</span><span>]</span><span>/</span><span>128</span> mailbox_size_limit = <span>0</span> recipient_delimiter = + inet_interfaces = all inet_protocols = all   <span># added to configure accessing the relay host via authenticating SMTP</span> smtp_sasl_auth_enable = <span>yes</span> smtp_sasl_password_maps = hash:<span>/</span>etc<span>/</span>postfix<span>/</span>relay_password smtp_sasl_security_options = smtp_tls_security_level = encrypt   <span># add this if you're using Ubuntu 20.04, and comment out (with a "#") the</span> <span># earlier line smtp_tls_security_level = may to save errors in 'postfix check'</span> <span># and uncomment this line (by removing the #)</span> smtp_tls_wrappermode = <span>yes</span></pre></div></div> <p>Here's an example of what the final code could look like (don't use these exact values as they won't work for you):</p> <p>First, I commented this out:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>#</span><span>smtp_tls_security_level</span>=may</pre></div></div> <p>Made sure this was my [Domain]</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>myhostname = bbbtest.milll.ws</pre></div></div> <p>Made sure this had my [SMTP server] and [SMTP port] details:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>relayhost = about.oerfoundation.org:<span>465</span></pre></div></div> <p>And I added this at the end:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># added to configure accessing the relay host via authenticating SMTP</span> smtp_sasl_auth_enable = <span>yes</span> smtp_sasl_password_maps = hash:<span>/</span>etc<span>/</span>postfix<span>/</span>relay_password smtp_sasl_security_options = smtp_tls_security_level = encrypt   <span># add this if you're using Ubuntu 20.04, and comment out (with a "#") the</span> <span># earlier line smtp_tls_security_level = may to save errors in 'postfix check'</span> <span># and uncomment this line (by removing the #)</span> smtp_tls_wrappermode = <span>yes</span></pre></div></div> <p>After this, the Postfix configuration is done. We can check that we haven't got any typos via</p> <p><code>sudo postfix check</code></p> <p>If not, we can apply our configuration changes:</p> <p><code>sudo postfix reload</code></p> <p>and we can confirm it all worked correctly by checking the log file for Postfix:</p> <p><code>sudo less +G /var/log/mail.log</code></p> <p>Note: you might see a warning like this: <code>postfix/postfix-script: warning: symlink leaves directory: /etc/postfix/./makedefs.out</code> - it's spurious and you don't need to worry about it.</p> <h3><a id="user-content-email-test" href="#email-test" name="email-test" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Email test</h3> <p>Next we can test our outgoing SMTP by sending a test message via the command line - <em>use a test email that you can check!</em>:</p> <p><code>mail [Test email]</code></p> <p>for example:</p> <p><code>mail demo@oerfoundation.org</code></p> <p>After you hit Enter, you'll be shown</p> <p><code>Subject:</code> {Enter an email subject, e.g. "Test email from [Domain]"}</p> <p>After that, hit Enter, and you'll be shown a blank line. On that line:</p> <p>{Enter your email text, e.g. "This is just a test."}</p> <p>Then, to finish your email, type <em>CTRL-D</em> which will show you another line</p> <p><code>CC:</code> {Enter any CC email addresses, e.g. <a href="mailto:mybackupemail@anotherprovider.tld">mybackupemail@anotherprovider.tld</a> }</p> <p>After you hit Enter, your email should be sent if all your configuration options are accepted by your remote host (the SMTP server at the address [SMTP server] with all the other details you entered).</p> <p>You can check if it worked by looking at the Postfix log again:</p> <p><code>sudo less +G /var/log/mail.log</code></p> <p>If it sent the email, you'll see something like (but with different IP addresses, serial numbers, and addresses) - the key bit is the <code>status=sent</code> bit and the <code>250</code> code from the SMTP server:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>08FBD2B501C: <span>to</span>=<span>&lt;</span>demo<span>@</span>oerfoundation.org<span>&gt;</span>, <span>relay</span>=dovecot<span>[</span>172.22.1.250<span>]</span>:<span>24</span>, <span>delay</span>=<span>1.3</span>, <span>delays</span>=<span>1.3</span><span>/</span><span>0.02</span><span>/</span><span>0</span><span>/</span><span>0.04</span>, <span>dsn</span>=2.0.0, <span>status</span>=sent <span>(</span><span>250</span> 2.0.0 <span>&lt;</span>demo<span>@</span>oerfoundation.org<span>&gt;</span> MMokCa2TpWEffAAAPgmdMA Saved<span>)</span></pre></div></div> <p>You can also check for receipt of email and verify receipt (note, if you don't get it quickily, check your email spam folder).</p> <h2><a id="user-content-install-nginx-webserver-and-lets-encrypt-tools" href="#install-nginx-webserver-and-lets-encrypt-tools" name="install-nginx-webserver-and-lets-encrypt-tools" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Install Nginx webserver and Let's Encrypt tools</h2> <p><code>sudo apt install -y nginx-full ca-certificates letsencrypt ssl-cert</code></p> <p>You need to tell your firewall to open the ports that the Nginx webserver will use:</p> <p><code>sudo ufw allow "Nginx Full"</code></p> <p>And then we need to create a special configuration for Let's Encrypt and then an identify verification directory:</p> <p><code>sudo mkdir /etc/nginx/includes</code></p> <p><code>sudo mkdir /var/www/letsencrypt</code></p> <p>To create the specific configuration, we create this file:</p> <p><code>sudo $EDIT /etc/nginx/includes/letsencrypt.conf</code></p> <p>And fill it with this information:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># Rule for legitimate ACME Challenge requests</span> location ^~ <span>/</span>.well-known<span>/</span>acme-challenge<span>/</span> <span>{</span> default_type <span>"text/plain"</span>; <span># this can be any directory, but this name keeps it clear</span> root <span>/</span>var<span>/</span>www<span>/</span>letsencrypt; <span>}</span>   <span># Hide /acme-challenge subdirectory and return 404 on all requests.</span> <span># It is somewhat more secure than letting Nginx return 403.</span> <span># Ending slash is important!</span> location = <span>/</span>.well-known<span>/</span>acme-challenge<span>/</span> <span>{</span> <span>return</span> <span>404</span>; <span>}</span></pre></div></div> <p>We'll reference this file in our Nginx configuration file for the reverse proxy functionality.</p> <h2><a id="user-content-set-up-coturn-certificates" href="#set-up-coturn-certificates" name="set-up-coturn-certificates" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Set up COTURN certificates</h2> <p>In preparation for creating our Let's Encrypt SSL certificate for our [Domain], we're going to set up a 'hook' which is triggered when our SSL certificate is created, and it copies our current certificate to a place where the COTURN server can find it.</p> <p><code>sudo $EDIT /etc/letsencrypt/renewal-hooks/deploy/coturn.sh</code></p> <p>Copy and past the following into your file (<strong>replacing [Domain] with your domain name</strong>):</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>#!/bin/bash</span>   <span>DOMAIN</span>=<span>[</span>Domain<span>]</span> <span>DEST</span>=<span>/</span>etc<span>/</span>coturn-ssl   <span>#if [[ 1 == 1 ]]; then</span> <span>if</span> <span>[</span><span>[</span> <span>$RENEWED_DOMAINS</span> == <span>*</span><span>"<span>$DOMAIN</span>"</span><span>*</span> <span>]</span><span>]</span>; <span>then</span> <span>cp</span> <span>-L</span> <span>/</span>etc<span>/</span>letsencrypt<span>/</span>live<span>/</span><span>${DOMAIN}</span><span>/</span>fullchain.pem <span>$DEST</span> <span>cp</span> <span>-L</span> <span>/</span>etc<span>/</span>letsencrypt<span>/</span>live<span>/</span><span>${DOMAIN}</span><span>/</span>privkey.pem <span>$DEST</span> <span>echo</span> <span>"updated <span>$DOMAIN</span> certificates in <span>$DEST</span>"</span> <span>fi</span></pre></div></div> <p>Next, make that script executable:</p> <p><code>sudo chmod a+x /etc/letsencrypt/renewal-hooks/deploy/coturn.sh</code></p> <p>so that it is run automatically anytime the relevant certificate is created or renewed.</p> <p>After that, we have to create a place for the COTURN-specific certificates to go:</p> <p><code>sudo mkdir /etc/coturn-ssl</code></p> <h2><a id="user-content-set-up-nginx-reverse-proxy-configuration" href="#set-up-nginx-reverse-proxy-configuration" name="set-up-nginx-reverse-proxy-configuration" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Set up Nginx Reverse Proxy Configuration</h2> <p>Next we have to set up our Nginx webserver on our virtual server, which will be the Docker container host for our BigBlueButton system. The reverse proxy configuration will allow the virtual server to receive web requests from BBB users and pass them securely through to the right Docker container.</p> <p>Create a suitable configuration file - at the OERF we use the convention of calling our configuration</p> <p><code>sudo $EDIT /etc/nginx/sites-available/$SITE</code></p> <p>In this file, you'll past in the following, replacing [Domain] as appropriate - now might be a good idea to try out search and replace in your text editor!</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>server <span>{</span> listen <span>80</span>; <span># if your host doens't support IPv6, comment out the following line</span> listen <span>[</span>::<span>]</span>:<span>80</span>; server_name <span>[</span>Domain<span>]</span>;   access_log <span>/</span>var<span>/</span>log<span>/</span>nginx<span>/</span><span>[</span>Domain<span>]</span>.access.log; error_log <span>/</span>var<span>/</span>log<span>/</span>nginx<span>/</span><span>[</span>Domain<span>]</span>.error.log;   include includes<span>/</span>letsencrypt.conf;   <span># redirect all HTTP traffic to HTTPS.</span> location <span>/</span> <span>{</span> <span>return</span> <span>302</span> https:<span>//</span><span>$server_name</span><span>$request_uri</span>; <span>}</span> <span>}</span>   server <span>{</span> listen <span>443</span> ssl http2 default_server; <span># if your host doens't support IPv6, comment out the following line</span> listen <span>[</span>::<span>]</span>:<span>443</span> ssl http2 default_server; server_name <span>[</span>Domain<span>]</span>;   <span># We comment these out *after* we have successfully geneated our Let's Encrypt certificate for [Domain].</span> ssl_certificate <span>/</span>etc<span>/</span>ssl<span>/</span>certs<span>/</span>ssl-cert-snakeoil.pem; ssl_certificate_key <span>/</span>etc<span>/</span>ssl<span>/</span>private<span>/</span>ssl-cert-snakeoil.key; <span># we start with these commented out until after we can generate our Let's Encrypt certificate for [Domain]!</span> <span>#ssl_certificate /etc/letsencrypt/live/[Domain]/fullchain.pem;</span> <span>#ssl_certificate_key /etc/letsencrypt/live/[Domain]/privkey.pem;</span> ssl_dhparam <span>/</span>etc<span>/</span>ssl<span>/</span>certs<span>/</span>dhparam.pem;   access_log <span>/</span>var<span>/</span>log<span>/</span>nginx<span>/</span><span>[</span>Domain<span>]</span>.access.log; error_log <span>/</span>var<span>/</span>log<span>/</span>nginx<span>/</span><span>[</span>Domain<span>]</span>.error.log;   location <span>/</span> <span>{</span> proxy_http_version <span>1.1</span>; <span># for BBB 2.4, the port used is 48087. For earlier versions of BBB, it's 8080</span> proxy_pass http:<span>//</span>127.0.0.1:<span>48087</span>; proxy_set_header Host <span>$host</span>; proxy_set_header X-Real-IP <span>$remote_addr</span>; proxy_set_header X-Forwarded-For <span>$proxy_add_x_forwarded_for</span>; proxy_set_header X-Forwarded-Proto <span>$scheme</span>; proxy_set_header Upgrade <span>$http_upgrade</span>; <span>#proxy_set_header Connection $connection_upgrade;</span> proxy_set_header Connection <span>"Upgrade"</span>; proxy_cache_bypass <span>$http_upgrade</span>; <span>}</span> <span>}</span></pre></div></div> <p><em><strong>Update 2021-12-30</strong></em> With the release of BBB 2.4 a few weeks ago, the Docker configuration now has one 'breaking' change: the port has been changed to 48087! So, if you are installing an older version of BBB, the previous default was port 8080. If you get a 502 error after setting up your containers, check to make sure you haven't been bitten by this little detail!</p> <p>Right - back to the process...</p> <p>After creating it, we have to 'enable' the configuration:</p> <p><code>sudo ln -sf /etc/nginx/sites-available/$SITE /etc/nginx/sites-enabled/</code></p> <p>It's also not a bad idea to disable the Nginx default configuration, as it can sometimes interfere with things:</p> <p><code>sudo rm /etc/nginx/sites-enabled/default</code></p> <h3><a id="user-content-extra-security-with-dhparampem" href="#extra-security-with-dhparampem" name="extra-security-with-dhparampem" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Extra security with dhparam.pem</h3> <p>We're going to create a 'dhparam' certificate for your configuration. These take a long time to generate - between 10-30 minutes, depending not on the speed of your computer, but on the rate at which it creates 'random events' that allow it to create a suitablely complex random prime number.</p> <p><code>sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048</code></p> <p>If your time isn't limited, you can increase the size of your dhparam from 2048 to 4096 - it'll take quite a lot longer to create.</p> <p>Ok - now that we've created our dhparam.pem, referenced in our Nginx configuration for [Domain], we should have everything in place. We can now test Nginx's configuration:</p> <p><code>sudo nginx -t</code></p> <p>If you don't get any errors or warnings, you can activate the new configuration:</p> <p><code>sudo service nginx reload</code></p> <p>You server should now successfully respond to your [Domain], but it'll redirect to HTTPS using the default (self-signed) certificates that are valid as far as Nginx is concerned, but your browser won't let you access the page due to those inappropriate certificates. You can test it by putting <code>http://[Domain]</code> into your browser and seeing if it redirects you to <code>https://[Domain]</code> and a 'bad certificate` (or similar) page.</p> <p>To fix that, we can now generate a Let's Encrypt certificate which <em>will</em> be valid.</p> <p><code>sudo letsencrypt certonly --webroot -w /var/www/letsencrypt -d ${SITE}</code></p> <p>Since this is your first time running the letsencrypt script, you'll be asked for a contact email (so the Let's Encrypt system can warn you if your certificates are going to be expiring soon!) - use your [Admin email] for this. You can also opt in to allowing them to get anonymous statistics from your site.</p> <p>Once you've done that, the Let's Encrypt system will verify that you (the person requesting the certificate) also controls the server that's requesting it (using the details specified in the <code>/etc/nginx/includes/letsencrypt.conf</code> file, along with data that the letsencrypt script writes into a special file in the <code>/var/www/letsencrypt</code> directory) and, all going well, you'll see a "Congratulations!" message telling you that you have new certificates for your [Domain].</p> <p>Now is a good time to check if your renew-hook worked properly - there should now be two files in <code>/etc/coturn-ssl</code>, namely <code>fullchain.pem</code> and <code>privkey.pem</code>. If that's not the case, something might have gone wrong.</p> <p>Then you can re-edit your Nginx confguration file:</p> <p><code>sudo $EDIT /etc/nginx/sites-available/$SITE</code></p> <p>and comment out the default certificates and uncomment the [Domain]-specific certificates, like this (we'll assume you've already got your [Domain] substituted!):</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>  ...   <span># We comment these out *after* we have successfully geneated our Let's Encrypt certificate for [Domain].</span> <span>#ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;</span> <span>#ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; </span> <span># we start with these commented out until after we can generate our Let's Encrypt certificate for [Domain]!</span> ssl_certificate <span>/</span>etc<span>/</span>letsencrypt<span>/</span>live<span>/</span><span>[</span>Domain<span>]</span><span>/</span>fullchain.pem; ssl_certificate_key <span>/</span>etc<span>/</span>letsencrypt<span>/</span>live<span>/</span><span>[</span>Domain<span>]</span><span>/</span>privkey.pem; ssl_dhparam <span>/</span>etc<span>/</span>ssl<span>/</span>certs<span>/</span>dhparam.pem;   ...  </pre></div></div> <p>Once you've got that going, you again check to make sure your Nginx config is valid:</p> <p><code>sudo nginx -t</code></p> <p>and apply your configruation changes:</p> <p><code>sudo service nginx reload</code></p> <p>If you now go to <code>http://[Domain]</code> in your browser, you should be redirected to <code>https://[Domain]</code> and you shouldn't get any <em>certificate</em> errors, although you might get a "502" error because the service that reverse proxy configuration is trying to send you to doesn't yet exist! That's what we're expecting.</p> <p>One more thing - we need to make sure that the <code>coturn.sh</code> renewal hook script has run. We can check to make sure that there are two files, <code>fullchain.pem</code> and <code>privkey.pem</code> in the <code>/etc/coturn-ssl</code> directory. If we start the Docker containers before these files exist, the Docker daemon creates them <em>as directories</em> by default which can lead to all sorts of trickiness. Run</p> <p><code>sudo ls -l /etc/coturn-ssl/</code></p> <p>and make sure you get a result like this:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>-rw-r--r--</span> <span>1</span> root root <span>5588</span> Dec <span>13</span> 08:<span>42</span> fullchain.pem <span>-rw-------</span> <span>1</span> root root <span>1704</span> Dec <span>13</span> 08:<span>42</span> privkey.pem</pre></div></div> <h2><a id="user-content-install-docker" href="#install-docker" name="install-docker" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Install Docker</h2> <p>Now we have to set up the Docker container support by first registering a new package source...</p> <p><code>sudo apt-get install -y apt-transport-https curl gnupg lsb-release</code></p> <p><code>sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg</code></p> <p><code>sudo echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</code></p> <p>updating our list of available packages to include the Docker-related packages...</p> <p><code>sudo apt-get update</code></p> <p>and then install them:</p> <p><code>sudo apt-get install -y docker-ce docker-ce-cli containerd.io</code></p> <p>Which installs a bunch of dependencies as well.</p> <p>To make sure that our non-root user can talk to the Docker server, we need to jump through a couple more hoops:</p> <p><code>sudo addgroup docker</code></p> <p><code>sudo adduser dave docker</code></p> <p>After doing this, the easiest thing is to log out and log back in again to make sure my user can access the new permissions. To confirm that my user has docker group privileges, I can run</p> <p><code>id</code></p> <p>which should give a result like</p> <p><code>uid=1000(dave) gid=1000(dave) groups=1000(dave),4(adm),27(sudo),1001(docker)</code></p> <p>with the latter being the confirmation.</p> <h2><a id="user-content-install-docker-compose" href="#install-docker-compose" name="install-docker-compose" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Install Docker-compose</h2> <p>Now we're almost to the BigBlueButton part - we just need to install the Docker Compose framework:</p> <p><code>sudo apt install python3-pip</code></p> <p><code>sudo pip install -U pip</code></p> <p><code>sudo pip install -U docker-compose</code></p> <p>Once you've done that, you should be able to run <code>docker-compose</code> at your command prompt and it should give you the docker-compose help page. If so, great work! Almost there.</p> <h2><a id="user-content-git-clone-bbb-docker-repository" href="#git-clone-bbb-docker-repository" name="git-clone-bbb-docker-repository" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Git-Clone BBB Docker repository</h2> <p>Now it's time to install the set of Docker containers that make up the BigBlueButton stack of coordinated services.</p> <p>We make a place for the docker configuration and data to live:</p> <p><code>sudo mkdir -p /home/docker/</code></p> <p>go into it</p> <p><code>cd /home/docker</code></p> <p>and then we use the magic of 'git' (if you don't know it, this is one of the single most powerful tools programmers use - everyone would benefit from knowing how 'version control' works - git is by far the most widely used version control aka 'source code control' system in the world. It's open source.) to 'clone' the BigBlueButton developers' Docker definitions code:</p> <p><code>sudo git clone -b main --recurse-submodules https://github.com/bigbluebutton/docker.git bbb-docker</code></p> <p>That command puts all the code into a directory called <code>bbb-docker</code> so let's go there:</p> <p><code>cd bbb-docker</code></p> <p>and in there, we run this command to gram a second layer of code that is referenced by the first layer we already downloaded in the previous step:</p> <p><code>sudo git submodule update --init</code></p> <p>Finally, we run this handy script provided by the BBB developers to set up a working Docker Compose configuration:</p> <p><code>sudo ./scripts/setup</code></p> <p>This will script will ask you some questions about your system. These are the questions <em>and</em> the answers we'll use - note, where I've written [IPv4] and [IPv6] below, you should see the actual IPv4 and IPv6 addresses for your server:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>Should greenlight be included? <span>(</span>y<span>/</span>n<span>)</span>: y Should an automatic HTTPS Proxy be included? <span>(</span>y<span>/</span>n<span>)</span>: n Should a coturn be included? <span>(</span>y<span>/</span>n<span>)</span>: y Coturn needs TLS to <span>function</span> properly. Since automatic HTTPS Proxy is disabled, you must provide a relative or absolute path to your certificates. Please enter path to cert.pem: <span>/</span>etc<span>/</span>coturn-ssl<span>/</span>fullchain.pem Please enter path to key.pem: <span>/</span>etc<span>/</span>coturn-ssl<span>/</span>privkey.pem Should a Prometheus exporter be included? <span>(</span>y<span>/</span>n<span>)</span>: y Please enter the domain name: bbbtest.milll.ws Should the recording feature be included? IMPORTANT: this is currently a big privacy issues, because it will record everything <span>which</span> happens <span>in</span> the conference, even when the button suggests, that it does not. <span>make</span> sure that you always get people<span>'s consent, before they join a room! https://github.com/bigbluebutton/bigbluebutton/issues/9202 Choice (y/n): y Is [IPv4] your external IPv4 address? (y/n): y Is [IPv6] your external IPv6 address? (y/n): y</span></pre></div></div> <p>Once you finish answering these questions, the script creates a file called <code>.env</code> (the leading '.' means it's a 'hidden' file that won't show up in normal directory listings - you have to know it's there, as it holds important system values and shouldn't be deleted.</p> <h2><a id="user-content-configure-your-bbb" href="#configure-your-bbb" name="configure-your-bbb" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Configure your BBB</h2> <p>But now, we're going to tweak it, because it holds all the important customised values we need to configure our BigBlueButton service.</p> <p><code>sudo $EDIT .env</code></p> <p>When you're editing the file, scroll down through it and adjust the values you find as follows.</p> <p>Uncomment this one</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>ALLOW_MAIL_NOTIFICATIONS</span>=<span>true</span></pre></div></div> <p>Set the following</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>SMTP_SERVER</span>=<span>[</span>SMTP server<span>]</span> <span>SMTP_PORT</span>=<span>[</span>SMTP port<span>]</span> <span>SMTP_DOMAIN</span>=<span>[</span>Domain<span>]</span> <span>SMTP_USERNAME</span>=<span>[</span>SMTP username<span>]</span> <span>SMTP_PASSWORD</span>=<span>[</span>SMTP password<span>]</span> <span>SMTP_AUTH</span>=plain <span>SMTP_STARTTLS_AUTO</span>=<span>true</span> <span>#</span> <span>SMTP_SENDER</span>=<span>[</span>Outgoing email<span>]</span></pre></div></div> <p>and finally set</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span>DEFAULT_REGISTRATION</span>=invite</pre></div></div> <p>This last one makes it possible for you to invite external people to make use of your BigBlueButton instance - they can create a log in and create their own rooms over which they'll have some control. These can be people in your organisation or community for whom you want your BBB to be available as a resource.</p> <h3><a id="user-content-fix-minor-configuration-error-that-blocks-jodconverter" href="#fix-minor-configuration-error-that-blocks-jodconverter" name="fix-minor-configuration-error-that-blocks-jodconverter" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Fix minor configuration error that blocks JODConverter</h3> <p>One of the containers used in the BBB stack is called <a href="https://github.com/bigbluebutton/docker/tree/develop/mod/jodconverter">JODConverter</a> which, in standard Free and Open Source Software tradition, makes use of an <a href="https://github.com/EugenMayer/docker-image-jodconverter">'upstream' development</a> by another developer, <a href="https://github.com/EugenMayer">EugenMayer</a>, which in turn makes use of an <a href="https://hub.docker.com/r/bellsoft/liberica-openjdk-debian">upstream development</a>...</p> <p>The issue is that a recent update (in early December 2021) has broken the process of launching this container. This is a big problem because the JODConverter provides the services of converting uploaded presentations and downloaded documents (like BBB's Public Chat or Shared Notes) in various useful file formats.</p> <p>Turns out the fix is easy. While still in <code>bbb-docker</code>, Just run</p> <p><code>sudo $EDIT mod/jodconverter/Dockerfile</code></p> <p>and add the following line to the very bottom of the file:</p> <p><code>CMD ["--spring.config.additional-location=optional:/etc/app/"]</code></p> <p>Save it, and you're done. I have <a href="https://github.com/bigbluebutton/docker/issues/178">submitted an issue</a> with the fix I've found to the <code>bigbluebutton/docker</code> project.</p> <p>Once that configuration is done, it's finally time to...</p> <h2><a id="user-content-build-your-bbb" href="#build-your-bbb" name="build-your-bbb" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Build your BBB</h2> <p>The first time it's run, this command will trigger the building of a set of no less than 22 separate Docker containers, each running its own crucial service as part of the BigBlueButton stack.</p> <p><code>sudo docker-compose up -d</code></p> <p><em>Note: this process can take a LONG time, like an hour or more</em> depending on your server's internet connection speed.</p> <p>If you want to see how long it takes the first time, run this instead:</p> <p><code>sudo time docker-compose up -d</code></p> <p>which will give you a readout of the time the command takes to complete.</p> <h2><a id="user-content-visit-your-new-bbb" href="#visit-your-new-bbb" name="visit-your-new-bbb" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Visit your new BBB</h2> <p>Once you next see the command prompt ($ or #), it means that your BBB system is starting up. You can see the status of the containers by running</p> <p><code>sudo docker-compose ps</code></p> <p>which should give you something that looks like this, once everything is running (it might take a few minutes!):</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre> Name Command State Ports <span>-----------------------------------------------------------------------------------------------------------</span> bbb-docker_apps-akka_1 <span>/</span>bin<span>/</span><span>sh</span> <span>-c</span> dockerize - ... Up bbb-docker_bbb-web_1 <span>/</span>entrypoint.sh Up <span>(</span>healthy<span>)</span> bbb-docker_coturn_1 docker-entrypoint.sh <span>--ext</span> ... Up bbb-docker_etherpad_1 <span>/</span>entrypoint.sh Up <span>9001</span><span>/</span>tcp bbb-docker_freeswitch_1 <span>/</span>bin<span>/</span><span>sh</span> <span>-c</span> <span>/</span>entrypoint.sh Up bbb-docker_fsesl-akka_1 <span>/</span>bin<span>/</span><span>sh</span> <span>-c</span> dockerize - ... Up bbb-docker_greenlight_1 bin<span>/</span>start Up 10.7.7.1:<span>5000</span>-<span>&gt;</span><span>80</span><span>/</span>tcp bbb-docker_html5-backend-<span>1</span>_1 <span>/</span>entrypoint.sh Up bbb-docker_html5-backend-<span>2</span>_1 <span>/</span>entrypoint.sh Up bbb-docker_html5-frontend-<span>1</span>_1 <span>/</span>entrypoint.sh Up bbb-docker_html5-frontend-<span>2</span>_1 <span>/</span>entrypoint.sh Up bbb-docker_jodconverter_1 <span>/</span>docker-entrypoint.sh <span>--sp</span> ... Up bbb-docker_kurento_1 <span>/</span>entrypoint.sh Up <span>(</span>healthy<span>)</span> bbb-docker_mongodb_1 docker-entrypoint.sh mongo ... Up <span>(</span>healthy<span>)</span> <span>27017</span><span>/</span>tcp bbb-docker_nginx_1 <span>/</span>docker-entrypoint.sh ngin ... Up bbb-docker_periodic_1 <span>/</span>entrypoint.sh Up bbb-docker_postgres_1 docker-entrypoint.sh postgres Up <span>(</span>healthy<span>)</span> <span>5432</span><span>/</span>tcp bbb-docker_prometheus-exporter_1 python server.py Up <span>9688</span><span>/</span>tcp bbb-docker_recordings_1 <span>/</span>bin<span>/</span><span>sh</span> <span>-c</span> <span>/</span>entrypoint.sh Up bbb-docker_redis_1 docker-entrypoint.sh redis ... Up <span>(</span>healthy<span>)</span> <span>6379</span><span>/</span>tcp bbb-docker_webhooks_1 <span>/</span>bin<span>/</span><span>sh</span> <span>-c</span> <span>/</span>entrypoint.sh Up bbb-docker_webrtc-sfu_1 .<span>/</span>docker-entrypoint.sh npm ... Up 127.0.0.1:<span>3008</span>-<span>&gt;</span><span>3008</span><span>/</span>tcp</pre></div></div> <p>At that point, you can visit <code>https://[Domain]</code> and instead of a 502 error, you should see the BigBlueButton 'Greenlight' front page.</p> <p>Now the final step...</p> <h2><a id="user-content-create-an-admin-user" href="#create-an-admin-user" name="create-an-admin-user" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Create an admin user:</h2> <p>To create an admin user, you run this:</p> <p><code>sudo docker-compose exec greenlight bundle exec rake admin:create</code></p> <p>It will give you a login email address and a randomly generated password. Use these to log in. In the profile (top right Admin menu dropdown), you can alter the admin user's email to a real email (perhaps [Admin email]?), and change the password if you like.</p> <p>Then you can go back to the Admin menu and select "Organisation" which should put you on the Manage Users page. You then invite yourself to join as a user by sending yourself an email (which, if your SMTP settings are correct, will work). If you don't receive it in a minute or two, check your spam folder.</p> <p>Either log out of Greenlight before clicking the link in the email or open the link (by copying and pasting it) in a different browser where you're not logged in to this Greenlight instance, and create a user account for yourself.</p> <p>Then log back into Greenlight as the Admin user (if you've previously logged out) and refresh the "Manage Users" page. You should find your newly created user. Select 'Edit' from the vertical 3 dotted menu. On the "Update your Account Info" page, set the User Role for your user to 'Admin" and click the "Update" button. You should now be able to log out as Admin, and back in as your own user, but with administrative privileges.</p> <h2><a id="user-content-run-your-first-conference" href="#run-your-first-conference" name="run-your-first-conference" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Run your first conference</h2> <p>Now it's time to try running a video conference. Click on the "Home" link (top right menu) and you should see that you have a default room called "Home Room" with a URL specified in the form [Domain]/b/[first 3 letters of your name]-[3 random hex digits]-[3 more random hex digits]. For me, it might look something like this: https://[Domain]/b/dav-a5b-73z</p> <p>Click "Start" to initiate a conference in that Home Room.</p> <p>In the conference room, you should select 'Microphone' so you can test speaking and listening, and also test your video camera (assming you have access to both on the computing device you're using to access BBB - note, a modern cellphone normally offers all of these.</p> <p><em>We always encourage people participating in video conferences (regardless of the technology) to employ headphones to separate the audio from the conference form their input. It greatly improves the audio quality for all involved!</em>.</p> <p>If you select 'Microphone', there will be a brief delay whil your browser negotiates with the COTURN server in your BBB stack to link your data and audio channels. Once it's done that, it will give you an "echo test" window (see included screen shots). You should speak into your microphone when you see that screen and check whether you can hear yourself. This will confirm that your audio settings are right (or not) and will help the BBB system adjust its echo cancellation algorithms.</p> <p>You can then also clidk the 'camera' icon (bottom middle of the page) to activate your webcam if you have one (or choose one if you have multiple cameras) as well as the video quality.</p> <p>You can also try to 'Start recording' if you want to test your ability to record a session. Note that there is a delay after the end of a session in which you've recorded before the recording is displayed on the 'room' page in Greenlight. It might take minutes or even hourse to generate depending on the power of your server and the length of the session.</p> <p>To see other administrative functionality including moderation and breakout rooms, click on the "gear" icon next to the "Users" heading in the left hand column. You can also experiment with the "Public Chat" and the "Shared Notes". Both can be saved at any time (via the top right 3 dot menu in that section) and will be included in any recordings you make.</p> <p><em>Note</em> the contents of Public Chat and Shared Notes will be wiped at the end of a session unless you explicitly save them or record the session (at least briefly at the end).</p> <p>You can provide that "room address" to anyone and they can join your room (via an modern browser) when you have a session running. Alternatively, you can click on the 3 dotted menu associated with your room below the 'Search for room" form, and change the default properties of your room, including configuring it to let anyone who knows the room's address start a session. You can also create additional rooms (as an Admin user, you can set the limits on many of these properties via Organisation in the top right menu under your user name.</p> <p>Have fun with your new, world class, cost-effective, large-scale BigBlueButton video conferencing application!</p> <h2><a id="user-content-next-steps" href="#next-steps" name="next-steps" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Next Steps</h2> <p>In a future tutorial, we'll provide information on how to troubleshoot BBB issues, how to upgrade it as new versions are made available by developers, and how to ensure that your recordings, user database, and configuration are backed up incrementally, encrypted, in remote storage.</p></div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <h2 class="comment-field__title">Blog comments</h2> <article data-comment-user-id="0" id="comment-826" class="comment js-comment by-anonymous has-title clearfix"> <div class="comment__container"> <h3 class="comment__title"> <a href="/comment/826#comment-826" class="permalink" rel="bookmark" hreflang="en">It says in the nginx-config…</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1641281859"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><span>LPup (not verified)</span></span> <span class="comment__pubdate">Tue 04/01/2022 - 20:32</span> </div> </div> <div class="comment__content"> <div class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div class="field__item"><p>It says in the nginx-config to change the port for the proxy and not forget to change it in the .env file. Where do I need to put what into the .env file for using a different port than 8080?</p> </div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=826&amp;1=default&amp;2=en&amp;3=" token="ab5iIZGMF89MSnCPgbrcuI7yHqfoKEyjDb9VtJ299fg"></drupal-render-placeholder> </div> </div> </article> <div class="indented"><article data-comment-user-id="1" id="comment-827" class="comment js-comment by-node-author has-title clearfix"> <div class="comment__container"> <h3 class="comment__title"> <a href="/comment/827#comment-827" class="permalink" rel="bookmark" hreflang="en">Hmm - thanks for pointing…</a> <span class="comment__new marker marker--success hidden" data-comment-timestamp="1641282307"></span> </h3> <div class="comment__meta"> <div class="comment__submitted"> <span class="comment__author"><a title="View user profile." href="/user/1" class="username">dave</a></span> <span class="comment__pubdate">Tue 04/01/2022 - 20:44</span> </div> </div> <div class="comment__content"> <p class="comment__parent visually-hidden">In reply to <a href="/comment/826#comment-826" class="permalink" rel="bookmark" hreflang="en">It says in the nginx-config…</a> by <span>LPup (not verified)</span></p> <div class="clearfix text-formatted field field-comment--comment-body field-name-comment-body field-type-text-long field-label-hidden"> <div class="field__items"> <div class="field__item"><p>Hmm - thanks for pointing that out. Yes, I think I made an error with that instruction (I've updated the Nginx config). If you implement BBB today via this set of instructions, you'll be installing BBB 2.4 - port 8080 was used in 2.3 and earlier versions. For 2.4, use port 48087. To be honest, I'm not sure of the right way to alter it if you have to due to other services running on the same system. Writing it manually in the docker-compose.yml file will be overwritten the next time you do an update... It might be necessary to change the port specified in the relevant container's Dockerfile under <code>mods</code>...</p></div> </div> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=827&amp;1=default&amp;2=en&amp;3=" token="IQwvVUQ-NlVwsrO5mMYIbQJJDVotfViSFCA7rOx4_Aw"></drupal-render-placeholder> </div> </div> </article> </div> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=46&amp;2=field_blog_comments&amp;3=comment" token="bVuNCPDsukUifzhtwLxvL6UanvQCaG2zyJeoiuzQckE"></drupal-render-placeholder> </div> </section> Wed, 10 Nov 2021 02:42:41 +0000 dave 46 at http://tech.oeru.org Configuring a Linux server to send email via the Postfix SMTP server using an external authenticating SMTP host http://tech.oeru.org/configuring-linux-server-send-email-postfix-smtp-server-using-external-authenticating-smtp-host <span class="field field--name-title field--type-string field--label-hidden">Configuring a Linux server to send email via the Postfix SMTP server using an external authenticating SMTP host</span> <div class="field field-node--field-blog-tags field-name-field-blog-tags field-type-entity-reference field-label-above"> <h3 class="field__label">Blog tags</h3> <div class="field__items"> <div class="field__item field__item--ubuntu-linux"> <span class="field__item-wrapper"><a href="/taxonomy/term/12" hreflang="en">ubuntu linux</a></span> </div> <div class="field__item field__item--postfix"> <span class="field__item-wrapper"><a href="/taxonomy/term/66" hreflang="en">postfix</a></span> </div> <div class="field__item field__item--smtp"> <span class="field__item-wrapper"><a href="/taxonomy/term/67" hreflang="en">smtp</a></span> </div> <div class="field__item field__item--_804"> <span class="field__item-wrapper"><a href="/taxonomy/term/68" hreflang="en">18.04</a></span> </div> <div class="field__item field__item--free--open-source"> <span class="field__item-wrapper"><a href="/taxonomy/term/6" hreflang="en">free &amp; open source</a></span> </div> <div class="field__item field__item--foss"> <span class="field__item-wrapper"><a href="/taxonomy/term/10" hreflang="en">foss</a></span> </div> <div class="field__item field__item--_004"> <span class="field__item-wrapper"><a href="/taxonomy/term/75" hreflang="en">20.04</a></span> </div> <div class="field__item field__item--_204"> <span class="field__item-wrapper"><a href="/taxonomy/term/85" hreflang="en">22.04</a></span> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/1" class="username">dave</a></span> <span class="field field--name-created field--type-created field--label-hidden">Mon 02/08/2021 - 14:08</span> <div class="clearfix text-formatted field field-node--body field-name-body field-type-text-with-summary field-label-hidden"> <div class="field__items"> <div class="field__item"><p>Just about any and every server needs to be able to send email - whether it's end-user-email, like password recovery services for a website to emails to system administrators reporting on the status of system backups and errors. The problem is that it's <em>non trivial</em> (understatement) to set up a mail server properly.</p> <p>This howto assumes you have a Linux server (these instructions are for Ubuntu 22.04 and 20.04, although it should work on earlier versions of Ubuntu server and Debian Linux with minor changes, and the concepts will be very similar on other Linuxen) with a static IP address, with one or more fully-qualified-domain-names (fdqn) pointing at that address, and you have SSH-based access to it. I've <a href="/setting-your-own-bitwarden-password-keeper-and-sync-server">previously provided tips</a> on how to get to this stage.</p> <h2>Authenticating SMTP</h2> <p>To send email, you need access to a server, somewhere on the Internet, that provides the <a href="https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol">Simple Mail Transfer Protocol</a> (SMTP) service. It's an open standard, and for most of the history of the Internet, email services have been mostly provided by Free and Open Source Software (FOSS) tools - the first SMTP was called "<a href="https://en.wikipedia.org/wiki/Sendmail">Sendmail</a>" and it was fully FOSS, and it's still in use today (although it has mostly been superseded by faster, more secure systems, the best of which are also FOSS).</p> <p>At the OERu, we use the <a href="https://mailcow.github.io/mailcow-dockerized-docs/" title="Dockerised MailCow">Docker-based installation of the amazing, completely FOSS MailCow project</a> to provide our organisational email services. I might cover that set up in a future tutorial here, because MailCow makes an otherwise almost intractable problem - hosting your own email service - much more tractable. Having a MailCow set up means we can offer "full service" email for any number of domains and users and aliases with all the bells and whistles including incoming and outgoing mail with all the virus scanning (we don't really need it because we use Linux desktops, but for other folks it's useful) and dynamic spam filtering services you'd expect from a much larger operation: <a href="https://mailcow.email/">Team MailCow</a> have done an amazing job in pulling together a comprehensive set of FOSS applications to provide all the conceivable requirements of a full-fledged, multi-domain email system, including shared calendaring, contacts, and webmail. A great companion to your organisation's MailCow server would be a <a href="/setting-your-own-bitwarden-password-keeper-and-sync-server">BitWarden password safe</a> server (also FOSS)... just sayin'.</p> <p>So, now, assuming that we have a MailCow server or some other functionally equivalent SMTP service available (apparently you can <a href="https://support.google.com/a/answer/2956491">do this with Gmail</a>, if you're a paying using although because of Google's terms of use, we recommend finding a more trustworthy solution), we have the option of "authenticated SMTP" for outgoing email using credentials we can set up. For example, in MailCow, we can specify a domain we host, like say <strong>oeru.org</strong> (and for which we've defined an MX record and a few other relevant records as guided by MailCow administrative web interface). On top of that, we can specify a mailbox for a dedicated "send stuff from remote relay hosts" email address using that domain, like <strong><a href="mailto:smtp@oeru.org">smtp@oeru.org</a></strong>, with a strong password. With that, we can <em>securely </em>send email using that email address as the username and that password from <em>anywhere we have access to the Internet</em>.</p> <p>The <strong>only tricky part</strong> is that we have to ensure that whatever "reply to" email address we specify from our applications, say <strong><a href="mailto:notifications@tech.oeru.org">notifications@tech.oeru.org</a></strong>, is using a domain we <em>also host on the same server, </em>and that there's an <em>email alias</em> of that email address defined and set as "allow to send from <a href="mailto:smtp@oeru.org">smtp@oeru.org</a>" in the MailCow interface. If we haven't made sure of that, our mail server is likely to reject sending emails with that "mismatching" email address. This is a basic spam deterrence measure, which is for the best, despite sometimes making a email system administrator's life harder.</p> <p>Once we've got that (and it's easy once you've done it once or twice - I'm mostly writing this down now so I don't have to try to re-remember every time I need to set up a new server - and I hope it helps others, too), we can set up any server we control to send secure (and spam-filter-resilient) email. For what it's worth, too, MailCow uses Postfix as its SMTP server component (there're a bunch of other components, too).</p> <h2>Postfix SMTP with SmartHost</h2> <p>The first thing you need to do to create a postfix <a href="https://en.wikipedia.org/wiki/Smart_host">smarthost</a> is to install the postfix application on a new server (this assumes you're logged in with a user who has "sudo" - aka admin - permissions):</p> <p><code>sudo apt update &amp;&amp; sudo apt install postfix bsd-mailx</code></p> <p>During the install, you'll be asked to select a bunch of configuration parameters. Select the defaults except:</p> <ul><li>Select "Internet Site with Smarthost",</li> <li>fill in the domain name for your server,</li> <li>the domain name and port (in the form <code>[smtp server domain]:[port]</code>, e.g. <code>smtp.oeru.org:587</code> ) of your "smarthost" who'll be doing the authenticating SMTP for you, and</li> <li>the email address to which you want to receive system-related messages.</li> </ul><p>After that's done, you can proceed.</p> <h2>Next Steps</h2> <p>For the rest of this tutorial, you'll need to do the following. First, select your text editor. I use vim, but if you're new to the command line, I recommend using nano - it's more straightforward:</p> <p><code>EDIT=`which nano`</code> or <code>EDIT=`which vim`</code></p> <p><code>sudo $EDIT /etc/aliases</code></p> <p>We need to make sure the "root" user points to a real email address. Add a line at the bottom which says (replacing [your email] with <em>your email :) </em>)</p> <p><code>root: [your email]</code></p> <p>After which you'll need to convert the aliases file into a form that postfix can process, simply by running this:</p> <p><code>sudo newaliases</code></p> <p>Then we have to define the authentication credentials required to convince your mail server that you're you!</p> <p><code>sudo $EDIT /etc/postfix/relay_password</code></p> <p>The resulting file only needs one line with three bits of information:</p> <p><code>[smtp server domain] [user name]:[password]</code></p> <p>for example:</p> <p><code>smtp.oeru.org smtp@oeru.org:SomeObscurePassw0rd</code></p> <p>Then save the file and, like the aliases file, run the conversion process (which uses a slightly different mechanism):</p> <p><code>sudo postmap /etc/postfix/relay_password</code></p> <p>Finally, we'll edit the main configuration file for Postfix to tell it about all this stuff:</p> <p><code>sudo $EDIT /etc/postfix/main.cf</code></p> <p>If your SMTP server uses port 25 (the default for <em>unencrypted</em> SMTP) you don't have to change anything, although most people nowadays prefer to use StartTLS or otherwise encrypted transport to at least ensure that your SMTP authentication details (<em>at least</em>) are transferred encrypted. That means using port 587 or 465. If you're using either of those ports, find the "relayhost = [your server name]" line... and add your port number after a colon, like this</p> <p><code>relayhost = [your server name]:[server port] </code></p> <p>or, for example:</p> <p><code>relayhost = smtp.oeru.org:465 </code></p> <p>Next, add the following lines at the bottom of the file:</p> <p><code># added to configure accessing the relay host via authenticating SMTP<br /> smtp_sasl_auth_enable = yes<br /> smtp_sasl_password_maps = hash:/etc/postfix/relay_password<br /> smtp_sasl_security_options = noanonymous</code><br /><code>smtp_tls_security_level = encrypt</code></p> <p><code># if you're using Ubuntu prior to 20.04, uncomment (remove the #) the </code><br /><code># earlier line smtp_tls_security_level = may to save errors in 'postfix check'<br /> # and comment this line (by adding a # at the start)<br /> smtp_tls_wrappermode = yes</code></p> <p>And, finally, comment out the line <code>smtp_tls_security_level = may</code> higher in the file - careful not to confuse it with the very similar <code>smtpd_tls_security_level</code> variable (note the extra '''d''' in `smtpd...`) line.</p> <p>Save the file, and then check that your syntax is correct:</p> <p><code>sudo postfix check</code></p> <p>If it is (running the command returns no errors, and it might not return anything at all - that's a good thing!), then you can run</p> <p><code>sudo postfix reload</code></p> <p>to get postfix to reload its configurations and you can test out your new smarthost-configured SMTP server!</p> <p>If not, the output of the check command will usually give you a helpful insight into what is wrong with your configuration... you'll also find that looking at the mail log is very helpful and offers great insights:</p> <p><code>sudo less +G /var/log/mail.log</code></p> <p>and if you're not able to fix it based on those, you'll find postfix is widely documented and has rich set of easily discoverable resources out there on the web - a search engine is your best resource!</p> <h2>Testing your outgoing email</h2> <p>By default, a command line application called "mail" is installed as part of the bsd-mailx package we installed alongside postfix. You can use it to send test email from the command line on your host to verify you've got things working correctly! The stuff in &lt;&gt; are the keys to hit at the end of the line...</p> <p><code>$ mail you@email.domain&lt;ENTER&gt;</code></p> <p><code>Subject: Testing from your.relay.server.domain&lt;ENTER&gt;<br /> Testing postfix remote host&lt;ENTER&gt;<br /> &lt;CTRL-D&gt;<br /> Cc:&lt;ENTER&gt;</code></p> <p>Typing &lt;CTRL-D&gt; (hold down the Control or Ctrl key on your keyboard and press the "d" key) will finish your message, showing you a "CC:" field, in which you can type in other email addresses if you want to test sending to multiple addresses. When you then hit &lt;ENTER&gt;, it will attempt to send this email. It might take a few minutes to work its way through to the receiving email system (having to run the gauntlet of spam and virus filters on the way).</p> <p>You can also always check the postfix system logs to see what postfix thinks about it using the command above. Hit &lt;SHIFT-F&gt; to have the log update in real time.</p> <h2>Done</h2> <p>Now you've got working outgoing email from your server. That means many higher-level web applications you might install on your infrastructure will work out-of-the-box, because what you've set up, for example, enables the default PHP email service and that used by other stacks.</p> <h2>Sending from Docker Containers</h2> <p>You can configure your server so you can reference it from services you run from Docker containers on your host. You do this by referencing the host, like via an ad hoc SMTP server on your container like <a href="https://marlam.de/msmtp/">msmtp</a>, and you can just reference it as 172.17.0.1, which is the default base IP for Docker hosts from the perspective of Docker containers. You might find it's different on your particular install. In that case, you have to make your Postfix SmartHost accept email for sending from the Docker containers on that server. There're quite a few examples of that among <a href="https://git.oeru.org/explore/projects?utf8=%E2%9C%93&amp;name=docker&amp;sort=latest_activity_desc">my Docker recipes on the OERu's git repository</a>.</p> </div> </div> </div> <section class="field field-node--field-blog-comments field-name-field-blog-comments field-type-comment field-label-above comment-wrapper"> <a name="comments"></a> <div class="comment-form-wrapper"> <h2 class="comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=28&amp;2=field_blog_comments&amp;3=comment" token="lLRkGAi5P6j9iM99_jKG1YxvJLITyB02GoT7oM-A7oA"></drupal-render-placeholder> </div> </section> Mon, 02 Aug 2021 02:08:28 +0000 dave 28 at http://tech.oeru.org