Compare commits

...

25 Commits

Author SHA1 Message Date
argo-bot
402da6f64c Bump version to 2.3.7 2022-07-29 14:48:08 +00:00
argo-bot
89c600a6fe Bump version to 2.3.7 2022-07-29 14:47:55 +00:00
jannfis
76cd161e0a test: Remove cluster e2e tests not intended for release-2.3
Signed-off-by: jannfis <jann@mistrust.net>
2022-07-29 07:51:33 +00:00
jannfis
4b8b5918f8 test: Remove circular symlinks from testdata (#9886)
* test: Remove circular symlinks from testdata

Signed-off-by: jannfis <jann@mistrust.net>

* Another test case

Signed-off-by: jannfis <jann@mistrust.net>

* Use defer for changing back to original workdir

Signed-off-by: jannfis <jann@mistrust.net>

* Abort the test on error in defer

Signed-off-by: jannfis <jann@mistrust.net>
2022-07-28 20:25:30 +00:00
Michael Crenshaw
6d0b9caed5 fix: skip redirect url validation when it's the base href (#10058) (#10116)
* fix: skip redirect url validation when it's the base href (#10058)

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

nicer way of doing it

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

* fix missin arg

Signed-off-by: CI <michael@crenshaw.dev>
2022-07-27 16:38:24 -04:00
dependabot[bot]
4bb00bade6 chore(deps): bump moment from 2.29.3 to 2.29.4 in /ui (#9897)
Bumps [moment](https://github.com/moment/moment) from 2.29.3 to 2.29.4.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.29.3...2.29.4)

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

---
updated-dependencies:
- dependency-name: moment
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-27 16:28:30 -04:00
Snyk bot
14424c58b2 fix: upgrade moment from 2.29.2 to 2.29.3 (#9330)
Snyk has created this PR to upgrade moment from 2.29.2 to 2.29.3.

See this package in npm:

See this project in Snyk:
https://app.snyk.io/org/argoproj/project/d2931792-eef9-4d7c-b9d6-c0cbd2bd4dbe?utm_source=github&utm_medium=referral&page=upgrade-pr

Signed-off-by: CI <michael@crenshaw.dev>
2022-07-27 16:22:59 -04:00
Alexander Matyushentsev
c0d2e13b42 chore: upgrade moment to latest version to fix CVE (#9005)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2022-07-27 16:04:37 -04:00
Michael Crenshaw
6de0d9dced chore: move dependencies to dev dependencies (#8541)
chore: move dependencies to dev dependencies (#8541)

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>
2022-07-27 15:37:49 -04:00
Michael Crenshaw
5c31b47517 docs: add OpenSSH breaking change notes (#10104)
Signed-off-by: CI <michael@crenshaw.dev>
2022-07-27 15:28:23 -04:00
Michael Crenshaw
92d26b55bd fix: avoid CVE-2022-28948 (#10093)
Signed-off-by: CI <michael@crenshaw.dev>
2022-07-27 15:08:17 -04:00
Michael Crenshaw
a25a6dcfe3 chore: update parse-url (#10101)
* chore: upgrade parse-url

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

* edit a generated file, because that's smart

Signed-off-by: CI <michael@crenshaw.dev>
2022-07-27 15:00:13 -04:00
Michael Crenshaw
fe6c0f1a38 chore: upgrade base image to 22.04 (#10103)
Signed-off-by: douhunt <douhunt@protonmail.com>

Co-authored-by: douhunt <douhunt@protonmail.com>
Co-authored-by: Michael Crenshaw <michael@crenshaw.dev>
Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

chore: update Kex-Algorithms (#9561)

* chore: update Kex-Algorithms

Signed-off-by: douhunt <douhunt@protonmail.com>

* sorted kex-algorithms

Signed-off-by: 34FathomBelow <34fathombelow@protonmail.com>

Co-authored-by: douhunt <douhunt@protonmail.com>
Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

chore upgrade base image for test containers Ubuntu:22.04 (#9563)

Signed-off-by: 34FathomBelow <34fathombelow@protonmail.com>

Co-authored-by: 34FathomBelow <34fathombelow@protonmail.com>
2022-07-26 14:49:06 -04:00
Michael Crenshaw
0851ea54b8 docs: simplify Docker toolchain docs (#9966) (#10006)
* docs: simplify Docker toolchain docs (#9966)

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

* to be or not to be

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

* pin dependencies to avoid absurdity

Signed-off-by: CI <michael@crenshaw.dev>
2022-07-26 14:06:27 -04:00
34FathomBelow
3e920bf3b6 chore: update redis to 6.2.7 avoid CVE-2022-30065/CVE-2022-2097 (#10062)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2022-07-25 14:24:45 -04:00
Michael Crenshaw
a606b0ab01 chore: upgrade Dex to 2.32.0 (#10036) (#10042)
Signed-off-by: CI <michael@crenshaw.dev>
2022-07-20 10:45:44 -04:00
34FathomBelow
e4074454c6 chore: update haproxy to 2.0.29 for redis-ha (#10045)
Signed-off-by: Justin Marquis <34fathombelow@protonmail.com>
2022-07-19 15:05:54 -04:00
Daniel Helfand
d0e30d961a fix: use serviceaccount name instead of struct (#9614)
* fix: use serviceaccount name instead of struct

Signed-off-by: Daniel Helfand <helfand.4@gmail.com>

* fix: change name of param from sa to serviceAccount

Signed-off-by: Daniel Helfand <helfand.4@gmail.com>
2022-07-13 16:29:39 -04:00
Daniel Helfand
98aadc7dc1 fix: create serviceaccount token for v1.24 clusters (#9546)
* fix: create serviceaccount token for v1.24 clusters

Signed-off-by: Daniel Helfand <helfand.4@gmail.com>

* change create to get in err

Signed-off-by: Daniel Helfand <helfand.4@gmail.com>
2022-07-13 16:29:03 -04:00
Michael Crenshaw
efdec2888e test: check for error messages from CI env (#9953)
test: check for error messages from CI env (#9953)

Signed-off-by: CI <michael@crenshaw.dev>
2022-07-12 15:20:20 -04:00
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
48 changed files with 1389 additions and 183 deletions

View File

@@ -394,7 +394,7 @@ jobs:
run: |
docker pull quay.io/dexidp/dex:v2.25.0
docker pull argoproj/argo-cd-ci-builder:v1.0.0
docker pull redis:6.2.6-alpine
docker pull redis:6.2.7-alpine
- name: Create target directory for binaries in the build-process
run: |
mkdir -p dist

View File

@@ -1,4 +1,4 @@
ARG BASE_IMAGE=docker.io/library/ubuntu:21.10
ARG BASE_IMAGE=docker.io/library/ubuntu:22.04
####################################################################################################
# Builder image
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
@@ -69,7 +69,7 @@ RUN ln -s /usr/local/aws-cli/v2/current/dist/aws /usr/local/bin/aws
# support for mounting configuration from a configmap
RUN mkdir -p /app/config/ssh && \
touch /app/config/ssh/ssh_known_hosts && \
ln -s /app/config/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts
ln -s /app/config/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts
RUN mkdir -p /app/config/tls
RUN mkdir -p /app/config/gpg/source && \

View File

@@ -1,7 +1,7 @@
controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
api-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} "
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.30.2 dex serve /dex.yaml"
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.6-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.7-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
repo-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-/tmp/argo-e2e/app/config/plugin} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
git-server: test/fixture/testrepos/start-git.sh

View File

@@ -1 +1 @@
2.3.5
2.3.7

View File

@@ -609,7 +609,7 @@ func GenerateToken(clusterOpts cmdutil.ClusterOptions, conf *rest.Config) (strin
clientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
bearerToken, err := clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount)
bearerToken, err := clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount, common.BearerTokenTimeout)
if err != nil {
return "", err
}

View File

@@ -113,7 +113,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
clientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
if clusterOpts.ServiceAccount != "" {
managerBearerToken, err = clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount)
managerBearerToken, err = clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount, common.BearerTokenTimeout)
} else {
isTerminal := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
@@ -123,7 +123,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
os.Exit(1)
}
}
managerBearerToken, err = clusterauth.InstallClusterManagerRBAC(clientset, clusterOpts.SystemNamespace, clusterOpts.Namespaces)
managerBearerToken, err = clusterauth.InstallClusterManagerRBAC(clientset, clusterOpts.SystemNamespace, clusterOpts.Namespaces, common.BearerTokenTimeout)
}
errors.CheckError(err)
}

View File

@@ -212,6 +212,12 @@ const (
CacheVersion = "1.8.3"
)
// Constants used by util/clusterauth package
const (
ClusterAuthRequestTimeout = 10 * time.Second
BearerTokenTimeout = 30 * time.Second
)
const (
DefaultGitRetryMaxDuration time.Duration = time.Second * 5 // 5s
DefaultGitRetryDuration time.Duration = time.Millisecond * 250 // 0.25s

View File

@@ -24,8 +24,7 @@ You will need at least the following things in your toolchain in order to develo
* A Kubernetes cluster. You won't need a fully blown multi-master, multi-node cluster, but you will need something like K3S, Minikube or microk8s. You will also need a working Kubernetes client (`kubectl`) configuration in your development environment. The configuration must reside in `~/.kube/config` and the API server URL must point to the IP address of your local machine (or VM), and **not** to `localhost` or `127.0.0.1` if you are using the virtualized development toolchain (see below)
* You will also need a working Docker runtime environment, to be able to build and run images.
The Docker version must be fairly recent, and support multi-stage builds. You should not work as root. Make your local user a member of the `docker` group to be able to control the Docker service on your machine.
* You will also need a working Docker runtime environment, to be able to build and run images. The Docker version must be 17.05.0 or higher, to support multi-stage builds.
* Obviously, you will need a `git` client for pulling source code and pushing back your changes.

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

@@ -14,3 +14,76 @@ Note that bundled Helm has been upgraded from 3.6.0 to v3.7+. This includes foll
- Experimental OCI support has been rewritten.
More information in the [Helm v3.7.0 release notes](https://github.com/helm/helm/releases/tag/v3.7.0).
## Support for private repo SSH keys using the SHA-1 signature hash algorithm is removed in 2.2.12
Argo CD 2.2.12 upgraded its base image from Ubuntu 21.10 to Ubuntu 22.04, which upgraded OpenSSH to 8.9. OpenSSH starting
with 8.8 [dropped support for the `ssh-rsa` SHA-1 key signature algorithm](https://www.openssh.com/txt/release-8.8).
The signature algorithm is _not_ the same as the algorithm used when generating the key. There is no need to update
keys.
The signature algorithm is negotiated with the SSH server when the connection is being set up. The client offers its
list of accepted signature algorithms, and if the server has a match, the connection proceeds. For most SSH servers on
up-to-date git providers, acceptable algorithms other than `ssh-rsa` should be available.
Before upgrading to Argo CD 2.2.12, check whether your git provider(s) using SSH authentication support algorithms newer
than `rsa-ssh`.
1. Make sure your version of SSH >= 8.9 (the version used by Argo CD). If not, upgrade it before proceeding.
```shell
ssh -V
```
Example output: `OpenSSH_8.9p1 Ubuntu-3, OpenSSL 3.0.2 15 Mar 2022`
2. Once you have a recent version of OpenSSH, follow the directions from the [OpenSSH 8.8 release notes](https://www.openssh.com/txt/release-8.7):
> To check whether a server is using the weak ssh-rsa public key
> algorithm, for host authentication, try to connect to it after
> removing the ssh-rsa algorithm from ssh(1)'s allowed list:
>
> ```shell
> ssh -oHostKeyAlgorithms=-ssh-rsa user@host
> ```
>
> If the host key verification fails and no other supported host key
> types are available, the server software on that host should be
> upgraded.
If the server does not support an acceptable version, you will get an error similar to this;
```
$ ssh -oHostKeyAlgorithms=-ssh-rsa vs-ssh.visualstudio.com
Unable to negotiate with 20.42.134.1 port 22: no matching host key type found. Their offer: ssh-rsa
```
This indicates that the server needs to update its supported key signature algorithms, and Argo CD will not connect
to it.
### Workaround
The [OpenSSH 8.8 release notes](https://www.openssh.com/txt/release-8.8) describe a workaround if you cannot change the
server's key signature algorithms configuration.
> Incompatibility is more likely when connecting to older SSH
> implementations that have not been upgraded or have not closely tracked
> improvements in the SSH protocol. For these cases, it may be necessary
> to selectively re-enable RSA/SHA1 to allow connection and/or user
> authentication via the HostkeyAlgorithms and PubkeyAcceptedAlgorithms
> options. For example, the following stanza in ~/.ssh/config will enable
> RSA/SHA1 for host and user authentication for a single destination host:
>
> ```
> Host old-host
> HostkeyAlgorithms +ssh-rsa
> PubkeyAcceptedAlgorithms +ssh-rsa
> ```
>
> We recommend enabling RSA/SHA1 only as a stopgap measure until legacy
> implementations can be upgraded or reconfigured with another key type
> (such as ECDSA or Ed25519).
To apply this to Argo CD, you could create a ConfigMap with the desired ssh config file and then mount it at
`/home/argocd/.ssh/config`.

View File

@@ -6,12 +6,15 @@ The Argo CD Notifications and ApplicationSet are part of Argo CD now. You no lon
The Notifications and ApplicationSet components are bundled into default Argo CD installation manifests.
The bundled manifests are drop-in replacements for the previous versions. If you are using Kustomize to bundle the manifests together then just
remove references to https://github.com/argoproj-labs/argocd-notifications and https://github.com/argoproj-labs/applicationset. No action is required
if you are using `kubectl apply`.
remove references to https://github.com/argoproj-labs/argocd-notifications and https://github.com/argoproj-labs/applicationset.
## Configure Additional ArgoCD Binaries
If you are using [the argocd-notifications helm chart](https://github.com/argoproj/argo-helm/tree/argocd-notifications-1.8.1/charts/argocd-notifications), you can move the chart [values](https://github.com/argoproj/argo-helm/blob/argocd-notifications-1.8.1/charts/argocd-notifications/values.yaml) to the `notifications` section of the argo-cd chart [values](https://github.com/argoproj/argo-helm/blob/main/charts/argo-cd/values.yaml#L2152). Although most values remain as is, for details please look up the values that are relevant to you.
We have removed non-Linux ArgoCD binaries (Darwin amd64 and Windows amd64) from the image ([#7668](https://github.com/argoproj/argo-cd/pull/7668)) and the associated download buttons in the help page in the UI.
No action is required if you are using `kubectl apply`.
## Configure Additional Argo CD Binaries
We have removed non-Linux Argo CD binaries (Darwin amd64 and Windows amd64) from the image ([#7668](https://github.com/argoproj/argo-cd/pull/7668)) and the associated download buttons in the help page in the UI.
Those removed binaries will still be included in the release assets and we made those configurable in [#7755](https://github.com/argoproj/argo-cd/pull/7755). You can add download buttons for other OS architectures by adding the following to your `argocd-cm` ConfigMap:
@@ -31,10 +34,89 @@ data:
help.download.windows-amd64: "path-or-url-to-download"
```
## Removed Python from the base image
If you are using a [Config Management Plugin](../../user-guide/config-management-plugins.md) that relies on Python, you
will need to build a custom image on the Argo CD base to install Python.
## Upgraded Kustomize Version
Note that bundled Kustomize version has been upgraded from 4.2.0 to 4.4.1.
## Upgrade Helm Version
## Upgraded Helm Version
Note that bundled Helm version has been upgraded from 3.7.1 to 3.8.0.
## Support for private repo SSH keys using the SHA-1 signature hash algorithm is removed in 2.3.7
Argo CD 2.3.7 upgraded its base image from Ubuntu 21.04 to Ubuntu 22.04, which upgraded OpenSSH to 8.9. OpenSSH starting
with 8.8 [dropped support for the `ssh-rsa` SHA-1 key signature algorithm](https://www.openssh.com/txt/release-8.8).
The signature algorithm is _not_ the same as the algorithm used when generating the key. There is no need to update
keys.
The signature algorithm is negotiated with the SSH server when the connection is being set up. The client offers its
list of accepted signature algorithms, and if the server has a match, the connection proceeds. For most SSH servers on
up-to-date git providers, acceptable algorithms other than `ssh-rsa` should be available.
Before upgrading to Argo CD 2.3.7, check whether your git provider(s) using SSH authentication support algorithms newer
than `rsa-ssh`.
1. Make sure your version of SSH >= 8.9 (the version used by Argo CD). If not, upgrade it before proceeding.
```shell
ssh -V
```
Example output: `OpenSSH_8.9p1 Ubuntu-3, OpenSSL 3.0.2 15 Mar 2022`
2. Once you have a recent version of OpenSSH, follow the directions from the [OpenSSH 8.8 release notes](https://www.openssh.com/txt/release-8.7):
> To check whether a server is using the weak ssh-rsa public key
> algorithm, for host authentication, try to connect to it after
> removing the ssh-rsa algorithm from ssh(1)'s allowed list:
>
> ```shell
> ssh -oHostKeyAlgorithms=-ssh-rsa user@host
> ```
>
> If the host key verification fails and no other supported host key
> types are available, the server software on that host should be
> upgraded.
If the server does not support an acceptable version, you will get an error similar to this;
```
$ ssh -oHostKeyAlgorithms=-ssh-rsa vs-ssh.visualstudio.com
Unable to negotiate with 20.42.134.1 port 22: no matching host key type found. Their offer: ssh-rsa
```
This indicates that the server needs to update its supported key signature algorithms, and Argo CD will not connect
to it.
### Workaround
The [OpenSSH 8.8 release notes](https://www.openssh.com/txt/release-8.8) describe a workaround if you cannot change the
server's key signature algorithms configuration.
> Incompatibility is more likely when connecting to older SSH
> implementations that have not been upgraded or have not closely tracked
> improvements in the SSH protocol. For these cases, it may be necessary
> to selectively re-enable RSA/SHA1 to allow connection and/or user
> authentication via the HostkeyAlgorithms and PubkeyAcceptedAlgorithms
> options. For example, the following stanza in ~/.ssh/config will enable
> RSA/SHA1 for host and user authentication for a single destination host:
>
> ```
> Host old-host
> HostkeyAlgorithms +ssh-rsa
> PubkeyAcceptedAlgorithms +ssh-rsa
> ```
>
> We recommend enabling RSA/SHA1 only as a stopgap measure until legacy
> implementations can be upgraded or reconfigured with another key type
> (such as ECDSA or Ed25519).
To apply this to Argo CD, you could create a ConfigMap with the desired ssh config file and then mount it at
`/home/argocd/.ssh/config`.
Note that bundled Helm version has been upgraded from 3.7.1 to 3.8.0.

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

@@ -2,4 +2,5 @@ mkdocs==1.2.3
mkdocs-material==7.1.7
markdown_include==0.6.0
pygments==2.7.4
jinja2===3.0.3
jinja2==3.0.3
markdown==3.3.7

7
go.mod
View File

@@ -66,7 +66,7 @@ require (
github.com/stretchr/testify v1.7.0
github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
golang.org/x/net v0.0.0-20211209124913-491a49abca63
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
@@ -203,7 +203,7 @@ require (
gopkg.in/square/go-jose.v2 v2.2.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/apiserver v0.23.1 // indirect
k8s.io/apiserver v0.23.1
k8s.io/cli-runtime v0.23.1 // indirect
k8s.io/component-base v0.23.1 // indirect
k8s.io/component-helpers v0.23.1 // indirect
@@ -226,6 +226,9 @@ replace (
google.golang.org/grpc => google.golang.org/grpc v1.15.0
// Avoid CVE-2022-28948
gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
k8s.io/api => k8s.io/api v0.23.1
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.23.1
k8s.io/apimachinery => k8s.io/apimachinery v0.23.1

10
go.sum
View File

@@ -1073,8 +1073,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1179,6 +1180,7 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1547,10 +1549,8 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=

View File

@@ -28,7 +28,7 @@ spec:
name: dexconfig
containers:
- name: dex
image: ghcr.io/dexidp/dex:v2.30.2
image: ghcr.io/dexidp/dex:v2.32.0
imagePullPolicy: Always
command: [/shared/argocd-dex, rundex]
securityContext:

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.7
resources:
- ./application-controller
- ./dex

View File

@@ -21,7 +21,7 @@ spec:
serviceAccountName: argocd-redis
containers:
- name: redis
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: Always
args:
- "--save"

View File

@@ -9564,7 +9564,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -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.7
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.7
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.7
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.7

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.7
resources:
- ../../base/application-controller
- ../../base/dex

View File

@@ -770,7 +770,7 @@ spec:
topologyKey: kubernetes.io/hostname
initContainers:
- name: config-init
image: haproxy:2.0.25-alpine
image: haproxy:2.0.29-alpine
imagePullPolicy: IfNotPresent
resources:
{}
@@ -790,7 +790,7 @@ spec:
runAsUser: 1000
containers:
- name: haproxy
image: haproxy:2.0.25-alpine
image: haproxy:2.0.29-alpine
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
@@ -878,7 +878,7 @@ spec:
automountServiceAccountToken: false
initContainers:
- name: config-init
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
resources:
{}
@@ -906,7 +906,7 @@ spec:
containers:
- name: redis
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
command:
- redis-server
@@ -947,7 +947,7 @@ spec:
lifecycle:
{}
- name: sentinel
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
command:
- redis-sentinel

View File

@@ -9,12 +9,12 @@ redis-ha:
haproxy:
enabled: true
image:
tag: 2.0.25-alpine
tag: 2.0.29-alpine
timeout:
server: 6m
client: 6m
checkInterval: 3s
image:
tag: 6.2.6-alpine
tag: 6.2.7-alpine
sentinel:
bind: "0.0.0.0"

View File

@@ -10494,7 +10494,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.30.2
image: ghcr.io/dexidp/dex:v2.32.0
imagePullPolicy: Always
name: dex
ports:
@@ -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.7
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.7
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -10612,7 +10612,7 @@ spec:
app.kubernetes.io/name: argocd-redis-ha-haproxy
topologyKey: kubernetes.io/hostname
containers:
- image: haproxy:2.0.25-alpine
- image: haproxy:2.0.29-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -10641,7 +10641,7 @@ spec:
- /readonly/haproxy_init.sh
command:
- sh
image: haproxy:2.0.25-alpine
image: haproxy:2.0.29-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:
@@ -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.7
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.7
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.7
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.7
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -11342,7 +11342,7 @@ spec:
- /data/conf/redis.conf
command:
- redis-server
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -11380,7 +11380,7 @@ spec:
- /data/conf/sentinel.conf
command:
- redis-sentinel
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -11426,7 +11426,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:

View File

@@ -7790,7 +7790,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.30.2
image: ghcr.io/dexidp/dex:v2.32.0
imagePullPolicy: Always
name: dex
ports:
@@ -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.7
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.7
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -7908,7 +7908,7 @@ spec:
app.kubernetes.io/name: argocd-redis-ha-haproxy
topologyKey: kubernetes.io/hostname
containers:
- image: haproxy:2.0.25-alpine
- image: haproxy:2.0.29-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -7937,7 +7937,7 @@ spec:
- /readonly/haproxy_init.sh
command:
- sh
image: haproxy:2.0.25-alpine
image: haproxy:2.0.29-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:
@@ -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.7
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.7
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.7
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.7
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -8638,7 +8638,7 @@ spec:
- /data/conf/redis.conf
command:
- redis-server
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -8676,7 +8676,7 @@ spec:
- /data/conf/sentinel.conf
command:
- redis-sentinel
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -8722,7 +8722,7 @@ spec:
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
- name: SENTINEL_ID_2
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: IfNotPresent
name: config-init
volumeMounts:

View File

@@ -9864,7 +9864,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.30.2
image: ghcr.io/dexidp/dex:v2.32.0
imagePullPolicy: Always
name: dex
ports:
@@ -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.7
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.7
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -9988,7 +9988,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -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.7
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.7
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.7
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.7
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -7160,7 +7160,7 @@ spec:
- command:
- /shared/argocd-dex
- rundex
image: ghcr.io/dexidp/dex:v2.30.2
image: ghcr.io/dexidp/dex:v2.32.0
imagePullPolicy: Always
name: dex
ports:
@@ -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.7
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.7
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -7284,7 +7284,7 @@ spec:
- ""
- --appendonly
- "no"
image: redis:6.2.6-alpine
image: redis:6.2.7-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -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.7
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.7
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.7
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.7
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -133,6 +133,31 @@ func newServiceWithCommitSHA(root, revision string) *Service {
return service
}
// createSymlink creates a symlink with name linkName to file destName in
// workingDir
func createSymlink(t *testing.T, workingDir, destName, linkName string) error {
oldWorkingDir, err := os.Getwd()
if err != nil {
return err
}
if workingDir != "" {
err = os.Chdir(workingDir)
if err != nil {
return err
}
defer func() {
if err := os.Chdir(oldWorkingDir); err != nil {
t.Fatal(err.Error())
}
}()
}
err = os.Symlink(destName, linkName)
if err != nil {
return err
}
return nil
}
func TestGenerateYamlManifestInDir(t *testing.T) {
service := newService("../..")
@@ -1999,7 +2024,12 @@ func Test_getPotentiallyValidManifests(t *testing.T) {
})
t.Run("circular link should throw an error", func(t *testing.T) {
require.DirExists(t, "./testdata/circular-link")
const testDir = "./testdata/circular-link"
require.DirExists(t, testDir)
require.NoError(t, createSymlink(t, testDir, "a.json", "b.json"))
defer os.Remove(path.Join(testDir, "a.json"))
require.NoError(t, createSymlink(t, testDir, "b.json", "a.json"))
defer os.Remove(path.Join(testDir, "b.json"))
manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/circular-link", "./testdata/circular-link", false, "", "", resource.MustParse("0"))
assert.Empty(t, manifests)
assert.Error(t, err)
@@ -2094,7 +2124,12 @@ func Test_findManifests(t *testing.T) {
})
t.Run("circular link should throw an error", func(t *testing.T) {
require.DirExists(t, "./testdata/circular-link")
const testDir = "./testdata/circular-link"
require.DirExists(t, testDir)
require.NoError(t, createSymlink(t, testDir, "a.json", "b.json"))
defer os.Remove(path.Join(testDir, "a.json"))
require.NoError(t, createSymlink(t, testDir, "b.json", "a.json"))
defer os.Remove(path.Join(testDir, "b.json"))
manifests, err := findManifests(logCtx, "./testdata/circular-link", "./testdata/circular-link", nil, noRecurse, nil, resource.MustParse("0"))
assert.Empty(t, manifests)
assert.Error(t, err)

View File

View File

@@ -1 +0,0 @@
b.json

View File

@@ -1 +0,0 @@
a.json

View File

@@ -1,4 +1,4 @@
FROM redis:6.2.6 as redis
FROM redis:6.2.7 as redis
FROM node:12.18.4 as node
@@ -6,7 +6,7 @@ FROM golang:1.17 as golang
FROM registry:2.7.1 as registry
FROM ubuntu:21.10
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install --fix-missing -y \
@@ -66,6 +66,11 @@ COPY ./test/fixture/testrepos/ssh_host_*_key* /etc/ssh/
# Copy redis binaries to the image
COPY --from=redis /usr/local/bin/* /usr/local/bin/
# Copy redis dependencies/shared libraries
# Ubuntu 22.04+ has moved to OpenSSL3 and no longer provides these libraries
COPY --from=redis /usr/lib/x86_64-linux-gnu/libssl.so.1.1 /usr/lib/x86_64-linux-gnu/
COPY --from=redis /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 /usr/lib/x86_64-linux-gnu/
# Copy registry binaries to the image
COPY --from=registry /bin/registry /usr/local/bin/
COPY --from=registry /etc/docker/registry/config.yml /etc/docker/registry/config.yml

View File

@@ -182,3 +182,4 @@ func TestClusterURLInRestAPI(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, map[string]string{"test": "val"}, cluster.Labels)
}

View File

@@ -6,7 +6,11 @@ import (
"fmt"
"log"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/clusterauth"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
@@ -63,6 +67,30 @@ func (a *Actions) Create(args ...string) *Actions {
return a
}
func (a *Actions) CreateWithRBAC(args ...string) *Actions {
pathOpts := clientcmd.NewDefaultPathOptions()
config, err := pathOpts.GetStartingConfig()
if err != nil {
a.lastError = err
return a
}
clientConfig := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{})
conf, err := clientConfig.ClientConfig()
if err != nil {
a.lastError = err
return a
}
client := kubernetes.NewForConfigOrDie(conf)
_, err = clusterauth.InstallClusterManagerRBAC(client, "kube-system", []string{}, common.BearerTokenTimeout)
if err != nil {
a.lastError = err
return a
}
return a.Create()
}
func (a *Actions) List() *Actions {
a.context.t.Helper()
a.runCli("cluster", "list")
@@ -75,6 +103,20 @@ func (a *Actions) Get() *Actions {
return a
}
func (a *Actions) DeleteByName() *Actions {
a.context.t.Helper()
a.runCli("cluster", "rm", a.context.name)
return a
}
func (a *Actions) DeleteByServer() *Actions {
a.context.t.Helper()
a.runCli("cluster", "rm", a.context.server)
return a
}
func (a *Actions) Then() *Consequences {
a.context.t.Helper()
return &Consequences{a.context, a}

View File

@@ -3,7 +3,7 @@ FROM golang:1.17 AS go
RUN go install github.com/mattn/goreman@latest && \
go install github.com/kisielk/godepgraph@latest
FROM ubuntu:21.10
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \

View File

@@ -10,45 +10,22 @@
"test": "jest"
},
"dependencies": {
"@types/classnames": "^2.2.3",
"@types/cookie": "^0.3.1",
"@types/dagre": "^0.7.40",
"@types/deepmerge": "^2.2.0",
"@types/git-url-parse": "^9.0.0",
"@types/js-yaml": "^3.11.2",
"@types/minimatch": "^3.0.3",
"@types/prop-types": "^15.5.2",
"@types/react": "^16.8.5",
"@types/react-autocomplete": "^1.8.4",
"@types/react-dom": "^16.9.14",
"@types/react-form": "^2.16.0",
"@types/react-helmet": "^5.0.17",
"@types/react-paginate": "^6.2.0",
"@types/react-router": "^4.0.27",
"@types/react-router-dom": "^4.2.3",
"@types/superagent": "^3.5.7",
"ansi-to-react": "^6.1.6",
"argo-ui": "git+https://github.com/argoproj/argo-ui.git",
"classnames": "^2.2.5",
"color": "^3.1.0",
"cookie": "^0.3.1",
"copy-webpack-plugin": "^6.1.1",
"dagre": "^0.8.2",
"deepmerge": "^3.2.0",
"foundation-sites": "^6.4.3",
"git-url-parse": "^11.1.2",
"html-webpack-plugin": "^3.2.0",
"jest-junit": "^6.4.0",
"js-yaml": "^3.13.1",
"json-merge-patch": "^0.2.3",
"lodash-es": "^4.17.21",
"minimatch": "^3.0.4",
"moment": "^2.24.0",
"moment": "^2.29.4",
"monaco-editor": "^0.27.0",
"monaco-editor-webpack-plugin": "^6.0.0",
"node-sass": "^6.0.1",
"prop-types": "^15.6.0",
"raw-loader": "^0.5.1",
"react": "^16.9.3",
"react-autocomplete": "^1.8.1",
"react-diff-view": "^2.4.7",
@@ -64,21 +41,10 @@
"react-svg-piechart": "^2.1.1",
"redoc": "^2.0.0-rc.64",
"rxjs": "^6.6.6",
"sass-loader": "^6.0.6",
"source-map-loader": "^0.2.3",
"style-loader": "^0.20.1",
"superagent": "^3.8.2",
"superagent-promise": "^1.1.0",
"timezones-list": "3.0.1",
"ts-loader": "^6.0.4",
"ts-node": "^4.1.0",
"tslint": "^6.1.3",
"tslint-react": "^3.4.0",
"typescript": "^4.0.3",
"unidiff": "^1.0.2",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
"unidiff": "^1.0.2"
},
"resolutions": {
"@types/react": "^16.9.3",
@@ -90,23 +56,57 @@
"@babel/preset-env": "^7.7.1",
"@babel/preset-react": "^7.7.0",
"@babel/preset-typescript": "^7.7.2",
"@types/classnames": "^2.2.3",
"@types/cookie": "^0.3.1",
"@types/dagre": "^0.7.40",
"@types/deepmerge": "^2.2.0",
"@types/git-url-parse": "^9.0.0",
"@types/jest": "^24.0.13",
"@types/js-yaml": "^3.11.2",
"@types/lodash-es": "^4.17.5",
"@types/minimatch": "^3.0.3",
"@types/prop-types": "^15.5.2",
"@types/react": "^16.8.5",
"@types/react-autocomplete": "^1.8.4",
"@types/react-dom": "^16.9.14",
"@types/react-form": "^2.16.0",
"@types/react-helmet": "^5.0.17",
"@types/react-paginate": "^6.2.0",
"@types/react-router": "^4.0.27",
"@types/react-router-dom": "^4.2.3",
"@types/react-test-renderer": "^16.8.3",
"@types/superagent": "^3.5.7",
"add": "^2.0.6",
"babel-jest": "^24.9.0",
"babel-loader": "^8.0.6",
"codecov": "^3.7.2",
"copy-webpack-plugin": "^6.1.1",
"esbuild-loader": "^2.15.1",
"html-webpack-plugin": "^3.2.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.9.0",
"jest-junit": "^6.4.0",
"jest-transform-css": "^2.0.0",
"monaco-editor-webpack-plugin": "^6.0.0",
"node-sass": "^6.0.1",
"postcss": "^8.2.13",
"prettier": "1.19",
"raw-loader": "^0.5.1",
"react-test-renderer": "16.8.3",
"sass-loader": "^6.0.6",
"source-map-loader": "^0.2.3",
"style-loader": "^0.20.1",
"ts-jest": "^24.1.0",
"ts-loader": "^6.0.4",
"ts-node": "^4.1.0",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.0.1",
"tslint-react": "^3.4.0",
"typescript": "^4.0.3",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"yarn": "^1.22.10"
}
}

View File

@@ -6159,6 +6159,11 @@ moment-timezone@^0.5.33:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
moment@^2.29.4:
version "2.29.4"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
monaco-editor-webpack-plugin@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-6.0.0.tgz#628956ce1851afa2a5f6c88d0ecbb24e9a444898"
@@ -6788,9 +6793,9 @@ parse-path@^4.0.0:
query-string "^6.13.8"
parse-url@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d"
integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==
version "6.0.5"
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.5.tgz#4acab8982cef1846a0f8675fa686cef24b2f6f9b"
integrity sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA==
dependencies:
is-ssh "^1.3.0"
normalize-url "^6.1.0"

View File

@@ -2,16 +2,19 @@ package clusterauth
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/argoproj/argo-cd/v2/common"
jwt "github.com/golang-jwt/jwt/v4"
log "github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
)
@@ -173,7 +176,7 @@ func upsertRoleBinding(clientset kubernetes.Interface, name string, roleName str
}
// InstallClusterManagerRBAC installs RBAC resources for a cluster manager to operate a cluster. Returns a token
func InstallClusterManagerRBAC(clientset kubernetes.Interface, ns string, namespaces []string) (string, error) {
func InstallClusterManagerRBAC(clientset kubernetes.Interface, ns string, namespaces []string, bearerTokenTimeout time.Duration) (string, error) {
err := CreateServiceAccount(clientset, ArgoCDManagerServiceAccount, ns)
if err != nil {
@@ -212,42 +215,123 @@ func InstallClusterManagerRBAC(clientset kubernetes.Interface, ns string, namesp
}
}
return GetServiceAccountBearerToken(clientset, ns, ArgoCDManagerServiceAccount)
return GetServiceAccountBearerToken(clientset, ns, ArgoCDManagerServiceAccount, bearerTokenTimeout)
}
// GetServiceAccountBearerToken will attempt to get the provided service account until it
// exists, iterate the secrets associated with it looking for one of type
// kubernetes.io/service-account-token, and return it's token if found.
func GetServiceAccountBearerToken(clientset kubernetes.Interface, ns string, sa string) (string, error) {
var serviceAccount *corev1.ServiceAccount
// GetServiceAccountBearerToken determines if a ServiceAccount has a
// bearer token secret to use or if a secret should be created. It then
// waits for the secret to have a bearer token if a secret needs to
// be created and returns the token in encoded base64.
func GetServiceAccountBearerToken(clientset kubernetes.Interface, ns string, sa string, timeout time.Duration) (string, error) {
secretName, err := getOrCreateServiceAccountTokenSecret(clientset, sa, ns)
if err != nil {
return "", err
}
var secret *corev1.Secret
var err error
err = wait.Poll(500*time.Millisecond, 30*time.Second, func() (bool, error) {
serviceAccount, err = clientset.CoreV1().ServiceAccounts(ns).Get(context.Background(), sa, metav1.GetOptions{})
err = wait.PollImmediate(500*time.Millisecond, timeout, func() (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), common.ClusterAuthRequestTimeout)
defer cancel()
secret, err = clientset.CoreV1().Secrets(ns).Get(ctx, secretName, metav1.GetOptions{})
if err != nil {
return false, err
return false, fmt.Errorf("failed to get secret %q for serviceaccount %q: %w", secretName, sa, err)
}
// Scan all secrets looking for one of the correct type:
for _, oRef := range serviceAccount.Secrets {
var getErr error
secret, err = clientset.CoreV1().Secrets(ns).Get(context.Background(), oRef.Name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("Failed to retrieve secret %q: %v", oRef.Name, getErr)
}
if secret.Type == corev1.SecretTypeServiceAccountToken {
return true, nil
}
_, ok := secret.Data["token"]
if !ok {
return false, nil
}
return false, nil
return true, nil
})
if err != nil {
return "", fmt.Errorf("Failed to wait for service account secret: %v", err)
return "", fmt.Errorf("failed to get token for serviceaccount %q: %w", sa, err)
}
token, ok := secret.Data["token"]
if !ok {
return "", fmt.Errorf("Secret %q for service account %q did not have a token", secret.Name, serviceAccount)
return string(secret.Data["token"]), nil
}
// getOrCreateServiceAccountTokenSecret will check if a ServiceAccount
// already has a kubernetes.io/service-account-token secret associated
// with it or creates one if the ServiceAccount doesn't have one. This
// was added to help add k8s v1.24+ clusters.
func getOrCreateServiceAccountTokenSecret(clientset kubernetes.Interface, sa, ns string) (string, error) {
// Wait for sa to have secret, but don't wait too
// long for 1.24+ clusters
var serviceAccount *corev1.ServiceAccount
err := wait.PollImmediate(500*time.Millisecond, 5*time.Second, func() (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), common.ClusterAuthRequestTimeout)
defer cancel()
var getErr error
serviceAccount, getErr = clientset.CoreV1().ServiceAccounts(ns).Get(ctx, sa, metav1.GetOptions{})
if getErr != nil {
return false, fmt.Errorf("failed to get serviceaccount %q: %w", sa, getErr)
}
if len(serviceAccount.Secrets) == 0 {
return false, nil
}
return true, nil
})
if err != nil && err != wait.ErrWaitTimeout {
return "", fmt.Errorf("failed to get serviceaccount token secret: %w", err)
}
return string(token), nil
if serviceAccount == nil {
log.Errorf("Unexpected nil serviceaccount '%s/%s' with no error returned", ns, sa)
return "", fmt.Errorf("failed to create serviceaccount token secret: nil serviceaccount returned for '%s/%s' with no error", ns, sa)
}
outerCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
for _, s := range serviceAccount.Secrets {
innerCtx, cancel := context.WithTimeout(outerCtx, common.ClusterAuthRequestTimeout)
defer cancel()
existingSecret, err := clientset.CoreV1().Secrets(ns).Get(innerCtx, s.Name, metav1.GetOptions{})
if err != nil {
return "", fmt.Errorf("failed to retrieve secret %q: %w", s.Name, err)
}
if existingSecret.Type == corev1.SecretTypeServiceAccountToken {
return existingSecret.Name, nil
}
}
return createServiceAccountToken(clientset, serviceAccount)
}
func createServiceAccountToken(clientset kubernetes.Interface, serviceAccount *corev1.ServiceAccount) (string, error) {
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
GenerateName: serviceAccount.Name + "-token-",
Namespace: serviceAccount.Namespace,
Annotations: map[string]string{
corev1.ServiceAccountNameKey: serviceAccount.Name,
},
},
Type: corev1.SecretTypeServiceAccountToken,
}
ctx, cancel := context.WithTimeout(context.Background(), common.ClusterAuthRequestTimeout)
defer cancel()
secret, err := clientset.CoreV1().Secrets(serviceAccount.Namespace).Create(ctx, secret, metav1.CreateOptions{})
if err != nil {
return "", fmt.Errorf("failed to create secret for serviceaccount %q: %w", serviceAccount.Name, err)
}
log.Infof("Created bearer token secret for ServiceAccount %q", serviceAccount.Name)
serviceAccount.Secrets = []corev1.ObjectReference{{
Name: secret.Name,
Namespace: secret.Namespace,
}}
patch, err := json.Marshal(serviceAccount)
if err != nil {
return "", fmt.Errorf("failed marshaling patch for serviceaccount %q: %w", serviceAccount.Name, err)
}
_, err = clientset.CoreV1().ServiceAccounts(serviceAccount.Namespace).Patch(ctx, serviceAccount.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{})
if err != nil {
return "", fmt.Errorf("failed to patch serviceaccount %q with bearer token secret: %w", serviceAccount.Name, err)
}
return secret.Name, nil
}
// UninstallClusterManagerRBAC removes RBAC resources for a cluster manager to operate a cluster

View File

@@ -4,21 +4,24 @@ import (
"context"
"io/ioutil"
"testing"
"time"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/client-go/kubernetes/fake"
kubetesting "k8s.io/client-go/testing"
"github.com/argoproj/argo-cd/v2/util/errors"
)
const (
testToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhcmdvY2QtbWFuYWdlci10b2tlbi10ajc5ciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhcmdvY2QtbWFuYWdlciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjkxZGQzN2NmLThkOTItMTFlOS1hMDkxLWQ2NWYyYWU3ZmE4ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphcmdvY2QtbWFuYWdlciJ9.ytZjt2pDV8-A7DBMR06zQ3wt9cuVEfq262TQw7sdra-KRpDpMPnziMhc8bkwvgW-LGhTWUh5iu1y-1QhEx6mtbCt7vQArlBRxfvM5ys6ClFkplzq5c2TtZ7EzGSD0Up7tdxuG9dvR6TGXYdfFcG779yCdZo2H48sz5OSJfdEriduMEY1iL5suZd3ebOoVi1fGflmqFEkZX6SvxkoArl5mtNP6TvZ1eTcn64xh4ws152hxio42E-eSnl_CET4tpB5vgP5BVlSKW2xB7w2GJxqdETA5LJRI_OilY77dTOp8cMr_Ck3EOeda3zHfh4Okflg8rZFEeAuJYahQNeAILLkcA"
testToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhcmdvY2QtbWFuYWdlci10b2tlbi10ajc5ciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhcmdvY2QtbWFuYWdlciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjkxZGQzN2NmLThkOTItMTFlOS1hMDkxLWQ2NWYyYWU3ZmE4ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphcmdvY2QtbWFuYWdlciJ9.ytZjt2pDV8-A7DBMR06zQ3wt9cuVEfq262TQw7sdra-KRpDpMPnziMhc8bkwvgW-LGhTWUh5iu1y-1QhEx6mtbCt7vQArlBRxfvM5ys6ClFkplzq5c2TtZ7EzGSD0Up7tdxuG9dvR6TGXYdfFcG779yCdZo2H48sz5OSJfdEriduMEY1iL5suZd3ebOoVi1fGflmqFEkZX6SvxkoArl5mtNP6TvZ1eTcn64xh4ws152hxio42E-eSnl_CET4tpB5vgP5BVlSKW2xB7w2GJxqdETA5LJRI_OilY77dTOp8cMr_Ck3EOeda3zHfh4Okflg8rZFEeAuJYahQNeAILLkcA"
testBearerTokenTimeout = 5 * time.Second
)
var (
@@ -132,7 +135,7 @@ func TestInstallClusterManagerRBAC(t *testing.T) {
Namespace: "test",
},
Secrets: []corev1.ObjectReference{
corev1.ObjectReference{
{
Kind: secret.GetObjectKind().GroupVersionKind().Kind,
APIVersion: secret.APIVersion,
Name: secret.GetName(),
@@ -145,7 +148,7 @@ func TestInstallClusterManagerRBAC(t *testing.T) {
t.Run("Cluster Scope - Success", func(t *testing.T) {
cs := fake.NewSimpleClientset(ns, secret, sa)
token, err := InstallClusterManagerRBAC(cs, "test", nil)
token, err := InstallClusterManagerRBAC(cs, "test", nil, testBearerTokenTimeout)
assert.NoError(t, err)
assert.Equal(t, "foobar", token)
})
@@ -154,14 +157,14 @@ func TestInstallClusterManagerRBAC(t *testing.T) {
nsecret := secret.DeepCopy()
nsecret.Data = make(map[string][]byte)
cs := fake.NewSimpleClientset(ns, nsecret, sa)
token, err := InstallClusterManagerRBAC(cs, "test", nil)
token, err := InstallClusterManagerRBAC(cs, "test", nil, testBearerTokenTimeout)
assert.Error(t, err)
assert.Empty(t, token)
})
t.Run("Namespace Scope - Success", func(t *testing.T) {
cs := fake.NewSimpleClientset(ns, secret, sa)
token, err := InstallClusterManagerRBAC(cs, "test", []string{"nsa"})
token, err := InstallClusterManagerRBAC(cs, "test", []string{"nsa"}, testBearerTokenTimeout)
assert.NoError(t, err)
assert.Equal(t, "foobar", token)
})
@@ -170,7 +173,7 @@ func TestInstallClusterManagerRBAC(t *testing.T) {
nsecret := secret.DeepCopy()
nsecret.Data = make(map[string][]byte)
cs := fake.NewSimpleClientset(ns, nsecret, sa)
token, err := InstallClusterManagerRBAC(cs, "test", []string{"nsa"})
token, err := InstallClusterManagerRBAC(cs, "test", []string{"nsa"}, testBearerTokenTimeout)
assert.Error(t, err)
assert.Empty(t, token)
})
@@ -254,7 +257,108 @@ func TestGetServiceAccountBearerToken(t *testing.T) {
}
kubeclientset := fake.NewSimpleClientset(sa, dockercfgSecret, tokenSecret)
token, err := GetServiceAccountBearerToken(kubeclientset, "kube-system", sa.Name)
token, err := GetServiceAccountBearerToken(kubeclientset, "kube-system", sa.Name, testBearerTokenTimeout)
assert.NoError(t, err)
assert.Equal(t, testToken, token)
}
func Test_getOrCreateServiceAccountTokenSecret_NoSecretForSA(t *testing.T) {
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "kube-system",
},
}
saWithoutSecret := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: ArgoCDManagerServiceAccount,
Namespace: ns.Name,
},
}
cs := fake.NewSimpleClientset(ns, saWithoutSecret)
cs.PrependReactor("create", "secrets",
func(a kubetesting.Action) (handled bool, ret runtime.Object, err error) {
s, ok := a.(kubetesting.CreateAction).GetObject().(*corev1.Secret)
if !ok {
return
}
if s.Name == "" && s.GenerateName != "" {
s.SetName(names.SimpleNameGenerator.GenerateName(s.GenerateName))
}
s.Data = make(map[string][]byte)
s.Data["token"] = []byte("fake-token")
return
})
got, err := getOrCreateServiceAccountTokenSecret(cs, ArgoCDManagerServiceAccount, ns.Name)
assert.NoError(t, err)
assert.Contains(t, got, "argocd-manager-token-")
obj, err := cs.Tracker().Get(schema.GroupVersionResource{Version: "v1", Resource: "serviceaccounts"},
ns.Name, ArgoCDManagerServiceAccount)
if err != nil {
t.Errorf("ServiceAccount %s not found but was expected to be found: %s", ArgoCDManagerServiceAccount, err.Error())
}
sa := obj.(*corev1.ServiceAccount)
assert.Equal(t, 1, len(sa.Secrets))
}
func Test_getOrCreateServiceAccountTokenSecret_SAHasSecret(t *testing.T) {
ns := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "kube-system",
},
}
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "sa-secret",
Namespace: ns.Name,
},
Type: corev1.SecretTypeServiceAccountToken,
Data: map[string][]byte{
"token": []byte("foobar"),
},
}
saWithSecret := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: ArgoCDManagerServiceAccount,
Namespace: ns.Name,
},
Secrets: []corev1.ObjectReference{
{
Kind: secret.GetObjectKind().GroupVersionKind().Kind,
APIVersion: secret.APIVersion,
Name: secret.GetName(),
Namespace: secret.GetNamespace(),
UID: secret.GetUID(),
ResourceVersion: secret.GetResourceVersion(),
},
},
}
cs := fake.NewSimpleClientset(ns, saWithSecret, secret)
got, err := getOrCreateServiceAccountTokenSecret(cs, ArgoCDManagerServiceAccount, ns.Name)
assert.NoError(t, err)
assert.Equal(t, "sa-secret", got)
obj, err := cs.Tracker().Get(schema.GroupVersionResource{Version: "v1", Resource: "serviceaccounts"},
ns.Name, ArgoCDManagerServiceAccount)
if err != nil {
t.Errorf("ServiceAccount %s not found but was expected to be found: %s", ArgoCDManagerServiceAccount, err.Error())
}
sa := obj.(*corev1.ServiceAccount)
assert.Equal(t, 1, len(sa.Secrets))
// Adding if statement to prevent case where secret not found
// since accessing name by first index.
if len(sa.Secrets) != 0 {
assert.Equal(t, "sa-secret", sa.Secrets[0].Name)
}
}

View File

@@ -11,14 +11,14 @@ import (
// Unfortunately, crypto/ssh does not offer public constants or list for
// this.
var SupportedSSHKeyExchangeAlgorithms = []string{
"diffie-hellman-group1-sha1",
"diffie-hellman-group14-sha1",
"curve25519-sha256",
"curve25519-sha256@libssh.org",
"ecdh-sha2-nistp256",
"ecdh-sha2-nistp384",
"ecdh-sha2-nistp521",
"curve25519-sha256@libssh.org",
"diffie-hellman-group-exchange-sha1",
"diffie-hellman-group-exchange-sha256",
"diffie-hellman-group14-sha256",
"diffie-hellman-group14-sha1",
}
// List of default key exchange algorithms to use. We use those that are

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, a.baseHRef}) {
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,12 +1,15 @@
package oidc
import (
"crypto/tls"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
gooidc "github.com/coreos/go-oidc"
@@ -19,6 +22,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 +108,220 @@ 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)
if !strings.Contains(w.Body.String(), "certificate signed by unknown authority") && !strings.Contains(w.Body.String(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
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")
assert.NotContains(t, w.Body.String(), "certificate signed by unknown authority")
})
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)
if !strings.Contains(w.Body.String(), "certificate signed by unknown authority") && !strings.Contains(w.Body.String(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
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")
assert.NotContains(t, w.Body.String(), "certificate signed by unknown authority")
})
}
func Test_Login_Flow(t *testing.T) {
// Show that SSO login works when no redirect URL is provided, and we fall back to the configured base href for the
// Argo CD instance.
oidcTestServer := test.GetOIDCTestServer(t)
t.Cleanup(oidcTestServer.Close)
cdSettings := &settings.ArgoCDSettings{
URL: "https://argocd.example.com",
OIDCConfigRAW: fmt.Sprintf(`
name: Test
issuer: %s
clientID: xxx
clientSecret: yyy
requestedScopes: ["oidc"]`, oidcTestServer.URL),
OIDCTLSInsecureSkipVerify: true,
}
// The base href (the last argument for NewClientApp) is what HandleLogin will fall back to when no explicit
// redirect URL is given.
app, err := NewClientApp(cdSettings, "", "/")
require.NoError(t, err)
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "https://argocd.example.com/auth/login", nil)
app.HandleLogin(w, req)
redirectUrl, err := w.Result().Location()
require.NoError(t, err)
state := redirectUrl.Query()["state"]
req = httptest.NewRequest("GET", fmt.Sprintf("https://argocd.example.com/auth/callback?state=%s&code=abc", state), nil)
for _, cookie := range w.Result().Cookies() {
req.AddCookie(cookie)
}
w = httptest.NewRecorder()
app.HandleCallback(w, req)
assert.NotContains(t, w.Body.String(), InvalidRedirectURLError.Error())
}
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)
if !strings.Contains(w.Body.String(), "certificate signed by unknown authority") && !strings.Contains(w.Body.String(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
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")
assert.NotContains(t, w.Body.String(), "certificate signed by unknown authority")
})
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)
if !strings.Contains(w.Body.String(), "certificate signed by unknown authority") && !strings.Contains(w.Body.String(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
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")
assert.NotContains(t, w.Body.String(), "certificate signed by unknown authority")
})
}
func TestIsValidRedirect(t *testing.T) {
var tests = []struct {
name string
@@ -191,7 +409,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 +437,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,249 @@ 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. The error message is environment-dependent, so
// we check for either of the error messages associated with a failed cert check.
assert.NotContains(t, err.Error(), "certificate is not trusted")
assert.NotContains(t, err.Error(), "certificate signed by unknown authority")
})
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)
require.Error(t, err)
if !strings.Contains(err.Error(), "certificate signed by unknown authority") && !strings.Contains(err.Error(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
})
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)
require.Error(t, err)
if !strings.Contains(err.Error(), "certificate signed by unknown authority") && !strings.Contains(err.Error(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
})
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)
require.Error(t, err)
if !strings.Contains(err.Error(), "certificate signed by unknown authority") && !strings.Contains(err.Error(), "certificate is not trusted") {
t.Fatal("did not receive expected certificate verification failure error")
}
})
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 is not trusted")
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")
assert.NotContains(t, err.Error(), "certificate signed by unknown authority")
})
}

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 {