diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..04042df --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +secrets.yaml filter=sops diff --git a/README.md b/README.md index 94ad701..b56760e 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,20 @@ this repo holds the code describing my very own infra (machines I use/manage) and is very much a WIP. -should contain zero secrets, except encrypted either with [`age`][age] or -[`ansible-vault`][ansible-vault]. +:nixos: [NixOS][nixos] configurations are present in the [`./nix`](nix) folder. + +should contain zero secrets, except encrypted either with [`age`][age], +[`sops-nix`][sops-nix], or [`ansible-vault`][ansible-vault]. [`terraform`][tf] secrets are supplied as ENV vars at runtime by sourcing the -decrypted `infra-vars` file using [`direnv`][direnv], which is in turn -stationed in its place using [`home-manager`][hm]. +decrypted `infra-vars` file (stationed in its place with [`home-manager`][hm]) +using [`direnv`][direnv]. [infra]: https://git.dotya.ml/wanderer/infra +[nixos]: https://nixos.org/ [age]: https://github.com/FiloSottile/age +[sops-nix]: https://github.com/Mic92/sops-nix [ansible-vault]: https://docs.ansible.com/ansible/latest/cli/ansible-vault.html [tf]: https://www.terraform.io/ -[direnv]: https://direnv.net/ [hm]: https://github.com/nix-community/home-manager +[direnv]: https://direnv.net/ diff --git a/nix/.sops.yaml b/nix/.sops.yaml new file mode 100644 index 0000000..862e5c9 --- /dev/null +++ b/nix/.sops.yaml @@ -0,0 +1,19 @@ +--- +keys: + - &it age1nt7a9nsgwsf7c9x8yx3qu8w24svz02hpfuwtmk8dazw6j6lh33hsgv8erk + - &loki age136558pknq6glx2xftavt7mm3p4jcpu54kej2kxryeu78m5r59e0qvawl5l + - &backup age15959gprm59azjflvpj97yt0lj6dj4d2yv0nd6u9jp32lzwp3de7qzhf85y + - &surtur age1drh8uq93mhzhj3rz9s2gcnht04wc5hukzutlu4l5qc55hxaznd5s9xs2f6 +creation_rules: + - path_regex: hosts/loki/*.* + key_groups: + - age: + - *backup + - *loki + - path_regex: ./secrets/* + key_groups: + - age: + - *backup + - *surtur + - *loki +... diff --git a/nix/README.md b/nix/README.md new file mode 100644 index 0000000..1442b62 --- /dev/null +++ b/nix/README.md @@ -0,0 +1,18 @@ +# [`infra/nix`](.) + +## [:nixos: NixOS][nixos] configurations + +* [`./hosts`](hosts) folder contains host-specific configurations +* [`./modules`](modules) folder contains reusable code + +:rocket: deploy (build and switch to a new system) remotely using: +```sh +nixos-rebuild switch --fast --flake .#loki --target-host loki +``` + +where the *target host* `loki` is the `ssh-config` name of the host being +configured using the `.#loki` attribute of `nixosConfigurations`. + +see `nixosConfigurations` attr in [`./flake.nix`](flake.nix) for a complete list of hosts. + +[nixos]: https://nixos.org/ diff --git a/nix/flake.lock b/nix/flake.lock new file mode 100644 index 0000000..7e11a03 --- /dev/null +++ b/nix/flake.lock @@ -0,0 +1,524 @@ +{ + "nodes": { + "agenix": { + "inputs": { + "darwin": "darwin", + "home-manager": "home-manager", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1696775529, + "narHash": "sha256-TYlE4B0ktPtlJJF9IFxTWrEeq+XKG8Ny0gc2FGEAdj0=", + "owner": "ryantm", + "repo": "agenix", + "rev": "daf42cb35b2dc614d1551e37f96406e4c4a2d3e4", + "type": "github" + }, + "original": { + "owner": "ryantm", + "repo": "agenix", + "type": "github" + } + }, + "attic": { + "inputs": { + "crane": "crane", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1698258239, + "narHash": "sha256-qnhoYYIJ0L/P7H/f56lQUEvpzNlXh4sxuHpRERV+B44=", + "owner": "zhaofengli", + "repo": "attic", + "rev": "e9918bc6be268da6fa97af6ced15193d8a0421c0", + "type": "github" + }, + "original": { + "owner": "zhaofengli", + "repo": "attic", + "type": "github" + } + }, + "authentik-nix": { + "inputs": { + "authentik-src": "authentik-src", + "flake-compat": "flake-compat_2", + "flake-parts": "flake-parts", + "flake-utils": "flake-utils_2", + "napalm": "napalm", + "nixpkgs": "nixpkgs_2", + "poetry2nix": "poetry2nix" + }, + "locked": { + "lastModified": 1696443205, + "narHash": "sha256-aEhAb4GBqOgkGYEHWJ+Y6ADa/EnwnF9TcuyZbSvLtw8=", + "owner": "mayflower", + "repo": "authentik-nix", + "rev": "e3e7edaba410014bd246d05783dd93dc827fa53c", + "type": "github" + }, + "original": { + "owner": "mayflower", + "repo": "authentik-nix", + "type": "github" + } + }, + "authentik-src": { + "flake": false, + "locked": { + "lastModified": 1694451308, + "narHash": "sha256-dpGvxhA5NWO8LKrGXzalV9EVn/nUIj6sMy2HdY5tjlM=", + "owner": "goauthentik", + "repo": "authentik", + "rev": "f885f8c0395df639ccabd762910867bef0f4577c", + "type": "github" + }, + "original": { + "owner": "goauthentik", + "ref": "version/2023.8.3", + "repo": "authentik", + "type": "github" + } + }, + "crane": { + "inputs": { + "flake-compat": [ + "attic", + "flake-compat" + ], + "flake-utils": [ + "attic", + "flake-utils" + ], + "nixpkgs": [ + "attic", + "nixpkgs" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1677892403, + "narHash": "sha256-/Wi0L1spSWLFj+UQxN3j0mPYMoc7ZoAujpUF/juFVII=", + "owner": "ipetkov", + "repo": "crane", + "rev": "105e27adb70a9890986b6d543a67761cbc1964a2", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "darwin": { + "inputs": { + "nixpkgs": [ + "agenix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1673295039, + "narHash": "sha256-AsdYgE8/GPwcelGgrntlijMg4t3hLFJFCRF3tL5WVjA=", + "owner": "lnl7", + "repo": "nix-darwin", + "rev": "87b9d090ad39b25b2400029c64825fc2a8868943", + "type": "github" + }, + "original": { + "owner": "lnl7", + "ref": "master", + "repo": "nix-darwin", + "type": "github" + } + }, + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1697073574, + "narHash": "sha256-Np603TUNj+fzQYmaNPS7pmsy52KHq4fpWP5GCpTJ38Y=", + "owner": "nix-community", + "repo": "disko", + "rev": "3c41ae36ff12afbada9396c7d8282c2c74f74e06", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "disko", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1693611461, + "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1692799911, + "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "agenix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1682203081, + "narHash": "sha256-kRL4ejWDhi0zph/FpebFYhzqlOBrk0Pl3dzGEKSAlEw=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "32d3e39c491e2f91152c84f8ad8b003420eab0a1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "napalm": { + "inputs": { + "flake-utils": [ + "authentik-nix", + "flake-utils" + ], + "nixpkgs": [ + "authentik-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1693989153, + "narHash": "sha256-gx39Y3opGB25+44OjM+h1bdJyzgLD963va8ULGYlbhM=", + "owner": "nix-community", + "repo": "napalm", + "rev": "a8215ccf1c80070f51a92771f3bc637dd9b9f7ee", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "napalm", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "authentik-nix", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1691853136, + "narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f0451844bbdf545f696f029d1448de4906c7f753", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1693471703, + "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685004253, + "narHash": "sha256-AbVL1nN/TDicUQ5wXZ8xdLERxz/eJr7+o8lqkIOVuaE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3e01645c40b92d29f3ae76344a6d654986a91a91", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable_2": { + "locked": { + "lastModified": 1697332183, + "narHash": "sha256-ACYvYsgLETfEI2xM1jjp8ZLVNGGC0onoCGe+69VJGGE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0e1cff585c1a85aeab059d3109f66134a8f76935", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1697226376, + "narHash": "sha256-cumLLb1QOUtWieUnLGqo+ylNt3+fU8Lcv5Zl+tYbRUE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "898cb2064b6e98b8c5499f37e81adbdf2925f7c5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1697144559, + "narHash": "sha256-pzo1nxxr2niEnkvZEHdG8E5f8BPgj1dWxN0NvW/OnTk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "30e70aded1a399e13b515426ec8e17841b9a9f1d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1697009197, + "narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "poetry2nix": { + "inputs": { + "flake-utils": [ + "authentik-nix", + "flake-utils" + ], + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "authentik-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1694165861, + "narHash": "sha256-FMiPKVcNxb9QWATnQrC68nIL2t8Fm4zBH0XyLz9uqko=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "c3d3c4a0396b1bcccd72c82551a319229997f6e4", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "root": { + "inputs": { + "agenix": "agenix", + "attic": "attic", + "authentik-nix": "authentik-nix", + "disko": "disko", + "nixpkgs": "nixpkgs_3", + "sops-nix": "sops-nix" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "attic", + "crane", + "flake-utils" + ], + "nixpkgs": [ + "attic", + "crane", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1675391458, + "narHash": "sha256-ukDKZw922BnK5ohL9LhwtaDAdCsJL7L6ScNEyF1lO9w=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "383a4acfd11d778d5c2efcf28376cbd845eeaedf", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": "nixpkgs_4", + "nixpkgs-stable": "nixpkgs-stable_2" + }, + "locked": { + "lastModified": 1697339241, + "narHash": "sha256-ITsFtEtRbCBeEH9XrES1dxZBkE1fyNNUfIyQjQ2AYQs=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "51186b8012068c417dac7c31fb12861726577898", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nix/flake.nix b/nix/flake.nix new file mode 100644 index 0000000..fb8658c --- /dev/null +++ b/nix/flake.nix @@ -0,0 +1,89 @@ +{ + description = "NixOS configuration for all the things (as many as we can get)"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs"; + inputs.disko.url = "github:nix-community/disko"; + inputs.disko.inputs.nixpkgs.follows = "nixpkgs"; + inputs.agenix.url = "github:ryantm/agenix"; + inputs.agenix.inputs.nixpkgs.follows = "nixpkgs"; + inputs.sops-nix.url = "github:Mic92/sops-nix"; + inputs.attic.url = "github:zhaofengli/attic"; + inputs.authentik-nix.url = "github:mayflower/authentik-nix"; + + outputs = { + self, + nixpkgs, + disko, + agenix, + sops-nix, + attic, + authentik-nix, + ... + }: let + projname = "nix-infra"; + system = "x86_64-linux"; + supportedSystems = ["x86_64-linux" "aarch64-linux"]; + # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'. + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + # Nixpkgs instantiated for supported system types. + nixpkgsFor = forAllSystems (system: + import nixpkgs { + inherit system; + overlays = [ + # no overlay imports atm + ]; + }); + pkgs = nixpkgs.legacyPackages.${system}; + # pkgs = nixpkgsFor.${system}; + in { + formatter = forAllSystems ( + system: + nixpkgsFor.${system}.alejandra + ); + # formatter.${system} = pkgs.alejandra; + + nixosConfigurations.loki = nixpkgs.lib.nixosSystem { + inherit pkgs system; + modules = [ + disko.nixosModules.disko + agenix.nixosModules.default + sops-nix.nixosModules.sops + attic.nixosModules.atticd + authentik-nix.nixosModules.default + + ./hosts/loki/configuration.nix + ]; + }; + + devShells = forAllSystems ( + system: let + pkgs = import nixpkgs { + inherit system; + overlays = [ + ]; + }; + in { + default = with pkgs; + mkShell + { + name = "${projname}"; + + shellHook = '' + echo " -- in ${projname} dev shell..." + ''; + + nativeBuildInputs = [ + ]; + packages = + [cachix] + ++ ( + if stdenv.isLinux + then [ + ] + else [] + ); + }; + } + ); + }; +} diff --git a/nix/hosts/loki/configuration.nix b/nix/hosts/loki/configuration.nix new file mode 100644 index 0000000..844f82b --- /dev/null +++ b/nix/hosts/loki/configuration.nix @@ -0,0 +1,219 @@ +{ + config, + lib, + pkgs, + ... +}: { + imports = [ + # Include the results of the hardware scan. + ./hardware-configuration.nix + ./disko-config.nix + + ./modules/caddy.nix + ./modules/coredns.nix + # ./modules/authelia.nix + ./modules/authentik.nix + ./modules/gonic.nix + ./modules/attic.nix + + ../../modules/base.nix + ../../modules/dnscrypt.nix + # ../../modules/nix-serve.nix + ../../modules/uptime-kuma.nix + ]; + + sops = { + defaultSopsFile = ./secrets.yaml; + age = { + keyFile = "/root/.age/loki-key"; + sshKeyPaths = ["/root/.ssh/lokiage" "/etc/ssh/ssh_host_ed25519_key"]; + generateKey = false; + }; + + secrets.domainName.restartUnits = ["caddy.service" "coredns.service"]; + secrets.nixServeSecretKey.restartUnits = ["nix-serve.service"]; + }; + + age = { + # `lokiage` key needs to be manually when setting up the machine; + identityPaths = ["/root/.ssh/lokiage"]; + # identityPaths = ["/root/.ssh/lokiage" "/var/lib/persistent/ssh_host_ed25519_key"]; + + secrets.rootPassphrase.file = ./secrets/rootPassphrase.age; + # secrets."zfs-DATA".file = ./secrets/zfs-DATA.age; + }; + + nix.settings.trusted-users = ["@wheel" "root"]; + + nix.sshServe.enable = true; + nix.sshServe.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBtG6NCgdLHX4ztpfvYNRaslKWZcl6KdTc1DehVH4kAL" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBZbkw9vjCfbMPEH7ZAFq20XE9oIJ4w/HRIMu2ivNcej caelum's nixbldr key" + ]; + + # forbid hibernation due to zfs-on-root. + boot.kernelParams = ["nohibernate"]; + boot.kernel.sysctl = { + "dev.i915.perf_stream_paranoid" = 0; + }; + + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.systemd-boot.configurationLimit = 42; + boot.loader.systemd-boot.netbootxyz.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + boot.supportedFilesystems = ["zfs"]; + boot.zfs.forceImportRoot = true; + + boot.initrd.kernelModules = ["zfs" "e1000e"]; + boot.initrd.network = { + # This will use udhcp to get an ip address. + # Make sure you have added the kernel module for your network driver to `boot.initrd.availableKernelModules`, + # so your initrd can load it! + # Static ip addresses might be configured using the ip argument in kernel command line: + # https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt + enable = false; + ssh = { + enable = false; + # To prevent ssh clients from freaking out because a different host key is used, + # a different port for ssh is useful (assuming the same host has also a regular sshd running) + port = 2222; + # hostKeys paths must be unquoted strings, otherwise you'll run into issues with boot.initrd.secrets + # the keys are copied to initrd from the path specified; multiple keys can be set + # you can generate any number of host keys using + # `ssh-keygen -t ed25519 -N "" -f /path/to/ssh_host_ed25519_key` + # hostKeys = [/root/.initrd-ssh_host_ed25519_key]; + ignoreEmptyHostKeys = true; + authorizedKeys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIODmLwtQj6ylgdTPo1/H5jW7jsLzwaCTGdIsTQAdc896"]; + }; + }; + # boot.initrd.systemd.contents + + boot.binfmt = { + emulatedSystems = [ + "wasm32-wasi" + "aarch64-linux" + ]; + }; + + networking = { + # hostId = pkgs.lib.mkForce "00000000"; + hostId = "deadb33f"; + hostName = "loki"; + + nftables.enable = true; + + networkmanager.enable = true; + interfaces.enp0s25.wakeOnLan.enable = true; + + firewall = { + allowPing = true; + }; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + }; + + users.users.root = { + shell = pkgs.zsh; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBtG6NCgdLHX4ztpfvYNRaslKWZcl6KdTc1DehVH4kAL" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJaXmXbNegxiXLldy/sMYX8kCsghY1SGqn2FZ5Jk7QJw" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBZbkw9vjCfbMPEH7ZAFq20XE9oIJ4w/HRIMu2ivNcej caelum's nixbldr key" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGKzPC0ZK4zrOEBUdu1KNThEleVb1T5Pl3+n3KB3o0b8 surtur's nixbldr key" + ]; + hashedPasswordFile = config.age.secrets.rootPassphrase.path; + subUidRanges = [ + { + count = 65536; + startUid = 65536 * 28; # 1835008, docker + } + ]; + }; + + services = { + atd.enable = true; + + udev.extraRules = '' + # set brightness to minimum level. + ACTION=="add", SUBSYSTEM=="backlight", ATTR{brightness}!="0", ATTR{brightness}="0" + + # wol + ACTION=="add", SUBSYSTEM=="net", NAME=="en*", RUN+="${pkgs.ethtool}/bin/ethtool -s $name wol g" + ''; + + power-profiles-daemon.enable = false; + #tlp.enable = + # lib.mkDefault ((lib.versionOlder (lib.versions.majorMinor lib.version) "23.11") + # || !config.services.power-profiles-daemon.enable); + auto-cpufreq.enable = true; + auto-cpufreq.settings = { + battery = { + governor = "powersave"; + turbo = "never"; + }; + charger = { + governor = "schedutil"; + turbo = "auto"; + }; + }; + + prometheus = { + # WIP. + enable = true; + # openFirewall = true; + port = 9090; + exporters = { + node = { + enable = true; + enabledCollectors = [ + "logind" + "systemd" + ]; + port = 9100; + }; + }; + + scrapeConfigs = [ + { + job_name = "node"; + static_configs = [ + { + targets = [ + "loki.local:${toString config.services.prometheus.exporters.node.port}" + ]; + } + ]; + } + { + job_name = "coredns"; + static_configs = [{targets = ["loki.local:9153"];}]; + } + ]; + }; + + nix-serve.secretKeyFile = config.sops.secrets.nixServeSecretKey.path; + + # TS is enabled in the imported module, this is additional config. + tailscale = { + useRoutingFeatures = "both"; + # accept-routes = true; + }; + + zfs = { + autoScrub = { + enable = true; + interval = "weekly"; + }; + trim.enable = true; + }; + }; + + # Copy the NixOS configuration file and link it from the resulting system + # (/run/current-system/configuration.nix). This is useful in case you + # accidentally delete configuration.nix. + # Does not work with flakes - yet™. + system.copySystemConfiguration = false; +} diff --git a/nix/hosts/loki/disko-config.nix b/nix/hosts/loki/disko-config.nix new file mode 100644 index 0000000..20fb6d7 --- /dev/null +++ b/nix/hosts/loki/disko-config.nix @@ -0,0 +1,86 @@ +{ + config, + disks ? ["/dev/sda"], + lib, + ... +}: let + zfs-DATA = config.age.secrets.zfs-DATA; +in { + disko.devices = { + disk = { + x = { + type = "disk"; + device = "/dev/sda"; + content = { + type = "gpt"; + partitions = { + ESP = { + type = "EF00"; + size = "700M"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "zroot"; + }; + }; + }; + }; + }; + }; + zpool = { + zroot = { + type = "zpool"; + mode = ""; # == single disk + options = { + ashift = "12"; + autotrim = "on"; + }; + rootFsOptions = { + checksum = "sha512"; + compression = "zstd"; + "com.sun:auto-snapshot" = "false"; + }; + mountpoint = null; + postCreateHook = "zfs snapshot zroot@blank"; + + datasets = { + "ROOT" = { + type = "zfs_fs"; + mountpoint = null; + options."com.sun:auto-snapshot" = "false"; + }; + "ROOT/nixos" = { + type = "zfs_fs"; + mountpoint = "/"; + options."com.sun:auto-snapshot" = "true"; + }; + nix = { + type = "zfs_fs"; + mountpoint = "/nix"; + options."com.sun:auto-snapshot" = "true"; + }; + #DATA = { + # type = "zfs_fs"; + # options = { + # encryption = "aes-256-gcm"; + # keyformat = "passphrase"; + # keylocation = "file://${zfs-DATA.path}"; + # mountpoint = "none"; + # "com.sun:auto-snapshot" = "true"; + # }; + # # postCreateHook = '' + # # zfs set keylocation="file://${zfs-DATA}.path" "zroot/$name"; + # # ''; + #}; + }; + }; + }; + }; +} diff --git a/nix/hosts/loki/hardware-configuration.nix b/nix/hosts/loki/hardware-configuration.nix new file mode 100644 index 0000000..4408baf --- /dev/null +++ b/nix/hosts/loki/hardware-configuration.nix @@ -0,0 +1,31 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + config, + lib, + pkgs, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["ehci_pci" "ahci" "usb_storage" "sd_mod" "sdhci_pci" "vfat" "zfs"]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-intel"]; + boot.extraModulePackages = []; + + swapDevices = []; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.enp0s25.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/nix/hosts/loki/modules/attic.nix b/nix/hosts/loki/modules/attic.nix new file mode 100644 index 0000000..13c6429 --- /dev/null +++ b/nix/hosts/loki/modules/attic.nix @@ -0,0 +1,25 @@ +{config, ...}: let + svc = "atticd.service"; + p = config.sops.placeholder; +in { + imports = [ + ../../../modules/attic.nix + ]; + + sops = { + secrets = { + "attic/serverToken".restartUnits = [svc]; + }; + templates.atticCreds = { + content = '' + ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64="${p."attic/serverToken"}" + ''; + }; + }; + + services.atticd = { + enable = true; + credentialsFile = config.sops.templates.atticCreds.path; + settings.listen = "127.0.0.1:5000"; + }; +} diff --git a/nix/hosts/loki/modules/authelia.nix b/nix/hosts/loki/modules/authelia.nix new file mode 100644 index 0000000..ecadaec --- /dev/null +++ b/nix/hosts/loki/modules/authelia.nix @@ -0,0 +1,46 @@ +{ + config, + pkgs, + ... +}: { + imports = [ + ../../../modules/authelia.nix + ]; + + age = { + secrets.autheliaEnv.file = ./secrets/autheliaEnv.age; + secrets.autheliaStorage.file = ./secrets/autheliaStorage.age; + secrets.autheliaJWT.file = ./secrets/autheliaJWT.age; + secrets.autheliaStorage.owner = "${toString config.services.authelia.instances.main.user}"; + secrets.autheliaJWT.owner = "${toString config.services.authelia.instances.main.user}"; + }; + + services = { + authelia.instances.main = { + secrets.storageEncryptionKeyFile = config.age.secrets.autheliaStorage.path; + secrets.jwtSecretFile = config.age.secrets.autheliaJWT.path; + + settings = { + access_control = { + rules = [ + { + domain = "*.*"; + policy = "one_factor"; + } + ]; + }; + + storage.local.path = "/var/lib/authelia-main/data"; + authentication_backend.file.path = "/var/lib/authelia-main/users_database.yml"; + notifier.filesystem.filename = "/var/lib/authelia-main/notif.txt"; + + # ntp.address = "ptbtime.ptb.de:123" + ntp.disable_startup_check = true; + }; + }; + }; + + systemd.services.authelia-main.serviceConfig = { + EnvironmentFile = config.age.secrets.autheliaEnv.path; + }; +} diff --git a/nix/hosts/loki/modules/authentik.nix b/nix/hosts/loki/modules/authentik.nix new file mode 100644 index 0000000..036f249 --- /dev/null +++ b/nix/hosts/loki/modules/authentik.nix @@ -0,0 +1,41 @@ +{ + config, + pkgs, + sops-nix, + ... +}: let + svc = "authentik.service"; +in { + sops.secrets = { + "authentik/secretKey".restartUnits = [svc]; + "authentik/emailHost".restartUnits = [svc]; + "authentik/emailUsername".restartUnits = [svc]; + "authentik/emailPassword".restartUnits = [svc]; + "authentik/emailFrom".restartUnits = [svc]; + }; + + services.authentik = { + enable = true; + # The environmentFile needs to be on the target host! + # Best use something like sops-nix or agenix to manage it + environmentFile = config.sops.templates.authentikEnv.path; + settings = { + disable_startup_analytics = true; + avatars = "initials"; + disable_update_check = true; + error_reporting_enabled = false; + }; + }; + + sops.templates.authentikEnv = { + content = '' + AUTHENTIK_SECRET_KEY=${config.sops.placeholder."authentik/secretKey"} + AUTHENTIK_EMAIL__HOST=${config.sops.placeholder."authentik/emailHost"} + AUTHENTIK_EMAIL__USERNAME=${config.sops.placeholder."authentik/emailUsername"} + AUTHENTIK_EMAIL__PASSWORD=${config.sops.placeholder."authentik/emailPassword"} + AUTHENTIK_EMAIL__FROM=${config.sops.placeholder."authentik/emailFrom"} + # AUTHENTIK_DISABLE_UPDATE_CHECK=true + # AUTHENTIK_ERROR_REPORTING__ENABLED=false + ''; + }; +} diff --git a/nix/hosts/loki/modules/caddy.nix b/nix/hosts/loki/modules/caddy.nix new file mode 100644 index 0000000..b056fba --- /dev/null +++ b/nix/hosts/loki/modules/caddy.nix @@ -0,0 +1,195 @@ +{ + config, + pkgs, + sops-nix, + ... +}: let + caddyPkg = pkgs.callPackage ../../../modules/caddy-custom-package.nix { + plugins = [ + "github.com/caddy-dns/njalla" + "github.com/caddy-dns/desec" + "github.com/dulli/caddy-wol" + "github.com/ueffel/caddy-brotli" + "github.com/greenpau/caddy-security" + ]; + vendorSha256 = "sha256-4Yzqo8aUUivNtgV7hljzoN9VZ5J51AQgV+NrbZ8on5s="; + }; + p = config.sops.placeholder; + domain = p.domainName; + svc = "caddy.service"; +in { + networking.firewall.allowedTCPPorts = [80 443]; + + services = { + caddy = { + enable = true; + package = caddyPkg; + configFile = config.sops.templates.caddyPls.path; + adapter = "caddyfile"; + }; + }; + + systemd.services.caddy = { + description = "Caddy web server"; + after = ["network-online.target" "sops-nix.service"]; + wants = ["network-online.target"]; + wantedBy = ["multi-user.target"]; + serviceConfig = { + TimeoutStopSec = "5s"; + # LimitNOFILE = 1048576; + # LimitNPROC = 512; + PrivateTmp = true; + # ProtectSystem = "full"; + AmbientCapabilities = "cap_net_bind_service"; + }; + }; + + sops.secrets = { + "caddy/njallaApiKey".restartUnits = [svc]; + "caddy/email".restartUnits = [svc]; + }; + + sops.templates.caddyPls = { + owner = config.systemd.services.caddy.serviceConfig.User; + content = '' + (tlsCommon) { + tls { + dns njalla ${p."caddy/njallaApiKey"} + # propagation_timeout 1m + propagation_timeout -1 + curves x25519 + key_type p384 + protocols tls1.2 tls1.3 + } + } + + (headersCommon) { + header / { + x-frame-options "sameorigin" + x-content-type-options "nosniff" + x-xss-protection "1; mode=block" + content-security-policy " + upgrade-insecure-requests; + default-src 'self'; + style-src 'self'; + script-src 'self'; + font-src 'self'; + img-src data: 'self'; + form-action 'self'; + connect-src 'self'; + frame-ancestors 'none'; + " + cross-origin-opener-policy "same-origin" + permissions-policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()" + referrer-policy "no-referrer; strict-origin-when-cross-origin" + -Server + } + } + + (authentik) { + # Always forward outpost path to actual outpost + reverse_proxy /outpost.goauthentik.io/* http://localhost:9000 + + # Forward authentication to outpost + forward_auth http://localhost:9000 { + uri /outpost.goauthentik.io/auth/caddy + + # Capitalization of the headers is important, otherwise they will be empty + copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version + } + } + + { + admin off + acme_dns njalla ${p."caddy/njallaApiKey"} + email ${p."caddy/email"} + grace_period 60s + + log default { + output stdout + format json + } + } + + auth.${domain} { + encode zstd br + log { + level INFO + } + + import tlsCommon + + # authelia + # reverse_proxy localhost:9091 + + # authentik + reverse_proxy localhost:9000 + + import headersCommon + } + + whoami.${domain} { + encode zstd br + log { + level INFO + } + + import tlsCommon + import headersCommon + import authentik + + respond "I am whoami" + } + + gonic.${domain} { + encode zstd br + log { + level INFO + } + + import tlsCommon + import headersCommon + # import authentik + reverse_proxy localhost:4747 + } + + ffsync.${domain} { + encode zstd br + log { + level INFO + } + + import tlsCommon + import headersCommon + # import authentik + reverse_proxy localhost:${toString config.services.firefox-syncserver.settings.port} + } + + # attic - nix cache. + cache.${domain} nixcache.${domain} { + encode zstd br + log { + level INFO + } + + import tlsCommon + import headersCommon + # import authentik + reverse_proxy localhost:5000 + } + + # uptime kuma + uptime.${domain} { + encode zstd br + log { + level INFO + } + + import tlsCommon + import headersCommon + # import authentik + reverse_proxy localhost:3001 + } + ''; + }; +} diff --git a/nix/hosts/loki/modules/coredns.nix b/nix/hosts/loki/modules/coredns.nix new file mode 100644 index 0000000..3461a61 --- /dev/null +++ b/nix/hosts/loki/modules/coredns.nix @@ -0,0 +1,369 @@ +{ + lib, + config, + pkgs, + sops-nix, + ... +}: let + serial = toString 14; + svc = "coredns.service"; + usr = "${toString config.users.users.coredns.name}"; + domain = p.domainName; + p = config.sops.placeholder; +in { + networking.firewall = { + allowedTCPPorts = [53]; + allowedUDPPorts = [53]; + }; + + age = { + secrets.zoneInternal.file = ../secrets/zoneInternal.age; + secrets.zoneInternal.owner = "${toString config.users.users.coredns.name}"; + secrets.zoneExternal.file = ../secrets/zoneExternal.age; + secrets.zoneExternal.owner = "${toString config.users.users.coredns.name}"; + # secrets.corednsEnv.file = ../secrets/corednsEnv.age; + }; + + sops = { + secrets = { + "coredns/cidrHomenet".restartUnits = [svc]; + "coredns/cidrTailnet".restartUnits = [svc]; + "coredns/ip".restartUnits = [svc]; + "coredns/ipwlan".restartUnits = [svc]; + "coredns/iptailscale".restartUnits = [svc]; + "coredns/localDNSCryptResolver".restartUnits = [svc]; + "net/ethLoki".restartUnits = [svc]; + "net/ethCaelum".restartUnits = [svc]; + "net/ethCarina".restartUnits = [svc]; + "net/wlanLoki".restartUnits = [svc]; + "net/wlanCarina".restartUnits = [svc]; + + "coredns/cidrHomenet".owner = usr; + "coredns/cidrTailnet".owner = usr; + "coredns/ip".owner = usr; + "coredns/ipwlan".owner = usr; + "coredns/iptailscale".owner = usr; + "coredns/localDNSCryptResolver".owner = usr; + "net/ethLoki".owner = usr; + "net/ethCaelum".owner = usr; + "net/ethCarina".owner = usr; + "net/wlanLoki".owner = usr; + "net/wlanCarina".owner = usr; + }; + }; + + sops.templates.corednsZoneInternal = { + owner = usr; + content = '' + $ORIGIN ${domain}. + @ 1D IN SOA ${domain}. root.${domain}. ( + ${serial} ; serial (yyyymmdd##) + 1M ; refresh + 1M ; retry + 1M ; expiry + 1m ) ; minimum ttl + + 5m IN NS ${p."net/ethLoki"}. + 5m IN NS ${p."net/wlanLoki"}. + 5m IN NS ${p."net/ethCarina"}. + 5m IN NS ${p."net/wlanCarina"}. + + grocy.${domain}. 5m IN A ${p."net/ethCaelum"} + gonic.${domain}. 5m IN A ${p."net/ethLoki"} + cloud.${domain}. 5m IN A ${p."net/ethCaelum"} + media.${domain}. 5m IN A ${p."net/ethCaelum"} + llama.${domain}. 5m IN A ${p."net/ethCaelum"} + llama2.${domain}. 5m IN A ${p."net/ethCaelum"} + auth.${domain}. 5m IN A ${p."net/ethLoki"} + whoami.${domain}. 5m IN A ${p."net/ethLoki"} + ffsync.${domain}. 5m IN A ${p."net/ethLoki"} + cache.${domain}. 5m IN A ${p."net/ethLoki"} + nixcache.${domain}. 5m IN CNAME cache.${domain} + uptime.${domain}. 5m IN A ${p."net/ethLoki"} + + carina.${domain}. 5m IN A ${p."net/ethCarina"} + caelum.${domain}. 5m IN A ${p."net/ethCaelum"} + loki.${domain}. 5m IN A ${p."net/ethLoki"} + ''; + }; + + sops.templates.corednsPls = { + owner = usr; + content = '' + . { + # TODO: listen on 853 and 443 and 1443 for DoT and DoH, + # certs will be courtesy of caddy (or acme). + + # TODO: ad blocking? + # hosts /etc/coredns/blocklist.hosts { + # fallthrough + # } + + reload + + bufsize 1232 + + # TODO: add wlan and tailscale IPs + + # bind {$IP} {$IPWLAN} {$IPTailscale} + bind ${p."coredns/ip"} ${p."coredns/ipwlan"} + acl { + allow net 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 192.0.0.0/24 100.64.0.0/10 + block + } + + hosts { + reload 0 + fallthrough + } + + # loadbalance + # local dnscrypt-proxy. + forward . ${p."coredns/localDNSCryptResolver"} { + health_check 5s + expire 600s + policy sequential + } + + #cache { + # success 4096 + # success 10000 + # denial 2048 + # prefetch 512 + #} + + whoami + health + + prometheus :9153 + errors + log + local + any + } + + # ${domain} { + # bind {$IPTailscale} + # view tailscale { + # expr incidr(server_ip(), '{$cidrTailnet}') + # } + + # reload 300s + # file /etc/coredns/external-tailnet.zone + + # cache { + # #success 1000 + # success 4096 + # denial 2048 + # prefetch 512 + # keepttl + # } + # errors + # log + #} + + ${domain} { + bind ${p."coredns/ip"} ${p."coredns/ipwlan"} + view homenet { + expr incidr(server_ip(), '${p."coredns/cidrHomenet"}') + } + + reload 300s + # file ${config.age.secrets.zoneInternal.path} + file ${config.sops.templates.corednsZoneInternal.path} + + cache { + success 4096 + denial 2048 + prefetch 512 + keepttl + } + errors + log + local + any + } + + # vim: noexpandtab:ft=Corefile + ''; + }; + + sops.templates.corednsEnv = { + content = '' + cidrHomenet=${p."coredns/cidrHomenet"} + cidrTailnet=${p."coredns/cidrTailnet"} + domainName=${domain} + IP=${p."coredns/ip"} + IPWLAN=${p."coredns/ipwlan"} + IPTailscale=${p."coredns/iptailscale"} + localDNSCryptResolver=${p."coredns/localDNSCryptResolver"} + ''; + }; + + services.coredns = { + enable = true; + config = "import ${config.sops.templates.corednsPls.path}"; + #config = '' + # . { + # # TODO: listen on 853 and 443 and 1443 for DoT and DoH, + # # certs will be courtesy of caddy + + # # TODO: ad blocking? + # # hosts /etc/coredns/blocklist.hosts { + # # fallthrough + # # } + + # reload + + # bufsize 1232 + + # # TODO: add wlan and tailscale IPs + + # # bind {$IP} {$IPWLAN} {$IPTailscale} + # bind {$IP} + # acl { + # allow net 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 192.0.0.0/24 100.64.0.0/10 + # block + # } + + # hosts { + # reload 0 + # fallthrough + # } + + # # loadbalance + # # local dnscrypt-proxy. + # forward . {$localDNSCryptResolver} { + # health_check 5s + # expire 600s + # policy sequential + # } + + # #cache { + # # success 4096 + # # success 10000 + # # denial 2048 + # # prefetch 512 + # #} + + # whoami + # health + + # prometheus :9153 + # errors + # log + # } + + # # {$domainName} { + # # bind {$IPTailscale} + # # view tailscale { + # # expr incidr(server_ip(), '{$cidrTailnet}') + # # } + + # # reload 300s + # # file /etc/coredns/external-tailnet.zone + + # # cache { + # # #success 1000 + # # success 4096 + # # denial 2048 + # # prefetch 512 + # # keepttl + # # } + # # errors + # # log + # #} + + # {$domainName} { + # bind {$IP} + # view homenet { + # expr incidr(server_ip(), '{$cidrHomenet}') + # } + + # reload 300s + # # file ${config.age.secrets.zoneInternal.path} + # file ${config.sops.templates.corednsZoneInternal.path} + + # cache { + # success 4096 + # denial 2048 + # prefetch 512 + # keepttl + # } + # errors + # log + # } + + # # vim: noexpandtab:ft=Corefile + #''; + }; + + # systemd.services.coredns.unitConfig = { + # upholds = config.systemd.services.dnscrypt-proxy2; + # wants = config.systemd.services.dnscrypt-proxy2; + # }; + # systemd.services.coredns.serviceConfig = { + systemd.services.coredns = { + after = ["sops-nix.service"]; + wants = ["dnscrypt-proxy2.service"]; + serviceConfig = { + # StateDirectory = "coredns"; + # WorkingDirectory = "/etc/coredns"; + WorkingDirectory = "/"; + # StartLimitIntervalSec = 5; + StartLimitBurst = 10; + Restart = lib.mkDefault "always"; + RestartSec = 10; + # PermissionsStartOnly = true; + ProtectSystem = "strict"; + LimitNOFILE = 1048576; + LimitNPROC = 512; + User = "coredns"; + # EnvironmentFile = config.age.secrets.corednsEnv.path; + EnvironmentFile = config.sops.templates.corednsEnv.path; + # LoadCredential = lib.mapAttrsToList (name: path: "${name}:${path}") cfg.credentials; + DeviceAllow = ""; + LockPersonality = true; + MemoryDenyWriteExecute = false; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + # DynamicUser = true; + ProtectProc = "invisible"; + RemoveIPC = true; + # RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ + "@system-service" + "~@cpu-emulation" + "~@debug" + "~@keyring" + "~@memlock" + "~@obsolete" + # "~@privileged" + "~@setuid" + ]; + UMask = 0027; + }; + }; + + users.users.coredns = { + group = "coredns"; + home = "/etc/coredns"; + createHome = false; + isSystemUser = true; + extraGroups = ["users"]; + }; + users.groups.coredns = {}; +} diff --git a/nix/hosts/loki/modules/gonic.nix b/nix/hosts/loki/modules/gonic.nix new file mode 100644 index 0000000..1e5a22d --- /dev/null +++ b/nix/hosts/loki/modules/gonic.nix @@ -0,0 +1,15 @@ +{config, ...}: { + services.gonic = { + enable = true; + settings = { + music-path = "/DATA/music"; + podcast-path = "/DATA/podcasts"; + cache-path = "/var/cache/gonic"; + # db-path ="/DATA/services/gonic/gonic.db"; + db-path = "/var/lib/gonic/gonic.db"; + jukebox-enabled = false; + listen-addr = "127.0.0.1:4747"; + scan-interval = 60; + }; + }; +} diff --git a/nix/hosts/loki/secrets.yaml b/nix/hosts/loki/secrets.yaml new file mode 100644 index 0000000..e7cd3cb --- /dev/null +++ b/nix/hosts/loki/secrets.yaml @@ -0,0 +1,70 @@ +domainName: ENC[AES256_GCM,data:E2UFsEHoCPASUxB/YqqWrUavqA==,iv:IM6iMZLeKztTHjF8Fy4gbZGUX5Orh77opYvOmrER3RY=,tag:o3KmFgHgP1dXDUiWTIxT+Q==,type:str] +shortDomain: ENC[AES256_GCM,data:L80YrbWy69gW3r0L5PJ3+zE=,iv:q32iqxRzFW4nOfk+7+ceOURF/CO6Y6ewnaVXV3vopv0=,tag:qVjXHB/GtsV9Ej7VZvQ2TQ==,type:str] +nixServeSecretKey: ENC[AES256_GCM,data:0VFFgsYGl6/FdM9KQ6PiNxApFlYlDQd+Qfes1BkVn9u9h4XBqAQa1jYuF7FSuemDoikQX040fDIEwmwBtFgfZ7+hBuQHVDyErh817CeDhMcsnKlyf+UVZCL3atV3hgrq/w==,iv:kcyylbpHtB2Nniwp9uxGAHS8Q0E7QRLndZ26dBTFb70=,tag:WF+r1gEdGq7fVUBOjmZyUw==,type:str] +nixServePubKey: ENC[AES256_GCM,data:XcnS7U6y47CmF3EgLDvovukI3ZrWHF/78L/uYrvfzNgT1RJD6UDTzR2iJzXV2wISfv/EGt0=,iv:21dN0bqlFhqoNdnx/GOmRgGPJqAnv7+p7XTIYBEyRCQ=,tag:iUP47jEsd0B1eteJjL2oyQ==,type:str] +caddy: + njallaApiKey: ENC[AES256_GCM,data:MOgTmZF+kWPJ/pEhGLpZ6Dh3rRdSemByxYfWbgJDuzScKzlpe47sfA==,iv:X7aZcwALwUmKS3JF9/+1eqTot+7FTApqHCMv0zx9gLs=,tag:8cpdrWQI6p1aAtvTUim99A==,type:str] + email: ENC[AES256_GCM,data:3heYJXQd7lg6p5PUI9BmEhodCvb9kzrn80nvZOk=,iv:8WAPyK8wT+CTjLbjyTsfruAaIIdFLrDT8TKuq7YC+XU=,tag:FVMM8rBTuVr5sg2VLYgugQ==,type:str] +authentik: + secretKey: ENC[AES256_GCM,data:UIGYAi/g7sgM+MEn7wBS3hbWsgPl5ePBi8fR9AbbFf0NjfE4Pw8VqoCfW5Q=,iv:y0tQVsIBl0Tu9kere6hEw4caxg0y+Rst8JLTMmX6rys=,tag:Y2DfgSPvqLNFIF6hlDXJFg==,type:str] + emailHost: ENC[AES256_GCM,data:lRyFRzhuNgC9VNPO9A==,iv:R8AWmA7JTLPWAXnN2Lwg9lTE5FnGgOC3zYRhGDthKXQ=,tag:sqPaHQPVDW6lbc7rJ6Fb1A==,type:str] + emailUsername: ENC[AES256_GCM,data:XIN/hRzwIMlH2Vt9jhSAK+WN,iv:f4KhU7v6SXlW9wbyl8jrOJn/OyhR6XDoI9Xs8imTBwc=,tag:7O6sXLyLQrrYo93Si2mAQw==,type:str] + emailFrom: ENC[AES256_GCM,data:aWpZR5jq1XSCYCDaSx8pE3Xx,iv:HAKQbnoA+uXNh/N3EjoIjId7MYu5ivZd5G7ccwmlz0I=,tag:yw9of9h8a+6annAi+rBdVA==,type:str] + #ENC[AES256_GCM,data:7Ux8lB94gwD/7pab3THr8ExJ5DwsMBikqECFIRYEmIAIJh8RnGjORnGIk+Dx06NZ0yr16JMD3o0kyjNL,iv:bIfJmwB4Y/oS241keTPG7Ty9hT7U12ES3XV2vHKFKgI=,tag:qDTXF62SzpMqDNqklkZdsg==,type:comment] + emailPassword: ENC[AES256_GCM,data:Jr1lpggvsxO50dvQ/jWjinN9CtSA5KiVbIuisYtx+lzzkOZojBlYkOiX3aYNfxX1MOPlsA==,iv:Bl6siYZ6wneYOeZ2PivAUJS1JnLFRgYtdbjrmrKOOBI=,tag:YrsvF3Q1cs6w+bUlHA9Wgw==,type:str] +net: + ethLoki: ENC[AES256_GCM,data:dP23Oj9pPPntNnx0,iv:kdfdkKhHQQED/iH1BDRUB/C3R/vdVgY4Pm8nZMc62uQ=,tag:8qb669FIhwI5AU/LHfj7wg==,type:str] + ethCaelum: ENC[AES256_GCM,data:KRiIHgqJVZHbMOEPlw==,iv:xbZBkEboi5B7M0PuWytkc6+Y2FoZ7LhDox39yX4ZTIk=,tag:Y2wElHZzxTn68kTK0e48UQ==,type:str] + ethCarina: ENC[AES256_GCM,data:IIzTlIdGo17ie1XA6w==,iv:v79kkPFbhj5x+8xTkxSKCS9xCaTzlMK+RaGQgiKnDn8=,tag:cFNDqag0JGLHgVFQ3tA9mA==,type:str] + wlanLoki: ENC[AES256_GCM,data:eSa++RH6t/W5yQWt,iv:xn6IEROjq6CLZ4mGBZB6vZCIAtVJmrjCTs66G+OzCcY=,tag:jLFogLZtyPbprXK2OhWXIw==,type:str] + wlanCarina: ENC[AES256_GCM,data:ugykYJujsQLk4RvwGw==,iv:Ge4c+bmUWcJCKv8cVXX1Wos14rCfUTA+AvLBLq4SsyM=,tag:9litWR7kWu8f+aml0MXzEQ==,type:str] +coredns: + cidrHomenet: ENC[AES256_GCM,data:Br7ixh52tVp4fqr9W6U=,iv:neSAnc66BXK++PhIIOQSrs5gyMtB2IX1nLwClTwemq8=,tag:bgqIL/nPOnbbRPjBXC0Azg==,type:str] + cidrTailnet: ENC[AES256_GCM,data:+ZqzEqfERBFHwTNV2w==,iv:9VZitgr4zvy3l/EwQx2M8P8fAo2UZ9sMQ7jp3Soblto=,tag:MWxn1PXtA3BLo/1WXRUrcg==,type:str] + ip: ENC[AES256_GCM,data:zucOcXk1dnGvhmlM,iv:rWIO6uMmMSNi+SvKtZGrCF1J/7hvvWzW6vZUqMkwQZg=,tag:/v93vM42IQJQJhd7kbGLbw==,type:str] + ipwlan: ENC[AES256_GCM,data:2aMXVAMm5TmPuPog,iv:B8Rl+udtRGBHSTij8w1xvxAaVcjyyuSwXJYwQKcqNQU=,tag:bp/EhvEGI0hK8+le0j8OKQ==,type:str] + iptailscale: ENC[AES256_GCM,data:eNAUjBp8Ad5E,iv:EOd/go9iW36tXjPr+T9J32RNIRk+oLG25GqWcUww2dI=,tag:03yCgvgSayY/gkQ73X74jA==,type:str] + localDNSCryptResolver: ENC[AES256_GCM,data:ANwDFvg1dMFF77jJ,iv:yIZOhD1G78saflyeR7BBqeM1s/PBGbeb5zg0hYLmGTo=,tag:nM41w2n1cfbkrhPdPJfoyw==,type:str] +ffsync: + masterSecret: ENC[AES256_GCM,data:os90pvduX4nni2pM6suYr7PODNitUSN3sqsu062eI9PE9XYM6aAVlCubFDBfzgDIs/UAZpULD5Q20ZXQF70gUllNS2QzEoaMU8NerrGWYufjZO8n4Xvm5K/zRTyZbjBcFgKwwC9pQ785oISnumX0EF7hWyfVv/XX5g0ietQOpgk=,iv:xSVg5QB9EzXmOWp+66Wu8tZQjQQ6DMJzYOT2lKNVFfM=,tag:XDmgsXNeP2lzTSVS2//kbg==,type:str] + tokenserverMetricsHashSecret: ENC[AES256_GCM,data:OGMjG+JfWdfo8q38QbauVEpJOTZLkW1IsCJjHCPcEbMxjvhyIWhON9iczIdkALiQgjY7RK8YzE3Uss8U/caqmqNszy8uJ7X31XV6fIpM57vHn0X9vPhcthcNG7qLgKZ4kouYLA4ERtpOhpaBGL1FJbJsYoJi3oA9PprxkRoz65M=,iv:pPzK7D4UlvuRDqAwFcPnwy1rWc5zm091q0qKafT0IZ4=,tag:xlH8DRzBoICknSgkYuRJdA==,type:str] +attic: + serverToken: ENC[AES256_GCM,data:jDdtY2pao7Hbfn09nCB2M9mag8tMOtVTZOkbFmc6XzKWu6cvQdkYqRVfWfl7mlig/7BRdKg+Y4N0D91NqhN7UWXJwCJ1ZjjipsahDMj95hYiOzMNuVx5Vg==,iv:EbEuGPgY20zeumOk8kQ7vppaCWj1IorjIroMMXXwJE4=,tag:Zo0vvQv0NM4yLhgryPqREw==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1nt7a9nsgwsf7c9x8yx3qu8w24svz02hpfuwtmk8dazw6j6lh33hsgv8erk + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0RWVwWDBtSFdxVzArbDUw + MDVxOHU0RSt5eVJRTE5zT2g1eVQwaDl6NEJJCmErblkxWkkrOG9Sbk14K2dtd3VZ + MFYrSDB2ejFBcnJQZFFsSktEblEzV0EKLS0tIGxzU0RRVXAybnFPZm9xNFJ5RU9p + b3grVTRBL2NqdkZyb2JkL2tRRzlHZXcKQ2h/wKDs0P1g2tXgfAi/DszSdLYhcbeL + hZP5Bb1zkCXadRlncRMMS05ZqAdErP0fTy410jcpX5iQFZqHA3zj5Q== + -----END AGE ENCRYPTED FILE----- + - recipient: age136558pknq6glx2xftavt7mm3p4jcpu54kej2kxryeu78m5r59e0qvawl5l + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzT2xKNmFKb0ZWNzF1Ymxj + cEs0cWFRdmlKb3pHSmNwajhyRWYwK0RTYnlZClVBUVV2NGNqWDdzMkVRNGZoT1J0 + UFFUU1B0UVpWbENQTThrQUdSN2tINHMKLS0tIHc0ZlIxdXNXUEVYZWNTTFJxdE5z + ZHRmZ2lzci9ZVlJRVCsxa3pWNVl3eGcKFA9A2nwRcYMf/RnEHUgtxV53l/Kn6rhG + BEffZq7es2mZH6PEt5DZ4T8LZG2vi7H9RTQAfFBzGiocB41QIk50Nw== + -----END AGE ENCRYPTED FILE----- + - recipient: age15959gprm59azjflvpj97yt0lj6dj4d2yv0nd6u9jp32lzwp3de7qzhf85y + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2UDVoakZxTCtlSGN6ZGRx + clRCMzVLMGI2L1ZIcUc0bmFnS01oSFBjMkJnCmNoWE0zUy8ySll1YnV0Z0NpR0xD + aFJuVnBDU05ha3dSS2NhN3d4MWVnSWMKLS0tIGNDVTM0Z25BYWY5MkhDQm5Cd2JD + c200TjlWUnFqRCs4V0FjM25iT3YrZTQKfpfrN++o6SZerazvwpuiYLpvJL4Bb4U/ + UIpMVS/rJhDrrBfMsCj253CRYRu73mbN28xnK+e68cl8l3EiMyEkEA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2023-11-01T22:20:35Z" + mac: ENC[AES256_GCM,data:tR1SlKiL9frBg3/KYrb8Igdgbx17mDreNOZEbkR1b7KmwoCvzJbO//5DT7yNPp4xiezTB/fW9xKNVSpBTJCbeifpj2hJGGC3VgUDpuZ9PiNcslIgvdw3tesGkRNq8srDgCx78CGl2q8wYxTtm4CjmjHv662OgNiXqbVHTRzOmZo=,iv:9SzPvUVlh8yNnSKEbaTyXw4JlQ0kbmR+L+9tyI3s2SU=,tag:wbq7R+tkt9uSGQzRs2g/Cw==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.7.3 diff --git a/nix/hosts/loki/secrets/autheliaEnv.age b/nix/hosts/loki/secrets/autheliaEnv.age new file mode 100644 index 0000000..fd87312 --- /dev/null +++ b/nix/hosts/loki/secrets/autheliaEnv.age @@ -0,0 +1,7 @@ +age-encryption.org/v1 +-> ssh-ed25519 2raHcA UopagrzhQ7o6Ei5ohB/AKvXAQpYoymqzRfkSq6Te+FI +B4sKbyqTDQYLkAufkmCKpqNnfsBqRIFW9Na8Wmaes+0 +-> -T{-grease &FHP)IRf ^^=1 +sO0uI5cTK1vzegmFu6Z1MWOmdHl5KLNNt8zM +--- 9Yi8JpNjdS7+7f1iDqiMimO+NZPOWQlANzxv0+CRjh4 +2 o6+>n կbOc_x$M4Ym0p! k qrѰx@x;6FӕHE Lq60V[h39)>N_7P(, \ No newline at end of file diff --git a/nix/hosts/loki/secrets/autheliaJWT.age b/nix/hosts/loki/secrets/autheliaJWT.age new file mode 100644 index 0000000..4386d08 Binary files /dev/null and b/nix/hosts/loki/secrets/autheliaJWT.age differ diff --git a/nix/hosts/loki/secrets/autheliaStorage.age b/nix/hosts/loki/secrets/autheliaStorage.age new file mode 100644 index 0000000..ed2798d --- /dev/null +++ b/nix/hosts/loki/secrets/autheliaStorage.age @@ -0,0 +1,7 @@ +age-encryption.org/v1 +-> ssh-ed25519 2raHcA 48PYTDeCjvtaFRWJIx7v4SNpXfEpyZLtZDv6v5ncTDg +17y3dtifigTR9UhE4pYwiL5mxHV55euqqHujAjRLfWA +-> 4v-grease +Ng4P0N2e3Y8 +--- EA0m2jKaIGJTCpByITWXMpKYMOYl8YDhtQ36SWyamvQ +R4<8h.:# QO F̀ y@@V>җ4]ڲ t%!a yFoBҟm.f &- \ No newline at end of file diff --git a/nix/hosts/loki/secrets/corednsEnv.age b/nix/hosts/loki/secrets/corednsEnv.age new file mode 100644 index 0000000..62dc2ca --- /dev/null +++ b/nix/hosts/loki/secrets/corednsEnv.age @@ -0,0 +1,7 @@ +age-encryption.org/v1 +-> ssh-ed25519 2raHcA iMG/QdssSuUj8g+BzXHVSmxVQhjFjSFSF2mXzsDLKyw +hyWCfDgSNeqdOMHvxLUlN4tIoNIkWbHLtFDgNe3PPnI +-> [ln-grease +Mg +--- Ftv6pBfLBnx+g53L2JlHuuEDnFSFyQCA64dRVm7A+Lk +z=q?KaW9.CWnQCkr 5L=b# ] ssh-ed25519 2raHcA fLJf572ssCY1R/j8Ab6M78mVbu0LB4WnCoYWLUX0ThY +62C1EHeQw6TiEmvJzrnT6mL57KYrd0u3ac7fXLkysCc +-> k.)s"?Q{-grease xrn[$ K g/46Dt[ +BPLyp(2 +Reov4yvYn+RO3uF24VMGVRbc7ON9FaJyTmKI6RTtN63eCV3so5N+857CAPX5Wgkl +9A9THJyJGmOiOH0 +--- YFSiKZSahCQd0NzVN+cqbYudvqSVfaT93G5cCk7pF9o +yfE`~)ů5V?9lהϜE8q w` ssh-ed25519 2raHcA HUqsFDZZMXcgzLwLg0qoCUGLg6KoQtHV2CULJD01ST8 +/R7VIHIiH5OgObzxZ+uME1tsrd0pRXWw5nk7i5IfCZo +-> ssh-ed25519 6Tz/TQ IXXM4piUxgyVPFp85sI/5ZsZJR5X57QRky/9QZ+Cgw4 +P3HNkpcbVnZyaAILG7J6Zcv4m3g62tOqeDtFIKt9ZQE +-> cmd/caddy/main.go + cat cmd/caddy/main.go + ''; + + postConfigure = '' + cp vendor/go.sum ./ + cp vendor/go.mod ./ + ''; + + meta = with lib; { + homepage = https://caddyserver.com; + description = "Fast, cross-platform HTTP/2 web server with automatic HTTPS"; + license = licenses.asl20; + maintainers = with maintainers; [rushmorem fpletz zimbatm]; + }; + } diff --git a/nix/modules/dnscrypt.nix b/nix/modules/dnscrypt.nix new file mode 100644 index 0000000..77bf7b5 --- /dev/null +++ b/nix/modules/dnscrypt.nix @@ -0,0 +1,118 @@ +{lib, ...}: { + services.dnscrypt-proxy2 = { + enable = true; + # don't go from scratch. + upstreamDefaults = true; + settings = { + listen_addresses = [ + "127.0.0.1:53" + "[::1]:53" + ]; + ipv4_servers = true; + ipv6_servers = false; + dnscrypt_servers = true; + doh_servers = true; + odoh_servers = false; + require_dnssec = true; + require_nolog = true; + require_nofilter = true; + disabled_server_names = [ + "google-ipv6" + "cloudflare" + "cloudflare-ipv6" + "cisco" + "cisco-ipv6" + "cisco-familyshield" + "cisco-familyshield-ipv6" + "yandex" + "apple" + "doh.dns.apple.com" + "ffmuc.net" + # "dnswarden-uncensor-dc", + # "dnswarden-uncensor-dc-swiss", + # "techsaviours.org-dnscrypt", + "dns.watch" + "pryv8boi" + "dct-at1" + "dct-ru1" + "dct-de1" + # "dnscrypt.be", + # "meganerd", + "scaleway-ams" + "scaleway-fr" + "dnscrypt.pl" + "acsacsar-ams-ipv4" + "dnscrypt.uk-ipv4" + "adguard-dns-unfiltered" + ]; + http3 = true; + timeout = 1000; + keepalive = 30; + lb_strategy = "p7"; + lb_estimator = true; + log_level = 2; + use_syslog = true; + cert_refresh_delay = 60; + bootstrap_resolvers = [ + "9.9.9.9:53" + "84.200.69.80:53" + "84.200.70.40:53" + "185.38.27.139:53" + "130.226.161.34:53" + # "[2a01:3a0:53:53::]:53" + # "[2001:67c:28a4::]:53" + # "[2001:1608:10:25::1c04:b12f]:53" + ]; + ignore_system_dns = true; + # never timeout; + netprobe_timeout = -1; + netprobe_address = "9.9.9.9:53"; + # netprobe_address = "144.91.70.62:80"; + block_ipv6 = false; + block_unqualified = true; + # block_undelegated = true; + block_undelegated = false; + reject_ttl = 10; + cache = true; + cache_size = 10000; + cache_min_ttl = 2400; + cache_max_ttl = 86400; + cache_neg_min_ttl = 60; + cache_neg_max_ttl = 600; + + sources.opennic = { + urls = [ + "https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/opennic.md" + "https://download.dnscrypt.info/resolvers-list/v3/opennic.md" + ]; + minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3"; + cache_file = "/var/cache/dnscrypt-proxy/opennic.md"; + refresh_delay = 24; + prefix = ""; + }; + + static."dotya.ml".stamp = "sdns://AQcAAAAAAAAAETE0NC45MS43MC42Mjo1NDQzIHF-JiN46cNwFXJleEVWGWgrhe2QeysUtZoo9HwzYCMzITIuZG5zY3J5cHQtY2VydC5kbnNjcnlwdC5kb3R5YS5tbA"; + + #sources.public-resolvers = { + # urls = [ + # "https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md" + # "https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md" + # ]; + # cache_file = "/var/lib/dnscrypt-proxy2/public-resolvers.md"; + # minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3"; + #}; + + # You can choose a specific set of servers from https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v3/public-resolvers.md + # server_names = [ ... ]; + }; + }; + + systemd.services.dnscrypt-proxy2.serviceConfig = { + StateDirectory = "dnscrypt-proxy"; + WorkingDirectory = "/"; + # StartLimitIntervalSec = 5; + StartLimitBurst = 10; + Restart = "always"; + RestartSec = 7; + }; +} diff --git a/nix/modules/firefox-syncserver.nix b/nix/modules/firefox-syncserver.nix new file mode 100644 index 0000000..f956cc0 --- /dev/null +++ b/nix/modules/firefox-syncserver.nix @@ -0,0 +1,73 @@ +{ + config, + pkgs, + sops-nix, + ... +}: let + domain = p.domainName; + d = p.shortDomain; + p = config.sops.placeholder; + svc = "firefox-syncserver.service"; +in { + # ref: https://nixos.org/manual/nixos/stable/#module-services-firefox-syncserver + + sops = { + secrets = { + "shortDomain" = { + restartUnits = [svc]; + }; + "ffsync/masterSecret" = { + restartUnits = [svc]; + }; + "ffsync/tokenserverMetricsHashSecret" = { + restartUnits = [svc]; + }; + }; + templates = { + ffsync-secrets = { + content = '' + SYNC_MASTER_SECRET=${p."ffsync/masterSecret"} + SYNC_TOKENSERVER__FXA_METRICS_HASH_SECRET=${p."ffsync/tokenserverMetricsHashSecret"} + ''; + }; + }; + }; + + services.mysql.package = pkgs.mariadb; + + services.firefox-syncserver = { + enable = true; + secrets = config.sops.templates.ffsync-secrets.path; + #secrets = builtins.toFile "sync-secrets" '' + # SYNC_MASTER_SECRET=this-secret-is-actually-leaked-to-/nix/store + #''; + database.createLocally = true; + singleNode = { + # autoconfigure. + enable = true; + hostname = "localhost"; + # hostname = "ffsync." + domain; + # hostname = "ffsync." + d; + # url = "https://ffsync." + d; + # url = "https://ffsync." + domain; + # url = "https://ffsync.${domain}"; + #url = "http://localhost:" + toString config.services.firefox-syncserver.settings.port; + # url = "http://localhost:5000"; + }; + settings = { + port = 5678; + syncserver = { + public_url = "https://ffsync.${domain}/"; + sqluri = "sqlite://///tmp/syncserver.db"; + }; + browserid = { + backend = "tokenserver.verifiers.LocalVerifier"; + audiences = "https://ffsync.${domain}/"; + }; + tokenserver = { + node_type = "sqlite"; + }; + }; + }; + systemd.services.firefox-syncserver.wants = ["sops-nix.service"]; +} diff --git a/nix/modules/nix-serve.nix b/nix/modules/nix-serve.nix new file mode 100644 index 0000000..62e1b49 --- /dev/null +++ b/nix/modules/nix-serve.nix @@ -0,0 +1,7 @@ +{config, ...}: { + services.nix-serve = { + enable = true; + openFirewall = true; + bindAddress = "127.0.0.1"; + }; +} diff --git a/nix/modules/nix.nix b/nix/modules/nix.nix new file mode 100644 index 0000000..bfb6af6 --- /dev/null +++ b/nix/modules/nix.nix @@ -0,0 +1,18 @@ +{pkgs, ...}: { + nix = { + gc.automatic = true; + gc.options = "--delete-older-than 30d"; + optimise.automatic = true; + settings = { + experimental-features = ["nix-command" "flakes" "recursive-nix"]; + keep-derivations = true; + keep-outputs = true; + auto-optimise-store = true; + fallback = true; + sandbox = true; + trusted-public-keys = ["nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="]; + trusted-substituters = ["trusted-substituters = https://nix-community.cachix.org https://cache.nixos.org"]; + }; + package = pkgs.nixUnstable; + }; +} diff --git a/nix/modules/packages.nix b/nix/modules/packages.nix new file mode 100644 index 0000000..303bad2 --- /dev/null +++ b/nix/modules/packages.nix @@ -0,0 +1,48 @@ +{pkgs, ...}: { + environment = { + # https://github.com/NixOS/nixpkgs/issues/195795 + defaultPackages = []; + # List packages installed in system profile. To search, run: + # $ nix search wget + systemPackages = with pkgs; [ + vim + git + curl + wget + rsync + file + gnused + bat + p7zip + zstd + b3sum + + btop + htop + iotop + + lsof + tcpdump + dnsutils + netcat + ethtool + avahi + + nix-zsh-completions + direnv + + sops + age + + starship + eza + silver-searcher + ripgrep + zellij + du-dust + # dhall + + tailscale + ]; + }; +} diff --git a/nix/modules/sysctl.nix b/nix/modules/sysctl.nix new file mode 100644 index 0000000..723a6ef --- /dev/null +++ b/nix/modules/sysctl.nix @@ -0,0 +1,146 @@ +{...}: { + boot.kernel.sysctl = { + "kernel.panic" = 60; + "vm.swappiness" = 2; + #"vm.vfs_cache_pressure" = 80; + "net.ipv4.ip_forward" = 1; + #"net.ipv4.tcp_window_scaling" = 0; + # as per https://wiki.archlinux.org/index.php/Sysctl#Improving_performance + "net.core.rmem_default" = 1048576; + "net.core.rmem_max" = 16777216; + # "net.core.rmem_max" = 268435456; + "net.core.wmem_default" = 1048576; + "net.core.wmem_max" = 16777216; + # "net.core.wmem_max" = 268435456; + "net.core.optmem_max" = 65536; + + # https://unix.stackexchange.com/a/471951 + # + # "net.ipv4.tcp_rmem" = "4096 87380 20097152"; + # "net.ipv4.tcp_wmem" = "4096 65536 16777216"; + "net.ipv4.tcp_rmem" = "4096 87380 134217728"; + "net.ipv4.tcp_wmem" = "4096 65536 134217728"; + + "net.ipv4.udp_rmem_min" = 8192; + "net.ipv4.udp_wmem_min" = 8192; + + # TCP Fast Open is an extension to the transmission control protocol (TCP) that + # helps reduce network latency by enabling data to be exchanged during the + # sender's initial TCP SYN. Using the value 3 instead of the default 1 allows + # TCP Fast Open for both incoming and outgoing connections + "net.ipv4.tcp_fastopen" = 3; + + # tcp_max_tw_buckets is the maximum number of sockets in TIME_WAIT state. + # After reaching this number the system will start destroying the socket that + # are in this state. Increase this to prevent simple DOS attacks + "net.ipv4.tcp_max_tw_buckets" = 2000000; + + # tcp_tw_reuse sets whether TCP should reuse an existing connection in the + # TIME-WAIT state for a new outgoing connection if the new timestamp is + # strictly bigger than the most recent timestamp recorded for the previous + # connection. + # This helps avoid from running out of available network sockets + "net.ipv4.tcp_tw_reuse" = 1; + + # With the following settings, your application will detect dead TCP + # connections after 120 seconds (60s + 10s + 10s + 10s + 10s + 10s + 10s). + "net.ipv4.tcp_keepalive_time" = 60; + "net.ipv4.tcp_keepalive_intvl" = 10; + "net.ipv4.tcp_keepalive_probes" = 6; + + "net.ipv4.conf.default.rp_filter" = 2; + "net.ipv4.conf.all.rp_filter" = 2; + + "net.ipv4.conf.default.log_martians" = 1; + "net.ipv4.conf.all.log_martians" = 1; + + # Route cache is full: consider increasing sysctl net.ipv6.route.max_size + # net.ipv6.route.max_size = 8192; + "net.ipv6.route.max_size" = 65536; + + # https://developer.akamai.com/blog/2012/09/27/linux-tcpip-tuning-scalability + "net.ipv4.ip_local_port_range" = "18000 65535"; + #"net.netfilter.nf_conntrack_tcp_timeout_time_wait" = 30; + "net.netfilter.nf_conntrack_tcp_timeout_time_wait" = 60; + "net.netfilter.nf_conntrack_tcp_timeout_established" = 600; + "net.ipv4.tcp_slow_start_after_idle" = 0; + + "net.ipv4.tcp_no_metrics_save" = 1; + # doesn't work on arch with Zen, works on fedora with XanMod. + "net.core.default_qdisc" = "fq"; + + # failed to initialize inotify - default value here was 128 + "fs.inotify.max_user_instances" = 256; + + "net.ipv4.tcp_window_scaling" = 1; + + # The longer the maximum transmission unit (MTU) the better for performance, + # but the worse for reliability. This is because a lost packet means more data + # to be retransmitted and because many routers on the Internet cannot deliver + # very long packets + "net.ipv4.tcp_mtu_probing" = 1; + + # sync disk when buffer reach 6% of memory + "vm.dirty_ratio" = 6; + + "kernel.numa_balancing" = 1; + + "net.core.netdev_max_backlog" = 250000; + + # tcp_max_syn_backlog is the maximum queue length of pending connections + # 'Waiting Acknowledgment'. In the event of a synflood DOS attack, this queue + # can fill up pretty quickly, at which point TCP SYN cookies will kick in + # allowing your system to continue to respond to legitimate traffic, and + # allowing you to gain access to block malicious IPs. If the server suffers + # from overloads at peak times, you may want to increase this value a little + # bit + "net.ipv4.tcp_max_syn_backlog" = 8192; + + # TCP SYN cookie protection + # Helps protect against SYN flood attacks. Only kicks in when + # net.ipv4.tcp_max_syn_backlog is reached. More details at, for example, [6]. + # As of linux 5.10, it is set by default. + "net.ipv4.tcp_syncookies" = 1; + + # Protect against tcp time-wait assassination hazards, drop RST packets for + # sockets in the time-wait state. Not widely supported outside of Linux, but + # conforms to RFC + "net.ipv4.tcp_rfc1337" = 1; + + # Specify how many seconds to wait for a final FIN packet before the socket is + # forcibly closed. This is strictly a violation of the TCP specification, but + # required to prevent denial-of-service attacks. In Linux 2.2, the default + # value was 180 + "net.ipv4.tcp_fin_timeout" = 30; + + # When an attacker is trying to exploit the local kernel, it is often + # helpful to be able to examine where in memory the kernel, modules, + # and data structures live. As such, kernel addresses should be treated + # as sensitive information. + # + # Many files and interfaces contain these addresses (e.g. /proc/kallsyms, + # /proc/modules, etc), and this setting can censor the addresses. A value + # of "0" allows all users to see the kernel addresses. A value of "1" + # limits visibility to the root user, and "2" blocks even the root user. + "kernel.kptr_restrict" = 1; + + # mitigate JIT spraying attacks from unprivileged users + "net.core.bpf_jit_harden" = 1; + # disallow regular users to run BPF programs + "kernel.unprivileged_bpf_disabled" = 0; + + "fs.protected_fifos" = 1; + "fs.protected_symlinks" = 1; + "fs.protected_hardlinks" = 1; + "fs.protected_regular" = 2; + + # full randomisation + "kernel.randomize_va_space" = 2; + + "kernel.pid_max " = 4194304; + + # ad rootless podman + "user.max_user_namespaces" = 15000; + "net.ipv4.ping_group_range" = "0 2000000"; + }; +} diff --git a/nix/modules/tailscale.nix b/nix/modules/tailscale.nix new file mode 100644 index 0000000..95c522f --- /dev/null +++ b/nix/modules/tailscale.nix @@ -0,0 +1,10 @@ +{config, ...}: { + services.tailscale.enable = true; + + networking.firewall = { + trustedInterfaces = [config.services.tailscale.interfaceName]; + allowedUDPPorts = [config.services.tailscale.port]; + # specifically for Tailscale. + checkReversePath = "loose"; + }; +} diff --git a/nix/modules/uptime-kuma.nix b/nix/modules/uptime-kuma.nix new file mode 100644 index 0000000..7bd77de --- /dev/null +++ b/nix/modules/uptime-kuma.nix @@ -0,0 +1,3 @@ +{config, ...}: { + services.uptime-kuma.enable = true; +} diff --git a/nix/modules/zsh.nix b/nix/modules/zsh.nix new file mode 100644 index 0000000..07baa30 --- /dev/null +++ b/nix/modules/zsh.nix @@ -0,0 +1,77 @@ +{pkgs, ...}: { + users.defaultUserShell = pkgs.zsh; + + programs.zsh.interactiveShellInit = '' + if [[ ! -f $HOME/.local/share/zinit/zinit.git/zinit.zsh ]]; then + print -P "%F{33} %F{220}Installing %F{33}ZDHARMA-CONTINUUM%F{220} Initiative Plugin Manager (%F{33}zdharma-continuum/zinit%F{220})…%f" + command mkdir -p "$HOME/.local/share/zinit" && command chmod g-rwX "$HOME/.local/share/zinit" + command git clone https://github.com/zdharma-continuum/zinit "$HOME/.local/share/zinit/zinit.git" && \ + print -P "%F{33} %F{34}Installation successful.%f%b" || \ + print -P "%F{160} The clone has failed.%f%b" + fi + + source "$HOME/.local/share/zinit/zinit.git/zinit.zsh" + autoload -Uz _zinit + (( ''${+_comps} )) && _comps[zinit]=_zinit + + # Load the pure theme, with zsh-async library that's bundled with it. + zi ice pick"async.zsh" src"pure.zsh" + zi light sindresorhus/pure + + # A glance at the new for-syntax - load all of the above + # plugins with a single command. For more information see: + # https://zdharma-continuum.github.io/zinit/wiki/For-Syntax/ + zinit for \ + light-mode \ + zsh-users/zsh-autosuggestions \ + light-mode \ + zdharma-continuum/fast-syntax-highlighting \ + zdharma-continuum/history-search-multi-word #\ + #light-mode \ + #pick"async.zsh" \ + #src"pure.zsh" \ + #sindresorhus/pure + + zinit ice wait"2" lucid # load after 2 seconds + zinit load zdharma-continuum/history-search-multi-word + zinit load sindresorhus/pure + + zi ice as"program" make'!' atclone'./direnv hook zsh > zhook.zsh' atpull'%atclone' src"zhook.zsh" + zi light direnv/direnv + + plugins=( + git + gitignore + golang + # fzf + terraform + systemd + safe-paste + colored-man-pages + ) + zi snippet OMZP::git + zi snippet OMZP::gitignore + zi snippet OMZP::golang + # zi snippet OMZP::fzf + zi snippet OMZP::systemd + zi snippet OMZP::terraform + zi snippet OMZP::safe-paste + zi snippet OMZP::colored-man-pages + + HISTSIZE=10000 + SAVEHIST=10000 + setopt inc_append_history + setopt extended_history # write the history file in the ":start:elapsed;command" format + setopt share_history + setopt hist_expire_dups_first # expire duplicate entries first when trimming history + setopt hist_ignore_dups + setopt hist_ignore_all_dups + setopt hist_save_no_dups + setopt hist_ignore_space + setopt hist_reduce_blanks # remove superfluous blanks before recording entry + setopt hist_verify # don't execute immediately after expansion + + # eval "$(starship init zsh)" + # zinit load sindresorhus/pure + ''; +}