github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/base/ca/ca.go (about)

     1  /*
     2   * Copyright contributors to the Hyperledger Fabric Operator project
     3   *
     4   * SPDX-License-Identifier: Apache-2.0
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at:
     9   *
    10   * 	  http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  package baseca
    20  
    21  import (
    22  	"context"
    23  	"crypto/ecdsa"
    24  	"crypto/elliptic"
    25  	crand "crypto/rand"
    26  	"crypto/x509"
    27  	"crypto/x509/pkix"
    28  	"encoding/base64"
    29  	"encoding/json"
    30  	"encoding/pem"
    31  	"fmt"
    32  	"math/big"
    33  	"net"
    34  	"os"
    35  	"strings"
    36  	"time"
    37  
    38  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    39  	config "github.com/IBM-Blockchain/fabric-operator/operatorconfig"
    40  	cav1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/ca/v1"
    41  	"github.com/IBM-Blockchain/fabric-operator/pkg/certificate"
    42  	initializer "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/ca"
    43  	commonconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config"
    44  	controllerclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient"
    45  	"github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources"
    46  	resourcemanager "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/manager"
    47  	"github.com/IBM-Blockchain/fabric-operator/pkg/offering/common"
    48  	"github.com/IBM-Blockchain/fabric-operator/pkg/offering/common/reconcilechecks"
    49  	"github.com/IBM-Blockchain/fabric-operator/pkg/operatorerrors"
    50  	"github.com/IBM-Blockchain/fabric-operator/pkg/restart"
    51  	"github.com/IBM-Blockchain/fabric-operator/pkg/util"
    52  	"github.com/IBM-Blockchain/fabric-operator/pkg/util/pointer"
    53  	"github.com/IBM-Blockchain/fabric-operator/version"
    54  	"github.com/pkg/errors"
    55  	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
    56  	"sigs.k8s.io/yaml"
    57  
    58  	appsv1 "k8s.io/api/apps/v1"
    59  	corev1 "k8s.io/api/core/v1"
    60  	rbacv1 "k8s.io/api/rbac/v1"
    61  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    62  	"k8s.io/apimachinery/pkg/runtime"
    63  	"k8s.io/apimachinery/pkg/types"
    64  
    65  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    66  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    67  )
    68  
    69  var log = logf.Log.WithName("base_ca")
    70  
    71  const (
    72  	DaysToSecondsConversion = int64(24 * 60 * 60)
    73  )
    74  
    75  type Override interface {
    76  	Deployment(v1.Object, *appsv1.Deployment, resources.Action) error
    77  	Service(v1.Object, *corev1.Service, resources.Action) error
    78  	PVC(v1.Object, *corev1.PersistentVolumeClaim, resources.Action) error
    79  	Role(v1.Object, *rbacv1.Role, resources.Action) error
    80  	RoleBinding(v1.Object, *rbacv1.RoleBinding, resources.Action) error
    81  	ServiceAccount(v1.Object, *corev1.ServiceAccount, resources.Action) error
    82  	IsPostgres(instance *current.IBPCA) bool
    83  }
    84  
    85  //go:generate counterfeiter -o mocks/update.go -fake-name Update . Update
    86  
    87  type Update interface {
    88  	SpecUpdated() bool
    89  	CAOverridesUpdated() bool
    90  	TLSCAOverridesUpdated() bool
    91  	ConfigOverridesUpdated() bool
    92  	RestartNeeded() bool
    93  	CACryptoUpdated() bool
    94  	CACryptoCreated() bool
    95  	RenewTLSCert() bool
    96  	FabricVersionUpdated() bool
    97  	ImagesUpdated() bool
    98  	CATagUpdated() bool
    99  }
   100  
   101  //go:generate counterfeiter -o mocks/restart_manager.go -fake-name RestartManager . RestartManager
   102  
   103  type RestartManager interface {
   104  	ForConfigOverride(instance v1.Object) error
   105  	TriggerIfNeeded(instance restart.Instance) error
   106  	ForTLSReenroll(instance v1.Object) error
   107  	ForRestartAction(instance v1.Object) error
   108  }
   109  
   110  type IBPCA interface {
   111  	Initialize(instance *current.IBPCA, update Update) error
   112  	PreReconcileChecks(instance *current.IBPCA, update Update) (bool, error)
   113  	ReconcileManagers(instance *current.IBPCA, update Update) error
   114  	Reconcile(instance *current.IBPCA, update Update) (common.Result, error)
   115  }
   116  
   117  //go:generate counterfeiter -o mocks/initialize.go -fake-name InitializeIBPCA . InitializeIBPCA
   118  
   119  type InitializeIBPCA interface {
   120  	HandleEnrollmentCAInit(instance *current.IBPCA, update Update) (*initializer.Response, error)
   121  	HandleConfigResources(name string, instance *current.IBPCA, resp *initializer.Response, update Update) error
   122  	HandleTLSCAInit(instance *current.IBPCA, update Update) (*initializer.Response, error)
   123  	SyncDBConfig(*current.IBPCA) (*current.IBPCA, error)
   124  	CreateOrUpdateConfigMap(instance *current.IBPCA, data map[string][]byte, name string) error
   125  	ReadConfigMap(instance *current.IBPCA, name string) (*corev1.ConfigMap, error)
   126  }
   127  
   128  //go:generate counterfeiter -o mocks/certificate_manager.go -fake-name CertificateManager . CertificateManager
   129  
   130  type CertificateManager interface {
   131  	GetDurationToNextRenewalForCert(string, []byte, v1.Object, int64) (time.Duration, error)
   132  	GetSecret(string, string) (*corev1.Secret, error)
   133  	Expires([]byte, int64) (bool, time.Time, error)
   134  	UpdateSecret(v1.Object, string, map[string][]byte) error
   135  }
   136  
   137  var _ IBPCA = &CA{}
   138  
   139  type CA struct {
   140  	Client controllerclient.Client
   141  	Scheme *runtime.Scheme
   142  	Config *config.Config
   143  
   144  	DeploymentManager     resources.Manager
   145  	ServiceManager        resources.Manager
   146  	PVCManager            resources.Manager
   147  	RoleManager           resources.Manager
   148  	RoleBindingManager    resources.Manager
   149  	ServiceAccountManager resources.Manager
   150  
   151  	Override    Override
   152  	Initializer InitializeIBPCA
   153  
   154  	CertificateManager CertificateManager
   155  	RenewCertTimers    map[string]*time.Timer
   156  
   157  	Restart RestartManager
   158  }
   159  
   160  func New(client controllerclient.Client, scheme *runtime.Scheme, config *config.Config, o Override) *CA {
   161  	ca := &CA{
   162  		Client:   client,
   163  		Scheme:   scheme,
   164  		Config:   config,
   165  		Override: o,
   166  	}
   167  	ca.CreateManagers()
   168  	ca.Initializer = NewInitializer(config.CAInitConfig, scheme, client, ca.GetLabels, config.Operator.CA.Timeouts.HSMInitJob)
   169  	ca.Restart = restart.New(client, config.Operator.Restart.WaitTime.Get(), config.Operator.Restart.Timeout.Get())
   170  	ca.CertificateManager = &certificate.CertificateManager{
   171  		Client: client,
   172  		Scheme: scheme,
   173  	}
   174  	ca.RenewCertTimers = make(map[string]*time.Timer)
   175  
   176  	return ca
   177  }
   178  
   179  func (ca *CA) CreateManagers() {
   180  	override := ca.Override
   181  	resourceManager := resourcemanager.New(ca.Client, ca.Scheme)
   182  	ca.DeploymentManager = resourceManager.CreateDeploymentManager("", override.Deployment, ca.GetLabels, ca.Config.CAInitConfig.DeploymentFile)
   183  	ca.ServiceManager = resourceManager.CreateServiceManager("", override.Service, ca.GetLabels, ca.Config.CAInitConfig.ServiceFile)
   184  	ca.PVCManager = resourceManager.CreatePVCManager("", override.PVC, ca.GetLabels, ca.Config.CAInitConfig.PVCFile)
   185  	ca.RoleManager = resourceManager.CreateRoleManager("", override.Role, ca.GetLabels, ca.Config.CAInitConfig.RoleFile)
   186  	ca.RoleBindingManager = resourceManager.CreateRoleBindingManager("", override.RoleBinding, ca.GetLabels, ca.Config.CAInitConfig.RoleBindingFile)
   187  	ca.ServiceAccountManager = resourceManager.CreateServiceAccountManager("", override.ServiceAccount, ca.GetLabels, ca.Config.CAInitConfig.ServiceAccountFile)
   188  }
   189  
   190  func (ca *CA) Reconcile(instance *current.IBPCA, update Update) (common.Result, error) {
   191  	var err error
   192  
   193  	versionSet, err := ca.SetVersion(instance)
   194  	if err != nil {
   195  		return common.Result{}, errors.Wrap(err, fmt.Sprintf("failed updating CR '%s' to version '%s'", instance.Name, version.Operator))
   196  	}
   197  	if versionSet {
   198  		log.Info("Instance version updated, requeuing request...")
   199  		return common.Result{
   200  			Result: reconcile.Result{
   201  				Requeue: true,
   202  			},
   203  		}, nil
   204  	}
   205  
   206  	instanceUpdated, err := ca.PreReconcileChecks(instance, update)
   207  	if err != nil {
   208  		return common.Result{}, errors.Wrap(err, "failed pre reconcile checks")
   209  	}
   210  
   211  	if instanceUpdated {
   212  		log.Info("Updating instance after pre reconcile checks")
   213  		err := ca.Client.Patch(context.TODO(), instance, nil, controllerclient.PatchOption{
   214  			Resilient: &controllerclient.ResilientPatch{
   215  				Retry:    3,
   216  				Into:     &current.IBPCA{},
   217  				Strategy: k8sclient.MergeFrom,
   218  			},
   219  		})
   220  		if err != nil {
   221  			return common.Result{}, errors.Wrap(err, "failed to update instance")
   222  		}
   223  
   224  		log.Info("Instance updated, requeuing request...")
   225  		return common.Result{
   226  			Result: reconcile.Result{
   227  				Requeue: true,
   228  			},
   229  		}, nil
   230  	}
   231  
   232  	err = ca.AddTLSCryptoIfMissing(instance, ca.GetEndpointsDNS(instance))
   233  	if err != nil {
   234  		return common.Result{}, errors.Wrap(err, "failed to generate tls crypto")
   235  	}
   236  
   237  	err = ca.Initialize(instance, update)
   238  	if err != nil {
   239  		return common.Result{}, operatorerrors.Wrap(err, operatorerrors.CAInitilizationFailed, "failed to initialize ca")
   240  	}
   241  
   242  	err = ca.ReconcileManagers(instance, update)
   243  	if err != nil {
   244  		return common.Result{}, errors.Wrap(err, "failed to reconcile managers")
   245  	}
   246  
   247  	err = ca.UpdateConnectionProfile(instance)
   248  	if err != nil {
   249  		return common.Result{}, errors.Wrap(err, "failed to create connection profile")
   250  	}
   251  
   252  	err = ca.CheckStates(instance)
   253  	if err != nil {
   254  		return common.Result{}, errors.Wrap(err, "failed to check and restore state")
   255  	}
   256  
   257  	if update.CACryptoUpdated() {
   258  		log.Info("TLS crypto updated, triggering restart")
   259  		err = ca.Restart.ForTLSReenroll(instance)
   260  		if err != nil {
   261  			return common.Result{}, errors.Wrap(err, "failed to update restart config")
   262  		}
   263  	}
   264  
   265  	err = ca.HandleActions(instance, update)
   266  	if err != nil {
   267  		return common.Result{}, err
   268  	}
   269  
   270  	err = ca.HandleRestart(instance, update)
   271  	if err != nil {
   272  		return common.Result{}, err
   273  	}
   274  
   275  	return common.Result{}, nil
   276  }
   277  
   278  // PreReconcileChecks validate CR request before starting reconcile flow
   279  func (ca *CA) PreReconcileChecks(instance *current.IBPCA, update Update) (bool, error) {
   280  	var err error
   281  
   282  	imagesUpdated, err := reconcilechecks.FabricVersionHelper(instance, ca.Config.Operator.Versions, update)
   283  	if err != nil {
   284  		return false, errors.Wrap(err, "failed to during version and image checks")
   285  	}
   286  
   287  	var maxNameLength *int
   288  	if instance.Spec.ConfigOverride != nil {
   289  		maxNameLength = instance.Spec.ConfigOverride.MaxNameLength
   290  	}
   291  	err = util.ValidationChecks(instance.TypeMeta, instance.ObjectMeta, "IBPCA", maxNameLength)
   292  	if err != nil {
   293  		return false, err
   294  	}
   295  
   296  	if instance.Spec.HSMSet() {
   297  		err = util.ValidateHSMProxyURL(instance.Spec.HSM.PKCS11Endpoint)
   298  		if err != nil {
   299  			return false, errors.Wrapf(err, "invalid HSM endpoint for ca instance '%s'", instance.GetName())
   300  		}
   301  	}
   302  
   303  	if !instance.Spec.DomainSet() {
   304  		return false, fmt.Errorf("domain not set for ca instance '%s'", instance.GetName())
   305  	}
   306  
   307  	zoneUpdated, err := ca.SelectZone(instance)
   308  	if err != nil {
   309  		return false, err
   310  	}
   311  
   312  	regionUpdated, err := ca.SelectRegion(instance)
   313  	if err != nil {
   314  		return false, err
   315  	}
   316  
   317  	hsmImageUpdated := ca.ReconcileHSMImages(instance)
   318  
   319  	var replicasUpdated bool
   320  	if instance.Spec.Replicas == nil {
   321  		replicas := int32(1)
   322  		instance.Spec.Replicas = &replicas
   323  		replicasUpdated = true
   324  	}
   325  
   326  	updated := zoneUpdated || regionUpdated || hsmImageUpdated || replicasUpdated || imagesUpdated
   327  
   328  	if updated {
   329  		log.Info(fmt.Sprintf("zoneUpdated %t, regionUpdated %t, hsmImageUpdated %t, replicasUpdated %t, imagesUpdated %t",
   330  			zoneUpdated, regionUpdated, hsmImageUpdated, replicasUpdated, imagesUpdated))
   331  	}
   332  
   333  	return updated, nil
   334  }
   335  
   336  func (ca *CA) SetVersion(instance *current.IBPCA) (bool, error) {
   337  	if instance.Status.Version == "" || !version.String(instance.Status.Version).Equal(version.Operator) {
   338  		log.Info("Version of Operator: ", "version", version.Operator)
   339  		log.Info("Version of CR: ", "version", instance.Status.Version)
   340  		log.Info(fmt.Sprintf("Setting '%s' to version '%s'", instance.Name, version.Operator))
   341  
   342  		instance.Status.Version = version.Operator
   343  		err := ca.Client.PatchStatus(context.TODO(), instance, nil, controllerclient.PatchOption{
   344  			Resilient: &controllerclient.ResilientPatch{
   345  				Retry:    3,
   346  				Into:     &current.IBPCA{},
   347  				Strategy: k8sclient.MergeFrom,
   348  			},
   349  		})
   350  		if err != nil {
   351  			return false, err
   352  		}
   353  		return true, nil
   354  	}
   355  	return false, nil
   356  }
   357  
   358  func (ca *CA) Initialize(instance *current.IBPCA, update Update) error {
   359  	var err error
   360  
   361  	// TODO: Add checks to determine if initialization is neeeded. Split this method into
   362  	// two, one should handle initialization during the create event of a CR and the other
   363  	// should update events
   364  
   365  	// Service account is required by job
   366  	err = ca.ReconcileRBAC(instance)
   367  	if err != nil {
   368  		return err
   369  	}
   370  
   371  	if instance.IsHSMEnabled() {
   372  		// If HSM config not found, HSM proxy is being used
   373  		if instance.UsingHSMProxy() {
   374  			err = os.Setenv("PKCS11_PROXY_SOCKET", instance.Spec.HSM.PKCS11Endpoint)
   375  			if err != nil {
   376  				return err
   377  			}
   378  		} else {
   379  			hsmConfig, err := commonconfig.ReadHSMConfig(ca.Client, instance)
   380  			if err != nil {
   381  				return errors.New("using non-proxy HSM, but no HSM config defined as config map 'ibp-hsm-config'")
   382  			}
   383  
   384  			if hsmConfig.Daemon != nil {
   385  				log.Info("Using daemon based HSM, creating pvc...")
   386  				ca.PVCManager.SetCustomName(instance.Spec.CustomNames.PVC.CA)
   387  				err = ca.PVCManager.Reconcile(instance, update.SpecUpdated())
   388  				if err != nil {
   389  					return errors.Wrap(err, "failed PVC reconciliation")
   390  				}
   391  			}
   392  		}
   393  	}
   394  
   395  	instance, err = ca.Initializer.SyncDBConfig(instance)
   396  	if err != nil {
   397  		return err
   398  	}
   399  
   400  	eresp, err := ca.Initializer.HandleEnrollmentCAInit(instance, update)
   401  	if err != nil {
   402  		return err
   403  	}
   404  
   405  	if eresp != nil {
   406  		err = ca.Initializer.HandleConfigResources(fmt.Sprintf("%s-ca", instance.GetName()), instance, eresp, update)
   407  		if err != nil {
   408  			return err
   409  		}
   410  	}
   411  
   412  	tresp, err := ca.Initializer.HandleTLSCAInit(instance, update)
   413  	if err != nil {
   414  		return err
   415  	}
   416  
   417  	if tresp != nil {
   418  		err = ca.Initializer.HandleConfigResources(fmt.Sprintf("%s-tlsca", instance.GetName()), instance, tresp, update)
   419  		if err != nil {
   420  			return err
   421  		}
   422  	}
   423  
   424  	// If deployment exists, and configoverride update detected need to restart pod(s) to pick up
   425  	// the latest configuration from configmap and secret
   426  	if ca.DeploymentManager.Exists(instance) && update.ConfigOverridesUpdated() {
   427  		// Request deployment restart for config override update
   428  		if err := ca.Restart.ForConfigOverride(instance); err != nil {
   429  			return err
   430  		}
   431  	}
   432  
   433  	return nil
   434  }
   435  
   436  func (ca *CA) SelectZone(instance *current.IBPCA) (bool, error) {
   437  	if instance.Spec.Zone == "select" {
   438  		zone := util.GetZone(ca.Client)
   439  		instance.Spec.Zone = zone
   440  		return true, nil
   441  	}
   442  	if instance.Spec.Zone != "" {
   443  		err := util.ValidateZone(ca.Client, instance.Spec.Zone)
   444  		if err != nil {
   445  			return false, err
   446  		}
   447  	}
   448  	return false, nil
   449  }
   450  
   451  func (ca *CA) SelectRegion(instance *current.IBPCA) (bool, error) {
   452  	if instance.Spec.Region == "select" {
   453  		region := util.GetRegion(ca.Client)
   454  		instance.Spec.Region = region
   455  		return true, nil
   456  	}
   457  	if instance.Spec.Region != "" {
   458  		err := util.ValidateRegion(ca.Client, instance.Spec.Region)
   459  		if err != nil {
   460  			return false, err
   461  		}
   462  	}
   463  	return false, nil
   464  }
   465  
   466  func (ca *CA) ReconcileManagers(instance *current.IBPCA, updated Update) error {
   467  	var err error
   468  
   469  	update := updated.SpecUpdated()
   470  
   471  	if !ca.Override.IsPostgres(instance) {
   472  		log.Info("Using sqlite database, creating pvc...")
   473  		ca.PVCManager.SetCustomName(instance.Spec.CustomNames.PVC.CA)
   474  		err = ca.PVCManager.Reconcile(instance, update)
   475  		if err != nil {
   476  			return errors.Wrap(err, "failed PVC reconciliation")
   477  		}
   478  	}
   479  
   480  	err = ca.ServiceManager.Reconcile(instance, update)
   481  	if err != nil {
   482  		return errors.Wrap(err, "failed Service reconciliation")
   483  	}
   484  
   485  	err = ca.ReconcileRBAC(instance)
   486  	if err != nil {
   487  		return errors.Wrap(err, "failed RBAC reconciliation")
   488  	}
   489  
   490  	err = ca.DeploymentManager.Reconcile(instance, update)
   491  	if err != nil {
   492  		return errors.Wrap(err, "failed Deployment reconciliation")
   493  	}
   494  
   495  	// TODO: Can this be removed?
   496  	err = ca.createSecret(instance, "-ca")
   497  	if err != nil {
   498  		return errors.Wrap(err, "failed CA Secret reconciliation")
   499  	}
   500  
   501  	// TODO: Can this be removed?
   502  	err = ca.createSecret(instance, "-tlsca")
   503  	if err != nil {
   504  		return errors.Wrap(err, "failed TLS Secret reconciliation")
   505  	}
   506  
   507  	return nil
   508  }
   509  
   510  func (ca *CA) ReconcileRBAC(instance *current.IBPCA) error {
   511  	var err error
   512  
   513  	err = ca.RoleManager.Reconcile(instance, false)
   514  	if err != nil {
   515  		return err
   516  	}
   517  
   518  	err = ca.RoleBindingManager.Reconcile(instance, false)
   519  	if err != nil {
   520  		return err
   521  	}
   522  
   523  	err = ca.ServiceAccountManager.Reconcile(instance, false)
   524  	if err != nil {
   525  		return err
   526  	}
   527  
   528  	return nil
   529  }
   530  
   531  func (ca *CA) UpdateConnectionProfile(instance *current.IBPCA) error {
   532  	var err error
   533  
   534  	endpoints := ca.GetEndpoints(instance)
   535  
   536  	cacrypto, err := common.GetCACryptoEncoded(ca.Client, instance)
   537  	if err != nil {
   538  		return err
   539  	}
   540  	tlscacrypto, err := common.GetTLSCACryptoEncoded(ca.Client, instance)
   541  	if err != nil {
   542  		return err
   543  	}
   544  
   545  	err = ca.UpdateConnectionProfileConfigmap(instance, *endpoints, cacrypto.TLSCert, cacrypto.Cert, tlscacrypto.Cert)
   546  	if err != nil {
   547  		return err
   548  	}
   549  
   550  	return nil
   551  }
   552  
   553  func (ca *CA) UpdateConnectionProfileConfigmap(instance *current.IBPCA, endpoints current.CAEndpoints, tlscert, cacert, tlscacert string) error {
   554  	var err error
   555  
   556  	name := instance.Name + "-connection-profile"
   557  	nn := types.NamespacedName{
   558  		Name:      name,
   559  		Namespace: instance.GetNamespace(),
   560  	}
   561  
   562  	log.Info(fmt.Sprintf("Create connection profle configmap called for %s", instance.Name))
   563  	connectionProfile := &current.CAConnectionProfile{
   564  		Endpoints: endpoints,
   565  		TLS: &current.ConnectionProfileTLS{
   566  			Cert: tlscert,
   567  		},
   568  		CA: &current.MSP{
   569  			SignCerts: cacert,
   570  		},
   571  		TLSCA: &current.MSP{
   572  			SignCerts: tlscacert,
   573  		},
   574  	}
   575  
   576  	bytes, err := json.Marshal(connectionProfile)
   577  	if err != nil {
   578  		return errors.Wrap(err, "failed to marshal connectionprofile")
   579  	}
   580  
   581  	cm := &corev1.ConfigMap{
   582  		ObjectMeta: v1.ObjectMeta{
   583  			Name:      name,
   584  			Namespace: instance.GetNamespace(),
   585  			Labels:    ca.GetLabels(instance),
   586  		},
   587  		BinaryData: map[string][]byte{"profile.json": bytes},
   588  	}
   589  
   590  	err = ca.Client.Get(context.TODO(), nn, &corev1.ConfigMap{})
   591  	if err == nil {
   592  		err = ca.Client.Update(context.TODO(), cm, controllerclient.UpdateOption{Owner: instance, Scheme: ca.Scheme})
   593  		if err != nil {
   594  			return errors.Wrap(err, "failed to update connection profile configmap")
   595  		}
   596  	} else {
   597  		err = ca.Client.Create(context.TODO(), cm, controllerclient.CreateOption{Owner: instance, Scheme: ca.Scheme})
   598  		if err != nil {
   599  			return errors.Wrap(err, "failed to create connection profile configmap")
   600  		}
   601  	}
   602  
   603  	return nil
   604  }
   605  
   606  func (ca *CA) GetEndpoints(instance *current.IBPCA) *current.CAEndpoints {
   607  	endpoints := &current.CAEndpoints{
   608  		API:        "https://" + instance.Namespace + "-" + instance.Name + "-ca." + instance.Spec.Domain + ":443",
   609  		Operations: "https://" + instance.Namespace + "-" + instance.Name + "-operations." + instance.Spec.Domain + ":443",
   610  	}
   611  	return endpoints
   612  }
   613  
   614  func (ca *CA) CheckStates(instance *current.IBPCA) error {
   615  	// Check state if deployment exists, make sure that deployment matches what is expected
   616  	// base on IBPCA spec
   617  	if ca.DeploymentManager.Exists(instance) {
   618  		err := ca.DeploymentManager.CheckState(instance)
   619  		if err != nil {
   620  			log.Error(err, "unexpected state")
   621  			err = ca.DeploymentManager.RestoreState(instance)
   622  			if err != nil {
   623  				return err
   624  			}
   625  		}
   626  	}
   627  
   628  	return nil
   629  }
   630  
   631  func (ca *CA) GetLabels(instance v1.Object) map[string]string {
   632  	return instance.GetLabels()
   633  }
   634  
   635  // TODO: Can this be removed?
   636  func (ca *CA) createSecret(instance *current.IBPCA, suffix string) error {
   637  	secretCA := &corev1.Secret{}
   638  	secretCA.Name = instance.Name + suffix
   639  	secretCA.Namespace = instance.Namespace
   640  	secretCA.Labels = ca.GetLabels(instance)
   641  
   642  	secretCA.Data = map[string][]byte{}
   643  	secretCA.Data["_shared_creation"] = []byte("-----BEGIN")
   644  
   645  	err := ca.Client.Create(context.TODO(), secretCA, controllerclient.CreateOption{
   646  		Owner:  instance,
   647  		Scheme: ca.Scheme,
   648  	})
   649  	if err != nil {
   650  		return err
   651  	}
   652  
   653  	return nil
   654  }
   655  
   656  func (ca *CA) CreateCACryptoSecret(instance *current.IBPCA, caCrypto map[string][]byte) error {
   657  	// Create CA secret with crypto
   658  	secret := &corev1.Secret{
   659  		Data: caCrypto,
   660  		Type: corev1.SecretTypeOpaque,
   661  	}
   662  	secret.Name = instance.Name + "-ca-crypto"
   663  	secret.Namespace = instance.Namespace
   664  	secret.Labels = ca.GetLabels(instance)
   665  
   666  	err := ca.Client.Create(context.TODO(), secret, controllerclient.CreateOption{
   667  		Owner:  instance,
   668  		Scheme: ca.Scheme,
   669  	})
   670  	if err != nil {
   671  		return errors.Wrap(err, "failed to create CA crypto secret")
   672  	}
   673  
   674  	return nil
   675  }
   676  
   677  func (ca *CA) CreateTLSCACryptoSecret(instance *current.IBPCA, tlscaCrypto map[string][]byte) error {
   678  	// Create TLSCA secret with crypto
   679  	secret := &corev1.Secret{
   680  		Data: tlscaCrypto,
   681  		Type: corev1.SecretTypeOpaque,
   682  	}
   683  	secret.Name = instance.Name + "-tlsca-crypto"
   684  	secret.Namespace = instance.Namespace
   685  	secret.Labels = ca.GetLabels(instance)
   686  
   687  	err := ca.Client.Create(context.TODO(), secret, controllerclient.CreateOption{
   688  		Owner:  instance,
   689  		Scheme: ca.Scheme,
   690  	})
   691  	if err != nil {
   692  		return errors.Wrap(err, "failed to create TLS CA crypto secret")
   693  	}
   694  
   695  	return nil
   696  }
   697  
   698  func (ca *CA) AddTLSCryptoIfMissing(instance *current.IBPCA, endpoints *current.CAEndpoints) error {
   699  	var err error
   700  	caOverrides := &cav1.ServerConfig{}
   701  
   702  	genTLSCrypto := func() error {
   703  		tlskey, tlscert, err := ca.GenTLSCrypto(instance, endpoints)
   704  		if err != nil {
   705  			return err
   706  		}
   707  
   708  		base64cert := base64.StdEncoding.EncodeToString(tlscert)
   709  		base64key := base64.StdEncoding.EncodeToString(tlskey)
   710  
   711  		caOverrides.TLS = cav1.ServerTLSConfig{
   712  			Enabled:  pointer.True(),
   713  			CertFile: base64cert,
   714  			KeyFile:  base64key,
   715  		}
   716  
   717  		caBytes, err := json.Marshal(caOverrides)
   718  		if err != nil {
   719  			return err
   720  		}
   721  
   722  		instance.Spec.ConfigOverride.CA = &runtime.RawExtension{Raw: caBytes}
   723  		return nil
   724  	}
   725  
   726  	// check for cert
   727  	err = ca.CheckForTLSSecret(instance)
   728  	if err != nil {
   729  		log.Info(fmt.Sprintf("No TLS crypto configurated for CA '%s', generating TLS crypto...", instance.GetName()))
   730  		// that means secret is not found on cluster
   731  		if instance.Spec.ConfigOverride == nil {
   732  			instance.Spec.ConfigOverride = &current.ConfigOverride{}
   733  			err := genTLSCrypto()
   734  			if err != nil {
   735  				return err
   736  			}
   737  
   738  			return nil
   739  		}
   740  
   741  		if instance.Spec.ConfigOverride.CA == nil {
   742  			err := genTLSCrypto()
   743  			if err != nil {
   744  				return err
   745  			}
   746  
   747  			return nil
   748  		}
   749  
   750  		if instance.Spec.ConfigOverride != nil && instance.Spec.ConfigOverride.CA != nil {
   751  			err = json.Unmarshal(instance.Spec.ConfigOverride.CA.Raw, caOverrides)
   752  			if err != nil {
   753  				return err
   754  			}
   755  
   756  			if caOverrides.TLS.CertFile == "" {
   757  				err := genTLSCrypto()
   758  				if err != nil {
   759  					return err
   760  				}
   761  			}
   762  
   763  			return nil
   764  		}
   765  	}
   766  
   767  	return nil
   768  }
   769  
   770  func (ca *CA) GenTLSCrypto(instance *current.IBPCA, endpoints *current.CAEndpoints) ([]byte, []byte, error) {
   771  	priv, err := ecdsa.GenerateKey(elliptic.P256(), crand.Reader)
   772  	if err != nil {
   773  		return nil, nil, errors.Wrap(err, "failed to generate key")
   774  	}
   775  
   776  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
   777  	serialNumber, err := crand.Int(crand.Reader, serialNumberLimit)
   778  	if err != nil {
   779  		return nil, nil, errors.Wrap(err, "failed to generate serial number")
   780  	}
   781  
   782  	notBefore := time.Now()
   783  	notAfter := notBefore.Add(time.Hour * 87600) // valid for 10 years
   784  
   785  	template := x509.Certificate{
   786  		SerialNumber: serialNumber,
   787  		Issuer: pkix.Name{
   788  			Country:            []string{"US"},
   789  			Province:           []string{"North Carolina"},
   790  			Locality:           []string{"Durham"},
   791  			Organization:       []string{"IBM"},
   792  			OrganizationalUnit: []string{"Blockchain"},
   793  			CommonName:         endpoints.API,
   794  		},
   795  		Subject: pkix.Name{
   796  			Country:            []string{"US"},
   797  			Province:           []string{"North Carolina"},
   798  			Locality:           []string{"Durham"},
   799  			Organization:       []string{"IBM"},
   800  			OrganizationalUnit: []string{"Blockchain"},
   801  			CommonName:         endpoints.API,
   802  		},
   803  
   804  		NotBefore: notBefore,
   805  		NotAfter:  notAfter,
   806  	}
   807  
   808  	ip := net.ParseIP(endpoints.API)
   809  	if ip == nil {
   810  		template.DNSNames = append(template.DNSNames, endpoints.API)
   811  	} else {
   812  		template.IPAddresses = append(template.IPAddresses, ip)
   813  	}
   814  	ip = net.ParseIP(endpoints.Operations)
   815  	if ip == nil {
   816  		template.DNSNames = append(template.DNSNames, endpoints.Operations)
   817  	} else {
   818  		template.IPAddresses = append(template.IPAddresses, ip)
   819  	}
   820  
   821  	derBytes, err := x509.CreateCertificate(crand.Reader, &template, &template, &priv.PublicKey, priv)
   822  	if err != nil {
   823  		return nil, nil, errors.Wrap(err, "failed to create certificate")
   824  	}
   825  
   826  	keyBytes, err := x509.MarshalECPrivateKey(priv)
   827  	if err != nil {
   828  		return nil, nil, errors.Wrap(err, "failed to marshal key")
   829  	}
   830  
   831  	certPEM := &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}
   832  	keyPEM := &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
   833  
   834  	certBytes := pem.EncodeToMemory(certPEM)
   835  	keyBytes = pem.EncodeToMemory(keyPEM)
   836  
   837  	return keyBytes, certBytes, nil
   838  }
   839  
   840  func (ca *CA) CheckForTLSSecret(instance *current.IBPCA) error {
   841  	secret := &corev1.Secret{}
   842  	err := ca.Client.Get(context.TODO(), types.NamespacedName{
   843  		Name:      fmt.Sprintf("%s-tlsca-crypto", instance.Name),
   844  		Namespace: instance.Namespace}, secret)
   845  	return err
   846  }
   847  
   848  func (ca *CA) CheckCertificates(instance *current.IBPCA) (*current.CRStatus, error) {
   849  	secret, err := ca.CertificateManager.GetSecret(
   850  		fmt.Sprintf("%s-ca-crypto", instance.GetName()),
   851  		instance.GetNamespace(),
   852  	)
   853  
   854  	numSecondsBeforeExpire := instance.GetNumSecondsWarningPeriod()
   855  	expiring, expireDate, err := ca.CertificateManager.Expires(secret.Data["tls-cert.pem"], numSecondsBeforeExpire)
   856  	if err != nil {
   857  		return nil, err
   858  	}
   859  
   860  	var message string
   861  	statusType := current.Deployed
   862  
   863  	if expiring {
   864  		statusType = current.Warning
   865  		// Check if tls cert's expiration date has already passed
   866  		if expireDate.Before(time.Now()) {
   867  			statusType = current.Error
   868  			message += fmt.Sprintf("TLS cert for '%s' has expired", instance.GetName())
   869  		} else {
   870  			message += fmt.Sprintf("TLS cert for '%s' expires on %s", instance.GetName(), expireDate.String())
   871  		}
   872  	}
   873  
   874  	crStatus := &current.CRStatus{
   875  		Type:    statusType,
   876  		Message: message,
   877  	}
   878  
   879  	switch statusType {
   880  	case current.Deployed:
   881  		crStatus.Reason = "allPodsDeployed"
   882  	default:
   883  		crStatus.Reason = "certRenewalRequired"
   884  	}
   885  
   886  	return crStatus, nil
   887  }
   888  
   889  func (ca *CA) RenewCert(instance *current.IBPCA, endpoints *current.CAEndpoints) error {
   890  	log.Info(fmt.Sprintf("Renewing TLS certificate for CA '%s'", instance.GetName()))
   891  
   892  	tlskey, tlscert, err := ca.GenTLSCrypto(instance, endpoints)
   893  	if err != nil {
   894  		return err
   895  	}
   896  
   897  	name := fmt.Sprintf("%s-ca-crypto", instance.GetName())
   898  	secret, err := ca.CertificateManager.GetSecret(
   899  		name,
   900  		instance.GetNamespace(),
   901  	)
   902  
   903  	secret.Data["tls-cert.pem"] = tlscert
   904  	secret.Data["tls-key.pem"] = tlskey
   905  	secret.Data["operations-cert.pem"] = tlscert
   906  	secret.Data["operations-key.pem"] = tlskey
   907  
   908  	if err := ca.CertificateManager.UpdateSecret(instance, name, secret.Data); err != nil {
   909  		return err
   910  	}
   911  
   912  	return nil
   913  }
   914  
   915  func (ca *CA) GetEndpointsDNS(instance *current.IBPCA) *current.CAEndpoints {
   916  	return &current.CAEndpoints{
   917  		API:        fmt.Sprintf("%s-%s-ca.%s", instance.Namespace, instance.Name, instance.Spec.Domain),
   918  		Operations: fmt.Sprintf("%s-%s-operations.%s", instance.Namespace, instance.Name, instance.Spec.Domain),
   919  	}
   920  }
   921  
   922  func (ca *CA) ReconcileHSMImages(instance *current.IBPCA) bool {
   923  	hsmConfig, err := commonconfig.ReadHSMConfig(ca.Client, instance)
   924  	if err != nil {
   925  		return false
   926  	}
   927  
   928  	if hsmConfig.Library.AutoUpdateDisabled {
   929  		return false
   930  	}
   931  
   932  	updated := false
   933  	if hsmConfig.Library.Image != "" {
   934  		hsmImage := hsmConfig.Library.Image
   935  		lastIndex := strings.LastIndex(hsmImage, ":")
   936  		image := hsmImage[:lastIndex]
   937  		tag := hsmImage[lastIndex+1:]
   938  
   939  		if instance.Spec.Images.HSMImage != image {
   940  			instance.Spec.Images.HSMImage = image
   941  			updated = true
   942  		}
   943  
   944  		if instance.Spec.Images.HSMTag != tag {
   945  			instance.Spec.Images.HSMTag = tag
   946  			updated = true
   947  		}
   948  	}
   949  
   950  	return updated
   951  }
   952  
   953  func (ca *CA) HandleActions(instance *current.IBPCA, update Update) error {
   954  	orig := instance.DeepCopy()
   955  
   956  	if update.RenewTLSCert() {
   957  		if err := common.BackupCACrypto(ca.Client, ca.Scheme, instance, ca.GetLabels(instance)); err != nil {
   958  			return errors.Wrap(err, "failed to backup crypto before renewing cert")
   959  		}
   960  
   961  		if err := ca.RenewCert(instance, ca.GetEndpointsDNS(instance)); err != nil {
   962  			log.Error(err, "Resetting action flag on failure")
   963  			instance.ResetTLSRenew()
   964  			return err
   965  		}
   966  		instance.ResetTLSRenew()
   967  	}
   968  
   969  	if update.RestartNeeded() {
   970  		if err := ca.RestartAction(instance); err != nil {
   971  			log.Error(err, "Resetting action flag on failure")
   972  			instance.ResetRestart()
   973  			return err
   974  		}
   975  		instance.ResetRestart()
   976  	}
   977  
   978  	if err := ca.Client.Patch(context.TODO(), instance, k8sclient.MergeFrom(orig)); err != nil {
   979  		return errors.Wrap(err, "failed to reset action flags")
   980  	}
   981  
   982  	return nil
   983  }
   984  
   985  func (ca *CA) RestartAction(instance *current.IBPCA) error {
   986  	log.Info("Restart triggered via action parameter")
   987  	if err := ca.Restart.ForRestartAction(instance); err != nil {
   988  		return errors.Wrap(err, "failed to restart ca pods")
   989  	}
   990  	return nil
   991  }
   992  
   993  func (ca *CA) HandleRestart(instance *current.IBPCA, update Update) error {
   994  	// If restart is disabled for components, can return immediately
   995  	if ca.Config.Operator.Restart.Disable.Components {
   996  		return nil
   997  	}
   998  
   999  	err := ca.Restart.TriggerIfNeeded(instance)
  1000  	if err != nil {
  1001  		return errors.Wrap(err, "failed to restart deployment")
  1002  	}
  1003  
  1004  	return nil
  1005  }
  1006  
  1007  func (ca *CA) ReconcileFabricCAMigration(instance *current.IBPCA) error {
  1008  	cmname := fmt.Sprintf("%s-ca-config", instance.GetName())
  1009  	cm, err := ca.Initializer.ReadConfigMap(instance, cmname)
  1010  	if err != nil {
  1011  		return err
  1012  	}
  1013  
  1014  	log.Info(fmt.Sprintf("Migrating config map '%s'", cmname))
  1015  
  1016  	serverConfig := &cav1.ServerConfig{}
  1017  	err = yaml.Unmarshal(cm.BinaryData["fabric-ca-server-config.yaml"], serverConfig)
  1018  	if err != nil {
  1019  		return err
  1020  	}
  1021  
  1022  	if serverConfig.CA.ReenrollIgnoreCertExpiry == pointer.True() {
  1023  		// if it is already updated no need to update configmap
  1024  		return nil
  1025  	} else {
  1026  		serverConfig.CA.ReenrollIgnoreCertExpiry = pointer.True()
  1027  	}
  1028  
  1029  	caConfigBytes, err := yaml.Marshal(serverConfig)
  1030  	if err != nil {
  1031  		return err
  1032  	}
  1033  
  1034  	log.Info(fmt.Sprintf("Updating config map '%s'", cmname))
  1035  
  1036  	cm.BinaryData["fabric-ca-server-config.yaml"] = caConfigBytes
  1037  
  1038  	err = ca.Initializer.CreateOrUpdateConfigMap(instance, cm.BinaryData, cmname)
  1039  	if err != nil {
  1040  		return err
  1041  	}
  1042  	return nil
  1043  }