Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enhance TLS support by taking path to CA file for redis conn #5

Merged
merged 1 commit into from
Oct 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ bootstrap_redis_tls: redis.conf redis-per-second.conf
cat key.pem cert.pem > private.pem
sudo cp cert.pem /usr/local/share/ca-certificates/redis-stunnel.crt
chmod 640 key.pem cert.pem private.pem
sudo update-ca-certificates
#sudo update-ca-certificates - not needed with the RedisTlsCACerts feature
Copy link
Author

@bhepburn bhepburn Oct 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commenting this out allows us to test our new code by not adding the self-signed certificate into the system level trust-store.

sudo stunnel redis.conf
sudo stunnel redis-per-second.conf
.PHONY: docs_format
Expand Down
10 changes: 10 additions & 0 deletions src/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/tls"
"time"

"github.com/envoyproxy/ratelimit/src/utils"
"github.com/kelseyhightower/envconfig"
"google.golang.org/grpc"
)
Expand Down Expand Up @@ -58,6 +59,8 @@ type Settings struct {
RedisTls bool `envconfig:"REDIS_TLS" default:"false"`
// TODO: Make this setting configurable out of the box instead of having to provide it through code.
RedisTlsConfig *tls.Config
// Custom logic to allow CA Certs to be specified
RedisTlsCACerts string `envconfig:"REDIS_TLS_CA_CERTS" default:""`

// RedisPipelineWindow sets the duration after which internal pipelines will be flushed.
// If window is zero then implicit pipelining will be disabled. Radix use 150us for the
Expand Down Expand Up @@ -102,6 +105,13 @@ func NewSettings() Settings {
panic(err)
}

if s.RedisTlsCACerts != "" {
s.RedisTlsConfig, err = utils.GenerateTlsConfig(s.RedisTlsCACerts)
if err != nil {
panic(err)
}
}

return s
}

Expand Down
29 changes: 29 additions & 0 deletions src/utils/utilities.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package utils

import (
"crypto/tls"
"crypto/x509"
"io/ioutil"

pb "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v3"
"github.com/golang/protobuf/ptypes/duration"
logger "github.com/sirupsen/logrus"
)

// Interface for a time source.
Expand Down Expand Up @@ -41,3 +46,27 @@ func Max(a uint32, b uint32) uint32 {
}
return b
}

func GenerateTlsConfig(redisTlsCACerts string) (*tls.Config, error) {
// Get the SystemCertPool, continue with an empty pool on error
rootCAs, _ := x509.SystemCertPool()
if rootCAs == nil {
rootCAs = x509.NewCertPool()
}

// Read in the cert file
certs, err := ioutil.ReadFile(redisTlsCACerts)
if err != nil {
return nil, err
}

// Append our cert to the system pool
if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
logger.Warnf("No certs appended, using system certs only")
}

// Trust the augmented cert pool in our client
return &tls.Config{
RootCAs: rootCAs,
}, nil
}
29 changes: 28 additions & 1 deletion test/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/envoyproxy/ratelimit/src/memcached"
"github.com/envoyproxy/ratelimit/src/service_cmd/runner"
"github.com/envoyproxy/ratelimit/src/settings"
"github.com/envoyproxy/ratelimit/src/utils"
"github.com/envoyproxy/ratelimit/test/common"
"github.com/golang/protobuf/ptypes/duration"
"github.com/kelseyhightower/envconfig"
Expand Down Expand Up @@ -233,9 +234,35 @@ func TestMultiNodeMemcache(t *testing.T) {
})
}

func TestTlsConfigFailure(t *testing.T) {
s := makeSimpleRedisSettings(16381, 16382, false, 0)
s.RedisTlsConfig = nil
s.RedisAuth = "password123"
s.RedisTls = true
s.RedisPerSecondAuth = "password123"
s.RedisPerSecondTls = true

enable_local_cache := s.LocalCacheSizeInBytes > 0

// HACK: Wait for the server to come up. Make a hook that we can wait on
common.WaitForTcpPort(context.Background(), s.GrpcPort, 1*time.Second)

assert := assert.New(t)
conn, err := grpc.Dial(fmt.Sprintf("localhost:%v", s.GrpcPort), grpc.WithInsecure())
assert.NoError(err)
defer conn.Close()
c := pb.NewRateLimitServiceClient(conn)

// Assert this fails because of missing TLS CA Config Definition and the test Redis certs are self-signed
_, err = c.ShouldRateLimit(
context.Background(),
common.NewRateLimitRequest("reload", [][][2]string{{{getCacheKey("block", enable_local_cache), "foo"}}}, 1))
assert.EqualError(err, "rpc error: code = Unavailable desc = connection error: desc = \"transport: Error while dialing dial tcp 127.0.0.1:8083: connect: connection refused\"")
}

func testBasicConfigAuthTLS(perSecond bool, local_cache_size int) func(*testing.T) {
s := makeSimpleRedisSettings(16381, 16382, perSecond, local_cache_size)
s.RedisTlsConfig = nil
s.RedisTlsConfig, _ = utils.GenerateTlsConfig("/usr/local/share/ca-certificates/redis-stunnel.crt")
s.RedisAuth = "password123"
s.RedisTls = true
s.RedisPerSecondAuth = "password123"
Expand Down