feat(cache): redis TLS support (#1297)

This commit is contained in:
Taufik Mulyana
2022-01-18 20:16:00 +07:00
committed by GitHub
parent 02c3c3659d
commit cabd18daae
7 changed files with 108 additions and 6 deletions

View File

@@ -41,3 +41,14 @@ Two options:
``` ```
$ trivy server --cache-backend redis://localhost:6379 $ trivy server --cache-backend redis://localhost:6379
``` ```
Trivy also support for connecting to Redis using TLS, you only need to specify `--redis-ca` , `--redis-cert` , and `--redis-key` option.
```
$ trivy server --cache-backend redis://localhost:6379 \
--redis-ca /path/to/ca-cert.pem \
--redis-cert /path/to/cert.pem \
--redis-key /path/to/key.pem
```
TLS option for redis is hidden from Trivy command-line flag, but you still can use it.

View File

@@ -166,6 +166,27 @@ var (
EnvVars: []string{"TRIVY_CACHE_BACKEND"}, EnvVars: []string{"TRIVY_CACHE_BACKEND"},
} }
redisBackendCACert = cli.StringFlag{
Name: "redis-ca",
Usage: "redis ca file location, if using redis as cache backend",
EnvVars: []string{"TRIVY_REDIS_BACKEND_CA"},
Hidden: true,
}
redisBackendCert = cli.StringFlag{
Name: "redis-cert",
Usage: "redis certificate file location, if using redis as cache backend",
EnvVars: []string{"TRIVY_REDIS_BACKEND_CERT"},
Hidden: true,
}
redisBackendKey = cli.StringFlag{
Name: "redis-key",
Usage: "redis key file location, if using redis as cache backend",
EnvVars: []string{"TRIVY_REDIS_BACKEND_KEY"},
Hidden: true,
}
ignoreFileFlag = cli.StringFlag{ ignoreFileFlag = cli.StringFlag{
Name: "ignorefile", Name: "ignorefile",
Value: result.DefaultIgnoreFile, Value: result.DefaultIgnoreFile,
@@ -407,6 +428,9 @@ func NewImageCommand() *cli.Command {
&ignorePolicy, &ignorePolicy,
&listAllPackages, &listAllPackages,
&cacheBackendFlag, &cacheBackendFlag,
&redisBackendCACert,
&redisBackendCert,
&redisBackendKey,
&offlineScan, &offlineScan,
&insecureFlag, &insecureFlag,
stringSliceFlag(skipFiles), stringSliceFlag(skipFiles),
@@ -437,6 +461,9 @@ func NewFilesystemCommand() *cli.Command {
&securityChecksFlag, &securityChecksFlag,
&ignoreFileFlag, &ignoreFileFlag,
&cacheBackendFlag, &cacheBackendFlag,
&redisBackendCACert,
&redisBackendCert,
&redisBackendKey,
&timeoutFlag, &timeoutFlag,
&noProgressFlag, &noProgressFlag,
&ignorePolicy, &ignorePolicy,
@@ -472,6 +499,9 @@ func NewRootfsCommand() *cli.Command {
&securityChecksFlag, &securityChecksFlag,
&ignoreFileFlag, &ignoreFileFlag,
&cacheBackendFlag, &cacheBackendFlag,
&redisBackendCACert,
&redisBackendCert,
&redisBackendKey,
&timeoutFlag, &timeoutFlag,
&noProgressFlag, &noProgressFlag,
&ignorePolicy, &ignorePolicy,
@@ -510,6 +540,9 @@ func NewRepositoryCommand() *cli.Command {
&securityChecksFlag, &securityChecksFlag,
&ignoreFileFlag, &ignoreFileFlag,
&cacheBackendFlag, &cacheBackendFlag,
&redisBackendCACert,
&redisBackendCert,
&redisBackendKey,
&timeoutFlag, &timeoutFlag,
&noProgressFlag, &noProgressFlag,
&ignorePolicy, &ignorePolicy,
@@ -582,6 +615,9 @@ func NewServerCommand() *cli.Command {
&downloadDBOnlyFlag, &downloadDBOnlyFlag,
&resetFlag, &resetFlag,
&cacheBackendFlag, &cacheBackendFlag,
&redisBackendCACert,
&redisBackendCert,
&redisBackendKey,
// original flags // original flags
&token, &token,

View File

@@ -94,7 +94,7 @@ func runWithTimeout(ctx context.Context, opt Option, initializeScanner Initializ
func initFSCache(c Option) (cache.Cache, error) { func initFSCache(c Option) (cache.Cache, error) {
utils.SetCacheDir(c.CacheDir) utils.SetCacheDir(c.CacheDir)
cache, err := operation.NewCache(c.CacheBackend) cache, err := operation.NewCache(c.CacheOption)
if err != nil { if err != nil {
return operation.Cache{}, xerrors.Errorf("unable to initialize the cache: %w", err) return operation.Cache{}, xerrors.Errorf("unable to initialize the cache: %w", err)
} }

View File

@@ -2,6 +2,7 @@ package operation
import ( import (
"context" "context"
"crypto/tls"
"os" "os"
"strings" "strings"
@@ -11,6 +12,7 @@ import (
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/metadata" "github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/aquasecurity/trivy/pkg/db" "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/policy" "github.com/aquasecurity/trivy/pkg/policy"
@@ -30,13 +32,27 @@ type Cache struct {
} }
// NewCache is the factory method for Cache // NewCache is the factory method for Cache
func NewCache(backend string) (Cache, error) { func NewCache(c option.CacheOption) (Cache, error) {
if strings.HasPrefix(backend, "redis://") { if strings.HasPrefix(c.CacheBackend, "redis://") {
log.Logger.Infof("Redis cache: %s", backend) log.Logger.Infof("Redis cache: %s", c.CacheBackend)
options, err := redis.ParseURL(backend) options, err := redis.ParseURL(c.CacheBackend)
if err != nil { if err != nil {
return Cache{}, err return Cache{}, err
} }
if (option.RedisOption{}) != c.RedisOption {
caCert, cert, err := utils.GetTLSConfig(c.RedisCACert, c.RedisCert, c.RedisKey)
if err != nil {
return Cache{}, err
}
options.TLSConfig = &tls.Config{
RootCAs: caCert,
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
}
redisCache := cache.NewRedisCache(options) redisCache := cache.NewRedisCache(options)
return Cache{Cache: redisCache}, nil return Cache{Cache: redisCache}, nil
} }

View File

@@ -10,12 +10,25 @@ import (
// CacheOption holds the options for cache // CacheOption holds the options for cache
type CacheOption struct { type CacheOption struct {
CacheBackend string CacheBackend string
RedisOption
}
// RedisOption holds the options for redis cache
type RedisOption struct {
RedisCACert string
RedisCert string
RedisKey string
} }
// NewCacheOption returns an instance of CacheOption // NewCacheOption returns an instance of CacheOption
func NewCacheOption(c *cli.Context) CacheOption { func NewCacheOption(c *cli.Context) CacheOption {
return CacheOption{ return CacheOption{
CacheBackend: c.String("cache-backend"), CacheBackend: c.String("cache-backend"),
RedisOption: RedisOption{
RedisCACert: c.String("redis-ca"),
RedisCert: c.String("redis-cert"),
RedisKey: c.String("redis-key"),
},
} }
} }
@@ -27,5 +40,11 @@ func (c *CacheOption) Init() error {
c.CacheBackend != "fs" && c.CacheBackend != "" { c.CacheBackend != "fs" && c.CacheBackend != "" {
return xerrors.Errorf("unsupported cache backend: %s", c.CacheBackend) return xerrors.Errorf("unsupported cache backend: %s", c.CacheBackend)
} }
// if one of redis option not nil, make sure CA, cert, and key provided
if (RedisOption{}) != c.RedisOption {
if c.RedisCACert == "" || c.RedisCert == "" || c.RedisKey == "" {
return xerrors.Errorf("you must provide CA, cert and key file path when using tls")
}
}
return nil return nil
} }

View File

@@ -28,7 +28,7 @@ func run(c Config) (err error) {
// configure cache dir // configure cache dir
utils.SetCacheDir(c.CacheDir) utils.SetCacheDir(c.CacheDir)
cache, err := operation.NewCache(c.CacheBackend) cache, err := operation.NewCache(c.CacheOption)
if err != nil { if err != nil {
return xerrors.Errorf("server cache error: %w", err) return xerrors.Errorf("server cache error: %w", err)
} }

View File

@@ -1,6 +1,8 @@
package utils package utils
import ( import (
"crypto/tls"
"crypto/x509"
"fmt" "fmt"
"io" "io"
"os" "os"
@@ -65,3 +67,21 @@ func CopyFile(src, dst string) (int64, error) {
n, err := io.Copy(destination, source) n, err := io.Copy(destination, source)
return n, err return n, err
} }
// getTLSConfig get tls config from CA, Cert and Key file
func GetTLSConfig(caCertPath, certPath, keyPath string) (*x509.CertPool, tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCert, err := os.ReadFile(caCertPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
return caCertPool, cert, nil
}