diff --git a/dockers/docker-base-bookworm/Dockerfile.j2 b/dockers/docker-base-bookworm/Dockerfile.j2 index 14032ed308f..07e132c61df 100644 --- a/dockers/docker-base-bookworm/Dockerfile.j2 +++ b/dockers/docker-base-bookworm/Dockerfile.j2 @@ -63,6 +63,10 @@ RUN apt update && \ libwrap0 \ libatomic1 +# Security fixes: upgrade vulnerable base packages (S360 scan remediation) +RUN apt-get update && apt-get upgrade -y \ + && rm -rf /var/lib/apt/lists/* + # Add a config file to allow pip to install packages outside of apt/the Debian repos COPY ["pip.conf", "/etc/pip.conf"] @@ -88,7 +92,8 @@ RUN apt-get -y purge \ exim4 \ exim4-base \ exim4-config \ - exim4-daemon-light + exim4-daemon-light \ + 2>/dev/null; apt-get -y autoremove || true {% if docker_base_bookworm_debs.strip() -%} # Copy locally-built Debian package dependencies diff --git a/dockers/docker-ptf/Dockerfile.j2 b/dockers/docker-ptf/Dockerfile.j2 index 30242a33829..a8eb481bf55 100644 --- a/dockers/docker-ptf/Dockerfile.j2 +++ b/dockers/docker-ptf/Dockerfile.j2 @@ -52,6 +52,11 @@ RUN apt-get update \ libboost-atomic1.71.0 \ {% else %} libboost-atomic1.74.0 \ + flex \ + bison \ + tcpd \ + libwrap0 \ + libwrap0-dev \ {% endif %} less \ git \ @@ -90,8 +95,39 @@ RUN apt-get update \ iproute2 \ wireshark-common \ freeradius \ - quilt + quilt \ + && rm -rf /var/lib/apt/lists/* +# Install Go toolchain for building grpcurl and gnoic from source +# to ensure they use a patched Go stdlib (GO-2026-4337: crypto/tls) +{% if CONFIGURED_ARCH == "armhf" %} +RUN GO_ARCH=armv6l \ +{% elif CONFIGURED_ARCH == "arm64" %} +RUN GO_ARCH=arm64 \ +{% else %} +RUN GO_ARCH=amd64 \ +{% endif %} + && GO_VERSION=1.25.8 \ + && curl -L "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz \ + && tar -C /usr/local -xzf /tmp/go.tar.gz \ + && rm /tmp/go.tar.gz + +ENV PATH="/usr/local/go/bin:$HOME/go/bin:$PATH" + +# Build grpcurl from source with patched Go and golang.org/x/* deps +# upgraded to latest to address current and future golang.org/x/* CVEs. +RUN GRPCURL_VERSION=v1.9.3 \ + && git clone --depth 1 --branch "${GRPCURL_VERSION}" https://github.com/fullstorydev/grpcurl.git /tmp/grpcurl \ + && cd /tmp/grpcurl \ + && go get google.golang.org/grpc@v1.79.3 \ + && go get golang.org/x/crypto@latest golang.org/x/net@latest golang.org/x/text@latest golang.org/x/sys@latest golang.org/x/oauth2@latest \ + && go mod tidy \ + && go build -o /usr/local/bin/grpcurl ./cmd/grpcurl \ + && chmod +x /usr/local/bin/grpcurl \ + && rm -rf /tmp/grpcurl +# Security fixes: upgrade all vulnerable system packages (S360 scan remediation) +RUN apt-get update && apt-get upgrade -y \ + && rm -rf /var/lib/apt/lists/* {% if PTF_ENV_PY_VER == "py3" %} RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1 \ && update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1 \ @@ -112,7 +148,7 @@ RUN rm -rf /debs \ && rm -f get-pip.py \ && pip install setuptools \ && pip install supervisor \ - && pip install ipython==5.4.1 \ + && pip install ipython \ && git clone https://github.com/p4lang/scapy-vxlan.git \ && cd scapy-vxlan \ && python setup.py install \ @@ -171,14 +207,41 @@ RUN rm -rf /debs \ && cd /opt \ && wget https://raw.githubusercontent.com/p4lang/ptf/master/ptf_nn/ptf_nn_agent.py -{% if PTF_ENV_PY_VER == "mixed" %} -RUN python3 -m venv --system-site-packages env-python3 +{% if PTF_ENV_PY_VER == "py3" %} +RUN curl -L -o tacacs.tar.gz https://shrubbery.net/pub/tac_plus/tacacs-F4.0.4.31.tar.gz\ + && mkdir -p tac_plus\ + && tar -xvzf tacacs.tar.gz -C tac_plus\ + && cd tac_plus \ + && cd tacacs-F4.0.4.31 \ + && ./configure LDFLAGS="-Wl,-rpath=/usr/local/lib" \ + && make install \ + && ln -s /usr/local/sbin/tac_plus /usr/sbin/tac_plus \ + && ln -s /usr/local/bin/tac_pwd /usr/sbin/tac_pwd \ + && mkdir /etc/tacacs+ \ + && chmod 0755 /etc/tacacs+ +COPY ["tacacs_plus", "/etc/init.d"] +COPY ["tacacs+", "/etc/default"] +{% endif %} + +# Workaround: Tornado installed outside of the +# virtualenv as the call to the process API +# Ansible -> Supervisor -> ExaBGP -> Process API +# causes the process API to have a restricted +# environment without access to the virtualenv. +{% if PTF_ENV_PY_VER == "py3" %} + RUN pip3 install --break-system-packages tornado +{% endif %} + + +RUN python3 -m venv env-python3 # Activating a virtualenv. The virtualenv automatically works for RUN, ENV and CMD. ENV VIRTUAL_ENV=/root/env-python3 ARG BACKUP_OF_PATH="$PATH" ENV PATH="$VIRTUAL_ENV/bin:$PATH" ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 PYTHONIOENCODING=UTF-8 -{% endif %} + +# Upgrade pip to address CVE vulnerabilities in older pip versions +RUN pip3 install --upgrade pip {% if PTF_ENV_PY_VER == "mixed" %} RUN python3 -m pip install --upgrade --ignore-installed pip @@ -212,9 +275,17 @@ RUN pip3 install setuptools \ && pip3 install six==1.16.0 \ && pip3 install itsdangerous \ && pip3 install retrying \ - && pip3 install jinja2 \ - && pip3 install scapy==2.5.0 \ - && pip3 install thrift + && pip3 install jinja2 + +# gnxi/gnmi_cli_py ships pre-generated _pb2.py stubs; they are +# Pin to 6.33.5 to match grpcio-tools keep a known-good version. +RUN set -e; \ + . /etc/os-release; \ + if [ "$VERSION_CODENAME" = "bookworm" ]; then \ + pip install protobuf==6.33.5; \ + else \ + pip install protobuf; \ + fi {% if docker_ptf_whls.strip() -%} # Copy locally-built Python wheel dependencies @@ -229,6 +300,9 @@ RUN pip3 install setuptools \ ENV PATH="$BACKUP_OF_PATH" {% endif %} +# Ensure setuptools >= 70.0.0 to address GHSA-cx63-2mw6-8hw5 +RUN pip3 install "setuptools>=70.0.0" + ## Adjust sshd settings RUN mkdir /var/run/sshd \ && echo 'root:root' | chpasswd \ @@ -255,16 +329,58 @@ RUN git clone https://github.com/google/gnxi.git \ COPY gnxi-patches/ gnxi/patches/ +{% if PTF_ENV_PY_VER == "mixed" %} RUN cd gnxi \ && quilt push -a \ && cd gnmi_cli_py \ -{% if PTF_ENV_PY_VER == "mixed" %} - && pip install -r requirements.txt + && pip install -r requirements.txt \ + && pip3 install protobuf==6.33.5 --no-binary=protobuf {% else %} - && cat requirements.txt | grep -v futures > /tmp/requirements.txt \ - && pip3 install -r /tmp/requirements.txt + +# Since gnxi were generated with proto 3.x we need to regenerate _pb2 with the new proto version +RUN cd gnxi \ + && quilt push -a \ + && cd gnmi_cli_py \ + && wget -q -O gnmi_ext.proto https://raw.githubusercontent.com/openconfig/gnmi/master/proto/gnmi_ext/gnmi_ext.proto \ + && wget -q -O gnmi.proto https://raw.githubusercontent.com/openconfig/gnmi/master/proto/gnmi/gnmi.proto \ + && sed -i 's|github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto|gnmi_ext.proto|' gnmi.proto \ + && python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. gnmi_ext.proto gnmi.proto \ + && rm -f gnmi.proto gnmi_ext.proto {% endif %} +# Deactivating a virtualenv. +# ENV PATH="$BACKUP_OF_PATH" + +# Build gnoic from source with patched Go and golang.org/x/* deps +# upgraded to latest to address current and future golang.org/x/* CVEs. +RUN git clone https://github.com/karimra/gnoic.git \ + && cd gnoic \ + && git checkout 27bc5a6 \ + && go get google.golang.org/grpc@v1.79.3 \ + && go get github.com/go-viper/mapstructure/v2@v2.4.0 \ + && go get golang.org/x/crypto@latest golang.org/x/net@latest golang.org/x/text@latest golang.org/x/sys@latest golang.org/x/oauth2@latest \ + && go mod tidy \ + && go build -o /usr/local/bin/gnoic . \ + && cd .. \ + && rm -rf gnoic + +# Build gnmic from source with patched Go and upgraded deps +# to address CVE-2026-33186 (grpc), CVE-2025-8556/CVE-2026-1229 (circl), +# CVE-2026-25934 (go-git), CVE-2026-27571 (nats-server), CVE-2026-24051 (otel) +RUN GNMIC_VERSION=v0.43.0 \ + && git clone --depth 1 --branch "${GNMIC_VERSION}" https://github.com/openconfig/gnmic.git /tmp/gnmic \ + && cd /tmp/gnmic \ + && go get google.golang.org/grpc@v1.79.3 \ + && go get github.com/cloudflare/circl@v1.6.3 \ + && go get github.com/go-git/go-git/v5@v5.16.5 \ + && go get github.com/nats-io/nats-server/v2@v2.11.12 \ + && go get go.opentelemetry.io/otel/sdk@v1.40.0 \ + && go get golang.org/x/crypto@latest golang.org/x/net@latest golang.org/x/text@latest golang.org/x/sys@latest golang.org/x/oauth2@latest \ + && go mod tidy \ + && go build -o /usr/local/bin/gnmic . \ + && chmod +x /usr/local/bin/gnmic \ + && rm -rf /tmp/gnmic + COPY \ {% for deb in docker_ptf_debs.split(' ') -%} debs/{{ deb }}{{' '}} @@ -276,6 +392,10 @@ RUN dpkg -i \ debs/{{ deb }}{{' '}} {%- endfor %} +# Remove Go toolchain to reduce image size +RUN rm -rf /usr/local/go "$(go env GOPATH 2>/dev/null || echo $HOME/go)" +ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + {% if PTF_ENV_PY_VER == "py3" %} # Create symlink so that test scripts and ptf_runner invocation path # is same across python 2 and python 3 envs. Note that for virtual-env diff --git a/dockers/docker-ptf/gnxi-patches/0005-Enhance-gnmi_cli_py-4.patch b/dockers/docker-ptf/gnxi-patches/0005-Enhance-gnmi_cli_py-4.patch index f9fc30d96eb..1e40d586b03 100644 --- a/dockers/docker-ptf/gnxi-patches/0005-Enhance-gnmi_cli_py-4.patch +++ b/dockers/docker-ptf/gnxi-patches/0005-Enhance-gnmi_cli_py-4.patch @@ -316,7 +316,7 @@ index dab2db6..e32b3ff 100644 -grpcio-tools==1.15.0 +grpcio==1.41.1 +grpcio-tools==1.41.1 - protobuf==3.6.1 --no-binary=protobuf + protobuf==6.33.5 --no-binary=protobuf six==1.12.0 -- 2.48.1.windows.1 diff --git a/dockers/docker-ptf/gnxi-patches/0009-chore-remove-deprecated-use_aliases-field-and-fix-syntax.patch b/dockers/docker-ptf/gnxi-patches/0009-chore-remove-deprecated-use_aliases-field-and-fix-syntax.patch new file mode 100644 index 00000000000..b0787f20a8f --- /dev/null +++ b/dockers/docker-ptf/gnxi-patches/0009-chore-remove-deprecated-use_aliases-field-and-fix-syntax.patch @@ -0,0 +1,47 @@ +From 82efb3e99dfffbb137858bce18a6acdec79c1e44 Mon Sep 17 00:00:00 2001 +From: Austin Pham +Date: Thu, 12 Mar 2026 09:57:50 +1100 +Subject: [PATCH] chore: remove deprecated use_aliases field and fix syntax + warning is not + +Signed-off-by: Austin Pham +--- + gnmi_cli_py/py_gnmicli.py | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/gnmi_cli_py/py_gnmicli.py b/gnmi_cli_py/py_gnmicli.py +index b7d1165..150239a 100644 +--- a/gnmi_cli_py/py_gnmicli.py ++++ b/gnmi_cli_py/py_gnmicli.py +@@ -172,7 +172,6 @@ def _create_parser(): + parser.add_argument('--subscribe_mode', default=0, type=int, help='[0=STREAM, 1=ONCE, 2=POLL]') + parser.add_argument('--encoding', default=0, type=int, help='[0=JSON, 1=BYTES, 2=PROTO, 3=ASCII, 4=JSON_IETF]') + parser.add_argument('--qos', default=0, type=int, help='') +- parser.add_argument('--use_alias', action='store_true', help='use alias') + parser.add_argument('--create_connections', type=int, nargs='?', const=1, default=1, + help='Creates specific number of TCP connections with gNMI server side. ' + 'Default number of TCP connections is 1 and use -1 to create ' +@@ -489,7 +488,7 @@ def gen_request(paths, opt, prefix): + myqos = None + mysblist = gnmi_pb2.SubscriptionList(prefix=myprefix, mode=opt['subscribe_mode'], + allow_aggregation=opt['aggregate'], encoding=opt['encoding'], +- subscription=mysubs, use_aliases=opt['use_alias'], qos=myqos) ++ subscription=mysubs, qos=myqos) + mysubreq = gnmi_pb2.SubscribeRequest(subscribe=mysblist) + + print('Sending SubscribeRequest\n'+str(mysubreq)) +@@ -538,9 +537,9 @@ def subscribe_start(stub, options, req_iterator): + ' received\n'+str(response.error.message) + str(response.error)) + elif response.HasField('update'): + if filter_event_regex is not None: +- if filter_event_regex is not "": ++ if filter_event_regex != "": + match = check_event_response(response, filter_event_regex) +- if len(match) is not 0: ++ if len(match) != 0: + print(response) + update_count = update_count + 1 + else: +-- +2.34.1 + diff --git a/dockers/docker-ptf/gnxi-patches/series b/dockers/docker-ptf/gnxi-patches/series index f74c348cc05..394fac1e53a 100644 --- a/dockers/docker-ptf/gnxi-patches/series +++ b/dockers/docker-ptf/gnxi-patches/series @@ -5,3 +5,4 @@ 0005-Enhance-gnmi_cli_py-4.patch 0006-Add-support-for-extensive-configurations.patch 0007-Fix-py_gnmicli.py-POLL-mode.patch +0009-chore-remove-deprecated-use_aliases-field-and-fix-syntax.patch \ No newline at end of file diff --git a/dockers/dockerfile-macros.j2 b/dockers/dockerfile-macros.j2 index 9917cb17aba..17ede61d46e 100644 --- a/dockers/dockerfile-macros.j2 +++ b/dockers/dockerfile-macros.j2 @@ -37,3 +37,14 @@ COPY \ {%- endfor %} {{ dest }} {%- endmacro %} + +{% macro install_offending_packages(packages) -%} + {%- for pkg in packages %} + RUN pip3 install {{ pkg }} + RUN pip3 install setuptools + {%- endfor %} +{%- endmacro %} + +{% macro rsync_from_builder_stage() -%} +RUN --mount=type=bind,from=base,target=/changes-to-image rsync -axAX --omit-dir-times --no-D --exclude=/sys --exclude=/proc --exclude=/dev --exclude=resolv.conf /changes-to-image/ / +{%- endmacro %} diff --git a/rules/config b/rules/config index 11cdd18f2b9..17967fe7f25 100644 --- a/rules/config +++ b/rules/config @@ -330,7 +330,7 @@ BUILD_REDUCE_IMAGE_SIZE = n # SONIC_PTF_ENV_PY_VER - SONiC PTF test Python version. Set to 'mixed' to build the # image with both Python 2 and 3. Set to 'py3' to build a Python 3 only image -SONIC_PTF_ENV_PY_VER = mixed +SONIC_PTF_ENV_PY_VER = py3 # Add timeout on some process which may hangs BUILD_PROCESS_TIMEOUT ?= 0