1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Feature] Integration TLS (#1710)

This commit is contained in:
Adam Janikowski 2024-09-02 08:40:32 +02:00 committed by GitHub
parent 1095567432
commit efbbc79439
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 672 additions and 180 deletions

View file

@ -23,100 +23,102 @@ linters-settings:
importas:
no-unaliased: true
alias:
- pkg: k8s.io/api/core/v1
alias: core
- pkg: k8s.io/apimachinery/pkg/apis/meta/v1
alias: meta
- pkg: k8s.io/client-go/kubernetes/typed/core/v1
alias: typedCore
- pkg: k8s.io/api/apps/v1
alias: apps
- pkg: k8s.io/api/batch/v1
alias: batch
- pkg: k8s.io/api/storage/v1
alias: storage
- pkg: github.com/arangodb/kube-arangodb/pkg/deployment/reconcile/shared
alias: sharedReconcile
- pkg: k8s.io/api/policy/v1
alias: policy
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/shared/v1
alias: sharedApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1
alias: schedulerApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/profiles
alias: schedulerProfiles
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container
alias: schedulerContainerApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container/resources
alias: schedulerContainerResourcesApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/pod
alias: schedulerPodApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/pod/resources
alias: schedulerPodResourcesApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1
alias: schedulerApiv1alpha1
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/profiles
alias: schedulerProfilesv1alpha1
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/container
alias: schedulerContainerApiv1alpha1
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/container/resources
alias: schedulerContainerResourcesApiv1alpha1
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/pod
alias: schedulerPodApiv1alpha1
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/pod/resources
alias: schedulerPodResourcesApiv1alpha1
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/shared
alias: shared
- pkg: github.com/arangodb/kube-arangodb/pkg/handlers/enterprise/analytics/shared
alias: analyticsShared
- pkg: github.com/arangodb/kube-arangodb/pkg/handlers/enterprise/shared
alias: enterpriseShared
- pkg: github.com/arangodb/kube-arangodb/pkg/handlers/enterprise/ml/shared
alias: mlShared
- pkg: github.com/arangodb/kube-arangodb/pkg/handlers/enterprise/ml/shared/test
alias: mlSharedTests
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/analytics/v1alpha1
alias: analyticsApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/networking/v1alpha1
alias: networkingApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/ml/v1beta1
alias: mlApi
- pkg: github.com/arangodb/kube-arangodb/pkg/apis/ml/v1alpha1
alias: mlApiv1alpha1
- pkg: github.com/arangodb/kube-arangodb/integrations/scheduler/v1/definition
alias: pbSchedulerV1
- pkg: github.com/arangodb/kube-arangodb/integrations/scheduler/v1
alias: pbImplSchedulerV1
- pkg: github.com/arangodb/kube-arangodb/integrations/shutdown/v1/definition
alias: pbShutdownV1
- pkg: github.com/arangodb/kube-arangodb/integrations/shutdown/v1
alias: pbImplShutdownV1
- pkg: github.com/arangodb/kube-arangodb/integrations/authentication/v1/definition
alias: pbAuthenticationV1
- pkg: github.com/arangodb/kube-arangodb/integrations/authentication/v1
alias: pbImplAuthenticationV1
- pkg: github.com/arangodb/kube-arangodb/integrations/authorization/v0/definition
alias: pbAuthorizationV0
- pkg: github.com/arangodb/kube-arangodb/integrations/authorization/v0
alias: pbImplAuthorizationV0
- pkg: github.com/arangodb/kube-arangodb/integrations/config/v1/definition
alias: pbConfigV1
- pkg: github.com/arangodb/kube-arangodb/integrations/config/v1
alias: pbImplConfigV1
- pkg: github.com/arangodb/kube-arangodb/integrations/pong/v1/definition
alias: pbPongV1
- alias: pbImplAuthenticationV1
pkg: github.com/arangodb/kube-arangodb/integrations/authentication/v1
- alias: pbAuthenticationV1
pkg: github.com/arangodb/kube-arangodb/integrations/authentication/v1/definition
- alias: pbImplAuthorizationV0
pkg: github.com/arangodb/kube-arangodb/integrations/authorization/v0
- alias: pbAuthorizationV0
pkg: github.com/arangodb/kube-arangodb/integrations/authorization/v0/definition
- alias: pbImplConfigV1
pkg: github.com/arangodb/kube-arangodb/integrations/config/v1
- alias: pbConfigV1
pkg: github.com/arangodb/kube-arangodb/integrations/config/v1/definition
- alias: pbImplEnvoyAuthV3
pkg: github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3
- pkg: github.com/arangodb/kube-arangodb/integrations/pong/v1
alias: pbImplPongV1
- pkg: github.com/arangodb/kube-arangodb/integrations/shared/v1/definition
alias: pbSharedV1
- pkg: github.com/arangodb/kube-arangodb/integrations/shared/v1
alias: pbImplSharedV1
- pkg: github.com/envoyproxy/go-control-plane/envoy/service/auth/v3
alias: pbEnvoyAuthV3
- pkg: github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3
alias: pbImplEnvoyAuthV3
- pkg: github.com/arangodb/kube-arangodb/pkg/util/k8sutil/resources
alias: kresources
- pkg: github.com/arangodb/kube-arangodb/integrations/pong/v1/definition
alias: pbPongV1
- alias: pbImplSchedulerV1
pkg: github.com/arangodb/kube-arangodb/integrations/scheduler/v1
- alias: pbSchedulerV1
pkg: github.com/arangodb/kube-arangodb/integrations/scheduler/v1/definition
- alias: pbImplSharedV1
pkg: github.com/arangodb/kube-arangodb/integrations/shared/v1
- alias: pbSharedV1
pkg: github.com/arangodb/kube-arangodb/integrations/shared/v1/definition
- alias: pbImplShutdownV1
pkg: github.com/arangodb/kube-arangodb/integrations/shutdown/v1
- alias: pbShutdownV1
pkg: github.com/arangodb/kube-arangodb/integrations/shutdown/v1/definition
- alias: analyticsApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/analytics/v1alpha1
- alias: mlApiv1alpha1
pkg: github.com/arangodb/kube-arangodb/pkg/apis/ml/v1alpha1
- alias: mlApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/ml/v1beta1
- alias: networkingApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/networking/v1alpha1
- alias: schedulerApiv1alpha1
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1
- alias: schedulerContainerApiv1alpha1
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/container
- alias: schedulerContainerResourcesApiv1alpha1
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/container/resources
- alias: schedulerPodApiv1alpha1
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/pod
- alias: schedulerPodResourcesApiv1alpha1
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/pod/resources
- alias: schedulerProfilesv1alpha1
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/profiles
- alias: schedulerApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1
- alias: schedulerContainerApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container
- alias: schedulerContainerResourcesApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container/resources
- alias: schedulerPodApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/pod
- alias: schedulerPodResourcesApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/pod/resources
- alias: schedulerProfiles
pkg: github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/profiles
- alias: shared
pkg: github.com/arangodb/kube-arangodb/pkg/apis/shared
- alias: sharedApi
pkg: github.com/arangodb/kube-arangodb/pkg/apis/shared/v1
- alias: sharedReconcile
pkg: github.com/arangodb/kube-arangodb/pkg/deployment/reconcile/shared
- alias: analyticsShared
pkg: github.com/arangodb/kube-arangodb/pkg/handlers/enterprise/analytics/shared
- alias: mlShared
pkg: github.com/arangodb/kube-arangodb/pkg/handlers/enterprise/ml/shared
- alias: mlSharedTests
pkg: github.com/arangodb/kube-arangodb/pkg/handlers/enterprise/ml/shared/test
- alias: enterpriseShared
pkg: github.com/arangodb/kube-arangodb/pkg/handlers/enterprise/shared
- alias: kresources
pkg: github.com/arangodb/kube-arangodb/pkg/util/k8sutil/resources
- alias: ktls
pkg: github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls
- alias: pbEnvoyAuthV3
pkg: github.com/envoyproxy/go-control-plane/envoy/service/auth/v3
- alias: apps
pkg: k8s.io/api/apps/v1
- alias: batch
pkg: k8s.io/api/batch/v1
- alias: core
pkg: k8s.io/api/core/v1
- alias: policy
pkg: k8s.io/api/policy/v1
- alias: storage
pkg: k8s.io/api/storage/v1
- alias: meta
pkg: k8s.io/apimachinery/pkg/apis/meta/v1
- alias: typedCore
pkg: k8s.io/client-go/kubernetes/typed/core/v1
gci:
sections:
- standard

View file

@ -20,6 +20,7 @@
- (Feature) Custom Gateway image
- (Bugfix) Fix race condition in ArangoBackup
- (Feature) Improve Gateway Config gen
- (Feature) Integration Service TLS
## [1.2.42](https://github.com/arangodb/kube-arangodb/tree/1.2.42) (2024-07-23)
- (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries

View file

@ -22,6 +22,7 @@ Flags:
--health.auth.token string Token for health service (when auth service is token)
--health.auth.type string Auth type for health service (default "None")
--health.shutdown.enabled Determines if shutdown service should be enabled and exposed (default true)
--health.tls.keyfile string Path to the keyfile
-h, --help help for arangodb_operator_integration
--integration.authentication.v1 Enable AuthenticationV1 Integration Service
--integration.authentication.v1.enabled Defines if Authentication is enabled (default true)
@ -74,6 +75,8 @@ Flags:
--services.external.auth.token string Token for external service (when auth service is token)
--services.external.auth.type string Auth type for external service (default "None")
--services.external.enabled Defines if external access is enabled
--services.external.tls.keyfile string Path to the keyfile
--services.tls.keyfile string Path to the keyfile
Use "arangodb_operator_integration [command] --help" for more information about a command.
```
@ -94,6 +97,10 @@ Available Commands:
Flags:
--address string GRPC Service Address (default "127.0.0.1:8080")
-h, --help help for client
--tls.ca string Path to the custom CA
--tls.enabled Defines if GRPC is protected with TLS
--tls.fallback Enables TLS Fallback
--tls.insecure Enables Insecure TLS Connection
--token string GRPC Token
Use "arangodb_operator_integration client [command] --help" for more information about a command.

View file

@ -29,7 +29,6 @@ import (
pbPongV1 "github.com/arangodb/kube-arangodb/integrations/pong/v1/definition"
pbSharedV1 "github.com/arangodb/kube-arangodb/integrations/shared/v1/definition"
pbShutdownV1 "github.com/arangodb/kube-arangodb/integrations/shutdown/v1/definition"
"github.com/arangodb/kube-arangodb/pkg/util/svc"
)
@ -45,7 +44,7 @@ type impl struct {
}
func (i *impl) Name() string {
return pbShutdownV1.Name
return pbPongV1.Name
}
func (i *impl) Health() svc.HealthState {

View file

@ -49,7 +49,7 @@ const (
)
const (
defaultTLSTTL = Duration("2610h") // About 3 month
DefaultTLSTTL = Duration("2610h") // About 3 month
)
// TLSSpec holds TLS specific configuration settings
@ -157,7 +157,7 @@ func (s *TLSSpec) SetDefaults(defaultCASecretName string) {
if s.GetTTL() == "" {
// Note that we don't check for nil here, since even a specified, but zero
// should result in the default value.
s.TTL = NewDuration(defaultTLSTTL)
s.TTL = NewDuration(DefaultTLSTTL)
}
}

View file

@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -60,6 +60,6 @@ func TestTLSSpecSetDefaults(t *testing.T) {
assert.Equal(t, "foo", def(TLSSpec{CASecretName: util.NewType[string]("foo")}).GetCASecretName())
assert.Len(t, def(TLSSpec{}).GetAltNames(), 0)
assert.Len(t, def(TLSSpec{AltNames: []string{"foo.local"}}).GetAltNames(), 1)
assert.Equal(t, defaultTLSTTL, def(TLSSpec{}).GetTTL())
assert.Equal(t, DefaultTLSTTL, def(TLSSpec{}).GetTTL())
assert.Equal(t, time.Hour, def(TLSSpec{TTL: NewDuration("1h")}).GetTTL().AsDuration())
}

View file

@ -49,7 +49,7 @@ const (
)
const (
defaultTLSTTL = Duration("2610h") // About 3 month
DefaultTLSTTL = Duration("2610h") // About 3 month
)
// TLSSpec holds TLS specific configuration settings
@ -157,7 +157,7 @@ func (s *TLSSpec) SetDefaults(defaultCASecretName string) {
if s.GetTTL() == "" {
// Note that we don't check for nil here, since even a specified, but zero
// should result in the default value.
s.TTL = NewDuration(defaultTLSTTL)
s.TTL = NewDuration(DefaultTLSTTL)
}
}

View file

@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -60,6 +60,6 @@ func TestTLSSpecSetDefaults(t *testing.T) {
assert.Equal(t, "foo", def(TLSSpec{CASecretName: util.NewType[string]("foo")}).GetCASecretName())
assert.Len(t, def(TLSSpec{}).GetAltNames(), 0)
assert.Len(t, def(TLSSpec{AltNames: []string{"foo.local"}}).GetAltNames(), 1)
assert.Equal(t, defaultTLSTTL, def(TLSSpec{}).GetTTL())
assert.Equal(t, DefaultTLSTTL, def(TLSSpec{}).GetTTL())
assert.Equal(t, time.Hour, def(TLSSpec{TTL: NewDuration("1h")}).GetTTL().AsDuration())
}

View file

@ -47,7 +47,7 @@ import (
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
memberTls "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
ktls "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tools"
"github.com/arangodb/kube-arangodb/pkg/util/strings"
)
@ -542,13 +542,13 @@ func (r *Reconciler) keyfileRenewalRequired(ctx context.Context, apiObject k8sut
}
// Verify AltNames
var altNames memberTls.KeyfileInput
var altNames ktls.KeyfileInput
switch group.Type() {
case api.ServerGroupTypeArangoD:
altNames, err = memberTls.GetServerAltNames(apiObject, spec, tlsSpec, service, group, member)
altNames, err = ktls.GetServerAltNames(apiObject, spec, tlsSpec, service, group, member)
case api.ServerGroupTypeArangoSync:
altNames, err = memberTls.GetSyncAltNames(apiObject, spec, tlsSpec, group, member)
altNames, err = ktls.GetSyncAltNames(apiObject, spec, tlsSpec, group, member)
default:
assertion.InvalidGroupKey.Assert(true, "Unable to check TLS Key Renewal for an unknown group: %s", group.AsRole())
return false, false

View file

@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,36 +23,23 @@ package resources
import (
"context"
"fmt"
"time"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
certificates "github.com/arangodb-helper/go-certificates"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
secretv1 "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret/v1"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
)
const (
clientAuthECDSACurve = "P256" // This curve is the default that ArangoDB accepts and plenty strong
ktls "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
)
// createClientAuthCACertificate creates a client authentication CA certificate and stores it in a secret with name
// specified in the given spec.
func (r *Resources) createClientAuthCACertificate(ctx context.Context, secrets secretv1.ModInterface, spec api.SyncAuthenticationSpec, deploymentName string, ownerRef *meta.OwnerReference) error {
log := r.log.Str("section", "secrets")
options := certificates.CreateCertificateOptions{
CommonName: fmt.Sprintf("%s Client Authentication Root Certificate", deploymentName),
ValidFrom: time.Now(),
ValidFor: caTTL,
IsCA: true,
IsClientAuth: true,
ECDSACurve: clientAuthECDSACurve,
}
cert, priv, err := certificates.CreateCertificate(options, nil)
cert, priv, err := ktls.CreateTLSCACertificate(fmt.Sprintf("%s Client Authentication Root Certificate", deploymentName))
if err != nil {
log.Err(err).Str("name", spec.GetClientCASecretName()).Debug("Failed to create CA certificate")
return errors.WithStack(err)

View file

@ -23,27 +23,19 @@ package resources
import (
"context"
"fmt"
"strings"
"time"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
certificates "github.com/arangodb-helper/go-certificates"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/logging"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/globals"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
secretv1 "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret/v1"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
)
const (
caTTL = time.Hour * 24 * 365 * 10 // 10 year
tlsECDSACurve = "P256" // This curve is the default that ArangoDB accepts and plenty strong
ktls "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
)
// createTLSCACertificate creates a CA certificate and stores it in a secret with name
@ -52,18 +44,12 @@ func (r *Resources) createTLSCACertificate(ctx context.Context, secrets secretv1
deploymentName string, ownerRef *meta.OwnerReference) error {
log := r.log.Str("section", "tls").Str("secret", spec.GetCASecretName())
options := certificates.CreateCertificateOptions{
CommonName: fmt.Sprintf("%s Root Certificate", deploymentName),
ValidFrom: time.Now(),
ValidFor: caTTL,
IsCA: true,
ECDSACurve: tlsECDSACurve,
}
cert, priv, err := certificates.CreateCertificate(options, nil)
cert, priv, err := ktls.CreateTLSCACertificate(fmt.Sprintf("%s Root Certificate", deploymentName))
if err != nil {
log.Err(err).Debug("Failed to create CA certificate")
return errors.WithStack(err)
}
if err := k8sutil.CreateCASecret(ctx, secrets, spec.GetCASecretName(), cert, priv, ownerRef); err != nil {
if kerrors.IsAlreadyExists(err) {
log.Debug("CA Secret already exists")
@ -78,9 +64,13 @@ func (r *Resources) createTLSCACertificate(ctx context.Context, secrets secretv1
// createTLSServerCertificate creates a TLS certificate for a specific server and stores
// it in a secret with the given name.
func createTLSServerCertificate(ctx context.Context, log logging.Logger, cachedStatus inspectorInterface.Inspector, secrets secretv1.ModInterface, names tls.KeyfileInput, spec api.TLSSpec,
func createTLSServerCertificate(ctx context.Context, log logging.Logger, cachedStatus inspectorInterface.Inspector, secrets secretv1.ModInterface, names ktls.KeyfileInput, spec api.TLSSpec,
secretName string, ownerRef *meta.OwnerReference) (bool, error) {
log = log.Str("secret", secretName)
// Setup defaults
if names.TTL == nil {
names.TTL = util.NewType(spec.GetTTL().AsDuration())
}
// Load CA certificate
ctxChild, cancel := globals.GetGlobalTimeouts().Kubernetes().WithTimeout(ctx)
defer cancel()
@ -89,28 +79,11 @@ func createTLSServerCertificate(ctx context.Context, log logging.Logger, cachedS
log.Err(err).Debug("Failed to load CA certificate")
return false, errors.WithStack(err)
}
ca, err := certificates.LoadCAFromPEM(caCert, caKey)
if err != nil {
log.Err(err).Debug("Failed to decode CA certificate")
return false, errors.WithStack(err)
}
options := certificates.CreateCertificateOptions{
CommonName: names.AltNames[0],
Hosts: names.AltNames,
EmailAddresses: names.Email,
ValidFrom: time.Now(),
ValidFor: spec.GetTTL().AsDuration(),
IsCA: false,
ECDSACurve: tlsECDSACurve,
}
cert, priv, err := certificates.CreateCertificate(options, &ca)
keyfile, err := ktls.CreateTLSServerKeyfile(caCert, caKey, names)
if err != nil {
log.Err(err).Debug("Failed to create server certificate")
return false, errors.WithStack(err)
}
keyfile := strings.TrimSpace(cert) + "\n" +
strings.TrimSpace(priv)
err = globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
_, err := k8sutil.CreateTLSKeyfileSecret(ctxChild, secrets, secretName, keyfile, ownerRef)

View file

@ -51,7 +51,7 @@ import (
podv1 "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/pod/v1"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/interfaces"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
ktls "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tolerations"
)
@ -549,7 +549,7 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect
// Create TLS secret
tlsKeyfileSecretName := k8sutil.CreateTLSKeyfileSecretName(apiObject.GetName(), role, m.ID)
names, err := tls.GetSyncAltNames(apiObject, spec, spec.Sync.TLS, group, m)
names, err := ktls.GetSyncAltNames(apiObject, spec, spec.Sync.TLS, group, m)
if err != nil {
return errors.WithStack(errors.Wrapf(err, "Failed to render alt names"))
}

View file

@ -49,7 +49,7 @@ import (
inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
secretv1 "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret/v1"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
ktls "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
"github.com/arangodb/kube-arangodb/pkg/util/token"
)
@ -146,7 +146,7 @@ func (r *Resources) EnsureSecrets(ctx context.Context, cachedStatus inspectorInt
tlsKeyfileSecretName := k8sutil.AppendTLSKeyfileSecretPostfix(member.GetName())
if _, exists := cachedStatus.Secret().V1().GetSimple(tlsKeyfileSecretName); !exists {
serverNames, err := tls.GetServerAltNames(apiObject, spec, spec.TLS, service, members[id].Group, members[id].Member)
serverNames, err := ktls.GetServerAltNames(apiObject, spec, spec.TLS, service, members[id].Group, members[id].Member)
if err != nil {
return errors.WithStack(errors.Wrapf(err, "Failed to render alt names"))
}

View file

@ -22,7 +22,10 @@ package clients
import (
"context"
"crypto/tls"
"crypto/x509"
"io"
"os"
"github.com/spf13/cobra"
"google.golang.org/grpc"
@ -74,6 +77,39 @@ func client[T any](ctx context.Context, cfg *Config, in func(cc grpc.ClientConnI
opts = append(opts, util.TokenAuthInterceptors(token)...)
}
if cfg.TLS.Enabled {
config := &tls.Config{}
if ca := cfg.TLS.CA; ca != "" {
pemServerCA, err := os.ReadFile(ca)
if err != nil {
return util.Default[T](), nil, err
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(pemServerCA) {
return util.Default[T](), nil, err
}
config.RootCAs = certPool
}
if cfg.TLS.Insecure {
config.InsecureSkipVerify = true
}
if cfg.TLS.Fallback {
client, closer, err := util.NewOptionalTLSGRPCClient(ctx, in, cfg.Address, config, opts...)
if err != nil {
return util.Default[T](), nil, err
}
return client, closer, nil
} else {
opts = append(opts, util.ClientTLS(config)...)
}
}
client, closer, err := util.NewGRPCClient(ctx, in, cfg.Address, opts...)
if err != nil {
return util.Default[T](), nil, err

View file

@ -33,6 +33,10 @@ type Factory func(c *Config) Client
type Config struct {
Address string
Token string
TLS struct {
Enabled, Insecure, Fallback bool
CA string
}
}
func (c *Config) Register(cmd *cobra.Command) error {
@ -40,6 +44,10 @@ func (c *Config) Register(cmd *cobra.Command) error {
f.StringVar(&c.Address, "address", "127.0.0.1:8080", "GRPC Service Address")
f.StringVar(&c.Token, "token", "", "GRPC Token")
f.BoolVar(&c.TLS.Enabled, "tls.enabled", false, "Defines if GRPC is protected with TLS")
f.StringVar(&c.TLS.CA, "tls.ca", "", "Path to the custom CA")
f.BoolVar(&c.TLS.Insecure, "tls.insecure", false, "Enables Insecure TLS Connection")
f.BoolVar(&c.TLS.Fallback, "tls.fallback", false, "Enables TLS Fallback")
return nil
}

View file

@ -28,7 +28,6 @@ import (
"sync"
"github.com/spf13/cobra"
"google.golang.org/grpc"
pbImplPongV1 "github.com/arangodb/kube-arangodb/integrations/pong/v1"
pbImplShutdownV1 "github.com/arangodb/kube-arangodb/integrations/shutdown/v1"
@ -73,6 +72,10 @@ type serviceConfiguration struct {
address string
tls struct {
keyfile string
}
auth struct {
t string
@ -81,7 +84,9 @@ type serviceConfiguration struct {
}
func (s *serviceConfiguration) Config() (svc.Configuration, error) {
var opts []grpc.ServerOption
var cfg svc.Configuration
cfg.Address = s.address
switch strings.ToLower(s.auth.t) {
case "none":
@ -91,16 +96,21 @@ func (s *serviceConfiguration) Config() (svc.Configuration, error) {
return util.Default[svc.Configuration](), errors.Errorf("Token is empty")
}
opts = append(opts,
cfg.Options = append(cfg.Options,
basicTokenAuthUnaryInterceptor(s.auth.token),
basicTokenAuthStreamInterceptor(s.auth.token),
)
}
return svc.Configuration{
Options: opts,
Address: s.address,
}, nil
if keyfile := s.tls.keyfile; keyfile != "" {
if tls, err := tlsServerOptions(keyfile); err != nil {
return svc.Configuration{}, err
} else {
cfg.TLSOptions = tls
}
}
return cfg, nil
}
func (c *configuration) Register(cmd *cobra.Command) error {
@ -120,16 +130,19 @@ func (c *configuration) Register(cmd *cobra.Command) error {
f.BoolVar(&c.health.shutdownEnabled, "health.shutdown.enabled", true, "Determines if shutdown service should be enabled and exposed")
f.StringVar(&c.health.auth.t, "health.auth.type", "None", "Auth type for health service")
f.StringVar(&c.health.auth.token, "health.auth.token", "", "Token for health service (when auth service is token)")
f.StringVar(&c.health.tls.keyfile, "health.tls.keyfile", "", "Path to the keyfile")
f.BoolVar(&c.services.internal.enabled, "services.enabled", true, "Defines if internal access is enabled")
f.StringVar(&c.services.internal.address, "services.address", "127.0.0.1:9092", "Address to expose internal services")
f.StringVar(&c.services.internal.auth.t, "services.auth.type", "None", "Auth type for internal service")
f.StringVar(&c.services.internal.auth.token, "services.auth.token", "", "Token for internal service (when auth service is token)")
f.StringVar(&c.services.internal.tls.keyfile, "services.tls.keyfile", "", "Path to the keyfile")
f.BoolVar(&c.services.external.enabled, "services.external.enabled", false, "Defines if external access is enabled")
f.StringVar(&c.services.external.address, "services.external.address", "0.0.0.0:9093", "Address to expose external services")
f.StringVar(&c.services.external.auth.t, "services.external.auth.type", "None", "Auth type for external service")
f.StringVar(&c.services.external.auth.token, "services.external.auth.token", "", "Token for external service (when auth service is token)")
f.StringVar(&c.services.external.tls.keyfile, "services.external.tls.keyfile", "", "Path to the keyfile")
for _, service := range c.registered {
prefix := fmt.Sprintf("integration.%s", service.Name())
@ -227,7 +240,7 @@ func (c *configuration) runWithContext(ctx context.Context, cancel context.Cance
healthHandler := health.Start(ctx)
logger.Str("address", healthHandler.Address()).Info("Health handler started")
logger.Str("address", healthHandler.Address()).Bool("ssl", healthConfig.TLSOptions != nil).Info("Health handler started")
var wg sync.WaitGroup
@ -240,7 +253,7 @@ func (c *configuration) runWithContext(ctx context.Context, cancel context.Cance
defer wg.Done()
s := svc.NewService(internalConfig, internalHandlers...).StartWithHealth(ctx, health)
logger.Str("address", s.Address()).Str("type", "internal").Info("Service handler started")
logger.Str("address", s.Address()).Str("type", "internal").Bool("ssl", internalConfig.TLSOptions != nil).Info("Service handler started")
internal = s.Wait()
@ -257,7 +270,7 @@ func (c *configuration) runWithContext(ctx context.Context, cancel context.Cance
defer wg.Done()
s := svc.NewService(externalConfig, externalHandlers...).StartWithHealth(ctx, health)
logger.Str("address", s.Address()).Str("type", "external").Info("Service handler started")
logger.Str("address", s.Address()).Str("type", "external").Bool("ssl", externalConfig.TLSOptions != nil).Info("Service handler started")
external = s.Wait()

View file

@ -24,6 +24,7 @@ import (
"context"
"fmt"
"os"
"strings"
"testing"
"github.com/spf13/cobra"
@ -68,6 +69,7 @@ func executeSync(t *testing.T, ctx context.Context, args ...string) error {
cmd.SetOut(os.Stdout)
cmd.SetArgs(append([]string{"test"}, args...))
logger.Info("Command: %s", strings.Join(args, " "))
return cmd.Execute()
}

39
pkg/integrations/tls.go Normal file
View file

@ -0,0 +1,39 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package integrations
import (
"crypto/tls"
"github.com/arangodb-helper/go-certificates"
)
func tlsServerOptions(keyfile string) (*tls.Config, error) {
cert, err := certificates.LoadKeyFile(keyfile)
if err != nil {
return nil, err
}
return &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.NoClientCert,
}, nil
}

View file

@ -0,0 +1,243 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package integrations
import (
"fmt"
"os"
"path"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
ktls "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tls"
"github.com/arangodb/kube-arangodb/pkg/util/shutdown"
"github.com/arangodb/kube-arangodb/pkg/util/tests/tgrpc"
)
func Test_TLSCases(t *testing.T) {
directory := t.TempDir()
ca1 := path.Join(directory, "CA1.keyfile")
ca1Pem := path.Join(directory, "CA1.pem")
server1 := path.Join(directory, "server1.keyfile")
ca2 := path.Join(directory, "CA2.keyfile")
ca2Pem := path.Join(directory, "CA2.pem")
server2 := path.Join(directory, "server2.keyfile")
t.Run("Arrange CA 1", func(t *testing.T) {
caCert, caKey, err := ktls.CreateTLSCACertificate("Test Root Certificate")
require.NoError(t, err)
require.NoError(t, os.WriteFile(ca1, []byte(ktls.AsKeyfile(caCert, caKey)), 0644))
require.NoError(t, os.WriteFile(ca1Pem, []byte(caCert), 0644))
serverCert, serverKey, err := ktls.CreateTLSServerCertificate(caCert, caKey, ktls.KeyfileInput{
AltNames: []string{
"127.0.0.1",
},
Email: nil,
})
require.NoError(t, err)
require.NoError(t, os.WriteFile(server1, []byte(ktls.AsKeyfile(serverCert, serverKey)), 0644))
})
t.Run("Arrange CA 2", func(t *testing.T) {
caCert, caKey, err := ktls.CreateTLSCACertificate("Test Root Certificate")
require.NoError(t, err)
require.NoError(t, os.WriteFile(ca2, []byte(ktls.AsKeyfile(caCert, caKey)), 0644))
require.NoError(t, os.WriteFile(ca2Pem, []byte(caCert), 0644))
serverCert, serverKey, err := ktls.CreateTLSServerCertificate(caCert, caKey, ktls.KeyfileInput{
AltNames: []string{
"127.0.0.1",
},
Email: nil,
})
require.NoError(t, err)
require.NoError(t, os.WriteFile(server2, []byte(ktls.AsKeyfile(serverCert, serverKey)), 0644))
})
c, health, internal, external := startService(t,
"--health.tls.keyfile=",
fmt.Sprintf("--services.tls.keyfile=%s", server1),
fmt.Sprintf("--services.external.tls.keyfile=%s", server2),
)
defer c.Require(t)
t.Run("Without TLS", func(t *testing.T) {
t.Run("health", func(t *testing.T) {
require.NoError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", health),
"--tls.enabled=false",
"client",
"health",
"v1"))
})
t.Run("internal", func(t *testing.T) {
tgrpc.AsGRPCError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", internal),
"--tls.enabled=false",
"client",
"health",
"v1")).Code(t, codes.Unavailable).Errorf(t, "connection error: desc = \"error reading server preface: EOF\"")
})
t.Run("external", func(t *testing.T) {
tgrpc.AsGRPCError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", external),
"--tls.enabled=false",
"client",
"health",
"v1")).Code(t, codes.Unavailable).Errorf(t, "connection error: desc = \"error reading server preface: EOF\"")
})
})
t.Run("With TLS Fallback", func(t *testing.T) {
t.Run("health", func(t *testing.T) {
require.NoError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", health),
"--tls.enabled=true",
"--tls.fallback=true",
"--tls.insecure=true",
"client",
"health",
"v1"))
})
t.Run("internal", func(t *testing.T) {
require.NoError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", internal),
"--tls.enabled=true",
"--tls.insecure=true",
"--tls.fallback=true",
"client",
"health",
"v1"))
})
t.Run("external", func(t *testing.T) {
require.NoError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", external),
"--tls.enabled=true",
"--tls.insecure=true",
"--tls.fallback=true",
"client",
"health",
"v1"))
})
})
t.Run("With TLS - wrong CA", func(t *testing.T) {
t.Run("health", func(t *testing.T) {
tgrpc.AsGRPCError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", health),
"--tls.enabled=true",
"client",
"health",
"v1")).Code(t, codes.Unavailable).Errorf(t, "connection error: desc = \"transport: authentication handshake failed: tls: first record does not look like a TLS handshake\"")
})
t.Run("internal", func(t *testing.T) {
tgrpc.AsGRPCError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", internal),
"--tls.enabled=true",
"client",
"health",
"v1")).Code(t, codes.Unavailable).Errorf(t, "connection error: desc = \"transport: authentication handshake failed: tls: failed to verify certificate: x509: certificate signed by unknown authority\"")
})
t.Run("external", func(t *testing.T) {
tgrpc.AsGRPCError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", external),
"--tls.enabled=true",
"client",
"health",
"v1")).Code(t, codes.Unavailable).Errorf(t, "connection error: desc = \"transport: authentication handshake failed: tls: failed to verify certificate: x509: certificate signed by unknown authority\"")
})
})
t.Run("With TLS - valid CA1", func(t *testing.T) {
t.Run("health", func(t *testing.T) {
tgrpc.AsGRPCError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", health),
"--tls.enabled=true",
fmt.Sprintf("--tls.ca=%s", ca1Pem),
"client",
"health",
"v1")).Code(t, codes.Unavailable).Errorf(t, "connection error: desc = \"transport: authentication handshake failed: tls: first record does not look like a TLS handshake\"")
})
t.Run("internal", func(t *testing.T) {
require.NoError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", internal),
"--tls.enabled=true",
fmt.Sprintf("--tls.ca=%s", ca1Pem),
"client",
"health",
"v1"))
})
t.Run("external", func(t *testing.T) {
tgrpc.AsGRPCError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", external),
"--tls.enabled=true",
fmt.Sprintf("--tls.ca=%s", ca1Pem),
"client",
"health",
"v1")).Code(t, codes.Unavailable).Errorf(t, "connection error: desc = \"transport: authentication handshake failed: tls: failed to verify certificate: x509: certificate signed by unknown authority (possibly because of \\\"x509: ECDSA verification failure\\\" while trying to verify candidate authority certificate \\\"Test Root Certificate\\\")\"")
})
})
t.Run("With TLS - insecure", func(t *testing.T) {
t.Run("health", func(t *testing.T) {
tgrpc.AsGRPCError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", health),
"--tls.enabled=true",
"--tls.insecure=true",
"client",
"health",
"v1")).Code(t, codes.Unavailable).Errorf(t, "connection error: desc = \"transport: authentication handshake failed: tls: first record does not look like a TLS handshake\"")
})
t.Run("internal", func(t *testing.T) {
require.NoError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", internal),
"--tls.enabled=true",
"--tls.insecure=true",
"client",
"health",
"v1"))
})
t.Run("external", func(t *testing.T) {
require.NoError(t, executeSync(t, shutdown.Context(),
fmt.Sprintf("--address=127.0.0.1:%d", external),
"--tls.enabled=true",
"--tls.insecure=true",
"client",
"health",
"v1"))
})
})
}

View file

@ -22,11 +22,18 @@ package util
import (
"context"
"crypto/tls"
"io"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
pbPongV1 "github.com/arangodb/kube-arangodb/integrations/pong/v1/definition"
pbSharedV1 "github.com/arangodb/kube-arangodb/integrations/shared/v1/definition"
"github.com/arangodb/kube-arangodb/pkg/util/svc"
)
const AuthorizationGRPCHeader = "adb-authorization"
@ -40,7 +47,54 @@ func NewGRPCClient[T any](ctx context.Context, in func(cc grpc.ClientConnInterfa
return in(con), con, nil
}
func NewGRPCConn(ctx context.Context, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
func NewOptionalTLSGRPCClient[T any](ctx context.Context, in func(cc grpc.ClientConnInterface) T, addr string, tls *tls.Config, opts ...grpc.DialOption) (T, io.Closer, error) {
con, err := NewOptionalTLSGRPCConn(ctx, addr, tls, opts...)
if err != nil {
return Default[T](), nil, err
}
return in(con), con, nil
}
func NewOptionalTLSGRPCConn(ctx context.Context, addr string, tls *tls.Config, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
if tls != nil {
// Try with TLS
tlsOpts := ClientTLS(tls)
newOpts := make([]grpc.DialOption, len(opts)+len(tlsOpts))
copy(newOpts, opts)
copy(newOpts[len(opts):], tlsOpts)
// Create conn
conn, err := newGRPCConn(ctx, addr, tlsOpts...)
if err != nil {
return nil, err
}
if _, err := pbPongV1.NewPongV1Client(conn).Ping(ctx, &pbSharedV1.Empty{}); err != nil {
if v, ok := svc.AsGRPCErrorStatus(err); !ok {
return nil, err
} else {
if status := v.GRPCStatus(); status == nil {
return nil, err
} else {
if status.Code() != codes.Unavailable {
return nil, err
}
}
}
} else {
return conn, nil
}
if err := conn.Close(); err != nil {
return nil, err
}
}
return newGRPCConn(ctx, addr, opts...)
}
func newGRPCConn(ctx context.Context, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
var z []grpc.DialOption
z = append(z, grpc.WithTransportCredentials(insecure.NewCredentials()))
@ -55,6 +109,16 @@ func NewGRPCConn(ctx context.Context, addr string, opts ...grpc.DialOption) (*gr
return conn, nil
}
func NewGRPCConn(ctx context.Context, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
return newGRPCConn(ctx, addr, opts...)
}
func ClientTLS(config *tls.Config) []grpc.DialOption {
return []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(config)),
}
}
func TokenAuthInterceptors(token string) []grpc.DialOption {
return []grpc.DialOption{
grpc.WithUnaryInterceptor(func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {

View file

@ -0,0 +1,94 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package tls
import (
"strings"
"time"
"github.com/arangodb-helper/go-certificates"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
)
const (
caTTL = time.Hour * 24 * 365 * 10 // 10 year
tlsECDSACurve = "P256" // This curve is the default that ArangoDB accepts and plenty strong
)
// CreateTLSCACertificate creates a CA certificate
func CreateTLSCACertificate(commonName string) (string, string, error) {
options := certificates.CreateCertificateOptions{
CommonName: commonName,
ValidFrom: time.Now(),
ValidFor: caTTL,
IsCA: true,
ECDSACurve: tlsECDSACurve,
}
cert, priv, err := certificates.CreateCertificate(options, nil)
if err != nil {
return "", "", errors.WithStack(err)
}
return cert, priv, nil
}
// CreateTLSServerCertificate creates Sever Cert in PEM Format
func CreateTLSServerCertificate(caCert, caKey string, names KeyfileInput) (string, string, error) {
ca, err := certificates.LoadCAFromPEM(caCert, caKey)
if err != nil {
return "", "", errors.WithStack(err)
}
options := certificates.CreateCertificateOptions{
CommonName: names.AltNames[0],
Hosts: names.AltNames,
EmailAddresses: names.Email,
ValidFrom: time.Now(),
ValidFor: util.TypeOrDefault(names.TTL, api.DefaultTLSTTL.AsDuration()),
IsCA: false,
ECDSACurve: tlsECDSACurve,
}
cert, priv, err := certificates.CreateCertificate(options, &ca)
if err != nil {
return "", "", errors.WithStack(err)
}
return cert, priv, nil
}
// CreateTLSServerKeyfile creates Sever Cert in Keyfile Format
func CreateTLSServerKeyfile(caCert, caKey string, names KeyfileInput) (string, error) {
cert, priv, err := CreateTLSServerCertificate(caCert, caKey, names)
if err != nil {
return "", err
}
return AsKeyfile(cert, priv), nil
}
func AsKeyfile(cert, priv string) string {
return strings.TrimSpace(cert) + "\n" +
strings.TrimSpace(priv)
}

View file

@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ package tls
import (
"net/url"
"time"
core "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -33,6 +34,7 @@ import (
)
type KeyfileInput struct {
TTL *time.Duration
AltNames []string
Email []string
}

View file

@ -20,10 +20,32 @@
package svc
import "google.golang.org/grpc"
import (
"crypto/tls"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
type Configuration struct {
Address string
TLSOptions *tls.Config
Options []grpc.ServerOption
}
func (c *Configuration) RenderOptions() []grpc.ServerOption {
if c == nil {
return nil
}
ret := make([]grpc.ServerOption, len(c.Options))
copy(ret, c.Options)
if tls := c.TLSOptions; tls != nil {
ret = append(ret, grpc.Creds(credentials.NewTLS(tls)))
}
return ret
}

View file

@ -56,7 +56,7 @@ func newService(cfg Configuration, handlers ...Handler) *service {
var q service
q.cfg = cfg
q.server = grpc.NewServer(cfg.Options...)
q.server = grpc.NewServer(cfg.RenderOptions()...)
q.handlers = handlers
for _, handler := range q.handlers {

View file

@ -46,13 +46,13 @@ func WaitForAddress(t *testing.T, addr string, port int) {
tickerT := time.NewTicker(125 * time.Millisecond)
defer tickerT.Stop()
timerT := time.NewTimer(1 * time.Second)
timerT := time.NewTimer(5 * time.Second)
defer timerT.Stop()
for {
select {
case <-timerT.C:
require.Fail(t, "Timeouted")
require.Fail(t, "Timeouted", addr, port)
case <-tickerT.C:
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", addr, port), 125*time.Millisecond)
if err != nil {