Convert CLI tests to work through an HTTP forward proxy.

This change deploys a small Squid-based proxy into the `dex` namespace in our integration test environment. This lets us use the cluster-local DNS name (`http://dex.dex.svc.cluster.local/dex`) as the OIDC issuer. It will make generating certificates easier, and most importantly it will mean that our CLI can see Dex at the same name/URL as the supervisor running inside the cluster.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
Matt Moyer 2020-11-16 10:40:18 -06:00
parent a4733025ce
commit c8b17978a9
No known key found for this signature in database
GPG Key ID: EAE88AD172C5AE2D
6 changed files with 92 additions and 13 deletions

View File

@ -286,7 +286,8 @@ export PINNIPED_TEST_SUPERVISOR_APP_NAME=${supervisor_app_name}
export PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS='${supervisor_custom_labels}' export PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS='${supervisor_custom_labels}'
export PINNIPED_TEST_SUPERVISOR_HTTP_ADDRESS="127.0.0.1:12345" export PINNIPED_TEST_SUPERVISOR_HTTP_ADDRESS="127.0.0.1:12345"
export PINNIPED_TEST_SUPERVISOR_HTTPS_ADDRESS="localhost:12344" export PINNIPED_TEST_SUPERVISOR_HTTPS_ADDRESS="localhost:12344"
export PINNIPED_TEST_CLI_OIDC_ISSUER=http://127.0.0.1:12346/dex export PINNIPED_TEST_PROXY=http://127.0.0.1:12346
export PINNIPED_TEST_CLI_OIDC_ISSUER=http://dex.dex.svc.cluster.local/dex
export PINNIPED_TEST_CLI_OIDC_CLIENT_ID=pinniped-cli export PINNIPED_TEST_CLI_OIDC_CLIENT_ID=pinniped-cli
export PINNIPED_TEST_CLI_OIDC_LOCALHOST_PORT=48095 export PINNIPED_TEST_CLI_OIDC_LOCALHOST_PORT=48095
export PINNIPED_TEST_CLI_OIDC_USERNAME=pinny@example.com export PINNIPED_TEST_CLI_OIDC_USERNAME=pinny@example.com

View File

@ -6,13 +6,13 @@
#@ load("@ytt:yaml", "yaml") #@ load("@ytt:yaml", "yaml")
#@ def dexConfig(): #@ def dexConfig():
issuer: #@ "http://127.0.0.1:" + str(data.values.ports.local) + "/dex" issuer: http://dex.dex.svc.cluster.local/dex
storage: storage:
type: sqlite3 type: sqlite3
config: config:
file: ":memory:" file: ":memory:"
web: web:
http: 0.0.0.0:5556 http: 0.0.0.0:80
oauth2: oauth2:
skipApprovalScreen: true skipApprovalScreen: true
staticClients: staticClients:
@ -77,7 +77,7 @@ spec:
- /etc/dex/cfg/config.yaml - /etc/dex/cfg/config.yaml
ports: ports:
- name: http - name: http
containerPort: 5556 containerPort: 80
volumeMounts: volumeMounts:
- name: config - name: config
mountPath: /etc/dex/cfg mountPath: /etc/dex/cfg
@ -94,9 +94,8 @@ metadata:
labels: labels:
app: dex app: dex
spec: spec:
type: NodePort type: ClusterIP
selector: selector:
app: dex app: dex
ports: ports:
- port: 5556 - port: 80
nodePort: #@ data.values.ports.node

View File

@ -0,0 +1,58 @@
#! Copyright 2020 the Pinniped contributors. All Rights Reserved.
#! SPDX-License-Identifier: Apache-2.0
#@ load("@ytt:data", "data")
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: proxy
namespace: dex
labels:
app: proxy
spec:
replicas: 1
selector:
matchLabels:
app: proxy
template:
metadata:
labels:
app: proxy
spec:
containers:
- name: proxy
image: docker.io/getpinniped/test-forward-proxy
imagePullPolicy: Always
ports:
- name: http
containerPort: 3128
resources:
requests:
cpu: "10m"
memory: "64Mi"
limits:
cpu: "10m"
memory: "64Mi"
readinessProbe:
tcpSocket:
port: http
initialDelaySeconds: 5
timeoutSeconds: 5
periodSeconds: 5
failureThreshold: 2
---
apiVersion: v1
kind: Service
metadata:
name: proxy
namespace: dex
labels:
app: proxy
spec:
type: NodePort
selector:
app: proxy
ports:
- port: 3128
nodePort: #@ data.values.ports.node

View File

@ -8,10 +8,10 @@ ports:
#! Used in the Dex configuration to form the valid redirect URIs for our test client. #! Used in the Dex configuration to form the valid redirect URIs for our test client.
cli: 48095 cli: 48095
#! Kubernetes NodePort that should be forwarded to the Dex service. #! Kubernetes NodePort that should be forwarded to the proxy service.
#! Used to create a Service of type: NodePort #! Used to create a Service of type: NodePort
node: 31235 node: 31235
#! External port where Dex ends up exposed on localhost during tests. This value comes from our #! External port where the proxy ends up exposed on localhost during tests. This value comes from
#! Kind configuration which maps 127.0.0.1:12346 to port 31235 on the Kind worker node. #! our Kind configuration which maps 127.0.0.1:12346 to port 31235 on the Kind worker node.
local: 12346 local: 12346

View File

@ -130,8 +130,8 @@ func getLoginProvider(t *testing.T) *loginProviderPatterns {
}, },
{ {
Name: "Dex", Name: "Dex",
IssuerPattern: regexp.MustCompile(`\Ahttp://127\.0\.0\.1.+/dex.*\z`), IssuerPattern: regexp.MustCompile(`\Ahttp://dex\.dex\.svc\.cluster\.local/dex.*\z`),
LoginPagePattern: regexp.MustCompile(`\Ahttp://127\.0\.0\.1.+/dex/auth/local.+\z`), LoginPagePattern: regexp.MustCompile(`\Ahttp://dex\.dex\.svc\.cluster\.local/dex/auth/local.+\z`),
UsernameSelector: "input#login", UsernameSelector: "input#login",
PasswordSelector: "input#password", PasswordSelector: "input#password",
LoginButtonSelector: "button#submit-login", LoginButtonSelector: "button#submit-login",
@ -156,7 +156,18 @@ func TestCLILoginOIDC(t *testing.T) {
// Start the browser driver. // Start the browser driver.
t.Logf("opening browser driver") t.Logf("opening browser driver")
caps := agouti.NewCapabilities()
if env.Proxy != "" {
t.Logf("configuring Chrome to use proxy %q", env.Proxy)
caps = caps.Proxy(agouti.ProxyConfig{
ProxyType: "manual",
HTTPProxy: env.Proxy,
SSLProxy: env.Proxy,
NoProxy: "127.0.0.1",
})
}
agoutiDriver := agouti.ChromeDriver( agoutiDriver := agouti.ChromeDriver(
agouti.Desired(caps),
agouti.ChromeOptions("args", []string{ agouti.ChromeOptions("args", []string{
"--no-sandbox", "--no-sandbox",
"--headless", // Comment out this line to see the tests happen in a visible browser window. "--headless", // Comment out this line to see the tests happen in a visible browser window.
@ -395,11 +406,19 @@ func spawnTestGoroutine(t *testing.T, f func() error) {
func oidcLoginCommand(ctx context.Context, t *testing.T, pinnipedExe string, sessionCachePath string) *exec.Cmd { func oidcLoginCommand(ctx context.Context, t *testing.T, pinnipedExe string, sessionCachePath string) *exec.Cmd {
env := library.IntegrationEnv(t) env := library.IntegrationEnv(t)
return exec.CommandContext(ctx, pinnipedExe, "login", "oidc", cmd := exec.CommandContext(ctx, pinnipedExe, "login", "oidc",
"--issuer", env.OIDCUpstream.Issuer, "--issuer", env.OIDCUpstream.Issuer,
"--client-id", env.OIDCUpstream.ClientID, "--client-id", env.OIDCUpstream.ClientID,
"--listen-port", strconv.Itoa(env.OIDCUpstream.LocalhostPort), "--listen-port", strconv.Itoa(env.OIDCUpstream.LocalhostPort),
"--session-cache", sessionCachePath, "--session-cache", sessionCachePath,
"--skip-browser", "--skip-browser",
) )
if env.Proxy != "" {
cmd.Env = append(os.Environ(),
"http_proxy="+env.Proxy,
"https_proxy="+env.Proxy,
"no_proxy=127.0.0.1",
)
}
return cmd
} }

View File

@ -38,6 +38,7 @@ type TestEnv struct {
SupervisorHTTPSAddress string `json:"supervisorHttpsAddress"` SupervisorHTTPSAddress string `json:"supervisorHttpsAddress"`
SupervisorHTTPSIngressAddress string `json:"supervisorHttpsIngressAddress"` SupervisorHTTPSIngressAddress string `json:"supervisorHttpsIngressAddress"`
SupervisorHTTPSIngressCABundle string `json:"supervisorHttpsIngressCABundle"` SupervisorHTTPSIngressCABundle string `json:"supervisorHttpsIngressCABundle"`
Proxy string `json:"proxy"`
TestUser struct { TestUser struct {
Token string `json:"token"` Token string `json:"token"`
@ -126,6 +127,7 @@ func loadEnvVars(t *testing.T, result *TestEnv) {
require.NoErrorf(t, err, "PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS must be a YAML map of string to string") require.NoErrorf(t, err, "PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS must be a YAML map of string to string")
result.SupervisorCustomLabels = supervisorCustomLabels result.SupervisorCustomLabels = supervisorCustomLabels
require.NotEmpty(t, result.SupervisorCustomLabels, "PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS cannot be empty") require.NotEmpty(t, result.SupervisorCustomLabels, "PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS cannot be empty")
result.Proxy = os.Getenv("PINNIPED_TEST_PROXY")
result.OIDCUpstream.Issuer = needEnv(t, "PINNIPED_TEST_CLI_OIDC_ISSUER") result.OIDCUpstream.Issuer = needEnv(t, "PINNIPED_TEST_CLI_OIDC_ISSUER")
result.OIDCUpstream.ClientID = needEnv(t, "PINNIPED_TEST_CLI_OIDC_CLIENT_ID") result.OIDCUpstream.ClientID = needEnv(t, "PINNIPED_TEST_CLI_OIDC_CLIENT_ID")