about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmile <git@emile.space>2024-03-25 15:32:47 +0100
committerEmile <git@emile.space>2024-03-25 15:32:47 +0100
commit27c9efa48891a7bb69ba0ef642e18cf5884a9e23 (patch)
tree5bda74a691749cb8c7b054074cba18d0e6ee022f
parent22c9f80f14c598c8e38bb26d4f90706e76742946 (diff)
authelia
-rw-r--r--README.md16
-rw-r--r--nix/hosts/corrino/configuration.nix47
-rw-r--r--nix/hosts/corrino/modules/authelia.emile.space.nix194
-rw-r--r--nix/hosts/corrino/secrets/authelia_jwt_secret.age8
-rw-r--r--nix/hosts/corrino/secrets/authelia_mail_password.agebin0 -> 387 bytes
-rw-r--r--nix/hosts/corrino/secrets/authelia_oidc_hmac_secret.age7
-rw-r--r--nix/hosts/corrino/secrets/authelia_oidc_issuer_private_key.agebin0 -> 2026 bytes
-rw-r--r--nix/hosts/corrino/secrets/authelia_session_secret.agebin0 -> 387 bytes
-rw-r--r--nix/hosts/corrino/secrets/authelia_storage_encryption_key.age7
-rw-r--r--nix/hosts/corrino/secrets/hedgedoc_environment_variables.agebin0 -> 875 bytes
-rw-r--r--nix/hosts/corrino/secrets/restic_password.agebin0 -> 387 bytes
-rw-r--r--nix/hosts/corrino/www/git.emile.space.nix95
-rw-r--r--nix/hosts/corrino/www/md.emile.space.nix102
-rw-r--r--nix/hosts/corrino/www/znc.emile.space.nix73
14 files changed, 499 insertions, 50 deletions
diff --git a/README.md b/README.md
index 8096032..781b830 100644
--- a/README.md
+++ b/README.md
@@ -4,10 +4,22 @@
 
 - Managed using agenix
 - Don't forget to add secrets to git!
-- Edit secrets such as below
 
+Create secrets:
 ```bash
-; EDITOR=hx nix run git+https://github.com/ryantm/agenix -- -e nix/hosts/corrino/secrets/pretix.age
+./secret_create.sh
+```
+
+Edit secrets:
+
+```bash
+; EDITOR=hx nix run git+https://github.com/ryantm/agenix -- -e <secret>
+```
+
+Print the generated secrets file as follows:
+
+```bash
+; nix eval -I nixpkgs=flake:nixpkgs --file secrets.nix
 ```
 
 ## Deploy
diff --git a/nix/hosts/corrino/configuration.nix b/nix/hosts/corrino/configuration.nix
index 7730481..c97d09f 100644
--- a/nix/hosts/corrino/configuration.nix
+++ b/nix/hosts/corrino/configuration.nix
@@ -3,29 +3,31 @@
   imports =
     [ # Include the results of the hardware scan.
       ./hardware-configuration.nix
-      # ./age_secrets.nix
 
+      # web
       ./www/emile.space.nix
       ./www/git.emile.space.nix
       ./www/hydra.emile.space.nix
       ./www/netbox.emile.space.nix
       # ./www/grafana.emile.space.nix
       ./www/photo.emile.space.nix
-
-      
       # ./www/events.emile.space.nix
       ./www/tickets.emile.space.nix
       ./www/talks.emile.space.nix
       ./www/stream.emile.space.nix
-
       ./www/pgweb.emile.space.nix
-
       ./www/ctf.emile.space.nix
+      ./www/md.emile.space.nix
       # ./www/magic-hash.emile.space.nix
-
       # ./www/znc.emile.space.nix
 
+      # gemini
       ./gemini/emile.space.nix
+
+      # general purpose modules
+      ./modules/authelia.emile.space.nix
+
+      # containers
     ];
 
   # Use GRUB2 as the boot loader.
@@ -133,6 +135,8 @@
       # helix
 
       sshfs
+
+      virter
     ];
   };
 
@@ -218,7 +222,7 @@
       enable = true;
       enableIPv6 = true;
       externalInterface = "enp35s0";
-      internalInterfaces = [ "wg0" ];
+      internalInterfaces = [ "wg0" "ve-+"];
     };
 
     wireguard = {
@@ -337,7 +341,7 @@
   # allowed-uris = https://git.emile.space/ https://git.emile.space/ https://portswigger-cdn.net/ https://git.sr.ht/ https://gitlab.com/simple-nixos-mailserver/ https://github.com/nixos/nixpkgs/ http:// https://
     extraOptions = ''
   builders-use-substitutes = true
-  allowed-uris = git.emile.space: gitea@git.emile.space: ssh://gitea@git.emile.space/hanemile/hefe-internal.git
+  allowed-uris = git.emile.space: gitea@git.emile.space: ssh://gitea@git.emile.space/hanemile/hefe-internal.git git+ssh: git+https:
     '';
 
     settings.allowed-uris = [
@@ -394,17 +398,24 @@
 
   virtualisation = {
     docker.enable = true;
-    # libvirtd = {
-    #   enable = true;
-    #   qemu = {
-    #     swtpm.enable = true;
-    #     ovmf.enable = true;
-    #     ovmf.packages = [ pkgs.OVMFFull.fd ];
-    #   };
-    # };
-    # spiceUSBRedirection.enable = true;
+    libvirtd = {
+      enable = true;
+      qemu = {
+        package = pkgs.qemu_kvm;
+        runAsRoot = true;
+        swtpm.enable = true;
+        ovmf = {
+          enable = true;
+          packages = [
+            (pkgs.unstable.OVMF.override {
+              secureBoot = true;
+              tpmSupport = true;
+            }).fd
+          ];
+        };
+      };
+    };
   };
-
   # programs.virt-manager.enable = true;
 
   fileSystems."/proc" = {
diff --git a/nix/hosts/corrino/modules/authelia.emile.space.nix b/nix/hosts/corrino/modules/authelia.emile.space.nix
new file mode 100644
index 0000000..9817f61
--- /dev/null
+++ b/nix/hosts/corrino/modules/authelia.emile.space.nix
@@ -0,0 +1,194 @@
+{ config, pkgs, ... }:
+
+{
+
+	services.nginx.virtualHosts."sso.emile.space" = {
+		forceSSL = true;
+		enableACME = true;
+
+		locations = {
+			"/" = {
+				proxyPass = "http://127.0.0.1:9091";
+
+				extraConfig = ''
+					## Headers
+					proxy_set_header Host $host;
+					proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
+					proxy_set_header X-Forwarded-Proto $scheme;
+					proxy_set_header X-Forwarded-Host $http_host;
+					proxy_set_header X-Forwarded-URI $request_uri;
+					proxy_set_header X-Forwarded-Ssl on;
+					proxy_set_header X-Forwarded-For $remote_addr;
+					proxy_set_header X-Real-IP $remote_addr;
+
+					## Basic Proxy Configuration
+					client_body_buffer_size 128k;
+					proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; ## Timeout if the real server is dead.
+					proxy_redirect  http://  $scheme://;
+					proxy_http_version 1.1;
+					proxy_cache_bypass $cookie_session;
+					proxy_no_cache $cookie_session;
+					proxy_buffers 64 256k;
+
+					## Trusted Proxies Configuration
+					## Please read the following documentation before configuring this:
+					##     https://www.authelia.com/integration/proxies/nginx/#trusted-proxies
+					# set_real_ip_from 10.0.0.0/8;
+					# set_real_ip_from 172.16.0.0/12;
+					# set_real_ip_from 192.168.0.0/16;
+					# set_real_ip_from fc00::/7;
+					set_real_ip_from 127.0.0.1/32;
+					real_ip_header X-Forwarded-For;
+					real_ip_recursive on;
+
+					## Advanced Proxy Configuration
+					send_timeout 5m;
+					proxy_read_timeout 360;
+					proxy_send_timeout 360;
+					proxy_connect_timeout 360;
+				'';
+			};
+
+			"/api/verify" = {
+				proxyPass = "http://127.0.0.1:9091";
+	    };
+
+	    "/api/authz/" = {
+				proxyPass = "http://127.0.0.1:9091";
+	    };
+		};
+	};
+
+	# set the permissions for the secrets...
+	age.secrets = {
+		# ... passwed via environment vars
+		authelia_session_secret.owner = "authelia-main";
+		authelia_session_secret.group = "authelia-main";
+		authelia_mail_password.owner = "authelia-main";
+		authelia_mail_password.group = "authelia-main";
+
+		# ... passed via the services.authelia.instances.main.secrets attribute
+		authelia_storage_encryption_key.owner = "authelia-main";
+		authelia_storage_encryption_key.group = "authelia-main";
+		authelia_jwt_secret.owner = "authelia-main";
+		authelia_jwt_secret.group = "authelia-main";
+		authelia_oidc_issuer_private_key.owner = "authelia-main";
+		authelia_oidc_issuer_private_key.group = "authelia-main";
+		authelia_oidc_hmac_secret.owner = "authelia-main";
+		authelia_oidc_hmac_secret.group = "authelia-main";
+	};
+
+
+	services.authelia.instances = {
+		main = {
+			enable = true;
+			package = pkgs.authelia;
+
+			# pass some of the secrets in as env-vars
+			environmentVariables = with config.age.secrets; {
+				AUTHELIA_SESSION_SECRET_FILE = authelia_session_secret.path;
+				AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = authelia_mail_password.path;
+			};
+			secrets = with config.age.secrets; {
+				manual = true;
+
+				# some other secrets can be defined here, but not all...
+		    storageEncryptionKeyFile = authelia_storage_encryption_key.path;
+		    jwtSecretFile = authelia_jwt_secret.path;
+		    oidcIssuerPrivateKeyFile = authelia_oidc_issuer_private_key.path;
+		    oidcHmacSecretFile = authelia_oidc_hmac_secret.path;
+			};
+			settings = {
+				theme = "dark";
+
+				server = {
+					host = "127.0.0.1";
+					port = 9091;
+				};
+
+				# we're using a file to store the user information
+        authentication_backend = {
+					refresh_interval = "1m";
+					file = {
+						path = "/var/lib/authelia-main/user.yml";
+						watch = true;
+						password = {
+							algorithm = "argon2id";
+							iterations = 3;
+							key_length = 32;
+							salt_length = 16;
+							memory = 65;
+							parallelism = 4;
+						};
+					};
+        };
+
+        storage.local.path = "/var/lib/authelia-main/db.sqlite";
+
+        session = {
+					domain = "sso.emile.space";
+					expiration = 3600; # 1 hour
+					inactivity = 300; # 5 minutes
+        };
+
+				notifier = {
+					disable_startup_check = false;
+					smtp = {
+						host = "mail.emile.space";
+						port = 587;
+						timeout = "30s";
+						username = "mail@emile.space";
+
+						sender = "mail@emile.space";
+						subject = "[Authelia] {title}";
+
+ 						disable_require_tls = false;
+						disable_starttls = false;
+						disable_html_emails = true;
+
+						tls = {
+							server_name = "mail.emile.space";
+							skip_verify = true;
+							minimum_version = "TLS1.3";
+						};
+					};
+				};
+
+				identity_providers = {
+					oidc = {
+						# regenerate keys like this:
+						# ; nix run nixpkgs#authelia -- crypto certificate rsa generate
+						# current serial: deb83f17e27e663f544a16ad2947631d
+
+						enable_client_debug_messages = false;
+					    minimum_parameter_entropy = 8;
+					    enforce_pkce = "public_clients_only";
+					    enable_pkce_plain_challenge = false;
+					    cors = {
+							endpoints = [
+								"authorization"
+								"token"
+								"revocation"
+								"introspection"
+							];
+							allowed_origins = [
+								"https://emile.space"
+							];
+							allowed_origins_from_client_redirect_uris = false;
+						};
+					};
+				};
+
+        access_control = {
+          default_policy = "deny";
+          rules = [
+						{
+							domain = "*.emile.space";
+							policy = "two_factor";
+						}
+					];
+				};
+			};
+		};
+	};
+}
diff --git a/nix/hosts/corrino/secrets/authelia_jwt_secret.age b/nix/hosts/corrino/secrets/authelia_jwt_secret.age
new file mode 100644
index 0000000..9be87cc
--- /dev/null
+++ b/nix/hosts/corrino/secrets/authelia_jwt_secret.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q VtbGsF2Tt1ULvk0uphKdtlYb9pDQ6qyLWgLLuRfGoSs
+1ej6KBYHsYoP86FD1tTutTTtZLaB9Q7RPJOhs0qp4rI
+-> ssh-ed25519 m8VklA VnK3k8GOgjTaVpmMNM9e+7H2CRJLvdildQ1xrR5GdCs
+NSRA/5DEySGP+pAOj5bD4voFDTqHSDQLn3GmHJzbLfM
+--- bQqJjZW7yq51fYLhXYhvIy/yrxqd9brNEkBbyKKIaNU
+'Zff
+cQ&ggN̛tgsqDmnl~Sbm}#>S^ڳZW
u
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/authelia_mail_password.age b/nix/hosts/corrino/secrets/authelia_mail_password.age
new file mode 100644
index 0000000..39792be
--- /dev/null
+++ b/nix/hosts/corrino/secrets/authelia_mail_password.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/authelia_oidc_hmac_secret.age b/nix/hosts/corrino/secrets/authelia_oidc_hmac_secret.age
new file mode 100644
index 0000000..7c1ea38
--- /dev/null
+++ b/nix/hosts/corrino/secrets/authelia_oidc_hmac_secret.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q hCxFkqYmPR41dNEQ7LZmWhsS8Q4Er2mZnn7X8Z1r9AI
+rUGgVMPWfJzLxjhahAYj7XTVi9D61ZVZ9/ACRrIY7Gw
+-> ssh-ed25519 m8VklA PwIDj4E8KU3s7VZE/1HRD0V6cAaq/OdXH6FECfhSPH8
+HM0faUMaemIMnJH60Q0AvmNb8xIfKgxrOMZS9W2sY6M
+--- 4r5DXKaMPRt2DLld9iJhpB4UHCO22znFPreKB9NKRs8
+s%YcS$*$DoDo,pw	}9?Y0Z^ݳ 5`F V~z%B
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/authelia_oidc_issuer_private_key.age b/nix/hosts/corrino/secrets/authelia_oidc_issuer_private_key.age
new file mode 100644
index 0000000..979b7e5
--- /dev/null
+++ b/nix/hosts/corrino/secrets/authelia_oidc_issuer_private_key.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/authelia_session_secret.age b/nix/hosts/corrino/secrets/authelia_session_secret.age
new file mode 100644
index 0000000..6baeeb3
--- /dev/null
+++ b/nix/hosts/corrino/secrets/authelia_session_secret.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/authelia_storage_encryption_key.age b/nix/hosts/corrino/secrets/authelia_storage_encryption_key.age
new file mode 100644
index 0000000..9945057
--- /dev/null
+++ b/nix/hosts/corrino/secrets/authelia_storage_encryption_key.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q YbxCU9nVq+EyP+6qRAPXgfuEymX7DmkKIuKuzC/x+GM
+/C/iX6PMLE6zAMQ23hEC0FFk2k0kOIKhe0D/hvoUKpI
+-> ssh-ed25519 m8VklA 33DBjImMRvGXK6qf5MtpHV6nMPQusjtMvsPwfj56eH8
+++k6v2xd6jKWAAG5p+7w2ic2ux76r8zHrWrir4Hmigg
+--- 9nQtiCOo6h4J6KybRWvcOvqurn3nRtqvNmBJQlq5rlk
+JW2ўaSϓ[a7<Z+e6y\ ♞6Aq࣡RY+7pLf>?o		x↱]\Q
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/hedgedoc_environment_variables.age b/nix/hosts/corrino/secrets/hedgedoc_environment_variables.age
new file mode 100644
index 0000000..47f9b3e
--- /dev/null
+++ b/nix/hosts/corrino/secrets/hedgedoc_environment_variables.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/restic_password.age b/nix/hosts/corrino/secrets/restic_password.age
new file mode 100644
index 0000000..f4cf4b2
--- /dev/null
+++ b/nix/hosts/corrino/secrets/restic_password.age
Binary files differdiff --git a/nix/hosts/corrino/www/git.emile.space.nix b/nix/hosts/corrino/www/git.emile.space.nix
index 2c7d64e..dceadb6 100644
--- a/nix/hosts/corrino/www/git.emile.space.nix
+++ b/nix/hosts/corrino/www/git.emile.space.nix
@@ -2,18 +2,113 @@
 
 let
   cfg = config.services.gitea;
+  authelia-location = ''
+    set $upstream_authelia http://127.0.0.1:9091/api/authz/auth-request;
+
+    ## Virtual endpoint created by nginx to forward auth requests.
+    location /internal/authelia/authz {
+      ## Essential Proxy Configuration
+      internal;
+      proxy_pass $upstream_authelia;
+
+      ## Headers
+      ## The headers starting with X-* are required.
+      proxy_set_header X-Original-Method $request_method;
+      proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
+      proxy_set_header X-Forwarded-For $remote_addr;
+      proxy_set_header Content-Length "";
+      proxy_set_header Connection "";
+
+      ## Basic Proxy Configuration
+      proxy_pass_request_body off;
+      proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead
+      proxy_redirect http:// $scheme://;
+      proxy_http_version 1.1;
+      proxy_cache_bypass $cookie_session;
+      proxy_no_cache $cookie_session;
+      proxy_buffers 4 32k;
+      client_body_buffer_size 128k;
+
+      ## Advanced Proxy Configuration
+      send_timeout 5m;
+      proxy_read_timeout 240;
+      proxy_send_timeout 240;
+      proxy_connect_timeout 240;
+    }
+  '';
+
+  authelia-authrequest = ''
+    ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
+    auth_request /internal/authelia/authz;
+
+    ## Save the upstream metadata response headers from Authelia to variables.
+    auth_request_set $user $upstream_http_remote_user;
+    auth_request_set $groups $upstream_http_remote_groups;
+    auth_request_set $name $upstream_http_remote_name;
+    auth_request_set $email $upstream_http_remote_email;
+
+    ## Inject the metadata response headers from the variables into the request made to the backend.
+    proxy_set_header Remote-User $user;
+    proxy_set_header Remote-Groups $groups;
+    proxy_set_header Remote-Email $email;
+    proxy_set_header Remote-Name $name;
+
+    ## Configure the redirection when the authz failure occurs. Lines starting with 'Modern Method' and 'Legacy Method'
+    ## should be commented / uncommented as pairs. The modern method uses the session cookies configuration's authelia_url
+    ## value to determine the redirection URL here. It's much simpler and compatible with the mutli-cookie domain easily.
+
+    ## Modern Method: Set the $redirection_url to the Location header of the response to the Authz endpoint.
+    auth_request_set $redirection_url $upstream_http_location;
+
+    ## Modern Method: When there is a 401 response code from the authz endpoint redirect to the $redirection_url.
+    error_page 401 =302 $redirection_url;
+
+    ## Legacy Method: Set $target_url to the original requested URL.
+    ## This requires http_set_misc module, replace 'set_escape_uri' with 'set' if you don't have this module.
+    # set_escape_uri $target_url $scheme://$http_host$request_uri;
+
+    ## Legacy Method: When there is a 401 response code from the authz endpoint redirect to the portal with the 'rd'
+    ## URL parameter set to $target_url. This requires users update 'auth.example.com/' with their external authelia URL.
+    # error_page 401 =302 https://auth.example.com/?rd=$target_url;
+  '';
 in {
   services.nginx.virtualHosts."git.emile.space" = {
     forceSSL = true;
     enableACME = true;
 
+    # TODO(emile): figure out why this doesn't work when enabled, has to do with authelia
+    # extraConfig = authelia-location;
+
     locations = {
       "/" = {
         proxyPass = "http://127.0.0.1:3000";
+
+        # TODO(emile): figure out why this doesn't work when enabled, has to do with authelia
+        # extraConfig = authelia-authrequest;
       };
     };
   };
 
+	# auth via authelia
+	services.authelia.instances.main.settings.identity_providers.oidc.clients = [
+  	{
+  		id = "git";
+
+  		# ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
+  		secret = "$pbkdf2-sha512$310000$4bi9wRkfcqnjbdmgt7rU.g$pQ2mC6GW4.BQwanGKKFhFyIx6Y.WY80xd/YpmlYOPnlnGBWpp0dSOTv6a/2yqSA5D.EuRkGCyeexSE5FdCK2TA";
+  		public = false;
+  		authorization_policy = "two_factor";
+  		redirect_uris = [
+  			"https://git.emile.space/user/oauth2/authelia/callback"
+  		];
+  		scopes = [
+  			"openid"
+  			"email"
+  			"profile"
+  		];
+  	}
+  ];
+
   services.gitea = rec {
     enable = true;
 
diff --git a/nix/hosts/corrino/www/md.emile.space.nix b/nix/hosts/corrino/www/md.emile.space.nix
new file mode 100644
index 0000000..a983729
--- /dev/null
+++ b/nix/hosts/corrino/www/md.emile.space.nix
@@ -0,0 +1,102 @@
+{ config, pkgs, ... }:
+
+{
+	services.nginx.virtualHosts."md.emile.space" = {
+		forceSSL = true;
+		enableACME = true;
+
+		# TODO(emile): figure out why this doesn't work when enabled, has to do with authelia
+		# extraConfig = authelia-location;
+
+		locations = {
+			"/" = {
+				proxyPass = "http://127.0.0.1:3003";
+
+				# TODO(emile): figure out why this doesn't work when enabled, has to do with authelia
+				# extraConfig = authelia-authrequest;
+			};
+		};
+	};
+
+	# auth via authelia
+	services.authelia.instances.main.settings.identity_providers.oidc.clients = [
+		{
+			id = "HedgeDoc";
+
+			# ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
+			secret = "$pbkdf2-sha512$310000$l4Kyec7Q9oY2GAhWA/xMig$P/MYFmulfgsDNyyiclUzd6le0oSiOvqCIvl4op5DkXtVTxLWlMA3ZwhJ6Z7u.OfIREuEM2htH6asxWPhBhkpNQ"; 
+			public = false;
+			authorization_policy = "two_factor";
+			redirect_uris = [
+        "https://md.emile.space/auth/oauth2/callback"
+			];
+			scopes = [
+				"openid"
+				"email"
+				"profile"
+			];
+      grant_types = [
+        "refresh_token"
+        "authorization_code"
+			];
+      response_types = [
+        "code"
+			];
+      response_modes = [
+        "form_post"
+        "query"
+        "fragment"
+			];
+		}
+	];
+
+	services.hedgedoc = {
+    enable = true;
+		package = pkgs.hedgedoc;
+
+		environmentFile = config.age.secrets.hedgedoc_environment_variables.path;
+
+		settings = {
+			host = "127.0.0.1";
+			port = 3003;
+
+			domain = "md.emile.space";
+
+			urlPath = null; # we're hosting on the root of the subdomain and not a subpath
+			allowGravatar = true;
+
+			# we're terminating tls at the reverse proxy
+			useSSL = false;
+
+			# Use https:// for all links.
+			# This is useful if you are trying to run hedgedoc behind a reverse proxy.
+			# Only applied if domain is set.
+			protocolUseSSL = true;
+
+			db = {
+			  dialect = "sqlite";
+			  storage = "/var/lib/hedgedoc/db.sqlite";
+			};
+
+			uploadsPath = "/var/lib/hedgedoc/uploads";
+
+			path = null; # we want to use HTTP and not UNIX domain sockets...
+
+			allowOrigin = with config.services.hedgedoc.settings; [ host domain ];
+		};
+  };
+
+	# backups
+	services.restic.backups = {
+	  storagebox = {
+	    user = "u331921";
+	    repository = "stfp:u331921@u331921.your-storagebox-de:23/restic";
+	    initialize = true; # initializes the repo, don't set if you want manual control
+	    passwordFile = config.age.secrets.restic_password.path;
+			paths = [
+				"/var/lib/hedgedoc/"
+			];
+	  };
+	};
+
+}
diff --git a/nix/hosts/corrino/www/znc.emile.space.nix b/nix/hosts/corrino/www/znc.emile.space.nix
index 7a790ed..0ba0751 100644
--- a/nix/hosts/corrino/www/znc.emile.space.nix
+++ b/nix/hosts/corrino/www/znc.emile.space.nix
@@ -1,47 +1,60 @@
-{ ... }:
+{ config, pkgs, lib, ... }:
 
 {
   services.nginx.virtualHosts."znc.emile.space" = {
     forceSSL = true;
     enableACME = true;
 
-    locations = {
-      "/" = {
-        proxyPass = "http://127.0.0.1:5000";
-      };
-    };
+    locations."/".proxyPass = "http://127.0.0.1:5002";
   };
 
   services.znc = {
     enable = true;
-    openFirewall = true;
+    mutable = true;
     useLegacyConfig = false;
 
-    config = {
-      LoadModule = [ ];
-      User.Emile = {
-        Admin = true;
-        Nick = "hanemile";
-        RealName = "Emile";
-        # QuitMsg = "iowait()";
-        LoadModule = [ "chansaver" "controlpanel" ];
+    openFirewall = false;
 
-        Network.libera = {
-          Server = "irc.libera.chat +6697";
-          LoadModule = [ "simple_away" ];
-          Chan = {
-            "#nixos" = { Detached = false; };
-            "##linux" = { Disabled = true; };
-          };
-        };
+    modulePackages = with pkgs.zncModules; [
+      clientbuffer
+      clientaway
+      playback
+      privmsg
+    ];
 
-        Pass.password = { # hunter2
-          Method = "sha256";
-          Hash =
-            "31357a874d929871b7c2267721501aaa1f3c570ddc72eb6fb6d065fe72dbc2e4";
-          Salt = "Oo1du8jahquataexai6Eiph9OcohpoL3";
+    config = lib.mkMerge [
+      ({
+        Version = lib.getVersion pkgs.znc;
+        Listener = {
+          l = { Port = 5002; SSL = false; AllowWeb = true;  };
+          j = { Port = 5001; SSL = true;  AllowWeb = false; };
+          LoadModule = [ "webadmin" "adminlog" "playback" "privmsg" ];
+          User = {
+            emile = {
+              Admin = true;
+              Nick = "hanemile";
+              AltNick = "hanemile_";
+              AutoClearChanBuffer = false;
+              AutoClearQueryBuffer = false;
+              LoadModule = [
+                "clientbuffer autoadd"
+                "buffextras"
+                "clientaway"
+              ];
+              Network = {
+                liberachat = {
+                  Server = "irc.libera.char +6697";
+                  Nick = "hanemile";
+                  AltNick = "hanemile";
+                  JoinDelay = 2;
+                  LoadModule = [ "simple_away" "nickserv" "cert"];
+                };
+              };
+            };
+          };
         };
-      };
-    };
+      })
+    ];
+    configFile = config.age.secrets.znc-config.path;
   };
 }