Compare commits

...

5 Commits

Author SHA1 Message Date
argo-bot
0badce7840 Bump version to 2.3.6 2022-07-12 16:31:10 +00:00
argo-bot
ac6fce35e2 Bump version to 2.3.6 2022-07-12 16:30:56 +00:00
Michael Crenshaw
3172b6b2c7 Merge pull request from GHSA-7943-82jg-wmw5
* add tests to demonstrate issue

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

more

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

docs

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

settings tests

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

tests for OIDC handlers, consolidating test helpers

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

consolidate

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

consolidate

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

docs

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

* fix log message

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>
2022-07-12 08:46:13 -04:00
Michael Crenshaw
8d5119b1e3 Merge pull request from GHSA-pmjg-52h9-72qv
Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

formatting

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

fixes from comments

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

fix test

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>
2022-07-12 08:45:22 -04:00
Nicolas Fillot
068ca899ce [ArgoCD] Fixing webhook typo in case of error in GetManifests (#9671)
Signed-off-by: Nicolas Fillot <nfillot@weborama.com>

Co-authored-by: Nicolas Fillot <nfillot@weborama.com>
Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>
2022-06-23 09:27:06 -04:00
19 changed files with 747 additions and 48 deletions

View File

@@ -1 +1 @@
2.3.5
2.3.6

View File

@@ -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"

View File

@@ -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.

View File

@@ -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

View File

@@ -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:

View File

@@ -11,4 +11,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.3.5
newTag: v2.3.6

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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: &lt;script&gt;alert(&#39;hello&#39;)&lt;/script&gt;\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)

View File

@@ -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,

View File

@@ -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")
})
}

View File

@@ -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

View File

@@ -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
View 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
}

View File

@@ -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 {