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 }