Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/scan-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}"
41 changes: 27 additions & 14 deletions docs/authentication-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 │
└────────────────────────────────────┘
Expand All @@ -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)
Expand All @@ -61,30 +72,32 @@ 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

### 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

---
Expand Down
109 changes: 68 additions & 41 deletions docs/setup-gitlab.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
10 changes: 5 additions & 5 deletions scanners/boostsecurityio/trivy-fs/tests.yaml
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 1 addition & 1 deletion scanners/boostsecurityio/trivy-image/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Loading