diff --git a/.gitignore b/.gitignore index bb24878..a2fa571 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ result *.qcow2 .direnv/ book/ +fanny-efi-vars.fd diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 18c465d..afc55a1 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -14,6 +14,7 @@ - [How-to]() - [Create New Host](./anleitung/create.md) - [Sops](./anleitung/sops.md) + - [Wireguard](./anleitung/wireguard.md) - [Updates](./anleitung/updates.md) - [Rollbacks](./anleitung/rollback.md) - [MicroVM](./anleitung/microvm.md) diff --git a/doc/src/anleitung/microvm.md b/doc/src/anleitung/microvm.md index 1775767..9856ed0 100644 --- a/doc/src/anleitung/microvm.md +++ b/doc/src/anleitung/microvm.md @@ -23,18 +23,21 @@ In order to test persistent microvms locally we need to create them using the `` This is necessary to be able to mount persistent /etc and /var volumes on those hosts. Do the following: +Prepare your host by including `microvm.nixosModules.host` in your `flake.nix` [Microvm Docs](https://astro.github.io/microvm.nix/host.html) + + ```bash -# go into our repo and start the default dev shell (or us direnv) +# go into our repo and start the default dev shell (or use direnv) nix develop .# # create a microvm on your host (on the example of durruti) sudo microvm -c durruti -f git+file:///home/username/path/to/infrastructure/repo # start the vm -sudo systemctl start microvm@durruti.serivce +sudo systemctl start microvm@durruti.service # this may fail, if so we most probably need to create /var /etc manually, then restart -sudo mkdir /var/lib/microvms/durruti/{var, etc} +sudo mkdir -p /var/lib/microvms/durruti/{var,etc} # now you can for example get the rsa host key from /var/lib/microvms/durruti/etc/ssh/ diff --git a/doc/src/anleitung/wireguard.md b/doc/src/anleitung/wireguard.md new file mode 100644 index 0000000..1cae422 --- /dev/null +++ b/doc/src/anleitung/wireguard.md @@ -0,0 +1,11 @@ +# Wireguard +Running on the raspberry pi + +- Create new keys + - Enter nix shell for wg commands `nix-shell -p wireguard-tools` + - New private key `wg genkey > secrets/keys/wireguard/example.key` + - Encrypt with `sops -e -i secrets/keys/wireguard/example.key` + - commit keys only after encrypting + - Decrypt to stdout `sops -d secrets/keys/wireguard/example.key` + - Decrypt for use on a client `sops -d secrets/keys/wireguard/private.key > /tmp/private.key` + - Display public key `sops -d secrets/keys/wireguard/example.key | wg pubkey` diff --git a/machines/.sops.yaml b/machines/.sops.yaml index 44f2af4..720f546 100644 --- a/machines/.sops.yaml +++ b/machines/.sops.yaml @@ -34,3 +34,10 @@ creation_rules: - *machine_durruti age: - *admin_atlan + - path_regex: secrets/keys/wireguard/.* + key_groups: + - pgp: + - *admin_kalipso + - *admin_kalipso_dsktp + age: + - *admin_atlan \ No newline at end of file diff --git a/machines/configuration.nix b/machines/configuration.nix index 5b69f0a..bb5f4c7 100644 --- a/machines/configuration.nix +++ b/machines/configuration.nix @@ -123,7 +123,6 @@ in ]; }; - durruti = nixosSystem { system = "x86_64-linux"; specialArgs.inputs = inputs; @@ -133,6 +132,16 @@ in ]; }; + vpn = nixosSystem { + system = "x86_64-linux"; + specialArgs.inputs = inputs; + specialArgs.self = self; + modules = makeMicroVM "vpn" "10.0.0.10" [ + self.nixosModules.malobeo + ./vpn/configuration.nix + ]; + }; + lucia = nixosSystem { system = "aarch64-linux"; specialArgs.inputs = inputs; diff --git a/machines/lucia/secrets.yaml b/machines/lucia/secrets.yaml index 4a8aa1f..8b93981 100644 --- a/machines/lucia/secrets.yaml +++ b/machines/lucia/secrets.yaml @@ -1,5 +1,6 @@ hello: ENC[AES256_GCM,data:3VuyuX7MaLSmor4W22F3FUCGp8SUq4pE6z5nuiZenH07+zEeMAllVCP6g/j1fQ==,iv:A3Oh99AchsmrkMEb4ZRSIigb8Cr+3WlQtsgyZJGpLY8=,tag:TOHF9BaydkRD6cJAndryTg==,type:str] njala_api_key: ENC[AES256_GCM,data:qXGngMJaAOk2Gb8B4nwMTht9Vp/OEhGmKS5vh1kpi0MyqcsmwuwpWuUz+RWD6NDFn2w/35M=,iv:lsZyCrmcT1xJcLjzK4zkcRYmbKUeLUFYZ7oDfCVJV8c=,tag:WK+aF3XGBRDQuvL87Qdusw==,type:str] +wireguard_private: ENC[AES256_GCM,data:ZxGbYLQKvrPibLpId+xbvqphlcgm/U5Se9XMS4FogmY4HfJnh9Y4Ja/x20I=,iv:PnZjiyKk1XuIq5/NLtOdWh20ytDEMYM7LJqmCoSrD0s=,tag:CZErG28Lo3aiQGovxEeZtA==,type:str] sops: kms: [] gcp_kms: [] @@ -15,8 +16,8 @@ sops: aVFGZjk4UjVJa3FoMDJiaXR2MmdiQ2cKSVgIdxPBNTbNFQbdI5ECNGQrDUK9dQI3 f3mHj+XAPmEtjUXLyxUI1gQ+8toctnU6cgJ+HdGLX01lgTHwz7uieQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-10-24T15:09:51Z" - mac: ENC[AES256_GCM,data:f/wf0EuNmy+ic/k+fHg3IJ8p4I8BftFn6QwGJsXJgTBDspe7Plnwh+kGEqdPg8OEbWy/1niRfCXJa/vKoquWsxL7LUP2lGYT7lj7QYuj2F8fo2WIe2qhCikuxO6Q1asKyBcebYv5KAY/yQlVBYs9X9tcU6Fu4IU2AmJhjYB6m3s=,iv:K3DCEV4/FocdnEulNM9snH4uym8pAZRSmsYbM+rghe4=,tag:429oJE1du0IRl4aDuLzoZA==,type:str] + lastmodified: "2024-11-14T18:10:54Z" + mac: ENC[AES256_GCM,data:DPQsRraMAvoezHsA7uM8q8sEevnZRnpU1vydEL72r6KJj12dT58KXCTuUeNgD+320LE1i83k6HLdM9C/+uniu73Ba5JSwglLLDBkZpfsdCde0aqkGjQd/RF/0Vb8ZbE/KCCCMVOjT6hX6RSDSEujoRMY26n1CWYtPeivqpWb5NY=,iv:TarRTCyPRoyQEb3qoXAJcOYtrTtftyZO4ahkyTZT8qU=,tag:A0kqa1szfk6Z5etivjB/lA==,type:str] pgp: - created_at: "2024-11-14T13:02:46Z" enc: |- @@ -77,4 +78,4 @@ sops: -----END PGP MESSAGE----- fp: 3474196f3adf27cfb70f8f56bcd52d1ed55033db unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/machines/modules/disko/fanny.nix b/machines/modules/disko/fanny.nix index 53380c6..9366fe6 100644 --- a/machines/modules/disko/fanny.nix +++ b/machines/modules/disko/fanny.nix @@ -17,6 +17,13 @@ mountOptions = [ "umask=0077" ]; }; }; + encryptedSwap = { + size = "8G"; #set to 100M for testing + content = { + type = "swap"; + randomEncryption = true; + }; + }; zfs = { size = "100%"; content = { @@ -70,6 +77,7 @@ # Workaround: cannot import 'zroot': I/O error in disko tests options.cachefile = "none"; rootFsOptions = { + mountpoint = "none"; compression = "zstd"; "com.sun:auto-snapshot" = "false"; }; @@ -114,6 +122,7 @@ storage = { type = "zpool"; mode = "mirror"; + rootFsOptions = { mountpoint = "none"; }; datasets = { encrypted = { diff --git a/machines/modules/malobeo/peers.nix b/machines/modules/malobeo/peers.nix new file mode 100644 index 0000000..c42ae11 --- /dev/null +++ b/machines/modules/malobeo/peers.nix @@ -0,0 +1,24 @@ +{ + "vpn" = { + role = "server"; + publicIp = "5.9.153.217"; + ips = [ "10.100.0.1/24" ]; + allowedIPs = [ "10.100.0.0/24" ]; + listenPort = 51821; + publicKey = ""; + }; + + "fanny" = { + role = "client"; + ips = [ "10.100.0.2/24" ]; + allowedIPs = [ "10.100.0.0/24" ]; + publicKey = ""; + }; + + "test" = { + role = "client"; + ips = [ "10.100.0.3/24" ]; + allowedIPs = [ "10.100.0.0/24" ]; + publicKey = ""; + }; +} diff --git a/machines/modules/malobeo/wireguard.nix b/machines/modules/malobeo/wireguard.nix new file mode 100644 index 0000000..ffefa89 --- /dev/null +++ b/machines/modules/malobeo/wireguard.nix @@ -0,0 +1,90 @@ +{ config, self, lib, inputs, options, pkgs, ... }: + +with lib; + +let + cfg = config.services.malobeo.vpn; + peers = import ./peers.nix; + myPeer = if cfg.name == "" then peers.${config.networking.hostName} else peers.${cfg.name}; + + peerList = builtins.filter (peer: peer.role != myPeer.role) (builtins.attrValues peers); + peerListWithEndpoint = map (host: + if host.role == "server" then + host // { endpoint = "${host.publicIp}:${builtins.toString host.listenPort}"; } + else + host + ) peerList; + filteredPeerlist = map (host: builtins.removeAttrs host [ + "role" + "ips" + "listenPort" + "publicIp" + ] ) peerListWithEndpoint; +in +{ + options = { + services.malobeo.vpn = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc "Setup wireguard to access malobeo maintainance vpn"; + }; + + name = mkOption { + default = ""; + type = types.str; + description = '' + Name of the host in peers.nix, if empty uses hostname + ''; + }; + + privateKey = mkOption { + default = ""; + type = types.str; + description = '' + Path to private key + ''; + }; + }; + }; + + imports = [ + inputs.microvm.nixosModules.host + ]; + + config = mkIf cfg.enable { + assertions = [ + #{ + # assertion = !(myPeer != "client" && cfg.role != "server"); + # message = '' + # VPN Role must be either client or server, nothing else! + # ''; + #} + ]; + + networking.wireguard = { + enable = true; + interfaces = { + wg0 = { + ips = myPeer.ips; + listenPort = mkIf (myPeer.role == "server") myPeer.listenPort; + + # This allows the wireguard server to route your traffic to the internet and hence be like a VPN + # For this to work you have to set the dnsserver IP of your router (or dnsserver of choice) in your clients + postSetup = mkIf (myPeer.role == "server") '' + ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.100.0.0/24 -o eth0 -j MASQUERADE + ''; + + # This undoes the above command + postShutdown = mkIf (myPeer.role == "server") '' + ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.100.0.0/24 -o eth0 -j MASQUERADE + ''; + + privateKey = cfg.privateKey; + + peers = filteredPeerlist; + }; + }; + }; + }; +} diff --git a/machines/secrets/keys/wireguard/private.key b/machines/secrets/keys/wireguard/private.key new file mode 100644 index 0000000..c24c237 --- /dev/null +++ b/machines/secrets/keys/wireguard/private.key @@ -0,0 +1,31 @@ +{ + "data": "ENC[AES256_GCM,data:MJnybSouJW9QcWks/6fBgYXhM1zREa76FDVh0vGF9LwffY4ceLMQpOsFXEN7,iv:z0H0r6VSXy92uiS9bGXL5KxqiA3jqAiAgAH5KMxppsE=,tag:RKwFFHgv+tnIlKRTyV68Ww==,type:str]", + "sops": { + "kms": null, + "gcp_kms": null, + "azure_kv": null, + "hc_vault": null, + "age": [ + { + "recipient": "age1ljpdczmg5ctqyeezn739hv589fwhssjjnuqf7276fqun6kc62v3qmhkd0c", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjRlRzcldvSVkxV3VSeDBF\nRG9KK2NzYmtPWXE3a1JPN3NMRlJtbnJML0hzCmNTT0JFMTR6SDl0WVNBNk50VmdZ\nYi9pQU9FQW9qQ3NZdTM5T3FDcjNUQXMKLS0tIEpBcFhtbFMrbWlRYVdPSXpYM0xp\neW5MZ3dOYmphYXk2Tjh2Rk8xOGRkSGMKOLVuj75jqZeZ0SS1iHDRLONLbJ/UQXfO\nEN1ZhYXq7u5s+wKidmGoFVHWFAxM0O3kXaAQAHws4ttP0v6YqeSuBg==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2024-11-14T18:24:30Z", + "mac": "ENC[AES256_GCM,data:1sVhca19IbHJUv+qfkn+cJXjYIaXLX12S9N3QvDUoeUSTT4m2GxArKjvKJSpmc3KZCbOwpF1TObHjDs88pqsCxkzl7J9TSu4EgESRfSUy0lRhIveN/38wvEGI/0yNZXwFisB0nNpPbwAUp4JUZnfcqihlDINbVCw7mzVShHlnvU=,iv:J7/9uhlisRJUkqEFeO9aBRX5rgv0392DCuF5Yu1a5gI=,tag:sd0eoBkxns2pitnMZWvPzQ==,type:str]", + "pgp": [ + { + "created_at": "2024-11-14T18:24:30Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\nhQGMA5HdvEwzh/H7AQv/cFzlEwHCdmrKutzeJHOVANtF93aunkV89avpcjNtxjKJ\nzWeDrxZPIhApRsS0Q5kvuYbplwJbDPDbQlDeRsAzZbGVzXisDEYVSLbSEDX253fV\npDoD3MdBU/syMus0x7gSulT288Ije5lY76kBoqrzzsDG1RbHHeQMBP4hrLrFdQhh\nlCtjXJHMPlxR+bsTLhmKFUl6UWA22QeevhIU2VSTU7ROgcE6qRAknJLVVhTBhHmB\nkm2JpTQuM6Vhq+zIYDgLegV2fOiOW9O6ONsUt5N/jQYFSj2T4WL5Wnix/bxVg6vL\nkAto2cO1GsRBRH994AUWq4h5dwYWUCafYkXMILCQmMy81YftAPiAoCTUBsc6DSJ1\nV+gr4G3wLetwY2DdM8HN2Cru49PI923aOtKztjX8+r/w22RZl99INY5F/RP6NAYA\nLCEdw9LlW6Ctct1B6JU+JlCdJ/FW2Q9RMazw9wF4ZCg0AfqC/tLW9ETZF0cRYmA5\nH9LJJIxjNyizlGoJ7p780lgBDNBJD2v3ST5ESJ9TctQLS2XBWHtgskW1rPaCxrVX\nrgE/0PUnqxofOLofu4ktKOxtutYOqyVeP6Tvr0TLLEjwikgZ92LxqMx5dW6h46rL\nUjGuKKBz7FyA\n=UMiq\n-----END PGP MESSAGE-----", + "fp": "c4639370c41133a738f643a591ddbc4c3387f1fb" + }, + { + "created_at": "2024-11-14T18:24:30Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\nhQIMA98TrrsQEbXUAQ//eu/1ioAXlJ2Po2xQ8xQ4HYTQqsMF0lWijN3kv4cH1w+W\nvnQRjB86do4L4DnZz66DDbjHClauLNeuDkYL1ejvetMtENp7KKT8LRJCH4X53eko\ngw6xAYXA8X6e3shM6eFOYIW97pbGQSlfzuh7IbAPZjEuV2ov67sxmWkd9ZJPxw+E\nn2dd/mxw96NM76o8WclwL/W1qrrIqydCJiBtqL09I2z9j9bJ4AxMWTB0kjpJZK4U\nNo5Hk6OwL3C6a3q0xfO0DIb3fy5O7VSwl7AuiRjGxclqy4mH+L9DBS0ONMjguTlo\n+yGZoJi7vaWNvVVW0U9RBEJyCjX6iYje5/gWlWXaMlyIubuOGFy5iXQOSMXk6589\niNcz+ouGAvK6Jy19zo+SQtvmUki+SSRGEzUbx85R13Hz3E5TTlq7wONsgZE4EqqM\ny/6OMCGOvHOzJyWMdKCJ+7DzWzKyQNGWco57hczX74iPGhlH7XfNa3Q0292qziT5\nVFnONWGgN7PLa6rJXOAsxPNlgH5Qbdi2XBgBso8rlAYUXTmKtK/5cDN3rDtRbzgX\nVDu64snQJvGOEKwgv/UXybMRe8OocuCW6zFQDjJMaRtEsg2LP2FjVaYzLhDyuDJ6\ntAIoxWMaMSxgGJkd/E45dOQq/oWBVTFKD8ECGORNOy4RCUMs2LHDbhesvo/PzvbS\nWAFsQZCvjXPe+YZmIuMt7MfgX8d/NhGTtGOaNfX3D+orBuhzWmIAAvlAwMrxorFb\ngayWLO7mYRDUw45uudFzJYql+QLGuvcrFP5BYjY5wk17u6cYYQzlNxs=\n=Qlcg\n-----END PGP MESSAGE-----", + "fp": "aef8d6c7e4761fc297cda833df13aebb1011b5d4" + } + ], + "unencrypted_suffix": "_unencrypted", + "version": "3.8.1" + } +} \ No newline at end of file diff --git a/machines/vpn/configuration.nix b/machines/vpn/configuration.nix new file mode 100644 index 0000000..f6ff2c5 --- /dev/null +++ b/machines/vpn/configuration.nix @@ -0,0 +1,28 @@ +{ config, lib, pkgs, inputs, ... }: + +with lib; + +{ + #sops.defaultSopsFile = ./secrets.yaml; + + networking = { + hostName = mkDefault "vpn"; + useDHCP = false; + nameservers = [ "1.1.1.1" ]; + }; + + imports = [ + ../modules/malobeo_user.nix + ../modules/sshd.nix + ../modules/minimal_tools.nix + ]; + + services.malobeo.vpn = { + enable = true; + name = "vpn"; + privateKey = "somepath"; + }; + + system.stateVersion = "22.11"; # Did you read the comment? +} + diff --git a/machines/vpn/wireguard.nix b/machines/vpn/wireguard.nix new file mode 100644 index 0000000..087ac79 --- /dev/null +++ b/machines/vpn/wireguard.nix @@ -0,0 +1,73 @@ +{config, pkgs, ...}: +{ + sops.secrets.wireguard_private = {}; + + # enable NAT + networking.nat.enable = true; + networking.nat.externalInterface = "eth0"; + networking.nat.internalInterfaces = [ "wg0" ]; + networking.firewall = { + allowedUDPPorts = [ 51820 ]; + }; + + + networking.wireguard.enable = true; + networking.wireguard.interfaces = { + # "wg0" is the network interface name. You can name the interface arbitrarily. + wg0 = { + # Determines the IP address and subnet of the server's end of the tunnel interface. + ips = [ "10.100.0.1/24" ]; + + # The port that WireGuard listens to. Must be accessible by the client. + listenPort = 51820; + + # This allows the wireguard server to route your traffic to the internet and hence be like a VPN + # For this to work you have to set the dnsserver IP of your router (or dnsserver of choice) in your clients + postSetup = '' + ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.100.0.0/24 -o eth0 -j MASQUERADE + ''; + + # This undoes the above command + postShutdown = '' + ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.100.0.0/24 -o eth0 -j MASQUERADE + ''; + + # Path to the private key file. + # + # Note: The private key can also be included inline via the privateKey option, + # but this makes the private key world-readable; thus, using privateKeyFile is + # recommended. + privateKey = config.sops.secrets.wireguard_private.path; + + peers = [ + # List of allowed peers. + { # Feel free to give a meaningfull name + # Public key of the peer (not a file path). + publicKey = "SfokXbgmvSmodgPFoVHjwmHE3nriQ3OTQ+hISU/3eW4="; + + # List of IPs assigned to this peer within the tunnel subnet. Used to configure routing. + allowedIPs = [ "10.100.0.2/32" ]; + + } + ]; + }; + }; + + #sops.secrets.wireguard_host = {}; + #sops.secrets.mullvad_secret = {}; + + #networking.wg-quick.interfaces = { + # wg0 = { + # address = [ "50.100.0.2/24" ]; + # privateKeyFile = "/home/kalipso/.config/wireguard-keys/private"; + + # peers = [ + # { + # publicKey = "Anme1N482rGSZ14wqtZQbzUHvX4oFhoVct0d187H0iM="; + # allowedIPs = [ "50.100.0.0/24" ]; + # endpoint = "5.9.153.217:51820"; + # } + # ]; + # }; + +} diff --git a/outputs.nix b/outputs.nix index 630be36..36bceff 100644 --- a/outputs.nix +++ b/outputs.nix @@ -115,6 +115,7 @@ in (utils.lib.eachSystem (builtins.filter filter_system utils.lib.defaultSystems nixosModules.malobeo.imports = [ ./machines/durruti/host_config.nix ./machines/modules/malobeo/microvm_host.nix + ./machines/modules/malobeo/wireguard.nix ]; hydraJobs = nixpkgs.lib.mapAttrs (_: nixpkgs.lib.hydraJob) (