Repository: emissary-ingress/emissary

Issue:
Can't originate TLS to external domains
```
    getambassador.io/config: |
      ---
      apiVersion: ambassador/v0
      kind:  Mapping
      name:  httpbin2_mapping
      prefix: /
      service: https://httpbin.org:443
      host_rewrite: httpbin.org
```

Doesn't work (you get an upstream connect error or disconnect/reset before headers).




Discussion:
@kflynn 

This seems to be related to #424 and #398 

I tested this with ambassador 0.33.1 with this configuration:

```
      ---
      apiVersion: ambassador/v0
      kind:  Mapping
      name:  example-home-http-mapping
      prefix: /api
      host_redirect: true
      service: https://example.com
```

The redirect leads me to: **https//example.com/**

Can you have a look at this issue?
The big difference between using a redirect and not is that when you use the redirect, the entity speaking TLS to `example.com` is actually the browser, rather than Ambassador's Envoy.  The actual error that happens when I try the non-redirect example (to `httpbin.org`) is

```
SSL error: 268436536:SSL routines:OPENSSL_internal:TLSV1_ALERT_INTERNAL_ERROR
```

which implies that we're looking at a version mismatch when Envoy tries to speak to `httpbin.org`. Sigh.
The previous comment is wrong. The problem is that unless SNI is explicitly configured, Envoy won't offer SNI, and `httpbin.org` requires SNI or it shuts the connection down. Looking at the minimal path forward here...

Resolving patch:
diff --git a/ambassador/ambassador/config.py b/ambassador/ambassador/config.py
--- a/ambassador/ambassador/config.py
+++ b/ambassador/ambassador/config.py
@@ -667,7 +667,7 @@ def diag_service(self):
     def add_intermediate_cluster(self, _source, name, _service, urls, 
                                  type="strict_dns", lb_type="round_robin",
                                  cb_name=None, od_name=None, originate_tls=None,
-                                 grpc=False):
+                                 grpc=False, host_rewrite=None):
         if name not in self.envoy_clusters:
             self.logger.debug("CLUSTER %s: new from %s" % (name, _source))
 
@@ -703,8 +703,10 @@ def add_intermediate_cluster(self, _source, name, _service, urls,
                         continue
 
                     tls_array.append({ 'key': key, 'value': value })
+                    cluster['tls_array'] = sorted(tls_array, key=lambda x: x['key'])
 
-                cluster['tls_array'] = sorted(tls_array, key=lambda x: x['key'])
+            if host_rewrite and originate_tls:
+                cluster['tls_array'].append({'key': 'sni', 'value': host_rewrite })
 
             if grpc:
                 cluster['features'] = 'http2'
@@ -761,7 +763,7 @@ def add_intermediate_route(self, _source, mapping, svc, cluster_name, shadow=Fal
         route = mapping.new_route(svc, cluster_name)
         self.envoy_routes[mapping.group_id] = route
 
-    def service_tls_check(self, svc, context):
+    def service_tls_check(self, svc, context, host_rewrite):
         originate_tls = False
         name_fields = None
 
@@ -786,6 +788,9 @@ def service_tls_check(self, svc, context):
             else:
                 self.logger.error("Originate-TLS context %s is not defined" % context)
 
+        if originate_tls and host_rewrite:
+            name_fields.append("hr-%s" % host_rewrite)
+
         port = 443 if originate_tls else 80
         context_name = "_".join(name_fields) if name_fields else None
 
@@ -800,6 +805,7 @@ def add_clusters_for_mapping(self, mapping):
         svc = mapping['service']
         tls_context = mapping.get('tls', None)
         grpc = mapping.get('grpc', False)
+        host_rewrite = mapping.get('host_rewrite', None)
 
         # Given the service and the TLS context, first initialize the cluster name for the
         # main service with the incoming service string...
@@ -829,7 +835,7 @@ def add_clusters_for_mapping(self, mapping):
         # versions of Ambassador. (This isn't a functional issue, just a matter of 
         # trying not to confuse people on upgrades.)
 
-        (svc, url, originate_tls, otls_name) = self.service_tls_check(svc, tls_context)
+        (svc, url, originate_tls, otls_name) = self.service_tls_check(svc, tls_context, host_rewrite)
         
         # Build up the common name stuff that we'll need for the service and
         # the shadow service.
@@ -869,7 +875,7 @@ def add_clusters_for_mapping(self, mapping):
         self.add_intermediate_cluster(mapping['_source'], cluster_name,
                                       svc, [ url ],
                                       cb_name=cb_name, od_name=od_name, grpc=grpc,
-                                      originate_tls=originate_tls)
+                                      originate_tls=originate_tls, host_rewrite=host_rewrite)
         
         return svc, cluster_name
 
@@ -1339,6 +1345,8 @@ def module_config_ratelimit(self, ratelimit_config):
         if not cluster_hosts or not sources:
             return (None, None)
 
+        host_rewrite = config.get("host_rewrite", None)
+
         cluster_name = "cluster_ext_ratelimit"
         filter_config = {
             "domain": "ambassador",
@@ -1360,11 +1368,11 @@ def module_config_ratelimit(self, ratelimit_config):
         )
 
         if cluster_name not in self.envoy_clusters:
-            (svc, url, originate_tls, otls_name) = self.service_tls_check(cluster_hosts, None)
+            (svc, url, originate_tls, otls_name) = self.service_tls_check(cluster_hosts, None, host_rewrite)
             self.add_intermediate_cluster(first_source, cluster_name,
                                           'extratelimit', [url],
                                           type="strict_dns", lb_type="round_robin",
-                                          grpc=True)
+                                          grpc=True, host_rewrite=host_rewrite)
 
         for source in sources:
             filter._mark_referenced_by(source)
@@ -1440,6 +1448,7 @@ def module_config_authentication(self, name, amod, auth_mod, auth_configs):
         )
 
         cluster_name = filter_config['cluster']
+        host_rewrite = filter_config.get('host_rewrite', None)
 
         if cluster_name not in self.envoy_clusters:
             if not cluster_hosts:
@@ -1451,7 +1460,7 @@ def module_config_authentication(self, name, amod, auth_mod, auth_configs):
             for svc in sorted(cluster_hosts.keys()):
                 weight, tls_context = cluster_hosts[svc]
 
-                (svc, url, originate_tls, otls_name) = self.service_tls_check(svc, tls_context)
+                (svc, url, originate_tls, otls_name) = self.service_tls_check(svc, tls_context, host_rewrite)
 
                 if originate_tls:
                     protocols['https'] = True
@@ -1470,7 +1479,7 @@ def module_config_authentication(self, name, amod, auth_mod, auth_configs):
             self.add_intermediate_cluster(first_source, cluster_name,
                                           'extauth', urls,
                                           type="strict_dns", lb_type="round_robin",
-                                          originate_tls=originate_tls)
+                                          originate_tls=originate_tls, host_rewrite=host_rewrite)
 
         for source in sources:
             filter._mark_referenced_by(source)


Test patch:
diff --git a/ambassador/tests/001-broader-v0/gold.intermediate.json b/ambassador/tests/001-broader-v0/gold.intermediate.json
--- a/ambassador/tests/001-broader-v0/gold.intermediate.json
+++ b/ambassador/tests/001-broader-v0/gold.intermediate.json
@@ -107,8 +107,13 @@
                 "_service": "google.com",
                 "_source": "mapping-google.yaml.1",
                 "lb_type": "round_robin",
-                "name": "cluster_https___google_com_otls",
-                "tls_array": [],
+                "name": "cluster_https___google_com_otls_hr_google_com",
+                "tls_array": [
+                    {
+                        "key": "sni",
+                        "value": "google.com"
+                    }
+                ],
                 "tls_context": {
                     "_ambassador_enabled": true
                 },
@@ -293,8 +298,13 @@
                 "_service": "myservice.org/",
                 "_source": "mapping-httpbin-shadow.yaml.1",
                 "lb_type": "round_robin",
-                "name": "cluster_shadow_https___myservice_org__otls",
-                "tls_array": [],
+                "name": "cluster_shadow_https___myservice_org__otls_hr_httpbin_org",
+                "tls_array": [
+                    {
+                        "key": "sni",
+                        "value": "httpbin.org"
+                    }
+                ],
                 "tls_context": {
                     "_ambassador_enabled": true
                 },
@@ -428,7 +438,7 @@
                 "prefix": "/httpbin/",
                 "prefix_rewrite": "/",
                 "shadow": {
-                    "name": "cluster_shadow_https___myservice_org__otls"
+                    "name": "cluster_shadow_https___myservice_org__otls_hr_httpbin_org"
                 },
                 "timeout_ms": 50
             },
@@ -912,7 +922,7 @@
                 "_source": "mapping-google.yaml.1",
                 "clusters": [
                     {
-                        "name": "cluster_https___google_com_otls",
+                        "name": "cluster_https___google_com_otls_hr_google_com",
                         "weight": 100.0
                     }
                 ],
diff --git a/ambassador/tests/001-broader-v0/gold.json b/ambassador/tests/001-broader-v0/gold.json
--- a/ambassador/tests/001-broader-v0/gold.json
+++ b/ambassador/tests/001-broader-v0/gold.json
@@ -141,7 +141,7 @@
                             ]
                         },
                         "shadow": {
-                          "cluster": "cluster_shadow_https___myservice_org__otls"
+                          "cluster": "cluster_shadow_https___myservice_org__otls_hr_httpbin_org"
                         }
 
                       
@@ -167,7 +167,7 @@
                         "weighted_clusters": {
                             "clusters": [
                                 
-                                  { "name": "cluster_https___google_com_otls", "weight": 100.0 }
+                                  { "name": "cluster_https___google_com_otls_hr_google_com", "weight": 100.0 }
                                 
                             ]
                         }
@@ -409,7 +409,7 @@
           
         ]},
       {
-        "name": "cluster_https___google_com_otls",
+        "name": "cluster_https___google_com_otls_hr_google_com",
         "connect_timeout_ms": 3000,
         "type": "strict_dns",
         "lb_type": "round_robin",        
@@ -421,6 +421,8 @@
         ],
         "ssl_context": {
           
+          "sni": "google.com"
+          
         }},
       {
         "name": "cluster_precedence_none",
@@ -573,7 +575,7 @@
           
         ]},
       {
-        "name": "cluster_shadow_https___myservice_org__otls",
+        "name": "cluster_shadow_https___myservice_org__otls_hr_httpbin_org",
         "connect_timeout_ms": 3000,
         "type": "strict_dns",
         "lb_type": "round_robin",        
@@ -585,6 +587,8 @@
         ],
         "ssl_context": {
           
+          "sni": "httpbin.org"
+          
         }},
       {
         "name": "cluster_slack_com",