Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[alpha] Create mirror QUIC listeners for HTTPS servers on gateways #33817

Merged
merged 7 commits into from
Sep 1, 2021

Conversation

su225
Copy link
Contributor

@su225 su225 commented Jul 3, 2021

This PR demonstrates using QUIC in Istio. This is an experimental implementation where I have added a feature which creates mirror listener for an HTTPS listener at the gateway. At the moment, it is not ideal and there are many issues and caveats. I hope to iterate and refine it based on the feedback (relevant comment over the direction - #23018 (comment))

Caveats, open questions, tasks to-do
This is experimental even in upstream Envoy.

  • Come up with a cleaner approach for this - maybe we can support QUIC protocol on the gateway for the users instead of doing shady things like this? API stuffs are not yet figured out - Running H3/QUIC and H2/TLS is the common case now. So nothing shady here.
  • Unit and integration testing these changes
  • ALPN is not set properly. In envoy configs, it shows h2 and http 1.1 as it is just copied over from the filter chain config generated for TCP
  • Refactor and clean up repetitive code (make it nicer)
  • If the user wants both TCP+HTTP2 and UDP/QUIC+HTTP3 on the same port, then Kubernetes does not allow having two entries with same service->target port (even when protocols are different) for service of type LoadBalancer. (tested this in Kubernetes 1.21) - It does. In K8s >= 1.20, turn on MixedProtocolLBService feature gate.
  • If we have to create UDP listener to a different port specified by the user, then all things like authn, authz policies including ports should be translated as well - Not doing this.

Reference on generating QUIC config - https://github.com/envoyproxy/envoy/blob/main/configs/envoyproxy_io_proxy_http3_downstream.yaml

Istio doc - https://docs.google.com/document/d/1Yqx_tCpMlCRDyBqHTyAZfJhjYF9B2BYHyXkHE2xXCho/edit#

[ ] Configuration Infrastructure
[ ] Docs
[ ] Installation
[X] Networking
[ ] Performance and Scalability
[ ] Policies and Telemetry
[ ] Security
[ ] Test and Release
[ ] User Experience
[ ] Developer Infrastructure

@su225 su225 requested review from a team as code owners July 3, 2021 16:08
@istio-testing istio-testing added the do-not-merge/work-in-progress Block merging of a PR because it isn't ready yet. label Jul 3, 2021
@google-cla google-cla bot added the cla: yes Set by the Google CLA bot to indicate the author of a PR has signed the Google CLA. label Jul 3, 2021
@istio-testing istio-testing added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jul 3, 2021
@su225
Copy link
Contributor Author

su225 commented Jul 3, 2021

Experiment
Installed IstioOperator with the following settings on the latest main build of Istio (1.11-dev)

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-with-quic
  namespace: istio-system
spec:
  meshConfig:
    accessLogFile: /dev/stdout
  hub: localhost:5000
  tag: istio-quic
  components:
    ingressGateways:
    - name: bookinfo-ingress
      namespace: bookinfo
      enabled: true
      label:
        gateway: bookinfo
        quic: enabled
      k8s:
        service:
          type: NodePort
          ports:
          - name: https-bookinfo
            port: 443
            targetPort: 8443
            protocol: TCP

          # Same service and target port with
          # the only difference being the protocol
          - name: http-quic-bookinfo
            port: 8443
            targetPort: 8443
            protocol: UDP
  values:
    global:
      imagePullPolicy: Always
      logging:
        level: "default:debug"
    pilot:
      env:
        PILOT_ENABLE_QUIC_LISTENERS: "true"

Then deployed the bookinfo application and setup routing (notice that there is no QUIC related stuffs in configs. QUIC mirror listener happens behind the scenes

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: bookinfo-gw
  namespace: bookinfo
spec:
  selector:
    gateway: bookinfo
    quic: enabled
  servers:
  - port:
      number: 443
      protocol: HTTPS
      name: https-bookinfo
    hosts:
    - bookinfo.com
    tls:
      mode: SIMPLE
      credentialName: bookinfo-secret
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo-vs
  namespace: bookinfo
spec:
  hosts:
  - bookinfo.com
  gateways:
  - bookinfo-gw
  http:
  - name: productpage-route
    route:
    - destination:
        host: productpage.bookinfo.svc.cluster.local
        port: { number: 9080 }

Finally test it with QUIC enabled cURL (it is not enabled by default. Instructions here - https://github.com/curl/curl/blob/master/docs/HTTP3.md). In my computer it is called qcurl which is built from main tree instead of release one.

$ qcurl --resolve bookinfo.com:32409:172.18.0.2 -svk -H'Host: bookinfo.com' -o /dev/null --http3 https://bookinfo.com:32409
* Added bookinfo.com:32409:172.18.0.2 to DNS cache
* Hostname bookinfo.com was found in DNS cache
*   Trying 172.18.0.2:32409...
* Connect socket 5 over QUIC to 172.18.0.2:32409
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* Connected to bookinfo.com () port 32409 (#0)
* h3 [:method: GET]
* h3 [:path: /]
* h3 [:scheme: https]
* h3 [:authority: bookinfo.com]
* h3 [user-agent: curl/7.78.0-DEV]
* h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0x17e12e0)
> GET / HTTP/3
> Host: bookinfo.com
> user-agent: curl/7.78.0-DEV
> accept: */*
> 
< HTTP/3 200
< content-type: text/html; charset=utf-8
< content-length: 1683
< server: istio-envoy
< date: Sat, 03 Jul 2021 15:41:18 GMT
< x-envoy-upstream-service-time: 7
< 

And Eureka!

Access logs from the gateway

[2021-07-03T15:36:26.271Z] "GET / HTTP/3" 200 - via_upstream - "-" 0 1683 22 21 "10.244.0.1" "curl/7.78.0-DEV" "99c3438c-1a1d-490d-b4ee-1551575c24a1" "bookinfo.com" "10.244.0.16:9080" outbound|9080||productpage.bookinfo.svc.cluster.local 10.244.0.29:55838 10.244.0.29:8443 10.244.0.1:29394 bookinfo.com productpage-route
[2021-07-03T15:36:37.365Z] "GET / HTTP/3" 200 - via_upstream - "-" 0 1683 9 7 "10.244.0.1" "curl/7.78.0-DEV" "410c1c4f-e5ca-4791-b55c-0a8be70c3509" "bookinfo.com" "10.244.0.16:9080" outbound|9080||productpage.bookinfo.svc.cluster.local 10.244.0.29:55840 10.244.0.29:8443 10.244.0.1:34725 bookinfo.com productpage-route
[2021-07-03T15:41:18.330Z] "GET / HTTP/3" 200 - via_upstream - "-" 0 1683 9 7 "10.244.0.1" "curl/7.78.0-DEV" "d89e444b-a33c-44d8-99ab-0e170e11a6f3" "bookinfo.com" "10.244.0.16:9080" outbound|9080||productpage.bookinfo.svc.cluster.local 10.244.0.29:55844 10.244.0.29:8443 10.244.0.1:57109 bookinfo.com productpage-route

Then I wanted to make sure that it is indeed sending UDP packets (it should). So tested with tcpdump (br-9dea9a86894f is the bridge to which my kind cluster is connected. So any packet entering the cluster is routed through this. Confirmed through ip route get command). Indeed there is some encrypted gibberish

$ sudo tcpdump -i br-9dea9a86894f udp -vv -X
21:11:18.318757 IP (tos 0x0, ttl 64, id 11170, offset 0, flags [DF], proto UDP (17), length 1228)
    su225.36820 > 172.18.0.2.32409: [bad udp cksum 0x5cf1 -> 0xe58c!] UDP, length 1200
	0x0000:  4500 04cc 2ba2 4000 4011 b257 ac12 0001  E...+.@.@..W....
	0x0010:  ac12 0002 8fd4 7e99 04b8 5cf1 cb00 0000  ......~...\.....
	0x0020:  0110 97a0 23b4 2892 c421 8857 01ca a61c  ....#.(..!.W....
	0x0030:  bf8a 1439 b96a a2fd eef7 f37f 6429 94d7  ...9.j......d)..
	0x0040:  5590 ac3e 3910 a700 4123 a926 b9ec 82fe  U..>9...A#.&....
	0x0050:  b0bd f4c1 945f 05ca 51c2 051a 1cd3 1d07  ....._..Q.......

Corresponding filter chain config in Envoy (it is long. So I have omitted many details. With the experiment you can inspect with istioctl proxy-config listeners command)

- address:
    socketAddress:
      address: 0.0.0.0
      portValue: 8443
      protocol: UDP
  filterChains:
  - filterChainMatch:
      serverNames:
      - bookinfo.com
    filters:
    - name: envoy.filters.network.http_connection_manager
      typedConfig:
        '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
        codecType: HTTP3
        http3ProtocolOptions: {}
        httpFilters:
        - name: istio.metadata_exchange
        # Rest of HTTP filters go here
        - name: envoy.filters.http.router
          typedConfig:
            '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

    transportSocket:
      name: envoy.transport_sockets.quic
      typedConfig:
        '@type': type.googleapis.com/envoy.extensions.transport_sockets.quic.v3.QuicDownstreamTransport
        downstreamTlsContext:
          commonTlsContext:
            alpnProtocols:
            - h2
            - http/1.1
            tlsCertificateSdsSecretConfigs:
            - name: kubernetes://bookinfo-secret
              sdsConfig:
                ads: {}
                resourceApiVersion: V3
          requireClientCertificate: false
  name: quic_0.0.0.0_8443
  reusePort: true
  trafficDirection: OUTBOUND
  udpListenerConfig:
    quicOptions: {}

@howardjohn
Copy link
Member

Nice! I haven't had a chance to review this yet but wanted to point you to https://github.com/howardjohn/istio/tree/pilot/http3-gateway where I have done similar experiments

@su225
Copy link
Contributor Author

su225 commented Jul 4, 2021

Thanks @howardjohn - Your implementation is much simpler and understandable :) Do you mind if I borrow from your implementation and continue from there?

With some modification, it would be possible to support HTTP3 only ports, although the dominant use case is running both TCP+TLS and QUIC on the same port. Plus, I want to fix some stuffs like proper ALPN (h3 instead of h2 although from my yesterday's experiment it looked fine).

However, some changes though (not major ones) - I wouldn't call them UDPServer although they are on UDP ports, but instead QUICServer. QUIC is NOT UDP although it uses it - one of the main reasons being not to scare middleboxes according to the original paper. It is a separate transport protocol than TCP and UDP (it looks closer than TCP in the sense that it provides some order at stream level, then flow and congestion control etc). I initially thought of including UDP, but that would be more complex and so reduced the scope to QUIC at gateways. (Hence the branch name).

From RFC 9000

QUIC is a connection-oriented protocol that creates a stateful interaction between a client and server.

There seem to be some problems at Kubernetes layer though. Looks like it does not allow both TCP and UDP for the same port when service type is LoadBalancer (but NodePort works though). I just tried an experiment with the following spec and it fails

The Service "nginx-https" is invalid: spec.ports: Invalid value: []core.ServicePort{core.ServicePort{Name:"https-tcp", Protocol:"TCP", AppProtocol:(*string)(nil), Port:443, TargetPort:intstr.IntOrString{Type:0, IntVal:8443, StrVal:""}, NodePort:0}, core.ServicePort{Name:"https-quic", Protocol:"UDP", AppProtocol:(*string)(nil), Port:443, TargetPort:intstr.IntOrString{Type:0, IntVal:8443, StrVal:""}, NodePort:0}}: may not contain more than 1 protocol when type is 'LoadBalancer'

Service YAML

apiVersion: v1
kind: Service
metadata:
  name: nginx-https
  namespace: test
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
  - name: https-tcp
    port: 443
    targetPort: 8443
    protocol: TCP
  - name: https-quic
    port: 443
    targetPort: 8443
    protocol: UDP

But it accepts same port, different protocol in Deployment spec though. So I have raised kubernetes/kubernetes#103464

@su225
Copy link
Contributor Author

su225 commented Jul 5, 2021

UPDATE: Turns out that Kubernetes has an alpha feature gate called MixedProtocolLBService to support the use cases mentioned. But then there is one more bug in Istio operator. So I opened - #33841 . Until then I would be using NodePort (not ideal. But fixing that bug + MixedProtocolLBService = ideal for QUIC)

@istio-testing istio-testing added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 10, 2021
@su225
Copy link
Contributor Author

su225 commented Jul 10, 2021

@howardjohn - I re-started with your code, fixed some issues and refactored the code. I also added some basic unit tests. I observed that Envoy is buggy for QUIC and not all settings worked. In fact, they mention that it is still alpha. So this definitely needs integration tests. Unfortunately, this is blocked by #33930 . For my experiments, I built istioctl from the branch containing the fix (#33930) and using it and here are the results

Notice that there are no NodePort hacks this time. It is the service of type LoadBalancer with TCP and UDP exposed on 443. I'm also thinking of adding alt-svc header for the response if QUIC is enabled on that port instead of user having to configure VirtualService for it.

For QUIC on port 443

$ qcurl -sv --cacert bookinfo.crt -o /dev/null --resolve bookinfo.com:443:172.18.210.1 --http3 https://bookinfo.com
* Added bookinfo.com:443:172.18.210.1 to DNS cache
* Hostname bookinfo.com was found in DNS cache
*   Trying 172.18.210.1:443...
* Connect socket 5 over QUIC to 172.18.210.1:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* Connected to bookinfo.com () port 443 (#0)
* h3 [:method: GET]
* h3 [:path: /]
* h3 [:scheme: https]
* h3 [:authority: bookinfo.com]
* h3 [user-agent: curl/7.78.0-DEV]
* h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0x690290)
> GET / HTTP/3
> Host: bookinfo.com
> user-agent: curl/7.78.0-DEV
> accept: */*
> 
< HTTP/3 200
< content-type: text/html; charset=utf-8
< content-length: 1683
< server: istio-envoy
< date: Sat, 10 Jul 2021 17:48:03 GMT
< x-envoy-upstream-service-time: 8

For HTTP-over-TCP on port 443

* Added bookinfo.com:443:172.18.210.1 to DNS cache
* Hostname bookinfo.com was found in DNS cache
*   Trying 172.18.210.1:443...
* Connected to bookinfo.com (172.18.210.1) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: bookinfo.crt
*  CApath: none
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
...... Rest of TLS handshake omitted
.......
.......
< HTTP/1.1 200 OK
< content-type: text/html; charset=utf-8
< content-length: 1683
< server: istio-envoy
< date: Sat, 10 Jul 2021 17:49:24 GMT
< x-envoy-upstream-service-time: 3
< 

@su225
Copy link
Contributor Author

su225 commented Jul 10, 2021

Oh! I just noticed they have an issue already - envoyproxy/envoy#15953

@su225
Copy link
Contributor Author

su225 commented Jul 14, 2021

UPDATE: When HTTPS over TCP is used, it sends alt-svc header for the same port (currently hard-coded to the same port and constant max age of 1 day). The port is service port 443 (where pod is listening on 8443) as the client expects

[istio-quic]$ qcurl -o /dev/null -sv --cacert bookinfo-ca.crt --resolve bookinfo.com:443:172.18.210.1 https://bookinfo.com
#.... Lots of verbose stuffs
#
#
< HTTP/1.1 200 OK
< content-type: text/html; charset=utf-8
< content-length: 1683
< server: istio-envoy
< date: Wed, 14 Jul 2021 17:53:45 GMT
< x-envoy-upstream-service-time: 26
< alt-svc: h3=":443"; ma=86400; h3-29=":443"; ma=86400

Trying the same with H3/QUIC

[istio-quic]$ qcurl -o /dev/null -sv --cacert bookinfo-ca.crt --resolve bookinfo.com:443:172.18.210.1 --http3 https://bookinfo.com
* Added bookinfo.com:443:172.18.210.1 to DNS cache
* Hostname bookinfo.com was found in DNS cache
*   Trying 172.18.210.1:443...
* Connect socket 5 over QUIC to 172.18.210.1:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* Connected to bookinfo.com () port 443 (#0)
* h3 [:method: GET]
* h3 [:path: /]
* h3 [:scheme: https]
* h3 [:authority: bookinfo.com]
* h3 [user-agent: curl/7.78.0-DEV]
* h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0x1cdc290)
> GET / HTTP/3
> Host: bookinfo.com
> user-agent: curl/7.78.0-DEV
> accept: */*
> 
< HTTP/3 200
< content-type: text/html; charset=utf-8
< content-length: 1683
< server: istio-envoy
< date: Wed, 14 Jul 2021 17:54:39 GMT
< x-envoy-upstream-service-time: 8

@istio-testing istio-testing added the needs-rebase Indicates a PR needs to be rebased before being merged label Jul 20, 2021
@su225 su225 requested review from a team as code owners July 21, 2021 10:48
@istio-testing istio-testing removed the needs-rebase Indicates a PR needs to be rebased before being merged label Jul 21, 2021
@su225 su225 changed the title [WIP][alpha] Create mirror QUIC listeners for HTTPS servers on gateways [alpha] Create mirror QUIC listeners for HTTPS servers on gateways Jul 24, 2021
@istio-testing istio-testing removed the do-not-merge/work-in-progress Block merging of a PR because it isn't ready yet. label Jul 24, 2021
@su225 su225 requested a review from a team as a code owner July 28, 2021 08:09
pilot/pkg/model/gateway.go Outdated Show resolved Hide resolved
if features.EnableQUICListeners && gateway.IsEligibleForHTTP3Upgrade(s) &&
udpSupportedPort(s.GetPort().GetNumber(), gwAndInstance.instances) {
log.Debugf("Server at port %d eligible for HTTP3 upgrade. Add QUIC listener", serverPort.Number)
h3MirrorRouteName := "h3-mirror." + routeName
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why can we not use the same route? (I might answer myself after reading more)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh for alt-svc... 2x routes is fairly expensive. We may consider adding support directly to envoy (in HCM) to add this - I think there was high level support for that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we would like to reduce the number of routes, how about alt-svc in both cases? I get it - it is not needed when the client talks H3 and it is redundant. According to the RFC-7838 that is something the client can ignore.

From https://datatracker.ietf.org/doc/html/rfc7838#section-2.4

By their nature, alternative services are OPTIONAL: clients do not need to use them.

Therefore, if a client supporting this specification becomes aware of an alternative service, the client SHOULD use that alternative service for all requests to the associated origin as soon as it is available

Copy link
Contributor Author

@su225 su225 Aug 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed that cloudflare sends alt-svc even for H3. So I assume this shouldn't be a problem in general! (EDIT: So does google!)

server.GetPort().GetNumber(), server.GetPort().GetName(), server.GetPort().GetProtocol())
routeName := mergedGateway.TLSServerInfo[server].RouteName
if mergedGateway.TLSToQUICServerRouteMap[routeName] != "" {
routeName = mergedGateway.TLSToQUICServerRouteMap[routeName]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is mergedGateway.TLSToQUICServerRouteMap[routeName] ever "" here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally wrote something and this remained. I changed the name and type to set. It is the set of routes on which H3 has to be advertised (by setting alt-svc header)

// Not doing so is a security hole as would allow bypassing auth.
ListenerProtocol: istionetworking.ListenerProtocolHTTP,
TransportProtocol: istionetworking.TransportProtocolQUIC,
IstioMutualGateway: false, // Currently, we don't support this. Revisit later
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious, why not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simple answer - I didn't know much about that. I vaguely knew it is about mTLS. TBH searching for its usages, I still don't understand why it is there. So I set it to false and moved on so that I can focus on getting simple TLS to work.

case istionetworking.TransportProtocolTCP:
return bind + "_" + strconv.Itoa(port)
case istionetworking.TransportProtocolQUIC:
return "quic_" + bind + "_" + strconv.Itoa(port)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may consider here, and many other places replacing quic with udp. The rationale is that in the future we may add non-quic UDP and it would be on the same listener?

Unless quic + non-quic UDP on the same listener will never be supported by envoy

Copy link
Contributor Author

@su225 su225 Jul 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand it correctly RFC-9000 gives a hint on QUIC co-existing with other UDP based protocols on the same port.

Fixed Bit: The next bit (0x40) of byte 0 is set to 1. Packets
containing a zero value for this bit are not valid packets in this
version and MUST be discarded. A value of 1 for this bit allows
QUIC to coexist with other protocols; see [RFC7983].

So I agree with you that although QUIC is connection-oriented protocol, it is still based on UDP now and does not have its own format like TCP and UDP. So I changed it to udp_ as you suggested. I know that these names show up on istioctl proxy-config listeners. Question - Is this something that needs to be backwards-compatible?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question - Is this something that needs to be backwards-compatible?

No, but if you change it it would probably make upgrades not work because Envoy would complain it has two listeners on the same port. But aside from that users are not supposed to depend on XDS details

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I thought we send the entire state of the world on each LDS push - meaning, if the new update received does not have the name then envoy would clean up the listener. Hmm...That does not seem to be the case here? (I don't know the details of Envoy XDS implementation). Anyways, I changed it to udp_ as suggested.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do but envoy doesn't actually atomically apply them like that IIUC. So the new ones still can conflict with old ones

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! Ok. Learned something new :) I didn't know "does not atomically apply them" part. I guess even if it applies atomically, Envoy has to put old listeners to draining mode for deletion (socket still not closed at that time). For modification, simple way is delete+add - in this case we will run into port-conflict.

But still - while upgrading don't we restart pods for newer version of sidecar to be injected and it connects to newer revision of istiod. Due to restart, envoy should have forgotten configs and starts with clean slate. Right? I see that the problem occurs when someone upgrades it in-place (which I think is not recommended) and sidecar reconnects. Another possibility is somebody changing Istiod tag (due to say, hot fix) resulting in newer Istiod sending something (again - hot-fix should be avoided).

Anyways, I have changed it to udp_ as you suggested already. Still not fully convinced with "upgrades does not work" part. I have outlined scenarios that I could think of and yes - I agree that it would be a problem in case of in-case upgrades as for the sidecar it would be "same istiod address and reconnect" resulting in mixing up of envoy config state. But for canary upgrade scenario it should not cause issues as the pod would be restarted to point to the new revision.

tests/integration/security/sds_ingress/ingress_test.go Outdated Show resolved Hide resolved
prow/config/trustworthy-jwt.yaml Outdated Show resolved Hide resolved
@su225
Copy link
Contributor Author

su225 commented Jul 30, 2021

/test integ-security-multicluster-tests_istio

@istio-testing istio-testing added the needs-rebase Indicates a PR needs to be rebased before being merged label Aug 25, 2021
…nal traffic

Signed-off-by: su225 <suchithjn22@gmail.com>
@istio-testing istio-testing removed the needs-rebase Indicates a PR needs to be rebased before being merged label Aug 26, 2021
pilot/pkg/model/gateway.go Outdated Show resolved Hide resolved
pilot/pkg/model/gateway.go Outdated Show resolved Hide resolved
istionetworking.TransportProtocolQUIC: mergedGateway.MergedQUICTransportServers,
}

for transport, gwServers := range transportToServers {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it seems TransportProtocolTCP and TransportProtocolQUIC have no reusable code below, why not handle it separately

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will add difficulty to review, could not distinguish whether it is changed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much of the code - like building listener options, calling plugins to create Auth filters, code to generate listeners and filter chains are common to both. The difference is in building HTTP related filter chains. I have moved TCP and QUIC sections of the switch case to its own functions for clarity.

Copy link
Member

@hzxuzhonghu hzxuzhonghu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After first flance, the implement does not comply with the rfc, it does not add a HTTP3 protocol in gateway port?

pilot/pkg/networking/core/v1alpha3/listener.go Outdated Show resolved Hide resolved
@@ -536,9 +540,34 @@ func translateRoute(node *model.Proxy, in *networking.HTTPRoute,
out.TypedPerFilterConfig[wellknown.Fault] = util.MessageToAny(translateFault(in.Fault))
}

if isH3DiscoveryNeeded {
h3DiscoveryHeader := buildH3AltSvcHeader(port, util.ALPNHttp3OverQUIC)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it need add alt-svc? Doesn't envoy do it automatically?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if Envoy does that. I have to check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure, just a question here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest adding note to envoyproxy/envoy#15953

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a note about our usage.

pilot/pkg/networking/core/v1alpha3/route/route.go Outdated Show resolved Hide resolved
pilot/pkg/networking/core/v1alpha3/route/route.go Outdated Show resolved Hide resolved
@howardjohn
Copy link
Member

After first flance, the implement does not comply with the rfc

What RFC is it not complying?

it does not add a HTTP3 protocol in gateway port?

IMO its not needed. Just like you don't need to declare a port "HTTP2" for it to accept HTTP2 traffic at Gateway. If http3 is possible on the port (ie they have tls settings and UDP defined in service) we can transparently add it

@hzxuzhonghu
Copy link
Member

Is it safe when the client does not support http3? Is it a must the gateway will require http3 for the second request?

@hzxuzhonghu
Copy link
Member

@howardjohn I mean the proposal, it added HTTP3 there

@howardjohn
Copy link
Member

@howardjohn I mean the proposal, it added HTTP3 there

Ah, got it. Thought you meant IETF rfc. I agree it doesn't match that design but IMO this is better. Declaring "HTTP3" doesn't really make much sense IMO, since you almost never want just h3 - you want h1+h2+h3 all together

@hzxuzhonghu
Copy link
Member

make sense, it is safe AFAIK

@su225
Copy link
Contributor Author

su225 commented Aug 28, 2021

Is it safe when the client does not support http3? Is it a must the gateway will require http3 for the second request?

@hzxuzhonghu - Yes. It is safe. This creates a QUIC mirror listener for every HTTPS port on the gateway provided there is a UDP port opened on the gateway with the same number. (Like 443/TCP and 443/UDP). Existing listeners are not removed. With Alt-Svc, we tell the client that they can use HTTP/3, but it is not mandatory, but additional choices. They can continue to use HTTP/1.1 or HTTP/2 over TCP.

In integration tests, both TCP and QUIC are tested as part of the same test to make sure both of them work correctly.

Signed-off-by: su225 <suchithjn22@gmail.com>
@hzxuzhonghu
Copy link
Member

lgtm, @howardjohn @ramaraochavali to put more eyes on it.

Copy link
Member

@howardjohn howardjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! just a few nits

// We have TLS settings defined and we have already taken care of unique route names
// if it is HTTPS. So we can construct a QUIC server on the same port. It is okay as
// QUIC listens on UDP port, not TCP
if features.EnableQUICListeners && gateway.IsEligibleForHTTP3Upgrade(s) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: IsEligibleForHTTP3Upgrade already checks EnableQUICListeners, no need to replace

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I see it is redundant. Guess it is fine for readability?

pilot/pkg/features/pilot.go Outdated Show resolved Hide resolved
pilot/pkg/model/gateway.go Outdated Show resolved Hide resolved
if mergedQUICServers[serverPort] == nil {
mergedQUICServers[serverPort] = &MergedServers{
Servers: []*networking.Server{s},
RouteName: routeName,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if routeName changes between serverPort? Is it possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something I need to go through once again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible because different HTTPS servers would have different route names. I went through the code again and found that RouteName is unnecessary for HTTPS and HTTP/3 cases. This is because we take it from TLSServerInfo. The field is used only for plaintext HTTP. Hence I removed this in both places.

pilot/pkg/networking/core/v1alpha3/listener.go Outdated Show resolved Hide resolved
@@ -536,9 +540,34 @@ func translateRoute(node *model.Proxy, in *networking.HTTPRoute,
out.TypedPerFilterConfig[wellknown.Fault] = util.MessageToAny(translateFault(in.Fault))
}

if isH3DiscoveryNeeded {
h3DiscoveryHeader := buildH3AltSvcHeader(port, util.ALPNHttp3OverQUIC)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest adding note to envoyproxy/envoy#15953

pilot/pkg/networking/core/v1alpha3/route/route.go Outdated Show resolved Hide resolved
pilot/pkg/networking/util/util.go Outdated Show resolved Hide resolved
tests/integration/security/sds_ingress/util/util.go Outdated Show resolved Hide resolved
// See the License for the specific language governing permissions and
// limitations under the License.

package quic
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we don't need this one. k8sca is a variant on sds_ingress. We don't need all combinatoric explosion of possible options - k8sca and quic have no relation and are extremely unlikely to conflict

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this. Looking at it, I guess only the PKI stuff changes. Correct? As long as we have the right CA certs for verification, it does not affect QUIC-TLS handshake.

Signed-off-by: su225 <suchithjn22@gmail.com>
Signed-off-by: su225 <suchithjn22@gmail.com>
Signed-off-by: su225 <suchithjn22@gmail.com>
Signed-off-by: su225 <suchithjn22@gmail.com>
Signed-off-by: su225 <suchithjn22@gmail.com>
Copy link
Member

@howardjohn howardjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome job! I am excited to see this land and impressed with the work done here.

@su225 su225 removed the do-not-merge/hold Block automatic merging of a PR. label Sep 1, 2021
@istio-testing istio-testing merged commit 925be20 into istio:master Sep 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/networking cla: yes Set by the Google CLA bot to indicate the author of a PR has signed the Google CLA. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants