Skip to content

Instantly share code, notes, and snippets.

@Saturn745
Last active March 24, 2024 18:47
Show Gist options
  • Save Saturn745/8773e3a44dc073c40600ca89027cd72e to your computer and use it in GitHub Desktop.
Save Saturn745/8773e3a44dc073c40600ca89027cd72e to your computer and use it in GitHub Desktop.
Forward NixOS Container ports without them being filtered. NFTables and SNAT

Fixes NixOS/nixpkgs#46975 and possibly NixOS/nixpkgs#28721

Previously when using forwardPorts or exposing it with a containers firewall you would have no way to expose it to the public (outside the machine) because the port would be filtered.

This adds a optiont o the containers called extraPorts which will forward the ports with SNAT.

This probably isn't the best fix but it works
{
config,
lib,
...
}: let
portMappingType = lib.types.submodule {
options = {
host = lib.mkOption {
type = lib.types.port;
description = "The host port number";
};
container = lib.mkOption {
type = lib.types.port;
description = "The container port number";
};
proto = lib.mkOption {
type = lib.types.enum ["tcp" "udp"];
default = "tcp";
description = "The protocol (tcp, udp)";
};
};
};
generateNftablesRules = ip: portMappings: let
rules = lib.lists.forEach portMappings (
mapping: ''
iifname "${config.networking.nat.externalInterface}" ${mapping.proto} dport ${toString mapping.host} dnat ${ip}:${toString mapping.container}
''
);
in
lib.concatStringsSep "\n" rules;
containerModule = {
name,
config,
...
}: {
options.exposePorts = lib.mkOption {
type = lib.types.listOf portMappingType;
default = [];
description = "List of ports to expose";
};
};
in {
options.containers = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule containerModule);
};
config.networking.nftables.ruleset = ''
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat;
# AUTO GENERATED RULES FOR CONTAINERS
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: cfg: generateNftablesRules cfg.localAddress cfg.exposePorts) config.containers)}
}
}
'';
}
{pkgs, ...}: {
networking.firewall.allowedTCPPorts = [8096];
containers.media-server = {
autoStart = true;
privateNetwork = true;
hostAddress = "192.168.100.10";
localAddress = "192.168.100.11";
# Expose it with the custom module
exposePorts = [
{
host = 8096;
container = 8096;
proto = "tcp";
}
];
config = {...}: {
system.stateVersion = "24.05";
nixpkgs.pkgs = pkgs;
services.jellyfin = {
enable = true;
openFirewall = true; # Make sure the port is opened on the containers firewall
};
networking = {
firewall = {
enable = true;
};
useHostResolvConf = false;
};
services.resolved.enable = true;
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment