2020-07-22 21:02:47 +00:00
---
layout: docs
2022-09-14 22:48:49 +00:00
page_title: Configure Terminating Gateways for Consul on Kubernetes
description: >-
Terminating gateways send secure requests from the service mesh to locations outside of the Kubernetes cluster. Learn how to configure terminating gateways for k8s, register external services in Consul’ s service catalog, and define external sources as upstreams in your service mesh.
2020-07-22 21:02:47 +00:00
---
2022-09-14 22:48:49 +00:00
# Configure Terminating Gateways for Consul on Kubernetes
2020-07-22 21:02:47 +00:00
Adding a terminating gateway is a multi-step process:
2021-04-16 19:49:02 +00:00
- Update the Helm chart with terminating gateway config options
- Deploy the Helm chart
2021-01-08 22:30:41 +00:00
- Access the Consul agent
2020-08-18 22:22:29 +00:00
- Register external services with Consul
2020-07-22 21:02:47 +00:00
2022-08-25 17:02:55 +00:00
## Requirements
2022-08-25 18:44:33 +00:00
- [Consul](https://www.consul.io/docs/install#install-consul)
- [Consul on Kubernetes CLI](/docs/k8s/k8s-cli)
2022-08-25 17:02:55 +00:00
- Familiarity with [Terminating Gateways](/docs/connect/gateways/terminating-gateway)
2022-08-25 18:44:45 +00:00
## Update the Helm chart with terminating gateway config options
2020-07-22 21:02:47 +00:00
Minimum required Helm options:
2022-09-09 20:56:33 +00:00
<CodeBlockConfig filename="values.yaml">
2021-07-31 01:37:33 +00:00
2020-07-22 21:02:47 +00:00
```yaml
global:
name: consul
connectInject:
enabled: true
2021-01-08 22:30:41 +00:00
controller:
enabled: true
2020-07-22 21:02:47 +00:00
terminatingGateways:
enabled: true
```
2021-07-31 01:37:33 +00:00
</CodeBlockConfig>
2021-04-16 19:49:02 +00:00
## Deploying the Helm chart
2020-07-22 21:02:47 +00:00
2022-08-25 16:49:54 +00:00
The Helm chart may be deployed using the [Consul on Kubernetes CLI](/docs/k8s/k8s-cli).
```shell-session
2022-09-09 20:56:33 +00:00
$ consul-k8s install -f values.yaml
2022-08-25 16:49:54 +00:00
```
2020-07-22 21:02:47 +00:00
## Accessing the Consul agent
2022-08-25 17:02:55 +00:00
You can access the Consul server directly from your host via `kubectl port-forward`. This is helpful for interacting with your Consul UI locally as well as for validating the connectivity of the application.
<Tabs>
<Tab heading="Without TLS">
2020-07-22 21:02:47 +00:00
```shell-session
2021-01-08 22:30:41 +00:00
$ kubectl port-forward consul-server-0 8500 &
2020-07-22 21:02:47 +00:00
```
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-08-25 17:02:55 +00:00
$ export CONSUL_HTTP_ADDR=http://localhost:8500
2020-07-22 21:02:47 +00:00
```
2022-08-25 17:02:55 +00:00
</Tab>
<Tab heading="With TLS">
2020-07-22 21:02:47 +00:00
2022-08-25 17:02:55 +00:00
If TLS is enabled use port 8501:
2020-07-22 21:02:47 +00:00
```shell-session
2022-08-25 17:02:55 +00:00
$ kubectl port-forward consul-server-0 8501 &
2020-07-22 21:02:47 +00:00
```
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
$ export CONSUL_HTTP_ADDR=https://localhost:8501
$ export CONSUL_HTTP_SSL_VERIFY=false
```
2022-08-25 17:02:55 +00:00
</Tab>
</Tabs>
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
If ACLs are enabled also set:
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2021-09-24 01:47:40 +00:00
$ export CONSUL_HTTP_TOKEN=$(kubectl get secret consul-bootstrap-acl-token --template='{{.data.token | base64decode }}')
2020-07-22 21:02:47 +00:00
```
## Register external services with Consul
Registering the external services with Consul is a multi-step process:
2020-08-18 22:22:29 +00:00
- Register external services with Consul
- Update the terminating gateway ACL token if ACLs are enabled
2021-01-08 22:30:41 +00:00
- Create a [`TerminatingGateway`](/docs/connect/config-entries/terminating-gateway) resource to configure the terminating gateway
- Create a [`ServiceIntentions`](/docs/connect/config-entries/service-intentions) resource to allow access from services in the mesh to external service
2020-08-18 22:22:29 +00:00
- Define upstream annotations for any services that need to talk to the external services
2020-07-22 21:02:47 +00:00
### Register external services with Consul
2022-08-25 17:27:43 +00:00
You may register an external service with Consul using `ServiceDefaults` if
[`TransparentProxy`](/docs/connect/transparent-proxy) is enabled. Otherwise,
you may register the service as a node in the Consul catalog.
2022-08-02 18:57:58 +00:00
2022-08-25 17:27:43 +00:00
<Tabs>
<Tab heading="Using ServiceDefaults and TransparentProxy">
2022-08-02 18:57:58 +00:00
2022-08-25 17:27:43 +00:00
The [`destination`](/docs/connect/config-entries/service-defaults#terminating-gateway-destination) field of the `ServiceDefaults` Custom Resource Definition (CRD) allows clients to dial an external service directly. For this method to work, [`TransparentProxy`](/docs/connect/transparent-proxy) must be enabled.
The following table describes traffic behaviors when using the `destination` field to route traffic through a terminating gateway:
2022-08-02 18:57:58 +00:00
2022-08-25 17:56:13 +00:00
| <nobr>External Services Layer</nobr> | <nobr>Client dials</nobr> | <nobr>Client uses TLS</nobr> | Allowed | Notes |
|--------------------------------------|---------------------------|------------------------------|--------------------------|-----------------------------------------------------------------------------------------------|
| L4 | Hostname | Yes | <nobr>Allowed</nobr> | `CAFiles` are not allowed because traffic is already end-to-end encrypted by the client. |
| L4 | IP | Yes | <nobr>Allowed</nobr> | `CAFiles` are not allowed because traffic is already end-to-end encrypted by the client. |
| L4 | Hostname | No | <nobr>Not allowed</nobr> | The sidecar is not protocol aware and can not identify traffic going to the external service. |
| L4 | IP | No | <nobr>Allowed</nobr> | There are no limitations on dialing IPs without TLS. |
| L7 | Hostname | Yes | <nobr>Not allowed</nobr> | Because traffic is already encrypted before the sidecar, it cannot route as L7 traffic. |
| L7 | IP | Yes | <nobr>Not allowed</nobr> | Because traffic is already encrypted before the sidecar, it cannot route as L7 traffic. |
| L7 | Hostname | No | <nobr>Allowed</nobr> | A `Host` or `:authority` header is required. |
| L7 | IP | No | <nobr>Allowed</nobr> | There are no limitations on dialing IPs without TLS. |
2022-08-02 18:57:58 +00:00
You can provide a `caFile` to secure traffic between unencrypted clients that connect to external services through the terminating gateway.
2022-08-19 20:51:11 +00:00
Refer to [Create the configuration entry for the terminating gateway](#create-the-configuration-entry-for-the-terminating-gateway) for details.
2022-08-02 18:57:58 +00:00
2022-08-25 17:56:13 +00:00
-> **Note:** Regardless of the `protocol` specified in the `ServiceDefaults`, [L7 intentions](/docs/connect/config-entries/service-intentions#permissions) are not currently supported with `ServiceDefaults` destinations.
2022-08-02 18:57:58 +00:00
2022-08-19 20:51:11 +00:00
Create a `ServiceDefaults` custom resource for the external service:
2022-08-25 17:56:13 +00:00
<CodeBlockConfig filename="service-defaults.yaml">
2022-08-02 18:57:58 +00:00
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
name: example-https
spec:
protocol: tcp
destination:
addresses:
- "example.com"
port: 443
```
</CodeBlockConfig>
Apply the `ServiceDefaults` resource with `kubectl apply`:
```shell-session
2022-08-25 17:56:13 +00:00
$ kubectl apply --filename service-defaults.yaml
2022-08-02 18:57:58 +00:00
```
2022-08-25 17:56:13 +00:00
All other terminating gateway operations can use the name of the `ServiceDefaults` component, in this case "example-https", as a Consul service name.
2022-08-25 17:27:43 +00:00
</Tab>
<Tab heading="Using Consul catalog">
2022-08-02 18:57:58 +00:00
2022-08-25 17:56:13 +00:00
Normally, Consul services are registered with the Consul client on the node that
2021-06-14 16:15:40 +00:00
they're running on. Since this is an external service, there is no Consul node
to register it onto. Instead, we will make up a node name and register the
service to that node.
2020-07-22 21:02:47 +00:00
Create a sample external service and register it with Consul.
2020-08-18 22:22:29 +00:00
2021-07-31 01:37:33 +00:00
<CodeBlockConfig filename="external.json">
2020-07-22 21:02:47 +00:00
```json
{
2021-06-14 16:15:40 +00:00
"Node": "example_com",
2020-08-18 22:22:29 +00:00
"Address": "example.com",
"NodeMeta": {
"external-node": "true",
"external-probe": "true"
},
"Service": {
2021-06-14 16:15:40 +00:00
"Address": "example.com",
2020-08-18 22:22:29 +00:00
"ID": "example-https",
"Service": "example-https",
"Port": 443
}
2020-07-22 21:02:47 +00:00
}
```
2021-07-31 01:37:33 +00:00
</CodeBlockConfig>
2021-06-14 16:15:40 +00:00
- `"Node": "example_com"` is our made up node name.
- `"Address": "example.com"` is the address of our node. Services registered to that node will use this address if
their own address isn't specified. If you're registering multiple external services, ensure you
use different node names with different addresses or set the `Service.Address` key.
- `"Service": { "Address": "example.com" ... }` is the address of our service. In this example this doesn't need to be set
since the address of the node is the same, but if there were two services registered to that same node
then this should be set.
2020-07-22 21:02:47 +00:00
Register the external service with Consul:
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-01-12 23:05:01 +00:00
$ curl --request PUT --data @external.json --insecure $CONSUL_HTTP_ADDR/v1/catalog/register
2021-01-08 22:30:41 +00:00
true
2020-07-22 21:02:47 +00:00
```
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
If ACLs and TLS are enabled :
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-01-12 23:05:01 +00:00
$ curl --request PUT --header "X-Consul-Token: $CONSUL_HTTP_TOKEN" --data @external.json --insecure $CONSUL_HTTP_ADDR/v1/catalog/register
2021-01-08 22:30:41 +00:00
true
2020-07-22 21:02:47 +00:00
```
2022-08-25 17:27:43 +00:00
</Tab>
</Tabs>
2022-04-06 21:54:27 +00:00
### Update terminating gateway ACL role if ACLs are enabled
2020-08-18 22:22:29 +00:00
2022-04-06 21:54:27 +00:00
If ACLs are enabled, update the terminating gateway acl role to have `service: write` permissions on all of the services
2022-08-25 18:03:43 +00:00
being represented by the gateway.
2020-08-18 22:22:29 +00:00
2022-08-25 18:03:43 +00:00
Create a new policy that includes the write permission for the service you created.
2020-07-22 21:02:47 +00:00
2021-07-31 01:37:33 +00:00
<CodeBlockConfig filename="write-policy.hcl">
2020-07-22 21:02:47 +00:00
```hcl
service "example-https" {
policy = "write"
}
```
2020-08-18 22:22:29 +00:00
2021-07-31 01:37:33 +00:00
</CodeBlockConfig>
2020-07-22 21:02:47 +00:00
```shell-session
$ consul acl policy create -name "example-https-write-policy" -rules @write-policy.hcl
2021-01-08 22:30:41 +00:00
ID: xxxxxxxxxxxxxxx
Name: example-https-write-policy
Description:
Datacenters:
Rules:
service "example-https" {
policy = "write"
}
2020-07-22 21:02:47 +00:00
```
2020-08-18 22:22:29 +00:00
2022-08-25 18:04:33 +00:00
Fetch the ID of the terminating gateway token.
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-04-06 21:54:27 +00:00
consul acl role list | grep -B 6 -- "- RELEASE_NAME-terminating-gateway-policy" | grep ID
2021-01-08 22:30:41 +00:00
2022-04-06 21:54:27 +00:00
ID: <role id>
2020-07-22 21:02:47 +00:00
```
2020-08-18 22:22:29 +00:00
2022-08-25 18:03:43 +00:00
Update the terminating gateway ACL token with the new policy.
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-04-06 21:54:27 +00:00
$ consul acl role update -id <role id> -policy-name example-https-write-policy
AccessorID: <role id>
2021-01-08 22:30:41 +00:00
SecretID: <secret id>
2022-04-06 21:54:27 +00:00
Description: RELEASE_NAME-terminating-gateway-acl-role
2021-01-08 22:30:41 +00:00
Local: true
Create Time: 2021-01-08 21:18:47.957450486 +0000 UTC
Policies:
2022-04-06 21:54:27 +00:00
63bf1d9b-a87d-8672-ddcb-d25e2d88adb8 - RELEASE_NAME-terminating-gateway-policy
2021-01-08 22:30:41 +00:00
f63d1ae6-ffe7-44bd-bf7a-704a86939a63 - example-https-write-policy
2020-07-22 21:02:47 +00:00
```
### Create the configuration entry for the terminating gateway
2020-08-18 22:22:29 +00:00
2022-04-06 21:54:27 +00:00
Once the roles have been updated, create the [TerminatingGateway](/docs/connect/config-entries/terminating-gateway)
2021-01-08 22:30:41 +00:00
resource to configure the terminating gateway:
2020-08-18 22:22:29 +00:00
2021-07-31 01:37:33 +00:00
<CodeBlockConfig filename="terminating-gateway.yaml">
```yaml
2021-01-08 22:30:41 +00:00
apiVersion: consul.hashicorp.com/v1alpha1
kind: TerminatingGateway
metadata:
name: terminating-gateway
spec:
services:
- name: example-https
2020-07-22 21:02:47 +00:00
```
2020-08-18 22:22:29 +00:00
2021-07-31 01:37:33 +00:00
</CodeBlockConfig>
2022-08-19 20:51:11 +00:00
If TLS is enabled for external services registered through the Consul catalog and you are not using [transparent proxy `destination`](#register-an-external-service-as-a-destination), you must include the [`caFile`](/docs/connect/config-entries/terminating-gateway#cafile) parameter that points to the system trust store of the terminating gateway container.
2022-08-02 18:57:58 +00:00
By default, the trust store is located in the `/etc/ssl/certs/ca-certificates.crt` directory.
2022-08-19 20:51:11 +00:00
Configure the [`caFile`](https://www.consul.io/docs/connect/config-entries/terminating-gateway#cafile) parameter in the `TerminatingGateway` config entry to point to the `/etc/ssl/cert.pem` directory if TLS is enabled and you are using one of the following components:
- Consul Helm chart 0.43 or older
- An Envoy image with an alpine base image
2021-01-08 22:30:41 +00:00
Apply the `TerminatingGateway` resource with `kubectl apply`:
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-01-12 23:05:01 +00:00
$ kubectl apply --filename terminating-gateway.yaml
2020-07-22 21:02:47 +00:00
```
2022-08-19 20:51:11 +00:00
If using ACLs and TLS, create a [`ServiceIntentions`](/docs/connect/config-entries/service-intentions) resource to allow access from services in the mesh to the external service:
2021-01-08 22:30:41 +00:00
2021-07-31 01:37:33 +00:00
<CodeBlockConfig filename="service-intentions.yaml">
2021-01-08 22:30:41 +00:00
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceIntentions
metadata:
name: example-https
spec:
destination:
name: example-https
sources:
- name: static-client
action: allow
```
2021-07-31 01:37:33 +00:00
</CodeBlockConfig>
2022-08-19 20:51:11 +00:00
-> **NOTE**: [L7 Intentions](/docs/connect/config-entries/service-intentions#permissions) are not currently supported for `ServiceDefaults` destinations.
2021-01-08 22:30:41 +00:00
Apply the `ServiceIntentions` resource with `kubectl apply`:
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-01-12 23:05:01 +00:00
$ kubectl apply --filename service-intentions.yaml
2020-07-22 21:02:47 +00:00
```
### Define the external services as upstreams for services in the mesh
2020-08-18 22:22:29 +00:00
2022-08-25 18:40:18 +00:00
As a final step, you may define and deploy the external services as upstreams for the internal mesh services that wish to talk to them.
2020-07-22 21:02:47 +00:00
An example deployment is provided which will serve as a static client for the terminating gateway service.
2021-07-31 01:37:33 +00:00
<CodeBlockConfig filename="static-client.yaml">
2020-07-22 21:02:47 +00:00
```yaml
apiVersion: v1
2021-04-16 19:49:02 +00:00
kind: Service
metadata:
name: static-client
spec:
selector:
app: static-client
ports:
- port: 80
---
apiVersion: v1
2020-07-22 21:02:47 +00:00
kind: ServiceAccount
metadata:
name: static-client
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: static-client
spec:
replicas: 1
selector:
matchLabels:
app: static-client
template:
metadata:
name: static-client
labels:
app: static-client
annotations:
2020-08-18 22:22:29 +00:00
'consul.hashicorp.com/connect-inject': 'true'
'consul.hashicorp.com/connect-service-upstreams': 'example-https:1234'
2020-07-22 21:02:47 +00:00
spec:
containers:
- name: static-client
2021-06-29 20:23:36 +00:00
image: curlimages/curl:latest
2020-08-18 22:22:29 +00:00
command: ['/bin/sh', '-c', '--']
args: ['while true; do sleep 30; done;']
2020-07-22 21:02:47 +00:00
serviceAccountName: static-client
```
2021-07-31 01:37:33 +00:00
</CodeBlockConfig>
2022-08-25 18:40:18 +00:00
Deploy the service with `kubectl apply`.
2020-08-18 22:22:29 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-01-12 23:05:01 +00:00
$ kubectl apply --filename static-client.yaml
2020-07-22 21:02:47 +00:00
```
2022-08-25 18:40:18 +00:00
Wait for the service to be ready.
2021-01-08 22:30:41 +00:00
```shell-session
$ kubectl rollout status deploy static-client --watch
deployment "static-client" successfully rolled out
```
2022-08-25 18:40:18 +00:00
You can verify connectivity of the static-client and terminating gateway via a curl command.
2020-08-18 22:22:29 +00:00
2022-08-25 18:40:18 +00:00
<Tabs>
<Tab heading="Registered with `ServiceDefaults` destinations">
2022-08-02 18:57:58 +00:00
2020-07-22 21:02:47 +00:00
```shell-session
2022-08-25 18:40:18 +00:00
$ kubectl exec deploy/static-client -- curl -vvvs https://example.com/
2020-07-22 21:02:47 +00:00
```
2022-08-02 18:57:58 +00:00
2022-08-25 18:40:18 +00:00
</Tab>
<Tab heading="Registered with the Consul catalog">
2022-08-02 18:57:58 +00:00
```shell-session
2022-08-25 18:40:18 +00:00
$ kubectl exec deploy/static-client -- curl -vvvs --header "Host: example-https.com" http://localhost:1234/
2022-08-02 18:57:58 +00:00
```
2022-08-25 18:40:18 +00:00
</Tab>
</Tabs>