diff --git a/.devcontainer/docker-env/devcontainer.json b/.devcontainer/docker-env/devcontainer.json new file mode 100644 index 0000000..5a027f8 --- /dev/null +++ b/.devcontainer/docker-env/devcontainer.json @@ -0,0 +1,23 @@ +{ + "name": "JF cli, docker, linting", + "image": "mcr.microsoft.com/devcontainers/base:jammy", + "features": { + "ghcr.io/devcontainers-extra/features/jfrog-cli:1": { + "version": "latest" + }, + "ghcr.io/devcontainers-community/npm-features/prettier:1": { + "version": "latest" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "moby": true, + "azureDnsAutoDetection": true, + "installDockerBuildx": true, + "installDockerComposeSwitch": true, + "version": "latest", + "dockerDashComposeVersion": "v2" + }, + "ghcr.io/devcontainers-extra/features/shfmt:1": { + "version": "latest" + } + } +} diff --git a/.devcontainer/go-env/devcontainer.json b/.devcontainer/go-env/devcontainer.json new file mode 100644 index 0000000..f4bcb79 --- /dev/null +++ b/.devcontainer/go-env/devcontainer.json @@ -0,0 +1,18 @@ +{ + "name": "JF cli, Go, linting", + "image": "mcr.microsoft.com/devcontainers/base:jammy", + "features": { + "ghcr.io/devcontainers-extra/features/jfrog-cli:1": { + "version": "latest" + }, + "ghcr.io/devcontainers/features/go:1": { + "version": "latest" + }, + "ghcr.io/devcontainers-community/npm-features/prettier:1": { + "version": "latest" + }, + "ghcr.io/devcontainers-extra/features/shfmt:1": { + "version": "latest" + } + } +} diff --git a/.frogbot-config.yml b/.frogbot-config.yml index 4132f78..53aca8e 100644 --- a/.frogbot-config.yml +++ b/.frogbot-config.yml @@ -1,16 +1,16 @@ # Directories should be scanned for vulnerabilities projects: - - workingDirectory: "examples/java-app" - tool: "gradle" - - workingDirectory: "examples/python-app" - tool: "pip" - - workingDirectory: "examples/node-app" - tool: "npm" + - workingDirectory: 'examples/java-app' + tool: 'gradle' + - workingDirectory: 'examples/python-app' + tool: 'pip' + - workingDirectory: 'examples/node-app' + tool: 'npm' # Exclude root directory scanning since it's not an application excludeDirectories: - - "scripts/" - - "stats/" - - "venvdir/" - - ".github/" - - "docs/" + - 'scripts/' + - 'stats/' + - 'venvdir/' + - '.github/' + - 'docs/' diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json new file mode 100644 index 0000000..ae1d201 --- /dev/null +++ b/.github/linters/.jscpd.json @@ -0,0 +1,6 @@ +{ + "threshold": 10.0, + "reporters": ["console"], + "absolute": true, + "blame": true +} diff --git a/.github/workflows/calculate-public-usage.yml b/.github/workflows/calculate-public-usage.yml index d7a98c4..d2c1502 100644 --- a/.github/workflows/calculate-public-usage.yml +++ b/.github/workflows/calculate-public-usage.yml @@ -1,83 +1,83 @@ -# .github/workflows/calculate-public-usage.yml -name: Calculate Usage - -on: - schedule: - - cron: '0 3 * * *' - workflow_dispatch: - -permissions: - contents: write - -jobs: - count_repos: - runs-on: ubuntu-latest - - steps: - - name: Check out repository - uses: actions/checkout@v4 - with: - persist-credentials: true - - - name: Count unique repos referencing JF_URL - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Capture current date in YYYY-MM-DD (UTC) - current_date=$(date -u +%Y-%m-%d) - - # Run the GitHub Code Search API, extract repo names, dedupe, and count - count=$( - curl -sSL \ - -H "Authorization: Bearer $GH_TOKEN" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/search/code?q=%22JF_URL%3A+https%3A%2F%2Fartifacts-artefacts.devops.cloud-nuage.canada.ca%22+language%3AYAML+path%3A.github%2Fworkflows%2F" \ - | jq -r '.items[].repository.full_name' \ - | sort -u \ - | wc -l - ) - - echo "Date: $current_date" - echo "Unique repos found: $count" - - # Ensure data directory exists - mkdir -p stats - - # Define the CSV file path - file="stats/usage.csv" - - # If the file doesn't exist, add a header - if [ ! -f "$file" ]; then - echo "date,count" > "$file" - fi - - # Append today's date and the count - echo "$current_date,$count" >> "$file" - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Install plotting dependencies - run: | - pip install pandas matplotlib - - - name: Generate XKCD-style usage graph - run: | - python3 scripts/generate_graph.py - - - name: Commit and push updated data file + graph - run: | - # Configure git author - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - # Stage CSV and PNG - git add stats/usage.csv stats/usage.png - - # Commit changes if there are any - git commit -m "Add usage for $(date -u +%Y-%m-%d): ${{ steps.count_repos.outputs.count }}" || echo "No changes to commit" - - # Push back to the default branch - git push \ No newline at end of file +# .github/workflows/calculate-public-usage.yml +name: Calculate Usage + +on: + schedule: + - cron: '0 3 * * *' + workflow_dispatch: + +permissions: + contents: write + +jobs: + count_repos: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + persist-credentials: true + + - name: Count unique repos referencing JF_URL + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Capture current date in YYYY-MM-DD (UTC) + current_date=$(date -u +%Y-%m-%d) + + # Run the GitHub Code Search API, extract repo names, dedupe, and count + count=$( + curl -sSL \ + -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/search/code?q=%22JF_URL%3A+https%3A%2F%2Fartifacts-artefacts.devops.cloud-nuage.canada.ca%22+language%3AYAML+path%3A.github%2Fworkflows%2F" \ + | jq -r '.items[].repository.full_name' \ + | sort -u \ + | wc -l + ) + + echo "Date: $current_date" + echo "Unique repos found: $count" + + # Ensure data directory exists + mkdir -p stats + + # Define the CSV file path + file="stats/usage.csv" + + # If the file doesn't exist, add a header + if [ ! -f "$file" ]; then + echo "date,count" > "$file" + fi + + # Append today's date and the count + echo "$current_date,$count" >> "$file" + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install plotting dependencies + run: | + pip install pandas matplotlib + + - name: Generate XKCD-style usage graph + run: | + python3 scripts/generate_graph.py + + - name: Commit and push updated data file + graph + run: | + # Configure git author + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Stage CSV and PNG + git add stats/usage.csv stats/usage.png + + # Commit changes if there are any + git commit -m "Add usage for $(date -u +%Y-%m-%d): ${{ steps.count_repos.outputs.count }}" || echo "No changes to commit" + + # Push back to the default branch + git push diff --git a/.github/workflows/cc-code-scanning.yml b/.github/workflows/cc-code-scanning.yml new file mode 100644 index 0000000..56bbca6 --- /dev/null +++ b/.github/workflows/cc-code-scanning.yml @@ -0,0 +1,40 @@ +--- +name: Compliance Code Linting + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' + +permissions: read-all + +jobs: + build: + name: Full Code Scan + runs-on: ubuntu-latest + + permissions: + contents: read + packages: read + statuses: write # To report GitHub Actions status checks + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - name: Lint Codebase + uses: super-linter/super-linter/slim@v7.4.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # To report GitHub Actions status checks + VALIDATE_ALL_CODEBASE: true # Lint only changed files + SUPPRESS_POSSUM: true + LINTER_RULES_PATH: / + IGNORE_GITIGNORED_FILES: true + NATURAL_LANGUAGE_CONFIG_FILE: '.textlintrc.json' + PYTHON_BLACK_CONFIG_FILE: 'pyproject.toml' + PYTHON_ISORT_CONFIG_FILE: 'pyproject.toml' + PYTHON_RUFF_CONFIG_FILE: 'pyproject.toml' + PYTHON_PYINK_CONFIG_FILE: 'pyproject.toml' + VALIDATE_PYTHON_PYINK: false diff --git a/.github/workflows/java-app.yml b/.github/workflows/java-app.yml index 57e2401..5b22807 100644 --- a/.github/workflows/java-app.yml +++ b/.github/workflows/java-app.yml @@ -5,13 +5,13 @@ on: push: branches: [main] paths: - - "examples/java-app/**" - - ".github/workflows/**" + - 'examples/java-app/**' + - '.github/workflows/**' pull_request: branches: [main] paths: - - "examples/java-app/**" - - ".github/workflows/**" + - 'examples/java-app/**' + - '.github/workflows/**' env: REGISTRY: artifacts-artefacts.devops.cloud-nuage.canada.ca @@ -117,11 +117,11 @@ jobs: JF_URL: https://${{ env.REGISTRY }} JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JF_GIT_USE_GITHUB_ENVIRONMENT: "false" - JF_INCLUDE_ALL_VULNERABILITIES: "true" - JF_ENABLE_SAST: "true" - JF_ENABLE_SECRETS: "true" - JF_ENABLE_IAC: "true" + JF_GIT_USE_GITHUB_ENVIRONMENT: 'false' + JF_INCLUDE_ALL_VULNERABILITIES: 'true' + JF_ENABLE_SAST: 'true' + JF_ENABLE_SECRETS: 'true' + JF_ENABLE_IAC: 'true' summary: needs: [build-and-scan, cleanup] runs-on: ubuntu-latest diff --git a/.github/workflows/node-app.yml b/.github/workflows/node-app.yml index 01ac9f8..01335e7 100644 --- a/.github/workflows/node-app.yml +++ b/.github/workflows/node-app.yml @@ -5,13 +5,13 @@ on: push: branches: [main] paths: - - "examples/node-app/**" - - ".github/workflows/**" + - 'examples/node-app/**' + - '.github/workflows/**' pull_request: branches: [main] paths: - - "examples/node-app/**" - - ".github/workflows/**" + - 'examples/node-app/**' + - '.github/workflows/**' env: REGISTRY: artifacts-artefacts.devops.cloud-nuage.canada.ca IMAGE_NAME: ssc-aurora-docker-local/node-app @@ -115,11 +115,11 @@ jobs: JF_URL: https://${{ env.REGISTRY }} JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JF_GIT_USE_GITHUB_ENVIRONMENT: "false" - JF_INCLUDE_ALL_VULNERABILITIES: "true" - JF_ENABLE_SAST: "true" - JF_ENABLE_SECRETS: "true" - JF_ENABLE_IAC: "true" + JF_GIT_USE_GITHUB_ENVIRONMENT: 'false' + JF_INCLUDE_ALL_VULNERABILITIES: 'true' + JF_ENABLE_SAST: 'true' + JF_ENABLE_SECRETS: 'true' + JF_ENABLE_IAC: 'true' summary: needs: [build-and-scan, cleanup] diff --git a/.github/workflows/pr-code-scanning.yml b/.github/workflows/pr-code-scanning.yml new file mode 100644 index 0000000..2319aff --- /dev/null +++ b/.github/workflows/pr-code-scanning.yml @@ -0,0 +1,42 @@ +--- +name: Code Linting + +on: + push: {} + pull_request: {} + workflow_dispatch: + +permissions: read-all + +jobs: + build: + name: Incoming Scan Code + runs-on: ubuntu-latest + + permissions: + contents: read + packages: read + statuses: write # To report GitHub Actions status checks + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - name: Lint Incoming Changes + uses: super-linter/super-linter/slim@v7.4.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # To report GitHub Actions status checks + VALIDATE_ALL_CODEBASE: false # Lint only changed files + VALIDATE_CHECKOV: false + VALIDATE_JSCPD: false + SUPPRESS_POSSUM: true + LINTER_RULES_PATH: / + IGNORE_GITIGNORED_FILES: true + NATURAL_LANGUAGE_CONFIG_FILE: '.textlintrc.json' + PYTHON_BLACK_CONFIG_FILE: 'pyproject.toml' + PYTHON_ISORT_CONFIG_FILE: 'pyproject.toml' + PYTHON_RUFF_CONFIG_FILE: 'pyproject.toml' + PYTHON_PYINK_CONFIG_FILE: 'pyproject.toml' + VALIDATE_PYTHON_PYINK: false diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 0d33a14..af2fa3b 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -5,13 +5,13 @@ on: push: branches: [main] paths: - - "examples/python-app/**" - - ".github/workflows/**" + - 'examples/python-app/**' + - '.github/workflows/**' pull_request: branches: [main] paths: - - "examples/python-app/**" - - ".github/workflows/**" + - 'examples/python-app/**' + - '.github/workflows/**' env: REGISTRY: artifacts-artefacts.devops.cloud-nuage.canada.ca IMAGE_NAME: ssc-aurora-docker-local/python-app @@ -115,11 +115,11 @@ jobs: JF_URL: https://${{ env.REGISTRY }} JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JF_GIT_USE_GITHUB_ENVIRONMENT: "false" - JF_INCLUDE_ALL_VULNERABILITIES: "true" - JF_ENABLE_SAST: "true" - JF_ENABLE_SECRETS: "true" - JF_ENABLE_IAC: "true" + JF_GIT_USE_GITHUB_ENVIRONMENT: 'false' + JF_INCLUDE_ALL_VULNERABILITIES: 'true' + JF_ENABLE_SAST: 'true' + JF_ENABLE_SECRETS: 'true' + JF_ENABLE_IAC: 'true' summary: needs: [build-and-scan, cleanup] diff --git a/.github/workflows/update-chainguard-digests.yml b/.github/workflows/update-chainguard-digests.yml index 87650fb..2d5d2ab 100644 --- a/.github/workflows/update-chainguard-digests.yml +++ b/.github/workflows/update-chainguard-digests.yml @@ -3,7 +3,7 @@ name: Update Chainguard Digests on: workflow_dispatch: schedule: - - cron: "0 0 * * 0" + - cron: '0 0 * * 0' jobs: update-digests: @@ -66,8 +66,8 @@ jobs: uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "Update Chainguard image digests" - title: "Update Chainguard image digests" + commit-message: 'Update Chainguard image digests' + title: 'Update Chainguard image digests' body: | Automated update of Chainguard base images to latest digests. diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..6de870e --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/.textlintrc.json b/.textlintrc.json new file mode 100644 index 0000000..589faae --- /dev/null +++ b/.textlintrc.json @@ -0,0 +1,32 @@ +{ + "plugins": {}, + "filters": {}, + "rules": { + "date-weekday-mismatch": true, + "footnote-order": true, + "no-curly-quotes": true, + "no-todo": false, + "period-in-header": true, + "editorconfig": true, + "spelling": { + "language": "en", + "skipPatterns": [ + "(http|https)://[^\\s]+", + "\\[.*?\\]\\([^)]+\\)", + "/repos/g", + "JavaScript", + "TypeScript", + "GitHub", + "ESLint", + "npm", + "Node\\.js", + "JSDoc", + "VisualStudioCode", + "SemVer" + ], + "wordDefinitionRegexp": "/[\\w']+/g", + "suggestCorrections": true + }, + "terminology": true + } +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 2538092..2722308 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,7 +6,7 @@ Shared Services Canada follows the [CNCF Code of Conduct](https://github.com/cnc Please review before contributing issues, pull requests, or joining the GitHub organization. -______________________ +--- ## Code de conduite diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 743bb48..c80ee95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ If this is your first time contributing on GitHub, don't worry! Let us know if y **Do not post any security issues on the public repository!** See [SECURITY.md](SECURITY.md) -______________________ +--- ## Comment contribuer @@ -26,4 +26,4 @@ Si c'est la première fois que vous contribuez à GitHub, ne vous en faites pas! ### Sécurité -**Ne publiez aucun problème de sécurité sur le dépôt publique!** Voir [SECURITY.md](SECURITY.md) \ No newline at end of file +**Ne publiez aucun problème de sécurité sur le dépôt publique!** Voir [SECURITY.md](SECURITY.md) diff --git a/LICENSE.md b/LICENSE.md index c06b229..a093a54 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -19,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 354c0c2..4baa662 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,8 @@ # GC Secure Artifacts -![Java](https://github.com/gccloudone/artifacts-artefacts/actions/workflows/java-app.yml/badge.svg) -![Python](https://github.com/gccloudone/artifacts-artefacts/actions/workflows/python-app.yml/badge.svg) -![Node.js](https://github.com/gccloudone/artifacts-artefacts/actions/workflows/node-app.yml/badge.svg) - Shared Services Canada (SSC) has initiated a one-year pilot project of [GC Secure Artifacts](https://artifacts-artefacts.devops.cloud-nuage.canada.ca), a secure, scalable, centralized artifact management service underpinned by the JFrog Enterprise+ platform, self-hosted on the GC Private Cloud. -- https://artifacts-artefacts.devops.cloud-nuage.canada.ca +- [https://artifacts-artefacts.devops.cloud-nuage.canada.ca](https://artifacts-artefacts.devops.cloud-nuage.canada.ca) Available to all federal departments and agencies, this service represents SSC's initial venture into a unified DevSecOps service, designed to bolster software supply chain security and expedite delivery across the Government of Canada. Outcomes of this pioneering initiative will contribute to the decision-making process regarding the project's long-term sustainability and potential expansion. @@ -14,9 +10,27 @@ For access to GC Secure Artifacts, please completing the following GC Form: - [GC Secure Artifacts Onboarding Form](https://forms-formulaires.alpha.canada.ca/en/id/cmavw8p4l006eyi01cx1qtxxd) +## Continual Compliance Report + + +| Security Control | Continual Validation Status | +|---|---| +|SD-3|[![Code Linting](../../actions/workflows/cc-code-scanning.yml/badge.svg)](../../actions/workflows/cc-code-scanning.yml)| + + +## Build Statuses + +![Java](../../actions/workflows/java-app.yml/badge.svg) +![Python](../../actions/workflows/python-app.yml/badge.svg) +![Node.js](../../actions/workflows/node-app.yml/badge.svg) + ## Background -This service supports the Government of Canada's efforts to reduce duplication. While departments currently maintain independent Artifactory instances, a centralized approach provides potential access to advanced features. These features, such as JFrog Advanced Security and/or Runtime, could be cost-prohibitive for individual departments but become feasible through pooled resources. By offering a unified secure service, we can improve cybersecurity and delivery speed while creating potential for significant savings. +This service supports the Government of Canada's efforts to reduce duplication. While departments currently maintain +independent Artifactory instances, a centralized approach provides potential access to advanced features. These features, +such as JFrog Advanced Security and/or Runtime, could be cost-prohibitive for individual departments but become feasible +through pooled resources. By offering a unified secure service, we can improve cybersecurity and delivery speed while +creating potential for significant savings. GC Secure Artifacts, by consolidating efforts across departments, aims to: @@ -29,11 +43,11 @@ For further insight into the GC Secure Artifacts initiative, below is an present - [GC Secure Artifacts Presentation](https://gccloudone.blob.core.windows.net/artifacts-artefacts/unclassified/gc-secure-artifacts.pptx) +## GitHub Public Adoption -## Github Public Adoption Usage Over Time -As the days go by, you can see how the count evolves… here is our github public adoption(Search Results). +As the days go by, you can see how the count evolves… here is our GitHub public adoption(Search Results). ## Features @@ -43,10 +57,10 @@ JFrog Platform (Enterprise Plus) offers an enterprise-grade solution for publish The platform provides: -* A centralized registry for internal builds and deployments -* Advanced access controls and repository segmentation -* Built-in vulnerability scanning (via JFrog Xray) -* Support for multiple package types (Docker, Maven, NPM, Nuget, Helm, etc.) +- A centralized registry for internal builds and deployments +- Advanced access controls and repository segmentation +- Built-in vulnerability scanning (via JFrog Xray) +- Support for multiple package types (Docker, Maven, NPM, Nuget, Helm, etc.) ### Chainguard Secure Images @@ -56,13 +70,13 @@ These images have been set up as a pull through from Chainguard's registry to ou The container images which are available: -* Python -* OpenJDK (JDK & JRE) -* PowerShell -* Node.js -* ASP.NET Runtime -* .NET Runtime -* .NET SDK +- Python +- OpenJDK (JDK & JRE) +- PowerShell +- Node.js +- ASP.NET Runtime +- .NET Runtime +- .NET SDK ## Implementation Examples @@ -71,6 +85,7 @@ This repository includes practical examples demonstrating how to integrate JFrog ### Quick Start **Replace standard base images with Chainguard equivalents:** + ```dockerfile # Python FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/python:3.13.3 @@ -83,6 +98,7 @@ FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/s ``` **Add JFrog CLI to your GitHub Actions:** + ```yaml - name: Setup JFrog CLI uses: jfrog/setup-jfrog-cli@v4 @@ -93,6 +109,7 @@ FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/s ``` **Enable security scanning:** + ```yaml - name: Scan Dependencies run: jf audit --format=simple @@ -105,7 +122,7 @@ FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/s The repository contains complete implementation examples: -``` +```text examples/ ├── java-app/ # Java application with JFrog + Chainguard integration ├── python-app/ # Python application example @@ -114,6 +131,7 @@ examples/ ``` Each example demonstrates: + - JFrog CLI dependency scanning - Chainguard image integration - Container vulnerability scanning @@ -123,12 +141,14 @@ Each example demonstrates: ### Repository Setup Configure these secrets in your GitHub repository: + - `JFROG_USERNAME`: Your Artifactory username - `JFROG_JWT_TOKEN`: Your Artifactory access token ### Local Development **Configure JFrog CLI:** + ```bash jf config add --url=https://artifacts-artefacts.devops.cloud-nuage.canada.ca jf rt ping @@ -136,6 +156,7 @@ jf audit ``` **Access Chainguard Images:** + ```bash docker login artifacts-artefacts.devops.cloud-nuage.canada.ca docker pull artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/python:3.13.3 @@ -158,6 +179,6 @@ Feel free to share your feedback by emailing us at [devops.artifacts-artefacts.d ## Resources -* [Chainguard](https://chainguard.dev/) -* [JFrog Devops Platform](https://jfrog.com/) -* [jFrog Academy](https://academy.jfrog.com/) +- [Chainguard](https://chainguard.dev/) +- [JFrog Devops Platform](https://jfrog.com/) +- [jFrog Academy](https://academy.jfrog.com/) diff --git a/SECURITY.md b/SECURITY.md index ac07327..ac8a9c8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -13,18 +13,22 @@ If you discover a vulnerability, please report it privately by email to: ### Security Policy #### Supported Versions + All container images are automatically scanned and maintained with the latest security patches. #### Reporting Vulnerabilities + Contact: `devops.artifacts-artefacts.devops@ssc-spc.gc.ca` #### Automated Security Measures + - JFrog Xray scanning on all builds - Frogbot analysis on all pull requests - Weekly Chainguard digest updates - Automated remediation of known vulnerabilities #### Security Standards + This repository adheres to Government of Canada security standards and is integrated with GC Secure Artifacts for enterprise-grade monitoring and compliance. --- @@ -38,16 +42,20 @@ Si vous découvrez une vulnérabilité, veuillez la signaler de façon confident ### Politique de sécurité #### Versions prises en charge + Toutes les images de conteneur font l’objet d’analyses automatiques et sont maintenues à jour avec les derniers correctifs de sécurité. #### Signalement des vulnérabilités + Veuillez communiquer avec nous à l’adresse suivante : `devops.artifacts-artefacts.devops@ssc-spc.gc.ca` #### Mécanismes de sécurité automatisés + - Analyse avec JFrog Xray pour chaque compilation - Vérification avec Frogbot sur chaque demande de tirage (pull request) - Mises à jour hebdomadaires des digests Chainguard - Correction automatique des vulnérabilités connues #### Normes de sécurité + Ce dépôt respecte les normes de sécurité du gouvernement du Canada et est intégré à GC Secure Artifacts afin d’assurer une surveillance de niveau entreprise et la conformité aux exigences de sécurité. diff --git a/docs/artifactory-standards.md b/docs/artifactory-standards.md index 89a1c54..10636ce 100644 --- a/docs/artifactory-standards.md +++ b/docs/artifactory-standards.md @@ -1,101 +1,101 @@ -# Artifactory Standards - -Standards for the management of the [GC Secure Artifacts](https://artifacts-artefacts.devops.cloud-nuage.canada.ca/) Artifactory instance. - -## Background - -Shared Services Canada (SSC) is implementing a 1-year experiment of operating a Government of Canada centralized Artifactory instance. The results of this experiment will feed into a decision of whether to renew/continue the project. - -## Purpose - -This document outlines the policies and procedures of operating the centralized Artifactory instance. - -## Policies and Procedures - -### Configuration Management - -1. **Configuration as Code** - - All configuration of Artifactory and/or X-Ray shall be performed using the Configuration as Code [repository](https://github.com/gccloudone/artifacts-configuration) hosted in the GC Cloud One organization on GitHub, to the extent supported by the JFrog Terraform modules. - -### User Management - -2. **Centralized Authentication** - - Users will authenticate using Single Sign-On (SSO) from the 163ENT Entra ID tenant. - - - Users not already in the 163ENT tenant must request an invite [here](https://forms-formulaires.alpha.canada.ca/en/id/cmapffzfp00v9xb017pnmyb94) - -3. **Local Accounts** - - Local accounts will not be utilized, except for any break glass accounts. - -4. **Administrators** - - The number of “global administrators” should be limited to at-most 3 individuals. - -### Project Management - -5. **Project Eligibility** - - The following entities are eligible for a Project in Artifactory - - - Government of Canada Department or Agency - - Shared Services Canada (SSC) Service Line - -6. **Project Naming** - - Projects must be named with the entity’s name. - - Project Keys will use the entity’s official acronym (e.g., ssc, statcan) - - - SSC Service Lines shall be named `SSC – [Service Line]`. Keys are prefixed with `ssc-` - -7. **Project Ownership** - - Projects must have at least 2 administrators and should ideally have 3. - - - Projects with no active administrators are to be removed once identified. - - Project Administrators must notify by [email](mailto:devops.artifacts-artefacts.devops@ssc-spc.gc.ca) when an administrator departs their organization or when changing roles. If the total number of administrators will drop to 1, the Project Administrators must identify a new administrator. - -8. **Project Management** - - Project Administrators are responsible for managing all aspects of their project, including: - - - Repository management - - User permissions - - Retention periods - -9. **Project Support** - - Project Administrators are expected to be the “first-line” support for their organization, and will be the contact point for escalations to SSC. - -10. **Project Quotas** - - Projects should have a default quota for 200GB, expandable upon request. - -### Repository Management - -11. **Repository Naming** - - Repositories should follow the naming convention: `[org]-[group]-[project]-[type]-[locator]`, where: - - - [org] represents the owning entity (this is automatically prefixed) - - [group] represents the group within the owning entity (this is automatically prefixed for SSC Service Lines) - - [project] represents the project the repository belongs to (optional) - - [type] represents the repository type (e.g., docker, debian, helm) - - For OS distributions, `type` may be replaced by the OS name (e.g., ubuntu) - - [locator] represents the locator. Values are: - - remote - - local - - federated - - virtual (value is omitted in the repository name) - -12. **Common Repositories** - - SSC will configure common repositories for use by all projects to prevent duplication across all organisations. - - - Common repositories include: - - Operating System distributions (e.g., Debian, Ubuntu, RedHat Enterprise Linux) - - Common Community Repositories (e.g., Maven, PyPI, Docker Hub, Quay.io, Helm) - - Common repositories will follow a different naming convention than normal repositories, `[type]-[locator]` +# Artifactory Standards + +Standards for the management of the [GC Secure Artifacts](https://artifacts-artefacts.devops.cloud-nuage.canada.ca/) Artifactory instance. + +## Background + +Shared Services Canada (SSC) is implementing a 1-year experiment of operating a Government of Canada centralized Artifactory instance. The results of this experiment will feed into a decision of whether to renew/continue the project. + +## Purpose + +This document outlines the policies and procedures of operating the centralized Artifactory instance. + +## Policies and Procedures + +### Configuration Management + +1. **Configuration as Code** + + All configuration of Artifactory and/or X-Ray shall be performed using the Configuration as Code [repository](https://github.com/gccloudone/artifacts-configuration) hosted in the GC Cloud One organization on GitHub, to the extent supported by the JFrog Terraform modules. + +### User Management + +2. **Centralized Authentication** + + Users will authenticate using Single Sign-On (SSO) from the 163ENT Entra ID tenant. + + - Users not already in the 163ENT tenant must request an invite [here](https://forms-formulaires.alpha.canada.ca/en/id/cmapffzfp00v9xb017pnmyb94) + +3. **Local Accounts** + + Local accounts will not be utilized, except for any break glass accounts. + +4. **Administrators** + + The number of “global administrators” should be limited to at-most 3 individuals. + +### Project Management + +5. **Project Eligibility** + + The following entities are eligible for a Project in Artifactory + + - Government of Canada Department or Agency + - Shared Services Canada (SSC) Service Line + +6. **Project Naming** + + Projects must be named with the entity’s name. + + Project Keys will use the entity’s official acronym (e.g., ssc, statcan) + + - SSC Service Lines shall be named `SSC – [Service Line]`. Keys are prefixed with `ssc-` + +7. **Project Ownership** + + Projects must have at least 2 administrators and should ideally have 3. + + - Projects with no active administrators are to be removed once identified. + - Project Administrators must notify by [email](mailto:devops.artifacts-artefacts.devops@ssc-spc.gc.ca) when an administrator departs their organization or when changing roles. If the total number of administrators will drop to 1, the Project Administrators must identify a new administrator. + +8. **Project Management** + + Project Administrators are responsible for managing all aspects of their project, including: + + - Repository management + - User permissions + - Retention periods + +9. **Project Support** + + Project Administrators are expected to be the “first-line” support for their organization, and will be the contact point for escalations to SSC. + +10. **Project Quotas** + + Projects should have a default quota for 200GB, expandable upon request. + +### Repository Management + +11. **Repository Naming** + + Repositories should follow the naming convention: `[org]-[group]-[project]-[type]-[locator]`, where: + + - [org] represents the owning entity (this is automatically prefixed) + - [group] represents the group within the owning entity (this is automatically prefixed for SSC Service Lines) + - [project] represents the project the repository belongs to (optional) + - [type] represents the repository type (e.g., docker, debian, helm) + - For OS distributions, `type` may be replaced by the OS name (e.g., ubuntu) + - [locator] represents the locator. Values are: + - remote + - local + - federated + - virtual (value is omitted in the repository name) + +12. **Common Repositories** + + SSC will configure common repositories for use by all projects to prevent duplication across all organisations. + + - Common repositories include: + - Operating System distributions (e.g., Debian, Ubuntu, RedHat Enterprise Linux) + - Common Community Repositories (e.g., Maven, PyPI, Docker Hub, Quay.io, Helm) + - Common repositories will follow a different naming convention than normal repositories, `[type]-[locator]` diff --git a/docs/chainguard-images.md b/docs/chainguard-images.md index 2b9363d..dd36f59 100644 --- a/docs/chainguard-images.md +++ b/docs/chainguard-images.md @@ -1,44 +1,44 @@ -# Chainguard Images - -Chainguard offers a broad catalogue of minimal, hardened container base images with a strong emphasis on security. Regularly updated, these images come with integrated SBOMs (Software Bill of Materials) and are maintained to ensure they contain no known security vulnerabilities at the moment of release. - -## Background - -By selecting Chainguard images as your base images, you can significantly cut down on the number of CVEs in your container. This is because commonplace risk-prone components are either eliminated or replaced with safer alternatives. This aligns with the GoC’s cyber security strategy to reduce vulnerabilities in our software supply chain. - -Artifactory has been set up as a pull through from Chainguard's registry to our JFrog Artifactory instance and available to anyone from within the GC. In other words, Artifactory acts as a mirror for Chainguard images, enabling you to retrieve base images from a local trusted source instead of through the public internet. This not only enhances build speeds, but also sidesteps any potential rate limiting and/or network-related issues when pulling images. - -The container images which are available: - -* Python -* OpenJDK (JDK & JRE) -* PowerShell -* Node.js -* ASP.NET Runtime -* .NET Runtime -* .NET SDK - -## Purpose - -This document outlines the policies and procedures for working with Chainguard images. - -## Policies and Procedures - -### Using Chainguard Images - -Artifactory’s container registry is set up through a Docker repository named `docker-chainguard-remote`, available as a pull-through cache. - -To pull the Chainguard procured images, follow these steps: - -```sh -# Login to Artifactory -docker login artifacts-artefacts.devops.cloud-nuage.canada.ca - -# Pull Python -docker pull artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/python:3.13.3 - -# Pull Node -docker pull artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/node:23.11.0-slim -``` - -Utilizing the internal registry comes with the advantage of quicker and more reliable access if the image has been previously pulled. It will be served from Artifactory’s cache, and all downloads are automatically logged and scanned by JFrog XRay. +# Chainguard Images + +Chainguard offers a broad catalogue of minimal, hardened container base images with a strong emphasis on security. Regularly updated, these images come with integrated SBOMs (Software Bill of Materials) and are maintained to ensure they contain no known security vulnerabilities at the moment of release. + +## Background + +By selecting Chainguard images as your base images, you can significantly cut down on the number of CVEs in your container. This is because commonplace risk-prone components are either eliminated or replaced with safer alternatives. This aligns with the GoC’s cyber security strategy to reduce vulnerabilities in our software supply chain. + +Artifactory has been set up as a pull through from Chainguard's registry to our JFrog Artifactory instance and available to anyone from within the GC. In other words, Artifactory acts as a mirror for Chainguard images, enabling you to retrieve base images from a local trusted source instead of through the public internet. This not only enhances build speeds, but also sidesteps any potential rate limiting and/or network-related issues when pulling images. + +The container images which are available: + +- Python +- OpenJDK (JDK & JRE) +- PowerShell +- Node.js +- ASP.NET Runtime +- .NET Runtime +- .NET SDK + +## Purpose + +This document outlines the policies and procedures for working with Chainguard images. + +## Policies and Procedures + +### Using Chainguard Images + +Artifactory’s container registry is set up through a Docker repository named `docker-chainguard-remote`, available as a pull-through cache. + +To pull the Chainguard procured images, follow these steps: + +```sh +# Login to Artifactory +docker login artifacts-artefacts.devops.cloud-nuage.canada.ca + +# Pull Python +docker pull artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/python:3.13.3 + +# Pull Node +docker pull artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/node:23.11.0-slim +``` + +Utilizing the internal registry comes with the advantage of quicker and more reliable access if the image has been previously pulled. It will be served from Artifactory’s cache, and all downloads are automatically logged and scanned by JFrog XRay. diff --git a/docs/quickstart.md b/docs/quickstart.md index 2855031..a518ba3 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -12,11 +12,13 @@ Replace your current base image with a Chainguard equivalent: **Before:** + ```dockerfile FROM python:3.13-slim ``` **After:** + ```dockerfile FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/python:3.13.3 ``` @@ -35,6 +37,7 @@ FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/s ## Step 3: Add Security Scanning **Scan Dependencies:** + ```yaml - name: Scan Dependencies run: | @@ -44,6 +47,7 @@ FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/s ``` **Scan Container Images:** + ```yaml - name: Scan Container run: | @@ -65,13 +69,13 @@ frogbot: pull-requests: write security-events: write steps: - - uses: actions/checkout@v4 - - uses: jfrog/frogbot@v2 - env: - JF_URL: https://artifacts-artefacts.devops.cloud-nuage.canada.ca - JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} - JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JF_GIT_USE_GITHUB_ENVIRONMENT: "false" + - uses: actions/checkout@v4 + - uses: jfrog/frogbot@v2 + env: + JF_URL: https://artifacts-artefacts.devops.cloud-nuage.canada.ca + JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} + JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JF_GIT_USE_GITHUB_ENVIRONMENT: 'false' ``` ## Step 5: Add Cost Management @@ -84,17 +88,17 @@ cleanup: if: github.event_name == 'push' needs: [build-and-scan] steps: - - name: Setup JFrog CLI - uses: jfrog/setup-jfrog-cli@v4 - env: - JF_URL: https://artifacts-artefacts.devops.cloud-nuage.canada.ca - JF_USER: ${{ secrets.JFROG_USERNAME }} - JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} - - name: Cleanup Analysis - run: | - echo "Running automated cleanup to save storage costs..." - CLEANUP_COUNT=$(jf rt search "repo-name/*" --older-than=30d --count 2>/dev/null || echo "0") - echo "Found $CLEANUP_COUNT old images that could be cleaned up" + - name: Setup JFrog CLI + uses: jfrog/setup-jfrog-cli@v4 + env: + JF_URL: https://artifacts-artefacts.devops.cloud-nuage.canada.ca + JF_USER: ${{ secrets.JFROG_USERNAME }} + JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} + - name: Cleanup Analysis + run: | + echo "Running automated cleanup to save storage costs..." + CLEANUP_COUNT=$(jf rt search "repo-name/*" --older-than=30d --count 2>/dev/null || echo "0") + echo "Found $CLEANUP_COUNT old images that could be cleaned up" ``` ## Step 6: Push to JFrog Registry @@ -117,6 +121,7 @@ cleanup: Use these image paths in your Dockerfiles: **Java:** + ```dockerfile # JDK for building FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/jdk:openjdk-21 @@ -126,11 +131,13 @@ FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/s ``` **Python:** + ```dockerfile FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/python:3.13.3 ``` **Node.js:** + ```dockerfile FROM artifacts-artefacts.devops.cloud-nuage.canada.ca/docker-chainguard-remote/ssc-spc.gc.ca/node:24.1.0 ``` @@ -144,7 +151,7 @@ name: Update Chainguard Digests on: workflow_dispatch: schedule: - - cron: "0 0 * * 0" + - cron: '0 0 * * 0' jobs: update-digests: runs-on: ubuntu-latest @@ -152,19 +159,20 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@v4 - - name: Setup JFrog CLI - uses: jfrog/setup-jfrog-cli@v4 - env: - JF_URL: https://artifacts-artefacts.devops.cloud-nuage.canada.ca - JF_USER: ${{ secrets.JFROG_USERNAME }} - JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} + - uses: actions/checkout@v4 + - name: Setup JFrog CLI + uses: jfrog/setup-jfrog-cli@v4 + env: + JF_URL: https://artifacts-artefacts.devops.cloud-nuage.canada.ca + JF_USER: ${{ secrets.JFROG_USERNAME }} + JF_ACCESS_TOKEN: ${{ secrets.JFROG_JWT_TOKEN }} # Automatically updates Dockerfiles with latest secure digests ``` ## Local Development **Install JFrog CLI:** + ```bash # Configure CLI jf config add --url=https://artifacts-artefacts.devops.cloud-nuage.canada.ca @@ -180,6 +188,7 @@ jf audit --fix ``` **Pull Chainguard Images:** + ```bash # Login docker login artifacts-artefacts.devops.cloud-nuage.canada.ca @@ -208,6 +217,7 @@ git push origin main ## What You'll See **JFrog CLI Output:** + ``` Scanning dependencies for security issues... No issues found @@ -215,6 +225,7 @@ Developer tip: Run 'jf audit --fix' locally to auto-fix vulnerabilities ``` **Container Scan Results:** + ``` Scanning chainguard image... No vulnerable components were found @@ -222,6 +233,7 @@ Scan completed successfully ``` **Cost Management:** + ``` Running automated cleanup to save storage costs... Found 0 old images that could be cleaned up @@ -229,6 +241,7 @@ Cleanup saves storage costs and improves performance ``` **Build Summary:** + ``` Build 42 completed @@ -244,6 +257,7 @@ JFrog Developer Tools Used: ## Common Commands **Dependency Scanning:** + ```bash jf audit # Scan dependencies jf audit --fix # Get fix suggestions @@ -251,12 +265,14 @@ jf audit --format=json # JSON output ``` **Container Operations:** + ```bash jf docker scan image:tag # Scan container jf docker push image:tag # Push to registry ``` **Repository Management:** + ```bash jf rt search "repo/*" # Search artifacts jf rt delete "repo/*" --older-than=30d # Clean old artifacts @@ -265,6 +281,7 @@ jf rt delete "repo/*" --older-than=30d # Clean old artifacts ## Implementation Options **Single Dockerfile Approach:** + ```yaml - name: Build image run: | @@ -273,18 +290,19 @@ jf rt delete "repo/*" --older-than=30d # Clean old artifacts ``` **Multiple Variants (Optional):** + ```yaml strategy: matrix: dockerfile: [standard, chainguard] steps: -- name: Build image - run: | - IMAGE_TAG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-${{ matrix.dockerfile }} - docker build -f Dockerfile.${{ matrix.dockerfile }} -t $IMAGE_TAG . + - name: Build image + run: | + IMAGE_TAG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}-${{ matrix.dockerfile }} + docker build -f Dockerfile.${{ matrix.dockerfile }} -t $IMAGE_TAG . ``` -*Note: Matrix strategy is demonstrated in this repository but optional for your implementation.* +_Note: Matrix strategy is demonstrated in this repository but optional for your implementation._ ## Support diff --git a/examples/node-app/app.js b/examples/node-app/app.js index 6bd50a5..e14f2fe 100644 --- a/examples/node-app/app.js +++ b/examples/node-app/app.js @@ -1,19 +1,19 @@ -const http = require('http'); - -const server = http.createServer((req, res) => { - res.writeHead(200, {'Content-Type': 'application/json'}); - - const response = { - status: 'healthy', - service: 'node-api', - nodeVersion: process.version, - timestamp: new Date().toISOString() - }; - - res.end(JSON.stringify(response)); -}); - -const port = process.env.PORT || 8080; -server.listen(port, '0.0.0.0', () => { - console.log(`Server started on port ${port}`); -}); +const http = require('http'); + +const server = http.createServer((req, res) => { + res.writeHead(200, { 'Content-Type': 'application/json' }); + + const response = { + status: 'healthy', + service: 'node-api', + nodeVersion: process.version, + timestamp: new Date().toISOString(), + }; + + res.end(JSON.stringify(response)); +}); + +const port = process.env.PORT || 8080; +server.listen(port, '0.0.0.0', () => { + console.log(`Server started on port ${port}`); +});