I’m running Istio 1.16.1 with cert-manager 1.11.0 ClusterIssuer pointed at Let’s Encrypt using HTTP-01 challenge. I have a gateway with a virtual service and I would like to automatically redirect all HTTP traffic to HTTPS. Except of the Let’s Encrypt challenge. Because, after Let’s Encrypt documentation[1]:
The HTTP-01 challenge can only be done on port 80. Allowing clients to specify arbitrary ports would make the challenge less secure, and so it is not allowed by the ACME standard.
Istio documentation suggests using the ServerTLSSettings[2] httpRedirect: true but this has a very nasty side effect. All traffic, even challenge solving is redirected to HTTPS. No certificate will be issued or renewed until httpsRedirect is set to false. This can be observed in cert-manager logs (broken into multiple lines for readability):
E0708 15:04:16.187682 1 sync.go:190] cert-manager/challenges
"msg"="propagation check failed"
"error"="failed to perform self check GET request 'http://test.svcs.sh/.well-known/acme-challenge/KVPhulYn7MtBxU9rPnFumBfh8mutovrVi50UGzaEOCA': Get \"https://test.svcs.sh/.well-known/acme-challenge/KVPhulYn7MtBxU9rPnFumBfh8mutovrVi50UGzaEOCA\": EOF"
"dnsName"="test.svcs.sh"
"resource_kind"="Challenge"
"resource_name"="test-certificate-mfjkm-1639063799-1954195061"
"resource_namespace"="istio-system"
"resource_version"="v1"
"type"="HTTP-01"
The challenge needs to be done over HTTP but due to the httpRedirect=true, it gets redirected to HTTPS, which cannot work. The EOF is intriguing. Someone closes the connection!
The fix is relatively simple and if you are interesting in that part only, jump right fixing the problem. The rest of this post is an investigation for a supporting evidence why it is working and why the httpsRedirect: true doesn’t work.
table of contents
§istio configuration
Here’s my baseline configuration.
|
|
§fixing the problem
The solution can be found in one of the comments to this GitHub issue[3]. It’s really simple:
- Keep the httpRedirect set to false in the Gateway.
- Use a match.uri.prefix with scheme.exact: http and redirect.scheme: https to handle the redirect selectively in a VirtualService.
We arrive at the following definition:
|
|
Problem fixed. The certificate is issued and the redirect works as expected:
|
|
* Trying 167.235.105.89:80...
* Connected to test.svcs.sh (167.235.105.89) port 80 (#0)
> GET / HTTP/1.1
> Host: test.svcs.sh
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< location: https://test.svcs.sh/
< date: Sat, 08 Jul 2023 15:06:07 GMT
< server: istio-envoy
< content-length: 0
<
* Connection #0 to host test.svcs.sh left intact
§what’s the difference?
The obvious question is: what’s different? It works but why? Let’s do some digging. I’m using Istio Gateway so the component to start investigating from is the istio-ingressgateway which is installed into the namespace where Istio installs to.
|
|
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-77cf7f984b-smhhn 1/1 Running 0 2d2h
Istio does many interesting things. Most of those features are supported by the Envoy proxy. Istio uses and wraps Envoy extensively. As soon as the Gateway is deployed, an Envoy listener is created, it remains the same during the whole investigation and there’s nothing interesting in it so let’s not focus too much on it.
|
|
The HTTPS listener is built from this part of the Gateway declaration:
|
|
§how istio interacts with envoy proxy
- Envoy configuration starts with a listener.
- A listener contains a list of filters.
- One of the filters deals with HTTP protocol traffic.
- A filter uses a router do distribute the traffic inside of the mesh.
- A router contains a list of virtual hosts.
- A virtual host contains individual routes.
From Istio perspective: there’s always going to be one Envoy router dealing with HTTP traffic for each Istio ingress gateway component. Istio Gateway maps to Envoy’s virtual host. Individual routes are constructed from Istio VirtualService.
The relevant Istio code dealing with Envoy[4].
§gateway httpsRedirect in simple terms
I can use the istioctl proxy-config routes command to look at Envoy’s virtual hosts and routes.
I’m investigating HTTP traffic redirection to HTTPS so the point at which HTTP (port 80) traffic is accepted seems like a good place to start. The name of the virtual host is http.<Envoy’s listener bind port>, which is usually 8080. I’m looking for test.svcs.sh traffic handling Let’s look at the routing configuration for test.svcs.sh:80.
Here’s a good starting point to get the hand of some of those commands[5] used later in this post.
|
|
|
|
We can see the requireTls: ALL property.
Okay, let’s set the Gateway httpsRedirect to false and apply the change. Repeat the last istioctl command. This time the output is:
|
|
Exactly the same except that the requireTls: ALL is gone. Envoy’s requireTls: ALL setting[6] has exactly the same meaning as httpsRedirect: true in Istio Gateway,:
All requests must use TLS. If a request is not using TLS, a 301 redirect will be sent telling the client to use HTTPS.
This can be observed, with httpsRedirect: true:
|
|
* Trying 167.235.105.89:80...
* Connected to test.svcs.sh (167.235.105.89) port 80 (#0)
> GET / HTTP/1.1
> Host: test.svcs.sh
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< location: https://test.svcs.sh
< date: Sat, 08 Jul 2023 15:46:40 GMT
< server: istio-envoy
< content-length: 0
<
* Connection #0 to host test.svcs.sh left intact
§scheme redirect
The fix to the catch-all redirect and working ACME challenge uses the scheme redirect. The scheme redirect has been added as part of this feature request[7].
§why the eof
Indeed, this is a good question. At the end of the day, it shouldn’t matter if httpRedirect: true, or an individual route redirect is in use. Both should work 🤞.
From the perspective of the client the redirect is 301, 302, or any other 3xx supported by Istio.
Something else must be going on.
§cert-manager ingress and solver
What happens when cert-manager enters the challenge solving state?
- An Ingress resource is created, as instructed by the ClusterIssuer .spec.acme.solvers.
- This Ingress points at a solver Service resource.
- The Service points at the solver pod.
Relevant cert-manager source code[8].
§clean investigation: investigation.svcs.sh
Let’s pick a new domain name and start from the non-working condition by requesting a certificate for investigation.svcs.sh while httpsRedirect is true. This immediately triggers the behavior:
E0708 16:32:22.491714 1 sync.go:190] cert-manager/challenges
"msg"="propagation check failed"
"error"="failed to perform self check GET request 'http://investigation.svcs.sh/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0': Get \"https://investigation.svcs.sh/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0\": EOF"
"dnsName"="investigation.svcs.sh"
"resource_kind"="Challenge"
"resource_name"="test-certificate-p7zmc-788698794-972312837"
"resource_namespace"="istio-system"
"resource_version"="v1"
"type"="HTTP-01"
§the ingress
cert-manager creates a new Ingress in the namespace where Istio is installed:
|
|
NAME CLASS HOSTS ADDRESS PORTS AGE
cm-acme-http-solver-7djvq <none> investigation.svcs.sh 80 22m
|
|
|
|
§the service
The Ingress use a Service for its backend. The Service:
|
|
|
|
§pods
Since there’s a service, there are also pods, let’s find them with one of the Service selectors:
|
|
NAME READY STATUS RESTARTS AGE
cm-acme-http-solver-djwm7 1/1 Running 0 31m
§the ingress is picked up by envoy
Because of the ClusterIssuer .spec.acme.solvers having an istio solver in the list, the solver route is picked up by Envoy:
|
|
Produces:
|
|
Exactly the same as in case of test.svcs.sh before but with a new route:
|
|
Explanation:
- Istio Gateway and a VirtualService are created for the domain name investigation.svcs.sh.
- Istio creates virtual host routes in Envoy for the domain name.
- The certificate resource for the domain name is requested, the cert-manager ClusterIssuer starts the dance.
- cert-manager creates an Ingress for the investigation.svcs.sh domain name.
- This gets picked up by Envoy and the route is rolled into an already existing Envoy virtual host.
We see that the route is configured to forward the traffic to the solver service:
|
|
But the pod doesn’t receive anything. The log just sits there waiting for something to come through:
|
|
I0708 17:17:28.613173 1 solver.go:39] cert-manager/acmesolver
"msg"="starting listener"
"expected_domain"="investigation.svcs.sh"
"expected_key"="bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0.fXDK-85QmDd5AVhxsp1Aj3jeez2KozCIcoubbYfervY"
"expected_token"="bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0"
"listen_port"=8089
Nothing ever comes through.
§cert-manager.io/issue-temporary-certificate
One of the comments from the GitHub issue suggests using the cert-manager.io/issue-temporary-certificate: true annotation. Let’s see how that goes. I change the Certificate manifest to:
|
|
cert-manager now logs:
E0708 17:22:18.802362 1 sync.go:190] cert-manager/challenges
"msg"="propagation check failed"
"error"="wrong status code '404', expected '200'"
"dnsName"="investigation.svcs.sh"
"resource_kind"="Challenge"
"resource_name"="test-certificate-wq6ft-788698794-972312837"
"resource_namespace"="istio-system"
"resource_version"="v1"
"type"="HTTP-01"
This can be replicated with curl:
|
|
* Trying 167.235.105.89:80...
* Connected to investigation.svcs.sh (167.235.105.89) port 80 (#0)
> HEAD /.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0 HTTP/1.1
> Host: investigation.svcs.sh
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
HTTP/1.1 301 Moved Permanently
< location: https://investigation.svcs.sh/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0
location: https://investigation.svcs.sh/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0
< date: Sat, 08 Jul 2023 17:25:31 GMT
date: Sat, 08 Jul 2023 17:25:31 GMT
< server: istio-envoy
server: istio-envoy
< transfer-encoding: chunked
transfer-encoding: chunked
<
* Connection #0 to host investigation.svcs.sh left intact
* Clear auth, redirects to port from 80 to 443
* Issue another request to this URL: 'https://investigation.svcs.sh/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0'
* Trying 167.235.105.89:443...
* Connected to investigation.svcs.sh (167.235.105.89) port 443 (#1)
* ALPN: offers h2
* ALPN: offers http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN: server accepted h2
* Server certificate:
* subject: serialNumber=1234567890
* start date: Jul 8 17:21:47 2023 GMT
* expire date: Oct 6 17:21:47 2023 GMT
* issuer: CN=cert-manager.local
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multiplexing
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* h2h3 [:method: HEAD]
* h2h3 [:path: /.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0]
* h2h3 [:scheme: https]
* h2h3 [:authority: investigation.svcs.sh]
* h2h3 [user-agent: curl/7.86.0]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0x15a811e00)
> HEAD /.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0 HTTP/2
> Host: investigation.svcs.sh
> user-agent: curl/7.86.0
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 2147483647)!
< HTTP/2 404
HTTP/2 404
< x-dns-prefetch-control: on
x-dns-prefetch-control: on
< x-frame-options: SAMEORIGIN
x-frame-options: SAMEORIGIN
< etag: "wz51d2hkyd1sp"
etag: "wz51d2hkyd1sp"
< content-type: text/html; charset=utf-8
content-type: text/html; charset=utf-8
< content-length: 2329
content-length: 2329
< vary: Accept-Encoding
vary: Accept-Encoding
< date: Sat, 08 Jul 2023 17:25:31 GMT
date: Sat, 08 Jul 2023 17:25:31 GMT
< x-envoy-upstream-service-time: 11
x-envoy-upstream-service-time: 11
< server: istio-envoy
server: istio-envoy
<
* Connection #1 to host investigation.svcs.sh left intact
Hmm, this doesn’t work either but… it’s no longer an EOF, it’s a HTTP 404. This is significant in the context of cert-manager code responsible for the handling of the challenge[9]. Especially this part:
|
|
Aha, cert-manager will accept any certificate as long as there is one.
- cert-manager issued a temporary certificate and stored it in the secret expected by the Istio Gateway.
- The request on port 80 is redirected to HTTPS at the httpsRedirect level.
- The request arrives in the mesh and is matched against the VirtualService https match for the /* path prefix.
- cert-manager ignores invalid certificate and accepts the redirect.
- The redirect leads to the HTTPS endpoint but the response is HTTP 404. This error comes from some-backend-service.
§enough evidence for final conclusions
Frankly, the virtual host output with cert-manager solver route gives it all away but let’s break it down.
§why httpsRedirect: true cannot work
- The original request from cert-manager self-check arrives on port 80.
- Envoy immediately redirects it to HTTPS with the HTTP 301 status code.
- cert-manager follows the redirect. There are two possibilities:
- The ClusterIssuer uses the cert-manager.io/issue-temporary-certificate: true annotation.
- Because a temporary certificate exists, the Istio Gateway accepts a HTTPS request and Envoy doesn’t trip over as the redirected request was HTTPS.
- The request enters the mesh via the Gateway port 443 and is dealt with inside of the route with the decorator.operation: some-backend-service.test.svc.cluster.local:3000/*.
- It is forwarded to the backend service but backend service doesn’t known anything about /.well-known/acme-challenge/… path, so returns HTTP 404.
- The ClusterIssuer doesn’t use a temporary certificate.
- The request enters the mesh via the Gateway port 443 and is dealt with inside of the route with the decorator.operation: some-backend-service.test.svc.cluster.local:3000/*.
- There’s no certificate under the secret configured for the Gateway, Istio closes the connection.
- cert-manager HTTP client receives an EOF error.
- The ClusterIssuer uses the cert-manager.io/issue-temporary-certificate: true annotation.
§why httpsRedirect with an explicit route redirect works
This is really obvious in its non-obviousness:
- The route for the cert-manager solver sits before other routes defined by the VirtualService.
- There is no redirect, the handling of the solver request happens before VirtualService.
§verify the final conclusion
To verify the final conclusion, I can do the following:
- While cert-manager continues failing the self-check:
- Enable trace logging on the Istio ingressgateway pod.
- Disable httpsRedirect.
- Observe the log.
§enable trace logging on istio ingressgateway
|
|
§observe the log
|
|
There’s a lot of output so I need to be fast. Among other messages, I can observe:
2023-07-08T18:43:59.970913Z trace envoy http [C8] parsed 75 bytes
2023-07-08T18:44:00.372329Z trace envoy http [C144420] parsing 264 bytes
2023-07-08T18:44:00.372364Z trace envoy http [C144420] message begin
2023-07-08T18:44:00.372372Z debug envoy http [C144420] new stream
2023-07-08T18:44:00.372392Z trace envoy http [C144420] completed header: key=Host value=investigation.svcs.sh
2023-07-08T18:44:00.372402Z trace envoy http [C144420] completed header: key=User-Agent value=cert-manager-challenges/v1.11.0 (linux/amd64) cert-manager/2a0ef53b06e183356d922cd58af2510d8885bef5
2023-07-08T18:44:00.372408Z trace envoy http [C144420] completed header: key=Accept-Encoding value=gzip
2023-07-08T18:44:00.372415Z trace envoy http [C144420] onHeadersCompleteBase
2023-07-08T18:44:00.372456Z trace envoy http [C144420] completed header: key=Connection value=close
2023-07-08T18:44:00.372464Z trace envoy http [C144420] Server: onHeadersComplete size=4
2023-07-08T18:44:00.372475Z trace envoy http [C144420] message complete
2023-07-08T18:44:00.372488Z debug envoy http [C144420][S18069518382980252164] request headers complete (end_stream=true):
':authority', 'investigation.svcs.sh'
':path', '/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0'
':method', 'GET'
'user-agent', 'cert-manager-challenges/v1.11.0 (linux/amd64) cert-manager/2a0ef53b06e183356d922cd58af2510d8885bef5'
'accept-encoding', 'gzip'
'connection', 'close'
2023-07-08T18:44:00.372497Z debug envoy http [C144420][S18069518382980252164] request end stream
2023-07-08T18:44:00.372602Z trace envoy http [C144420][S18069518382980252164] decode headers called: filter=istio.metadata_exchange status=0
2023-07-08T18:44:00.372618Z trace envoy http [C144420][S18069518382980252164] decode headers called: filter=istio.alpn status=0
2023-07-08T18:44:00.372623Z trace envoy http [C144420][S18069518382980252164] decode headers called: filter=envoy.filters.http.fault status=0
2023-07-08T18:44:00.372626Z trace envoy http [C144420][S18069518382980252164] decode headers called: filter=envoy.filters.http.cors status=0
2023-07-08T18:44:00.372634Z trace envoy http [C144420][S18069518382980252164] decode headers called: filter=istio.stats status=0
2023-07-08T18:44:00.372642Z debug envoy http [C144420][S18069518382980252164] Sending local reply with details direct_response
2023-07-08T18:44:00.372672Z trace envoy http [C144420][S18069518382980252164] encode headers called: filter=istio.stats status=0
2023-07-08T18:44:00.372678Z trace envoy http [C144420][S18069518382980252164] encode headers called: filter=envoy.filters.http.cors status=0
2023-07-08T18:44:00.372681Z trace envoy http [C144420][S18069518382980252164] encode headers called: filter=envoy.filters.http.fault status=0
2023-07-08T18:44:00.372687Z trace envoy http [C144420][S18069518382980252164] encode headers called: filter=istio.metadata_exchange status=0
2023-07-08T18:44:00.372696Z debug envoy http [C144420][S18069518382980252164] closing connection due to connection close header
2023-07-08T18:44:00.372708Z debug envoy http [C144420][S18069518382980252164] encoding headers via codec (end_stream=true):
':status', '301'
'location', 'https://investigation.svcs.sh/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0'
'date', 'Sat, 08 Jul 2023 17:44:00 GMT'
'server', 'istio-envoy'
'connection', 'close'
This is coming from the httpsRedirect: true setting.
§disable the httpsRedirect
Change the httpsRedirect to false and watch the log closely. cert-manager doesn’t waste time. The following happens almost immediately:
2023-07-08T18:44:09.969969Z trace envoy http [C6] parsed 75 bytes
2023-07-08T18:44:10.771798Z trace envoy http [C144427] parsing 264 bytes
2023-07-08T18:44:10.771840Z trace envoy http [C144427] message begin
2023-07-08T18:44:10.771849Z debug envoy http [C144427] new stream
2023-07-08T18:44:10.771876Z trace envoy http [C144427] completed header: key=Host value=investigation.svcs.sh
2023-07-08T18:44:10.771888Z trace envoy http [C144427] completed header: key=User-Agent value=cert-manager-challenges/v1.11.0 (linux/amd64) cert-manager/2a0ef53b06e183356d922cd58af2510d8885bef5
2023-07-08T18:44:10.771896Z trace envoy http [C144427] completed header: key=Accept-Encoding value=gzip
2023-07-08T18:44:10.771905Z trace envoy http [C144427] onHeadersCompleteBase
2023-07-08T18:44:10.771908Z trace envoy http [C144427] completed header: key=Connection value=close
2023-07-08T18:44:10.771917Z trace envoy http [C144427] Server: onHeadersComplete size=4
2023-07-08T18:44:10.771930Z trace envoy http [C144427] message complete
2023-07-08T18:44:10.771945Z debug envoy http [C144427][S18087745377321120087] request headers complete (end_stream=true):
':authority', 'investigation.svcs.sh'
':path', '/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0'
':method', 'GET'
'user-agent', 'cert-manager-challenges/v1.11.0 (linux/amd64) cert-manager/2a0ef53b06e183356d922cd58af2510d8885bef5'
'accept-encoding', 'gzip'
'connection', 'close'
2023-07-08T18:44:10.771965Z debug envoy http [C144427][S18087745377321120087] request end stream
2023-07-08T18:44:10.772238Z trace envoy http [C144427][S18087745377321120087] decode headers called: filter=istio.metadata_exchange status=0
2023-07-08T18:44:10.772273Z trace envoy http [C144427][S18087745377321120087] decode headers called: filter=istio.alpn status=0
2023-07-08T18:44:10.772313Z trace envoy http [C144427][S18087745377321120087] decode headers called: filter=envoy.filters.http.fault status=0
2023-07-08T18:44:10.772336Z trace envoy http [C144427][S18087745377321120087] decode headers called: filter=envoy.filters.http.cors status=0
2023-07-08T18:44:10.772350Z trace envoy http [C144427][S18087745377321120087] decode headers called: filter=istio.stats status=0
2023-07-08T18:44:10.772969Z trace envoy http [C144427][S18087745377321120087] decode headers called: filter=envoy.filters.http.upstream_codec status=4
2023-07-08T18:44:10.773192Z trace envoy http [C144427][S18087745377321120087] decode headers called: filter=envoy.filters.http.router status=1
2023-07-08T18:44:10.773377Z trace envoy http [C144427] parsed 264 bytes
2023-07-08T18:44:10.773609Z trace envoy http [C144427][S18087745377321120087] continuing filter chain: filter=0x55d94218c000
2023-07-08T18:44:10.778127Z trace envoy http [C144428] parsing 256 bytes
2023-07-08T18:44:10.778160Z trace envoy http [C144428] message begin
2023-07-08T18:44:10.778180Z trace envoy http [C144428] completed header: key=Cache-Control value=no-cache, no-store, must-revalidate
2023-07-08T18:44:10.778193Z trace envoy http [C144428] completed header: key=Date value=Sat, 08 Jul 2023 17:44:10 GMT
2023-07-08T18:44:10.778200Z trace envoy http [C144428] completed header: key=Content-Length value=87
2023-07-08T18:44:10.778208Z trace envoy http [C144428] onHeadersCompleteBase
2023-07-08T18:44:10.778212Z trace envoy http [C144428] completed header: key=Content-Type value=text/plain; charset=utf-8
2023-07-08T18:44:10.778221Z trace envoy http [C144428] status_code 200
2023-07-08T18:44:10.778225Z trace envoy http [C144428] Client: onHeadersComplete size=4
2023-07-08T18:44:10.778331Z trace envoy http [C144427][S18087745377321120087] encode headers called: filter=istio.stats status=0
2023-07-08T18:44:10.778389Z trace envoy http [C144427][S18087745377321120087] encode headers called: filter=envoy.filters.http.cors status=0
2023-07-08T18:44:10.778423Z trace envoy http [C144427][S18087745377321120087] encode headers called: filter=envoy.filters.http.fault status=0
2023-07-08T18:44:10.778494Z trace envoy http [C144427][S18087745377321120087] encode headers called: filter=istio.metadata_exchange status=0
2023-07-08T18:44:10.778513Z debug envoy http [C144427][S18087745377321120087] closing connection due to connection close header
2023-07-08T18:44:10.778533Z debug envoy http [C144427][S18087745377321120087] encoding headers via codec (end_stream=false):
':status', '200'
'cache-control', 'no-cache, no-store, must-revalidate'
'date', 'Sat, 08 Jul 2023 17:44:10 GMT'
'content-length', '87'
'content-type', 'text/plain; charset=utf-8'
'x-envoy-upstream-service-time', '5'
'server', 'istio-envoy'
'connection', 'close'
followed by a bunch of requests similar to:
2023-07-08T18:44:11.968591Z trace envoy http [C8] parsed 75 bytes
2023-07-08T18:44:13.019511Z trace envoy http [C144430] parsing 264 bytes
2023-07-08T18:44:13.019601Z trace envoy http [C144430] message begin
2023-07-08T18:44:13.019611Z debug envoy http [C144430] new stream
2023-07-08T18:44:13.019637Z trace envoy http [C144430] completed header: key=Host value=investigation.svcs.sh
2023-07-08T18:44:13.019649Z trace envoy http [C144430] completed header: key=User-Agent value=cert-manager-challenges/v1.11.0 (linux/amd64) cert-manager/2a0ef53b06e183356d922cd58af2510d8885bef5
2023-07-08T18:44:13.019658Z trace envoy http [C144430] completed header: key=Accept-Encoding value=gzip
2023-07-08T18:44:13.019667Z trace envoy http [C144430] onHeadersCompleteBase
2023-07-08T18:44:13.019670Z trace envoy http [C144430] completed header: key=Connection value=close
2023-07-08T18:44:13.019680Z trace envoy http [C144430] Server: onHeadersComplete size=4
2023-07-08T18:44:13.019694Z trace envoy http [C144430] message complete
2023-07-08T18:44:13.019710Z debug envoy http [C144430][S11783251202062423115] request headers complete (end_stream=true):
':authority', 'investigation.svcs.sh'
':path', '/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0'
':method', 'GET'
'user-agent', 'cert-manager-challenges/v1.11.0 (linux/amd64) cert-manager/2a0ef53b06e183356d922cd58af2510d8885bef5'
'accept-encoding', 'gzip'
'connection', 'close'
2023-07-08T18:44:13.019723Z debug envoy http [C144430][S11783251202062423115] request end stream
2023-07-08T18:44:13.019858Z trace envoy http [C144430][S11783251202062423115] decode headers called: filter=istio.metadata_exchange status=0
2023-07-08T18:44:13.019880Z trace envoy http [C144430][S11783251202062423115] decode headers called: filter=istio.alpn status=0
2023-07-08T18:44:13.019887Z trace envoy http [C144430][S11783251202062423115] decode headers called: filter=envoy.filters.http.fault status=0
2023-07-08T18:44:13.019893Z trace envoy http [C144430][S11783251202062423115] decode headers called: filter=envoy.filters.http.cors status=0
2023-07-08T18:44:13.019903Z trace envoy http [C144430][S11783251202062423115] decode headers called: filter=istio.stats status=0
2023-07-08T18:44:13.020260Z trace envoy http [C144430][S11783251202062423115] decode headers called: filter=envoy.filters.http.upstream_codec status=4
2023-07-08T18:44:13.020367Z trace envoy http [C144430][S11783251202062423115] decode headers called: filter=envoy.filters.http.router status=1
2023-07-08T18:44:13.020400Z trace envoy http [C144430] parsed 264 bytes
2023-07-08T18:44:13.020544Z trace envoy http [C144430][S11783251202062423115] continuing filter chain: filter=0x55d941dc5710
2023-07-08T18:44:13.023215Z trace envoy http [C144431] parsing 256 bytes
2023-07-08T18:44:13.023309Z trace envoy http [C144431] message begin
2023-07-08T18:44:13.023360Z trace envoy http [C144431] completed header: key=Cache-Control value=no-cache, no-store, must-revalidate
2023-07-08T18:44:13.023385Z trace envoy http [C144431] completed header: key=Date value=Sat, 08 Jul 2023 17:44:13 GMT
2023-07-08T18:44:13.023403Z trace envoy http [C144431] completed header: key=Content-Length value=87
2023-07-08T18:44:13.023445Z trace envoy http [C144431] onHeadersCompleteBase
2023-07-08T18:44:13.023464Z trace envoy http [C144431] completed header: key=Content-Type value=text/plain; charset=utf-8
2023-07-08T18:44:13.023487Z trace envoy http [C144431] status_code 200
2023-07-08T18:44:13.023569Z trace envoy http [C144431] Client: onHeadersComplete size=4
2023-07-08T18:44:13.023705Z trace envoy http [C144430][S11783251202062423115] encode headers called: filter=istio.stats status=0
2023-07-08T18:44:13.023770Z trace envoy http [C144430][S11783251202062423115] encode headers called: filter=envoy.filters.http.cors status=0
2023-07-08T18:44:13.023791Z trace envoy http [C144430][S11783251202062423115] encode headers called: filter=envoy.filters.http.fault status=0
2023-07-08T18:44:13.023817Z trace envoy http [C144430][S11783251202062423115] encode headers called: filter=istio.metadata_exchange status=0
2023-07-08T18:44:13.023861Z debug envoy http [C144430][S11783251202062423115] closing connection due to connection close header
2023-07-08T18:44:13.023898Z debug envoy http [C144430][S11783251202062423115] encoding headers via codec (end_stream=false):
':status', '200'
'cache-control', 'no-cache, no-store, must-revalidate'
'date', 'Sat, 08 Jul 2023 17:44:13 GMT'
'content-length', '87'
'content-type', 'text/plain; charset=utf-8'
'x-envoy-upstream-service-time', '3'
'server', 'istio-envoy'
'connection', 'close'
and another bunch of requests like this one:
2023-07-08T18:44:21.968492Z trace envoy http [C3] parsed 75 bytes
2023-07-08T18:44:22.749799Z trace envoy http [C144440] parsing 265 bytes
2023-07-08T18:44:22.749841Z trace envoy http [C144440] message begin
2023-07-08T18:44:22.749850Z debug envoy http [C144440] new stream
2023-07-08T18:44:22.749875Z trace envoy http [C144440] completed header: key=Host value=investigation.svcs.sh
2023-07-08T18:44:22.749884Z trace envoy http [C144440] completed header: key=User-Agent value=Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)
2023-07-08T18:44:22.749890Z trace envoy http [C144440] completed header: key=Accept value=*/*
2023-07-08T18:44:22.749898Z trace envoy http [C144440] completed header: key=Accept-Encoding value=gzip
2023-07-08T18:44:22.749904Z trace envoy http [C144440] onHeadersCompleteBase
2023-07-08T18:44:22.749907Z trace envoy http [C144440] completed header: key=Connection value=close
2023-07-08T18:44:22.749914Z trace envoy http [C144440] Server: onHeadersComplete size=5
2023-07-08T18:44:22.749925Z trace envoy http [C144440] message complete
2023-07-08T18:44:22.749939Z debug envoy http [C144440][S3714970174883588932] request headers complete (end_stream=true):
':authority', 'investigation.svcs.sh'
':path', '/.well-known/acme-challenge/bgLcgdeOvQ4nbDaD4wENG-qR76dxR8GkZKe2Wp1msX0'
':method', 'GET'
'user-agent', 'Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)'
'accept', '*/*'
'accept-encoding', 'gzip'
'connection', 'close'
2023-07-08T18:44:22.749956Z debug envoy http [C144440][S3714970174883588932] request end stream
2023-07-08T18:44:22.750082Z trace envoy http [C144440][S3714970174883588932] decode headers called: filter=istio.metadata_exchange status=0
2023-07-08T18:44:22.750731Z trace envoy http [C144440][S3714970174883588932] decode headers called: filter=istio.alpn status=0
2023-07-08T18:44:22.750789Z trace envoy http [C144440][S3714970174883588932] decode headers called: filter=envoy.filters.http.fault status=0
2023-07-08T18:44:22.750842Z trace envoy http [C144440][S3714970174883588932] decode headers called: filter=envoy.filters.http.cors status=0
2023-07-08T18:44:22.750865Z trace envoy http [C144440][S3714970174883588932] decode headers called: filter=istio.stats status=0
2023-07-08T18:44:22.751403Z trace envoy http [C144440][S3714970174883588932] decode headers called: filter=envoy.filters.http.upstream_codec status=4
2023-07-08T18:44:22.751436Z trace envoy http [C144440][S3714970174883588932] decode headers called: filter=envoy.filters.http.router status=1
2023-07-08T18:44:22.751443Z trace envoy http [C144440] parsed 265 bytes
2023-07-08T18:44:22.751507Z trace envoy http [C144440][S3714970174883588932] continuing filter chain: filter=0x55d941e44900
2023-07-08T18:44:22.754621Z trace envoy http [C144441] parsing 256 bytes
2023-07-08T18:44:22.754641Z trace envoy http [C144441] message begin
2023-07-08T18:44:22.754656Z trace envoy http [C144441] completed header: key=Cache-Control value=no-cache, no-store, must-revalidate
2023-07-08T18:44:22.754666Z trace envoy http [C144441] completed header: key=Date value=Sat, 08 Jul 2023 17:44:22 GMT
2023-07-08T18:44:22.754671Z trace envoy http [C144441] completed header: key=Content-Length value=87
2023-07-08T18:44:22.754710Z trace envoy http [C144441] onHeadersCompleteBase
2023-07-08T18:44:22.754720Z trace envoy http [C144441] completed header: key=Content-Type value=text/plain; charset=utf-8
2023-07-08T18:44:22.754731Z trace envoy http [C144441] status_code 200
2023-07-08T18:44:22.754734Z trace envoy http [C144441] Client: onHeadersComplete size=4
2023-07-08T18:44:22.754827Z trace envoy http [C144440][S3714970174883588932] encode headers called: filter=istio.stats status=0
2023-07-08T18:44:22.754935Z trace envoy http [C144440][S3714970174883588932] encode headers called: filter=envoy.filters.http.cors status=0
2023-07-08T18:44:22.755045Z trace envoy http [C144440][S3714970174883588932] encode headers called: filter=envoy.filters.http.fault status=0
2023-07-08T18:44:22.755092Z trace envoy http [C144440][S3714970174883588932] encode headers called: filter=istio.metadata_exchange status=0
2023-07-08T18:44:22.755185Z debug envoy http [C144440][S3714970174883588932] closing connection due to connection close header
2023-07-08T18:44:22.755289Z debug envoy http [C144440][S3714970174883588932] encoding headers via codec (end_stream=false):
':status', '200'
'cache-control', 'no-cache, no-store, must-revalidate'
'date', 'Sat, 08 Jul 2023 17:44:22 GMT'
'content-length', '87'
'content-type', 'text/plain; charset=utf-8'
'x-envoy-upstream-service-time', '3'
'server', 'istio-envoy'
'connection', 'close'
Let’s Encrypt talks over port 80. We just observed the solving of the challenge, the certificate has been issued.
§final conclusion
- Keep the httpRedirect set to false in the Gateway.
- Use a match.uri.prefix with scheme.exact: http and redirect.scheme: https to handle the redirect selectively in a VirtualService.
That’s the best way to handle catch-all HTTPS redirect and I was able to fnd an explanation for it.