diff --git a/.github/workflows/container-build-neutron.yaml b/.github/workflows/container-build-neutron.yaml new file mode 100644 index 0000000..b60b9c2 --- /dev/null +++ b/.github/workflows/container-build-neutron.yaml @@ -0,0 +1,162 @@ +--- +name: Create and publish a Neutron image + +permissions: + actions: read + contents: read + id-token: write + packages: write + pull-requests: write + security-events: write + +on: + pull_request: + paths: + - .github/workflows/container-build-neutron.yaml + - ContainerFiles/neutron + - scripts/neutron-cve-patching.sh + schedule: + - cron: '0 0 * * 0' # Run Weekly at midnight UTC + workflow_dispatch: + inputs: + openstack-constraints: + description: 'Version of OpenStack Constraints to use' + required: true + default: "master" + type: choice + options: + - master + - stable/2024.1 + - stable/2025.1 + project-version: + description: 'Version of OpenStack Neutroon to build, defaults to openstack-constraints if unspecified' + required: false + type: string + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }}/neutron + project_version: ${{ github.event.inputs.project-version }} + # NOTE(cloudnull): This is used to parse the workflow_dispatch inputs, sadly the inputs are not available in the + # workflow_dispatch event, so they're being stored in the environment variables. This is a + # workaround until there's a better way to handle this. + openstack_constraints: > + ["stable/2024.1", "stable/2025.1"] + +jobs: + init: + runs-on: ubuntu-latest + outputs: + openstack-constraints: ${{ steps.generate-matrix.outputs.openstack_constraints }} + steps: + - name: generate-matrix + id: generate-matrix + run: | + if [ "${{ github.event_name == 'workflow_dispatch' }}" = "true" ]; then + openstack_constraints="$(echo '${{ github.event.inputs.openstack-constraints }}' | jq -R '[select(length>0)]' | jq -c '.')" + fi + echo "openstack_constraints=${openstack_constraints:-${{ env.openstack_constraints }}}" >> $GITHUB_OUTPUT + build-and-push-image: + needs: + - init + strategy: + matrix: + openstack-constraints: ${{ fromJSON(needs.init.outputs.openstack-constraints)}} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Dynamically set MY_DATE environment variable + run: echo "MY_DATE=$(date +%s)" >> $GITHUB_ENV + - name: Dynamically set OS_VERSION_PARSE environment variable + run: | + VERSION=$(echo -n "${{ env.project_version != '' && env.project_version || matrix.openstack-constraints }}" | awk -F'/' '{($2=="" ? x=$1 : x=$2); print x}') + echo "OS_VERSION_PARSE=${VERSION}" >> $GITHUB_ENV + NAME=$(echo -n "${{ env.IMAGE_NAME }}" | awk -F'/' '{print $NF}') + echo "CATEGORY_NAME=${VERSION}-${NAME}" >> $GITHUB_ENV + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ContainerFiles/neutron + push: false + load: true + cache-from: type=gha + cache-to: type=gha,mode=max + tags: | + ${{ env.IMAGE_NAME }}:local + labels: ${{ steps.meta.outputs.labels }} + build-args: | + OS_VERSION=${{ env.project_version != '' && env.project_version || matrix.openstack-constraints }} + OS_CONSTRAINTS=${{ matrix.openstack-constraints }} + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@0.28.0 + if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' }} + with: + image-ref: '${{ env.IMAGE_NAME }}:local' + format: 'sarif' + output: 'trivy-results.sarif' + ignore-unfixed: true + severity: 'CRITICAL,HIGH' + - name: Upload Trivy scan results to GitHub Security tab + continue-on-error: true + if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' }} + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: 'trivy-results.sarif' + category: "${{ env.CATEGORY_NAME }}" + - name: Run Trivy scanner + uses: aquasecurity/trivy-action@0.28.0 + if: ${{ github.event_name == 'pull_request' }} + with: + image-ref: '${{ env.IMAGE_NAME }}:local' + output: trivy.txt + ignore-unfixed: true + severity: 'CRITICAL,HIGH' + - name: Create trivy output file in markdown format + if: ${{ github.event_name == 'pull_request' }} + run: | + if [[ -s trivy.txt ]]; then + echo "### Security Output" > trivy-output.txt + echo '```terraform' >> trivy-output.txt + cat trivy.txt >> trivy-output.txt + echo '```' >> trivy-output.txt + fi + - name: Publish Trivy Output to Summary + if: ${{ github.event_name == 'pull_request' }} + run: | + if [[ -s trivy-output.txt ]]; then + { + cat trivy-output.txt + } >> $GITHUB_STEP_SUMMARY + fi + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ContainerFiles/neutron + push: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' }} + cache-from: type=gha + cache-to: type=gha,mode=max + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.OS_VERSION_PARSE }}-latest + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.OS_VERSION_PARSE }}-${{ env.MY_DATE }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + OS_VERSION=${{ env.project_version != '' && env.project_version || matrix.openstack-constraints }} + OS_CONSTRAINTS=${{ matrix.openstack-constraints }} diff --git a/ContainerFiles/neutron b/ContainerFiles/neutron new file mode 100644 index 0000000..afca81d --- /dev/null +++ b/ContainerFiles/neutron @@ -0,0 +1,88 @@ +# syntax = docker/dockerfile:1 +# This Dockerfile uses multi-stage build to customize DEV and PROD images: +# https://docs.docker.com/develop/develop-images/multistage-build/ + +ARG VENV_TAG=3.12-latest +FROM ghcr.io/rackerlabs/genestack-images/openstack-venv:${VENV_TAG} AS dependency_build +ARG OS_VERSION=master +ARG OS_CONSTRAINTS=master +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y \ + bash \ + brotli \ + build-essential \ + curl \ + docutils-common \ + gettext \ + git \ + libffi-dev \ + libjs-sphinxdoc \ + libjs-underscore \ + libldap2-dev \ + libpq-dev \ + libsasl2-dev \ + libssl-dev \ + libsystemd-dev \ + libxml2-dev \ + libxslt1-dev \ + libxslt1.1 \ + pkg-config \ + ssl-cert \ + xmlsec1 +RUN /var/lib/openstack/bin/pip install --constraint https://opendev.org/openstack/requirements/raw/branch/${OS_CONSTRAINTS}/upper-constraints.txt \ + git+https://opendev.org/openstack/ovn-bgp-agent@${OS_VERSION}#egg=ovn-bgp-agent \ + git+https://opendev.org/openstack/neutron.git@${OS_VERSION}#egg=neutron \ + git+https://opendev.org/openstack/neutron-dynamic-routing@${OS_VERSION}#egg=neutron-dynamic-routing \ + git+https://opendev.org/openstack/neutron-fwaas@${OS_VERSION}#egg=neutron-fwaas \ + git+https://opendev.org/openstack/neutron-vpnaas@${OS_VERSION}#egg=neutron-vpnaas \ + PyMySQL + +COPY scripts/neutron-cve-patching.sh /opt/ +RUN bash /opt/neutron-cve-patching.sh + +RUN find / -name '*.pyc' -delete \ + && find / -name '*.pyo' -delete \ + && find / -name '__pycache__' -delete \ + && find / -name '*.whl' -delete \ + && rm -f /var/lib/openstack/lib/python*/site-packages/slapdtest/certs/client.key \ + && rm -f /var/lib/openstack/lib/python*/site-packages/slapdtest/certs/server.key + + +FROM python:3.12-slim-bookworm +LABEL maintainer="Rackspace" +LABEL vendor="Rackspace OpenStack Team" +LABEL org.opencontainers.image.name="neutron" +LABEL org.opencontainers.image.description="OpenStack Service (neutron) built for the enterprise." +COPY --from=dependency_build /var/lib/openstack /var/lib/openstack +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y conntrack \ + dnsmasq-utils \ + ebtables \ + ipset \ + iptables \ + iputils-arping \ + net-tools \ + keepalived \ + radvd \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* \ + && find / -name '*.pyc' -delete \ + && find / -name '*.pyo' -delete \ + && find / -name '__pycache__' -delete \ + && groupadd --system --gid 42424 neutron \ + && useradd --system --gid 42424 --uid 42424 --shell /sbin/nologin --create-home --home /var/lib/neutron neutron \ + && mkdir -p /var/lib/openstack/etc/neutron \ + && ln -s /var/lib/openstack/etc/neutron /etc/neutron \ + && chown neutron:neutron -h /etc/neutron \ + && chown -R neutron:neutron /var/lib/openstack/etc/neutron +# Set the environment variables for the neutron venv +ENV PATH="/var/lib/openstack/bin:$PATH" +# Set the working directory +WORKDIR /var/lib/openstack +# Set the user and group to match the original build +USER 42424:42424 +# Set the entrypoint to the neutron-manage command +ENTRYPOINT ["/var/lib/openstack/bin/neutron-manage"] diff --git a/README.md b/README.md index cf0ab45..e54ce7e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This repository contains GitHub Actions workflows and Containerfiles for buildin - **Keystone** - OpenStack Identity Service - **Glance** - OpenStack Image Service - **Heat** - OpenStack Orchestration Service +- **Neutron** - OpenStack Orchestration Service ### Supporting Services @@ -144,6 +145,41 @@ docker build \ -t heat:local . ``` +### Neutron + +The Neutron container provides OpenStack's image service: + +**Features:** + +- Built to ensure compatibility with OVN +- uWSGI application server +- CVE patching for security compliance + +**Build Arguments:** + +- `OS_VERSION` - OpenStack version (default: master) +- `OS_CONSTRAINTS` - OpenStack constraints version + +#### Running Neutron + +```bash +docker run -d \ + --name neutron \ + -p 9292:9292 \ + -v /etc/neutron:/etc/neutron \ + ghcr.io/rackspace/genestack-images/neutron:master-latest +``` + +#### Building Neutron + +```bash +docker build \ + --build-arg OS_VERSION=master \ + --build-arg OS_CONSTRAINTS=master \ + -f ContainerFiles/neutron \ + -t neutron:local . +``` + ### Shibd Lightweight Shibboleth Service Provider container: @@ -291,6 +327,7 @@ The build process includes automatic patching for known CVEs - **Keystone**: Patches applied via `scripts/keystone-cve-patching.sh` - **Glance**: Patches applied via `scripts/glance-cve-patching.sh` - **Heat**: Patches applied via `scripts/heat-cve-patching.sh` +- **Neutron**: Patches applied via `scripts/neutron-cve-patching.sh` ### Vulnerability Scanning diff --git a/scripts/neutron-cve-patching.sh b/scripts/neutron-cve-patching.sh new file mode 100644 index 0000000..2444a31 --- /dev/null +++ b/scripts/neutron-cve-patching.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +if [ ${OS_VERSION:-master} = "stable/2025.1" ]; then + echo "Patching neutron for CVE fixes in stable/2025.1" +elif [ ${OS_VERSION:-master} = "stable/2024.1" ]; then + # CVE fixes CVE-2024-36039 + /var/lib/openstack/bin/pip install --upgrade \ + "PyMySQL==1.1.1" +fi