mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-28 05:28:47 +01:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0badce7840 | ||
|
|
ac6fce35e2 | ||
|
|
3172b6b2c7 | ||
|
|
8d5119b1e3 | ||
|
|
068ca899ce |
@@ -265,3 +265,9 @@ data:
|
||||
# published to the repository. Reconciliation by timeout is disabled if timeout is set to 0. Three minutes by default.
|
||||
# > Note: argocd-repo-server deployment must be manually restarted after changing the setting.
|
||||
timeout.reconciliation: 180s
|
||||
|
||||
# oidc.tls.insecure.skip.verify determines whether certificate verification is skipped when verifying tokens with the
|
||||
# configured OIDC provider (either external or the bundled Dex instance). Setting this to "true" will cause JWT
|
||||
# token verification to pass despite the OIDC provider having an invalid certificate. Only set to "true" if you
|
||||
# understand the risks.
|
||||
oidc.tls.insecure.skip.verify: "false"
|
||||
|
||||
@@ -495,3 +495,20 @@ data:
|
||||
clientSecret: $another-secret:oidc.auth0.clientSecret # Mind the ':'
|
||||
...
|
||||
```
|
||||
|
||||
### Skipping certificate verification on OIDC provider connections
|
||||
|
||||
By default, all connections made by the API server to OIDC providers (either external providers or the bundled Dex
|
||||
instance) must pass certificate validation. These connections occur when getting the OIDC provider's well-known
|
||||
configuration, when getting the OIDC provider's keys, and when exchanging an authorization code or verifying an ID
|
||||
token as part of an OIDC login flow.
|
||||
|
||||
Disabling certificate verification might make sense if:
|
||||
* You are using the bundled Dex instance **and** your Argo CD instance has TLS configured with a self-signed certificate
|
||||
**and** you understand and accept the risks of skipping OIDC provider cert verification.
|
||||
* You are using an external OIDC provider **and** that provider uses an invalid certificate **and** you cannot solve
|
||||
the problem by setting `oidcConfig.rootCA` **and** you understand and accept the risks of skipping OIDC provider cert
|
||||
verification.
|
||||
|
||||
If either of those two applies, then you can disable OIDC provider certificate verification by setting
|
||||
`oidc.tls.insecure.skip.verify` to `"true"` in the `argocd-cm` ConfigMap.
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.3.5
|
||||
newTag: v2.3.6
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -9698,7 +9698,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -9747,7 +9747,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -9912,7 +9912,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -11,4 +11,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.3.5
|
||||
newTag: v2.3.6
|
||||
|
||||
@@ -11,7 +11,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.3.5
|
||||
newTag: v2.3.6
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/dex
|
||||
|
||||
@@ -10516,7 +10516,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -10549,7 +10549,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -10788,7 +10788,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -10837,7 +10837,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -11064,7 +11064,7 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -11260,7 +11260,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -7812,7 +7812,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -7845,7 +7845,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -8084,7 +8084,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -8133,7 +8133,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -8360,7 +8360,7 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -8556,7 +8556,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -9886,7 +9886,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -9919,7 +9919,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -10122,7 +10122,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -10171,7 +10171,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -10394,7 +10394,7 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -10584,7 +10584,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -7182,7 +7182,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -7215,7 +7215,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -7418,7 +7418,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -7467,7 +7467,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -7690,7 +7690,7 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -7880,7 +7880,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.3.5
|
||||
image: quay.io/argoproj/argocd:v2.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -28,6 +28,8 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
var InvalidRedirectURLError = fmt.Errorf("invalid return URL")
|
||||
|
||||
const (
|
||||
GrantTypeAuthorizationCode = "authorization_code"
|
||||
GrantTypeImplicit = "implicit"
|
||||
@@ -185,10 +187,18 @@ func (a *ClientApp) verifyAppState(r *http.Request, w http.ResponseWriter, state
|
||||
return "", err
|
||||
}
|
||||
cookieVal := string(val)
|
||||
returnURL := a.baseHRef
|
||||
redirectURL := a.baseHRef
|
||||
parts := strings.SplitN(cookieVal, ":", 2)
|
||||
if len(parts) == 2 && parts[1] != "" {
|
||||
returnURL = parts[1]
|
||||
if !isValidRedirectURL(parts[1], []string{a.settings.URL}) {
|
||||
sanitizedUrl := parts[1]
|
||||
if len(sanitizedUrl) > 100 {
|
||||
sanitizedUrl = sanitizedUrl[:100]
|
||||
}
|
||||
log.Warnf("Failed to verify app state - got invalid redirectURL %q", sanitizedUrl)
|
||||
return "", fmt.Errorf("failed to verify app state: %w", InvalidRedirectURLError)
|
||||
}
|
||||
redirectURL = parts[1]
|
||||
}
|
||||
if parts[0] != state {
|
||||
return "", fmt.Errorf("invalid state in '%s' cookie", common.AuthCookieName)
|
||||
@@ -201,7 +211,7 @@ func (a *ClientApp) verifyAppState(r *http.Request, w http.ResponseWriter, state
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Secure: a.secureCookie,
|
||||
})
|
||||
return returnURL, nil
|
||||
return redirectURL, nil
|
||||
}
|
||||
|
||||
// isValidRedirectURL checks whether the given redirectURL matches on of the
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -19,6 +21,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util"
|
||||
"github.com/argoproj/argo-cd/v2/util/crypto"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/test"
|
||||
)
|
||||
|
||||
func TestInferGrantType(t *testing.T) {
|
||||
@@ -104,6 +107,162 @@ func TestHandleCallback(t *testing.T) {
|
||||
assert.Equal(t, "login-failed: <script>alert('hello')</script>\n", w.Body.String())
|
||||
}
|
||||
|
||||
func TestClientApp_HandleLogin(t *testing.T) {
|
||||
oidcTestServer := test.GetOIDCTestServer(t)
|
||||
t.Cleanup(oidcTestServer.Close)
|
||||
|
||||
dexTestServer := test.GetDexTestServer(t)
|
||||
t.Cleanup(dexTestServer.Close)
|
||||
|
||||
t.Run("oidc certificate checking during login should toggle on config", func(t *testing.T) {
|
||||
cdSettings := &settings.ArgoCDSettings{
|
||||
URL: "https://argocd.example.com",
|
||||
OIDCConfigRAW: fmt.Sprintf(`
|
||||
name: Test
|
||||
issuer: %s
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]`, oidcTestServer.URL),
|
||||
}
|
||||
app, err := NewClientApp(cdSettings, dexTestServer.URL, "https://argocd.example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest("GET", "https://argocd.example.com/auth/login", nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
app.HandleLogin(w, req)
|
||||
|
||||
assert.Contains(t, w.Body.String(), "certificate is not trusted")
|
||||
|
||||
cdSettings.OIDCTLSInsecureSkipVerify = true
|
||||
|
||||
app, err = NewClientApp(cdSettings, dexTestServer.URL, "https://argocd.example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
app.HandleLogin(w, req)
|
||||
|
||||
assert.NotContains(t, w.Body.String(), "certificate is not trusted")
|
||||
})
|
||||
|
||||
t.Run("dex certificate checking during login should toggle on config", func(t *testing.T) {
|
||||
cdSettings := &settings.ArgoCDSettings{
|
||||
URL: "https://argocd.example.com",
|
||||
DexConfig: `connectors:
|
||||
- type: github
|
||||
name: GitHub
|
||||
config:
|
||||
clientID: aabbccddeeff00112233
|
||||
clientSecret: aabbccddeeff00112233`,
|
||||
}
|
||||
cert, err := tls.X509KeyPair(test.Cert, test.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
cdSettings.Certificate = &cert
|
||||
|
||||
app, err := NewClientApp(cdSettings, dexTestServer.URL, "https://argocd.example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest("GET", "https://argocd.example.com/auth/login", nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
app.HandleLogin(w, req)
|
||||
|
||||
assert.Contains(t, w.Body.String(), "certificate signed by unknown authority")
|
||||
|
||||
cdSettings.OIDCTLSInsecureSkipVerify = true
|
||||
|
||||
app, err = NewClientApp(cdSettings, dexTestServer.URL, "https://argocd.example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
app.HandleLogin(w, req)
|
||||
|
||||
assert.NotContains(t, w.Body.String(), "certificate signed by unknown authority")
|
||||
})
|
||||
}
|
||||
|
||||
func TestClientApp_HandleCallback(t *testing.T) {
|
||||
oidcTestServer := test.GetOIDCTestServer(t)
|
||||
t.Cleanup(oidcTestServer.Close)
|
||||
|
||||
dexTestServer := test.GetDexTestServer(t)
|
||||
t.Cleanup(dexTestServer.Close)
|
||||
|
||||
t.Run("oidc certificate checking during oidc callback should toggle on config", func(t *testing.T) {
|
||||
cdSettings := &settings.ArgoCDSettings{
|
||||
URL: "https://argocd.example.com",
|
||||
OIDCConfigRAW: fmt.Sprintf(`
|
||||
name: Test
|
||||
issuer: %s
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]`, oidcTestServer.URL),
|
||||
}
|
||||
app, err := NewClientApp(cdSettings, dexTestServer.URL, "https://argocd.example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest("GET", "https://argocd.example.com/auth/callback", nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
app.HandleCallback(w, req)
|
||||
|
||||
assert.Contains(t, w.Body.String(), "certificate is not trusted")
|
||||
|
||||
cdSettings.OIDCTLSInsecureSkipVerify = true
|
||||
|
||||
app, err = NewClientApp(cdSettings, dexTestServer.URL, "https://argocd.example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
app.HandleCallback(w, req)
|
||||
|
||||
assert.NotContains(t, w.Body.String(), "certificate is not trusted")
|
||||
})
|
||||
|
||||
t.Run("dex certificate checking during oidc callback should toggle on config", func(t *testing.T) {
|
||||
cdSettings := &settings.ArgoCDSettings{
|
||||
URL: "https://argocd.example.com",
|
||||
DexConfig: `connectors:
|
||||
- type: github
|
||||
name: GitHub
|
||||
config:
|
||||
clientID: aabbccddeeff00112233
|
||||
clientSecret: aabbccddeeff00112233`,
|
||||
}
|
||||
cert, err := tls.X509KeyPair(test.Cert, test.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
cdSettings.Certificate = &cert
|
||||
|
||||
app, err := NewClientApp(cdSettings, dexTestServer.URL, "https://argocd.example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest("GET", "https://argocd.example.com/auth/callback", nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
app.HandleCallback(w, req)
|
||||
|
||||
assert.Contains(t, w.Body.String(), "certificate signed by unknown authority")
|
||||
|
||||
cdSettings.OIDCTLSInsecureSkipVerify = true
|
||||
|
||||
app, err = NewClientApp(cdSettings, dexTestServer.URL, "https://argocd.example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
app.HandleCallback(w, req)
|
||||
|
||||
assert.NotContains(t, w.Body.String(), "certificate signed by unknown authority")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsValidRedirect(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
@@ -191,7 +350,7 @@ func TestGenerateAppState(t *testing.T) {
|
||||
signature, err := util.MakeSignature(32)
|
||||
require.NoError(t, err)
|
||||
expectedReturnURL := "http://argocd.example.com/"
|
||||
app, err := NewClientApp(&settings.ArgoCDSettings{ServerSignature: signature}, "", "")
|
||||
app, err := NewClientApp(&settings.ArgoCDSettings{ServerSignature: signature, URL: expectedReturnURL}, "", "")
|
||||
require.NoError(t, err)
|
||||
generateResponse := httptest.NewRecorder()
|
||||
state, err := app.generateAppState(expectedReturnURL, generateResponse)
|
||||
@@ -219,6 +378,56 @@ func TestGenerateAppState(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateAppState_XSS(t *testing.T) {
|
||||
signature, err := util.MakeSignature(32)
|
||||
require.NoError(t, err)
|
||||
app, err := NewClientApp(
|
||||
&settings.ArgoCDSettings{
|
||||
// Only return URLs starting with this base should be allowed.
|
||||
URL: "https://argocd.example.com",
|
||||
ServerSignature: signature,
|
||||
},
|
||||
"", "",
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("XSS fails", func(t *testing.T) {
|
||||
// This attack assumes the attacker has compromised the server's secret key. We use `generateAppState` here for
|
||||
// convenience, but an attacker with access to the server secret could write their own code to generate the
|
||||
// malicious cookie.
|
||||
|
||||
expectedReturnURL := "javascript: alert('hi')"
|
||||
generateResponse := httptest.NewRecorder()
|
||||
state, err := app.generateAppState(expectedReturnURL, generateResponse)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
for _, cookie := range generateResponse.Result().Cookies() {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
|
||||
returnURL, err := app.verifyAppState(req, httptest.NewRecorder(), state)
|
||||
assert.ErrorIs(t, err, InvalidRedirectURLError)
|
||||
assert.Empty(t, returnURL)
|
||||
})
|
||||
|
||||
t.Run("valid return URL succeeds", func(t *testing.T) {
|
||||
expectedReturnURL := "https://argocd.example.com/some/path"
|
||||
generateResponse := httptest.NewRecorder()
|
||||
state, err := app.generateAppState(expectedReturnURL, generateResponse)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
for _, cookie := range generateResponse.Result().Cookies() {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
|
||||
returnURL, err := app.verifyAppState(req, httptest.NewRecorder(), state)
|
||||
assert.NoError(t, err, InvalidRedirectURLError)
|
||||
assert.Equal(t, expectedReturnURL, returnURL)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateAppState_NoReturnURL(t *testing.T) {
|
||||
signature, err := util.MakeSignature(32)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -123,10 +123,7 @@ func NewSessionManager(settingsMgr *settings.SettingsManager, projectsLister v1a
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tlsConfig := settings.TLSConfig()
|
||||
if tlsConfig != nil {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
}
|
||||
tlsConfig := settings.OIDCTLSConfig()
|
||||
s.client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
|
||||
@@ -2,6 +2,7 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/password"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
utiltest "github.com/argoproj/argo-cd/v2/util/test"
|
||||
)
|
||||
|
||||
func getProjLister(objects ...runtime.Object) v1alpha1.AppProjectNamespaceLister {
|
||||
@@ -456,3 +458,239 @@ func TestFailedAttemptsExpiry(t *testing.T) {
|
||||
|
||||
os.Setenv(envLoginFailureWindowSeconds, "")
|
||||
}
|
||||
|
||||
func getKubeClientWithConfig(config map[string]string, secretConfig map[string][]byte) *fake.Clientset {
|
||||
mergedSecretConfig := map[string][]byte{
|
||||
"server.secretkey": []byte("Hello, world!"),
|
||||
}
|
||||
for key, value := range secretConfig {
|
||||
mergedSecretConfig[key] = value
|
||||
}
|
||||
|
||||
return fake.NewSimpleClientset(&corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "argocd-cm",
|
||||
Namespace: "argocd",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: config,
|
||||
}, &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "argocd-secret",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Data: mergedSecretConfig,
|
||||
})
|
||||
}
|
||||
|
||||
func TestSessionManager_VerifyToken(t *testing.T) {
|
||||
oidcTestServer := utiltest.GetOIDCTestServer(t)
|
||||
t.Cleanup(oidcTestServer.Close)
|
||||
|
||||
dexTestServer := utiltest.GetDexTestServer(t)
|
||||
t.Cleanup(dexTestServer.Close)
|
||||
|
||||
t.Run("oidcConfig.rootCA is respected", func(t *testing.T) {
|
||||
cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: oidcTestServer.TLS.Certificates[0].Certificate[0]})
|
||||
|
||||
dexConfig := map[string]string{
|
||||
"url": "",
|
||||
"oidc.config": fmt.Sprintf(`
|
||||
name: Test
|
||||
issuer: %s
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]
|
||||
rootCA: |
|
||||
%s
|
||||
`, oidcTestServer.URL, strings.Replace(string(cert), "\n", "\n ", -1)),
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(dexConfig, nil), "argocd")
|
||||
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
|
||||
mgr.verificationDelayNoiseEnabled = false
|
||||
|
||||
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
|
||||
claims.Issuer = oidcTestServer.URL
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
tokenString, err := token.SignedString(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = mgr.VerifyToken(tokenString)
|
||||
// If the root CA is being respected, we won't get this error.
|
||||
assert.NotContains(t, err.Error(), "certificate is not trusted")
|
||||
})
|
||||
|
||||
t.Run("OIDC provider is Dex, TLS is configured", func(t *testing.T) {
|
||||
dexConfig := map[string]string{
|
||||
"url": dexTestServer.URL,
|
||||
"dex.config": `connectors:
|
||||
- type: github
|
||||
name: GitHub
|
||||
config:
|
||||
clientID: aabbccddeeff00112233
|
||||
clientSecret: aabbccddeeff00112233`,
|
||||
}
|
||||
|
||||
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
|
||||
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
|
||||
secretConfig := map[string][]byte{
|
||||
"tls.crt": utiltest.Cert,
|
||||
"tls.key": utiltest.PrivateKey,
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(dexConfig, secretConfig), "argocd")
|
||||
mgr := NewSessionManager(settingsMgr, getProjLister(), dexTestServer.URL, NewUserStateStorage(nil))
|
||||
mgr.verificationDelayNoiseEnabled = false
|
||||
|
||||
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
|
||||
claims.Issuer = fmt.Sprintf("%s/api/dex", dexTestServer.URL)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
tokenString, err := token.SignedString(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = mgr.VerifyToken(tokenString)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "certificate signed by unknown authority")
|
||||
})
|
||||
|
||||
t.Run("OIDC provider is external, TLS is configured", func(t *testing.T) {
|
||||
dexConfig := map[string]string{
|
||||
"url": "",
|
||||
"oidc.config": fmt.Sprintf(`
|
||||
name: Test
|
||||
issuer: %s
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]`, oidcTestServer.URL),
|
||||
}
|
||||
|
||||
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
|
||||
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
|
||||
secretConfig := map[string][]byte{
|
||||
"tls.crt": utiltest.Cert,
|
||||
"tls.key": utiltest.PrivateKey,
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(dexConfig, secretConfig), "argocd")
|
||||
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
|
||||
mgr.verificationDelayNoiseEnabled = false
|
||||
|
||||
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
|
||||
claims.Issuer = oidcTestServer.URL
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
tokenString, err := token.SignedString(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = mgr.VerifyToken(tokenString)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "certificate is not trusted")
|
||||
})
|
||||
|
||||
t.Run("OIDC provider is Dex, TLS is configured", func(t *testing.T) {
|
||||
dexConfig := map[string]string{
|
||||
"url": dexTestServer.URL,
|
||||
"dex.config": `connectors:
|
||||
- type: github
|
||||
name: GitHub
|
||||
config:
|
||||
clientID: aabbccddeeff00112233
|
||||
clientSecret: aabbccddeeff00112233`,
|
||||
}
|
||||
|
||||
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
|
||||
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
|
||||
secretConfig := map[string][]byte{
|
||||
"tls.crt": utiltest.Cert,
|
||||
"tls.key": utiltest.PrivateKey,
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(dexConfig, secretConfig), "argocd")
|
||||
mgr := NewSessionManager(settingsMgr, getProjLister(), dexTestServer.URL, NewUserStateStorage(nil))
|
||||
mgr.verificationDelayNoiseEnabled = false
|
||||
|
||||
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
|
||||
claims.Issuer = fmt.Sprintf("%s/api/dex", dexTestServer.URL)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
tokenString, err := token.SignedString(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = mgr.VerifyToken(tokenString)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "certificate signed by unknown authority")
|
||||
})
|
||||
|
||||
t.Run("OIDC provider is external, TLS is configured, OIDCTLSInsecureSkipVerify is true", func(t *testing.T) {
|
||||
dexConfig := map[string]string{
|
||||
"url": "",
|
||||
"oidc.config": fmt.Sprintf(`
|
||||
name: Test
|
||||
issuer: %s
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]`, oidcTestServer.URL),
|
||||
"oidc.tls.insecure.skip.verify": "true",
|
||||
}
|
||||
|
||||
// This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair
|
||||
// must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled.
|
||||
secretConfig := map[string][]byte{
|
||||
"tls.crt": utiltest.Cert,
|
||||
"tls.key": utiltest.PrivateKey,
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(dexConfig, secretConfig), "argocd")
|
||||
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
|
||||
mgr.verificationDelayNoiseEnabled = false
|
||||
|
||||
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
|
||||
claims.Issuer = oidcTestServer.URL
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
tokenString, err := token.SignedString(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = mgr.VerifyToken(tokenString)
|
||||
assert.NotContains(t, err.Error(), "certificate signed by unknown authority")
|
||||
})
|
||||
|
||||
t.Run("OIDC provider is external, TLS is not configured, OIDCTLSInsecureSkipVerify is true", func(t *testing.T) {
|
||||
dexConfig := map[string]string{
|
||||
"url": "",
|
||||
"oidc.config": fmt.Sprintf(`
|
||||
name: Test
|
||||
issuer: %s
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]`, oidcTestServer.URL),
|
||||
"oidc.tls.insecure.skip.verify": "true",
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), getKubeClientWithConfig(dexConfig, nil), "argocd")
|
||||
mgr := NewSessionManager(settingsMgr, getProjLister(), "", NewUserStateStorage(nil))
|
||||
mgr.verificationDelayNoiseEnabled = false
|
||||
|
||||
claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))}
|
||||
claims.Issuer = oidcTestServer.URL
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
tokenString, err := token.SignedString(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = mgr.VerifyToken(tokenString)
|
||||
// This is the error thrown when the test server's certificate _is_ being verified.
|
||||
assert.NotContains(t, err.Error(), "certificate is not trusted")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -91,6 +91,11 @@ type ArgoCDSettings struct {
|
||||
PasswordPattern string `json:"passwordPattern,omitempty"`
|
||||
// BinaryUrls contains the URLs for downloading argocd binaries
|
||||
BinaryUrls map[string]string `json:"binaryUrls,omitempty"`
|
||||
// OIDCTLSInsecureSkipVerify determines whether certificate verification is skipped when verifying tokens with the
|
||||
// configured OIDC provider (either external or the bundled Dex instance). Setting this to `true` will cause JWT
|
||||
// token verification to pass despite the OIDC provider having an invalid certificate. Only set to `true` if you
|
||||
// understand the risks.
|
||||
OIDCTLSInsecureSkipVerify bool `json:"oidcTLSInsecureSkipVerify"`
|
||||
}
|
||||
|
||||
type GoogleAnalytics struct {
|
||||
@@ -390,6 +395,8 @@ const (
|
||||
settingsPasswordPatternKey = "passwordPattern"
|
||||
// helmValuesFileSchemesKey is the key to configure the list of supported helm values file schemas
|
||||
helmValuesFileSchemesKey = "helm.valuesFileSchemes"
|
||||
// oidcTLSInsecureSkipVerifyKey is the key to configure whether TLS cert verification is skipped for OIDC connections
|
||||
oidcTLSInsecureSkipVerifyKey = "oidc.tls.insecure.skip.verify"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -1239,6 +1246,7 @@ func updateSettingsFromConfigMap(settings *ArgoCDSettings, argoCDCM *apiv1.Confi
|
||||
if settings.PasswordPattern == "" {
|
||||
settings.PasswordPattern = common.PasswordPatten
|
||||
}
|
||||
settings.OIDCTLSInsecureSkipVerify = argoCDCM.Data[oidcTLSInsecureSkipVerifyKey] == "true"
|
||||
}
|
||||
|
||||
// validateExternalURL ensures the external URL that is set on the configmap is valid
|
||||
@@ -1608,22 +1616,28 @@ func (a *ArgoCDSettings) OAuth2ClientSecret() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// OIDCTLSConfig returns the TLS config for the OIDC provider. If an external provider is configured, returns a TLS
|
||||
// config using the root CAs (if any) specified in the OIDC config. If an external OIDC provider is not configured,
|
||||
// returns the API server TLS config, because the API server proxies requests to Dex.
|
||||
func (a *ArgoCDSettings) OIDCTLSConfig() *tls.Config {
|
||||
if oidcConfig := a.OIDCConfig(); oidcConfig != nil {
|
||||
var tlsConfig *tls.Config
|
||||
|
||||
oidcConfig := a.OIDCConfig()
|
||||
if oidcConfig != nil {
|
||||
tlsConfig = &tls.Config{}
|
||||
if oidcConfig.RootCA != "" {
|
||||
certPool := x509.NewCertPool()
|
||||
ok := certPool.AppendCertsFromPEM([]byte(oidcConfig.RootCA))
|
||||
if !ok {
|
||||
log.Warn("invalid oidc root ca cert - returning default tls.Config instead")
|
||||
return &tls.Config{}
|
||||
}
|
||||
return &tls.Config{
|
||||
RootCAs: certPool,
|
||||
log.Warn("failed to append certificates from PEM: proceeding without custom rootCA")
|
||||
} else {
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tlsConfig = a.TLSConfig()
|
||||
}
|
||||
tlsConfig := a.TLSConfig()
|
||||
if tlsConfig != nil {
|
||||
if tlsConfig != nil && a.OIDCTLSInsecureSkipVerify {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
}
|
||||
return tlsConfig
|
||||
|
||||
@@ -4,12 +4,15 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
testutil "github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -1105,3 +1108,68 @@ func TestGetHelmSettings(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestArgoCDSettings_OIDCTLSConfig_OIDCTLSInsecureSkipVerify(t *testing.T) {
|
||||
certParsed, err := tls.X509KeyPair(test.Cert, test.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct{
|
||||
name string
|
||||
settings *ArgoCDSettings
|
||||
expectNilTLSConfig bool
|
||||
}{
|
||||
{
|
||||
name: "OIDC configured, no root CA",
|
||||
settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test
|
||||
issuer: aaa
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]`},
|
||||
},
|
||||
{
|
||||
name: "OIDC configured, valid root CA",
|
||||
settings: &ArgoCDSettings{OIDCConfigRAW: fmt.Sprintf(`
|
||||
name: Test
|
||||
issuer: aaa
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]
|
||||
rootCA: |
|
||||
%s
|
||||
`, strings.Replace(string(test.Cert), "\n", "\n ", -1))},
|
||||
},
|
||||
{
|
||||
name: "OIDC configured, invalid root CA",
|
||||
settings: &ArgoCDSettings{OIDCConfigRAW: `name: Test
|
||||
issuer: aaa
|
||||
clientID: xxx
|
||||
clientSecret: yyy
|
||||
requestedScopes: ["oidc"]
|
||||
rootCA: "invalid"`},
|
||||
},
|
||||
{
|
||||
name: "OIDC not configured, no cert configured",
|
||||
settings: &ArgoCDSettings{},
|
||||
expectNilTLSConfig: true,
|
||||
},
|
||||
{
|
||||
name: "OIDC not configured, cert configured",
|
||||
settings: &ArgoCDSettings{Certificate: &certParsed},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
if testCase.expectNilTLSConfig {
|
||||
assert.Nil(t, testCase.settings.OIDCTLSConfig())
|
||||
} else {
|
||||
assert.False(t, testCase.settings.OIDCTLSConfig().InsecureSkipVerify)
|
||||
|
||||
testCase.settings.OIDCTLSInsecureSkipVerify = true
|
||||
|
||||
assert.True(t, testCase.settings.OIDCTLSConfig().InsecureSkipVerify)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
140
util/test/testutil.go
Normal file
140
util/test/testutil.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Cert is a certificate for tests. It was generated like this:
|
||||
// opts := tls.CertOptions{Hosts: []string{"localhost"}, Organization: "Acme"}
|
||||
// certBytes, privKey, err := tls.generatePEM(opts)
|
||||
var Cert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIC8zCCAdugAwIBAgIQCSoocl6e/FR4mQy1wX6NbjANBgkqhkiG9w0BAQsFADAP
|
||||
MQ0wCwYDVQQKEwRBY21lMB4XDTIyMDYyMjE3Mjk1MloXDTIzMDYyMjE3Mjk1Mlow
|
||||
DzENMAsGA1UEChMEQWNtZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
ANih5Kdn3tEXh6gLfQYplhHnNq8lmSMoPY7wdwXT95sxX9GzrVpR5tRQBExcR+Ie
|
||||
Y2AElGmlhMETTchpU9RoU6fozjAuMYTkm+f0pyNnbdhCE5LnUBSrEhVHSQJ3ajs5
|
||||
I6z9qS+H4uG+yVobiwzt+rnwD+Jdpt7ZwLHhkkkyHHFr8yxRVLN8LBzh8TnCRgj9
|
||||
We64s8ZepkymC/2fhh6jdezibJQ3/dNbj17FHgwmC9oooBj4QwKOpPDzrH26aixu
|
||||
6aAg0yudBS50uahKHI8bfieGYwRFk1PwzhV1mLLc324ZvDmT0KUkhIgQsaYPs47Z
|
||||
EHwsmcVweUUPOAmO/H1ziPUCAwEAAaNLMEkwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||
JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJbG9jYWxo
|
||||
b3N0MA0GCSqGSIb3DQEBCwUAA4IBAQA+8cGJfYRhXQxan7FATsbtC+1DwW1cPc60
|
||||
5eLOuI0jPdvXLDmtOulBEjR4KOfJ5oTKXGjs/+gR3sffP6s8gm2XFQn4+OsmxHbO
|
||||
b2RjPHgKUtJmrI4ZCN8iPGlKIar5u6Q8NZwzpeZ2XL0bpPp7RQsfHqMyhsqDinWR
|
||||
vvwQB+Bri0oIOtzW2645vWmYc2SaFMd8+8g6Ipa+PRSJezeUxIVZG12zlhsio18F
|
||||
9SHY2ONcYISjfrGTIcu4cZRGxCZGTIwMngBlb71mia+K7uH+UE6qfJy/t6KiFsCP
|
||||
yOwMb95nGQSQLDNoGr8gwgE2qPuR0kR9Z5OrWF0DoVCyL3xnxr02
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
// PrivateKey is an RSA key used only for tests.
|
||||
var PrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEA2KHkp2fe0ReHqAt9BimWEec2ryWZIyg9jvB3BdP3mzFf0bOt
|
||||
WlHm1FAETFxH4h5jYASUaaWEwRNNyGlT1GhTp+jOMC4xhOSb5/SnI2dt2EITkudQ
|
||||
FKsSFUdJAndqOzkjrP2pL4fi4b7JWhuLDO36ufAP4l2m3tnAseGSSTIccWvzLFFU
|
||||
s3wsHOHxOcJGCP1Z7rizxl6mTKYL/Z+GHqN17OJslDf901uPXsUeDCYL2iigGPhD
|
||||
Ao6k8POsfbpqLG7poCDTK50FLnS5qEocjxt+J4ZjBEWTU/DOFXWYstzfbhm8OZPQ
|
||||
pSSEiBCxpg+zjtkQfCyZxXB5RQ84CY78fXOI9QIDAQABAoIBAG8jL0FLIp62qZvm
|
||||
uO9ualUo/37/lP7aaCpq50UQJ9lwjS3yNh8+IWQO4QWj2iUBXg4mi1Vf2ymKk78b
|
||||
eixgkXp1D0Lcj/8ToYBwnUami04FKDGXhhf0Y8SS27vuM4vKlqjrQd7modkangYi
|
||||
V0X82UKHDD8fuLpfkGIxzXDLypfMzjMuVpSntnWaf2YX3VR/0/66yEp9GejftF2k
|
||||
wqhGoWM6r68pN5XuCqWd5PRluSoDy/o4BAFMhYCSfp9PjgZE8aoeWHgYzlZ3gUyn
|
||||
r+HaDDNWbibhobXk/9h8lwAJ6KCZ5RZ+HFfh0HuwIxmocT9OCFgy/S0g1p+o3m9K
|
||||
VNd5AMkCgYEA5fbS5UK7FBzuLoLgr1hktmbLJhpt8y8IPHNABHcUdE+O4/1xTQNf
|
||||
pMUwkKjGG1MtrGjLOIoMGURKKn8lR1GMZueOTSKY0+mAWUGvSzl6vwtJwvJruT8M
|
||||
otEO03o0tPnRKGxbFjqxkp2b6iqJ8MxCRZ3lSidc4mdi7PHzv9lwgvsCgYEA8Siq
|
||||
7weCri9N6y+tIdORAXgRzcW54BmJyqB147c72RvbMacb6rN28KXpM3qnRXyp3Llb
|
||||
yh81TW3FH10GqrjATws7BK8lP9kkAw0Z/7kNiS1NgH3pUbO+5H2kAa/6QW35nzRe
|
||||
Jw2lyfYGWqYO4hYXH14ML1kjgS1hgd3XHOQ64M8CgYAKcjDYSzS2UC4dnMJaFLjW
|
||||
dErsGy09a7iDDnUs/r/GHMsP3jZkWi/hCzgOiiwdl6SufUAl/FdaWnjH/2iRGco3
|
||||
7nLPXC/3CFdVNp+g2iaSQRADtAFis9N+HeL/hkCYq/RtUqa8lsP0NgacF3yWnKCy
|
||||
Ct8chDc67ZlXzBHXeCgdOwKBgHHGFPbWXUHeUW1+vbiyvrupsQSanznp8oclMtkv
|
||||
Dk48hSokw9fzuU6Jh77gw9/Vk7HtxS9Tj+squZA1bDrJFPl1u+9WzkUUJZhG6xgp
|
||||
bwhj1iejv5rrKUlVOTYOlwudXeJNa4oTNz9UEeVcaLMjZt9GmIsSC90a0uDZD26z
|
||||
AlAjAoGAEoqm2DcNN7SrH6aVFzj1EVOrNsHYiXj/yefspeiEmf27PSAslP+uF820
|
||||
SDpz4h+Bov5qTKkzcxuu1QWtA4M0K8Iy6IYLwb83DZEm1OsAf4i0pODz21PY/I+O
|
||||
VHzjB10oYgaInHZgMUdyb6F571UdiYSB6a/IlZ3ngj5touy3VIM=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
func dexMockHandler(t *testing.T, url string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
switch r.RequestURI {
|
||||
case "/api/dex/.well-known/openid-configuration":
|
||||
_, err := io.WriteString(w, fmt.Sprintf(`
|
||||
{
|
||||
"issuer": "%[1]s/api/dex",
|
||||
"authorization_endpoint": "%[1]s/api/dex/auth",
|
||||
"token_endpoint": "%[1]s/api/dex/token",
|
||||
"jwks_uri": "%[1]s/api/dex/keys",
|
||||
"userinfo_endpoint": "%[1]s/api/dex/userinfo",
|
||||
"device_authorization_endpoint": "%[1]s/api/dex/device/code",
|
||||
"grant_types_supported": ["authorization_code"],
|
||||
"response_types_supported": ["code"],
|
||||
"subject_types_supported": ["public"],
|
||||
"id_token_signing_alg_values_supported": ["RS512"],
|
||||
"code_challenge_methods_supported": ["S256", "plain"],
|
||||
"scopes_supported": ["openid"],
|
||||
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
|
||||
"claims_supported": ["sub", "aud", "exp"]
|
||||
}`, url))
|
||||
require.NoError(t, err)
|
||||
default:
|
||||
w.WriteHeader(404)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetDexTestServer(t *testing.T) *httptest.Server {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Start with a placeholder. We need the server URL before setting up the real handler.
|
||||
}))
|
||||
ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
dexMockHandler(t, ts.URL)(w, r)
|
||||
})
|
||||
return ts
|
||||
}
|
||||
|
||||
func oidcMockHandler(t *testing.T, url string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
switch r.RequestURI {
|
||||
case "/.well-known/openid-configuration":
|
||||
_, err := io.WriteString(w, fmt.Sprintf(`
|
||||
{
|
||||
"issuer": "%[1]s",
|
||||
"authorization_endpoint": "%[1]s/auth",
|
||||
"token_endpoint": "%[1]s/token",
|
||||
"jwks_uri": "%[1]s/keys",
|
||||
"userinfo_endpoint": "%[1]s/userinfo",
|
||||
"device_authorization_endpoint": "%[1]s/device/code",
|
||||
"grant_types_supported": ["authorization_code"],
|
||||
"response_types_supported": ["code"],
|
||||
"subject_types_supported": ["public"],
|
||||
"id_token_signing_alg_values_supported": ["RS512"],
|
||||
"code_challenge_methods_supported": ["S256", "plain"],
|
||||
"scopes_supported": ["openid"],
|
||||
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
|
||||
"claims_supported": ["sub", "aud", "exp"]
|
||||
}`, url))
|
||||
require.NoError(t, err)
|
||||
default:
|
||||
w.WriteHeader(404)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetOIDCTestServer(t *testing.T) *httptest.Server {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Start with a placeholder. We need the server URL before setting up the real handler.
|
||||
}))
|
||||
ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
oidcMockHandler(t, ts.URL)(w, r)
|
||||
})
|
||||
return ts
|
||||
}
|
||||
@@ -283,7 +283,7 @@ func (a *ArgoCDWebhookHandler) storePreviouslyCachedManifests(app *v1alpha1.Appl
|
||||
return err
|
||||
}
|
||||
var cachedManifests cache.CachedManifestResponse
|
||||
if err := a.repoCache.GetManifests(change.shaBefore, &app.Spec.Source, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, &cachedManifests); err == nil {
|
||||
if err := a.repoCache.GetManifests(change.shaBefore, &app.Spec.Source, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, &cachedManifests); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = a.repoCache.SetManifests(change.shaAfter, &app.Spec.Source, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, &cachedManifests); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user