add script to generate k3s certificate

This commit is contained in:
Pim Kunis 2024-03-27 20:10:14 +01:00
parent 6aec465750
commit 89a9561fc2
6 changed files with 116 additions and 17 deletions

View file

@ -38,9 +38,17 @@ To deploy only one server: `nix run nixpkgs#deploy-rs -- -k --targets .#<host>`
## Known bugs
### Failed to connect to socket
When deploying a new virtiofs share, the error `Failed to connect to '<name>.sock': No such file or directory` can occur.
This seems to be a bug in `microvm.nix` and I opened a bug report [here](https://github.com/astro/microvm.nix/issues/200).
A workaround is to deploy the share without `deploy-rs`'s rollback feature enabled:
```
nix run nixpkgs#deploy-rs -- -k --targets .#<host> --auto-rollback false --magic-rollback false
```
### Rsync not available during bootstrap
The `rsync` command was removed from recent NixOS ISO which causes nixos-anywhere to fail when copying extra files.
See [this](https://github.com/nix-community/nixos-anywhere/issues/260) issue.
Solution is to execute `nix-env -iA nixos.rsync` on the host.

View file

@ -48,7 +48,7 @@
physicalMachines = hostPkgs.lib.filterAttrs (n: v: v.isPhysical) machines;
in
flake-utils.lib.meld (inputs // { inherit hostPkgs machines physicalMachines; }) [
./nix/flake/bootstrap
./nix/flake/scripts
./nix/flake/checks.nix
./nix/flake/deploy.nix
./nix/flake/nixos.nix

View file

@ -1,16 +0,0 @@
{ flake-utils, hostPkgs, ... }: flake-utils.lib.eachDefaultSystem (system: {
packages.bootstrap =
let
name = "bootstrap";
buildInputs = with hostPkgs; [ libsecret coreutils nixos-anywhere ];
script = (hostPkgs.writeScriptBin name (builtins.readFile ./bootstrap.sh)).overrideAttrs (old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
in
hostPkgs.symlinkJoin {
inherit name;
paths = [ script ] ++ buildInputs;
buildInputs = [ hostPkgs.makeWrapper ];
postBuild = "wrapProgram $out/bin/${name} --set PATH $out/bin";
};
})

View file

@ -0,0 +1,19 @@
{ flake-utils, hostPkgs, ... }: flake-utils.lib.eachDefaultSystem (system:
let
createScript = name: runtimeInputs: scriptPath:
let
script = (hostPkgs.writeScriptBin name (builtins.readFile scriptPath)).overrideAttrs (old: {
buildCommand = "${old.buildCommand}\n patchShebangs $out";
});
in
hostPkgs.symlinkJoin {
inherit name;
paths = [ script ] ++ runtimeInputs;
buildInputs = [ hostPkgs.makeWrapper ];
postBuild = "wrapProgram $out/bin/${name} --set PATH $out/bin";
};
in
{
packages.bootstrap = createScript "bootstrap" (with hostPkgs; [ libsecret coreutils nixos-anywhere ]) ./bootstrap.sh;
packages.gen-k3s-cert = createScript "create-k3s-cert" (with hostPkgs; [ openssl coreutils openssh yq ]) ./gen-k3s-cert.sh;
})

View file

@ -0,0 +1,88 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
username="${1-}"
host="${2-}"
output_path="${3:-.}"
if [ -z "$username" ] || [ -z "$host" ]
then
echo "Usage: $0 USERNAME HOST [OUTPUTPATH]"
exit 1
fi
# Create a temporary directory
temp=$(mktemp -d)
# Function to cleanup temporary directory on exit
cleanup() {
rm -rf "$temp"
}
trap cleanup EXIT
echo Generating the private key
openssl genpkey -algorithm ed25519 -out "$temp/key.pem"
echo Generating the certificate request
openssl req -new -key "$temp/key.pem" -out "$temp/req.csr" -subj "/CN=$username"
echo Creating K8S CSR manifest
csr="$(cat "$temp/req.csr" | base64 | tr -d '\n')"
k8s_csr="apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: $username-csr
spec:
request: $csr
expirationSeconds: 307584000 # 10 years
signerName: kubernetes.io/kube-apiserver-client
usages:
- digital signature
- key encipherment
- client auth
"
echo Creating K8S CSR resource
ssh "$host" "echo \"$k8s_csr\" | k3s kubectl apply -f -"
echo Approving K8S CSR
ssh "$host" "k3s kubectl certificate approve $username-csr"
echo Retrieving approved certificate
encoded_cert="$(ssh root@"$host" "k3s kubectl get csr $username-csr -o jsonpath='{.status.certificate}'")"
echo Retrieving default K3S kubeconfig
base_kubeconfig="$(ssh root@"$host" "cat /etc/rancher/k3s/k3s.yaml")"
echo Getting certificate authority data from default kubeconfig
cert_authority_data="$(echo -n "$base_kubeconfig" | yq -r '.clusters[0].cluster."certificate-authority-data"')"
echo Generating final kubeconfig
result_kubeconfig="apiVersion: v1
clusters:
- cluster:
certificate-authority-data: $cert_authority_data
server: https://$host:6443
name: default
contexts:
- context:
cluster: default
user: pim
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: $username
user:
client-certificate: $username.crt
client-key: $username.key
"
echo Writing resulting files to "$output_path"
echo -n "$encoded_cert" | base64 -d > $output_path/$username.crt
echo -n "$result_kubeconfig" > $output_path/config
cp $temp/key.pem $output_path/$username.key