mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-24 07:53:19 +00:00
e7203cb03d
Provide the option forwardDns in virtualisation.xen.bridge, which enables forwarding of DNS queries to the default resolver, allowing outside internet access for the xen guests.
421 lines
14 KiB
Nix
421 lines
14 KiB
Nix
# Xen hypervisor (Dom0) support.
|
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.virtualisation.xen;
|
|
in
|
|
|
|
{
|
|
###### interface
|
|
|
|
options = {
|
|
|
|
virtualisation.xen.enable =
|
|
mkOption {
|
|
default = false;
|
|
description =
|
|
''
|
|
Setting this option enables the Xen hypervisor, a
|
|
virtualisation technology that allows multiple virtual
|
|
machines, known as <emphasis>domains</emphasis>, to run
|
|
concurrently on the physical machine. NixOS runs as the
|
|
privileged <emphasis>Domain 0</emphasis>. This option
|
|
requires a reboot to take effect.
|
|
'';
|
|
};
|
|
|
|
virtualisation.xen.package = mkOption {
|
|
type = types.package;
|
|
defaultText = "pkgs.xen";
|
|
example = literalExample "pkgs.xen-light";
|
|
description = ''
|
|
The package used for Xen binary.
|
|
'';
|
|
};
|
|
|
|
virtualisation.xen.qemu = mkOption {
|
|
type = types.path;
|
|
defaultText = "\${pkgs.xen}/lib/xen/bin/qemu-system-i386";
|
|
example = literalExample "''${pkgs.qemu_xen-light}/bin/qemu-system-i386";
|
|
description = ''
|
|
The qemu binary to use for Dom-0 backend.
|
|
'';
|
|
};
|
|
|
|
virtualisation.xen.qemu-package = mkOption {
|
|
type = types.package;
|
|
defaultText = "pkgs.xen";
|
|
example = literalExample "pkgs.qemu_xen-light";
|
|
description = ''
|
|
The package with qemu binaries for xendomains.
|
|
'';
|
|
};
|
|
|
|
virtualisation.xen.bootParams =
|
|
mkOption {
|
|
default = "";
|
|
description =
|
|
''
|
|
Parameters passed to the Xen hypervisor at boot time.
|
|
'';
|
|
};
|
|
|
|
virtualisation.xen.domain0MemorySize =
|
|
mkOption {
|
|
default = 0;
|
|
example = 512;
|
|
description =
|
|
''
|
|
Amount of memory (in MiB) allocated to Domain 0 on boot.
|
|
If set to 0, all memory is assigned to Domain 0.
|
|
'';
|
|
};
|
|
|
|
virtualisation.xen.bridge = {
|
|
name = mkOption {
|
|
default = "xenbr0";
|
|
description = ''
|
|
Name of bridge the Xen domUs connect to.
|
|
'';
|
|
};
|
|
|
|
address = mkOption {
|
|
type = types.str;
|
|
default = "172.16.0.1";
|
|
description = ''
|
|
IPv4 address of the bridge.
|
|
'';
|
|
};
|
|
|
|
prefixLength = mkOption {
|
|
type = types.addCheck types.int (n: n >= 0 && n <= 32);
|
|
default = 16;
|
|
description = ''
|
|
Subnet mask of the bridge interface, specified as the number of
|
|
bits in the prefix (<literal>24</literal>).
|
|
A DHCP server will provide IP addresses for the whole, remaining
|
|
subnet.
|
|
'';
|
|
};
|
|
|
|
forwardDns = mkOption {
|
|
default = false;
|
|
description = ''
|
|
If set to <literal>true</literal>, the DNS queries from the
|
|
hosts connected to the bridge will be forwarded to the DNS
|
|
servers specified in /etc/resolv.conf .
|
|
'';
|
|
};
|
|
|
|
};
|
|
|
|
virtualisation.xen.stored =
|
|
mkOption {
|
|
type = types.path;
|
|
description =
|
|
''
|
|
Xen Store daemon to use. Defaults to oxenstored of the xen package.
|
|
'';
|
|
};
|
|
|
|
virtualisation.xen.trace =
|
|
mkOption {
|
|
default = false;
|
|
description =
|
|
''
|
|
Enable Xen tracing.
|
|
'';
|
|
};
|
|
};
|
|
|
|
|
|
###### implementation
|
|
|
|
config = mkIf cfg.enable {
|
|
assertions = [ {
|
|
assertion = pkgs.stdenv.isx86_64;
|
|
message = "Xen currently not supported on ${pkgs.stdenv.system}";
|
|
} {
|
|
assertion = config.boot.loader.grub.enable && (config.boot.loader.grub.efiSupport == false);
|
|
message = "Xen currently does not support EFI boot";
|
|
} ];
|
|
|
|
virtualisation.xen.package = mkDefault pkgs.xen;
|
|
virtualisation.xen.qemu = mkDefault "${pkgs.xen}/lib/xen/bin/qemu-system-i386";
|
|
virtualisation.xen.qemu-package = mkDefault pkgs.xen;
|
|
virtualisation.xen.stored = mkDefault "${cfg.package}/bin/oxenstored";
|
|
|
|
environment.systemPackages = [ cfg.package ];
|
|
|
|
# Make sure Domain 0 gets the required configuration
|
|
#boot.kernelPackages = pkgs.boot.kernelPackages.override { features={xen_dom0=true;}; };
|
|
|
|
boot.kernelModules =
|
|
[ "xen-evtchn" "xen-gntdev" "xen-gntalloc" "xen-blkback" "xen-netback"
|
|
"xen-pciback" "evtchn" "gntdev" "netbk" "blkbk" "xen-scsibk"
|
|
"usbbk" "pciback" "xen-acpi-processor" "blktap2" "tun" "netxen_nic"
|
|
"xen_wdt" "xen-acpi-processor" "xen-privcmd" "xen-scsiback"
|
|
"xenfs"
|
|
];
|
|
|
|
# The xenfs module is needed in system.activationScripts.xen, but
|
|
# the modprobe command there fails silently. Include xenfs in the
|
|
# initrd as a work around.
|
|
boot.initrd.kernelModules = [ "xenfs" ];
|
|
|
|
# The radeonfb kernel module causes the screen to go black as soon
|
|
# as it's loaded, so don't load it.
|
|
boot.blacklistedKernelModules = [ "radeonfb" ];
|
|
|
|
# Increase the number of loopback devices from the default (8),
|
|
# which is way too small because every VM virtual disk requires a
|
|
# loopback device.
|
|
boot.extraModprobeConfig =
|
|
''
|
|
options loop max_loop=64
|
|
'';
|
|
|
|
virtualisation.xen.bootParams = [] ++
|
|
optionals cfg.trace [ "loglvl=all" "guest_loglvl=all" ] ++
|
|
optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M";
|
|
|
|
system.extraSystemBuilderCmds =
|
|
''
|
|
ln -s ${cfg.package}/boot/xen.gz $out/xen.gz
|
|
echo "${toString cfg.bootParams}" > $out/xen-params
|
|
'';
|
|
|
|
# Mount the /proc/xen pseudo-filesystem.
|
|
system.activationScripts.xen =
|
|
''
|
|
if [ -d /proc/xen ]; then
|
|
${pkgs.kmod}/bin/modprobe xenfs 2> /dev/null
|
|
${pkgs.utillinux}/bin/mountpoint -q /proc/xen || \
|
|
${pkgs.utillinux}/bin/mount -t xenfs none /proc/xen
|
|
fi
|
|
'';
|
|
|
|
# Domain 0 requires a pvops-enabled kernel.
|
|
system.requiredKernelConfig = with config.lib.kernelConfig;
|
|
[ (isYes "XEN")
|
|
(isYes "X86_IO_APIC")
|
|
(isYes "ACPI")
|
|
(isYes "XEN_DOM0")
|
|
(isYes "PCI_XEN")
|
|
(isYes "XEN_DEV_EVTCHN")
|
|
(isYes "XENFS")
|
|
(isYes "XEN_COMPAT_XENFS")
|
|
(isYes "XEN_SYS_HYPERVISOR")
|
|
(isYes "XEN_GNTDEV")
|
|
(isYes "XEN_BACKEND")
|
|
(isModule "XEN_NETDEV_BACKEND")
|
|
(isModule "XEN_BLKDEV_BACKEND")
|
|
(isModule "XEN_PCIDEV_BACKEND")
|
|
(isYes "XEN_BALLOON")
|
|
(isYes "XEN_SCRUB_PAGES")
|
|
];
|
|
|
|
|
|
environment.etc =
|
|
[ { source = "${cfg.package}/etc/xen/xl.conf";
|
|
target = "xen/xl.conf";
|
|
}
|
|
{ source = "${cfg.package}/etc/xen/scripts";
|
|
target = "xen/scripts";
|
|
}
|
|
{ source = "${cfg.package}/etc/default/xendomains";
|
|
target = "default/xendomains";
|
|
}
|
|
];
|
|
|
|
# Xen provides udev rules.
|
|
services.udev.packages = [ cfg.package ];
|
|
|
|
services.udev.path = [ pkgs.bridge-utils pkgs.iproute ];
|
|
|
|
systemd.services.xen-store = {
|
|
description = "Xen Store Daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network.target" "xen-store.socket" ];
|
|
requires = [ "xen-store.socket" ];
|
|
preStart = ''
|
|
export XENSTORED_ROOTDIR="/var/lib/xenstored"
|
|
rm -f "$XENSTORED_ROOTDIR"/tdb* &>/dev/null
|
|
|
|
mkdir -p /var/run
|
|
mkdir -p /var/log/xen # Running xl requires /var/log/xen and /var/lib/xen,
|
|
mkdir -p /var/lib/xen # so we create them here unconditionally.
|
|
grep -q control_d /proc/xen/capabilities
|
|
'';
|
|
serviceConfig.ExecStart = ''
|
|
${cfg.stored}${optionalString cfg.trace " -T /var/log/xen/xenstored-trace.log"} --no-fork
|
|
'';
|
|
postStart = ''
|
|
time=0
|
|
timeout=30
|
|
# Wait for xenstored to actually come up, timing out after 30 seconds
|
|
while [ $time -lt $timeout ] && ! `${cfg.package}/bin/xenstore-read -s / >/dev/null 2>&1` ; do
|
|
time=$(($time+1))
|
|
sleep 1
|
|
done
|
|
|
|
# Exit if we timed out
|
|
if ! [ $time -lt $timeout ] ; then
|
|
echo "Could not start Xenstore Daemon"
|
|
exit 1
|
|
fi
|
|
|
|
${cfg.package}/bin/xenstore-write "/local/domain/0/name" "Domain-0"
|
|
${cfg.package}/bin/xenstore-write "/local/domain/0/domid" 0
|
|
'';
|
|
};
|
|
|
|
systemd.sockets.xen-store = {
|
|
description = "XenStore Socket for userspace API";
|
|
wantedBy = [ "sockets.target" ];
|
|
socketConfig = {
|
|
ListenStream = [ "/var/run/xenstored/socket" "/var/run/xenstored/socket_ro" ];
|
|
SocketMode = "0660";
|
|
SocketUser = "root";
|
|
SocketGroup = "root";
|
|
};
|
|
};
|
|
|
|
|
|
systemd.services.xen-console = {
|
|
description = "Xen Console Daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "xen-store.service" ];
|
|
preStart = ''
|
|
mkdir -p /var/run/xen
|
|
${optionalString cfg.trace "mkdir -p /var/log/xen"}
|
|
grep -q control_d /proc/xen/capabilities
|
|
'';
|
|
serviceConfig = {
|
|
ExecStart = ''
|
|
${cfg.package}/bin/xenconsoled${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"}
|
|
'';
|
|
};
|
|
};
|
|
|
|
|
|
systemd.services.xen-qemu = {
|
|
description = "Xen Qemu Daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "xen-console.service" ];
|
|
serviceConfig.ExecStart = ''
|
|
${cfg.qemu} -xen-attach -xen-domid 0 -name dom0 -M xenpv \
|
|
-nographic -monitor /dev/null -serial /dev/null -parallel /dev/null
|
|
'';
|
|
};
|
|
|
|
|
|
systemd.services.xen-watchdog = {
|
|
description = "Xen Watchdog Daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "xen-qemu.service" ];
|
|
serviceConfig.ExecStart = "${cfg.package}/bin/xenwatchdogd 30 15";
|
|
serviceConfig.Type = "forking";
|
|
serviceConfig.RestartSec = "1";
|
|
serviceConfig.Restart = "on-failure";
|
|
};
|
|
|
|
|
|
systemd.services.xen-bridge = {
|
|
description = "Xen bridge";
|
|
wantedBy = [ "multi-user.target" ];
|
|
before = [ "xen-domains.service" ];
|
|
preStart = ''
|
|
mkdir -p /var/run/xen
|
|
touch /var/run/xen/dnsmasq.pid
|
|
touch /var/run/xen/dnsmasq.etherfile
|
|
touch /var/run/xen/dnsmasq.leasefile
|
|
|
|
IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Usable\ range`
|
|
export XEN_BRIDGE_IP_RANGE_START="${"\${data[1]//[[:blank:]]/}"}"
|
|
export XEN_BRIDGE_IP_RANGE_END="${"\${data[2]//[[:blank:]]/}"}"
|
|
|
|
IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ address`
|
|
export XEN_BRIDGE_NETWORK_ADDRESS="${"\${data[1]//[[:blank:]]/}"}"
|
|
|
|
echo "${cfg.bridge.address} host gw dns" > /var/run/xen/dnsmasq.hostsfile
|
|
|
|
cat <<EOF > /var/run/xen/dnsmasq.conf
|
|
no-daemon
|
|
pid-file=/var/run/xen/dnsmasq.pid
|
|
interface=${cfg.bridge.name}
|
|
except-interface=lo
|
|
bind-interfaces
|
|
auth-zone=xen.local,$XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength}
|
|
domain=xen.local
|
|
addn-hosts=/var/run/xen/dnsmasq.hostsfile
|
|
expand-hosts
|
|
strict-order
|
|
no-hosts
|
|
bogus-priv
|
|
${optionalString (!cfg.bridge.forwardDns) ''
|
|
no-resolv
|
|
no-poll
|
|
auth-server=dns.xen.local,${cfg.bridge.name}
|
|
''}
|
|
filterwin2k
|
|
clear-on-reload
|
|
domain-needed
|
|
dhcp-hostsfile=/var/run/xen/dnsmasq.etherfile
|
|
dhcp-authoritative
|
|
dhcp-range=$XEN_BRIDGE_IP_RANGE_START,$XEN_BRIDGE_IP_RANGE_END
|
|
dhcp-no-override
|
|
no-ping
|
|
dhcp-leasefile=/var/run/xen/dnsmasq.leasefile
|
|
EOF
|
|
|
|
# DHCP
|
|
${pkgs.iptables}/bin/iptables -w -I INPUT -i ${cfg.bridge.name} -p tcp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT
|
|
${pkgs.iptables}/bin/iptables -w -I INPUT -i ${cfg.bridge.name} -p udp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT
|
|
# DNS
|
|
${pkgs.iptables}/bin/iptables -w -I INPUT -i ${cfg.bridge.name} -p tcp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
|
|
${pkgs.iptables}/bin/iptables -w -I INPUT -i ${cfg.bridge.name} -p udp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
|
|
|
|
${pkgs.bridge-utils}/bin/brctl addbr ${cfg.bridge.name}
|
|
${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} ${cfg.bridge.address}
|
|
${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} up
|
|
'';
|
|
serviceConfig.ExecStart = "${pkgs.dnsmasq}/bin/dnsmasq --conf-file=/var/run/xen/dnsmasq.conf";
|
|
postStop = ''
|
|
${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} down
|
|
${pkgs.bridge-utils}/bin/brctl delbr ${cfg.bridge.name}
|
|
|
|
# DNS
|
|
${pkgs.iptables}/bin/iptables -w -D INPUT -i ${cfg.bridge.name} -p udp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
|
|
${pkgs.iptables}/bin/iptables -w -D INPUT -i ${cfg.bridge.name} -p tcp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
|
|
# DHCP
|
|
${pkgs.iptables}/bin/iptables -w -D INPUT -i ${cfg.bridge.name} -p udp --sport 68 --dport 67 -j ACCEPT
|
|
${pkgs.iptables}/bin/iptables -w -D INPUT -i ${cfg.bridge.name} -p tcp --sport 68 --dport 67 -j ACCEPT
|
|
'';
|
|
};
|
|
|
|
|
|
systemd.services.xen-domains = {
|
|
description = "Xen domains - automatically starts, saves and restores Xen domains";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "xen-bridge.service" "xen-qemu.service" ];
|
|
## To prevent a race between dhcpcd and xend's bridge setup script
|
|
## (which renames eth* to peth* and recreates eth* as a virtual
|
|
## device), start dhcpcd after xend.
|
|
before = [ "dhcpd.service" ];
|
|
restartIfChanged = false;
|
|
serviceConfig.RemainAfterExit = "yes";
|
|
path = [ cfg.package cfg.qemu-package ];
|
|
environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains";
|
|
preStart = "mkdir -p /var/lock/subsys -m 755";
|
|
serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start";
|
|
serviceConfig.ExecStop = "${cfg.package}/etc/init.d/xendomains stop";
|
|
};
|
|
|
|
};
|
|
|
|
}
|