nixos/tests/networking: add eap tests (#484773)
This commit is contained in:
@@ -8,10 +8,34 @@ with import ../../lib/testing-python.nix { inherit system pkgs; };
|
||||
|
||||
let
|
||||
lib = pkgs.lib;
|
||||
|
||||
# Generate EAP certificates on the fly (CA, server, and client certs)
|
||||
eapCerts = pkgs.runCommand "eap-certs" { buildInputs = [ pkgs.openssl ]; } ''
|
||||
mkdir -p $out
|
||||
|
||||
# Create CA certificate
|
||||
openssl req -x509 -newkey rsa:2048 -days 365000 -nodes \
|
||||
-keyout $out/ca.key -out $out/ca.cert \
|
||||
-subj "/CN=ExampleCA"
|
||||
|
||||
# Create server certificate
|
||||
openssl req -newkey rsa:2048 -nodes \
|
||||
-keyout $out/server.key -out server.csr \
|
||||
-subj "/CN=server.example.com"
|
||||
openssl x509 -req -in server.csr -CA $out/ca.cert -CAkey $out/ca.key \
|
||||
-days 365000 -set_serial 100 -out $out/server.cert
|
||||
|
||||
# Create client certificate
|
||||
openssl req -newkey rsa:2048 -nodes \
|
||||
-keyout $out/client1.key -out client1.csr \
|
||||
-subj "/CN=client1.example.com"
|
||||
openssl x509 -req -in client1.csr -CA $out/ca.cert -CAkey $out/ca.key \
|
||||
-days 365000 -set_serial 101 -out $out/client1.cert
|
||||
'';
|
||||
|
||||
# this is intended as a client test since you shouldn't use NetworkManager for a router or server
|
||||
# so using systemd-networkd for the router vm is fine in these tests.
|
||||
router = import ./router.nix { networkd = true; };
|
||||
qemu-common = import ../../lib/qemu-common.nix { inherit (pkgs) lib pkgs; };
|
||||
clientConfig =
|
||||
extraConfig:
|
||||
lib.recursiveUpdate {
|
||||
@@ -180,6 +204,100 @@ let
|
||||
client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::23")
|
||||
'';
|
||||
};
|
||||
eap =
|
||||
let
|
||||
toBase64Blob =
|
||||
file:
|
||||
"data:;base64,"
|
||||
+ builtins.readFile (
|
||||
pkgs.runCommand "base64" { } ''
|
||||
${pkgs.coreutils}/bin/base64 -w0 ${file} > $out
|
||||
''
|
||||
);
|
||||
in
|
||||
{
|
||||
name = "eap / 802.1x with secrets in blob encoding";
|
||||
nodes = {
|
||||
router = import ./router-eap.nix { inherit eapCerts; };
|
||||
client = clientConfig {
|
||||
networking.networkmanager.ensureProfiles.profiles.default = {
|
||||
ipv4.method = "auto";
|
||||
"802-1x" = {
|
||||
eap = "tls";
|
||||
identity = "client1.example.com";
|
||||
ca-cert = toBase64Blob "${eapCerts}/ca.cert";
|
||||
client-cert = toBase64Blob "${eapCerts}/client1.cert";
|
||||
private-key = toBase64Blob "${eapCerts}/client1.key";
|
||||
private-key-password-flags = "4";
|
||||
};
|
||||
};
|
||||
networking.wireless = {
|
||||
# it is a bit unfortunate that wpa-supplicant is equated with
|
||||
# `wireless` when it also works for wired connections
|
||||
enable = lib.mkOverride 9 true;
|
||||
driver = "wired";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
client.wait_for_unit("NetworkManager.service")
|
||||
router.wait_for_unit("freeradius.service")
|
||||
router.wait_for_unit("hostapd.service")
|
||||
router.wait_until_succeeds("journalctl -b --unit freeradius.service | grep \"Sent Access-Accept\"")
|
||||
router.wait_until_succeeds("journalctl -b --unit freeradius.service | grep \"TLS-Client-Cert-Common-Name := \\\"client1.example.com\\\"\"")
|
||||
'';
|
||||
};
|
||||
eapFiles = {
|
||||
name = "eap / 802.1x with secrets in stored in files";
|
||||
nodes = {
|
||||
router = import ./router-eap.nix { inherit eapCerts; };
|
||||
client = clientConfig {
|
||||
environment.etc = {
|
||||
"wpa_supplicant/ca.cert" = {
|
||||
source = "${eapCerts}/ca.cert";
|
||||
user = "wpa_supplicant";
|
||||
mode = "0400";
|
||||
};
|
||||
"wpa_supplicant/client1.cert" = {
|
||||
source = "${eapCerts}/client1.cert";
|
||||
user = "wpa_supplicant";
|
||||
mode = "0400";
|
||||
};
|
||||
"wpa_supplicant/client1.key" = {
|
||||
source = "${eapCerts}/client1.key";
|
||||
user = "wpa_supplicant";
|
||||
mode = "0400";
|
||||
};
|
||||
};
|
||||
networking.networkmanager.ensureProfiles.profiles.default = {
|
||||
ipv4.method = "auto";
|
||||
"802-1x" = {
|
||||
eap = "tls";
|
||||
identity = "client1.example.com";
|
||||
ca-cert = "/etc/wpa_supplicant/ca.cert";
|
||||
client-cert = "/etc/wpa_supplicant/client1.cert";
|
||||
private-key = "/etc/wpa_supplicant/client1.key";
|
||||
private-key-password-flags = "4";
|
||||
};
|
||||
};
|
||||
networking.wireless = {
|
||||
enable = lib.mkOverride 9 true;
|
||||
driver = "wired";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
client.wait_for_unit("NetworkManager.service")
|
||||
router.wait_for_unit("freeradius.service")
|
||||
router.wait_for_unit("hostapd.service")
|
||||
router.wait_until_succeeds("journalctl -b --unit freeradius.service | grep \"Sent Access-Accept\"")
|
||||
router.wait_until_succeeds("journalctl -b --unit freeradius.service | grep \"TLS-Client-Cert-Common-Name := \\\"client1.example.com\\\"\"")
|
||||
'';
|
||||
};
|
||||
};
|
||||
in
|
||||
lib.mapAttrs (lib.const (
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
{ eapCerts }:
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
radiusDir =
|
||||
let
|
||||
eapConfig = builtins.toFile "eap.conf" ''
|
||||
eap {
|
||||
default_eap_type = tls
|
||||
timer_expire = 60
|
||||
ignore_unknown_eap_types = no
|
||||
|
||||
tls {
|
||||
# Path to CA certificate
|
||||
ca_file = ''${certdir}/ca.cert
|
||||
|
||||
# Path to server certificate and private key
|
||||
certificate_file = ''${certdir}/server.cert
|
||||
private_key_file = ''${certdir}/server.key
|
||||
|
||||
# Enable mutual authentication
|
||||
require_client_cert = yes
|
||||
|
||||
# Cipher suite (example)
|
||||
ciphers = "DEFAULT"
|
||||
}
|
||||
}
|
||||
'';
|
||||
clientsConfig = builtins.toFile "client.conf" ''
|
||||
client localhost {
|
||||
ipaddr = 127.0.0.1
|
||||
secret = insecure
|
||||
require_message_authenticator = no
|
||||
}
|
||||
'';
|
||||
|
||||
# sample users file, not used for eap-tls, only for e.g. eap-ttls
|
||||
usersConfig = builtins.toFile "users.conf" ''
|
||||
testuser Cleartext-Password := "supersecret"
|
||||
'';
|
||||
|
||||
# this constructs the freeradius config directory
|
||||
# it starts with the upstream config, then overwrites certain files
|
||||
# and uses the generated certs from eapCerts
|
||||
buildScript = pkgs.writeShellApplication {
|
||||
name = "builder";
|
||||
|
||||
runtimeInputs = [ pkgs.coreutils ];
|
||||
|
||||
# https://www.shellcheck.net/wiki/SC2154 -- out is referenced but not assigned.
|
||||
excludeShellChecks = [ "SC2154" ];
|
||||
|
||||
text = ''
|
||||
cp --recursive ${pkgs.freeradius}/etc/* "$out"
|
||||
chmod +w -R "$out"
|
||||
cp --force ${eapCerts}/ca.cert "$out/certs/ca.cert"
|
||||
cp --force ${eapCerts}/ca.key "$out/certs/ca.key"
|
||||
cp --force ${eapCerts}/server.cert "$out/certs/server.cert"
|
||||
cp --force ${eapCerts}/server.key "$out/certs/server.key"
|
||||
cp --force ${eapConfig} "$out/mods-enabled/eap"
|
||||
cp --force ${usersConfig} "$out/users"
|
||||
cp --force ${clientsConfig} "$out/clients.conf"
|
||||
'';
|
||||
};
|
||||
in
|
||||
derivation {
|
||||
name = "radius_dir";
|
||||
builder = "${pkgs.bash}/bin/bash";
|
||||
args = [ "${buildScript}/bin/builder" ];
|
||||
inherit (pkgs) system;
|
||||
};
|
||||
inherit (pkgs) lib;
|
||||
vlanIfs = lib.range 1 (lib.length config.virtualisation.vlans);
|
||||
in
|
||||
{
|
||||
virtualisation.vlans = [
|
||||
1
|
||||
2
|
||||
3
|
||||
];
|
||||
boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
|
||||
networking = {
|
||||
useDHCP = false;
|
||||
useNetworkd = true;
|
||||
firewall.checkReversePath = true;
|
||||
firewall.allowedUDPPorts = [ 547 ];
|
||||
interfaces = lib.mkOverride 0 (
|
||||
lib.listToAttrs (
|
||||
lib.forEach vlanIfs (
|
||||
n:
|
||||
lib.nameValuePair "eth${toString n}" {
|
||||
ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.${toString n}.1";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
ipv6.addresses = [
|
||||
{
|
||||
address = "fd00:1234:5678:${toString n}::1";
|
||||
prefixLength = 64;
|
||||
}
|
||||
];
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
services.freeradius = {
|
||||
enable = true;
|
||||
configDir = radiusDir;
|
||||
debug = true;
|
||||
};
|
||||
|
||||
# upstream nixpkgs hostapd is focused on Wifi
|
||||
systemd.services.hostapd =
|
||||
let
|
||||
hostapdConfig = builtins.toFile "hostapd.conf" ''
|
||||
interface=eth1
|
||||
driver=wired
|
||||
logger_stdout=-1
|
||||
logger_stdout_level=1
|
||||
debug=2
|
||||
dump_file=/tmp/hostapd.dump
|
||||
ieee8021x=1
|
||||
eap_reauth_period=3600
|
||||
use_pae_group_addr=1
|
||||
##### RADIUS configuration ####################################################
|
||||
own_ip_addr=127.0.0.1
|
||||
nas_identifier=ap.example.com
|
||||
auth_server_addr=127.0.0.1
|
||||
auth_server_port=1812
|
||||
auth_server_shared_secret=insecure
|
||||
'';
|
||||
in
|
||||
{
|
||||
description = "IEEE 802.11 Host Access-Point Daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.hostapd}/bin/hostapd ${hostapdConfig}";
|
||||
Restart = "always";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
RuntimeDirectory = "hostapd";
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user