diff --git a/.github/workflows/scan-test.yml b/.github/workflows/scan-test.yml index 1a35e9cf..94dfcd87 100644 --- a/.github/workflows/scan-test.yml +++ b/.github/workflows/scan-test.yml @@ -67,3 +67,24 @@ jobs: } registry-repo: "${{ github.repository_owner }}/${{ github.event.repository.name }}" base-ref: "${{ github.base_ref }}" + + gitlab-ci: + name: Gitlab-CI + runs-on: ubuntu-latest + steps: + - name: Checkout scanner registry + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Need full history to detect changes + - name: Run Tests + uses: boostsecurityio/scan-test-action@53e2f687ab93ac5d150b88abd7341b72f6fbf384 + with: + provider: gitlab-ci + provider-config: | + { + "trigger_token": "${{ secrets.BOOST_SCAN_RUNNER_GITLAB_TRIGGER_TOKEN }}", + "api_token": "${{ secrets.BOOST_SCAN_RUNNER_GITLAB_READ_TOKEN }}", + "project_id": "boostsecurityio/scan-test-runner-gitlab-ci" + } + registry-repo: "${{ github.repository_owner }}/${{ github.event.repository.name }}" + base-ref: "${{ github.base_ref }}" diff --git a/docs/authentication-strategy.md b/docs/authentication-strategy.md index 392003a9..fd0bd2c1 100644 --- a/docs/authentication-strategy.md +++ b/docs/authentication-strategy.md @@ -8,12 +8,12 @@ Migration from long-lived user tokens to short-lived OAuth2/OIDC tokens across a ## Authentication Solution -| Platform | Auth Method | Token Lifetime | User-Independent | -|------------------|---------------------------|----------------|------------------| -| **GitHub** | GitHub App | 1 hour | ✅ | -| **GitLab** | OAuth2 Application | 2 hours | ✅ | -| **Azure DevOps** | OIDC (Federated Identity) | ~1 hour | ✅ | -| **Bitbucket** | OAuth2 Consumer | 2 hours | ✅ | +| Platform | Auth Method | Token Lifetime | User-Independent | +|------------------|--------------------------------------|-----------------------|------------------| +| **GitHub** | GitHub App | 1 hour | ✅ | +| **GitLab** | Trigger Token + Read Token (2-token) | Trigger: ∞, Read: 1yr | ⚠️ Read token | +| **Azure DevOps** | OIDC (Federated Identity) | ~1 hour | ✅ | +| **Bitbucket** | OAuth2 Consumer | 2 hours | ✅ | ### Architecture Flow @@ -25,7 +25,7 @@ GitHub Actions Workflow Triggered ┌────────────────────────────────────┐ │ Token Generation (in GH Actions) │ │ - GitHub: Official Action │ -│ - GitLab: OAuth2 API call │ +│ - GitLab: Stored tokens (2-token) │ │ - Azure: OIDC (no secrets) │ │ - Bitbucket: OAuth2 API call │ └────────────────────────────────────┘ @@ -39,6 +39,17 @@ Trigger test pipelines on each platform Tokens expire automatically ``` +### GitLab Two-Token Approach + +GitLab does not support OAuth2 client credentials flow. We use two purpose-specific tokens: + +| Token | Purpose | Scope | +|----------------------------|-------------------|---------------------------------| +| **Pipeline Trigger Token** | Trigger pipelines | Can only trigger, no API access | +| **Project Access Token** | Poll status | `read_api` only, Guest role | + +This provides least-privilege access - neither token alone can both trigger and read. + ### Key Security Improvements - ✅ **Short-lived tokens** - Auto-expire in 1-2 hours (vs. indefinite) @@ -61,6 +72,8 @@ Tokens expire automatically **OIDC for GitHub API** - OIDC is for external services authenticating to GitHub, not for GitHub Actions triggering other GitHub workflows; GitHub Apps are the correct solution. +**GitLab OAuth2 Client Credentials** - GitLab does not support OAuth2 client credentials flow for machine-to-machine authentication; two-token approach (trigger + read) provides equivalent security with least-privilege separation. + --- ## Implementation Requirements @@ -68,23 +81,23 @@ Tokens expire automatically ### One-Time Setup (per platform) 1. **GitHub**: Register GitHub App with `contents: read`, `actions: write` permissions -2. **GitLab**: Create OAuth2 Application with `api` scope +2. **GitLab**: Create Pipeline Trigger Token + Project Access Token (`read_api`, Guest role) 3. **Azure DevOps**: Register Microsoft Entra ID application, add federated credential for GitHub Actions OIDC, grant Build (Read & Execute) to Azure DevOps project 4. **Bitbucket**: Create OAuth2 Consumer with `pipeline`, `pipeline:write`, `repository` scopes ### Secrets Configuration -Store client credentials in GitHub Actions repository secrets: -- `GH_APP_ID`, `GH_APP_PRIVATE_KEY` -- `GITLAB_CLIENT_ID`, `GITLAB_CLIENT_SECRET` -- `AZURE_TENANT_ID`, `AZURE_CLIENT_ID` (no client secret - uses OIDC; no subscription needed for Azure DevOps API) -- `BITBUCKET_CLIENT_ID`, `BITBUCKET_CLIENT_SECRET` +Store credentials in GitHub Actions repository secrets: +- `BOOST_SCAN_RUNNER_GITHUB_APP_ID`, `BOOST_SCAN_RUNNER_GITHUB_APP_PRIVATE_KEY` +- `BOOST_SCAN_RUNNER_GITLAB_TRIGGER_TOKEN`, `BOOST_SCAN_RUNNER_GITLAB_READ_TOKEN` +- `BOOST_SCAN_RUNNER_ADO_TENANT_ID`, `BOOST_SCAN_RUNNER_ADO_CLIENT_ID` (no client secret - uses OIDC) +- `BOOST_SCAN_RUNNER_BITBUCKET_CLIENT_ID`, `BOOST_SCAN_RUNNER_BITBUCKET_CLIENT_SECRET` ### Code Changes - **GitHub Actions workflow**: Add token generation steps (GitHub App action, OAuth API calls, azure/login with OIDC) - **GitHub Actions permissions**: Add `id-token: write` permission for Azure OIDC -- **test-action CLI**: Accept `--{platform}-token` arguments instead of client credentials +- **test-action CLI**: Accept `--{platform}-token` arguments (GitLab: `--gitlab-trigger-token`, `--gitlab-read-token`) - **Providers**: Use provided tokens directly instead of generating them --- diff --git a/docs/setup-gitlab.md b/docs/setup-gitlab.md index ab21bfee..0648384c 100644 --- a/docs/setup-gitlab.md +++ b/docs/setup-gitlab.md @@ -1,5 +1,14 @@ # GitLab Test Runner Setup +GitLab does not support OAuth2 client credentials flow. Instead, we use a **two-token approach** for least-privilege access: + +| Token | Purpose | Permissions | +|----------------------------|----------------------|------------------------------| +| **Pipeline Trigger Token** | Trigger pipelines | Trigger only (no API access) | +| **Project Access Token** | Poll pipeline status | `read_api` scope, Guest role | + +--- + ## 1. Create Test Runner Repository 1. Create a new project named `scan-test-runner-gitlab-ci` in your GitLab group @@ -8,75 +17,93 @@ --- -## 2. Create OAuth2 Application +## 2. Create Pipeline Trigger Token + +1. Navigate to your project: + **Project → Settings → CI/CD → Pipeline trigger tokens** + + Or: `https://gitlab.com/{GROUP}/{PROJECT}/-/settings/ci_cd#js-pipeline-triggers` + +2. Click **Add new token** + +3. Enter a description: `Scanner Registry Test Orchestrator` + +4. Click **Create pipeline trigger token** + +5. Copy the **trigger token** - it's only shown once! + +6. Ensure the ** CI/CD > Variables > Minimum role to use pipeline variables ** is set to **Developer** + +--- + +## 3. Create Project Access Token (for polling) -1. Navigate to your group settings: - **Group → Settings → Applications** +1. Navigate to your project: + **Project → Settings → Access tokens** - Or for self-hosted: `https://{GITLAB_HOST}/groups/{GROUP}/-/settings/applications` + Or: `https://gitlab.com/{GROUP}/{PROJECT}/-/settings/access_tokens` -2. Click **Add new application** +2. Click **Add new token** -3. Configure the application: +3. Configure the token: - | Field | Value | - |------------------|------------------------------------------------------| - | **Name** | `BoostSecurity.io Scan Test Runner` | - | **Redirect URI** | `http://localhost` (not used for client credentials) | - | **Confidential** | ✅ Checked | - | **Scopes** | ✅ `api` | + | Field | Value | + |---------------------|------------------------------------------------| + | **Token name** | `Scanner Registry Status Poller` | + | **Expiration date** | Set to 1 year (maximum), add rotation reminder | + | **Role** | **Guest** (minimal) | + | **Scopes** | ✅ `read_api` only | -4. Click **Save application** +4. Click **Create project access token** -5. Note the **Application ID** and **Secret** - the secret is only shown once! +5. Copy the **token** - it's only shown once! --- -## 3. Configure Secrets on Scanner Registry Repository +## 4. Configure Secrets on Scanner Registry Repository Navigate to the scanner registry repository (GitHub): **Settings → Secrets and variables → Actions → New repository secret** -| Secret Name | Value | -|------------------------------------------|----------------------------| -| `BOOST_SCAN_RUNNER_GITLAB_CLIENT_ID` | Application ID from step 2 | -| `BOOST_SCAN_RUNNER_GITLAB_CLIENT_SECRET` | Secret from step 2 | +| Secret Name | Value | +|-----------------------------------------|------------------------------------| +| `BOOST_SCAN_RUNNER_GITLAB_TRIGGER_TOKEN` | Pipeline trigger token from step 2 | +| `BOOST_SCAN_RUNNER_GITLAB_READ_TOKEN` | Project access token from step 3 | --- -## 4. Usage in GitHub Actions Workflow +## 5. Usage in GitHub Actions Workflow ```yaml -- name: Generate GitLab OAuth Token - id: gitlab-token +- name: Run test-action run: | - response=$(curl -s -X POST "https://gitlab.com/oauth/token" \ - -d "grant_type=client_credentials" \ - -d "client_id=${{ secrets.GITLAB_CLIENT_ID }}" \ - -d "client_secret=${{ secrets.GITLAB_CLIENT_SECRET }}") + use "${{ secrets.BOOST_SCAN_RUNNER_GITLAB_TRIGGER_TOKEN }}" \ + use "${{ secrets.BOOST_SCAN_RUNNER_GITLAB_READ_TOKEN }}" +``` - token=$(echo "$response" | jq -r '.access_token') - echo "token=$token" >> $GITHUB_OUTPUT - echo "::add-mask::$token" +--- -- name: Run test-action - ... -``` +## 6. Token Details + +| Token | Lifetime | Scope | Rotation | +|-------------------|---------------|------------------------|--------------------------------| +| **Trigger Token** | No expiration | Trigger pipelines only | Manual (revoke if compromised) | +| **Read Token** | 1 year max | `read_api` (read-only) | Annual rotation required | --- -## 5. Token Details +## 7. Security Notes -| Property | Value | -|--------------|--------------------------------------------------| -| **Lifetime** | 2 hours | -| **Refresh** | New token generated per workflow run | -| **Scope** | `api` (required for pipeline trigger and status) | +- **Trigger token** can only start pipelines - cannot read data or modify anything +- **Read token** has Guest role with `read_api` - cannot trigger or modify anything +- Neither token can access code, settings, or other projects +- Separation ensures compromise of one token limits blast radius --- ## References -- [GitLab OAuth2 Provider](https://docs.gitlab.com/ee/integration/oauth_provider.html) -- [GitLab OAuth2 API](https://docs.gitlab.com/ee/api/oauth2.html#client-credentials-flow) -- [GitLab Pipelines API](https://docs.gitlab.com/ee/api/pipelines.html) +- [Pipeline Trigger Tokens](https://docs.gitlab.com/ee/ci/triggers/) +- [Project Access Tokens](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html) +- [GitLab API Authentication](https://docs.gitlab.com/ee/api/rest/#authentication) +- [Pipelines API](https://docs.gitlab.com/ee/api/pipelines.html) diff --git a/scanners/boostsecurityio/trivy-fs/tests.yaml b/scanners/boostsecurityio/trivy-fs/tests.yaml index d19c65d5..239eb64e 100644 --- a/scanners/boostsecurityio/trivy-fs/tests.yaml +++ b/scanners/boostsecurityio/trivy-fs/tests.yaml @@ -1,12 +1,12 @@ version: "1.0" tests: - - name: "gitleaks" - type: "source-code" - source: - url: "git@github.com:gitleaks/gitleaks.git" - ref: "v8.15.2" - name: "osv-scanner" type: "source-code" source: url: "git@github.com:google/osv-scanner.git" ref: "main" + - name: "gitleaks" + type: "source-code" + source: + url: "git@github.com:gitleaks/gitleaks.git" + ref: "v8.15.2" diff --git a/scanners/boostsecurityio/trivy-image/tests.yaml b/scanners/boostsecurityio/trivy-image/tests.yaml index 73e729ce..68b3c391 100644 --- a/scanners/boostsecurityio/trivy-image/tests.yaml +++ b/scanners/boostsecurityio/trivy-image/tests.yaml @@ -6,5 +6,5 @@ tests: url: "https://github.com/martin-boost-dev/boost-poc-registry-testing-trivy" ref: "main" scan_paths: - - "rclone" - "osv-scanner" + - "rclone"