Archived
feat(ha): wire sops secrets and disable NetworkManager for HA servers
- cluster-config.nix: add corosync_authkey sops binary secret
(/etc/corosync/authkey, mode 0400) and force-disable NetworkManager
(common config enables it; HA nodes need stable static IP networking)
- hosts/ha-server-{1,2}/host.nix: add host-token.nix import for
sops-managed beszel-token; add KEY placeholder for beszel hub pairing
- .sops.yaml: add creation rules for secrets/ha-server-{1,2}.yaml and
secrets/ha-corosync-authkey (admin-only until sync-host-keys.sh runs)
- secrets/ha-server-{1,2}.yaml, secrets/ha-corosync-authkey: stub files
so eval passes before real secrets are provisioned
Bootstrap order (post-merge):
1. bash scripts/secrets/sync-host-keys.sh proxmox-ha-server-1
2. bash scripts/secrets/sync-host-keys.sh proxmox-ha-server-2
3. sops updatekeys secrets/common.yaml (grants HA nodes common secrets)
4. sops secrets/ha-server-{1,2}.yaml (set beszel-token values)
5. On node1: corosync-keygen; sops -e --input-type binary
/etc/corosync/authkey > secrets/ha-corosync-authkey; git add/commit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HaH1cSGvhogRP5ExoF6nD8
This commit is contained in:
+26
@@ -85,6 +85,32 @@ creation_rules:
|
|||||||
- *lxc-tailscale-router
|
- *lxc-tailscale-router
|
||||||
- *proxmox-tailscale-router
|
- *proxmox-tailscale-router
|
||||||
|
|
||||||
|
# HA file server per-node secrets (beszel-token).
|
||||||
|
# proxmox-ha-server-1 / proxmox-ha-server-2 keys are added automatically
|
||||||
|
# by scripts/secrets/sync-host-keys.sh once the hosts are provisioned;
|
||||||
|
# until then only the admin key can decrypt these files.
|
||||||
|
- path_regex: secrets/ha-server-1\.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
# proxmox-ha-server-1 added by sync-host-keys.sh
|
||||||
|
|
||||||
|
- path_regex: secrets/ha-server-2\.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
# proxmox-ha-server-2 added by sync-host-keys.sh
|
||||||
|
|
||||||
|
# Shared HA cluster corosync authkey (binary sops file).
|
||||||
|
# Encrypted for both HA nodes so either can decrypt on boot.
|
||||||
|
# Both host keys added by sync-host-keys.sh; admin key allows initial creation.
|
||||||
|
- path_regex: secrets/ha-corosync-authkey$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
# proxmox-ha-server-1 added by sync-host-keys.sh
|
||||||
|
# proxmox-ha-server-2 added by sync-host-keys.sh
|
||||||
|
|
||||||
# gui-host-specific secrets (currently: wifi-password, see
|
# gui-host-specific secrets (currently: wifi-password, see
|
||||||
# modules/networking/wifi.nix). Only *lxc-gui has a registered key today
|
# modules/networking/wifi.nix). Only *lxc-gui has a registered key today
|
||||||
# -- proxmox-gui/linode-gui/baremetal-gui haven't been provisioned via
|
# -- proxmox-gui/linode-gui/baremetal-gui haven't been provisioned via
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
{ vars, ... }:
|
{ vars, ... }:
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
(import ../../modules/beszel/host-token.nix {
|
||||||
|
name = "ha-server-1";
|
||||||
|
sopsFile = ../../secrets/ha-server-1.yaml;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
hostName = vars.haServer1Host;
|
hostName = vars.haServer1Host;
|
||||||
hostId = "3a4b5c6d";
|
hostId = "3a4b5c6d";
|
||||||
@@ -12,13 +19,8 @@
|
|||||||
nameservers = [ "192.168.2.1" "8.8.8.8" ];
|
nameservers = [ "192.168.2.1" "8.8.8.8" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: after running `bash scripts/secrets/sync-host-keys.sh proxmox-ha-server-1`
|
# Set KEY after pairing this host with the beszel hub; the token is sops-managed.
|
||||||
# add beszel agent pairing and sops-managed corosync authkey:
|
services.beszel.agent.environment.KEY = "";
|
||||||
# imports = [ (import ../../modules/beszel/host-token.nix {
|
|
||||||
# name = "ha-server-1";
|
|
||||||
# sopsFile = ../../secrets/ha-server-1.yaml;
|
|
||||||
# }) ];
|
|
||||||
# services.beszel.agent.environment.KEY = "...";
|
|
||||||
|
|
||||||
system.stateVersion = "26.05";
|
system.stateVersion = "26.05";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
{ vars, ... }:
|
{ vars, ... }:
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
(import ../../modules/beszel/host-token.nix {
|
||||||
|
name = "ha-server-2";
|
||||||
|
sopsFile = ../../secrets/ha-server-2.yaml;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
hostName = vars.haServer2Host;
|
hostName = vars.haServer2Host;
|
||||||
hostId = "7e8f9a0b";
|
hostId = "7e8f9a0b";
|
||||||
@@ -12,13 +19,8 @@
|
|||||||
nameservers = [ "192.168.2.1" "8.8.8.8" ];
|
nameservers = [ "192.168.2.1" "8.8.8.8" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: after running `bash scripts/secrets/sync-host-keys.sh proxmox-ha-server-2`
|
# Set KEY after pairing this host with the beszel hub; the token is sops-managed.
|
||||||
# add beszel agent pairing and sops-managed corosync authkey:
|
services.beszel.agent.environment.KEY = "";
|
||||||
# imports = [ (import ../../modules/beszel/host-token.nix {
|
|
||||||
# name = "ha-server-2";
|
|
||||||
# sopsFile = ../../secrets/ha-server-2.yaml;
|
|
||||||
# }) ];
|
|
||||||
# services.beszel.agent.environment.KEY = "...";
|
|
||||||
|
|
||||||
system.stateVersion = "26.05";
|
system.stateVersion = "26.05";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,10 @@
|
|||||||
# (hostname, static IP, stateVersion) lives in hosts/ha-server-{1,2}/host.nix.
|
# (hostname, static IP, stateVersion) lives in hosts/ha-server-{1,2}/host.nix.
|
||||||
#
|
#
|
||||||
# Corosync authkey:
|
# Corosync authkey:
|
||||||
# /etc/corosync/authkey must be present (mode 0400) for corosync to start.
|
# /etc/corosync/authkey (mode 0400) is managed by sops-nix below.
|
||||||
# It is NOT managed declaratively here — the initial deploy uses
|
# Bootstrap: run scripts/ha/cluster-init.sh on node1 to generate the key,
|
||||||
# scripts/ha/cluster-init.sh to generate it via corosync-keygen and
|
# then encrypt it with: sops -e --input-type binary /etc/corosync/authkey > secrets/ha-corosync-authkey
|
||||||
# distribute it to both nodes.
|
# Both host keys must be registered via sync-host-keys.sh first so both nodes can decrypt it.
|
||||||
# TODO: once both hosts have their sops keys registered via
|
|
||||||
# scripts/secrets/sync-host-keys.sh, add a sops secret here so the
|
|
||||||
# authkey survives nixos-rebuild.
|
|
||||||
#
|
#
|
||||||
# DRBD fencing:
|
# DRBD fencing:
|
||||||
# Production setting is resource-only: DRBD waits for the STONITH fence
|
# Production setting is resource-only: DRBD waits for the STONITH fence
|
||||||
@@ -20,7 +17,7 @@
|
|||||||
# (see scripts/ha/cluster-enable-stonith.sh). On a fresh cluster with
|
# (see scripts/ha/cluster-enable-stonith.sh). On a fresh cluster with
|
||||||
# no fence device yet, temporarily change to dont-care and run
|
# no fence device yet, temporarily change to dont-care and run
|
||||||
# cluster-enable-stonith.sh once the fence key is deployed.
|
# cluster-enable-stonith.sh once the fence key is deployed.
|
||||||
{ vars, ... }:
|
{ lib, vars, ... }:
|
||||||
{
|
{
|
||||||
services.drbd = {
|
services.drbd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -60,6 +57,20 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# /etc/corosync/authkey — sops binary secret, identical on both nodes.
|
||||||
|
# Decryptable by both ha-server host keys (added by sync-host-keys.sh).
|
||||||
|
sops.secrets.corosync_authkey = {
|
||||||
|
sopsFile = ../../secrets/ha-corosync-authkey;
|
||||||
|
format = "binary";
|
||||||
|
path = "/etc/corosync/authkey";
|
||||||
|
mode = "0400";
|
||||||
|
restartUnits = [ "corosync.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# NixOS common config enables NetworkManager by default; HA cluster nodes
|
||||||
|
# need stable static IPs with predictable interface names — NM is not suitable.
|
||||||
|
networking.networkmanager.enable = lib.mkForce false;
|
||||||
|
|
||||||
# services.corosync.enable is set by modules/ha/pacemaker-stack.nix.
|
# services.corosync.enable is set by modules/ha/pacemaker-stack.nix.
|
||||||
services.corosync = {
|
services.corosync = {
|
||||||
clusterName = "ha-cluster";
|
clusterName = "ha-cluster";
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
STUB: run cluster-init.sh to generate, then: sops -e --input-type binary /etc/corosync/authkey > secrets/ha-corosync-authkey
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# STUB — not yet encrypted with sops.
|
||||||
|
# Bootstrap:
|
||||||
|
# bash scripts/secrets/sync-host-keys.sh proxmox-ha-server-1
|
||||||
|
# sops updatekeys secrets/common.yaml (allows ha-server-1 to decrypt shared secrets)
|
||||||
|
# sops secrets/ha-server-1.yaml (create with: beszel-token)
|
||||||
|
beszel-token: REPLACE
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# STUB — not yet encrypted with sops.
|
||||||
|
# Bootstrap:
|
||||||
|
# bash scripts/secrets/sync-host-keys.sh proxmox-ha-server-2
|
||||||
|
# sops updatekeys secrets/common.yaml (allows ha-server-2 to decrypt shared secrets)
|
||||||
|
# sops secrets/ha-server-2.yaml (create with: beszel-token)
|
||||||
|
beszel-token: REPLACE
|
||||||
Reference in New Issue
Block a user