diff --git a/board/common/rootfs/usr/sbin/container b/board/common/rootfs/usr/sbin/container index 79e50504..4b622315 100755 --- a/board/common/rootfs/usr/sbin/container +++ b/board/common/rootfs/usr/sbin/container @@ -1,5 +1,7 @@ #!/bin/sh - +DOWNLOADS=/var/lib/containers/oci +BUILTIN=/lib/oci +checksum="" all="" env="" port="" @@ -10,6 +12,72 @@ log() logger -I $PPID -t container -p local1.notice -- "$*" } +check() +{ + file=$1 + + if [ -z "$checksum" ]; then + log "no checksum to verify $file against, continuing." + return 0 + fi + + if echo "${checksum} ${file}" | "$cmdsum" -c -s; then + log "$file checksum verified OK." + return 0 + fi + + got=$("$cmdsum" "${file}" | awk '{print $1}') + log "$file checksum mismatch, got $got, expected $checksum, removing file." + rm -f "$file" + + return 1 +} + +# Fetch an OCI image over ftp/http/https. Use wget for FTP, which curl +# empirically does not work well with. Log progress+ & error to syslog. +fetch() +{ + url=$1 + file=$(basename "$url") + dst="$DOWNLOADS/$file" + + cd "$DOWNLOADS" || return + if [ -e "$file" ]; then + log "$file already available." + if check "$file"; then + echo "$dst" + return 0 + fi + fi + + log "Fetching $url" + + if echo "$url" | grep -qE "^ftp://"; then + cmd="wget -q $url" + elif echo "$url" | grep -qE "^https?://"; then + cmd="curl $creds -sSL --fail -o \"$file\" $url" + else + log "Unsupported URL scheme: $url" + return 1 + fi + + if out=$(eval "$cmd" 2>&1); then + log "$file downloaded successfully." + if check "$file"; then + echo "$dst" + return 0 + fi + fi + + # log error message from backend + while IFS= read -r line; do + log "$line" + done </dev/null @@ -73,10 +154,6 @@ unpack_archive() name=$dir fi - if [ "$remove" = "true" ]; then - rm -rf "$file" - fi - echo "$name" } @@ -102,7 +179,9 @@ create() # Unpack and load docker-archive/oci/oci-archive, returning image # name, or return docker:// URL for download. - image=$(unpack_archive "$image") + if ! image=$(unpack_archive "$image"); then + exit 1 + fi if [ -z "$logging" ]; then logging="--log-driver k8s-file --log-opt path=/run/containers/$name.fifo" @@ -247,6 +326,7 @@ options: --dns-search LIST Set host lookup search list when creating container --cap-add CAP Add capability to unprivileged container --cap-drop CAP Drop capability, for privileged containter + --checksum TYPE:SUM Use md5/sha256/sha512 to verify ftp/http/https archives -c, --creds USR[:PWD] Credentials to pass to curl -u for remote ops -d, --detach Detach a container started with 'run IMG [CMD]' -e, --env FILE Environment variables when creating container @@ -306,6 +386,25 @@ while [ "$1" != "" ]; do shift caps="$caps --cap-drop=$1" ;; + --checksum) + shift + type="${1%%:*}" + checksum="${1#*:}" + case "$type" in + md5) + cmdsum=md5sum + ;; + sha256) + cmdsum=sha256sum + ;; + sha512) + cmdsum=sha512sum + ;; + *) + err 1 "Unsupported checksum type: $type" + ;; + esac + ;; -c | --creds) shift creds="-u $1" @@ -350,7 +449,6 @@ while [ "$1" != "" ]; do --log-path) shift logging="$logging --log-opt path=$1" - log_path="$1" ;; -m | --mount) shift @@ -455,25 +553,12 @@ case $cmd in usage ;; load) - url=$1 - name=$2 # shellcheck disable=SC2086 - if echo "$url" | grep -q "://"; then - file=$(basename "$url") - curl -k $creds -Lo "$file" "$url" - else - file="$url" - fi - - # shellcheck disable=SC2086 - name=$(unpack_archive "$file" $name) + name=$(unpack_archive "$1" $2) + [ -n "$name" ] || exit 1 # Show resulting image(s) matching $name - if [ -n "$name" ]; then - podman images -n "$name" - else - exit 1 - fi + podman images -n "$name" ;; locate) # Find where the host's ifname lives if [ -z "$network" ]; then @@ -500,7 +585,7 @@ case $cmd in podman images $all --format "{{.Repository}}:{{.Tag}}" ;; oci) - find /lib/oci /var/lib/containers/oci -type f 2>/dev/null + find $BUILTIN $DOWNLOADS -type f 2>/dev/null ;; *) podman ps $all --format "{{.Names}}" diff --git a/src/confd/src/infix-containers.c b/src/confd/src/infix-containers.c index 5db409fd..1e39905f 100644 --- a/src/confd/src/infix-containers.c +++ b/src/confd/src/infix-containers.c @@ -175,6 +175,16 @@ static int add(const char *name, struct lyd_node *cif) if (lydx_is_enabled(cif, "manual")) fprintf(fp, " --manual"); + node = lydx_get_descendant(lyd_child(cif), "checksum", NULL); + if (node) { + if ((string = lydx_get_cattr(node, "md5"))) + fprintf(fp, " --checksum md5:%s", string); + if ((string = lydx_get_cattr(node, "sha256"))) + fprintf(fp, " --checksum sha256:%s", string); + if ((string = lydx_get_cattr(node, "sha512"))) + fprintf(fp, " --checksum sha512:%s", string); + } + fprintf(fp, " create %s %s", name, image); if ((string = lydx_get_cattr(cif, "command"))) diff --git a/src/confd/yang/containers.inc b/src/confd/yang/containers.inc index c143090d..8d69b662 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-11-12.yang -e vlan-filtering -e containers" - "infix-containers@2024-10-14.yang" + "infix-containers@2024-11-15.yang" ) diff --git a/src/confd/yang/infix-containers.yang b/src/confd/yang/infix-containers.yang index 31b3eb5e..a5817fae 100644 --- a/src/confd/yang/infix-containers.yang +++ b/src/confd/yang/infix-containers.yang @@ -26,24 +26,26 @@ module infix-containers { prefix infix-sys; } - revision 2024-11-12 { - description "Deprecate read-only, it is now always true."; + revision 2024-11-15 { + description "Two major changes: + - Add support for ftp/http/https images with checksum + - Deprecate read-only, it is now always true"; reference "internal"; } - revision 2024-10-14 { + 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 { + revision 2024-03-27 { description "Add support for capabilities."; reference "internal"; } - revision 2024-02-01 { + revision 2024-02-01 { description "Initial revision"; reference "internal"; } @@ -138,6 +140,13 @@ module infix-containers { oci-archive:/lib/oci/archive -- Use archive:latest from OCI archive May be in .tar or .tar.gz format + Additionally, the following URIs are also supported for setups + that do not use a HUB or similar. Recommend using 'checksum'! + + ftp://addr/path/to/archive -- Downloaded using wget + http://addr/path/to/archive -- Downloaded using curl + https://addr/path/to/archive -- Downloaded using curl + Note: if a remote repository cannot be reached, the creation of the container will be put on a queue that retries pull every time there is a route change in the host's system."; @@ -145,6 +154,35 @@ module infix-containers { type string; } + container checksum { + description "Checksum for ftp/http/https OCI archives."; + choice checksum { + leaf md5 { + description "MD5 checksum for the archive."; + type string { + length "32"; + pattern "[a-fA-F0-9]{32}"; + } + } + + leaf sha256 { + description "SHA256 checksum for the archive."; + type string { + length "64"; + pattern "[a-fA-F0-9]{64}"; + } + } + + leaf sha512 { + description "SHA512 checksum for the archive."; + type string { + length "128"; + pattern "[a-fA-F0-9]{128}"; + } + } + } + } + leaf image-id { description "Docker image ID, exact hash used."; config false; diff --git a/src/confd/yang/infix-containers@2024-11-12.yang b/src/confd/yang/infix-containers@2024-11-15.yang similarity index 100% rename from src/confd/yang/infix-containers@2024-11-12.yang rename to src/confd/yang/infix-containers@2024-11-15.yang diff --git a/src/klish-plugin-infix/xml/containers.xml b/src/klish-plugin-infix/xml/containers.xml index 45ffcbb2..76fefe6d 100644 --- a/src/klish-plugin-infix/xml/containers.xml +++ b/src/klish-plugin-infix/xml/containers.xml @@ -75,11 +75,15 @@ + + + creds=${KLISH_PARAM_creds:+-c $KLISH_PARAM_creds} + sha=${KLISH_PARAM_sha:+--checksum $KLISH_PARAM_sha} cd /var/lib/containers/oci - doas container $creds load $KLISH_PARAM_url $KLISH_PARAM_name + doas container $creds $sha load $KLISH_PARAM_url $KLISH_PARAM_name