Begin testing bootstrapping of vault authentication
However, the nixos-install script fails on khs openstack
as the system won't boot up after installation due it
being unable to locate the root disk. I am not sure what disk
it ends up finding.
Former-commit-id: 608d758f30
This commit is contained in:
parent
07360c41da
commit
d406c83aa1
32 changed files with 706 additions and 195 deletions
|
@ -29,3 +29,12 @@ jobs:
|
||||||
nix build --no-link '.#packages.x86_64-linux.terraform-provider-hcloud'
|
nix build --no-link '.#packages.x86_64-linux.terraform-provider-hcloud'
|
||||||
nix build --no-link '.#packages.x86_64-linux.terraform-provider-openstack'
|
nix build --no-link '.#packages.x86_64-linux.terraform-provider-openstack'
|
||||||
nix build --no-link '.#packages.x86_64-linux.terraform-provider-unifi'
|
nix build --no-link '.#packages.x86_64-linux.terraform-provider-unifi'
|
||||||
|
nix build --no-link '.#packages.x86_64-linux.terraform-provider-vault'
|
||||||
|
systems:
|
||||||
|
runs-on: cache.kaareskovgaard.net
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
nix build --no-link '.#nixosConfigurations."desktop.kaareskovgaard.net".config.system.build.toplevel'
|
||||||
|
nix build --no-link '.#nixosConfigurations."desktop.kaareskovgaard.net".config.system.build.vm'
|
||||||
|
nix build --no-link '.#nixosConfigurations."test.kaareskovgaard.net".config.system.build.toplevel'
|
||||||
|
|
|
@ -40,6 +40,8 @@ To delete the resources again run:
|
||||||
nix run '.#destroy-instance' -- <hostname>
|
nix run '.#destroy-instance' -- <hostname>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE: It is normal for the secret id associated with vault/openbao roles to not be deletable. Simply run the destroy-instance command a 2nd time and everything should work just fine.
|
||||||
|
|
||||||
## Secrets
|
## Secrets
|
||||||
|
|
||||||
To transfer the secrets needed for OpenTofu from Bitwarden to OpenBAO run:
|
To transfer the secrets needed for OpenTofu from Bitwarden to OpenBAO run:
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
fdf6ba679a51aab2a95570b227e011fdfdf436ce
|
c232e59d1bbe8b0774b7714233fbff517d1790ed
|
18
flake.lock
generated
18
flake.lock
generated
|
@ -546,11 +546,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751741127,
|
"lastModified": 1751943650,
|
||||||
"narHash": "sha256-t75Shs76NgxjZSgvvZZ9qOmz5zuBE8buUaYD28BMTxg=",
|
"narHash": "sha256-7orTnNqkGGru8Je6Un6mq1T8YVVU/O5kyW4+f9C1mZQ=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "29e290002bfff26af1db6f64d070698019460302",
|
"rev": "88983d4b665fb491861005137ce2b11a9f89f203",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -624,11 +624,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751942411,
|
"lastModified": 1752028888,
|
||||||
"narHash": "sha256-01uMHCt2U9tP4f24DGch145tT8YQppLY5TC9mWK7O0A=",
|
"narHash": "sha256-LRj3/PUpII6taWOrX1w/OeI6f1ncND02PP/kEHvPCqU=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "c587235f892930a61c9e415f0d9792a1b27a41a2",
|
"rev": "a0f1c656e053463b47639234b151a05e4441bb19",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -681,11 +681,11 @@
|
||||||
"tinted-zed": "tinted-zed"
|
"tinted-zed": "tinted-zed"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1752009340,
|
"lastModified": 1752084754,
|
||||||
"narHash": "sha256-6IKc+fdgJ+mWW8pBOVS5MYvttHBhvWSbff/31pG3SAY=",
|
"narHash": "sha256-JorlZGCWxlYV01lXmUuDeKOZoLPdoN3fAKJv8YIuavs=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "stylix",
|
"repo": "stylix",
|
||||||
"rev": "c647aaa1dead3752fb49f226a4f67ae1030d7747",
|
"rev": "2df042576646d012d15637f43d6075995e785ce3",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -97,7 +97,7 @@
|
||||||
inherit inputs;
|
inherit inputs;
|
||||||
khscodesLib = inputs.self.lib;
|
khscodesLib = inputs.self.lib;
|
||||||
};
|
};
|
||||||
terranixModules.openbao = import ./nix/modules/terranix/openbao {
|
terranixModules.vault = import ./nix/modules/terranix/vault {
|
||||||
inherit inputs;
|
inherit inputs;
|
||||||
khscodesLib = inputs.self.lib;
|
khscodesLib = inputs.self.lib;
|
||||||
};
|
};
|
||||||
|
|
35
nix/lib/disko-root-bios/default.nix
Normal file
35
nix/lib/disko-root-bios/default.nix
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
disko-root-bios =
|
||||||
|
{
|
||||||
|
diskName,
|
||||||
|
device,
|
||||||
|
bootPartName ? "boot",
|
||||||
|
rootPartName ? "root",
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
devices.disk = {
|
||||||
|
"${diskName}" = {
|
||||||
|
inherit device;
|
||||||
|
type = "disk";
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
${bootPartName} = {
|
||||||
|
size = "1M";
|
||||||
|
type = "EF02"; # for grub MBR
|
||||||
|
};
|
||||||
|
${rootPartName} = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
let
|
let
|
||||||
cfg = config.khscodes.infrastructure.hetzner-instance;
|
cfg = config.khscodes.infrastructure.hetzner-instance;
|
||||||
fqdn = config.khscodes.networking.fqdn;
|
fqdn = config.khscodes.networking.fqdn;
|
||||||
|
provisioningUserData = config.khscodes.infrastructure.provisioning.instanceUserData;
|
||||||
firewallTcpRules = lib.lists.map (p: {
|
firewallTcpRules = lib.lists.map (p: {
|
||||||
direction = "in";
|
direction = "in";
|
||||||
protocol = "tcp";
|
protocol = "tcp";
|
||||||
|
@ -160,6 +161,7 @@ in
|
||||||
initial_image = "debian-12";
|
initial_image = "debian-12";
|
||||||
rdns = fqdn;
|
rdns = fqdn;
|
||||||
ssh_keys = [ config.khscodes.hcloud.output.data.ssh_key.khs.id ];
|
ssh_keys = [ config.khscodes.hcloud.output.data.ssh_key.khs.id ];
|
||||||
|
user_data = provisioningUserData;
|
||||||
};
|
};
|
||||||
khscodes.cloudflare = {
|
khscodes.cloudflare = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
let
|
let
|
||||||
cfg = config.khscodes.infrastructure.khs-openstack-instance;
|
cfg = config.khscodes.infrastructure.khs-openstack-instance;
|
||||||
fqdn = config.khscodes.networking.fqdn;
|
fqdn = config.khscodes.networking.fqdn;
|
||||||
|
provisioningUserData = config.khscodes.infrastructure.provisioning.instanceUserData;
|
||||||
firewallTcpRules = lib.lists.flatten (
|
firewallTcpRules = lib.lists.flatten (
|
||||||
lib.lists.map (p: [
|
lib.lists.map (p: [
|
||||||
{
|
{
|
||||||
|
@ -83,14 +84,6 @@ in
|
||||||
default = "${fqdn}.tfstate";
|
default = "${fqdn}.tfstate";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
secretsSource = lib.mkOption {
|
|
||||||
type = lib.types.enum [
|
|
||||||
"bitwarden"
|
|
||||||
"vault"
|
|
||||||
];
|
|
||||||
description = "Whether to load opentofu secrets from Bitwarden or Vault";
|
|
||||||
default = "vault";
|
|
||||||
};
|
|
||||||
flavor = lib.mkOption {
|
flavor = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
description = "The server type to create";
|
description = "The server type to create";
|
||||||
|
@ -188,6 +181,7 @@ in
|
||||||
flavor = cfg.flavor;
|
flavor = cfg.flavor;
|
||||||
ssh_public_key = cfg.ssh_key;
|
ssh_public_key = cfg.ssh_key;
|
||||||
firewall_rules = firewallRules;
|
firewall_rules = firewallRules;
|
||||||
|
user_data = provisioningUserData;
|
||||||
};
|
};
|
||||||
khscodes.unifi.enable = true;
|
khscodes.unifi.enable = true;
|
||||||
khscodes.unifi.static_route.compute = {
|
khscodes.unifi.static_route.compute = {
|
||||||
|
@ -236,11 +230,15 @@ in
|
||||||
message = "Must set config.khscodes.networking.fqdn when using opentofu";
|
message = "Must set config.khscodes.networking.fqdn when using opentofu";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
khscodes.services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
hostCertificate = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
khscodes.infrastructure.provisioning = {
|
khscodes.infrastructure.provisioning = {
|
||||||
pre = {
|
pre = {
|
||||||
modules = modules;
|
modules = modules;
|
||||||
secretsSource = cfg.secretsSource;
|
|
||||||
endpoints = [
|
endpoints = [
|
||||||
"aws"
|
"aws"
|
||||||
"cloudflare"
|
"cloudflare"
|
||||||
|
|
|
@ -29,6 +29,8 @@ let
|
||||||
"unifi"
|
"unifi"
|
||||||
"hcloud"
|
"hcloud"
|
||||||
"cloudflare"
|
"cloudflare"
|
||||||
|
"vault"
|
||||||
|
"authentik"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
description = "Needed endpoints to be used during provisioning";
|
description = "Needed endpoints to be used during provisioning";
|
||||||
|
@ -40,6 +42,11 @@ in
|
||||||
options.khscodes.infrastructure.provisioning = {
|
options.khscodes.infrastructure.provisioning = {
|
||||||
pre = provisioning;
|
pre = provisioning;
|
||||||
post = provisioning;
|
post = provisioning;
|
||||||
|
instanceUserData = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "User data that should be added to the instance during provisioning";
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
preConfig = lib.mkOption {
|
preConfig = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = lib.types.nullOr lib.types.path;
|
||||||
description = "The generated config for the pre provisioning, if any was specified";
|
description = "The generated config for the pre provisioning, if any was specified";
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.khscodes.infrastructure.vault-server-approle;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.khscodes.infrastructure.vault-server-approle = {
|
||||||
|
enable = lib.mkEnableOption "Enables creating an OpenBAO role for the server";
|
||||||
|
stage = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"pre"
|
||||||
|
"post"
|
||||||
|
];
|
||||||
|
description = "The provisioning stage that should include the provisioning. This should be pre for every server except the OpenBAO server itself";
|
||||||
|
default = "pre";
|
||||||
|
};
|
||||||
|
role_name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Name of the role being created";
|
||||||
|
default = config.networking.fqdnOrHostName;
|
||||||
|
};
|
||||||
|
policy = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.khscodes.mkSubmodule {
|
||||||
|
options = {
|
||||||
|
capabilities = lib.mkOption {
|
||||||
|
type = lib.types.listOf (
|
||||||
|
lib.types.enum [
|
||||||
|
"create"
|
||||||
|
"update"
|
||||||
|
"patch"
|
||||||
|
"read"
|
||||||
|
"delete"
|
||||||
|
"list"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "Vault role policy";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
stageModules = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.anything;
|
||||||
|
description = "Extra modules to add to the configured stage";
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
khscodes.infrastructure.provisioning.${cfg.stage} = {
|
||||||
|
endpoints = [ "vault" ];
|
||||||
|
modules = [
|
||||||
|
(
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
imports = [ inputs.self.terranixModules.vault ];
|
||||||
|
khscodes.vault = {
|
||||||
|
enable = true;
|
||||||
|
approle_auth_backend_role.${cfg.role_name} = {
|
||||||
|
backend = "approle";
|
||||||
|
role_name = cfg.role_name;
|
||||||
|
# I keep the secret ids alive for quite long, as I have no way of
|
||||||
|
# automatically bootstrapping a new secret id.
|
||||||
|
secret_id_ttl = 5 * 60 * 60;
|
||||||
|
secret_id_num_uses = 5 * 60;
|
||||||
|
token_ttl = 20 * 60;
|
||||||
|
token_max_ttl = 30 * 60;
|
||||||
|
token_policies = [ cfg.role_name ];
|
||||||
|
};
|
||||||
|
approle_auth_backend_role_secret_id.${cfg.role_name} = {
|
||||||
|
backend = "approle";
|
||||||
|
# Not hardcoding the role name here, as reading it like this will create a dependency
|
||||||
|
# on the role being created first, which is needed.
|
||||||
|
role_name = config.khscodes.vault.output.approle_auth_backend_role.${cfg.role_name}.role_name;
|
||||||
|
# Should only be 5-10 mins once done testing
|
||||||
|
wrapping_ttl = 5 * 60 * 60;
|
||||||
|
|
||||||
|
# All of this simply tries to ensure that I never recreate this secret id
|
||||||
|
# even if the original wrapped secret id is expired (which I expect it to be).
|
||||||
|
with_wrapped_accessor = true;
|
||||||
|
lifecycle = {
|
||||||
|
ignore_changes = [
|
||||||
|
"num_uses"
|
||||||
|
"ttl"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
policy.${cfg.role_name} = {
|
||||||
|
name = cfg.role_name;
|
||||||
|
policy = lib.strings.concatStringsSep "\n\n" (
|
||||||
|
lib.lists.map (
|
||||||
|
{ name, value }:
|
||||||
|
''
|
||||||
|
path "${name}" {
|
||||||
|
capabilities = ${builtins.toJSON value.capabilities}
|
||||||
|
}
|
||||||
|
''
|
||||||
|
) (lib.attrsToList cfg.policy)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
] ++ cfg.stageModules;
|
||||||
|
};
|
||||||
|
# I can only provide the user data if the stage is pre (along with the instance creation)
|
||||||
|
# Also I should probably find a way of injecting this in a nicer way than this mess.
|
||||||
|
khscodes.infrastructure.provisioning.instanceUserData = lib.mkIf (cfg.stage == "pre") ''
|
||||||
|
{
|
||||||
|
"VAULT_ROLE_ID": "''${ vault_approle_auth_backend_role.${lib.khscodes.sanitize-terraform-name cfg.role_name}.role_id }",
|
||||||
|
"VAULT_SECRET_ID_WRAPPED": "''${ vault_approle_auth_backend_role_secret_id.${lib.khscodes.sanitize-terraform-name cfg.role_name}.wrapping_token }"
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ in
|
||||||
{
|
{
|
||||||
networking.hostName = lib.mkForce hostname;
|
networking.hostName = lib.mkForce hostname;
|
||||||
networking.domain = lib.mkForce domain;
|
networking.domain = lib.mkForce domain;
|
||||||
|
networking.fqdn = cfg;
|
||||||
# Add the name of the server to the ssh host certificate domains, but let other configs enable getting the host certificates.
|
# Add the name of the server to the ssh host certificate domains, but let other configs enable getting the host certificates.
|
||||||
khscodes.services.openssh.hostCertificate.hostNames = [ cfg ];
|
khscodes.services.openssh.hostCertificate.hostNames = [ cfg ];
|
||||||
boot.kernel.sysctl = {
|
boot.kernel.sysctl = {
|
||||||
|
|
|
@ -16,12 +16,14 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
disko = lib.khscodes.disko-root-lvm-bios {
|
disko = lib.mkDefault (
|
||||||
device = "/dev/sda";
|
lib.khscodes.disko-root-bios {
|
||||||
diskName = cfg.diskName;
|
device = "/dev/sda";
|
||||||
};
|
diskName = cfg.diskName;
|
||||||
|
}
|
||||||
|
);
|
||||||
boot.loader.grub.efiSupport = false;
|
boot.loader.grub.efiSupport = false;
|
||||||
boot.loader.timeout = 1;
|
boot.loader.timeout = 1;
|
||||||
khscodes.virtualisation.qemu-guest.enable = true;
|
# khscodes.virtualisation.qemu-guest.enable = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,6 @@ in
|
||||||
enable = lib.mkEnableOption "Enables openssh service for the instance";
|
enable = lib.mkEnableOption "Enables openssh service for the instance";
|
||||||
hostCertificate = {
|
hostCertificate = {
|
||||||
enable = lib.mkEnableOption "Enables getting host certificates from OpenBAO";
|
enable = lib.mkEnableOption "Enables getting host certificates from OpenBAO";
|
||||||
secretName = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Secret where the certificate is stored";
|
|
||||||
example = "ssh-host/sign/ca-kaareskovgaard.net";
|
|
||||||
};
|
|
||||||
hostNames = lib.mkOption {
|
hostNames = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
description = "The list of host names to get certificates for";
|
description = "The list of host names to get certificates for";
|
||||||
|
@ -24,6 +19,9 @@ in
|
||||||
let
|
let
|
||||||
certificateNames = lib.lists.unique cfg.hostCertificate.hostNames;
|
certificateNames = lib.lists.unique cfg.hostCertificate.hostNames;
|
||||||
hostCertificatEnable = cfg.hostCertificate.enable && cfg.hostCertificate.hostNames != [ ];
|
hostCertificatEnable = cfg.hostCertificate.enable && cfg.hostCertificate.hostNames != [ ];
|
||||||
|
vaultRoleName = config.khscodes.infrastructure.vault-server-approle.role_name;
|
||||||
|
fqdn = config.networking.fqdnOrHostName;
|
||||||
|
sshHostBackend = "ssh-host";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
|
@ -37,6 +35,34 @@ in
|
||||||
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
|
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
khscodes.infrastructure.vault-server-approle = {
|
||||||
|
enable = true;
|
||||||
|
policy."${sshHostBackend}/sign/${vaultRoleName}" = {
|
||||||
|
capabilities = [
|
||||||
|
"read"
|
||||||
|
"update"
|
||||||
|
"create"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
stageModules = [
|
||||||
|
{
|
||||||
|
khscodes.vault.ssh_secret_backend_role.${vaultRoleName} = {
|
||||||
|
name = fqdn;
|
||||||
|
backend = sshHostBackend;
|
||||||
|
key_type = "ca";
|
||||||
|
allow_host_certificates = true;
|
||||||
|
allow_bare_domains = true;
|
||||||
|
allowed_domains = certificateNames;
|
||||||
|
allowed_user_key_config = [
|
||||||
|
{
|
||||||
|
type = "ed25519";
|
||||||
|
lengths = [ 0 ];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
khscodes.services.vault-agent = lib.mkIf hostCertificatEnable {
|
khscodes.services.vault-agent = lib.mkIf hostCertificatEnable {
|
||||||
enable = true;
|
enable = true;
|
||||||
templates = [
|
templates = [
|
||||||
|
@ -44,7 +70,7 @@ in
|
||||||
contents = ''
|
contents = ''
|
||||||
{{- $public_key := file "/etc/ssh/ssh_host_ed25519_key.pub" -}}
|
{{- $public_key := file "/etc/ssh/ssh_host_ed25519_key.pub" -}}
|
||||||
{{- $public_key = printf "public_key=%s" $public_key -}}
|
{{- $public_key = printf "public_key=%s" $public_key -}}
|
||||||
{{- with secret "${cfg.hostCertificate.secretName}" "cert_type=host" $public_key "valid_principals=${lib.strings.concatStringsSep "," certificateNames}" -}}
|
{{- with secret "ssh-host/sign/${fqdn}" "cert_type=host" $public_key "valid_principals=${lib.strings.concatStringsSep "," certificateNames}" -}}
|
||||||
{{ .Data.signed_key }}
|
{{ .Data.signed_key }}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.khscodes.services.openstack-read-vault-auth-from-userdata;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.khscodes.services.openstack-read-vault-auth-from-userdata = {
|
||||||
|
enable = lib.mkEnableOption "Enables reading vault auth information from instance userdata";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf (cfg.enable && config.khscodes.services.vault-agent.enable) (
|
||||||
|
let
|
||||||
|
vault_addr = config.khscodes.services.vault-agent.vault.address;
|
||||||
|
secretIdFilePath = config.khscodes.services.vault-agent.secretIdFilePath;
|
||||||
|
roleIdFilePath = config.khscodes.services.vault-agent.roleIdFilePath;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
systemd.services."openstack-read-vault-auth-from-userdata" = {
|
||||||
|
enable = true;
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = lib.getExe (
|
||||||
|
pkgs.writeShellApplication {
|
||||||
|
name = "openstack-read-vault-auth-from-userdata";
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.curl
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.openbao
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
if [[ -f "${lib.escapeShellArg secretIdFilePath}" ]]; then
|
||||||
|
echo "Secret id already found, not copying new id"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
userdata="$(curl http://169.254.169.254/openstack/2012-08-10/user_data)"
|
||||||
|
role_id="$(echo "$userdata" | jq --raw '.VAULT_ROLE_ID')"
|
||||||
|
secret_id_wrapped="$(echo "$userdata" | jq --raw '.VAULT_SECRET_ID_WRAPPED')"
|
||||||
|
secret_id="$(BAO_ADDR=${lib.escapeShellArg vault_addr} bao unwrap -field=secret_id "$secret_id_wrapped")"
|
||||||
|
mkdir -p "$(dirname ${lib.escapeShellArg secretIdFilePath})"
|
||||||
|
mkdir -p "$(dirname ${lib.escapeShellArg roleIdFilePath})"
|
||||||
|
echo -n "$role_id" > ${lib.escapeShellArg roleIdFilePath}
|
||||||
|
echo -n "$secret_id" > ${lib.escapeShellArg secretIdFilePath}
|
||||||
|
chown root:root "$${lib.escapeShellArg secretIdFilePath}"
|
||||||
|
chmod 0600 "$${lib.escapeShellArg secretIdFilePath}"
|
||||||
|
chown root:root "$${lib.escapeShellArg roleIdFilePath}"
|
||||||
|
chmod 0600 "$${lib.escapeShellArg roleIdFilePath}"
|
||||||
|
echo "Role id and secret id copied, restart vault-agent"
|
||||||
|
systemctl restart vault-agent-openbao.service
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -5,9 +5,7 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.input-output.openbao.agent;
|
cfg = config.khscodes.services.vault-agent;
|
||||||
secretIdFilePath = "/var/lib/vault-agent/secret-id";
|
|
||||||
roleIdFilePath = "/var/lib/vault-agent/role-id";
|
|
||||||
mkSubmodule =
|
mkSubmodule =
|
||||||
{
|
{
|
||||||
options,
|
options,
|
||||||
|
@ -62,8 +60,8 @@ let
|
||||||
type = "approle";
|
type = "approle";
|
||||||
config = {
|
config = {
|
||||||
mount_path = "auth/approle";
|
mount_path = "auth/approle";
|
||||||
role_id_file_path = roleIdFilePath;
|
role_id_file_path = cfg.vault.roleIdFilePath;
|
||||||
secret_id_file_path = secretIdFilePath;
|
secret_id_file_path = cfg.vault.secretIdFilePath;
|
||||||
remove_secret_id_file_after_reading = false;
|
remove_secret_id_file_after_reading = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -90,10 +88,22 @@ in
|
||||||
default = pkgs.openbao;
|
default = pkgs.openbao;
|
||||||
defaultText = "pkgs.openbao";
|
defaultText = "pkgs.openbao";
|
||||||
};
|
};
|
||||||
vault.address = lib.mkOption {
|
vault = {
|
||||||
type = lib.types.str;
|
address = lib.mkOption {
|
||||||
description = "Address of the Vault/OpenBAO service";
|
type = lib.types.str;
|
||||||
default = "https://vault.kaareskovgaard.net";
|
description = "Address of the Vault/OpenBAO service";
|
||||||
|
default = "https://vault.kaareskovgaard.net";
|
||||||
|
};
|
||||||
|
roleIdFilePath = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Location of the role id";
|
||||||
|
default = "/var/lib/vault-agent/role-id";
|
||||||
|
};
|
||||||
|
secretIdFilePath = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Location of the secret id";
|
||||||
|
default = "/var/lib/vault-agent/secret-id";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
templates = lib.mkOption {
|
templates = lib.mkOption {
|
||||||
default = [ ];
|
default = [ ];
|
||||||
|
@ -146,42 +156,10 @@ in
|
||||||
wantedBy = unitsDependsOnAgent;
|
wantedBy = unitsDependsOnAgent;
|
||||||
unitConfig = {
|
unitConfig = {
|
||||||
ConditionPathExists = [
|
ConditionPathExists = [
|
||||||
secretIdFilePath
|
cfg.vault.secretIdFilePath
|
||||||
roleIdFilePath
|
cfg.vault.roleIdFilePath
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
environment.systemPackages = [
|
|
||||||
(pkgs.writeShellApplication {
|
|
||||||
name = "vault-agent-load-credentials";
|
|
||||||
meta = {
|
|
||||||
mainProgram = "vault-agent-load-credentials";
|
|
||||||
};
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.systemd
|
|
||||||
pkgs.openbao
|
|
||||||
];
|
|
||||||
text = ''
|
|
||||||
if [[ -z "''${1:-}" || -z "''${2:-}" ]]; then
|
|
||||||
>&2 echo "Usage: vault-agent-load-credentials <role-id> <secret-id-wrapped>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
role_id="$1"
|
|
||||||
secret_id_wrapped="$2"
|
|
||||||
secret_id="$(BAO_ADDR=${lib.escapeShellArg cfg.vault.address} bao unwrap -field=secret_id "$secret_id_wrapped")"
|
|
||||||
mkdir -p "$(dirname ${lib.escapeShellArg secretIdFilePath})"
|
|
||||||
mkdir -p "$(dirname ${lib.escapeShellArg roleIdFilePath})"
|
|
||||||
echo -n "$role_id" > ${lib.escapeShellArg roleIdFilePath}
|
|
||||||
echo -n "$secret_id" > ${lib.escapeShellArg secretIdFilePath}
|
|
||||||
chown root:root "$${lib.escapeShellArg secretIdFilePath}"
|
|
||||||
chmod 0600 "$${lib.escapeShellArg secretIdFilePath}"
|
|
||||||
chown root:root "$${lib.escapeShellArg roleIdFilePath}"
|
|
||||||
chmod 0600 "$${lib.escapeShellArg roleIdFilePath}"
|
|
||||||
systemctl restart vault-agent-openbao.service
|
|
||||||
${restartUnits unitsDependsOnAgent}
|
|
||||||
${reloadOrRestartUnits unitsDependsOnAgent}
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
modulesPath,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
@ -24,22 +23,21 @@ in
|
||||||
enableWhenVmTarget = lib.mkEnableOption "Enables some enhancement settings when building as a vm";
|
enableWhenVmTarget = lib.mkEnableOption "Enables some enhancement settings when building as a vm";
|
||||||
};
|
};
|
||||||
|
|
||||||
imports = [ "${modulesPath}/virtualisation/qemu-vm.nix" ];
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enableWhenVmTarget {
|
config = lib.mkIf cfg.enableWhenVmTarget {
|
||||||
virtualisation = {
|
virtualisation = {
|
||||||
vmVariant = {
|
vmVariant = {
|
||||||
services.qemuGuest.enable = true;
|
|
||||||
services.spice-vdagentd.enable = true;
|
|
||||||
khscodes.virtualisation.qemu-guest.enable = true;
|
khscodes.virtualisation.qemu-guest.enable = true;
|
||||||
};
|
services.spice-vdagentd.enable = true;
|
||||||
memorySize = 1024 * 8;
|
virtualisation = {
|
||||||
qemu = {
|
memorySize = 1024 * 8;
|
||||||
options = [
|
qemu = {
|
||||||
"-smp 8"
|
options = [
|
||||||
"-vga none -device virtio-gpu-gl,hostmem=2G,blob=true,venus=true"
|
"-smp 8"
|
||||||
rng
|
"-vga none -device virtio-gpu-gl,hostmem=2G,blob=true,venus=true"
|
||||||
] ++ spice;
|
rng
|
||||||
|
] ++ spice;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,5 +8,10 @@ let
|
||||||
cfg = config.khscodes.virtualisation.qemu-guest;
|
cfg = config.khscodes.virtualisation.qemu-guest;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf cfg.enable (import "${modulesPath}/profiles/qemu-guest.nix" { });
|
config = lib.mkIf cfg.enable (
|
||||||
|
(import "${modulesPath}/profiles/qemu-guest.nix" { })
|
||||||
|
// {
|
||||||
|
services.qemuGuest.enable = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,11 @@ let
|
||||||
default = null;
|
default = null;
|
||||||
description = "FQDN to map rDNS to";
|
description = "FQDN to map rDNS to";
|
||||||
};
|
};
|
||||||
|
user_data = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = "User data for the instance";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
hcloudDataSshKeys = khscodesLib.mkSubmodule {
|
hcloudDataSshKeys = khscodesLib.mkSubmodule {
|
||||||
|
@ -106,6 +111,7 @@ in
|
||||||
ipv6_enabled = true;
|
ipv6_enabled = true;
|
||||||
ipv6 = "\${ hcloud_primary_ip.${name}_ipv6.id }";
|
ipv6 = "\${ hcloud_primary_ip.${name}_ipv6.id }";
|
||||||
};
|
};
|
||||||
|
user_data = builtins.toJSON value.user_data;
|
||||||
lifecycle = {
|
lifecycle = {
|
||||||
ignore_changes = [
|
ignore_changes = [
|
||||||
"ssh_keys"
|
"ssh_keys"
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
{ khscodesLib, inputs }:
|
|
||||||
{ lib, config, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.khscodes.openbao;
|
|
||||||
modules = [
|
|
||||||
./output.nix
|
|
||||||
./vault_mount.nix
|
|
||||||
];
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.khscodes.openbao = {
|
|
||||||
enable = lib.mkEnableOption "Enables the openbao provider";
|
|
||||||
};
|
|
||||||
|
|
||||||
imports = lib.lists.map (m: import m { inherit khscodesLib inputs; }) modules;
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
provider.vault = {
|
|
||||||
address = "https://auth.kaareskovgaard.net";
|
|
||||||
};
|
|
||||||
terraform.required_providers.vault = {
|
|
||||||
source = "hashicorp/vault";
|
|
||||||
version = "5.0.0";
|
|
||||||
};
|
|
||||||
resource.vault_mount = lib.mapAttrs' (
|
|
||||||
name: value: {
|
|
||||||
name = khscodesLib.sanitize-terraform-name name;
|
|
||||||
value = value;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
{ khscodesLib, ... }:
|
|
||||||
{ config, lib, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.khscodes.openbao;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.khscodes.openbao = { };
|
|
||||||
config = {
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
{ khscodesLib, ... }:
|
|
||||||
{ lib, config, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.khscodes.openbao;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.khscodes.openbao = {
|
|
||||||
vault_ssh_secret_backend_ca = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (
|
|
||||||
khscodesLib.mkSubmodule {
|
|
||||||
options = {
|
|
||||||
backend = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Path of the backend mount";
|
|
||||||
};
|
|
||||||
generate_signing_key = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
description = "Generate a signing key on the server";
|
|
||||||
};
|
|
||||||
key_type = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "The type of the signing key to use/generate";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
description = "vault_ssh_secret_backend_ca";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
provider.vault = {
|
|
||||||
address = "https://auth.kaareskovgaard.net";
|
|
||||||
};
|
|
||||||
terraform.required_providers.vault = {
|
|
||||||
source = "hashicorp/vault";
|
|
||||||
version = "5.0.0";
|
|
||||||
};
|
|
||||||
resource.vault_ssh_secret_backend_ca = lib.mapAttrs' (
|
|
||||||
name: value: {
|
|
||||||
name = khscodesLib.sanitize-terraform-name name;
|
|
||||||
value = value;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -85,6 +85,11 @@ let
|
||||||
"1.0.0.1"
|
"1.0.0.1"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
user_data = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = "User data for the instance";
|
||||||
|
};
|
||||||
volume_size = lib.mkOption {
|
volume_size = lib.mkOption {
|
||||||
type = lib.types.int;
|
type = lib.types.int;
|
||||||
description = "Size of the root volume, in gigabytes";
|
description = "Size of the root volume, in gigabytes";
|
||||||
|
@ -432,6 +437,7 @@ in
|
||||||
uuid = "\${ openstack_networking_network_v2.${sanitizedName}.id }";
|
uuid = "\${ openstack_networking_network_v2.${sanitizedName}.id }";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
user_data = value.user_data;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
) cfg.compute_instance;
|
) cfg.compute_instance;
|
||||||
|
|
120
nix/modules/terranix/vault/approle_auth_backend.nix
Normal file
120
nix/modules/terranix/vault/approle_auth_backend.nix
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
{ khscodesLib, ... }:
|
||||||
|
{ lib, config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.khscodes.vault;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.khscodes.vault = {
|
||||||
|
approle_auth_backend_role = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
khscodesLib.mkSubmodule {
|
||||||
|
options = {
|
||||||
|
backend = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Path of the backend";
|
||||||
|
default = "approle";
|
||||||
|
};
|
||||||
|
role_name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Name of the role";
|
||||||
|
};
|
||||||
|
secret_id_ttl = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
description = "TTL for the secret id, in seconds";
|
||||||
|
};
|
||||||
|
secret_id_num_uses = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
description = "Maximum number of uses per secret id";
|
||||||
|
};
|
||||||
|
token_ttl = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
description = "TTL for the tokens issued, in seconds";
|
||||||
|
};
|
||||||
|
token_max_ttl = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
description = "Max TTL for the tokens issued, in seconds";
|
||||||
|
};
|
||||||
|
token_policies = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = "Policies attached to the backend role";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "vault_approle_auth_backend_role";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
description = "Defines an app backend role";
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
approle_auth_backend_role_secret_id = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
khscodesLib.mkSubmodule {
|
||||||
|
options = {
|
||||||
|
backend = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Path of the backend";
|
||||||
|
default = "approle";
|
||||||
|
};
|
||||||
|
role_name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "NThe name of the role to create the SecretID for";
|
||||||
|
};
|
||||||
|
cidr_list = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = "If set, specifies blocks of IP addresses which can perform the login operation using this SecretID";
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
metadata = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf lib.types.str;
|
||||||
|
description = "Metadata associated with tokens issued by this secret";
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
num_uses = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
description = "Number of uses for the secret id";
|
||||||
|
default = 300;
|
||||||
|
};
|
||||||
|
wrapping_ttl = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.int;
|
||||||
|
description = "If set, the SecretID response will be response-wrapped and available for the duration specified. Only a single unwrapping of the token is allowed.";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
with_wrapped_accessor = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
description = "Set to `true` to use the wrapped secret-id accessor as the resource ID. If `false` (default value), a fresh secret ID will be regenerated whenever the wrapping token is expired or invalidated through unwrapping.";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
lifecycle.ignore_changes = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = "Ignores changes to the following properties when rerunning the terraform script";
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "vault_approle_auth_backend_role_secret_id";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
description = "Defines an app backend role secret id";
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
resource.vault_approle_auth_backend_role = lib.mapAttrs' (name: value: {
|
||||||
|
name = khscodesLib.sanitize-terraform-name name;
|
||||||
|
value = value;
|
||||||
|
}) cfg.approle_auth_backend_role;
|
||||||
|
resource.vault_approle_auth_backend_role_secret_id = lib.mapAttrs' (name: value: {
|
||||||
|
name = khscodesLib.sanitize-terraform-name name;
|
||||||
|
value = {
|
||||||
|
inherit (value)
|
||||||
|
backend
|
||||||
|
role_name
|
||||||
|
cidr_list
|
||||||
|
wrapping_ttl
|
||||||
|
num_uses
|
||||||
|
with_wrapped_accessor
|
||||||
|
lifecycle
|
||||||
|
;
|
||||||
|
metadata = if value.metadata != null then builtins.toJSON value.metadata else null;
|
||||||
|
};
|
||||||
|
}) cfg.approle_auth_backend_role_secret_id;
|
||||||
|
};
|
||||||
|
}
|
49
nix/modules/terranix/vault/default.nix
Normal file
49
nix/modules/terranix/vault/default.nix
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{ khscodesLib, inputs }:
|
||||||
|
{ lib, config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.khscodes.vault;
|
||||||
|
modules = [
|
||||||
|
./approle_auth_backend.nix
|
||||||
|
./output.nix
|
||||||
|
./mount.nix
|
||||||
|
./ssh_secret_backend.nix
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.khscodes.vault = {
|
||||||
|
enable = lib.mkEnableOption "Enables the openbao provider";
|
||||||
|
policy = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
khscodesLib.mkSubmodule {
|
||||||
|
options = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Name of the policy";
|
||||||
|
};
|
||||||
|
policy = lib.mkOption {
|
||||||
|
type = lib.types.lines;
|
||||||
|
description = "The policy";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "vault_policy";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
imports = lib.lists.map (m: import m { inherit khscodesLib inputs; }) modules;
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
provider.vault = {
|
||||||
|
address = "https://vault.kaareskovgaard.net";
|
||||||
|
};
|
||||||
|
terraform.required_providers.vault = {
|
||||||
|
source = "hashicorp/vault";
|
||||||
|
version = "5.0.0";
|
||||||
|
};
|
||||||
|
resource.vault_policy = lib.mapAttrs' (name: value: {
|
||||||
|
name = khscodesLib.sanitize-terraform-name name;
|
||||||
|
value = value;
|
||||||
|
}) cfg.policy;
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
{ khscodesLib, ... }:
|
{ khscodesLib, ... }:
|
||||||
{ lib, config, ... }:
|
{ lib, config, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.khscodes.openbao;
|
cfg = config.khscodes.vault;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.khscodes.openbao = {
|
options.khscodes.vault = {
|
||||||
vault_mount = lib.mkOption {
|
mount = lib.mkOption {
|
||||||
type = lib.types.attrsOf (
|
type = lib.types.attrsOf (
|
||||||
khscodesLib.mkSubmodule {
|
khscodesLib.mkSubmodule {
|
||||||
options = {
|
options = {
|
||||||
|
@ -32,21 +32,14 @@ in
|
||||||
description = "vault_mount";
|
description = "vault_mount";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
description = "Defines a vault mount";
|
||||||
|
default = { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
provider.vault = {
|
resource.vault_mount = lib.mapAttrs' (name: value: {
|
||||||
address = "https://auth.kaareskovgaard.net";
|
name = khscodesLib.sanitize-terraform-name name;
|
||||||
};
|
value = value;
|
||||||
terraform.required_providers.vault = {
|
}) cfg.mount;
|
||||||
source = "hashicorp/vault";
|
|
||||||
version = "5.0.0";
|
|
||||||
};
|
|
||||||
resource.vault_mount = lib.mapAttrs' (
|
|
||||||
name: value: {
|
|
||||||
name = khscodesLib.sanitize-terraform-name name;
|
|
||||||
value = value;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
35
nix/modules/terranix/vault/output.nix
Normal file
35
nix/modules/terranix/vault/output.nix
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{ khscodesLib, ... }:
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.khscodes.vault;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.khscodes.vault = {
|
||||||
|
output = {
|
||||||
|
approle_auth_backend_role = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
khscodesLib.mkSubmodule {
|
||||||
|
options = {
|
||||||
|
role_name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "The name of the role. Can be used instead of hardcoding the role, to create a dependency in OpenTofu";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "vault_approle_auth_backend_role output";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
khscodes.vault.output.approle_auth_backend_role = lib.mapAttrs (
|
||||||
|
name: value:
|
||||||
|
let
|
||||||
|
sanitizedName = khscodesLib.sanitize-terraform-name name;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
role_name = "\${ vault_approle_auth_backend_role.${sanitizedName}.role_name }";
|
||||||
|
}
|
||||||
|
) cfg.approle_auth_backend_role;
|
||||||
|
};
|
||||||
|
}
|
129
nix/modules/terranix/vault/ssh_secret_backend.nix
Normal file
129
nix/modules/terranix/vault/ssh_secret_backend.nix
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
{ khscodesLib, ... }:
|
||||||
|
{ lib, config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.khscodes.vault;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.khscodes.vault = {
|
||||||
|
ssh_secret_backend_role = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
khscodesLib.mkSubmodule {
|
||||||
|
options = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Specifies the name of the role to create.";
|
||||||
|
};
|
||||||
|
backend = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "The path where the SSH secret backend is mounted.";
|
||||||
|
};
|
||||||
|
key_type = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"otp"
|
||||||
|
"dynamic"
|
||||||
|
"ca"
|
||||||
|
];
|
||||||
|
description = "Specifies the type of credentials generated by this role.";
|
||||||
|
};
|
||||||
|
allow_bare_domains = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.bool);
|
||||||
|
description = "Specifies if host certificates that are requested are allowed to use the base domains listed in allowed_domains.";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
allow_host_certificates = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.bool);
|
||||||
|
description = "Specifies if certificates are allowed to be signed for use as a 'host'.";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
allow_subdomains = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.bool);
|
||||||
|
description = "Specifies if host certificates that are requested are allowed to be subdomains of those listed in allowed_domains.";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
allow_user_certificates = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.bool);
|
||||||
|
description = "Specifies if certificates are allowed to be signed for use as a 'user'.";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
allowed_critical_options = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = "Specifies a list of critical options that certificates can have when signed.";
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
allowed_users = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = "Specifies a list of usernames that are to be allowed, only if certain usernames are to be allowed.";
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
default_user = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
description = "Specifies the default username for which a credential will be generated.";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
allowed_domains = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
description = "The list of domains for which a client can request a host certificate";
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
allowed_user_key_config = lib.mkOption {
|
||||||
|
type = lib.types.listOf (
|
||||||
|
khscodesLib.mkSubmodule {
|
||||||
|
options = {
|
||||||
|
type = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"rsa"
|
||||||
|
"ecdsa"
|
||||||
|
"ec"
|
||||||
|
"dsa"
|
||||||
|
"ed25519"
|
||||||
|
"ssh-rsa"
|
||||||
|
"ssh-dss"
|
||||||
|
"ssh-ed25519"
|
||||||
|
"ecdsa-sha2-nistp256"
|
||||||
|
"ecdsa-sha2-nistp384"
|
||||||
|
"ecdsa-sha2-nistp521"
|
||||||
|
];
|
||||||
|
description = "The SSH public key type.";
|
||||||
|
};
|
||||||
|
lengths = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.int;
|
||||||
|
description = "A list of allowed key lengths as integers. For key types that do not support setting the length a value of [0] should be used.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "allowed_user_key_config";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
description = "Set of configuration blocks to define allowed user key configuration, like key type and their lengths.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = "vault_ssh_secret_backend_role";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
description = "Defines an ssh secret backend";
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
resource.vault_ssh_secret_backend_role = lib.mapAttrs' (name: value: {
|
||||||
|
name = khscodesLib.sanitize-terraform-name name;
|
||||||
|
value = {
|
||||||
|
inherit (value)
|
||||||
|
name
|
||||||
|
backend
|
||||||
|
key_type
|
||||||
|
allow_bare_domains
|
||||||
|
allow_host_certificates
|
||||||
|
allow_subdomains
|
||||||
|
allow_user_certificates
|
||||||
|
default_user
|
||||||
|
allowed_user_key_config
|
||||||
|
;
|
||||||
|
allowed_critical_options = lib.strings.concatStringsSep "," (
|
||||||
|
lib.lists.unique value.allowed_critical_options
|
||||||
|
);
|
||||||
|
allowed_domains = lib.strings.concatStringsSep "," (lib.lists.unique value.allowed_domains);
|
||||||
|
allowed_users = lib.strings.concatStringsSep "," (lib.lists.unique value.allowed_users);
|
||||||
|
};
|
||||||
|
}) cfg.ssh_secret_backend_role;
|
||||||
|
};
|
||||||
|
}
|
|
@ -4,4 +4,5 @@ pkgs.opentofu.withPlugins (p: [
|
||||||
pkgs.khscodes.terraform-provider-cloudflare
|
pkgs.khscodes.terraform-provider-cloudflare
|
||||||
pkgs.khscodes.terraform-provider-hcloud
|
pkgs.khscodes.terraform-provider-hcloud
|
||||||
pkgs.khscodes.terraform-provider-openstack
|
pkgs.khscodes.terraform-provider-openstack
|
||||||
|
pkgs.khscodes.terraform-provider-vault
|
||||||
])
|
])
|
||||||
|
|
10
nix/packages/terraform-provider-vault/default.nix
Normal file
10
nix/packages/terraform-provider-vault/default.nix
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{ pkgs }:
|
||||||
|
pkgs.terraform-providers.mkProvider {
|
||||||
|
hash = "sha256-Vqnmw69fktBQhSkj/W0legJ4sHOQP9Moqqi6Z5qYFy4=";
|
||||||
|
homepage = "https://registry.terraform.io/providers/hashicorp/vault";
|
||||||
|
owner = "hashicorp";
|
||||||
|
repo = "terraform-provider-vault";
|
||||||
|
rev = "v5.0.0";
|
||||||
|
spdx = "MPL-2.0";
|
||||||
|
vendorHash = "sha256-6gWw4ypQZWPX7VC9cZxHiU/KhTYEdMTZ276B9neGAiI=";
|
||||||
|
}
|
|
@ -8,7 +8,6 @@
|
||||||
enable = true;
|
enable = true;
|
||||||
mapRdns = true;
|
mapRdns = true;
|
||||||
server_type = "cax11";
|
server_type = "cax11";
|
||||||
secretsSource = "bitwarden";
|
|
||||||
};
|
};
|
||||||
khscodes.networking.fqdn = "khs.codes";
|
khscodes.networking.fqdn = "khs.codes";
|
||||||
system.stateVersion = "25.05";
|
system.stateVersion = "25.05";
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
{
|
{
|
||||||
inputs,
|
inputs,
|
||||||
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
imports = [ "${inputs.self}/nix/profiles/nixos/khs-desktop.nix" ];
|
imports = [ "${inputs.self}/nix/profiles/nixos/khs-desktop.nix" ];
|
||||||
khscodes.networking.fqdn = "desktop.kaareskovgaard.net";
|
khscodes.networking.fqdn = "desktop.kaareskovgaard.net";
|
||||||
|
disko = lib.khscodes.disko-root-bios {
|
||||||
|
device = "/dev/sda";
|
||||||
|
diskName = "nixos";
|
||||||
|
};
|
||||||
system.stateVersion = "25.05";
|
system.stateVersion = "25.05";
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,10 @@
|
||||||
khscodes.infrastructure.khs-openstack-instance = {
|
khscodes.infrastructure.khs-openstack-instance = {
|
||||||
enable = true;
|
enable = true;
|
||||||
flavor = "m.medium";
|
flavor = "m.medium";
|
||||||
secretsSource = "vault";
|
|
||||||
};
|
};
|
||||||
snowfallorg.users.khs.admin = true;
|
snowfallorg.users.khs.admin = true;
|
||||||
users.users.khs = {
|
users.users.khs = {
|
||||||
initialPassword = "changeMe";
|
initialPassword = "changeme";
|
||||||
openssh.authorizedKeys.keys = [
|
openssh.authorizedKeys.keys = [
|
||||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqY0FHnWFKfLG2yfgr4qka5sR9CK+EMAhzlHUkaQyWHTKD+G0/vC/fNPyL1VV3Dxc/ajxGuPzVE+mBMoyxazL3EtuCDOVvHJ5CR+MUSEckg/DDwcGHqy6rC8BvVVpTAVL04ByQdwFnpE1qNSBaQLkxaFVdtriGKkgMkc7+UNeYX/bv7yn+APqfP1a3xr6wdkSSdO8x4N2jsSygOIMx10hLyCV4Ueu7Kp8Ww4rGY8j5o7lKJhbgfItBfSOuQHdppHVF/GKYRhdnK6Y2fZVYbhq4KipUtclbZ6O/VYd8/sOO98+LMm7cOX+K35PQjUpYgcoNy5+Sw3CNS/NHn4JvOtTaUEYP7fK6c9LhMULOO3T7Cm6TMdiFjUKHkyG+s2Mu/LXJJoilw571zwuh6chkeitW8+Ht7k0aPV96kNEvTdoXwLhBifVEaChlAsLAzSUjUq+YYCiXVk0VIXCZQWKj8LoVNTmaqDksWwbcT64fw/FpVC0N18WHbKcFUEIW/O4spJMa30CQwf9FeqpoWoaF1oRClCSDPvX0AauCu0JcmRinz1/JmlXljnXWbSfm20/V+WyvktlI0wTD0cdpNuSasT9vS77YfJ8nutcWWZKSkCj4R4uHeCNpDTX5YXzapy7FxpM9ANCXLIvoGX7Yafba2Po+er7SSsUIY1AsnBBr8ZoDVw=="
|
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqY0FHnWFKfLG2yfgr4qka5sR9CK+EMAhzlHUkaQyWHTKD+G0/vC/fNPyL1VV3Dxc/ajxGuPzVE+mBMoyxazL3EtuCDOVvHJ5CR+MUSEckg/DDwcGHqy6rC8BvVVpTAVL04ByQdwFnpE1qNSBaQLkxaFVdtriGKkgMkc7+UNeYX/bv7yn+APqfP1a3xr6wdkSSdO8x4N2jsSygOIMx10hLyCV4Ueu7Kp8Ww4rGY8j5o7lKJhbgfItBfSOuQHdppHVF/GKYRhdnK6Y2fZVYbhq4KipUtclbZ6O/VYd8/sOO98+LMm7cOX+K35PQjUpYgcoNy5+Sw3CNS/NHn4JvOtTaUEYP7fK6c9LhMULOO3T7Cm6TMdiFjUKHkyG+s2Mu/LXJJoilw571zwuh6chkeitW8+Ht7k0aPV96kNEvTdoXwLhBifVEaChlAsLAzSUjUq+YYCiXVk0VIXCZQWKj8LoVNTmaqDksWwbcT64fw/FpVC0N18WHbKcFUEIW/O4spJMa30CQwf9FeqpoWoaF1oRClCSDPvX0AauCu0JcmRinz1/JmlXljnXWbSfm20/V+WyvktlI0wTD0cdpNuSasT9vS77YfJ8nutcWWZKSkCj4R4uHeCNpDTX5YXzapy7FxpM9ANCXLIvoGX7Yafba2Po+er7SSsUIY1AsnBBr8ZoDVw=="
|
||||||
];
|
];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue