github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/common/backupcrypto.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 common
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"time"
    26  
    27  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    28  	"github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common"
    29  	k8sclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient"
    30  	"github.com/pkg/errors"
    31  	corev1 "k8s.io/api/core/v1"
    32  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    33  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/runtime"
    35  	"k8s.io/apimachinery/pkg/types"
    36  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    37  )
    38  
    39  var log = logf.Log.WithName("backup_crypto")
    40  
    41  // Number of iterations we are storing
    42  const ITERATIONS = 10
    43  
    44  type Backup struct {
    45  	List      []*current.MSP `json:"list"`
    46  	Timestamp string         `json:"timestamp"`
    47  }
    48  
    49  type Crypto struct {
    50  	TLS        *current.MSP
    51  	Ecert      *current.MSP
    52  	Operations *current.MSP
    53  	CA         *current.MSP
    54  }
    55  
    56  func BackupCrypto(client k8sclient.Client, scheme *runtime.Scheme, instance v1.Object, labels map[string]string) error {
    57  	tlsCrypto := GetCrypto("tls", client, instance)
    58  	ecertCrypto := GetCrypto("ecert", client, instance)
    59  
    60  	if tlsCrypto == nil && ecertCrypto == nil {
    61  		// No backup required if crypto doesn't exist/no found
    62  		log.Info(fmt.Sprintf("No TLS or ecert crypto found for %s, not performing backup", instance.GetName()))
    63  		return nil
    64  	}
    65  
    66  	crypto := &Crypto{
    67  		TLS:   tlsCrypto,
    68  		Ecert: ecertCrypto,
    69  	}
    70  
    71  	return backupCrypto(client, scheme, instance, labels, crypto)
    72  }
    73  
    74  func BackupCACrypto(client k8sclient.Client, scheme *runtime.Scheme, instance v1.Object, labels map[string]string) error {
    75  	caCrypto, operationsCrypto, tlsCrypto := GetCACrypto(client, instance)
    76  
    77  	if caCrypto == nil && operationsCrypto == nil && tlsCrypto == nil {
    78  		log.Info(fmt.Sprintf("No crypto found for %s, not performing backup", instance.GetName()))
    79  		return nil
    80  	}
    81  
    82  	crypto := &Crypto{
    83  		CA:         caCrypto,
    84  		Operations: operationsCrypto,
    85  		TLS:        tlsCrypto,
    86  	}
    87  	return backupCrypto(client, scheme, instance, labels, crypto)
    88  }
    89  
    90  func backupCrypto(client k8sclient.Client, scheme *runtime.Scheme, instance v1.Object, labels map[string]string, crypto *Crypto) error {
    91  	backupSecret, err := GetBackupSecret(client, instance)
    92  	if err != nil {
    93  		if k8serrors.IsNotFound(err) {
    94  			// Create secret
    95  			data, err := CreateBackupSecretData(crypto)
    96  			if err != nil {
    97  				return errors.Wrap(err, "failed to create backup secret data")
    98  			}
    99  
   100  			newSecret := &corev1.Secret{
   101  				ObjectMeta: v1.ObjectMeta{
   102  					Name:      fmt.Sprintf("%s-crypto-backup", instance.GetName()),
   103  					Namespace: instance.GetNamespace(),
   104  					Labels:    labels,
   105  				},
   106  				Data: data,
   107  				Type: corev1.SecretTypeOpaque,
   108  			}
   109  
   110  			err = CreateBackupSecret(client, scheme, instance, newSecret)
   111  			if err != nil {
   112  				return errors.Wrap(err, "failed to create backup secret")
   113  			}
   114  			return nil
   115  		}
   116  		return errors.Wrap(err, "failed to get backup secret")
   117  	}
   118  
   119  	// Update secret
   120  	data, err := UpdateBackupSecretData(backupSecret.Data, crypto)
   121  	if err != nil {
   122  		return errors.Wrap(err, "failed to update backup secret data")
   123  	}
   124  	backupSecret.Data = data
   125  
   126  	err = UpdateBackupSecret(client, scheme, instance, backupSecret)
   127  	if err != nil {
   128  		return errors.Wrap(err, "failed to update backup secret")
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  func CreateBackupSecretData(crypto *Crypto) (map[string][]byte, error) {
   135  	data := map[string][]byte{}
   136  
   137  	if crypto.TLS != nil {
   138  		tlsBackup := &Backup{
   139  			List:      []*current.MSP{crypto.TLS},
   140  			Timestamp: time.Now().String(),
   141  		}
   142  		tlsBytes, err := json.Marshal(tlsBackup)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		data["tls-backup.json"] = tlsBytes
   147  	}
   148  
   149  	if crypto.Ecert != nil {
   150  		ecertBackup := &Backup{
   151  			List:      []*current.MSP{crypto.Ecert},
   152  			Timestamp: time.Now().String(),
   153  		}
   154  		ecertBytes, err := json.Marshal(ecertBackup)
   155  		if err != nil {
   156  			return nil, err
   157  		}
   158  		data["ecert-backup.json"] = ecertBytes
   159  	}
   160  
   161  	if crypto.Operations != nil {
   162  		opBackup := &Backup{
   163  			List:      []*current.MSP{crypto.Operations},
   164  			Timestamp: time.Now().String(),
   165  		}
   166  		opBytes, err := json.Marshal(opBackup)
   167  		if err != nil {
   168  			return nil, err
   169  		}
   170  		data["operations-backup.json"] = opBytes
   171  	}
   172  
   173  	if crypto.CA != nil {
   174  		caBackup := &Backup{
   175  			List:      []*current.MSP{crypto.CA},
   176  			Timestamp: time.Now().String(),
   177  		}
   178  		caBytes, err := json.Marshal(caBackup)
   179  		if err != nil {
   180  			return nil, err
   181  		}
   182  		data["ca-backup.json"] = caBytes
   183  	}
   184  
   185  	return data, nil
   186  }
   187  
   188  func UpdateBackupSecretData(data map[string][]byte, crypto *Crypto) (map[string][]byte, error) {
   189  	if crypto.TLS != nil {
   190  		tlsBackup, err := getUpdatedBackup(data["tls-backup.json"], crypto.TLS)
   191  		if err != nil {
   192  			return nil, err
   193  		}
   194  		tlsBytes, err := json.Marshal(tlsBackup)
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  		data["tls-backup.json"] = tlsBytes
   199  	}
   200  
   201  	if crypto.Ecert != nil {
   202  		ecertBackup, err := getUpdatedBackup(data["ecert-backup.json"], crypto.Ecert)
   203  		if err != nil {
   204  			return nil, err
   205  		}
   206  		ecertBytes, err := json.Marshal(ecertBackup)
   207  		if err != nil {
   208  			return nil, err
   209  		}
   210  		data["ecert-backup.json"] = ecertBytes
   211  	}
   212  
   213  	if crypto.Operations != nil {
   214  		opBackup, err := getUpdatedBackup(data["operations-backup.json"], crypto.Operations)
   215  		if err != nil {
   216  			return nil, err
   217  		}
   218  		opBytes, err := json.Marshal(opBackup)
   219  		if err != nil {
   220  			return nil, err
   221  		}
   222  		data["operations-backup.json"] = opBytes
   223  	}
   224  
   225  	if crypto.CA != nil {
   226  		caBackup, err := getUpdatedBackup(data["ca-backup.json"], crypto.CA)
   227  		if err != nil {
   228  			return nil, err
   229  		}
   230  		caBytes, err := json.Marshal(caBackup)
   231  		if err != nil {
   232  			return nil, err
   233  		}
   234  		data["ca-backup.json"] = caBytes
   235  	}
   236  
   237  	return data, nil
   238  }
   239  
   240  func getUpdatedBackup(data []byte, crypto *current.MSP) (*Backup, error) {
   241  	backup := &Backup{}
   242  	if data != nil {
   243  		err := json.Unmarshal(data, backup)
   244  		if err != nil {
   245  			return nil, err
   246  		}
   247  
   248  		if len(backup.List) < ITERATIONS {
   249  			// Insert to back of queue
   250  			backup.List = append(backup.List, crypto)
   251  		} else {
   252  			// Remove oldest backup and insert new crypto
   253  			backup.List = append(backup.List[1:], crypto)
   254  		}
   255  	} else {
   256  		// Create backup
   257  		backup.List = []*current.MSP{crypto}
   258  	}
   259  
   260  	backup.Timestamp = time.Now().String()
   261  
   262  	return backup, nil
   263  }
   264  
   265  func GetCrypto(prefix common.SecretType, client k8sclient.Client, instance v1.Object) *current.MSP {
   266  	var cryptoExists bool
   267  
   268  	// Doesn't return error if can't get secret/secret not found
   269  	signcert, err := getSignCertEncoded(prefix, client, instance)
   270  	if err == nil && signcert != "" {
   271  		cryptoExists = true
   272  	}
   273  
   274  	keystore, err := getKeystoreEncoded(prefix, client, instance)
   275  	if err == nil && keystore != "" {
   276  		cryptoExists = true
   277  	}
   278  
   279  	cacerts, err := getCACertEncoded(prefix, client, instance)
   280  	if err == nil && cacerts != nil {
   281  		cryptoExists = true
   282  	}
   283  
   284  	admincerts, err := getAdmincertEncoded(prefix, client, instance)
   285  	if err == nil && admincerts != nil {
   286  		cryptoExists = true
   287  	}
   288  
   289  	intercerts, err := getIntermediateCertEncoded(prefix, client, instance)
   290  	if err == nil && intercerts != nil {
   291  		cryptoExists = true
   292  	}
   293  
   294  	if cryptoExists {
   295  		return &current.MSP{
   296  			SignCerts:         signcert,
   297  			KeyStore:          keystore,
   298  			CACerts:           cacerts,
   299  			AdminCerts:        admincerts,
   300  			IntermediateCerts: intercerts,
   301  		}
   302  	}
   303  
   304  	return nil
   305  }
   306  
   307  func GetCACrypto(client k8sclient.Client, instance v1.Object) (*current.MSP, *current.MSP, *current.MSP) {
   308  	encoded, err := GetCACryptoEncoded(client, instance)
   309  	if err != nil || encoded == nil {
   310  		return nil, nil, nil
   311  	}
   312  
   313  	caMSP := &current.MSP{
   314  		SignCerts: encoded.Cert,
   315  		KeyStore:  encoded.Key,
   316  	}
   317  
   318  	operationsMSP := &current.MSP{
   319  		SignCerts: encoded.OperationsCert,
   320  		KeyStore:  encoded.OperationsKey,
   321  	}
   322  
   323  	tlsMSP := &current.MSP{
   324  		SignCerts: encoded.TLSCert,
   325  		KeyStore:  encoded.TLSKey,
   326  	}
   327  
   328  	return caMSP, operationsMSP, tlsMSP
   329  }
   330  
   331  func GetBackupSecret(client k8sclient.Client, instance v1.Object) (*corev1.Secret, error) {
   332  	secretName := fmt.Sprintf("%s-crypto-backup", instance.GetName())
   333  	namespacedName := types.NamespacedName{
   334  		Name:      secretName,
   335  		Namespace: instance.GetNamespace(),
   336  	}
   337  
   338  	secret := &corev1.Secret{}
   339  	err := client.Get(context.TODO(), namespacedName, secret)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  
   344  	return secret, nil
   345  }
   346  
   347  func CreateBackupSecret(client k8sclient.Client, scheme *runtime.Scheme, instance v1.Object, secret *corev1.Secret) error {
   348  	err := client.Create(context.TODO(), secret, k8sclient.CreateOption{
   349  		Owner:  instance,
   350  		Scheme: scheme,
   351  	})
   352  	if err != nil {
   353  		return err
   354  	}
   355  	return nil
   356  }
   357  
   358  func UpdateBackupSecret(client k8sclient.Client, scheme *runtime.Scheme, instance v1.Object, secret *corev1.Secret) error {
   359  	err := client.Update(context.TODO(), secret, k8sclient.UpdateOption{
   360  		Owner:  instance,
   361  		Scheme: scheme,
   362  	})
   363  	if err != nil {
   364  		return err
   365  	}
   366  	return nil
   367  }