-
Notifications
You must be signed in to change notification settings - Fork 2
/
credentials.go
150 lines (127 loc) · 5.59 KB
/
credentials.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"fmt"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
"cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
auth "github.com/GoogleCloudBuild/cicd-images/cmd/git-steps/pkg"
"github.com/spf13/cobra"
"golang.org/x/oauth2/google"
"google.golang.org/api/developerconnect/v1"
"google.golang.org/api/option"
)
var (
url string
urlPath string
sshServerPublicKeys []string
sshPrivateKeySecretsResource string
gitRepositoryLink string
)
const gcpAccessTokenURL = "https://www.googleapis.com/auth/cloud-platform"
var generateCredentialsCmd = &cobra.Command{
Use: "generate-credentials",
Short: "Fetch secrets and generate credential files for authentication.",
Long: `Peform authentication by fetching secrets and generating related credential files.
Two authentication methods:
- SSH Keys: provided public keys, fetch private key from Secret Manager, and store the keys in their corresponding .ssh files
- Developer Connect: provided url of developer connect git repository, fetch access token, and store the credentials in the corresponding .git files
`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
// Get default access token
credentials, err := getAccessToken(ctx)
if err != nil {
return fmt.Errorf("error finding default credentials: %v", err)
}
if gitRepositoryLink != "" {
// Connect to Developer Connect API with access token
developerconnectService, err := developerconnect.NewService(ctx, option.WithTokenSource(credentials.TokenSource))
if err != nil {
return fmt.Errorf("error creating new developer connect service: %v", err)
}
arf := &repositoryFetcher{dc: developerconnectService}
if err := auth.AuthenticateWithDeveloperConnect(gitRepositoryLink, arf, urlPath); err != nil {
return err
}
} else if sshPrivateKeySecretsResource != "" {
// Authenticate with Secret Manager
client, err := secretmanager.NewClient(ctx, option.WithTokenSource(credentials.TokenSource))
if err != nil {
return fmt.Errorf("error authenticating and connecting to Secret Manager: %v\n", err)
}
defer client.Close()
sf := &secretVersionFetcher{client: client, ctx: ctx}
if err := auth.AuthenticateWithSSHKeys(sf, sshPrivateKeySecretsResource, url, sshServerPublicKeys, urlPath); err != nil {
return err
}
} else {
// for public repositories, just store url without auth
if err := auth.StoreURL(url, urlPath); err != nil {
return fmt.Errorf("error storing url without auth: %v", err)
}
}
return nil
},
}
func init() {
rootCmd.AddCommand(generateCredentialsCmd)
generateCredentialsCmd.Flags().StringVar(&url, "url", "", "The URL of the repository.")
generateCredentialsCmd.Flags().StringVar(&urlPath, "urlPath", "", "The path to store the extracted URL of the repository.")
generateCredentialsCmd.Flags().StringSliceVar(&sshServerPublicKeys, "sshServerPublicKeys", []string{}, "The public keys of the SSH server in an array.")
generateCredentialsCmd.Flags().StringVar(&sshPrivateKeySecretsResource, "sshPrivateKeySecretsResource", "", "The secret version resource name of the SSH private key saved on Secret Manager.")
generateCredentialsCmd.Flags().StringVar(&gitRepositoryLink, "gitRepositoryLink", "", "The resource name of the repository linked to Developer Connect.")
}
type repositoryFetcher struct {
dc *developerconnect.Service
}
var _ auth.DCRepoClient = (*repositoryFetcher)(nil) // Verify repositoryFetcher implements DCRepoClient
func (r *repositoryFetcher) Get(gitRepositoryLink string) (string, error) {
repoDetails, err := developerconnect.NewProjectsLocationsConnectionsGitRepositoryLinksService(r.dc).Get(gitRepositoryLink).Do()
if err != nil {
return "", err
}
return repoDetails.CloneUri, nil
}
func (r *repositoryFetcher) AccessReadWriteToken(gitRepositoryLink string) (string, error) {
tokenResponse, err := developerconnect.NewProjectsLocationsConnectionsGitRepositoryLinksService(r.dc).FetchReadWriteToken(gitRepositoryLink, &developerconnect.FetchReadWriteTokenRequest{}).Do()
if err != nil {
return "", err
}
return tokenResponse.Token, nil
}
type secretVersionFetcher struct {
client *secretmanager.Client
ctx context.Context
}
var _ auth.SMClient = (*secretVersionFetcher)(nil) // Verify secretVersionFetcher implements SMClient
func (r *secretVersionFetcher) AccessSecretVersion(req *secretmanagerpb.AccessSecretVersionRequest) (*secretmanagerpb.AccessSecretVersionResponse, error) {
resp, err := r.client.AccessSecretVersion(r.ctx, req)
if err != nil {
return &secretmanagerpb.AccessSecretVersionResponse{}, fmt.Errorf("error accessing secret version: %v\n", err)
}
return resp, nil
}
// Get access token for google cloud service.
func getAccessToken(ctx context.Context) (*google.Credentials, error) {
scopes := []string{
gcpAccessTokenURL,
}
credentials, err := google.FindDefaultCredentials(ctx, scopes...)
if err != nil {
return &google.Credentials{}, err
}
return credentials, nil
}