Skip to content

Commit 7fda2b3

Browse files
csweichelroboquat
authored andcommitted
[ws-manager] Only extract secrets when FF is set
1 parent d7bac05 commit 7fda2b3

File tree

8 files changed

+124
-84
lines changed

8 files changed

+124
-84
lines changed

components/content-service-api/go/initializer.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,20 @@ func GetCheckoutLocationsFromInitializer(init *WorkspaceInitializer) []string {
3333

3434
const extractedSecretPrefix = "extracted-secret/"
3535

36-
// ExtractSecretsFromInitializer removes secrets to the initializer.
36+
// GatherSecretsFromInitializer collects all from an initializer. This function does not
37+
// alter the initializer in any way.
38+
func GatherSecretsFromInitializer(init *WorkspaceInitializer) map[string]string {
39+
return extractSecretsFromInitializer(init, false)
40+
}
41+
42+
// ExtractAndReplaceSecretsFromInitializer removes secrets to the initializer.
43+
// This function alters the initializer, which only becomes useful calling InjectSecretsToInitializer.
3744
// This is the counterpart of InjectSecretsToInitializer.
38-
func ExtractSecretsFromInitializer(init *WorkspaceInitializer) map[string]string {
45+
func ExtractAndReplaceSecretsFromInitializer(init *WorkspaceInitializer) map[string]string {
46+
return extractSecretsFromInitializer(init, true)
47+
}
48+
49+
func extractSecretsFromInitializer(init *WorkspaceInitializer, replaceValue bool) map[string]string {
3950
res := make(map[string]string)
4051

4152
_ = WalkInitializer([]string{"initializer"}, init, func(path []string, init *WorkspaceInitializer) error {
@@ -51,7 +62,10 @@ func ExtractSecretsFromInitializer(init *WorkspaceInitializer) map[string]string
5162

5263
name := strings.Join(path, ".")
5364
res[name] = pwd
54-
git.Git.Config.AuthPassword = extractedSecretPrefix + name
65+
66+
if replaceValue {
67+
git.Git.Config.AuthPassword = extractedSecretPrefix + name
68+
}
5569

5670
return nil
5771
})

components/content-service-api/go/initializer_test.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111

1212
"github.com/gitpod-io/gitpod/content-service/api"
1313
"github.com/google/go-cmp/cmp"
14+
"github.com/google/go-cmp/cmp/cmpopts"
15+
"google.golang.org/protobuf/proto"
1416
)
1517

1618
func TestGetCheckoutLocationsFromInitializer(t *testing.T) {
@@ -176,7 +178,24 @@ func TestExtractInjectSecretsFromInitializer(t *testing.T) {
176178

177179
for _, test := range tests {
178180
t.Run(test.Name, func(t *testing.T) {
179-
act := api.ExtractSecretsFromInitializer(test.Input)
181+
original := proto.Clone(test.Input)
182+
act := api.GatherSecretsFromInitializer(test.Input)
183+
if diff := cmp.Diff(test.Expectation, act); diff != "" {
184+
t.Errorf("unexpected GatherSecretsFromInitializer (-want +got):\n%s", diff)
185+
}
186+
187+
ignoreUnexported := []interface{}{
188+
api.WorkspaceInitializer{},
189+
api.WorkspaceInitializer_Git{},
190+
api.GitInitializer{},
191+
api.GitConfig{},
192+
api.PrebuildInitializer{},
193+
}
194+
if diff := cmp.Diff(original, test.Input, cmpopts.IgnoreUnexported(ignoreUnexported...)); diff != "" {
195+
t.Errorf("unexpected alteration from GatherSecretsFromInitializer (-want +got):\n%s", diff)
196+
}
197+
198+
act = api.ExtractAndReplaceSecretsFromInitializer(test.Input)
180199
if diff := cmp.Diff(test.Expectation, act); diff != "" {
181200
t.Errorf("unexpected ExtractSecretsFromInitializer (-want +got):\n%s", diff)
182201
}

components/ws-manager/pkg/manager/create.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -969,30 +969,6 @@ func (m *Manager) newStartWorkspaceContext(ctx context.Context, req *api.StartWo
969969
}
970970
}
971971

972-
var (
973-
secrets = make(map[string]string)
974-
secretsLen int
975-
)
976-
for _, env := range req.Spec.Envvars {
977-
if env.Secret != nil {
978-
continue
979-
}
980-
if !isProtectedEnvVar(env.Name) {
981-
continue
982-
}
983-
984-
name := fmt.Sprintf("%x", sha256.Sum256([]byte(env.Name)))
985-
secrets[name] = env.Value
986-
secretsLen += len(env.Value)
987-
}
988-
for k, v := range csapi.ExtractSecretsFromInitializer(req.Spec.Initializer) {
989-
secrets[k] = v
990-
secretsLen += len(v)
991-
}
992-
if secretsLen > maxSecretsLength {
993-
return nil, xerrors.Errorf("secrets exceed maximum permitted length (%d > %d bytes): please reduce the numer or length of environment variables", secretsLen, maxSecretsLength)
994-
}
995-
996972
return &startWorkspaceContext{
997973
Labels: labels,
998974
CLIAPIKey: cliAPIKey,
@@ -1004,7 +980,6 @@ func (m *Manager) newStartWorkspaceContext(ctx context.Context, req *api.StartWo
1004980
Headless: headless,
1005981
Class: class,
1006982
VolumeSnapshot: volumeSnapshot,
1007-
Secrets: secrets,
1008983
}, nil
1009984
}
1010985

components/ws-manager/pkg/manager/create_test.go

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,67 +17,15 @@ import (
1717
"sigs.k8s.io/yaml"
1818

1919
ctesting "github.com/gitpod-io/gitpod/common-go/testing"
20-
csapi "github.com/gitpod-io/gitpod/content-service/api"
2120
"github.com/gitpod-io/gitpod/ws-manager/api"
2221
config "github.com/gitpod-io/gitpod/ws-manager/api/config"
23-
"github.com/google/go-cmp/cmp"
2422
)
2523

2624
var (
2725
team = "awesome"
2826
project = "gitpod"
2927
)
3028

31-
func TestNewStartWorkspaceContext(t *testing.T) {
32-
type Expectation struct {
33-
Context *startWorkspaceContext
34-
Error string
35-
}
36-
tests := []struct {
37-
Name string
38-
Req *api.StartWorkspaceRequest
39-
Expectation Expectation
40-
}{
41-
{
42-
Name: "oversized secrets",
43-
Req: &api.StartWorkspaceRequest{
44-
Metadata: &api.WorkspaceMetadata{Owner: "foo"},
45-
Spec: &api.StartWorkspaceSpec{
46-
Initializer: &csapi.WorkspaceInitializer{
47-
Spec: &csapi.WorkspaceInitializer_Empty{},
48-
},
49-
Envvars: []*api.EnvironmentVariable{
50-
{Name: "too_large", Value: string(func() []byte { return make([]byte, 2*maxSecretsLength) }())},
51-
},
52-
},
53-
},
54-
Expectation: Expectation{
55-
Error: "secrets exceed maximum permitted length (1610612736 > 805306368 bytes): please reduce the numer or length of environment variables",
56-
},
57-
},
58-
}
59-
60-
for _, test := range tests {
61-
t.Run(test.Name, func(t *testing.T) {
62-
mgmtCfg := forTestingOnlyManagerConfig()
63-
manager := &Manager{Config: mgmtCfg}
64-
65-
sctx, err := manager.newStartWorkspaceContext(context.Background(), test.Req)
66-
67-
act := Expectation{
68-
Context: sctx,
69-
}
70-
if err != nil {
71-
act.Error = err.Error()
72-
}
73-
74-
if diff := cmp.Diff(test.Expectation, act); diff != "" {
75-
t.Errorf("unexpected newStartWorkspaceContext (-want +got):\n%s", diff)
76-
}
77-
})
78-
}
79-
}
80-
8129
func TestCreateDefiniteWorkspacePod(t *testing.T) {
8230
type WorkspaceClass struct {
8331
DefaultTemplate *corev1.Pod `json:"defaultTemplate,omitempty"`

components/ws-manager/pkg/manager/manager.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package manager
66

77
import (
88
"context"
9+
"crypto/sha256"
910
"encoding/base64"
1011
"encoding/json"
1112
"fmt"
@@ -39,6 +40,7 @@ import (
3940
wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
4041
"github.com/gitpod-io/gitpod/common-go/log"
4142
"github.com/gitpod-io/gitpod/common-go/tracing"
43+
csapi "github.com/gitpod-io/gitpod/content-service/api"
4244
"github.com/gitpod-io/gitpod/content-service/pkg/layer"
4345
regapi "github.com/gitpod-io/gitpod/registry-facade/api"
4446
wsdaemon "github.com/gitpod-io/gitpod/ws-daemon/api"
@@ -82,7 +84,6 @@ type startWorkspaceContext struct {
8284
Headless bool `json:"headless"`
8385
Class *config.WorkspaceClass `json:"class"`
8486
VolumeSnapshot *workspaceVolumeSnapshotStatus `json:"volumeSnapshot"`
85-
Secrets map[string]string `json:"secrets"`
8687
}
8788

8889
func (swctx *startWorkspaceContext) ContainerConfiguration() config.ContainerConfiguration {
@@ -267,13 +268,21 @@ func (m *Manager) StartWorkspace(ctx context.Context, req *api.StartWorkspaceReq
267268
}
268269
}
269270
if createSecret {
271+
secrets, _ := buildWorkspaceSecrets(startContext.Request.Spec)
272+
273+
// This call actually modifies the initializer and removes the secrets.
274+
// Prior to the `InitWorkspace` call, we inject the secrets back into the initializer.
275+
// We do this so that no Git token is stored as annotation on the pod, but solely
276+
// remains within the Kubernetes secret.
277+
_ = csapi.ExtractAndReplaceSecretsFromInitializer(startContext.Request.Spec.Initializer)
278+
270279
secret := &corev1.Secret{
271280
ObjectMeta: metav1.ObjectMeta{
272281
Name: podName(startContext.Request),
273282
Namespace: m.Config.Namespace,
274283
Labels: startContext.Labels,
275284
},
276-
StringData: startContext.Secrets,
285+
StringData: secrets,
277286
}
278287
err = m.Clientset.Create(ctx, secret)
279288
if err != nil && !k8serr.IsAlreadyExists(err) {
@@ -411,6 +420,27 @@ func (m *Manager) StartWorkspace(ctx context.Context, req *api.StartWorkspaceReq
411420
return okResponse, nil
412421
}
413422

423+
func buildWorkspaceSecrets(spec *api.StartWorkspaceSpec) (secrets map[string]string, secretsLen int) {
424+
secrets = make(map[string]string)
425+
for _, env := range spec.Envvars {
426+
if env.Secret != nil {
427+
continue
428+
}
429+
if !isProtectedEnvVar(env.Name) {
430+
continue
431+
}
432+
433+
name := fmt.Sprintf("%x", sha256.Sum256([]byte(env.Name)))
434+
secrets[name] = env.Value
435+
secretsLen += len(env.Value)
436+
}
437+
for k, v := range csapi.GatherSecretsFromInitializer(spec.Initializer) {
438+
secrets[k] = v
439+
secretsLen += len(v)
440+
}
441+
return secrets, secretsLen
442+
}
443+
414444
func (m *Manager) restoreVolumeSnapshotFromHandle(ctx context.Context, id, handle string) (err error) {
415445
span, ctx := tracing.FromContext(ctx, "restoreVolumeSnapshotFromHandle")
416446
defer tracing.FinishSpan(span, &err)
@@ -586,6 +616,10 @@ func validateStartWorkspaceRequest(req *api.StartWorkspaceRequest) error {
586616
return xerrors.Errorf("invalid request: %w", err)
587617
}
588618

619+
if _, secretsLen := buildWorkspaceSecrets(req.Spec); secretsLen > maxSecretsLength {
620+
return xerrors.Errorf("secrets exceed maximum permitted length (%d > %d bytes): please reduce the numer or length of environment variables", secretsLen, maxSecretsLength)
621+
}
622+
589623
rules := make([]*validation.FieldRules, 0)
590624
rules = append(rules, validation.Field(&req.Id, validation.Required))
591625
rules = append(rules, validation.Field(&req.Spec, validation.Required))

components/ws-manager/pkg/manager/manager_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import (
2323

2424
func TestValidateStartWorkspaceRequest(t *testing.T) {
2525
type fixture struct {
26-
Req *api.StartWorkspaceRequest `json:"request"`
26+
Req *api.StartWorkspaceRequest `json:"request"`
27+
BloatEnv bool `json:"bloatEnv"`
2728
}
2829
type gold struct {
2930
Error string `json:"error,omitempty"`
@@ -41,6 +42,13 @@ func TestValidateStartWorkspaceRequest(t *testing.T) {
4142
return nil
4243
}
4344

45+
if fixture.BloatEnv {
46+
fixture.Req.Spec.Envvars = append(fixture.Req.Spec.Envvars, &api.EnvironmentVariable{
47+
Name: "BLOAT",
48+
Value: string(make([]byte, 2*maxSecretsLength)),
49+
})
50+
}
51+
4452
err := validateStartWorkspaceRequest(fixture.Req)
4553
if err != nil {
4654
return &gold{Error: err.Error()}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"error": "secrets exceed maximum permitted length (1610612739 \u003e 805306368 bytes): please reduce the numer or length of environment variables"
3+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"bloatEnv": true,
3+
"request": {
4+
"id": "foobar",
5+
"type": 0,
6+
"metadata": {
7+
"owner": "tester",
8+
"metaId": "foobar"
9+
},
10+
"servicePrefix": "foobarservice",
11+
"spec": {
12+
"ideImage": {
13+
"webRef": "eu.gcr.io/gitpod-core-dev/buid/theia-ide:someversion"
14+
},
15+
"workspace_image": "eu.gcr.io/gitpod-dev/workspace-images/ac1c0755007966e4d6e090ea821729ac747d22ac/eu.gcr.io/gitpod-dev/workspace-base-images/github.com/typefox/gitpod:80a7d427a1fcd346d420603d80a31d57cf75a7af",
16+
"workspace_location": "/",
17+
"initializer": {
18+
"snapshot": {
19+
"snapshot": "workspaces/cryptic-id-goes-herg/fd62804b-4cab-11e9-843a-4e645373048e.tar@gitpod-dev-user-christesting"
20+
}
21+
},
22+
"ports": [
23+
{
24+
"port": 8080
25+
}
26+
],
27+
"envvars": [
28+
{
29+
"name": "foo",
30+
"value": "bar"
31+
}
32+
],
33+
"git": {
34+
"username": "usernameGoesHere",
35+
"email": "some@user.com"
36+
}
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)