forked from mirrors/nixpkgs
nixos/system/boot/initrd-openvpn: Add openvpn options for initrd
nixos/tests/initrd-openvpn: Add test for openvpn in the initramfs The module in this commit adds new options that allows the integration of an OpenVPN client into the initrd. This can be used e.g. to remotely unlock LUKS devices. This commit also adds two tests for `boot.initrd.network.openvpn`. The first one is a basic test to validate that a failing connection does not prevent the machine from booting. The second test validates that this module actually creates a valid openvpn connection. For this, it spawns three nodes: - The client that uses boot.initrd.network.openvpn - An OpenVPN server that acts as gateway and forwards a port to the client - A node that is external to the OpenVPN network The client connects to the OpenVPN server and spawns a netcat instance that echos a value to every client. Afterwards, the external node checks if it receives this value over the forwarded port on the OpenVPN gateway.
This commit is contained in:
parent
db5bbef31f
commit
c684398c6a
|
@ -937,6 +937,7 @@
|
||||||
./system/boot/grow-partition.nix
|
./system/boot/grow-partition.nix
|
||||||
./system/boot/initrd-network.nix
|
./system/boot/initrd-network.nix
|
||||||
./system/boot/initrd-ssh.nix
|
./system/boot/initrd-ssh.nix
|
||||||
|
./system/boot/initrd-openvpn.nix
|
||||||
./system/boot/kernel.nix
|
./system/boot/kernel.nix
|
||||||
./system/boot/kexec.nix
|
./system/boot/kexec.nix
|
||||||
./system/boot/loader/efi.nix
|
./system/boot/loader/efi.nix
|
||||||
|
|
81
nixos/modules/system/boot/initrd-openvpn.nix
Normal file
81
nixos/modules/system/boot/initrd-openvpn.nix
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
cfg = config.boot.initrd.network.openvpn;
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
options = {
|
||||||
|
|
||||||
|
boot.initrd.network.openvpn.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Starts an OpenVPN client during initrd boot. It can be used to e.g.
|
||||||
|
remotely accessing the SSH service controlled by
|
||||||
|
<option>boot.initrd.network.ssh</option> or other network services
|
||||||
|
included. Service is killed when stage-1 boot is finished.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
boot.initrd.network.openvpn.configuration = mkOption {
|
||||||
|
type = types.path; # Same type as boot.initrd.secrets
|
||||||
|
description = ''
|
||||||
|
The configuration file for OpenVPN.
|
||||||
|
|
||||||
|
<warning>
|
||||||
|
<para>
|
||||||
|
Unless your bootloader supports initrd secrets, this configuration
|
||||||
|
is stored insecurely in the global Nix store.
|
||||||
|
</para>
|
||||||
|
</warning>
|
||||||
|
'';
|
||||||
|
example = "./configuration.ovpn";
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf (config.boot.initrd.network.enable && cfg.enable) {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.configuration != null;
|
||||||
|
message = "You should specify a configuration for initrd OpenVPN";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Add kernel modules needed for OpenVPN
|
||||||
|
boot.initrd.kernelModules = [ "tun" "tap" ];
|
||||||
|
|
||||||
|
# Add openvpn and ip binaries to the initrd
|
||||||
|
# The shared libraries are required for DNS resolution
|
||||||
|
boot.initrd.extraUtilsCommands = ''
|
||||||
|
copy_bin_and_libs ${pkgs.openvpn}/bin/openvpn
|
||||||
|
copy_bin_and_libs ${pkgs.iproute}/bin/ip
|
||||||
|
|
||||||
|
cp -pv ${pkgs.glibc}/lib/libresolv.so.2 $out/lib
|
||||||
|
cp -pv ${pkgs.glibc}/lib/libnss_dns.so.2 $out/lib
|
||||||
|
'';
|
||||||
|
|
||||||
|
boot.initrd.secrets = {
|
||||||
|
"/etc/initrd.ovpn" = cfg.configuration;
|
||||||
|
};
|
||||||
|
|
||||||
|
# openvpn --version would exit with 1 instead of 0
|
||||||
|
boot.initrd.extraUtilsCommandsTest = ''
|
||||||
|
$out/bin/openvpn --show-gateway
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Add `iproute /bin/ip` to the config, to ensure that openvpn
|
||||||
|
# is able to set the routes
|
||||||
|
boot.initrd.network.postCommands = ''
|
||||||
|
(cat /etc/initrd.ovpn; echo -e '\niproute /bin/ip') | \
|
||||||
|
openvpn /dev/stdin &
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -150,6 +150,7 @@ in
|
||||||
incron = handleTest ./incron.nix {};
|
incron = handleTest ./incron.nix {};
|
||||||
influxdb = handleTest ./influxdb.nix {};
|
influxdb = handleTest ./influxdb.nix {};
|
||||||
initrd-network-ssh = handleTest ./initrd-network-ssh {};
|
initrd-network-ssh = handleTest ./initrd-network-ssh {};
|
||||||
|
initrd-network-openvpn = handleTest ./initrd-network-openvpn {};
|
||||||
initrdNetwork = handleTest ./initrd-network.nix {};
|
initrdNetwork = handleTest ./initrd-network.nix {};
|
||||||
installer = handleTest ./installer.nix {};
|
installer = handleTest ./installer.nix {};
|
||||||
iodine = handleTest ./iodine.nix {};
|
iodine = handleTest ./iodine.nix {};
|
||||||
|
|
145
nixos/tests/initrd-network-openvpn/default.nix
Normal file
145
nixos/tests/initrd-network-openvpn/default.nix
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import ../make-test-python.nix ({ lib, ...}:
|
||||||
|
|
||||||
|
{
|
||||||
|
name = "initrd-network-openvpn";
|
||||||
|
|
||||||
|
nodes =
|
||||||
|
let
|
||||||
|
|
||||||
|
# Inlining of the shared secret for the
|
||||||
|
# OpenVPN server and client
|
||||||
|
secretblock = ''
|
||||||
|
secret [inline]
|
||||||
|
<secret>
|
||||||
|
${lib.readFile ./shared.key}
|
||||||
|
</secret>
|
||||||
|
'';
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
|
||||||
|
# Minimal test case to check a successful boot, even with invalid config
|
||||||
|
minimalboot =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
boot.initrd.network = {
|
||||||
|
enable = true;
|
||||||
|
openvpn = {
|
||||||
|
enable = true;
|
||||||
|
configuration = "/dev/null";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# initrd VPN client
|
||||||
|
ovpnclient =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
virtualisation.useBootLoader = true;
|
||||||
|
virtualisation.vlans = [ 1 ];
|
||||||
|
|
||||||
|
boot.initrd = {
|
||||||
|
# This command does not fork to keep the VM in the state where
|
||||||
|
# only the initramfs is loaded
|
||||||
|
preLVMCommands =
|
||||||
|
''
|
||||||
|
/bin/nc -p 1234 -lke /bin/echo TESTVALUE
|
||||||
|
'';
|
||||||
|
|
||||||
|
network = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# Work around udhcpc only getting a lease on eth0
|
||||||
|
postCommands = ''
|
||||||
|
/bin/ip addr add 192.168.1.2/24 dev eth1
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Example configuration for OpenVPN
|
||||||
|
# This is the main reason for this test
|
||||||
|
openvpn = {
|
||||||
|
enable = true;
|
||||||
|
configuration = "${./initrd.ovpn}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# VPN server and gateway for ovpnclient between vlan 1 and 2
|
||||||
|
ovpnserver =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
virtualisation.vlans = [ 1 2 ];
|
||||||
|
|
||||||
|
# Enable NAT and forward port 12345 to port 1234
|
||||||
|
networking.nat = {
|
||||||
|
enable = true;
|
||||||
|
internalInterfaces = [ "tun0" ];
|
||||||
|
externalInterface = "eth2";
|
||||||
|
forwardPorts = [ { destination = "10.8.0.2:1234";
|
||||||
|
sourcePort = 12345; } ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Trust tun0 and allow the VPN Server to be reached
|
||||||
|
networking.firewall = {
|
||||||
|
trustedInterfaces = [ "tun0" ];
|
||||||
|
allowedUDPPorts = [ 1194 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Minimal OpenVPN server configuration
|
||||||
|
services.openvpn.servers.testserver =
|
||||||
|
{
|
||||||
|
config = ''
|
||||||
|
dev tun0
|
||||||
|
ifconfig 10.8.0.1 10.8.0.2
|
||||||
|
${secretblock}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Client that resides in the "external" VLAN
|
||||||
|
testclient =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
virtualisation.vlans = [ 2 ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
''
|
||||||
|
# Minimal test case, checks whether enabling (with invalid config) harms
|
||||||
|
# the boot process
|
||||||
|
with subtest("Check for successful boot with broken openvpn config"):
|
||||||
|
minimalboot.start()
|
||||||
|
# If we get to multi-user.target, we booted successfully
|
||||||
|
minimalboot.wait_for_unit("multi-user.target")
|
||||||
|
minimalboot.shutdown()
|
||||||
|
|
||||||
|
# Elaborated test case where the ovpnclient (where this module is used)
|
||||||
|
# can be reached by testclient only over ovpnserver.
|
||||||
|
# This is an indirect test for success.
|
||||||
|
with subtest("Check for connection from initrd VPN client, config as file"):
|
||||||
|
ovpnserver.start()
|
||||||
|
testclient.start()
|
||||||
|
ovpnclient.start()
|
||||||
|
|
||||||
|
# Wait until the OpenVPN Server is available
|
||||||
|
ovpnserver.wait_for_unit("openvpn-testserver.service")
|
||||||
|
ovpnserver.succeed("ping -c 1 10.8.0.1")
|
||||||
|
|
||||||
|
# Wait for the client to connect
|
||||||
|
ovpnserver.wait_until_succeeds("ping -c 1 10.8.0.2")
|
||||||
|
|
||||||
|
# Wait until the testclient has network
|
||||||
|
testclient.wait_for_unit("network.target")
|
||||||
|
|
||||||
|
# Check that ovpnclient is reachable over vlan 1
|
||||||
|
ovpnserver.succeed("nc -w 2 192.168.1.2 1234 | grep -q TESTVALUE")
|
||||||
|
|
||||||
|
# Check that ovpnclient is reachable over tun0
|
||||||
|
ovpnserver.succeed("nc -w 2 10.8.0.2 1234 | grep -q TESTVALUE")
|
||||||
|
|
||||||
|
# Check that ovpnclient is reachable from testclient over the gateway
|
||||||
|
testclient.succeed("nc -w 2 192.168.2.3 12345 | grep -q TESTVALUE")
|
||||||
|
'';
|
||||||
|
})
|
29
nixos/tests/initrd-network-openvpn/initrd.ovpn
Normal file
29
nixos/tests/initrd-network-openvpn/initrd.ovpn
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
remote 192.168.1.3
|
||||||
|
dev tun
|
||||||
|
ifconfig 10.8.0.2 10.8.0.1
|
||||||
|
# Only force VLAN 2 through the VPN
|
||||||
|
route 192.168.2.0 255.255.255.0 10.8.0.1
|
||||||
|
secret [inline]
|
||||||
|
<secret>
|
||||||
|
#
|
||||||
|
# 2048 bit OpenVPN static key
|
||||||
|
#
|
||||||
|
-----BEGIN OpenVPN Static key V1-----
|
||||||
|
553aabe853acdfe51cd6fcfea93dcbb0
|
||||||
|
c8797deadd1187606b1ea8f2315eb5e6
|
||||||
|
67c0d7e830f50df45686063b189d6c6b
|
||||||
|
aab8bb3430cc78f7bb1f78628d5c3742
|
||||||
|
0cef4f53a5acab2894905f4499f95d8e
|
||||||
|
e69b7b6748b17016f89e19e91481a9fd
|
||||||
|
bf8c10651f41a1d4fdf5f438925a6733
|
||||||
|
13cec8f04701eb47b8f7ffc48bc3d7af
|
||||||
|
65f07bce766015b87c3db4d668c655ff
|
||||||
|
be5a69522a8e60ccb217f8521681b45d
|
||||||
|
27c0b70bdfbfbb426c7646d80adf7482
|
||||||
|
3ddac58b25cb1c1bb100de974478b4c6
|
||||||
|
8b45a94261a2405e99810cb2b3abd49f
|
||||||
|
21b3198ada87ff3c4e656a008e540a8d
|
||||||
|
e7811584363597599cce2040a68ac00e
|
||||||
|
f2125540e0f7f4adc37cb3f0d922eeb7
|
||||||
|
-----END OpenVPN Static key V1-----
|
||||||
|
</secret>
|
21
nixos/tests/initrd-network-openvpn/shared.key
Normal file
21
nixos/tests/initrd-network-openvpn/shared.key
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#
|
||||||
|
# 2048 bit OpenVPN static key
|
||||||
|
#
|
||||||
|
-----BEGIN OpenVPN Static key V1-----
|
||||||
|
553aabe853acdfe51cd6fcfea93dcbb0
|
||||||
|
c8797deadd1187606b1ea8f2315eb5e6
|
||||||
|
67c0d7e830f50df45686063b189d6c6b
|
||||||
|
aab8bb3430cc78f7bb1f78628d5c3742
|
||||||
|
0cef4f53a5acab2894905f4499f95d8e
|
||||||
|
e69b7b6748b17016f89e19e91481a9fd
|
||||||
|
bf8c10651f41a1d4fdf5f438925a6733
|
||||||
|
13cec8f04701eb47b8f7ffc48bc3d7af
|
||||||
|
65f07bce766015b87c3db4d668c655ff
|
||||||
|
be5a69522a8e60ccb217f8521681b45d
|
||||||
|
27c0b70bdfbfbb426c7646d80adf7482
|
||||||
|
3ddac58b25cb1c1bb100de974478b4c6
|
||||||
|
8b45a94261a2405e99810cb2b3abd49f
|
||||||
|
21b3198ada87ff3c4e656a008e540a8d
|
||||||
|
e7811584363597599cce2040a68ac00e
|
||||||
|
f2125540e0f7f4adc37cb3f0d922eeb7
|
||||||
|
-----END OpenVPN Static key V1-----
|
Loading…
Reference in a new issue