mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-12-25 23:23:07 +00:00
415 lines
13 KiB
Nix
415 lines
13 KiB
Nix
{
|
|
lib,
|
|
pkgs,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
mkOption
|
|
types
|
|
mkIf
|
|
optionalString
|
|
;
|
|
cfg = config.services.opengfw;
|
|
in
|
|
{
|
|
options.services.opengfw = {
|
|
enable = lib.mkEnableOption ''
|
|
OpenGFW, A flexible, easy-to-use, open source implementation of GFW on Linux
|
|
'';
|
|
|
|
package = lib.mkPackageOption pkgs "opengfw" { default = "opengfw"; };
|
|
|
|
user = mkOption {
|
|
default = "opengfw";
|
|
type = types.singleLineStr;
|
|
description = "Username of the OpenGFW user.";
|
|
};
|
|
|
|
dir = mkOption {
|
|
default = "/var/lib/opengfw";
|
|
type = types.singleLineStr;
|
|
description = ''
|
|
Working directory of the OpenGFW service and home of `opengfw.user`.
|
|
'';
|
|
};
|
|
|
|
logFile = mkOption {
|
|
default = null;
|
|
type = types.nullOr types.path;
|
|
example = "/var/lib/opengfw/opengfw.log";
|
|
description = ''
|
|
File to write the output to instead of systemd.
|
|
'';
|
|
};
|
|
|
|
logFormat = mkOption {
|
|
description = ''
|
|
Format of the logs. [logFormatMap](https://github.com/apernet/OpenGFW/blob/d7737e92117a11c9a6100d53019fac3b9d724fe3/cmd/root.go#L62)
|
|
'';
|
|
default = "json";
|
|
example = "console";
|
|
type = types.enum [
|
|
"json"
|
|
"console"
|
|
];
|
|
};
|
|
|
|
pcapReplay = mkOption {
|
|
default = null;
|
|
example = "./opengfw.pcap";
|
|
type = types.nullOr types.path;
|
|
description = ''
|
|
Path to PCAP replay file.
|
|
In pcap mode, none of the actions in the rules have any effect.
|
|
This mode is mainly for debugging.
|
|
'';
|
|
};
|
|
|
|
logLevel = mkOption {
|
|
description = ''
|
|
Level of the logs. [logLevelMap](https://github.com/apernet/OpenGFW/blob/d7737e92117a11c9a6100d53019fac3b9d724fe3/cmd/root.go#L55)
|
|
'';
|
|
default = "info";
|
|
example = "warn";
|
|
type = types.enum [
|
|
"debug"
|
|
"info"
|
|
"warn"
|
|
"error"
|
|
];
|
|
};
|
|
|
|
rulesFile = mkOption {
|
|
default = null;
|
|
type = types.nullOr types.path;
|
|
description = ''
|
|
Path to file containing OpenGFW rules.
|
|
'';
|
|
};
|
|
|
|
settingsFile = mkOption {
|
|
default = null;
|
|
type = types.nullOr types.path;
|
|
description = ''
|
|
Path to file containing OpenGFW settings.
|
|
'';
|
|
};
|
|
|
|
settings = mkOption {
|
|
default = null;
|
|
description = ''
|
|
Settings passed to OpenGFW. [Example config](https://gfw.dev/docs/build-run/#config-example)
|
|
'';
|
|
type = types.nullOr (
|
|
types.submodule {
|
|
options = {
|
|
replay = mkOption {
|
|
description = ''
|
|
PCAP replay settings.
|
|
'';
|
|
default = { };
|
|
type = types.submodule {
|
|
options = {
|
|
realtime = mkOption {
|
|
description = ''
|
|
Whether the packets in the PCAP file should be replayed in "real time" (instead of as fast as possible).
|
|
'';
|
|
default = false;
|
|
example = true;
|
|
type = types.bool;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
io = mkOption {
|
|
description = ''
|
|
IO settings.
|
|
'';
|
|
default = { };
|
|
type = types.submodule {
|
|
options = {
|
|
queueSize = mkOption {
|
|
description = "IO queue size.";
|
|
type = types.int;
|
|
default = 1024;
|
|
example = 2048;
|
|
};
|
|
local = mkOption {
|
|
description = ''
|
|
Set to false if you want to run OpenGFW on FORWARD chain. (e.g. on a router)
|
|
'';
|
|
type = types.bool;
|
|
default = true;
|
|
example = false;
|
|
};
|
|
rst = mkOption {
|
|
description = ''
|
|
Set to true if you want to send RST for blocked TCP connections, needs `local = false`.
|
|
'';
|
|
type = types.bool;
|
|
default = !cfg.settings.io.local;
|
|
defaultText = "`!config.services.opengfw.settings.io.local`";
|
|
example = false;
|
|
};
|
|
rcvBuf = mkOption {
|
|
description = "Netlink receive buffer size.";
|
|
type = types.int;
|
|
default = 4194304;
|
|
example = 2097152;
|
|
};
|
|
sndBuf = mkOption {
|
|
description = "Netlink send buffer size.";
|
|
type = types.int;
|
|
default = 4194304;
|
|
example = 2097152;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
ruleset = mkOption {
|
|
description = ''
|
|
The path to load specific local geoip/geosite db files.
|
|
If not set, they will be automatically downloaded from (Loyalsoldier/v2ray-rules-dat)[https://github.com/Loyalsoldier/v2ray-rules-dat].
|
|
'';
|
|
default = { };
|
|
type = types.submodule {
|
|
options = {
|
|
geoip = mkOption {
|
|
description = "Path to `geoip.dat`.";
|
|
default = null;
|
|
type = types.nullOr types.path;
|
|
};
|
|
geosite = mkOption {
|
|
description = "Path to `geosite.dat`.";
|
|
default = null;
|
|
type = types.nullOr types.path;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
workers = mkOption {
|
|
default = { };
|
|
description = "Worker settings.";
|
|
type = types.submodule {
|
|
options = {
|
|
count = mkOption {
|
|
type = types.int;
|
|
description = ''
|
|
Number of workers.
|
|
Recommended to be no more than the number of CPU cores
|
|
'';
|
|
default = 4;
|
|
example = 8;
|
|
};
|
|
queueSize = mkOption {
|
|
type = types.int;
|
|
description = "Worker queue size.";
|
|
default = 16;
|
|
example = 32;
|
|
};
|
|
tcpMaxBufferedPagesTotal = mkOption {
|
|
type = types.int;
|
|
description = ''
|
|
TCP max total buffered pages.
|
|
'';
|
|
default = 4096;
|
|
example = 8192;
|
|
};
|
|
tcpMaxBufferedPagesPerConn = mkOption {
|
|
type = types.int;
|
|
description = ''
|
|
TCP max total bufferd pages per connection.
|
|
'';
|
|
default = 64;
|
|
example = 128;
|
|
};
|
|
tcpTimeout = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
How long a connection is considered dead when no data is being transferred.
|
|
Dead connections are purged from TCP reassembly pools once per minute.
|
|
'';
|
|
default = "10m";
|
|
example = "5m";
|
|
};
|
|
udpMaxStreams = mkOption {
|
|
type = types.int;
|
|
description = "UDP max streams.";
|
|
default = 4096;
|
|
example = 8192;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|
|
);
|
|
};
|
|
|
|
rules = mkOption {
|
|
default = [ ];
|
|
description = ''
|
|
Rules passed to OpenGFW. [Example rules](https://gfw.dev/docs/rules)
|
|
'';
|
|
type = types.listOf (
|
|
types.submodule {
|
|
options = {
|
|
name = mkOption {
|
|
description = "Name of the rule.";
|
|
example = "block google dns";
|
|
type = types.singleLineStr;
|
|
};
|
|
|
|
action = mkOption {
|
|
description = ''
|
|
Action of the rule. [Supported actions](https://gfw.dev/docs/rules#supported-actions)
|
|
'';
|
|
default = "allow";
|
|
example = "block";
|
|
type = types.enum [
|
|
"allow"
|
|
"block"
|
|
"drop"
|
|
"modify"
|
|
];
|
|
};
|
|
|
|
log = mkOption {
|
|
description = "Whether to enable logging for the rule.";
|
|
default = true;
|
|
example = false;
|
|
type = types.bool;
|
|
};
|
|
|
|
expr = mkOption {
|
|
description = ''
|
|
[Expr Language](https://expr-lang.org/docs/language-definition) expression using [analyzers](https://gfw.dev/docs/analyzers) and [functions](https://gfw.dev/docs/functions).
|
|
'';
|
|
type = types.str;
|
|
example = ''dns != nil && dns.qr && any(dns.questions, {.name endsWith "google.com"})'';
|
|
};
|
|
|
|
modifier = mkOption {
|
|
default = null;
|
|
description = ''
|
|
Modification of specified packets when using the `modify` action. [Available modifiers](https://github.com/apernet/OpenGFW/tree/master/modifier)
|
|
'';
|
|
type = types.nullOr (
|
|
types.submodule {
|
|
options = {
|
|
name = mkOption {
|
|
description = "Name of the modifier.";
|
|
type = types.singleLineStr;
|
|
example = "dns";
|
|
};
|
|
|
|
args = mkOption {
|
|
description = "Arguments passed to the modifier.";
|
|
type = types.attrs;
|
|
example = {
|
|
a = "0.0.0.0";
|
|
aaaa = "::";
|
|
};
|
|
};
|
|
};
|
|
}
|
|
);
|
|
};
|
|
};
|
|
}
|
|
);
|
|
|
|
example = [
|
|
{
|
|
name = "block v2ex http";
|
|
action = "block";
|
|
expr = ''string(http?.req?.headers?.host) endsWith "v2ex.com"'';
|
|
}
|
|
{
|
|
name = "block google socks";
|
|
action = "block";
|
|
expr = ''string(socks?.req?.addr) endsWith "google.com" && socks?.req?.port == 80'';
|
|
}
|
|
{
|
|
name = "v2ex dns poisoning";
|
|
action = "modify";
|
|
modifier = {
|
|
name = "dns";
|
|
args = {
|
|
a = "0.0.0.0";
|
|
aaaa = "::";
|
|
};
|
|
};
|
|
expr = ''dns != nil && dns.qr && any(dns.questions, {.name endsWith "v2ex.com"})'';
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
config =
|
|
let
|
|
format = pkgs.formats.yaml { };
|
|
|
|
settings =
|
|
if cfg.settings != null then
|
|
format.generate "opengfw-config.yaml" cfg.settings
|
|
else
|
|
cfg.settingsFile;
|
|
rules = if cfg.rules != [ ] then format.generate "opengfw-rules.yaml" cfg.rules else cfg.rulesFile;
|
|
in
|
|
mkIf cfg.enable {
|
|
security.wrappers.OpenGFW = {
|
|
owner = cfg.user;
|
|
group = cfg.user;
|
|
capabilities = "cap_net_admin+ep";
|
|
source = "${cfg.package}/bin/OpenGFW";
|
|
};
|
|
|
|
systemd.services.opengfw = {
|
|
description = "OpenGFW";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network.target" ];
|
|
path = with pkgs; [ iptables ];
|
|
|
|
preStart = ''
|
|
${optionalString (rules != null) "ln -sf ${rules} rules.yaml"}
|
|
${optionalString (settings != null) "ln -sf ${settings} config.yaml"}
|
|
'';
|
|
|
|
script = ''
|
|
${config.security.wrapperDir}/OpenGFW \
|
|
-f ${cfg.logFormat} \
|
|
-l ${cfg.logLevel} \
|
|
${optionalString (cfg.pcapReplay != null) "-p ${cfg.pcapReplay}"} \
|
|
-c config.yaml \
|
|
rules.yaml
|
|
'';
|
|
|
|
serviceConfig = rec {
|
|
WorkingDirectory = cfg.dir;
|
|
ExecReload = "kill -HUP $MAINPID";
|
|
Restart = "always";
|
|
User = cfg.user;
|
|
StandardOutput = mkIf (cfg.logFile != null) "append:${cfg.logFile}";
|
|
StandardError = StandardOutput;
|
|
};
|
|
};
|
|
|
|
users = {
|
|
groups.${cfg.user} = { };
|
|
users.${cfg.user} = {
|
|
description = "opengfw user";
|
|
isSystemUser = true;
|
|
group = cfg.user;
|
|
home = cfg.dir;
|
|
createHome = true;
|
|
homeMode = "750";
|
|
};
|
|
};
|
|
};
|
|
meta.maintainers = with lib.maintainers; [ eum3l ];
|
|
}
|