Merge pull request #290008 from eum3l/add-opengfw

opengfw: init at 0.4.0 (+NixOS module)
This commit is contained in:
Masum Reza 2024-07-24 20:39:41 +05:30 committed by GitHub
commit 81cb83b07f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 460 additions and 0 deletions

View File

@ -6239,6 +6239,12 @@
githubId = 2147649;
name = "Euan Kemp";
};
eum3l = {
email = "eum3l@proton.me";
githubId = 77971322;
github = "eum3l";
name = "Emil";
};
eureka-cpu = {
email = "github.eureka@gmail.com";
github = "eureka-cpu";

View File

@ -60,6 +60,8 @@
- [Improved File Manager](https://github.com/misterunknown/ifm), or IFM, a single-file web-based file manager.
- [OpenGFW](https://github.com/apernet/OpenGFW), an implementation of the Great Firewall on Linux. Available as [services.opengfw](#opt-services.opengfw.enable).
## Backward Incompatibilities {#sec-release-24.11-incompatibilities}
- `transmission` package has been aliased with a `trace` warning to `transmission_3`. Since [Transmission 4 has been released last year](https://github.com/transmission/transmission/releases/tag/4.0.0), and Transmission 3 will eventually go away, it was decided perform this warning alias to make people aware of the new version. The `services.transmission.package` defaults to `transmission_3` as well because the upgrade can cause data loss in certain specific usage patterns (examples: [#5153](https://github.com/transmission/transmission/issues/5153), [#6796](https://github.com/transmission/transmission/issues/6796)). Please make sure to back up to your data directory per your usage:

View File

@ -1137,6 +1137,7 @@
./services/networking/oink.nix
./services/networking/onedrive.nix
./services/networking/openconnect.nix
./services/networking/opengfw.nix
./services/networking/openvpn.nix
./services/networking/ostinato.nix
./services/networking/owamp.nix

View File

@ -0,0 +1,414 @@
{
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 ];
}

View File

@ -0,0 +1,37 @@
{
lib,
buildGoModule,
fetchFromGitHub,
}:
let
pname = "opengfw";
version = "0.4.0";
in
buildGoModule {
inherit pname version;
CGO_ENABLED = 0;
vendorHash = "sha256-F8jTvgxOhOGVtl6B8u0xAIvjNwVjBtvAhApzjIgykpY=";
src = fetchFromGitHub {
owner = "apernet";
repo = "opengfw";
rev = "refs/tags/v${version}";
hash = "sha256-kmbG6l5CtZGM/zpvl2pukq5xsOIy28RDyb4sHBsoyOw=";
};
meta = {
mainProgram = "OpenGFW";
description = "Flexible, easy-to-use, open source implementation of GFW on Linux";
longDescription = ''
OpenGFW is your very own DIY Great Firewall of China, available as a flexible,
easy-to-use open source program on Linux. Why let the powers that be have all the fun?
It's time to give power to the people and democratize censorship.
Bring the thrill of cyber-sovereignty right into your home router
and start filtering like a pro - you too can play Big Brother.
'';
homepage = "https://gfw.dev/";
license = lib.licenses.mpl20;
platforms = lib.platforms.linux;
maintainers = with lib.maintainers; [ eum3l ];
};
}