diff --git a/.builds/apu.yml b/.builds/apu.yml new file mode 100644 index 0000000..4082ef4 --- /dev/null +++ b/.builds/apu.yml @@ -0,0 +1,7 @@ +image: nixos/unstable +environment: + NIX_CONFIG: "experimental-features = nix-command flakes" +tasks: + - build: | + cd NixOS + nix build '.#nixosConfigurations.apu.config.system.build.toplevel' diff --git a/apu/configuration.nix b/apu/configuration.nix new file mode 100644 index 0000000..cd4bbfb --- /dev/null +++ b/apu/configuration.nix @@ -0,0 +1,295 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is availanodev"; +# https://search.nixos.org/options and in the NixOS manual (`nixos-help`). + +{ lib, pkgs, ... }: +let + services = { + ssh = { tcp = [ 22 ]; }; + dhcp = { udp = [ 67 68 ]; }; + dns = { udp = [ 53 ]; }; + web = { tcp = [ 80 443 ]; }; + }; + + vlans = { + koti = { + id = 10; + ipv6 = true; + availableServices = with services; [ dhcp dns ssh ]; + }; + gast = { + id = 20; + ipv6 = true; + staticLeases = [ + { macAddress = "dc:a6:32:05:08:5d"; address = "10.20.1.235"; } + ]; + availableServices = with services; [ dhcp dns ]; + }; + iot = { + id = 30; + availableServices = with services; [ dhcp dns ]; + }; + cfg = { + id = 40; + staticLeases = [ + { macAddress = "8c:3b:ad:c5:b8:ee"; address = "10.40.0.10"; } + ]; + availableServices = with services; [ dhcp dns ]; + }; + }; + + pvid = vlans.cfg.id; + vlanIds = lib.map ({ id, ... }: id) (lib.attrValues vlans); + vlanRange = { + min = lib.foldr lib.min 999 vlanIds; + max = lib.foldr lib.max 0 vlanIds; + }; + + buildVlanNetdev = name: { id, ... }: { + name = "20-${name}"; + value = { + netdevConfig = { + Name = name; + Kind = "vlan"; + MACAddress = "00:0d:b9:49:d2:${toString id}"; + }; + vlanConfig = { Id = id; }; + }; + }; + + buildStaticLease = { macAddress, address }: '' + [DHCPServerStaticLease] + MACAddress=${macAddress} + Address=${address} + ''; + + buildBridgeVLANConfig = id: { bridgeVLANConfig.VLAN = id; }; + + buildVlanNetwork = name: { id, ipv6 ? false, staticLeases ? [ ], ... }: { + name = "30-${name}"; + value = { + matchConfig = { + Name = name; + }; + networkConfig = { + Address = "10.${toString id}.0.1/23"; + IPMasquerade = "ipv4"; + DHCPServer = true; + IPv6AcceptRA = false; + IPv6SendRA = ipv6; + DHCPPrefixDelegation = ipv6; + }; + dhcpServerConfig = { + PoolOffset = 255; + DNS = "10.${toString id}.0.1"; + }; + extraConfig = lib.concatLines (lib.map buildStaticLease staticLeases); + }; + }; + + buildFirewallRules = name: { availableServices ? [ ], ... }: { + allowedUDPPorts = lib.flatten (lib.map ({ udp ? [ ], ... }: udp) availableServices); + allowedTCPPorts = lib.flatten (lib.map ({ tcp ? [ ], ... }: tcp) availableServices); + }; + + vlanNetdevs = lib.mapAttrs' buildVlanNetdev vlans; + vlanNetworks = lib.mapAttrs' buildVlanNetwork vlans; + firewallRules = lib.mapAttrs buildFirewallRules ( + vlans // { "tailscale*".availableServices = with services; [ ssh web ]; } + ); + + nginxVhost = options: { + http2 = true; + forceSSL = true; + enableACME = true; + acmeRoot = null; + } // options; + + nginxProxy = options: { + proxyWebsockets = true; + } // options; +in +{ + boot.loader.grub.enable = true; + boot.loader.grub.device = "/dev/sda"; + + networking.hostName = "apu"; + networking.useNetworkd = true; + + systemd.network = { + enable = true; + links = { + "10-extern0" = { + matchConfig.Path = "pci-0000:01:00.0"; + linkConfig.Name = "extern0"; + }; + "10-intern0" = { + matchConfig.Path = "pci-0000:02:00.0"; + linkConfig.Name = "intern0"; + }; + "10-intern1" = { + matchConfig.Path = "pci-0000:03:00.0"; + linkConfig.Name = "intern1"; + }; + }; + netdevs = { + "20-lan" = { + netdevConfig = { + Name = "lan"; + Kind = "bridge"; + }; + bridgeConfig = { + VLANFiltering = true; + DefaultPVID = pvid; + }; + }; + } // vlanNetdevs; + networks = { + "30-bind-lan" = { + matchConfig = { + Name = "intern*"; + }; + networkConfig = { + Bridge = "lan"; + }; + extraConfig = '' + [BridgeVLAN] + VLAN=${toString vlanRange.min}-${toString vlanRange.max} + EgressUntagged=${toString pvid} + PVID=${toString pvid} + ''; + }; + "30-lan" = { + matchConfig = { + Name = "lan"; + }; + networkConfig = { + IPv6AcceptRA = false; + ConfigureWithoutCarrier = true; + }; + vlan = lib.attrNames vlans; + bridgeVLANs = lib.map buildBridgeVLANConfig vlanIds; + }; + "30-wan" = { + matchConfig = { + Name = "extern0"; + }; + networkConfig = { + DHCP = true; + DNS = "127.0.0.1"; + IPv6AcceptRA = true; + IPForward = true; + }; + dhcpV6Config = { + PrefixDelegationHint = "::/56"; + }; + dhcpV4Config = { + Use6RD = true; + }; + }; + } // vlanNetworks; + }; + + time.timeZone = "Europe/Helsinki"; + + users.users.jokke = { + isNormalUser = true; + extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. + openssh.authorizedKeys.keys = [ + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLIUkESu5NnBi1M0+ZjYrkp6/rIFuwc3aguspf98jmOydNce6l65cnS3GRzc9oWx4lu11ahi87ZuE+pYV+gaHm4=" + ]; + initialPassword = "change-me"; + }; + + environment.systemPackages = with pkgs; [ + wget + curl + dig + neovim + vim + htop + ]; + + services.openssh = { + enable = true; + openFirewall = false; + settings.PasswordAuthentication = false; + }; + + services.tailscale = { + enable = true; + useRoutingFeatures = "both"; + }; + + services.resolved.enable = false; + + services.nextdns = { + enable = true; + arguments = [ "-profile" "9c4ac9" "-setup-router" "-mdns" "koti" ]; + }; + + services.home-assistant = { + enable = true; + extraComponents = [ + # Components required to complete the onboarding + "esphome" + "met" + "radio_browser" + + "yeelight" + "xiaomi_aqara" + "shelly" + ]; + extraPackages = python3Packages: with python3Packages; [ + gtts + numpy + ]; + config = { + homeassistant = { + name = "Koti"; + unit_system = "metric"; + time_zone = "Europe/Helsinki"; + }; + http = { + use_x_forwarded_for = true; + trusted_proxies = "127.0.0.1"; + }; + default_config = { }; + }; + }; + + services.nginx.virtualHosts."koti.repomaa.com" = nginxVhost { + extraConfig = '' + proxy_buffering off; + ''; + locations."/" = nginxProxy { + proxyPass = "http://127.0.0.1:8123"; + }; + }; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + recommendedBrotliSettings = true; + recommendedGzipSettings = true; + recommendedZstdSettings = true; + recommendedOptimisation = true; + }; + + security.acme = { + acceptTerms = true; + defaults = { + dnsProvider = "hetzner"; + environmentFile = "/var/secrets/lego"; + email = "admin@j.repomaa.com"; + }; + }; + + networking.nftables.enable = true; + networking.firewall.enable = true; + networking.useDHCP = false; + networking.firewall.interfaces = firewallRules; + + system.stateVersion = "24.05"; +} diff --git a/apu/default.nix b/apu/default.nix new file mode 100644 index 0000000..9e36e42 --- /dev/null +++ b/apu/default.nix @@ -0,0 +1,11 @@ +{ inputs, ... }: +let + inherit (inputs) nixos-hardware; +in +{ + imports = [ + ./hardware-configuration.nix + ./configuration.nix + nixos-hardware.nixosModules.pcengines-apu + ]; +} diff --git a/apu/hardware-configuration.nix b/apu/hardware-configuration.nix new file mode 100644 index 0000000..e5daa45 --- /dev/null +++ b/apu/hardware-configuration.nix @@ -0,0 +1,53 @@ +# 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 = [ "xhci_pci" "ahci" "ehci_pci" "usb_storage" "sd_mod" "sdhci_pci" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/f221c6a7-e05e-40dc-bc85-7970d7c8f22b"; + fsType = "btrfs"; + options = [ "subvol=@" ]; + }; + + fileSystems."/var/log" = + { device = "/dev/disk/by-uuid/f221c6a7-e05e-40dc-bc85-7970d7c8f22b"; + fsType = "btrfs"; + options = [ "subvol=@var_log" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/14D2-F8F4"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + fileSystems."/swap" = + { device = "/dev/disk/by-uuid/f221c6a7-e05e-40dc-bc85-7970d7c8f22b"; + fsType = "btrfs"; + options = [ "subvol=@swap" ]; + }; + + 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.enp1s0.useDHCP = lib.mkDefault true; + # networking.interfaces.enp2s0.useDHCP = lib.mkDefault true; + # networking.interfaces.enp3s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/flake.nix b/flake.nix index 26763dc..4f3b354 100644 --- a/flake.nix +++ b/flake.nix @@ -57,6 +57,12 @@ specialArgs = { inherit inputs; }; modules = [ ./radish ]; }; + + apu = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = { inherit inputs; }; + modules = [ ./apu ]; + }; }; }; }