Skip to content

Commit

Permalink
Always use CAPI generated certificates for etcd management
Browse files Browse the repository at this point in the history
- Perform etcd memebership management only when the local certificates
  were created, and skip otherwise.

Signed-off-by: Danil-Grigorev <[email protected]>
  • Loading branch information
Danil-Grigorev committed Oct 2, 2024
1 parent cca2dfc commit 392d58d
Show file tree
Hide file tree
Showing 6 changed files with 9 additions and 146 deletions.
1 change: 0 additions & 1 deletion bootstrap/internal/cloudinit/controlplane_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ runcmd:
- '/opt/rke2-cis-script.sh'{{ end }}
- 'systemctl enable rke2-server.service'
- 'systemctl start rke2-server.service'
- '/var/lib/rancher/rke2/bin/kubectl create secret tls cluster-etcd -o yaml --dry-run=client -n kube-system --cert=/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt --key=/var/lib/rancher/rke2/server/tls/etcd/server-ca.key --kubeconfig /etc/rancher/rke2/rke2.yaml | /var/lib/rancher/rke2/bin/kubectl apply -f- --kubeconfig /etc/rancher/rke2/rke2.yaml'
- 'mkdir -p /run/cluster-api'
- '{{ .SentinelFileCommand }}'
{{- template "commands" .PostRKE2Commands }}
Expand Down
4 changes: 0 additions & 4 deletions bootstrap/internal/ignition/ignition.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ var (
"setenforce 0",
"systemctl enable rke2-server.service",
"systemctl start rke2-server.service",
"/var/lib/rancher/rke2/bin/kubectl create secret tls cluster-etcd -o yaml --dry-run=client -n kube-system " +
"--cert=/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt --key=/var/lib/rancher/rke2/server/tls/etcd/server-ca.key " +
"--kubeconfig /etc/rancher/rke2/rke2.yaml |" +
" /var/lib/rancher/rke2/bin/kubectl apply -f- --kubeconfig /etc/rancher/rke2/rke2.yaml",
"restorecon /etc/systemd/system/rke2-server.service",
"mkdir -p /run/cluster-api /etc/cluster-api",
"echo success | tee /run/cluster-api/bootstrap-success.complete /etc/cluster-api/bootstrap-success.complete > /dev/null",
Expand Down
14 changes: 7 additions & 7 deletions bootstrap/internal/ignition/ignition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ limitations under the License.
package ignition

import (
"fmt"
"compress/gzip"
"bytes"
"io/ioutil"
"compress/gzip"
"encoding/base64"
"fmt"
"io/ioutil"
"strings"
"testing"

Expand Down Expand Up @@ -133,7 +133,7 @@ var _ = Describe("NewJoinWorker", func() {
scriptContentsGzip, err := base64.StdEncoding.DecodeString(scriptContentsEnc)
Expect(err).ToNot(HaveOccurred())
reader := bytes.NewReader(scriptContentsGzip)
gzreader, err := gzip.NewReader(reader);
gzreader, err := gzip.NewReader(reader)
Expect(err).ToNot(HaveOccurred())
scriptContents, err := ioutil.ReadAll(gzreader)
Expect(err).ToNot(HaveOccurred())
Expand Down Expand Up @@ -254,15 +254,15 @@ var _ = Describe("getControlPlaneRKE2Commands", func() {
It("should return slice of control plane commands", func() {
commands, err := getControlPlaneRKE2Commands(baseUserData)
Expect(err).ToNot(HaveOccurred())
Expect(commands).To(HaveLen(10))
Expect(commands).To(HaveLen(9))
Expect(commands).To(ContainElements(fmt.Sprintf(controlPlaneCommand, baseUserData.RKE2Version), serverDeployCommands[0], serverDeployCommands[1]))
})

It("should return slice of control plane commands with air gapped", func() {
baseUserData.AirGapped = true
commands, err := getControlPlaneRKE2Commands(baseUserData)
Expect(err).ToNot(HaveOccurred())
Expect(commands).To(HaveLen(10))
Expect(commands).To(HaveLen(9))
Expect(commands).To(ContainElements(airGappedControlPlaneCommand, serverDeployCommands[0], serverDeployCommands[1]))
})

Expand All @@ -271,7 +271,7 @@ var _ = Describe("getControlPlaneRKE2Commands", func() {
baseUserData.AirGappedChecksum = "abcd"
commands, err := getControlPlaneRKE2Commands(baseUserData)
Expect(err).ToNot(HaveOccurred())
Expect(commands).To(HaveLen(11))
Expect(commands).To(HaveLen(10))
Expect(commands).To(ContainElements(fmt.Sprintf(airGappedChecksumCommand, "abcd"), airGappedControlPlaneCommand, serverDeployCommands[0], serverDeployCommands[1]))
})

Expand Down
17 changes: 0 additions & 17 deletions pkg/rke2/management_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
"time"

"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
Expand Down Expand Up @@ -160,22 +159,6 @@ func (m *Management) getEtcdCAKeyPair(ctx context.Context, cl ctrlclient.Reader,
return keypair, nil
}

func (m *Management) getRemoteKeyPair(ctx context.Context, remoteClient ctrlclient.Client, clusterKey ctrlclient.ObjectKey) (*certs.KeyPair, error) {
etcdCertificate := &secret.ExternalCertificate{
Reader: remoteClient,
Purpose: secret.EtcdServerCA,
}
externalCertificates := secret.Certificates{etcdCertificate}

if err := externalCertificates.LookupOrGenerate(ctx, m.Client, clusterKey, metav1.OwnerReference{}); err != nil {
log.FromContext(ctx).Error(err, "unable to lookup or create cluster certificates")

return nil, err
}

return etcdCertificate.GetKeyPair(), nil
}

func generateClientCert(caCertEncoded, caKeyEncoded []byte, clientKey *rsa.PrivateKey) (tls.Certificate, error) {
caCert, err := certs.DecodeCertPEM(caCertEncoded)
if err != nil {
Expand Down
12 changes: 2 additions & 10 deletions pkg/rke2/workload_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,9 @@ func (m *Management) NewWorkload(
}

if apierrors.IsNotFound(err) || etcdKeyPair == nil {
log.FromContext(ctx).Info("Collecting etcd key pair from remote")
log.FromContext(ctx).Info("Cluster does not provide etcd certificates for creating child etcd ctrlclient.")

etcdKeyPair, err = m.getRemoteKeyPair(ctx, cl, clusterKey)
if ctrlclient.IgnoreNotFound(err) != nil {
return nil, err
} else if apierrors.IsNotFound(err) {
log.FromContext(ctx).Info("Cluster does not provide etcd certificates for creating child etcd ctrlclient." +
"Please scale up the CP nodes by one to bootstrap the etcd secret content.")

return workload, nil
}
return workload, nil
}

clientCert, err := tls.X509KeyPair(etcdKeyPair.Cert, etcdKeyPair.Key)
Expand Down
107 changes: 0 additions & 107 deletions pkg/secret/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,41 +160,9 @@ func (c *ManagedCertificate) Lookup(ctx context.Context, ctrlclient client.Reade
return s, nil
}

// ExternalCertificate represents a single certificate CA.
type ExternalCertificate struct {
client.Reader
Purpose Purpose
Generated bool
KeyPair *certs.KeyPair
}

// SaveGenerated implements Certificate.
func (c *ExternalCertificate) SaveGenerated(ctx context.Context, cl client.Client, key types.NamespacedName, owner metav1.OwnerReference) error {
s := c.AsSecret(key, owner)

if err := cl.Get(ctx, client.ObjectKeyFromObject(s), s); apierrors.IsNotFound(err) {
if err := cl.Create(ctx, c.AsSecret(key, owner)); client.IgnoreAlreadyExists(err) != nil {
return errors.WithStack(err)
}
} else if err != nil {
return errors.WithStack(err)
}

source := c.AsSecret(key, owner)
s.Data = source.Data
s.Labels = source.Labels

if err := cl.Update(ctx, s); client.IgnoreAlreadyExists(err) != nil {
return errors.WithStack(err)
}

return nil
}

var (
_ CertificatesGenerator = &Certificates{}
_ Certificate = &ManagedCertificate{}
_ Certificate = &ExternalCertificate{}
)

// Certificates are the certificates necessary to bootstrap a cluster.
Expand Down Expand Up @@ -311,74 +279,6 @@ func (c *ManagedCertificate) IsExternal() bool {
return c.External
}

// Lookup implements certificate lookup for external source.
func (c *ExternalCertificate) Lookup(ctx context.Context, _ client.Reader, _ client.ObjectKey) (*corev1.Secret, error) {
s := &corev1.Secret{}
key := client.ObjectKey{
Name: Name("cluster", c.GetPurpose()),
Namespace: metav1.NamespaceSystem,
}

if err := c.Get(ctx, key, s); err != nil {
if apierrors.IsNotFound(err) {
if c.IsExternal() {
return nil, errors.WithMessage(err, "external certificate not found")
}

return nil, nil //nolint:nilnil
}

return nil, errors.WithStack(err)
}

return s, nil
}

// AsFiles for external certificate is a no-op, due to being externally managed.
func (*ExternalCertificate) AsFiles() []bootstrapv1.File {
return []bootstrapv1.File{}
}

// AsSecret implements Certificate.
func (c *ExternalCertificate) AsSecret(clusterName types.NamespacedName, owner metav1.OwnerReference) *corev1.Secret {
s := asExternalSecret(map[string][]byte{
TLSKeyDataName: c.KeyPair.Key,
TLSCrtDataName: c.KeyPair.Cert,
}, c.GetPurpose(), clusterName, owner)

return s
}

// Generate implements key pair collection from external source.
func (c *ExternalCertificate) Generate() error {
return nil
}

// GetKeyPair implements key pair retriever for ExternalCertificate.
func (c *ExternalCertificate) GetKeyPair() *certs.KeyPair {
return c.KeyPair
}

// GetPurpose implements purpose check for ExternalCertificate.
func (c *ExternalCertificate) GetPurpose() Purpose {
return c.Purpose
}

// IsExternal represents extenally managed scenario for ExternalCertificate so is always true.
func (*ExternalCertificate) IsExternal() bool {
return true
}

// IsGenerated is always false for externally managed certificate.
func (c *ExternalCertificate) IsGenerated() bool {
return true
}

// SetKeyPair sets ExternalCertificate key pair.
func (c *ExternalCertificate) SetKeyPair(keyPair *certs.KeyPair) {
c.KeyPair = keyPair
}

// Generate will generate any certificates that do not have KeyPair data.
func (c Certificates) Generate() error {
for _, certificate := range c {
Expand Down Expand Up @@ -598,13 +498,6 @@ func generateServiceAccountKeys() (*certs.KeyPair, error) {
}, nil
}

func asExternalSecret(data map[string][]byte, purpose Purpose, clusterName types.NamespacedName, owner metav1.OwnerReference) *corev1.Secret {
secret := asSecret(data, purpose, clusterName, owner)
secret.Labels[ExternalPurposeLabel] = string(purpose)

return secret
}

func asSecret(data map[string][]byte, purpose Purpose, clusterName types.NamespacedName, _ metav1.OwnerReference) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Expand Down

0 comments on commit 392d58d

Please sign in to comment.