Skip to content

Commit

Permalink
derive default OIDC issuer from current tenant
Browse files Browse the repository at this point in the history
Signed-off-by: Brian DeHamer <bdehamer@github.com>
  • Loading branch information
bdehamer committed Aug 16, 2024
1 parent 279e891 commit fa6cc53
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 23 deletions.
1 change: 1 addition & 0 deletions packages/attest/RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### 1.4.0

- Add new `headers` parameter to the `attest` and `attestProvenance` functions.
- Update `buildSLSAProvenancePredicate`/`attestProvenance` to automatically derive default OIDC issuer URL from current execution context.

### 1.3.1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
"workflow": {
"path": ".github/workflows/main.yml",
"ref": "main",
"repository": "https://github.com/owner/repo",
"repository": "https://foo.ghe.com/owner/repo",
},
},
"internalParameters": {
Expand All @@ -25,16 +25,16 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
"digest": {
"gitCommit": "babca52ab0c93ae16539e5923cb0d7403b9a093b",
},
"uri": "git+https://github.com/owner/repo@refs/heads/main",
"uri": "git+https://foo.ghe.com/owner/repo@refs/heads/main",
},
],
},
"runDetails": {
"builder": {
"id": "https://github.com/owner/workflows/.github/workflows/publish.yml@main",
"id": "https://foo.ghe.com/owner/workflows/.github/workflows/publish.yml@main",
},
"metadata": {
"invocationId": "https://github.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
"invocationId": "https://foo.ghe.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
},
},
},
Expand Down
25 changes: 10 additions & 15 deletions packages/attest/__tests__/provenance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {attestProvenance, buildSLSAProvenancePredicate} from '../src/provenance'

describe('provenance functions', () => {
const originalEnv = process.env
const issuer = 'https://example.com'
const issuer = 'https://token.actions.foo.ghe.com'
const audience = 'nobody'
const jwksPath = '/.well-known/jwks.json'
const tokenPath = '/token'
Expand Down Expand Up @@ -38,7 +38,7 @@ describe('provenance functions', () => {
...originalEnv,
ACTIONS_ID_TOKEN_REQUEST_URL: `${issuer}${tokenPath}?`,
ACTIONS_ID_TOKEN_REQUEST_TOKEN: 'token',
GITHUB_SERVER_URL: 'https://github.com',
GITHUB_SERVER_URL: 'https://foo.ghe.com',
GITHUB_REPOSITORY: claims.repository
}

Expand Down Expand Up @@ -68,7 +68,7 @@ describe('provenance functions', () => {

describe('buildSLSAProvenancePredicate', () => {
it('returns a provenance hydrated from an OIDC token', async () => {
const predicate = await buildSLSAProvenancePredicate(issuer)
const predicate = await buildSLSAProvenancePredicate()
expect(predicate).toMatchSnapshot()
})
})
Expand Down Expand Up @@ -96,9 +96,9 @@ describe('provenance functions', () => {
})

describe('when using the github Sigstore instance', () => {
const {fulcioURL, tsaServerURL} = signingEndpoints('github')

beforeEach(async () => {
const {fulcioURL, tsaServerURL} = signingEndpoints('github')

// Mock Sigstore
await mockFulcio({baseURL: fulcioURL, strict: false})
await mockTSA({baseURL: tsaServerURL})
Expand All @@ -118,8 +118,7 @@ describe('provenance functions', () => {
subjectName,
subjectDigest,
token: 'token',
sigstore: 'github',
issuer
sigstore: 'github'
})

expect(attestation).toBeDefined()
Expand All @@ -146,8 +145,7 @@ describe('provenance functions', () => {
const attestation = await attestProvenance({
subjectName,
subjectDigest,
token: 'token',
issuer
token: 'token'
})

expect(attestation).toBeDefined()
Expand Down Expand Up @@ -183,8 +181,7 @@ describe('provenance functions', () => {
subjectName,
subjectDigest,
token: 'token',
sigstore: 'public-good',
issuer
sigstore: 'public-good'
})

expect(attestation).toBeDefined()
Expand All @@ -211,8 +208,7 @@ describe('provenance functions', () => {
const attestation = await attestProvenance({
subjectName,
subjectDigest,
token: 'token',
issuer
token: 'token'
})

expect(attestation).toBeDefined()
Expand All @@ -238,8 +234,7 @@ describe('provenance functions', () => {
subjectDigest,
token: 'token',
sigstore: 'public-good',
skipWrite: true,
issuer
skipWrite: true
})

expect(attestation).toBeDefined()
Expand Down
26 changes: 25 additions & 1 deletion packages/attest/src/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import * as jose from 'jose'

const OIDC_AUDIENCE = 'nobody'

const VALID_SERVER_URLS = [
'https://github.com',
new RegExp('^https://[a-z0-9-]+\\.ghe\\.com$')
] as const

const REQUIRED_CLAIMS = [
'iss',
'ref',
Expand All @@ -25,7 +30,8 @@ type OIDCConfig = {
jwks_uri: string
}

export const getIDTokenClaims = async (issuer: string): Promise<ClaimSet> => {
export const getIDTokenClaims = async (issuer?: string): Promise<ClaimSet> => {
issuer = issuer || getIssuer()
try {
const token = await getIDToken(OIDC_AUDIENCE)
const claims = await decodeOIDCToken(token, issuer)
Expand Down Expand Up @@ -82,3 +88,21 @@ function assertClaimSet(claims: jose.JWTPayload): asserts claims is ClaimSet {
throw new Error(`Missing claims: ${missingClaims.join(', ')}`)
}
}

// Derive the current OIDC issuer based on the server URL
function getIssuer(): string {
const serverURL = process.env.GITHUB_SERVER_URL || 'https://github.com'

// Ensure the server URL is a valid GitHub server URL
if (!VALID_SERVER_URLS.some(valid_url => serverURL.match(valid_url))) {
throw new Error(`Invalid server URL: ${serverURL}`)
}

let host = new URL(serverURL).hostname

if (host === 'github.com') {
host = 'githubusercontent.com'
}

return `https://token.actions.${host}`
}
4 changes: 1 addition & 3 deletions packages/attest/src/provenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import type {Attestation, Predicate} from './shared.types'
const SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1'
const GITHUB_BUILD_TYPE = 'https://actions.github.io/buildtypes/workflow/v1'

const DEFAULT_ISSUER = 'https://token.actions.githubusercontent.com'

export type AttestProvenanceOptions = Omit<
AttestOptions,
'predicate' | 'predicateType'
Expand All @@ -24,7 +22,7 @@ export type AttestProvenanceOptions = Omit<
* @returns The SLSA provenance predicate.
*/
export const buildSLSAProvenancePredicate = async (
issuer: string = DEFAULT_ISSUER
issuer?: string
): Promise<Predicate> => {
const serverURL = process.env.GITHUB_SERVER_URL
const claims = await getIDTokenClaims(issuer)
Expand Down

0 comments on commit fa6cc53

Please sign in to comment.