diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..76d08c6c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,79 @@ +version: 2 +updates: + # GitHub Actions updates + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + reviewers: + - "fuomag9" + labels: + - "dependencies" + - "github-actions" + - "security" + commit-message: + prefix: "ci" + include: "scope" + + # NPM dependencies updates + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + reviewers: + - "fuomag9" + labels: + - "dependencies" + - "npm" + commit-message: + prefix: "deps" + include: "scope" + # Group non-security updates + groups: + development-dependencies: + dependency-type: "development" + update-types: + - "minor" + - "patch" + production-dependencies: + dependency-type: "production" + update-types: + - "minor" + - "patch" + # Security updates always get their own PR + versioning-strategy: increase + + # Docker base images updates + - package-ecosystem: "docker" + directory: "/docker/web" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + reviewers: + - "fuomag9" + labels: + - "dependencies" + - "docker" + - "security" + commit-message: + prefix: "docker" + + - package-ecosystem: "docker" + directory: "/docker/caddy" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + reviewers: + - "fuomag9" + labels: + - "dependencies" + - "docker" + - "security" + commit-message: + prefix: "docker" diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 801047e8..1eaad3cc 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -11,6 +11,8 @@ on: branches: - main - develop + pull_request_target: + types: [labeled] workflow_dispatch: env: @@ -18,7 +20,36 @@ env: IMAGE_NAME: ${{ github.repository }} jobs: + # Security check for fork PRs + security-check: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + permissions: + pull-requests: read + outputs: + is_fork: ${{ steps.check.outputs.is_fork }} + steps: + - name: Check if PR is from fork + id: check + run: | + if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then + echo "is_fork=true" >> $GITHUB_OUTPUT + echo "::warning::This PR is from a fork. Builds from forks require manual approval for security." + else + echo "is_fork=false" >> $GITHUB_OUTPUT + fi + build-and-push: + needs: security-check + # Run on push/tag events (security-check is skipped but that's ok) + # For PRs, only run on non-fork PRs or manually approved fork PRs + if: | + always() && ( + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && needs.security-check.result == 'success' && needs.security-check.outputs.is_fork == 'false') || + (github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-build')) + ) runs-on: ubuntu-latest permissions: contents: read @@ -37,12 +68,15 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + # For pull_request_target, checkout the PR head + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request' && github.event_name != 'pull_request_target' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} @@ -60,17 +94,23 @@ jobs: type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - type=sha,prefix={{branch}}- + type=sha type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Docker image + id: build uses: docker/build-push-action@v5 with: context: ${{ matrix.context }} file: ${{ matrix.dockerfile }} - push: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' && github.event_name != 'pull_request_target' }} + load: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 + # Only specify platforms for push (multi-platform), not for load (single-platform only) + platforms: ${{ (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') && 'linux/amd64,linux/arm64' || '' }} + # SBOM and provenance create manifest lists, incompatible with load (PRs) + sbom: ${{ github.event_name != 'pull_request' && github.event_name != 'pull_request_target' }} + provenance: ${{ github.event_name != 'pull_request' && github.event_name != 'pull_request_target' }} diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..e8c2bea3 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,81 @@ +# Security Policy + +## Supported Versions + +We release patches for security vulnerabilities for the following versions: + +| Version | Supported | +| ------- | ------------------ | +| latest | :white_check_mark: | +| < 1.0 | :x: | + +## Reporting a Vulnerability + +If you discover a security vulnerability, please report it by: + +1. **DO NOT** open a public issue +2. Email the maintainers or use GitHub's private vulnerability reporting +3. Include detailed information about the vulnerability: + - Type of vulnerability + - Steps to reproduce + - Potential impact + - Suggested fix (if any) + +We will respond within 48 hours and provide regular updates on the fix progress. + +## Security Measures + +### Build Pipeline Security + +Our CI/CD pipeline implements multiple security layers: + +1. **Fork PR Protection**: Pull requests from forks require manual approval (via `safe-to-build` label) before builds run +2. **SBOM Generation**: Software Bill of Materials is generated for all builds +3. **Provenance Attestation**: Build provenance is recorded for supply chain security +4. **Limited Permissions**: Workflows use minimal required permissions +5. **No Push from PRs**: Pull requests only build images locally, never push to registry + +### Container Security + +- Multi-architecture support (amd64, arm64) +- Regular base image updates +- Minimal attack surface +- Non-root user execution where possible + +### Dependency Management + +- Automated dependency updates via Dependabot +- Security alerts enabled +- Regular security audits + +## Security Best Practices for Contributors + +When contributing: + +1. Never commit secrets, tokens, or credentials +2. Use environment variables for sensitive configuration +3. Keep dependencies up to date +4. Follow principle of least privilege +5. Validate and sanitize all user inputs +6. Use parameterized queries for database operations + +## Automated Security Checks + +Our repository includes: + +- **Dependabot** for dependency updates +- **GitHub Security Advisories** monitoring + +## Safe-to-Build Label + +For maintainers reviewing fork PRs: + +1. Review the PR code thoroughly for malicious content +2. Check for suspicious file modifications +3. Verify no secrets or credentials are exposed +4. Only add `safe-to-build` label if code is verified safe +5. Remove label immediately if concerns arise + +## Security Updates + +Security updates are prioritized and released as soon as possible. Subscribe to repository releases to stay informed.