Compare commits
2 commits
6447486643
...
c461f04c13
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c461f04c13 | ||
|
|
f733a5238c |
9 changed files with 131 additions and 226 deletions
|
|
@ -9,18 +9,18 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
|
nix.settings.experimental-features = [
|
||||||
|
"nix-command"
|
||||||
|
"flakes"
|
||||||
|
];
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
|
||||||
sops.defaultSopsFile = ./secrets/home.yaml;
|
sops.defaultSopsFile = ./secrets/home.yaml;
|
||||||
sops.age.keyFile = "/.persist/root/.config/sops/age/keys.txt";
|
sops.age.keyFile = "/.persist/root/.config/sops/age/keys.txt";
|
||||||
sops.secrets."root/ssh/desktop" = {
|
sops.secrets."root/ssh/desktop" = {
|
||||||
path = "/root/.ssh/desktop";
|
path = "/root/.ssh/desktop";
|
||||||
mode = "0600";
|
mode = "0600";
|
||||||
};
|
};
|
||||||
nix.settings.experimental-features = [
|
|
||||||
"nix-command"
|
|
||||||
"flakes"
|
|
||||||
];
|
|
||||||
|
|
||||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
|
||||||
|
|
||||||
boot.initrd.systemd.enable = true;
|
boot.initrd.systemd.enable = true;
|
||||||
boot.loader.systemd-boot.enable = true;
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
|
|
||||||
12
executables/claude-code.sh
Normal file
12
executables/claude-code.sh
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SSH_TARGET="sandbox@vsock%3"
|
||||||
|
|
||||||
|
if [[ "$PWD" == /home/user* ]] || [[ "$PWD" == /etc/nixos* ]]; then
|
||||||
|
project="${PWD/\/home\/user/\/home\/sandbox}"
|
||||||
|
else
|
||||||
|
project="$(ssh "$SSH_TARGET" 'mktemp -d -p /tmp claude-XXXXXX')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec ssh -t "$SSH_TARGET" claude-code "$project" "$@"
|
||||||
|
|
@ -3,7 +3,7 @@ set -euo pipefail
|
||||||
|
|
||||||
SSH_TARGET="sandbox@vsock%3"
|
SSH_TARGET="sandbox@vsock%3"
|
||||||
|
|
||||||
if [[ "$PWD" == /home/user* ]]; then
|
if [[ "$PWD" == /home/user* ]] || [[ "$PWD" == /etc/nixos* ]]; then
|
||||||
project="${PWD/\/home\/user/\/home\/sandbox}"
|
project="${PWD/\/home\/user/\/home\/sandbox}"
|
||||||
else
|
else
|
||||||
project="$(ssh "$SSH_TARGET" 'mktemp -d -p /tmp opencode-XXXXXX')"
|
project="$(ssh "$SSH_TARGET" 'mktemp -d -p /tmp opencode-XXXXXX')"
|
||||||
|
|
|
||||||
49
firejail.nix
49
firejail.nix
|
|
@ -4,59 +4,10 @@
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
|
||||||
agentProfile = pkgs.writeText "agent.profile" ''
|
|
||||||
whitelist /nix/store
|
|
||||||
whitelist /nix/var
|
|
||||||
read-only /nix/store
|
|
||||||
whitelist ''${HOME}/.cache/nix
|
|
||||||
whitelist ''${HOME}/.local/state/nix
|
|
||||||
whitelist /etc/nix
|
|
||||||
whitelist /etc/nixos
|
|
||||||
whitelist /nix/var/nix/daemon-socket
|
|
||||||
whitelist ''${HOME}/dev
|
|
||||||
whitelist ''${HOME}/work
|
|
||||||
whitelist ''${HOME}/.config
|
|
||||||
|
|
||||||
blacklist ''${HOME}
|
|
||||||
blacklist /etc/nixos/secrets
|
|
||||||
blacklist /etc/shadow
|
|
||||||
blacklist /etc/sudoers
|
|
||||||
blacklist /etc/sudoers.d
|
|
||||||
blacklist /root
|
|
||||||
|
|
||||||
caps.drop all
|
|
||||||
nonewprivs
|
|
||||||
noroot
|
|
||||||
seccomp
|
|
||||||
|
|
||||||
no3d
|
|
||||||
nodvd
|
|
||||||
notv
|
|
||||||
nou2f
|
|
||||||
novideo
|
|
||||||
|
|
||||||
|
|
||||||
ignore net none
|
|
||||||
|
|
||||||
machine-id
|
|
||||||
disable-mnt
|
|
||||||
private-dev
|
|
||||||
private-tmp
|
|
||||||
private-cache
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
programs.firejail = {
|
programs.firejail = {
|
||||||
enable = true;
|
enable = true;
|
||||||
wrappedBinaries = {
|
wrappedBinaries = {
|
||||||
claude = {
|
|
||||||
executable = "${pkgs.claude-code}/bin/claude";
|
|
||||||
profile = agentProfile;
|
|
||||||
extraArgs = [
|
|
||||||
"--private=~/.local/state/claude-home"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
chromium-sandbox = {
|
chromium-sandbox = {
|
||||||
executable = "${pkgs.chromium}/bin/chromium";
|
executable = "${pkgs.chromium}/bin/chromium";
|
||||||
profile = "${pkgs.firejail}/etc/firejail/chromium-browser.profile";
|
profile = "${pkgs.firejail}/etc/firejail/chromium-browser.profile";
|
||||||
|
|
|
||||||
|
|
@ -58,25 +58,38 @@
|
||||||
# "Xft.antialias" = 1;
|
# "Xft.antialias" = 1;
|
||||||
# "Xft.rgba" = "rgb";
|
# "Xft.rgba" = "rgb";
|
||||||
# };
|
# };
|
||||||
|
systemd.user.services.screenshot-latest = {
|
||||||
systemd.user.services.waypipe-socket = {
|
|
||||||
Unit = {
|
Unit = {
|
||||||
Description = "start waypipe client";
|
Description = "Update latest.png symlink";
|
||||||
After = [ "niri.service" ];
|
|
||||||
Requires = [ "niri.service" ];
|
|
||||||
};
|
|
||||||
Install = {
|
|
||||||
WantedBy = [ "graphical-session.target" ];
|
|
||||||
};
|
};
|
||||||
Service = {
|
Service = {
|
||||||
ExecStart = "${pkgs.waypipe}/bin/waypipe --socket /tmp/waypipe.sock client";
|
Type = "oneshot";
|
||||||
ExecStartPost = "${pkgs.acl}/bin/setfacl -m g:sandbox:rw /tmp/waypipe.sock";
|
ExecStart =
|
||||||
ExecStopPost = "${pkgs.coreutils}/bin/rm /tmp/waypipe.sock";
|
let
|
||||||
RuntimeDirectory = "waypipe";
|
screenshotDir = "${config.home.homeDirectory}/pictures/screenshots";
|
||||||
Type = "simple";
|
script = pkgs.writeShellScript "screenshot-latest-update" ''
|
||||||
Restart = "on-failure";
|
latest=$(${pkgs.coreutils}/bin/ls -t "${screenshotDir}"/*.png 2>/dev/null | head -1)
|
||||||
|
if [ -n "$latest" ]; then
|
||||||
|
${pkgs.coreutils}/bin/ln -sf "$latest" "${screenshotDir}/latest.png"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
"${script}";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.user.paths.screenshot-latest = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Watch screenshot directory for new files";
|
||||||
|
};
|
||||||
|
Install = {
|
||||||
|
WantedBy = [ "default.target" ];
|
||||||
|
};
|
||||||
|
Path = {
|
||||||
|
PathChanged = "${config.home.homeDirectory}/pictures/screenshots";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
sops.secrets."user/gpg/legacy_fnzr" = { };
|
sops.secrets."user/gpg/legacy_fnzr" = { };
|
||||||
home.activation.importGpgKey = config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
home.activation.importGpgKey = config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
if [[ -f "${config.sops.secrets."user/gpg/legacy_fnzr".path}" ]]; then
|
if [[ -f "${config.sops.secrets."user/gpg/legacy_fnzr".path}" ]]; then
|
||||||
|
|
@ -258,7 +271,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
programs = {
|
programs = {
|
||||||
opencode.enable = true;
|
# opencode.enable = true;
|
||||||
dank-material-shell.enable = true;
|
dank-material-shell.enable = true;
|
||||||
ssh = {
|
ssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
@ -364,11 +377,13 @@
|
||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
xrdb
|
xrdb
|
||||||
# (writeShellScriptBin "agent" ''
|
|
||||||
# machinectl shell agent@ ${waypipe}/bin/waypipe --socket /run/waypipe.sock server fish
|
|
||||||
# '')
|
|
||||||
(writeShellApplication {
|
(writeShellApplication {
|
||||||
name = "opencode-sandbox";
|
name = "claude-code";
|
||||||
|
runtimeInputs = [ openssh ];
|
||||||
|
text = builtins.readFile ../executables/claude-code.sh;
|
||||||
|
})
|
||||||
|
(writeShellApplication {
|
||||||
|
name = "opencode";
|
||||||
runtimeInputs = [ openssh ];
|
runtimeInputs = [ openssh ];
|
||||||
text = builtins.readFile ../executables/opencode.sh;
|
text = builtins.readFile ../executables/opencode.sh;
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ in
|
||||||
keepEnv = true;
|
keepEnv = true;
|
||||||
persist = true;
|
persist = true;
|
||||||
setEnv = [
|
setEnv = [
|
||||||
|
"HOME=/root"
|
||||||
"EDITOR"
|
"EDITOR"
|
||||||
"TERM"
|
"TERM"
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,13 @@ in
|
||||||
homeManagerModule
|
homeManagerModule
|
||||||
];
|
];
|
||||||
|
|
||||||
|
nix.settings.experimental-features = [
|
||||||
|
"nix-command"
|
||||||
|
"flakes"
|
||||||
|
];
|
||||||
|
nix.nixPath = [ "nixpkgs=${pkgs.path}" ];
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
|
||||||
sops.defaultSopsFile = ./secrets/sandbox.yaml;
|
sops.defaultSopsFile = ./secrets/sandbox.yaml;
|
||||||
sops.age.keyFile = "/.persist/secrets/age-keys.txt";
|
sops.age.keyFile = "/.persist/secrets/age-keys.txt";
|
||||||
sops.secrets."root-password-hash" = {
|
sops.secrets."root-password-hash" = {
|
||||||
|
|
@ -92,6 +99,7 @@ in
|
||||||
fd
|
fd
|
||||||
ripgrep
|
ripgrep
|
||||||
podman-compose
|
podman-compose
|
||||||
|
claude-code
|
||||||
];
|
];
|
||||||
|
|
||||||
environment.persistence."/.persist" = {
|
environment.persistence."/.persist" = {
|
||||||
|
|
@ -186,17 +194,17 @@ in
|
||||||
{
|
{
|
||||||
mountPoint = "/var";
|
mountPoint = "/var";
|
||||||
image = "var.img";
|
image = "var.img";
|
||||||
size = 256;
|
size = 2 * 1024;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
mountPoint = "/nix/.rw-store";
|
mountPoint = "/nix/.rw-store";
|
||||||
image = "nix-store-overlay.img";
|
image = "nix-store-overlay.img";
|
||||||
size = 512;
|
size = 2 * 1024;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
mountPoint = "/.persist";
|
mountPoint = "/.persist";
|
||||||
image = "persist.img";
|
image = "persist.img";
|
||||||
size = 1;
|
size = 2 * 1024;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
shares = [
|
shares = [
|
||||||
|
|
@ -212,6 +220,12 @@ in
|
||||||
source = "/home/user/work";
|
source = "/home/user/work";
|
||||||
mountPoint = "/home/sandbox/work";
|
mountPoint = "/home/sandbox/work";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
proto = "virtiofs";
|
||||||
|
tag = "nixos";
|
||||||
|
source = "/etc/nixos";
|
||||||
|
mountPoint = "/etc/nixos";
|
||||||
|
}
|
||||||
{
|
{
|
||||||
proto = "virtiofs";
|
proto = "virtiofs";
|
||||||
tag = "screenshots";
|
tag = "screenshots";
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
imports = [ ./wireguard.nix ];
|
imports = [
|
||||||
|
./wireguard.nix
|
||||||
|
./tailscale.nix
|
||||||
|
];
|
||||||
sops.secrets = {
|
sops.secrets = {
|
||||||
"wg0/conf".sopsFile = ./secrets/vpn.yaml;
|
"wg0/conf".sopsFile = ./secrets/vpn.yaml;
|
||||||
"wg-br0/conf".sopsFile = ./secrets/vpn.yaml;
|
"wg-br0/conf".sopsFile = ./secrets/vpn.yaml;
|
||||||
|
|
@ -13,6 +16,13 @@
|
||||||
"wg-uk0/conf".sopsFile = ./secrets/vpn.yaml;
|
"wg-uk0/conf".sopsFile = ./secrets/vpn.yaml;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.tailscale-netns = {
|
||||||
|
enable = true;
|
||||||
|
instances.work = {
|
||||||
|
extraDaemonFlags = [ "--accept-routes=false" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
hostName = hostname;
|
hostName = hostname;
|
||||||
networkmanager.enable = false;
|
networkmanager.enable = false;
|
||||||
|
|
|
||||||
196
tailscale.nix
196
tailscale.nix
|
|
@ -10,137 +10,24 @@ with lib;
|
||||||
let
|
let
|
||||||
cfg = config.services.tailscale-netns;
|
cfg = config.services.tailscale-netns;
|
||||||
|
|
||||||
instanceOptions =
|
instanceOptions = {
|
||||||
{ name, ... }:
|
options.extraDaemonFlags = mkOption {
|
||||||
{
|
type = types.listOf types.str;
|
||||||
options = {
|
default = [ ];
|
||||||
authKeyFile = mkOption {
|
description = "Extra flags passed to `tailscaled`.";
|
||||||
type = types.nullOr types.path;
|
|
||||||
default = null;
|
|
||||||
description = "Path to file containing a Tailscale auth key.";
|
|
||||||
};
|
|
||||||
|
|
||||||
hostname = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Tailscale hostname for this instance.
|
|
||||||
Defaults to "<system-hostname>-<instance-name>".
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
advertiseRoutes = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ ];
|
|
||||||
description = "Subnet routes to advertise (e.g. [ \"10.0.0.0/24\" ]).";
|
|
||||||
};
|
|
||||||
|
|
||||||
dns = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Bootstrap DNS server for the namespace before Tailscale is connected.
|
|
||||||
Defaults to `1.1.1.1`. Only used for the initial coordination server
|
|
||||||
connection; once Tailscale is up it manages DNS inside the namespace.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
advertiseExitNode = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = "Whether to advertise this node as an exit node.";
|
|
||||||
};
|
|
||||||
|
|
||||||
acceptRoutes = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = "Whether to accept routes advertised by other nodes.";
|
|
||||||
};
|
|
||||||
|
|
||||||
exitNode = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = "Tailscale IP or hostname of exit node to use.";
|
|
||||||
};
|
|
||||||
|
|
||||||
extraUpFlags = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ ];
|
|
||||||
description = "Extra flags passed to `tailscale up`.";
|
|
||||||
};
|
|
||||||
|
|
||||||
extraDaemonFlags = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ ];
|
|
||||||
description = "Extra flags passed to `tailscaled`.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
mkUpFlags =
|
|
||||||
instanceCfg:
|
|
||||||
let
|
|
||||||
hostFlag = optional (
|
|
||||||
instanceCfg.hostname != null
|
|
||||||
) "--hostname=${escapeShellArg instanceCfg.hostname}";
|
|
||||||
routeFlags = map (r: "--advertise-routes=${escapeShellArg r}") instanceCfg.advertiseRoutes;
|
|
||||||
exitFlag = optional instanceCfg.advertiseExitNode "--advertise-exit-node";
|
|
||||||
acceptFlag = optional instanceCfg.acceptRoutes "--accept-routes";
|
|
||||||
exitNodeFlag = optional (
|
|
||||||
instanceCfg.exitNode != null
|
|
||||||
) "--exit-node=${escapeShellArg instanceCfg.exitNode}";
|
|
||||||
in
|
|
||||||
hostFlag ++ routeFlags ++ exitFlag ++ acceptFlag ++ exitNodeFlag ++ instanceCfg.extraUpFlags;
|
|
||||||
|
|
||||||
mkAutoconnectScript =
|
|
||||||
name: instanceCfg:
|
|
||||||
pkgs.writers.writeBash "tailscale-autoconnect-${name}" ''
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
TS="${pkgs.tailscale}/bin/tailscale"
|
|
||||||
SOCKET="/run/tailscale-${name}/tailscaled.sock"
|
|
||||||
TS_ARGS="--socket $SOCKET"
|
|
||||||
|
|
||||||
get_state() {
|
|
||||||
$TS $TS_ARGS status --json --peers=false 2>/dev/null | ${pkgs.jq}/bin/jq -r '.BackendState // "NoState"'
|
|
||||||
}
|
|
||||||
|
|
||||||
last_state=""
|
|
||||||
while state="$(get_state)"; do
|
|
||||||
if [[ "$state" != "$last_state" ]]; then
|
|
||||||
case "$state" in
|
|
||||||
NeedsLogin|NeedsMachineAuth|Stopped)
|
|
||||||
echo "[tailscale-netns:${name}] server needs authentication, sending auth key"
|
|
||||||
$TS $TS_ARGS up \
|
|
||||||
--auth-key "$(cat ${instanceCfg.authKeyFile})" \
|
|
||||||
${escapeShellArgs (mkUpFlags instanceCfg)}
|
|
||||||
;;
|
|
||||||
Running)
|
|
||||||
echo "[tailscale-netns:${name}] tailscale is running"
|
|
||||||
${pkgs.systemd}/bin/systemd-notify --ready
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
NoState)
|
|
||||||
echo "[tailscale-netns:${name}] tailscaled not ready yet"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "[tailscale-netns:${name}] waiting for Running (state=$state)"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
last_state="$state"
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
'';
|
|
||||||
|
|
||||||
mkNetnsSetup =
|
mkNetnsSetup =
|
||||||
index: name: instanceCfg:
|
index: name: instanceCfg:
|
||||||
let
|
let
|
||||||
ns = "ts-${name}ns";
|
ns = "ts-${name}ns";
|
||||||
hostIP = "10.201.${toString index}.1/24";
|
hostAddr = "10.201.${toString index}.1";
|
||||||
|
hostIP = "${hostAddr}/24";
|
||||||
nsIP = "10.201.${toString index}.2/24";
|
nsIP = "10.201.${toString index}.2/24";
|
||||||
vethHost = "veth-ts-${name}";
|
vethHost = "veth-ts-${name}";
|
||||||
vethNS = "veth-ts-${name}-ns";
|
vethNS = "veth-ts-${name}-ns";
|
||||||
|
fallbackLines = concatMapStrings (n: " echo nameserver ${n}\n") config.networking.nameservers;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
"netns@ts-${name}" = {
|
"netns@ts-${name}" = {
|
||||||
|
|
@ -153,12 +40,42 @@ let
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
${pkgs.coreutils}/bin/mkdir -p /etc/netns/${ns}
|
${pkgs.coreutils}/bin/mkdir -p /etc/netns/${ns}
|
||||||
|
|
||||||
# Build resolv.conf for the netns. Skip loopback addresses
|
|
||||||
# (systemd-resolved stub at 127.0.0.53 is not reachable from the netns).
|
|
||||||
NS_DNS="${if instanceCfg.dns != null then instanceCfg.dns else "1.1.1.1"}"
|
|
||||||
{
|
{
|
||||||
${pkgs.gawk}/bin/awk '$1 == "nameserver" && $2 !~ /^127\./ && $2 != "::1" {print}' /etc/resolv.conf
|
dns_found=0
|
||||||
echo "nameserver $NS_DNS"
|
if ${pkgs.systemd}/bin/resolvectl dns 2>/dev/null; then
|
||||||
|
saved_ifs="$IFS"
|
||||||
|
IFS=$'\n'
|
||||||
|
for line in $(${pkgs.systemd}/bin/resolvectl dns 2>/dev/null); do
|
||||||
|
case "$line" in
|
||||||
|
*:*)
|
||||||
|
server=$(echo "$line" | ${pkgs.gnused}/bin/sed 's/^[^:]*: //')
|
||||||
|
case "$server" in
|
||||||
|
127.*|::1|"") ;;
|
||||||
|
*) echo "nameserver $server"; dns_found=1 ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
IFS="$saved_ifs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$dns_found" -eq 0 ]; then
|
||||||
|
while IFS= read -r rline; do
|
||||||
|
case "$rline" in
|
||||||
|
nameserver*)
|
||||||
|
ip=$(echo "$rline" | ${pkgs.gnused}/bin/sed 's/^nameserver //')
|
||||||
|
case "$ip" in
|
||||||
|
127.*|::1) ;;
|
||||||
|
*) echo "$rline"; dns_found=1 ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < /etc/resolv.conf
|
||||||
|
fi
|
||||||
|
${optionalString (fallbackLines != "") ''
|
||||||
|
if [ "$dns_found" -eq 0 ]; then
|
||||||
|
${fallbackLines} fi
|
||||||
|
''}
|
||||||
} > /etc/netns/${ns}/resolv.conf
|
} > /etc/netns/${ns}/resolv.conf
|
||||||
|
|
||||||
${pkgs.iproute2}/bin/ip netns add ${ns}
|
${pkgs.iproute2}/bin/ip netns add ${ns}
|
||||||
|
|
@ -171,6 +88,7 @@ let
|
||||||
${pkgs.iproute2}/bin/ip -n ${ns} addr add ${nsIP} dev ${vethNS}
|
${pkgs.iproute2}/bin/ip -n ${ns} addr add ${nsIP} dev ${vethNS}
|
||||||
${pkgs.iproute2}/bin/ip -n ${ns} link set ${vethNS} up
|
${pkgs.iproute2}/bin/ip -n ${ns} link set ${vethNS} up
|
||||||
${pkgs.iproute2}/bin/ip -n ${ns} link set lo up
|
${pkgs.iproute2}/bin/ip -n ${ns} link set lo up
|
||||||
|
${pkgs.iproute2}/bin/ip -n ${ns} route add default via ${hostAddr}
|
||||||
|
|
||||||
${pkgs.iptables}/bin/iptables -t nat -C POSTROUTING -s ${nsIP} -j MASQUERADE 2>/dev/null \
|
${pkgs.iptables}/bin/iptables -t nat -C POSTROUTING -s ${nsIP} -j MASQUERADE 2>/dev/null \
|
||||||
|| ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s ${nsIP} -j MASQUERADE
|
|| ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s ${nsIP} -j MASQUERADE
|
||||||
|
|
@ -231,10 +149,7 @@ in
|
||||||
description = "Tailscale instances configured in separate network namespaces.";
|
description = "Tailscale instances configured in separate network namespaces.";
|
||||||
example = literalExpression ''
|
example = literalExpression ''
|
||||||
{
|
{
|
||||||
work = {
|
work = {};
|
||||||
authKeyFile = "/run/secrets/ts-work/authkey";
|
|
||||||
acceptRoutes = true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
@ -247,25 +162,12 @@ in
|
||||||
index: elem:
|
index: elem:
|
||||||
let
|
let
|
||||||
name = elem.name;
|
name = elem.name;
|
||||||
instanceCfg = elem.value;
|
units = mkNetnsSetup index name elem.value;
|
||||||
units = mkNetnsSetup index name instanceCfg;
|
|
||||||
in
|
in
|
||||||
[
|
[
|
||||||
units."netns@ts-${name}"
|
{ name = "netns@ts-${name}"; value = units."netns@ts-${name}"; }
|
||||||
units."tailscaled-${name}"
|
{ name = "tailscaled-${name}"; value = units."tailscaled-${name}"; }
|
||||||
]
|
]
|
||||||
++ optional (instanceCfg.authKeyFile != null) {
|
|
||||||
"tailscaled-autoconnect-${name}" = {
|
|
||||||
description = "Tailscale autoconnect for instance ${name}";
|
|
||||||
after = [ "tailscaled-${name}.service" ];
|
|
||||||
requires = [ "tailscaled-${name}.service" ];
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "notify";
|
|
||||||
};
|
|
||||||
script = "${mkAutoconnectScript name instanceCfg}";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
) (attrsToList cfg.instances)
|
) (attrsToList cfg.instances)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue