Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ path = [
"overlays/custom-packages/system76-scheduler/0001-fix-add-missing-loop-in-process-scheduler-refresh-ta.patch",
"modules/secureboot/keys/*",
"modules/hardware/passthrough/pci-acs-override/0001-pci-add-pcie_acs_override-for-pci-passthrough.patch",
"overlays/custom-packages/systemd/0001-pam_systemd_home-Use-PAM_TEXT_INFO-for-token-prompts.patch"
"overlays/custom-packages/systemd/0001-pam_systemd_home-Use-PAM_TEXT_INFO-for-token-prompts.patch",
"overlays/custom-packages/spire-mini/0001-removed-cloud-infra-to-reduce-memory-footprint.patch"
]

[[annotations]]
Expand Down
21 changes: 21 additions & 0 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,27 @@ in
};
# keep-sorted end
};

spireWorkloads = lib.types.listOf (
lib.types.submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
description = "Name of workload";
};
selectors = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "Selector of the workload";
};
};
}
);

spireNodeAttestationMode = lib.types.enum [
# Add new modes here
"join_token"
];

};

# Launcher utilities (remove desktop entries from packages)
Expand Down
20 changes: 20 additions & 0 deletions lib/global-config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ rec {
debug = mkEnableOption "GIVC debug mode";
};

spire = {
enable = mkEnableOption "SPIFFE SPIRE globally";
debug = mkEnableOption "SPIRE debug logs";
};

storage = {
encryption.enable = mkEnableOption "storage encryption globally";
storeOnDisk = mkEnableOption "storing VM nix stores on disk rather than virtiofs";
Expand Down Expand Up @@ -334,6 +339,11 @@ rec {
debug = false;
};

spire = {
enable = true;
debug = true;
};

storage = {
encryption.enable = false;
storeOnDisk = false;
Expand Down Expand Up @@ -425,6 +435,11 @@ rec {
debug = false;
};

spire = {
enable = true;
debug = false;
};

storage = {
encryption.enable = true;
storeOnDisk = false;
Expand Down Expand Up @@ -513,6 +528,11 @@ rec {
debug = false;
};

spire = {
enable = true;
debug = false;
};

storage = {
encryption.enable = false;
storeOnDisk = false;
Expand Down
45 changes: 45 additions & 0 deletions modules/common/common.nix
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,51 @@ in

options.ghaf = {
common = {
spire = {
server = {
address = lib.mkOption {
type = lib.types.str;
default = "0.0.0.0";
description = "SPIRE server bind address";
};
port = lib.mkOption {
type = lib.types.port;
default = 8081;
description = "SPIRE server bind port (agents connect here)";
};
trustDomain = lib.mkOption {
type = lib.types.str;
default = "ghaf.internal";
description = "SPIFFE trust domain used by SPIRE (spiffe://<trustDomain>/...)";
};
};
agents = mkOption {
description = "Spire agents configuration";
default = { };
type = types.attrsOf (
types.submodule {
options = {
nodeAttestationMode = mkOption {
type = types.spireNodeAttestationMode;
default = "join_token";
description = "Spire node attestation mode";
};
workloads = mkOption {
type = types.spireWorkloads;
default = [ ];
description = "Spire workloads";
};
};
}
);
};
package = lib.mkOption {
type = lib.types.package;
default = pkgs.spire-mini;
example = lib.literalExpression "pkgs.spire";
description = "Spire package to use.";
};
};
policies = mkOption {
description = "System policies";
default = { };
Expand Down
1 change: 1 addition & 0 deletions modules/common/security/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
./pwquality.nix
./ssh-tarpit
./fleet
./spire
./tpm2-packages.nix
../../secureboot/secureboot.nix
];
Expand Down
162 changes: 162 additions & 0 deletions modules/common/security/spire/agent.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# SPDX-FileCopyrightText: 2022-2026 TII (SSRC) and the Ghaf contributors
# SPDX-License-Identifier: Apache-2.0
{
config,
lib,
pkgs,
...
}:
let
cfg = config.ghaf.security.spire.agent;
dataDir = "/var/lib/spire/agent";
runtimeDataDir = "/run/spire/agent";
inherit (lib)
getExe
mkIf
mkOption
mkEnableOption
types
optionalString
;
spire-package = config.ghaf.common.spire.package;

joinTokenConf = optionalString (cfg.nodeAttestationMode == "join_token") ''
join_token_file = "${cfg.settings.join_token.token}"
'';
joinTokenPlugin = optionalString (cfg.nodeAttestationMode == "join_token") ''
NodeAttestor "join_token" {
plugin_data {}
}
'';
agentConf = ''
agent {
data_dir = "${dataDir}"
log_level = "${cfg.logLevel}"
server_address = "${config.ghaf.common.spire.server.address}"
server_port = ${toString config.ghaf.common.spire.server.port}
trust_domain = "${config.ghaf.common.spire.server.trustDomain}"
trust_bundle_path = "${cfg.trustBundlePath}"
socket_path = "${runtimeDataDir}/agent.sock"
${joinTokenConf}
}

plugins {
${joinTokenPlugin}

WorkloadAttestor "unix" {
plugin_data {}
}
KeyManager "disk" {
plugin_data {
directory = "${dataDir}/keys"
}
}
}
'';

server-health = pkgs.writeShellApplication {
name = "spire-server-healthcheck";

runtimeInputs = [
pkgs.curl
pkgs.spire-agent
];

text = ''
SERVER_URL="http://${config.ghaf.common.spire.server.address}:8080/ready"
MODE="${cfg.nodeAttestationMode}"

echo "Checking SPIRE Server readiness at $SERVER_URL..."

until curl --fail --silent "$SERVER_URL" > /dev/null 2>&1; do
echo "SPIRE Server is not ready yet. Retrying in 3 seconds..."
sleep 3
done

if [ "$MODE" == "join_token" ]; then
while [ ! -e "${cfg.settings.join_token.token}" ]; do
echo "Waiting for server token..."
sleep 1
done
fi
echo "SPIRE Server is ready! Starting SPIRE Agent..."
'';
};
in
{
_file = ./agent.nix;

options.ghaf.security.spire.agent = {
enable = mkEnableOption "SPIRE agent";
nodeAttestationMode = mkOption {
type = types.spireNodeAttestationMode;
default = "join_token";
description = "Node attestation mode";
};
workloads = mkOption {
type = types.spireWorkloads;
default = [ ];
description = "List of workloads for this spire agent";
};
trustBundlePath = mkOption {
type = types.str;
default = "/etc/common/spire/bundle.pem";
description = "Path to the SPIRE trust bundle PEM file (used to verify the server during bootstrap)";
};
logLevel = mkOption {
type = types.str;
default = "INFO";
description = "SPIRE server log level";
};
settings = {
join_token = {
token = mkOption {
type = types.path;
default = "/etc/common/spire/tokens/${config.networking.hostName}.token";
description = "SPIRE server log level";
};
};
};
};

config = mkIf cfg.enable {
environment.etc."spire/agent.conf".text = agentConf;
services.spire.agent = {
enable = true;
package = spire-package;
configFile = "/etc/spire/agent.conf";
};

environment.systemPackages = [ spire-package ];
systemd = {
services = {
spire-agent = {
requires = [ "network-online.target" ];
after = [
"network-online.target"
];

unitConfig = {
RequiresMountsFor = [ "/etc/common" ];
};

serviceConfig = {
ExecStartPre = getExe server-health;
NoNewPrivileges = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [
"${dataDir}"
"${runtimeDataDir}"
];
};
};
};
tmpfiles.rules = [
"d /run/spire 2750 spire-agent spire-agent - -"
"d ${runtimeDataDir} 2750 spire-agent spire-agent - -"
];
};
};
}
Loading
Loading