diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index 4dc2d098..52015513 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -13,6 +13,13 @@ addresses for interfaces! For details, see below issue #680. - Update CONTRIBUTING.md for scaling core team and helping external contributors understand the development process, issue #672 - OSPF: Add limitation to only allow one interface per area +- Add support for "dummy" interfaces, mostly useful for testing +- Add support for container hostname format specifiers, just like it + already works for the host's hostname setting +- Updated container documentation, improved images, detail how to set + interface name inside the container, and some syntax fixes +- Greatly reduced size of bundled curiOS httpd OCI container image, + reduced from 1.8 MiB to 281 KiB - The default builds now include the curiOS nftables container image, which can be used for advanced firewall setups. For an introduction see @@ -37,6 +44,8 @@ addresses for interfaces! For details, see below issue #680. this includes all operational data in ietf-routing:/routing/ribs. - Fix #697: password is not always set for new users, bug introduced in v24.06.0 when replacing Augeas with native user handling +- Fix #708: allow all container networks to set interface name inside + container, not just auto-generated veth-pair ends for `docker0` bridge - Fix BFD in OSPF, previously you could not enable BFD on a single interface without it was enabled on all interfaces diff --git a/doc/container.md b/doc/container.md index d7cd6437..95f5ef49 100644 --- a/doc/container.md +++ b/doc/container.md @@ -1,5 +1,6 @@ Containers in Infix =================== +Docker whale * [Introduction](#introduction) * [Caution](#caution) @@ -78,16 +79,17 @@ In the CLI, containers can be run in one of two ways: 1. `container run IMAGE [COMMAND]`, and 2. enter `configure` context, then `edit container NAME` -The first is useful mostly for testing, or running single commands in an -image. It is a wrapper for `podman run -it --rm ...`, while the latter -is a wrapper and adaptation of `podman create ...`. +The former is useful mostly for testing, or running single commands in +an image. It is a wrapper for `podman run -it --rm ...`, while the +latter is a wrapper and adaptation of `podman create ...`. The second create a container with a semi-persistent writable layer that survives container restarts and host system restarts. However, if you change the container configuration or upgrade the image (see below), the container will be recreated and the writable layer is lost. This is why it is recommended to set up a named volume for directories, or use file -[Content Mounts][], in your container you want truly persistent content. +[Content Mounts](#content-mounts), in your container if you want truly +persistent content. In fact, in many cases the best way is to create a `read-only` container and use file mounts and volumes only for the critical parts. Podman @@ -299,14 +301,19 @@ Currently two types of of container networks are supported: > For more information on VETH pairs, see the [Networking Guide][0]. + ### Container Bridge A container bridge is what most container setups use and users want. + +![IP Masquerading Container Bridge](img/cni-bridge-firewall.svg) + The difference from a regular bridge is that the container runtime fully -manages them -- connecting containers automatically with VETH pairs and -setting up firewall rules between the host and other containers, as well -as managing port forwarding. This transparent background management is -what makes container use seem to be so simple. +manages them -- connecting containers with automatically created VETH +pairs (look at the bridge port names) and setting up firewall rules +between the host and other containers, as well as managing port +forwarding. This transparent background management is what makes +container use seem to be so simple. All interface configuration is done in configure context. @@ -394,20 +401,21 @@ namespace of a container[^3]. This of course works with plain Ethernet interfaces as well, but here we will use one end of a VETH pair as an example. +The network `option` setting is available also for this case, but only +the `interface_name=foo0` option works. Which is still very useful. To +change the MAC address, you need to use the `custom-phys-adderss` in the +general network settings. + [^3]: Something which the container bridge network type does behind the scenes with one end of an automatically created VETH pair. + #### Routed Setup In this routed setup we reserve 192.168.0.0/24 for the network between the host and the `ntpd` container. - _____________ - | | - veth0 .1 | .2 eth0 | - \ 192.168.0.0/24 | / | - '--------------------' [ntpd] | - |_____________| +![Basic VETH pair connecting ntpd container.](img/cni-ntpd-routed.svg) Configuration is a straight-forward VETH pair setup where we name the container-end of pair `ntpd`. This is just a convenience for us when @@ -429,9 +437,12 @@ Adding the interface to the container is the same as before, but since everything for host interfaces is set up in the interfaces context, we can take a bit of a shortcut. - admin@example:/config/container/ntpd/> set network ntpd + admin@example:/config/container/ntpd/> set network interface ntpd admin@example:/config/container/ntpd/> leave +> Use the `set network inteface ntpd option interface_name=foo0` to set +> the name of the interface inside the container to `foo0`. + The point of the routed case is that port forwarding from the container in this case is limited to a single interface, not *all interfaces* as is the default in the masquerading container bridge setup. @@ -443,21 +454,15 @@ A perhaps more common case is to bridge the other end of the VETH pair with other physical ports. In this section we show how to add a new pair to give our container two interfaces: - _______________ - | | .1 br0 - veth0 .1 | .2 eth0 | / \ - \ 192.168.0.0/24 | / eth1 .2 | veth1b e1 - '------------------' \ | 192.168.1.0/24 / - | [ntpd] '------------------' - |_______________| +![Bridged setup with ntpd container.](img/cni-ntpd-bridged.svg) We start by adding the second VETH pair: admin@example:/config/> edit interface veth1a - admin@example:/config/interface/veth1a/> set veth peer veth1b + admin@example:/config/interface/veth1a/> set veth peer veth1 admin@example:/config/interface/veth1a/> set ipv4 address 192.168.1.2 prefix-length 24 -> The LAN bridge (br0) in this example has IP address 192.168.1.1. +> The LAN bridge (br1) in this example has IP address 192.168.1.1. When a container has multiple host interfaces it can often be useful to have a default route installed. This can be added from the host with a @@ -473,15 +478,16 @@ have a default route installed. This can be added from the host with a } } veth { - peer veth1b; + peer veth1; } admin@example:/config/interface/veth1a/> end - admin@example:/config/> set interface veth1b bridge-port bridge br0 + admin@example:/config/> set interface veth1 bridge-port bridge br1 Please note, container network routes require the base interface also have a static IP address set. Setting only the route, but no address, means the route is skipped. + ### Host Networking The third use-case is host networking, this is where a container share @@ -491,7 +497,7 @@ the host operating system. The host networking setup cannot be combined with any other network. -For an example, see below. +For an example, [see below](#application-container-nftables). Mounts and Volumes @@ -599,6 +605,17 @@ container ID (hash) is used, but this can be easily changed: admin@example:/> container shell system root@sys101:/# +In fact, the container `hostname` setting supports the same format +specifiers as the host's `hostname` setting: + + - `%i`: OS ID, from `/etc/os-release`, from Menuconfig branding + - `%h`: Default hostname, from `/etc/os-release`, from branding + - `%m`: NIC specific part of base MAC, e.g., to `c0-ff-ee` + - `%%`: Literal % + +The most useful combination is probably `"container-name-%m"`, which in +this example give the container hostname `container-name-c0-ff-ee`. + [^1]: this does not apply to the admin-exec command `container run`. This command is intended to be used for testing and evaluating container images. Such containers are given a private network @@ -626,6 +643,7 @@ Notice how we `set network host`, so the container can see and act on all the host's interfaces, and that we also have to run the container in *privileged* mode. + ### Application Container: ntpd The default NTP server/client in Infix is Chrony, a fully working and diff --git a/doc/img/cni-bridge-firewall.svg b/doc/img/cni-bridge-firewall.svg new file mode 100644 index 00000000..04fb8861 --- /dev/null +++ b/doc/img/cni-bridge-firewall.svg @@ -0,0 +1,4 @@ + + + +
veth124
eth0
ip
10.0.10.0/24
.2
docker0
ip
.1
veth123
eth0
ip
.3
\ No newline at end of file diff --git a/doc/img/cni-ntpd-bridged.svg b/doc/img/cni-ntpd-bridged.svg new file mode 100644 index 00000000..28b33ffc --- /dev/null +++ b/doc/img/cni-ntpd-bridged.svg @@ -0,0 +1,4 @@ + + + +
veth0
eth0
ntpd container
ip
192.168.0.0/24
ip
.1
.2
eth1
veth1
ip
.2
bridge
ip
.1
192.168.1.0/24
e1
\ No newline at end of file diff --git a/doc/img/cni-ntpd-routed.svg b/doc/img/cni-ntpd-routed.svg new file mode 100644 index 00000000..033e08f4 --- /dev/null +++ b/doc/img/cni-ntpd-routed.svg @@ -0,0 +1,4 @@ + + + +
veth0
eth0
ntpd container
ip
192.168.0.0/24
ip
.1
.2
\ No newline at end of file diff --git a/doc/img/docker.webp b/doc/img/docker.webp new file mode 100644 index 00000000..b29b95ec Binary files /dev/null and b/doc/img/docker.webp differ diff --git a/src/confd/src/ietf-system.c b/src/confd/src/ietf-system.c index 39ef7dfd..c95b8b7b 100644 --- a/src/confd/src/ietf-system.c +++ b/src/confd/src/ietf-system.c @@ -1625,7 +1625,7 @@ static char *get_mac(struct confd *confd, char *mac, size_t len) * * XXX: PLEASE REFACTOR THIS INTO A PYTHON HELPER FOR FUTURE EXTENSIONS, OR BUGS! */ -static int hostnamefmt(struct confd *confd, char **fmt) +int hostnamefmt(struct confd *confd, char **fmt) { size_t hostlen, fmt_len; char *hostname; diff --git a/src/confd/src/infix-containers.c b/src/confd/src/infix-containers.c index 320070ef..ef576c8f 100644 --- a/src/confd/src/infix-containers.c +++ b/src/confd/src/infix-containers.c @@ -21,6 +21,9 @@ #define ACTIVE_QUEUE "/var/lib/containers/active" #define LOGGER "logger -t container -p local1.notice" +int hostnamefmt(struct confd *confd, char **fmt); + + static int add(const char *name, struct lyd_node *cif) { const char *image = lydx_get_cattr(cif, "image"); @@ -46,8 +49,16 @@ static int add(const char *name, struct lyd_node *cif) LYX_LIST_FOR_EACH(lyd_child(cif), node, "search") fprintf(fp, " --dns-search %s", lyd_get_value(node)); - if ((string = lydx_get_cattr(cif, "hostname"))) - fprintf(fp, " --hostname %s", string); + if ((string = lydx_get_cattr(cif, "hostname"))) { + char *fmt = (char *)string; + + if (hostnamefmt(&confd, &fmt)) { + ERRNO("%s: failed setting custom hostname", name); + } else { + fprintf(fp, " --hostname %s", fmt); + free(fmt); + } + } if (lydx_is_enabled(cif, "read-only")) fprintf(fp, " --read-only"); diff --git a/src/confd/yang/containers.inc b/src/confd/yang/containers.inc index 7e63b0f2..5e6a45af 100644 --- a/src/confd/yang/containers.inc +++ b/src/confd/yang/containers.inc @@ -2,5 +2,5 @@ # REMEMBER TO UPDATE infix-interfaces ALSO IN confd.inc MODULES=( "infix-interfaces@2024-09-23.yang -e vlan-filtering -e containers" - "infix-containers@2024-03-27.yang" + "infix-containers@2024-10-14.yang" ) diff --git a/src/confd/yang/infix-containers.yang b/src/confd/yang/infix-containers.yang index c9dfc9d6..beb86601 100644 --- a/src/confd/yang/infix-containers.yang +++ b/src/confd/yang/infix-containers.yang @@ -22,6 +22,17 @@ module infix-containers { prefix infix-if; } + import infix-system { + prefix infix-sys; + } + + revision 2024-10-14 { + description "Two major changes: + - Allow changing name of host interfaces inside container + - Support hostname format specifiers, like ietf-system"; + reference "internal"; + } + revision 2024-03-27 { description "Add support for capabilities."; reference "internal"; @@ -160,8 +171,11 @@ module infix-containers { } leaf hostname { - description "Sets the container host name that is available inside the container."; - type inet:domain-name; + description "Sets the container host name, available inside the container. + + Format specifiers are for, default hostname, ID, and the + last three octets in base MAC, e.g., c0-ff-ee"; + type infix-sys:hostname; } leaf privileged { @@ -201,12 +215,16 @@ module infix-containers { } leaf-list option { - when "deref(../name)/../infix-if:container-network/infix-if:type = 'infix-if:bridge'"; - description "Options for masquerading container bridges. + description "Options for interface inside container, e.g., interface_name=foo0 - Example: ip=1.2.3.4 -- request a specific IP (IPv4 or IPv6) - mac=00:01:02:c0:ff:ee -- set fixed MAC address in container - interface_name=foo0 -- set interface name inside container"; + Options: ip=1.2.3.4 -- Set static IPv4 address* + ip6=2001:0:130f::9c0:876a:130b -- Set static IPv6 address* + mac=00:01:02:c0:ff:ee -- Set fixed MAC address + interface_name=foo0 -- Set interface name + + NOTE: Some (*) options only work with masquerading container bridges, + which automatically create VETH pairs with one end in the host + bridge and the other in the container."; type string; } } diff --git a/src/confd/yang/infix-containers@2024-03-27.yang b/src/confd/yang/infix-containers@2024-10-14.yang similarity index 100% rename from src/confd/yang/infix-containers@2024-03-27.yang rename to src/confd/yang/infix-containers@2024-10-14.yang