Container Security Scan #8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Container Security Scan | |
| on: | |
| schedule: | |
| # Run nightly security scans at 2 AM UTC | |
| - cron: '0 2 * * *' | |
| workflow_dispatch: | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| jobs: | |
| security-scan: | |
| name: Container Security Analysis | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| security-events: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| # 1. DOCKERFILE SECURITY | |
| - name: Run Hadolint (Dockerfile Linter) | |
| uses: hadolint/[email protected] | |
| with: | |
| dockerfile: Dockerfile | |
| format: sarif | |
| output-file: hadolint-results.sarif | |
| no-color: true | |
| failure-threshold: error | |
| - name: Upload Hadolint results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: hadolint-results.sarif | |
| # 2. BUILD IMAGE ONCE | |
| - name: Build container image for security testing | |
| id: build | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| load: true | |
| tags: ${{ env.IMAGE_NAME }}:security-test | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # 3. VULNERABILITY SCANNING (Trivy only - more comprehensive than Grype) | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.IMAGE_NAME }}:security-test | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| - name: Upload Trivy scan results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| - name: Run Trivy for high/critical vulnerabilities (fail build) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.IMAGE_NAME }}:security-test | |
| format: 'table' | |
| severity: 'HIGH,CRITICAL' | |
| exit-code: '1' | |
| # 4. CONTAINER STRUCTURE TESTS | |
| - name: Install container-structure-test | |
| run: | | |
| curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 | |
| chmod +x container-structure-test-linux-amd64 | |
| sudo mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test | |
| - name: Create structure test config | |
| run: | | |
| cat > container-structure-test.yaml << EOF | |
| schemaVersion: 2.0.0 | |
| commandTests: | |
| - name: "Check binary exists and is executable" | |
| command: "ls" | |
| args: ["-la", "/app/Honua.Server"] | |
| expectedOutput: [".*-rwxr-xr-x.*Honua.Server"] | |
| - name: "Check non-root user" | |
| command: "whoami" | |
| expectedOutput: ["honua"] | |
| - name: "Check required directories exist" | |
| command: "ls" | |
| args: ["-la", "/tmp"] | |
| expectedOutput: [".*honua-logs.*", ".*honua-cache.*"] | |
| fileExistenceTests: | |
| - name: 'Application binary' | |
| path: '/app/Honua.Server' | |
| shouldExist: true | |
| permissions: '-rwxr-xr-x' | |
| - name: 'Log directory' | |
| path: '/tmp/honua-logs' | |
| shouldExist: true | |
| isDirectory: true | |
| - name: 'Cache directory' | |
| path: '/tmp/honua-cache' | |
| shouldExist: true | |
| isDirectory: true | |
| metadataTest: | |
| exposedPorts: ["8080"] | |
| user: "honua" | |
| workdir: "/app" | |
| EOF | |
| - name: Run container structure tests | |
| run: | | |
| container-structure-test test \ | |
| --image ${{ env.IMAGE_NAME }}:security-test \ | |
| --config container-structure-test.yaml | |
| # 5. RUNTIME SECURITY TESTING | |
| - name: Start container for runtime security analysis | |
| run: | | |
| # Start container in detached mode with security constraints | |
| docker run -d \ | |
| --name honua-runtime-test \ | |
| --read-only \ | |
| --cap-drop ALL \ | |
| --security-opt no-new-privileges:true \ | |
| -p 8080:8080 \ | |
| ${{ env.IMAGE_NAME }}:security-test | |
| # Wait for container to start | |
| sleep 10 | |
| - name: Test runtime security constraints | |
| run: | | |
| echo "π Testing runtime security constraints..." | |
| # Test read-only filesystem | |
| if docker exec honua-runtime-test touch /test-write 2>/dev/null; then | |
| echo "β Container filesystem is writable (should be read-only)" | |
| exit 1 | |
| else | |
| echo "β Container filesystem is read-only" | |
| fi | |
| # Test user | |
| user=$(docker exec honua-runtime-test whoami) | |
| if [[ "$user" != "honua" ]]; then | |
| echo "β Container not running as expected user: $user" | |
| exit 1 | |
| else | |
| echo "β Container running as non-root user: $user" | |
| fi | |
| # Test application health | |
| if curl -f http://localhost:8080/healthz/live; then | |
| echo "β Application liveness check passed" | |
| else | |
| echo "β Application liveness check failed" | |
| exit 1 | |
| fi | |
| - name: Cleanup runtime test | |
| if: always() | |
| run: | | |
| docker stop honua-runtime-test || true | |
| docker rm honua-runtime-test || true | |
| # 6. GENERATE SECURITY SUMMARY | |
| - name: Generate security summary | |
| if: always() | |
| run: | | |
| echo "# π Container Security Report" > security-report.md | |
| echo "" >> security-report.md | |
| echo "**Scan Date**: $(date)" >> security-report.md | |
| echo "**Commit**: ${{ github.sha }}" >> security-report.md | |
| echo "**Image**: ${{ env.IMAGE_NAME }}:security-test" >> security-report.md | |
| echo "" >> security-report.md | |
| echo "## π‘οΈ Security Checks Completed" >> security-report.md | |
| echo "" >> security-report.md | |
| echo "β Dockerfile Security Analysis (Hadolint)" >> security-report.md | |
| echo "β Vulnerability Scanning (Trivy)" >> security-report.md | |
| echo "β Container Structure Tests" >> security-report.md | |
| echo "β Runtime Security Analysis" >> security-report.md | |
| echo "" >> security-report.md | |
| echo "## π§ Security Features Validated" >> security-report.md | |
| echo "" >> security-report.md | |
| echo "- Non-root user execution" >> security-report.md | |
| echo "- Read-only filesystem" >> security-report.md | |
| echo "- Minimal Linux capabilities" >> security-report.md | |
| echo "- No privilege escalation" >> security-report.md | |
| echo "- Secure application startup" >> security-report.md | |
| - name: Upload security report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: container-security-report | |
| path: security-report.md | |
| retention-days: 30 |