diff --git a/servers/mebsuta/default.nix b/servers/mebsuta/default.nix index 631c088..3b22082 100644 --- a/servers/mebsuta/default.nix +++ b/servers/mebsuta/default.nix @@ -7,6 +7,7 @@ in imports = [ inputs.serokell-nix.nixosModules.hetzner-cloud inputs.subspace.nixosModule + ./xray.nix ]; networking.hostName = "mebsuta"; diff --git a/servers/mebsuta/xray.nix b/servers/mebsuta/xray.nix new file mode 100644 index 0000000..121f439 --- /dev/null +++ b/servers/mebsuta/xray.nix @@ -0,0 +1,115 @@ +{ config, lib, pkgs, ... }: + +let + vs = config.vault-secrets.secrets; + dataDir = "/var/lib/xray"; + xrayPort = 9433; +in +{ + networking.firewall.allowedTCPPorts = [ + xrayPort + ]; + users.users.xray = { + isSystemUser = true; + group = "xray"; + home = dataDir; + createHome = true; + }; + users.groups.xray = {}; + vault-secrets.secrets.xray = { + user = "xray"; + group = "xray"; + }; + services.xray = { + enable = true; + settingsFile = "${dataDir}/config.json"; + }; + systemd.services.xray = let + privateKeyPlaceholder = ""; + clientIdPlaceholder = ""; + shortIdPlaceholder = ""; + # Settings file without secrets that is safe to store in /nix/store + settings = pkgs.writeText "settings-without-secrets" (builtins.toJSON { + log = { + loglevel = "warning"; + }; + inbounds = [ + { port = xrayPort; + protocol = "vless"; + tag = "vless"; + sniffing = { + enable = true; + destOverrides = ["http" "tls"]; + }; + settings = { + clients = [ + { + id = clientIdPlaceholder; + flow = "xtls-rprx-vision"; + } + ]; + decryption = "none"; + }; + streamSettings = { + network = "tcp"; + security = "reality"; + realitySettings = { + show = false; + dest = "serokell.io:443"; + serverNames = [ + "serokell.io" + ]; + privateKey = privateKeyPlaceholder; + shortIds = [ shortIdPlaceholder ]; + }; + }; + } + ]; + outbounds = [ + { protocol = "freedom"; + tag = "direct"; + } + { protocol = "blackhole"; + tag = "block"; + } + # IPv4-only outbound for Google + { tag = "IPv4"; + protocol = "freedom"; + settings = { + domainStrategy = "UseIPv4"; + }; + } + ]; + routing = { + domainStrategy = "IPIfNonMatch"; + domainMatcher = "hybrid"; + # Force IPv4 for Google, otherwise it keeps giving 403 + rules = [ + { type = "field"; + outboundTag = "IPv4"; + domain = [ "geosite:google" ]; + } + ]; + balancers = []; + }; + }); + in { + # Add secrets from Vault to the config file + preStart = '' + cp --no-preserve=mode "${settings}" "${config.services.xray.settingsFile}" + private_key="$(cat "${vs.xray}/private-key")" + client_id="$(cat "${vs.xray}/client-id")" + short_id="$(cat "${vs.xray}/short-id")" + ${pkgs.gnused}/bin/sed -i -e "s/${privateKeyPlaceholder}/$private_key/" -e "s/${clientIdPlaceholder}/$client_id/" -e "s/${shortIdPlaceholder}/$short_id/" \ + "${config.services.xray.settingsFile}" + ''; + serviceConfig = { + # In nixpkgs this service uses DynamicUser=true. However, this doesn't work with vault-secrets because the user that needs + # to read secrets is not know in prior. + DynamicUser = lib.mkForce "false"; + User = "xray"; + Group = "xray"; + ReadWritePaths = [ "/var/lib/xray" ]; + }; + }; +}