mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-07-06 17:13:24 -05:00
nixos/spire: init
This commit is contained in:
parent
7553fce9f7
commit
e5d8795180
5 changed files with 345 additions and 0 deletions
|
|
@ -1515,6 +1515,8 @@
|
|||
./services/security/reaction.nix
|
||||
./services/security/shibboleth-sp.nix
|
||||
./services/security/sks.nix
|
||||
./services/security/spire/agent.nix
|
||||
./services/security/spire/server.nix
|
||||
./services/security/ssh-agent-switcher.nix
|
||||
./services/security/sshguard.nix
|
||||
./services/security/sslmate-agent.nix
|
||||
|
|
|
|||
123
nixos/modules/services/security/spire/agent.nix
Normal file
123
nixos/modules/services/security/spire/agent.nix
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
format = pkgs.formats.hcl1 { };
|
||||
cfg = config.services.spire.agent;
|
||||
in
|
||||
{
|
||||
meta.maintainers = [ lib.maintainers.arianvp ];
|
||||
|
||||
options.services.spire.agent = {
|
||||
enable = lib.mkEnableOption "SPIRE agent";
|
||||
|
||||
package = lib.mkPackageOption pkgs "spire" { };
|
||||
|
||||
settings = lib.mkOption {
|
||||
description = ''
|
||||
SPIRE Agent configuration file options. See [the documentation](https://spiffe.io/docs/latest/deploying/spire_agent/) for all available options.
|
||||
'';
|
||||
type = lib.types.submodule {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
agent = {
|
||||
trust_domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The trust domain that this agent belongs to";
|
||||
example = "example.com";
|
||||
};
|
||||
data_dir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "$STATE_DIRECTORY";
|
||||
description = "The directory where the SPIRE agent stores its data";
|
||||
};
|
||||
server_address = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The address of the SPIRE server";
|
||||
example = "server.example.com";
|
||||
};
|
||||
server_port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8081;
|
||||
description = "The port on which the SPIRE server is listening";
|
||||
};
|
||||
socket_path = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = "/run/spire/agent/public/api.sock";
|
||||
description = "The path to the SPIRE agent socket";
|
||||
};
|
||||
};
|
||||
plugins = lib.mkOption {
|
||||
description = ''
|
||||
Built-in plugin types can be found at [the plugin types documentation](https://spiffe.io/docs/latest/deploying/spire_agent/#plugin-types).
|
||||
See [plugin configuration](https://spiffe.io/docs/latest/deploying/spire_agent/#plugin-configuration) for options and how to configure external plugins.
|
||||
'';
|
||||
# TODO: We can probably enforce some of these constraints with a submodule
|
||||
type = format.type;
|
||||
example = {
|
||||
KeyManager.memory.plugin_data = { };
|
||||
NodeAttestor.join_token.plugin_data = { };
|
||||
WorkloadAttestor.systemd.plugin_data = { };
|
||||
WorkloadAttestor.unix.plugin_data = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
defaultText = "Config file generated from services.spire.agent.settings";
|
||||
default = format.generate "agent.conf" cfg.settings;
|
||||
description = ''
|
||||
Path to the SPIRE agent configuration file. See [the documentation](https://spiffe.io/docs/latest/deploying/spire_agent/) for more information.
|
||||
'';
|
||||
};
|
||||
|
||||
expandEnv = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Expand environment variables in SPIRE config file";
|
||||
};
|
||||
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
# TODO: Switch to DynamicUser once https://github.com/NixOS/nixpkgs/issues/299476 lands
|
||||
users.users.spire-agent = {
|
||||
isSystemUser = true;
|
||||
group = "spire-agent";
|
||||
};
|
||||
users.groups.spire-agent = { };
|
||||
|
||||
systemd.services.spire-agent = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
description = "SPIRE agent";
|
||||
serviceConfig = {
|
||||
ExecStart =
|
||||
"${lib.getExe' cfg.package "spire-agent"} run "
|
||||
+ lib.cli.toCommandLineShellGNU { } {
|
||||
inherit (cfg) expandEnv;
|
||||
config = cfg.configFile;
|
||||
};
|
||||
Restart = "on-failure";
|
||||
StateDirectory = "spire/agent";
|
||||
StateDirectoryMode = "0700";
|
||||
RuntimeDirectory = "spire/agent";
|
||||
|
||||
# TODO: Switch to DynamicUser once https://github.com/NixOS/nixpkgs/issues/299476 lands
|
||||
# Without it, the systemd plugin can not talk to dbus
|
||||
# DynamicUser = true;
|
||||
User = "spire-agent";
|
||||
Group = "spire-agent";
|
||||
UMask = "0027";
|
||||
|
||||
# TODO: Hardening
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
120
nixos/modules/services/security/spire/server.nix
Normal file
120
nixos/modules/services/security/spire/server.nix
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
format = pkgs.formats.hcl1 { };
|
||||
cfg = config.services.spire.server;
|
||||
in
|
||||
{
|
||||
meta.maintainers = [ lib.maintainers.arianvp ];
|
||||
|
||||
options.services.spire.server = {
|
||||
enable = lib.mkEnableOption "SPIRE Server";
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
description = "Whether to open firewall";
|
||||
default = false;
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
description = ''
|
||||
SPIRE Server configuration file options. See [the documentation](https://spiffe.io/docs/latest/deploying/spire_server/) for all available options.
|
||||
'';
|
||||
type = lib.types.submodule {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
server = {
|
||||
trust_domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The trust domain that this server belongs to";
|
||||
example = "example.com";
|
||||
};
|
||||
data_dir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "The directory where SPIRE server stores its data";
|
||||
default = "$STATE_DIRECTORY";
|
||||
};
|
||||
socket_path = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/run/spire/server/private/api.sock";
|
||||
description = "Path to bind the SPIRE Server API Socket to";
|
||||
};
|
||||
bind_address = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "[::]";
|
||||
description = "The address on which the SPIRE server is listening";
|
||||
};
|
||||
bind_port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8081;
|
||||
description = "The port on which the SPIRE server is listening";
|
||||
};
|
||||
};
|
||||
plugins = lib.mkOption {
|
||||
description = ''
|
||||
Built-in plugin types can be found at [the plugin types documentation](https://spiffe.io/docs/latest/deploying/spire_server/#plugin-types).
|
||||
See [plugin configuration](https://spiffe.io/docs/latest/deploying/spire_server/#plugin-configuration) for options and how to configure external plugins.
|
||||
'';
|
||||
# TODO: We can probably enforce some of these constraints with a submodule
|
||||
type = format.type;
|
||||
example = {
|
||||
KeyManager.memory.plugin_data = { };
|
||||
DataStore.sql.plugin_data = {
|
||||
database_type = "sqlite3";
|
||||
connection_string = "$STATE_DIRECTORY/datastore.sqlite3";
|
||||
};
|
||||
NodeAttestor.join_token.plugin_data = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = format.generate "server.conf" cfg.settings;
|
||||
defaultText = "Config file generated from services.spire.server.settings";
|
||||
description = ''
|
||||
Path to the SPIRE server configuration file. See [the documentation](https://spiffe.io/docs/latest/deploying/spire_server/) for more information.
|
||||
'';
|
||||
};
|
||||
|
||||
expandEnv = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Expand environment variables in services.spire.server.settings and services.spire.server.configFile";
|
||||
};
|
||||
|
||||
package = lib.mkPackageOption pkgs "spire" { };
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.settings.server.bind_port ];
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
systemd.services.spire-server = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
description = "SPIRE Server";
|
||||
documentation = [ "https://spiffe.io/docs/latest/deploying/spire_server/" ];
|
||||
serviceConfig = {
|
||||
ExecStart =
|
||||
"${lib.getExe' cfg.package "spire-server"} run "
|
||||
+ lib.cli.toCommandLineShellGNU { } {
|
||||
inherit (cfg) expandEnv;
|
||||
config = cfg.configFile;
|
||||
};
|
||||
Restart = "on-failure";
|
||||
StateDirectory = "spire/server";
|
||||
StateDirectoryMode = "0700";
|
||||
RuntimeDirectory = "spire/server";
|
||||
DynamicUser = true;
|
||||
UMask = "0027";
|
||||
# TODO: hardening
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1490,6 +1490,7 @@ in
|
|||
spacecookie = runTest ./spacecookie.nix;
|
||||
spark = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./spark { };
|
||||
spiped = runTest ./spiped.nix;
|
||||
spire = runTest ./spire.nix;
|
||||
sqlite3-to-mysql = runTest ./sqlite3-to-mysql.nix;
|
||||
squid = runTest ./squid.nix;
|
||||
ssh-agent-auth = runTest ./ssh-agent-auth.nix;
|
||||
|
|
|
|||
99
nixos/tests/spire.nix
Normal file
99
nixos/tests/spire.nix
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
let
|
||||
trustDomain = "example.com";
|
||||
in
|
||||
{
|
||||
name = "spire";
|
||||
|
||||
nodes = {
|
||||
server =
|
||||
{ config, ... }:
|
||||
{
|
||||
networking.domain = trustDomain;
|
||||
services.spire.server = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
settings = {
|
||||
server.trust_domain = trustDomain;
|
||||
plugins = {
|
||||
KeyManager.memory.plugin_data = { };
|
||||
DataStore.sql.plugin_data = {
|
||||
database_type = "sqlite3";
|
||||
connection_string = "$STATE_DIRECTORY/datastore.sqlite3";
|
||||
};
|
||||
NodeAttestor.join_token.plugin_data = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
agent = {
|
||||
virtualisation.credentials = {
|
||||
"spire.join_token".source = "./join_token";
|
||||
"spire.trust_bundle".source = "./trust_bundle";
|
||||
};
|
||||
|
||||
systemd.services.spire-agent.serviceConfig.ImportCredential = [
|
||||
"spire.join_token"
|
||||
"spire.trust_bundle"
|
||||
];
|
||||
|
||||
services.spire.agent = {
|
||||
enable = true;
|
||||
settings = {
|
||||
agent = {
|
||||
trust_domain = trustDomain;
|
||||
server_address = "server.${trustDomain}";
|
||||
join_token_file = "$CREDENTIALS_DIRECTORY/spire.join_token";
|
||||
trust_bundle_format = "pem";
|
||||
trust_bundle_path = "$CREDENTIALS_DIRECTORY/spire.trust_bundle";
|
||||
};
|
||||
plugins = {
|
||||
KeyManager.memory.plugin_data = { };
|
||||
NodeAttestor.join_token.plugin_data = { };
|
||||
WorkloadAttestor.systemd.plugin_data = { };
|
||||
WorkloadAttestor.unix.plugin_data = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
{ nodes }:
|
||||
let
|
||||
adminSocket = nodes.server.services.spire.server.settings.server.socket_path;
|
||||
workloadSocket = nodes.agent.services.spire.agent.settings.agent.socket_path;
|
||||
in
|
||||
''
|
||||
# TODO: instead of trust bundle to talk to the spire-server, use an upstream CA?
|
||||
def provision(agent, spiffe_id):
|
||||
|
||||
# expose as system credentials
|
||||
bundle = server.succeed("spire-server bundle show -socketPath ${adminSocket}")
|
||||
with open(agent.state_dir / "trust_bundle", "w") as f:
|
||||
f.write(bundle)
|
||||
join_token = server.succeed("spire-server token generate -socketPath ${adminSocket}").split()[1]
|
||||
with open(agent.state_dir / "join_token", "w") as f:
|
||||
f.write(join_token)
|
||||
|
||||
# register a workload on the node
|
||||
parent_id=f"spiffe://${trustDomain}/spire/agent/join_token/{join_token}"
|
||||
server.succeed(f"spire-server entry create -socketPath ${adminSocket} -selector 'systemd:id:backdoor.service' -parentID {parent_id} -spiffeID 'spiffe://${trustDomain}/service/backdoor'")
|
||||
|
||||
with subtest("SPIRE server startup and health checks"):
|
||||
server.wait_for_unit("spire-server.service")
|
||||
server.wait_until_succeeds("spire-server healthcheck -socketPath ${adminSocket}", timeout=5)
|
||||
|
||||
|
||||
with subtest("Setup SPIRE agent on agent node"):
|
||||
provision(agent, "spiffe://${trustDomain}/server/agent")
|
||||
agent.wait_for_unit("spire-agent.service")
|
||||
agent.wait_until_succeeds("spire-agent healthcheck -socketPath ${workloadSocket}", timeout=5)
|
||||
|
||||
|
||||
with subtest("Test certificate authentication from agent node"):
|
||||
agent.succeed("spire-agent api fetch x509 -socketPath ${workloadSocket} -write .")
|
||||
|
||||
# TODO: Add something to communicate with
|
||||
'';
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue