mirror of
https://github.com/CHN-beta/nixos.git
synced 2026-01-12 04:39:23 +08:00
modules.services.nginx.transparentProxy: cleanup
This commit is contained in:
@@ -1,111 +1,99 @@
|
||||
inputs:
|
||||
{
|
||||
options.nixos.services.nginx = let inherit (inputs.lib) mkOption types; in
|
||||
options.nixos.services.nginx.transparentProxy = let inherit (inputs.lib) mkOption types; in
|
||||
{
|
||||
transparentProxy =
|
||||
# only disable in some rare cases
|
||||
enable = mkOption { type = types.bool; default = true; };
|
||||
externalIp = mkOption { type = types.listOf types.nonEmptyStr; default = [ "0.0.0.0" ]; };
|
||||
# proxy to 127.0.0.1:${specified port}
|
||||
map = mkOption
|
||||
{
|
||||
# only disable in some rare cases
|
||||
enable = mkOption { type = types.bool; default = true; };
|
||||
externalIp = mkOption { type = types.listOf types.nonEmptyStr; default = [ "0.0.0.0" ]; };
|
||||
# proxy to 127.0.0.1:${specified port}
|
||||
map = mkOption
|
||||
{
|
||||
type = types.attrsOf (types.oneOf
|
||||
[
|
||||
# proxy to 127.0.0.1:${specified port}
|
||||
types.ints.unsigned
|
||||
# proxy to specified ip:port
|
||||
types.nonEmptyStr
|
||||
]);
|
||||
default = {};
|
||||
};
|
||||
type = types.attrsOf (types.oneOf
|
||||
[
|
||||
# proxy to 127.0.0.1:${specified port}
|
||||
types.ints.unsigned
|
||||
# proxy to specified ip:port
|
||||
types.nonEmptyStr
|
||||
]);
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
config =
|
||||
let
|
||||
inherit (inputs.localLib) attrsToList;
|
||||
inherit (inputs.config.nixos.services) nginx;
|
||||
inherit (builtins) map listToAttrs concatStringsSep toString filter attrValues concatLists;
|
||||
concatAttrs = list: listToAttrs (concatLists (map (attrs: attrsToList attrs) list));
|
||||
in inputs.lib.mkIf nginx.enable (inputs.lib.mkMerge
|
||||
[
|
||||
(inputs.lib.mkIf nginx.transparentProxy.enable
|
||||
config = let inherit (inputs.config.nixos.services) nginx; in
|
||||
inputs.lib.mkIf (nginx.enable && nginx.transparentProxy.enable)
|
||||
{
|
||||
services.nginx.streamConfig =
|
||||
''
|
||||
log_format transparent_proxy '[$time_local] $remote_addr-$geoip2_data_country_code '
|
||||
'"$ssl_preread_server_name"->$transparent_proxy_backend $bytes_sent $bytes_received';
|
||||
map $ssl_preread_server_name $transparent_proxy_backend {
|
||||
${builtins.concatStringsSep "\n " (inputs.lib.mapAttrsToList
|
||||
(n: v: ''"${n}" ${if builtins.isInt v then "127.0.0.1:${builtins.toString v}" else v};'')
|
||||
nginx.transparentProxy.map)}
|
||||
default 127.0.0.1:${toString (with nginx.global; (httpsPort + httpsPortShift.http2))};
|
||||
}
|
||||
server {
|
||||
${builtins.concatStringsSep "\n " (builtins.map (ip: "listen ${ip}:443;")
|
||||
nginx.transparentProxy.externalIp)}
|
||||
ssl_preread on;
|
||||
proxy_bind $remote_addr transparent;
|
||||
proxy_pass $transparent_proxy_backend;
|
||||
proxy_connect_timeout 1s;
|
||||
proxy_socket_keepalive on;
|
||||
proxy_buffer_size 128k;
|
||||
access_log syslog:server=unix:/dev/log transparent_proxy;
|
||||
}
|
||||
'';
|
||||
# TODO: use existing options
|
||||
systemd.services.nginx-proxy =
|
||||
let
|
||||
ip = "${inputs.pkgs.iproute2}/bin/ip";
|
||||
start = inputs.pkgs.writeShellScript "nginx-proxy.start"
|
||||
''
|
||||
${ip} rule add fwmark 2/2 table 200
|
||||
${ip} route add local 0.0.0.0/0 dev lo table 200
|
||||
'';
|
||||
stop = inputs.pkgs.writeShellScript "nginx-proxy.stop"
|
||||
''
|
||||
${ip} rule del fwmark 2/2 table 200
|
||||
${ip} route del local 0.0.0.0/0 dev lo table 200
|
||||
'';
|
||||
in
|
||||
{
|
||||
description = "nginx transparent proxy";
|
||||
after = [ "network.target" ];
|
||||
serviceConfig =
|
||||
{
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = start;
|
||||
ExecStop = stop;
|
||||
};
|
||||
wants = [ "network.target" ];
|
||||
wantedBy= [ "multi-user.target" ];
|
||||
};
|
||||
networking.nftables.tables.nginx =
|
||||
{
|
||||
services.nginx.streamConfig =
|
||||
family = "inet";
|
||||
content =
|
||||
''
|
||||
log_format transparent_proxy '[$time_local] $remote_addr-$geoip2_data_country_code '
|
||||
'"$ssl_preread_server_name"->$transparent_proxy_backend $bytes_sent $bytes_received';
|
||||
map $ssl_preread_server_name $transparent_proxy_backend {
|
||||
${concatStringsSep "\n " (builtins.map
|
||||
(x:
|
||||
let upstrem = if builtins.isInt x.value then "127.0.0.1:${builtins.toString x.value}" else x.value;
|
||||
in ''"${x.name}" ${upstrem};'')
|
||||
(attrsToList nginx.transparentProxy.map))}
|
||||
default 127.0.0.1:${toString (with nginx.global; (httpsPort + httpsPortShift.http2))};
|
||||
chain output {
|
||||
type route hook output priority mangle; policy accept;
|
||||
# 由本机发出、gid 为 nginx、但源地址不是本地监听的地址,说明是透明代理的第一个包,将这个流标记
|
||||
# 但这个包本身不需要处理,正常路由即可。
|
||||
meta skgid ${builtins.toString inputs.config.users.groups.nginx.gid} fib saddr type != local \
|
||||
ct state new counter ct mark set ct mark | 2 return
|
||||
# 由本机发出、作为透明代理的回复,它不能按照通常的路由,它需要被打上标记并被路由到本地
|
||||
# 这对应于透明代理到本地的服务的情况
|
||||
ct mark & 2 == 2 ct direction reply counter meta mark set meta mark | 2 return
|
||||
return
|
||||
}
|
||||
server {
|
||||
${concatStringsSep "\n " (map (ip: "listen ${ip}:443;") nginx.transparentProxy.externalIp)}
|
||||
ssl_preread on;
|
||||
proxy_bind $remote_addr transparent;
|
||||
proxy_pass $transparent_proxy_backend;
|
||||
proxy_connect_timeout 1s;
|
||||
proxy_socket_keepalive on;
|
||||
proxy_buffer_size 128k;
|
||||
access_log syslog:server=unix:/dev/log transparent_proxy;
|
||||
# 还需要处理透明代理到其它机器的情况,它们的回复需要在 prerouting 中标记
|
||||
chain prerouting {
|
||||
type filter hook prerouting priority mangle; policy accept;
|
||||
ct mark & 2 == 2 ct direction reply counter meta mark set meta mark | 2 return
|
||||
return
|
||||
}
|
||||
'';
|
||||
# TODO: use existing options
|
||||
systemd.services.nginx-proxy =
|
||||
let
|
||||
ip = "${inputs.pkgs.iproute2}/bin/ip";
|
||||
start = inputs.pkgs.writeShellScript "nginx-proxy.start"
|
||||
''
|
||||
${ip} rule add fwmark 2/2 table 200
|
||||
${ip} route add local 0.0.0.0/0 dev lo table 200
|
||||
'';
|
||||
stop = inputs.pkgs.writeShellScript "nginx-proxy.stop"
|
||||
''
|
||||
${ip} rule del fwmark 2/2 table 200
|
||||
${ip} route del local 0.0.0.0/0 dev lo table 200
|
||||
'';
|
||||
in
|
||||
{
|
||||
description = "nginx transparent proxy";
|
||||
after = [ "network.target" ];
|
||||
serviceConfig =
|
||||
{
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = start;
|
||||
ExecStop = stop;
|
||||
};
|
||||
wants = [ "network.target" ];
|
||||
wantedBy= [ "multi-user.target" ];
|
||||
};
|
||||
networking.nftables.tables.nginx =
|
||||
{
|
||||
family = "inet";
|
||||
content =
|
||||
''
|
||||
chain output {
|
||||
type route hook output priority mangle; policy accept;
|
||||
# 由本机发出、gid 为 nginx、但源地址不是本地监听的地址,说明是透明代理的第一个包,将这个流标记
|
||||
# 但这个包本身不需要处理,正常路由即可。
|
||||
meta skgid ${builtins.toString inputs.config.users.groups.nginx.gid} fib saddr type != local \
|
||||
ct state new counter ct mark set ct mark | 2 return
|
||||
# 由本机发出、作为透明代理的回复,它不能按照通常的路由,它需要被打上标记并被路由到本地
|
||||
# 这对应于透明代理到本地的服务的情况
|
||||
ct mark & 2 == 2 ct direction reply counter meta mark set meta mark | 2 return
|
||||
return
|
||||
}
|
||||
# 还需要处理透明代理到其它机器的情况,它们的回复需要在 prerouting 中标记
|
||||
chain prerouting {
|
||||
type filter hook prerouting priority mangle; policy accept;
|
||||
ct mark & 2 == 2 ct direction reply counter meta mark set meta mark | 2 return
|
||||
return
|
||||
}
|
||||
'';
|
||||
};
|
||||
})
|
||||
]);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user