Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions components/ws-proxy/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ require (

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/configcat/go-sdk/v7 v7.6.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch/v5 v5.8.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions components/ws-proxy/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions components/ws-proxy/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@
package config

import (
"context"
"encoding/json"
"os"
"time"

"golang.org/x/xerrors"

"github.com/gitpod-io/gitpod/common-go/experiments"
"github.com/gitpod-io/gitpod/common-go/log"
"github.com/gitpod-io/gitpod/ws-proxy/pkg/proxy"
)

const experimentsCorsEnabled = "ws_proxy_cors_enabled"

// Config configures this service.
type Config struct {
Ingress proxy.HostBasedIngressConfig `json:"ingress"`
Expand Down Expand Up @@ -64,5 +70,31 @@ func GetConfig(fn string) (*Config, error) {
return nil, xerrors.Errorf("config validation error: %w", err)
}

timeout := time.Minute * 5
log.WithField("timeout", timeout).Info("waiting for Feature Flag")
experimentsClient := experiments.NewClient(experiments.WithPollInterval(time.Second * 3))
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
ffValue := waitExperimentsStringValue(ctx, experimentsClient, experimentsCorsEnabled, "nope", experiments.Attributes{})
corsEnabled := ffValue == "true"
cfg.Proxy.CorsEnabled = corsEnabled
log.WithField("ffValue", ffValue).WithField("corsEnabled", cfg.Proxy.CorsEnabled).Info("feature flag final value")

return &cfg, nil
}

func waitExperimentsStringValue(ctx context.Context, client experiments.Client, experimentName, nopeValue string, attributes experiments.Attributes) string {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return nopeValue
case <-ticker.C:
value := client.GetStringValue(ctx, experimentName, nopeValue, attributes)
if value != nopeValue {
return value
}
}
}
}
1 change: 1 addition & 0 deletions components/ws-proxy/pkg/proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Config struct {

BuiltinPages BuiltinPagesConfig `json:"builtinPages"`
SSHGatewayCAKeyFile string `json:"sshCAKeyFile"`
CorsEnabled bool
}

// Validate validates the configuration to catch issues during startup and not at runtime.
Expand Down
12 changes: 9 additions & 3 deletions components/ws-proxy/pkg/proxy/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,10 @@ func WithDefaultAuth(infoprov common.WorkspaceInfoProvider) RouteHandlerConfigOp

// NewRouteHandlerConfig creates a new instance.
func NewRouteHandlerConfig(config *Config, opts ...RouteHandlerConfigOpt) (*RouteHandlerConfig, error) {
corsHandler, err := corsHandler(config.GitpodInstallation.Scheme, config.GitpodInstallation.HostName)
corsHandler, err := corsHandler(config.CorsEnabled, config.GitpodInstallation.Scheme, config.GitpodInstallation.HostName)
if err != nil {
return nil, err
}

cfg := &RouteHandlerConfig{
Config: config,
DefaultTransport: createDefaultTransport(config.TransportConfig),
Expand Down Expand Up @@ -190,6 +189,7 @@ func (ir *ideRoutes) HandleCreateKeyRoute(route *mux.Route, hostKeyList []ssh.Si
r := route.Subrouter()
r.Use(logRouteHandlerHandler("HandleCreateKeyRoute"))
r.Use(ir.Config.CorsHandler)

r.Use(ir.workspaceMustExistHandler)
r.Use(ir.Config.WorkspaceAuthHandler)

Expand Down Expand Up @@ -655,7 +655,13 @@ func buildWorkspacePodURL(protocol api.PortProtocol, ipAddress string, port stri
}

// corsHandler produces the CORS handler for workspaces.
func corsHandler(scheme, hostname string) (mux.MiddlewareFunc, error) {
func corsHandler(enabled bool, scheme, hostname string) (mux.MiddlewareFunc, error) {
if !enabled {
// empty handler
return func(h http.Handler) http.Handler {
return h
}, nil
}
origin := fmt.Sprintf("%s://%s", scheme, hostname)

domainRegex := strings.ReplaceAll(hostname, ".", "\\.")
Expand Down
12 changes: 5 additions & 7 deletions components/ws-proxy/pkg/proxy/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ var (
BuiltinPages: BuiltinPagesConfig{
Location: "../../public",
},
CorsEnabled: false,
}
)

Expand Down Expand Up @@ -450,7 +451,7 @@ func TestRoutes(t *testing.T) {
},
},
{
Desc: "CORS preflight",
Desc: "no CORS allow in workspace urls",
Config: &config,
Request: modifyRequest(httptest.NewRequest("GET", workspaces[0].URL+"somewhere/in/the/ide", nil),
addHostHeader,
Expand All @@ -462,12 +463,9 @@ func TestRoutes(t *testing.T) {
Expectation: Expectation{
Status: http.StatusOK,
Header: http.Header{
"Access-Control-Allow-Credentials": {"true"},
"Access-Control-Allow-Origin": {"test-domain.com"},
"Access-Control-Expose-Headers": {"Authorization"},
"Content-Length": {"37"},
"Content-Type": {"text/plain; charset=utf-8"},
"Vary": {"Accept-Encoding"},
"Content-Length": {"37"},
"Content-Type": {"text/plain; charset=utf-8"},
"Vary": {"Accept-Encoding"},
},
Body: "workspace hit: /somewhere/in/the/ide\n",
},
Expand Down
25 changes: 25 additions & 0 deletions install/installer/pkg/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,31 @@ func ConfigcatEnv(ctx *RenderContext) []corev1.EnvVar {
}
}

func ConfigcatEnvOutOfCluster(ctx *RenderContext) []corev1.EnvVar {
var sdkKey string
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
if cfg.WebApp != nil && cfg.WebApp.ConfigcatKey != "" {
sdkKey = cfg.WebApp.ConfigcatKey
}
return nil
})

if sdkKey == "" {
return nil
}

return []corev1.EnvVar{
{
Name: "CONFIGCAT_SDK_KEY",
Value: "gitpod",
},
{
Name: "CONFIGCAT_BASE_URL",
Value: fmt.Sprintf("https://%s/configcat", ctx.Config.Domain),
},
}
}

func ConfigcatProxyEnv(ctx *RenderContext) []corev1.EnvVar {
var (
sdkKey string
Expand Down
18 changes: 18 additions & 0 deletions install/installer/pkg/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import (
"github.com/gitpod-io/gitpod/common-go/baseserver"
"github.com/gitpod-io/gitpod/installer/pkg/common"
config "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"
"github.com/gitpod-io/gitpod/installer/pkg/config/versions"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
)

func TestKubeRBACProxyContainer_DefaultPorts(t *testing.T) {
Expand Down Expand Up @@ -73,3 +75,19 @@ func TestServerComponentWaiterContainer(t *testing.T) {
require.Equal(t, labels, "app=gitpod,component=server")
require.Equal(t, []string{"-v", "component", "--namespace", "test_namespace", "--component", common.ServerComponent, "--labels", labels, "--image", ctx.Config.Repository + "/server:" + "happy_path_server_image"}, container.Args)
}

func TestConfigcatEnvOutOfCluster(t *testing.T) {
ctx, err := common.NewRenderContext(config.Config{
Domain: "gitpod.io",
Experimental: &experimental.Config{
WebApp: &experimental.WebAppConfig{
ConfigcatKey: "foo",
},
},
}, versions.Manifest{}, "test_namespace")
require.NoError(t, err)

envVars := common.ConfigcatEnvOutOfCluster(ctx)
require.Equal(t, len(envVars), 2)
require.Equal(t, envVars, []v1.EnvVar([]v1.EnvVar{{Name: "CONFIGCAT_SDK_KEY", Value: "gitpod"}, {Name: "CONFIGCAT_BASE_URL", Value: "https://gitpod.io/configcat"}}))
}
2 changes: 2 additions & 0 deletions install/installer/pkg/components/ws-proxy/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) {
common.DefaultEnv(&ctx.Config),
common.WorkspaceTracingEnv(ctx, Component),
common.AnalyticsEnv(&ctx.Config),
// ws-proxy and proxy may not in the same cluster
common.ConfigcatEnvOutOfCluster(ctx),
)),
ReadinessProbe: &corev1.Probe{
InitialDelaySeconds: int32(2),
Expand Down
Loading