Serving argocd behind traefik ingress?

I would like to run ArgoCD behind Traefik such that I can both access the ArgoCD web ui and interact with the argocd API using the argocd command line tool.

  • I have deployed ArgoCD into a Kubernetes cluster from the upstream manifests.

  • I have deployed Traefik as in ingress server from the helm chart. I haven't set any chart values other than the log level.

  • I am using the following Ingress resource:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: argocd
    
    spec:
      tls:
        - hosts:
          - argocd.internal
          secretName: argocd-certificate
      rules:
        - host: argocd.internal
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: argocd-server
                    port:
                      number: 80
    
  • The secret argocd-certificate exists and has a valid certificate and key in tls.crt and tls.key.

With this in place, I can access ArgoCD at http://argocd.internal, but attempts to access it at https://argocd.internal fail with:

$ curl https://argocd.internal
404 page not found

(Note that there is no certificate error; the endpoint is servering the certificate configured in the Ingress resource.)

I'm not sure what's going on here: I haven't bound this to any particular Traefik endpoint, so I would expect it to be available on all endpoints...and when we query the Traefik API, we see:

$ curl -sf 'http://localhost:9000/api/http/routers?search=&status=&per_page=6&page=1' |
  jq '.[]|select(.service == "argocd-argocd-server-80")'
{
  "entryPoints": [
    "metrics",
    "web",
    "websecure"
  ],
  "service": "argocd-argocd-server-80",
  "rule": "Host(`argocd.internal`) && PathPrefix(`/`)",
  "status": "enabled",
  "using": [
    "metrics",
    "web",
    "websecure"
  ],
  "name": "argocd-argocd-argocd-internal@kubernetes",
  "provider": "kubernetes"
}

Which confirms that the router is available on all endpoints.

What's going on here -- why does this only seem to do the right thing when accessed via http:// and not via https://?

While I would like to understand why the original configuration doesn't work, I was able to get the functionality I want by replacing the Ingress with a pair of IngressRoutes.

One for http traffic to handle an http to https redirect:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: argocd-http
spec:
  entryPoints:
    - web
  routes:
  - kind: Rule
    match: Host(`argocd.internal`)
    priority: 10
    middlewares:
      - name: redirect-http-https
    services:
    - kind: Service
      name: argocd-server
      port: http

And a second to handle https connections (that differentiates between https and grpc traffic):

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: argocd-https
spec:
  entryPoints:
    - websecure
  routes:
  - kind: Rule
    match: Host(`argocd.internal`)
    priority: 10
    services:
    - kind: Service
      name: argocd-server
      port: http
  - kind: Rule
    match: >-
      Host(`argocd.internal`) &&
      Headers(`Content-Type`, `application/grpc`)
    priority: 11
    services:
    - kind: Service
      name: argocd-server
      port: http
      scheme: h2c
  tls:
    secretName: argocd-certificate

With these in place, I can access the web UI and I can interact with the server using the argocd command line client.