Skip to content

Instantly share code, notes, and snippets.

@antifuchs
Created September 12, 2022 19:40
Show Gist options
  • Save antifuchs/e30d58a64988907f282c82231dde2cbc to your computer and use it in GitHub Desktop.
Save antifuchs/e30d58a64988907f282c82231dde2cbc to your computer and use it in GitHub Desktop.
# To set this up, first get tailscale working in an isolated linux shell:
# 1. sudo systemctl stop tailscaled.service
# 2. tailscaled -port 9993 -state tailscale-luks-setup.state -tun userspace-networking -socket ./tailscaled.sock
# 3. tailscale -socket ./tailscaled.sock up -hostname HOSTNAME-luks
# 4. tailscale -socket ./tailscaled.sock down
# 5. ctrl-c out of tailscaled
# 6 sudo systemctl start tailscaled.service
#
# Then add the .state file to your machine secrets and pass its path as tailscaleStatePath.
{ config, lib, pkgs, ... }: {
options = {
remote-machine.boot.tailscaleUnlock = with lib; {
enable = mkOption {
description = "Turn on unlock via tailscale";
default = false;
};
tailscaleStatePath = mkOption {
description = "Pre-initialized tailscale state file as a secret. Make sure to set it to not require re-authentication, otherwise the machine may not boot up after a few weeks.";
};
};
};
config =
let
cfg = config.remote-machine.boot.tailscaleUnlock;
# TODO: This uses old-style non-nftables iptables; ideally, we wouldn't have to opt out of that.
# Enabling nftables compat means having to shuffle the list of
# modules down in availableKernelModules; that's a bunch of work
# (deploying to a linux machine & rebooting to see what doesn't
# work this time), so I'm a bit too lazy for that now.
iptables-static = (pkgs.iptables.override { nftablesCompat = false; }).overrideAttrs (old: {
dontDisableStatic = true;
configureFlags = (lib.remove "--enable-shared" old.configureFlags) ++ [
"--enable-static"
"--disable-shared"
];
});
in
lib.mkIf cfg.enable {
boot.initrd = {
secrets = {
"/var/lib/tailscale/tailscaled.state" = cfg.tailscaleStatePath;
"/etc/ssl/certs/ca-certificates.crt" = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
"/etc/ssl/certs/ca-bundle.crt" = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
};
network = {
enable = true;
flushBeforeStage2 = true;
postCommands = ''
# Bring up tailscaled and dial in
echo 'nameserver 8.8.8.8' > /etc/resolv.conf
mkdir /dev/net
mknod /dev/net/tun c 10 200
.tailscaled-wrapped 2>/dev/null &
sleep 5
.tailscale-wrapped up
.tailscale-wrapped status
echo "echo 'Use cryptsetup-askpass to unlock!'" >> /root/.profile
'';
};
availableKernelModules = [
"ip6_tables"
"ip6table_filter"
"ip6table_nat"
"ip6table_raw"
"ip_tables"
"iptable_filter"
"iptable_nat"
"iptable_raw"
"nf_conntrack"
"nf_nat"
"tun"
"xt_comment"
"xt_conntrack"
"xt_mark"
"xt_MASQUERADE"
"xt_LOG"
"xt_tcpudp"
];
extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.tailscale}/bin/.tailscaled-wrapped
copy_bin_and_libs ${pkgs.tailscale}/bin/.tailscale-wrapped
copy_bin_and_libs ${pkgs.iproute}/bin/ip
copy_bin_and_libs ${iptables-static}/bin/iptables
copy_bin_and_libs ${iptables-static}/bin/xtables-legacy-multi
copy_bin_and_libs ${pkgs.strace}/bin/strace
'';
postMountCommands = ''
# tear down tailscale
pkill .tailscaled-wrapped
.tailscaled-wrapped --cleanup
'';
};
};
}
@razielgn
Copy link

Just tested: it works, thanks! NixOS 23.05

@antifuchs
Copy link
Author

Glad to hear it! I was talking to some tailscale folks about this earlier and they said that this does work, but they'll eventually start requiring a write-able connection state. I'm thinking about redoing this in the form of a little partition that is the home for that state store and the server SSH keys; if the host system has SecureBoot, it could even be encrypted on disk and unlock automatically via the TPM2. But for now, this should continue to work ok (:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment