commit 0dd7fb7ac01a81c22343783c1120041d869b7e7a Author: Ember 'n0emis' Keske Date: Sat Apr 23 18:00:23 2022 +0200 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..dd0ebed --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Colmena NixOS Config Template + +* user gpg-keys (for them to be able to decrypt the secrets): `secrets/.gpg-id` +* collect/generate host gpg-keys: `./lib/create-keys.sh` +* edit stuff in password store: `./lib/pass.sh` + +## How to deploy +``` +./deploy.sh apply switch --on vpn7 +``` + +or to deploy all gateways (all hosts with the gateway-tag): + +``` +./deploy.sh apply switch --on gateway +``` + +There is a special case for the nixdeploy-host: +``` +./deploy.sh apply-local --node nixdeploy +``` diff --git a/common/default.nix b/common/default.nix new file mode 100644 index 0000000..9fcc83c --- /dev/null +++ b/common/default.nix @@ -0,0 +1,44 @@ +{ pkgs, lib, ... }: + +{ + nix = { + package = pkgs.nixFlakes; + extraOptions = '' + experimental-features = nix-command flakes + ''; + trustedUsers = [ "root" "@wheel" ]; + }; + + services.openssh.enable = true; + services.openssh.permitRootLogin = "without-password"; + + security.sudo.wheelNeedsPassword = false; + + time.timeZone = "Europe/Berlin"; + + networking.firewall.logRefusedConnections = false; + + programs.mtr.enable = true; + + environment.systemPackages = with pkgs; [ + bind.dnsutils + colmena + git + gnupg + htop + rsync + tmux + wget + ]; + + programs.vim.defaultEditor = true; + + users.users.root = { + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcOPtW5FWNIdlMQFoqeyA1vHw+cA8ft8oXSbXPzQNL9 n0emis@n0emis.eu" + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC8xqVakxJ+AwcIrS/wyL03N++pE09epwMFlIMXWvlpwwEp1J/0H7nygwxk/9LIZdabs/ETWn0s8oHAkc7YR1c6ajSTCDiZEYATAWt7t8t4Gw/80c8u8T50lIqmiDEEVbOVv3Vta/pAN4hAUp9U5DpYCkQbvF+NKKcK3Yp8d9usNC6ohqgTK+IGAEdMhvpbbNppDMXoWHuynBzUX7TS6ST6yEr0tD+CBbCpbfcMuwTI3lNtfywEVpuFaeHqDZx2QDrEX4bg0dRKgQstbXYdqmBfnOiBpUr8Wyl8U1J24rN+E07pBw/8KDGWbVg19/Ex8o4ht/p5voUfKVjD/DwWXTLntBirjfAgQAm4GH/qP4x3zNiTtlYlQFbXSk6VEVrTrxCB5rTWvGnhg31tk5P3YwvagDmGABazY5s/8tlttSc1yWBctWQJCjxSqcCLekxG4D1rVuGKCKOZgflQ9QFdQlKycInPBek3zi0i3GYkE1YnNFye5ggOnxT8qGuKjfdtZI9qvMJQO8lbEDzbYQvNns1V/k4ZobiihYwrG5TJUzZFEpMYetDK6tI8BRU11d+ja0jWzguj5/7wc0nrr/BiZ8FkAr2fZ60j2aI5kG0s3qjbrQbB/RXaGP9hRU0+480+IokNJJIcjv5iwH5ophdrjC8GH4So2kPPt0NXob1yNysdjw== simeon@noemis.me (OLD)" + ]; + }; + + environment.noXlibs = lib.mkForce false; +} diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..4edc885 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +DEPLOY_HOST="nixdeploy.neo.cccgoe.de" + +rsync -r . root@${DEPLOY_HOST}:/root/nixfiles/ +ssh -tA root@${DEPLOY_HOST} "cd /root/nixfiles && colmena $@" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..cbecf3f --- /dev/null +++ b/flake.lock @@ -0,0 +1,65 @@ +{ + "nodes": { + "ffnix": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1649686725, + "narHash": "sha256-t2ByQJ52I67nxdocQRtYOvBD7ZHNt6wRcWqq79EZmGw=", + "owner": "n0emis", + "repo": "ffnix", + "rev": "bdf5dfb77cd474d7fbddc1297526dc1ce339fdb2", + "type": "github" + }, + "original": { + "owner": "n0emis", + "ref": "main", + "repo": "ffnix", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1644229661, + "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1647992509, + "narHash": "sha256-AG40Nt5OWz0LBs5p457emOuwLKOvTtcv/2fUdnEN3Ws=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "d2caa9377539e3b5ff1272ac3aa2d15f3081069f", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "release-21.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "ffnix": "ffnix", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..07e3792 --- /dev/null +++ b/flake.nix @@ -0,0 +1,48 @@ +{ + description = "FFGoe NixOS Config"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/release-21.11"; + ffnix = { + url = "github:n0emis/ffnix/main"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + outputs = { self, nixpkgs, ffnix }: { + colmena = { + meta = { + nixpkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ + ffnix.overlay + ]; + }; + }; + + defaults = { config, lib, name, ... }: { + deployment.targetHost = lib.mkDefault (config.networking.hostName + "." + config.networking.domain); + + imports = [ + (./. + "/hosts/${name}") + ./common + ./modules + ffnix.nixosModule + ]; + }; + + vpn7 = { + deployment.tags = [ "gateway" "legacy" ]; + }; + + nixdeploy = { + deployment.allowLocalDeployment = true; + }; + + testhost = { + deployment.targetHost = "10.152.32.1"; + }; + + dfz-el = {}; + }; + }; +} diff --git a/hosts/nixdeploy/default.nix b/hosts/nixdeploy/default.nix new file mode 100644 index 0000000..4623fa6 --- /dev/null +++ b/hosts/nixdeploy/default.nix @@ -0,0 +1,37 @@ +{ config, pkgs, modulesPath, lib, ... }: + +{ + imports = [ + (modulesPath + "/virtualisation/lxc-container.nix") + ]; + + networking.hostName = "nixdeploy"; + networking.domain = "neo.cccgoe.de"; + + networking.useNetworkd = true; + networking.useDHCP = false; + networking.firewall.enable = false; + networking.useHostResolvConf = false; + + systemd.suppressedSystemUnits = [ + "dev-mqueue.mount" + "sys-kernel-debug.mount" + "sys-fs-fuse-connections.mount" + ]; + + systemd.network = { + links."10-eth0" = { + matchConfig.MACAddress = "02:a6:13:d1:e4:0b"; + linkConfig.Name = "eth0"; + }; + networks."10-eth0" = { + name = "eth0"; + address = [ "192.168.42.101/24" "2a01:4f8:10b:7e1::1:101/64" ]; + gateway = [ "192.168.42.1" "2a01:4f8:10b:7e1::1:1" ]; + }; + }; + + services.resolved.dnssec = "false"; + + system.stateVersion = "21.11"; +} diff --git a/hosts/vpn7/default.nix b/hosts/vpn7/default.nix new file mode 100644 index 0000000..7456e8f --- /dev/null +++ b/hosts/vpn7/default.nix @@ -0,0 +1,62 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running ‘nixos-help’). + +{ config, pkgs, ... }: + +{ + imports = + [ # Include the results of the hardware scan. + ./hardware-configuration.nix + ]; + + # Use the GRUB 2 boot loader. + boot.loader.grub.enable = true; + boot.loader.grub.version = 2; + # boot.loader.grub.efiSupport = true; + # boot.loader.grub.efiInstallAsRemovable = true; + # boot.loader.efi.efiSysMountPoint = "/boot/efi"; + # Define on which hard drive you want to install Grub. + boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only + + networking.hostName = "vpn7"; + networking.domain = "cccgoe.de"; + + # The global useDHCP flag is deprecated, therefore explicitly set to false here. + # Per-interface useDHCP will be mandatory in the future, so this generated config + # replicates the default behaviour. + networking.useDHCP = false; + networking.useNetworkd = true; + + systemd.network = { + links."10-eth0" = { + matchConfig.MACAddress = "f6:ac:c8:5c:f5:ce"; + linkConfig.Name = "eth0"; + }; + networks."10-eth0" = { + matchConfig = { + Name = "eth0"; + }; + addresses = [ + { + addressConfig = { + Address = "88.99.250.3/32"; + Peer = "88.99.250.30/32"; + }; + } + ]; + gateway = [ "88.99.250.30"]; + dns = [ "5.1.66.255" "185.150.99.255" "2001:678:e68:f000::" "2001:678:ed0:f000::" ]; + }; + }; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "21.11"; # Did you read the comment? + +} + diff --git a/hosts/vpn7/hardware-configuration.nix b/hosts/vpn7/hardware-configuration.nix new file mode 100644 index 0000000..e5e4d9d --- /dev/null +++ b/hosts/vpn7/hardware-configuration.nix @@ -0,0 +1,24 @@ +# 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 + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/49598cf8-8b50-46cf-b4dc-67dfa5fdfde8"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/lib/create-keys.sh b/lib/create-keys.sh new file mode 100755 index 0000000..0379c1b --- /dev/null +++ b/lib/create-keys.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +cd $(dirname $0)/.. + +read -r -a HOSTS <<< "$(colmena eval lib/get-hosts.nix | jq -r 'to_entries | map("\(.key) \(.value)") | @tsv')" + +p=0 +while [ "${HOSTS[$p]}" ] +do + hostname=${HOSTS[$p]} + ssh_host=root@${HOSTS[$p+1]} + + echo + echo "##### $hostname - $ssh_host" + ssh -o ConnectTimeout=10 $ssh_host "echo > /dev/null" + rc=$? + if [ $rc = 0 ]; then + echo "$hostname is online, checking gpg-key" + ssh $ssh_host "sudo -u root gpg --fingerprint --with-colons | grep '^fpr' | grep $(cat secrets/$hostname/.gpg-id | tail -n1) > /dev/null" + rc=$? + else + echo "$hostname is offline" + rc=0 + fi + + if ! [ $rc = 0 ]; then + echo "generating gpg-key" + mkdir -p secrets/$hostname + ssh $ssh_host "sudo rm -rf /root/.gnupg" + cat lib/keygen | sed "s/NAME/${hostname}/" | ssh -o RequestTTY=yes $ssh_host "sudo -u root gpg --generate-key --pinentry-mode loopback --batch /dev/stdin" + cp secrets/.gpg-id secrets/$hostname/.gpg-id + ssh $ssh_host "sudo -u root gpg --fingerprint --with-colons | grep '^fpr' | head -n1 | cut -d: -f10" >> secrets/$hostname/.gpg-id + ssh $ssh_host "sudo -u root gpg --fingerprint --with-colons | grep '^fpr' | head -n1 | cut -d: -f10" >> secrets/all/.gpg-id + ssh $ssh_host "sudo -u root gpg --export --armor" > secrets/.public-keys/$hostname + lib/pass.sh init -p $hostname $(cat secrets/$hostname/.gpg-id); + lib/pass.sh init -p all $(cat secrets/all/.gpg-id); + else + echo "key does already exist..." + fi + + let p=p+2 +done + diff --git a/lib/get-hosts.nix b/lib/get-hosts.nix new file mode 100644 index 0000000..312b040 --- /dev/null +++ b/lib/get-hosts.nix @@ -0,0 +1,3 @@ +{ nodes, pkgs, lib, ... }: +# Feels like a NixOS module - But you can return any JSON-serializable value +lib.attrsets.mapAttrs (k: v: v.config.deployment.targetHost) nodes diff --git a/lib/keygen b/lib/keygen new file mode 100644 index 0000000..5f5e7e7 --- /dev/null +++ b/lib/keygen @@ -0,0 +1,11 @@ +%echo Generating ed25519 primary and cv25519 subkey +Key-Type: eddsa +Key-Curve: Ed25519 +Key-Usage: sign +Name-Real: NAME +Subkey-Type: ecdh +Subkey-Curve: Curve25519 +Expire-Date: 0 +%no-protection +%commit +%echo done diff --git a/lib/pass.sh b/lib/pass.sh new file mode 100755 index 0000000..96d8182 --- /dev/null +++ b/lib/pass.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +cd $(dirname $0)/.. + +for key in secrets/.public-keys/* +do + gpg --import "$key" 2>/dev/null +done + +export PASSWORD_STORE_GPG_OPTS="--trust-model always" +export PASSWORD_STORE_DIR="$PWD/secrets" +pass $@ + diff --git a/modules/default.nix b/modules/default.nix new file mode 100644 index 0000000..7c2df14 --- /dev/null +++ b/modules/default.nix @@ -0,0 +1,7 @@ +{ ... }: + +{ + imports = [ + ./secrets + ]; +} diff --git a/modules/secrets/default.nix b/modules/secrets/default.nix new file mode 100644 index 0000000..58cef80 --- /dev/null +++ b/modules/secrets/default.nix @@ -0,0 +1,98 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + secret-file = types.submodule ({ ... }@moduleAttrs: { + options = { + name = mkOption { + type = types.str; + default = moduleAttrs.config._module.args.name; + }; + path = mkOption { + type = types.str; + readOnly = true; + default = "/run/secrets/${removeSuffix ".gpg" (baseNameOf moduleAttrs.config.source-path)}"; + }; + mode = mkOption { + type = types.str; + default = "0400"; + }; + owner = mkOption { + type = types.str; + default = "root"; + }; + group-name = mkOption { + type = types.str; + default = "root"; + }; + source-path = mkOption { + type = types.str; + default = pkgs.copyPathToStore "${toString ../../secrets}/${config.networking.hostName}/${moduleAttrs.config.name}.gpg"; + }; + encrypted = mkOption { + type = types.bool; + default = true; + }; + enable = mkOption { + type = types.bool; + default = true; + }; + }; + }); + enabledFiles = filterAttrs (n: file: file.enable) config.petabyte.secrets; + + mkDecryptSecret = file: pkgs.writeScript "decrypt-secret-${removeSuffix ".gpg" (baseNameOf file.source-path)}.sh" '' + #!${pkgs.runtimeShell} + set -eu pipefail + if [ ! -f "${file.path}" ]; then + umask 0077 + echo "${file.source-path} -> ${file.path}" + ${if file.encrypted then '' + ${pkgs.gnupg}/bin/gpg --decrypt ${escapeShellArg file.source-path} > ${file.path} + '' else '' + cat ${escapeShellArg file.source-path} > ${file.path} + ''} + fi + ''; + mkSetupSecret = file: pkgs.writeScript "setup-secret-${removeSuffix ".gpg" (baseNameOf file.source-path)}.sh" '' + #!${pkgs.runtimeShell} + set -eu pipefail + chown ${escapeShellArg file.owner}:${escapeShellArg file.group-name} ${escapeShellArg file.path} + chmod ${escapeShellArg file.mode} ${escapeShellArg file.path} + ''; + +in { + options.petabyte.secrets = mkOption { + type = with types; attrsOf secret-file; + default = {}; + }; + config = mkIf (enabledFiles != {}) { + system.activationScripts = let + files = unique (map (flip removeAttrs ["_module"]) (attrValues enabledFiles)); + decrypt = '' + function fail() { + rm $1 + echo "failed to decrypt $1" + } + echo setting up secrets... + mkdir -p /run/secrets + chown 0:0 /run/secrets + chmod 0755 /run/secrets + ${concatMapStringsSep "\n" (file: '' + ${mkDecryptSecret file} || fail ${file.path} + '') files} + ''; + setup = '' + echo setting up secrets... + ${concatMapStringsSep "\n" (file: '' + ${mkSetupSecret file} || echo "failed to set up ${file.path}" + '') files} + ''; + in { + decrypt-secrets.text = "source ${pkgs.writeText "setup-secrets.sh" decrypt}"; + setup-secrets = stringAfter [ "users" "groups" ] "source ${pkgs.writeText "setup-secrets.sh" setup}"; + users.deps = [ "decrypt-secrets" ]; + }; + }; +} diff --git a/secrets/.gpg-id b/secrets/.gpg-id new file mode 100644 index 0000000..e5f29f1 --- /dev/null +++ b/secrets/.gpg-id @@ -0,0 +1 @@ +6E10217E3187069E057DF5ABE0262A773B824745 diff --git a/secrets/.public-keys/nixdeploy b/secrets/.public-keys/nixdeploy new file mode 100644 index 0000000..c7847b0 --- /dev/null +++ b/secrets/.public-keys/nixdeploy @@ -0,0 +1,13 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEYiyiyxYJKwYBBAHaRw8BAQdAPsPSocI7hwg1Ll+MQtAkXB97Fs4wS6jAgHJy +4tIV6Yq0CW5peGRlcGxveYiQBBMWCAA4FiEERpX3ufdo1Ms5XHiPp9zPzW66SHsF +AmIsossCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQp9zPzW66SHvCUQEA +wDc+ifC0KX1diclphsmr379u1zIyCgQnzo1CZfzbfksBAMGHrPrGC01Yhijvp8Op +97rCKi5Tc9o3pzIGaQhxheMGuDgEYiyiyxIKKwYBBAGXVQEFAQEHQNBwRBfv1C/n +Kj7Zwa4tW/oDO/CofWpZc/rBLW2DTQVnAwEIB4h4BBgWCAAgFiEERpX3ufdo1Ms5 +XHiPp9zPzW66SHsFAmIsossCGwwACgkQp9zPzW66SHtIvgEAm3V0VlCWh3vAu2sS +3kolo3C3O8VNljTZpjyHX9uZb0sA/0kjj9ySpLWg9ct1w9Xq1rx8NAqg3717QfEV +Eu93PqIH +=sw4f +-----END PGP PUBLIC KEY BLOCK----- diff --git a/secrets/.public-keys/vpn7 b/secrets/.public-keys/vpn7 new file mode 100644 index 0000000..521cd98 --- /dev/null +++ b/secrets/.public-keys/vpn7 @@ -0,0 +1,13 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEYiyjMBYJKwYBBAHaRw8BAQdA6Eqvi71nq1N0YXSv2H7bQPuNhRN71OrMDCav +hcuHz9i0BHZwbjeIkAQTFggAOBYhBIkqg13mIq22arDfh8nOp4wJbO8QBQJiLKMw +AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEMnOp4wJbO8Q+YQBAPka4X71 +eeJ+fOLgE2JlyqXRcGzKtEz5YzAFP74oC2vBAP9w0MrbRVtzwQPa4Omyy1SMtwoP +a2QmPB52aJ6nhfBlArg4BGIsozASCisGAQQBl1UBBQEBB0C+DsCLXf4vApdVZ71N +wjQUGQp9nd6zyMXXsl7oPfGvDQMBCAeIeAQYFggAIBYhBIkqg13mIq22arDfh8nO +p4wJbO8QBQJiLKMwAhsMAAoJEMnOp4wJbO8QrhMBAKcljw2BYS05IPev6kKdrlZ4 +0H3l4So/GlaI/bkhyEdHAQDytHkdN4TK4QF/p6i/6+F0pClZJINm9aOlCksq3thv +AQ== +=qCbs +-----END PGP PUBLIC KEY BLOCK----- diff --git a/secrets/all/.gpg-id b/secrets/all/.gpg-id new file mode 100644 index 0000000..c394205 --- /dev/null +++ b/secrets/all/.gpg-id @@ -0,0 +1,4 @@ +6E10217E3187069E057DF5ABE0262A773B824745 +F1D6AA729DE5DDF5F94CEEE2D764341778B42777 +4695F7B9F768D4CB395C788FA7DCCFCD6EBA487B +892A835DE622ADB66AB0DF87C9CEA78C096CEF10 diff --git a/secrets/nixdeploy/.gpg-id b/secrets/nixdeploy/.gpg-id new file mode 100644 index 0000000..e60d075 --- /dev/null +++ b/secrets/nixdeploy/.gpg-id @@ -0,0 +1,2 @@ +6E10217E3187069E057DF5ABE0262A773B824745 +4695F7B9F768D4CB395C788FA7DCCFCD6EBA487B diff --git a/secrets/vpn7/.gpg-id b/secrets/vpn7/.gpg-id new file mode 100644 index 0000000..7d41c90 --- /dev/null +++ b/secrets/vpn7/.gpg-id @@ -0,0 +1,2 @@ +6E10217E3187069E057DF5ABE0262A773B824745 +892A835DE622ADB66AB0DF87C9CEA78C096CEF10 diff --git a/secrets/vpn7/fastd.gpg b/secrets/vpn7/fastd.gpg new file mode 100644 index 0000000..b52d939 --- /dev/null +++ b/secrets/vpn7/fastd.gpg @@ -0,0 +1 @@ +N!]<%@$&$9qM-;&AIc$ ) &z5ܡeϡFxD V=V3ۄNűZ1r@80_1#kݐUmnf[r4q(ґuƒ+ >.h4E5 kX#ӛÉ1u2ޖez=9Zib"CEv',k byijzu=i(8D/Igps̳(MVZ&7# Bjt \ No newline at end of file