envoy-tools icon indicating copy to clipboard operation
envoy-tools copied to clipboard

feature: adds support for go-control-plane based xDS implementations

Open owayss opened this issue 2 years ago • 0 comments

👋 This proposal adds support for OSS implementations of the xDS control plane.

Kindly let me know whether this is something you would like integrated into envoy-tools/csds-client, and what are your thoughts on this approach.

platform go

This patch adds support for a second type of platform, named go. It does so by providing utility function ConnToXDS and DialOptions to initiate gRPCs to the address specified by the flag service_uri.

authentication options for platform go

Support is added for mTLS via the introduction of the following new flags:

  -authority string
        the :authority header to use when connecting to uri
  -cacert string
        filepath to the CAs certs used to verify the server's certificate
  -cert string
        filepath to the client TLS certificate
  -key string
        filepath to the client TLS private key

The the client certificate/private key pair are included in the ClientHello message for establishing a connection over TLS.

If a certificate authority is provided via cacert, it is used to validate the server's certificate identity.

Additionally, the -authority flag allows for setting the HTTP/2 :authority header to use for SNI.

Example

$ ~/github/owayss/envoy-tools/csds-client/csds-client \
    -request_file ~/github/owayss/envoy-tools/csds-client/client/v3/test_request.yaml \
    -cert client.crt \
    -key client.key \
    -cacert ca.crt \
    -service_uri localhost:18000 \
    -platform go \
    -api_version v3 
Click to expand response output
Client ID                                          xDS stream type                Config Status                  
api-gateway                                                                       CDS   SYNCED                   
                                                                                  LDS   SYNCED                   
Detailed Config:
{
  "config": [
    {
      "node": {
        "id": "api-gateway"
      },
      "xdsConfig": [
        {
          "status": "SYNCED",
          "clusterConfig": {
            "staticClusters": [
              {
                "cluster": {
                  "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
                  "name": "service_api-gateway-stg",
                  "type": "STRICT_DNS",
                  "connectTimeout": "5s",
                  "loadAssignment": {
                    "clusterName": "service_api-gateway-stg",
                    "endpoints": [
                      {
                        "lbEndpoints": [
                          {},
                          {
                            "endpoint": {
                              "address": {
                                "socketAddress": {
                                  "address": "envoyproxy.io",
                                  "portValue": 443
                                }
                              }
                            }
                          }
                        ]
                      }
                    ]
                  },
                  "healthChecks": [
                    {
                      "timeout": "5s",
                      "interval": "5s",
                      "unhealthyThreshold": 3,
                      "healthyThreshold": 3,
                      "reuseConnection": true,
                      "httpHealthCheck": {
                        "host": "api-gateway-stg.mydomain.com",
                        "path": "/api/health",
                        "expectedStatuses": [
                          {
                            "start": "200",
                            "end": "405"
                          }
                        ]
                      },
                      "noTrafficInterval": "60s",
                      "eventLogPath": "/dev/stdout",
                      "alwaysLogHealthCheckFailures": true
                    }
                  ],
                  "dnsLookupFamily": "V4_ONLY",
                  "transportSocket": {
                    "name": "envoy.transport_sockets.tls"
                  }
                }
              }
            ]
          }
        },
        {
          "status": "SYNCED",
          "listenerConfig": {
            "staticListeners": [
              {
                "listener": {
                  "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
                  "name": "listener_api-gateway-stg",
                  "address": {
                    "socketAddress": {
                      "address": "0.0.0.0",
                      "portValue": 4455
                    }
                  },
                  "filterChains": [
                    {
                      "filters": [
                        {
                          "name": "envoy.filters.network.http_connection_manager",
                          "typedConfig": {
                            "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
                            "statPrefix": "http",
                            "routeConfig": {
                              "name": "route_api-gateway-stg",
                              "virtualHosts": [
                                {
                                  "name": "route_api-gateway-stg",
                                  "domains": [
                                    "*"
                                  ],
                                  "routes": [
                                    {
                                      "match": {
                                        "path": "/__envoy__/status"
                                      },
                                      "directResponse": {
                                        "status": 200,
                                        "body": {
                                          "inlineString": "OK"
                                        }
                                      }
                                    },
                                    {
                                      "match": {
                                        "prefix": "/"
                                      },
                                      "route": {
                                        "cluster": "service_api-gateway-stg",
                                        "hostRewriteLiteral": "api-gateway-stg.mydomain.com"
                                      }
                                    }
                                  ]
                                }
                              ]
                            },
                            "httpFilters": [
                              {
                                "name": "envoy.filters.http.router"
                              }
                            ],
                            "accessLog": [
                              {
                                "name": "envoy.access_loggers.file",
                                "typedConfig": {
                                  "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog",
                                  "path": "/dev/stdout",
                                  "logFormat": {
                                    "jsonFormat": {
                                      "bytes_received": "%BYTES_RECEIVED%",
                                      "bytes_sent": "%BYTES_SENT%",
                                      "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%",
                                      "downstream_local_uri_san": "%DOWNSTREAM_LOCAL_URI_SAN%",
                                      "downstream_peer_uri_san": "%DOWNSTREAM_PEER_URI_SAN%",
                                      "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%",
                                      "duration": "%DURATION%",
                                      "envoy_upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%",
                                      "epoch_micro": "%START_TIME(%s.%6f)%",
                                      "jwt_aud": "%DYNAMIC_METADATA(envoy.filters.http.jwt_authn\\:config)%",
                                      "protocol": "%PROTOCOL%",
                                      "req_authority": "%REQ(:AUTHORITY)%",
                                      "req_envoy_original_path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%",
                                      "req_path": "%REQ(:PATH)%",
                                      "req_user_agent": "%REQ(USER-AGENT)%",
                                      "req_x_request_id": "%REQ(X-REQUEST-ID)%",
                                      "req_xff": "%REQ(X-FORWARDED-FOR)%",
                                      "request_method": "%REQ(:METHOD)%",
                                      "response_code": "%RESPONSE_CODE%",
                                      "response_duration": "%RESPONSE_DURATION%",
                                      "response_flags": "%RESPONSE_FLAGS%",
                                      "response_tx_duration": "%RESPONSE_TX_DURATION%",
                                      "start_time": "%START_TIME(%Y/%m/%d %H:%M:%S)%",
                                      "upstream_cluster": "%UPSTREAM_CLUSTER%",
                                      "upstream_host": "%UPSTREAM_HOST%",
                                      "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%",
                                      "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%"
                                    }
                                  }
                                }
                              }
                            ]
                          }
                        }
                      ]
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  ]
}

Testing coverage

The patch does not include any additional tests. The various test functions in the client/v2 and client/v3 seem to cover the existing utility functions (for parsing and printing out detailed responses) with golden JSON data files.

The part that I think would benefit from a unit test is the DialOptions functions that configures the HTTP/2 connection depending on the provided command-line flags for authentication.


Best, Owayss.

owayss avatar Mar 31 '22 16:03 owayss