Skip to content

[Feature] TLS CA Secret Key #1516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- (Feature) (ML) Metadata Service Implementation
- (Feature) License Manager for ML Deployment
- (Feature) (ML) Storage S3 sidecar implementation
- (Feature) TLS CA Secret Key

## [1.2.35](https://github.com/arangodb/kube-arangodb/tree/1.2.35) (2023-11-06)
- (Maintenance) Update go-driver to v1.6.0, update IsNotFound() checks
Expand Down
2 changes: 2 additions & 0 deletions docs/generated/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
| RebalancerGenerateV2 | yes | 10m0s | no | Community & Enterprise | Generates the Rebalancer plan |
| RebuildOutSyncedShards | no | 24h0m0s | no | Community & Enterprise | Run Rebuild Out Synced Shards procedure for DBServers |
| RecreateMember | no | 15m0s | no | Community & Enterprise | Recreate member with same ID and Data |
| RefreshTLSCA | no | 30m0s | no | Enterprise Only | Refresh internal CA |
| RefreshTLSKeyfileCertificate | no | 30m0s | no | Enterprise Only | Recreate Server TLS Certificate secret |
| RemoveMember | no | 15m0s | no | Community & Enterprise | Removes member from the Cluster and Status |
| RemoveMemberPVC | no | 15m0s | no | Community & Enterprise | Removes member PVC and enforce recreate procedure |
Expand Down Expand Up @@ -150,6 +151,7 @@ spec:
RebalancerGenerateV2: 10m0s
RebuildOutSyncedShards: 24h0m0s
RecreateMember: 15m0s
RefreshTLSCA: 30m0s
RefreshTLSKeyfileCertificate: 30m0s
RemoveMember: 15m0s
RemoveMemberPVC: 15m0s
Expand Down
4 changes: 4 additions & 0 deletions internal/actions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ actions:
enterprise: true
description: Remove Certificate from CA TrustStore
timeout: 30m
RefreshTLSCA:
enterprise: true
description: Refresh internal CA
timeout: 30m
CleanTLSKeyfileCertificate:
enterprise: true
description: Remove old TLS certificate from server
Expand Down
12 changes: 12 additions & 0 deletions pkg/apis/deployment/v1/actions.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ const (
// ActionRecreateMemberDefaultTimeout define default timeout for action ActionRecreateMember
ActionRecreateMemberDefaultTimeout time.Duration = 900 * time.Second // 15m0s

// ActionRefreshTLSCADefaultTimeout define default timeout for action ActionRefreshTLSCA
ActionRefreshTLSCADefaultTimeout time.Duration = 1800 * time.Second // 30m0s

// ActionRefreshTLSKeyfileCertificateDefaultTimeout define default timeout for action ActionRefreshTLSKeyfileCertificate
ActionRefreshTLSKeyfileCertificateDefaultTimeout time.Duration = 1800 * time.Second // 30m0s

Expand Down Expand Up @@ -433,6 +436,9 @@ const (
// ActionTypeRecreateMember in scopes Normal. Recreate member with same ID and Data
ActionTypeRecreateMember ActionType = "RecreateMember"

// ActionTypeRefreshTLSCA in scopes Normal. Refresh internal CA
ActionTypeRefreshTLSCA ActionType = "RefreshTLSCA"

// ActionTypeRefreshTLSKeyfileCertificate in scopes Normal. Recreate Server TLS Certificate secret
ActionTypeRefreshTLSKeyfileCertificate ActionType = "RefreshTLSKeyfileCertificate"

Expand Down Expand Up @@ -643,6 +649,8 @@ func (a ActionType) DefaultTimeout() time.Duration {
return ActionRebuildOutSyncedShardsDefaultTimeout
case ActionTypeRecreateMember:
return ActionRecreateMemberDefaultTimeout
case ActionTypeRefreshTLSCA:
return ActionRefreshTLSCADefaultTimeout
case ActionTypeRefreshTLSKeyfileCertificate:
return ActionRefreshTLSKeyfileCertificateDefaultTimeout
case ActionTypeRemoveMember:
Expand Down Expand Up @@ -819,6 +827,8 @@ func (a ActionType) Priority() ActionPriority {
return ActionPriorityHigh
case ActionTypeRecreateMember:
return ActionPriorityNormal
case ActionTypeRefreshTLSCA:
return ActionPriorityNormal
case ActionTypeRefreshTLSKeyfileCertificate:
return ActionPriorityNormal
case ActionTypeRemoveMember:
Expand Down Expand Up @@ -1007,6 +1017,8 @@ func (a ActionType) Optional() bool {
return false
case ActionTypeRecreateMember:
return false
case ActionTypeRefreshTLSCA:
return false
case ActionTypeRefreshTLSKeyfileCertificate:
return false
case ActionTypeRemoveMember:
Expand Down
12 changes: 12 additions & 0 deletions pkg/apis/deployment/v2alpha1/actions.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ const (
// ActionRecreateMemberDefaultTimeout define default timeout for action ActionRecreateMember
ActionRecreateMemberDefaultTimeout time.Duration = 900 * time.Second // 15m0s

// ActionRefreshTLSCADefaultTimeout define default timeout for action ActionRefreshTLSCA
ActionRefreshTLSCADefaultTimeout time.Duration = 1800 * time.Second // 30m0s

// ActionRefreshTLSKeyfileCertificateDefaultTimeout define default timeout for action ActionRefreshTLSKeyfileCertificate
ActionRefreshTLSKeyfileCertificateDefaultTimeout time.Duration = 1800 * time.Second // 30m0s

Expand Down Expand Up @@ -433,6 +436,9 @@ const (
// ActionTypeRecreateMember in scopes Normal. Recreate member with same ID and Data
ActionTypeRecreateMember ActionType = "RecreateMember"

// ActionTypeRefreshTLSCA in scopes Normal. Refresh internal CA
ActionTypeRefreshTLSCA ActionType = "RefreshTLSCA"

// ActionTypeRefreshTLSKeyfileCertificate in scopes Normal. Recreate Server TLS Certificate secret
ActionTypeRefreshTLSKeyfileCertificate ActionType = "RefreshTLSKeyfileCertificate"

Expand Down Expand Up @@ -643,6 +649,8 @@ func (a ActionType) DefaultTimeout() time.Duration {
return ActionRebuildOutSyncedShardsDefaultTimeout
case ActionTypeRecreateMember:
return ActionRecreateMemberDefaultTimeout
case ActionTypeRefreshTLSCA:
return ActionRefreshTLSCADefaultTimeout
case ActionTypeRefreshTLSKeyfileCertificate:
return ActionRefreshTLSKeyfileCertificateDefaultTimeout
case ActionTypeRemoveMember:
Expand Down Expand Up @@ -819,6 +827,8 @@ func (a ActionType) Priority() ActionPriority {
return ActionPriorityHigh
case ActionTypeRecreateMember:
return ActionPriorityNormal
case ActionTypeRefreshTLSCA:
return ActionPriorityNormal
case ActionTypeRefreshTLSKeyfileCertificate:
return ActionPriorityNormal
case ActionTypeRemoveMember:
Expand Down Expand Up @@ -1007,6 +1017,8 @@ func (a ActionType) Optional() bool {
return false
case ActionTypeRecreateMember:
return false
case ActionTypeRefreshTLSCA:
return false
case ActionTypeRefreshTLSKeyfileCertificate:
return false
case ActionTypeRemoveMember:
Expand Down
17 changes: 17 additions & 0 deletions pkg/deployment/reconcile/action.register.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ var (
_ Action = &actionRecreateMember{}
_ actionFactory = newRecreateMemberAction

_ Action = &actionRefreshTLSCA{}
_ actionFactory = newRefreshTLSCAAction

_ Action = &actionRefreshTLSKeyfileCertificate{}
_ actionFactory = newRefreshTLSKeyfileCertificateAction

Expand Down Expand Up @@ -951,6 +954,20 @@ func init() {
registerAction(action, function)
}

// RefreshTLSCA
{
// Get Action type
action := api.ActionTypeRefreshTLSCA

// Get Action defition
function := newRefreshTLSCAAction

// Wrap action main function

// Register action
registerAction(action, function)
}

// RefreshTLSKeyfileCertificate
{
// Get Action type
Expand Down
10 changes: 10 additions & 0 deletions pkg/deployment/reconcile/action.register.generated_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,16 @@ func Test_Actions(t *testing.T) {
})
})

t.Run("RefreshTLSCA", func(t *testing.T) {
ActionsExistence(t, api.ActionTypeRefreshTLSCA)
t.Run("Internal", func(t *testing.T) {
require.False(t, api.ActionTypeRefreshTLSCA.Internal())
})
t.Run("Optional", func(t *testing.T) {
require.False(t, api.ActionTypeRefreshTLSCA.Optional())
})
})

t.Run("RefreshTLSKeyfileCertificate", func(t *testing.T) {
ActionsExistence(t, api.ActionTypeRefreshTLSKeyfileCertificate)
t.Run("Internal", func(t *testing.T) {
Expand Down
93 changes: 93 additions & 0 deletions pkg/deployment/reconcile/action_tls_ca_refresh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 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 reconcile

import (
"context"
"encoding/base64"

meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/globals"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
)

// newRefreshTLSCAAction creates a new Action that implements the given
// planned RefreshTLSCA action.
func newRefreshTLSCAAction(action api.Action, actionCtx ActionContext) Action {
a := &actionRefreshTLSCA{}

a.actionImpl = newBaseActionImpl(action, actionCtx, &a.newMemberID)

return a
}

// actionRefreshTLSCA implements an RefreshTLSCAAction.
type actionRefreshTLSCA struct {
// actionImpl implement timeout and member id functions
actionImpl

// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress

newMemberID string
}

// Start performs the start of the action.
// Returns true if the action is completely finished, false in case
// the start time needs to be recorded and a ready condition needs to be checked.
func (a *actionRefreshTLSCA) Start(ctx context.Context) (bool, error) {
caFolder, exists := a.actionCtx.ACS().CurrentClusterCache().Secret().V1().GetSimple(resources.GetCASecretName(a.actionCtx.GetAPIObject()))
if !exists {
a.log.Warn("Secret %s is missing", resources.GetCASecretName(a.actionCtx.GetAPIObject()))
return true, nil
}

caLocalData := buildInternalCA(caFolder)

if string(caFolder.Data[resources.CACertName]) != caLocalData {
p := patch.NewPatch()
p.ItemAdd(patch.NewPath("data", resources.CACertName), base64.StdEncoding.EncodeToString([]byte(caLocalData)))

patch, err := p.Marshal()
if err != nil {
a.log.Err(err).Error("Unable to encrypt patch")
return true, nil
}

err = globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
_, err := a.actionCtx.ACS().CurrentClusterCache().SecretsModInterface().V1().Patch(ctxChild, resources.GetCASecretName(a.actionCtx.GetAPIObject()), types.JSONPatchType, patch, meta.PatchOptions{})
return err
})
if err != nil {
if !kerrors.IsInvalid(err) {
return false, errors.Wrapf(err, "Unable to update secret: %s", string(patch))
}
}
}

return true, nil
}
27 changes: 27 additions & 0 deletions pkg/deployment/reconcile/plan_builder_tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import (
"net/http"
"net/url"
"reflect"
"sort"
"time"

core "k8s.io/api/core/v1"

"github.com/arangodb/go-driver"

api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
Expand All @@ -49,6 +52,22 @@ import (

const CertificateRenewalMargin = 7 * 24 * time.Hour

func buildInternalCA(secret *core.Secret) string {
// Let's check if ca.crt needs to be created
keys := make([]string, 0, len(secret.Data))
for k, v := range secret.Data {
if k == resources.CACertName {
continue
}

keys = append(keys, string(v))
}

sort.Strings(keys)

return strings.Join(keys, "\n")
}

func (r *Reconciler) createTLSStatusPropagatedFieldUpdate(ctx context.Context, apiObject k8sutil.APIObject,
spec api.DeploymentSpec, status api.DeploymentStatus,
context PlanBuilderContext, w WithPlanBuilder, builders ...planBuilder) api.Plan {
Expand Down Expand Up @@ -192,6 +211,10 @@ func (r *Reconciler) createCAAppendPlan(ctx context.Context, apiObject k8sutil.A
AddParam(checksum, certSha)}
}

if string(trusted.Data[resources.CACertName]) != buildInternalCA(trusted) {
return api.Plan{actions.NewClusterAction(api.ActionTypeRefreshTLSCA, "Refresh CA")}
}

return nil
}

Expand Down Expand Up @@ -270,6 +293,10 @@ func (r *Reconciler) createCACleanPlan(ctx context.Context, apiObject k8sutil.AP
certSha := util.SHA256(caData)

for sha := range trusted.Data {
if sha == resources.CACertName {
continue
}

if certSha != sha {
return api.Plan{actions.NewClusterAction(api.ActionTypeCleanTLSCACertificate, "Clean CA from truststore").
AddParam(checksum, sha)}
Expand Down