diff --git a/flake.nix b/flake.nix index abd9da6..0981a0c 100644 --- a/flake.nix +++ b/flake.nix @@ -212,6 +212,18 @@ }; }; + massicot = + { ... }: + { + deployment.targetHost = "49.13.13.122"; + deployment.buildOnTarget = true; + + imports = [ + { nixpkgs.system = "aarch64-linux"; } + machines/massicot + ] ++ sharedColmenaModules; + }; + la-00 = { ... }: { diff --git a/machines/massicot/default.nix b/machines/massicot/default.nix new file mode 100644 index 0000000..7b56e15 --- /dev/null +++ b/machines/massicot/default.nix @@ -0,0 +1,74 @@ +{ + pkgs, + ... +}: + +{ + imports = [ + ./hardware-configuration.nix + ./networking.nix + ./services.nix + ./services + ]; + + sops = { + defaultSopsFile = ./secrets.yaml; + age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + secrets = { + gts_env = { + owner = "gotosocial"; + }; + }; + }; + + boot.loader.efi.canTouchEfiVariables = true; + boot.loader.efi.efiSysMountPoint = "/boot"; + boot.loader.grub = { + enable = true; + efiSupport = true; + configurationLimit = 5; + }; + environment.systemPackages = with pkgs; [ + cifs-utils + git + ]; + + # Disable docs on servers + documentation.nixos.enable = false; + documentation.man.enable = false; + + system.stateVersion = "22.11"; + + networking = { + hostName = "massicot"; + }; + + services.tailscale.enable = true; + + commonSettings = { + auth.enable = true; + nix = { + enable = true; + }; + }; + + security.sudo = { + execWheelOnly = true; + wheelNeedsPassword = false; + }; + + services.openssh = { + enable = true; + settings = { + PasswordAuthentication = false; + KbdInteractiveAuthentication = false; + PermitRootLogin = "no"; + GSSAPIAuthentication = "no"; + KerberosAuthentication = "no"; + }; + }; + services.fail2ban.enable = true; + programs.mosh.enable = true; + + systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ]; +} diff --git a/machines/massicot/hardware-configuration.nix b/machines/massicot/hardware-configuration.nix new file mode 100644 index 0000000..36e673c --- /dev/null +++ b/machines/massicot/hardware-configuration.nix @@ -0,0 +1,32 @@ +{ modulesPath, ... }: +{ + imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; + boot.loader.grub = { + efiSupport = true; + device = "nodev"; + }; + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/AC27-D9D6"; + fsType = "vfat"; + }; + boot.initrd.availableKernelModules = [ + "ata_piix" + "uhci_hcd" + "xen_blkfront" + ]; + boot.initrd.kernelModules = [ "nvme" ]; + fileSystems."/" = { + device = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_35068215-part1"; + fsType = "ext4"; + }; + + fileSystems."/mnt/storage" = { + device = "/dev/disk/by-id/scsi-0HC_Volume_101302395"; + fsType = "btrfs"; + options = [ + "subvol=storage" + "compress=zstd" + "noatime" + ]; + }; +} diff --git a/machines/massicot/kanidm-provision.nix b/machines/massicot/kanidm-provision.nix new file mode 100644 index 0000000..e44c729 --- /dev/null +++ b/machines/massicot/kanidm-provision.nix @@ -0,0 +1,239 @@ +{ pkgs, config, ... }: +let + inherit (config.my-lib.settings) + gotosocialUrl + minifluxUrl + hedgedocDomain + forgejoDomain + grafanaUrl + synapseDelegateUrl + ; +in +{ + services.kanidm.provision = { + enable = true; + autoRemove = true; + groups = { + forgejo-access = { + members = [ "xin" ]; + }; + forgejo-admin = { + members = [ "xin" ]; + }; + gts-users = { + members = [ "xin" ]; + }; + ocis-users = { + members = [ "xin" ]; + }; + linux_users = { + members = [ "xin" ]; + }; + hedgedoc-users = { + members = [ "xin" ]; + }; + immich-users = { + members = [ + "xin" + "zhuo" + "ycm" + "yzl" + ]; + }; + grafana-superadmins = { + members = [ "xin" ]; + }; + grafana-admins = { + members = [ "xin" ]; + }; + grafana-editors = { + members = [ "xin" ]; + }; + grafana-users = { + members = [ "xin" ]; + }; + miniflux-users = { + members = [ "xin" ]; + }; + synapse-users = { + members = [ "xin" ]; + }; + idm_people_self_mail_write = { + members = [ ]; + }; + }; + persons = { + xin = { + displayName = "Xinyang Li"; + mailAddresses = [ "lixinyang411@gmail.com" ]; + }; + + zhuo = { + displayName = "Zhuo"; + mailAddresses = [ "13681104320@163.com" ]; + }; + + ycm = { + displayName = "Chunming"; + mailAddresses = [ "chunmingyou@gmail.com" ]; + }; + + yzl = { + displayName = "Zhengli Yang"; + mailAddresses = [ "13391935399@189.cn" ]; + }; + }; + systems.oauth2 = { + forgejo = { + displayName = "ForgeJo"; + originUrl = "https://${forgejoDomain}/user/oauth2/kanidm/callback"; + originLanding = "https://${forgejoDomain}/user/oauth2/kanidm"; + allowInsecureClientDisablePkce = true; + scopeMaps = { + forgejo-access = [ + "openid" + "email" + "profile" + "groups" + ]; + }; + claimMaps = { + forgejo_role = { + joinType = "array"; + valuesByGroup = { + forgejo-access = [ "Access" ]; + forgejo-admin = [ "Admin" ]; + }; + }; + }; + }; + gts = { + displayName = "GoToSocial"; + originUrl = "https://xinyang.life/auth/callback"; + originLanding = "https://xinyang.life/auth/callback"; + allowInsecureClientDisablePkce = true; + scopeMaps = { + gts-users = [ + "openid" + "email" + "profile" + "groups" + ]; + }; + }; + gotosocial = { + displayName = "GoToSocial"; + originUrl = "${gotosocialUrl}/auth/callback"; + originLanding = "${gotosocialUrl}/auth/callback"; + allowInsecureClientDisablePkce = true; + scopeMaps = { + gts-users = [ + "openid" + "email" + "profile" + "groups" + ]; + }; + }; + # It's used for all the clients. I'm too lazy to change the name. + owncloud-android = { + displayName = "ownCloud Apps"; + originLanding = "https://drive.xinyang.life:8443/"; + originUrl = [ + "http://localhost:38622/" + "http://localhost:43580/" + "https://drive.xinyang.life:8443/" + # TODO: Should allow mobile redirect url not ending with / + # "oc://android.owncloud.com" + ]; + public = true; + preferShortUsername = true; + scopeMaps = { + ocis-users = [ + "openid" + "email" + "profile" + "offline_access" + ]; + }; + }; + + hedgedoc = { + displayName = "HedgeDoc"; + originUrl = "https://${hedgedocDomain}/auth/oauth2/callback"; + originLanding = "https://${hedgedocDomain}/auth/oauth2"; + allowInsecureClientDisablePkce = true; + scopeMaps = { + hedgedoc-users = [ + "openid" + "email" + "profile" + ]; + }; + }; + immich = { + displayName = "Immich"; + originUrl = [ + "https://immich.xinyang.life:8000/api/oauth/mobile-redirect/" + "https://immich.xinyang.life:8000/auth/login" + "https://immich.xinyang.life:8000/user-settings" + ]; + originLanding = "https://immich.xinyang.life:8000/auth/login?autoLaunch=0"; + allowInsecureClientDisablePkce = true; + scopeMaps = { + immich-users = [ + "openid" + "email" + "profile" + ]; + }; + }; + miniflux = { + displayName = "Miniflux"; + originUrl = "${minifluxUrl}/oauth2/oidc/callback"; + originLanding = "${minifluxUrl}/oauth2/oidc/redirect"; + scopeMaps = { + miniflux-users = [ + "openid" + "email" + "profile" + ]; + }; + }; + grafana = { + displayName = "Grafana"; + originUrl = "${grafanaUrl}/login/generic_oauth"; + originLanding = "${grafanaUrl}/"; + scopeMaps = { + grafana-users = [ + "openid" + "email" + "profile" + "groups" + ]; + }; + claimMaps = { + grafana_role = { + joinType = "array"; + valuesByGroup = { + grafana-superadmins = [ "GrafanaAdmin" ]; + grafana-admins = [ "Admin" ]; + grafana-editors = [ "Editor" ]; + }; + }; + }; + }; + synapse = { + displayName = "Synapse"; + originUrl = "${synapseDelegateUrl}/_synapse/client/oidc/callback"; + originLanding = "${synapseDelegateUrl}/"; + scopeMaps = { + synapse-users = [ + "openid" + "profile" + ]; + }; + }; + }; + }; +} diff --git a/machines/massicot/networking.nix b/machines/massicot/networking.nix new file mode 100644 index 0000000..2a4c529 --- /dev/null +++ b/machines/massicot/networking.nix @@ -0,0 +1,14 @@ +{ pkgs, ... }: +{ + networking.useNetworkd = true; + systemd.network.networks."10-wan" = { + matchConfig.MACAddress = "96:00:02:68:7d:2d"; + networkConfig = { + DHCP = "ipv4"; + Gateway = "fe80::1"; + }; + address = [ + "2a01:4f8:c17:345f::3/64" + ]; + }; +} diff --git a/machines/massicot/secrets.yaml b/machines/massicot/secrets.yaml new file mode 100644 index 0000000..9393192 --- /dev/null +++ b/machines/massicot/secrets.yaml @@ -0,0 +1,40 @@ +storage_box_mount: ENC[AES256_GCM,data:9lOAL3tkfB0pN4/cuM4SX0xoMrW0UUEzTN8spw3MQ3BWrfsRc3Stsce3puXz1sRf,iv:7Q9wzpBgQ3tqcfy0n/c6Ya84Kg60nhR/e2H0pVntWsY=,tag:9a0xvNBGQpCvhxgmV3hrww==,type:str] +gts_env: ENC[AES256_GCM,data:StggMdJPevrDbrVDrBDETdQYnSOaTESkgSqpGKrSHXhS21nyCE5ya7/X4l0GVTXoGCyfWG7vK+PDW22mJxpYcj2CBaVUYDu/,iv:2fqWDaWAWxTXdG7w5HU6jBcappFEByNtYs0Jd6PaYnA=,tag:KGhrMemao6g4FkEAZmmacg==,type:str] +hedgedoc_env: ENC[AES256_GCM,data:+rjEctM6IJUpn7WcAnBS9TkQi2lCq4wKPxbaOApffH0tFyu56SpECrLpmM749I7th3N+UGb0pLM7+Ywr7fbuuMfUuIWom6Y+CKYw4yMlgjzTaaNqBmstvMxLaPnmA01G9ie1rQ==,iv:YBIyQQ6xiUyxSnR5epE5hV9OqETLKC5CFTEaRJdErGU=,tag:77kHYQ2i2APVyadhMhmvWA==,type:str] +grafana_oauth_secret: ENC[AES256_GCM,data:43+EBnN912eK/08MdJokWPxi2Lxn/D4hSHPhNmHOk9awWQ7ut/el0vaAa+Epqnui3le2p4VuotQT6XlIuDLrixIomrc6Qw5HERAEdZmbrGvDlrrNhw==,iv:Pfn8rL0LtG3hym9EdSZRjaPLMlWlut/nt2FEtRWnULo=,tag:moDWqF3aBbnO4aG0Cysfcw==,type:str] +miniflux: + oauth2_secret: ENC[AES256_GCM,data:jcZR9E9jXNKfkAoGgBI19qQeaz26R6qiAWjP4XrftHSCQV974tjJl+fiU8Xgi0bViA==,iv:/aY0bL/oAAHBhohy3FHB/UEDYryw7A7JOKvEbLtDHJg=,tag:Fn/6NurNkRphXySR+y9S9Q==,type:str] +forgejo: + env: ENC[AES256_GCM,data:TMeguXfanISeyvsay9SBqm3SSGKpp5nCkqhHblf0QHNzHWGQKwpORmWfOtVfgOh9qdDqq8wYBpXznmbvixjV,iv:IR/rMoAIvZCw9FURmau4+g8c3pvI9BRs7v1NJ5ia4jI=,tag:kjwf6RN5HN8I2sUhDcr4UQ==,type:str] +restic: + repo_url: ENC[AES256_GCM,data:GMHbrjgwajnYSiqtoYaKiFT/aDWDwlzEkvMLPzYf7C9PvLr7T4zeWyAA9//8huldyxO3+nk6O9lR9ORZKZfb8/MYB7nRB03sZQ==,iv:6uBhsksOGDjoc13U2xWLz7I+0fzGRhnw0nStACqlnug=,tag:uhH28NYq+ly1bmCV/cpxkQ==,type:str] + repo_password: ENC[AES256_GCM,data:jRHNgOk5ChWdqMKsd/V4Xg==,iv:wrgF5pau/RylG1nmJYmvrZ02o67qkkT5PrZAQlXb6Qo=,tag:X0WVpMqi8xeoATss/sSPMA==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1uw059wcwfvd9xuj0hpqzqpeg7qemecspjrsatg37wc7rs2pumfdsgken0c + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1aGRvUUtjcDU2bnhaNDJD + K3c5TnFJeHQzM2VpeHphR2dGeS9NYzcyYjJnCnNrQ3dxL1hqR2MyQXhldUZ1VEJp + N25nVHZ1QjRydW9hTWE5d0x2M2pPNkkKLS0tIFpiRW8rZ1Q1R1RCZGN1ZGs3ek45 + UENaRjJPWFJqUlpzd3dHSC9pdnZ6STQKQaaY28FYUk3O9TTkX9LQTzlrqZVojgxY + M+N6LApfdoioQCmXduDbj18i0eUbECTBXR/uEFEIHbn6AJVD/vx7iw== + -----END AGE ENCRYPTED FILE----- + - recipient: age1jle2auermhswqtehww9gqada8car5aczrx43ztzqf9wtcld0sfmqzaecta + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRY0lIeE9tWDA3Q21IWk1E + YnlaQUJybFB2bmFpbG1UZ0UyNG16WkRkZlNVCmUySHVBcXpWekpVN3R5dGs5ODY1 + V1ZlUk4zRSs1NkVjY3JSMVVQSXJ1OEkKLS0tIFMzeUNaYVpoNnV3TE1oamEwTEo2 + dnFBa0lDWWZtS1BHdzBoVzNTaGNkSEEKi/W1n7RT8NpTp00SBMwxsUJAPDhumJ/i + V2VnaSNwouD3SswTcoBzqQpBP9XrqzjIYGke90ZODFQbMY9WDQ+O0g== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-11-28T03:57:35Z" + mac: ENC[AES256_GCM,data:xjZrlwfWLtZNYfH+KiE2ICt9Jo4nx/LKaEYi/ECN/Od+ZTjety0V6RJ/RfmI6q3K1WMj0sAGc56hCZ0iOn25L8wK6dc14hZVoSwwbIiQ7hTQE5LcK+NbXNmy3r/YC855DHG9kE08eYGHdNcBbckZg3HhkHQ9UYS/Ox/QFFuBa5Q=,iv:N3AW+sr9ET3c/ArXr176haRewYFsfgsNn+hkC0MDJwA=,tag:SCikn+F8btuSBswV+oCdXg==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.9.1 diff --git a/machines/massicot/services.nix b/machines/massicot/services.nix new file mode 100644 index 0000000..a4f0d72 --- /dev/null +++ b/machines/massicot/services.nix @@ -0,0 +1,102 @@ +{ + config, + pkgs, + lib, + ... +}: +let + kanidm_listen_port = 5324; +in +{ + imports = [ + ./kanidm-provision.nix + ]; + networking.firewall.allowedTCPPorts = [ + 80 + 443 + 2222 + 8448 + ]; + networking.firewall.allowedUDPPorts = [ + 80 + 443 + 8448 + ]; + + custom.monitoring = { + promtail.enable = true; + }; + + custom.prometheus.exporters = { + enable = true; + blackbox = { + enable = true; + }; + node = { + enable = true; + }; + }; + + security.acme = { + acceptTerms = true; + certs."auth.xinyang.life" = { + email = "lixinyang411@gmail.com"; + listenHTTP = "127.0.0.1:1360"; + group = "kanidm"; + }; + }; + + services.kanidm = { + package = pkgs.kanidm.withSecretProvisioning; + enableServer = true; + serverSettings = { + domain = "auth.xinyang.life"; + origin = "https://auth.xinyang.life"; + bindaddress = "[::]:${toString kanidm_listen_port}"; + tls_key = ''${config.security.acme.certs."auth.xinyang.life".directory}/key.pem''; + tls_chain = ''${config.security.acme.certs."auth.xinyang.life".directory}/fullchain.pem''; + online_backup.versions = 7; + # db_path = "/var/lib/kanidm/kanidm.db"; + }; + }; + + users.users.conduit = { + isSystemUser = true; + group = "conduit"; + }; + users.groups.conduit = { }; + + services.gotosocial = { + enable = true; + settings = { + log-level = "debug"; + host = "xinyang.life"; + letsencrypt-enabled = false; + bind-address = "localhost"; + instance-expose-public-timeline = true; + oidc-enabled = true; + oidc-idp-name = "Kanidm"; + oidc-issuer = "https://auth.xinyang.life/oauth2/openid/gts"; + oidc-client-id = "gts"; + oidc-link-existing = true; + storage-local-base-path = "/mnt/storage/gotosocial/storage"; + }; + environmentFile = config.sops.secrets.gts_env.path; + }; + + services.caddy = { + enable = true; + virtualHosts."http://auth.xinyang.life:80".extraConfig = '' + reverse_proxy ${config.security.acme.certs."auth.xinyang.life".listenHTTP} + ''; + virtualHosts."https://auth.xinyang.life".extraConfig = '' + reverse_proxy https://127.0.0.1:${toString kanidm_listen_port} { + header_up Host {upstream_hostport} + header_down Access-Control-Allow-Origin "*" + transport http { + tls_server_name ${config.services.kanidm.serverSettings.domain} + } + } + ''; + }; +} diff --git a/machines/massicot/services/default.nix b/machines/massicot/services/default.nix new file mode 100644 index 0000000..fdf054b --- /dev/null +++ b/machines/massicot/services/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./restic.nix + ]; +} diff --git a/machines/massicot/services/restic.nix b/machines/massicot/services/restic.nix new file mode 100644 index 0000000..e8d2501 --- /dev/null +++ b/machines/massicot/services/restic.nix @@ -0,0 +1,42 @@ +{ + config, + lib, + pkgs, + ... +}: +let + sqliteBackup = fromPath: toPath: file: '' + mkdir -p ${toPath} + ${lib.getExe pkgs.sqlite} ${fromPath} ".backup '${toPath}/${file}'" + ''; +in +{ + sops.secrets = { + "restic/repo_url" = { + sopsFile = ../secrets.yaml; + }; + "restic/repo_password" = { + sopsFile = ../secrets.yaml; + }; + }; + + custom.restic = { + enable = true; + paths = [ + "/backup" + "/mnt/storage" + ]; + backupPrepareCommand = [ + (sqliteBackup "/var/lib/hedgedoc/db.sqlite" "/backup/hedgedoc" "db.sqlite") + (sqliteBackup "/var/lib/bitwarden_rs/db.sqlite3" "/backup/bitwarden_rs" "db.sqlite3") + (sqliteBackup "/var/lib/gotosocial/database.sqlite" "/backup/gotosocial" "database.sqlite") + (sqliteBackup "/var/lib/kanidm/kanidm.db" "/backup/kanidm" "kanidm.db") + ]; + }; + + services.restic.backups.${config.networking.hostName} = { + extraBackupArgs = [ + "--limit-upload=1024" + ]; + }; +} diff --git a/machines/thorite/monitoring.nix b/machines/thorite/monitoring.nix index 311ffed..8fe05e7 100644 --- a/machines/thorite/monitoring.nix +++ b/machines/thorite/monitoring.nix @@ -181,6 +181,7 @@ in ]) ++ (mkNodeScrapes [ { address = "thorite.coho-tet.ts.net"; } + { address = "massicot.coho-tet.ts.net"; } { address = "weilite.coho-tet.ts.net"; } { address = "biotite.coho-tet.ts.net"; } { address = "hk-00.coho-tet.ts.net"; } @@ -192,6 +193,10 @@ in hostAddress = "thorite.coho-tet.ts.net"; targetAddresses = probeList ++ [ "49.13.13.122:443" ]; } + { + hostAddress = "massicot.coho-tet.ts.net"; + targetAddresses = probeList ++ [ "45.142.178.32:443" ]; + } { hostAddress = "weilite.coho-tet.ts.net"; targetAddresses = [ diff --git a/machines/weilite/default.nix b/machines/weilite/default.nix index 8846704..a750205 100644 --- a/machines/weilite/default.nix +++ b/machines/weilite/default.nix @@ -20,19 +20,7 @@ }; config = { - networking = { - hostName = "weilite"; - useNetworkd = true; - }; - systemd.network = { - enable = true; - networks = { - "10-wan" = { - matchConfig.MACAddress = "52:54:00:db:23:d0"; - networkConfig.DHCP = "ipv4"; - }; - }; - }; + networking.hostName = "weilite"; commonSettings = { auth.enable = true; nix = { diff --git a/machines/weilite/services/restic.nix b/machines/weilite/services/restic.nix index 730266c..be272eb 100644 --- a/machines/weilite/services/restic.nix +++ b/machines/weilite/services/restic.nix @@ -34,6 +34,7 @@ in services.restic.backups = builtins.listToAttrs [ (mkPrune "xin" "calcite") + (mkPrune "xin" "massicot") (mkPrune "xin" "biotite") (mkPrune "xin" "thorite") ]; diff --git a/machines/weilite/services/transmission.nix b/machines/weilite/services/transmission.nix index 58b198c..0c1e969 100644 --- a/machines/weilite/services/transmission.nix +++ b/machines/weilite/services/transmission.nix @@ -86,7 +86,6 @@ in TRANSMISSION_USERNAME = "xin"; }; after = [ "tailscaled.service" ]; - wants = [ "tailscaled.service" ]; serviceConfig = { ExecStart = "${lib.getExe pkgs.transmission-exporter}"; EnvironmentFile = config.sops.templates."transmission-cred.env".path;