diff --git a/flake.lock b/flake.lock index 4719a70..7bd0259 100644 --- a/flake.lock +++ b/flake.lock @@ -39,7 +39,80 @@ "root": { "inputs": { "home-manager": "home-manager", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "sops-nix": "sops-nix", + "tide": "tide" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777944972, + "narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "c591bf665727040c6cc5cb409079acb22dcce33c", + "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" + } + }, + "tide": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "utils": "utils" + }, + "locked": { + "lastModified": 1778226636, + "narHash": "sha256-YaOeH7HHvThS+qB7AU35iJgE9PnYuooHIu0oEdh2md4=", + "path": "/home/yisroel/Projects/TIDE", + "type": "path" + }, + "original": { + "path": "/home/yisroel/Projects/TIDE", + "type": "path" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index f90fcd3..992c9f9 100644 --- a/flake.nix +++ b/flake.nix @@ -8,9 +8,19 @@ url = "github:nix-community/home-manager"; inputs.nixpkgs.follows = "nixpkgs"; }; + sops-nix = { + url = "github:Mic92/sops-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + tide = { + # Local path while iterating; switch to + # git+https://git.yisroelbaum.com/yisroelbaum/TIDE once pushed. + url = "path:/home/yisroel/Projects/TIDE"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, home-manager, ... }: + outputs = { self, nixpkgs, home-manager, sops-nix, tide, ... }: let system = "x86_64-linux"; domainName = "yisroelbaum.com"; @@ -18,7 +28,10 @@ nixosConfigurations.nixos = nixpkgs.lib.nixosSystem { modules = [ ./configuration.nix + ./tide.nix home-manager.nixosModules.home-manager + sops-nix.nixosModules.sops + tide.nixosModules.tide { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; @@ -32,6 +45,8 @@ in pkgs.mkShell { buildInputs = with pkgs; [ nixos-rebuild + sops + age ]; }; }; diff --git a/secrets/README.md b/secrets/README.md new file mode 100644 index 0000000..d923746 --- /dev/null +++ b/secrets/README.md @@ -0,0 +1,23 @@ +# Secrets + +Encrypted with [sops](https://github.com/getsops/sops) using the +host's age key. + +## First-time setup on the server + +1. Generate an age key for the host: + ``` + sudo mkdir -p /var/lib/sops-nix + sudo age-keygen -o /var/lib/sops-nix/key.txt + sudo chmod 600 /var/lib/sops-nix/key.txt + ``` +2. Read the public key: + ``` + sudo grep "public key" /var/lib/sops-nix/key.txt + ``` +3. On a workstation, put that public key into `.sops.yaml` at + the repo root and encrypt `tide.yaml.example` into + `tide.yaml`. + +`tide.yaml` is encrypted and committed. `tide.yaml.example` is +the plaintext template. diff --git a/secrets/tide.yaml b/secrets/tide.yaml new file mode 100644 index 0000000..fea1aa9 --- /dev/null +++ b/secrets/tide.yaml @@ -0,0 +1,3 @@ +# PLACEHOLDER - replace with sops-encrypted content before deploy. +# See secrets/README.md and secrets/tide.yaml.example. +tide-env: "" diff --git a/secrets/tide.yaml.example b/secrets/tide.yaml.example new file mode 100644 index 0000000..40dfc8a --- /dev/null +++ b/secrets/tide.yaml.example @@ -0,0 +1,20 @@ +# Encrypt this with sops to produce ./tide.yaml: +# +# sops --encrypt --age $(cat ~/.config/sops/age/keys.txt | grep public | cut -d: -f2 | tr -d ' ') \ +# secrets/tide.yaml.example > secrets/tide.yaml +# +# Or set up .sops.yaml with the host's age public key and run +# `sops secrets/tide.yaml`. +# +# Generate APP_KEY with: +# php -r "echo 'base64:'.base64_encode(random_bytes(32)).PHP_EOL;" + +tide-env: | + APP_KEY=base64:REPLACE_ME + DB_PASSWORD=REPLACE_ME + MAIL_HOST=127.0.0.1 + MAIL_PORT=1025 + MAIL_USERNAME= + MAIL_PASSWORD= + MAIL_FROM_ADDRESS=noreply@tide.yisroelbaum.com + MAIL_FROM_NAME=TIDE diff --git a/tide.nix b/tide.nix new file mode 100644 index 0000000..fac2eba --- /dev/null +++ b/tide.nix @@ -0,0 +1,29 @@ +{ + domainName, + ... +}: +{ + services.tide = { + enable = true; + domain = "tide.${domainName}"; + apiDomain = "apitide.${domainName}"; + secretsFile = "/run/secrets/tide-env"; + # Reuse the wildcard cert already issued for *.${domainName} + # in configuration.nix instead of requesting a new one per + # subdomain. + nginx.useACMEHost = domainName; + }; + + # Don't fail evaluation when secrets/tide.yaml is missing (e.g. + # before the operator has encrypted it on a fresh checkout). + # sops-install-secrets will still error at activation time if + # the file is absent, which is the right place for that failure. + sops.validateSopsFiles = false; + + sops.secrets."tide-env" = { + sopsFile = ./secrets/tide.yaml; + # phpfpm reads this via EnvironmentFile, which runs as root + # before dropping to the tide user, so root readable is enough. + mode = "0400"; + }; +}