Move monitoring.kaareskovgaard.net to new openbao setup
Some checks failed
/ dev-shell (push) Successful in 1m52s
/ rust-packages (push) Successful in 4m3s
/ check (push) Failing after 4m59s
/ terraform-providers (push) Successful in 11m1s
/ systems (push) Successful in 31m7s

This commit is contained in:
Kaare Hoff Skovgaard 2025-07-18 00:18:26 +02:00
parent 905b1096ac
commit 1f7139f793
Signed by: khs
GPG key ID: C7D890804F01E9F0
20 changed files with 367 additions and 177 deletions

View file

@ -106,6 +106,7 @@
allowUnfreePackages = [ allowUnfreePackages = [
"spotify" "spotify"
"google-chrome" "google-chrome"
"terraform"
]; ];
in in
(inputs.flake-base.lib.mkFlake { (inputs.flake-base.lib.mkFlake {

View file

@ -48,5 +48,7 @@ pkgs.nixosTest {
machine.wait_for_unit("hetzner-static-ip.service") machine.wait_for_unit("hetzner-static-ip.service")
ipv6 = machine.succeed("ip addr") ipv6 = machine.succeed("ip addr")
assert "dead:beef:cafe::1" in ipv6 assert "dead:beef:cafe::1" in ipv6
# Ensure that rerunning the service works, even when the ip address is already set
machine.succeed("systemctl restart hetzner-static-ip.service")
''; '';
} }

View file

@ -5,7 +5,7 @@ network-config:
subnets: subnets:
- ipv4: true - ipv4: true
type: dhcp type: dhcp
- address: dead:beef:cafe::1/64 - address: dead:beef:cafe::1/128
gateway: fe80::1 gateway: fe80::1
ipv6: true ipv6: true
type: static type: static

View file

@ -96,7 +96,6 @@ in
"hcloud" "hcloud"
"cloudflare" "cloudflare"
"vault" "vault"
"authentik"
] ]
); );
description = "Needed endpoints to be used during provisioning"; description = "Needed endpoints to be used during provisioning";
@ -120,7 +119,6 @@ in
"hcloud" "hcloud"
"cloudflare" "cloudflare"
"vault" "vault"
"authentik"
] ]
); );
description = "Needed endpoints to be used during provisioning"; description = "Needed endpoints to be used during provisioning";

View file

@ -98,8 +98,7 @@ in
# Not hardcoding the role name here, as reading it like this will create a dependency # Not hardcoding the role name here, as reading it like this will create a dependency
# on the role being created first, which is needed. # on the role being created first, which is needed.
role_name = config.khscodes.vault.output.approle_auth_backend_role.${cfg.role_name}.role_name; 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 = 30 * 60;
wrapping_ttl = 5 * 60;
num_uses = 0; num_uses = 0;
# This should simply mean that we never attempt to recreate the secret id, as we don't want a rerun of the # This should simply mean that we never attempt to recreate the secret id, as we don't want a rerun of the

View file

@ -18,7 +18,7 @@ pkgs.writeShellApplication {
>&2 echo "Must run as root" >&2 echo "Must run as root"
exit 2 exit 2
fi fi
secret_id="$(bao unwrap -field=secret_id "$wrapped_secret_id")" secret_id="$(BAO_ADDR=https://secrets.kaareskovgaard.net bao unwrap -field=secret_id "$wrapped_secret_id")"
mkdir -p /var/lib/vault-agent mkdir -p /var/lib/vault-agent
touch /var/lib/vault-agent/role-id touch /var/lib/vault-agent/role-id
touch /var/lib/vault-agent/secret-id touch /var/lib/vault-agent/secret-id

View file

@ -0,0 +1,19 @@
{ pkgs, ... }:
pkgs.writeShellApplication {
name = "instance-new-secret-id";
runtimeInputs = [
pkgs.openbao
pkgs.uutils-coreutils-noprefix
];
text = ''
instance="''${1:-}"
if [[ "$instance" == "" ]]; then
>&2 echo "Usage: instance-new-secret-id <instance>"
exit 1
fi
secret_id="$(bao write -field=wrapping_token -force -wrap-ttl=1m "auth/approle/role/$instance/secret-id")"
role_id="$(bao read -field=role_id "auth/approle/role/$instance/role-id")"
ssh -t "$instance" -- sudo bao-import-secret "$role_id" "$secret_id"
'';
}

View file

@ -1,12 +1,12 @@
{ pkgs, ... }: { pkgs, lib, ... }:
let let
opentofu = pkgs.khscodes.opentofu; opentofu = pkgs.khscodes.opentofu;
opentofuExe = lib.getExe opentofu;
in in
pkgs.writeShellApplication { pkgs.writeShellApplication {
name = "instance-opentofu"; name = "instance-opentofu";
runtimeInputs = [ runtimeInputs = [
pkgs.uutils-coreutils-noprefix pkgs.uutils-coreutils-noprefix
opentofu
]; ];
text = '' text = ''
fqdn="$1" fqdn="$1"
@ -16,12 +16,12 @@ pkgs.writeShellApplication {
mkdir -p "$dir" mkdir -p "$dir"
cat "''${config}" > "$dir/config.tf.json" cat "''${config}" > "$dir/config.tf.json"
tofu -chdir="$dir" init > /dev/null ${opentofuExe} -chdir="$dir" init > /dev/null
if [[ "$cmd" == "apply" ]]; then if [[ "$cmd" == "apply" ]]; then
tofu -chdir="$dir" "$cmd" >&2 ${opentofuExe} -chdir="$dir" "$cmd" >&2
tofu -chdir="$dir" output -json ${opentofuExe} -chdir="$dir" output -json
else else
tofu -chdir="$dir" "$cmd" ${opentofuExe} -chdir="$dir" "$cmd"
fi fi
''; '';
} }

View file

@ -1,4 +1,6 @@
{ pkgs }: { pkgs }:
# I really want to use opentofu here, but waiting for write only values and ephemeral support.
# Should land in 1.11.0 of opentofu
pkgs.opentofu.withPlugins (p: [ pkgs.opentofu.withPlugins (p: [
pkgs.khscodes.terraform-provider-unifi pkgs.khscodes.terraform-provider-unifi
pkgs.khscodes.terraform-provider-cloudflare pkgs.khscodes.terraform-provider-cloudflare
@ -6,4 +8,5 @@ pkgs.opentofu.withPlugins (p: [
pkgs.khscodes.terraform-provider-openstack pkgs.khscodes.terraform-provider-openstack
pkgs.khscodes.terraform-provider-vault pkgs.khscodes.terraform-provider-vault
pkgs.khscodes.terraform-provider-random pkgs.khscodes.terraform-provider-random
pkgs.khscodes.terraform-provider-tls
]) ])

View file

@ -0,0 +1,10 @@
{ pkgs }:
pkgs.terraform-providers.mkProvider {
hash = "sha256-t/nUt0deyckP8opNiPZc5rbC1SleZwkrFXuQFw47sqA=";
homepage = "https://registry.terraform.io/providers/hashicorp/tls";
owner = "hashicorp";
repo = "terraform-provider-tls";
rev = "v4.1.0";
spdx = "MPL-2.0";
vendorHash = "sha256-tYvQURTrFtr+rgSMGq2zi/5p5jJVGIse7+hj95gz68U=";
}

View file

@ -2,6 +2,7 @@
{ {
imports = [ ./khs-base.nix ]; imports = [ ./khs-base.nix ];
config = { config = {
environment.systemPackages = [ pkgs.khscodes.bao-import-secret ];
khscodes = { khscodes = {
services.openssh.enable = true; services.openssh.enable = true;
machine.type = "server"; machine.type = "server";
@ -9,6 +10,7 @@
infrastructure = { infrastructure = {
vault-server-approle.enable = lib.mkDefault true; vault-server-approle.enable = lib.mkDefault true;
vault-loki-sender.enable = lib.mkDefault true; vault-loki-sender.enable = lib.mkDefault true;
vault-prometheus-sender.enable = lib.mkDefault true;
}; };
}; };
stylix = { stylix = {

View file

@ -0,0 +1,34 @@
{
khscodes.services.vault-agent.templates = [
{
contents = ''
{{- with secret "forgejo/data/mailserver/dkim" -}}
{{ .Data.data.dkim_private_key }}
{{- end -}}
'';
destination = "/var/lib/vault-agent/mailserver/dkim/private.key";
perms = "0600";
owner = "rspamd";
group = "rspamd";
restartUnits = [
"rspamd.service"
"postfix.service"
];
}
{
contents = ''
{{- with secret "forgejo/data/mailserver/forgejo-user" -}}
{{ .Data.data.hashed_password }}
{{- end -}}
'';
destination = "/var/lib/vault-agent/mailserver/users/forgejo.passwd.hash";
perms = "0600";
owner = "rspamd";
group = "rspamd";
restartUnits = [
"rspamd.service"
"postfix.service"
];
}
];
}

View file

@ -1,9 +1,12 @@
{ {
inputs, inputs,
lib, lib,
pkgs,
... ...
}: }:
let
# Change this if recreating the server from scratch. See README for this instance.
bootstrapping = false;
in
{ {
options.khscodes."security.kaareskovgaard.net" = { options.khscodes."security.kaareskovgaard.net" = {
bootstrap = { bootstrap = {
@ -17,8 +20,8 @@
./post ./post
]; ];
config = { config = {
environment.systemPackages = [ pkgs.khscodes.bao-import-secret ];
khscodes.services.nginx.enable = true; khscodes.services.nginx.enable = true;
khscodes."security.kaareskovgaard.net".bootstrap.enable = bootstrapping;
khscodes.infrastructure.hetzner-instance = { khscodes.infrastructure.hetzner-instance = {
enable = true; enable = true;
server_type = "cax11"; server_type = "cax11";

View file

@ -8,11 +8,12 @@ let
domain = "login.kaareskovgaard.net"; domain = "login.kaareskovgaard.net";
bootstrapping = config.khscodes."security.kaareskovgaard.net".bootstrap.enable; bootstrapping = config.khscodes."security.kaareskovgaard.net".bootstrap.enable;
openbaoAppBasicSecretFile = "/var/lib/vault-agent/kanidm/openbao_basic_secret"; openbaoAppBasicSecretFile = "/var/lib/vault-agent/kanidm/openbao_basic_secret";
openbaoCliAppBasicSecretFile = "/var/lib/vault-agent/kanidm/openbao_cli_basic_secret";
monitoringAppBasicSecretFile = "/var/lib/vault-agent/kanidm/monitoring_basic_secret";
openbaoDomain = config.khscodes.infrastructure.openbao.domain; openbaoDomain = config.khscodes.infrastructure.openbao.domain;
openbaoAllowedRedirectUrls = [ openbaoAllowedRedirectUrls = [
"https://${openbaoDomain}/ui/vault/auth/oidc/oidc/callback" "https://${openbaoDomain}/ui/vault/auth/kanidm/oidc/callback"
"https://${openbaoDomain}/oidc/callback" "https://${openbaoDomain}/kanidm/callback"
"http://localhost:8250/oidc/callback"
]; ];
kanidm-reset-password = pkgs.writeShellApplication { kanidm-reset-password = pkgs.writeShellApplication {
name = "kanidm-reset-password"; name = "kanidm-reset-password";
@ -75,7 +76,7 @@ in
present = true; present = true;
public = false; public = false;
preferShortUsername = true; preferShortUsername = true;
basicSecretFile = openbaoAppBasicSecretFile; basicSecretFile = lib.mkIf (!bootstrapping) openbaoAppBasicSecretFile;
originUrl = openbaoAllowedRedirectUrls; originUrl = openbaoAllowedRedirectUrls;
originLanding = "https://${openbaoDomain}"; originLanding = "https://${openbaoDomain}";
displayName = "OpenBAO"; displayName = "OpenBAO";
@ -90,13 +91,52 @@ in
joinType = "array"; joinType = "array";
valuesByGroup = { valuesByGroup = {
"openbao_admin" = [ "openbao_admin" = [
"openbao_reader"
"openbao_writer" "openbao_writer"
"openbao_sudo" "openbao_cli_writer"
]; ];
}; };
}; };
}; };
openbao-cli = {
present = true;
public = false;
preferShortUsername = true;
basicSecretFile = lib.mkIf (!bootstrapping) openbaoCliAppBasicSecretFile;
originUrl = [ "http://localhost:8250/oidc/callback" ];
originLanding = "http://localhost:8250";
displayName = "OpenBAO CLI";
scopeMaps = {
"openbao_admin" = [
"profile"
"email"
"openid"
];
};
claimMaps.groups = {
joinType = "array";
valuesByGroup = {
"openbao_admin" = [
"openbao_writer"
];
};
};
};
monitoring = {
present = true;
public = false;
preferShortUsername = true;
basicSecretFile = lib.mkIf (!bootstrapping) monitoringAppBasicSecretFile;
originUrl = [ "https://monitoring.kaareskovgaard.net/login/generic_oauth" ];
originLanding = "http://monitoring.kaareskovgaard.net";
displayName = "Monitoring";
scopeMaps = {
"openbao_admin" = [
"profile"
"email"
"openid"
];
};
};
}; };
}; };
}; };
@ -150,6 +190,34 @@ in
{ "basic_secret": "''${ resource.random_password.openbao_secret.result }" } { "basic_secret": "''${ resource.random_password.openbao_secret.result }" }
''; '';
}; };
resource.random_password.openbao_cli_secret = {
length = 48;
numeric = true;
lower = true;
upper = true;
special = false;
};
resource.vault_kv_secret_v2.openbao_cli_secret = {
mount = config.khscodes.vault.output.mount.kanidm.path;
name = "apps/openbao_cli";
data_json = ''
{ "basic_secret": "''${ resource.random_password.openbao_cli_secret.result }" }
'';
};
resource.random_password.monitoring = {
length = 48;
numeric = true;
lower = true;
upper = true;
special = false;
};
resource.vault_kv_secret_v2.monitoring_secret = {
mount = config.khscodes.vault.output.mount.kanidm.path;
name = "apps/monitoring";
data_json = ''
{ "basic_secret": "''${ resource.random_password.monitoring.result }" }
'';
};
} }
) )
# Sets up OIDC authentication within OpenBAO. # Sets up OIDC authentication within OpenBAO.
@ -160,13 +228,38 @@ in
{ } { }
else else
{ {
resource.vault_jwt_auth_backend.oidc = { resource.vault_jwt_auth_backend.kanidm_cli = {
description = "Kanidm auth backend"; description = "Kanidm cli auth backend";
path = "oidc"; path = "oidc";
type = "oidc"; type = "oidc";
oidc_discovery_url = "https://${domain}/oauth2/openid/openbao-cli";
oidc_client_id = "openbao-cli";
oidc_client_secret = "\${ resource.random_password.openbao_cli_secret.result }";
default_role = "kanidm_cli_writer";
jwt_supported_algs = [
"ES256"
];
tune = [
{
listing_visibility = "hidden";
default_lease_ttl = "2h";
max_lease_ttl = "2h";
token_type = "default-service";
passthrough_request_headers = [ ];
allowed_response_headers = [ ];
audit_non_hmac_request_keys = [ ];
audit_non_hmac_response_keys = [ ];
}
];
};
resource.vault_jwt_auth_backend.kanidm = {
description = "Kanidm auth backend";
path = "kanidm";
type = "oidc";
oidc_discovery_url = "https://${domain}/oauth2/openid/openbao"; oidc_discovery_url = "https://${domain}/oauth2/openid/openbao";
oidc_client_id = "openbao"; oidc_client_id = "openbao";
oidc_client_secret = "\${ resource.random_password.openbao_secret.result }"; oidc_client_secret = "\${ resource.random_password.openbao_secret.result }";
default_role = "kanidm_writer";
jwt_supported_algs = [ jwt_supported_algs = [
"ES256" "ES256"
]; ];
@ -184,13 +277,13 @@ in
]; ];
}; };
resource.vault_jwt_auth_backend_role.oidc_reader = { resource.vault_jwt_auth_backend_role.kanidm_cli_writer = {
backend = "\${ resource.vault_jwt_auth_backend.oidc.path }"; backend = "\${ resource.vault_jwt_auth_backend.kanidm_cli.path }";
role_name = "reader"; role_name = "kanidm_cli_writer";
bound_audiences = [ "openbao" ]; bound_audiences = [ "openbao-cli" ];
allowed_redirect_uris = openbaoAllowedRedirectUrls; allowed_redirect_uris = [ "http://localhost:8250/oidc/callback" ];
user_claim = "sub"; user_claim = "sub";
token_policies = [ "reader" ]; token_policies = [ "writer" ];
groups_claim = "groups"; groups_claim = "groups";
oidc_scopes = [ oidc_scopes = [
"openid" "openid"
@ -199,9 +292,9 @@ in
]; ];
}; };
resource.vault_jwt_auth_backend_role.oidc_writer = { resource.vault_jwt_auth_backend_role.kanidm_writer = {
backend = "\${ resource.vault_jwt_auth_backend.oidc.path }"; backend = "\${ resource.vault_jwt_auth_backend.kanidm.path }";
role_name = "writer"; role_name = "kanidm_writer";
bound_audiences = [ "openbao" ]; bound_audiences = [ "openbao" ];
allowed_redirect_uris = openbaoAllowedRedirectUrls; allowed_redirect_uris = openbaoAllowedRedirectUrls;
user_claim = "sub"; user_claim = "sub";
@ -214,65 +307,28 @@ in
]; ];
}; };
resource.vault_jwt_auth_backend_role.oidc_sudo = { resource.vault_identity_group.kanidm_writer = {
backend = "\${ resource.vault_jwt_auth_backend.oidc.path }"; name = "kanidm_writer";
role_name = "sudo";
bound_audiences = [ "openbao" ];
allowed_redirect_uris = openbaoAllowedRedirectUrls;
user_claim = "sub";
token_policies = [ "sudo" ];
groups_claim = "groups";
oidc_scopes = [
"openid"
"profile"
"email"
];
};
resource.vault_identity_group.reader = {
name = "reader";
policies = [ "reader" ];
type = "external";
};
resource.vault_identity_group.writer = {
name = "writer";
policies = [ "writer" ]; policies = [ "writer" ];
type = "external"; type = "external";
}; };
resource.vault_identity_group.sudo = { resource.vault_identity_group.kanidm_cli_writer = {
name = "sudo"; name = "kanidm_cli_writer";
policies = [ "sudo" ]; policies = [ "writer" ];
type = "external"; type = "external";
}; };
resource.vault_identity_group_alias.reader = { resource.vault_identity_group_alias.oidc_writer = {
name = "openbao_reader"; name = "openbao_cli_writer";
mount_accessor = "\${ vault_jwt_auth_backend.oidc.accessor }"; mount_accessor = "\${ vault_jwt_auth_backend.kanidm_cli.accessor }";
canonical_id = "\${ vault_identity_group.reader.id }"; canonical_id = "\${ vault_identity_group.kanidm_cli_writer.id }";
}; };
resource.vault_identity_group_alias.writer = { resource.vault_identity_group_alias.kanidm_writer = {
name = "openbao_writer"; name = "openbao_writer";
mount_accessor = "\${ vault_jwt_auth_backend.oidc.accessor }"; mount_accessor = "\${ vault_jwt_auth_backend.kanidm.accessor }";
canonical_id = "\${ vault_identity_group.writer.id }"; canonical_id = "\${ vault_identity_group.kanidm_writer.id }";
};
resource.vault_identity_group_alias.sudo = {
name = "openbao_sudo";
mount_accessor = "\${ vault_jwt_auth_backend.oidc.accessor }";
canonical_id = "\${ vault_identity_group.writer.id }";
};
resource.vault_policy.reader = {
name = "reader";
policy = ''
path "*" {
capabilities = ["read", "list"]
}
'';
}; };
resource.vault_policy.writer = { resource.vault_policy.writer = {
@ -284,16 +340,6 @@ in
} }
''; '';
}; };
resource.vault_policy.sudo = {
name = "sudo";
policy = ''
path "auth/token/create" {
capabilities = ["create", "update", "sudo"]
}
'';
};
} }
) )
]; ];
@ -311,6 +357,30 @@ in
group = "kanidm"; group = "kanidm";
reloadOrRestartUnits = [ "kanidm.service" ]; reloadOrRestartUnits = [ "kanidm.service" ];
} }
{
contents = ''
{{- with secret "kanidm/data/apps/openbao_cli" -}}
{{ .Data.data.basic_secret }}
{{- end -}}
'';
destination = openbaoCliAppBasicSecretFile;
perms = "0600";
owner = "kanidm";
group = "kanidm";
reloadOrRestartUnits = [ "kanidm.service" ];
}
{
contents = ''
{{- with secret "kanidm/data/apps/monitoring" -}}
{{ .Data.data.basic_secret }}
{{- end -}}
'';
destination = monitoringAppBasicSecretFile;
perms = "0600";
owner = "kanidm";
group = "kanidm";
reloadOrRestartUnits = [ "kanidm.service" ];
}
]; ];
security.acme.certs.${domain}.reloadServices = [ "kanidm.service" ]; security.acme.certs.${domain}.reloadServices = [ "kanidm.service" ];

View file

@ -11,12 +11,13 @@ in
{ {
imports = [ imports = [
"${inputs.self}/nix/profiles/nixos/khs-openstack-server.nix" "${inputs.self}/nix/profiles/nixos/khs-openstack-server.nix"
./vault_loki.nix
./vault_prometheus.nix
]; ];
services.grafana = { services.grafana = {
enable = true; enable = true;
settings = { settings = {
security = {
disable_initial_admin_creation = true;
};
server = { server = {
http_addr = "127.0.0.1"; http_addr = "127.0.0.1";
http_port = 3000; http_port = 3000;
@ -24,6 +25,32 @@ in
root_url = "https://monitoring.kaareskovgaard.net"; root_url = "https://monitoring.kaareskovgaard.net";
serve_from_sub_path = false; serve_from_sub_path = false;
}; };
"auth" = {
disable_login_form = true;
};
"auth.basic" = {
enabled = false;
};
"auth.generic_oauth" = {
enabled = true;
allow_sign_up = true;
auto_login = false;
team_ids = null;
allowed_organizations = null;
name = "Kanidm";
auth_url = "https://login.kaareskovgaard.net/ui/oauth2";
token_url = "https://login.kaareskovgaard.net/oauth2/token";
api_url = "https://login.kaareskovgaard.net/oauth2/openid/monitoring/userinfo";
client_id = "monitoring";
client_secret = "$__file{/var/lib/vault-agent/grafana/kanidm_client_secret}";
scopes = "openid profile email";
use_pkce = true;
skip_org_role_sync = false;
allow_assign_grafana_admin = true;
org_attribute_path = "['Main org.']";
org_mapping = "*:*:Admin";
role_attribute_path = "'GrafanaAdmin'";
};
}; };
provision = { provision = {
enable = true; enable = true;
@ -167,7 +194,24 @@ in
perms = "0644"; perms = "0644";
reloadOrRestartUnits = [ "nginx.service" ]; reloadOrRestartUnits = [ "nginx.service" ];
} }
{
contents = ''
{{- with secret "kanidm/data/apps/monitoring" -}}
{{ .Data.data.basic_secret }}
{{- end -}}
'';
destination = "/var/lib/vault-agent/grafana/kanidm_client_secret";
owner = "grafana";
group = "grafana";
perms = "0600";
reloadOrRestartUnits = [ "grafana.service" ];
}
]; ];
infrastructure.vault-server-approle.policy = {
"kanidm/data/apps/monitoring" = {
capabilities = [ "read" ];
};
};
}; };
snowfallorg.users.khs.admin = true; snowfallorg.users.khs.admin = true;
users.users.khs = { users.users.khs = {

View file

@ -1,28 +0,0 @@
# This should go into the setup of the vault server itself, as the vault server also needs stuff that depends on this.
{
khscodes.infrastructure.vault-loki-sender = {
enable = true;
# This is quite ugly, but should get the job done. Sadly I cannot reference the output from here.
terranixBackendName = "\${ vault_mount.loki-mtls.path }";
};
khscodes.infrastructure.provisioning.pre.modules = [
(
{ config, ... }:
{
khscodes.vault.enable = true;
khscodes.vault.mount.loki-mtls = {
type = "pki";
path = "loki-mtls";
max_lease_ttl_seconds = 10 * 365 * 24 * 60 * 60;
default_lease_ttl_seconds = 60 * 60;
};
khscodes.vault.pki_secret_backend_root_cert.loki-mtls = {
backend = config.khscodes.vault.output.mount.loki-mtls.path;
type = "internal";
common_name = "loki.kaareskovgaard.net";
issuer_name = "loki-mtls-root-ca";
};
}
)
];
}

View file

@ -1,28 +0,0 @@
# This should go into the setup of the vault server itself, as the vault server also needs stuff that depends on this.
{
khscodes.infrastructure.vault-prometheus-sender = {
enable = true;
# This is quite ugly, but should get the job done. Sadly I cannot reference the output from here.
terranixBackendName = "\${ vault_mount.prometheus-mtls.path }";
};
khscodes.infrastructure.provisioning.pre.modules = [
(
{ config, ... }:
{
khscodes.vault.enable = true;
khscodes.vault.mount.prometheus-mtls = {
type = "pki";
path = "prometheus-mtls";
max_lease_ttl_seconds = 10 * 365 * 24 * 60 * 60;
default_lease_ttl_seconds = 60 * 60;
};
khscodes.vault.pki_secret_backend_root_cert.prometheus-mtls = {
backend = config.khscodes.vault.output.mount.prometheus-mtls.path;
type = "internal";
common_name = "prometheus.kaareskovgaard.net";
issuer_name = "prometheus-mtls-root-ca";
};
}
)
];
}

View file

@ -0,0 +1,68 @@
use std::collections::BTreeSet;
use anyhow::Context as _;
use common::proc::Command;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Link {
ifname: String,
}
#[derive(Debug, Deserialize)]
struct Addr {
addr_info: Vec<AddrInfo>,
}
#[derive(Debug, Deserialize)]
struct AddrInfo {
local: String,
prefixlen: usize,
}
#[derive(Debug, Deserialize)]
struct Route {
dst: String,
gateway: Option<String>,
}
pub fn get_link_names() -> anyhow::Result<BTreeSet<String>> {
let mut proc = Command::new("ip");
proc.args(["-j", "link"]);
let out = proc.try_spawn_to_json::<Vec<Link>>()?;
Ok(out.into_iter().map(|l| l.ifname).collect())
}
pub fn has_ipv6_address(dev: &str, addr: &str) -> anyhow::Result<bool> {
let mut proc = Command::new("ip");
proc.args(["-j", "-6", "addr", "show", dev]);
let out: Vec<Addr> = proc.try_spawn_to_json()?;
let idx = addr
.char_indices()
.find_map(|(idx, ch)| if ch == '/' { Some(idx) } else { None })
.expect("Address should contain prefixlen");
let prefixlen = &addr[idx + 1..];
let addr = &addr[..idx];
let prefixlen: usize = prefixlen
.parse()
.context("Ipv6 prefixlen was not a usize")?;
Ok(out.iter().any(|a| {
a.addr_info
.iter()
.any(|a| a.local == addr && a.prefixlen == prefixlen)
}))
}
pub fn has_default_route(dev: &str, gateway: &str) -> anyhow::Result<bool> {
let mut proc = Command::new("ip");
proc.args(["-j", "-6", "route", "show", "dev", dev]);
let out: Vec<Route> = proc.try_spawn_to_json()?;
Ok(out
.iter()
.any(|r| r.dst == "default" && r.gateway.as_deref().is_some_and(|gw| gw == gateway)))
}

View file

@ -3,6 +3,7 @@ use clap::{Parser, Subcommand};
use crate::metadata::Instance; use crate::metadata::Instance;
mod iproute2;
mod metadata; mod metadata;
fn main() { fn main() {
@ -35,6 +36,7 @@ fn configure() -> anyhow::Result<()> {
let metadata = common::curl::read_text_as_string(&metadata_api)?; let metadata = common::curl::read_text_as_string(&metadata_api)?;
let metadata: Instance = common::yaml::from_str(&metadata) let metadata: Instance = common::yaml::from_str(&metadata)
.context("Could not parse instance metadata into expected format")?; .context("Could not parse instance metadata into expected format")?;
let link_names = iproute2::get_link_names()?;
for m in metadata.network_config.config { for m in metadata.network_config.config {
for subnet in m.subnets { for subnet in m.subnets {
match subnet { match subnet {
@ -44,27 +46,32 @@ fn configure() -> anyhow::Result<()> {
address, address,
gateway, gateway,
} => { } => {
let mut cmd = common::proc::Command::new("ip");
if ipv6.is_some_and(|v| v) {
cmd.arg("-6");
}
if ipv4.is_some_and(|v| v) { if ipv4.is_some_and(|v| v) {
cmd.arg("-4"); continue;
} }
if !ipv6.is_some_and(|v| v) {
continue;
}
// Apparently the configuration used renames the eth0 device to enp1s0, I should really figure out why this happens, but for now // Apparently the configuration used renames the eth0 device to enp1s0, I should really figure out why this happens, but for now
// this will do. // this will do.
let dev_name = if m.name == "eth0" { "enp1s0" } else { &m.name }; let dev_name = if m.name == "eth0" && !link_names.contains("eth0") {
cmd.args(["addr", "add", &address, "dev", dev_name]); "enp1s0"
cmd.try_spawn_to_string()?; } else {
let mut cmd = common::proc::Command::new("ip"); &m.name
if ipv6.is_some_and(|v| v) { };
cmd.arg("-6"); if !iproute2::has_ipv6_address(dev_name, &address)? {
let mut cmd = common::proc::Command::new("ip");
cmd.args(["-6", "addr", "add", &address, "dev", dev_name]);
cmd.try_spawn_to_string()?;
} }
if ipv4.is_some_and(|v| v) { if !iproute2::has_default_route(dev_name, &gateway)? {
cmd.arg("-4"); let mut cmd = common::proc::Command::new("ip");
cmd.args([
"-6", "route", "add", "default", "via", &gateway, "dev", dev_name,
]);
cmd.try_spawn_to_string()?;
} }
cmd.args(["route", "add", "default", "via", &gateway, "dev", dev_name]);
cmd.try_spawn_to_string()?;
} }
metadata::InstanceNetworkConfigConfigSubnet::Dhcp {} => continue, metadata::InstanceNetworkConfigConfigSubnet::Dhcp {} => continue,
} }

View file

@ -54,8 +54,6 @@ pub enum Endpoint {
Unifi, Unifi,
#[value(name = "vault")] #[value(name = "vault")]
Vault, Vault,
#[value(name = "authentik")]
Authentik,
} }
impl Endpoint { impl Endpoint {
@ -81,10 +79,6 @@ impl Endpoint {
let data = UnifiData::read_from_bao()?; let data = UnifiData::read_from_bao()?;
Ok(data.into()) Ok(data.into())
} }
Self::Authentik => {
let data = AuthentikData::read_from_bao()?;
Ok(data.into())
}
Self::Vault => { Self::Vault => {
let data = VaultData::read_from_bao()?; let data = VaultData::read_from_bao()?;
Ok(data.into()) Ok(data.into())
@ -152,12 +146,6 @@ entry_definition!(
&["UNIFI_USERNAME", "UNIFI_PASSWORD", "UNIFI_API"] &["UNIFI_USERNAME", "UNIFI_PASSWORD", "UNIFI_API"]
); );
entry_definition!(VaultDataConfig, VaultData, "vault", &["VAULT_TOKEN"]); entry_definition!(VaultDataConfig, VaultData, "vault", &["VAULT_TOKEN"]);
entry_definition!(
AuthentikDataConfig,
AuthentikData,
"authentik",
&["AUTHENTIK_TOKEN", "TF_VAR_authentik_username"]
);
fn transfer() -> anyhow::Result<()> { fn transfer() -> anyhow::Result<()> {
let openstack = OpenstackData::try_new_from_env()?; let openstack = OpenstackData::try_new_from_env()?;
@ -165,7 +153,6 @@ fn transfer() -> anyhow::Result<()> {
let aws = AwsData::try_new_from_env()?; let aws = AwsData::try_new_from_env()?;
let hcloud = HcloudData::try_new_from_env()?; let hcloud = HcloudData::try_new_from_env()?;
let unifi = UnifiData::try_new_from_env()?; let unifi = UnifiData::try_new_from_env()?;
let authentik = AuthentikData::try_new_from_env()?;
let vault = VaultData::try_new_from_env()?; let vault = VaultData::try_new_from_env()?;
write_kv_data(openstack)?; write_kv_data(openstack)?;
@ -173,7 +160,6 @@ fn transfer() -> anyhow::Result<()> {
write_kv_data(aws)?; write_kv_data(aws)?;
write_kv_data(hcloud)?; write_kv_data(hcloud)?;
write_kv_data(unifi)?; write_kv_data(unifi)?;
write_kv_data(authentik)?;
write_kv_data(vault)?; write_kv_data(vault)?;
Ok(()) Ok(())
} }