kuberneteshttpsistiocert-manageracme

Istio: How to redirect to HTTPS except for /.well-known/acme-challenge


I want the traffic thar comes to my cluster as HTTP to be redirected to HTTPS. However, the cluster receives requests from hundreds of domains that change dinamically (creating new certs with cert-manager). So I want the redirect to happen only when the URI doesn't have the prefix /.well-known/acme-challenge

I am using a gateway that listens to 443 and other gateway that listens to 80 and send the HTTP to an acme-solver virtual service.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: default-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - site1.com
    port:
      name: https-site1.com
      number: 443
      protocol: HTTPS
    tls:
      credentialName: cert-site1.com
      mode: SIMPLE
  - hosts:
    - site2.com
    port:
      name: https-site2.com
      number: 443
      protocol: HTTPS
    tls:
      credentialName: cert-site2.com
      mode: SIMPLE
  ...
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: acme-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: http
      number: 80
      protocol: HTTP
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: acme-solver
  namespace: istio-system
spec:
  hosts:
  - "*"
  gateways:
  - acme-gateway
  http:
  - match:
    - uri:
        prefix: /.well-known/acme-challenge
    route:
    - destination:
        host: acme-solver.istio-system.svc.cluster.local
        port:
          number: 8089
  - redirect:
      authority: # Should redirect to https://$HOST, but I don't know how to get the $HOST

How can I do that using istio?


Solution

  • Looking into the documentation:

    As a workaround:

    1. Please consider using DNS-01 challenge:

    a) it only makes sense to use DNS-01 challenges if your DNS provider has an API you can use to automate updates.

    b) using this approach you should consider additional security risk as stated in the docs:

    Pros: You can use this challenge to issue certificates containing wildcard domain names. It works well even if you have multiple web servers.

    Cons: *Keeping API credentials on your web server is risky. Your DNS provider might not offer an API. Your DNS API may not provide information on propagation times.

    As mentioned here:

    In order to be automatic, though, the software that requests the certificate will also need to be able to modify the DNS records for that domain. In order to modify the DNS records, that software will also need to have access to the credentials for the DNS service (e.g. the login and password, or a cryptographic token), and those credentials will have to be stored wherever the automation takes place. In many cases, this means that if the machine handling the process gets compromised, so will the DNS credentials, and this is where the real danger lies.


    1. I would suggest also another approach to use some simple nginx pod which would redirect all http traffic to https.

    There is a tutorial on medium with nginx configuration you might try to use.

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: nginx-config
    data:
      nginx.conf: |
        server {
          listen 80 default_server;
          server_name _;
          return 301 https://$host$request_uri;
        }
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: redirect
      labels:
        app: redirect
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: redirect
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: redirect
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: redirect
      template:
        metadata:
          labels:
            app: redirect
        spec:
          containers:
          - name: redirect
            image: nginx:stable
            resources:
              requests:
                cpu: "100m"
            imagePullPolicy: IfNotPresent
            ports:
            - containerPort: 80
            volumeMounts:
            - mountPath: /etc/nginx/conf.d
              name: config
          volumes:
          - name: config
            configMap:
              name: nginx-config
    

    Additionally you would have to change your virtual service to send all the traffic except prefix: /.well-known/acme-challenge to nginx.

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: acme-solver
      namespace: istio-system
    spec:
      hosts:
      - "*"
      gateways:
      - acme-gateway
      http:
      - name: "acmesolver"
        match:
        - uri:
            prefix: /.well-known/acme-challenge
        route:
        - destination:
            host: reviews.prod.svc.cluster.local
            port:
              number: 8089
      - name: "nginx"
        route:
        - destination:
            host: nginx