Secrets

As the nix store is world-readable, we want to avoid writing secrets during evaluation.

We can roughly break the entire process, from nix to kube, down to

eval -> deploy -> run

eval time #

This is the process of taking nix configuration and generating a JSON manifest.

The generated manifest is written to the nix store; so inlining (unencrypted) secrets is entirely possible but not ideal.

deploy time #

The simplest option is to inject secrets during deploy; that is, after manifests have been generated but prior to running kubectl apply (or equivalent).

example #

We can pipe manifests through vals prior to apply. Such that using the file provider might look like

    default.nix
    
{ kubenix ? import ../../../.. }:
kubenix.evalModules.${builtins.currentSystem} {
  module = { kubenix, ... }: {
    imports = [ kubenix.modules.k8s ];
    kubernetes.resources.secrets.example.stringData = {
      password = "ref+file:///path/to/secret";
    };
  };
}
NOTE: the creation of /path/to/secret is out of scope but we recommend checking out one of the secret managing schemes

Then the apply might look something like

pkgs.writeShellScript "apply" ''
  cat manifest.json | ${pkgs.vals}/bin/vals eval | ${pkgs.kubectl}/bin/kubectl -f -
''
NOTE: the builtin kubenix CLI uses this approach so it’s not necessary to implement yourself

runtime #

A more robust option is to resolve secrets from within the cluster itself.

This can be done with tools that either

  • reference external sources

    similar to the deploy time example; instead, resolving secrets with a controller running inside the cluster (e.g., external-secrets)

  • decrypt inline secrets

    values can be decrypted by a controller within the cluster itself (e.g., sealed-secrets) or using external keys (e.g., sops)