From 04b365d484f5389af817f3f51f6ff93e18e72eb5 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Wed, 1 Jan 2020 16:58:54 +0100 Subject: [PATCH] Add support for gRPC transport credentials --- pkg/app/options.go | 2 +- pkg/app/options/corredor.go | 25 ++++++++++++++-- pkg/corredor/conn.go | 57 +++++++++++++++++++++++++++++++++++-- pkg/corredor/service.go | 7 +++++ 4 files changed, 85 insertions(+), 6 deletions(-) diff --git a/pkg/app/options.go b/pkg/app/options.go index b0da004a1..abc1f617b 100644 --- a/pkg/app/options.go +++ b/pkg/app/options.go @@ -38,7 +38,7 @@ func NewOptions(prefix ...string) *Options { Provision: *options.Provision(p), Sentry: *options.Sentry(p), Storage: *options.Storage(p), - Corredor: *options.Corredor(p), + Corredor: *options.Corredor(), Monitor: *options.Monitor(p), WaitFor: *options.WaitFor(p), HTTPServer: *options.HTTP(p), diff --git a/pkg/app/options/corredor.go b/pkg/app/options/corredor.go index 1a4aa0bf8..40067d953 100644 --- a/pkg/app/options/corredor.go +++ b/pkg/app/options/corredor.go @@ -1,6 +1,7 @@ package options import ( + "path" "time" ) @@ -18,22 +19,42 @@ type ( DefaultExecTimeout time.Duration `env:"CORREDOR_DEFAULT_EXEC_TIMEOUT"` + ListTimeout time.Duration `env:"CORREDOR_LIST_TIMEOUT"` + // Allow scripts to have runner explicitly defined RunAsEnabled bool `env:"CORREDOR_RUN_AS_ENABLED"` + + TlsCertEnabled bool `env:"CORREDOR_CLIENT_CERTIFICATES_ENABLED"` + TlsCertPath string `env:"CORREDOR_CLIENT_CERTIFICATES_PATH"` + TlsCertCA string `env:"CORREDOR_CLIENT_CERTIFICATES_CA"` + TlsCertPrivate string `env:"CORREDOR_CLIENT_CERTIFICATES_PUBLIC"` + TlsCertPublic string `env:"CORREDOR_CLIENT_CERTIFICATES_PRIVATE"` + TlsServerName string `env:"CORREDOR_CLIENT_CERTIFICATES_SERVER_NAME"` } ) -func Corredor(pfix string) (o *CorredorOpt) { +func Corredor() (o *CorredorOpt) { o = &CorredorOpt{ Enabled: true, RunAsEnabled: true, Addr: "corredor:80", MaxBackoffDelay: time.Minute, DefaultExecTimeout: time.Minute, + ListTimeout: time.Second * 2, Log: false, + + TlsCertEnabled: true, + TlsCertPath: "/corredor-certificates", + TlsCertCA: "ca.crt", + TlsCertPublic: "public.crt", + TlsCertPrivate: "private.key", } - fill(o, pfix) + fill(o, "") + + o.TlsCertCA = path.Join(o.TlsCertPath, o.TlsCertCA) + o.TlsCertPrivate = path.Join(o.TlsCertPath, o.TlsCertPrivate) + o.TlsCertPublic = path.Join(o.TlsCertPath, o.TlsCertPublic) return } diff --git a/pkg/corredor/conn.go b/pkg/corredor/conn.go index 2364f3d3c..329e0afd6 100644 --- a/pkg/corredor/conn.go +++ b/pkg/corredor/conn.go @@ -2,10 +2,17 @@ package corredor import ( "context" + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "os" + "github.com/pkg/errors" "go.uber.org/zap" "go.uber.org/zap/zapgrpc" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/grpclog" "github.com/cortezaproject/corteza-server/pkg/app/options" @@ -25,9 +32,53 @@ func NewConnection(ctx context.Context, opt options.CorredorOpt, logger *zap.Log grpclog.SetLogger(zapgrpc.NewLogger(logger.Named("grpc"))) } - var dialOpts = []grpc.DialOption{ - // @todo insecure? - grpc.WithInsecure(), + var ( + dialOpts = make([]grpc.DialOption, 0) + ) + + // opt.PublicKey = "" + if opt.TlsCertEnabled { + // Check paths + paths := map[string]string{ + "CA public key ": opt.TlsCertCA, + "client public key": opt.TlsCertPublic, + "client private key": opt.TlsCertPrivate, + } + for label, path := range paths { + if _, err = os.Stat(path); os.IsNotExist(err) { + return nil, fmt.Errorf("%s (%s) not found", label, path) + } else if err != nil { + return nil, fmt.Errorf("%s (%s) could not be loaded: %s", label, path, err) + } + } + + // Load the certificates from disk + certificate, err := tls.LoadX509KeyPair(opt.TlsCertPublic, opt.TlsCertPrivate) + if err != nil { + return nil, fmt.Errorf("could not load client key pair: %s", err) + } + + // Create a certificate pool from the certificate authority + certPool := x509.NewCertPool() + ca, err := ioutil.ReadFile(opt.TlsCertCA) + if err != nil { + return nil, fmt.Errorf("could not read ca certificate: %s", err) + } + + // Append the client certificates from the CA + if ok := certPool.AppendCertsFromPEM(ca); !ok { + return nil, errors.New("failed to append ca certs") + } + + crds := credentials.NewTLS(&tls.Config{ + ServerName: opt.TlsServerName, + Certificates: []tls.Certificate{certificate}, + RootCAs: certPool, + }) + + dialOpts = append(dialOpts, grpc.WithTransportCredentials(crds)) + } else { + dialOpts = append(dialOpts, grpc.WithInsecure()) } if opt.MaxBackoffDelay > 0 { diff --git a/pkg/corredor/service.go b/pkg/corredor/service.go index 4403a7e71..b1c00ef4c 100644 --- a/pkg/corredor/service.go +++ b/pkg/corredor/service.go @@ -199,6 +199,9 @@ func (svc *service) loadServerScripts(ctx context.Context) { svc.log.Debug("reloading server scripts") + ctx, cancel := context.WithTimeout(ctx, svc.opt.ListTimeout) + defer cancel() + rsp, err = svc.ssClient.List(ctx, &ServerScriptListRequest{}, grpc.WaitForReady(true)) if err != nil { svc.log.Error("could not load corredor server scripts", zap.Error(err)) @@ -476,6 +479,10 @@ func (svc *service) loadClientScripts(ctx context.Context) { ) svc.log.Debug("reloading client scripts") + + ctx, cancel := context.WithTimeout(ctx, svc.opt.ListTimeout) + defer cancel() + rsp, err = svc.csClient.List(ctx, &ClientScriptListRequest{}, grpc.WaitForReady(true)) if err != nil { svc.log.Error("could not load corredor client scripts", zap.Error(err))