docker updates
This commit is contained in:
parent
7a8a5443f6
commit
b8f61a9a06
5 changed files with 363 additions and 60 deletions
118
.github/DOCKER_SECURITY.md
vendored
118
.github/DOCKER_SECURITY.md
vendored
|
|
@ -6,48 +6,68 @@ This document outlines our approach to maintaining secure Docker images for the
|
|||
|
||||
Keeping system dependencies updated is crucial for security. Our strategy includes:
|
||||
|
||||
1. **Automated Security Updates**
|
||||
- Base images are updated regularly with the latest security patches
|
||||
- `unattended-upgrades` is installed to automatically apply security updates
|
||||
- We use `apt-get upgrade --security` to prioritize security-related updates
|
||||
- Full system update with `apt-get dist-upgrade` to handle package dependencies
|
||||
1. **Regular Base Image Updates**
|
||||
- We use specific dated versions of Debian slim images (`bullseye-YYYYMMDD-slim`)
|
||||
- Images are rebuilt weekly via CI to incorporate latest security patches
|
||||
- Each build uses `--pull` to ensure we get the latest base image versions
|
||||
|
||||
2. **Continuous Integration Checks**
|
||||
2. **Full Package Updates During Build**
|
||||
- Every build performs `apt-get update && apt-get upgrade` in both build and runtime stages
|
||||
- We explicitly remove package lists after updates to reduce image size
|
||||
- Only necessary runtime packages are installed in the final image
|
||||
|
||||
3. **Minimized Attack Surface**
|
||||
- Non-root user execution with specific UID/GID
|
||||
- Removal of setuid/setgid permissions
|
||||
- Secure permissions on system files
|
||||
- Use of tini as init process
|
||||
- Minimal set of installed packages
|
||||
|
||||
4. **Continuous Monitoring**
|
||||
- Weekly automated builds via GitHub Actions
|
||||
- Security scanning on every build with Trivy
|
||||
- Detection of outdated dependencies with separate vulnerability reports
|
||||
- Security scanning with Trivy and Docker Scout
|
||||
- Detailed vulnerability reports for OS and application dependencies
|
||||
- Automatic failure on critical vulnerabilities
|
||||
|
||||
3. **Multi-stage Build Optimization**
|
||||
- Builder stage includes only development dependencies
|
||||
- Runtime stage includes only production dependencies
|
||||
- Each stage performs full security updates
|
||||
## How System Updates Are Applied
|
||||
|
||||
4. **Minimal Attack Surface**
|
||||
- Distroless or slim base images when possible
|
||||
- Unnecessary packages are removed
|
||||
- Non-root user execution
|
||||
- Removal of setuid/setgid permissions
|
||||
- Only required capabilities are enabled
|
||||
System dependencies are updated at several points:
|
||||
|
||||
## How Updates Are Applied
|
||||
|
||||
System dependencies are updated at multiple points:
|
||||
|
||||
1. **During Image Build**
|
||||
1. **During Image Build Time**
|
||||
```dockerfile
|
||||
RUN apt-get update -y && \
|
||||
apt-get upgrade -y --security && \
|
||||
apt-get dist-upgrade -y
|
||||
apt-get upgrade -y && \
|
||||
apt-get clean && \
|
||||
rm -f /var/lib/apt/lists/*_*
|
||||
```
|
||||
|
||||
2. **Automatically at Runtime**
|
||||
- The `unattended-upgrades` package applies security updates automatically
|
||||
- Configuration prioritizes official security updates
|
||||
|
||||
3. **Via Scheduled Rebuilds**
|
||||
2. **Via Scheduled Rebuilds**
|
||||
- Weekly GitHub Actions workflow rebuilds the image with latest dependencies
|
||||
- Base image is pulled with `--pull` flag to ensure latest version
|
||||
- No-cache builds ensure all layers are rebuilt with fresh packages
|
||||
|
||||
3. **During Deployment**
|
||||
- Images are rebuilt for each deployment
|
||||
- CI/CD pipeline includes security scanning before deployment
|
||||
|
||||
## Security Scanning Process
|
||||
|
||||
Our Docker images undergo multiple security scans:
|
||||
|
||||
1. **Trivy Scanning**
|
||||
- OS package vulnerabilities detection
|
||||
- Application dependency vulnerabilities detection
|
||||
- Configuration issue detection
|
||||
|
||||
2. **Docker Scout**
|
||||
- Deep analysis of base image security
|
||||
- Comprehensive CVE detection
|
||||
- Dependency analysis
|
||||
|
||||
3. **Artifact Storage**
|
||||
- Scan results are stored as GitHub Actions artifacts
|
||||
- Summary reports are generated for easy review
|
||||
- Historical data allows tracking security improvements
|
||||
|
||||
## Manual Update Process
|
||||
|
||||
|
|
@ -59,28 +79,26 @@ To manually update the Docker image with the latest system dependencies:
|
|||
```
|
||||
|
||||
2. This script will:
|
||||
- Check for newer versions of the base image
|
||||
- Update the Dockerfile if needed
|
||||
- Rebuild the image with `--no-cache --pull` to ensure fresh dependencies
|
||||
- Run a security scan on the updated image
|
||||
- Build a fresh image with latest dependencies using `--no-cache --pull`
|
||||
- Run a security scan if Trivy is available
|
||||
- Provide guidance on next steps
|
||||
|
||||
3. Verify that tests pass with the updated image before deployment
|
||||
3. Verify the updated image works as expected before deployment
|
||||
|
||||
## Security Monitoring
|
||||
## Best Practices We Follow
|
||||
|
||||
We continuously monitor for security issues through:
|
||||
- We pin specific versions of Elixir, OTP, and Debian in our Dockerfile
|
||||
- Multi-stage builds minimize the final image size
|
||||
- We use non-root users with minimal permissions
|
||||
- We secure system files and directories
|
||||
- We remove unnecessary files and packages
|
||||
- We use tini as an init process for proper signal handling
|
||||
- We scan for vulnerabilities in both OS and application dependencies
|
||||
- We update base images regularly (at least monthly)
|
||||
|
||||
1. GitHub Security tab showing Trivy scan results
|
||||
2. Weekly automated security scans
|
||||
3. Dependency updates via Dependabot
|
||||
4. Container registry vulnerability scanning
|
||||
## Improvement Roadmap
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Never use the `latest` tag in production
|
||||
- Pin specific versions in Dockerfile (ARG declarations)
|
||||
- Update base images at least monthly
|
||||
- Review and update the security strategy quarterly
|
||||
- Use multi-stage builds to minimize final image size
|
||||
- Implement least privilege principle (non-root user, minimal capabilities)
|
||||
- Keep secrets out of the image (use environment variables or secrets management)
|
||||
- Consider distroless images for further attack surface reduction
|
||||
- Implement auto-update PR creation for base image versions
|
||||
- Add dependency confusion detection
|
||||
- Implement more granular vulnerability allowlisting for false positives
|
||||
122
.github/workflows/dependency-updates.yml
vendored
Normal file
122
.github/workflows/dependency-updates.yml
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
name: System Dependency Updates
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Weekly on Sunday at midnight
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
|
||||
jobs:
|
||||
update-dependencies:
|
||||
name: Rebuild with Latest Dependencies
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build with latest dependencies
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
tags: aprs:latest
|
||||
load: true
|
||||
no-cache: true
|
||||
pull: true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: aprs:latest
|
||||
format: "sarif"
|
||||
output: "trivy-results.sarif"
|
||||
severity: "CRITICAL,HIGH"
|
||||
timeout: "10m"
|
||||
scanners: "vuln"
|
||||
|
||||
- name: Upload Trivy scan results
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
if: always()
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
category: 'Dependency Update Scan'
|
||||
|
||||
- name: Generate vulnerability summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "# System Dependency Update Report" > dep-update-report.md
|
||||
echo "## Date: $(date -u +'%Y-%m-%d')" >> dep-update-report.md
|
||||
echo "" >> dep-update-report.md
|
||||
|
||||
echo "### OS Package Vulnerabilities" >> dep-update-report.md
|
||||
echo "```" >> dep-update-report.md
|
||||
trivy image --severity HIGH,CRITICAL --no-progress aprs:latest >> dep-update-report.md
|
||||
echo "```" >> dep-update-report.md
|
||||
|
||||
- name: Upload vulnerability summary
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dependency-update-report
|
||||
path: dep-update-report.md
|
||||
retention-days: 30
|
||||
|
||||
- name: Check if base image is outdated
|
||||
id: check_base_image
|
||||
run: |
|
||||
# Extract current Debian version from Dockerfile
|
||||
CURRENT_VERSION=$(grep 'DEBIAN_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"')
|
||||
echo "Current Debian version: $CURRENT_VERSION"
|
||||
|
||||
# Get latest version from Docker Hub
|
||||
LATEST_VERSION=$(curl -s https://registry.hub.docker.com/v2/repositories/library/debian/tags | \
|
||||
grep -o '"name":"[^"]*-slim"' | grep bullseye | sort -r | head -1 | cut -d'"' -f4)
|
||||
echo "Latest Debian version: $LATEST_VERSION"
|
||||
|
||||
if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ] && [ ! -z "$LATEST_VERSION" ]; then
|
||||
echo "Base image is outdated. Current: $CURRENT_VERSION, Latest: $LATEST_VERSION"
|
||||
echo "outdated=true" >> $GITHUB_OUTPUT
|
||||
echo "latest_version=$LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Base image is up to date or couldn't determine latest version"
|
||||
echo "outdated=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Create PR for base image update
|
||||
if: steps.check_base_image.outputs.outdated == 'true'
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "chore: update Debian base image to ${{ steps.check_base_image.outputs.latest_version }}"
|
||||
title: "Update Debian base image to ${{ steps.check_base_image.outputs.latest_version }}"
|
||||
body: |
|
||||
This PR updates the Debian base image to the latest version:
|
||||
- Current: `${{ env.CURRENT_VERSION }}`
|
||||
- Latest: `${{ steps.check_base_image.outputs.latest_version }}`
|
||||
|
||||
This update includes the latest security patches and system dependencies.
|
||||
|
||||
*This PR was automatically created by the dependency updates workflow.*
|
||||
branch: update-base-image
|
||||
base: main
|
||||
delete-branch: true
|
||||
add-paths: |
|
||||
Dockerfile
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Dependency Update Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- ✅ Image rebuilt with latest dependencies" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- 📊 Vulnerability scan completed" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "${{ steps.check_base_image.outputs.outdated }}" == "true" ]; then
|
||||
echo "- 🔄 Base image update PR created" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- ✓ Base image is up to date" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
21
Dockerfile
21
Dockerfile
|
|
@ -16,7 +16,7 @@ RUN apt-get update -y && \
|
|||
apt-get upgrade -y && \
|
||||
apt-get install -y build-essential git && \
|
||||
apt-get clean && \
|
||||
rm -f /var/lib/apt/lists/*_*
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# prepare build dir
|
||||
WORKDIR /app
|
||||
|
|
@ -73,10 +73,10 @@ RUN apt-get update -y && \
|
|||
ca-certificates \
|
||||
tini && \
|
||||
apt-get clean && \
|
||||
rm -f /var/lib/apt/lists/*_* && \
|
||||
rm -rf /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 && \
|
||||
groupadd -g 1000 aprs && \
|
||||
useradd -r -g aprs -u 1000 -s /bin/false -M aprs && \
|
||||
# Remove setuid and setgid permissions
|
||||
find / -perm /6000 -type f -exec chmod a-s {} \; || true && \
|
||||
# Secure system configurations
|
||||
|
|
@ -113,18 +113,19 @@ ENV MIX_ENV="prod" \
|
|||
PHX_SERVER=true
|
||||
|
||||
# Only copy the final release from the build stage
|
||||
COPY --from=builder --chown=${USER_ID}:${GROUP_ID} /app/_build/${MIX_ENV}/rel/aprs ./
|
||||
COPY --from=builder --chown=1000:1000 /app/_build/${MIX_ENV}/rel/aprs ./
|
||||
|
||||
USER ${USER_ID}
|
||||
USER 1000
|
||||
|
||||
# Ensure the server binary is executable
|
||||
RUN chmod +x /app/bin/server
|
||||
|
||||
# Use tini as init process to handle signals and zombie processes
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
# If using an environment that doesn't automatically reap zombie processes, it is
|
||||
# advised to add an init process such as tini via `apt-get install`
|
||||
# above and adding an entrypoint. See https://github.com/krallin/tini for details
|
||||
# ENTRYPOINT ["/tini", "--"]
|
||||
|
||||
# Add specific capabilities needed by the app
|
||||
CMD ["/app/bin/server"]
|
||||
CMD /app/bin/server
|
||||
|
||||
# Add security-related metadata
|
||||
LABEL org.opencontainers.image.vendor="APRS.me" \
|
||||
|
|
|
|||
98
SYSTEM_UPDATES.md
Normal file
98
SYSTEM_UPDATES.md
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# System Dependency Updates for APRS.me Docker Image
|
||||
|
||||
This document outlines strategies for keeping system dependencies updated in the APRS.me Docker image without modifying the existing Dockerfile, which could potentially break the build process.
|
||||
|
||||
## Current Challenges
|
||||
|
||||
The Dockerfile builds successfully but maintaining up-to-date system dependencies is important for security. Standard approaches like adding `unattended-upgrades` or using `--security` flags with `apt-get` have proven problematic in our build environment.
|
||||
|
||||
## Recommended Approaches
|
||||
|
||||
### 1. Regular Rebuilds
|
||||
|
||||
The most reliable way to keep system dependencies updated is through regular rebuilds:
|
||||
|
||||
```bash
|
||||
# Rebuild the Docker image with latest base image and dependencies
|
||||
docker build --no-cache --pull -t aprs:latest .
|
||||
```
|
||||
|
||||
Key flags:
|
||||
- `--no-cache`: Forces all layers to be rebuilt, including running `apt-get update` and `apt-get upgrade`
|
||||
- `--pull`: Ensures the latest version of the base image is used
|
||||
|
||||
### 2. Base Image Updates
|
||||
|
||||
Periodically update the base image version in the Dockerfile:
|
||||
|
||||
```diff
|
||||
- ARG DEBIAN_VERSION=bullseye-20250520-slim
|
||||
+ ARG DEBIAN_VERSION=bullseye-20250615-slim
|
||||
```
|
||||
|
||||
The Debian team regularly releases updated images with security patches.
|
||||
|
||||
### 3. CI/CD Integration
|
||||
|
||||
Automate the update process:
|
||||
|
||||
1. Set up a weekly GitHub Actions workflow to:
|
||||
- Build the image with `--no-cache --pull`
|
||||
- Run security scans with Trivy
|
||||
- Create a PR if updates are needed
|
||||
|
||||
2. Include base image version checks:
|
||||
```yaml
|
||||
- name: Check for newer base image
|
||||
run: |
|
||||
# Logic to check for newer base image versions
|
||||
# Create PR if newer version available
|
||||
```
|
||||
|
||||
### 4. Security Scanning
|
||||
|
||||
Regularly scan for vulnerabilities:
|
||||
|
||||
```bash
|
||||
# Install Trivy
|
||||
brew install aquasecurity/trivy/trivy # macOS
|
||||
# or appropriate command for your OS
|
||||
|
||||
# Scan the image
|
||||
trivy image aprs:latest
|
||||
```
|
||||
|
||||
### 5. Manual Update Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# update-deps.sh
|
||||
|
||||
# Pull latest base image
|
||||
docker pull debian:$(grep 'DEBIAN_VERSION=' Dockerfile | cut -d'=' -f2 | tr -d '"')
|
||||
|
||||
# Rebuild with latest dependencies
|
||||
docker build --no-cache --pull -t aprs:latest .
|
||||
|
||||
# Scan for vulnerabilities
|
||||
if command -v trivy &> /dev/null; then
|
||||
trivy image aprs:latest
|
||||
fi
|
||||
|
||||
echo "Image rebuilt with latest dependencies"
|
||||
```
|
||||
|
||||
## Deployment Strategy
|
||||
|
||||
1. Rebuild images at least weekly
|
||||
2. Deploy updated images after testing
|
||||
3. Monitor security advisories for critical updates
|
||||
4. Perform out-of-band updates for critical CVEs
|
||||
|
||||
## Monitoring
|
||||
|
||||
1. Set up alerts for high/critical vulnerabilities
|
||||
2. Subscribe to security mailing lists for Debian and key packages
|
||||
3. Use image scanning in your container registry
|
||||
|
||||
By following these approaches, you can maintain up-to-date system dependencies without modifying the Dockerfile in ways that might break the build process.
|
||||
64
scripts/update-deps.sh
Executable file
64
scripts/update-deps.sh
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/bash
|
||||
# update-deps.sh - Simple script to rebuild Docker image with latest dependencies
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}=== APRS.me Dependency Update Tool ===${NC}"
|
||||
|
||||
# Check if Docker is installed and running
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}Error: Docker is not installed or not in PATH${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Docker daemon is running
|
||||
if ! docker info &> /dev/null; then
|
||||
echo -e "${RED}Error: Docker daemon is not running${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get current versions from Dockerfile for reference
|
||||
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 '"')
|
||||
|
||||
echo -e "${GREEN}Current versions:${NC}"
|
||||
echo " Elixir: $ELIXIR_VERSION"
|
||||
echo " OTP: $OTP_VERSION"
|
||||
echo " Debian: $DEBIAN_VERSION"
|
||||
|
||||
# Pull the latest base image to ensure we have fresh packages
|
||||
echo -e "${YELLOW}Pulling latest base image...${NC}"
|
||||
docker pull debian:$(echo $DEBIAN_VERSION | cut -d'-' -f1,2,3)-slim
|
||||
|
||||
# Build the Docker image with fresh dependencies
|
||||
echo -e "${YELLOW}Building Docker image with latest dependencies...${NC}"
|
||||
echo "This may take several minutes..."
|
||||
docker build --no-cache --pull -t aprs:latest .
|
||||
|
||||
# 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. 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}=== Dependency 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"
|
||||
Loading…
Add table
Reference in a new issue