oauth2 http://tech.oeru.org/ en Installing Authentik for Authentication and Single-Sign-On http://tech.oeru.org/installing-authentik-authentication-and-single-sign <span class="field field--name-title field--type-string field--label-hidden">Installing Authentik for Authentication and Single-Sign-On </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--single-sign-on"> <span class="field__item-wrapper"><a href="/taxonomy/term/87" hreflang="en">single sign on</a></span> </div> <div class="field__item field__item--sso"> <span class="field__item-wrapper"><a href="/taxonomy/term/88" hreflang="en">sso</a></span> </div> <div class="field__item field__item--authentik"> <span class="field__item-wrapper"><a href="/taxonomy/term/89" hreflang="en">authentik</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--nginx"> <span class="field__item-wrapper"><a href="/taxonomy/term/30" hreflang="en">nginx</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--python"> <span class="field__item-wrapper"><a href="/taxonomy/term/90" hreflang="en">python</a></span> </div> <div class="field__item field__item--django"> <span class="field__item-wrapper"><a href="/taxonomy/term/91" hreflang="en">django</a></span> </div> <div class="field__item field__item--openid"> <span class="field__item-wrapper"><a href="/taxonomy/term/92" hreflang="en">OpenID</a></span> </div> <div class="field__item field__item--oauth2"> <span class="field__item-wrapper"><a href="/taxonomy/term/23" hreflang="en">oauth2</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">Tue 24/10/2023 - 11:15</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/2023-10/Screenshot%202023-10-26%20at%2017-50-10%20Logged%20in%20to%20FOSSDLE%20Commons%20with%20available%20applications.png?itok=BaDedMB2" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Example of the user &#039;dashboard&#039; of available applications to log into after authenticating into the Authentik system. The array of available services will vary depending on the groups in which the autheticated user has been included.&quot;}" role="button" title="Example of the user &#039;dashboard&#039; of available applications to log into after authenticating into the Authentik system. The array of available services will vary depending on the groups in which the autheticated user has been included." data-colorbox-gallery="gallery-field_image-9R_772UzhgE" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Example of the user &#039;dashboard&#039; of available applications to log into after authenticating into the Authentik system. The array of available services will vary depending on the groups in which the autheticated user has been included.&quot;}"><img src="/sites/default/files/styles/medium/public/2023-10/Screenshot%202023-10-26%20at%2017-50-10%20Logged%20in%20to%20FOSSDLE%20Commons%20with%20available%20applications.png?itok=0ckMhPl9" width="220" height="167" alt="Example of the user &#039;dashboard&#039; of available applications to log into after authenticating into the Authentik system. The array of available services will vary depending on the groups in which the autheticated user has been included." 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/2023-10/Screenshot%202023-10-27%20at%2009-37-44%20Admin%20-%20FOSSLE%20Login%20and%20Authentication.png?itok=QTsUT2qD" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;The default administrator&#039;s page showing basic statistics about the Authentik instance. &quot;}" role="button" title="The default administrator&#039;s page showing basic statistics about the Authentik instance. " data-colorbox-gallery="gallery-field_image-9R_772UzhgE" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;The default administrator&#039;s page showing basic statistics about the Authentik instance. &quot;}"><img src="/sites/default/files/styles/medium/public/2023-10/Screenshot%202023-10-27%20at%2009-37-44%20Admin%20-%20FOSSLE%20Login%20and%20Authentication.png?itok=uYoyGXfX" width="220" height="167" alt="The default administrator&#039;s page showing basic statistics about the Authentik instance. " 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/2023-10/Screenshot%202023-10-27%20at%2009-38-13%20Providers%20-%20Admin%20-%20FOSSLE%20Login%20and%20Authentication.png?itok=iSQ-oLDw" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Sample view of a list of &#039;providers&#039; or applications for which our Authentik provides acceptable authentication/authorisation tokens. &quot;}" role="button" title="Sample view of a list of &#039;providers&#039; or applications for which our Authentik provides acceptable authentication/authorisation tokens. " data-colorbox-gallery="gallery-field_image-9R_772UzhgE" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Sample view of a list of &#039;providers&#039; or applications for which our Authentik provides acceptable authentication/authorisation tokens. &quot;}"><img src="/sites/default/files/styles/medium/public/2023-10/Screenshot%202023-10-27%20at%2009-38-13%20Providers%20-%20Admin%20-%20FOSSLE%20Login%20and%20Authentication.png?itok=tU5d-Zas" width="220" height="167" alt="Sample view of a list of &#039;providers&#039; or applications for which our Authentik provides acceptable authentication/authorisation tokens. " 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/2023-10/Screenshot%202023-10-27%20at%2009-38-35%20Applications%20-%20Admin%20-%20FOSSLE%20Login%20and%20Authentication.png?itok=3I3R6Fth" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;An example look at the applications configured for use with our Authentik instance (we plan to expand this greatly!).&quot;}" role="button" title="An example look at the applications configured for use with our Authentik instance (we plan to expand this greatly!)." data-colorbox-gallery="gallery-field_image-9R_772UzhgE" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;An example look at the applications configured for use with our Authentik instance (we plan to expand this greatly!).&quot;}"><img src="/sites/default/files/styles/medium/public/2023-10/Screenshot%202023-10-27%20at%2009-38-35%20Applications%20-%20Admin%20-%20FOSSLE%20Login%20and%20Authentication.png?itok=-mEDX6dn" width="220" height="167" alt="An example look at the applications configured for use with our Authentik instance (we plan to expand this greatly!)." 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/2023-10/Screenshot%202023-10-27%20at%2009-39-15%20Flows%20-%20Admin%20-%20FOSSLE%20Login%20and%20Authentication.png?itok=6LkZSLdu" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Sample view of the &#039;flows&#039; defined in our Authentik instance. Arbitrarily complex &#039;flows&#039; (or paths through interaction with the Authentik system) can be defined. &quot;}" role="button" title="Sample view of the &#039;flows&#039; defined in our Authentik instance. Arbitrarily complex &#039;flows&#039; (or paths through interaction with the Authentik system) can be defined. " data-colorbox-gallery="gallery-field_image-9R_772UzhgE" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Sample view of the &#039;flows&#039; defined in our Authentik instance. Arbitrarily complex &#039;flows&#039; (or paths through interaction with the Authentik system) can be defined. &quot;}"><img src="/sites/default/files/styles/medium/public/2023-10/Screenshot%202023-10-27%20at%2009-39-15%20Flows%20-%20Admin%20-%20FOSSLE%20Login%20and%20Authentication.png?itok=cOut-nXA" width="220" height="167" alt="Sample view of the &#039;flows&#039; defined in our Authentik instance. Arbitrarily complex &#039;flows&#039; (or paths through interaction with the Authentik system) can be defined. " 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>The OER Foundation offers many Free and Open Source Software services to our learners and educators to provide them with resources for learning, developing Open Educational Resources (OER), and collaborating - both professionally and socially - with one another. Due to this proliferation of 'point source' technologies, users have to create a myriad of user accounts, each requesting an email and password and perhaps a user name. For many, especially those who have not adopted a <a href="/node/25">password keeper</a>, this can be an onerous situation, and often results in very poor 'internet hygiene' due to people choosing simplistic passwords and using the same small set of passwords in many contexts.</p> <p>An alternative to this default approach is for the OER Foundation to adopt an authentication and authorisation solution. Authentication means creating a single link between a real (physical) person and their online (virtual) identity - usually managed with a login and password and might also include one or more additional factors of identification, also known as '<a href="https://en.wikipedia.org/wiki/Multi-factor_authentication">Multi-Factor Authentication</a>' or MFA. Authorisation involves allowing authenticated users access to systems to which they should have access, and not those to which they should not. An suitable authentication and authorisation system can then be used to implement a '<a href="https://en.wikipedia.org/wiki/Single_sign-on">Single Sign-On</a>' (SSO) system that provides the option to all those with credentials on it to access - subject to any authorisation limitations - any of the OER Foundation's services transparently without needing to create a new, separate account.</p> <p>The Authentik project offers <a href="https://goauthentik.io/docs/installation/docker-compose">quite good documentation for Docker Compose installation</a>, too. This tutorial should be seen as a complement to that, perhaps providing a bit more guidance.</p> <ul class="table-of-contents"><li> <p><a href="#preparing-a-suitable-server">Preparing a suitable server</a></p> </li> <li> <p><a href="#ground-work">Ground work</a></p> </li> <li> <p><a href="#configuring-the-reverse-proxy">Configuring the reverse proxy</a></p> </li> <li> <p><a href="#getting-your-lets-encrypt-ssl-certificate">Getting your Let's Encrypt SSL certificate</a></p> </li> <li> <p><a href="#docker-compose-configuration">Docker Compose configuration</a></p> </li> <li> <p><a href="#boom-setting-up-your-authentik-instance">Boom: Setting up your Authentik instance</a></p> </li> <li> <p><a href="#upgrading-your-authentik">Upgrading your Authentik</a></p> </li> <li> <p><a href="#backing-up-your-authentik-data">Backing up your Authentik data</a></p> </li> </ul><h2><a id="user-content-preparing-a-suitable-server" href="#preparing-a-suitable-server" name="preparing-a-suitable-server" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Preparing a suitable server</h2> <p>All of our OER Foundation services share a common hosting platform. You'll need to <a href="/node/59">create a replica of that platform</a> - if you don't already have one - so you can add this service. <em>Everything else in this post assumes you've followed those instructions</em>!</p> <p>Note, the Authentik developers suggest a minimum VPS specification of 2 CPU cores and 2 GB RAM, so a very small instance should suffice.</p> <h2><a id="user-content-ground-work" href="#ground-work" name="ground-work" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Ground work</h2> <p>To set up Authentik, you'll need a few key details specific to your instance. For the rest of this tutorial, we'll use the convention of representing those variables as a name inside [], for example, the domain name that identifies your instance, which is referred to by the token [domain name]. Note - you'll see a few other instances of [] in the content below - the ones with multiple ::: in them, e.g. [:::], are not tokens, they're the way IPv6</p> <p>The tokens for which you'll need values are as follows:</p> <ul><li>your instance [domain name], i.e. a fully qualified domain name or a sub domain by which people will reach your instance. In our case, for example, we use 'auth.fossdle.org', which is the 'auth' subdomain of the 'fossdle.org' fully qualified domain name.</li> <li>Authenticating SMTP (Simple Mail Transfer Protocol, i.e. the language for sending email) 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 host]</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 username]</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> <li> <strong>[smtp from email]</strong> - this is the email address <em>from</em> which the system emails will be sent - it's what recipients will see and the address to whom they'll be able to respond. You might want something like 'noreply@[domain name]'... or maybe (if you do accept responses) 'webmaster@[domain name]'.</li> </ul></li> <li> <strong>[postgresql password]</strong> - a strong random password, say 12-99 characters long, sticking with letters and numbers as some symbols might be mis-interpreted in the context of the configuration file in which it's stored (e.g. if it contains a '#', the rest of the password might be interpreted as a comment. Or if you enclosed it in "", if it includes a " as a symbol, it might be interpreted as prematurely closing your double quotes). To generate, see below.</li> <li> <strong>[authentik secret]</strong> - a random 'secret key' of 50 characters is recommended. Again, see below.</li> </ul><p>To generate (relatively) random passwords/keys, you can install the following:</p> <p><code>sudo apt install -y pwgen</code></p> <p>To generate, say, a 40 character [postgresql password], you could run</p> <p><code>pwgen -s 40 1</code></p> <p>and grab the output, e.g. 28RFCIFtnVM9tFOmT2zx2fKiK5sf42WGK8FHr9Bn</p> <p>For a 50 character [authentik secret] just copy the output of</p> <p><code>pwgen -s 50 1</code></p> <p>Learn more about specifying random passwords via <code>man pwgen</code> or <code>pwgen --help</code> and look at our tutorial on <a href="/node/43">creating good passwords</a>.</p> <p>Also, you'll need to edit files, so it's worth setting up a text editor. If you're new to Linux, 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> <h2><a id="user-content-configuring-the-reverse-proxy" href="#configuring-the-reverse-proxy" name="configuring-the-reverse-proxy" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Configuring the reverse proxy</h2> <p>We need to configure a 'reverse proxy' so that external requests to our new Authentik service are routed to our containers, and so that all communication between our service's users and our service are suitably encrypted via Secure Sockets Layer (SSL).</p> <p>You should already have Nginx and Let's Encrypt installed on your server, so all you need to do is set up a new configuration file. The convention I use would involve doing this:</p> <p><code>sudo $EDIT /etc/nginx/sites-available/[domain name]</code></p> <p>where you put your domain name as the filename as indicated. You can copy and paste the following into the configuration file, replacing tokens like [domain name] with your value:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># Upstream where your authentik server is hosted.</span> upstream authentik <span>{</span> server 127.0.0.1:<span>9443</span>; <span># Improve performance by keeping some connections alive.</span> keepalive <span>10</span>; <span>}</span>   <span># Upgrade WebSocket if requested, otherwise use keepalive</span> map <span>$http_upgrade</span> <span>$connection_upgrade_keepalive</span> <span>{</span> default upgrade; <span>''</span> <span>''</span>; <span>}</span>   server <span>{</span> <span># HTTP server config</span> listen <span>80</span>; listen <span>[</span>::<span>]</span>:<span>80</span>; server_name <span>[</span>domain name<span>]</span>;   include includes<span>/</span>letsencrypt.conf;   access_log <span>/</span>var<span>/</span>log<span>/</span>nginx<span>/</span><span>[</span>domain name<span>]</span>_access.log; error_log <span>/</span>var<span>/</span>log<span>/</span>nginx<span>/</span><span>[</span>domain name<span>]</span>_error.log;   <span># 301 redirect to HTTPS</span> location <span>/</span> <span>{</span> <span>return</span> <span>301</span> https:<span>//</span><span>$host</span><span>$request_uri</span>; <span>}</span> <span>}</span> server <span>{</span> <span># HTTPS server config</span> listen <span>443</span> ssl http2; listen <span>[</span>::<span>]</span>:<span>443</span> ssl http2; server_name <span>[</span>domain name<span>]</span>;   <span># TLS certificates</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>#ssl_certificate /etc/letsencrypt/live/[domain name]/fullchain.pem;</span> <span>#ssl_certificate_key /etc/letsencrypt/live/[domain name]/privkey.pem;</span> <span># to create this, see https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html</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 name<span>]</span>_access.log; error_log <span>/</span>var<span>/</span>log<span>/</span>nginx<span>/</span><span>[</span>domain name<span>]</span>_error.log;   <span># Proxy site</span> location <span>/</span> <span>{</span> proxy_pass https:<span>//</span>authentik; proxy_http_version <span>1.1</span>; proxy_set_header X-Forwarded-Proto <span>$scheme</span>; proxy_set_header X-Forwarded-For <span>$proxy_add_x_forwarded_for</span>; proxy_set_header Host <span>$host</span>; proxy_set_header Upgrade <span>$http_upgrade</span>; proxy_set_header Connection <span>$connection_upgrade_keepalive</span>; <span>}</span> <span>}</span></pre></div></div> <p>Note that if your server has other services on it and, by coincidence, one is already using port 9443, then you can pick another port (e.g. 9444) and see if that works. If so, you'll need to make sure that you replace 9443 in the <code>docker-compose.yml</code> below, too.</p> <p>Once you've saved the file, you can 'enable' it straight away. To do that, you need to do the following (again, replacing the tokens):</p> <p><code>sudo ln -sf /etc/nginx/sites-available/[domain name] /etc/nginx/sites-enabled</code></p> <p>which links the configuration file you've just created into the 'sites-enabled' directory. You can then test whether Nginx accepts the configuration you've added:</p> <p><code>sudo nginx -t</code></p> <p>If there're no errors here, you can make the changes live:</p> <p><code>sudo service nginx reload</code></p> <h2><a id="user-content-getting-your-lets-encrypt-ssl-certificate" href="#getting-your-lets-encrypt-ssl-certificate" name="getting-your-lets-encrypt-ssl-certificate" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Getting your Let's Encrypt SSL certificate</h2> <p>You can now request an SSL certificate for your new domain (making use of the <a href="/node/59">configuration set up the server tutorial</a>), obviously replacing [domain name] with yours:</p> <p>`sudo letsencrypt certonly --webroot -w /var/www/letsencrypt -d [domain name]</p> <p>After you've got your certificate (which should only take a few seconds), you can go back into your proxy configuration (remember it's the same file as /etc/nginx/sites-enabled/[domain name]):</p> <p><code>sudo $EDIT /etc/nginx/sites-available/[domain name]</code></p> <p>and alter the certificates by changing just the following lines:</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre> <span># TLS certificates</span> <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>Then you just need to make sure there're no typos:</p> <p><code>sudo nginx -t</code></p> <p>and make the changes live:</p> <p><code>sudo service nginx reload</code></p> <p>Now external people will be able to securely access your Authentik once the service is running!</p> <h2><a id="user-content-docker-compose-configuration" href="#docker-compose-configuration" name="docker-compose-configuration" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Docker Compose configuration</h2> <p>The next thing you need is a <code>docker-compose.yml</code> file. I usually put this in the a directory called <code>/home/docker/[domain name]</code> (replacing the token with your domain name!). You'll want to be in that directory:</p> <p><code>cd /home/docker/[domain name]</code></p> <p>Because the developers of Authentik periodically change the default configuration of their <code>docker-compose.yml</code> file, it's best to grab their current one from here:</p> <p><code>wget https://goauthentik.io/docker-compose.yml</code></p> <p>which should result in a file called <code>docker-compose.yml</code> appearing in that directory (run <code>ls -l</code> to confirm).</p> <p>I'd make a backup of this file for future reference:</p> <p><code>cp docker-compose.yml docker-compose.yml-default</code></p> <p>You can then</p> <p><code>$EDIT docker-compose.yml</code></p> <p>and it should look similar to the one below, albeit probably with a different AUTHENTIK_TAG number - the version as of this writing is 2023.10.2 - and with different 'volumes' specified.</p> <p>The only things you'll likely need to change are the designations for the 'volumes:'. See below where I've specified volumes in your <code>/home/data/[domain name]</code> directory rather than the local directory as the default docker-compose.yml defaults to.</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre>version: <span>'3.4'</span>   services: postgresql: image: docker.io<span>/</span>library<span>/</span>postgres:<span>12</span>-alpine restart: unless-stopped healthcheck: test: <span>[</span><span>"CMD-SHELL"</span>, <span>"pg_isready -d $<span>${POSTGRES_DB}</span> -U $<span>${POSTGRES_USER}</span>"</span><span>]</span> start_period: 20s interval: 30s retries: <span>5</span> timeout: 5s volumes: - <span>/</span>home<span>/</span>data<span>/</span><span>[</span>domain name<span>]</span><span>/</span>postgres:<span>/</span>var<span>/</span>lib<span>/</span>postgresql<span>/</span>data environment: - <span>POSTGRES_PASSWORD</span>=<span>${PG_PASS:?database password required}</span> - <span>POSTGRES_USER</span>=<span>${PG_USER:-authentik}</span> - <span>POSTGRES_DB</span>=<span>${PG_DB:-authentik}</span> env_file: - .env redis: image: docker.io<span>/</span>library<span>/</span>redis:alpine command: <span>--save</span> <span>60</span> <span>1</span> <span>--loglevel</span> warning restart: unless-stopped healthcheck: test: <span>[</span><span>"CMD-SHELL"</span>, <span>"redis-cli ping | grep PONG"</span><span>]</span> start_period: 20s interval: 30s retries: <span>5</span> timeout: 3s volumes: - <span>/</span>home<span>/</span>data<span>/</span><span>[</span>domain name<span>]</span><span>/</span>redis:<span>/</span>data server: image: <span>${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}</span>:<span>${AUTHENTIK_TAG:-2023.10.2}</span> restart: unless-stopped command: server environment: AUTHENTIK_REDIS__HOST: redis AUTHENTIK_POSTGRESQL__HOST: postgresql AUTHENTIK_POSTGRESQL__USER: <span>${PG_USER:-authentik}</span> AUTHENTIK_POSTGRESQL__NAME: <span>${PG_DB:-authentik}</span> AUTHENTIK_POSTGRESQL__PASSWORD: <span>${PG_PASS}</span> AUTHENTIK_ERROR_REPORTING__ENABLED: <span>"true"</span> volumes: - <span>/</span>home<span>/</span>data<span>/</span><span>[</span>domain name<span>]</span><span>/</span>media:<span>/</span>media - <span>/</span>home<span>/</span>data<span>/</span><span>[</span>domain name<span>]</span><span>/</span>custom-templates:<span>/</span>templates env_file: - .env ports: - <span>"127.0.0.1:<span>${AUTHENTIK_PORT_HTTP:-9080}</span>:9080"</span> - <span>"127.0.0.1:<span>${AUTHENTIK_PORT_HTTPS:-9443}</span>:9443"</span> worker: image: <span>${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}</span>:<span>${AUTHENTIK_TAG:-2023.10.2}</span> restart: unless-stopped command: worker environment: AUTHENTIK_REDIS__HOST: redis AUTHENTIK_POSTGRESQL__HOST: postgresql AUTHENTIK_POSTGRESQL__USER: <span>${PG_USER:-authentik}</span> AUTHENTIK_POSTGRESQL__NAME: <span>${PG_DB:-authentik}</span> AUTHENTIK_POSTGRESQL__PASSWORD: <span>${PG_PASS}</span> AUTHENTIK_ERROR_REPORTING__ENABLED: <span>"true"</span> user: root volumes: - <span>/</span>home<span>/</span>data<span>/</span><span>[</span>domain name<span>]</span><span>/</span>media:<span>/</span>media - <span>/</span>home<span>/</span>data<span>/</span><span>[</span>domain name<span>]</span><span>/</span>certs:<span>/</span>certs - <span>/</span>var<span>/</span>run<span>/</span>docker.sock:<span>/</span>var<span>/</span>run<span>/</span>docker.sock - <span>/</span>home<span>/</span>data<span>/</span><span>[</span>domain name<span>]</span><span>/</span>custom-templates:<span>/</span>templates env_file: - .env</pre></div></div> <p>One you've got that saved, there's just one thing left! You just need to set up your specific configuration file, which is held in a 'hidden' file, <code>.env</code> in <code>/home/docker/[domain name]</code>.</p> <p>This is what you should put in it, replacing [tokens]. Note that you can comment out any line you want by putting a '#' at the start of the line.</p> <p>Note that if you set the AUTHENTIK_TAG below and uncomment the line (remove the #) you won't (normally) need to update the <code>docker-compose.yml</code> file to apply updates to Authentik in future.</p> <div class="geshifilter"><div class="bash geshifilter-bash"><pre><span># AUTHENTIK_TAG=2023.10.2</span> <span>PG_PASS</span>=<span>[</span>postgresql password<span>]</span> <span>AUTHENTIK_SECRET_KEY</span>=<span>[</span>authentik secret<span>]</span> <span>AUTHENTIK_ERROR_REPORTING__ENABLED</span>=<span>true</span> <span># SMTP Host Emails are sent to</span> <span>AUTHENTIK_EMAIL__HOST</span>=<span>[</span>smtp host<span>]</span> <span>AUTHENTIK_EMAIL__PORT</span>=<span>[</span>smtp port<span>]</span> <span># Optionally authenticate (don't add quotation marks to your password)</span> <span>AUTHENTIK_EMAIL__USERNAME</span>=<span>[</span>smtp username<span>]</span> <span>AUTHENTIK_EMAIL__PASSWORD</span>=<span>[</span>smtp password<span>]</span> <span># Use StartTLS</span> <span>AUTHENTIK_EMAIL__USE_TLS</span>=<span>true</span> <span># Use SSL</span> <span>AUTHENTIK_EMAIL__USE_SSL</span>=<span>false</span> <span>AUTHENTIK_EMAIL__TIMEOUT</span>=<span>10</span> <span># Email address authentik will send from, should have a correct @domain</span> <span>AUTHENTIK_EMAIL__FROM</span>=<span>[</span>smtp from email<span>]</span> <span>AUTHENTIK_PORT_HTTP</span>=<span>9080</span> <span>AUTHENTIK_PORT_HTTPS</span>=<span>9443</span></pre></div></div> <p>Once that's done and saved, you can start your Authentik service!</p> <p><code>docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>This will trigger Docker to download the container images specified in your <code>docker-compose.yml</code> (based on AUTHENTIK_TAG), and start them up. That, in turn, will cause Docker Compose to create the directories specified in your <code>volumes:</code> stanzas in your <code>docker-compose.yml</code> file in <code>/home/data/[domain name]</code>, including creating your PostgreSQL database.</p> <h2><a id="user-content-boom-setting-up-your-authentik-instance" href="#boom-setting-up-your-authentik-instance" name="boom-setting-up-your-authentik-instance" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Boom: Setting up your Authentik instance</h2> <p>Once that is up and running, you should be able to point your browser at</p> <p><code>https://[domain name]/if/flow/initial-setup/</code></p> <p>Here's more <a href="https://goauthentik.io/docs/">documentation</a> to help you from here. Also, <a href="https://www.youtube.com/watch?v=Nh1qiqCYDt4&amp;list=PLH73rprBo7vSkDq-hAuXOoXx2es-1ExOP"><strong>cooptonian</strong>'s YouTube Channel</a> is most helpful!</p> <h2><a id="user-content-upgrading-your-authentik" href="#upgrading-your-authentik" name="upgrading-your-authentik" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Upgrading your Authentik</h2> <p>Periodically, you'll see in the admin interface (or via an email to the site's administrators) that your Authenitik can be upgraded to a newer version.</p> <p>Before doing anything to upgrade, I encourage you to make a copy of both your key files:</p> <p><code>DATE=$(date +%d-%m-%Y</code>)<code> </code>cp docker-compose.yml docker-compose.yml-$DATE<code> </code>cp .env .env-$DATE`</p> <p>because having them handy might save your bacon if, for example, you need to revert back to your old configuration.</p> <p>Sometimes the Authentik developers change the configuration of their default <code>docker-compose.yml</code> file. You'll want to verify that your version is the same <em>except for any changes to <code>volumes:</code> you've made</em> as follows:</p> <p>Download the current reference version as <code>docker-compose.yml-default</code> (so it doesn't overwrite your <code>docker-compose.yml</code>):</p> <p><code>wget -O docker-compose.yml-default https://goauthentik.io/version/2023.10/docker-compose.yml</code></p> <p>Then, you'll want to run</p> <p><code>diff docker-compose.yml-default docker-compose.yml</code></p> <p>to see how the new file differs. If it differs in areas other than <code>volumes:</code> and the value for <code>AUTHENTIK_TAG</code>, you'll want to apply those changes to your <code>docker-compose.yml</code> via</p> <p><code>$EDIT docker-compose.yml</code></p> <p>Also, because upgrades might well result in a change to your PostgreSQL database's schema, you'll want a full backup of the existing database!</p> <p>Once you have that, you can run</p> <p><code>docker-compose pull &amp;&amp; docker-compose up -d &amp;&amp; docker-compose logs -f</code></p> <p>to pull the new containers, run them, and watch the logs of the upgrade as it progresses and, hopefully, completes successfully.</p> <h2><a id="user-content-backing-up-your-authentik-data" href="#backing-up-your-authentik-data" name="backing-up-your-authentik-data" class="heading-permalink" aria-hidden="true" title="Permalink"></a>Backing up your Authentik data</h2> <p>At the OER Foundation, we recognise that our Authentik is pretty mission-critical. To protect our users' data integrity and <em>privacy</em>, we do nightly encrypted incremental file backups and hourly PostgreSQL database backups. For the former, we use a <a href="https://restic.net">Restic</a> remote server <a href="https://git.oeru.org/dave/restic-backup">script</a> we've written (which requires that you have either sufficient local storage, or (preferably) another server you can access via SSH which has sufficient disk space to hold versioned backups), and the latter, we use a <a href="https://git.oeru.org/oeru/docker-compose-dbbackup">backup script we've developed</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=60&amp;2=field_blog_comments&amp;3=comment" token="gczezEir2FTP_q4_d-D-ocoNHXv3-ZNo43C6OApzx08"></drupal-render-placeholder> </div> </section> Mon, 23 Oct 2023 22:15:36 +0000 dave 60 at http://tech.oeru.org Multiple Discourse Forums on the same server http://tech.oeru.org/multiple-discourse-forums-same-server <span class="field field--name-title field--type-string field--label-hidden">Multiple Discourse Forums on the same 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--discourse"> <span class="field__item-wrapper"><a href="/taxonomy/term/19" hreflang="en">discourse</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--docker"> <span class="field__item-wrapper"><a href="/taxonomy/term/16" hreflang="en">docker</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--redis"> <span class="field__item-wrapper"><a href="/taxonomy/term/21" hreflang="en">redis</a></span> </div> <div class="field__item field__item--ruby-on-rails"> <span class="field__item-wrapper"><a href="/taxonomy/term/22" hreflang="en">ruby on rails</a></span> </div> <div class="field__item field__item--oauth2"> <span class="field__item-wrapper"><a href="/taxonomy/term/23" hreflang="en">oauth2</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">Fri 24/03/2017 - 13:20</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/2017-03/CommunityDiscourse_frontpage.png?itok=zflBNfcG" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Our Discourse front page from a user&#039;s perspective&quot;}" role="button" title="Our Discourse front page from a user&#039;s perspective" data-colorbox-gallery="gallery-field_image-bJQbOpH2zXo" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Our Discourse front page from a user&#039;s perspective&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/CommunityDiscourse_frontpage.png?itok=PKmMwvtz" width="220" height="125" alt="Our Discourse front page from a user&#039;s perspective" 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/2017-03/CommunityDiscourse_badgepage.png?itok=wB27ZtNy" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;&quot;Social credit&quot;: A user&#039;s &quot;badges&quot; which reflect the user&#039;s participation and trust levels within the forum community&quot;}" role="button" title="&quot;Social credit&quot;: A user&#039;s &quot;badges&quot; which reflect the user&#039;s participation and trust levels within the forum community" data-colorbox-gallery="gallery-field_image-bJQbOpH2zXo" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;&quot;Social credit&quot;: A user&#039;s &quot;badges&quot; which reflect the user&#039;s participation and trust levels within the forum community&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/CommunityDiscourse_badgepage.png?itok=j_6aa6dT" width="220" height="125" alt="&quot;Social credit&quot;: A user&#039;s &quot;badges&quot; which reflect the user&#039;s participation and trust levels within the forum community" 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/2017-03/CommunityDiscourse_topicediting.png?itok=UKvo5xRW" aria-controls="colorbox" aria-label="{&quot;alt&quot;:&quot;Editing topics - Discourse uses markdown for editing with side-by-side rendering and a simple &quot;rich text&quot; interface to assist new users&quot;}" role="button" title="Editing topics - Discourse uses markdown for editing with side-by-side rendering and a simple &quot;rich text&quot; interface to assist new users" data-colorbox-gallery="gallery-field_image-bJQbOpH2zXo" class="colorbox" data-cbox-img-attrs="{&quot;alt&quot;:&quot;Editing topics - Discourse uses markdown for editing with side-by-side rendering and a simple &quot;rich text&quot; interface to assist new users&quot;}"><img src="/sites/default/files/styles/medium/public/2017-03/CommunityDiscourse_topicediting.png?itok=osrNHUAn" width="220" height="164" alt="Editing topics - Discourse uses markdown for editing with side-by-side rendering and a simple &quot;rich text&quot; interface to assist new users" 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>At the OERu we have two separate instances of market category leading <a href="https://discourse.org" title="The Discourse Forum Community"> Discourse Forum</a>: one for <a href="https://community.oeru.org" title="The OERu Partner and Contributor Collaboration Forum">OER partner and contributor collaborators</a> and the <a href="https://forum.oeru.org" title="The OERu Learner Discourse Forum">other for learners</a>. These days, online forums are seen as a bit old-school: fuddy-duddy. From my point of view, however, Discourse is "Forum-NG" (a Next Generation forum). I think it's both very cool and innovative - not at all fuddy-duddy. Even better, Discourse also happens to be free and open source. Its active development community is storming ahead with updates and improvements at an impressive pace.</p> <p>Discourse is what we developers refer to as a "non-trivial" application. It's complex, no question, but it's also very mature and well engineered. It's built entirely on open source components. It uses the <a href="https://rubyonrails.org">Ruby on Rails</a> framework and pulls in a bunch of external systems including <a href="https://redis.io">Redis</a> (for caching and queuing) and <a href="https://www.postgresql.org/">PostgreSQL</a> for persistent data storage. The most common mode for running Discourse is via a single Docker container which includes PostgreSQL, Redis, and the full Ruby on Rails stack and Discourse application. Typically, an organisation only deploys a single Discourse instance. We, however, identified the need to segment our audiences and so decided to deploy the two instances on our main hosting server. This was much more challenging deployment, and not overly well documented. It took a while to get it right. I wrote up a blow-by-blow of how I did it in hopes it would benefit others in my position! See these two threads:</p> <ul><li><a href="Multiple Discoursen, multiple Docker containers, one server with one nginx">Multiple Discourses, multiple containers, one server</a> for the whole story (and some community comments)</li> <li><a class="fancy-title" data-ember-action="" data-ember-action-932="932" href="https://meta.discourse.org/t/discourse-in-docker-nginx-reverse-proxy-ssl-everywhere-oauth2-custom/52280">Discourse in Docker + NGINX reverse proxy + SSL everywhere + OAuth2 Custom</a> - for protecting the privacy and security of our users, and making it quick and easy for them to log in using existing credentials (but preferably not ones controlled by foreign corporations)</li> </ul><p>Discourse is impressive. It <a href="https://meta.discourse.org/t/benefits-of-discourse-have-i-missed-anything/39849">offers a lot more</a> than I've described so far. I recommend your organisation has a look - if you don't want to manage it in-house (it's easy once it's set up), by all means support the developers by buying their hosted service!</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=9&amp;2=field_blog_comments&amp;3=comment" token="q1H1hJgcXmYH07bFGODqnJapU96HbkXSo3XOYNpq97w"></drupal-render-placeholder> </div> </section> Fri, 24 Mar 2017 00:20:39 +0000 dave 9 at http://tech.oeru.org