docker updates
This commit is contained in:
parent
158ec1ec2b
commit
c5617b6e8d
4 changed files with 323 additions and 37 deletions
86
.github/DOCKER_SECURITY.md
vendored
Normal file
86
.github/DOCKER_SECURITY.md
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# Docker Security Strategy for APRS.me
|
||||
|
||||
This document outlines our approach to maintaining secure Docker images for the APRS.me application.
|
||||
|
||||
## System Dependencies Update Strategy
|
||||
|
||||
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
|
||||
|
||||
2. **Continuous Integration Checks**
|
||||
- Weekly automated builds via GitHub Actions
|
||||
- Security scanning on every build with Trivy
|
||||
- Detection of outdated dependencies with separate vulnerability reports
|
||||
- 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
|
||||
|
||||
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
|
||||
|
||||
## How Updates Are Applied
|
||||
|
||||
System dependencies are updated at multiple points:
|
||||
|
||||
1. **During Image Build**
|
||||
```dockerfile
|
||||
RUN apt-get update -y && \
|
||||
apt-get upgrade -y --security && \
|
||||
apt-get dist-upgrade -y
|
||||
```
|
||||
|
||||
2. **Automatically at Runtime**
|
||||
- The `unattended-upgrades` package applies security updates automatically
|
||||
- Configuration prioritizes official security updates
|
||||
|
||||
3. **Via Scheduled Rebuilds**
|
||||
- Weekly GitHub Actions workflow rebuilds the image with latest dependencies
|
||||
- Base image is pulled with `--pull` flag to ensure latest version
|
||||
|
||||
## Manual Update Process
|
||||
|
||||
To manually update the Docker image with the latest system dependencies:
|
||||
|
||||
1. Run the update script:
|
||||
```bash
|
||||
./scripts/update-docker-image.sh
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
3. Verify that tests pass with the updated image before deployment
|
||||
|
||||
## Security Monitoring
|
||||
|
||||
We continuously monitor for security issues through:
|
||||
|
||||
1. GitHub Security tab showing Trivy scan results
|
||||
2. Weekly automated security scans
|
||||
3. Dependency updates via Dependabot
|
||||
4. Container registry vulnerability scanning
|
||||
|
||||
## 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)
|
||||
151
.github/workflows/docker-security-scan.yml
vendored
151
.github/workflows/docker-security-scan.yml
vendored
|
|
@ -2,22 +2,22 @@ name: Docker Security Scan
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- 'Dockerfile'
|
||||
- '.github/workflows/docker-security-scan.yml'
|
||||
- 'mix.exs'
|
||||
- 'mix.lock'
|
||||
- "Dockerfile"
|
||||
- ".github/workflows/docker-security-scan.yml"
|
||||
- "mix.exs"
|
||||
- "mix.lock"
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- 'Dockerfile'
|
||||
- '.github/workflows/docker-security-scan.yml'
|
||||
- 'mix.exs'
|
||||
- 'mix.lock'
|
||||
- "Dockerfile"
|
||||
- ".github/workflows/docker-security-scan.yml"
|
||||
- "mix.exs"
|
||||
- "mix.lock"
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Run weekly on Sundays at midnight
|
||||
workflow_dispatch: # Allow manual triggers
|
||||
- cron: "0 0 * * 0" # Run weekly on Sundays at midnight
|
||||
workflow_dispatch: # Allow manual triggers
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
|
|
@ -33,29 +33,132 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build Docker image
|
||||
run: docker build -t aprs:${{ github.sha }} .
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
tags: aprs:${{ github.sha }}
|
||||
load: 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:${{ github.sha }}
|
||||
format: 'sarif'
|
||||
output: 'trivy-results.sarif'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
timeout: '10m'
|
||||
scanners: 'vuln,config,secret'
|
||||
format: "sarif"
|
||||
output: "trivy-results.sarif"
|
||||
severity: "CRITICAL,HIGH"
|
||||
timeout: "10m"
|
||||
scanners: "vuln,config,secret"
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
if: always()
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
category: 'Trivy Scan'
|
||||
sarif_file: "trivy-results.sarif"
|
||||
category: "Trivy Scan"
|
||||
|
||||
- name: Check for outdated base images
|
||||
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"
|
||||
vuln-type: "os"
|
||||
|
||||
- name: Check 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"
|
||||
vuln-type: "library"
|
||||
|
||||
- name: Upload outdated dependencies report
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: dependency-reports
|
||||
path: |
|
||||
outdated-dependencies.txt
|
||||
outdated-app-dependencies.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
|
||||
with:
|
||||
image-ref: aprs:${{ github.sha }}
|
||||
format: "table"
|
||||
output: "misconfig-results.txt"
|
||||
exit-code: "0"
|
||||
scanners: "config"
|
||||
|
||||
- name: Upload misconfiguration report
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: misconfig-report
|
||||
path: misconfig-results.txt
|
||||
retention-days: 7
|
||||
|
||||
- name: Generate comprehensive security report
|
||||
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
|
||||
|
||||
- name: Upload comprehensive security report
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: security-report
|
||||
path: security-report.md
|
||||
retention-days: 14
|
||||
|
||||
- name: Fail on CRITICAL vulnerabilities
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
echo "Critical vulnerabilities found in the Docker image."
|
||||
echo "Please review the scan results in the Security tab."
|
||||
exit 1
|
||||
if grep -q "CRITICAL" trivy-results.sarif; then
|
||||
echo "Critical vulnerabilities found in the Docker image."
|
||||
echo "Please review the scan results in the Security tab."
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
37
Dockerfile
37
Dockerfile
|
|
@ -11,9 +11,10 @@ ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
|
|||
|
||||
FROM ${BUILDER_IMAGE} AS builder
|
||||
|
||||
# install build dependencies
|
||||
# install build dependencies with full security updates
|
||||
RUN apt-get update -y && \
|
||||
apt-get upgrade -y && \
|
||||
apt-get upgrade -y --security && \
|
||||
apt-get dist-upgrade -y && \
|
||||
apt-get install -y build-essential git && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
|
@ -64,19 +65,31 @@ FROM ${RUNNER_IMAGE}
|
|||
# Install security updates and required packages
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update -y && \
|
||||
apt-get upgrade -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 install -y --no-install-recommends \
|
||||
libstdc++6 \
|
||||
openssl \
|
||||
libncurses5 \
|
||||
locales \
|
||||
ca-certificates \
|
||||
tini && \
|
||||
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 && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
# Create a non-root user and group with specific ID
|
||||
groupadd -g 1000 aprs && \
|
||||
useradd -r -g aprs -u 1000 -s /bin/false -M aprs && \
|
||||
groupadd -g ${GROUP_ID} aprs && \
|
||||
useradd -r -g aprs -u ${USER_ID} -s /bin/false -M aprs && \
|
||||
# Remove setuid and setgid permissions
|
||||
find / -perm /6000 -type f -exec chmod a-s {} \; || true && \
|
||||
# Secure system configurations
|
||||
|
|
@ -113,20 +126,18 @@ ENV MIX_ENV="prod" \
|
|||
PHX_SERVER=true
|
||||
|
||||
# Only copy the final release from the build stage
|
||||
COPY --from=builder --chown=1000:1000 /app/_build/${MIX_ENV}/rel/aprs ./
|
||||
COPY --from=builder --chown=${USER_ID}:${GROUP_ID} /app/_build/${MIX_ENV}/rel/aprs ./
|
||||
|
||||
USER 1000
|
||||
USER ${USER_ID}
|
||||
|
||||
# Ensure the server binary is executable
|
||||
RUN chmod +x /app/bin/server
|
||||
|
||||
# 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", "--"]
|
||||
# Use tini as init process to handle signals and zombie processes
|
||||
ENTRYPOINT ["/usr/bin/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" \
|
||||
|
|
|
|||
86
scripts/update-docker-image.sh
Executable file
86
scripts/update-docker-image.sh
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
#!/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
|
||||
|
||||
# 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 Docker Image Update Tool ===${NC}"
|
||||
echo "This script will update your Docker image with the latest dependencies"
|
||||
|
||||
# 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
|
||||
|
||||
# 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}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Current base image:${NC} $BASE_IMAGE"
|
||||
|
||||
# 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 "${YELLOW}Building Docker image with latest dependencies...${NC}"
|
||||
docker build --no-cache --pull -t aprs:latest .
|
||||
|
||||
echo -e "${GREEN}Running security scan on updated image...${NC}"
|
||||
if command -v trivy &> /dev/null; then
|
||||
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"
|
||||
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
|
||||
|
||||
# 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
|
||||
fi
|
||||
Loading…
Add table
Reference in a new issue