Skip to content

Commit f7225ad

Browse files
authored
Merge pull request #265 from docker/dockerhub-oidc
build/bake: support Docker Hub OIDC registry identities
2 parents 9dc0afa + e4e2676 commit f7225ad

7 files changed

Lines changed: 303 additions & 1 deletion

File tree

.github/workflows/.test-bake.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,63 @@ jobs:
187187
const builderOutputs = JSON.parse(core.getInput('builder-outputs'));
188188
core.info(JSON.stringify(builderOutputs, null, 2));
189189
190+
bake-dockerhub-oidc:
191+
uses: ./.github/workflows/bake.yml
192+
if: ${{ github.event_name != 'pull_request' }}
193+
permissions:
194+
contents: read
195+
id-token: write
196+
with:
197+
job-name-prefix: bake-dockerhub-oidc
198+
context: test
199+
output: image
200+
push: true
201+
sbom: true
202+
set: |
203+
*.args.VERSION={{meta.version}}
204+
target: hello
205+
registry-identities: |
206+
- type: dockerhub
207+
registry: docker.io
208+
username: ${{ vars.DOCKERHUB_OIDC_USERNAME }}
209+
connection_id: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
210+
meta-images: |
211+
docker.io/dockereng/github-builder-test
212+
meta-tags: |
213+
type=raw,value=bake-ghbuilder-oidc-${{ github.run_id }}
214+
215+
bake-dockerhub-oidc-verify:
216+
uses: ./.github/workflows/verify.yml
217+
if: ${{ github.event_name != 'pull_request' }}
218+
needs:
219+
- bake-dockerhub-oidc
220+
permissions:
221+
contents: read
222+
id-token: write
223+
with:
224+
builder-outputs: ${{ toJSON(needs.bake-dockerhub-oidc.outputs) }}
225+
registry-identities: |
226+
- type: dockerhub
227+
registry: docker.io
228+
username: ${{ vars.DOCKERHUB_OIDC_USERNAME }}
229+
connection_id: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
230+
231+
bake-dockerhub-oidc-outputs:
232+
runs-on: ubuntu-24.04
233+
if: ${{ github.event_name != 'pull_request' }}
234+
needs:
235+
- bake-dockerhub-oidc
236+
steps:
237+
-
238+
name: Builder outputs
239+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
240+
env:
241+
INPUT_BUILDER-OUTPUTS: ${{ toJSON(needs.bake-dockerhub-oidc.outputs) }}
242+
with:
243+
script: |
244+
const builderOutputs = JSON.parse(core.getInput('builder-outputs'));
245+
core.info(JSON.stringify(builderOutputs, null, 2));
246+
190247
bake-dockerhub-scan:
191248
runs-on: ubuntu-24.04
192249
if: ${{ github.event_name != 'pull_request' }}

.github/workflows/.test-build.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,62 @@ jobs:
184184
const builderOutputs = JSON.parse(core.getInput('builder-outputs'));
185185
core.info(JSON.stringify(builderOutputs, null, 2));
186186
187+
build-dockerhub-oidc:
188+
uses: ./.github/workflows/build.yml
189+
if: ${{ github.event_name != 'pull_request' }}
190+
permissions:
191+
contents: read
192+
id-token: write
193+
with:
194+
job-name-prefix: build-dockerhub-oidc
195+
build-args: |
196+
VERSION={{meta.version}}
197+
file: test/hello.Dockerfile
198+
output: image
199+
push: true
200+
sbom: true
201+
registry-identities: |
202+
- type: dockerhub
203+
registry: docker.io
204+
username: ${{ vars.DOCKERHUB_OIDC_USERNAME }}
205+
connection_id: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
206+
meta-images: |
207+
docker.io/dockereng/github-builder-test
208+
meta-tags: |
209+
type=raw,value=build-ghbuilder-oidc-${{ github.run_id }}
210+
211+
build-dockerhub-oidc-verify:
212+
uses: ./.github/workflows/verify.yml
213+
if: ${{ github.event_name != 'pull_request' }}
214+
needs:
215+
- build-dockerhub-oidc
216+
permissions:
217+
contents: read
218+
id-token: write
219+
with:
220+
builder-outputs: ${{ toJSON(needs.build-dockerhub-oidc.outputs) }}
221+
registry-identities: |
222+
- type: dockerhub
223+
registry: docker.io
224+
username: ${{ vars.DOCKERHUB_OIDC_USERNAME }}
225+
connection_id: ${{ vars.DOCKERHUB_OIDC_CONNECTIONID }}
226+
227+
build-dockerhub-oidc-outputs:
228+
runs-on: ubuntu-24.04
229+
if: ${{ github.event_name != 'pull_request' }}
230+
needs:
231+
- build-dockerhub-oidc
232+
steps:
233+
-
234+
name: Builder outputs
235+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
236+
env:
237+
INPUT_BUILDER-OUTPUTS: ${{ toJSON(needs.build-dockerhub-oidc.outputs) }}
238+
with:
239+
script: |
240+
const builderOutputs = JSON.parse(core.getInput('builder-outputs'));
241+
core.info(JSON.stringify(builderOutputs, null, 2));
242+
187243
build-dockerhub-scan:
188244
runs-on: ubuntu-24.04
189245
if: ${{ github.event_name != 'pull_request' }}

.github/workflows/bake.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,16 @@ jobs:
10211021
- registry: ${{ needs.registry-identities.outputs.gcp-wif-registry }}
10221022
username: oauth2accesstoken
10231023
password: ${{ steps.gcp-wif-auth.outputs.access_token }}
1024+
-
1025+
name: Login to Docker Hub with OIDC
1026+
if: ${{ needs.registry-identities.outputs.dockerhub-oidc-enabled == 'true' }}
1027+
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
1028+
env:
1029+
DOCKERHUB_OIDC_CONNECTIONID: ${{ needs.registry-identities.outputs.dockerhub-oidc-connection-id }}
1030+
with:
1031+
registry-auth: |
1032+
- registry: ${{ needs.registry-identities.outputs.dockerhub-oidc-registry }}
1033+
username: ${{ needs.registry-identities.outputs.dockerhub-oidc-username }}
10241034
-
10251035
name: Login to registry
10261036
if: ${{ inputs.push && inputs.output == 'image' && env.REGISTRY_AUTHS_PRESENT == 'true' }}
@@ -1263,6 +1273,16 @@ jobs:
12631273
- registry: ${{ needs.registry-identities.outputs.gcp-wif-registry }}
12641274
username: oauth2accesstoken
12651275
password: ${{ steps.gcp-wif-auth.outputs.access_token }}
1276+
-
1277+
name: Login to Docker Hub with OIDC
1278+
if: ${{ inputs.push && inputs.output == 'image' && needs.registry-identities.outputs.dockerhub-oidc-enabled == 'true' }}
1279+
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
1280+
env:
1281+
DOCKERHUB_OIDC_CONNECTIONID: ${{ needs.registry-identities.outputs.dockerhub-oidc-connection-id }}
1282+
with:
1283+
registry-auth: |
1284+
- registry: ${{ needs.registry-identities.outputs.dockerhub-oidc-registry }}
1285+
username: ${{ needs.registry-identities.outputs.dockerhub-oidc-username }}
12661286
-
12671287
name: Login to registry
12681288
if: ${{ inputs.push && inputs.output == 'image' && env.REGISTRY_AUTHS_PRESENT == 'true' }}

.github/workflows/build.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,16 @@ jobs:
887887
- registry: ${{ needs.registry-identities.outputs.gcp-wif-registry }}
888888
username: oauth2accesstoken
889889
password: ${{ steps.gcp-wif-auth.outputs.access_token }}
890+
-
891+
name: Login to Docker Hub with OIDC
892+
if: ${{ needs.registry-identities.outputs.dockerhub-oidc-enabled == 'true' }}
893+
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
894+
env:
895+
DOCKERHUB_OIDC_CONNECTIONID: ${{ needs.registry-identities.outputs.dockerhub-oidc-connection-id }}
896+
with:
897+
registry-auth: |
898+
- registry: ${{ needs.registry-identities.outputs.dockerhub-oidc-registry }}
899+
username: ${{ needs.registry-identities.outputs.dockerhub-oidc-username }}
890900
-
891901
name: Login to registry
892902
if: ${{ inputs.push && inputs.output == 'image' && env.REGISTRY_AUTHS_PRESENT == 'true' }}
@@ -1125,6 +1135,16 @@ jobs:
11251135
- registry: ${{ needs.registry-identities.outputs.gcp-wif-registry }}
11261136
username: oauth2accesstoken
11271137
password: ${{ steps.gcp-wif-auth.outputs.access_token }}
1138+
-
1139+
name: Login to Docker Hub with OIDC
1140+
if: ${{ inputs.push && inputs.output == 'image' && needs.registry-identities.outputs.dockerhub-oidc-enabled == 'true' }}
1141+
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
1142+
env:
1143+
DOCKERHUB_OIDC_CONNECTIONID: ${{ needs.registry-identities.outputs.dockerhub-oidc-connection-id }}
1144+
with:
1145+
registry-auth: |
1146+
- registry: ${{ needs.registry-identities.outputs.dockerhub-oidc-registry }}
1147+
username: ${{ needs.registry-identities.outputs.dockerhub-oidc-username }}
11281148
-
11291149
name: Login to registry
11301150
if: ${{ inputs.push && inputs.output == 'image' && env.REGISTRY_AUTHS_PRESENT == 'true' }}

.github/workflows/setup-registry-identities.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ on:
3535
gcp-wif-project-id:
3636
description: "GCP project ID"
3737
value: ${{ jobs.setup-registry-identities.outputs.gcp-wif-project-id }}
38+
dockerhub-oidc-enabled:
39+
description: "Whether a Docker Hub OIDC registry identity was configured"
40+
value: ${{ jobs.setup-registry-identities.outputs.dockerhub-oidc-enabled }}
41+
dockerhub-oidc-registry:
42+
description: "Docker Hub registry hostname"
43+
value: ${{ jobs.setup-registry-identities.outputs.dockerhub-oidc-registry }}
44+
dockerhub-oidc-username:
45+
description: "Docker Hub username or organization to authenticate as"
46+
value: ${{ jobs.setup-registry-identities.outputs.dockerhub-oidc-username }}
47+
dockerhub-oidc-connection-id:
48+
description: "Docker Hub OIDC connection ID"
49+
value: ${{ jobs.setup-registry-identities.outputs.dockerhub-oidc-connection-id }}
3850

3951
env:
4052
RUNTIME_MODULE: "@docker/github-builder-runtime@0.94.0"
@@ -62,6 +74,10 @@ jobs:
6274
gcp-wif-workload-identity-provider: ${{ steps.validate.outputs.gcp-wif-workload-identity-provider }}
6375
gcp-wif-service-account: ${{ steps.validate.outputs.gcp-wif-service-account }}
6476
gcp-wif-project-id: ${{ steps.validate.outputs.gcp-wif-project-id }}
77+
dockerhub-oidc-enabled: ${{ steps.validate.outputs.dockerhub-oidc-enabled }}
78+
dockerhub-oidc-registry: ${{ steps.validate.outputs.dockerhub-oidc-registry }}
79+
dockerhub-oidc-username: ${{ steps.validate.outputs.dockerhub-oidc-username }}
80+
dockerhub-oidc-connection-id: ${{ steps.validate.outputs.dockerhub-oidc-connection-id }}
6581
steps:
6682
-
6783
name: Install dependencies
@@ -111,6 +127,10 @@ jobs:
111127
core.setOutput('gcp-wif-workload-identity-provider', '');
112128
core.setOutput('gcp-wif-service-account', '');
113129
core.setOutput('gcp-wif-project-id', '');
130+
core.setOutput('dockerhub-oidc-enabled', 'false');
131+
core.setOutput('dockerhub-oidc-registry', '');
132+
core.setOutput('dockerhub-oidc-username', '');
133+
core.setOutput('dockerhub-oidc-connection-id', '');
114134
};
115135
116136
const registryIdentities = core.getInput('registry-identities', {trimWhitespace: false});
@@ -142,6 +162,12 @@ jobs:
142162
}
143163
return value.trim();
144164
};
165+
const optionalString = (entry, key, path, defaultValue) => {
166+
if (!Object.prototype.hasOwnProperty.call(entry, key)) {
167+
return defaultValue;
168+
}
169+
return requireString(entry, key, path);
170+
};
145171
let parsed;
146172
try {
147173
parsed = yaml.load(registryIdentities);
@@ -161,6 +187,7 @@ jobs:
161187
162188
let awsEcr;
163189
let gcpWif;
190+
let dockerhubOidc;
164191
entries.forEach((entry, index) => {
165192
const path = `registry-identities[${index}]`;
166193
ensureObject(entry, path);
@@ -201,6 +228,23 @@ jobs:
201228
};
202229
break;
203230
}
231+
case 'dockerhub': {
232+
const allowedKeys = new Set(['type', 'registry', 'username', 'connection_id']);
233+
for (const key of Object.keys(entry)) {
234+
if (!allowedKeys.has(key)) {
235+
fail(`${path}.${key} is not supported for dockerhub`);
236+
}
237+
}
238+
if (dockerhubOidc) {
239+
fail('only one dockerhub registry identity is supported');
240+
}
241+
dockerhubOidc = {
242+
registry: optionalString(entry, 'registry', path, 'docker.io'),
243+
username: requireString(entry, 'username', path),
244+
connectionID: requireString(entry, 'connection_id', path)
245+
};
246+
break;
247+
}
204248
default:
205249
fail(`${path}.type has unsupported provider ${type}`);
206250
}
@@ -215,3 +259,7 @@ jobs:
215259
core.setOutput('gcp-wif-workload-identity-provider', gcpWif?.workloadIdentityProvider || '');
216260
core.setOutput('gcp-wif-service-account', gcpWif?.serviceAccount || '');
217261
core.setOutput('gcp-wif-project-id', gcpWif?.projectId || '');
262+
core.setOutput('dockerhub-oidc-enabled', dockerhubOidc ? 'true' : 'false');
263+
core.setOutput('dockerhub-oidc-registry', dockerhubOidc?.registry || '');
264+
core.setOutput('dockerhub-oidc-username', dockerhubOidc?.username || '');
265+
core.setOutput('dockerhub-oidc-connection-id', dockerhubOidc?.connectionID || '');

.github/workflows/verify.yml

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ on:
77
type: string
88
description: "JSON build outputs from Docker GitHub Builder reusable workflows"
99
required: true
10+
registry-identities:
11+
type: string
12+
description: "Keyless registry identity configuration as YAML objects"
13+
required: false
1014
secrets:
1115
registry-auths:
1216
description: "Registry authentication details as YAML objects"
@@ -26,8 +30,17 @@ env:
2630
NPM_CONFIG_FETCH_RETRIES: "5"
2731

2832
jobs:
33+
registry-identities:
34+
uses: ./.github/workflows/setup-registry-identities.yml
35+
with:
36+
registry-identities: ${{ inputs.registry-identities }}
37+
2938
verify:
3039
runs-on: ubuntu-24.04
40+
needs:
41+
- registry-identities
42+
env:
43+
REGISTRY_AUTHS_PRESENT: ${{ secrets.registry-auths != '' }}
3144
steps:
3245
-
3346
name: Extract builder outputs
@@ -103,9 +116,54 @@ jobs:
103116
104117
const cosign = new Cosign();
105118
await cosign.printVersion();
119+
-
120+
name: Configure AWS credentials
121+
if: ${{ steps.vars.outputs.signed == 'true' && steps.vars.outputs.output-type == 'image' && needs.registry-identities.outputs.aws-ecr-enabled == 'true' }}
122+
uses: aws-actions/configure-aws-credentials@517a711dbcd0e402f90c77e7e2f81e849156e31d # v6.2.2
123+
with:
124+
role-to-assume: ${{ needs.registry-identities.outputs.aws-ecr-role-to-assume }}
125+
aws-region: ${{ needs.registry-identities.outputs.aws-ecr-region }}
126+
-
127+
name: Login to Amazon ECR
128+
if: ${{ steps.vars.outputs.signed == 'true' && steps.vars.outputs.output-type == 'image' && needs.registry-identities.outputs.aws-ecr-enabled == 'true' }}
129+
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
130+
with:
131+
registry-auth: |
132+
- registry: ${{ needs.registry-identities.outputs.aws-ecr-registry }}
133+
-
134+
name: Authenticate to Google Cloud
135+
id: gcp-wif-auth
136+
if: ${{ steps.vars.outputs.signed == 'true' && steps.vars.outputs.output-type == 'image' && needs.registry-identities.outputs.gcp-wif-enabled == 'true' }}
137+
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
138+
with:
139+
token_format: access_token
140+
workload_identity_provider: ${{ needs.registry-identities.outputs.gcp-wif-workload-identity-provider }}
141+
service_account: ${{ needs.registry-identities.outputs.gcp-wif-service-account }}
142+
project_id: ${{ needs.registry-identities.outputs.gcp-wif-project-id }}
143+
create_credentials_file: false
144+
export_environment_variables: false
145+
-
146+
name: Login to Google Artifact Registry
147+
if: ${{ steps.vars.outputs.signed == 'true' && steps.vars.outputs.output-type == 'image' && needs.registry-identities.outputs.gcp-wif-enabled == 'true' }}
148+
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
149+
with:
150+
registry-auth: |
151+
- registry: ${{ needs.registry-identities.outputs.gcp-wif-registry }}
152+
username: oauth2accesstoken
153+
password: ${{ steps.gcp-wif-auth.outputs.access_token }}
154+
-
155+
name: Login to Docker Hub with OIDC
156+
if: ${{ steps.vars.outputs.signed == 'true' && steps.vars.outputs.output-type == 'image' && needs.registry-identities.outputs.dockerhub-oidc-enabled == 'true' }}
157+
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
158+
env:
159+
DOCKERHUB_OIDC_CONNECTIONID: ${{ needs.registry-identities.outputs.dockerhub-oidc-connection-id }}
160+
with:
161+
registry-auth: |
162+
- registry: ${{ needs.registry-identities.outputs.dockerhub-oidc-registry }}
163+
username: ${{ needs.registry-identities.outputs.dockerhub-oidc-username }}
106164
-
107165
name: Login to registry
108-
if: ${{ steps.vars.outputs.signed == 'true' && steps.vars.outputs.output-type == 'image' }}
166+
if: ${{ steps.vars.outputs.signed == 'true' && steps.vars.outputs.output-type == 'image' && env.REGISTRY_AUTHS_PRESENT == 'true' }}
109167
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
110168
with:
111169
registry-auth: ${{ secrets.registry-auths }}

0 commit comments

Comments
 (0)