Skip to content

Instantly share code, notes, and snippets.

@proger
Last active October 8, 2015 22:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save proger/3563bc77184a12654fee to your computer and use it in GitHub Desktop.
Save proger/3563bc77184a12654fee to your computer and use it in GitHub Desktop.
private nix stores

This is a proof of concept of using multiple nix stores to isolate private data.

The main idea is to show that this can be done using existing mechanism with the exception of being able to reference between different nix stores without additional transformations.

  1. applications' private configuration is evaluated with nix-build into a separate "ephemeral" nix-store, evaluated without nixpkgs or stdenv, with its paths stored in some "index" file.
  2. the contents of the "ephemeral" nix-store are stored into a nar-like file
  3. the nar-like file is encryped with some key that's also known to the compute node that runs the application, but not the build host (the software nix-store)
  4. the nixos module references the nar-like file (adding it to the "big" store), code that knows how to decrypt it at runtime and (optionally) names of private files

for (1) (2) and (3) see sproxy-config.nix and Makefile (which references build-secrets.sh) for (4) see nixos-module.nix

for an additional example of doing inter-store references see internix.nix

#!/usr/bin/env bash
store="$1"
expression="$2"
oldpwd="$PWD"
me="$(dirname $0)"
cd "$me"
me="$PWD"
cd "$oldpwd"
secretlib="
let
system = ''whatever'';
builder = $me/proxy-bash.sh;
PATH = ''/usr/bin:/usr/local/bin:/bin:/run/current-system/sw/bin'';
preferLocalBuild = true;
in rec {
writeText = name: text: derivation {
inherit system builder PATH name preferLocalBuild;
args = [''-c'' ''echo -n '\${text}' > \$out''];
};
runCommand = name: _: command: derivation {
inherit system builder PATH name preferLocalBuild;
args = [''-c'' command];
};
decrypt = file: runCommand (builtins.baseNameOf (builtins.toString file)) {} ''
gpg --use-agent --no-tty --decrypt \${toString file} > \$out
'';
}
"
if [ -z "$GPG_AGENT_INFO" ]; then
echo 'gpg agent path is not present in $GPG_AGENT_INFO' >&2
echo 'you should start it otherwise decryption will not succeed.' >&2
echo
echo 'use:' >&2
echo 'file=/tmp/.build-secrets-helper; gpg-agent --daemon --write-env-file $file; echo "GNUPGHOME=$HOME/.gnupg; export GNUPGHOME GPG_AGENT_INFO" >> $file; source $file' >&2
echo 'gpg -d --use-agent secrets/client_secret.asc # to warmup the agent' >&2
exit 1
fi
lib="(import <nixpkgs> {}).lib"
export NIX_STATE_DIR=$(mktemp -d -t nix-state.XXXXXXX)
export NIX_STORE_DIR=$store
set -e
nix-build --no-out-link --option system whatever --arg secretlib "$secretlib" --arg lib "$lib" --show-trace "$expression" >&2
nix-instantiate --eval --strict --option system whatever --show-trace \
-E "let lib = $lib; in
lib.mapAttrs (k: v: builtins.storePath v) (import $expression { secretlib = $secretlib; inherit lib; })"
helper = /tmp/.build-secrets-helper
# path to the ephemeral store
store = /run/sproxy-sink/store
aes_path = secrets/aes-password
gpg_key = C4DECDD2
secrets-encrypted: paths-sproxy.nix $(aes_path)
tar c $(store) | openssl aes-256-cbc -pass file:secrets/aes-password -a > $@
paths-sproxy.nix: ./sproxy-config.nix
$$(nix-instantiate --eval -E './build-secrets') $(store) ./$< > $@
$(aes_path): $(aes_path).asc
gpg --use-agent -d $< > $@
decrypt: secrets-encrypted
openssl aes-256-cbc -d -a -pass file:$(aes_path) < secrets-encrypted
new-aes-password:
openssl rand 512 -base64 -out $(aes_path)
gpg -e -r $(gpg_key) --trust-model always --armor $(aes_path)
help:
@echo 'file=/tmp/.build-secrets-helper; gpg-agent --daemon --write-env-file $$file; echo "GNUPGHOME=$$HOME/.gnupg; export GNUPGHOME GPG_AGENT_INFO" >> $$file; source $$file'
.PHONY: decrypt new-aes-password help
.DELETE_ON_ERROR: paths-sproxy.nix secrets-encrypted $(aes_path)
{ config
, pkgs
, lib
, ... }: with lib;
let
sproxy = (import <sproxy> { inherit pkgs; }).build;
paths = if builtins.pathExists ./paths-sproxy.nix
then import ./paths-sproxy.nix
else abort "could not find ./paths-sproxy.nix: have you ran make?";
jq = "/usr/bin/env LD_LIBRARY_PATH=${pkgs.jq}/lib ${pkgs.jq}/bin/jq";
curl = "${pkgs.curl}/bin/curl -s --retry 3 --retry-delay 0 --fail";
openssl = "${pkgs.openssl}/bin/openssl";
ip = "${pkgs.iproute}/sbin/ip";
tar = "${pkgs.gnutar}/bin/tar";
bash = "${pkgs.bash}/bin/bash";
master-decrypt = pkgs.writeScript "master-decrypt" ''
#!${bash}
${ip} route delete blackhole 169.254.169.254 2>/dev/null || true
${curl} http://169.254.169.254/latest/user-data | ${jq} -r '."host-aes-key"' \
| ${openssl} aes-256-cbc -d -a -pass stdin -in ${./secrets-encrypted} | ${tar} xvf - -C /
'';
in {
config = {
generic-services.sproxy = {
enable = true;
command = let script = pkgs.writeScript "sproxy-decrypt-start" ''
#!${bash} -e
${master-decrypt}
exec ${sproxy}/bin/sproxy --config=${paths.sproxy-config}
''; in "${script}";
dataDir = "/var/sproxy";
paths = [
"/run/sproxy-sink"
];
user = "root";
};
};
}
#!/usr/bin/env bash
# needed to avoid capturing the real bash binary to the ephemeral store
# also used to do impure things so can't work in a chroot
test -f /tmp/.build-secrets-helper && source /tmp/.build-secrets-helper
exec bash "$@"
{ secretlib, lib, ... }:
let
sproxy-args = {
sproxy-id = "main";
sproxy-domain = "sink.example.com";
ssl-certs = ./secrets/star.example.com.crt;
client-secret = secretlib.decrypt ./secrets/client_secret.asc;
auth-token-key = secretlib.decrypt ./secrets/auth_token_key.asc;
ssl-key = secretlib.decrypt ./secrets/star.example.com.key.asc;
sproxy-google-client-id = secretlib.decrypt ./secrets/google_client_id.asc;
};
inherit (sproxy-args) sproxy-id sproxy-domain client-secret auth-token-key ssl-key ssl-certs sproxy-google-client-id;
backend-port = 80;
backend-address = "localhost";
in
{
sproxy-config = secretlib.runCommand "sproxy-${sproxy-id}.conf" {} ''
cat <<EOF > $out
listen: 443
backend_address: "127.0.0.1"
backend_port: ${toString backend-port}
log_level: error
redirect_http_to_https: no
client_id: $(cat ${sproxy-google-client-id})
database: "unused"
cookie_name: sproxy-auth
cookie_domain: ${sproxy-domain}
client_secret: ${client-secret}
auth_token_key: ${auth-token-key}
ssl_key: ${ssl-key}
ssl_certs: ${ssl-certs}
EOF
'';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment