github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/initializer/common/enroller/enroller.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 enroller
    20  
    21  import (
    22  	"bytes"
    23  	"crypto/x509"
    24  	"encoding/pem"
    25  	"time"
    26  
    27  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    28  	"github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config"
    29  	"github.com/IBM-Blockchain/fabric-operator/pkg/util"
    30  	"github.com/hyperledger/fabric-ca/lib"
    31  	"github.com/pkg/errors"
    32  
    33  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    34  )
    35  
    36  var log = logf.Log.WithName("init_enroller")
    37  
    38  //go:generate counterfeiter -o mocks/cryptoenroller.go -fake-name CryptoEnroller . CryptoEnroller
    39  
    40  type CryptoEnroller interface {
    41  	GetEnrollmentRequest() *current.Enrollment
    42  	Enroll() (*config.Response, error)
    43  	PingCA(time.Duration) error
    44  }
    45  
    46  type Enroller struct {
    47  	Enroller CryptoEnroller
    48  	Timeout  time.Duration
    49  }
    50  
    51  func New(enroller CryptoEnroller) *Enroller {
    52  	return &Enroller{
    53  		Enroller: enroller,
    54  		Timeout:  30 * time.Second,
    55  	}
    56  }
    57  
    58  func (e *Enroller) GetCrypto() (*config.Response, error) {
    59  	log.Info("Getting crypto...")
    60  	resp, err := e.Enroller.Enroll()
    61  	if err != nil {
    62  		return nil, errors.Wrap(err, "failed to enroll with CA")
    63  	}
    64  
    65  	// Store crypto
    66  	for _, adminCert := range e.Enroller.GetEnrollmentRequest().AdminCerts {
    67  		bytes, err := util.Base64ToBytes(adminCert)
    68  		if err != nil {
    69  			return nil, errors.Wrap(err, "failed to parse admin cert")
    70  		}
    71  		resp.AdminCerts = append(resp.AdminCerts, bytes)
    72  	}
    73  
    74  	return resp, nil
    75  }
    76  
    77  func (e *Enroller) PingCA() error {
    78  	log.Info("Check if CA is reachable before triggering enroll job")
    79  	return e.Enroller.PingCA(e.Timeout)
    80  }
    81  
    82  func (e *Enroller) Validate() error {
    83  	req := e.Enroller.GetEnrollmentRequest()
    84  
    85  	if req.CAHost == "" {
    86  		return errors.New("unable to enroll, CA host not specified")
    87  	}
    88  
    89  	if req.CAPort == "" {
    90  		return errors.New("unable to enroll, CA port not specified")
    91  	}
    92  
    93  	if req.EnrollID == "" {
    94  		return errors.New("unable to enroll, enrollment ID not specified")
    95  	}
    96  
    97  	if req.EnrollSecret == "" {
    98  		return errors.New("unable to enroll, enrollment secret not specified")
    99  	}
   100  
   101  	if req.CATLS.CACert == "" {
   102  		return errors.New("unable to enroll, CA TLS certificate not specified")
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  func ParseEnrollmentResponse(resp *config.Response, si *lib.GetCAInfoResponse) (*config.Response, error) {
   109  	chain := si.CAChain
   110  	for len(chain) > 0 {
   111  		var block *pem.Block
   112  		block, chain = pem.Decode(chain)
   113  		if block == nil {
   114  			break
   115  		}
   116  
   117  		cert, err := x509.ParseCertificate(block.Bytes)
   118  		if err != nil {
   119  			return nil, errors.Wrap(err, "Failed to parse certificate in the CA chain")
   120  		}
   121  
   122  		if !cert.IsCA {
   123  			return nil, errors.New("A certificate in the CA chain is not a CA certificate")
   124  		}
   125  
   126  		// If authority key id is not present or if it is present and equal to subject key id,
   127  		// then it is a root certificate
   128  		if len(cert.AuthorityKeyId) == 0 || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) {
   129  			resp.CACerts = append(resp.CACerts, pem.EncodeToMemory(block))
   130  		} else {
   131  			resp.IntermediateCerts = append(resp.IntermediateCerts, pem.EncodeToMemory(block))
   132  		}
   133  	}
   134  
   135  	// for intermediate cert, put the whole chain as is
   136  	if len(resp.IntermediateCerts) > 0 {
   137  		resp.IntermediateCerts = [][]byte{si.CAChain}
   138  	}
   139  
   140  	return resp, nil
   141  }