diff --git a/devices/srv3/default.nix b/devices/srv3/default.nix index 192584d5..998c5fa5 100644 --- a/devices/srv3/default.nix +++ b/devices/srv3/default.nix @@ -113,7 +113,7 @@ inputs: gitea = {}; grafana = {}; fail2ban = {}; - xray.server = {}; + xray = { server = {}; xmuPersist = {}; }; podman = {}; peertube = {}; nginx.applications.webdav.instances."webdav.chn.moe" = {}; diff --git a/devices/srv3/secrets.yaml b/devices/srv3/secrets.yaml index d28bbeb4..3608c2b4 100644 --- a/devices/srv3/secrets.yaml +++ b/devices/srv3/secrets.yaml @@ -84,6 +84,8 @@ peertube: open-webui: openai: ENC[AES256_GCM,data:5B1wPAOx3GsLDoYBKHWFzoyXFmn93fdcq6UC2rCt/P5zYLA4VNzfsp0=,iv:Y2gTLCmwB5wY4dhN73HRvTqSMVXbAEd+RjRbgUEuTeE=,tag:vcfNhXpG0C3twFBsm7PHwA==,type:str] webui: ENC[AES256_GCM,data:Lg32DZ5GC+AYzWc4WloNMQlnpsqW67s5/kXzYwE=,iv:ECncgdYoLkX9GUOX26MXFSO8JOZahUDjTdKV87IRNJ8=,tag:J/5tTR3MI0iGIVDrlacYEg==,type:str] +xray-xmu-client: + cookie: ENC[AES256_GCM,data:z1KI3CUfPqyiI/B/qgrNhg==,iv:QEjUlMkkF/fdwwEIGiJJ5UxFGw869qAnpApmWaRn3GY=,tag:EbdJsYEslwJbARxDoEWrDA==,type:str] sops: age: - recipient: age19ax6vm3pv8rph5tq3mmehd9sy9jk823tw8svsd790r0lkslycquqvlwz9m @@ -104,7 +106,7 @@ sops: d0h3aDh5QXFZYWJFdmNVYnJxQ3pBeVUKTl0XVvtwJcz+RpSylgDPl/R8msInxvWX eQGmrDHibeE1V+KSDiuNzC4MVRIrOnh1beHrhnVQ86HwPVgJqs2FoQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-07-27T07:31:29Z" - mac: ENC[AES256_GCM,data:SKxLkMb07dubyKuLKXlnmUIXnzFQh6fTzN6BmsQ/FJzIOtT9T3G7WnoMkp71VhXDCMWCxiF5MvnX/EjrcquZjxnfPtib6OaAx+XFFcpPMLWSGJY+HtmECLYQncZpMJGC4hW2cj7T/iCHKAyyV9OGYeyNvRoZNpPRlpUL6VbN1UI=,iv:o3lEXl30PYCEUpe6jTXXB0AhuBwGFUaN88drFeeh7pc=,tag:Zbr2POkKq5/zk2AgHbTNMg==,type:str] + lastmodified: "2025-08-01T05:55:35Z" + mac: ENC[AES256_GCM,data:MEgCbEJ/bwx3EVWVYQjb1RbNx8OlJkqelHPbMG5DzSRgcBcllptTFCanLIPZrg4FihkHx3b41Q5xsCXbras1njh4R1FeyLVVGZH7pYjZaPF2MRaD8nYeCHKlItWUVvHQTf5bRrTOOQoo4Kmn2by/xdMWwZlZwt0aBoGnEYBxGf0=,iv:lrPlg8cM5qPgQPpIUHF6WBVglTe9YQtI28hfJSgJ1vU=,tag:tjucRsf+tt9F03Mhjf+jeg==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2 diff --git a/devices/vps6/default.nix b/devices/vps6/default.nix index 44677f92..23afe077 100644 --- a/devices/vps6/default.nix +++ b/devices/vps6/default.nix @@ -26,7 +26,7 @@ inputs: services = { sshd = {}; - xray.server = {}; + xray = { server = {}; xmuPersist = {}; }; nginx = { streamProxy.map = diff --git a/devices/vps6/secrets.yaml b/devices/vps6/secrets.yaml index ead78b2c..19f55ca7 100644 --- a/devices/vps6/secrets.yaml +++ b/devices/vps6/secrets.yaml @@ -44,6 +44,8 @@ send: coturn: auth-secret: ENC[AES256_GCM,data:50KqO4GQ1ERbCnK4IjYu6aywT+IPMtVlTzh/TE4MwWApU4pO9yqz25ENGUAKRLi4p+Ecug+Rn3InRl1b+q6bAQ==,iv:SgHkHvHg/+yA1Z5E9effgCnZMVXv5amGNUsVKErai54=,tag:PoYLV9Xr0IXXsA39n7wiTQ==,type:str] wireguard: ENC[AES256_GCM,data:5M7EAy/6+2UASWkjxE0Jrxwl0aNdAVZaUjQnD1wU3YvOAQ/c2DSL8hVtKf8=,iv:a2tXFf1+aP0JhdNtzP8e82KJ71m2o8nx+G0wIx4VMig=,tag:l4TS4QBz2fIkC9/GnZgHnQ==,type:str] +xray-xmu-client: + cookie: ENC[AES256_GCM,data:RZ2WFnsX7s/PVqA7ZKhGqw==,iv:CknFoAcHIiIwJI1IEXkFdWXcOCAZr50pfwmQN72OI8o=,tag:w2pNU1APxlSQsGMIEdE2OA==,type:str] sops: age: - recipient: age19ax6vm3pv8rph5tq3mmehd9sy9jk823tw8svsd790r0lkslycquqvlwz9m @@ -64,7 +66,7 @@ sops: ZXFTU3ZCaW1pTVh0RUJzdDdGdHlPYTgK2mlgcX2kEc8+2UDdBnhUm6IIuh8V6agW ooxH9OEPXUVI/4JcDo4v8ZUhAyU1ehLH0Ef7PJCChOZe2KZmWSNbhA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-06-12T23:51:02Z" - mac: ENC[AES256_GCM,data:3QxWxinb3a7jvmHJO1kcePNwd/igurjFWVJw/sGKBuZpo47LU+W8132b9GpKs79AedDa5BM5yu0XN+CPrkviMcNuX5a3lLy8oI22a1N8fuKjEehld1Jq/boitGIsgJgb/M0Hn6yIq1ytuWuxoj2cOvmkEfNuyWRew+htI4DhJ/E=,iv:OyCWfcn218oaA970T9miIWIGSwOFeUbtWI0xO/02Hrw=,tag:c8riJplInFN1ZSPH3ze0QQ==,type:str] + lastmodified: "2025-08-01T05:54:47Z" + mac: ENC[AES256_GCM,data:OtHwr58A1UOfYxQR88ay76fWmAyWPl5YtNbAiv0LXPLZPRtLGBJKuTjMaHr17AMepFZ+u5IPV2r8z1AUDj0opLXlv3Ik/DJ2PCcQTOBH+/lnSgzJKWfdCip9/wFR6N3dT0PKKLuBiURB9ZCYmtnq6E5+Guadc6ATYDSEpwbENZQ=,iv:kXsYMGjAtUlv1UqFU8Xv0zagohnpHkzSI72mq5HKY7k=,tag:KR+1A8l2VvbzDZV/00hbJg==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2 diff --git a/modules/services/xray/xmuClient.nix b/modules/services/xray/xmuClient.nix index fe5e2c3b..be09e256 100644 --- a/modules/services/xray/xmuClient.nix +++ b/modules/services/xray/xmuClient.nix @@ -46,20 +46,7 @@ inputs: security = "tls"; xhttpSettings = { - path = - let - inherit (xmuClient) hostname; - paddedLength = ((builtins.div ((builtins.stringLength hostname) - 1) 16) + 1) * 16; - paddedString = builtins.concatStringsSep "" - (builtins.genList - (n: if n < builtins.stringLength hostname then builtins.substring n 1 hostname else "0") - paddedLength); - paddedHex = inputs.pkgs.localPackages.aes128CfbHex - { data = hostname; key = "wrdvpnisthebest!"; iv = "wrdvpnisthebest!"; }; - prefix = builtins.concatStringsSep "" (builtins.map - (c: inputs.lib.toHexString (inputs.lib.strings.charToInt c)) - (inputs.lib.stringToCharacters "wrdvpnisthebest!")); - in "/https/${prefix}${paddedHex}/xsession"; + path = "${inputs.pkgs.localPackages.webvpnPath xmuClient.hostname}/xsession"; mode = "packet-up"; security = "tls"; extra.headers.Cookie = diff --git a/modules/services/xray/xmuPersist.nix b/modules/services/xray/xmuPersist.nix new file mode 100644 index 00000000..bab686e4 --- /dev/null +++ b/modules/services/xray/xmuPersist.nix @@ -0,0 +1,57 @@ +inputs: +{ + options.nixos.services.xray.xmuPersist = let inherit (inputs.lib) mkOption types; in mkOption + { + type = types.nullOr (types.submodule (submoduleInputs: { options = + { + keepAliveHost = mkOption { type = types.nonEmptyStr; default = "blog.chn.moe"; }; + };})); + default = null; + }; + config = let inherit (inputs.config.nixos.services.xray) xmuPersist; in inputs.lib.mkIf (xmuPersist != null) + { + nixos.system.sops = + { + templates."xray-xmu-persist-cookie.txt" = + { + owner = inputs.config.users.users.v2ray.name; + group = inputs.config.users.users.v2ray.group; + content = let cookie = inputs.config.nixos.system.sops.placeholder."xray-xmu-client/cookie"; in + '' + .webvpn.xmu.edu.cn TRUE / TRUE 0 wengine_vpn_ticketwebvpn_xmu_edu_cn ${cookie} + webvpn.xmu.edu.cn FALSE / TRUE 0 show_vpn 0 + webvpn.xmu.edu.cn FALSE / TRUE 0 heartbeat 1 + webvpn.xmu.edu.cn FALSE / TRUE 0 show_faq 0 + webvpn.xmu.edu.cn FALSE / FALSE 0 refresh 0 + ''; + }; + secrets."xray-xmu-client/cookie" = {}; + }; + systemd = + { + services.xray-xmu-persist = + { + script = + let + curl = "${inputs.pkgs.curl}/bin/curl"; + cookie = inputs.config.nixos.system.sops.templates."xray-xmu-persist-cookie.txt".path; + in + '' + ${curl} -s -o /dev/null -w "%{http_code}\n" -b ${cookie} \ + "https://webvpn.xmu.edu.cn${inputs.pkgs.localPackages.webvpnPath xmuPersist.keepAliveHost}/"; + ''; + serviceConfig = { Type = "oneshot"; User = "v2ray"; Group = "v2ray"; }; + }; + timers.xray-xmu-persist = + { + wantedBy = [ "timers.target" ]; + timerConfig = { OnCalendar = "*-*-* *:*:00"; Unit = "xray-xmu-persist.service"; }; + }; + }; + users = + { + users.v2ray = { uid = inputs.config.nixos.user.uid.v2ray; group = "v2ray"; isSystemUser = true; }; + groups.v2ray.gid = inputs.config.nixos.user.gid.v2ray; + }; + }; +} diff --git a/packages/default.nix b/packages/default.nix index 0a35680f..e347d646 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -165,4 +165,17 @@ inputs: rec hexEncryptedFile = inputs.pkgs.runCommand "aes128cfb-hex-encrypted" {} "${xxd} -p ${encryptedFile} | ${tr} -d '\n' > $out"; in builtins.readFile hexEncryptedFile; + webvpnPath = hostname: + let + paddedLength = ((builtins.div ((builtins.stringLength hostname) - 1) 16) + 1) * 16; + paddedString = builtins.concatStringsSep "" + (builtins.genList + (n: if n < builtins.stringLength hostname then builtins.substring n 1 hostname else "0") + paddedLength); + paddedHex = aes128CfbHex + { data = hostname; key = "wrdvpnisthebest!"; iv = "wrdvpnisthebest!"; }; + prefix = builtins.concatStringsSep "" (builtins.map + (c: inputs.pkgs.lib.toHexString (inputs.pkgs.lib.strings.charToInt c)) + (inputs.pkgs.lib.stringToCharacters "wrdvpnisthebest!")); + in "/https/${prefix}${paddedHex}"; }