Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/dev/preview/previewctl/cmd/admin.go
2500 views
1
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package cmd
6
7
import (
8
"context"
9
"encoding/hex"
10
"encoding/json"
11
"fmt"
12
"math/rand"
13
"time"
14
15
"crypto/sha512"
16
17
"github.com/gitpod-io/gitpod/previewctl/pkg/preview"
18
"github.com/sirupsen/logrus"
19
"github.com/spf13/cobra"
20
v1 "k8s.io/api/core/v1"
21
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22
"k8s.io/client-go/kubernetes"
23
"k8s.io/client-go/tools/clientcmd"
24
)
25
26
func newAdminCmd(logger *logrus.Logger) *cobra.Command {
27
cmd := &cobra.Command{
28
Use: "admin",
29
Short: "Manage installation's admin",
30
}
31
32
credentialsCmd := &cobra.Command{
33
Use: "credentials",
34
Short: "Manage credentials",
35
}
36
37
credentialsCmd.AddCommand(
38
newCreateAdminCredentialsCmd(logger),
39
)
40
41
cmd.AddCommand(credentialsCmd)
42
43
return cmd
44
}
45
46
func newCreateAdminCredentialsCmd(logger *logrus.Logger) *cobra.Command {
47
var (
48
expiry time.Duration
49
)
50
51
createAdminCredentialsCmd := &cobra.Command{
52
Use: "create",
53
Short: "Create admin credentials",
54
RunE: func(cmd *cobra.Command, args []string) error {
55
ctx := cmd.Context()
56
token, creds, err := createAdminCredentials(ctx, expiry)
57
if err != nil {
58
logger.WithError(err).Fatal("Failed to create admin credentials.")
59
}
60
previewName, err := preview.GetName(branch)
61
if err != nil {
62
return err
63
}
64
logger.
65
WithField("hash", creds.TokenHash).
66
WithField("algo", creds.Algo).
67
WithField("expires_unix", creds.ExpiresAt).
68
WithField("expires", time.Unix(creds.ExpiresAt, 0).Format(time.RFC3339)).
69
Infof("Created new admin credentials: https://%s.preview.gitpod-dev.com/api/login/ots/admin/%s", previewName, token)
70
return nil
71
},
72
}
73
74
createAdminCredentialsCmd.Flags().DurationVar(&expiry, "expiry", 20*time.Minute, "When the credentials expire, from now")
75
76
return createAdminCredentialsCmd
77
}
78
79
type adminCredentials struct {
80
TokenHash string `json:"tokenHash"`
81
Algo string `json:"algo"`
82
ExpiresAt int64 `json:"expiresAt"`
83
}
84
85
func createAdminCredentials(ctx context.Context, expiry time.Duration) (string, *adminCredentials, error) {
86
config, err := clientcmd.BuildConfigFromFlags("", getKubeConfigPath())
87
if err != nil {
88
return "", nil, fmt.Errorf("failed to get k8s config from env: %w", err)
89
}
90
91
// create the clientset
92
clientset, err := kubernetes.NewForConfig(config)
93
if err != nil {
94
return "", nil, fmt.Errorf("failed to construct k8s client: %w", err)
95
}
96
97
token := randStringBytes(20)
98
99
h := sha512.New()
100
h.Write([]byte(token))
101
102
digest := hex.EncodeToString(h.Sum(nil))
103
104
creds := &adminCredentials{
105
Algo: "sha512",
106
TokenHash: digest,
107
ExpiresAt: time.Now().UTC().Add(expiry).Unix(),
108
}
109
110
secretContents, err := json.Marshal(creds)
111
if err != nil {
112
return "", nil, fmt.Errorf("failed to serialize credentials into JSON: %w", err)
113
}
114
115
secretsAPI := clientset.CoreV1().Secrets("default")
116
secret := &v1.Secret{
117
TypeMeta: metav1.TypeMeta{
118
APIVersion: "v1",
119
Kind: "Secret",
120
},
121
ObjectMeta: metav1.ObjectMeta{
122
Name: "admin-credentials",
123
Namespace: "default",
124
Labels: map[string]string{
125
"app": "gitpod",
126
"component": "server",
127
},
128
},
129
Data: map[string][]byte{
130
"admin.json": secretContents,
131
},
132
}
133
existingSecret, err := secretsAPI.Get(ctx, secret.ObjectMeta.Name, metav1.GetOptions{})
134
if err != nil {
135
// secret does not yet exist, create it
136
_, err = secretsAPI.Create(ctx, secret, metav1.CreateOptions{})
137
if err != nil {
138
return "", nil, fmt.Errorf("failed to create secret with admin credentials: %w", err)
139
}
140
} else {
141
// secret exists, update it
142
existingSecret.Data = secret.Data // Update the Data of the existing secret
143
_, err = secretsAPI.Update(ctx, existingSecret, metav1.UpdateOptions{})
144
if err != nil {
145
return "", nil, fmt.Errorf("failed to update secret with admin credentials: %w", err)
146
}
147
}
148
149
return token, creds, nil
150
}
151
152
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
153
154
func randStringBytes(n int) string {
155
b := make([]byte, n)
156
for i := range b {
157
b[i] = letterBytes[rand.Intn(len(letterBytes))]
158
}
159
return string(b)
160
}
161
162