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

Merge pull request #186 from arangodb/bugfix/tls-and-sync-fixes

Various TLS & Sync related fixes
This commit is contained in:
Ewout Prangsma 2018-06-19 09:25:47 +02:00 committed by GitHub
commit ee52f80c55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 76 additions and 73 deletions

View file

@ -39,7 +39,7 @@ metadata:
name: "replication-from-a-to-b"
spec:
source:
endpoint: ["https://163.172.149.229:31888", "https://51.15.225.110:31888", "https://51.15.229.133:31888"]
masterEndpoint: ["https://163.172.149.229:31888", "https://51.15.225.110:31888", "https://51.15.229.133:31888"]
auth:
keyfileSecretName: cluster-a-sync-auth
tls:
@ -67,7 +67,7 @@ with sync enabled.
This cluster configured as the replication source.
### `spec.source.endpoint: []string`
### `spec.source.masterEndpoint: []string`
This setting specifies zero or more master endpoint URL's of the source cluster.
@ -108,7 +108,7 @@ with sync enabled.
This cluster configured as the replication destination.
### `spec.destination.endpoint: []string`
### `spec.destination.masterEndpoint: []string`
This setting specifies zero or more master endpoint URL's of the destination cluster.

View file

@ -35,7 +35,7 @@ import (
type SyncExternalAccessSpec struct {
ExternalAccessSpec
MasterEndpoint []string `json:"masterEndpoint,omitempty"`
AccessPackageSecretNames []string `json:accessPackageSecretNames,omitempty"`
AccessPackageSecretNames []string `json:"accessPackageSecretNames,omitempty"`
}
// GetMasterEndpoint returns the value of masterEndpoint.

View file

@ -36,8 +36,8 @@ type EndpointSpec struct {
// DeploymentName holds the name of an ArangoDeployment resource.
// If set this provides default values for masterEndpoint, auth & tls.
DeploymentName *string `json:"deploymentName,omitempty"`
// Endpoint holds a list of URLs used to reach the syncmaster(s).
Endpoint []string `json:"endpoint,omitempty"`
// MasterEndpoint holds a list of URLs used to reach the syncmaster(s).
MasterEndpoint []string `json:"masterEndpoint,omitempty"`
// Authentication holds settings needed to authentication at the syncmaster.
Authentication EndpointAuthenticationSpec `json:"auth"`
// TLS holds settings needed to verify the TLS connection to the syncmaster.
@ -60,13 +60,13 @@ func (s EndpointSpec) Validate(isSourceEndpoint bool) error {
if err := k8sutil.ValidateOptionalResourceName(s.GetDeploymentName()); err != nil {
return maskAny(err)
}
for _, ep := range s.Endpoint {
for _, ep := range s.MasterEndpoint {
if _, err := url.Parse(ep); err != nil {
return maskAny(errors.Wrapf(ValidationError, "Invalid master endpoint '%s': %s", ep, err))
}
}
hasDeploymentName := s.HasDeploymentName()
if !hasDeploymentName && len(s.Endpoint) == 0 {
if !hasDeploymentName && len(s.MasterEndpoint) == 0 {
return maskAny(errors.Wrapf(ValidationError, "Provide a deploy name or at least one master endpoint"))
}
if err := s.Authentication.Validate(isSourceEndpoint || !hasDeploymentName); err != nil {

View file

@ -240,8 +240,8 @@ func (in *EndpointSpec) DeepCopyInto(out *EndpointSpec) {
**out = **in
}
}
if in.Endpoint != nil {
in, out := &in.Endpoint, &out.Endpoint
if in.MasterEndpoint != nil {
in, out := &in.MasterEndpoint, &out.MasterEndpoint
*out = make([]string, len(*in))
copy(*out, *in)
}

View file

@ -25,6 +25,8 @@ package deployment
import (
"context"
"fmt"
"net"
"strconv"
"github.com/arangodb/arangosync/client"
"github.com/arangodb/arangosync/tasks"
@ -175,7 +177,11 @@ func (d *Deployment) GetSyncServerClient(ctx context.Context, group api.ServerGr
dnsName := k8sutil.CreatePodDNSName(d.apiObject, group.AsRole(), id)
// Build client
source := client.Endpoint{dnsName}
port := k8sutil.ArangoSyncMasterPort
if group == api.ServerGroupSyncWorkers {
port = k8sutil.ArangoSyncWorkerPort
}
source := client.Endpoint{"https://" + net.JoinHostPort(dnsName, strconv.Itoa(port))}
tlsAuth := tasks.TLSAuthentication{
TLSClientAuthentication: tasks.TLSClientAuthentication{
ClientToken: monitoringToken,

View file

@ -174,7 +174,7 @@ func (a *actionWaitForMemberUp) checkProgressCluster(ctx context.Context) (bool,
// of a sync master / worker.
func (a *actionWaitForMemberUp) checkProgressArangoSync(ctx context.Context) (bool, bool, error) {
log := a.log
c, err := a.actionCtx.GetSyncServerClient(ctx, a.action.Group, a.action.ID)
c, err := a.actionCtx.GetSyncServerClient(ctx, a.action.Group, a.action.MemberID)
if err != nil {
log.Debug().Err(err).Msg("Failed to create arangosync client")
return false, false, maskAny(err)

View file

@ -63,10 +63,14 @@ func createRotateTLSServerCertificatePlan(log zerolog.Logger, spec api.Deploymen
Msg("Failed to get TLS secret")
continue
}
renewalNeeded := tlsKeyfileNeedsRenewal(log, keyfile)
tlsSpec := spec.TLS
if group.IsArangosync() {
tlsSpec = spec.Sync.TLS
}
renewalNeeded, reason := tlsKeyfileNeedsRenewal(log, keyfile, tlsSpec)
if renewalNeeded {
plan = append(append(plan,
api.NewAction(api.ActionTypeRenewTLSCertificate, group, m.ID)),
api.NewAction(api.ActionTypeRenewTLSCertificate, group, m.ID, reason)),
createRotateMemberPlan(log, m, group, "TLS certificate renewal")...,
)
}
@ -133,47 +137,8 @@ func createRotateTLSCAPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
// tlsKeyfileNeedsRenewal decides if the certificate in the given keyfile
// should be renewed.
func tlsKeyfileNeedsRenewal(log zerolog.Logger, keyfile string) bool {
func tlsKeyfileNeedsRenewal(log zerolog.Logger, keyfile string, spec api.TLSSpec) (bool, string) {
raw := []byte(keyfile)
for {
var derBlock *pem.Block
derBlock, raw = pem.Decode(raw)
if derBlock == nil {
break
}
if derBlock.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(derBlock.Bytes)
if err != nil {
// We do not understand the certificate, let's renew it
log.Warn().Err(err).Msg("Failed to parse x509 certificate. Renewing it")
return true
}
if cert.IsCA {
// Only look at the server certificate, not CA or intermediate
continue
}
// Check expiration date. Renewal at 2/3 of lifetime.
ttl := cert.NotAfter.Sub(cert.NotBefore)
expirationDate := cert.NotBefore.Add((ttl / 3) * 2)
if expirationDate.Before(time.Now()) {
// We should renew now
log.Debug().
Str("not-before", cert.NotBefore.String()).
Str("not-after", cert.NotAfter.String()).
Str("expiration-date", expirationDate.String()).
Msg("TLS certificate renewal needed")
return true
}
}
}
return false
}
// tlsCANeedsRenewal decides if the given CA certificate
// should be renewed.
// Returns: shouldRenew, reason
func tlsCANeedsRenewal(log zerolog.Logger, cert string, spec api.TLSSpec) (bool, string) {
raw := []byte(cert)
// containsAll returns true when all elements in the expected list
// are in the actual list.
containsAll := func(actual []string, expected []string) bool {
@ -211,21 +176,21 @@ func tlsCANeedsRenewal(log zerolog.Logger, cert string, spec api.TLSSpec) (bool,
log.Warn().Err(err).Msg("Failed to parse x509 certificate. Renewing it")
return true, "Cannot parse x509 certificate: " + err.Error()
}
if !cert.IsCA {
// Only look at the CA certificate
if cert.IsCA {
// Only look at the server certificate, not CA or intermediate
continue
}
// Check expiration date. Renewal at 90% of lifetime.
// Check expiration date. Renewal at 2/3 of lifetime.
ttl := cert.NotAfter.Sub(cert.NotBefore)
expirationDate := cert.NotBefore.Add((ttl / 10) * 9)
expirationDate := cert.NotBefore.Add((ttl / 3) * 2)
if expirationDate.Before(time.Now()) {
// We should renew now
log.Debug().
Str("not-before", cert.NotBefore.String()).
Str("not-after", cert.NotAfter.String()).
Str("expiration-date", expirationDate.String()).
Msg("TLS CA certificate renewal needed")
return true, "CA Certificate about to expire"
Msg("TLS certificate renewal needed")
return true, "Server certificate about to expire"
}
// Check alternate names against spec
dnsNames, ipAddresses, emailAddress, err := spec.GetParsedAltNames()
@ -244,3 +209,42 @@ func tlsCANeedsRenewal(log zerolog.Logger, cert string, spec api.TLSSpec) (bool,
}
return false, ""
}
// tlsCANeedsRenewal decides if the given CA certificate
// should be renewed.
// Returns: shouldRenew, reason
func tlsCANeedsRenewal(log zerolog.Logger, cert string, spec api.TLSSpec) (bool, string) {
raw := []byte(cert)
for {
var derBlock *pem.Block
derBlock, raw = pem.Decode(raw)
if derBlock == nil {
break
}
if derBlock.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(derBlock.Bytes)
if err != nil {
// We do not understand the certificate, let's renew it
log.Warn().Err(err).Msg("Failed to parse x509 certificate. Renewing it")
return true, "Cannot parse x509 certificate: " + err.Error()
}
if !cert.IsCA {
// Only look at the CA certificate
continue
}
// Check expiration date. Renewal at 90% of lifetime.
ttl := cert.NotAfter.Sub(cert.NotBefore)
expirationDate := cert.NotBefore.Add((ttl / 10) * 9)
if expirationDate.Before(time.Now()) {
// We should renew now
log.Debug().
Str("not-before", cert.NotBefore.String()).
Str("not-after", cert.NotAfter.String()).
Str("expiration-date", expirationDate.String()).
Msg("TLS CA certificate renewal needed")
return true, "CA Certificate about to expire"
}
}
}
return false, ""
}

View file

@ -45,20 +45,13 @@ const (
// specified in the given spec.
func createTLSCACertificate(log zerolog.Logger, cli v1.CoreV1Interface, spec api.TLSSpec, deploymentName, namespace string, ownerRef *metav1.OwnerReference) error {
log = log.With().Str("secret", spec.GetCASecretName()).Logger()
dnsNames, ipAddresses, emailAddress, err := spec.GetParsedAltNames()
if err != nil {
log.Debug().Err(err).Msg("Failed to get alternate names")
return maskAny(err)
}
options := certificates.CreateCertificateOptions{
CommonName: fmt.Sprintf("%s Root Certificate", deploymentName),
Hosts: append(dnsNames, ipAddresses...),
EmailAddresses: emailAddress,
ValidFrom: time.Now(),
ValidFor: caTTL,
IsCA: true,
ECDSACurve: tlsECDSACurve,
CommonName: fmt.Sprintf("%s Root Certificate", deploymentName),
ValidFrom: time.Now(),
ValidFor: caTTL,
IsCA: true,
ECDSACurve: tlsECDSACurve,
}
cert, priv, err := certificates.CreateCertificate(options, nil)
if err != nil {

View file

@ -113,7 +113,7 @@ func (dr *DeploymentReplication) createArangoSyncEndpoint(epSpec api.EndpointSpe
dnsName := k8sutil.CreateSyncMasterClientServiceDNSName(depl)
return client.Endpoint{"https://" + net.JoinHostPort(dnsName, strconv.Itoa(k8sutil.ArangoSyncMasterPort))}, nil
}
return client.Endpoint(epSpec.Endpoint), nil
return client.Endpoint(epSpec.MasterEndpoint), nil
}
// createArangoSyncTLSAuthentication creates the authentication needed to authenticate