From 2386f0d5a5b296d7977a0fc9ec7434308b1e2d46 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 10 Feb 2026 17:58:43 -0600 Subject: [PATCH] Upgrade net-snmp to 5.9.5.2 to fix SIGSEGV in SNMP walks Alpine 3.23 ships net-snmp 5.9.4 which has segfault bugs in varbind construction and buffer overflows in octet string handling. Version 5.9.5.2 fixes these issues. Adds a pre-built base image (Dockerfile.netsnmp) compiled from source so the net-snmp build doesn't run on every agent deploy. The base image is built separately via the netsnmp-base workflow and referenced in the main Dockerfile. --- .github/workflows/netsnmp-base.yml | 99 ++++++++++++++++++++++++++++++ Dockerfile | 17 +++-- Dockerfile.netsnmp | 66 ++++++++++++++++++++ 3 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/netsnmp-base.yml create mode 100644 Dockerfile.netsnmp diff --git a/.github/workflows/netsnmp-base.yml b/.github/workflows/netsnmp-base.yml new file mode 100644 index 0000000..794119c --- /dev/null +++ b/.github/workflows/netsnmp-base.yml @@ -0,0 +1,99 @@ +# Builds the net-snmp base image used by the agent Dockerfile. +# Only needs to run when the net-snmp version changes. +# +# Trigger manually or on changes to Dockerfile.netsnmp. + +name: Build net-snmp base image + +on: + push: + branches: [main] + paths: [Dockerfile.netsnmp] + workflow_dispatch: + inputs: + netsnmp_version: + description: "Net-SNMP version to build" + default: "5.9.5.2" + required: true + +env: + REGISTRY: ghcr.io + IMAGE_NAME: towerops-app/netsnmp-alpine + +jobs: + build: + name: Build (${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + arch: amd64 + runner: blacksmith-4vcpu-ubuntu-2404 + - platform: linux/arm64 + arch: arm64 + runner: blacksmith-4vcpu-ubuntu-2404-arm + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Blacksmith Builder + uses: useblacksmith/setup-docker-builder@v1 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: useblacksmith/build-push-action@v2 + with: + context: . + file: Dockerfile.netsnmp + platforms: ${{ matrix.platform }} + push: true + provenance: false + build-args: | + NETSNMP_VERSION=${{ inputs.netsnmp_version || '5.9.5.2' }} + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.netsnmp_version || '5.9.5.2' }}-${{ matrix.arch }} + + manifest: + name: Create Manifest + needs: build + runs-on: blacksmith-4vcpu-ubuntu-2404 + permissions: + contents: read + packages: write + steps: + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create multi-arch manifest + run: | + VERSION="${{ inputs.netsnmp_version || '5.9.5.2' }}" + IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" + + docker buildx imagetools create -t ${IMAGE}:${VERSION} \ + ${IMAGE}:${VERSION}-amd64 \ + ${IMAGE}:${VERSION}-arm64 + + docker buildx imagetools create -t ${IMAGE}:latest \ + ${IMAGE}:${VERSION}-amd64 \ + ${IMAGE}:${VERSION}-arm64 + + - name: Summary + run: | + echo "### net-snmp base image built" >> $GITHUB_STEP_SUMMARY + echo "Version: ${{ inputs.netsnmp_version || '5.9.5.2' }}" >> $GITHUB_STEP_SUMMARY + echo "Image: \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.netsnmp_version || '5.9.5.2' }}\`" >> $GITHUB_STEP_SUMMARY diff --git a/Dockerfile b/Dockerfile index 6aca1c5..bd674db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,8 @@ # syntax=docker/dockerfile:1.4 +# Pre-built net-snmp 5.9.5.2 (fixes segfaults in Alpine's 5.9.4) +# Built from Dockerfile.netsnmp via .github/workflows/netsnmp-base.yml +FROM ghcr.io/towerops-app/netsnmp-alpine:5.9.5.2 AS netsnmp + # Build stage FROM rust:1.93-alpine AS builder @@ -9,8 +13,11 @@ ARG VERSION=0.1.0-unknown WORKDIR /app -# Install build dependencies (net-snmp-dev required for C FFI) -RUN apk add --no-cache musl-dev protobuf-dev openssl-dev openssl-libs-static net-snmp-dev cmake perl g++ +# Install build dependencies (net-snmp headers/libs from pre-built image) +RUN apk add --no-cache musl-dev protobuf-dev openssl-dev openssl-libs-static cmake perl g++ +COPY --from=netsnmp /usr/include/net-snmp /usr/include/net-snmp +COPY --from=netsnmp /usr/lib/libnetsnmp* /usr/lib/ +COPY --from=netsnmp /usr/lib/libsnmp* /usr/lib/ # Determine Rust target based on platform and add it RUN case "$TARGETPLATFORM" in \ @@ -58,8 +65,10 @@ FROM alpine:3.23 # Install runtime dependencies # iputils provides ping with setuid root (doesn't require CAP_NET_RAW) -# net-snmp-libs required for C FFI to libnetsnmp -RUN apk add --no-cache ca-certificates iputils openssl net-snmp-libs +RUN apk add --no-cache ca-certificates iputils openssl + +# Copy net-snmp 5.9.5.2 shared libraries from pre-built image +COPY --from=netsnmp /usr/lib/libnetsnmp.so* /usr/lib/ # Copy binary from builder COPY --from=builder /tmp/towerops-agent /usr/local/bin/towerops-agent diff --git a/Dockerfile.netsnmp b/Dockerfile.netsnmp new file mode 100644 index 0000000..a82eaf7 --- /dev/null +++ b/Dockerfile.netsnmp @@ -0,0 +1,66 @@ +# syntax=docker/dockerfile:1.4 +# Base image with net-snmp 5.9.5.2 compiled from source +# Alpine 3.23 ships 5.9.4 which has segfault bugs in varbind construction +# and buffer overflows in octet string handling. +# +# Build and push with: +# docker buildx build -f Dockerfile.netsnmp --platform linux/amd64,linux/arm64 \ +# -t ghcr.io/towerops-app/netsnmp-alpine:5.9.5.2 --push . +# +# Or use the GitHub Actions workflow: .github/workflows/netsnmp-base.yml + +FROM alpine:3.23 AS builder + +ARG NETSNMP_VERSION=5.9.5.2 + +# Install build dependencies +RUN apk add --no-cache \ + gcc \ + g++ \ + make \ + musl-dev \ + openssl-dev \ + linux-headers \ + perl \ + curl + +# Download and extract net-snmp source +RUN curl -fsSL "https://downloads.sourceforge.net/project/net-snmp/net-snmp/${NETSNMP_VERSION}/net-snmp-${NETSNMP_VERSION}.tar.gz" \ + -o /tmp/net-snmp.tar.gz && \ + tar xzf /tmp/net-snmp.tar.gz -C /tmp && \ + rm /tmp/net-snmp.tar.gz + +WORKDIR /tmp/net-snmp-${NETSNMP_VERSION} + +# Configure with minimal features (we only need libnetsnmp) +RUN ./configure \ + --prefix=/usr \ + --sysconfdir=/etc \ + --with-defaults \ + --with-openssl \ + --enable-shared \ + --enable-static \ + --disable-applications \ + --disable-manuals \ + --disable-scripts \ + --without-perl-modules \ + --without-python-modules \ + --with-default-snmp-version=3 + +# Build and install +RUN make -j$(nproc) && \ + make install DESTDIR=/netsnmp-install + +# Final stage: slim image with just the compiled artifacts +FROM alpine:3.23 + +# Install runtime dependency (OpenSSL) that net-snmp links against +RUN apk add --no-cache openssl-dev musl-dev + +# Copy compiled net-snmp (headers, static libs, shared libs) +COPY --from=builder /netsnmp-install/usr/include/net-snmp /usr/include/net-snmp +COPY --from=builder /netsnmp-install/usr/lib/libnetsnmp* /usr/lib/ +COPY --from=builder /netsnmp-install/usr/lib/libsnmp* /usr/lib/ + +# Ensure shared library cache is updated +RUN ldconfig /usr/lib 2>/dev/null || true