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 ¤t.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 := ¤t.MSP{ 314 SignCerts: encoded.Cert, 315 KeyStore: encoded.Key, 316 } 317 318 operationsMSP := ¤t.MSP{ 319 SignCerts: encoded.OperationsCert, 320 KeyStore: encoded.OperationsKey, 321 } 322 323 tlsMSP := ¤t.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 }