xray: config via sops, private routes spliced into sing-box at runtime

This commit is contained in:
Sergei Poljanski 2026-07-14 12:24:33 +04:00
commit 8458e0645d
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
3 changed files with 44 additions and 16 deletions

View file

@ -1,7 +1,6 @@
{ config, pkgs, lib, ... }: { config, pkgs, lib, ... }:
let let
xrayServer = (import ./private/xray-routes.nix).xrayServer;
wifiIf = "wlp194s0"; wifiIf = "wlp194s0";
in in
{ {
@ -87,7 +86,8 @@ in
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "killswitch-up" '' ExecStart = pkgs.writeShellScript "killswitch-up" ''
${pkgs.nftables}/bin/nft -f - <<'EOF' XRAY_SERVER=$(cat ${config.sops.secrets."xray-endpoint".path})
${pkgs.nftables}/bin/nft -f - <<EOF
table inet killswitch { table inet killswitch {
set captive_portals_v4 { set captive_portals_v4 {
type ipv4_addr; flags interval; type ipv4_addr; flags interval;
@ -107,7 +107,7 @@ in
ct state established,related accept ct state established,related accept
oif "lo" accept oif "lo" accept
oif { "wg0", "wg2", "tun0" } accept oif { "wg0", "wg2", "tun0" } accept
ip daddr ${xrayServer} accept ip daddr $XRAY_SERVER accept
udp dport 67 accept # DHCP client udp dport 67 accept # DHCP client
udp dport 547 accept # DHCPv6 udp dport 547 accept # DHCPv6
udp dport { 53, 5353 } ip daddr 127.0.0.0/8 accept udp dport { 53, 5353 } ip daddr 127.0.0.0/8 accept

View file

@ -1,6 +0,0 @@
# Stub. Real values kept locally via:
# git update-index --skip-worktree modules/private/xray-routes.nix
{
xrayServer = "0.0.0.0";
routeExcludeAddress = [ ];
}

View file

@ -2,10 +2,40 @@
{ config, pkgs, lib, ... }: { config, pkgs, lib, ... }:
let let
private = import ./private/xray-routes.nix; # Pin Firefox DevEdition to nixpkgs 2026-07-05: 152.0b8 is cached there,
xrayServer = private.xrayServer; # whereas the current nixpkgs ships the same version uncached with a broken
# source build (webrender COUNT identifier error). Drop this pin once a
# newer, cached devedition lands upstream.
pkgsFirefox = import (builtins.fetchTree {
type = "github";
owner = "NixOS";
repo = "nixpkgs";
rev = "d407951447dcd00442e97087bf374aad70c04cea";
}) { inherit (pkgs.stdenv.hostPlatform) system; config.allowUnfree = true; };
in in
{ {
# Config is sops-encrypted; decrypted to /run/secrets at activation and
# handed to the DynamicUser service via LoadCredential (stays root-only 0400).
sops.secrets."xray-config.json" = {
sopsFile = "/etc/nixos/secrets/xray-config.json";
format = "json";
key = "";
restartUnits = [ "xray.service" ];
};
# Private endpoints/routes, spliced into the sing-box config at runtime
# via _secret (values never enter the nix store or the repo in plaintext).
sops.secrets."xray-endpoint" = {
sopsFile = "/etc/nixos/secrets/network.yaml";
key = "xray-endpoint";
restartUnits = [ "sing-box.service" ];
};
sops.secrets."route-exclude" = {
sopsFile = "/etc/nixos/secrets/network.yaml";
key = "route-exclude";
restartUnits = [ "sing-box.service" ];
};
# Xray — VLESS+Reality with post-quantum encryption # Xray — VLESS+Reality with post-quantum encryption
systemd.services.xray = { systemd.services.xray = {
description = "Xray Proxy"; description = "Xray Proxy";
@ -13,13 +43,13 @@ in
wants = [ "network-online.target" ]; wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
ExecStart = "${pkgs.xray}/bin/xray run -config /etc/xray/config.json"; ExecStart = "${pkgs.xray}/bin/xray run -config %d/config.json";
LoadCredential = [ "config.json:${config.sops.secrets."xray-config.json".path}" ];
Restart = "on-failure"; Restart = "on-failure";
RestartSec = 5; RestartSec = 5;
DynamicUser = true; DynamicUser = true;
NoNewPrivileges = true; NoNewPrivileges = true;
ProtectSystem = "strict"; ProtectSystem = "strict";
ReadOnlyPaths = [ "/etc/xray" ];
}; };
}; };
@ -36,7 +66,11 @@ in
address = [ "198.18.0.1/15" "fdfe:dcba:9876::1/126" ]; address = [ "198.18.0.1/15" "fdfe:dcba:9876::1/126" ];
auto_route = true; auto_route = true;
strict_route = true; strict_route = true;
route_exclude_address = [ "${xrayServer}/32" ] ++ private.routeExcludeAddress; # File contains a JSON array (xray server + wg endpoints/nets + overlays)
route_exclude_address = {
_secret = config.sops.secrets."route-exclude".path;
quote = false;
};
stack = "gvisor"; stack = "gvisor";
}]; }];
@ -83,7 +117,7 @@ in
{ {
action = "route"; action = "route";
outbound = "direct"; outbound = "direct";
ip_cidr = [ "${xrayServer}/32" ]; ip_cidr = [ { _secret = config.sops.secrets."xray-endpoint".path; } ];
} }
{ {
action = "route"; action = "route";
@ -109,7 +143,7 @@ in
# Firefox dev edition # Firefox dev edition
programs.firefox = { programs.firefox = {
enable = true; enable = true;
package = pkgs.firefox-devedition; package = pkgsFirefox.firefox-devedition;
policies = { policies = {
# Estonian ID: load OpenSC PKCS#11 for TLS client-cert auth, # Estonian ID: load OpenSC PKCS#11 for TLS client-cert auth,
# and force-install the Web eID extension (talks to web-eid-app host). # and force-install the Web eID extension (talks to web-eid-app host).