diff --git a/README.md b/README.md index 4102bbb..fb4b38f 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,17 @@ To deploy only one server: `nix run nixpkgs#deploy-rs -- -k --targets .#` ## Known bugs +### Failed to connect to socket + When deploying a new virtiofs share, the error `Failed to connect to '.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 .# --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. diff --git a/flake.nix b/flake.nix index d3137c5..4ecafb9 100644 --- a/flake.nix +++ b/flake.nix @@ -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 diff --git a/nix/flake/bootstrap/default.nix b/nix/flake/bootstrap/default.nix deleted file mode 100644 index e20cf30..0000000 --- a/nix/flake/bootstrap/default.nix +++ /dev/null @@ -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"; - }; -}) diff --git a/nix/flake/bootstrap/bootstrap.sh b/nix/flake/scripts/bootstrap.sh similarity index 100% rename from nix/flake/bootstrap/bootstrap.sh rename to nix/flake/scripts/bootstrap.sh diff --git a/nix/flake/scripts/default.nix b/nix/flake/scripts/default.nix new file mode 100644 index 0000000..dd2dff8 --- /dev/null +++ b/nix/flake/scripts/default.nix @@ -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; +}) diff --git a/nix/flake/scripts/gen-k3s-cert.sh b/nix/flake/scripts/gen-k3s-cert.sh new file mode 100644 index 0000000..8473e31 --- /dev/null +++ b/nix/flake/scripts/gen-k3s-cert.sh @@ -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 +