diff --git a/.github/workflows/docker-security-scan.yml b/.github/workflows/docker-security-scan.yml index 9db45d4..da30645 100644 --- a/.github/workflows/docker-security-scan.yml +++ b/.github/workflows/docker-security-scan.yml @@ -62,100 +62,77 @@ jobs: sarif_file: "trivy-results.sarif" category: "Trivy Scan" - - name: Check for outdated base images + - name: Run Trivy for outdated OS packages uses: aquasecurity/trivy-action@master with: image-ref: aprs:${{ github.sha }} format: "table" - output: "outdated-dependencies.txt" - exit-code: "0" # Don't fail the build yet - severity: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL" - scanners: "vuln" + output: "os-packages.txt" + severity: "HIGH,CRITICAL" vuln-type: "os" - - name: Check for outdated application dependencies + - name: Run Trivy for outdated application dependencies uses: aquasecurity/trivy-action@master with: image-ref: aprs:${{ github.sha }} format: "table" - output: "outdated-app-dependencies.txt" - exit-code: "0" # Don't fail the build yet - severity: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL" - scanners: "vuln" + output: "app-packages.txt" + severity: "HIGH,CRITICAL" vuln-type: "library" - - name: Upload outdated dependencies report + - name: Upload detailed vulnerability reports uses: actions/upload-artifact@v3 if: always() with: - name: dependency-reports + name: vulnerability-reports path: | - outdated-dependencies.txt - outdated-app-dependencies.txt + os-packages.txt + app-packages.txt retention-days: 7 - - name: Check for update availability in base image - run: | - # Extract base image from Dockerfile - BASE_IMAGE=$(grep 'RUNNER_IMAGE=' Dockerfile | cut -d'"' -f2) - echo "Base image: $BASE_IMAGE" - - # Pull latest version of the base image - docker pull $BASE_IMAGE - - # Compare the digests - CURRENT_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' $BASE_IMAGE) - echo "Current digest: $CURRENT_DIGEST" - - # Log the findings - echo "Base image update check completed" - echo "It's recommended to periodically update the base image in your Dockerfile" - - - name: Scan for security misconfigurations - uses: aquasecurity/trivy-action@master + - name: Run Docker Scout vulnerability scan + uses: docker/scout-action@v1 with: - image-ref: aprs:${{ github.sha }} - format: "table" - output: "misconfig-results.txt" - exit-code: "0" - scanners: "config" + command: quickview,cves + image: aprs:${{ github.sha }} + output-format: sarif + only-severities: critical,high + sarif-file: scout-results.sarif - - name: Upload misconfiguration report + - name: Upload Docker Scout results uses: actions/upload-artifact@v3 if: always() with: - name: misconfig-report - path: misconfig-results.txt + name: docker-scout-report + path: scout-results.sarif retention-days: 7 - - name: Generate comprehensive security report + - name: Generate security report summary if: always() run: | - echo "# Docker Image Security Report" > security-report.md - echo "## Image: aprs:${{ github.sha }}" >> security-report.md - echo "## Date: $(date)" >> security-report.md - echo "## Base Image Update Status" >> security-report.md - echo "\`\`\`" >> security-report.md - cat outdated-dependencies.txt >> security-report.md - echo "\`\`\`" >> security-report.md - echo "## Application Dependencies Status" >> security-report.md - echo "\`\`\`" >> security-report.md - cat outdated-app-dependencies.txt >> security-report.md - echo "\`\`\`" >> security-report.md - echo "## Configuration Issues" >> security-report.md - echo "\`\`\`" >> security-report.md - cat misconfig-results.txt >> security-report.md - echo "\`\`\`" >> security-report.md + echo "# Docker Image Security Report" > security-summary.md + echo "## Image: aprs:${{ github.sha }}" >> security-summary.md + echo "## Date: $(date)" >> security-summary.md - - name: Upload comprehensive security report + echo "### OS Package Vulnerabilities" >> security-summary.md + echo '```' >> security-summary.md + cat os-packages.txt >> security-summary.md || echo "No OS package vulnerabilities report available" >> security-summary.md + echo '```' >> security-summary.md + + echo "### Application Dependencies Vulnerabilities" >> security-summary.md + echo '```' >> security-summary.md + cat app-packages.txt >> security-summary.md || echo "No application vulnerabilities report available" >> security-summary.md + echo '```' >> security-summary.md + + - name: Upload security summary uses: actions/upload-artifact@v3 if: always() with: - name: security-report - path: security-report.md + name: security-summary + path: security-summary.md retention-days: 14 - - name: Fail on CRITICAL vulnerabilities + - name: Check for critical vulnerabilities run: | if grep -q "CRITICAL" trivy-results.sarif; then echo "Critical vulnerabilities found in the Docker image." diff --git a/Dockerfile b/Dockerfile index 64767ec..d50ea1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,13 +11,12 @@ ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}" FROM ${BUILDER_IMAGE} AS builder -# install build dependencies with full security updates +# install build dependencies RUN apt-get update -y && \ - apt-get upgrade -y --security && \ - apt-get dist-upgrade -y && \ + apt-get upgrade -y && \ apt-get install -y build-essential git && \ apt-get clean && \ - rm -rf /var/lib/apt/lists/* + rm -f /var/lib/apt/lists/*_* # prepare build dir WORKDIR /app @@ -65,28 +64,16 @@ FROM ${RUNNER_IMAGE} # Install security updates and required packages ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -y && \ - # Upgrade all packages with focus on security updates - apt-get upgrade -y --security && \ - apt-get dist-upgrade -y && \ - # Install unattended-upgrades for automatic security updates + apt-get upgrade -y && \ apt-get install -y --no-install-recommends \ libstdc++6 \ openssl \ libncurses5 \ locales \ ca-certificates \ - tini \ - unattended-upgrades \ - apt-listchanges && \ - # Configure unattended-upgrades to only install security updates - echo 'APT::Periodic::Update-Package-Lists "1";' > /etc/apt/apt.conf.d/20auto-upgrades && \ - echo 'APT::Periodic::Unattended-Upgrade "1";' >> /etc/apt/apt.conf.d/20auto-upgrades && \ - echo 'Unattended-Upgrade::Origins-Pattern { "origin=Debian,codename=${distro_codename},label=Debian-Security"; };' > /etc/apt/apt.conf.d/50unattended-upgrades && \ - echo 'Unattended-Upgrade::Remove-Unused-Dependencies "true";' >> /etc/apt/apt.conf.d/50unattended-upgrades && \ - # Remove unnecessary packages - apt-get autoremove -y && \ + tini && \ apt-get clean && \ - rm -rf /var/lib/apt/lists/* && \ + rm -f /var/lib/apt/lists/*_* && \ # Create a non-root user and group with specific ID groupadd -g ${GROUP_ID} aprs && \ useradd -r -g aprs -u ${USER_ID} -s /bin/false -M aprs && \ diff --git a/scripts/update-docker-image.sh b/scripts/update-docker-image.sh index 0d9c954..1b13b12 100755 --- a/scripts/update-docker-image.sh +++ b/scripts/update-docker-image.sh @@ -1,8 +1,5 @@ #!/bin/bash # update-docker-image.sh - Script to update Docker image with latest dependencies -# -# This script helps ensure that your Docker image has the latest system dependencies -# by rebuilding the image with the latest base image and system packages. set -e # Exit immediately if a command exits with a non-zero status @@ -21,66 +18,56 @@ if ! command -v docker &> /dev/null; then exit 1 fi -# Get the latest version of the base image from Dockerfile -BASE_IMAGE=$(grep 'DEBIAN_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"') -if [ -z "$BASE_IMAGE" ]; then - echo -e "${RED}Error: Could not determine base image from Dockerfile${NC}" +# Check Docker daemon is running +if ! docker info &> /dev/null; then + echo -e "${RED}Error: Docker daemon is not running${NC}" exit 1 fi -echo -e "${GREEN}Current base image:${NC} $BASE_IMAGE" +# Get the current versions from Dockerfile +ELIXIR_VERSION=$(grep 'ELIXIR_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"') +OTP_VERSION=$(grep 'OTP_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"') +DEBIAN_VERSION=$(grep 'DEBIAN_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"') -# Check if we need to update the Dockerfile -echo -e "${YELLOW}Checking for newer Debian versions...${NC}" -latest_debian=$(curl -s https://hub.docker.com/v2/repositories/library/debian/tags/ | \ - grep -o '"name":"[^"]*-slim"' | grep bullseye | sort -r | head -1 | cut -d'"' -f4) - -if [ -z "$latest_debian" ]; then - echo -e "${YELLOW}Could not determine latest Debian version, keeping current version${NC}" -else - echo -e "${GREEN}Latest Debian version:${NC} $latest_debian" - - # Update Dockerfile with latest Debian version - current_version=$(grep 'DEBIAN_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"') - if [ "$current_version" != "$latest_debian" ]; then - echo -e "${YELLOW}Updating Dockerfile with latest Debian version...${NC}" - sed -i "s/ARG DEBIAN_VERSION=.*/ARG DEBIAN_VERSION=$latest_debian/" Dockerfile - echo -e "${GREEN}Updated Dockerfile with new base image:${NC} $latest_debian" - else - echo -e "${GREEN}Already using the latest Debian version${NC}" - fi -fi - -# Check for latest Elixir and OTP versions -echo -e "${YELLOW}Checking for newer Elixir and OTP versions...${NC}" -current_elixir=$(grep 'ELIXIR_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"') -current_otp=$(grep 'OTP_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"') - -echo -e "${GREEN}Current Elixir version:${NC} $current_elixir" -echo -e "${GREEN}Current OTP version:${NC} $current_otp" +echo -e "${GREEN}Current versions:${NC}" +echo " Elixir: $ELIXIR_VERSION" +echo " OTP: $OTP_VERSION" +echo " Debian: $DEBIAN_VERSION" +# Build the Docker image with a clean cache to ensure latest deps echo -e "${YELLOW}Building Docker image with latest dependencies...${NC}" +echo "This may take a few minutes..." + +# Use --pull to ensure we get the latest base image +# Use --no-cache to ensure all layers are rebuilt with latest packages docker build --no-cache --pull -t aprs:latest . -echo -e "${GREEN}Running security scan on updated image...${NC}" +# Run a security scan if Trivy is available if command -v trivy &> /dev/null; then + echo -e "${GREEN}Running security scan on updated image...${NC}" trivy image --severity HIGH,CRITICAL aprs:latest else - echo -e "${YELLOW}Trivy not installed. Skipping security scan.${NC}" - echo "Install Trivy with: brew install aquasecurity/trivy/trivy (macOS) or similar for your OS" + echo -e "${YELLOW}Trivy not installed. Security scan skipped.${NC}" + echo "To install Trivy:" + echo " - macOS: brew install aquasecurity/trivy/trivy" + echo " - Linux: see https://aquasecurity.github.io/trivy/latest/getting-started/installation/" fi echo -echo -e "${GREEN}=== Update Complete ===${NC}" -echo "The Docker image has been rebuilt with the latest dependencies" -echo "Recommendations:" -echo "1. Run tests to ensure everything still works" -echo "2. Check for any security issues reported above" -echo "3. If all is well, commit and push your changes" +echo -e "${GREEN}=== Docker Image Update Complete ===${NC}" echo +echo "The Docker image has been rebuilt with the latest dependencies." +echo +echo "Next steps:" +echo "1. Test the image: docker run --rm -it aprs:latest" +echo "2. If tests pass, tag and push the image to your registry" +echo "3. Deploy the updated image to your environment" +echo +echo -e "${YELLOW}Remember to update CI workflows if you've changed base image versions${NC}" # Check if git is available and we're in a git repository -if command -v git &> /dev/null && git rev-parse --is-inside-work-tree &> /dev/null; then - echo "Git changes:" - git diff Dockerfile +if command -v git &> /dev/null && git rev-parse --is-inside-work-tree &> /dev/null 2>&1; then + echo + echo -e "${GREEN}Git status:${NC}" + git status -s fi