Skip to content
Open
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 internal/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
FinalizerSccCredentials = "scc.cattle.io/managed-credentials"
FinalizerSccRegistration = "scc.cattle.io/managed-registration"
FinalizerSccRegistrationCode = "scc.cattle.io/managed-registration-code"
FinalizerSccRegistrationURLCert = "scc.cattle.io/managed-registration-url-cert"
)

const (
Expand Down
19 changes: 12 additions & 7 deletions internal/consts/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import "fmt"

// Secret names and name prefixes
const (
ResourceSCCEntrypointSecretName = "scc-registration"
SCCMetricsOutputSecretName = "rancher-scc-metrics"
RancherMetricsSecretRequestName = SCCMetricsOutputSecretName
SCCSystemCredentialsSecretNamePrefix = "scc-system-credentials-"
RegistrationCodeSecretNamePrefix = "registration-code-"
OfflineRequestSecretNamePrefix = "offline-request-"
OfflineCertificateSecretNamePrefix = "offline-certificate-"
ResourceSCCEntrypointSecretName = "scc-registration"
SCCMetricsOutputSecretName = "rancher-scc-metrics"
RancherMetricsSecretRequestName = SCCMetricsOutputSecretName
SCCSystemCredentialsSecretNamePrefix = "scc-system-credentials-"
RegistrationCodeSecretNamePrefix = "registration-code-"
OfflineRequestSecretNamePrefix = "offline-request-"
OfflineCertificateSecretNamePrefix = "offline-certificate-"
RegistrationURLCertificateSecretNamePrefix = "registration-url-cert-"
)

func RegistrationName(namePartIn string) string {
Expand All @@ -32,3 +33,7 @@ func OfflineRequestSecretName(namePartIn string) string {
func OfflineCertificateSecretName(namePartIn string) string {
return fmt.Sprintf("%s%s", OfflineCertificateSecretNamePrefix, namePartIn)
}

func RegistrationURLCertificateSecretName(namePartIn string) string {
return fmt.Sprintf("%s%s", RegistrationURLCertificateSecretNamePrefix, namePartIn)
}
10 changes: 6 additions & 4 deletions internal/consts/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ const (
SecretKeyOfflineRegRequest = "request"
SecretKeyOfflineRegCert = "certificate"
RegistrationURL = "registrationUrl"
RegistrationURLCert = "registrationUrlCert"
)

type SecretRole string

const (
SCCCredentialsRole SecretRole = "scc-credentials"
RegistrationCode SecretRole = "reg-code"
OfflineRequestRole SecretRole = "offline-request"
OfflineCertificate SecretRole = "offline-certificate"
SCCCredentialsRole SecretRole = "scc-credentials"
RegistrationCode SecretRole = "reg-code"
OfflineRequestRole SecretRole = "offline-request"
OfflineCertificate SecretRole = "offline-certificate"
RegistrationServerCertRole SecretRole = "registration-server-cert"
)
39 changes: 39 additions & 0 deletions internal/suseconnect/util.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package suseconnect

import (
"crypto/x509"
"encoding/pem"

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

"github.com/rancher/scc-operator/internal/consts"
Expand All @@ -24,3 +27,39 @@ func FetchSccRegistrationCodeFrom(secretRepo *secretrepo.SecretRepository, refer

return string(regCode)
}

// FetchRegistrationURLCertFrom fetches and parses the registration URL certificate from a secret
func FetchRegistrationURLCertFrom(secretRepo *secretrepo.SecretRepository, reference *corev1.SecretReference) *x509.Certificate {
if reference == nil {
return nil
}

sccContextLogger().Debugf("Fetching Registration URL Certificate from secret %s/%s", reference.Namespace, reference.Name)
certSecret, err := secretRepo.Cache.Get(reference.Namespace, reference.Name)
if err != nil {
sccContextLogger().Warnf("Failed to get Registration URL Certificate from secret %s/%s: %v", reference.Namespace, reference.Name, err)
return nil
}
sccContextLogger().Debugf("Found certificate secret %s/%s", reference.Namespace, reference.Name)

certData, ok := certSecret.Data[consts.RegistrationURLCert]
if !ok {
sccContextLogger().Warnf("registration URL cert secret `%v` does not contain expected data `%s`", reference, consts.RegistrationURLCert)
return nil
}

// Parse PEM encoded certificate
block, _ := pem.Decode(certData)
if block == nil {
sccContextLogger().Warnf("failed to decode PEM certificate from secret %s/%s", reference.Namespace, reference.Name)
return nil
}

cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
sccContextLogger().Warnf("failed to parse x509 certificate from secret %s/%s: %v", reference.Namespace, reference.Name, err)
return nil
}

return cert
}
12 changes: 7 additions & 5 deletions internal/suseconnect/wrapper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package suseconnect

import (
"crypto/x509"
"fmt"

"github.com/SUSE/connect-ng/pkg/connection"
Expand All @@ -25,11 +26,12 @@ type SccWrapper struct {
rancherMetrics telemetry.MetricsWrapper
}

func DefaultConnectionOptions(appName, version string) connection.Options {
// So this doesn't necessarily mean these have to match Rancher on the cluster.
// Rather the details about the HTTP client talking to SCC
// TODO: eventually add localization support?
return connection.DefaultOptions(appName, version, "en_US")
func DefaultConnectionOptions(appName, version string, cert *x509.Certificate) connection.Options {
opts := connection.DefaultOptions(appName, version, "en_US")
if cert != nil {
opts.Certificate = cert
}
return opts
}

type OnlineConnectionParams struct {
Expand Down
4 changes: 2 additions & 2 deletions internal/suseconnect/wrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func TestDefaultConnectionOptionsBasic(t *testing.T) {
defaultOptions := DefaultConnectionOptions("rancher-scc-integration", "0.0.1")
defaultOptions := DefaultConnectionOptions("rancher-scc-integration", "0.0.1", nil)
expected := connection.Options{
URL: connection.DefaultBaseURL,
Secure: true,
Expand All @@ -21,7 +21,7 @@ func TestDefaultConnectionOptionsBasic(t *testing.T) {
}

func TestDefaultConnectionOptions(t *testing.T) {
defaultOptions := DefaultConnectionOptions("rancher-scc-integration", "0.0.1")
defaultOptions := DefaultConnectionOptions("rancher-scc-integration", "0.0.1", nil)
assert.Equal(t, connection.DefaultBaseURL, defaultOptions.URL)
assert.Equal(t, "rancher-scc-integration", defaultOptions.AppName)
assert.Equal(t, "0.0.1", defaultOptions.Version)
Expand Down
17 changes: 14 additions & 3 deletions pkg/controllers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func (h *handler) OnSecretChange(_ string, incomingObj *corev1.Secret) (*corev1.
// This only applies to the SCC Entrypoint secrets - currently only used for/by Rancher
// This will adopt "unowned" secrets and ignore any that are owned by other operators
if !helpers.ShouldManage(incomingObj, h.options.OperatorName) {
// When the secret has no managedBy label, we should assume ownership I guess?
// When the secret has no managedBy label, we should assume ownership
if !helpers.HasManagedByLabel(incomingObj) {
h.log.Debugf("taking ownership of the unowned entrypoint secret")
prepared := incomingObj.DeepCopy()
Expand Down Expand Up @@ -258,8 +258,7 @@ func (h *handler) OnSecretChange(_ string, incomingObj *corev1.Secret) (*corev1.
return incomingObj, nil
}

// If secret hash has changed make sure that we submit objects that correspond to that hash
// are cleaned up
// Upon secret hash changes ensure that objects which correspond to that hash are cleaned up
// TODO: make it so that changes to the incoming Salt (which changes the nameID) are correctly handled
// Note that change would affect both name and content hashes - however something seems to not.
if incomingNameHash != params.nameID {
Expand Down Expand Up @@ -319,6 +318,18 @@ func (h *handler) OnSecretChange(_ string, incomingObj *corev1.Secret) (*corev1.
if _, err := h.secretRepo.CreateOrUpdateSecret(regCodeSecret); err != nil {
return incomingObj, err
}

// TODO - maybe pull this out if RMT expects to support offline certs too?
if params.hasRegURLCertData {
regCodeSecret, err := h.regURLCertFromSecretEntrypoint(params)
if err != nil {
return incomingObj, err
}

if _, err := h.secretRepo.CreateOrUpdateSecret(regCodeSecret); err != nil {
return incomingObj, err
}
}
}

// construct associated registration CRs
Expand Down
4 changes: 4 additions & 0 deletions pkg/controllers/lifecycle/deciders.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,7 @@ func SecretHasCredentialsFinalizer(objIn *corev1.Secret) bool {
func SecretHasRegCodeFinalizer(objIn *corev1.Secret) bool {
return hasFinalizer(objIn, consts.FinalizerSccRegistrationCode)
}

func SecretHasRegURLCertFinalizer(objIn *corev1.Secret) bool {
return hasFinalizer(objIn, consts.FinalizerSccRegistrationURLCert)
}
8 changes: 8 additions & 0 deletions pkg/controllers/lifecycle/mutators.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,11 @@ func SecretAddOfflineFinalizer(secret *corev1.Secret) *corev1.Secret {
func SecretRemoveOfflineFinalizer(secret *corev1.Secret) *corev1.Secret {
return runtimeRemoveFinalizer[*corev1.Secret](secret, consts.FinalizerSccOfflineSecret)
}

func SecretAddRegURLCertFinalizer(secret *corev1.Secret) *corev1.Secret {
return runtimeAddFinalizer[*corev1.Secret](secret, consts.FinalizerSccRegistrationURLCert)
}

func SecretRemoveRegURLCertFinalizer(secret *corev1.Secret) *corev1.Secret {
return runtimeRemoveFinalizer[*corev1.Secret](secret, consts.FinalizerSccRegistrationURLCert)
}
35 changes: 32 additions & 3 deletions pkg/controllers/online.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package controllers

import (
"crypto/x509"
"errors"
"fmt"
"net/http"
Expand Down Expand Up @@ -43,11 +44,17 @@ func (s *sccOnlineMode) prepareSCCOnlineConnection(
rancherMetrics telemetry.MetricsWrapper,
registrationURL string,
) suseconnect.SccWrapper {
// Fetch the registration URL certificate if provided
var cert *x509.Certificate
if s.registration.Spec.RegistrationRequest.RegistrationAPICertificateSecretRef != nil {
cert = suseconnect.FetchRegistrationURLCertFrom(s.secretRepo, s.registration.Spec.RegistrationRequest.RegistrationAPICertificateSecretRef)
}

return suseconnect.OnlineRancherConnection(
suseconnect.OnlineConnectionParams{
RancherURL: s.rancherURL,
RegistrationURL: registrationURL,
Options: suseconnect.DefaultConnectionOptions(s.options.OperatorName, s.options.OperatorMetadata.Version),
Options: suseconnect.DefaultConnectionOptions(s.options.OperatorName, s.options.OperatorMetadata.Version, cert),
},
s.sccCredentials.SccCredentials(),
rancherMetrics,
Expand Down Expand Up @@ -387,7 +394,7 @@ func (s *sccOnlineMode) Deregister() error {
if regCodeErr != nil && !apierrors.IsNotFound(regCodeErr) {
return regCodeErr
}
if lifecycle.SecretHasRegCodeFinalizer(regCodeSecret) {
if regCodeSecret != nil && lifecycle.SecretHasRegCodeFinalizer(regCodeSecret) {
updateRegCodeSecret := regCodeSecret.DeepCopy()
updateRegCodeSecret = lifecycle.SecretRemoveRegCodeFinalizer(updateRegCodeSecret)

Expand All @@ -397,10 +404,32 @@ func (s *sccOnlineMode) Deregister() error {
}
}

if err := s.secretRepo.Controller.Delete(regCodeSecretRef.Namespace, regCodeSecretRef.Name, &metav1.DeleteOptions{}); err != nil {
if err := s.secretRepo.Controller.Delete(regCodeSecretRef.Namespace, regCodeSecretRef.Name, &metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
return err
}

// Clean up registration URL certificate secret if it exists
regURLCertSecretRef := s.registration.Spec.RegistrationRequest.RegistrationAPICertificateSecretRef
if regURLCertSecretRef != nil {
regURLCertSecret, regURLCertErr := s.secretRepo.Get(regURLCertSecretRef.Namespace, regURLCertSecretRef.Name)
if regURLCertErr != nil && !apierrors.IsNotFound(regURLCertErr) {
return regURLCertErr
}
if regURLCertSecret != nil && lifecycle.SecretHasRegURLCertFinalizer(regURLCertSecret) {
updateRegURLCertSecret := regURLCertSecret.DeepCopy()
updateRegURLCertSecret = lifecycle.SecretRemoveRegURLCertFinalizer(updateRegURLCertSecret)

_, regURLCertErr = s.secretRepo.Controller.Update(updateRegURLCertSecret)
if regURLCertErr != nil {
return regURLCertErr
}
}

if err := s.secretRepo.Controller.Delete(regURLCertSecretRef.Namespace, regURLCertSecretRef.Name, &metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
return err
}
}

return nil
}

Expand Down
Loading
Loading