neb
This commit is contained in:
187
packages/system/nebula-sign-cert/default.nix
Normal file
187
packages/system/nebula-sign-cert/default.nix
Normal file
@@ -0,0 +1,187 @@
|
||||
{
|
||||
lib,
|
||||
writeShellApplication,
|
||||
nebula,
|
||||
sops,
|
||||
coreutils,
|
||||
jq,
|
||||
...
|
||||
}:
|
||||
|
||||
writeShellApplication {
|
||||
name = "nebula-sign-cert";
|
||||
|
||||
runtimeInputs = [
|
||||
nebula
|
||||
sops
|
||||
coreutils
|
||||
jq
|
||||
];
|
||||
|
||||
text = ''
|
||||
# ---------------------------------------------------------------------------
|
||||
# nebula-sign-cert
|
||||
#
|
||||
# Signs a new Nebula host certificate using the CA stored in a SOPS secrets
|
||||
# file and writes the resulting cert+key back into a (possibly different)
|
||||
# SOPS secrets file.
|
||||
#
|
||||
# The CA is read from:
|
||||
# <ca-sops-file> at YAML path <ca-prefix>/ca-cert and <ca-prefix>/ca-key
|
||||
#
|
||||
# The new cert+key are written to:
|
||||
# <host-sops-file> at YAML paths
|
||||
# <host-prefix>/<host-secret-name>-cert
|
||||
# <host-prefix>/<host-secret-name>-key
|
||||
#
|
||||
# Usage:
|
||||
# nebula-sign-cert \
|
||||
# --name <node-name> # e.g. "nas" — used in the cert
|
||||
# --ip <overlay-ip/mask> # e.g. "10.1.1.2/24"
|
||||
# --ca-file <path/to/ca-secrets.yaml>
|
||||
# --ca-prefix <sops-key-prefix> # e.g. "pi5/nebula"
|
||||
# --host-file <path/to/host-secrets.yaml>
|
||||
# --host-prefix <sops-key-prefix> # e.g. "jallen-nas/nebula"
|
||||
# --host-secret-name <name> # e.g. "nas" (cert stored as nas-cert/nas-key)
|
||||
# [--groups <group1,group2>] # optional Nebula groups
|
||||
# [--duration <duration>] # e.g. "8760h0m0s" (1 year), default: CA lifetime
|
||||
#
|
||||
# All temp files are written to a private tmpdir and shredded on exit.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── argument parsing ────────────────────────────────────────────────────────
|
||||
NAME=""
|
||||
IP=""
|
||||
CA_FILE=""
|
||||
CA_PREFIX=""
|
||||
HOST_FILE=""
|
||||
HOST_PREFIX=""
|
||||
HOST_SECRET_NAME=""
|
||||
NEBULA_GROUPS=""
|
||||
DURATION=""
|
||||
|
||||
usage() {
|
||||
echo "Usage: nebula-sign-cert \\"
|
||||
echo " --name <node-name> \\"
|
||||
echo " --ip <overlay-ip/mask> \\"
|
||||
echo " --ca-file <path/to/ca-sops-file.yaml> \\"
|
||||
echo " --ca-prefix <sops-key-prefix> (e.g. pi5/nebula) \\"
|
||||
echo " --host-file <path/to/host-sops-file.yaml> \\"
|
||||
echo " --host-prefix <sops-key-prefix> (e.g. jallen-nas/nebula) \\"
|
||||
echo " --host-secret-name <name> (e.g. nas) \\"
|
||||
echo " [--groups <group1,group2>] \\"
|
||||
echo " [--duration <8760h0m0s>]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--name) NAME="$2"; shift 2 ;;
|
||||
--ip) IP="$2"; shift 2 ;;
|
||||
--ca-file) CA_FILE="$2"; shift 2 ;;
|
||||
--ca-prefix) CA_PREFIX="$2"; shift 2 ;;
|
||||
--host-file) HOST_FILE="$2"; shift 2 ;;
|
||||
--host-prefix) HOST_PREFIX="$2"; shift 2 ;;
|
||||
--host-secret-name) HOST_SECRET_NAME="$2"; shift 2 ;;
|
||||
--groups) NEBULA_GROUPS="$2"; shift 2 ;;
|
||||
--duration) DURATION="$2"; shift 2 ;;
|
||||
-h|--help) usage ;;
|
||||
*) echo "Unknown argument: $1"; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── validate required args ──────────────────────────────────────────────────
|
||||
missing=()
|
||||
[[ -z "$NAME" ]] && missing+=(--name)
|
||||
[[ -z "$IP" ]] && missing+=(--ip)
|
||||
[[ -z "$CA_FILE" ]] && missing+=(--ca-file)
|
||||
[[ -z "$CA_PREFIX" ]] && missing+=(--ca-prefix)
|
||||
[[ -z "$HOST_FILE" ]] && missing+=(--host-file)
|
||||
[[ -z "$HOST_PREFIX" ]] && missing+=(--host-prefix)
|
||||
[[ -z "$HOST_SECRET_NAME" ]] && missing+=(--host-secret-name)
|
||||
if [[ ''${#missing[@]} -gt 0 ]]; then
|
||||
echo "error: missing required arguments: ''${missing[*]}"
|
||||
usage
|
||||
fi
|
||||
|
||||
[[ -f "$CA_FILE" ]] || { echo "error: CA secrets file not found: $CA_FILE"; exit 1; }
|
||||
[[ -f "$HOST_FILE" ]] || { echo "error: host secrets file not found: $HOST_FILE"; exit 1; }
|
||||
|
||||
# Convert "a/b/c" prefix → sops extract path ["a"]["b"]["c"]
|
||||
prefix_to_sops_path() {
|
||||
local prefix="$1"
|
||||
local IFS='/'
|
||||
local result=""
|
||||
for segment in $prefix; do
|
||||
result+="[\"$segment\"]"
|
||||
done
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
CA_SOPS_PATH=$(prefix_to_sops_path "$CA_PREFIX")
|
||||
HOST_SOPS_PATH=$(prefix_to_sops_path "$HOST_PREFIX")
|
||||
|
||||
# ── setup temp directory (cleaned up on exit) ───────────────────────────────
|
||||
TMPDIR=$(mktemp -d)
|
||||
cleanup() {
|
||||
# Shred all temp files before removing the directory
|
||||
find "$TMPDIR" -type f -exec shred -u {} \;
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
CA_CRT="$TMPDIR/ca.crt"
|
||||
CA_KEY="$TMPDIR/ca.key"
|
||||
HOST_CRT="$TMPDIR/host.crt"
|
||||
HOST_KEY="$TMPDIR/host.key"
|
||||
|
||||
# ── extract CA cert and key from SOPS ──────────────────────────────────────
|
||||
echo "» Extracting CA from $CA_FILE ($CA_PREFIX)..."
|
||||
sops decrypt --extract "''${CA_SOPS_PATH}[\"ca-cert\"]" "$CA_FILE" > "$CA_CRT"
|
||||
sops decrypt --extract "''${CA_SOPS_PATH}[\"ca-key\"]" "$CA_FILE" > "$CA_KEY"
|
||||
|
||||
# ── build nebula-cert sign args ─────────────────────────────────────────────
|
||||
SIGN_ARGS=(
|
||||
sign
|
||||
-name "$NAME"
|
||||
-ip "$IP"
|
||||
-ca-crt "$CA_CRT"
|
||||
-ca-key "$CA_KEY"
|
||||
-out-crt "$HOST_CRT"
|
||||
-out-key "$HOST_KEY"
|
||||
)
|
||||
[[ -n "$NEBULA_GROUPS" ]] && SIGN_ARGS+=(-groups "$NEBULA_GROUPS")
|
||||
[[ -n "$DURATION" ]] && SIGN_ARGS+=(-duration "$DURATION")
|
||||
|
||||
# ── sign the certificate ────────────────────────────────────────────────────
|
||||
echo "» Signing certificate for $NAME ($IP)..."
|
||||
nebula-cert "''${SIGN_ARGS[@]}"
|
||||
|
||||
echo "» Certificate details:"
|
||||
nebula-cert print -path "$HOST_CRT"
|
||||
|
||||
# ── write cert and key back into the host SOPS file ────────────────────────
|
||||
# sops set requires a JSON-encoded string value; use jq -Rs . to encode the
|
||||
# file contents and pipe via --value-stdin to avoid leaking secrets in ps.
|
||||
echo "» Writing ''${HOST_SECRET_NAME}-cert into $HOST_FILE ($HOST_PREFIX)..."
|
||||
jq -Rs . "$HOST_CRT" | sops set --value-stdin \
|
||||
"$HOST_FILE" \
|
||||
"''${HOST_SOPS_PATH}[\"''${HOST_SECRET_NAME}-cert\"]"
|
||||
|
||||
echo "» Writing ''${HOST_SECRET_NAME}-key into $HOST_FILE ($HOST_PREFIX)..."
|
||||
jq -Rs . "$HOST_KEY" | sops set --value-stdin \
|
||||
"$HOST_FILE" \
|
||||
"''${HOST_SOPS_PATH}[\"''${HOST_SECRET_NAME}-key\"]"
|
||||
|
||||
echo ""
|
||||
echo "✓ Done. Certificate for '$NAME' written to $HOST_FILE"
|
||||
echo " Rebuild the host to apply: sudo nixos-rebuild switch --flake .#<hostname>"
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Sign a Nebula host certificate using a CA stored in SOPS";
|
||||
mainProgram = "nebula-sign-cert";
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user