@@ -11,11 +11,15 @@ import (
1111 "strconv"
1212 "strings"
1313
14+ docker "github.com/drone-plugins/drone-docker"
15+
1416 "github.com/joho/godotenv"
1517 "github.com/sirupsen/logrus"
18+ "golang.org/x/oauth2"
1619 "golang.org/x/oauth2/google"
17-
18- docker "github.com/drone-plugins/drone-docker"
20+ "google.golang.org/api/iamcredentials/v1"
21+ "google.golang.org/api/option"
22+ "google.golang.org/api/sts/v1"
1923)
2024
2125type Config struct {
@@ -25,11 +29,21 @@ type Config struct {
2529 WorkloadIdentity bool
2630 Username string
2731 RegistryType string
32+ AccessToken string
33+ }
34+
35+ type staticTokenSource struct {
36+ token * oauth2.Token
37+ }
38+
39+ func (s * staticTokenSource ) Token () (* oauth2.Token , error ) {
40+ return s .token , nil
2841}
2942
3043func loadConfig () Config {
3144 // Default username
3245 username := "_json_key"
46+ var config Config
3347
3448 // Load env-file if it exists
3549 if env := os .Getenv ("PLUGIN_ENV_FILE" ); env != "" {
@@ -38,18 +52,36 @@ func loadConfig() Config {
3852 }
3953 }
4054
55+ idToken := getenv ("PLUGIN_OIDC_TOKEN_ID" )
56+ projectId := getenv ("PLUGIN_PROJECT_NUMBER" )
57+ poolId := getenv ("PLUGIN_POOL_ID" )
58+ providerId := getenv ("PLUGIN_PROVIDER_ID" )
59+ serviceAccountEmail := getenv ("PLUGIN_SERVICE_ACCOUNT_EMAIL" )
60+
61+ if idToken != "" && projectId != "" && poolId != "" && providerId != "" && serviceAccountEmail != "" {
62+ federalToken , err := getFederalToken (idToken , projectId , poolId , providerId )
63+ if err != nil {
64+ logrus .Fatalf ("Error (getFederalToken): %s" , err )
65+ }
66+ accessToken , err := getGoogleCloudAccessToken (federalToken , serviceAccountEmail )
67+ if err != nil {
68+ logrus .Fatalf ("Error (getGoogleCloudAccessToken): %s" , err )
69+ }
70+ config .AccessToken = accessToken
71+ } else {
72+ password := getenv (
73+ "PLUGIN_JSON_KEY" ,
74+ "GCR_JSON_KEY" ,
75+ "GOOGLE_CREDENTIALS" ,
76+ "TOKEN" ,
77+ )
78+ config .WorkloadIdentity = parseBoolOrDefault (false , getenv ("PLUGIN_WORKLOAD_IDENTITY" ))
79+ config .Username , config .Password = setUsernameAndPassword (username , password , config .WorkloadIdentity )
80+ }
81+
4182 location := getenv ("PLUGIN_LOCATION" )
4283 repo := getenv ("PLUGIN_REPO" )
4384
44- password := getenv (
45- "PLUGIN_JSON_KEY" ,
46- "GCR_JSON_KEY" ,
47- "GOOGLE_CREDENTIALS" ,
48- "TOKEN" ,
49- )
50- workloadIdentity := parseBoolOrDefault (false , getenv ("PLUGIN_WORKLOAD_IDENTITY" ))
51- username , password = setUsernameAndPassword (username , password , workloadIdentity )
52-
5385 registryType := getenv ("PLUGIN_REGISTRY_TYPE" )
5486 if registryType == "" {
5587 registryType = "GCR"
@@ -73,24 +105,24 @@ func loadConfig() Config {
73105 if ! strings .HasPrefix (repo , registry ) {
74106 repo = path .Join (registry , repo )
75107 }
76-
77- return Config {
78- Repo : repo ,
79- Registry : registry ,
80- Password : password ,
81- WorkloadIdentity : workloadIdentity ,
82- Username : username ,
83- RegistryType : registryType ,
84- }
108+ config .Repo = repo
109+ config .Registry = registry
110+ config .RegistryType = registryType
111+ return config
85112}
86113
87114func main () {
88115 config := loadConfig ()
116+ if config .AccessToken != "" {
117+ os .Setenv ("ACCESS_TOKEN" , config .AccessToken )
118+ } else if config .Username != "" && config .Password != "" {
119+ os .Setenv ("DOCKER_USERNAME" , config .Username )
120+ os .Setenv ("DOCKER_PASSWORD" , config .Password )
121+ os .Setenv ("" , strconv .FormatBool (config .WorkloadIdentity ))
122+ }
89123
90124 os .Setenv ("PLUGIN_REPO" , config .Repo )
91125 os .Setenv ("PLUGIN_REGISTRY" , config .Registry )
92- os .Setenv ("DOCKER_USERNAME" , config .Username )
93- os .Setenv ("DOCKER_PASSWORD" , config .Password )
94126 os .Setenv ("PLUGIN_REGISTRY_TYPE" , config .RegistryType )
95127
96128 // invoke the base docker plugin binary
@@ -152,3 +184,49 @@ func getenv(key ...string) (s string) {
152184 }
153185 return
154186}
187+
188+ func getFederalToken (idToken , projectNumber , poolId , providerId string ) (string , error ) {
189+ ctx := context .Background ()
190+ stsService , err := sts .NewService (ctx , option .WithoutAuthentication ())
191+ if err != nil {
192+ return "" , err
193+ }
194+ audience := fmt .Sprintf ("//iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/providers/%s" , projectNumber , poolId , providerId )
195+ tokenRequest := & sts.GoogleIdentityStsV1ExchangeTokenRequest {
196+ GrantType : "urn:ietf:params:oauth:grant-type:token-exchange" ,
197+ SubjectToken : idToken ,
198+ Audience : audience ,
199+ Scope : "https://www.googleapis.com/auth/cloud-platform" ,
200+ RequestedTokenType : "urn:ietf:params:oauth:token-type:access_token" ,
201+ SubjectTokenType : "urn:ietf:params:oauth:token-type:id_token" ,
202+ }
203+ tokenResponse , err := stsService .V1 .Token (tokenRequest ).Do ()
204+ if err != nil {
205+ return "" , err
206+ }
207+
208+ return tokenResponse .AccessToken , nil
209+ }
210+
211+ func getGoogleCloudAccessToken (federatedToken string , serviceAccountEmail string ) (string , error ) {
212+ ctx := context .Background ()
213+ tokenSource := & staticTokenSource {
214+ token : & oauth2.Token {AccessToken : federatedToken },
215+ }
216+ service , err := iamcredentials .NewService (ctx , option .WithTokenSource (tokenSource ))
217+ if err != nil {
218+ return "" , err
219+ }
220+
221+ name := "projects/-/serviceAccounts/" + serviceAccountEmail
222+ rb := & iamcredentials.GenerateAccessTokenRequest {
223+ Scope : []string {"https://www.googleapis.com/auth/cloud-platform" },
224+ }
225+
226+ resp , err := service .Projects .ServiceAccounts .GenerateAccessToken (name , rb ).Do ()
227+ if err != nil {
228+ return "" , err
229+ }
230+
231+ return resp .AccessToken , nil
232+ }
0 commit comments