Skip to content

Instantly share code, notes, and snippets.

@Majiir
Created September 23, 2025 21:45
Show Gist options
  • Select an option

  • Save Majiir/3791a768e0f7d47f6abe95ff301baca9 to your computer and use it in GitHub Desktop.

Select an option

Save Majiir/3791a768e0f7d47f6abe95ff301baca9 to your computer and use it in GitHub Desktop.
{ lib, pkgs, ... }:
let
vyatta = import ./vyatta.nix { inherit lib; };
exampleConfig = import ./example.nix { inherit lib vyatta; };
configText = vyatta.renderSetAttrs exampleConfig;
in
pkgs.writeText "example.config" configText
firewall {
all-ping enable
broadcast-ping disable
ip-src-route disable
ipv6-name LAN-LOCAL-6 {
default-action reject
description "LAN to local (IPv6)"
rule 10 {
action accept
description "Allow established/related"
state {
established enable
related enable
}
}
rule 20 {
action drop
description "Drop invalid state"
state {
invalid enable
}
}
rule 30 {
action accept
description "Allow ICMPv6"
protocol icmpv6
}
rule 40 {
action accept
description "Allow DHCPv6 (client)"
destination {
port dhcpv6-client
}
protocol udp
source {
port dhcpv6-server
}
}
rule 50 {
action accept
description "Allow SSH"
destination {
port ssh
}
protocol tcp
}
}
ipv6-receive-redirects disable
ipv6-src-route disable
log-martians enable
name LAN-LOCAL {
default-action reject
description "LAN to local (IPv4)"
rule 10 {
action accept
description "Allow established/related"
state {
established enable
related enable
}
}
rule 20 {
action drop
description "Drop invalid state"
state {
invalid enable
}
}
rule 30 {
action accept
description "Allow ICMP"
protocol icmp
}
rule 40 {
action accept
description "Allow DHCP (client)"
destination {
port bootpc
}
protocol udp
source {
port bootps
}
}
rule 50 {
action accept
description "Allow SSH"
destination {
port ssh
}
protocol tcp
}
}
receive-redirects disable
send-redirects enable
source-validation disable
syn-cookies enable
}
interfaces {
bridge br0 {
}
bridge br1 {
}
ethernet eth0 {
duplex auto
speed auto
vif 10 {
address dhcp
firewall {
local {
ipv6-name LAN-LOCAL-6
name LAN-LOCAL
}
}
ipv6 {
address {
autoconf
}
}
}
vif 20 {
bridge-group {
bridge br0
}
}
vif 30 {
bridge-group {
bridge br1
}
}
}
ethernet eth1 {
bridge-group {
bridge br0
}
duplex auto
speed auto
}
ethernet eth2 {
bridge-group {
bridge br1
}
duplex auto
speed auto
}
loopback lo {
}
}
service {
ssh {
disable-password-authentication
port 22
protocol-version v2
}
ubnt-discover {
disable
}
ubnt-discover-server {
disable
}
unms {
disable
}
}
system {
host-name erl
login {
user admin {
authentication {
plaintext-password test1234
}
full-name Administrator
level admin
}
}
ntp {
server 0.ubnt.pool.ntp.org {
}
server 1.ubnt.pool.ntp.org {
}
server 2.ubnt.pool.ntp.org {
}
server 3.ubnt.pool.ntp.org {
}
}
syslog {
global {
facility all {
level notice
}
facility protocols {
level debug
}
}
}
time-zone America/New_York
}
{ lib, vyatta, ... }:
with lib;
with vyatta;
let
numberRules = rules: listToAttrs (imap1 (i: v: { name = toString (i * 10); value = v; }) rules);
in {
firewall = let
allowRelated = {
action = "accept";
description = "Allow established/related";
state = {
established = "enable";
related = "enable";
};
};
dropInvalid = {
action = "drop";
description = "Drop invalid state";
state = {
invalid = "enable";
};
};
allowIcmpv6 = {
action = "accept";
description = "Allow ICMPv6";
protocol = "icmpv6";
};
allowDhcpv6 = {
action = "accept";
description = "Allow DHCPv6 (client)";
destination = {
port = "dhcpv6-client";
};
protocol = "udp";
source = {
port = "dhcpv6-server";
};
};
allowDhcpClient = {
action = "accept";
description = "Allow DHCP (client)";
destination = {
port = "bootpc";
};
protocol = "udp";
source = {
port = "bootps";
};
};
allowIcmp = {
action = "accept";
description = "Allow ICMP";
protocol = "icmp";
};
allowSsh = {
action = "accept";
description = "Allow SSH";
destination = {
port = "ssh";
};
protocol = "tcp";
};
in {
all-ping = "enable";
broadcast-ping = "disable";
ipv6-receive-redirects = "disable";
ipv6-src-route = "disable";
ip-src-route = "disable";
log-martians = "enable";
name = mkNamed {
"LAN-LOCAL" = {
default-action = "reject";
description = "LAN to local (IPv4)";
rule = mkNamed (numberRules [
allowRelated
dropInvalid
allowIcmp
allowDhcpClient
allowSsh
]);
};
};
ipv6-name = mkNamed {
"LAN-LOCAL-6" = {
default-action = "reject";
description = "LAN to local (IPv6)";
rule = mkNamed (numberRules [
allowRelated
dropInvalid
allowIcmpv6
allowDhcpv6
allowSsh
]);
};
};
receive-redirects = "disable";
send-redirects = "enable";
source-validation = "disable";
syn-cookies = "enable";
};
interfaces = {
bridge = mkNamed {
br0 = {};
br1 = {};
};
ethernet = mkNamed {
"eth0" = {
duplex = "auto";
speed = "auto";
vif = mkNamed {
"10" = {
address = "dhcp";
ipv6 = {
address = {
autoconf = null;
};
};
firewall = {
local = {
name = "LAN-LOCAL";
ipv6-name = "LAN-LOCAL-6";
};
};
};
"20" = {
bridge-group = {
bridge = "br0";
};
};
"30" = {
bridge-group = {
bridge = "br1";
};
};
};
};
"eth1" = {
duplex = "auto";
speed = "auto";
bridge-group = {
bridge = "br0";
};
};
"eth2" = {
duplex = "auto";
speed = "auto";
bridge-group = {
bridge = "br1";
};
};
};
loopback = mkNamed {
"lo" = {
};
};
};
service = {
ssh = {
disable-password-authentication = null;
port = "22";
protocol-version = "v2";
};
ubnt-discover = {
disable = null;
};
ubnt-discover-server = {
disable = null;
};
unms = {
disable = null;
};
};
system = {
host-name = "erl";
login = {
user = mkNamed {
"admin" = {
authentication = {
plaintext-password = "test1234";
};
full-name = "Administrator";
level = "admin";
};
};
};
ntp = {
server = mkNamed {
"0.ubnt.pool.ntp.org" = {
};
"1.ubnt.pool.ntp.org" = {
};
"2.ubnt.pool.ntp.org" = {
};
"3.ubnt.pool.ntp.org" = {
};
};
};
syslog = {
global = {
facility = mkNamed {
"all" = {
level = "notice";
};
"protocols" = {
level = "debug";
};
};
};
};
time-zone = "America/New_York";
};
}
{ lib, ... }:
with builtins;
with lib;
let
mkNamed = attrs:
if !isAttrs attrs
then abort "mkNamed: parameter must be an attrSet"
else
if !all isAttrs (attrValues attrs)
then abort "mkNamed: all attrs must be attrSets"
else { _namedSet = true; } // attrs;
isNamedSet = conf: conf ? "_namedSet";
renderNamedSet = key: conf:
let attrs = filterAttrs (k: v: k != "_namedSet") conf;
in concatLists (mapAttrsToList (k: v: prefix "${key} ${renderString k} " (renderSet v)) attrs);
renderSet = conf:
let
lines = renderSetAttrs conf;
in [ "{" ] ++ (indent lines) ++ [ "}" ];
renderSetAttrs = conf:
if !isAttrs conf
then abort "renderSetAttrs: parameter must be an attrSet"
else concatLists (mapAttrsToList (k: v: (renderSetAttr k v)) conf);
renderSetAttr = k: v:
if isNamedSet v
then (renderNamedSet k v)
else
let
cases = {
"set" = prefix "${k} " (renderSet v);
"list" = concatMap (x: renderSetAttr k x) v;
"null" = [ k ];
"string" = [ "${k} ${renderString v}" ];
"default" = [ "${k} ${renderString (toString v)}" ];
};
in cases.${typeOf v} or cases.default;
renderString = conf:
if hasAny "\"'" conf
then abort "renderString: cannot use quotes in string"
else
if (stringLength conf == 0) || (hasAny "*{}; " conf)
then "\"${conf}\""
else conf;
hasAny = chars: str: any (c: hasInfix c str) (stringToCharacters chars);
prefix = str: lines: [ (str + (head lines)) ] ++ tail lines;
indent = lines: map (line: " ${line}") lines;
wrap = func: conf: concatLines (func conf);
in {
inherit mkNamed;
renderSet = wrap renderSet;
renderSetAttrs = wrap renderSetAttrs;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment