Compare commits

...

74 Commits

Author SHA1 Message Date
Ebtasam Faridy 27310a8d25 Update pipeline drone-docker-harness 2026-03-31 13:16:12 +05:30
chhawchharia 2f6803e300 feat: [CI-21342]: Aws migrated and vulnerabilities fixed (#505)
Made-with: Cursor
2026-03-04 11:42:44 +05:30
ebtasam-faridy f5f11face3 Merge pull request #504 from drone-plugins/ci-18951
feat: [CI-18951]: check for HARNESS prefixed proxy variables
2026-02-23 17:07:20 +05:30
Chirag S e70d271e93 feat: [CI-18951]: added a log when harness fallback is used 2026-02-18 10:56:51 +05:30
Chirag S f32aa46ea8 feat: [CI-18951]: added unit tests and better comments for the changes 2026-02-18 10:40:37 +05:30
Chirag S 5810bf8a5a feat: [CI-18951]: check for HARNESS prefixed proxy variables 2026-02-13 14:47:58 +05:30
Anurag Madnawat 23887402c3 [feat]: [CI-20260]: Make daemon retry count configurable (#503) 2026-02-11 11:04:38 +05:30
ebtasam-faridy e9bba4ffcf Update pipeline drone-docker-harness (#502) 2026-01-28 19:07:35 +05:30
ebtasam-faridy 7b900ae75d Ci 20437 (#499)
* fix: [CI-20437] Golang version update for vulnerability fix

* fix: [CI-20437] Golang version update for vulnerability fix
2026-01-28 14:13:48 +05:30
OP (oppenheimer) aabeaaf7bb feat: [CI-20527]: add push-only mode to skip build and push pre-existing images (#500)
* Add push-only support

* Include support for PLUGIN_NO_PUSH as well
2026-01-26 22:55:18 +05:30
Abhay 6799ac9418 fix: [CI-19670]: vul fix for jws version (#497) 2025-11-26 13:41:15 +05:30
tapankarangiya 123a133f01 feat: [CI-19349]: Added oidc support for azure connector (#496)
* feat: [CI-19349]: Added oidc support for azure connector

* feat: [CI-19349]: Added env variables

* feat: [CI-19349]: Added tests

* Update cmd/drone-acr/main.go

* Update cmd/drone-acr/main.go

* feat: [CI-19349]: Added Debug statements

---------

Co-authored-by: OP (oppenheimer) <21008429+Ompragash@users.noreply.github.com>
2025-10-24 11:48:46 +05:30
OP (oppenheimer) 58bfad7a29 feat: [CI-18308]: Add Cosign Image Signing Support (#494)
* Add signing support via cosign

* Updated docker.go

* Add signing support via cosign

* Updated docker.go

* Updated docker.go

* Updated docker.go

* Updated docker.go

* Updated docker.go

* Updated dockerfiles
2025-08-01 00:42:10 +05:30
Raghav 0493478ac1 feat: [CI-17953]: Add warning if base image connector is not provided (#492) 2025-07-09 16:07:40 +05:30
ebtasam-faridy be488f7c55 feat: [CI-16559]: docker base image up (#488)
* feat: [CI-16559]: docker base image up

* feat: [CI-16559]: changing base img
2025-06-18 16:28:31 +05:30
ompragash.viswanathan@harness.io 256ec37505 Update pipeline drone-docker-harness 2025-06-18 13:59:07 +05:30
ci-reporunner 3e49e16f0d Update pipeline drone-docker-harness (#491)
Co-authored-by: ompragash.viswanathan@harness.io <ompragash.viswanathan@harness.io>
2025-06-18 13:45:00 +05:30
ci-reporunner d6c64364c4 Update pipeline drone-docker-harness (#490)
Co-authored-by: ompragash.viswanathan@harness.io <ompragash.viswanathan@harness.io>
2025-06-11 20:10:10 +05:30
ompragash.viswanathan@harness.io 8c5277ae89 Update pipeline drone-docker-harness 2025-03-26 20:51:49 +05:30
ci-reporunner 385bfe2f7b Update pipeline drone-docker-harness (#483)
* Update pipeline drone-docker-harness

* Update pipeline drone-docker-harness

* Update pipeline drone-docker-harness

---------

Co-authored-by: ompragash.viswanathan@harness.io <ompragash.viswanathan@harness.io>
2025-03-26 20:49:54 +05:30
ci-reporunner ffa3212940 Update pipeline drone-docker-harness (#482)
* Update pipeline drone-docker-harness

* Update pipeline drone-docker-harness

---------

Co-authored-by: ompragash.viswanathan@harness.io <ompragash.viswanathan@harness.io>
2025-03-26 19:28:29 +05:30
OP (oppenheimer) 78fc38da63 feat: [CI-16478]: Add Azure Container Registry (ACR) authentication support (#480)
* Updated cmd/drone-acr/main.go and the dependencies

* Updated cmd/drone-acr/main.go

* Import path/filepath
2025-03-26 18:34:28 +05:30
ci-reporunner 57234fcfd0 Update pipeline drone-docker-harness: Add matrix strategy at step level. (#481)
* Update pipeline drone-docker-harness

* Update pipeline drone-docker-harness

* Update pipeline drone-docker-harness: Add matrix strategy at step level.

* Update inputset event-Push

* Update inputset event-Tag

* Update inputset event-PR

---------

Co-authored-by: ompragash.viswanathan@harness.io <ompragash.viswanathan@harness.io>
Co-authored-by: Devansh Mathur <devanshmathur@harness.io>
2025-03-26 16:23:45 +05:30
OP (oppenheimer) 44157a2e29 Revert base ding image tag to 20.10.14 (#476) 2025-02-14 15:39:08 +05:30
OP (oppenheimer) feb90fe92d New input skip_push_if_tag_exists (#472) 2025-02-13 15:34:02 +05:30
ci-reporunner 55c76aeaf0 Update inputset event-Push (#474)
* Update inputset event-Push

* Update inputset event-Push

* Update inputset event-PR

* Update inputset event-Tag

---------

Co-authored-by: Archit Mallik <archit.mallik@harness.io>
2025-02-13 10:53:37 +05:30
ci-reporunner b54379f077 Update pipeline drone-docker-harness (#473)
Co-authored-by: Archit Mallik <archit.mallik@harness.io>
2025-02-12 17:04:26 +05:30
OP (oppenheimer) ab3fed4710 Merge pull request #469 from drone-plugins/CI-15946
Create pipeline drone-docker-harness
2025-02-12 13:26:14 +05:30
Archit Mallik e62984fe46 Create inputset event-Tag 2025-01-29 19:53:57 +05:30
Archit Mallik c48ca605a4 Create inputset event-Push 2025-01-29 19:51:36 +05:30
Archit Mallik 0e03ef3eab Create inputset event-PR 2025-01-29 19:48:48 +05:30
Archit Mallik 7a498e78b2 Create pipeline drone-docker-harness 2025-01-29 19:40:38 +05:30
OP (oppenheimer) 2140a2be40 Merge pull request #465 from drone-plugins/CI-15502
fix: [CI-15502]: "Updated docker and go versions to remove vulnerabilities"
2024-12-25 08:12:57 +05:30
sandeepharness dcc762d301 "Updated docker to 27.3.1-dind and go versions to 1.22.7" 2024-12-19 17:10:39 +05:30
sandeepharness b318c15acb "Updated docker version to remove vulnerabilities" 2024-12-19 16:14:55 +05:30
OP (oppenheimer) 86de147dc8 Merge pull request #462 from drone-plugins/CI-14845
fix:[CI-14845]: support for build args which has comma seperated values
2024-11-29 13:46:11 +05:30
sahithiharness 68605049b7 fix 2024-11-28 13:31:19 +05:30
sahithiharness e2e30f3015 fix:[CI-14845]: support for build args which has comma seperated values 2024-11-26 21:28:32 +05:30
OP (oppenheimer) 4999d4ca01 Merge pull request #453 from Aishwarya-Lad/CI-13924
print validation instead of raising error
2024-08-21 21:11:21 +05:30
Aishwarya Lad b28dae76d7 change hard validation to soft validation 2024-08-19 17:02:49 -07:00
OP (oppenheimer) 9530538f5c Merge pull request #450 from Aishwarya-Lad/CI-13562
fix:[CI-13562]:improve error message for anonymous connector used
2024-07-26 12:16:07 +05:30
Aishwarya Lad 27660b1342 add dependencies 2024-07-25 23:39:26 -07:00
Aishwarya Lad 81ce3b6d2e check for registry empty
check for registry empty

redo unwanted changes
2024-07-22 16:38:47 -07:00
Aishwarya Lad 3c461ba65b fix:[CI-13562]: add error in command login too 2024-07-22 16:27:35 -07:00
Aishwarya Lad e1e1a6c7dc fix:[CI-13562]:improve error message for anonymous connector used 2024-07-22 16:13:07 -07:00
Jamie Li f202de1604 Merge pull request #449 from Aishwarya-Lad/CI-13422
Change docker login method, from config writing to login func
2024-07-15 17:22:27 -04:00
Aishwarya Lad 60929c782c remove comment 2024-07-13 14:52:45 -07:00
Aishwarya Lad 90280c9c7b Change docker login method, from config writing to login func 2024-07-12 12:13:41 -07:00
Hen Amar 531cc5cb7a Merge pull request #447 from drone-plugins/CI-13178_go-upgrade_minor
fix: [CI-13178]: Upgraded go with minor version to 1.22.4
2024-07-10 15:10:59 +03:00
rahkumar56 a5459e5f20 Merge branch 'master' into CI-13178_go-upgrade_minor 2024-07-10 16:14:18 +05:30
OP (oppenheimer) 6ae21e3d8c Merge pull request #448 from Aishwarya-Lad/CI-13377
remove docker registry string match
2024-07-10 13:19:11 +05:30
Aishwarya Lad bee421255a remove docker registry string match 2024-07-09 23:05:03 -07:00
rahkumar56 a6b3e2f7b5 fix: [CI-13178]: Upgraded go with minor version to 1.22.4 2024-07-05 12:13:35 +05:30
Jamie Li 6cade1e98b Merge pull request #446 from Aishwarya-Lad/CI-11718-part2
update docker config for base image
2024-06-28 13:41:24 -04:00
Aishwarya Lad 0a2f635d57 typos 2024-06-26 16:22:01 -07:00
Aishwarya Lad 74d5558af0 resolve comments 2024-06-26 15:42:46 -07:00
Aishwarya Lad 5639b702aa dependencies 2024-06-25 15:06:41 -07:00
Aishwarya Lad 9a4e1ba483 address review comments 2024-06-25 15:04:24 -07:00
Aishwarya Lad ced9875ed0 add only docker registry auths to docker config 2024-06-24 12:50:00 -07:00
Aishwarya Lad e9b38c94b4 update docker config 2024-06-23 03:02:24 -07:00
OP (oppenheimer) 292ebe06de Merge pull request #440 from drone-plugins/CI-12566
Fixed 'error getting ECR auth: WebIdentityErr: unable to read file at…' issue
2024-05-20 16:16:44 +05:30
Ompragash Viswanathan 49e9dde7a7 Updated cmd/drone-ecr/main.go 2024-05-15 10:56:48 +05:30
Ompragash Viswanathan e7e8dd882d Fixed 'error getting ECR auth: WebIdentityErr: unable to read file at' issue 2024-05-15 10:56:48 +05:30
Brad Rydzewski 11015f0ddb Merge pull request #442 from drone-plugins/pin-docker-version
Update .drone.yml
2024-05-14 16:47:51 -04:00
Vistaar Juneja a946428e81 Update .drone.yml
Pin plugins/docker version for all builds
2024-05-14 21:46:20 +01:00
Vistaar Juneja 7c8c6ca9cb Merge pull request #438 from drone-plugins/CI-10849
feat: [CI-10849]: add git-leaks support
2024-05-10 12:39:40 +01:00
abhay084 87212938c2 corrected external reference 2024-05-10 11:18:24 +05:30
abhay084 3c4c8e5f10 feat: [CI-10849]: add git-leaks support 2024-05-10 11:18:24 +05:30
Dinesh Garg b009c711b5 Merge pull request #439 from Ompragash/patch-1
Added PLUGIN_OIDC_TOKEN_ID support
2024-04-23 23:34:53 -07:00
OP (oppenheimer) 12cc40aa62 Added PLUGIN_OIDC_TOKEN_ID support 2024-04-23 22:34:44 +05:30
Hen Amar a807dc91eb Merge pull request #437 from drone-plugins/scheduled_go_upgrade2024-03-04_08-03-22
Update go Version in all files
2024-03-06 11:52:44 +02:00
rahkumar56 e0ceb37f24 Update GO Version to latest version in all the files 2024-03-04 08:05:22 +00:00
Eoin McAfee 939591f018 fixes windows build for gr (#416) 2023-10-26 13:49:35 +01:00
Eoin McAfee 755ae001f9 ci-3011- split out gar/gcr (#415)
* split out gar/gcr
2023-10-26 11:41:06 +01:00
42 changed files with 2904 additions and 337 deletions
+213 -33
View File
@@ -12,7 +12,7 @@ platform:
steps: steps:
- name: vet - name: vet
image: golang:1.21 image: golang:1.24.11
commands: commands:
- go vet ./... - go vet ./...
environment: environment:
@@ -22,7 +22,7 @@ steps:
path: /go path: /go
- name: test - name: test
image: golang:1.21 image: golang:1.24.11
commands: commands:
- go test -cover ./... - go test -cover ./...
environment: environment:
@@ -55,7 +55,7 @@ platform:
steps: steps:
- name: go build - name: go build
image: golang:1.21 image: golang:1.24.11
environment: environment:
CGO_ENABLED: 0 CGO_ENABLED: 0
commands: commands:
@@ -63,6 +63,8 @@ steps:
- go build -o release/windows/amd64/drone-ecr.exe ./cmd/drone-ecr - go build -o release/windows/amd64/drone-ecr.exe ./cmd/drone-ecr
- go build -o release/windows/amd64/drone-gcr.exe ./cmd/drone-gcr - go build -o release/windows/amd64/drone-gcr.exe ./cmd/drone-gcr
- go build -o release/windows/amd64/drone-acr.exe ./cmd/drone-acr - go build -o release/windows/amd64/drone-acr.exe ./cmd/drone-acr
- go build -o release/windows/amd64/drone-gar.exe ./cmd/drone-gar
- name: build docker plugin - name: build docker plugin
image: plugins/docker@sha256:f0233d950ae87ee6cb5500b2d5497fe02aa338201c0bdce2619f443fd174cfa4 image: plugins/docker@sha256:f0233d950ae87ee6cb5500b2d5497fe02aa338201c0bdce2619f443fd174cfa4
settings: settings:
@@ -123,7 +125,21 @@ steps:
purge: false purge: false
when: when:
event: [push, tag] event: [push, tag]
- name: build gar plugin
image: plugins/docker@sha256:f0233d950ae87ee6cb5500b2d5497fe02aa338201c0bdce2619f443fd174cfa4
pull: never
settings:
dockerfile: docker/gar/Dockerfile.windows.amd64.1809
repo: plugins/gar
username:
from_secret: docker_username
password:
from_secret: docker_password
auto_tag: true
auto_tag_suffix: windows-1809-amd64
purge: false
when:
event: [push, tag]
depends_on: depends_on:
- testing - testing
@@ -146,7 +162,7 @@ platform:
steps: steps:
- name: go build - name: go build
image: golang:1.21 image: golang:1.24.11
environment: environment:
CGO_ENABLED: 0 CGO_ENABLED: 0
commands: commands:
@@ -154,8 +170,9 @@ steps:
- go build -o release/windows/amd64/drone-ecr.exe ./cmd/drone-ecr - go build -o release/windows/amd64/drone-ecr.exe ./cmd/drone-ecr
- go build -o release/windows/amd64/drone-gcr.exe ./cmd/drone-gcr - go build -o release/windows/amd64/drone-gcr.exe ./cmd/drone-gcr
- go build -o release/windows/amd64/drone-acr.exe ./cmd/drone-acr - go build -o release/windows/amd64/drone-acr.exe ./cmd/drone-acr
- go build -o release/windows/amd64/drone-gar.exe ./cmd/drone-gar
- name: build docker plugin - name: build docker plugin
image: plugins/docker image: plugins/docker@sha256:f0233d950ae87ee6cb5500b2d5497fe02aa338201c0bdce2619f443fd174cfa4
settings: settings:
dockerfile: docker/docker/Dockerfile.windows.amd64.ltsc2022 dockerfile: docker/docker/Dockerfile.windows.amd64.ltsc2022
repo: plugins/docker repo: plugins/docker
@@ -169,7 +186,7 @@ steps:
when: when:
event: [push, tag] event: [push, tag]
- name: build ecr plugin - name: build ecr plugin
image: plugins/docker image: plugins/docker@sha256:f0233d950ae87ee6cb5500b2d5497fe02aa338201c0bdce2619f443fd174cfa4
settings: settings:
dockerfile: docker/ecr/Dockerfile.windows.amd64.ltsc2022 dockerfile: docker/ecr/Dockerfile.windows.amd64.ltsc2022
repo: plugins/ecr repo: plugins/ecr
@@ -183,7 +200,7 @@ steps:
when: when:
event: [push, tag] event: [push, tag]
- name: build gcr plugin - name: build gcr plugin
image: plugins/docker image: plugins/docker@sha256:f0233d950ae87ee6cb5500b2d5497fe02aa338201c0bdce2619f443fd174cfa4
settings: settings:
dockerfile: docker/gcr/Dockerfile.windows.amd64.ltsc2022 dockerfile: docker/gcr/Dockerfile.windows.amd64.ltsc2022
repo: plugins/gcr repo: plugins/gcr
@@ -197,7 +214,7 @@ steps:
when: when:
event: [push, tag] event: [push, tag]
- name: build acr plugin - name: build acr plugin
image: plugins/docker image: plugins/docker@sha256:f0233d950ae87ee6cb5500b2d5497fe02aa338201c0bdce2619f443fd174cfa4
settings: settings:
dockerfile: docker/acr/Dockerfile.windows.amd64.ltsc2022 dockerfile: docker/acr/Dockerfile.windows.amd64.ltsc2022
repo: plugins/acr repo: plugins/acr
@@ -210,7 +227,20 @@ steps:
purge: false purge: false
when: when:
event: [push, tag] event: [push, tag]
- name: build gar plugin
image: plugins/docker@sha256:f0233d950ae87ee6cb5500b2d5497fe02aa338201c0bdce2619f443fd174cfa4
settings:
dockerfile: docker/gar/Dockerfile.windows.amd64.ltsc2022
repo: plugins/gar
username:
from_secret: docker_username
password:
from_secret: docker_password
auto_tag: true
auto_tag_suffix: windows-ltsc2022-amd64
purge: false
when:
event: [push, tag]
depends_on: depends_on:
- testing - testing
@@ -234,7 +264,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-docker ./cmd/drone-docker' - 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-docker ./cmd/drone-docker'
environment: environment:
@@ -245,7 +275,7 @@ steps:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-docker ./cmd/drone-docker' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-docker ./cmd/drone-docker'
environment: environment:
@@ -255,7 +285,7 @@ steps:
- tag - tag
- name: executable - name: executable
image: golang:1.21 image: golang:1.24.11
commands: commands:
- ./release/linux/amd64/drone-docker --help - ./release/linux/amd64/drone-docker --help
@@ -299,7 +329,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-docker ./cmd/drone-docker' - 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-docker ./cmd/drone-docker'
environment: environment:
@@ -310,7 +340,7 @@ steps:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-docker ./cmd/drone-docker' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-docker ./cmd/drone-docker'
environment: environment:
@@ -320,7 +350,7 @@ steps:
- tag - tag
- name: executable - name: executable
image: golang:1.21 image: golang:1.24.11
commands: commands:
- ./release/linux/arm64/drone-docker --help - ./release/linux/arm64/drone-docker --help
@@ -399,7 +429,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-gcr ./cmd/drone-gcr' - 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-gcr ./cmd/drone-gcr'
environment: environment:
@@ -410,7 +440,7 @@ steps:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-gcr ./cmd/drone-gcr' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-gcr ./cmd/drone-gcr'
environment: environment:
@@ -444,7 +474,6 @@ trigger:
depends_on: depends_on:
- linux-amd64-docker - linux-amd64-docker
--- ---
kind: pipeline kind: pipeline
name: linux-arm64-gcr name: linux-arm64-gcr
@@ -459,7 +488,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-gcr ./cmd/drone-gcr' - 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-gcr ./cmd/drone-gcr'
environment: environment:
@@ -470,7 +499,7 @@ steps:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-gcr ./cmd/drone-gcr' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-gcr ./cmd/drone-gcr'
environment: environment:
@@ -539,7 +568,158 @@ depends_on:
- windows-ltsc2022 - windows-ltsc2022
- linux-amd64-gcr - linux-amd64-gcr
- linux-arm64-gcr - linux-arm64-gcr
---
kind: pipeline
name: linux-amd64-gar
type: vm
pool:
use: ubuntu
platform:
os: linux
arch: amd64
steps:
- name: build-push
image: golang:1.24.11
commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-gar ./cmd/drone-gar'
environment:
CGO_ENABLED: 0
when:
event:
exclude:
- tag
- name: build-tag
image: golang:1.24.11
commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-gar ./cmd/drone-gar'
environment:
CGO_ENABLED: 0
when:
event:
- tag
- name: publish
image: plugins/docker:18
settings:
auto_tag: true
auto_tag_suffix: linux-amd64
daemon_off: false
dockerfile: docker/gar/Dockerfile.linux.amd64
password:
from_secret: docker_password
repo: plugins/gar
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
- "refs/pull/**"
depends_on:
- linux-amd64-docker
---
kind: pipeline
name: linux-arm64-gar
type: vm
pool:
use: ubuntu_arm64
platform:
os: linux
arch: arm64
steps:
- name: build-push
image: golang:1.24.11
commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-gar ./cmd/drone-gar'
environment:
CGO_ENABLED: 0
when:
event:
exclude:
- tag
- name: build-tag
image: golang:1.24.11
commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-gar ./cmd/drone-gar'
environment:
CGO_ENABLED: 0
when:
event:
- tag
- name: publish
image: plugins/docker:18
settings:
auto_tag: true
auto_tag_suffix: linux-arm64
daemon_off: false
dockerfile: docker/gar/Dockerfile.linux.arm64
password:
from_secret: docker_password
repo: plugins/gar
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
- "refs/pull/**"
depends_on:
- linux-arm64-docker
---
kind: pipeline
name: notifications-gar
type: vm
pool:
use: ubuntu
platform:
os: linux
arch: amd64
steps:
- name: manifest
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
password:
from_secret: docker_password
spec: docker/gar/manifest.tmpl
username:
from_secret: docker_username
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
depends_on:
- windows-1809
- windows-ltsc2022
- linux-amd64-gar
- linux-arm64-gar
--- ---
kind: pipeline kind: pipeline
name: linux-amd64-ecr name: linux-amd64-ecr
@@ -554,7 +734,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-ecr ./cmd/drone-ecr' - 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-ecr ./cmd/drone-ecr'
environment: environment:
@@ -564,7 +744,7 @@ steps:
exclude: exclude:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-ecr ./cmd/drone-ecr' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-ecr ./cmd/drone-ecr'
environment: environment:
@@ -612,7 +792,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-ecr ./cmd/drone-ecr' - 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-ecr ./cmd/drone-ecr'
environment: environment:
@@ -622,7 +802,7 @@ steps:
exclude: exclude:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-ecr ./cmd/drone-ecr' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-ecr ./cmd/drone-ecr'
environment: environment:
@@ -705,7 +885,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-heroku ./cmd/drone-heroku' - 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/amd64/drone-heroku ./cmd/drone-heroku'
environment: environment:
@@ -715,7 +895,7 @@ steps:
exclude: exclude:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-heroku ./cmd/drone-heroku' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/amd64/drone-heroku ./cmd/drone-heroku'
environment: environment:
@@ -764,7 +944,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-heroku ./cmd/drone-heroku' - 'go build -v -ldflags "-X main.version=${DRONE_COMMIT_SHA:0:8}" -a -tags netgo -o release/linux/arm64/drone-heroku ./cmd/drone-heroku'
environment: environment:
@@ -774,7 +954,7 @@ steps:
exclude: exclude:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-heroku ./cmd/drone-heroku' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v}" -a -tags netgo -o release/linux/arm64/drone-heroku ./cmd/drone-heroku'
environment: environment:
@@ -855,7 +1035,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.build=${DRONE_BUILD_NUMBER}" -a -tags netgo -o release/linux/amd64/drone-acr ./cmd/drone-acr' - 'go build -v -ldflags "-X main.build=${DRONE_BUILD_NUMBER}" -a -tags netgo -o release/linux/amd64/drone-acr ./cmd/drone-acr'
environment: environment:
@@ -865,7 +1045,7 @@ steps:
exclude: exclude:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}" -a -tags netgo -o release/linux/amd64/drone-acr ./cmd/drone-acr' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}" -a -tags netgo -o release/linux/amd64/drone-acr ./cmd/drone-acr'
environment: environment:
@@ -913,7 +1093,7 @@ platform:
steps: steps:
- name: build-push - name: build-push
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.build=${DRONE_BUILD_NUMBER}" -a -tags netgo -o release/linux/arm64/drone-acr ./cmd/drone-acr' - 'go build -v -ldflags "-X main.build=${DRONE_BUILD_NUMBER}" -a -tags netgo -o release/linux/arm64/drone-acr ./cmd/drone-acr'
environment: environment:
@@ -924,7 +1104,7 @@ steps:
- tag - tag
- name: build-tag - name: build-tag
image: golang:1.21 image: golang:1.24.11
commands: commands:
- 'go build -v -ldflags "-X main.version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}" -a -tags netgo -o release/linux/arm64/drone-acr ./cmd/drone-acr' - 'go build -v -ldflags "-X main.version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}" -a -tags netgo -o release/linux/arm64/drone-acr ./cmd/drone-acr'
environment: environment:
+15
View File
@@ -0,0 +1,15 @@
inputSet:
name: event-PR
tags: {}
identifier: eventPR
orgIdentifier: default
projectIdentifier: Drone_Plugins
pipeline:
identifier: dronedockerharness
properties:
ci:
codebase:
build:
type: PR
spec:
number: <+trigger.prNumber>
+15
View File
@@ -0,0 +1,15 @@
inputSet:
name: event-Push
tags: {}
identifier: eventPush
orgIdentifier: default
projectIdentifier: Drone_Plugins
pipeline:
identifier: dronedockerharness
properties:
ci:
codebase:
build:
type: branch
spec:
branch: <+trigger.branch>
+15
View File
@@ -0,0 +1,15 @@
inputSet:
name: event-Tag
tags: {}
identifier: eventTag
orgIdentifier: default
projectIdentifier: Drone_Plugins
pipeline:
identifier: dronedockerharness
properties:
ci:
codebase:
build:
type: tag
spec:
tag: <+trigger.tag>
+447
View File
@@ -0,0 +1,447 @@
pipeline:
orgIdentifier: default
tags: {}
properties:
ci:
codebase:
connectorRef: GitHub_Drone_Plugins_Org
repoName: drone-docker
build: <+input>
sparseCheckout: []
stages:
- stage:
name: Test
identifier: Test
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: false
paths: []
platform:
os: Linux
arch: Amd64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: GO VET
identifier: Run_1
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: golang:1.25.8
shell: Sh
command: go vet ./...
- step:
type: Run
name: GO TEST
identifier: Run_2
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: golang:1.25.8
shell: Sh
command: go test -cover ./...
- parallel:
- stage:
name: linux-amd64
identifier: linamd64
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: false
paths: []
platform:
os: Linux
arch: Amd64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: Build Binary
identifier: Build_Push
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: golang:1.25.8
shell: Sh
command: go build -a -tags netgo -o release/linux/amd64/drone-<+matrix.repo> ./cmd/drone-<+matrix.repo>
envVariables:
CGO_ENABLED: "0"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- heroku
- acr
- step:
type: Plugin
name: Build and Push on Tag
identifier: Docker_Build_and_Push
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/docker
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
repo: plugins/<+matrix.repo>
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.amd64
auto_tag: "true"
auto_tag_suffix: linux-amd64
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- heroku
- acr
- step:
type: BuildAndPushDockerRegistry
name: Build and Push on Branch
identifier: BuildAndPushDockerRegistry_1
spec:
connectorRef: Plugins_Docker_Hub_Connector
repo: plugins/<+matrix.repo>
tags:
- linux-amd64
caching: false
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.amd64
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- heroku
- acr
- stage:
name: linux-arm64
identifier: linarm64
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: false
paths: []
platform:
os: Linux
arch: Arm64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: Build Binary
identifier: buildpush
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: golang:1.25.8
shell: Sh
command: go build -a -tags netgo -o release/linux/arm64/drone-<+matrix.repo> ./cmd/drone-<+matrix.repo>
envVariables:
CGO_ENABLED: "0"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- heroku
- acr
- step:
type: Plugin
name: Build and Push on Tag
identifier: Docker_Build_and_Push
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/docker
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
repo: plugins/<+matrix.repo>
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.arm64
auto_tag: "true"
auto_tag_suffix: linux-arm64
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- heroku
- acr
- step:
type: BuildAndPushDockerRegistry
name: Build and Push on Branch
identifier: BuildAndPushDockerRegistry_1
spec:
connectorRef: Plugins_Docker_Hub_Connector
repo: plugins/<+matrix.repo>
tags:
- linux-arm64
caching: false
dockerfile: docker/<+matrix.repo>/Dockerfile.linux.arm64
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- heroku
- acr
- stage:
name: win-1809-amd64
identifier: win1809amd64
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: true
infrastructure:
type: VM
spec:
type: Pool
spec:
poolName: windows-2019
os: Windows
execution:
steps:
- step:
type: Run
name: Build Binary
identifier: go_build
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: golang:1.23.0
shell: Sh
command: |-
# disable cgo
export CGO_ENABLED=0
go build -o release/windows/amd64/drone-<+matrix.repo>.exe ./cmd/drone-<+matrix.repo>
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- acr
- step:
type: Plugin
name: Build and Push on Tag
identifier: Docker_Build_and_Push1
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/docker
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
repo: plugins/<+matrix.repo>
dockerfile: docker/<+matrix.repo>/Dockerfile.windows.amd64.1809
auto_tag: "true"
auto_tag_suffix: windows-1809-amd64
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- acr
- step:
type: BuildAndPushDockerRegistry
name: Build and Push on Branch
identifier: BuildAndPushDockerRegistry_2
spec:
connectorRef: Plugins_Docker_Hub_Connector
repo: plugins/<+matrix.repo>
tags:
- windows-1809-amd64
caching: false
dockerfile: docker/<+matrix.repo>/Dockerfile.windows.amd64.1809
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- acr
delegateSelectors:
- windows-vm
- stage:
name: win-ltsc2022-amd64
identifier: winamd64
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: false
paths: []
platform:
os: Windows
arch: Amd64
runtime:
type: Cloud
spec: {}
execution:
steps:
- step:
type: Run
name: Build Binary -ltsc2022
identifier: build_amd64ltsc2022
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: golang:1.23.0
shell: Sh
command: |-
# disable cgo
export CGO_ENABLED=0
go build -o release/windows/amd64/drone-<+matrix.repo>.exe ./cmd/drone-<+matrix.repo>
envVariables:
CGO_ENABLED: "0"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- acr
- step:
type: Plugin
name: Build and Push on Tag
identifier: Docker_Build_and_Push1
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/docker
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
repo: plugins/<+matrix.repo>
dockerfile: docker/<+matrix.repo>/Dockerfile.windows.amd64.ltsc2022
auto_tag: "true"
auto_tag_suffix: windows-ltsc2022-amd64
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- acr
- step:
type: BuildAndPushDockerRegistry
name: Build and Push on Branch
identifier: BuildAndPushDockerRegistry_2
spec:
connectorRef: Plugins_Docker_Hub_Connector
repo: plugins/<+matrix.repo>
tags:
- windows-ltsc2022-amd64
caching: false
dockerfile: docker/<+matrix.repo>/Dockerfile.windows.amd64.ltsc2022
when:
stageStatus: Success
condition: <+codebase.build.type> == "branch"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- acr
buildIntelligence:
enabled: false
- stage:
name: Manifest and Release
identifier: Manifest
description: ""
type: CI
spec:
cloneCodebase: true
caching:
enabled: false
paths: []
execution:
steps:
- step:
type: Plugin
name: Manifest
identifier: Plugin_1
spec:
connectorRef: Plugins_Docker_Hub_Connector
image: plugins/manifest
settings:
username: drone
password: <+secrets.getValue("Plugins_Docker_Hub_Pat")>
auto_tag: "true"
ignore_missing: "true"
spec: docker/<+matrix.repo>/manifest.tmpl
when:
stageStatus: Success
condition: <+codebase.build.type> == "tag"
strategy:
matrix:
repo:
- docker
- gcr
- gar
- ecr
- heroku
- acr
platform:
os: Linux
arch: Amd64
runtime:
type: Cloud
spec: {}
identifier: dronedockerharness
projectIdentifier: Drone_Plugins
name: drone-docker-harness
allowStageExecutions: true
+162
View File
@@ -0,0 +1,162 @@
# Cosign Integration for Drone-Docker
This document describes how to use the cosign container image signing feature in drone-docker.
## Overview
The drone-docker plugin now supports automatic container image signing using cosign after each successful push. This provides cryptographic verification that images haven't been tampered with.
## Environment Variables
The plugin accepts three cosign-related environment variables:
### `PLUGIN_COSIGN_PRIVATE_KEY` (Required for signing)
- **Description**: Private key for signing (PEM format content or file path)
- **Format**: Either PEM content or file path to private key
- **Usage**: Should be provided via secrets
### `PLUGIN_COSIGN_PASSWORD` (Optional)
- **Description**: Password for encrypted private keys
- **Usage**: Only needed if your private key is password-protected
### `PLUGIN_COSIGN_PARAMS` (Optional)
- **Description**: Additional cosign parameters
- **Examples**:
- `-a build_id=123` (add annotations)
- `--tlog-upload=false` (disable transparency log)
- `--rekor-url=https://custom-rekor.example.com` (custom rekor instance)
## Usage Examples
### 1. Basic Signing (Drone)
```yaml
kind: pipeline
type: docker
name: default
steps:
- name: docker
image: plugins/docker
settings:
repo: myregistry/myapp
tags: latest
cosign_private_key:
from_secret: cosign_private_key
cosign_password:
from_secret: cosign_password
```
### 2. Advanced Signing with Annotations (Drone)
```yaml
steps:
- name: docker
image: plugins/docker
settings:
repo: myregistry/myapp
tags:
- latest
- ${DRONE_BUILD_NUMBER}
cosign_private_key:
from_secret: cosign_private_key
cosign_params: "-a build_id=${DRONE_BUILD_NUMBER} -a commit_sha=${DRONE_COMMIT_SHA} -a branch=${DRONE_BRANCH}"
```
### 3. Harness CI/CD Usage
```yaml
- step:
type: Plugin
name: Build and Sign
identifier: build_and_sign
spec:
connectorRef: account.harnessImage
image: plugins/docker
settings:
repo: myregistry/myapp
tags: <+pipeline.sequenceId>
cosign_private_key: <+secrets.getValue("cosign_private_key")>
cosign_password: <+secrets.getValue("cosign_password")>
cosign_params: "-a harness_build=<+pipeline.sequenceId> -a harness_project=<+project.name>"
```
## Key Management
### Generating Cosign Keys
```bash
# Generate a new key pair
cosign generate-key-pair
# This creates:
# - cosign.key (private key)
# - cosign.pub (public key)
```
### Storing Keys Securely
**Harness Secrets:**
1. Go to Project Settings → Secrets
2. Create new secret with type "File" for private key
3. Create new secret with type "Text" for password
## Security Features
### Automatic Validation
-**Private key format validation**: Ensures PEM format is correct
-**Password requirement detection**: Warns if encrypted key needs password
-**Keyless signing prevention**: Warns that OIDC keyless signing isn't supported
### Error Handling
- **Invalid private key**: `❌ Invalid private key format. Expected PEM format`
- **Missing password**: `🔐 Encrypted private key requires password. Set PLUGIN_COSIGN_PASSWORD`
- **Keyless signing**: `⚠️ WARNING: Keyless signing (OIDC) isn't supported yet in this plugin`
## Signing Behavior
### When Signing Occurs
-**After each successful push**: Images are signed immediately after push
-**Multiple tags**: Each tag gets signed individually
-**Push-only mode**: Works with existing images
-**Dry-run respect**: Skips signing in dry-run mode
### Image References
- **Preferred**: Signs by digest (e.g., `image@sha256:abc123...`) for security
- **Fallback**: Signs by tag if digest unavailable
### Authentication
- **Registry auth**: Automatically uses existing Docker registry credentials
## Verification
To verify a signed image:
```bash
# Verify with public key
cosign verify --key cosign.pub myregistry/myapp:latest
# Verify with annotations
cosign verify --key cosign.pub \
-a build_id=123 \
myregistry/myapp:latest
```
## Troubleshooting
### Common Issues
1. **"cosign: command not found"**
- The container image includes cosign binary
- Use the latest plugin image: `plugins/docker:latest`
2. **"keyless signing not supported"**
- This plugin only supports private key signing
- Don't use `--oidc` or `--identity-token` in `cosign_params`
3. **"encrypted private key requires password"**
- Set `PLUGIN_COSIGN_PASSWORD` environment variable
- Or use an unencrypted private key
4. **Registry authentication issues**
- Cosign uses the same Docker registry credentials
- Ensure Docker login is working first
+16 -4
View File
@@ -10,6 +10,14 @@
Drone plugin uses Docker-in-Docker to build and publish Docker images to a container registry. For the usage information and a listing of the available options please take a look at [the docs](http://plugins.drone.io/drone-plugins/drone-docker/). Drone plugin uses Docker-in-Docker to build and publish Docker images to a container registry. For the usage information and a listing of the available options please take a look at [the docs](http://plugins.drone.io/drone-plugins/drone-docker/).
### Git Leaks
Run the following script to install git-leaks support to this repo.
```
chmod +x ./git-hooks/install.sh
./git-hooks/install.sh
```
## Build ## Build
Build the binaries with the following commands: Build the binaries with the following commands:
@@ -25,6 +33,7 @@ go build -v -a -tags netgo -o release/linux/amd64/drone-gcr ./cmd/drone-gcr
go build -v -a -tags netgo -o release/linux/amd64/drone-ecr ./cmd/drone-ecr go build -v -a -tags netgo -o release/linux/amd64/drone-ecr ./cmd/drone-ecr
go build -v -a -tags netgo -o release/linux/amd64/drone-acr ./cmd/drone-acr go build -v -a -tags netgo -o release/linux/amd64/drone-acr ./cmd/drone-acr
go build -v -a -tags netgo -o release/linux/amd64/drone-heroku ./cmd/drone-heroku go build -v -a -tags netgo -o release/linux/amd64/drone-heroku ./cmd/drone-heroku
go build -v -a -tags netgo -o release/linux/amd64/drone-gar ./cmd/drone-gar
``` ```
## Docker ## Docker
@@ -56,6 +65,11 @@ docker build \
--label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \ --label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \
--file docker/heroku/Dockerfile.linux.amd64 --tag plugins/heroku . --file docker/heroku/Dockerfile.linux.amd64 --tag plugins/heroku .
docker build \
--label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \
--file docker/gar/Dockerfile.linux.amd64 --tag plugins/gar .
``` ```
## Usage ## Usage
@@ -122,12 +136,11 @@ type: docker
steps: steps:
- name: push-to-gar - name: push-to-gar
image: plugins/gcr image: plugins/gar
pull: never pull: never
settings: settings:
tag: latest tag: latest
repo: project-id/repo/image-name repo: project-id/repo/image-name
registry_type: GAR
location: us location: us
json_key: json_key:
from_secret: gcr_json_key from_secret: gcr_json_key
@@ -138,12 +151,11 @@ steps:
```yaml ```yaml
steps: steps:
- name: push-to-gar - name: push-to-gar
image: plugins/gcr image: plugins/gar
pull: never pull: never
settings: settings:
tag: latest tag: latest
repo: project-id/repo/image-name repo: project-id/repo/image-name
registry_type: GAR
location: europe location: europe
project_number: project-number project_number: project-number
pool_id: workload identity pool id pool_id: workload identity pool id
+12 -2
View File
@@ -17,8 +17,14 @@ import (
"github.com/inhies/go-bytesize" "github.com/inhies/go-bytesize"
) )
// writeCard maintains backward compatibility by using TempTag
func (p Plugin) writeCard() error { func (p Plugin) writeCard() error {
cmd := exec.Command(dockerExe, "inspect", p.Build.TempTag) return p.writeCardForImage(p.Build.TempTag)
}
// writeCardForImage generates card for any image reference
func (p Plugin) writeCardForImage(imageRef string) error {
cmd := exec.Command(dockerExe, "inspect", imageRef)
data, err := cmd.CombinedOutput() data, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return err return err
@@ -38,7 +44,11 @@ func (p Plugin) writeCard() error {
for _, tag := range inspect.RepoTags { for _, tag := range inspect.RepoTags {
sliceTagStruct = append(sliceTagStruct, TagStruct{Tag: tag}) sliceTagStruct = append(sliceTagStruct, TagStruct{Tag: tag})
} }
inspect.ParsedRepoTags = sliceTagStruct[1:] // remove the first tag which is always "hash:latest" if len(sliceTagStruct) > 1 {
inspect.ParsedRepoTags = sliceTagStruct[1:] // remove the first tag which is always "hash:latest"
} else {
inspect.ParsedRepoTags = sliceTagStruct
}
// create the url from repo and registry // create the url from repo and registry
inspect.URL = mapRegistryToURL(p.Daemon.Registry, p.Build.Repo) inspect.URL = mapRegistryToURL(p.Daemon.Registry, p.Build.Repo)
cardData, _ := json.Marshal(inspect) cardData, _ := json.Marshal(inspect)
+235
View File
@@ -1,15 +1,50 @@
package main package main
import ( import (
"context"
"encoding/base64"
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http"
"net/url"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strings" "strings"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
docker "github.com/drone-plugins/drone-docker" docker "github.com/drone-plugins/drone-docker"
azureutil "github.com/drone-plugins/drone-docker/internal/azure"
)
type subscriptionUrlResponse struct {
Value []struct {
ID string `json:"id"`
} `json:"value"`
}
const (
acrCertFile = "acr-cert.pem"
azSubscriptionApiVersion = "2021-04-01"
azSubscriptionBaseUrl = "https://management.azure.com/subscriptions/"
basePublicUrl = "https://portal.azure.com/#view/Microsoft_Azure_ContainerRegistries/TagMetadataBlade/registryId/"
defaultUsername = "00000000-0000-0000-0000-000000000000"
// Environment variable names for Azure Environment Credential
clientIdEnv = "AZURE_CLIENT_ID"
clientSecretKeyEnv = "AZURE_CLIENT_SECRET"
tenantKeyEnv = "AZURE_TENANT_ID"
certPathEnv = "AZURE_CLIENT_CERTIFICATE_PATH"
)
var (
acrCertPath = filepath.Join(os.TempDir(), acrCertFile)
) )
func main() { func main() {
@@ -21,8 +56,21 @@ func main() {
var ( var (
repo = getenv("PLUGIN_REPO") repo = getenv("PLUGIN_REPO")
registry = getenv("PLUGIN_REGISTRY") registry = getenv("PLUGIN_REGISTRY")
// If these credentials are provided, they will be directly used
// for docker login
username = getenv("SERVICE_PRINCIPAL_CLIENT_ID") username = getenv("SERVICE_PRINCIPAL_CLIENT_ID")
password = getenv("SERVICE_PRINCIPAL_CLIENT_SECRET") password = getenv("SERVICE_PRINCIPAL_CLIENT_SECRET")
// Service principal credentials
clientId = getenv("CLIENT_ID", "AZURE_CLIENT_ID", "AZURE_APP_ID", "PLUGIN_CLIENT_ID")
clientSecret = getenv("CLIENT_SECRET", "PLUGIN_CLIENT_SECRET")
clientCert = getenv("CLIENT_CERTIFICATE", "PLUGIN_CLIENT_CERTIFICATE")
tenantId = getenv("TENANT_ID", "AZURE_TENANT_ID", "PLUGIN_TENANT_ID")
subscriptionId = getenv("SUBSCRIPTION_ID", "PLUGIN_SUBSCRIPTION_ID")
publicUrl = getenv("DAEMON_REGISTRY", "PLUGIN_DAEMON_REGISTRY")
authorityHost = getenv("AZURE_AUTHORITY_HOST", "PLUGIN_AZURE_AUTHORITY_HOST")
idToken = getenv("PLUGIN_OIDC_TOKEN_ID")
) )
// default registry value // default registry value
@@ -30,6 +78,37 @@ func main() {
registry = "azurecr.io" registry = "azurecr.io"
} }
// Get auth if username and password is not specified
if username == "" && password == "" {
// docker login credentials are not provided
var err error
username = defaultUsername
if idToken != "" && clientId != "" && tenantId != "" {
logrus.Debug("Using OIDC authentication flow")
var aadToken string
aadToken, err = azureutil.GetAADAccessTokenViaClientAssertion(context.Background(), tenantId, clientId, idToken, authorityHost)
if err != nil {
logrus.Fatal(err)
}
var p string
p, err = getPublicUrl(aadToken, registry, subscriptionId)
if err == nil {
publicUrl = p
} else {
fmt.Fprintf(os.Stderr, "failed to get public url with error: %s\n", err)
}
password, err = fetchACRToken(tenantId, aadToken, registry)
if err != nil {
logrus.Fatal(err)
}
} else {
password, publicUrl, err = getAuth(clientId, clientSecret, clientCert, tenantId, subscriptionId, registry)
if err != nil {
logrus.Fatal(err)
}
}
}
// must use the fully qualified repo name. If the // must use the fully qualified repo name. If the
// repo name does not have the registry prefix we // repo name does not have the registry prefix we
// should prepend. // should prepend.
@@ -42,6 +121,11 @@ func main() {
os.Setenv("DOCKER_USERNAME", username) os.Setenv("DOCKER_USERNAME", username)
os.Setenv("DOCKER_PASSWORD", password) os.Setenv("DOCKER_PASSWORD", password)
os.Setenv("PLUGIN_REGISTRY_TYPE", "ACR") os.Setenv("PLUGIN_REGISTRY_TYPE", "ACR")
if publicUrl != "" {
// Set this env variable if public URL for artifact is available
// If not, we will fall back to registry url
os.Setenv("ARTIFACT_REGISTRY", publicUrl)
}
// invoke the base docker plugin binary // invoke the base docker plugin binary
cmd := exec.Command(docker.GetDroneDockerExecCmd()) cmd := exec.Command(docker.GetDroneDockerExecCmd())
@@ -53,6 +137,157 @@ func main() {
} }
} }
func getAuth(clientId, clientSecret, clientCert, tenantId, subscriptionId, registry string) (string, string, error) {
// Verify inputs
if tenantId == "" {
return "", "", fmt.Errorf("tenantId cannot be empty for AAD authentication")
}
if clientId == "" {
return "", "", fmt.Errorf("clientId cannot be empty for AAD authentication")
}
if clientSecret == "" && clientCert == "" {
return "", "", fmt.Errorf("one of client secret or client cert should be defined")
}
// Setup cert
if clientCert != "" {
err := setupACRCert(clientCert, acrCertPath)
if err != nil {
errors.Wrap(err, "failed to push setup cert file")
}
}
// Get AZ env
if err := os.Setenv(clientIdEnv, clientId); err != nil {
return "", "", errors.Wrap(err, "failed to set env variable client Id")
}
if err := os.Setenv(clientSecretKeyEnv, clientSecret); err != nil {
return "", "", errors.Wrap(err, "failed to set env variable client secret")
}
if err := os.Setenv(tenantKeyEnv, tenantId); err != nil {
return "", "", errors.Wrap(err, "failed to set env variable tenant Id")
}
if err := os.Setenv(certPathEnv, acrCertPath); err != nil {
return "", "", errors.Wrap(err, "failed to set env variable cert path")
}
env, err := azidentity.NewEnvironmentCredential(nil)
if err != nil {
return "", "", errors.Wrap(err, "failed to get env credentials from azure")
}
os.Unsetenv(clientIdEnv)
os.Unsetenv(clientSecretKeyEnv)
os.Unsetenv(tenantKeyEnv)
os.Unsetenv(certPathEnv)
// Fetch AAD token
policy := policy.TokenRequestOptions{
Scopes: []string{"https://management.azure.com/.default"},
}
aadToken, err := env.GetToken(context.Background(), policy)
if err != nil {
return "", "", errors.Wrap(err, "failed to fetch access token")
}
// Get public URL for artifacts
publicUrl, err := getPublicUrl(aadToken.Token, registry, subscriptionId)
if err != nil {
// execution should not fail because of this error
fmt.Fprintf(os.Stderr, "failed to get public url with error: %s\n", err)
}
// Fetch token
ACRToken, err := fetchACRToken(tenantId, aadToken.Token, registry)
if err != nil {
return "", "", errors.Wrap(err, "failed to fetch ACR token")
}
return ACRToken, publicUrl, nil
}
func fetchACRToken(tenantId, token, registry string) (string, error) {
// oauth exchange
formData := url.Values{
"grant_type": {"access_token"},
"service": {registry},
"tenant": {tenantId},
"access_token": {token},
}
jsonResponse, err := http.PostForm(fmt.Sprintf("https://%s/oauth2/exchange", registry), formData)
if err != nil || jsonResponse == nil {
return "", errors.Wrap(err, "failed to fetch ACR token")
}
// fetch token from response
var response map[string]interface{}
err = json.NewDecoder(jsonResponse.Body).Decode(&response)
if err != nil {
return "", errors.Wrap(err, "failed to decode oauth exchange response")
}
// Parse the refresh_token from the response
if t, found := response["refresh_token"]; found {
if refreshToken, ok := t.(string); ok {
return refreshToken, nil
}
return "", errors.New("failed to cast refresh token from acr")
}
return "", errors.Wrap(err, "refresh token not found in response of oauth exchange call")
}
func setupACRCert(cert, certPath string) error {
decoded, err := base64.StdEncoding.DecodeString(cert)
if err != nil {
return errors.Wrap(err, "failed to base64 decode ACR certificate")
}
err = ioutil.WriteFile(certPath, decoded, 0644)
if err != nil {
return errors.Wrap(err, "failed to write ACR certificate")
}
return nil
}
func getPublicUrl(token, registryUrl, subscriptionId string) (string, error) {
if len(subscriptionId) == 0 || registryUrl == "" {
return "", nil
}
registry := strings.Split(registryUrl, ".")[0]
filter := fmt.Sprintf("resourceType eq 'Microsoft.ContainerRegistry/registries' and name eq '%s'", registry)
params := url.Values{}
params.Add("$filter", filter)
params.Add("api-version", azSubscriptionApiVersion)
params.Add("$select", "id")
url := azSubscriptionBaseUrl + subscriptionId + "/resources?" + params.Encode()
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println(err)
return "", errors.Wrap(err, "failed to create request for getting container registry setting")
}
req.Header.Add("Authorization", "Bearer "+token)
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return "", errors.Wrap(err, "failed to send request for getting container registry setting")
}
defer res.Body.Close()
var response subscriptionUrlResponse
err = json.NewDecoder(res.Body).Decode(&response)
if err != nil {
return "", errors.Wrap(err, "failed to send request for getting container registry setting")
}
if len(response.Value) == 0 {
return "", errors.New("no id present for base url")
}
return basePublicUrl + encodeParam(response.Value[0].ID), nil
}
func encodeParam(s string) string {
return url.QueryEscape(s)
}
func getenv(key ...string) (s string) { func getenv(key ...string) (s string) {
for _, k := range key { for _, k := range key {
s = os.Getenv(k) s = os.Getenv(k)
+32
View File
@@ -0,0 +1,32 @@
package main
import (
"os"
"testing"
)
func TestGetAuthInputValidation(t *testing.T) {
// missing tenant
if _, _, err := getAuth("client", "secret", "", "", "sub", "registry.azurecr.io"); err == nil {
t.Fatalf("expected error for missing tenantId")
}
// missing clientId
if _, _, err := getAuth("", "secret", "", "tenant", "sub", "registry.azurecr.io"); err == nil {
t.Fatalf("expected error for missing clientId")
}
// missing both secret and cert
if _, _, err := getAuth("client", "", "", "tenant", "sub", "registry.azurecr.io"); err == nil {
t.Fatalf("expected error for missing credentials")
}
}
func TestGetenvAuthorityHost(t *testing.T) {
os.Setenv("AZURE_AUTHORITY_HOST", "https://login.microsoftonline.us")
defer os.Unsetenv("AZURE_AUTHORITY_HOST")
got := getenv("AZURE_AUTHORITY_HOST")
if got != "https://login.microsoftonline.us" {
t.Fatalf("expected AZURE_AUTHORITY_HOST to be returned, got %q", got)
}
}
+33
View File
@@ -0,0 +1,33 @@
package main
import (
"strings"
)
// CustomStringSliceFlag is like a regular StringSlice flag but with
// semicolon as a delimiter
type CustomStringSliceFlag struct {
Value []string
}
func (f *CustomStringSliceFlag) GetValue() []string {
if f.Value == nil {
return make([]string, 0)
}
return f.Value
}
func (f *CustomStringSliceFlag) String() string {
if f.Value == nil {
return ""
}
return strings.Join(f.Value, ";")
}
func (f *CustomStringSliceFlag) Set(v string) error {
for _, s := range strings.Split(v, ";") {
s = strings.TrimSpace(s)
f.Value = append(f.Value, s)
}
return nil
}
+98 -27
View File
@@ -33,7 +33,7 @@ func main() {
cli.BoolFlag{ cli.BoolFlag{
Name: "dry-run", Name: "dry-run",
Usage: "dry run disables docker push", Usage: "dry run disables docker push",
EnvVar: "PLUGIN_DRY_RUN", EnvVar: "PLUGIN_DRY_RUN, PLUGIN_NO_PUSH",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "remote.url", Name: "remote.url",
@@ -112,6 +112,12 @@ func main() {
Usage: "don't start the docker daemon", Usage: "don't start the docker daemon",
EnvVar: "PLUGIN_DAEMON_OFF", EnvVar: "PLUGIN_DAEMON_OFF",
}, },
cli.IntFlag{
Name: "daemon.retry-count",
Usage: "number of retry attempts to reach docker daemon",
Value: 15,
EnvVar: "PLUGIN_DAEMON_RETRY_COUNT",
},
cli.StringFlag{ cli.StringFlag{
Name: "dockerfile", Name: "dockerfile",
Usage: "build dockerfile", Usage: "build dockerfile",
@@ -151,6 +157,17 @@ func main() {
Usage: "build args", Usage: "build args",
EnvVar: "PLUGIN_BUILD_ARGS_FROM_ENV", EnvVar: "PLUGIN_BUILD_ARGS_FROM_ENV",
}, },
cli.GenericFlag{
Name: "args-new",
Usage: "build args new",
EnvVar: "PLUGIN_BUILD_ARGS_NEW",
Value: new(CustomStringSliceFlag),
},
cli.BoolFlag{
Name: "plugin-multiple-build-agrs",
Usage: "plugin multiple build agrs",
EnvVar: "PLUGIN_MULTIPLE_BUILD_ARGS",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "quiet", Name: "quiet",
Usage: "quiet docker build", Usage: "quiet docker build",
@@ -222,6 +239,21 @@ func main() {
Usage: "docker password", Usage: "docker password",
EnvVar: "PLUGIN_PASSWORD,DOCKER_PASSWORD", EnvVar: "PLUGIN_PASSWORD,DOCKER_PASSWORD",
}, },
cli.StringFlag{
Name: "docker.baseimageusername",
Usage: "Docker username for base image registry",
EnvVar: "PLUGIN_DOCKER_USERNAME,PLUGIN_BASE_IMAGE_USERNAME,DOCKER_BASE_IMAGE_USERNAME",
},
cli.StringFlag{
Name: "docker.baseimagepassword",
Usage: "Docker password for base image registry",
EnvVar: "PLUGIN_DOCKER_PASSWORD,PLUGIN_BASE_IMAGE_PASSWORD,DOCKER_BASE_IMAGE_PASSWORD",
},
cli.StringFlag{
Name: "docker.baseimageregistry",
Usage: "Docker registry for base image registry",
EnvVar: "PLUGIN_DOCKER_REGISTRY,PLUGIN_BASE_IMAGE_REGISTRY,DOCKER_BASE_IMAGE_REGISTRY",
},
cli.StringFlag{ cli.StringFlag{
Name: "docker.email", Name: "docker.email",
Usage: "docker email", Usage: "docker email",
@@ -297,6 +329,32 @@ func main() {
Usage: "access token", Usage: "access token",
EnvVar: "ACCESS_TOKEN", EnvVar: "ACCESS_TOKEN",
}, },
// Cosign signing configuration
cli.StringFlag{
Name: "cosign.private-key",
Usage: "cosign private key content or file path for signing",
EnvVar: "PLUGIN_COSIGN_PRIVATE_KEY",
},
cli.StringFlag{
Name: "cosign.password",
Usage: "password for encrypted cosign private key",
EnvVar: "PLUGIN_COSIGN_PASSWORD",
},
cli.StringFlag{
Name: "cosign.params",
Usage: "additional cosign parameters (e.g., annotations, flags)",
EnvVar: "PLUGIN_COSIGN_PARAMS",
},
cli.BoolFlag{
Name: "push-only",
Usage: "skip build and only push images",
EnvVar: "PLUGIN_PUSH_ONLY",
},
cli.StringFlag{
Name: "source-image",
Usage: "source image to tag and push (format: repo:tag)",
EnvVar: "PLUGIN_SOURCE_IMAGE",
},
} }
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
@@ -324,32 +382,34 @@ func run(c *cli.Context) error {
CardPath: c.String("drone-card-path"), CardPath: c.String("drone-card-path"),
ArtifactFile: c.String("artifact-file"), ArtifactFile: c.String("artifact-file"),
Build: docker.Build{ Build: docker.Build{
Remote: c.String("remote.url"), Remote: c.String("remote.url"),
Name: c.String("commit.sha"), Name: c.String("commit.sha"),
TempTag: generateTempTag(), TempTag: generateTempTag(),
Dockerfile: c.String("dockerfile"), Dockerfile: c.String("dockerfile"),
Context: c.String("context"), Context: c.String("context"),
Tags: c.StringSlice("tags"), Tags: c.StringSlice("tags"),
Args: c.StringSlice("args"), Args: c.StringSlice("args"),
ArgsEnv: c.StringSlice("args-from-env"), ArgsEnv: c.StringSlice("args-from-env"),
Target: c.String("target"), ArgsNew: c.Generic("args-new").(*CustomStringSliceFlag).GetValue(),
Squash: c.Bool("squash"), IsMultipleBuildArgs: c.Bool("plugin-multiple-build-agrs"),
Pull: c.BoolT("pull-image"), Target: c.String("target"),
CacheFrom: c.StringSlice("cache-from"), Squash: c.Bool("squash"),
Compress: c.Bool("compress"), Pull: c.BoolT("pull-image"),
Repo: c.String("repo"), CacheFrom: c.StringSlice("cache-from"),
Labels: c.StringSlice("custom-labels"), Compress: c.Bool("compress"),
LabelSchema: c.StringSlice("label-schema"), Repo: c.String("repo"),
AutoLabel: c.BoolT("auto-label"), Labels: c.StringSlice("custom-labels"),
Link: c.String("link"), LabelSchema: c.StringSlice("label-schema"),
NoCache: c.Bool("no-cache"), AutoLabel: c.BoolT("auto-label"),
Secret: c.String("secret"), Link: c.String("link"),
SecretEnvs: c.StringSlice("secrets-from-env"), NoCache: c.Bool("no-cache"),
SecretFiles: c.StringSlice("secrets-from-file"), Secret: c.String("secret"),
AddHost: c.StringSlice("add-host"), SecretEnvs: c.StringSlice("secrets-from-env"),
Quiet: c.Bool("quiet"), SecretFiles: c.StringSlice("secrets-from-file"),
Platform: c.String("platform"), AddHost: c.StringSlice("add-host"),
SSHAgentKey: c.String("ssh-agent-key"), Quiet: c.Bool("quiet"),
Platform: c.String("platform"),
SSHAgentKey: c.String("ssh-agent-key"),
}, },
Daemon: docker.Daemon{ Daemon: docker.Daemon{
Registry: c.String("docker.registry"), Registry: c.String("docker.registry"),
@@ -365,8 +425,19 @@ func run(c *cli.Context) error {
DNSSearch: c.StringSlice("daemon.dns-search"), DNSSearch: c.StringSlice("daemon.dns-search"),
MTU: c.String("daemon.mtu"), MTU: c.String("daemon.mtu"),
Experimental: c.Bool("daemon.experimental"), Experimental: c.Bool("daemon.experimental"),
RetryCount: c.Int("daemon.retry-count"),
RegistryType: registryType, RegistryType: registryType,
}, },
BaseImageRegistry: c.String("docker.baseimageregistry"),
BaseImageUsername: c.String("docker.baseimageusername"),
BaseImagePassword: c.String("docker.baseimagepassword"),
Cosign: docker.CosignConfig{
PrivateKey: c.String("cosign.private-key"),
Password: c.String("cosign.password"),
Params: c.String("cosign.params"),
},
PushOnly: c.Bool("push-only"),
SourceImage: c.String("source-image"),
} }
if c.Bool("tags.auto") { if c.Bool("tags.auto") {
+142 -72
View File
@@ -1,50 +1,51 @@
package main package main
import ( import (
"context"
"encoding/base64" "encoding/base64"
"errors"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
"os/exec" "os/exec"
"strconv" "strconv"
"strings" "strings"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/ecr"
ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
docker "github.com/drone-plugins/drone-docker" docker "github.com/drone-plugins/drone-docker"
) )
const defaultRegion = "us-east-1" const defaultRegion = "us-east-1"
func main() { func main() {
// Load env-file if it exists first
if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" { if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" {
godotenv.Load(env) godotenv.Load(env)
} }
var ( var (
repo = getenv("PLUGIN_REPO") repo = getenv("PLUGIN_REPO")
registry = getenv("PLUGIN_REGISTRY") registry = getenv("PLUGIN_REGISTRY")
region = getenv("PLUGIN_REGION", "ECR_REGION", "AWS_REGION") region = getenv("PLUGIN_REGION", "ECR_REGION", "AWS_REGION")
key = getenv("PLUGIN_ACCESS_KEY", "ECR_ACCESS_KEY", "AWS_ACCESS_KEY_ID") key = getenv("PLUGIN_ACCESS_KEY", "ECR_ACCESS_KEY", "AWS_ACCESS_KEY_ID")
secret = getenv("PLUGIN_SECRET_KEY", "ECR_SECRET_KEY", "AWS_SECRET_ACCESS_KEY") secret = getenv("PLUGIN_SECRET_KEY", "ECR_SECRET_KEY", "AWS_SECRET_ACCESS_KEY")
create = parseBoolOrDefault(false, getenv("PLUGIN_CREATE_REPOSITORY", "ECR_CREATE_REPOSITORY")) create = parseBoolOrDefault(false, getenv("PLUGIN_CREATE_REPOSITORY", "ECR_CREATE_REPOSITORY"))
lifecyclePolicy = getenv("PLUGIN_LIFECYCLE_POLICY") lifecyclePolicy = getenv("PLUGIN_LIFECYCLE_POLICY")
repositoryPolicy = getenv("PLUGIN_REPOSITORY_POLICY") repositoryPolicy = getenv("PLUGIN_REPOSITORY_POLICY")
assumeRole = getenv("PLUGIN_ASSUME_ROLE") assumeRole = getenv("PLUGIN_ASSUME_ROLE")
externalId = getenv("PLUGIN_EXTERNAL_ID") externalId = getenv("PLUGIN_EXTERNAL_ID")
scanOnPush = parseBoolOrDefault(false, getenv("PLUGIN_SCAN_ON_PUSH")) scanOnPush = parseBoolOrDefault(false, getenv("PLUGIN_SCAN_ON_PUSH"))
idToken = os.Getenv("PLUGIN_OIDC_TOKEN_ID")
skipPushIfTagExists = parseBoolOrDefault(false, getenv("PLUGIN_SKIP_PUSH_IF_TAG_EXISTS"))
) )
// set the region
if region == "" { if region == "" {
region = defaultRegion region = defaultRegion
} }
@@ -56,13 +57,15 @@ func main() {
os.Setenv("AWS_SECRET_ACCESS_KEY", secret) os.Setenv("AWS_SECRET_ACCESS_KEY", secret)
} }
sess, err := session.NewSession(&aws.Config{Region: &region}) ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region))
if err != nil { if err != nil {
log.Fatal(fmt.Sprintf("error creating aws session: %v", err)) log.Fatal(fmt.Sprintf("error creating aws config: %v", err))
} }
svc := getECRClient(sess, assumeRole, externalId) svc := getECRClient(cfg, assumeRole, externalId, idToken)
username, password, defaultRegistry, err := getAuthInfo(svc) username, password, defaultRegistry, err := getAuthInfo(ctx, svc)
if registry == "" { if registry == "" {
registry = defaultRegistry registry = defaultRegistry
@@ -77,32 +80,32 @@ func main() {
} }
if create { if create {
err = ensureRepoExists(svc, trimHostname(repo, registry), scanOnPush) err = ensureRepoExists(ctx, svc, trimHostname(repo, registry), scanOnPush)
if err != nil { if err != nil {
log.Fatal(fmt.Sprintf("error creating ECR repo: %v", err)) log.Fatal(fmt.Sprintf("error creating ECR repo: %v", err))
} }
err = updateImageScannningConfig(svc, trimHostname(repo, registry), scanOnPush) err = updateImageScanningConfig(ctx, svc, trimHostname(repo, registry), scanOnPush)
if err != nil { if err != nil {
log.Fatal(fmt.Sprintf("error updating scan on push for ECR repo: %v", err)) log.Fatal(fmt.Sprintf("error updating scan on push for ECR repo: %v", err))
} }
} }
if lifecyclePolicy != "" { if lifecyclePolicy != "" {
p, err := ioutil.ReadFile(lifecyclePolicy) p, err := os.ReadFile(lifecyclePolicy)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
if err := uploadLifeCyclePolicy(svc, string(p), trimHostname(repo, registry)); err != nil { if err := uploadLifeCyclePolicy(ctx, svc, string(p), trimHostname(repo, registry)); err != nil {
log.Fatal(fmt.Sprintf("error uploading ECR lifecycle policy: %v", err)) log.Fatal(fmt.Sprintf("error uploading ECR lifecycle policy: %v", err))
} }
} }
if repositoryPolicy != "" { if repositoryPolicy != "" {
p, err := ioutil.ReadFile(repositoryPolicy) p, err := os.ReadFile(repositoryPolicy)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
if err := uploadRepositoryPolicy(svc, string(p), trimHostname(repo, registry)); err != nil { if err := uploadRepositoryPolicy(ctx, svc, string(p), trimHostname(repo, registry)); err != nil {
log.Fatal(fmt.Sprintf("error uploading ECR repository policy. %v", err)) log.Fatal(fmt.Sprintf("error uploading ECR repository policy. %v", err))
} }
} }
@@ -113,7 +116,33 @@ func main() {
os.Setenv("DOCKER_PASSWORD", password) os.Setenv("DOCKER_PASSWORD", password)
os.Setenv("PLUGIN_REGISTRY_TYPE", "ECR") os.Setenv("PLUGIN_REGISTRY_TYPE", "ECR")
// invoke the base docker plugin binary if skipPushIfTagExists {
tagInput := getenv("PLUGIN_TAG", "PLUGIN_TAGS")
var tags []string
if tagInput == "" {
tags = []string{"latest"}
} else {
for _, t := range strings.Split(tagInput, ",") {
trimmed := strings.TrimSpace(t)
if trimmed != "" {
tags = append(tags, trimmed)
}
}
}
repositoryName := trimHostname(repo, registry)
for _, t := range tags {
exists, err := tagExists(ctx, svc, repositoryName, t)
if err != nil {
logrus.Fatalf("Error checking if image exists for tag %s: %v", t, err)
}
if exists {
logrus.Infof("%s:%s: Image tag exists. Skipping push.", repo, t)
os.Exit(0)
}
}
}
cmd := exec.Command(docker.GetDroneDockerExecCmd()) cmd := exec.Command(docker.GetDroneDockerExecCmd())
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -128,57 +157,63 @@ func trimHostname(repo, registry string) string {
return repo return repo
} }
func ensureRepoExists(svc *ecr.ECR, name string, scanOnPush bool) (err error) { func ensureRepoExists(ctx context.Context, svc *ecr.Client, name string, scanOnPush bool) error {
input := &ecr.CreateRepositoryInput{} _, err := svc.CreateRepository(ctx, &ecr.CreateRepositoryInput{
input.SetRepositoryName(name) RepositoryName: aws.String(name),
input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush}) ImageScanningConfiguration: &ecrtypes.ImageScanningConfiguration{
_, err = svc.CreateRepository(input) ScanOnPush: scanOnPush,
},
})
if err != nil { if err != nil {
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ecr.ErrCodeRepositoryAlreadyExistsException { var rae *ecrtypes.RepositoryAlreadyExistsException
// eat it, we skip checking for existing to save two requests if errors.As(err, &rae) {
err = nil return nil
} }
return err
} }
return nil
return
} }
func updateImageScannningConfig(svc *ecr.ECR, name string, scanOnPush bool) (err error) { func updateImageScanningConfig(ctx context.Context, svc *ecr.Client, name string, scanOnPush bool) error {
input := &ecr.PutImageScanningConfigurationInput{} _, err := svc.PutImageScanningConfiguration(ctx, &ecr.PutImageScanningConfigurationInput{
input.SetRepositoryName(name) RepositoryName: aws.String(name),
input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush}) ImageScanningConfiguration: &ecrtypes.ImageScanningConfiguration{
_, err = svc.PutImageScanningConfiguration(input) ScanOnPush: scanOnPush,
},
})
return err return err
} }
func uploadLifeCyclePolicy(svc *ecr.ECR, lifecyclePolicy string, name string) (err error) { func uploadLifeCyclePolicy(ctx context.Context, svc *ecr.Client, lifecyclePolicy string, name string) error {
input := &ecr.PutLifecyclePolicyInput{} _, err := svc.PutLifecyclePolicy(ctx, &ecr.PutLifecyclePolicyInput{
input.SetLifecyclePolicyText(lifecyclePolicy) LifecyclePolicyText: aws.String(lifecyclePolicy),
input.SetRepositoryName(name) RepositoryName: aws.String(name),
_, err = svc.PutLifecyclePolicy(input) })
return err return err
} }
func uploadRepositoryPolicy(svc *ecr.ECR, repositoryPolicy string, name string) (err error) { func uploadRepositoryPolicy(ctx context.Context, svc *ecr.Client, repositoryPolicy string, name string) error {
input := &ecr.SetRepositoryPolicyInput{} _, err := svc.SetRepositoryPolicy(ctx, &ecr.SetRepositoryPolicyInput{
input.SetPolicyText(repositoryPolicy) PolicyText: aws.String(repositoryPolicy),
input.SetRepositoryName(name) RepositoryName: aws.String(name),
_, err = svc.SetRepositoryPolicy(input) })
return err return err
} }
func getAuthInfo(svc *ecr.ECR) (username, password, registry string, err error) { func getAuthInfo(ctx context.Context, svc *ecr.Client) (username, password, registry string, err error) {
var result *ecr.GetAuthorizationTokenOutput var result *ecr.GetAuthorizationTokenOutput
var decoded []byte var decoded []byte
result, err = svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{}) result, err = svc.GetAuthorizationToken(ctx, &ecr.GetAuthorizationTokenInput{})
if err != nil { if err != nil {
return return
} }
if len(result.AuthorizationData) == 0 {
err = fmt.Errorf("no authorization data returned from ECR")
return
}
auth := result.AuthorizationData[0] auth := result.AuthorizationData[0]
token := *auth.AuthorizationToken token := *auth.AuthorizationToken
decoded, err = base64.StdEncoding.DecodeString(token) decoded, err = base64.StdEncoding.DecodeString(token)
@@ -187,7 +222,11 @@ func getAuthInfo(svc *ecr.ECR) (username, password, registry string, err error)
} }
registry = strings.TrimPrefix(*auth.ProxyEndpoint, "https://") registry = strings.TrimPrefix(*auth.ProxyEndpoint, "https://")
creds := strings.Split(string(decoded), ":") creds := strings.SplitN(string(decoded), ":", 2)
if len(creds) < 2 {
err = fmt.Errorf("invalid ECR authorization token format")
return
}
username = creds[0] username = creds[0]
password = creds[1] password = creds[1]
return return
@@ -199,7 +238,6 @@ func parseBoolOrDefault(defaultValue bool, s string) (result bool) {
if err != nil { if err != nil {
result = defaultValue result = defaultValue
} }
return return
} }
@@ -213,19 +251,51 @@ func getenv(key ...string) (s string) {
return return
} }
func getECRClient(sess *session.Session, role string, externalId string) *ecr.ECR { func getECRClient(cfg aws.Config, role string, externalId string, idToken string) *ecr.Client {
if role == "" { if role == "" {
return ecr.New(sess) return ecr.NewFromConfig(cfg)
} }
stsSvc := sts.NewFromConfig(cfg)
if idToken != "" {
provider := stscreds.NewWebIdentityRoleProvider(stsSvc, role, identityToken(idToken))
cfg.Credentials = aws.NewCredentialsCache(provider)
return ecr.NewFromConfig(cfg)
}
var provider *stscreds.AssumeRoleProvider
if externalId != "" { if externalId != "" {
return ecr.New(sess, &aws.Config{ provider = stscreds.NewAssumeRoleProvider(stsSvc, role, func(o *stscreds.AssumeRoleOptions) {
Credentials: stscreds.NewCredentials(sess, role, func(p *stscreds.AssumeRoleProvider) { o.ExternalID = &externalId
p.ExternalID = &externalId
}),
}) })
} else { } else {
return ecr.New(sess, &aws.Config{ provider = stscreds.NewAssumeRoleProvider(stsSvc, role)
Credentials: stscreds.NewCredentials(sess, role),
})
} }
cfg.Credentials = aws.NewCredentialsCache(provider)
return ecr.NewFromConfig(cfg)
}
func tagExists(ctx context.Context, svc *ecr.Client, repository, tag string) (bool, error) {
input := &ecr.DescribeImagesInput{
RepositoryName: aws.String(repository),
ImageIds: []ecrtypes.ImageIdentifier{
{ImageTag: aws.String(tag)},
},
}
output, err := svc.DescribeImages(ctx, input)
if err != nil {
var inf *ecrtypes.ImageNotFoundException
if errors.As(err, &inf) {
return false, nil
}
return false, err
}
return len(output.ImageDetails) > 0, nil
}
type identityToken string
func (t identityToken) GetIdentityToken() ([]byte, error) {
return []byte(t), nil
} }
+165
View File
@@ -0,0 +1,165 @@
package main
import (
"context"
"encoding/base64"
"fmt"
"log"
"os"
"os/exec"
"path"
"strconv"
"strings"
docker "github.com/drone-plugins/drone-docker"
"github.com/drone-plugins/drone-docker/internal/gcp"
"github.com/joho/godotenv"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
type Config struct {
Repo string
Registry string
Password string
WorkloadIdentity bool
Username string
AccessToken string
}
type staticTokenSource struct {
token *oauth2.Token
}
func (s *staticTokenSource) Token() (*oauth2.Token, error) {
return s.token, nil
}
func loadConfig() Config {
// Default username
username := "_json_key"
var config Config
// Load env-file if it exists
if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" {
if err := godotenv.Load(env); err != nil {
log.Fatalf("Error loading .env file: %v", err)
}
}
idToken := getenv("PLUGIN_OIDC_TOKEN_ID")
projectId := getenv("PLUGIN_PROJECT_NUMBER")
poolId := getenv("PLUGIN_POOL_ID")
providerId := getenv("PLUGIN_PROVIDER_ID")
serviceAccountEmail := getenv("PLUGIN_SERVICE_ACCOUNT_EMAIL")
if idToken != "" && projectId != "" && poolId != "" && providerId != "" && serviceAccountEmail != "" {
federalToken, err := gcp.GetFederalToken(idToken, projectId, poolId, providerId)
if err != nil {
logrus.Fatalf("Error (getFederalToken): %s", err)
}
accessToken, err := gcp.GetGoogleCloudAccessToken(federalToken, serviceAccountEmail)
if err != nil {
logrus.Fatalf("Error (getGoogleCloudAccessToken): %s", err)
}
config.AccessToken = accessToken
} else {
password := getenv(
"PLUGIN_JSON_KEY",
"GCR_JSON_KEY",
"GOOGLE_CREDENTIALS",
"TOKEN",
)
config.WorkloadIdentity = parseBoolOrDefault(false, getenv("PLUGIN_WORKLOAD_IDENTITY"))
config.Username, config.Password = setUsernameAndPassword(username, password, config.WorkloadIdentity)
}
location := getenv("PLUGIN_LOCATION")
repo := getenv("PLUGIN_REPO")
registry := getenv("PLUGIN_REGISTRY")
if registry == "" {
registry = fmt.Sprintf("%s-docker.pkg.dev", location)
}
if !strings.HasPrefix(repo, registry) {
repo = path.Join(registry, repo)
}
config.Repo = repo
config.Registry = registry
return config
}
func main() {
config := loadConfig()
if config.AccessToken != "" {
os.Setenv("ACCESS_TOKEN", config.AccessToken)
} else if config.Username != "" && config.Password != "" {
os.Setenv("DOCKER_USERNAME", config.Username)
os.Setenv("DOCKER_PASSWORD", config.Password)
}
os.Setenv("PLUGIN_REPO", config.Repo)
os.Setenv("PLUGIN_REGISTRY", config.Registry)
// invoke the base docker plugin binary
cmd := exec.Command(docker.GetDroneDockerExecCmd())
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
logrus.Fatal(err)
}
}
func getOauthToken(data []byte) (s string) {
scopes := []string{
"https://www.googleapis.com/auth/cloud-platform",
}
ctx := context.Background()
credentials, err := google.CredentialsFromJSON(ctx, data, scopes...)
if err == nil {
token, err := credentials.TokenSource.Token()
if err == nil {
return token.AccessToken
}
}
return
}
func setUsernameAndPassword(user string, pass string, workloadIdentity bool) (u string, p string) {
// decode the token if base64 encoded
decoded, err := base64.StdEncoding.DecodeString(pass)
if err == nil {
pass = string(decoded)
}
// get oauth token and set username if using workload identity
if workloadIdentity {
data := []byte(pass)
pass = getOauthToken(data)
user = "oauth2accesstoken"
}
return user, pass
}
func parseBoolOrDefault(defaultValue bool, s string) (result bool) {
var err error
result, err = strconv.ParseBool(s)
if err != nil {
result = defaultValue
}
return
}
func getenv(key ...string) (s string) {
for _, k := range key {
s = os.Getenv(k)
if s != "" {
return
}
}
return
}
+4 -77
View File
@@ -3,7 +3,6 @@ package main
import ( import (
"context" "context"
"encoding/base64" "encoding/base64"
"fmt"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@@ -12,14 +11,11 @@ import (
"strings" "strings"
docker "github.com/drone-plugins/drone-docker" docker "github.com/drone-plugins/drone-docker"
"github.com/drone-plugins/drone-docker/internal/gcp"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
"google.golang.org/api/iamcredentials/v1"
"google.golang.org/api/option"
"google.golang.org/api/sts/v1"
) )
type Config struct { type Config struct {
@@ -28,18 +24,9 @@ type Config struct {
Password string Password string
WorkloadIdentity bool WorkloadIdentity bool
Username string Username string
RegistryType string
AccessToken string AccessToken string
} }
type staticTokenSource struct {
token *oauth2.Token
}
func (s *staticTokenSource) Token() (*oauth2.Token, error) {
return s.token, nil
}
func loadConfig() Config { func loadConfig() Config {
// Default username // Default username
username := "_json_key" username := "_json_key"
@@ -59,11 +46,11 @@ func loadConfig() Config {
serviceAccountEmail := getenv("PLUGIN_SERVICE_ACCOUNT_EMAIL") serviceAccountEmail := getenv("PLUGIN_SERVICE_ACCOUNT_EMAIL")
if idToken != "" && projectId != "" && poolId != "" && providerId != "" && serviceAccountEmail != "" { if idToken != "" && projectId != "" && poolId != "" && providerId != "" && serviceAccountEmail != "" {
federalToken, err := getFederalToken(idToken, projectId, poolId, providerId) federalToken, err := gcp.GetFederalToken(idToken, projectId, poolId, providerId)
if err != nil { if err != nil {
logrus.Fatalf("Error (getFederalToken): %s", err) logrus.Fatalf("Error (getFederalToken): %s", err)
} }
accessToken, err := getGoogleCloudAccessToken(federalToken, serviceAccountEmail) accessToken, err := gcp.GetGoogleCloudAccessToken(federalToken, serviceAccountEmail)
if err != nil { if err != nil {
logrus.Fatalf("Error (getGoogleCloudAccessToken): %s", err) logrus.Fatalf("Error (getGoogleCloudAccessToken): %s", err)
} }
@@ -79,9 +66,7 @@ func loadConfig() Config {
config.Username, config.Password = setUsernameAndPassword(username, password, config.WorkloadIdentity) config.Username, config.Password = setUsernameAndPassword(username, password, config.WorkloadIdentity)
} }
location := getenv("PLUGIN_LOCATION")
repo := getenv("PLUGIN_REPO") repo := getenv("PLUGIN_REPO")
registryType := getenv("PLUGIN_REGISTRY_TYPE") registryType := getenv("PLUGIN_REGISTRY_TYPE")
if registryType == "" { if registryType == "" {
registryType = "GCR" registryType = "GCR"
@@ -89,17 +74,7 @@ func loadConfig() Config {
registry := getenv("PLUGIN_REGISTRY") registry := getenv("PLUGIN_REGISTRY")
if registry == "" { if registry == "" {
switch registryType { registry = "gcr.io"
case "GCR":
registry = "gcr.io"
case "GAR":
if location == "" {
logrus.Fatalf("Error: For REGISTRY_TYPE of GAR, LOCATION must be set")
}
registry = fmt.Sprintf("%s-docker.pkg.dev", location)
default:
logrus.Fatalf("Unsupported registry type: %s", registryType)
}
} }
if !strings.HasPrefix(repo, registry) { if !strings.HasPrefix(repo, registry) {
@@ -107,7 +82,6 @@ func loadConfig() Config {
} }
config.Repo = repo config.Repo = repo
config.Registry = registry config.Registry = registry
config.RegistryType = registryType
return config return config
} }
@@ -122,7 +96,6 @@ func main() {
os.Setenv("PLUGIN_REPO", config.Repo) os.Setenv("PLUGIN_REPO", config.Repo)
os.Setenv("PLUGIN_REGISTRY", config.Registry) os.Setenv("PLUGIN_REGISTRY", config.Registry)
os.Setenv("PLUGIN_REGISTRY_TYPE", config.RegistryType)
// invoke the base docker plugin binary // invoke the base docker plugin binary
cmd := exec.Command(docker.GetDroneDockerExecCmd()) cmd := exec.Command(docker.GetDroneDockerExecCmd())
@@ -183,49 +156,3 @@ func getenv(key ...string) (s string) {
} }
return return
} }
func getFederalToken(idToken, projectNumber, poolId, providerId string) (string, error) {
ctx := context.Background()
stsService, err := sts.NewService(ctx, option.WithoutAuthentication())
if err != nil {
return "", err
}
audience := fmt.Sprintf("//iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/providers/%s", projectNumber, poolId, providerId)
tokenRequest := &sts.GoogleIdentityStsV1ExchangeTokenRequest{
GrantType: "urn:ietf:params:oauth:grant-type:token-exchange",
SubjectToken: idToken,
Audience: audience,
Scope: "https://www.googleapis.com/auth/cloud-platform",
RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token",
SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
}
tokenResponse, err := stsService.V1.Token(tokenRequest).Do()
if err != nil {
return "", err
}
return tokenResponse.AccessToken, nil
}
func getGoogleCloudAccessToken(federatedToken string, serviceAccountEmail string) (string, error) {
ctx := context.Background()
tokenSource := &staticTokenSource{
token: &oauth2.Token{AccessToken: federatedToken},
}
service, err := iamcredentials.NewService(ctx, option.WithTokenSource(tokenSource))
if err != nil {
return "", err
}
name := "projects/-/serviceAccounts/" + serviceAccountEmail
rb := &iamcredentials.GenerateAccessTokenRequest{
Scope: []string{"https://www.googleapis.com/auth/cloud-platform"},
}
resp, err := service.Projects.ServiceAccounts.GenerateAccessToken(name, rb).Do()
if err != nil {
return "", err
}
return resp.AccessToken, nil
}
+1
View File
@@ -11,6 +11,7 @@ import (
const dockerExe = "/usr/local/bin/docker" const dockerExe = "/usr/local/bin/docker"
const dockerdExe = "/usr/local/bin/dockerd" const dockerdExe = "/usr/local/bin/dockerd"
const dockerHome = "/root/.docker/" const dockerHome = "/root/.docker/"
const cosignExe = "/usr/local/bin/cosign"
func (p Plugin) startDaemon() { func (p Plugin) startDaemon() {
cmd := commandDaemon(p.Daemon) cmd := commandDaemon(p.Daemon)
+2
View File
@@ -1,3 +1,4 @@
//go:build windows
// +build windows // +build windows
package docker package docker
@@ -5,6 +6,7 @@ package docker
const dockerExe = "C:\\bin\\docker.exe" const dockerExe = "C:\\bin\\docker.exe"
const dockerdExe = "" const dockerdExe = ""
const dockerHome = "C:\\ProgramData\\docker\\" const dockerHome = "C:\\ProgramData\\docker\\"
const cosignExe = "C:\\bin\\cosign.exe"
func (p Plugin) startDaemon() { func (p Plugin) startDaemon() {
// this is a no-op on windows // this is a no-op on windows
+488 -42
View File
@@ -10,6 +10,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/drone-plugins/drone-docker/internal/docker"
"github.com/drone-plugins/drone-plugin-lib/drone" "github.com/drone-plugins/drone-plugin-lib/drone"
) )
@@ -29,6 +30,7 @@ type (
MTU string // Docker daemon mtu setting MTU string // Docker daemon mtu setting
IPv6 bool // Docker daemon IPv6 networking IPv6 bool // Docker daemon IPv6 networking
Experimental bool // Docker daemon enable experimental mode Experimental bool // Docker daemon enable experimental mode
RetryCount int // Number of retry attempts to reach Docker daemon
RegistryType drone.RegistryType // Docker registry type RegistryType drone.RegistryType // Docker registry type
} }
@@ -44,44 +46,59 @@ type (
// Build defines Docker build parameters. // Build defines Docker build parameters.
Build struct { Build struct {
Remote string // Git remote URL Remote string // Git remote URL
Name string // Docker build using default named tag Name string // Docker build using default named tag
TempTag string // Temporary tag used during docker build TempTag string // Temporary tag used during docker build
Dockerfile string // Docker build Dockerfile Dockerfile string // Docker build Dockerfile
Context string // Docker build context Context string // Docker build context
Tags []string // Docker build tags Tags []string // Docker build tags
Args []string // Docker build args Args []string // Docker build args
ArgsEnv []string // Docker build args from env ArgsEnv []string // Docker build args from env
Target string // Docker build target ArgsNew []string // docker build args which has comma seperated values
Squash bool // Docker build squash IsMultipleBuildArgs bool // env variable for fall back to old build args
Pull bool // Docker build pull Target string // Docker build target
CacheFrom []string // Docker build cache-from Squash bool // Docker build squash
Compress bool // Docker build compress Pull bool // Docker build pull
Repo string // Docker build repository CacheFrom []string // Docker build cache-from
LabelSchema []string // label-schema Label map Compress bool // Docker build compress
AutoLabel bool // auto-label bool Repo string // Docker build repository
Labels []string // Label map LabelSchema []string // label-schema Label map
Link string // Git repo link AutoLabel bool // auto-label bool
NoCache bool // Docker build no-cache Labels []string // Label map
Secret string // secret keypair Link string // Git repo link
SecretEnvs []string // Docker build secrets with env var as source NoCache bool // Docker build no-cache
SecretFiles []string // Docker build secrets with file as source Secret string // secret keypair
AddHost []string // Docker build add-host SecretEnvs []string // Docker build secrets with env var as source
Quiet bool // Docker build quiet SecretFiles []string // Docker build secrets with file as source
Platform string // Docker build platform AddHost []string // Docker build add-host
SSHAgentKey string // Docker build ssh agent key Quiet bool // Docker build quiet
SSHKeyPath string // Docker build ssh key path Platform string // Docker build platform
SSHAgentKey string // Docker build ssh agent key
SSHKeyPath string // Docker build ssh key path
}
// CosignConfig defines Cosign signing parameters.
CosignConfig struct {
PrivateKey string // Private key content (PEM format) or file path
Password string // Password for encrypted private keys
Params string // Additional cosign parameters
} }
// Plugin defines the Docker plugin parameters. // Plugin defines the Docker plugin parameters.
Plugin struct { Plugin struct {
Login Login // Docker login configuration Login Login // Docker login configuration
Build Build // Docker build configuration Build Build // Docker build configuration
Daemon Daemon // Docker daemon configuration Daemon Daemon // Docker daemon configuration
Dryrun bool // Docker push is skipped Cosign CosignConfig // Cosign signing configuration
Cleanup bool // Docker purge is enabled Dryrun bool // Docker push is skipped
CardPath string // Card path to write file to Cleanup bool // Docker purge is enabled
ArtifactFile string // Artifact path to write file to CardPath string // Card path to write file to
ArtifactFile string // Artifact path to write file to
BaseImageRegistry string // Docker registry to pull base image
BaseImageUsername string // Docker registry username to pull base image
BaseImagePassword string // Docker registry password to pull base image
PushOnly bool // Push only mode, skips build process
SourceImage string // Source image to push (optional)
} }
Card []struct { Card []struct {
@@ -121,14 +138,18 @@ func (p Plugin) Exec() error {
// poll the docker daemon until it is started. This ensures the daemon is // poll the docker daemon until it is started. This ensures the daemon is
// ready to accept connections before we proceed. // ready to accept connections before we proceed.
maxRetries := p.Daemon.RetryCount
if maxRetries <= 0 {
maxRetries = 15 // default value
}
for i := 0; ; i++ { for i := 0; ; i++ {
cmd := commandInfo() cmd := commandInfo()
err := cmd.Run() err := cmd.Run()
if err == nil { if err == nil {
break break
} }
if i == 15 { if i == maxRetries {
fmt.Println("Unable to reach Docker Daemon after 15 attempts.") fmt.Printf("Unable to reach Docker Daemon after %d attempts.\n", maxRetries)
break break
} }
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
@@ -160,6 +181,39 @@ func (p Plugin) Exec() error {
} }
} }
// instead of writing to config file directly, using docker's login func
// is better to integrate with various credential helpers,
// it also handles different registry specific logic in a better way,
// as opposed to config write where different registries need to be addressed differently.
// It handles any changes in the authentication process across different Docker versions.
if p.BaseImageRegistry != "" {
if p.BaseImageUsername == "" {
fmt.Printf("Username cannot be empty. The base image connector requires authenticated access. Please either use an authenticated connector, or remove the base image connector.")
}
if p.BaseImagePassword == "" {
fmt.Printf("Password cannot be empty. The base image connector requires authenticated access. Please either use an authenticated connector, or remove the base image connector.")
}
var baseConnectorLogin Login
baseConnectorLogin.Registry = p.BaseImageRegistry
baseConnectorLogin.Username = p.BaseImageUsername
baseConnectorLogin.Password = p.BaseImagePassword
cmd := commandLogin(baseConnectorLogin)
raw, err := cmd.CombinedOutput()
if err != nil {
out := string(raw)
out = strings.Replace(out, "WARNING! Using --password via the CLI is insecure. Use --password-stdin.", "", -1)
fmt.Println(out)
return fmt.Errorf("Error authenticating base connector: exit status 1")
}
} else if !p.PushOnly {
// Skip base image connector warning in push-only mode (not pulling anything)
fmt.Println("\033[33mTo ensure consistent and reliable pipeline execution, we recommend setting up a Base Image Connector.\033[0m\n" +
"\033[33mWhile optional at this time, configuring it helps prevent failures caused by Docker Hub's rate limits.\033[0m")
}
// login to the Docker registry // login to the Docker registry
if p.Login.Password != "" { if p.Login.Password != "" {
cmd := commandLogin(p.Login) cmd := commandLogin(p.Login)
@@ -183,6 +237,16 @@ func (p Plugin) Exec() error {
} }
} }
// Enforce mutual exclusivity: push-only and dry-run cannot be used together
if p.PushOnly && p.Dryrun {
return fmt.Errorf("conflict: push-only and dry-run cannot be used together")
}
// Handle push-only mode if requested
if p.PushOnly {
return p.pushOnly()
}
if p.Build.Squash && !p.Daemon.Experimental { if p.Build.Squash && !p.Daemon.Experimental {
fmt.Println("Squash build flag is only available when Docker deamon is started with experimental flag. Ignoring...") fmt.Println("Squash build flag is only available when Docker deamon is started with experimental flag. Ignoring...")
p.Build.Squash = false p.Build.Squash = false
@@ -211,6 +275,14 @@ func (p Plugin) Exec() error {
cmds = append(cmds, commandBuild(p.Build)) // docker build cmds = append(cmds, commandBuild(p.Build)) // docker build
// Validate cosign configuration if present
if p.shouldSignWithCosign() {
if err := validateCosignConfig(p.Cosign); err != nil {
return fmt.Errorf("cosign validation failed: %w", err)
}
fmt.Println("🔐 Cosign signing enabled - images will be signed after push")
}
for _, tag := range p.Build.Tags { for _, tag := range p.Build.Tags {
cmds = append(cmds, commandTag(p.Build, tag)) // docker tag cmds = append(cmds, commandTag(p.Build, tag)) // docker tag
@@ -252,6 +324,31 @@ func (p Plugin) Exec() error {
} }
} }
// Handle cosign signing after all commands complete (like artifact generation)
if p.shouldSignWithCosign() && !p.Dryrun {
// Set up environment variables for cosign
os.Setenv("COSIGN_YES", "true")
if digest, err := getDigest(p.Build.TempTag); err == nil {
fmt.Printf("🔐 Found image digest: %s\n", digest)
// Sign with digest reference
imageRef := fmt.Sprintf("%s@%s", p.Build.Repo, digest)
cosignCmd := createCosignCommand(imageRef, p.Cosign)
executeCosignCommand(cosignCmd)
} else {
fmt.Printf("⚠️ WARNING: Could not get image digest for cosign signing: %s\n", err)
fmt.Printf(" Falling back to tag-based signing\n")
// Fall back to tag-based signing for each tag
for _, tag := range p.Build.Tags {
imageRef := fmt.Sprintf("%s:%s", p.Build.Repo, tag)
cosignCmd := createCosignCommand(imageRef, p.Cosign)
executeCosignCommand(cosignCmd)
}
}
}
// execute cleanup routines in batch mode // execute cleanup routines in batch mode
if p.Cleanup { if p.Cleanup {
// clear the slice // clear the slice
@@ -270,6 +367,35 @@ func (p Plugin) Exec() error {
return nil return nil
} }
// helper function to set the credentials
func setDockerAuth(username, password, registry, baseImageUsername,
baseImagePassword, baseImageRegistry string) ([]byte, error) {
var credentials []docker.RegistryCredentials
// add only docker registry to the config
dockerConfig := docker.NewConfig()
if password != "" {
pushToRegistryCreds := docker.RegistryCredentials{
Registry: registry,
Username: username,
Password: password,
}
// push registry auth
credentials = append(credentials, pushToRegistryCreds)
}
if baseImageRegistry != "" {
pullFromRegistryCreds := docker.RegistryCredentials{
Registry: baseImageRegistry,
Username: baseImageUsername,
Password: baseImagePassword,
}
// base image registry auth
credentials = append(credentials, pullFromRegistryCreds)
}
// Creates docker config for both the registries used for authentication
return dockerConfig.CreateDockerConfigJson(credentials)
}
// helper function to create the docker login command. // helper function to create the docker login command.
func commandLogin(login Login) *exec.Cmd { func commandLogin(login Login) *exec.Cmd {
if login.Email != "" { if login.Email != "" {
@@ -351,8 +477,14 @@ func commandBuild(build Build) *exec.Cmd {
for _, arg := range build.ArgsEnv { for _, arg := range build.ArgsEnv {
addProxyValue(&build, arg) addProxyValue(&build, arg)
} }
for _, arg := range build.Args { if build.IsMultipleBuildArgs {
args = append(args, "--build-arg", arg) for _, arg := range build.ArgsNew {
args = append(args, "--build-arg", arg)
}
} else {
for _, arg := range build.Args {
args = append(args, "--build-arg", arg)
}
} }
for _, host := range build.AddHost { for _, host := range build.AddHost {
args = append(args, "--add-host", host) args = append(args, "--add-host", host)
@@ -457,11 +589,16 @@ func addProxyValue(build *Build, key string) {
build.Args = append(build.Args, fmt.Sprintf("%s=%s", key, value)) build.Args = append(build.Args, fmt.Sprintf("%s=%s", key, value))
build.Args = append(build.Args, fmt.Sprintf("%s=%s", strings.ToUpper(key), value)) build.Args = append(build.Args, fmt.Sprintf("%s=%s", strings.ToUpper(key), value))
} }
if len(value) > 0 && !hasProxyBuildArgNew(build, key) {
build.ArgsNew = append(build.ArgsNew, fmt.Sprintf("%s=%s", key, value))
build.ArgsNew = append(build.ArgsNew, fmt.Sprintf("%s=%s", strings.ToUpper(key), value))
}
} }
// helper function to get a proxy value from the environment. // helper function to get a proxy value from the environment.
// //
// assumes that the upper and lower case versions of are the same. // Checks in order: lowercase key, uppercase key, then HARNESS_<UPPERCASE_KEY>.
// Assumes that the upper and lower case versions are the same value.
func getProxyValue(key string) string { func getProxyValue(key string) string {
value := os.Getenv(key) value := os.Getenv(key)
@@ -469,15 +606,38 @@ func getProxyValue(key string) string {
return value return value
} }
return os.Getenv(strings.ToUpper(key)) value = os.Getenv(strings.ToUpper(key))
if len(value) > 0 {
return value
}
harnessValue := os.Getenv("HARNESS_" + strings.ToUpper(key))
if len(harnessValue) > 0 {
fmt.Printf("Using HARNESS_%s as proxy value for %s\n", strings.ToUpper(key), key)
}
return harnessValue
} }
// helper function that looks to see if a proxy value was set in the build args. // helper function that looks to see if a proxy value was set in the build args.
func hasProxyBuildArg(build *Build, key string) bool { func hasProxyBuildArg(build *Build, key string) bool {
keyUpper := strings.ToUpper(key) keyUpper := strings.ToUpper(key)
harnessKey := "HARNESS_" + keyUpper
for _, s := range build.Args { for _, s := range build.Args {
if strings.HasPrefix(s, key) || strings.HasPrefix(s, keyUpper) { if strings.HasPrefix(s, key) || strings.HasPrefix(s, keyUpper) || strings.HasPrefix(s, harnessKey) {
return true
}
}
return false
}
func hasProxyBuildArgNew(build *Build, key string) bool {
keyUpper := strings.ToUpper(key)
harnessKey := "HARNESS_" + keyUpper
for _, s := range build.ArgsNew {
if strings.HasPrefix(s, key) || strings.HasPrefix(s, keyUpper) || strings.HasPrefix(s, harnessKey) {
return true return true
} }
} }
@@ -557,6 +717,11 @@ func isCommandRmi(args []string) bool {
return len(args) > 2 && args[1] == "rmi" return len(args) > 2 && args[1] == "rmi"
} }
// helper to check if args match "cosign sign"
func isCommandCosign(args []string) bool {
return len(args) > 1 && args[0] == cosignExe
}
func commandRmi(tag string) *exec.Cmd { func commandRmi(tag string) *exec.Cmd {
return exec.Command(dockerExe, "rmi", tag) return exec.Command(dockerExe, "rmi", tag)
} }
@@ -593,7 +758,7 @@ func GetDroneDockerExecCmd() string {
} }
func getDigest(buildName string) (string, error) { func getDigest(buildName string) (string, error) {
cmd := exec.Command("docker", "inspect", "--format='{{index .RepoDigests 0}}'", buildName) cmd := exec.Command(dockerExe, "inspect", "--format='{{index .RepoDigests 0}}'", buildName)
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
return "", err return "", err
@@ -607,3 +772,284 @@ func getDigest(buildName string) (string, error) {
} }
return "", errors.New("unable to fetch digest") return "", errors.New("unable to fetch digest")
} }
// imageExists checks if an image exists in local daemon
func imageExists(tag string) bool {
cmd := exec.Command(dockerExe, "image", "inspect", tag)
return cmd.Run() == nil
}
// getDigestAfterPush gets digest from a pushed image
func getDigestAfterPush(tag string) (string, error) {
cmd := exec.Command(dockerExe, "inspect", "--format", "{{ index (split (index .RepoDigests 0) \"@\") 1 }}", tag)
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to get digest for %s: %w", tag, err)
}
return strings.TrimSpace(string(output)), nil
}
// shouldSignWithCosign determines if cosign signing should be performed
func (p Plugin) shouldSignWithCosign() bool {
return p.Cosign.PrivateKey != ""
}
// validateCosignConfig validates the cosign configuration
func validateCosignConfig(config CosignConfig) error {
if config.PrivateKey == "" {
return nil // No cosign config, skip silently
}
// Check if cosign binary is available
if _, err := exec.LookPath(cosignExe); err != nil {
fmt.Printf("❌ ERROR: cosign binary not found at %s\n", cosignExe)
fmt.Println(" Ensure you're using a plugin image that includes cosign")
return fmt.Errorf("cosign binary not available: %w", err)
}
// Check if it's trying to use keyless signing
if strings.Contains(config.Params, "--oidc") ||
strings.Contains(config.Params, "--identity-token") {
fmt.Println("⚠️ WARNING: Keyless signing (OIDC) isn't supported yet in this plugin. Use private key signing instead.")
return errors.New("keyless signing not supported")
}
// Validate private key format if it's PEM content
if strings.HasPrefix(config.PrivateKey, "-----BEGIN") {
if !isValidPEMKey(config.PrivateKey) {
return errors.New("❌ Invalid private key format. Expected PEM format")
}
// Check encrypted key password requirement
if isEncryptedPEMKey(config.PrivateKey) && config.Password == "" {
return errors.New("🔐 Encrypted private key requires password. Set PLUGIN_COSIGN_PASSWORD")
}
} else {
// File-based key - check if it's accessible (basic check)
if _, err := os.Stat(config.PrivateKey); err != nil {
fmt.Printf("⚠️ WARNING: Private key file may not be accessible: %s\n", config.PrivateKey)
fmt.Println(" This will be verified during signing")
}
}
return nil
}
// isEncryptedPEMKey checks if a PEM key is encrypted
func isEncryptedPEMKey(pemContent string) bool {
return strings.Contains(pemContent, "ENCRYPTED")
}
// isValidPEMKey performs basic PEM format validation
func isValidPEMKey(pemContent string) bool {
return strings.Contains(pemContent, "-----BEGIN") &&
strings.Contains(pemContent, "-----END") &&
(strings.Contains(pemContent, "PRIVATE KEY") ||
strings.Contains(pemContent, "RSA PRIVATE KEY") ||
strings.Contains(pemContent, "EC PRIVATE KEY"))
}
// createCosignCommand creates a cosign sign command with the given image reference
func createCosignCommand(imageRef string, cosign CosignConfig) *exec.Cmd {
args := []string{"sign", "--yes"}
// Handle private key (content vs file path)
if strings.HasPrefix(cosign.PrivateKey, "-----BEGIN") {
args = append(args, "--key", "env://COSIGN_PRIVATE_KEY")
os.Setenv("COSIGN_PRIVATE_KEY", cosign.PrivateKey)
} else {
args = append(args, "--key", cosign.PrivateKey)
}
// Set password if provided
if cosign.Password != "" {
os.Setenv("COSIGN_PASSWORD", cosign.Password)
}
// Add any extra parameters
if cosign.Params != "" {
extraArgs := strings.Fields(cosign.Params)
args = append(args, extraArgs...)
}
// Add the image reference to sign
args = append(args, imageRef)
return exec.Command(cosignExe, args...)
}
// executeCosignCommand executes the given cosign command and handles errors
func executeCosignCommand(cmd *exec.Cmd) {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Printf("🚀 Executing: %s %s\n", cmd.Path, strings.Join(cmd.Args[1:], " "))
if err := cmd.Run(); err != nil {
fmt.Printf("⚠️ WARNING: Image signing failed: %s\n", err)
fmt.Printf(" Image was pushed successfully but could not be signed\n")
fmt.Printf(" This is not fatal - continuing with the build\n")
}
}
// pushOnly handles pushing images without building them
func (p Plugin) pushOnly() error {
// Check if source image is specified
sourceImageName := p.SourceImage
var sourceTags []string
if sourceImageName == "" {
// If no source image specified, use the repo and first tag
fmt.Println("source_image not provided, using repo and tag value")
sourceImageName = p.Build.Repo
sourceTags = p.Build.Tags
} else {
// If source image is specified, check if it has a tag
lastColonIndex := strings.LastIndex(sourceImageName, ":")
if lastColonIndex > 0 && lastColonIndex < len(sourceImageName) {
// Check if there's a slash after the last colon (indicating it's a port, not a tag)
// For example: registry:5000/image (has slash after colon - port not tag)
// vs image:tag (no slash after colon - it's a tag)
if strings.LastIndex(sourceImageName, "/") > lastColonIndex {
// The last colon is part of the registry:port, not a tag separator
sourceTags = []string{"latest"}
} else {
// The last colon separates the tag
tag := sourceImageName[lastColonIndex+1:]
sourceImageName = sourceImageName[:lastColonIndex]
if tag == "" {
fmt.Printf("No tag specified in source image (or empty tag). Using 'latest' as the default tag.\n")
tag = "latest"
}
sourceTags = []string{tag}
}
} else {
// Default to "latest" if no tag specified
sourceTags = []string{"latest"}
}
fmt.Printf("Using source image: %s with tag(s): %s\n", sourceImageName, strings.Join(sourceTags, ", "))
}
// For each source tag and target tag combination
var digest string
var firstPushedImage string
for _, sourceTag := range sourceTags {
sourceFullImageName := fmt.Sprintf("%s:%s", sourceImageName, sourceTag)
// Check if the source image exists in local daemon
if !imageExists(sourceFullImageName) {
fmt.Printf("Warning: Source image %s not found\n", sourceFullImageName)
// Continue to the next source tag if available, otherwise return error
if len(sourceTags) > 1 {
continue
}
return fmt.Errorf("source image %s not found, cannot push", sourceFullImageName)
}
// For each target tag, tag and push
for _, targetTag := range p.Build.Tags {
targetFullImageName := fmt.Sprintf("%s:%s", p.Build.Repo, targetTag)
// Skip if source and target are identical
if sourceFullImageName == targetFullImageName {
fmt.Printf("Source and target image names are identical: %s\n", sourceFullImageName)
} else {
// Tag the source image with the target name
fmt.Printf("Tagging %s as %s\n", sourceFullImageName, targetFullImageName)
tagCmd := exec.Command(dockerExe, "tag", sourceFullImageName, targetFullImageName)
tagCmd.Stdout = os.Stdout
tagCmd.Stderr = os.Stderr
trace(tagCmd)
if err := tagCmd.Run(); err != nil {
return fmt.Errorf("failed to tag image %s as %s: %w", sourceFullImageName, targetFullImageName, err)
}
}
}
}
// Push all target images
for _, tag := range p.Build.Tags {
fullImageName := fmt.Sprintf("%s:%s", p.Build.Repo, tag)
// Check if image exists in local daemon
if !imageExists(fullImageName) {
return fmt.Errorf("image %s not found, cannot push", fullImageName)
}
// Push image
fmt.Println("Pushing image:", fullImageName)
pushCmd := commandPush(p.Build, tag)
pushCmd.Stdout = os.Stdout
pushCmd.Stderr = os.Stderr
trace(pushCmd)
if err := pushCmd.Run(); err != nil {
return fmt.Errorf("failed to push image %s: %w", fullImageName, err)
}
// Track the first pushed image for card generation
if firstPushedImage == "" {
firstPushedImage = fullImageName
}
// Get the digest after push (we only need one)
if digest == "" {
d, err := getDigestAfterPush(fullImageName)
if err == nil {
digest = d
} else {
fmt.Printf("Warning: Could not get digest for %s: %v\n", fullImageName, err)
}
}
}
// Output the adaptive card
if firstPushedImage != "" {
if err := p.writeCardForImage(firstPushedImage); err != nil {
fmt.Printf("Could not create adaptive card. %s\n", err)
}
}
// Write to artifact file
if p.ArtifactFile != "" && digest != "" {
if err := drone.WritePluginArtifactFile(
p.Daemon.RegistryType,
p.ArtifactFile,
p.Daemon.Registry,
p.Build.Repo,
digest,
p.Build.Tags,
); err != nil {
fmt.Printf("Failed to write plugin artifact file at path: %s with error: %s\n",
p.ArtifactFile, err)
}
}
// Handle cosign signing after push
if p.shouldSignWithCosign() {
// Set up environment variables for cosign
os.Setenv("COSIGN_YES", "true")
if digest != "" {
fmt.Printf("🔐 Found image digest: %s\n", digest)
// Sign with digest reference
imageRef := fmt.Sprintf("%s@%s", p.Build.Repo, digest)
cosignCmd := createCosignCommand(imageRef, p.Cosign)
executeCosignCommand(cosignCmd)
} else {
fmt.Printf("⚠️ WARNING: Could not get image digest for cosign signing\n")
fmt.Printf(" Falling back to tag-based signing\n")
// Fall back to tag-based signing for each tag
for _, tag := range p.Build.Tags {
imageRef := fmt.Sprintf("%s:%s", p.Build.Repo, tag)
cosignCmd := createCosignCommand(imageRef, p.Cosign)
executeCosignCommand(cosignCmd)
}
}
}
return nil
}
+5 -1
View File
@@ -1,6 +1,10 @@
FROM docker:20.10.14-dind FROM docker:28.1.1-dind
ENV DOCKER_HOST=unix:///var/run/docker.sock ENV DOCKER_HOST=unix:///var/run/docker.sock
# Install cosign for container image signing
RUN wget -O /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.5.3/cosign-linux-amd64 \
&& chmod +x /usr/local/bin/cosign
ADD release/linux/amd64/drone-docker /bin/ ADD release/linux/amd64/drone-docker /bin/
ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-docker"] ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-docker"]
+5 -1
View File
@@ -1,6 +1,10 @@
FROM arm64v8/docker:20.10.14-dind FROM arm64v8/docker:28.1.1-dind
ENV DOCKER_HOST=unix:///var/run/docker.sock ENV DOCKER_HOST=unix:///var/run/docker.sock
# Install cosign for container image signing
RUN wget -O /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.5.3/cosign-linux-arm64 \
&& chmod +x /usr/local/bin/cosign
ADD release/linux/arm64/drone-docker /bin/ ADD release/linux/arm64/drone-docker /bin/
ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-docker"] ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-docker"]
@@ -24,6 +24,10 @@ LABEL maintainer="Drone.IO Community <drone-dev@googlegroups.com>" `
org.label-schema.schema-version="1.0" org.label-schema.schema-version="1.0"
RUN mkdir C:\bin RUN mkdir C:\bin
# Install cosign for container image signing
ADD https://github.com/sigstore/cosign/releases/download/v2.5.3/cosign-windows-amd64.exe C:/bin/cosign.exe
COPY --from=download /windows/system32/netapi32.dll /windows/system32/netapi32.dll COPY --from=download /windows/system32/netapi32.dll /windows/system32/netapi32.dll
COPY --from=download /app/docker.exe C:/bin/docker.exe COPY --from=download /app/docker.exe C:/bin/docker.exe
ADD release/windows/amd64/drone-docker.exe C:/bin/drone-docker.exe ADD release/windows/amd64/drone-docker.exe C:/bin/drone-docker.exe
@@ -22,6 +22,10 @@ LABEL maintainer="Drone.IO Community <drone-dev@googlegroups.com>" `
org.label-schema.schema-version="1.0" org.label-schema.schema-version="1.0"
RUN mkdir C:\bin RUN mkdir C:\bin
# Install cosign for container image signing
ADD https://github.com/sigstore/cosign/releases/download/v2.5.3/cosign-windows-amd64.exe C:/bin/cosign.exe
COPY --from=download /windows/system32/netapi32.dll /windows/system32/netapi32.dll COPY --from=download /windows/system32/netapi32.dll /windows/system32/netapi32.dll
COPY --from=download /app/docker.exe C:/bin/docker.exe COPY --from=download /app/docker.exe C:/bin/docker.exe
ADD release/windows/amd64/drone-docker.exe C:/bin/drone-docker.exe ADD release/windows/amd64/drone-docker.exe C:/bin/drone-docker.exe
+4
View File
@@ -0,0 +1,4 @@
FROM plugins/docker:linux-amd64
ADD release/linux/amd64/drone-gar /bin/
ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-gar"]
+4
View File
@@ -0,0 +1,4 @@
FROM plugins/docker:linux-arm64
ADD release/linux/arm64/drone-gar /bin/
ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-gar"]
+10
View File
@@ -0,0 +1,10 @@
# escape=`
FROM plugins/docker:windows-1809-amd64
LABEL maintainer="Drone.IO Community <drone-dev@googlegroups.com>" `
org.label-schema.name="Drone GAR" `
org.label-schema.vendor="Drone.IO Community" `
org.label-schema.schema-version="1.0"
ADD release/windows/amd64/drone-gar.exe C:/bin/drone-gar.exe
ENTRYPOINT [ "C:\\bin\\drone-gar.exe" ]
@@ -0,0 +1,10 @@
# escape=`
FROM plugins/docker:windows-ltsc2022-amd64
LABEL maintainer="Drone.IO Community <drone-dev@googlegroups.com>" `
org.label-schema.name="Drone GAR" `
org.label-schema.vendor="Drone.IO Community" `
org.label-schema.schema-version="1.0"
ADD release/windows/amd64/drone-gar.exe C:/bin/drone-gar.exe
ENTRYPOINT [ "C:\\bin\\drone-gar.exe" ]
+31
View File
@@ -0,0 +1,31 @@
image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
-
image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
platform:
architecture: amd64
os: linux
-
image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
platform:
architecture: arm64
os: linux
variant: v8
-
image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1809-amd64
platform:
architecture: amd64
os: windows
version: 1809
-
image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-ltsc2022-amd64
platform:
architecture: amd64
os: windows
version: ltsc2022
+88
View File
@@ -1,6 +1,7 @@
package docker package docker
import ( import (
"os"
"os/exec" "os/exec"
"reflect" "reflect"
"strings" "strings"
@@ -179,3 +180,90 @@ func TestCommandBuild(t *testing.T) {
}) })
} }
} }
func TestGetProxyValue(t *testing.T) {
tests := []struct {
name string
key string
envVars map[string]string
expected string
}{
{
name: "lowercase env var set",
key: "http_proxy",
envVars: map[string]string{"http_proxy": "http://proxy:8080"},
expected: "http://proxy:8080",
},
{
name: "uppercase env var set",
key: "http_proxy",
envVars: map[string]string{"HTTP_PROXY": "http://proxy:8080"},
expected: "http://proxy:8080",
},
{
name: "HARNESS prefixed env var set",
key: "http_proxy",
envVars: map[string]string{"HARNESS_HTTP_PROXY": "http://harness-proxy:8080"},
expected: "http://harness-proxy:8080",
},
{
name: "standard takes precedence over HARNESS",
key: "http_proxy",
envVars: map[string]string{
"HTTP_PROXY": "http://standard:8080",
"HARNESS_HTTP_PROXY": "http://harness:8080",
},
expected: "http://standard:8080",
},
{
name: "lowercase takes precedence over uppercase",
key: "no_proxy",
envVars: map[string]string{
"no_proxy": "localhost,127.0.0.1",
"NO_PROXY": "*.example.com",
"HARNESS_NO_PROXY": "*.local",
},
expected: "localhost,127.0.0.1",
},
{
name: "lowercase takes precedence over HARNESS",
key: "https_proxy",
envVars: map[string]string{
"https_proxy": "https://standard:8080",
"HARNESS_HTTPS_PROXY": "https://harness:8080",
},
expected: "https://standard:8080",
},
{
name: "no env var set",
key: "http_proxy",
envVars: map[string]string{},
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Clean env
lowercaseKey := tt.key
uppercaseKey := strings.ToUpper(tt.key)
harnessKey := "HARNESS_" + strings.ToUpper(tt.key)
os.Unsetenv(lowercaseKey)
os.Unsetenv(uppercaseKey)
os.Unsetenv(harnessKey)
// Set test environment variables
for k, v := range tt.envVars {
os.Setenv(k, v)
defer os.Unsetenv(k)
}
// Execute and verify
result := getProxyValue(tt.key)
if result != tt.expected {
t.Errorf("getProxyValue(%q) = %q, want %q", tt.key, result, tt.expected)
}
})
}
}
View File
+8
View File
@@ -0,0 +1,8 @@
This document explains on how to install certain git hooks globally for all repositories in your machine.
Step 1: git clone https://github.com/drone-plugins/drone-docker.git
Step 2: cd git-hooks
Step 3: Run install.sh
"install.sh" script will create .git_template in the user directory and will put the git hook and its dependent scripts in it. Along with the .git_template folder, it will add 2 sections "init" and "hooks boolean" in the .gitconfig file in the same user's root directory.
After running "install.sh" if you create/clone a new git repository then all the hooks will get install automatically for the git repository. In case of existing git repository copy the contents of ~/.git_template/hooks into the .git/hooks directory of existing git repository.
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
#Helper script to be used as a pre-commit hook.
echo "This hook checks for any secrets getting pushed as part of commit. If you feel that scan is false positive. \
Then add the exclusion in .gitleaksignore file. For more info visit: https://github.com/zricethezav/gitleaks"
GIT_LEAKS_PRE_COMMIT=s$(git config --bool hook.pre-commit.gitleak)
echo "INFO: Scanning Commits information for any GIT LEAKS"
gitleaks protect --staged -v --exit-code=100
STATUS=$?
if [ $STATUS = 100 ]; then
echo "WARNING: GIT LEAKS has detected sensitive information in your changes. Please remove them or add them (IF NON-SENSITIVE) in .gitleaksignore file."
else
exit 0
fi
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
#Helper script to be used as a pre-commit hook.
echo "This hook checks for any secrets getting pushed as part of commit. If you feel that scan is false positive. \
Then add the exclusion in .gitleaksignore file. For more info visit: https://github.com/zricethezav/gitleaks"
GIT_LEAKS=$(git config --bool hook.pre-push.gitleaks)
echo "INFO: Scanning Commits information for any GIT LEAKS"
gitleaks detect -s ./ --log-level=debug --log-opts=-1 -v
STATUS=$?
if [ $STATUS != 0 ]; then
echo "WARNING: GIT LEAKS has detected sensitive information in your changes. Please remove them or add them (IF NON-SENSITIVE) in .gitleaksignore file."
exit $STATUS
else
exit 0
fi
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
GL_SCRIPT_PATH="$HOME/.git_template/hooks/git-leaks-pre-commit.sh"
pushd `dirname $0` > /dev/null && cd ../.. && BASEDIR=$(pwd -L) && popd > /dev/null
BASENAME=`basename $0`
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
#Initial commit : diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
GIT_LEAKS_PRE_COMMIT=hook.pre-commit.gitleaks
if [ "`git config $GIT_LEAKS_PRE_COMMIT`" == "false" ]
then
echo -e '\033[0;31m' checking git leaks is disabled - to enable: '\033[0;37m'git config --unset $GIT_LEAKS_PRE_COMMIT '\033[0m'
echo -e '\033[0;34m' checking git leaks ... to enable: '\033[0;37m'git config --add $GIT_LEAKS_PRE_COMMIT true '\033[0m'
else
echo -e '\033[0;34m' checking for git leaks...
[ -f "${GL_SCRIPT_PATH}" ] && . ${GL_SCRIPT_PATH} || echo "ERROR: Hook Script Not Found..." && exit 404
fi
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
GL_SCRIPT_PATH="$HOME/.git_template/hooks/git-leaks.sh"
pushd `dirname $0` > /dev/null && cd ../.. && BASEDIR=$(pwd -L) && popd > /dev/null
BASENAME=`basename $0`
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
#Initial commit : diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
GIT_LEAKS=hook.pre-push.gitleaks
if [ "`git config $GIT_LEAKS`" == "false" ]
then
echo -e '\033[0;31m' checking git leaks is disabled - to enable: '\033[0;37m'git config --unset $GIT_LEAKS '\033[0m'
echo -e '\033[0;34m' checking git leaks ... to enable: '\033[0;37m'git config --add $GIT_LEAKS true '\033[0m'
else
echo -e '\033[0;34m' checking for git leaks...
[ -f "${GL_SCRIPT_PATH}" ] && . ${GL_SCRIPT_PATH} || echo "ERROR: Hook Script Not Found..." && exit 404
fi
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
#Function to check if package is installed or not
#args: $1: Name of the Package
function check_package_installed() {
LOCAL_PACKAGE_NAME=$1
echo "Checking if $LOCAL_PACKAGE_NAME is installed or not..."
brew list $LOCAL_PACKAGE_NAME
if [ "$?" -eq 1 ];then
echo "Installing $LOCAL_PACKAGE_NAME package..."
brew install $LOCAL_PACKAGE_NAME
fi
}
function create_git_template() {
cd $BASEDIR
mkdir -p ~/.git_template/hooks
git config --global init.templatedir ${GIT_TEMPLATE}
git config --global --add $GIT_LEAKS true
git config --global --add $GIT_LEAKS_PRE_COMMIT true
find hooks/ -type f -exec cp "{}" ~/.git_template/hooks \;
#cp -f hooks/* ~/.git_template/hooks
cat ~/.gitconfig
}
GIT_TEMPLATE="~/.git_template"
GIT_LEAKS=hook.pre-push.gitleaks
GIT_LEAKS_PRE_COMMIT=hook.pre-commit.gitleaks
pushd `dirname $0` && BASEDIR=$(pwd -L) && popd
echo This script will install hooks that run scripts that could be updated without notice.
while true; do
read -p "Do you wish to install these hooks?" yn
case $yn in
[Yy]* ) check_package_installed "gitleaks";
break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done
create_git_template
+38 -14
View File
@@ -1,37 +1,59 @@
module github.com/drone-plugins/drone-docker module github.com/drone-plugins/drone-docker
require ( require (
github.com/aws/aws-sdk-go v1.26.7 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2
github.com/aws/aws-sdk-go-v2 v1.41.2
github.com/aws/aws-sdk-go-v2/config v1.32.10
github.com/aws/aws-sdk-go-v2/credentials v1.19.10
github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7
github.com/coreos/go-semver v0.3.0 github.com/coreos/go-semver v0.3.0
github.com/dchest/uniuri v1.2.0 github.com/dchest/uniuri v1.2.0
github.com/drone-plugins/drone-plugin-lib v0.4.1 github.com/drone-plugins/drone-plugin-lib v0.4.1
github.com/drone/drone-go v1.7.1 github.com/drone/drone-go v1.7.1
github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743
github.com/joho/godotenv v1.3.0 github.com/joho/godotenv v1.3.0
github.com/sirupsen/logrus v1.9.0 github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
github.com/urfave/cli v1.22.2 github.com/urfave/cli v1.22.2
golang.org/x/oauth2 v0.13.0 golang.org/x/oauth2 v0.27.0
google.golang.org/api v0.146.0
) )
require ( require (
cloud.google.com/go/compute v1.23.1 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect
github.com/aws/smithy-go v1.24.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/s2a-go v0.1.7 // indirect github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.3.1 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/kylelemons/godebug v1.1.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.14.0 // indirect golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.17.0 // indirect golang.org/x/net v0.37.0 // indirect
golang.org/x/sys v0.13.0 // indirect golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.13.0 // indirect golang.org/x/text v0.23.0 // indirect
google.golang.org/api v0.146.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/grpc v1.59.0 // indirect google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.31.0 // indirect
@@ -39,4 +61,6 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
go 1.21 go 1.24
toolchain go1.24.11
+93 -64
View File
@@ -1,15 +1,53 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.0 h1:nBbNSZyDpkNlo3DepaaLKVuO7ClyifSAmNloSCZrHnQ=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY= github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aws/aws-sdk-go v1.26.7 h1:ObjEnmzvSdYy8KVd3me7v/UMyCn81inLy2SyoIPoBkg= github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls=
github.com/aws/aws-sdk-go v1.26.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4=
github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI=
github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw=
github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8=
github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 h1:RtGctYMmkTerGClvdY6bHXdtly4FeYw9wz/NPz62LF8=
github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3/go.mod h1:vBfBu24Ka3/5UZtepbTV0gnc9VPLT8ok+0oDDaYAzn4=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs=
github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0=
github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
@@ -22,6 +60,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g= github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g=
github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/drone-plugins/drone-plugin-lib v0.4.1 h1:47rZlmcMpr1hSp+6Gl+1Z4t+efi/gMQU3lxukC1Yg64= github.com/drone-plugins/drone-plugin-lib v0.4.1 h1:47rZlmcMpr1hSp+6Gl+1Z4t+efi/gMQU3lxukC1Yg64=
github.com/drone-plugins/drone-plugin-lib v0.4.1/go.mod h1:KwCu92jFjHV3xv2hu5Qg/8zBNvGwbhoJDQw/EwnTvoM= github.com/drone-plugins/drone-plugin-lib v0.4.1/go.mod h1:KwCu92jFjHV3xv2hu5Qg/8zBNvGwbhoJDQw/EwnTvoM=
github.com/drone/drone-go v1.7.1 h1:ZX+3Rs8YHUSUQ5mkuMLmm1zr1ttiiE2YGNxF3AnyDKw= github.com/drone/drone-go v1.7.1 h1:ZX+3Rs8YHUSUQ5mkuMLmm1zr1ttiiE2YGNxF3AnyDKw=
@@ -30,13 +70,14 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -46,8 +87,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -57,122 +96,112 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ=
github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 h1:X3Xxno5Ji8idrNiUoFc7QyXpqhSYlDRYQmc7mlpMBzU= github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 h1:X3Xxno5Ji8idrNiUoFc7QyXpqhSYlDRYQmc7mlpMBzU=
github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743/go.mod h1:KrtyD5PFj++GKkFS/7/RRrfnRhAMGQwy75GLCHWrCNs= github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743/go.mod h1:KrtyD5PFj++GKkFS/7/RRrfnRhAMGQwy75GLCHWrCNs=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.146.0 h1:9aBYT4vQXt9dhCuLNfwfd3zpwu8atg0yPkjBymwSrOM= google.golang.org/api v0.146.0 h1:9aBYT4vQXt9dhCuLNfwfd3zpwu8atg0yPkjBymwSrOM=
google.golang.org/api v0.146.0/go.mod h1:OARJqIfoYjXJj4C1AiBSXYZt03qsoz8FQYU6fBEfrHM= google.golang.org/api v0.146.0/go.mod h1:OARJqIfoYjXJj4C1AiBSXYZt03qsoz8FQYU6fBEfrHM=
google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc=
google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8=
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE=
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI=
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -193,11 +222,11 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+75
View File
@@ -0,0 +1,75 @@
package azure
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
const DefaultResource = "https://management.azure.com/"
const defaultAuthorityHost = "https://login.microsoftonline.com"
const defaultHTTPTimeout = 30 * time.Second
// GetAADAccessTokenViaClientAssertion exchanges an external OIDC ID token for an Azure AD access token
func GetAADAccessTokenViaClientAssertion(ctx context.Context, tenantID, clientID, oidcToken, authorityHost string) (string, error) {
resource := DefaultResource
form := url.Values{
"client_id": {clientID},
"scope": {resource + ".default"},
"grant_type": {"client_credentials"},
"client_assertion_type": {"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"},
"client_assertion": {oidcToken},
}
base := authorityHost
if strings.TrimSpace(base) == "" {
base = defaultAuthorityHost
}
base = strings.TrimRight(base, "/")
endpoint := fmt.Sprintf("%s/%s/oauth2/v2.0/token", base, tenantID)
client := &http.Client{Timeout: defaultHTTPTimeout}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, strings.NewReader(form.Encode()))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
var aadErr struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
}
limited := io.LimitedReader{R: resp.Body, N: 4096}
_ = json.NewDecoder(&limited).Decode(&aadErr)
if aadErr.Error != "" {
return "", fmt.Errorf("AAD token request failed: status=%d, error=%s", resp.StatusCode, aadErr.Error)
}
return "", fmt.Errorf("AAD token request failed: status=%d", resp.StatusCode)
}
var payload struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
}
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
return "", err
}
if payload.AccessToken == "" {
return "", fmt.Errorf("AAD token response missing access_token")
}
return payload.AccessToken, nil
}
+104
View File
@@ -0,0 +1,104 @@
package azure
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestGetAADAccessTokenViaClientAssertion_Success(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Fatalf("expected POST, got %s", r.Method)
}
if ct := r.Header.Get("Content-Type"); !strings.Contains(ct, "application/x-www-form-urlencoded") {
t.Fatalf("expected form content-type, got %s", ct)
}
if err := r.ParseForm(); err != nil {
t.Fatalf("failed parsing form: %v", err)
}
assertEq(t, r.Form.Get("client_id"), "client")
assertEq(t, r.Form.Get("grant_type"), "client_credentials")
assertEq(t, r.Form.Get("client_assertion_type"), "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
assertEq(t, r.Form.Get("client_assertion"), "idtoken")
assertEq(t, r.Form.Get("scope"), DefaultResource+".default")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"access_token":"AT","token_type":"Bearer","expires_in":3600}`))
}))
defer ts.Close()
tok, err := GetAADAccessTokenViaClientAssertion(context.Background(), "tenant", "client", "idtoken", ts.URL)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tok != "AT" {
t.Fatalf("expected access token AT, got %q", tok)
}
}
func TestGetAADAccessTokenViaClientAssertion_400WithErrorField(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(`{"error":"invalid_client","error_description":"bad"}`))
}))
defer ts.Close()
_, err := GetAADAccessTokenViaClientAssertion(context.Background(), "tenant", "client", "idtoken", ts.URL)
if err == nil || !strings.Contains(err.Error(), "status=400") || !strings.Contains(err.Error(), "invalid_client") {
t.Fatalf("expected 400 with invalid_client error, got %v", err)
}
}
func TestGetAADAccessTokenViaClientAssertion_400WithoutErrorField(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("{}"))
}))
defer ts.Close()
_, err := GetAADAccessTokenViaClientAssertion(context.Background(), "tenant", "client", "idtoken", ts.URL)
if err == nil || !strings.Contains(err.Error(), "status=400") {
t.Fatalf("expected 400 error, got %v", err)
}
}
func TestGetAADAccessTokenViaClientAssertion_MalformedJSON(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("not-json"))
}))
defer ts.Close()
_, err := GetAADAccessTokenViaClientAssertion(context.Background(), "tenant", "client", "idtoken", ts.URL)
if err == nil {
t.Fatalf("expected JSON decode error, got nil")
}
}
func TestGetAADAccessTokenViaClientAssertion_MissingAccessToken(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"token_type":"Bearer","expires_in":3600}`))
}))
defer ts.Close()
_, err := GetAADAccessTokenViaClientAssertion(context.Background(), "tenant", "client", "idtoken", ts.URL)
if err == nil || !strings.Contains(err.Error(), "missing access_token") {
t.Fatalf("expected missing access_token error, got %v", err)
}
}
func assertEq(t *testing.T, got, want string) {
t.Helper()
if got != want {
t.Fatalf("mismatch: got=%q want=%q", got, want)
}
}
+70
View File
@@ -0,0 +1,70 @@
package docker
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
)
const (
v2HubRegistryURL string = "https://registry.hub.docker.com/v2/"
v1RegistryURL string = "https://index.docker.io/v1/" // Default registry
v2RegistryURL string = "https://index.docker.io/v2/" // v2 registry is not supported
)
type (
Auth struct {
Auth string `json:"auth"`
}
Config struct {
Auths map[string]Auth `json:"auths"`
CredHelpers map[string]string `json:"credHelpers,omitempty"`
}
)
type RegistryCredentials struct {
Registry string
Username string
Password string
}
func NewConfig() *Config {
return &Config{
Auths: make(map[string]Auth),
CredHelpers: make(map[string]string),
}
}
func (c *Config) SetAuth(registry, username, password string) {
authBytes := []byte(username + ":" + password)
encodedString := base64.StdEncoding.EncodeToString(authBytes)
c.Auths[registry] = Auth{Auth: encodedString}
}
func (c *Config) SetCredHelper(registry, helper string) {
c.CredHelpers[registry] = helper
}
func (c *Config) CreateDockerConfigJson(credentials []RegistryCredentials) ([]byte, error) {
for _, cred := range credentials {
if cred.Registry != "" {
if cred.Username == "" {
return nil, fmt.Errorf("Username must be specified for registry: %s", cred.Registry)
}
if cred.Password == "" {
return nil, fmt.Errorf("Password must be specified for registry: %s", cred.Registry)
}
c.SetAuth(cred.Registry, cred.Username, cred.Password)
}
}
jsonBytes, err := json.Marshal(c)
if err != nil {
return nil, errors.New("failed to serialize docker config json")
}
return jsonBytes, nil
}
+64
View File
@@ -0,0 +1,64 @@
package docker
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
const (
RegistryV1 string = "https://index.docker.io/v1/"
RegistryV2 string = "https://index.docker.io/v2/"
RegistryECRPublic string = "public.ecr.aws"
)
func TestConfig(t *testing.T) {
c := NewConfig()
assert.NotNil(t, c.Auths)
assert.NotNil(t, c.CredHelpers)
c.SetAuth(RegistryV1, "test", "password")
expectedAuth := Auth{Auth: "dGVzdDpwYXNzd29yZA=="}
assert.Equal(t, expectedAuth, c.Auths[RegistryV1])
c.SetCredHelper(RegistryECRPublic, "ecr-login")
assert.Equal(t, "ecr-login", c.CredHelpers[RegistryECRPublic])
tempDir, err := ioutil.TempDir("", "docker-config-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
credentials := []RegistryCredentials{
{
Registry: "https://index.docker.io/v1/",
Username: "user1",
Password: "pass1",
},
{
Registry: "gcr.io",
Username: "user2",
Password: "pass2",
},
}
jsonBytes, err := c.CreateDockerConfigJson(credentials)
assert.NoError(t, err)
configPath := filepath.Join(tempDir, "config.json")
err = ioutil.WriteFile(configPath, jsonBytes, 0644)
assert.NoError(t, err)
data, err := ioutil.ReadFile(configPath)
assert.NoError(t, err)
var configFromFile Config
err = json.Unmarshal(data, &configFromFile)
assert.NoError(t, err)
assert.Equal(t, c.Auths, configFromFile.Auths)
assert.Equal(t, c.CredHelpers, configFromFile.CredHelpers)
}
+65
View File
@@ -0,0 +1,65 @@
package gcp
import (
"context"
"fmt"
"golang.org/x/oauth2"
"google.golang.org/api/iamcredentials/v1"
"google.golang.org/api/option"
"google.golang.org/api/sts/v1"
)
type staticTokenSource struct {
token *oauth2.Token
}
func (s *staticTokenSource) Token() (*oauth2.Token, error) {
return s.token, nil
}
func GetFederalToken(idToken, projectNumber, poolId, providerId string) (string, error) {
ctx := context.Background()
stsService, err := sts.NewService(ctx, option.WithoutAuthentication())
if err != nil {
return "", err
}
audience := fmt.Sprintf("//iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/providers/%s", projectNumber, poolId, providerId)
tokenRequest := &sts.GoogleIdentityStsV1ExchangeTokenRequest{
GrantType: "urn:ietf:params:oauth:grant-type:token-exchange",
SubjectToken: idToken,
Audience: audience,
Scope: "https://www.googleapis.com/auth/cloud-platform",
RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token",
SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
}
tokenResponse, err := stsService.V1.Token(tokenRequest).Do()
if err != nil {
return "", err
}
return tokenResponse.AccessToken, nil
}
func GetGoogleCloudAccessToken(federatedToken string, serviceAccountEmail string) (string, error) {
ctx := context.Background()
tokenSource := &staticTokenSource{
token: &oauth2.Token{AccessToken: federatedToken},
}
service, err := iamcredentials.NewService(ctx, option.WithTokenSource(tokenSource))
if err != nil {
return "", err
}
name := "projects/-/serviceAccounts/" + serviceAccountEmail
rb := &iamcredentials.GenerateAccessTokenRequest{
Scope: []string{"https://www.googleapis.com/auth/cloud-platform"},
}
resp, err := service.Projects.ServiceAccounts.GenerateAccessToken(name, rb).Do()
if err != nil {
return "", err
}
return resp.AccessToken, nil
}