mirror of
https://github.com/CHN-beta/nixos.git
synced 2026-01-12 04:39:23 +08:00
Compare commits
1 Commits
e067993a14
...
dae
| Author | SHA1 | Date | |
|---|---|---|---|
| 83788181c3 |
@@ -60,13 +60,7 @@ inputs:
|
||||
shares = { home.path = "/home"; root.path = "/"; };
|
||||
};
|
||||
sshd.enable = true;
|
||||
xray.client =
|
||||
{
|
||||
enable = true;
|
||||
serverAddress = "74.211.99.69";
|
||||
serverName = "vps6.xserver.chn.moe";
|
||||
dns.extraInterfaces = [ "docker0" ];
|
||||
};
|
||||
xray.client.enable = true;
|
||||
xrdp = { enable = true; hostname = [ "nas.chn.moe" "office.chn.moe" ]; };
|
||||
groupshare.enable = true;
|
||||
smartd.enable = true;
|
||||
|
||||
@@ -93,26 +93,7 @@ inputs:
|
||||
};
|
||||
};
|
||||
sshd.enable = true;
|
||||
xray.client =
|
||||
{
|
||||
enable = true;
|
||||
serverAddress = "74.211.99.69";
|
||||
serverName = "vps6.xserver.chn.moe";
|
||||
dns =
|
||||
{
|
||||
extraInterfaces = [ "docker0" ];
|
||||
hosts =
|
||||
{
|
||||
"mirism.one" = "74.211.99.69";
|
||||
"beta.mirism.one" = "74.211.99.69";
|
||||
"ng01.mirism.one" = "74.211.99.69";
|
||||
"debug.mirism.one" = "127.0.0.1";
|
||||
"initrd.vps6.chn.moe" = "74.211.99.69";
|
||||
"nix-store.chn.moe" = "127.0.0.1";
|
||||
"initrd.nas.chn.moe" = "192.168.1.185";
|
||||
};
|
||||
};
|
||||
};
|
||||
xray.client.enable = true;
|
||||
firewall.trustedInterfaces = [ "virbr0" "waydroid0" ];
|
||||
acme = { enable = true; cert."debug.mirism.one" = {}; };
|
||||
frpClient =
|
||||
|
||||
@@ -50,13 +50,7 @@ inputs:
|
||||
snapper.enable = true;
|
||||
fontconfig.enable = true;
|
||||
sshd.enable = true;
|
||||
xray.client =
|
||||
{
|
||||
enable = true;
|
||||
serverAddress = "74.211.99.69";
|
||||
serverName = "vps6.xserver.chn.moe";
|
||||
dns.extraInterfaces = [ "docker0" ];
|
||||
};
|
||||
xray.client.enable = true;
|
||||
firewall.trustedInterfaces = [ "virbr0" ];
|
||||
wireguard =
|
||||
{
|
||||
|
||||
@@ -69,13 +69,7 @@ inputs:
|
||||
snapper.enable = true;
|
||||
fontconfig.enable = true;
|
||||
sshd = { enable = true; passwordAuthentication = true; };
|
||||
xray.client =
|
||||
{
|
||||
enable = true;
|
||||
serverAddress = "74.211.99.69";
|
||||
serverName = "vps6.xserver.chn.moe";
|
||||
dns.extraInterfaces = [ "docker0" ];
|
||||
};
|
||||
xray.client.enable = true;
|
||||
firewall.trustedInterfaces = [ "virbr0" "waydroid0" ];
|
||||
smartd.enable = true;
|
||||
beesd =
|
||||
|
||||
@@ -61,13 +61,7 @@ inputs:
|
||||
snapper.enable = false;
|
||||
fontconfig.enable = true;
|
||||
sshd = { enable = true; passwordAuthentication = true; };
|
||||
xray.client =
|
||||
{
|
||||
enable = true;
|
||||
serverAddress = "74.211.99.69";
|
||||
serverName = "vps6.xserver.chn.moe";
|
||||
dns.extraInterfaces = [ "docker0" ];
|
||||
};
|
||||
xray.client.enable = true;
|
||||
firewall.trustedInterfaces = [ "virbr0" "waydroid0" ];
|
||||
smartd.enable = true;
|
||||
beesd =
|
||||
|
||||
@@ -5,13 +5,13 @@ inputs:
|
||||
client =
|
||||
{
|
||||
enable = mkOption { type = types.bool; default = false; };
|
||||
serverAddress = mkOption { type = types.nonEmptyStr; };
|
||||
serverName = mkOption { type = types.nonEmptyStr; };
|
||||
dns = mkOption { type = types.submodule { options =
|
||||
serverAddress = mkOption { type = types.nonEmptyStr; default = "74.211.99.69"; };
|
||||
serverName = mkOption { type = types.nonEmptyStr; default = "vps6.xserver.chn.moe"; };
|
||||
dae.lanInterfaces = mkOption
|
||||
{
|
||||
hosts = mkOption { type = types.attrsOf types.nonEmptyStr; default = {}; };
|
||||
extraInterfaces = mkOption { type = types.listOf types.nonEmptyStr; default = []; };
|
||||
}; }; };
|
||||
type = types.listOf types.nonEmptyStr;
|
||||
default = inputs.lib.optionals inputs.config.nixos.virtualization.docker.enable [ "docker0" ];
|
||||
};
|
||||
};
|
||||
server =
|
||||
{
|
||||
@@ -19,178 +19,369 @@ inputs:
|
||||
serverName = mkOption { type = types.nonEmptyStr; };
|
||||
};
|
||||
};
|
||||
config =
|
||||
let
|
||||
inherit (inputs.lib) mkMerge mkIf;
|
||||
inherit (inputs.localLib) stripeTabs attrsToList;
|
||||
inherit (inputs.config.nixos.services) xray;
|
||||
inherit (builtins) map listToAttrs toString genList length concatStringsSep;
|
||||
in mkMerge
|
||||
[
|
||||
config = let inherit (inputs.config.nixos.services) xray; in inputs.lib.mkMerge
|
||||
[
|
||||
{
|
||||
assertions =
|
||||
[{
|
||||
assertion = !(xray.client.enable && xray.server.enable);
|
||||
message = "Currenty xray.client and xray.server could not be simutaniusly enabled.";
|
||||
}];
|
||||
}
|
||||
(
|
||||
inputs.lib.mkIf xray.client.enable
|
||||
{
|
||||
assertions =
|
||||
[{
|
||||
assertion = !(xray.client.enable && xray.server.enable);
|
||||
message = "Currenty xray.client and xray.server could not be simutaniusly enabled.";
|
||||
}];
|
||||
}
|
||||
(
|
||||
mkIf xray.client.enable
|
||||
services =
|
||||
{
|
||||
services =
|
||||
xray = { enable = true; settingsFile = inputs.config.sops.templates."xray-client.json".path; };
|
||||
dae =
|
||||
{
|
||||
dnsmasq =
|
||||
{
|
||||
enable = true;
|
||||
settings =
|
||||
enable = true;
|
||||
config =
|
||||
''
|
||||
global {
|
||||
tproxy_port: 12345
|
||||
tproxy_port_protect: true
|
||||
so_mark_from_dae: 0
|
||||
log_level: info
|
||||
disable_waiting_network: false
|
||||
|
||||
lan_interface: ${builtins.concatStringsSep "," xray.client.dae.lanInterfaces}
|
||||
wan_interface: auto
|
||||
auto_config_kernel_parameter: true
|
||||
|
||||
dial_mode: ip
|
||||
allow_insecure: false
|
||||
tls_implementation: tls
|
||||
}
|
||||
|
||||
node {
|
||||
'socks5://localhost:10884'
|
||||
}
|
||||
|
||||
dns {
|
||||
upstream {
|
||||
alidns: 'udp://223.5.5.5:53'
|
||||
googledns: 'tcp+udp://8.8.8.8:53'
|
||||
}
|
||||
routing {
|
||||
request {
|
||||
qname(geosite:geolocation-cn) -> alidns
|
||||
qname(geosite:geolocation-!cn) -> googledns
|
||||
fallback: alidns
|
||||
}
|
||||
response {
|
||||
upstream(alidns) && !ip(geoip:cn) -> googledns
|
||||
fallback: accept
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group {
|
||||
default_group {
|
||||
policy: fixed(0)
|
||||
}
|
||||
}
|
||||
|
||||
routing {
|
||||
# TODO: more general rules to bypass the proxy
|
||||
pname(xray) -> direct
|
||||
|
||||
dip(224.0.0.0/3, 'ff00::/8') -> direct
|
||||
dip(geoip:private) -> direct
|
||||
domain(geosite:geolocation-cn) -> direct
|
||||
domain('geosite:geolocation-!cn') -> default_group
|
||||
dip(8.8.8.8) -> default_group
|
||||
dip(223.5.5.5) -> direct
|
||||
dip(geoip:cn) -> direct
|
||||
!dip(geoip:cn) -> default_group
|
||||
fallback: default_group
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
sops =
|
||||
{
|
||||
templates."xray-client.json" =
|
||||
{
|
||||
owner = inputs.config.users.users.v2ray.name;
|
||||
group = inputs.config.users.users.v2ray.group;
|
||||
content =
|
||||
let
|
||||
chinaDns = "223.5.5.5";
|
||||
foreignDns = "8.8.8.8";
|
||||
in
|
||||
builtins.toJSON
|
||||
{
|
||||
no-poll = true;
|
||||
log-queries = true;
|
||||
server = [ "127.0.0.1#10853" ];
|
||||
interface = xray.client.dns.extraInterfaces ++ [ "lo" ];
|
||||
bind-dynamic = true;
|
||||
ipset = [ "/yuanshen.com/noproxy_net" ];
|
||||
address = map (host: "/${host.name}/${host.value}") (attrsToList xray.client.dns.hosts);
|
||||
log.loglevel = "info";
|
||||
inbounds =
|
||||
[
|
||||
{
|
||||
port = 10881;
|
||||
protocol = "dokodemo-door";
|
||||
settings = { network = "tcp,udp"; followRedirect = true; };
|
||||
streamSettings.sockopt.tproxy = "tproxy";
|
||||
tag = "xmu-in";
|
||||
}
|
||||
{ port = 10884; protocol = "socks"; settings.udp = true; tag = "proxy-in"; }
|
||||
{ port = 10882; protocol = "socks"; settings.udp = true; tag = "direct-in"; }
|
||||
];
|
||||
outbounds =
|
||||
[
|
||||
{
|
||||
protocol = "vless";
|
||||
settings.vnext =
|
||||
[{
|
||||
address = xray.client.serverAddress;
|
||||
port = 443;
|
||||
users =
|
||||
[{
|
||||
id = inputs.config.sops.placeholder."xray-client/uuid";
|
||||
encryption = "none";
|
||||
flow = "xtls-rprx-vision-udp443";
|
||||
}];
|
||||
}];
|
||||
streamSettings =
|
||||
{
|
||||
network = "tcp";
|
||||
security = "reality";
|
||||
realitySettings =
|
||||
{
|
||||
serverName = xray.client.serverName;
|
||||
publicKey = "Nl0eVZoDF9d71_3dVsZGJl3UWR9LCv3B14gu7G6vhjk";
|
||||
fingerprint = "firefox";
|
||||
};
|
||||
};
|
||||
tag = "proxy-vless";
|
||||
}
|
||||
{ protocol = "freedom"; tag = "direct"; }
|
||||
{ protocol = "dns"; tag = "dns-out"; }
|
||||
{
|
||||
protocol = "socks";
|
||||
settings.servers = [{ address = "127.0.0.1"; port = 10069; }];
|
||||
tag = "xmu-out";
|
||||
}
|
||||
{ protocol = "blackhole"; tag = "block"; }
|
||||
];
|
||||
routing =
|
||||
{
|
||||
domainStrategy = "AsIs";
|
||||
rules = builtins.map (rule: rule // { type = "field"; })
|
||||
[
|
||||
{ inboundTag = [ "xmu-in" ]; outboundTag = "xmu-out"; }
|
||||
{ inboundTag = [ "direct-in" ]; outboundTag = "direct"; }
|
||||
{ inboundTag = [ "proxy-in" ]; outboundTag = "proxy-vless"; }
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
secrets."xray-client/uuid" = {};
|
||||
};
|
||||
systemd.services =
|
||||
{
|
||||
xray =
|
||||
{
|
||||
serviceConfig =
|
||||
{
|
||||
DynamicUser = inputs.lib.mkForce false;
|
||||
User = "v2ray";
|
||||
Group = "v2ray";
|
||||
CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
|
||||
AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
|
||||
LimitNPROC = 65536;
|
||||
LimitNOFILE = 524288;
|
||||
};
|
||||
restartTriggers = [ inputs.config.sops.templates."xray-client.json".file ];
|
||||
};
|
||||
v2ray-forwarder =
|
||||
{
|
||||
description = "v2ray-forwarder Daemon";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig =
|
||||
let
|
||||
ipset = "${inputs.pkgs.ipset}/bin/ipset";
|
||||
iptables = "${inputs.pkgs.iptables}/bin/iptables";
|
||||
ip = "${inputs.pkgs.iproute}/bin/ip";
|
||||
xmuPort = "10881";
|
||||
in
|
||||
{
|
||||
Type = "simple";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = inputs.pkgs.writeShellScript "v2ray-forwarder.start" (builtins.concatStringsSep "\n"
|
||||
(
|
||||
[
|
||||
"${ipset} create xmu_net hash:net"
|
||||
"${iptables} -t mangle -N v2ray -w"
|
||||
"${iptables} -t mangle -A PREROUTING -j v2ray -w"
|
||||
]
|
||||
++ (map (action: "${iptables} -t mangle -A v2ray ${action} -w")
|
||||
[
|
||||
"-m set --match-set xmu_net dst -p tcp -j TPROXY --on-port ${xmuPort} --tproxy-mark 1/1"
|
||||
"-m set --match-set xmu_net dst -p udp -j TPROXY --on-port ${xmuPort} --tproxy-mark 1/1"
|
||||
])
|
||||
++ [
|
||||
"${iptables} -t mangle -N v2ray_mark -w"
|
||||
"${iptables} -t mangle -A OUTPUT -j v2ray_mark -w"
|
||||
]
|
||||
++ (map (action: "${iptables} -t mangle -A v2ray_mark ${action} -w")
|
||||
[
|
||||
"-m set --match-set xmu_net dst -p tcp -j MARK --set-mark 1/1"
|
||||
"-m set --match-set xmu_net dst -p udp -j MARK --set-mark 1/1"
|
||||
])
|
||||
++ [
|
||||
"${ip} rule add fwmark 1/1 table 100"
|
||||
"${ip} route add local 0.0.0.0/0 dev lo table 100"
|
||||
]
|
||||
));
|
||||
ExecStop = inputs.pkgs.writeShellScript "v2ray-forwarder.stop"
|
||||
''
|
||||
${iptables} -t mangle -F v2ray -w
|
||||
${iptables} -t mangle -D PREROUTING -j v2ray -w
|
||||
${iptables} -t mangle -X v2ray -w
|
||||
${iptables} -t mangle -F v2ray_mark -w
|
||||
${iptables} -t mangle -D OUTPUT -j v2ray_mark -w
|
||||
${iptables} -t mangle -X v2ray_mark -w
|
||||
${ip} rule del fwmark 1/1 table 100
|
||||
${ip} route del local 0.0.0.0/0 dev lo table 100
|
||||
${ipset} destroy xmu_net
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
users =
|
||||
{
|
||||
users.v2ray = { uid = inputs.config.nixos.system.user.user.v2ray; group = "v2ray"; isSystemUser = true; };
|
||||
groups.v2ray.gid = inputs.config.nixos.system.user.group.v2ray;
|
||||
};
|
||||
}
|
||||
)
|
||||
(
|
||||
inputs.lib.mkIf xray.server.enable (let userList = builtins.genList (n: n) 30; in
|
||||
{
|
||||
services.xray = { enable = true; settingsFile = inputs.config.sops.templates."xray-server.json".path; };
|
||||
sops =
|
||||
{
|
||||
templates."xray-server.json" =
|
||||
{
|
||||
owner = inputs.config.users.users.v2ray.name;
|
||||
group = inputs.config.users.users.v2ray.group;
|
||||
content = builtins.toJSON
|
||||
{
|
||||
log.loglevel = "warning";
|
||||
inbounds =
|
||||
[
|
||||
(
|
||||
let
|
||||
fallbackPort = toString
|
||||
(with inputs.config.nixos.services.nginx.global; httpsPort + httpsPortShift.http2);
|
||||
in
|
||||
{
|
||||
port = 4726;
|
||||
listen = "127.0.0.1";
|
||||
protocol = "vless";
|
||||
settings =
|
||||
{
|
||||
clients = map
|
||||
(n:
|
||||
{
|
||||
id = inputs.config.sops.placeholder."xray-server/clients/user${toString n}";
|
||||
flow = "xtls-rprx-vision";
|
||||
email = "${toString n}@xray.chn.moe";
|
||||
})
|
||||
userList;
|
||||
decryption = "none";
|
||||
fallbacks = [{ dest = "127.0.0.1:${fallbackPort}"; }];
|
||||
};
|
||||
streamSettings =
|
||||
{
|
||||
network = "tcp";
|
||||
security = "reality";
|
||||
realitySettings =
|
||||
{
|
||||
dest = "127.0.0.1:${fallbackPort}";
|
||||
serverNames = [ xray.server.serverName ];
|
||||
privateKey = inputs.config.sops.placeholder."xray-server/private-key";
|
||||
minClientVer = "1.8.0";
|
||||
shortIds = [ "" ];
|
||||
};
|
||||
};
|
||||
sniffing = { enabled = true; destOverride = [ "http" "tls" "quic" ]; routeOnly = true; };
|
||||
tag = "in";
|
||||
}
|
||||
)
|
||||
{
|
||||
port = 4638;
|
||||
listen = "127.0.0.1";
|
||||
protocol = "vless";
|
||||
settings = { clients = [{ id = "be01f0a0-9976-42f5-b9ab-866eba6ed393"; }]; decryption = "none"; };
|
||||
streamSettings.network = "tcp";
|
||||
sniffing = { enabled = true; destOverride = [ "http" "tls" "quic" ]; };
|
||||
tag = "in-localdns";
|
||||
}
|
||||
{
|
||||
listen = "127.0.0.1";
|
||||
port = 6149;
|
||||
protocol = "dokodemo-door";
|
||||
settings.address = "127.0.0.1";
|
||||
tag = "api";
|
||||
}
|
||||
];
|
||||
outbounds =
|
||||
[
|
||||
{ protocol = "freedom"; tag = "freedom"; }
|
||||
{
|
||||
protocol = "vless";
|
||||
settings.vnext =
|
||||
[{
|
||||
address = "127.0.0.1";
|
||||
port = 4638;
|
||||
users = [{ id = "be01f0a0-9976-42f5-b9ab-866eba6ed393"; encryption = "none"; }];
|
||||
}];
|
||||
streamSettings.network = "tcp";
|
||||
tag = "loopback-localdns";
|
||||
}
|
||||
];
|
||||
routing =
|
||||
{
|
||||
domainStrategy = "AsIs";
|
||||
rules = builtins.map (rule: rule // { type = "field"; })
|
||||
[
|
||||
{ inboundTag = [ "in" ]; domain = [ "domain:openai.com" ]; outboundTag = "loopback-localdns"; }
|
||||
{ inboundTag = [ "in" ]; outboundTag = "freedom"; }
|
||||
{ inboundTag = [ "in-localdns" ]; outboundTag = "freedom"; }
|
||||
{ inboundTag = [ "api" ]; outboundTag = "api"; }
|
||||
];
|
||||
};
|
||||
stats = {};
|
||||
api = { tag = "api"; services = [ "StatsService" ]; };
|
||||
policy =
|
||||
{
|
||||
levels."0" = { statsUserUplink = true; statsUserDownlink = true; };
|
||||
system =
|
||||
{
|
||||
statsInboundUplink = true;
|
||||
statsInboundDownlink = true;
|
||||
statsOutboundUplink = true;
|
||||
statsOutboundDownlink = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
xray = { enable = true; settingsFile = inputs.config.sops.templates."xray-client.json".path; };
|
||||
};
|
||||
sops =
|
||||
{
|
||||
templates."xray-client.json" =
|
||||
{
|
||||
owner = inputs.config.users.users.v2ray.name;
|
||||
group = inputs.config.users.users.v2ray.group;
|
||||
content =
|
||||
let
|
||||
chinaDns = "223.5.5.5";
|
||||
foreignDns = "8.8.8.8";
|
||||
in
|
||||
builtins.toJSON
|
||||
{
|
||||
log.loglevel = "info";
|
||||
dns =
|
||||
{
|
||||
servers =
|
||||
# 先尝试匹配域名列表进行查询,若匹配成功则使用前两个 dns 查询。
|
||||
# 若匹配域名列表失败,或者匹配成功但是查询到的 IP 不在期望的 IP 列表中,则回落到使用后两个 dns 依次查询。
|
||||
[
|
||||
{
|
||||
address = chinaDns;
|
||||
domains = [ "geosite:geolocation-cn" ];
|
||||
expectIPs = [ "geoip:cn" ];
|
||||
skipFallback = true;
|
||||
}
|
||||
{
|
||||
address = foreignDns;
|
||||
domains = [ "geosite:geolocation-!cn" ];
|
||||
expectIPs = [ "geoip:!cn" ];
|
||||
skipFallback = true;
|
||||
}
|
||||
{ address = chinaDns; expectIPs = [ "geoip:cn" ]; }
|
||||
{ address = foreignDns; }
|
||||
];
|
||||
disableCache = true;
|
||||
queryStrategy = "UseIPv4";
|
||||
tag = "dns-internal";
|
||||
};
|
||||
inbounds =
|
||||
[
|
||||
{
|
||||
port = 10853;
|
||||
protocol = "dokodemo-door";
|
||||
settings = { address = "8.8.8.8"; network = "tcp,udp"; port = 53; };
|
||||
tag = "dns-in";
|
||||
}
|
||||
{
|
||||
port = 10880;
|
||||
protocol = "dokodemo-door";
|
||||
settings = { network = "tcp,udp"; followRedirect = true; };
|
||||
streamSettings.sockopt.tproxy = "tproxy";
|
||||
sniffing = { enabled = true; destOverride = [ "http" "tls" "quic" ]; routeOnly = true; };
|
||||
tag = "common-in";
|
||||
}
|
||||
{
|
||||
port = 10881;
|
||||
protocol = "dokodemo-door";
|
||||
settings = { network = "tcp,udp"; followRedirect = true; };
|
||||
streamSettings.sockopt.tproxy = "tproxy";
|
||||
tag = "xmu-in";
|
||||
}
|
||||
{
|
||||
port = 10883;
|
||||
protocol = "dokodemo-door";
|
||||
settings = { network = "tcp,udp"; followRedirect = true; };
|
||||
streamSettings.sockopt.tproxy = "tproxy";
|
||||
tag = "proxy-in";
|
||||
}
|
||||
{ port = 10884; protocol = "socks"; tag = "proxy-socks-in"; }
|
||||
{ port = 10882; protocol = "socks"; tag = "direct-in"; }
|
||||
];
|
||||
outbounds =
|
||||
[
|
||||
{
|
||||
protocol = "vless";
|
||||
settings.vnext =
|
||||
[{
|
||||
address = xray.client.serverAddress;
|
||||
port = 443;
|
||||
users =
|
||||
[{
|
||||
id = inputs.config.sops.placeholder."xray-client/uuid";
|
||||
encryption = "none";
|
||||
flow = "xtls-rprx-vision-udp443";
|
||||
}];
|
||||
}];
|
||||
streamSettings =
|
||||
{
|
||||
network = "tcp";
|
||||
security = "reality";
|
||||
realitySettings =
|
||||
{
|
||||
serverName = xray.client.serverName;
|
||||
publicKey = "Nl0eVZoDF9d71_3dVsZGJl3UWR9LCv3B14gu7G6vhjk";
|
||||
fingerprint = "firefox";
|
||||
};
|
||||
};
|
||||
tag = "proxy-vless";
|
||||
}
|
||||
{ protocol = "freedom"; tag = "direct"; }
|
||||
{ protocol = "dns"; tag = "dns-out"; }
|
||||
{
|
||||
protocol = "socks";
|
||||
settings.servers = [{ address = "127.0.0.1"; port = 10069; }];
|
||||
tag = "xmu-out";
|
||||
}
|
||||
{ protocol = "blackhole"; tag = "block"; }
|
||||
];
|
||||
routing =
|
||||
{
|
||||
domainStrategy = "AsIs";
|
||||
rules = builtins.map (rule: rule // { type = "field"; })
|
||||
[
|
||||
{ inboundTag = [ "dns-in" ]; outboundTag = "dns-out"; }
|
||||
{ inboundTag = [ "dns-internal" ]; ip = [ chinaDns ]; outboundTag = "direct"; }
|
||||
{ inboundTag = [ "dns-internal" ]; ip = [ foreignDns ]; outboundTag = "proxy-vless"; }
|
||||
{ inboundTag = [ "dns-internal" ]; outboundTag = "block"; }
|
||||
{ inboundTag = [ "xmu-in" ]; outboundTag = "xmu-out"; }
|
||||
{ inboundTag = [ "direct-in" ]; outboundTag = "direct"; }
|
||||
{ inboundTag = [ "proxy-in" "proxy-socks-in" ]; outboundTag = "proxy-vless"; }
|
||||
{ inboundTag = [ "common-in" ]; domain = [ "geosite:geolocation-cn" ]; outboundTag = "direct"; }
|
||||
{
|
||||
inboundTag = [ "common-in" ];
|
||||
domain = [ "geosite:geolocation-!cn" ];
|
||||
outboundTag = "proxy-vless";
|
||||
}
|
||||
{ inboundTag = [ "common-in" ]; ip = [ "geoip:cn" ]; outboundTag = "direct"; }
|
||||
{ inboundTag = [ "common-in" ]; outboundTag = "proxy-vless"; }
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
secrets."xray-client/uuid" = {};
|
||||
};
|
||||
systemd.services =
|
||||
secrets = inputs.localLib.listToAttrs
|
||||
(map (n: { name = "xray-server/clients/user${toString n}"; value = {}; }) userList)
|
||||
// (inputs.localLib.listToAttrs (map
|
||||
(name:
|
||||
{
|
||||
name = "xray-server/telegram/${name}";
|
||||
value = (let user = inputs.config.users.users.v2ray; in { owner = user.name; inherit (user) group; });
|
||||
})
|
||||
[ "token" "chat" ]))
|
||||
// { "xray-server/private-key" = {}; };
|
||||
};
|
||||
systemd =
|
||||
{
|
||||
services =
|
||||
{
|
||||
xray =
|
||||
{
|
||||
@@ -204,305 +395,67 @@ inputs:
|
||||
LimitNPROC = 65536;
|
||||
LimitNOFILE = 524288;
|
||||
};
|
||||
restartTriggers = [ inputs.config.sops.templates."xray-client.json".file ];
|
||||
restartTriggers = [ inputs.config.sops.templates."xray-server.json".file ];
|
||||
};
|
||||
v2ray-forwarder =
|
||||
xray-stat =
|
||||
{
|
||||
description = "v2ray-forwarder Daemon";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig =
|
||||
script =
|
||||
let
|
||||
ipset = "${inputs.pkgs.ipset}/bin/ipset";
|
||||
iptables = "${inputs.pkgs.iptables}/bin/iptables";
|
||||
ip = "${inputs.pkgs.iproute}/bin/ip";
|
||||
autoPort = "10880";
|
||||
xmuPort = "10881";
|
||||
proxyPort = "10883";
|
||||
xray = "${inputs.pkgs.xray}/bin/xray";
|
||||
awk = "${inputs.pkgs.gawk}/bin/awk";
|
||||
curl = "${inputs.pkgs.curl}/bin/curl";
|
||||
jq = "${inputs.pkgs.jq}/bin/jq";
|
||||
sed = "${inputs.pkgs.gnused}/bin/sed";
|
||||
cat = "${inputs.pkgs.coreutils}/bin/cat";
|
||||
token = inputs.config.sops.secrets."xray-server/telegram/token".path;
|
||||
chat = inputs.config.sops.secrets."xray-server/telegram/chat".path;
|
||||
in
|
||||
{
|
||||
Type = "simple";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = inputs.pkgs.writeShellScript "v2ray-forwarder.start" (concatStringsSep "\n"
|
||||
(
|
||||
[ "${ipset} create lo_net hash:net" ]
|
||||
++ (map (host: "${ipset} add lo_net ${host}")
|
||||
[
|
||||
"0.0.0.0/8" "10.0.0.0/8" "100.64.0.0/10" "127.0.0.0/8" "169.254.0.0/16" "172.16.0.0/12"
|
||||
"192.0.0.0/24" "192.88.99.0/24" "192.168.0.0/16" "59.77.0.143" "198.18.0.0/15"
|
||||
"198.51.100.0/24" "203.0.113.0/24" "224.0.0.0/4" "240.0.0.0/4" "255.255.255.255/32"
|
||||
])
|
||||
++ [
|
||||
"${ipset} create xmu_net hash:net"
|
||||
"${ipset} create noproxy_net hash:net"
|
||||
"${ipset} add noproxy_net 223.5.5.5"
|
||||
"${ipset} create noproxy_src_net hash:net"
|
||||
"${ipset} create proxy_net hash:net"
|
||||
"${ipset} add proxy_net 8.8.8.8"
|
||||
]
|
||||
++ [
|
||||
"${iptables} -t mangle -N v2ray -w"
|
||||
"${iptables} -t mangle -A PREROUTING -j v2ray -w"
|
||||
]
|
||||
++ (map (action: "${iptables} -t mangle -A v2ray ${action} -w")
|
||||
[
|
||||
"-m set --match-set noproxy_src_net src -j RETURN"
|
||||
"-m set --match-set xmu_net dst -p tcp -j TPROXY --on-port ${xmuPort} --tproxy-mark 1/1"
|
||||
"-m set --match-set xmu_net dst -p udp -j TPROXY --on-port ${xmuPort} --tproxy-mark 1/1"
|
||||
"-m set --match-set noproxy_net dst -j RETURN"
|
||||
"-m set --match-set proxy_net dst -p tcp -j TPROXY --on-port ${proxyPort} --tproxy-mark 1/1"
|
||||
"-m set --match-set proxy_net dst -p udp -j TPROXY --on-port ${proxyPort} --tproxy-mark 1/1"
|
||||
"-m set --match-set lo_net dst -j RETURN"
|
||||
"-p tcp -j TPROXY --on-port ${autoPort} --tproxy-mark 1/1"
|
||||
"-p udp -j TPROXY --on-port ${autoPort} --tproxy-mark 1/1"
|
||||
])
|
||||
++ [
|
||||
"${iptables} -t mangle -N v2ray_mark -w"
|
||||
"${iptables} -t mangle -A OUTPUT -j v2ray_mark -w"
|
||||
]
|
||||
++ (map (action: "${iptables} -t mangle -A v2ray_mark ${action} -w")
|
||||
[
|
||||
"-m owner --uid-owner $(id -u v2ray) -j RETURN"
|
||||
"-m set --match-set noproxy_src_net src -j RETURN"
|
||||
"-m set --match-set xmu_net dst -p tcp -j MARK --set-mark 1/1"
|
||||
"-m set --match-set xmu_net dst -p udp -j MARK --set-mark 1/1"
|
||||
"-m set --match-set noproxy_net dst -j RETURN"
|
||||
"-m set --match-set proxy_net dst -p tcp -j MARK --set-mark 1/1"
|
||||
"-m set --match-set proxy_net dst -p udp -j MARK --set-mark 1/1"
|
||||
"-m set --match-set lo_net dst -j RETURN"
|
||||
"-p tcp -j MARK --set-mark 1/1"
|
||||
"-p udp -j MARK --set-mark 1/1"
|
||||
])
|
||||
++ [
|
||||
"${ip} rule add fwmark 1/1 table 100"
|
||||
"${ip} route add local 0.0.0.0/0 dev lo table 100"
|
||||
]
|
||||
));
|
||||
ExecStop = inputs.pkgs.writeShellScript "v2ray-forwarder.stop" (concatStringsSep "\n"
|
||||
(
|
||||
[
|
||||
"${iptables} -t mangle -F v2ray -w"
|
||||
"${iptables} -t mangle -D PREROUTING -j v2ray -w"
|
||||
"${iptables} -t mangle -X v2ray -w"
|
||||
"${iptables} -t mangle -F v2ray_mark -w"
|
||||
"${iptables} -t mangle -D OUTPUT -j v2ray_mark -w"
|
||||
"${iptables} -t mangle -X v2ray_mark -w"
|
||||
"${ip} rule del fwmark 1/1 table 100"
|
||||
"${ip} route del local 0.0.0.0/0 dev lo table 100"
|
||||
]
|
||||
++ (map (set: "${ipset} destroy ${set}")
|
||||
[ "lo_net" "xmu_net" "noproxy_net" "noproxy_src_net" "proxy_net" ])
|
||||
));
|
||||
};
|
||||
''
|
||||
message='xray:\n'
|
||||
for i in {0..${toString ((builtins.length userList) - 1)}}
|
||||
do
|
||||
upload_bytes=$(${xray} api stats --server=127.0.0.1:6149 \
|
||||
-name "user>>>''${i}@xray.chn.moe>>>traffic>>>uplink" | ${jq} '.stat.value' | ${sed} 's/"//g')
|
||||
[ -z "$upload_bytes" ] && upload_bytes=0
|
||||
download_bytes=$(${xray} api stats --server=127.0.0.1:6149 \
|
||||
-name "user>>>''${i}@xray.chn.moe>>>traffic>>>downlink" | ${jq} '.stat.value' | ${sed} 's/"//g')
|
||||
[ -z "$download_bytes" ] && download_bytes=0
|
||||
traffic_gb=$(echo | ${awk} "{printf \"%.3f\",(''${upload_bytes}+''${download_bytes})/1073741824}")
|
||||
message="$message$i"'\t'"''${traffic_gb}"'G\n'
|
||||
done
|
||||
${curl} -X POST -H 'Content-Type: application/json' \
|
||||
-d "{\"chat_id\": \"$(${cat} ${chat})\", \"text\": \"$message\"}" \
|
||||
https://api.telegram.org/bot$(${cat} ${token})/sendMessage
|
||||
'';
|
||||
serviceConfig = { Type = "oneshot"; User = "v2ray"; Group = "v2ray"; };
|
||||
};
|
||||
};
|
||||
users =
|
||||
timers.xray-stat =
|
||||
{
|
||||
users.v2ray = { uid = inputs.config.nixos.system.user.user.v2ray; group = "v2ray"; isSystemUser = true; };
|
||||
groups.v2ray.gid = inputs.config.nixos.system.user.group.v2ray;
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = { OnCalendar = "*-*-* 0:00:00"; Unit = "xray-stat.service"; };
|
||||
};
|
||||
environment.etc."resolv.conf".text = "nameserver 127.0.0.1";
|
||||
}
|
||||
)
|
||||
(
|
||||
mkIf xray.server.enable (let userList = genList (n: n) 30; in
|
||||
};
|
||||
users =
|
||||
{
|
||||
services.xray = { enable = true; settingsFile = inputs.config.sops.templates."xray-server.json".path; };
|
||||
sops =
|
||||
users.v2ray = { uid = inputs.config.nixos.system.user.user.v2ray; group = "v2ray"; isSystemUser = true; };
|
||||
groups.v2ray.gid = inputs.config.nixos.system.user.group.v2ray;
|
||||
};
|
||||
nixos.services =
|
||||
{
|
||||
acme = { enable = true; cert.${xray.server.serverName}.group = inputs.config.users.users.nginx.group; };
|
||||
nginx =
|
||||
{
|
||||
templates."xray-server.json" =
|
||||
enable = true;
|
||||
transparentProxy.map."${xray.server.serverName}" = 4726;
|
||||
https."${xray.server.serverName}" =
|
||||
{
|
||||
owner = inputs.config.users.users.v2ray.name;
|
||||
group = inputs.config.users.users.v2ray.group;
|
||||
content = builtins.toJSON
|
||||
{
|
||||
log.loglevel = "warning";
|
||||
inbounds =
|
||||
[
|
||||
(
|
||||
let
|
||||
fallbackPort = toString
|
||||
(with inputs.config.nixos.services.nginx.global; httpsPort + httpsPortShift.http2);
|
||||
in
|
||||
{
|
||||
port = 4726;
|
||||
listen = "127.0.0.1";
|
||||
protocol = "vless";
|
||||
settings =
|
||||
{
|
||||
clients = map
|
||||
(n:
|
||||
{
|
||||
id = inputs.config.sops.placeholder."xray-server/clients/user${toString n}";
|
||||
flow = "xtls-rprx-vision";
|
||||
email = "${toString n}@xray.chn.moe";
|
||||
})
|
||||
userList;
|
||||
decryption = "none";
|
||||
fallbacks = [{ dest = "127.0.0.1:${fallbackPort}"; }];
|
||||
};
|
||||
streamSettings =
|
||||
{
|
||||
network = "tcp";
|
||||
security = "reality";
|
||||
realitySettings =
|
||||
{
|
||||
dest = "127.0.0.1:${fallbackPort}";
|
||||
serverNames = [ xray.server.serverName ];
|
||||
privateKey = inputs.config.sops.placeholder."xray-server/private-key";
|
||||
minClientVer = "1.8.0";
|
||||
shortIds = [ "" ];
|
||||
};
|
||||
};
|
||||
sniffing = { enabled = true; destOverride = [ "http" "tls" "quic" ]; routeOnly = true; };
|
||||
tag = "in";
|
||||
}
|
||||
)
|
||||
{
|
||||
port = 4638;
|
||||
listen = "127.0.0.1";
|
||||
protocol = "vless";
|
||||
settings = { clients = [{ id = "be01f0a0-9976-42f5-b9ab-866eba6ed393"; }]; decryption = "none"; };
|
||||
streamSettings.network = "tcp";
|
||||
sniffing = { enabled = true; destOverride = [ "http" "tls" "quic" ]; };
|
||||
tag = "in-localdns";
|
||||
}
|
||||
{
|
||||
listen = "127.0.0.1";
|
||||
port = 6149;
|
||||
protocol = "dokodemo-door";
|
||||
settings.address = "127.0.0.1";
|
||||
tag = "api";
|
||||
}
|
||||
];
|
||||
outbounds =
|
||||
[
|
||||
{ protocol = "freedom"; tag = "freedom"; }
|
||||
{
|
||||
protocol = "vless";
|
||||
settings.vnext =
|
||||
[{
|
||||
address = "127.0.0.1";
|
||||
port = 4638;
|
||||
users = [{ id = "be01f0a0-9976-42f5-b9ab-866eba6ed393"; encryption = "none"; }];
|
||||
}];
|
||||
streamSettings.network = "tcp";
|
||||
tag = "loopback-localdns";
|
||||
}
|
||||
];
|
||||
routing =
|
||||
{
|
||||
domainStrategy = "AsIs";
|
||||
rules = builtins.map (rule: rule // { type = "field"; })
|
||||
[
|
||||
{ inboundTag = [ "in" ]; domain = [ "domain:openai.com" ]; outboundTag = "loopback-localdns"; }
|
||||
{ inboundTag = [ "in" ]; outboundTag = "freedom"; }
|
||||
{ inboundTag = [ "in-localdns" ]; outboundTag = "freedom"; }
|
||||
{ inboundTag = [ "api" ]; outboundTag = "api"; }
|
||||
];
|
||||
};
|
||||
stats = {};
|
||||
api = { tag = "api"; services = [ "StatsService" ]; };
|
||||
policy =
|
||||
{
|
||||
levels."0" = { statsUserUplink = true; statsUserDownlink = true; };
|
||||
system =
|
||||
{
|
||||
statsInboundUplink = true;
|
||||
statsInboundDownlink = true;
|
||||
statsOutboundUplink = true;
|
||||
statsOutboundDownlink = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
secrets = listToAttrs (map (n: { name = "xray-server/clients/user${toString n}"; value = {}; }) userList)
|
||||
// (listToAttrs (map
|
||||
(name:
|
||||
{
|
||||
name = "xray-server/telegram/${name}";
|
||||
value = (let user = inputs.config.users.users.v2ray; in { owner = user.name; inherit (user) group; });
|
||||
})
|
||||
[ "token" "chat" ]))
|
||||
// { "xray-server/private-key" = {}; };
|
||||
};
|
||||
systemd =
|
||||
{
|
||||
services =
|
||||
{
|
||||
xray =
|
||||
{
|
||||
serviceConfig =
|
||||
{
|
||||
DynamicUser = inputs.lib.mkForce false;
|
||||
User = "v2ray";
|
||||
Group = "v2ray";
|
||||
CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
|
||||
AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
|
||||
LimitNPROC = 65536;
|
||||
LimitNOFILE = 524288;
|
||||
};
|
||||
restartTriggers = [ inputs.config.sops.templates."xray-server.json".file ];
|
||||
};
|
||||
xray-stat =
|
||||
{
|
||||
script =
|
||||
let
|
||||
xray = "${inputs.pkgs.xray}/bin/xray";
|
||||
awk = "${inputs.pkgs.gawk}/bin/awk";
|
||||
curl = "${inputs.pkgs.curl}/bin/curl";
|
||||
jq = "${inputs.pkgs.jq}/bin/jq";
|
||||
sed = "${inputs.pkgs.gnused}/bin/sed";
|
||||
cat = "${inputs.pkgs.coreutils}/bin/cat";
|
||||
token = inputs.config.sops.secrets."xray-server/telegram/token".path;
|
||||
chat = inputs.config.sops.secrets."xray-server/telegram/chat".path;
|
||||
in
|
||||
''
|
||||
message='xray:\n'
|
||||
for i in {0..${toString ((length userList) - 1)}}
|
||||
do
|
||||
upload_bytes=$(${xray} api stats --server=127.0.0.1:6149 \
|
||||
-name "user>>>''${i}@xray.chn.moe>>>traffic>>>uplink" | ${jq} '.stat.value' | ${sed} 's/"//g')
|
||||
[ -z "$upload_bytes" ] && upload_bytes=0
|
||||
download_bytes=$(${xray} api stats --server=127.0.0.1:6149 \
|
||||
-name "user>>>''${i}@xray.chn.moe>>>traffic>>>downlink" | ${jq} '.stat.value' | ${sed} 's/"//g')
|
||||
[ -z "$download_bytes" ] && download_bytes=0
|
||||
traffic_gb=$(echo | ${awk} "{printf \"%.3f\",(''${upload_bytes}+''${download_bytes})/1073741824}")
|
||||
message="$message$i"'\t'"''${traffic_gb}"'G\n'
|
||||
done
|
||||
${curl} -X POST -H 'Content-Type: application/json' \
|
||||
-d "{\"chat_id\": \"$(${cat} ${chat})\", \"text\": \"$message\"}" \
|
||||
https://api.telegram.org/bot$(${cat} ${token})/sendMessage
|
||||
'';
|
||||
serviceConfig = { Type = "oneshot"; User = "v2ray"; Group = "v2ray"; };
|
||||
};
|
||||
};
|
||||
timers.xray-stat =
|
||||
{
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = { OnCalendar = "*-*-* 0:00:00"; Unit = "xray-stat.service"; };
|
||||
listen.main = { proxyProtocol = false; addToTransparentProxy = false; };
|
||||
location."/".return.return = "400";
|
||||
};
|
||||
};
|
||||
users =
|
||||
{
|
||||
users.v2ray = { uid = inputs.config.nixos.system.user.user.v2ray; group = "v2ray"; isSystemUser = true; };
|
||||
groups.v2ray.gid = inputs.config.nixos.system.user.group.v2ray;
|
||||
};
|
||||
nixos.services =
|
||||
{
|
||||
acme = { enable = true; cert.${xray.server.serverName}.group = inputs.config.users.users.nginx.group; };
|
||||
nginx =
|
||||
{
|
||||
enable = true;
|
||||
transparentProxy.map."${xray.server.serverName}" = 4726;
|
||||
https."${xray.server.serverName}" =
|
||||
{
|
||||
listen.main = { proxyProtocol = false; addToTransparentProxy = false; };
|
||||
location."/".return.return = "400";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
))
|
||||
];
|
||||
};
|
||||
}
|
||||
))
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user