Terminating Gateways on Kubernetes

    This topic requires familiarity with Terminating Gateways.

    Adding a terminating gateway is a multi-step process:

    • Update the Helm chart with terminating gateway config options
    • Deploy the Helm chart
    • Access the Consul agent
    • Register external services with Consul

    Minimum required Helm options:

    config.yaml

    Ensure you have the latest consul-helm chart and install Consul via helm using the following while being sure to provide the yaml configuration as previously discussed.

    You can access the Consul server directly from your host via . This is helpful for interacting with your Consul UI locally as well as to validate connectivity of the application.

    1. $ kubectl port-forward consul-server-0 8500 &

    If TLS is enabled use port 8501:

    1. $ kubectl port-forward consul-server-0 8501 &

    Be sure the latest consul binary is installed locally on your host. https://releases.hashicorp.com/consul/

    1. $ export CONSUL_HTTP_ADDR=http://localhost:8500

    If TLS is enabled set:

    1. $ export CONSUL_HTTP_ADDR=https://localhost:8501
    2. $ export CONSUL_HTTP_SSL_VERIFY=false

    If ACLs are enabled also set:

    1. $ export CONSUL_HTTP_TOKEN=$(kubectl get secret consul-bootstrap-acl-token --template='{{.data.token | base64decode }}')

    Registering the external services with Consul is a multi-step process:

    • Register external services with Consul
    • Update the terminating gateway ACL token if ACLs are enabled
    • Create a resource to configure the terminating gateway
    • Create a ServiceIntentions resource to allow access from services in the mesh to external service
    • Define upstream annotations for any services that need to talk to the external services

    Create a sample external service and register it with Consul.

    1. {
    2. "Node": "example_com",
    3. "Address": "example.com",
    4. "NodeMeta": {
    5. "external-node": "true",
    6. "external-probe": "true"
    7. },
    8. "Service": {
    9. "Address": "example.com",
    10. "ID": "example-https",
    11. "Service": "example-https",
    12. "Port": 443
    13. }
    14. }

    Terminating Gateways - 图2

    external.json

    • 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.
    • 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.

    Register the external service with Consul:

    If ACLs and TLS are enabled :

    1. $ curl --request PUT --header "X-Consul-Token: $CONSUL_HTTP_TOKEN" --data @external.json -k $CONSUL_HTTP_ADDR/v1/catalog/register
    2. true

    If ACLs are enabled, update the terminating gateway acl token to have service: write permissions on all of the services being represented by the gateway:

    • Update the existing token to include the new policy

    The CLI command should be run with the -merge-policies, -merge-roles and -merge-service-identities so nothing is removed from the terminating gateway token

    1. service "example-https" {
    2. policy = "write"
    3. }

    write-policy.hcl

    1. $ consul acl policy create -name "example-https-write-policy" -rules @write-policy.hcl
    2. ID: xxxxxxxxxxxxxxx
    3. Name: example-https-write-policy
    4. Description:
    5. Datacenters:
    6. Rules:
    7. service "example-https" {
    8. policy = "write"
    9. }

    Now fetch the ID of the terminating gateway token

    1. AccessorID: <token id>

    Update the terminating gateway acl token with the new policy

    1. $ consul acl token update -id <token-id> -policy-name example-https-write-policy -merge-policies -merge-roles -merge-service-identities
    2. AccessorID: <token id>
    3. SecretID: <secret id>
    4. Description: terminating-gateway-terminating-gateway-token Token
    5. Local: true
    6. Create Time: 2021-01-08 21:18:47.957450486 +0000 UTC
    7. Policies:
    8. 63bf1d9b-a87d-8672-ddcb-d25e2d88adb8 - terminating-gateway-terminating-gateway-token
    9. f63d1ae6-ffe7-44bd-bf7a-704a86939a63 - example-https-write-policy

    Once the tokens have been updated, create the TerminatingGateway resource to configure the terminating gateway:

    1. apiVersion: consul.hashicorp.com/v1alpha1
    2. kind: TerminatingGateway
    3. metadata:
    4. name: terminating-gateway
    5. spec:
    6. services:
    7. - name: example-https
    8. caFile: /etc/ssl/cert.pem

    terminating-gateway.yaml

    If TLS is enabled a caFile must be provided, it must point to the system trust store of the terminating gateway container (/etc/ssl/cert.pem).

    Apply the TerminatingGateway resource with kubectl apply:

    If using ACLs and TLS, create a resource to allow access from services in the mesh to the external service

    1. apiVersion: consul.hashicorp.com/v1alpha1
    2. kind: ServiceIntentions
    3. metadata:
    4. name: example-https
    5. spec:
    6. destination:
    7. name: example-https
    8. sources:
    9. - name: static-client

    Terminating Gateways - 图5

    service-intentions.yaml

    Apply the ServiceIntentions resource with kubectl apply:

    1. $ kubectl apply -f service-intentions.yaml

    Finally define and deploy the external services as upstreams for the internal mesh services that wish to talk to them. An example deployment is provided which will serve as a static client for the terminating gateway service.

    1. apiVersion: v1
    2. kind: Service
    3. metadata:
    4. spec:
    5. selector:
    6. app: static-client
    7. ports:
    8. - port: 80
    9. ---
    10. apiVersion: v1
    11. kind: ServiceAccount
    12. metadata:
    13. name: static-client
    14. ---
    15. apiVersion: apps/v1
    16. kind: Deployment
    17. metadata:
    18. name: static-client
    19. spec:
    20. replicas: 1
    21. selector:
    22. matchLabels:
    23. app: static-client
    24. template:
    25. metadata:
    26. name: static-client
    27. labels:
    28. app: static-client
    29. annotations:
    30. 'consul.hashicorp.com/connect-inject': 'true'
    31. 'consul.hashicorp.com/connect-service-upstreams': 'example-https:1234'
    32. spec:
    33. containers:
    34. - name: static-client
    35. image: curlimages/curl:latest
    36. command: ['/bin/sh', '-c', '--']
    37. args: ['while true; do sleep 30; done;']
    38. serviceAccountName: static-client

    static-client.yaml

    Run the service via kubectl apply:

    1. $ kubectl apply -f static-client.yaml

    Wait for the service to be ready:

    1. $ kubectl rollout status deploy static-client --watch