diff --git a/.sops.yaml b/.sops.yaml index bb35d01..7d28fdf 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -7,13 +7,13 @@ creation_rules: - path_regex: secrets/[^/]+\.(yaml|json|env|cfg)$ key_groups: - age: - - *jo_puzzlevision + - *jo_mcdonalds - *absolutesolver - path_regex: systems/x86_64-nixos/absolutesolver/secrets/.*\.(yaml|env|json|cfg)$ key_groups: - age: - - *jo_puzzlevision + - *jo_mcdonalds - *absolutesolver - path_regex: systems/x86_64-nixos/puzzlevision/secrets/.*\.(yaml|env|json|cfg)$ diff --git a/README.md b/README.md index 57d4d80..478850b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@
## 💡 Why the need for a second version? + At its core, version 2 of my NixOS flake was aimed at improving the following regions of my previous setup: - Implementing my own custom library at self.lib, recursively built from the contents of the `lib` directory. @@ -17,7 +18,9 @@ Since I am actively using this configuration on my main workstation and Server, leftover issues are sparse and the list of modules is nearing completion (for my purposes that is). ## 🚀 Deployment + ### NixOS systems + To deploy a system run the following command in your terminal of choice. ```sh @@ -32,12 +35,15 @@ sudo nixos-rebuild build-vm --flake .#hostname ``` ### Darwin systems + To deploy a darwin system run the following command in your terminal of choice. + ```sh sudo darwin-rebuild switch --flake .#hostname ``` ## 🔑 Secrets Management + Secrets are managed by the [sops-nix](https://github.com/Mic92/sops-nix) nixos/home-manager modules respectively. - General secrets are stored within the `secrets` directory. @@ -73,38 +79,46 @@ nix-shell -p sops --run "sops updatekeys secrets/example.yaml" ``` ## 👷 CI/CD coverage + Currently, this repository houses 4 workflows, which are executed when pushing to the stable/develop branch. #### ↪️ `Nix: check for unused code` + This workflow can be found in `.tangled/workflows/deadnix.yml`, and should be pretty self-explanatory. Here's what it does: + 1. Checks out current branch 2. Finds any unused variables/imports etc... 3. Creates a new commit, instantly removing any unused code #### ↪️ `Nix: validate flake` + This workflow can be found in `.tangled/workflows/validate.yml`. It simply validates a flake using `nix flake check`. #### ↪️ `Nix: validate flake.lock` + This workflow can be found in `.tangled/workflows/validate-lock.yml`. It simply scans flake lockfiles for duplicate entries using `nix run github:tgirlcloud/pkgs#locker`. Under the hood it makes use of the locker lockfile linter, created by the [tgirlcloud](https://github.com/tgirlcloud) team (mostly [isabelroses](https://github.com/isabelroses)). #### ↪️ `Trufflehog: check for exposed secrets` + This workflow can be found in `.tangled/workflows/trufflehog-scan.yml`. It runs the Trufflehog security tools on the entire repository, and tries to detect any leaked credentials. This is a last barrier of defense to minimize damage, in case of an emergency. ## 📝 Future goals (2026-02-22) + Some of my future goals for this flake are: - Implementing an automated release workflow with semver versioning, e.g. 2.3.0. - Further refining my usage of the Nix language, through language best-practices and CLI dev tools. ## 🏗️ Structure + This flake follows an opinionated directory structure, described below. ``` @@ -118,6 +132,7 @@ flake.nix --> The flake. ``` ## 🎨 Credits + Various aspects of this flake are inspired by the likes of: - [isabelroses](https://github.com/isabelroses) diff --git a/modules/nixos/services/seafile/default.nix b/modules/nixos/services/seafile/default.nix new file mode 100644 index 0000000..112e20c --- /dev/null +++ b/modules/nixos/services/seafile/default.nix @@ -0,0 +1,141 @@ +{ + lib, + self, + config, + namespace, + pkgs, + ... +}: +let + inherit (lib) mkEnableOption mkIf types; + inherit (self.lib.options) mkOpt; + + cfg = config.${namespace}.services.seafile; + + seafileHostname = "${cfg.subdomain}.${config.${namespace}.services.domain}"; + secrets = config.sops.secrets; +in +{ + options.${namespace}.services.seafile = { + enable = mkEnableOption "Seafile, an intuitive file sharing platform."; + sopsFile = mkOpt types.path null "The location of the sops secret file for the Seafile service."; + sopsFormat = mkOpt types.str "yaml" "The format of the sops secret file for the Seafile service."; + subdomain = + mkOpt types.str "seafile" + "The subdomain, of the system domain, the service should be exposed on."; + }; + + config = mkIf cfg.enable { + sops.secrets = { + "seafile/db_password" = { + inherit (cfg) sopsFile; + format = cfg.sopsFormat; + }; + "seafile/db_root_password" = { + inherit (cfg) sopsFile; + format = cfg.sopsFormat; + }; + "seafile/jwt_private_key" = { + inherit (cfg) sopsFile; + format = cfg.sopsFormat; + }; + "seafile/redis_password" = { + inherit (cfg) sopsFile; + format = cfg.sopsFormat; + }; + "seafile/admin_password" = { + inherit (cfg) sopsFile; + format = cfg.sopsFormat; + }; + }; + + systemd.tmpfiles.rules = [ + "d /opt/seafile-data 0700 root root -" + "d /opt/seafile-mysql/db 0700 root root -" + ]; + + system.activationScripts.seafile-network = '' + ${pkgs.docker}/bin/docker network inspect seafile-net > /dev/null 2>&1 \ + || ${pkgs.docker}/bin/docker network create seafile-net + ''; + + virtualisation.oci-containers.containers = { + seafile-db = { + image = "mariadb:10.11"; + autoStart = true; + environment = { + MYSQL_LOG_CONSOLE = "true"; + MARIADB_AUTO_UPGRADE = "1"; + }; + environmentFiles = [ secrets."seafile/db_root_password".path ]; + volumes = [ "/opt/seafile-mysql/db:/var/lib/mysql" ]; + extraOptions = [ + "--network=default" + "--health-cmd=/usr/local/bin/healthcheck.sh --connect --mariadbupgrade --innodb_initialized" + "--health-interval=20s" + "--health-start-period=30s" + "--health-timeout=5s" + "--health-retries=10" + ]; + }; + + seafile-redis = { + image = "redis"; + autoStart = true; + cmd = [ + "/bin/sh" + "-c" + ''redis-server --requirepass "$(cat ${secrets."seafile/redis_password".path})"'' + ]; + extraOptions = [ "--network=default" ]; + }; + + seafile = { + image = "seafileltd/seafile-mc:13.0-latest"; + autoStart = true; + environment = { + SEAFILE_MYSQL_DB_HOST = "seafile-db"; + SEAFILE_MYSQL_DB_PORT = "3306"; + SEAFILE_MYSQL_DB_USER = "seafile"; + SEAFILE_MYSQL_DB_CCNET_DB_NAME = "ccnet_db"; + SEAFILE_MYSQL_DB_SEAFILE_DB_NAME = "seafile_db"; + SEAFILE_MYSQL_DB_SEAHUB_DB_NAME = "seahub_db"; + TIME_ZONE = "Europe/Berlin"; + INIT_SEAFILE_ADMIN_EMAIL = "system@thevoid.cafe"; + SEAFILE_SERVER_HOSTNAME = seafileHostname; + SEAFILE_SERVER_PROTOCOL = "https"; + CACHE_PROVIDER = "redis"; + REDIS_HOST = "seafile-redis"; + REDIS_PORT = "6379"; + }; + environmentFiles = [ + secrets."seafile/db_password".path + secrets."seafile/db_root_password".path + secrets."seafile/jwt_private_key".path + secrets."seafile/redis_password".path + secrets."seafile/admin_password".path + ]; + volumes = [ "/opt/seafile-data:/shared" ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.seafile.entrypoints" = "websecure"; + "traefik.http.routers.seafile.rule" = "Host(`${seafileHostname}`)"; + "traefik.http.services.seafile.loadbalancer.server.port" = "80"; + }; + extraOptions = [ + "--network=default" + "--network=proxy" + "--health-cmd=curl -f http://localhost:80 || exit 1" + "--health-interval=30s" + "--health-timeout=10s" + "--health-retries=3" + "--health-start-period=10s" + ]; + dependsOn = [ + "seafile-db" + "seafile-redis" + ]; + }; + }; + }; +} diff --git a/systems/x86_64-nixos/absolutesolver/default.nix b/systems/x86_64-nixos/absolutesolver/default.nix index e4e07cf..6f33359 100644 --- a/systems/x86_64-nixos/absolutesolver/default.nix +++ b/systems/x86_64-nixos/absolutesolver/default.nix @@ -48,6 +48,12 @@ sopsFormat = "dotenv"; }; + seafile = { + enable = true; + sopsFile = ./secrets/seafile.yaml; + sopsFormat = "yaml"; + }; + languagetool = { enable = true; }; diff --git a/systems/x86_64-nixos/absolutesolver/secrets/seafile.yaml b/systems/x86_64-nixos/absolutesolver/secrets/seafile.yaml new file mode 100644 index 0000000..8e10290 --- /dev/null +++ b/systems/x86_64-nixos/absolutesolver/secrets/seafile.yaml @@ -0,0 +1,30 @@ +seafile: + db_password: ENC[AES256_GCM,data:B5bDQKICYKv9d7hZ9KaFfx/0SbeUP0ZrOOM=,iv:q7uaUV2al+3VcJPB7GBbN0duZgMErrd7NKFQaLk/f2M=,tag:RJLKvIHVNq0DwSEh2b6zQA==,type:str] + db_root_password: ENC[AES256_GCM,data:3eCtrHriBudQDbW7buCZrf9Hi1E=,iv:R6uU2EMB6qY2vAAEh3ZXWFU+tfuvrDLG9swEjGco1Uw=,tag:aTW22wAUTQ3W+yoKkHVNFQ==,type:str] + jwt_private_key: ENC[AES256_GCM,data:q100/rohSNZk99ekBB2/0mrDLjwfnmcJ1JyrK/WTPjLkw5BSHrehHuSXpDHpwPOBgVvp4ijHsvHi6wGS5cctpw==,iv:mMSe4ajoFp2VTSnVKMZUfZ3kM+jZUcXSN6bB+I4rCQg=,tag:Wja8RxRI0rlhJFFNdCky0Q==,type:str] + redis_password: ENC[AES256_GCM,data:Q6xAR/n1twNFavMU6H/W/LuBfKDLuuhE9M7X,iv:DlHG2SVU0piMQf1rAY0EmwfZFAPUZ1if5rUrjOqXiPc=,tag:uXm3lH8pAwW/GdK9m35U0A==,type:str] + admin_password: ENC[AES256_GCM,data:Ld/lqgGkXvakOnuRQ+NqyyrPl+61uZgprJo6Se2JwEo=,iv:phsmN8wAKs0u0FbzJdy9yfHXR3AzjnQTyndhOA+G/0w=,tag:P6+8Czi8aUEAvjMX2ovefg==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrRG9Ob2ZkVU5JdlBCVEdF + OFhpT0JFNGtGcTY3Ty9SaVBFWmRiQkxHY3pnCmJJa0NMOGRBVDJCd2dFZ1RHQUJv + dzEvelp0TDMwRFRJbEJHa3NJeGp6N1EKLS0tIEZKQlRRV0k5ckdDNFRYWnZMK3Vu + MUFlUVNmckxDQ0xxNHNvTUo2MkpXZE0K/dekJTXjHOVYSAKDduwzfJO5q/2YO9kZ + 9Hu0eGaSRpP6dyOToX3nnHK6vR1bXkjIroUZuE1ofgAdWGMKmZAPnA== + -----END AGE ENCRYPTED FILE----- + recipient: age1plr4p9vydup3elucpe59razqehzw7dv37kg5cuw4dk3qr0rfhqzqnct8mm + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTaGQvdWVpVldkalgxcFJi + cm5qR1EzTUdESlBoY0poM1lFU2tLMVZhOGlZCjhNLzdwcHdoSkc3U3Ird3I0WVBS + dm0yazNCeFBzZ0ZrWU0xZ2UrSUtFSk0KLS0tIEtnWk5sV1NzdkJMQVB1bUxLSEJR + aWdmZlJsL2ZBYkZtRUQyUFJ5UHErV1UKvs6/0bFqNgxUKQQjPhnUzhr65RnxCULL + kvYiKWVMUGhkNjgcw/sff0+ZF+gP1pt02doFtZWD52Ij7ltAtkumbw== + -----END AGE ENCRYPTED FILE----- + recipient: age1gudgza8lv02nwec0pejqpp5t7zu0tzjsfkmvgvy3ckfscr9f4qrq2sl5dv + lastmodified: "2026-06-23T20:21:13Z" + mac: ENC[AES256_GCM,data:qG2MRoxkq3RMHMwQSHiKH3MkJ14mpRXRA0B6/vP1eI5ssI1FT0csBGKDCPwaY1XxNOQ1EM2Khoisshpb89HGr4AZPlL61u0gGUV111F8oUT8KfyJsgRzjnTlYHcJffedM/Cp9gI1xiRvi5Ms2mk+2FcMlHwkfMGBSYPFoJCQrnI=,iv:j/iQmr66yY3paeuCMtL4cyQ0ethzYLmWnQgjjRiVPVI=,tag:Lo5dn9GqRFP9K9nFjwUh1g==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.1