-
-
Notifications
You must be signed in to change notification settings - Fork 174
Expand file tree
/
Copy pathtoken.go
More file actions
141 lines (126 loc) · 4.66 KB
/
Copy pathtoken.go
File metadata and controls
141 lines (126 loc) · 4.66 KB
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
package gke
import (
"context"
"encoding/json"
"fmt"
"os"
"time"
"github.com/spf13/cobra"
errUtils "github.com/cloudposse/atmos/errors"
"github.com/cloudposse/atmos/pkg/auth"
gcpCloud "github.com/cloudposse/atmos/pkg/auth/cloud/gcp"
"github.com/cloudposse/atmos/pkg/auth/credentials"
"github.com/cloudposse/atmos/pkg/auth/types"
"github.com/cloudposse/atmos/pkg/auth/validation"
cfg "github.com/cloudposse/atmos/pkg/config"
"github.com/cloudposse/atmos/pkg/data"
"github.com/cloudposse/atmos/pkg/perf"
"github.com/cloudposse/atmos/pkg/schema"
)
const execCredentialAPIVersion = "client.authentication.k8s.io/v1beta1"
var (
initCliConfigFn = cfg.InitCliConfig
authenticateForTokenFn = authenticateForToken
getGKETokenFn = gcpCloud.GetToken
newAuthManagerFn = auth.NewAuthManager
)
var tokenCmd = &cobra.Command{
Use: "token",
Short: "Generate a GKE bearer token for kubectl",
Long: "Generate a Kubernetes ExecCredential from an Atmos-managed GCP identity. This command is normally invoked by kubectl from an Atmos-generated kubeconfig.",
Args: cobra.NoArgs,
RunE: executeTokenCommand,
SilenceUsage: true,
}
type execCredential struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Status execCredentialStatus `json:"status"`
}
type execCredentialStatus struct {
ExpirationTimestamp string `json:"expirationTimestamp,omitempty"`
Token string `json:"token"`
}
func executeTokenCommand(cmd *cobra.Command, _ []string) error {
atmosConfig, err := initCliConfigFn(schema.ConfigAndStacksInfo{}, false)
if err != nil {
return fmt.Errorf(errUtils.ErrWrapFormat, errUtils.ErrFailedToInitConfig, err)
}
defer perf.Track(&atmosConfig, "gke.executeTokenCommand")()
identityName := resolveIdentity(cmd)
ctx := auth.ContextWithSkipIntegrations(context.Background())
creds, err := authenticateForTokenFn(ctx, &atmosConfig.Auth, atmosConfig.CliConfigPath, identityName)
if err != nil {
return fmt.Errorf("%w: %w", errUtils.ErrGKETokenGeneration, err)
}
token, expiresAt, err := getGKETokenFn(creds)
if err != nil {
return fmt.Errorf("%w: %w", errUtils.ErrGKETokenGeneration, err)
}
return writeExecCredential(token, expiresAt)
}
func writeExecCredential(token string, expiresAt time.Time) error {
status := execCredentialStatus{Token: token}
if !expiresAt.IsZero() {
status.ExpirationTimestamp = expiresAt.UTC().Format(time.RFC3339)
}
payload, err := json.Marshal(execCredential{
APIVersion: execCredentialAPIVersion,
Kind: "ExecCredential",
Status: status,
})
if err != nil {
return fmt.Errorf("%w: failed to marshal ExecCredential: %w", errUtils.ErrGKETokenGeneration, err)
}
return data.Write(string(payload))
}
func resolveIdentity(cmd *cobra.Command) string {
identityName, _ := cmd.Flags().GetString("identity")
if identityName != "" {
return identityName
}
return os.Getenv("ATMOS_IDENTITY") //nolint:forbidigo // Exec plugins inherit this explicit identity selector.
}
func authenticateForToken(ctx context.Context, authConfig *schema.AuthConfig, cliConfigPath, identityName string) (types.ICredentials, error) {
authStackInfo := &schema.ConfigAndStacksInfo{AuthContext: &schema.AuthContext{}}
mgr, err := newAuthManagerFn(
authConfig,
credentials.NewCredentialStoreWithConfig(authConfig),
validation.NewValidator(),
authStackInfo,
cliConfigPath,
)
if err != nil {
return nil, fmt.Errorf(errUtils.ErrWrapFormat, errUtils.ErrFailedToInitializeAuthManager, err)
}
if identityName == "" {
identityName = resolveDefaultIdentity(authConfig)
if identityName == "" {
return nil, fmt.Errorf("%w: no identity specified and no default identity found", errUtils.ErrGKETokenGeneration)
}
}
whoami, err := mgr.Authenticate(ctx, identityName)
if err != nil {
return nil, fmt.Errorf(errUtils.ErrWrapWithNameAndCauseFormat, errUtils.ErrIdentityAuthFailed, identityName, err)
}
if whoami.Credentials == nil {
return nil, fmt.Errorf(errUtils.ErrWrapWithNameAndCauseFormat, errUtils.ErrIdentityAuthFailed, identityName, errUtils.ErrIdentityCredentialsNone)
}
if _, ok := whoami.Credentials.(*types.GCPCredentials); !ok {
return nil, fmt.Errorf("%w: identity %q returned non-GCP credentials", errUtils.ErrGKETokenGeneration, identityName)
}
return whoami.Credentials, nil
}
func resolveDefaultIdentity(authConfig *schema.AuthConfig) string {
if authConfig == nil || len(authConfig.Identities) != 1 {
return ""
}
for name := range authConfig.Identities {
return name
}
return ""
}
func init() {
tokenCmd.Flags().StringP("identity", "i", "", "Atmos GCP identity to authenticate with")
GkeCmd.AddCommand(tokenCmd)
}