[full-ci] Introduce TLS Settings for go-micro based grpc services and clients (#4901)

* Introduce TLS Settings for go-micro based grpc services and clients

TLS for the services can be configure by setting the OCIS_MICRO_GRPC_TLS_ENABLED"
"OCIS_MICRO_GRPC_TLS_CERTIFICATE" and "OCIS_MICRO_GRPC_TLS_KEY"
enviroment variables.

TLS for the clients can configured by setting the "OCIS_MICRO_GRPC_CLIENT_TLS_MODE"
and "OCIS_MICRO_GRPC_CLIENT_TLS_CACERT" variables.

By default TLS is disabled.

Co-authored-by: Martin <github@diemattels.at>

* Unify TLS configuration for all grpc services

All grpc service (whether they're based on reva) or go-micro use the
same set of config vars now.

TLS for the services can be configure by setting the OCIS_GRPC_TLS_ENABLED,
OCIS_GRPC_TLS_CERTIFICATE and OCIS_GRPC_TLS_KEY enviroment variables.

TLS for the clients can configured by setting the OCIS_GRPC_CLIENT_TLS_MODE
and OCIS_MICRO_GRPC_CLIENT_TLS_CACERT variables.

There are no individual per service config vars currently. If really
needed, per service tls configurations can be specified via config file.

Co-authored-by: Martin <github@diemattels.at>

Co-authored-by: Martin <github@diemattels.at>
This commit is contained in:
Ralf Haferkamp
2022-11-03 10:17:08 +01:00
committed by GitHub
co-authored by Martin
parent b7482e5410
commit ee974afebf
91 changed files with 746 additions and 313 deletions
+5 -3
View File
@@ -56,9 +56,11 @@ type Runtime struct {
type Config struct {
*shared.Commons `yaml:"shared"`
Tracing *shared.Tracing `yaml:"tracing"`
Log *shared.Log `yaml:"log"`
CacheStore *shared.CacheStore `yaml:"cache_store"`
Tracing *shared.Tracing `yaml:"tracing"`
Log *shared.Log `yaml:"log"`
CacheStore *shared.CacheStore `yaml:"cache_store"`
GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`
GRPCServiceTLS *shared.GRPCServiceTLS `yaml:"grpc_service_tls"`
Mode Mode // DEPRECATED
File string
+15
View File
@@ -51,6 +51,13 @@ func EnsureDefaults(cfg *config.Config) {
if cfg.CacheStore == nil {
cfg.CacheStore = &shared.CacheStore{}
}
if cfg.GRPCClientTLS == nil {
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
}
if cfg.GRPCServiceTLS == nil {
cfg.GRPCServiceTLS = &shared.GRPCServiceTLS{}
}
}
// EnsureCommons copies applicable parts of the oCIS config into the commons part
@@ -94,6 +101,14 @@ func EnsureCommons(cfg *config.Config) {
cfg.Commons.CacheStore = &shared.CacheStore{}
}
if cfg.GRPCClientTLS != nil {
cfg.Commons.GRPCClientTLS = cfg.GRPCClientTLS
}
if cfg.GRPCServiceTLS != nil {
cfg.Commons.GRPCServiceTLS = cfg.GRPCServiceTLS
}
// copy token manager to the commons part if set
if cfg.TokenManager != nil {
cfg.Commons.TokenManager = cfg.TokenManager
+101
View File
@@ -0,0 +1,101 @@
package grpc
import (
"crypto/tls"
"crypto/x509"
"errors"
"io/ioutil"
"sync"
mgrpcc "github.com/go-micro/plugins/v4/client/grpc"
mbreaker "github.com/go-micro/plugins/v4/wrapper/breaker/gobreaker"
"github.com/owncloud/ocis/v2/ocis-pkg/registry"
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
"go-micro.dev/v4/client"
)
var (
defaultClient client.Client
once sync.Once
)
// ClientOptions represent options (e.g. tls settings) for the grpc clients
type ClientOptions struct {
tlsMode string
caCert string
}
// Option is used to pass client options
type ClientOption func(opts *ClientOptions)
// WithTLSMode allows to set the TLSMode option for grpc clients
func WithTLSMode(v string) ClientOption {
return func(o *ClientOptions) {
o.tlsMode = v
}
}
// WithTLSCACert allows to set the CA Certificate for grpc clients
func WithTLSCACert(v string) ClientOption {
return func(o *ClientOptions) {
o.caCert = v
}
}
// Configure configures the default oOCIS grpc client (e.g. TLS settings)
func Configure(opts ...ClientOption) error {
var options ClientOptions
for _, opt := range opts {
opt(&options)
}
var outerr error
once.Do(func() {
reg := registry.GetRegistry()
var tlsConfig *tls.Config
cOpts := []client.Option{
client.Registry(reg),
client.Wrap(mbreaker.NewClientWrapper()),
}
switch options.tlsMode {
case "insecure":
tlsConfig = &tls.Config{
InsecureSkipVerify: true,
}
cOpts = append(cOpts, mgrpcc.AuthTLS(tlsConfig))
case "on":
tlsConfig = &tls.Config{}
// Note: If caCert is empty we use the system's default set of trusted CAs
if options.caCert != "" {
certs := x509.NewCertPool()
pemData, err := ioutil.ReadFile(options.caCert)
if err != nil {
outerr = err
return
}
if !certs.AppendCertsFromPEM(pemData) {
outerr = errors.New("Error initializing LDAP Backend. Adding CA cert failed")
return
}
tlsConfig.RootCAs = certs
}
cOpts = append(cOpts, mgrpcc.AuthTLS(tlsConfig))
}
defaultClient = mgrpcc.NewClient(cOpts...)
})
return outerr
}
// DefaultClient returns a custom oCIS grpc configured client.
func DefaultClient() client.Client {
return defaultClient
}
func GetClientOptions(t *shared.GRPCClientTLS) []ClientOption {
opts := []ClientOption{
WithTLSMode(t.Mode),
WithTLSCACert(t.CACert),
}
return opts
}
+25 -7
View File
@@ -12,13 +12,16 @@ type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger log.Logger
Namespace string
Name string
Version string
Address string
Context context.Context
Flags []cli.Flag
Logger log.Logger
Namespace string
Name string
Version string
Address string
TLSEnabled bool
TLSCert string
TLSKey string
Context context.Context
Flags []cli.Flag
}
// newOptions initializes the available default options.
@@ -69,6 +72,21 @@ func Address(a string) Option {
}
}
// TLSEnabled provides a function to enable/disable TLS
func TLSEnabled(v bool) Option {
return func(o *Options) {
o.TLSEnabled = v
}
}
// TLSCert provides a function to set the TLS server certificate and key
func TLSCert(c string, k string) Option {
return func(o *Options) {
o.TLSCert = c
o.TLSKey = k
}
}
// Context provides a function to set the context option.
func Context(ctx context.Context) Option {
return func(o *Options) {
+40 -29
View File
@@ -1,54 +1,65 @@
package grpc
import (
"crypto/tls"
"fmt"
"net"
"strings"
"sync"
"time"
mgrpcc "github.com/go-micro/plugins/v4/client/grpc"
mgrpcs "github.com/go-micro/plugins/v4/server/grpc"
mbreaker "github.com/go-micro/plugins/v4/wrapper/breaker/gobreaker"
"github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus"
"github.com/go-micro/plugins/v4/wrapper/trace/opencensus"
"github.com/owncloud/ocis/v2/ocis-pkg/registry"
"go-micro.dev/v4"
"go-micro.dev/v4/client"
"go-micro.dev/v4/server"
mtls "go-micro.dev/v4/util/tls"
)
// DefaultClient is a custom oCIS grpc configured client.
var (
defaultClient client.Client
once sync.Once
)
func DefaultClient() client.Client {
return getDefaultGrpcClient()
}
func getDefaultGrpcClient() client.Client {
once.Do(func() {
reg := registry.GetRegistry()
defaultClient = mgrpcc.NewClient(
client.Registry(reg),
client.Wrap(mbreaker.NewClientWrapper()),
)
})
return defaultClient
}
// Service simply wraps the go-micro grpc service.
type Service struct {
micro.Service
}
// NewService initializes a new grpc service.
func NewService(opts ...Option) Service {
func NewService(opts ...Option) (Service, error) {
var mServer server.Server
sopts := newOptions(opts...)
tlsConfig := &tls.Config{}
if sopts.TLSEnabled {
var cert tls.Certificate
var err error
if sopts.TLSCert != "" {
cert, err = tls.LoadX509KeyPair(sopts.TLSCert, sopts.TLSKey)
if err != nil {
sopts.Logger.Error().Err(err).Str("cert", sopts.TLSCert).Str("key", sopts.TLSKey).Msg("error loading server certifcate and key")
return Service{}, fmt.Errorf("grpc service error loading server certificate and key: %w", err)
}
} else {
// Generate a self-signed server certificate on the fly. This requires the clients
// to connect with InsecureSkipVerify.
subj := []string{sopts.Address}
if host, _, err := net.SplitHostPort(sopts.Address); err == nil && host != "" {
subj = []string{host}
}
sopts.Logger.Warn().Str("address", sopts.Address).
Msg("GRPC: No server certificate configured. Generating a temporary self-signed certificate")
cert, err = mtls.Certificate(subj...)
if err != nil {
return Service{}, fmt.Errorf("grpc service error creating temporary self-signed certificate: %w", err)
}
}
tlsConfig.Certificates = []tls.Certificate{cert}
mServer = mgrpcs.NewServer(mgrpcs.AuthTLS(tlsConfig))
} else {
mServer = mgrpcs.NewServer()
}
mopts := []micro.Option{
// first add a server because it will reset any options
micro.Server(mgrpcs.NewServer()),
micro.Server(mServer),
// also add a client that can be used after initializing the service
micro.Client(DefaultClient()),
micro.Address(sopts.Address),
@@ -65,5 +76,5 @@ func NewService(opts ...Option) Service {
micro.WrapSubscriber(opencensus.NewSubscriberWrapper()),
}
return Service{micro.NewService(mopts...)}
return Service{micro.NewService(mopts...)}, nil
}
+3 -3
View File
@@ -13,7 +13,7 @@ func DefaultRevaConfig() *Reva {
}
func (r *Reva) GetRevaOptions() []pool.Option {
tm, _ := pool.StringToTLSMode(r.TLSMode)
tm, _ := pool.StringToTLSMode(r.TLS.Mode)
opts := []pool.Option{
pool.WithTLSMode(tm),
}
@@ -22,7 +22,7 @@ func (r *Reva) GetRevaOptions() []pool.Option {
func (r *Reva) GetGRPCClientConfig() map[string]interface{} {
return map[string]interface{}{
"tls_mode": r.TLSMode,
"tls_cacert": r.TLSCACert,
"tls_mode": r.TLS.Mode,
"tls_cacert": r.TLS.CACert,
}
}
+26 -14
View File
@@ -31,9 +31,19 @@ type TokenManager struct {
// Reva defines all available REVA client configuration.
type Reva struct {
Address string `yaml:"address" env:"REVA_GATEWAY" desc:"The CS3 gateway endpoint."`
TLSMode string `yaml:"tls_mode" env:"REVA_GATEWAY_TLS_MODE" desc:"TLS mode for grpc connection to the CS3 gateway endpoint. Possible values are 'off', 'insecure' and 'on'. 'off': disables transport security for the clients. 'insecure' allows to use transport security, but disables certificate verification (to be used with the autogenerated self-signed certificates). 'on' enables transport security, including server ceritificate verification."`
TLSCACert string `yaml:"tls_cacert" env:"REVA_GATEWAY_TLS_CACERT" desc:"The root CA certificate used to validate the gateway's TLS certificate."`
Address string `yaml:"address" env:"REVA_GATEWAY" desc:"The CS3 gateway endpoint."`
TLS GRPCClientTLS `yaml:"tls"`
}
type GRPCClientTLS struct {
Mode string `yaml:"mode" env:"OCIS_GRPC_CLIENT_TLS_MODE" desc:"TLS mode for grpc connection to the go-micro based grpc services. Possible values are 'off', 'insecure' and 'on'. 'off': disables transport security for the clients. 'insecure' allows to use transport security, but disables certificate verification (to be used with the autogenerated self-signed certificates). 'on' enables transport security, including server ceritificate verification."`
CACert string `yaml:"cacert env:"OCIS_GRPC_CLIENT_TLS_CACERT" desc:"The root CA certificate used to validate TLS server certificates of the go-micro based grpc services."`
}
type GRPCServiceTLS struct {
Enabled bool `yaml:"enabled" env:"OCIS_GRPC_TLS_ENABLED" desc:"Activates TLS for the grpcs based services using the server certifcate and key configured via OCIS_GRPC_TLS_CERTIFICATE and OCIS_GRPC_TLS_KEY. If OCIS_GRPC_TLS_CERTIFICATE is not set a temporary server certificate is generated - to be used with OCIS_GRPC_CLIENT_TLS_MODE=insecure."`
Cert string `yaml:"cert" env:"OCIS_GRPC_TLS_CERTIFICATE" desc:"Path/File name of the TLS server certificate (in PEM format) for the grpc services."`
Key string `yaml:"key" env:"OCIS_GRPC_TLS_KEY" desc:"Path/File name for the TLS certificate key (in PEM format) for the server certificate to use for the grpc services."`
}
type CacheStore struct {
@@ -45,15 +55,17 @@ type CacheStore struct {
// Commons holds configuration that are common to all extensions. Each extension can then decide whether
// to overwrite its values.
type Commons struct {
Log *Log `yaml:"log"`
Tracing *Tracing `yaml:"tracing"`
CacheStore *CacheStore `yaml:"cache_store"`
OcisURL string `yaml:"ocis_url" env:"OCIS_URL" desc:"URL, where oCIS is reachable for users."`
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
Reva *Reva `yaml:"reva"`
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services."`
TransferSecret string `mask:"password" yaml:"transfer_secret,omitempty" env:"REVA_TRANSFER_SECRET"`
SystemUserID string `yaml:"system_user_id" env:"OCIS_SYSTEM_USER_ID" desc:"ID of the oCIS storage-system system user. Admins need to set the ID for the storage-system system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format."`
SystemUserAPIKey string `mask:"password" yaml:"system_user_api_key" env:"SYSTEM_USER_API_KEY"`
AdminUserID string `yaml:"admin_user_id" env:"OCIS_ADMIN_USER_ID" desc:"ID of a user, that should receive admin privileges."`
Log *Log `yaml:"log"`
Tracing *Tracing `yaml:"tracing"`
CacheStore *CacheStore `yaml:"cache_store"`
GRPCClientTLS *GRPCClientTLS `yaml:"grpc_client_tls"`
GRPCServiceTLS *GRPCServiceTLS `yaml:"grpc_service_tls"`
OcisURL string `yaml:"ocis_url" env:"OCIS_URL" desc:"URL, where oCIS is reachable for users."`
TokenManager *TokenManager `mask:"struct" yaml:"token_manager"`
Reva *Reva `yaml:"reva"`
MachineAuthAPIKey string `mask:"password" yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services."`
TransferSecret string `mask:"password" yaml:"transfer_secret,omitempty" env:"REVA_TRANSFER_SECRET"`
SystemUserID string `yaml:"system_user_id" env:"OCIS_SYSTEM_USER_ID" desc:"ID of the oCIS storage-system system user. Admins need to set the ID for the storage-system system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format."`
SystemUserAPIKey string `mask:"password" yaml:"system_user_api_key" env:"SYSTEM_USER_API_KEY"`
AdminUserID string `yaml:"admin_user_id" env:"OCIS_ADMIN_USER_ID" desc:"ID of a user, that should receive admin privileges."`
}