···528528 '';
529529 };
530530531531+ virtualisation.restrictNetwork =
532532+ mkOption {
533533+ type = types.bool;
534534+ default = false;
535535+ example = true;
536536+ description =
537537+ lib.mdDoc ''
538538+ If this option is enabled, the guest will be isolated, i.e. it will
539539+ not be able to contact the host and no guest IP packets will be
540540+ routed over the host to the outside. This option does not affect
541541+ any explicitly set forwarding rules.
542542+ '';
543543+ };
544544+531545 virtualisation.vlans =
532546 mkOption {
533547 type = types.listOf types.ints.unsigned;
···936950 else "'guestfwd=${proto}:${guest.address}:${toString guest.port}-" +
937951 "cmd:${pkgs.netcat}/bin/nc ${host.address} ${toString host.port}',"
938952 );
953953+ restrictNetworkOption = lib.optionalString cfg.restrictNetwork "restrict=on,";
939954 in
940955 [
941956 "-net nic,netdev=user.0,model=virtio"
942942- "-netdev user,id=user.0,${forwardingOptions}\"$QEMU_NET_OPTS\""
957957+ "-netdev user,id=user.0,${forwardingOptions}${restrictNetworkOption}\"$QEMU_NET_OPTS\""
943958 ];
944959945960 # FIXME: Consolidate this one day.
···11+import ./make-test-python.nix ({
22+ name = "qemu-vm-restrictnetwork";
33+44+ nodes = {
55+ unrestricted = { config, pkgs, ... }: {
66+ virtualisation.restrictNetwork = false;
77+ };
88+99+ restricted = { config, pkgs, ... }: {
1010+ virtualisation.restrictNetwork = true;
1111+ };
1212+ };
1313+1414+ testScript = ''
1515+ import os
1616+1717+ if os.fork() == 0:
1818+ # Start some HTTP server on the qemu host to test guest isolation.
1919+ from http.server import HTTPServer, BaseHTTPRequestHandler
2020+ HTTPServer(("", 8000), BaseHTTPRequestHandler).serve_forever()
2121+2222+ else:
2323+ start_all()
2424+ unrestricted.wait_for_unit("network-online.target")
2525+ restricted.wait_for_unit("network-online.target")
2626+2727+ # Guests should be able to reach each other on the same VLAN.
2828+ unrestricted.succeed("ping -c1 restricted")
2929+ restricted.succeed("ping -c1 unrestricted")
3030+3131+ # Only the unrestricted guest should be able to reach host services.
3232+ # 10.0.2.2 is the gateway mapping to the host's loopback interface.
3333+ unrestricted.succeed("curl -s http://10.0.2.2:8000")
3434+ restricted.fail("curl -s http://10.0.2.2:8000")
3535+ '';
3636+})