Redis AUTH and mTLS with Envoy
Sample config to setup Envoy to proxy Redis traffic using mTLS and Redis AUTH.
Both client->envoy->redis is secured by redis AUTH
client
->envoy
-->redis
client->redis is on port :6000
while envoy->redis is on port :6379
the whole system uses mTLS end to end.
I’m writing this up since i found it really tricky to setup the envoy side of things…especially with both downstream and upstream AUTH:
What we’re going to do:
Setup a go redis client app to talk via TLS to envoy. Envoy will then proxy requests to Redis server.
This git repo is just the git version with the config files of a previous post here Redis with Envoy
First download docker and golang 1.14:
Docker:
Note: we are using envoy v3 1.17
docker cp `docker create envoyproxy/envoy-dev:latest`:/usr/local/bin/envoy .
Setup
Download the source files froe the git repo here. You should end up with:
basic.yaml
: Envoy config fileCA_crt.epm
: The CA Cert for mtlsserver.crt
, server.key: Server certs envoy will useclient.crt
, client.key: client side certs the go app will usemain.go
: Redis golang client that will connect to Envoy
Redis
Start Redis:
docker run -v `pwd`/certs:/certs -p 6379:6379 redis \
--tls-port 6379 --port 0 \
--tls-cert-file /certs/server.crt \
--tls-key-file /certs/server.key \
--tls-ca-cert-file /certs/CA_crt.pem \
--requirepass foobared
Envoy
Start Envoy:
envoy -c basic.yaml -l debug
The whole reason for this article is because i found it hard to configure enovy…so here it is:
Some things to point out below:
- Envoy Listens on mTLS: the config tls_context:
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
require_client_certificate: true
common_tls_context:
tls_certificates:
- certificate_chain:
filename: certs/server.crt
private_key:
filename: certs/server.key
validation_context:
trusted_ca:
filename: certs/CA_crt.pem
- Clients connecting to envoy must provide a redis password:
bar
filter_chains:
- filters:
- name: envoy.filters.network.redis_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy
stat_prefix: egress_redis
settings:
op_timeout: 5s
prefix_routes:
catch_all_route:
cluster: redis_cluster
downstream_auth_password:
inline_string: "bar"
- Envoy connect to Redis outbound with mtls: the config transport_socket
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
common_tls_context:
tls_certificates:
certificate_chain:
filename: "certs/client.crt"
private_key:
filename: "certs/client.key"
validation_context:
trusted_ca:
filename: "certs/CA_crt.pem"
- Envoy connects to Redis must provide a redis:
foobared
clusters:
- name: redis_cluster
connect_timeout: 1s
type: strict_dns
load_assignment:
cluster_name: redis_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 6379
typed_extension_protocol_options:
envoy.redis_proxy:
"@type": type.googleapis.com/google.protobuf.Struct
value:
auth_password:
inline_string: "foobared"
In enovy-speak, the client is downstream while redis is upstream as far as envoy is concerned.
Start go redis client
main.go connects to envoy on port :6000 and presents a client certificate to it:
If you run the app now you’ll see a pong and the value you just saved:
$ go run main.go
Ping Response: PONG
Incrementing key:
key 1
key 2
key 3
key 4
key 5
In the envoy logs, you'll see it traverses the filter
[2021-01-10 18:37:22.085][236708][debug][conn_handler] [source/server/connection_handler_impl.cc:503] [C4] new connection
[2021-01-10 18:37:22.088][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["set", "key", "0"]'
[2021-01-10 18:37:22.089][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["incr", "key"]'
[2021-01-10 18:37:22.089][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["get", "key"]'
[2021-01-10 18:37:22.089][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["incr", "key"]'
[2021-01-10 18:37:22.089][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["get", "key"]'
[2021-01-10 18:37:22.090][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["incr", "key"]'
[2021-01-10 18:37:22.090][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["get", "key"]'
[2021-01-10 18:37:22.090][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["incr", "key"]'
[2021-01-10 18:37:22.090][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["get", "key"]'
[2021-01-10 18:37:22.090][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["incr", "key"]'
[2021-01-10 18:37:22.090][236708][debug][redis] [source/extensions/filters/network/redis_proxy/command_splitter_impl.cc:539] redis: splitting '["get", "key"]'
[2021-01-10 18:37:22.091][236708][debug][connection] [source/common/network/connection_impl.cc:619] [C4] remote close
…yeah, thats it..