diff --git a/nix/modules/tack.nix b/nix/modules/tack.nix index 0fdb4dc..e392044 100644 --- a/nix/modules/tack.nix +++ b/nix/modules/tack.nix @@ -46,6 +46,16 @@ in description = "Path to the local SQLite store (TACK_DB_PATH)."; }; + repoDir = mkOption { + type = types.path; + default = "/var/lib/tack/repos"; + description = '' + Directory for Tack's sparse workflow-definition checkouts + (TACK_REPO_DIR). This must be persistent across restarts to avoid + cloning every repository again; the default lives in StateDirectory. + ''; + }; + jetstreamUrl = mkOption { type = types.str; default = "wss://jetstream1.us-west.bsky.network/subscribe"; @@ -86,6 +96,41 @@ in }; }; + # Tekton uses the pod's in-cluster credentials, so there is no token to + # place in environmentFile. Enabling it outside Kubernetes will make Tack + # fail fast while constructing the provider. + tekton = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable the in-cluster Tekton provider (TACK_TEKTON_ENABLED)."; + }; + + namespace = mkOption { + type = types.str; + default = "default"; + description = '' + Namespace where Tack creates PipelineRuns + (TACK_TEKTON_NAMESPACE). + ''; + }; + }; + + # The sourcehut token is sensitive and remains in environmentFile. Only + # the optional public instance URL belongs in the declarative module. + sourcehut = { + instance = mkOption { + type = types.nullOr types.str; + default = null; + example = "https://builds.sr.ht"; + description = '' + Base URL of the builds.sr.ht-compatible service + (TACK_SOURCEHUT_INSTANCE). The public builds.sr.ht service is used + when unset. + ''; + }; + }; + environmentFile = mkOption { type = with types; nullOr path; default = null; @@ -93,9 +138,10 @@ in description = '' Additional environment file as defined in {manpage}`systemd.exec(5)`. - Sensitive values such as {env}`TACK_BUILDKITE_TOKEN` and - {env}`TACK_BUILDKITE_WEBHOOK_SECRET` belong here so they - don't get baked into the world-readable Nix store. + Sensitive values such as {env}`TACK_BUILDKITE_TOKEN`, + {env}`TACK_BUILDKITE_WEBHOOK_SECRET`, and + {env}`TACK_SOURCEHUT_TOKEN` belong here so they don't get baked into + the world-readable Nix store. ''; }; @@ -133,9 +179,11 @@ in # settings override our defaults on conflict. serviceConfig = { - # StateDirectory creates /var/lib/tack with the right - # ownership; the default dbPath lives there. + # StateDirectory creates /var/lib/tack with the right ownership; + # both the default dbPath and repoDir live beneath it. Restrictive + # modes protect provider identifiers and checked-out workflow YAML. StateDirectory = "tack"; + StateDirectoryMode = "0700"; LogsDirectory = "tack"; EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; @@ -148,12 +196,18 @@ in "TACK_HOSTNAME=${cfg.hostname}" "TACK_OWNER_DID=${cfg.ownerDid}" "TACK_DB_PATH=${cfg.dbPath}" + "TACK_REPO_DIR=${cfg.repoDir}" "TACK_JETSTREAM_URL=${cfg.jetstreamUrl}" "TACK_BUILDKITE_WEBHOOK_MODE=${cfg.buildkite.webhookMode}" ] ++ optional cfg.dev "TACK_DEV=1" ++ optional (cfg.buildkite.org != null) - "TACK_BUILDKITE_ORG=${cfg.buildkite.org}"; + "TACK_BUILDKITE_ORG=${cfg.buildkite.org}" + ++ optional cfg.tekton.enable "TACK_TEKTON_ENABLED=1" + ++ optional cfg.tekton.enable + "TACK_TEKTON_NAMESPACE=${cfg.tekton.namespace}" + ++ optional (cfg.sourcehut.instance != null) + "TACK_SOURCEHUT_INSTANCE=${cfg.sourcehut.instance}"; ExecStart = "${cfg.package}/bin/tack -addr ${cfg.listenAddr}"; Restart = "always"; @@ -161,9 +215,18 @@ in # Light hardening. Tack only needs network access plus its # state directory, so we lock the rest down. DynamicUser = true; + UMask = "0077"; NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; + # StateDirectory is made writable automatically. These exceptions + # also make custom dbPath/repoDir parents usable under + # ProtectSystem=strict; operators remain responsible for creating + # and assigning ownership of custom directories. + ReadWritePaths = [ + "-${builtins.dirOf (toString cfg.dbPath)}" + "-${builtins.dirOf (toString cfg.repoDir)}" + ]; PrivateTmp = true; PrivateDevices = true; ProtectKernelTunables = true;