Repository navigation
feat(monitor): network hyperlight-unikraft guests through the container's namespace - #1135
Merged
Merged
Conversation
✅ Deploy Preview for urunc ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
cmainas
reviewed
Oct 21, 2026
cmainas
reviewed
Oct 26, 2026
cmainas
left a comment
Contributor
There was a problem hiding this comment.
Hello @danbugs ,
thank you for the update. It seems I did not have to build any new images, since the tests are using the existing one.
I just have added a couple cleanup related comments. Also, please rebase over main so the end-to-end tests can run without issues.
urunc gives every guest its network through a tap device: it creates one in the network namespace of the container and redirects the traffic of the container's interface to it. That fits a monitor that emulates a network device, but not one whose guest reaches the network through sockets that the monitor itself opens in the namespace. For such a monitor the redirect would take the traffic away from those sockets. Add UsesHostNet to the VMM interface, false on every monitor so far. When a monitor reports it, both the libcontainer and the plain paths skip SetupNet, so no tap is created and the interface of the container is left as it is, and mark the network parameters with HostNet so the monitor knows to open the network to its guest. Such a monitor needs neither /dev/net/tun in its rootfs nor the CAP_NET_ADMIN that urunc adds to the monitor's capabilities for the tap, so leave both out. Signed-off-by: danbugs <danilochiarlone@gmail.com>
hluk has no network device to offer a guest. It forwards each socket call of the guest to a real socket that it opens itself, so a guest reaches exactly the network of the namespace hluk runs in: the address of the pod, its routes and any NetworkPolicy that applies to it. Report that through UsesHostNet, so urunc leaves the interface of the container alone instead of redirecting its traffic to a tap that nothing reads. hluk gives a guest no network unless told to, and since v0.18.0 --port on its own only lets the guest serve: it can then connect nowhere, not even to a DNS server. A container expects the whole network of its namespace, outbound and inbound, so start hluk with both, --net and --port=all, for a booted and a resumed guest alike. No hluk policy is layered on top: the namespace, and the NetworkPolicy that applies to it, stay the boundary, as they are for every other container. Signed-off-by: danbugs <danilochiarlone@gmail.com>
A hyperlight-unikraft guest resolves names itself, over the sockets hluk opens for it, with the /etc/resolv.conf of its own filesystem. A booted guest gets that file from the rootfs CPIO, and a resumed one from the snapshot, which was saved wherever the image was built. Neither knows the nameserver or the search domains of the container, so cluster names do not resolve in the guest. hluk installs a resolver configuration in the guest, at boot and on every restore, with --resolv-conf. For a monitor on host networking only, bind-mount the resolv.conf that the engine mounts in the container read-only into the monitor rootfs, at /etc/resolv.conf, along with the other mounts the monitor gets, and point hluk at it. The guest follows the engine's file as a container does. A container without a resolv.conf leaves the guest with its own, and the nameserver that the tap-based guests take from the file is not extracted for one that gets the whole file. Signed-off-by: danbugs <danilochiarlone@gmail.com>
Explain that hluk forwards the sockets of the guest to the network namespace of the container, so urunc creates no tap for this monitor and starts hluk with both outbound and inbound networking, and that the resolv.conf of the container is installed in the guest, whether it boots or resumes from a snapshot. Signed-off-by: danbugs <danilochiarlone@gmail.com>
The Hyperlight case only prints a greeting, so the networking of the guest is verified by nothing but the arguments urunc builds for hluk. Add a docker and a nerdctl case that run the hello image with Python's http.server, which the Python driver runs like any script from the initrd, and fetch its root from the host at the address of the container: the guest binds in the network namespace of the container and answers through the sockets hluk opens for it. A hluk release that changed the meaning of --net or --port would fail it. ctr gets no case, since it gives a container no address to reach. Signed-off-by: danbugs <danilochiarlone@gmail.com>
A container serves on whatever port its workload binds, and so does a hyperlight-unikraft guest, since urunc starts hluk with --port=all. hluk can also restrict the ports a guest binds, so let a deployment ask for that with the com.urunc.unikernel.hyperlightPorts annotation: a comma-separated list of ports and LOW-HIGH ranges, passed to hluk as one --port each. An image configured through urunc.json takes it too, as a run-time annotation that replaces what the image says. urunc rejects the annotation on any other monitor, since no other can enforce it, and checks its format before it reaches the hluk command line. This is a second boundary inside the network namespace, opt-in, so that the default stays what a container gets. Signed-off-by: danbugs <danilochiarlone@gmail.com>
danbugs
force-pushed
the
hyperlight-networking
branch
from
October 26, 2026 17:48
a12b46b to
52f3b81
Compare
github-actions Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
urunc gives every guest its network through a tap device: it creates one in the network namespace of the container and redirects the traffic of the container's interface to it. That fits a monitor that emulates a network device, but not one whose guest reaches the network through sockets that the monitor itself opens in the namespace. For such a monitor the redirect would take the traffic away from those sockets. Add UsesHostNet to the VMM interface, false on every monitor so far. When a monitor reports it, both the libcontainer and the plain paths skip SetupNet, so no tap is created and the interface of the container is left as it is, and mark the network parameters with HostNet so the monitor knows to open the network to its guest. Such a monitor needs neither /dev/net/tun in its rootfs nor the CAP_NET_ADMIN that urunc adds to the monitor's capabilities for the tap, so leave both out. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
github-actions Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
hluk has no network device to offer a guest. It forwards each socket call of the guest to a real socket that it opens itself, so a guest reaches exactly the network of the namespace hluk runs in: the address of the pod, its routes and any NetworkPolicy that applies to it. Report that through UsesHostNet, so urunc leaves the interface of the container alone instead of redirecting its traffic to a tap that nothing reads. hluk gives a guest no network unless told to, and since v0.18.0 --port on its own only lets the guest serve: it can then connect nowhere, not even to a DNS server. A container expects the whole network of its namespace, outbound and inbound, so start hluk with both, --net and --port=all, for a booted and a resumed guest alike. No hluk policy is layered on top: the namespace, and the NetworkPolicy that applies to it, stay the boundary, as they are for every other container. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
github-actions Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
A hyperlight-unikraft guest resolves names itself, over the sockets hluk opens for it, with the /etc/resolv.conf of its own filesystem. A booted guest gets that file from the rootfs CPIO, and a resumed one from the snapshot, which was saved wherever the image was built. Neither knows the nameserver or the search domains of the container, so cluster names do not resolve in the guest. hluk installs a resolver configuration in the guest, at boot and on every restore, with --resolv-conf. For a monitor on host networking only, bind-mount the resolv.conf that the engine mounts in the container read-only into the monitor rootfs, at /etc/resolv.conf, along with the other mounts the monitor gets, and point hluk at it. The guest follows the engine's file as a container does. A container without a resolv.conf leaves the guest with its own, and the nameserver that the tap-based guests take from the file is not extracted for one that gets the whole file. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
github-actions Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
Explain that hluk forwards the sockets of the guest to the network namespace of the container, so urunc creates no tap for this monitor and starts hluk with both outbound and inbound networking, and that the resolv.conf of the container is installed in the guest, whether it boots or resumes from a snapshot. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
github-actions Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
The Hyperlight case only prints a greeting, so the networking of the guest is verified by nothing but the arguments urunc builds for hluk. Add a docker and a nerdctl case that run the hello image with Python's http.server, which the Python driver runs like any script from the initrd, and fetch its root from the host at the address of the container: the guest binds in the network namespace of the container and answers through the sockets hluk opens for it. A hluk release that changed the meaning of --net or --port would fail it. ctr gets no case, since it gives a container no address to reach. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
github-actions Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
A container serves on whatever port its workload binds, and so does a hyperlight-unikraft guest, since urunc starts hluk with --port=all. hluk can also restrict the ports a guest binds, so let a deployment ask for that with the com.urunc.unikernel.hyperlightPorts annotation: a comma-separated list of ports and LOW-HIGH ranges, passed to hluk as one --port each. An image configured through urunc.json takes it too, as a run-time annotation that replaces what the image says. urunc rejects the annotation on any other monitor, since no other can enforce it, and checks its format before it reaches the hluk command line. This is a second boundary inside the network namespace, opt-in, so that the default stays what a container gets. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
urunc-bot Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
urunc gives every guest its network through a tap device: it creates one in the network namespace of the container and redirects the traffic of the container's interface to it. That fits a monitor that emulates a network device, but not one whose guest reaches the network through sockets that the monitor itself opens in the namespace. For such a monitor the redirect would take the traffic away from those sockets. Add UsesHostNet to the VMM interface, false on every monitor so far. When a monitor reports it, both the libcontainer and the plain paths skip SetupNet, so no tap is created and the interface of the container is left as it is, and mark the network parameters with HostNet so the monitor knows to open the network to its guest. Such a monitor needs neither /dev/net/tun in its rootfs nor the CAP_NET_ADMIN that urunc adds to the monitor's capabilities for the tap, so leave both out. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
urunc-bot Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
hluk has no network device to offer a guest. It forwards each socket call of the guest to a real socket that it opens itself, so a guest reaches exactly the network of the namespace hluk runs in: the address of the pod, its routes and any NetworkPolicy that applies to it. Report that through UsesHostNet, so urunc leaves the interface of the container alone instead of redirecting its traffic to a tap that nothing reads. hluk gives a guest no network unless told to, and since v0.18.0 --port on its own only lets the guest serve: it can then connect nowhere, not even to a DNS server. A container expects the whole network of its namespace, outbound and inbound, so start hluk with both, --net and --port=all, for a booted and a resumed guest alike. No hluk policy is layered on top: the namespace, and the NetworkPolicy that applies to it, stay the boundary, as they are for every other container. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
urunc-bot Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
A hyperlight-unikraft guest resolves names itself, over the sockets hluk opens for it, with the /etc/resolv.conf of its own filesystem. A booted guest gets that file from the rootfs CPIO, and a resumed one from the snapshot, which was saved wherever the image was built. Neither knows the nameserver or the search domains of the container, so cluster names do not resolve in the guest. hluk installs a resolver configuration in the guest, at boot and on every restore, with --resolv-conf. For a monitor on host networking only, bind-mount the resolv.conf that the engine mounts in the container read-only into the monitor rootfs, at /etc/resolv.conf, along with the other mounts the monitor gets, and point hluk at it. The guest follows the engine's file as a container does. A container without a resolv.conf leaves the guest with its own, and the nameserver that the tap-based guests take from the file is not extracted for one that gets the whole file. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
urunc-bot Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
Explain that hluk forwards the sockets of the guest to the network namespace of the container, so urunc creates no tap for this monitor and starts hluk with both outbound and inbound networking, and that the resolv.conf of the container is installed in the guest, whether it boots or resumes from a snapshot. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
urunc-bot Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
The Hyperlight case only prints a greeting, so the networking of the guest is verified by nothing but the arguments urunc builds for hluk. Add a docker and a nerdctl case that run the hello image with Python's http.server, which the Python driver runs like any script from the initrd, and fetch its root from the host at the address of the container: the guest binds in the network namespace of the container and answers through the sockets hluk opens for it. A hluk release that changed the meaning of --net or --port would fail it. ctr gets no case, since it gives a container no address to reach. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
urunc-bot Bot
pushed a commit
that referenced
this pull request
Oct 26, 2026
A container serves on whatever port its workload binds, and so does a hyperlight-unikraft guest, since urunc starts hluk with --port=all. hluk can also restrict the ports a guest binds, so let a deployment ask for that with the com.urunc.unikernel.hyperlightPorts annotation: a comma-separated list of ports and LOW-HIGH ranges, passed to hluk as one --port each. An image configured through urunc.json takes it too, as a run-time annotation that replaces what the image says. urunc rejects the annotation on any other monitor, since no other can enforce it, and checks its format before it reaches the hluk command line. This is a second boundary inside the network namespace, opt-in, so that the default stays what a container gets. PR: #1135 Signed-off-by: danbugs <danilochiarlone@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
4 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
hluk has no network device to offer a guest. It forwards each socket call of the guest to a real socket that it opens itself, so a guest reaches exactly the network of the namespace hluk runs in. urunc's networking assumes a monitor with a virtual NIC: it creates a tap in the container's namespace and redirects the traffic of the container's interface to it, which would take that traffic away from hluk's sockets. This lets a monitor keep the namespace as it is, turns hluk's networking on, and installs the container's resolver configuration in the guest.
UsesTUNTAPjoinsUsesKVMon theVMMinterface. The six tap-based monitors report true and are unchanged. For a monitor that reports false,SetupNetcreates no tap and leaves the interface of the container alone, only describing it, and the monitor gets neither/dev/net/tunnorCAP_NET_ADMIN, which it does not need; the decision is recorded once in the monitor resources, where the capability code reads it.hyperlight-unikraftstartshlukwith--net --port=all, for a booted and a resumed guest alike, since hluk gives a guest no network unless told to and, since v0.18.0,--porton its own only lets the guest serve. The address of the pod, its routes and the NetworkPolicy that applies to it stay the boundary, as they are for every other container. A container started with no network has no interface in its namespace, and the guest is then left to hluk's default of no network, as a second boundary.com.urunc.unikernel.hyperlightPortsannotation limits the ports the guest may serve on, as ports andLOW-HIGHranges passed to hluk as one--porteach. Without it the guest serves on any port, as a container does. A deployment can set it on an image configured throughurunc.jsontoo, where it replaces what the image says. urunc validates the value and rejects the annotation on any other monitor, since no other can enforce it./etc/resolv.confmount in the monitor rootfs as well, read-only, and passes--resolv-conf, so hluk installs it at boot and on every restore, and the guest follows the engine's file as a container does. A container without one leaves the guest with its own. The nameserver extraction for tap guests is skipped when the guest gets the whole file.net.ipv4.ip_unprivileged_port_start=0in its namespace to serve on a privileged port. This is how every monitor behaves today and is only observable now that a guest can bind; the docs say so.Related issues
Part of #140, following #1058.
How was this tested?
Unit tests cover the opt-out (a monitor without a tap gets no tun device and no extra capability), the hluk arguments for booted and resumed guests with and without a network, a resolv.conf and a ports annotation, the validation of that annotation, the resolv.conf mount and the choice of the last
/etc/resolv.confmount in the spec.make test_dockerpasses, including the newHyperlight-unikraft-httpcase, except for the one case that needs anio.containerd.urunc-fc.v2runtime my docker does not define; the ctrHyperlight-unikraft-hellocase passes too. By hand on KVM with docker and hluk v0.18.0, using the v0.18.0 hello image with a script added to its initrd: the guest sees the container's resolv.conf, resolves github.com and fetches http://example.com (HTTP 200), and a Python http.server in the guest answers requests from the host on a published port and at the container's address. The ports annotation restricts that server to the listed ports (hluk gets one--portper entry, and a request on a port outside them fails),--network noneboots the guest with no network at all, and a qemu image rejects the annotation.LLM usage
Fable 5.1
Checklist
make lint).make test_ctr,make test_nerdctl,make test_docker,make test_crictl).