-
Notifications
You must be signed in to change notification settings - Fork 20
/
sks_kubeconfig.go
195 lines (161 loc) · 5.98 KB
/
sks_kubeconfig.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package cmd
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"time"
exoapi "github.com/exoscale/egoscale/v2/api"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
)
type sksKubeconfigCmd struct {
cliCommandSettings `cli-cmd:"-"`
_ bool `cli-cmd:"kubeconfig"`
Cluster string `cli-arg:"#" cli-usage:"CLUSTER-NAME|ID"`
User string `cli-arg:"#"`
ExecCredential bool `cli-short:"x" cli-usage:"output an ExecCredential object to use with a kubeconfig user.exec mode"`
Groups []string `cli-flag:"group" cli-short:"g" cli-usage:"client certificate group. Can be specified multiple times. Defaults to system:masters"`
TTL int64 `cli-short:"t" cli-usage:"client certificate validity duration in seconds"`
Zone string `cli-short:"z" cli-usage:"SKS cluster zone"`
}
func (c *sksKubeconfigCmd) cmdAliases() []string { return []string{"kc"} }
func (c *sksKubeconfigCmd) cmdShort() string {
return "Generate a Kubernetes kubeconfig file for an SKS cluster"
}
func (c *sksKubeconfigCmd) cmdLong() string {
return `This command generates a kubeconfig file to be used for authenticating to an SKS
cluster API.
The "user" command argument corresponds to the CN field of the generated X.509
client certificate. Optionally, you can specify client certificate groups
using the "-g|--group" option: those groups will be set in the "O" field of
the certificate. See [1] for more information about Kubernetes authentication
certificates.
Example usage:
# Obtain "cluster-admin" credentials
$ exo compute sks kubeconfig my-cluster admin \
--zone de-fra-1 \
-g system:masters \
-t $((86400 * 7)) > $HOME/.kube/my-cluster.config
$ kubeconfig --kubeconfig=$HOME/.kube/my-cluster.config get pods
Note: if no TTL value is specified, the API applies a default value as a
safety measure. Please look up the API documentation for more information.
## Using exo CLI as Kubernetes credential plugin
If you wish to avoid leaving sensitive credentials on your system, you can use
exo CLI as a Kubernetes client-go credential plugin[2] to generate and return
a kubeconfig dynamically when invoked by kubectl without storing it on disk.
To achieve this configuration, edit your kubeconfig file so that the
"users" section relating to your cluster ("my-sks-cluster" in the following
example) looks like:
apiVersion: v1
kind: Config
clusters:
- name: my-sks-cluster
cluster:
certificate-authority-data: **BASE64-ENCODED CLUSTER CERTIFICATE**
server: https://153fcc53-1197-46ae-a8e0-ccf6d09efcb0.sks-ch-gva-2.exo.io:443
users:
- name: exo@my-sks-cluster
user:
# The "exec" section replaces "client-certificate-data"/"client-key-data"
exec:
apiVersion: "client.authentication.k8s.io/v1beta1"
command: exo
args:
- sks
- kubeconfig
- my-sks-cluster
- --zone=ch-gva-2
- --exec-credential
- user
contexts:
- name: my-sks-cluster
context:
cluster: my-sks-cluster
user: exo@my-sks-cluster
current-context: my-sks-cluster
Notes:
* The "exo" CLI binary must be installed in a directory listed in your PATH
shell environment variable.
* You can specify the "--group" flag in the user.exec.args section referencing
a non-admin group to restrict the privileges of the operator using kubectl.
[1]: https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs
[2]: https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
`
}
func (c *sksKubeconfigCmd) cmdPreRun(cmd *cobra.Command, args []string) error {
cmdSetZoneFlagFromDefault(cmd)
return cliCommandDefaultPreRun(c, cmd, args)
}
func (c *sksKubeconfigCmd) cmdRun(_ *cobra.Command, _ []string) error {
ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(gCurrentAccount.Environment, c.Zone))
// We cannot use the flag's default here as it would be additive
if len(c.Groups) == 0 {
c.Groups = []string{"system:masters"}
}
cluster, err := cs.FindSKSCluster(ctx, c.Zone, c.Cluster)
if err != nil {
if errors.Is(err, exoapi.ErrNotFound) {
return fmt.Errorf("resource not found in zone %q", c.Zone)
}
return err
}
b64Kubeconfig, err := cs.GetSKSClusterKubeconfig(
ctx,
c.Zone,
cluster,
c.User,
c.Groups,
time.Duration(c.TTL)*time.Second,
)
if err != nil {
return fmt.Errorf("error retrieving kubeconfig: %w", err)
}
kubeconfig, err := base64.StdEncoding.DecodeString(b64Kubeconfig)
if err != nil {
return fmt.Errorf("error decoding kubeconfig content: %w", err)
}
if !c.ExecCredential {
fmt.Print(string(kubeconfig))
return nil
}
k := struct {
Users []struct {
Name string `yaml:"name"`
User map[string]string `yaml:"user"`
} `yaml:"users"`
}{}
if err := yaml.Unmarshal(kubeconfig, &k); err != nil {
return fmt.Errorf("error decoding kubeconfig content: %w", err)
}
ecClientCertificateData, err := base64.StdEncoding.DecodeString(k.Users[0].User["client-certificate-data"])
if err != nil {
return fmt.Errorf("error decoding kubeconfig content: %w", err)
}
ecClientKeyData, err := base64.StdEncoding.DecodeString(k.Users[0].User["client-key-data"])
if err != nil {
return fmt.Errorf("error decoding kubeconfig content: %w", err)
}
ecOut, err := json.Marshal(map[string]interface{}{
"apiVersion": "client.authentication.k8s.io/v1beta1",
"kind": "ExecCredential",
"status": map[string]string{
"clientCertificateData": string(ecClientCertificateData),
"clientKeyData": string(ecClientKeyData),
},
})
if err != nil {
return fmt.Errorf("error encoding exec credential content: %w", err)
}
fmt.Print(string(ecOut))
return nil
}
func init() {
cobra.CheckErr(registerCLICommand(sksCmd, &sksKubeconfigCmd{
cliCommandSettings: defaultCLICmdSettings(),
}))
// FIXME: remove this someday.
cobra.CheckErr(registerCLICommand(deprecatedSKSCmd, &sksKubeconfigCmd{
cliCommandSettings: defaultCLICmdSettings(),
}))
}