From 161dfd53adef95b7b7d690281dd7658294df5ae1 Mon Sep 17 00:00:00 2001
From: 3wc <3wc@doesthisthing.work>
Date: Sat, 30 Mar 2024 13:20:00 -0300
Subject: [PATCH] Add Selfhosted Infrastructure Any% post
---
.../blog/selfhosted-infrastructure-any%.md | 187 ++++++++++++++++++
static/img/speedrun-fluffychat.webp | Bin 0 -> 16854 bytes
static/img/speedrun-hometown.webp | Bin 0 -> 149842 bytes
static/img/speedrun-keycloak.webp | Bin 0 -> 14736 bytes
static/img/speedrun-matrix.webp | Bin 0 -> 71490 bytes
static/img/speedrun-nextcloud.webp | Bin 0 -> 22000 bytes
static/img/speedrun-traefik.webp | Bin 0 -> 43128 bytes
static/img/speedrun.jpg | Bin 0 -> 268718 bytes
8 files changed, 187 insertions(+)
create mode 100644 content/blog/selfhosted-infrastructure-any%.md
create mode 100644 static/img/speedrun-fluffychat.webp
create mode 100644 static/img/speedrun-hometown.webp
create mode 100644 static/img/speedrun-keycloak.webp
create mode 100644 static/img/speedrun-matrix.webp
create mode 100644 static/img/speedrun-nextcloud.webp
create mode 100644 static/img/speedrun-traefik.webp
create mode 100644 static/img/speedrun.jpg
diff --git a/content/blog/selfhosted-infrastructure-any%.md b/content/blog/selfhosted-infrastructure-any%.md
new file mode 100644
index 0000000..f7be868
--- /dev/null
+++ b/content/blog/selfhosted-infrastructure-any%.md
@@ -0,0 +1,187 @@
+
+---
+title: "Selfhosted Infrastructure Any%"
+date: 2024-03-30T16:00:00+00:00
+draft: false
+image: "/img/speedrun.jpg"
+---
+
+A Co-op Cloud speedrun attempt livestream! Brought to you by by libre software.
+
+
+
+It'd been so long since [the last Co-op Cloud demo video](https://asciinema.org/a/405836) that, since then, our core command-line tool `abra` had been completely rewritten (from Bash to Golang), and the entire Co-op Cloud project moved from an in-house Autonomic production to a participatory, democractic [Co-op Cloud federation](https://docs.coopcloud.tech/federation/). Definitely time for an update!
+
+So, Co-op Cloud nerd @3wc picked up the fine tradition of [speedrunning](https://en.wikipedia.org/wiki/Speedrunning) for a "Selfhosted Infrastructure Any%" livestream – attempting to beat their personal best time of 46m17s to deploy chat (Matrix / Fluffychat), microblogging (Hometown), and file-/calendar-sharing (Nextcloud) behind single sign-on (Keycloak).
+
+
+
+Before we dive into the write-up, mega-thanks to audience member `@yala` for putting together an excellent write-up during the stream, including some insightful questions. And, of course, to everyone who came along, and everyone who helped signal-boost the announcement beforehand!
+
+# Concept
+
+The integrated set-up of Keycloak/Matrix/Hometown/Nextcloud is a running start for a small-to-medium group looking for alternatives to corporate spyware. It comprises solid alternatives to Dropbox/Google Drive, Twitter, and Slack/Discord/Whatsapp. And, the entire stack can be hosted on a cheap server (or even an old laptop or desktop computer), and the services can be accessed using reasonably slick mobile and desktop apps.
+
+Single sign-on means that in some ways, this set-up is even more usable, even for non-technical users, than the corporate equivalents. And, highlighting where Co-op Cloud can help, getting this combination of tools running manually without Co-op Cloud (that is, following the separate set-up instructions for each platform) is a non-trivial technical challenge.
+
+The choice of apps is primarily geared towards working groups of 3-200 people, and the focus is on internal coördination platforms rather than publication.
+
+## Preparation
+
+To try and simulate an experience of setting up these apps for the first time, and as a nod to the purist conditions popular with videogame speedruns, the demo starts with a blank Debian Docker image (`docker run debian`) and a blank Debian 12 server. (All commands below are run inside that container, unless specified otherwise).
+
+To avoid needing to screenshare DNS and server configuration, there are DNS records for `speedrun.coopcloud.tech` and `*.speedrun.coopcloud.tech` set up, and a pre-generated SSH key has permission to log into the server as root.
+
+## Howto / script
+
+### 1 Local setup
+
+```
+apt update && apt install vim curl wget openssh-client -y
+export EDITOR=vim
+```
+
+(The recommended installation instructions for `abra` require both `curl` and `wget` for some reason. An issue has been opened to figure that out: [`#591`](https://git.coopcloud.tech/coop-cloud/organising/issues/591))
+
+The next step is to copy-paste this from the [`abra` installation instructions](https://docs.coopcloud.tech/operators/tutorial/#install-abra):
+
+```
+curl https://install.abra.coopcloud.tech | bash
+```
+
+> An audience member commented that `curl X | bash` is not amazing security, and indeed downloading [the latest `abra` release binary](https://git.coopcloud.tech/coop-cloud/abra/releases) manually, and comparing the file checksums, would be more secure – patches to [`docs.coopcloud.tech`](https://git.coopcloud.tech/coop-cloud/docs.coopcloud.tech/) extremely welcome! See more on [`#593`](https://git.coopcloud.tech/coop-cloud/organising/issues/593).
+
+Then, running `abra autocomplete bash` shows the necessary commands to set us up some autocompletion for `abra` commands.
+
+### 2. Server set-up
+
+The next step, on the virtual server, is to run the ["Server set-up" section](https://docs.coopcloud.tech/operators/tutorial/#server-setup) of the Co-op Cloud docs:
+
+```
+ssh speedrun.coopcloud.tech
+# docker install convenience script
+wget -O- https://get.docker.com | bash
+
+# setup swarm
+docker swarm init
+docker network create -d overlay proxy
+```
+
+(Skipping adding the current user to the `docker` group, because `root` already has permission to access the Docker daemon)
+
+> As above, `wget ... | bash` is a bit wacky, this is the Docker-recommended installation method but there are others – patches welcome!
+
+Lastly, back on the local machine, `abra app server add speedrun.coopcloud.tech`
+
+### 3. Traefik
+
+```
+abra app new traefik
+abra app config traefik.speedrun.coopcloud.tech
+abra app deploy traefik.speedrun.coopcloud.tech -c -n
+```
+
+(`-c` to skip waiting for the container to come up, and `-n` to skip the confirmation prompt)
+
+![screenshot of Traefik dashboard](/img/speedrun-traefik.webp)
+
+### 4. Keycloak
+
+```
+abra app new keycloak --secrets
+abra app config keycloak.speedrun.coopcloud.tech
+abra app deploy keycloak.speedrun.coopcloud.tech -c -n
+```
+
+Then the Keycloak interface is available at `https://keycloak.speedrun.coopcloud.tech`, necessary to set up the OpenID Connect "clients" for single sign-on for the other apps.
+
+![screenshot of Keycloak login page](/img/speedrun-keycloak.webp)
+
+### 5. Matrix
+
+```
+abra app new matrix-synapse
+```
+
+Meanwhile, by this point Keycloak is alive, and ready to create a new "client". Doing so requires specifying a "client ID", and generates a new "client secret", both of which get added:
+
+```
+abra app config matrix-synapse.speedrun.coopcloud.tech --secrets
+# fill in SSO details
+abra app secret insert matrix-synapse.speedrun.coopcloud.tech keycloak_client_secret v1 (value)
+abra app deploy matrix-synapse.speedrun.coopcloud.tech -n -c
+```
+
+![screenshot of a terminal showing the above abra commands](/img/speedrun-matrix.webp)
+
+### 6. Fluffychat
+
+
+```
+abra app new fluffychat
+abra app config fluffychat.speedrun.coopcloud.tech -n -c
+```
+
+![screenshot of fluffychat login page](/img/speedrun-fluffychat.webp)
+
+### 7. Hometown
+
+```bash
+abra app new hometown
+```
+
+Again, a new Keycloak "client" is needed, giving us a client secret and client ID.
+
+Additionally (at least, for now), it's necessary to do some slightly cursed hacking on the server:
+
+```bash
+docker run -it git.coopcloud.tech/coop-cloud-chaos-patchs/hometown:v4.0.15-hometown-1.1.1 bash
+/usr/local/bin/docker-entrypoint.sh bundle exec rake secret # once for otp_secret
+/usr/local/bin/docker-entrypoint.sh bundle exec rake secret # then a second time for secret_key
+/usr/local/bin/docker-entrypoint.sh bundle exec rake mastodon:webpush:generate_vapid_key
+```
+
+Then manually insert the secrets, generate the remaining ones, and edit in the `VAPID_PUBLIC_KEY` and the other SSO details:
+
+```bash
+abra app secret insert hometown.speedrun.coopcloud.tech vapid_private_key v1
+abra app secret insert hometown.speedrun.coopcloud.tech otp_secret v1
+abra app secret insert hometown.speedrun.coopcloud.tech secret_key v1
+abra app config hometown.speedrun.coopcloud.tech
+# set OIDC_* settings, and VAPID_PUBLIC_KEY
+```
+
+Now, finally, the app can be deployed!
+
+```bash
+abra app deploy hometown.speedrun.coopcloud.tech -n -c
+```
+
+![screenshot of the livestream, showing the speedrun timer, a quite-bad webcam
+still, a terminal window with the above abra commands, and a chat log](/img/speedrun-hometown.webp)
+
+### 8. Nextcloud
+
+```bash
+abra app new nextcloud --secrets
+abra app deploy nextcloud.speedrun.coopcloud.tech
+```
+
+Annoyingly, Nextcloud needs stuff done in the web interface, and in the PHP config file, for SSO. So, with another new Keycloak client set up, the next step is logging into https://nextcloud.speedrun.coopcloud.tech, going to "Apps", installing "OIDC login by pulsejet", and then editing the config file:
+
+```
+abra app run nextcloud.speedrun.coopcloud.tech
+apt update && apt install vim
+vim config/config.php
+# fill in from https://git.coopcloud.tech/coop-cloud/nextcloud#how-do-i-integrate-with-keycloak-sso
+```
+
+(in the demo, this was done from the server, but the above approach is probably conceptually simpler)
+
+![screenshot of nextcloud login page](/img/speedrun-nextcloud.webp)
+
+## Closing thoughts
+
+Even though no records got broken, this was fun to make, and definitely a better way to prepare for an in-person demo than silent solo-hacking. It's also nice to get a little "this is how Co-op Cloud is currently looking" demo video out of it.
+
+If you have ideas for future streams, hit us up on Mastodon ([@coopcloud@social.coop](https://social.coop/@coopcloud/)) or dive into our Matrix ([#coopcloud:autonomic.zone](https://matrix.to/#/#coopcloud:autonomic.zone)). Otherwise, follow us directly on owncast ([`@cast@cast.coopcloud.tech`](https://cast.coopcloud.tech/)) and/or peertube ([@coopcloud@tv.undersco.re](https://tv.undersco.re/a/coopcloud/)) to hear about future streams and videos when they happen!
diff --git a/static/img/speedrun-fluffychat.webp b/static/img/speedrun-fluffychat.webp
new file mode 100644
index 0000000000000000000000000000000000000000..98e8998869eddff8fe8b733e12a423f565ab5307
GIT binary patch
literal 16854
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
x_T`rfi0
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
Gr~m-Dg83H!
literal 0
HcmV?d00001
diff --git a/static/img/speedrun-hometown.webp b/static/img/speedrun-hometown.webp
new file mode 100644
index 0000000000000000000000000000000000000000..32ec28ae081a7f16f32f534e825387c787f19a85
GIT binary patch
literal 149842
zcmdqIQgOo=q<`q3H3B36jPA`g0EwPYuQmx?!c5H`~dVt60@hANk;N~ml%i^8lefk;@w@uic4B!MPd`5hvTmXhs27Hu>ujIhuf0vM~nwG8R{mPxF=s&*((#z#=L*Km-jf}qTS*4t&*{uMrBY)CxA{rbM#
z;3F09s=4%BehHdkOs85V)7<3Dj*Zg3t81q4)FbK%m`+STZkBP}??kv~g_zRLG