From 2e9ab7a5fa29968a92c372e6cadf1bcb0e4e8264 Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Tue, 7 Mar 2023 16:07:28 +0100 Subject: [PATCH] kes: add support for API key authentication This commit adds support for KES<->KES authentication via API keys. Now, a KES edge server can authenticate to a KES server (stateful) via API keys - not just via TLS private key / certificate files. --- internal/keystore/kes/kes.go | 38 ++++++++++++++++++++++++++++-------- keserv/config.go | 16 +++++++++++++++ keserv/yml.go | 1 + server-config.yaml | 1 + 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/internal/keystore/kes/kes.go b/internal/keystore/kes/kes.go index 5ef200e2..6fa636b6 100644 --- a/internal/keystore/kes/kes.go +++ b/internal/keystore/kes/kes.go @@ -30,6 +30,11 @@ type Config struct { // If empty, the default enclave is used. Enclave string + // APIKey is an API key to authenticate to the + // KES server. Either an API key or a mTLS private + // key and certificate can be used for authentication. + APIKey kes.APIKey + // PrivateKey is a path to a file containing // a X.509 private key for mTLS authentication. PrivateKey string @@ -50,17 +55,34 @@ func Connect(ctx context.Context, config *Config) (*Conn, error) { if len(config.Endpoints) == 0 { return nil, errors.New("kes: no endpoints provided") } - if config.Certificate == "" { - return nil, errors.New("kes: no certificate provided") - } - if config.PrivateKey == "" { - return nil, errors.New("kes: no private key provided") + if config.APIKey != nil && (config.PrivateKey != "" || config.Certificate != "") { + return nil, errors.New("kes: ambiguous configuration: API key as well as mTLS private key and certificate provided") } - cert, err := https.CertificateFromFile(config.Certificate, config.PrivateKey, "") - if err != nil { - return nil, err + var ( + err error + cert tls.Certificate + ) + switch { + case config.APIKey != nil: + cert, err = kes.GenerateCertificate(config.APIKey) + if err != nil { + } + case config.PrivateKey != "" || config.Certificate != "": + if config.Certificate == "" { + return nil, errors.New("kes: no certificate provided") + } + if config.PrivateKey == "" { + return nil, errors.New("kes: no private key provided") + } + cert, err = https.CertificateFromFile(config.Certificate, config.PrivateKey, "") + if err != nil { + return nil, err + } + default: + return nil, errors.New("kes: no API key nor private key and certificate provided") } + var rootCAs *x509.CertPool if config.CAPath != "" { rootCAs, err = https.CertPoolFromFile(config.CAPath) diff --git a/keserv/config.go b/keserv/config.go index 96629f01..db641bd8 100644 --- a/keserv/config.go +++ b/keserv/config.go @@ -337,6 +337,10 @@ type KESConfig struct { // the default enclave name will be used. Enclave Env[string] + // APIKey is an API key to authenticate to the + // KES server. + APIKey Env[string] + // CertificateFile is a path to a mTLS client // certificate file used to authenticate to // the KES server. @@ -362,9 +366,19 @@ func (c *KESConfig) Connect(ctx context.Context) (kms.Conn, error) { for _, endpoint := range c.Endpoints { endpoints = append(endpoints, endpoint.Value) } + + var key kes.APIKey + if c.APIKey.Value != "" { + k, err := kes.ParseAPIKey(c.APIKey.Value) + if err != nil { + return nil, err + } + key = k + } return kesstore.Connect(ctx, &kesstore.Config{ Endpoints: endpoints, Enclave: c.Enclave.Value, + APIKey: key, Certificate: c.CertificateFile.Value, PrivateKey: c.PrivateKeyFile.Value, CAPath: c.CAPath.Value, @@ -373,6 +387,7 @@ func (c *KESConfig) Connect(ctx context.Context) (kms.Conn, error) { func (c *KESConfig) toYAML(yml *serverConfigYAML) { yml.KeyStore.KES.Endpoint = c.Endpoints + yml.KeyStore.KES.APIKey = c.APIKey yml.KeyStore.KES.TLS.Certificate = c.CertificateFile yml.KeyStore.KES.TLS.PrivateKey = c.PrivateKeyFile yml.KeyStore.KES.TLS.CAPath = c.CAPath @@ -381,6 +396,7 @@ func (c *KESConfig) toYAML(yml *serverConfigYAML) { func (c *KESConfig) fromYAML(yml *serverConfigYAML) { c.Endpoints = yml.KeyStore.KES.Endpoint c.Enclave = yml.KeyStore.KES.Enclave + c.APIKey = yml.KeyStore.KES.APIKey c.CertificateFile = yml.KeyStore.KES.TLS.Certificate c.PrivateKeyFile = yml.KeyStore.KES.TLS.PrivateKey c.CAPath = yml.KeyStore.KES.TLS.CAPath diff --git a/keserv/yml.go b/keserv/yml.go index 91dd1c98..a0c34a1a 100644 --- a/keserv/yml.go +++ b/keserv/yml.go @@ -64,6 +64,7 @@ type serverConfigYAML struct { KES struct { Endpoint []Env[string] `yaml:"endpoint,omitempty"` Enclave Env[string] `yaml:"enclave,omitempty"` + APIKey Env[string] `yaml:"api_key,omitempty"` TLS struct { Certificate Env[string] `yaml:"cert,omitempty"` PrivateKey Env[string] `yaml:"key,omitempty"` diff --git a/server-config.yaml b/server-config.yaml index facab797..2f2ce286 100644 --- a/server-config.yaml +++ b/server-config.yaml @@ -189,6 +189,7 @@ keystore: endpoint: - "" # The endpoint (or list of endpoints) to the KES server(s) enclave: "" # An optional enclave name. If empty, the default enclave will be used + api_key: "" # The KES API key to authenticate to KES server tls: # The KES mTLS authentication credentials - i.e. client certificate. cert: "" # Path to the TLS client certificate for mTLS authentication key: "" # Path to the TLS client private key for mTLS authentication