liminix/examples/rotuer.nix
Daniel Barlow 9441f48819 new ppp module, used by rotuer
The objective here is that services which depend on global config
(e.g. kernel config or busybox options or static paths in the
filesystem) now live under config.system.service, and are added
to that collection by the module that defines the necessary state.

This is a first step: the services will be configured by a typechecked
attr set instead of the arbitrary arguments that
pkgs.liminix.networking.pppoe accepts
2023-07-13 19:44:14 +01:00

334 lines
8.5 KiB
Nix

# This is not part of Liminix per se. This is my "scratchpad"
# configuration for the device I'm testing with.
#
# Parts of it do do things that Liminix eventually needs to do, but
# don't look in here for solutions - just for identifying the
# problems.
{ config, pkgs, lib, ... } :
let
secrets = import ./rotuer-secrets.nix;
inherit (pkgs.liminix.networking)
address
dnsmasq
hostapd
interface
route;
inherit (pkgs.liminix.services) oneshot longrun bundle target;
inherit (pkgs)
dropbear
ifwait
writeText
writeFennelScript
serviceFns;
in rec {
boot = {
tftp = {
freeSpaceBytes = 3 * 1024 * 1024;
serverip = "10.0.0.1";
ipaddr = "10.0.0.8";
};
};
imports = [
../modules/wlan.nix
../modules/standard.nix
../modules/ppp.nix
];
rootfsType = "jffs2";
hostname = "rotuer";
kernel = {
config = {
BRIDGE = "y";
NETFILTER_XT_MATCH_CONNTRACK = "y";
IP6_NF_IPTABLES= "y"; # do we still need these
IP_NF_IPTABLES= "y"; # if using nftables directly
IP_NF_NAT = "y";
IP_NF_TARGET_MASQUERADE = "y";
NETFILTER = "y";
NETFILTER_ADVANCED = "y";
NETFILTER_XTABLES = "y";
NFT_COMPAT = "y";
NFT_CT = "y";
NFT_LOG = "y";
NFT_MASQ = "y";
NFT_NAT = "y";
NFT_REJECT = "y";
NFT_REJECT_INET = "y";
NF_CONNTRACK = "y";
NF_NAT = "y";
NF_NAT_MASQUERADE = "y";
NF_TABLES= "y";
NF_TABLES_INET = "y";
NF_TABLES_IPV4 = "y";
NF_TABLES_IPV6 = "y";
};
};
services.hostap = hostapd (config.hardware.networkInterfaces.wlan_24) {
params = {
ssid = "liminix";
country_code = "GB";
hw_mode="g";
channel = "2";
wmm_enabled = 1;
ieee80211n = 1;
inherit (secrets) wpa_passphrase;
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
};
services.hostap5 = hostapd (config.hardware.networkInterfaces.wlan_5) {
params = rec {
ssid = "liminix_5";
country_code = "GB";
hw_mode="a";
channel = 36;
ht_capab = "[HT40+]";
vht_oper_chwidth = 1;
vht_oper_centr_freq_seg0_idx = channel + 6;
ieee80211ac = 1;
wmm_enabled = 1;
inherit (secrets) wpa_passphrase;
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
};
services.int =
let iface = interface {
type = "bridge";
device = "int";
};
in address iface {
family = "inet4"; address ="10.8.0.1"; prefixLength = 16;
};
services.bridge =
let
primary = services.int;
addif = dev: oneshot {
name = "add-${dev.device}-to-bridge";
up = "${ifwait}/bin/ifwait -v ${dev.device} running && ip link set dev ${dev.device} master ${primary.device}";
down = "ip link set dev ${dev} nomaster";
dependencies = [ primary dev ];
};
in bundle {
name = "bridge-members";
contents = with config.hardware.networkInterfaces; map addif [
wlan_24 lan wlan_5
];
};
services.ntp =
let config = writeText "chrony.conf" ''
pool pool.ntp.org iburst
dumpdir /run/chrony
makestep 1.0 3
'';
in longrun {
name = "ntp";
run = "${pkgs.chrony}/bin/chronyd -f ${config} -d";
};
services.sshd = longrun {
name = "sshd";
# env -i clears the environment so we don't pass anything weird to
# ssh sessions. Dropbear params are
# -e pass environment to child
# -E log to stderr
# -R create hostkeys if needed
# -P pid-file
# -F don't fork into background
run = ''
if test -d /persist; then
mkdir -p /persist/secrets/dropbear
ln -s /persist/secrets/dropbear /run
fi
PATH=${lib.makeBinPath config.defaultProfile.packages}:/bin
exec env -i ENV=/etc/ashrc PATH=$PATH ${dropbear}/bin/dropbear -e -E -R -P /run/dropbear.pid -F
'';
};
users.dnsmasq = {
uid = 51; gid= 51; gecos = "DNS/DHCP service user";
dir = "/run/dnsmasq";
shell = "/bin/false";
};
users.root = secrets.root;
groups.dnsmasq = {
gid = 51; usernames = ["dnsmasq"];
};
groups.system.usernames = ["dnsmasq"];
services.dns =
let interface = services.int;
in dnsmasq {
resolvconf = services.resolvconf;
inherit interface;
ranges = [
"10.8.0.10,10.8.0.240"
"::,constructor:${interface.device},ra-stateless"
];
domain = "fake.liminix.org";
};
services.wan =
let iface = config.hardware.networkInterfaces.wan;
in config.system.service.pppoe iface {
ppp-options = [
"debug" "+ipv6" "noauth"
"name" secrets.l2tp.name
"password" secrets.l2tp.password
];
};
services.resolvconf = oneshot rec {
dependencies = [ services.wan ];
name = "resolvconf";
up = ''
. ${serviceFns}
( in_outputs ${name}
echo "nameserver $(output ${services.wan} ns1)" > resolv.conf
echo "nameserver $(output ${services.wan} ns2)" >> resolv.conf
)
'';
down = ''
rm -rf /run/service-state/${name}/
'';
};
services.defaultroute4 = route {
name = "defaultroute4";
via = "$(output ${services.wan} address)";
target = "default";
dependencies = [ services.wan ];
};
services.defaultroute6 = route {
name = "defaultroute6";
via = "$(output ${services.wan} ipv6-peer-address)";
target = "default";
dev = "$(output ${services.wan} ifname)";
dependencies = [ services.wan ];
};
services.firewall =
let
script= pkgs.firewallgen "firewall.nft" (import ./rotuer-firewall.nix);
kmodules = pkgs.kernel-modules.override {
kernelSrc = config.system.outputs.kernel.src;
modulesupport = config.system.outputs.kernel.modulesupport;
kconfig = {
NFT_FIB_IPV4 = "m";
NFT_FIB_IPV6 = "m";
NF_TABLES = "m";
NF_CT_PROTO_DCCP = "y";
NF_CT_PROTO_SCTP = "y";
NF_CT_PROTO_UDPLITE = "y";
# NF_CONNTRACK_FTP = "m";
NFT_CT = "m";
};
targets = [
"nft_fib_ipv4"
"nft_fib_ipv6"
];
};
in oneshot {
name = "firewall";
up = ''
sh ${kmodules}/load.sh
${script};
'';
down = "${pkgs.nftables}/bin/nft flush ruleset";
};
services.packet_forwarding =
let
ip4 = "/proc/sys/net/ipv4/conf/all/forwarding";
ip6 = "/proc/sys/net/ipv6/conf/all/forwarding";
in oneshot {
name = "let-the-ip-flow";
up = ''
echo 1 > ${ip4}
echo 1 > ${ip6}
'';
down = ''
echo 0 > ${ip4};
echo 0 > ${ip6};
'';
dependencies = [ services.firewall ];
};
services.dhcp6 =
let
name = "dhcp6c.wan";
in longrun {
inherit name;
notification-fd = 10;
run = ''
export SERVICE_STATE=/run/service-state/${name}
${pkgs.odhcp6c}/bin/odhcp6c -s ${pkgs.odhcp-script} -e -v -p /run/${name}.pid -P 48 $(output ${services.wan} ifname)
)
'';
dependencies = [ services.wan ];
};
services.acquire-lan-prefix =
let script = pkgs.callPackage ./acquire-delegated-prefix.nix { };
in longrun {
name = "acquire-lan-prefix";
run = "${script} /run/service-state/dhcp6c.wan ${services.int.device}";
dependencies = [ services.dhcp6 ];
};
services.acquire-wan-address =
let script = pkgs.callPackage ./acquire-wan-address.nix { };
in longrun {
name = "acquire-wan-address";
run = "${script} /run/service-state/dhcp6c.wan $(output ${services.wan} ifname)";
dependencies = [ services.dhcp6 ];
};
services.default = target {
name = "default";
contents = with config.services; [
config.hardware.networkInterfaces.lo
config.hardware.networkInterfaces.lan
int
bridge
hostap
hostap5
ntp
defaultroute4
defaultroute6
packet_forwarding
dns
resolvconf
sshd
config.services.hostname
dhcp6
acquire-lan-prefix
acquire-wan-address
];
};
defaultProfile.packages = with pkgs; [
min-collect-garbage
];
}