Skip to content

Commit

Permalink
opengfw: init at 0.3.0
Browse files Browse the repository at this point in the history
nixos/opengfw: init
maintainers: add eum3l
  • Loading branch information
eum3l committed Mar 22, 2024
1 parent d211b80 commit a7a00c4
Show file tree
Hide file tree
Showing 5 changed files with 382 additions and 0 deletions.
5 changes: 5 additions & 0 deletions maintainers/maintainer-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6015,6 +6015,11 @@
githubId = 2147649;
name = "Euan Kemp";
};
eum3l = {
githubId = 77971322;
github = "eum3l";
name = "Emil";
};
eureka-cpu = {
email = "[email protected]";
github = "eureka-cpu";
Expand Down
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2405.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m

- [Mealie](https://nightly.mealie.io/), a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in NuxtJS for a pleasant user experience for the whole family. Available as [services.mealie](#opt-services.mealie.enable)

- [OpenGFW](https://github.com/apernet/OpenGFW), a implementation of the Great Firewall on Linux. Available as [services.opengfw](#opt-services.opengfw.enable).

## Backward Incompatibilities {#sec-release-24.05-incompatibilities}

<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,7 @@
./services/networking/oidentd.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
Expand Down
341 changes: 341 additions & 0 deletions nixos/modules/services/networking/opengfw.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,341 @@
{ lib
, pkgs
, config
, ...
}:
let
inherit (lib) mkOption types mkIf mdDoc optionalString;
cfg = config.services.opengfw;
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
{
options.services.opengfw = {
enable = lib.mkEnableOption (mdDoc "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 = mdDoc ''
Username of OpenGFW user.
'';
};

dir = mkOption {
default = "/var/lib/opengfw";
type = types.singleLineStr;
description = mdDoc ''
Working directory of service and home of opengfw.user.
'';
};

logDir = mkOption {
default = null;
type = types.nullOr types.singleLineStr;
example = "/home/user/opengfw.log";
description = mdDoc ''
File to write the output to instead of systemd.
'';
};

rulesFile = mkOption {
default = null;
type = types.nullOr types.path;
description = mdDoc ''
File instead of declaring opengfw.rules.
'';
};

settingsFile = mkOption {
default = null;
type = types.nullOr types.path;
description = mdDoc ''
File instead of declaring opengfw.settings.
'';
};

settings = mkOption {
default = null;
description = mdDoc ''
Settings passed to OpenGFW. [Example config](https://github.com/apernet/OpenGFW#example-config)
'';
type = types.nullOr (types.submodule {
options = {
io = mkOption {
description = mdDoc ''
IO settings.
'';
default = { };
type = types.submodule {
options = {
queueSize = mkOption {
description = mdDoc ''
IO queue size.
'';
type = types.int;
default = 1024;
example = 2048;
};
local = mkOption {
description = mdDoc ''
Set to false if you want to run OpenGFW on FORWARD chain.
'';
type = types.bool;
default = true;
example = false;
};
rst = mkOption {
description = mdDoc ''
Set to true if you want to send RST for blocked TCP connections, needs `local = false`.
'';
type = types.bool;
default = ! cfg.settings.io.local;
example = false;
};
rcvBuf = mkOption {
description = mdDoc ''
Netlink recieve buffer size.
'';
type = types.int;
default = 4194304;
example = 2097152;
};
sndBuf = mkOption {
description = mdDoc ''
Netlink send buffer size.
'';
type = types.int;
default = 4194304;
example = 2097152;
};
};
};
};
geo = mkOption {
description = mdDoc ''
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 = mdDoc ''
IO queue size.
'';
default = null;
type = types.nullOr types.path;
};
geosite = mkOption {
description = mdDoc ''
Set to false if you want to run OpenGFW on FORWARD chain.
'';
default = null;
type = types.nullOr types.path;
};
};
};
};
workers = mkOption {
default = { };
description = ''
Worker settings.
'';
type = types.submodule {
options = {
count = mkOption {
type = types.int;
description = mdDoc ''
Number of workers.
'';
default = 4;
example = 8;
};
queueSize = mkOption {
type = types.int;
description = mdDoc ''
Worker queue size.
'';
default = 16;
example = 32;
};
tcpMaxBufferedPagesTotal = mkOption {
type = types.int;
description = mdDoc ''
TCP max total buffered pages.
'';
default = 4096;
example = 8192;
};
tcpMaxBufferedPagesPerConn = mkOption {
type = types.int;
description = mdDoc ''
TCP max total bufferd pages per connection.
'';
default = 64;
example = 128;
};
udpMaxStreams = mkOption {
type = types.int;
description = mdDoc ''
UDP max streams.
'';
default = 4096;
example = 8192;
};
};
};
};
};
});
};

rules = mkOption {
default = [ ];
description = mdDoc ''
Rules passed to OpenGFW. [Example rules](https://github.com/apernet/OpenGFW?tab=readme-ov-file#example-rules)
'';
type = types.listOf (
types.submodule {
options = {
name = mkOption {
description = mdDoc "Name of the rule.";
example = "block google dns";
type = types.singleLineStr;
};

action = mkOption {
description = mdDoc ''
Action of the rule. [Supported actions](https://github.com/apernet/OpenGFW?tab=readme-ov-file#supported-actions)
'';
default = "allow";
example = "block";
type = types.enum [ "allow" "block" "drop" "modify" ];
};

log = mkOption {
description = mdDoc "Wether to enable logging for the rule.";
default = true;
example = false;
type = types.bool;
};

expr = mkOption {
description = mdDoc ''
[Expr Language](https://expr-lang.org/docs/language-definition) expression using [OpenGFW analyzers](https://github.com/apernet/OpenGFW/blob/master/docs/Analyzers.md).
'';
type = types.str;
example = ''dns != nil && dns.qr && any(dns.questions, {.name endsWith "google.com"})'';
};

modifier = mkOption {
default = null;
description = mdDoc ''
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 = mdDoc "Name of the modifier.";
type = types.singleLineStr;
example = "dns";
};

args = mkOption {
description = mdDoc "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 = 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"}
'';

serviceConfig = rec {
WorkingDirectory = cfg.dir;
ExecStart = "${config.security.wrapperDir}/OpenGFW -c config.yaml rules.yaml";
ExecReload = "kill -HUP $MAINPID";
Restart = "always";
User = cfg.user;
StandardOutput = mkIf (cfg.logDir != null) "append:${cfg.logDir}";
StandardError = StandardOutput;
};
};

users = {
groups.${cfg.user} = { };
users.${cfg.user} = {
description = "opengfw user";
isNormalUser = true;
group = cfg.user;
home = cfg.dir;
};
};
};

meta.maintainers = with lib.maintainers; [ eum3l ];
}
Loading

0 comments on commit a7a00c4

Please sign in to comment.