From 41d82ae436fbb8f83b21d10b2ec5e13af0723848 Mon Sep 17 00:00:00 2001
From: stuebinm
Date: Thu, 11 Jan 2024 22:53:02 +0100
Subject: [PATCH] meta: new structure
we decided to:
- get rid of unused packages
- simpify the directory layout since we only have one host anyways
- move our docs (such as they are) in-tree
---
README.md | 45 ++---
docs/_index.md | 7 +
docs/auth.md | 10 ++
docs/domains.md | 20 +++
docs/hostnames.md | 16 ++
docs/lxc.md | 17 ++
docs/rebooting.md | 18 ++
docs/secrets.md | 21 +++
docs/services/_index.md | 5 +
docs/services/acme.md | 19 ++
docs/services/hedgedoc.md | 68 +++++++
docs/services/mail.md | 65 +++++++
docs/services/mumble.md | 40 +++++
docs/services/service.template.md | 18 ++
docs/snapshots.md | 24 +++
flake.nix | 7 +-
{hosts/parsons => parsons}/configuration.nix | 33 ++--
{services => parsons}/gitea.nix | 0
{hosts/parsons => parsons}/hardware.nix | 0
{services => parsons}/hedgedoc-hacc.nix | 0
{services => parsons}/hedgedoc-i4f.nix | 0
{hosts/parsons => parsons}/lxc.nix | 0
{services => parsons}/mail.nix | 0
{services => parsons}/mattermost.nix | 0
{services => parsons}/murmur.nix | 0
{services => parsons}/nextcloud.nix | 0
{services => parsons}/nginx-pages.nix | 0
{services => parsons}/tracktrain.nix | 0
{services => parsons}/uffd.nix | 0
{services => parsons}/vaultwarden.nix | 0
pkgs/default.nix | 33 +---
.../default.nix => mattermost.nix} | 0
.../netbox/0001-add-uffd-oauth2-backend.patch | 70 --------
pkgs/thelounge/css-patch.css | 24 ---
services/thelounge.nix | 65 -------
websites/docs.hacc.space/Readme.md | 44 +++++
websites/docs.hacc.space/config.toml | 35 ++++
websites/docs.hacc.space/content | 1 +
websites/docs.hacc.space/default.nix | 19 ++
websites/docs.hacc.space/sass/style.scss | 168 ++++++++++++++++++
.../static/ShareTech-Regular.ttf | Bin 0 -> 29536 bytes
websites/docs.hacc.space/static/favicon.png | Bin 0 -> 3798 bytes
.../docs.hacc.space/static/favicon_color.png | Bin 0 -> 5334 bytes
websites/docs.hacc.space/static/search.js | 52 ++++++
websites/docs.hacc.space/templates/base.html | 43 +++++
websites/docs.hacc.space/templates/blog.html | 12 ++
.../templates/categories/list.html | 17 ++
.../templates/categories/single.html | 12 ++
.../docs.hacc.space/templates/doc-page.html | 15 ++
websites/docs.hacc.space/templates/index.html | 23 +++
.../templates/post_macros.html | 15 ++
.../docs.hacc.space/templates/section.html | 15 ++
52 files changed, 854 insertions(+), 242 deletions(-)
create mode 100644 docs/_index.md
create mode 100644 docs/auth.md
create mode 100644 docs/domains.md
create mode 100644 docs/hostnames.md
create mode 100644 docs/lxc.md
create mode 100644 docs/rebooting.md
create mode 100644 docs/secrets.md
create mode 100644 docs/services/_index.md
create mode 100644 docs/services/acme.md
create mode 100644 docs/services/hedgedoc.md
create mode 100644 docs/services/mail.md
create mode 100644 docs/services/mumble.md
create mode 100644 docs/services/service.template.md
create mode 100644 docs/snapshots.md
rename {hosts/parsons => parsons}/configuration.nix (78%)
rename {services => parsons}/gitea.nix (100%)
rename {hosts/parsons => parsons}/hardware.nix (100%)
rename {services => parsons}/hedgedoc-hacc.nix (100%)
rename {services => parsons}/hedgedoc-i4f.nix (100%)
rename {hosts/parsons => parsons}/lxc.nix (100%)
rename {services => parsons}/mail.nix (100%)
rename {services => parsons}/mattermost.nix (100%)
rename {services => parsons}/murmur.nix (100%)
rename {services => parsons}/nextcloud.nix (100%)
rename {services => parsons}/nginx-pages.nix (100%)
rename {services => parsons}/tracktrain.nix (100%)
rename {services => parsons}/uffd.nix (100%)
rename {services => parsons}/vaultwarden.nix (100%)
rename pkgs/{mattermost/default.nix => mattermost.nix} (100%)
delete mode 100644 pkgs/netbox/0001-add-uffd-oauth2-backend.patch
delete mode 100644 pkgs/thelounge/css-patch.css
delete mode 100644 services/thelounge.nix
create mode 100644 websites/docs.hacc.space/Readme.md
create mode 100644 websites/docs.hacc.space/config.toml
create mode 120000 websites/docs.hacc.space/content
create mode 100644 websites/docs.hacc.space/default.nix
create mode 100644 websites/docs.hacc.space/sass/style.scss
create mode 100644 websites/docs.hacc.space/static/ShareTech-Regular.ttf
create mode 100644 websites/docs.hacc.space/static/favicon.png
create mode 100644 websites/docs.hacc.space/static/favicon_color.png
create mode 100644 websites/docs.hacc.space/static/search.js
create mode 100644 websites/docs.hacc.space/templates/base.html
create mode 100644 websites/docs.hacc.space/templates/blog.html
create mode 100644 websites/docs.hacc.space/templates/categories/list.html
create mode 100644 websites/docs.hacc.space/templates/categories/single.html
create mode 100644 websites/docs.hacc.space/templates/doc-page.html
create mode 100644 websites/docs.hacc.space/templates/index.html
create mode 100644 websites/docs.hacc.space/templates/post_macros.html
create mode 100644 websites/docs.hacc.space/templates/section.html
diff --git a/README.md b/README.md
index 0b24852..7894f1b 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,26 @@
# hacc nixfiles
-welcome to hacc nixfiles (haccfiles). this is the code describing our nix-based infrastructure.
+Welcome to the hacc nixfiles (haccfiles). This is how we configure (most of)
+our infrastructure.
-## structure
+## General layout
- `flake.nix`: Entrypoint & dependencies
-- `common/`: configuration common to all hosts
- `modules/`: home-grown modules for hacc-specific services
-- `pkgs/`: packages we built and don't want to upstream
-- `hosts/`: configuration.nix per host (currently there's only one of those)
-- `services/`: all services we run; imported in appropriate host config
-- `websites/`: static websites we deploy somewhere
+- `pkgs/`: packages we need which aren't in nixpkgs
+- `websites/`: static websites hosted by us
+- `common/`: meta-level config, reusable across machines
+- `parsons/`: our sole server, its config & the services it runs
-## working with the haccfiles
+Right now, we only have a single host. We might add more again in the future.
+
+## Working with this repo
You will need a flake-enabled nix installation, and have your ssh config set up
so that `ssh parsons` will connect to `parsons.hacc.space`.
+### Deploying remotely
+
It's recommended to use [deploy_rs](https://github.com/serokell/deploy-rs):
~~~shell
deploy .#parsons -k [--dry-activate]
@@ -28,30 +32,13 @@ nixos-rebuild --flake .#parsons --target-host parsons \
--use-remote-sudo --use-substitutes [test|switch|dry-activate]
~~~
-If for some reason you have `nix` but not `nixos-rebuild`, you can still build the
-system closure using:
+### Re-deploying on parsons itself
+
+Simply do:
~~~shell
-nix build .#nixosConfigurations.parsons.config.system.build.toplevel
+nixos-rebuild --flake .#parsons [test|switch|dry-activate]
~~~
-(but you might have trouble deploying it)
-
-## Secret management
-
-We use [sops-nix](https://github.com/Mic92/sops-nix) to manage secrets which we'd
-like to have in Git but don't want to be public. Entires in `secrets.yaml` are
-encrypted for each of the age keys listed in `.sops.yaml`, which are themselves
-derived from ssh keys.
-
-For the initial set up, please take a look at the sops-nix Readme file.
-
-To edit the secrets file, just use `sops secrets.yaml`, which will decrypt the
-file & open it in your $EDITOR, then re-encrypt it when you're done.
-
-To add a new key, use `ssh-to-age` to convert your ssh key to age, and add it to
-`sops.yaml`. Then do `sops updatekeys secrets.yaml` to re-encrypt the file for
-the new set of keys.
-
## Working on websites
Websites are exposed as flake outputs: if you're working on a website & want to
diff --git a/docs/_index.md b/docs/_index.md
new file mode 100644
index 0000000..9cf3041
--- /dev/null
+++ b/docs/_index.md
@@ -0,0 +1,7 @@
++++
+title = "hacc infra documentation"
+page_template = "doc-page.html"
+sort_by="title"
++++
+
+
diff --git a/docs/auth.md b/docs/auth.md
new file mode 100644
index 0000000..6efed0e
--- /dev/null
+++ b/docs/auth.md
@@ -0,0 +1,10 @@
++++
+title = "Authentication"
+categories = [ "services", "uffd" ]
++++
+
+We use [uffd](https://git.cccv.de/uffd/uffd) for our SSO, for better or worse.
+Mostly for worse.
+
+
+
diff --git a/docs/domains.md b/docs/domains.md
new file mode 100644
index 0000000..237a876
--- /dev/null
+++ b/docs/domains.md
@@ -0,0 +1,20 @@
++++
+title = "Domains"
+categories = [ "domains", "meta" ]
++++
+
+Perhaps too many of them.
+
+## Domains
+
+| domain | mc | status | date | reseller | owner | custody |
+| :------------------- | :-: | :-----: | :------: | :---------- | :--------- | :-----: |
+| 4future.dev | yes | | | | | hacc e.V. |
+| infra4future.de | yes | | | | | hacc e.V. |
+| hacc.space | yes | | | | | hacc e.V. |
+| hacc.earth | yes | | | | | hacc e.V. |
+| hacc.media | yes | | | | | hacc e.V. |
+| hacc.wiki | no | | | | | |
+
+mc = managed by cloudflare
+status = (renewl | autorenewl | expires)
diff --git a/docs/hostnames.md b/docs/hostnames.md
new file mode 100644
index 0000000..ea1dcf8
--- /dev/null
+++ b/docs/hostnames.md
@@ -0,0 +1,16 @@
++++
+title = "Hostname schema"
++++
+
+[Badass Anarchist Women](https://listverse.com/2018/09/27/10-absolutely-badass-anarchist-women-who-challenged-the-system/)
+- keller
+- deCleyre
+- davidNeel
+- leGuin
+- [parsons](../parsons)
+- ohair
+- berneri
+- michel
+- sanger
+- goldman
+
diff --git a/docs/lxc.md b/docs/lxc.md
new file mode 100644
index 0000000..a4d354f
--- /dev/null
+++ b/docs/lxc.md
@@ -0,0 +1,17 @@
++++
+title = "LXC"
+categories = [ "lxc" ]
++++
+
+Some things don't easily run on NixOS. For these we have LXC containers running
+debian.
+
+Right now, only onlyoffice is left.
+
+## Useful commands
+ - login to a container as root with a usable shell
+ `lxc-attach -n -- /usr/bin/sudo -i`
+ - restarting the keycloak and ldap containers
+ `lxc-stop -n && lxc-start -n `
+ - restarting their network bridge:
+ `systemctl restart lxcbr0-netdev.services`
diff --git a/docs/rebooting.md b/docs/rebooting.md
new file mode 100644
index 0000000..c7b57e1
--- /dev/null
+++ b/docs/rebooting.md
@@ -0,0 +1,18 @@
++++
+title = "Rebooting Parsons"
+categories = [ "nix" ]
++++
+
+## Check integrity after unexpected shutdown
+These steps are only required if the server shut down unexpectedly or you suspect tampering.
+
+TODO
+
+## Unlock full disk encryption
+Connection to the server via the command listed in the shared password manager.
+Only the Vorstand has access to it!
+
+Enter the passwords for dpool and zroot.
+
+If both are correct, you will be disconnected and the server continues the boot sequence.
+The server should be up after about minute. Please check all services for availability.
diff --git a/docs/secrets.md b/docs/secrets.md
new file mode 100644
index 0000000..31df55b
--- /dev/null
+++ b/docs/secrets.md
@@ -0,0 +1,21 @@
++++
+title = "Secrets"
+categories = [ "services", "sops" ]
++++
+
+## Secret management
+
+We use [sops-nix](https://github.com/Mic92/sops-nix) to manage secrets which we'd
+like to have in Git but don't want to be public. Entries in `secrets.yaml` are
+encrypted for each of the age keys listed in `.sops.yaml`, which are themselves
+derived from ssh keys.
+
+For the initial set up, please take a look at the sops-nix Readme file.
+
+To edit the secrets file, run `sops secrets.yaml`, which will decrypt the
+file & open it in your $EDITOR, then re-encrypt it when you're done.
+
+To add a new key, use `ssh-to-age` to convert your ssh key to age, and add it to
+`sops.yaml`. Then do `sops updatekeys secrets.yaml` to re-encrypt the file for
+the new set of keys.
+
diff --git a/docs/services/_index.md b/docs/services/_index.md
new file mode 100644
index 0000000..59a0b1e
--- /dev/null
+++ b/docs/services/_index.md
@@ -0,0 +1,5 @@
++++
+title = "Services"
+sort_by = "title"
+page_template = "doc-page.html"
++++
diff --git a/docs/services/acme.md b/docs/services/acme.md
new file mode 100644
index 0000000..f1c8a82
--- /dev/null
+++ b/docs/services/acme.md
@@ -0,0 +1,19 @@
++++
+title = "ACME / letsencrypt"
+categories = [ "domain", "https", "ssl", "tls", "Certificates" ]
++++
+
+
+# Usage
+
+We use the ACME module's nginx integration for basically everything. Beware of
+rate limits when redeploying lots of things at once! Let's Encrypt is a little
+picky about those.
+
+
+## Workarounds & peculiar configuration choices
+
+Certs live under `/var/lib/acme/`
+
+If you need to remove a cert for whatever reason, be aware that there is a
+hidden `.lego` folder, that contains state as well
diff --git a/docs/services/hedgedoc.md b/docs/services/hedgedoc.md
new file mode 100644
index 0000000..83b6d42
--- /dev/null
+++ b/docs/services/hedgedoc.md
@@ -0,0 +1,68 @@
++++
+title = "hedgedoc"
+taxonomies.categories = [ "services" ]
++++
+
+
+hegedoc was once called codiMD, so container, config and users are still called codimd.
+
+**Do NOT change this** unless you're sure what you're doing.
+
+We have two instances:
+ - `pad-hacc`/pad.hacc.space is connected to our SSO/uffd
+ - `pad-i4f`/pad.infra4future.de is not connected to our SSO and meant to be more public
+
+## Basic Troubleshooting
+
+Usually if hedgedoc dies, it's because postgresql wasn't there yet. Just restart
+hedgedoc.
+
+## More Troubles
+log into the container and take a look at the logs
+
+~~~shell
+sudo nixos-container root-login codimd
+journalctl -e
+~~~
+
+### fixing failed database upgrades
+
+see https://docs.hedgedoc.org/guides/migration-troubleshooting/ (copied below
+for convenience?):
+
+In some cases, HedgeDoc might apply migrations without correctly saving the
+progress. It will then refuse to start with "already exists"-errors like
+ERROR: type "enum_Notes_permission" already exists.
+
+Get the name of the failing migration and append .js to it. For example, if
+you encounter this error:
+
+~~~
+== 20180306150303-fix-enum: migrating =======
+
+ERROR: type "enum_Notes_permission" already exists
+~~~
+
+the name of the failed migration would be 20180306150303-fix-enum.js.
+
+The SQL-statement may look like this:
+
+~~~
+INSERT INTO "SequelizeMeta" (name) VALUES ('20180306150303-fix-enum.js');
+~~~
+
+Make sure HedgeDoc does not run and insert the name into the SequelizeMeta table.
+Enter the container switch to the postgres user, open psql and commect to the
+codimd database:
+
+~~~shell
+su postgres
+psql
+\l
+\c codimd
+UN adjusted SQL STAMEMENT from above ]
+\q
+~~~
+
+Start HedgeDoc again and observe if it starts correctly. It may be necessary to
+repeat this process and insert multiple migrations into the SequelizeMeta table.
diff --git a/docs/services/mail.md b/docs/services/mail.md
new file mode 100644
index 0000000..b6814a2
--- /dev/null
+++ b/docs/services/mail.md
@@ -0,0 +1,65 @@
++++
+title = "mail"
+taxonomies.categories = [ "services" ]
++++
+
+Mail is not connected to our SSO!
+
+## adding a mail account
+- We use `@hacc.space` for our mails
+- `@infra4future.de` is reserved for services, old user accounts will be
+ forwarded & logins disabled
+- choose a name (no aliases or other names can be the same)
+- generate a sha-512 password hash ```mkpasswd -m sha-512``` - **never add an
+ unhashed password!**
+- add your account to `loginAccounts =` in `//parsons/mail.nix`
+- build and redeploy parsons
+
+**example:**
+```
+zwoelfontheshelf@hacc.space" = {
+ hashedPassword = "$6$ISAaU8X6D$oGKe9WXDWrRpGzHUEdxrxdtgvzuGOkBMuDc82IZhegpsv1bqd550FhZZrI40IjZTA5Hy2MZ8j/0efpnQ4fOQH0";
+};
+```
+
+## adding to a forward address
+- add the mail address to the corresponding `extraVirtualAliases =`
+- build and redeploy parsons
+
+## adding a forward address
+- add the address to `extraVirtualAliases =`
+- add the addresses it should forward to
+- build and redeploy parsons
+
+**example:**
+```
+"himmel@hacc.space" = [
+ "hexchen@hacc.space"
+ "zauberberg@hacc.space"
+];
+```
+
+## sending & receiving mail
+
+### as a user
+- Your mail client should auto configure correctly
+
+~~~
+mailserver: mail.hacc.space (everywhere)
+username: $your_mail_address
+sending via smtp: port 587 or 465
+recieving
+ imap: port 993
+TLS and STARTTLS are supported
+~~~
+
+- You can send mail as you and any alias you receive mail from. Set a second Identity in your e-mail client
+
+### as an application
+- mailserver: `mail.hacc.space`
+- Do **not** use port 25. It's for server to server communication only.
+- Use smtp ports `587` or `465`
+- enable TLS if possible
+- only send mail from `noreply@infra4future.de`
+- Password is somewhere (TODO!)
+
diff --git a/docs/services/mumble.md b/docs/services/mumble.md
new file mode 100644
index 0000000..7591723
--- /dev/null
+++ b/docs/services/mumble.md
@@ -0,0 +1,40 @@
++++
+title = "mumble"
+taxonomies.categories = [ "mumble" ]
++++
+
+
+[offical Docmuentation](https://wiki.mumble.info/wiki/Main_Page)
+
+Mumble's server is called murmur, but the naming is inconsistent. Sometimes
+it's also just called mumble server.
+
+# Usage
+
+## registration
+Users need to be registered to join any other channel than public.
+An already registered user has to register them with the server.
+1. right click on the username
+2. choose register in the menu. Done.
+
+## restricted channels
+Every channel in the hacc category except for plenum can only be accessed by
+members of the hacc group.
+
+## adding users to a group
+Only admins can edit groups, and only registered users can be added to groups.
+1. right click on the Root channel
+2. select Edit...
+2. In Groups select $groupname
+3. make the change you want to make
+4. click "OK"
+
+# Config details
+- the server is not registered with mumble & not on the public server list
+- the bitrate is set to 128kb/s; otherwise the client would complain that the
+ server bitrate is less then the configured (default) in its local settings
+
+# Hacks
+- murmur needs a TLS cert, which we get via the ACME module
+- there's a funny group setup so that hopefully murmurd can read the cert
+- this seems to work fine now, but was some source of trouble in the past
diff --git a/docs/services/service.template.md b/docs/services/service.template.md
new file mode 100644
index 0000000..b8a2517
--- /dev/null
+++ b/docs/services/service.template.md
@@ -0,0 +1,18 @@
++++
+title = "$Service Name"
+draft = true ## Remove this line to make file appear on website
++++
+
+
+
+# Usage
+
+
+# Config Notes
+
+
+## Updating
+
+
+# Hacks
+
diff --git a/docs/snapshots.md b/docs/snapshots.md
new file mode 100644
index 0000000..23f506c
--- /dev/null
+++ b/docs/snapshots.md
@@ -0,0 +1,24 @@
++++
+title = "Use ZFS snapshot"
+taxonomies.categories = [ "zfs", "snapshot", "filesystem", "backup", "update", "upgrade" ]
++++
+
+## Make a ZFS snapshot
+~~~shell
+sudo zfs snapshot zroot/safe/persist@
+~~~
+
+## Rollback
+
+### single files
+The snapshots can be accessed under `/.zfs/snapshot/...`
+
+### fully
+~~~shell
+sudo zfs rollback zroot/safe/persist@
+~~~
+
+## Delete a ZFS snapshot
+~~~shell
+sudo zfs destroy zroot/safe/persist@
+~~~
diff --git a/flake.nix b/flake.nix
index bfc8dd8..c57ae6e 100644
--- a/flake.nix
+++ b/flake.nix
@@ -71,7 +71,7 @@
nixosConfigurations.parsons = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
- ./hosts/parsons/configuration.nix
+ ./parsons/configuration.nix
sops-nix.nixosModules.sops
{ nixpkgs.pkgs = pkgs; }
{ environment.etc."haccfiles".source = self.outPath; }
@@ -98,7 +98,10 @@
deploy-rs.lib;
packages.x86_64-linux =
- self.nixosConfigurations.parsons.config.hacc.websites.builders;
+ let
+ websites = self.nixosConfigurations.parsons.config.hacc.websites.builders;
+ in
+ { docs = websites."docs.hacc.space"; } // websites;
};
}
diff --git a/hosts/parsons/configuration.nix b/parsons/configuration.nix
similarity index 78%
rename from hosts/parsons/configuration.nix
rename to parsons/configuration.nix
index 31381d4..9366ef2 100644
--- a/hosts/parsons/configuration.nix
+++ b/parsons/configuration.nix
@@ -2,34 +2,27 @@
{
imports = [
- ../../common
+ ../common
./hardware.nix
modules.encboot
modules.network.nftables
modules.nftnat
sources.nix-hexchen.nixosModules.profiles.nopersist
-
- ../../services/nextcloud.nix
- ../../services/mattermost.nix
- ../../services/thelounge.nix
- ../../services/murmur.nix
- ../../services/hedgedoc-hacc.nix
- ../../services/hedgedoc-i4f.nix
- ../../services/mail.nix
- ../../services/gitea.nix
- ../../services/nginx-pages.nix
- ../../services/vaultwarden.nix
- ../../services/tracktrain.nix
- ../../services/uffd.nix
-
+ ./nextcloud.nix
+ ./mattermost.nix
+ ./murmur.nix
+ ./hedgedoc-hacc.nix
+ ./hedgedoc-i4f.nix
+ ./mail.nix
+ ./gitea.nix
+ ./nginx-pages.nix
+ ./vaultwarden.nix
+ ./tracktrain.nix
+ ./uffd.nix
./lxc.nix
];
hexchen.bindmounts."/var/lib/acme" = "/persist/var/lib/acme";
- # fileSystems."/var/lib/acme" = {
- # device = "/persist/var/lib/acme";
- # fsType = "bind";
- # };
hexchen.encboot = {
enable = true;
@@ -37,7 +30,7 @@
networkDrivers = [ "igb" ];
};
- sops.defaultSopsFile = ../../secrets.yaml;
+ sops.defaultSopsFile = ../secrets.yaml;
sops.age.sshKeyPaths = [ "/persist/ssh/ssh_host_ed25519_key" ];
boot.loader.grub.enable = true;
diff --git a/services/gitea.nix b/parsons/gitea.nix
similarity index 100%
rename from services/gitea.nix
rename to parsons/gitea.nix
diff --git a/hosts/parsons/hardware.nix b/parsons/hardware.nix
similarity index 100%
rename from hosts/parsons/hardware.nix
rename to parsons/hardware.nix
diff --git a/services/hedgedoc-hacc.nix b/parsons/hedgedoc-hacc.nix
similarity index 100%
rename from services/hedgedoc-hacc.nix
rename to parsons/hedgedoc-hacc.nix
diff --git a/services/hedgedoc-i4f.nix b/parsons/hedgedoc-i4f.nix
similarity index 100%
rename from services/hedgedoc-i4f.nix
rename to parsons/hedgedoc-i4f.nix
diff --git a/hosts/parsons/lxc.nix b/parsons/lxc.nix
similarity index 100%
rename from hosts/parsons/lxc.nix
rename to parsons/lxc.nix
diff --git a/services/mail.nix b/parsons/mail.nix
similarity index 100%
rename from services/mail.nix
rename to parsons/mail.nix
diff --git a/services/mattermost.nix b/parsons/mattermost.nix
similarity index 100%
rename from services/mattermost.nix
rename to parsons/mattermost.nix
diff --git a/services/murmur.nix b/parsons/murmur.nix
similarity index 100%
rename from services/murmur.nix
rename to parsons/murmur.nix
diff --git a/services/nextcloud.nix b/parsons/nextcloud.nix
similarity index 100%
rename from services/nextcloud.nix
rename to parsons/nextcloud.nix
diff --git a/services/nginx-pages.nix b/parsons/nginx-pages.nix
similarity index 100%
rename from services/nginx-pages.nix
rename to parsons/nginx-pages.nix
diff --git a/services/tracktrain.nix b/parsons/tracktrain.nix
similarity index 100%
rename from services/tracktrain.nix
rename to parsons/tracktrain.nix
diff --git a/services/uffd.nix b/parsons/uffd.nix
similarity index 100%
rename from services/uffd.nix
rename to parsons/uffd.nix
diff --git a/services/vaultwarden.nix b/parsons/vaultwarden.nix
similarity index 100%
rename from services/vaultwarden.nix
rename to parsons/vaultwarden.nix
diff --git a/pkgs/default.nix b/pkgs/default.nix
index e45732a..3c14078 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -8,46 +8,15 @@ let
newpkgs = {
- mattermost = callPackage ./mattermost {inherit sources;};
+ mattermost = callPackage ./mattermost.nix {inherit sources;};
tracktrain = import sources.tracktrain {
nixpkgs = pkgs;
compiler = "default";
};
- # a version of the lounge with some extra css that
- # hides things the hacc-voc doesn't need
- thelounge = pkgs.stdenv.mkDerivation {
- name = "thelounge-hacked";
- src = pkgs.thelounge;
-
- phases = [ "buildPhase" "installPhase" ];
- buildPhase = ''
- cp $src/* -r .
- chmod 777 lib/node_modules/thelounge/public/css/style.css
- cat ${./thelounge/css-patch.css} >> lib/node_modules/thelounge/public/css/style.css
- '';
-
- installPhase = ''
- mkdir -p $out
- cp * -r $out
- '';
- meta.mainProgram = "thelounge";
- };
-
uffd = oldstable.callPackage ./uffd { };
- netbox = pkgs.netbox.override (super: rec {
- python3 = super.python3.override (oldpy: {
- packageOverrides = self: super: {
- social-auth-core = super.social-auth-core.overrideAttrs ( old: {
- name = "social-auth-with-uffd";
- patches = [ ./netbox/0001-add-uffd-oauth2-backend.patch ];
- });
- };
- });
- });
-
inherit (oldstable) uwsgi flask;
};
diff --git a/pkgs/mattermost/default.nix b/pkgs/mattermost.nix
similarity index 100%
rename from pkgs/mattermost/default.nix
rename to pkgs/mattermost.nix
diff --git a/pkgs/netbox/0001-add-uffd-oauth2-backend.patch b/pkgs/netbox/0001-add-uffd-oauth2-backend.patch
deleted file mode 100644
index 07e9507..0000000
--- a/pkgs/netbox/0001-add-uffd-oauth2-backend.patch
+++ /dev/null
@@ -1,70 +0,0 @@
-From 00e282e32b46bb4b6040dc3810599c693306c0ec Mon Sep 17 00:00:00 2001
-From: David Croft
-Date: Thu, 24 Mar 2022 11:09:14 +0000
-Subject: [PATCH] add uffd oauth2 backend
-
----
- social_core/backends/uffd.py | 51 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 51 insertions(+)
- create mode 100644 social_core/backends/uffd.py
-
-diff --git a/social_core/backends/uffd.py b/social_core/backends/uffd.py
-new file mode 100644
-index 00000000..fb8ffb62
---- /dev/null
-+++ b/social_core/backends/uffd.py
-@@ -0,0 +1,51 @@
-+from urllib.parse import urlencode
-+
-+from .oauth import BaseOAuth2
-+
-+
-+class UffdOAuth2(BaseOAuth2):
-+ """Uffd OAuth2 authentication backend
-+
-+ You need to set the following config:
-+ SOCIAL_AUTH_UFFD_KEY - client id
-+ SOCIAL_AUTH_UFFD_SECRET - client secret
-+ SOCIAL_AUTH_UFFD_BASE_URL - base url to uffd installation
-+ """
-+
-+ name = 'uffd'
-+ ACCESS_TOKEN_METHOD = 'POST'
-+ REFRESH_TOKEN_METHOD = 'POST'
-+ SCOPE_SEPARATOR = ' '
-+ STATE_PARAMETER = True
-+ REDIRECT_STATE = False
-+ EXTRA_DATA = [
-+ ('id', 'id'),
-+ ]
-+
-+ def get_user_details(self, response):
-+ """Return user details from a Uffd account"""
-+ fullname, first_name, last_name = self.get_user_names(fullname=response.get('name'))
-+ return {
-+ 'username': response.get('nickname'),
-+ 'email': response.get('email') or '',
-+ 'fullname': fullname,
-+ 'first_name': first_name,
-+ 'last_name': last_name,
-+ }
-+
-+ def user_data(self, access_token, *args, **kwargs):
-+ """Loads user data from service"""
-+ url = self.userinfo_url() + '?' + urlencode({'access_token': access_token})
-+ try:
-+ return self.get_json(url)
-+ except ValueError:
-+ return None
-+
-+ def authorization_url(self):
-+ return self.setting('BASE_URL') + '/oauth2/authorize'
-+
-+ def access_token_url(self):
-+ return self.setting('BASE_URL') + '/oauth2/token'
-+
-+ def userinfo_url(self):
-+ return self.setting('BASE_URL') + '/oauth2/userinfo'
---
-2.38.1
-
diff --git a/pkgs/thelounge/css-patch.css b/pkgs/thelounge/css-patch.css
deleted file mode 100644
index 0d058b6..0000000
--- a/pkgs/thelounge/css-patch.css
+++ /dev/null
@@ -1,24 +0,0 @@
-
-/* Hides extra fields on connect screen */
-.connect-row:nth-of-type(4) {
- display: none !important;
-}
-
-.connect-row:nth-of-type(2) {
- display: none !important;
-}
-
-.connect-row:nth-of-type(5) {
- display: none !important;
-}
-
-
-/* Hides side panel button */
-.header > button:first-child {
- display: none !important;
-}
-
-/* Hides channel options button (includes leave option) */
-.header > button:nth-last-child(2) {
- display: none !important;
-}
diff --git a/services/thelounge.nix b/services/thelounge.nix
deleted file mode 100644
index be7b556..0000000
--- a/services/thelounge.nix
+++ /dev/null
@@ -1,65 +0,0 @@
-{ config, lib, pkgs, evalConfig, ... }:
-
-{
- containers.thelounge = {
- autoStart = true;
- privateNetwork = true;
- hostAddress = "192.168.100.1";
- localAddress = "192.168.100.4";
- bindMounts = {
- "/var/lib/thelounge" = {
- hostPath = "/persist/containers/thelounge";
- isReadOnly = false;
- };
- };
-
- path = evalConfig ({ config, lib, ... }: {
- services.thelounge = {
- enable = true;
-
- extraConfig = {
- public = true;
- package = pkgs.thelounge;
-
- # respect X-Forwarded-For
- reverseProxy = true;
- defaults = {
- name = "libera chat";
- host = "irc.eu.libera.chat";
- port = 6697;
- # encrypt things!
- tls = true;
- # yes, please do actually check the cert …
- rejectUnauthorized = true;
- nick = "haccGuest%%%%";
- join = "#hacc-webchat";
- };
- lockNetwork = true;
-
- # don't log messages (default is text / sqlite)
- messageStorage = [];
-
- # darker theme
- #theme = "morning";
-
- # these three should result in having link previews
- # which are fetched only by the server, then proxied
- # (i.e. clients won't directly connect to arbitrary
- # domains to get previews)
- prefetch = true;
- prefetchStorage = true;
- disableMediaPreview = true;
-
- leaveMessage = "happy haccing";
- };
- };
- });
- };
-
- services.nginx.virtualHosts."webchat.voc.hacc.space" = {
- locations."/".proxyPass =
- "http://${config.containers.thelounge.localAddress}:9000";
- enableACME = true;
- forceSSL = true;
- };
-}
diff --git a/websites/docs.hacc.space/Readme.md b/websites/docs.hacc.space/Readme.md
new file mode 100644
index 0000000..536c2c7
--- /dev/null
+++ b/websites/docs.hacc.space/Readme.md
@@ -0,0 +1,44 @@
+# Markdown docs with zola.
+
+[Zola](https://www.getzola.org/) is a static site generated written in Rust
+(which you'll notice since sometimes it panics).
+
+To run the site locally:
+
+```
+zola serve
+```
+
+## Directory Layout
+
+All the important stuff goes into `content`. If you create subdirectories, make
+sure you remembered to also create an `_index.md` file for it (if in doubt, just
+copy the one at `content/_index.md`); otherwise pages in there won't work.
+
+`templates` is *not* for site templates, but specifies how markdown files should
+be turned into html. If an autogenerated link broke, you'll probably have to
+change something in there. `sass` and `static` do exactly what they sound like.
+
+It usually shouldn't be necessary to change `config.toml`, but if it is, [here
+is the list of all available options](https://www.getzola.org/documentation/getting-started/configuration/).
+
+
+## File Layout
+
+Markdown files start with a frontmatter that should look something like so:
+
+```markdown
++++
+title = "blåhaj"
+taxonomies.categories = [ "flausch" ]
++++
+
+[actual markdown goes here]
+```
+
+The frontmatter is TOML; the `taxonomies.*` keys are special and can be used to
+aggregate posts if enabled in `config.toml` (currently that's only the case for
+`categories`, though). See also the [list of all available keys](https://www.getzola.org/documentation/content/page/).
+
+Please don't repeat the page's title in markdown, otherwise it'll appear twice
+in the html.
diff --git a/websites/docs.hacc.space/config.toml b/websites/docs.hacc.space/config.toml
new file mode 100644
index 0000000..c890821
--- /dev/null
+++ b/websites/docs.hacc.space/config.toml
@@ -0,0 +1,35 @@
+# Zola's configuration file,
+#
+# see https://www.getzola.org/documentation/getting-started/configuration/
+# for available keys.
+
+# The URL the site will be built for
+base_url = "https://docs.hacc.space"
+
+compile_sass = true
+default_language = "en"
+
+# might be useful — this isn't a blog, obviously, but updates for new entries
+# could still be nice, I guess
+generate_feed = true
+feed_filename = "atom.xml"
+
+build_search_index = true
+
+taxonomies = [
+ { name = "categories", feed = false},
+]
+
+[markdown]
+highlight_code = true
+
+[extra] # user-defined keys
+
+# site title text
+main_title = "haccfiles documentation"
+
+# navbar entries
+main_menu = [
+ {url = "$BASE_URL", name = "Home"},
+ {url = "$BASE_URL/categories", name = "Categories"}
+]
diff --git a/websites/docs.hacc.space/content b/websites/docs.hacc.space/content
new file mode 120000
index 0000000..92a7f82
--- /dev/null
+++ b/websites/docs.hacc.space/content
@@ -0,0 +1 @@
+../../docs
\ No newline at end of file
diff --git a/websites/docs.hacc.space/default.nix b/websites/docs.hacc.space/default.nix
new file mode 100644
index 0000000..c1c5453
--- /dev/null
+++ b/websites/docs.hacc.space/default.nix
@@ -0,0 +1,19 @@
+{ stdenvNoCC, zola, writeScriptBin }:
+
+stdenvNoCC.mkDerivation rec {
+ name = "docs.hacc.space-static";
+
+ src = ./.;
+
+ phases = [ "buildPhase" ];
+ buildInputs = [ zola ];
+ buildPhase = ''
+ cd $src
+ mkdir -p $out
+ zola build --output-dir $out
+ '';
+
+ watch = writeScriptBin "watch" ''
+ ${zola}/bin/zola serve ${src} "$@"
+ '';
+}
diff --git a/websites/docs.hacc.space/sass/style.scss b/websites/docs.hacc.space/sass/style.scss
new file mode 100644
index 0000000..2fd384b
--- /dev/null
+++ b/websites/docs.hacc.space/sass/style.scss
@@ -0,0 +1,168 @@
+
+@font-face {
+ font-family: 'share-tech';
+ src: url('ShareTech-Regular.ttf') format('truetype');
+}
+
+html {
+ overflow: hidden;
+ height: 100%;
+}
+
+body {
+ background-color: #000;
+ color: #fff;
+
+ overflow-y: auto;
+ overflow-x: hidden;
+ height: 100%;
+}
+
+#content {
+ font-family: 'share-tech';
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 60em;
+ font-size: 16pt;
+ position: relative;
+}
+
+p.subtitle {
+ font-size: 14pt;
+ font-style: normal;
+ color: gray;
+}
+
+article {
+ margin-bottom: 4em;
+}
+
+#searchresults {
+ width: 90%;
+ text-align: right;
+ right: 0;
+ position: absolute;
+ background-color: black;
+ top: 5em;
+ color: gray;
+}
+
+#searchresults div {
+ padding: 0.5em;
+ border-top: 1px dashed gray;
+}
+
+#searchresults div:last-child {
+ border-bottom: 1px dashed gray;
+}
+
+.searchresultprevtext {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ width: 40em;
+ float: left;
+}
+
+footer.content {
+ top: 50px;
+ color: #cccccc;
+ font-size: 14px;
+ margin-bottom: 4em;
+}
+
+footer a {
+ color: #cccccc;
+}
+
+.logo {
+ position: relative;
+ width: 100%;
+}
+.logo > img {
+ width: 300px;
+ max-width: 100%;
+}
+
+h1 {
+ font-size: 32pt;
+}
+
+#headernav {
+ text-align: right;
+ margin: 1em;
+}
+
+#headernav > a {
+ padding: 0.2em;
+ font-size: 20pt;
+ font-family: share-tech;
+}
+
+h1, h2, h3, h4, h5, h6{
+ font-weight: 600;
+ display: inline;
+ font-family: share-tech;
+ background: rgb(59,115,185);
+ background: linear-gradient(90deg, rgb(59, 115, 185) 0%, rgb(229, 35, 33) 100%);
+ background-clip: border-box;
+ color: transparent;
+ -webkit-background-clip: text;
+ background-clip: text;
+}
+/*
+h4 {
+ //font-weight: 600;
+ //display: inline;
+ font-family: share-tech;
+ //background: rgb(59,115,185);
+ //background: linear-gradient(90deg, rgb(59, 115, 185) 0%, rgb(229, 35, 33) 100%);
+ //background-clip: border-box;
+ color: #fff;
+ //-webkit-background-clip: text;
+ //background-clip: text;
+}
+*/
+a {
+ text-decoration: none;
+ color: #3b73b9;
+ transition: color .1s linear;
+}
+a:hover {
+ /*color: #e52321;*/
+ color: #4e9af9;
+}
+
+ul {
+ margin-top: 0;
+}
+
+
+pre {
+ padding: 1rem;
+ overflow: auto;
+ font-size: 16px;
+}
+// The line numbers already provide some kind of left/right padding
+pre[data-linenos] {
+ padding: 1rem 0;
+}
+pre table td {
+ padding: 0;
+}
+// The line number cells
+pre table td:nth-of-type(1) {
+ text-align: center;
+ user-select: none;
+}
+pre mark {
+ // If you want your highlights to take the full width.
+ display: block;
+ // The default background colour of a mark is bright yellow
+ background-color: rgba(254, 252, 232, 0.9);
+}
+pre table {
+ width: 100%;
+ border-collapse: collapse;
+}
diff --git a/websites/docs.hacc.space/static/ShareTech-Regular.ttf b/websites/docs.hacc.space/static/ShareTech-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..787ba839aaf259b220c4360211ca6a71536cccdd
GIT binary patch
literal 29536
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
literal 0
HcmV?d00001
diff --git a/websites/docs.hacc.space/static/search.js b/websites/docs.hacc.space/static/search.js
new file mode 100644
index 0000000..183095d
--- /dev/null
+++ b/websites/docs.hacc.space/static/search.js
@@ -0,0 +1,52 @@
+
+function mkDiv (res) {
+ let div = document.createElement("div");
+ let a = document.createElement("a");
+ a.innerText = res.doc.title;
+ a.href = res.ref;
+ let text = document.createElement("span");
+ text.innerText = res.doc.body.slice(0,400).replaceAll("\n", " ");
+ text.classList.add("searchresultprevtext");
+
+ div.appendChild(text);
+ div.appendChild(a);
+ return div;
+}
+
+window.onload = () => {
+ console.log("hello!")
+ let searchbox = document.getElementById("searchbox");
+ searchbox.innerHTML =
+ " \
+ ";
+
+ let searchinput = document.getElementById("searchinput");
+ let searchresults = document.getElementById("searchresults");
+
+ let index = elasticlunr.Index.load(window.searchIndex);
+
+ searchinput.addEventListener("keyup", () => {
+ let term = searchinput.value.trim();
+ let results = [];
+ if (term !== "") {
+ results = index.search(term, {});
+ console.log(results);
+
+ while (searchresults.lastChild) {
+ searchresults.removeChild(searchresults.lastChild)
+ }
+ if (results.length !== 0) {
+ let resultdivs = results.map(mkDiv);
+ resultdivs.map((div) => searchresults.appendChild(div));
+ } else {
+ searchresults.innerHTML =
+ term.length <= 4 ?
+ "Need at least four characters to search.
"
+ : "No results here.
";
+ }
+ searchresults.style.display = "initial";
+ } else {
+ searchresults.style.display = "none";
+ }
+ });
+}
diff --git a/websites/docs.hacc.space/templates/base.html b/websites/docs.hacc.space/templates/base.html
new file mode 100644
index 0000000..2810c83
--- /dev/null
+++ b/websites/docs.hacc.space/templates/base.html
@@ -0,0 +1,43 @@
+
+
+
+
+ {{ config.extra.main_title }}
+
+
+
+
+
+
+ {% block head_extra %}
+ {% endblock head_extra %}
+
+
+
+
+
+ {% block content %} {% endblock %}
+
+
+
+
+
+
diff --git a/websites/docs.hacc.space/templates/blog.html b/websites/docs.hacc.space/templates/blog.html
new file mode 100644
index 0000000..63775bc
--- /dev/null
+++ b/websites/docs.hacc.space/templates/blog.html
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+ {{ section.title }}
+
+
+{% endblock content %}
diff --git a/websites/docs.hacc.space/templates/categories/list.html b/websites/docs.hacc.space/templates/categories/list.html
new file mode 100644
index 0000000..1394c85
--- /dev/null
+++ b/websites/docs.hacc.space/templates/categories/list.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+
+ Categories
+
+ {% if terms %}
+
+ {% for term in terms %}
+ - {{ term.name }} ({{ term.pages | length }})
+ {% endfor %}
+
+ {% endif %}
+
+
+{% endblock content %}
diff --git a/websites/docs.hacc.space/templates/categories/single.html b/websites/docs.hacc.space/templates/categories/single.html
new file mode 100644
index 0000000..69133a4
--- /dev/null
+++ b/websites/docs.hacc.space/templates/categories/single.html
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+
+{% import "post_macros.html" as post_macros %}
+{% block content %}
+
+Posts in category "{{ term.name }}"
+ {% for page in term.pages %}
+
+
+
+ {% endfor %}
+{% endblock content %}
diff --git a/websites/docs.hacc.space/templates/doc-page.html b/websites/docs.hacc.space/templates/doc-page.html
new file mode 100644
index 0000000..f433657
--- /dev/null
+++ b/websites/docs.hacc.space/templates/doc-page.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{% import "post_macros.html" as post_macros %}
+{% block content %}
+
+
+
+ {{ page.content | safe }}
+
+
+{% endblock content %}
diff --git a/websites/docs.hacc.space/templates/index.html b/websites/docs.hacc.space/templates/index.html
new file mode 100644
index 0000000..8fae414
--- /dev/null
+++ b/websites/docs.hacc.space/templates/index.html
@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+ {% for page in section.pages %}
+
+
+
+ {% endfor %}
+
+ {% for sub in section.subsections %}
+ {% set subsection = get_section(path=sub) %}
+
+
+ {% for page in subsection.pages %}
+
+
+
+ {% endfor %}
+
+ {% endfor %}
+
+{% endblock content %}
diff --git a/websites/docs.hacc.space/templates/post_macros.html b/websites/docs.hacc.space/templates/post_macros.html
new file mode 100644
index 0000000..1fb7e9b
--- /dev/null
+++ b/websites/docs.hacc.space/templates/post_macros.html
@@ -0,0 +1,15 @@
+{% macro paginator_nav(paginator) %}
+
+
+
+{% endmacro paginator_nav %}
diff --git a/websites/docs.hacc.space/templates/section.html b/websites/docs.hacc.space/templates/section.html
new file mode 100644
index 0000000..d3a223b
--- /dev/null
+++ b/websites/docs.hacc.space/templates/section.html
@@ -0,0 +1,15 @@
+{% extends "index.html" %}
+
+{% block content %}
+ {{ section.title }}
+ {{ section.content | safe }}
+ Pages in section
+
+ {% for page in section.pages | reverse %}
+ -
+ {{ page.title }}
+
+ {% endfor %}
+
+
+{% endblock content %}