github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/base/ca/initialize.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  	"encoding/json"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"path/filepath"
    27  
    28  	"github.com/pkg/errors"
    29  
    30  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    31  	cav1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/ca/v1"
    32  	initializer "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/ca"
    33  	"github.com/IBM-Blockchain/fabric-operator/pkg/initializer/ca/config"
    34  	caconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/ca/config"
    35  	k8sclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient"
    36  
    37  	corev1 "k8s.io/api/core/v1"
    38  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    39  	"k8s.io/apimachinery/pkg/runtime"
    40  	"k8s.io/apimachinery/pkg/types"
    41  
    42  	"sigs.k8s.io/yaml"
    43  )
    44  
    45  //go:generate counterfeiter -o mocks/initializer.go -fake-name Initializer . Initializer
    46  
    47  type Initializer interface {
    48  	Create(*current.IBPCA, *cav1.ServerConfig, initializer.IBPCA) (*initializer.Response, error)
    49  	Update(*current.IBPCA, *cav1.ServerConfig, initializer.IBPCA) (*initializer.Response, error)
    50  }
    51  
    52  type Initialize struct {
    53  	Config *initializer.Config
    54  	Scheme *runtime.Scheme
    55  	Labels func(instance v1.Object) map[string]string
    56  
    57  	Initializer Initializer
    58  	Client      k8sclient.Client
    59  }
    60  
    61  func NewInitializer(config *initializer.Config, scheme *runtime.Scheme, client k8sclient.Client, labels func(instance v1.Object) map[string]string, timeouts initializer.HSMInitJobTimeouts) *Initialize {
    62  	return &Initialize{
    63  		Config:      config,
    64  		Initializer: &initializer.Initializer{Client: client, Timeouts: timeouts},
    65  		Scheme:      scheme,
    66  		Client:      client,
    67  		Labels:      labels,
    68  	}
    69  }
    70  
    71  func (i *Initialize) HandleEnrollmentCAInit(instance *current.IBPCA, update Update) (*initializer.Response, error) {
    72  	var err error
    73  	var resp *initializer.Response
    74  
    75  	log.Info(fmt.Sprintf("Checking if enrollment CA '%s' needs initialization", instance.GetName()))
    76  
    77  	if i.SecretExists(instance, fmt.Sprintf("%s-ca-crypto", instance.GetName())) {
    78  		if update.CAOverridesUpdated() {
    79  			resp, err = i.UpdateEnrollmentCAConfig(instance)
    80  			if err != nil {
    81  				return nil, err
    82  			}
    83  		}
    84  	} else {
    85  		resp, err = i.CreateEnrollmentCAConfig(instance)
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  
    90  	}
    91  
    92  	return resp, nil
    93  }
    94  
    95  func (i *Initialize) HandleTLSCAInit(instance *current.IBPCA, update Update) (*initializer.Response, error) {
    96  	var err error
    97  	var resp *initializer.Response
    98  
    99  	log.Info(fmt.Sprintf("Checking if TLS CA '%s' needs initialization", instance.GetName()))
   100  
   101  	if i.SecretExists(instance, fmt.Sprintf("%s-tlsca-crypto", instance.GetName())) {
   102  		if update.TLSCAOverridesUpdated() {
   103  			resp, err = i.UpdateTLSCAConfig(instance)
   104  			if err != nil {
   105  				return nil, err
   106  			}
   107  		}
   108  	} else {
   109  		resp, err = i.CreateTLSCAConfig(instance)
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  	}
   114  
   115  	return resp, nil
   116  }
   117  
   118  func (i *Initialize) CreateEnrollmentCAConfig(instance *current.IBPCA) (*initializer.Response, error) {
   119  	log.Info(fmt.Sprintf("Creating Enrollment CA config '%s'", instance.GetName()))
   120  	bytes, err := ioutil.ReadFile(i.Config.CADefaultConfigPath)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	sca, err := i.GetEnrollmentInitCA(instance, bytes)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	var caOverrides *cav1.ServerConfig
   131  	if instance.Spec.ConfigOverride != nil && instance.Spec.ConfigOverride.CA != nil {
   132  		caOverrides = &cav1.ServerConfig{}
   133  		err = json.Unmarshal(instance.Spec.ConfigOverride.CA.Raw, caOverrides)
   134  		if err != nil {
   135  			return nil, err
   136  		}
   137  	}
   138  
   139  	resp, err := i.Initializer.Create(instance, caOverrides, sca)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	return resp, nil
   145  }
   146  
   147  func (i *Initialize) UpdateEnrollmentCAConfig(instance *current.IBPCA) (*initializer.Response, error) {
   148  	log.Info(fmt.Sprintf("Updating Enrollment CA config '%s'", instance.GetName()))
   149  	cmname := fmt.Sprintf("%s-ca-config", instance.GetName())
   150  	cm, err := i.ReadConfigMap(instance, cmname)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	sca, err := i.GetEnrollmentInitCA(instance, cm.BinaryData["fabric-ca-server-config.yaml"])
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	var caOverrides *cav1.ServerConfig
   161  	if instance.Spec.ConfigOverride != nil && instance.Spec.ConfigOverride.CA != nil {
   162  		caOverrides = &cav1.ServerConfig{}
   163  		err = json.Unmarshal(instance.Spec.ConfigOverride.CA.Raw, caOverrides)
   164  		if err != nil {
   165  			return nil, err
   166  		}
   167  	}
   168  
   169  	resp, err := i.Initializer.Update(instance, caOverrides, sca)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	return resp, nil
   175  }
   176  
   177  func (i *Initialize) GetEnrollmentInitCA(instance *current.IBPCA, data []byte) (*initializer.CA, error) {
   178  	serverConfig := &cav1.ServerConfig{}
   179  	err := yaml.Unmarshal(data, serverConfig)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	initCAConfig := &caconfig.Config{
   185  		ServerConfig: serverConfig,
   186  		HomeDir:      filepath.Join(i.Config.SharedPath, instance.GetName(), "ca"),
   187  		MountPath:    "/crypto/ca",
   188  		SqlitePath:   instance.Spec.CustomNames.Sqlite,
   189  	}
   190  
   191  	cn := instance.GetName() + "-ca"
   192  	if instance.Spec.ConfigOverride != nil && instance.Spec.ConfigOverride.CA != nil {
   193  		configOverride, err := config.ReadFrom(&instance.Spec.ConfigOverride.CA.Raw)
   194  		if err != nil {
   195  			return nil, err
   196  		}
   197  		if configOverride.ServerConfig.CSR.CN != "" {
   198  			cn = configOverride.ServerConfig.CSR.CN
   199  		}
   200  	}
   201  
   202  	sca := initializer.NewCA(initCAConfig, caconfig.EnrollmentCA, i.Config.SharedPath, instance.UsingHSMProxy(), cn)
   203  
   204  	return sca, nil
   205  }
   206  
   207  func (i *Initialize) CreateTLSCAConfig(instance *current.IBPCA) (*initializer.Response, error) {
   208  	log.Info(fmt.Sprintf("Creating TLS CA config '%s'", instance.GetName()))
   209  	bytes, err := ioutil.ReadFile(i.Config.TLSCADefaultConfigPath)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	sca, err := i.GetTLSInitCA(instance, bytes)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	var tlscaOverrides *cav1.ServerConfig
   220  	if instance.Spec.ConfigOverride != nil && instance.Spec.ConfigOverride.TLSCA != nil {
   221  		tlscaOverrides = &cav1.ServerConfig{}
   222  		err = json.Unmarshal(instance.Spec.ConfigOverride.TLSCA.Raw, tlscaOverrides)
   223  		if err != nil {
   224  			return nil, err
   225  		}
   226  	}
   227  
   228  	resp, err := i.Initializer.Create(instance, tlscaOverrides, sca)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	return resp, nil
   234  }
   235  
   236  func (i *Initialize) UpdateTLSCAConfig(instance *current.IBPCA) (*initializer.Response, error) {
   237  	log.Info(fmt.Sprintf("Updating TLSCA config '%s'", instance.GetName()))
   238  	cmname := fmt.Sprintf("%s-tlsca-config", instance.GetName())
   239  	cm, err := i.ReadConfigMap(instance, cmname)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	tca, err := i.GetTLSInitCA(instance, cm.BinaryData["fabric-ca-server-config.yaml"])
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  
   249  	var tlscaOverrides *cav1.ServerConfig
   250  	if instance.Spec.ConfigOverride != nil && instance.Spec.ConfigOverride.TLSCA != nil {
   251  		tlscaOverrides = &cav1.ServerConfig{}
   252  		err = json.Unmarshal(instance.Spec.ConfigOverride.TLSCA.Raw, tlscaOverrides)
   253  		if err != nil {
   254  			return nil, err
   255  		}
   256  	}
   257  
   258  	resp, err := i.Initializer.Update(instance, tlscaOverrides, tca)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  
   263  	return resp, nil
   264  }
   265  
   266  func (i *Initialize) GetTLSInitCA(instance *current.IBPCA, data []byte) (*initializer.CA, error) {
   267  	serverConfig := &cav1.ServerConfig{}
   268  	err := yaml.Unmarshal(data, serverConfig)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	initCAConfig := &caconfig.Config{
   274  		ServerConfig: serverConfig,
   275  		HomeDir:      filepath.Join(i.Config.SharedPath, instance.GetName(), "tlsca"),
   276  		MountPath:    "/crypto/tlsca",
   277  		SqlitePath:   instance.Spec.CustomNames.Sqlite,
   278  	}
   279  
   280  	cn := instance.GetName() + "-tlsca"
   281  	if instance.Spec.ConfigOverride != nil && instance.Spec.ConfigOverride.TLSCA != nil {
   282  		configOverride, err := config.ReadFrom(&instance.Spec.ConfigOverride.TLSCA.Raw)
   283  		if err != nil {
   284  			return nil, err
   285  		}
   286  		if configOverride.ServerConfig.CSR.CN != "" {
   287  			cn = configOverride.ServerConfig.CSR.CN
   288  		}
   289  	}
   290  
   291  	tca := initializer.NewCA(initCAConfig, caconfig.TLSCA, i.Config.SharedPath, instance.UsingHSMProxy(), cn)
   292  
   293  	return tca, nil
   294  }
   295  
   296  func (i *Initialize) HandleConfigResources(name string, instance *current.IBPCA, resp *initializer.Response, update Update) error {
   297  	var err error
   298  
   299  	if update.CAOverridesUpdated() || update.TLSCAOverridesUpdated() {
   300  		log.Info(fmt.Sprintf("Updating config resources for '%s'", name))
   301  		err = i.UpdateConfigResources(name, instance, resp)
   302  		if err != nil {
   303  			return err
   304  		}
   305  	} else {
   306  		log.Info(fmt.Sprintf("Creating config resources for '%s'", name))
   307  		err = i.CreateConfigResources(name, instance, resp)
   308  		if err != nil {
   309  			return err
   310  		}
   311  	}
   312  
   313  	return nil
   314  }
   315  
   316  func (i *Initialize) UpdateConfigResources(name string, instance *current.IBPCA, resp *initializer.Response) error {
   317  	var err error
   318  
   319  	secretName := fmt.Sprintf("%s-crypto", name)
   320  	secret, err := i.GetCryptoSecret(instance, secretName)
   321  	if err != nil {
   322  		return err
   323  	}
   324  
   325  	mergedCrypto := i.MergeCryptoMaterial(secret.Data, resp.CryptoMap)
   326  
   327  	mergedResp := &initializer.Response{
   328  		CryptoMap: mergedCrypto,
   329  		Config:    resp.Config,
   330  	}
   331  
   332  	err = i.CreateConfigResources(name, instance, mergedResp)
   333  	if err != nil {
   334  		return err
   335  	}
   336  
   337  	return nil
   338  }
   339  
   340  func (i *Initialize) CreateConfigResources(name string, instance *current.IBPCA, resp *initializer.Response) error {
   341  	var err error
   342  
   343  	if len(resp.CryptoMap) > 0 {
   344  		secretName := fmt.Sprintf("%s-crypto", name)
   345  		err = i.CreateOrUpdateCryptoSecret(instance, resp.CryptoMap, secretName)
   346  		if err != nil {
   347  			return err
   348  		}
   349  	}
   350  
   351  	if resp.Config != nil {
   352  		bytes, err := ConfigToBytes(resp.Config)
   353  		if err != nil {
   354  			return err
   355  		}
   356  
   357  		data := map[string][]byte{
   358  			"fabric-ca-server-config.yaml": bytes,
   359  		}
   360  		cmName := fmt.Sprintf("%s-config", name)
   361  		err = i.CreateOrUpdateConfigMap(instance, data, cmName)
   362  		if err != nil {
   363  			return err
   364  		}
   365  	}
   366  
   367  	return nil
   368  }
   369  
   370  func (i *Initialize) ReadConfigMap(instance *current.IBPCA, name string) (*corev1.ConfigMap, error) {
   371  	n := types.NamespacedName{
   372  		Name:      name,
   373  		Namespace: instance.GetNamespace(),
   374  	}
   375  
   376  	cm := &corev1.ConfigMap{}
   377  	err := i.Client.Get(context.TODO(), n, cm)
   378  	if err != nil {
   379  		return nil, errors.Wrap(err, "failed to get config map")
   380  	}
   381  
   382  	return cm, nil
   383  }
   384  
   385  func (i *Initialize) CreateOrUpdateConfigMap(instance *current.IBPCA, data map[string][]byte, name string) error {
   386  	cm := &corev1.ConfigMap{
   387  		ObjectMeta: v1.ObjectMeta{
   388  			Name:      name,
   389  			Namespace: instance.GetNamespace(),
   390  			Labels:    i.Labels(instance),
   391  		},
   392  		BinaryData: data,
   393  	}
   394  
   395  	err := i.Client.CreateOrUpdate(context.TODO(), cm, k8sclient.CreateOrUpdateOption{
   396  		Owner:  instance,
   397  		Scheme: i.Scheme,
   398  	})
   399  	if err != nil {
   400  		return errors.Wrap(err, "failed to create/update config map")
   401  	}
   402  
   403  	return nil
   404  }
   405  
   406  func (i *Initialize) CreateOrUpdateCryptoSecret(instance *current.IBPCA, caCrypto map[string][]byte, name string) error {
   407  	secret := &corev1.Secret{
   408  		ObjectMeta: v1.ObjectMeta{
   409  			Name:      name,
   410  			Namespace: instance.Namespace,
   411  			Labels:    i.Labels(instance),
   412  		},
   413  		Data: caCrypto,
   414  		Type: corev1.SecretTypeOpaque,
   415  	}
   416  
   417  	err := i.Client.CreateOrUpdate(context.TODO(), secret, k8sclient.CreateOrUpdateOption{
   418  		Owner:  instance,
   419  		Scheme: i.Scheme,
   420  	})
   421  	if err != nil {
   422  		return errors.Wrap(err, "failed to create/update secret")
   423  	}
   424  
   425  	return nil
   426  }
   427  
   428  func (i *Initialize) GetCryptoSecret(instance *current.IBPCA, name string) (*corev1.Secret, error) {
   429  	log.Info(fmt.Sprintf("Getting secret '%s'", name))
   430  
   431  	nn := types.NamespacedName{
   432  		Name:      name,
   433  		Namespace: instance.GetNamespace(),
   434  	}
   435  
   436  	secret := &corev1.Secret{}
   437  	err := i.Client.Get(context.TODO(), nn, secret)
   438  	if err != nil {
   439  		return nil, errors.Wrap(err, "failed to create/update secret")
   440  	}
   441  
   442  	return secret, nil
   443  }
   444  
   445  func (i *Initialize) SyncDBConfig(orig *current.IBPCA) (*current.IBPCA, error) {
   446  	instance := orig.DeepCopy()
   447  	if instance.Spec.ConfigOverride != nil {
   448  		if instance.Spec.ConfigOverride.CA != nil {
   449  			eca := &cav1.ServerConfig{}
   450  			err := json.Unmarshal(instance.Spec.ConfigOverride.CA.Raw, eca)
   451  			if err != nil {
   452  				return nil, err
   453  			}
   454  
   455  			if instance.Spec.ConfigOverride.TLSCA == nil {
   456  				tca := &cav1.ServerConfig{}
   457  				tca.CAConfig.DB = eca.CAConfig.DB
   458  
   459  				tbytes, err := json.Marshal(tca)
   460  				if err != nil {
   461  					return nil, err
   462  				}
   463  
   464  				instance.Spec.ConfigOverride.TLSCA = &runtime.RawExtension{Raw: tbytes}
   465  			} else {
   466  				tca := &cav1.ServerConfig{}
   467  				err := json.Unmarshal(instance.Spec.ConfigOverride.TLSCA.Raw, tca)
   468  				if err != nil {
   469  					return nil, err
   470  				}
   471  
   472  				tca.CAConfig.DB = eca.CAConfig.DB
   473  				tbytes, err := json.Marshal(tca)
   474  				if err != nil {
   475  					return nil, err
   476  				}
   477  
   478  				instance.Spec.ConfigOverride.TLSCA = &runtime.RawExtension{Raw: tbytes}
   479  			}
   480  		}
   481  	}
   482  	return instance, nil
   483  }
   484  
   485  func (i *Initialize) MergeCryptoMaterial(current map[string][]byte, updated map[string][]byte) map[string][]byte {
   486  	for ukey, umaterial := range updated {
   487  		if len(umaterial) != 0 {
   488  			current[ukey] = umaterial
   489  		}
   490  	}
   491  
   492  	return current
   493  }
   494  
   495  func (i *Initialize) SecretExists(instance *current.IBPCA, name string) bool {
   496  	n := types.NamespacedName{
   497  		Name:      name,
   498  		Namespace: instance.GetNamespace(),
   499  	}
   500  
   501  	s := &corev1.Secret{}
   502  	err := i.Client.Get(context.TODO(), n, s)
   503  	if err != nil {
   504  		return false
   505  	}
   506  
   507  	return true
   508  }
   509  
   510  func ConfigToBytes(c *cav1.ServerConfig) ([]byte, error) {
   511  	bytes, err := yaml.Marshal(c)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  
   516  	return bytes, nil
   517  }