diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..31c4215 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +root = true + +[*] +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +max_line_length = 120 +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[*.{yaml,yml}] +indent_size = 2 + +[*.html] +indent_size = 2 + +[*.md] +indent_size = 2 + +[*.{mj,cj,j,t}s] +quote_type = single diff --git a/.github/workflows/golang.yml b/.github/workflows/golang.yml index dac9dad..6343a0e 100644 --- a/.github/workflows/golang.yml +++ b/.github/workflows/golang.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: "1.21" - name: Generate test mocks run: make generate - name: Unit test @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: stable - name: Generate test mocks run: make generate - name: Staticcheck @@ -38,29 +38,29 @@ jobs: fail-fast: false matrix: include: - - FABRIC_VERSION: '2.5.3' - CREATE_CHANNEL: 'create_channel' + - FABRIC_VERSION: "2.5.3" + CREATE_CHANNEL: "create_channel" CONSENSUS: RAFT - - FABRIC_VERSION: '2.5.3' - CREATE_CHANNEL: 'existing_channel' + - FABRIC_VERSION: "2.5.3" + CREATE_CHANNEL: "existing_channel" CONSENSUS: RAFT - - FABRIC_VERSION: '3.0.0-preview' - CREATE_CHANNEL: 'create_channel' + - FABRIC_VERSION: "3.0.0-preview" + CREATE_CHANNEL: "create_channel" CONSENSUS: RAFT - - FABRIC_VERSION: '3.0.0-preview' - CREATE_CHANNEL: 'existing_channel' + - FABRIC_VERSION: "3.0.0-preview" + CREATE_CHANNEL: "existing_channel" CONSENSUS: RAFT - - FABRIC_VERSION: '3.0.0-preview' - CREATE_CHANNEL: 'create_channel' + - FABRIC_VERSION: "3.0.0-preview" + CREATE_CHANNEL: "create_channel" CONSENSUS: BFT - - FABRIC_VERSION: '3.0.0-preview' - CREATE_CHANNEL: 'existing_channel' + - FABRIC_VERSION: "3.0.0-preview" + CREATE_CHANNEL: "existing_channel" CONSENSUS: BFT steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: "1.21" - name: run end to end test env: FABRIC_VERSION: ${{matrix.FABRIC_VERSION}} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 51f85b5..941bd2c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -12,7 +12,7 @@ concurrency: jobs: go: uses: ./.github/workflows/golang.yml - + node: uses: ./.github/workflows/node.yml @@ -23,4 +23,4 @@ jobs: name: Pull request success runs-on: ubuntu-latest steps: - - run: true + - run: "true" diff --git a/.github/workflows/schedule.yml b/.github/workflows/schedule.yml index 68ad3a8..4fd2e1b 100644 --- a/.github/workflows/schedule.yml +++ b/.github/workflows/schedule.yml @@ -2,11 +2,12 @@ name: Scheduled build on: schedule: - - cron: "10 01 * * *" + - cron: "10 01 * * 0" + workflow_dispatch: jobs: go: uses: ./.github/workflows/golang.yml - + node: uses: ./.github/workflows/node.yml diff --git a/.github/workflows/vulnerability-scan.yml b/.github/workflows/vulnerability-scan.yml index f20ce31..bff7381 100644 --- a/.github/workflows/vulnerability-scan.yml +++ b/.github/workflows/vulnerability-scan.yml @@ -2,7 +2,7 @@ name: Security vulnerability scan on: schedule: - - cron: '20 02 * * *' + - cron: "20 02 * * 0" workflow_dispatch: permissions: diff --git a/.golangci.yml b/.golangci.yml index e093878..5feab0f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,9 +4,23 @@ run: timeout: 5m linters: + disable-all: true enable: + - errcheck + - gocognit + - gocyclo - gofmt - goimports - gosec - disable: - - staticcheck + - gosimple + - govet + - ineffassign + - misspell + - typecheck + - unused + +linters-settings: + gocognit: + min-complexity: 15 + gocyclo: + min-complexity: 10 diff --git a/internal/configtxgen/encoder/encoder.go b/internal/configtxgen/encoder/encoder.go index 773a7fe..ec94087 100644 --- a/internal/configtxgen/encoder/encoder.go +++ b/internal/configtxgen/encoder/encoder.go @@ -133,6 +133,8 @@ func NewConfigGroup() *cb.ConfigGroup { // NewOrdererGroup, NewConsortiumsGroup, and NewApplicationGroup depending on whether these sub-elements are set in the // configuration. All mod_policy values are set to "Admins" for this group, with the exception of the OrdererAddresses // value which is set to "/Channel/Orderer/Admins". +// +//nolint:gocyclo func NewChannelGroup(conf *genesisconfig.Profile) (*cb.ConfigGroup, error) { channelGroup := NewConfigGroup() if err := AddPolicies(channelGroup, conf.Policies, icc.AdminsPolicyKey); err != nil { @@ -182,6 +184,8 @@ func NewChannelGroup(conf *genesisconfig.Profile) (*cb.ConfigGroup, error) { // NewOrdererGroup returns the orderer component of the channel configuration. It defines parameters of the ordering service // about how large blocks should be, how frequently they should be emitted, etc. as well as the organizations of the ordering network. // It sets the mod_policy of all elements to "Admins". This group is always present in any channel configuration. +// +//nolint:gocyclo func NewOrdererGroup(conf *genesisconfig.Orderer) (*cb.ConfigGroup, error) { ordererGroup := NewConfigGroup() if err := AddOrdererPolicies(ordererGroup, conf.Policies, icc.AdminsPolicyKey); err != nil { @@ -341,7 +345,7 @@ func NewApplicationOrgGroup(conf *genesisconfig.Organization) (*cb.ConfigGroup, for _, anchorPeer := range conf.AnchorPeers { anchorProtos = append(anchorProtos, &pb.AnchorPeer{ Host: anchorPeer.Host, - Port: int32(anchorPeer.Port), + Port: anchorPeer.Port, }) } @@ -555,6 +559,7 @@ func (bs *Bootstrapper) GenesisBlockForChannel(channelID string) *cb.Block { return genesis.NewFactoryImpl(bs.channelGroup).Block(channelID) } +//nolint:gocognit func consenterProtosFromConfig(consenterMapping []*genesisconfig.Consenter) ([]*cb.Consenter, error) { var consenterProtos []*cb.Consenter for _, consenter := range consenterMapping { diff --git a/internal/configtxgen/genesisconfig/config.go b/internal/configtxgen/genesisconfig/config.go index e784d9a..66421e1 100644 --- a/internal/configtxgen/genesisconfig/config.go +++ b/internal/configtxgen/genesisconfig/config.go @@ -146,7 +146,7 @@ type Organization struct { // AnchorPeer encodes the necessary fields to identify an anchor peer. type AnchorPeer struct { Host string `yaml:"Host"` - Port int `yaml:"Port"` + Port int32 `yaml:"Port"` } // Orderer contains configuration associated to a channel. @@ -260,7 +260,7 @@ func Load(profile string, configPaths ...string) (*Profile, error) { result, ok := uconf.Profiles[profile] if !ok { - panic(fmt.Errorf("Could not find profile: " + profile)) + panic(fmt.Errorf("could not find profile: %s", profile)) } result.completeInitialization(filepath.Dir(config.ConfigFileUsed())) @@ -316,6 +316,7 @@ func (org *Organization) completeInitialization(configDir string) { translatePaths(configDir, org) } +//nolint:gocognit,gocyclo func (ord *Orderer) completeInitialization(configDir string) { loop: for { diff --git a/internal/configtxgen/viperutil/config_util.go b/internal/configtxgen/viperutil/config_util.go index 1d64398..a780207 100644 --- a/internal/configtxgen/viperutil/config_util.go +++ b/internal/configtxgen/viperutil/config_util.go @@ -146,6 +146,7 @@ func (c *ConfigParser) getFromEnv(key string) string { // Prototype declaration for getFromEnv function. type envGetter func(key string) string +//nolint:gocognit,gocyclo func getKeysRecursively(base string, getenv envGetter, nodeKeys map[string]interface{}, oType reflect.Type) map[string]interface{} { subTypes := map[string]reflect.Type{} @@ -296,6 +297,7 @@ func stringFromFileDecodeHook(f reflect.Kind, t reflect.Kind, data interface{}) return data, nil } +//nolint:gocognit,gocyclo func pemBlocksFromFileDecodeHook(f reflect.Kind, t reflect.Kind, data interface{}) (interface{}, error) { // "to" type should be string if t != reflect.Slice { diff --git a/internal/configtxlator/update/update.go b/internal/configtxlator/update/update.go index f81e271..57dd08d 100644 --- a/internal/configtxlator/update/update.go +++ b/internal/configtxlator/update/update.go @@ -163,12 +163,7 @@ func computeGroupUpdate(original, updated *common.ConfigGroup) (readSet, writeSe if !(policiesMembersUpdated || valuesMembersUpdated || groupsMembersUpdated || original.ModPolicy != updated.ModPolicy) { // If there were no modified entries in any of the policies/values/groups maps - if len(readSetPolicies) == 0 && - len(writeSetPolicies) == 0 && - len(readSetValues) == 0 && - len(writeSetValues) == 0 && - len(readSetGroups) == 0 && - len(writeSetGroups) == 0 { + if len(readSetPolicies)+len(writeSetPolicies)+len(readSetValues)+len(writeSetValues)+len(readSetGroups)+len(writeSetGroups) == 0 { return &common.ConfigGroup{ Version: original.Version, }, &common.ConfigGroup{ @@ -189,20 +184,9 @@ func computeGroupUpdate(original, updated *common.ConfigGroup) (readSet, writeSe }, true } - for k, samePolicy := range sameSetPolicies { - readSetPolicies[k] = samePolicy - writeSetPolicies[k] = samePolicy - } - - for k, sameValue := range sameSetValues { - readSetValues[k] = sameValue - writeSetValues[k] = sameValue - } - - for k, sameGroup := range sameSetGroups { - readSetGroups[k] = sameGroup - writeSetGroups[k] = sameGroup - } + copyMap(sameSetPolicies, readSetPolicies, writeSetPolicies) + copyMap(sameSetValues, readSetValues, writeSetValues) + copyMap(sameSetGroups, readSetGroups, writeSetGroups) return &common.ConfigGroup{ Version: original.Version, @@ -218,6 +202,14 @@ func computeGroupUpdate(original, updated *common.ConfigGroup) (readSet, writeSe }, true } +func copyMap[K comparable, V any](source map[K]V, targets ...map[K]V) { + for key, value := range source { + for _, target := range targets { + target[key] = value + } + } +} + func Compute(original, updated *common.Config) (*common.ConfigUpdate, error) { if original.ChannelGroup == nil { return nil, fmt.Errorf("no channel group included for original config") diff --git a/internal/msp/msp.go b/internal/msp/msp.go index ea3d59b..e4719fb 100644 --- a/internal/msp/msp.go +++ b/internal/msp/msp.go @@ -60,6 +60,8 @@ func ProviderTypeToString(id ProviderType) string { return "" } + +//nolint:gocognit,gocyclo func getMspConfig(dir string, ID string, sigid *msp.SigningIdentityInfo) (*msp.MSPConfig, error) { cacertDir := filepath.Join(dir, cacerts) admincertDir := filepath.Join(dir, admincerts) diff --git a/internal/policies/policies.go b/internal/policies/policies.go index cd6ab83..23ac0bf 100644 --- a/internal/policies/policies.go +++ b/internal/policies/policies.go @@ -119,7 +119,7 @@ func EncodeBFTBlockVerificationPolicy(consenterProtos []*cb.Consenter, ordererGr for i, consenter := range consenterProtos { pols = append(pols, &cb.SignaturePolicy{ Type: &cb.SignaturePolicy_SignedBy{ - SignedBy: int32(i), + SignedBy: int32(i), //#nosec:G115 }, }) identities = append(identities, &msp.MSPPrincipal{ @@ -143,6 +143,6 @@ func EncodeBFTBlockVerificationPolicy(consenterProtos []*cb.Consenter, ordererGr } } -func ComputeBFTQuorum(totalNodes, faultyNodes int) int { - return int(math.Ceil(float64(totalNodes+faultyNodes+1) / 2)) +func ComputeBFTQuorum(totalNodes, faultyNodes int) int32 { + return int32(math.Ceil(float64(totalNodes+faultyNodes+1) / 2)) } diff --git a/internal/policydsl/policydsl.go b/internal/policydsl/policydsl.go index d7a3d7e..39ef3e5 100644 --- a/internal/policydsl/policydsl.go +++ b/internal/policydsl/policydsl.go @@ -85,6 +85,7 @@ func or(args ...interface{}) (interface{}, error) { return outof(args...) } +//nolint:gocognit,gocyclo func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) { // first we translate the and/or business into outof gates intermediate, err := govaluate.NewEvaluableExpressionWithFunctions( @@ -217,7 +218,7 @@ func firstPass(args ...interface{}) (interface{}, error) { } type context struct { - IDNum int + IDNum int32 principals []*mb.MSPPrincipal } @@ -225,6 +226,7 @@ func newContext() *context { return &context{IDNum: 0, principals: make([]*mb.MSPPrincipal, 0)} } +//nolint:gocognit,gocyclo func secondPass(args ...interface{}) (interface{}, error) { /* general sanity check, we expect at least 3 args */ if len(args) < 3 { @@ -242,10 +244,10 @@ func secondPass(args ...interface{}) (interface{}, error) { /* get the second argument, we expect an integer telling us how many of the remaining we expect to have*/ - var t int + var t int32 switch arg := args[1].(type) { case float64: - t = int(arg) + t = int32(arg) default: return nil, fmt.Errorf("unrecognized type, expected a number, got %s", reflect.TypeOf(args[1])) } @@ -254,7 +256,7 @@ func secondPass(args ...interface{}) (interface{}, error) { n := len(args) - 2 /* sanity check - t should be positive, permit equal to n+1, but disallow over n+1 */ - if t < 0 || t > n+1 { + if t < 0 || int(t) > n+1 { return nil, fmt.Errorf("invalid t-out-of-n predicate, t %d, n %d", t, n) } @@ -305,7 +307,7 @@ func secondPass(args ...interface{}) (interface{}, error) { /* create a SignaturePolicy that requires a signature from the principal we've just built*/ - dapolicy := SignedBy(int32(ctx.IDNum)) + dapolicy := SignedBy(ctx.IDNum) policies = append(policies, dapolicy) /* increment the identity counter. Note that this is @@ -324,7 +326,7 @@ func secondPass(args ...interface{}) (interface{}, error) { } } - return NOutOf(int32(t), policies), nil + return NOutOf(t, policies), nil } // SignedBy creates a SignaturePolicy requiring a given signer's signature diff --git a/internal/protoutil/protoutil.go b/internal/protoutil/protoutil.go index cae3e72..734d230 100644 --- a/internal/protoutil/protoutil.go +++ b/internal/protoutil/protoutil.go @@ -20,8 +20,8 @@ func CreateSignedTx( signer identity.Signer, resps ...*peer.ProposalResponse, ) (*common.Envelope, error) { - if len(resps) == 0 { - return nil, errors.New("at least one proposal response is required") + if err := ensureValidResponses(resps); err != nil { + return nil, err } // the original header @@ -36,28 +36,7 @@ func CreateSignedTx( return nil, err } - // ensure that all actions are bitwise equal and that they are successful - var a1 []byte - for n, r := range resps { - if r.Response.Status < 200 || r.Response.Status >= 400 { - return nil, fmt.Errorf("proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message) - } - - if n == 0 { - a1 = r.Payload - continue - } - - if !bytes.Equal(a1, r.Payload) { - return nil, errors.New("ProposalResponsePayloads do not match") - } - } - - // fill endorsements - endorsements := make([]*peer.Endorsement, len(resps)) - for n, r := range resps { - endorsements[n] = r.Endorsement - } + endorsements := fillEndorsements(resps) // create ChaincodeEndorsedAction cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements} @@ -103,3 +82,33 @@ func CreateSignedTx( // here's the envelope return &common.Envelope{Payload: paylBytes, Signature: sig}, nil } + +// ensureValidResponses checks that all actions are bitwise equal and that they are successful. +func ensureValidResponses(responses []*peer.ProposalResponse) error { + if len(responses) == 0 { + return errors.New("at least one proposal response is required") + } + + var firstResponse []byte + for n, r := range responses { + if r.Response.Status < 200 || r.Response.Status >= 400 { + return fmt.Errorf("proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message) + } + + if n == 0 { + firstResponse = r.Payload + } else if !bytes.Equal(firstResponse, r.Payload) { + return errors.New("ProposalResponsePayloads do not match") + } + } + + return nil +} + +func fillEndorsements(responses []*peer.ProposalResponse) []*peer.Endorsement { + endorsements := make([]*peer.Endorsement, len(responses)) + for n, r := range responses { + endorsements[n] = r.Endorsement + } + return endorsements +} diff --git a/pkg/chaincode/chaincodeCCAAS.go b/pkg/chaincode/chaincodeCCAAS.go index ff950c7..d3eb83e 100644 --- a/pkg/chaincode/chaincodeCCAAS.go +++ b/pkg/chaincode/chaincodeCCAAS.go @@ -31,54 +31,53 @@ ${CONTAINER_CLI} run --rm -d --name peer0org1_${CC_NAME}_ccaas \ ${CC_NAME}_ccaas_image:latest */ func PackageCCAAS(connection Connection, metadata Metadata, tmpPath, filename string) error { - err := os.MkdirAll(tmpPath+"/src/", 0766) - if err != nil { - return err - } - err = os.MkdirAll(tmpPath+"/pkg/", 0766) - if err != nil { - return err - } - // connection.json - connjson, err := json.Marshal(connection) - if err != nil { + if err := os.MkdirAll(tmpPath+"/src/", 0766); err != nil { return err } - connfile, err := os.Create(tmpPath + "/src/connection.json") - if err != nil { + + if err := os.MkdirAll(tmpPath+"/pkg/", 0766); err != nil { return err } - defer connfile.Close() - _, err = connfile.Write(connjson) - if err != nil { + if err := writeJSONFile(connection, tmpPath+"/src/connection.json"); err != nil { return err } + // code.tar.gz - err = creategzTar(tmpPath+"/pkg/"+"code.tar.gz", []string{tmpPath + "/src/connection.json"}) - if err != nil { + if err := creategzTar(tmpPath+"/pkg/"+"code.tar.gz", []string{tmpPath + "/src/connection.json"}); err != nil { return err } + // metadata.json - metajsonStr, err := json.Marshal(metadata) - if err != nil { + if err := writeJSONFile(metadata, tmpPath+"/pkg/metadata.json"); err != nil { + return err + } + + //filename + if err := creategzTar(tmpPath+"/"+filename, []string{tmpPath + "/pkg/metadata.json", tmpPath + "/pkg/code.tar.gz"}); err != nil { return err } - metadatafile, err := os.Create(tmpPath + "/pkg/metadata.json") + + return nil +} + +func writeJSONFile(jsonObject any, filePath string) error { + json, err := json.Marshal(jsonObject) if err != nil { return err } - defer metadatafile.Close() - _, err = metadatafile.Write(metajsonStr) + file, err := os.Create(filePath) if err != nil { return err } - //filename - err = creategzTar(tmpPath+"/"+filename, []string{tmpPath + "/pkg/metadata.json", tmpPath + "/pkg/code.tar.gz"}) + defer file.Close() + + _, err = file.Write(json) if err != nil { return err } + return nil } diff --git a/pkg/chaincode/chaincodeCCAAS_test.go b/pkg/chaincode/chaincodeCCAAS_test.go index d0eb02e..f810b68 100644 --- a/pkg/chaincode/chaincodeCCAAS_test.go +++ b/pkg/chaincode/chaincodeCCAAS_test.go @@ -39,6 +39,8 @@ var _ = Describe("Package", func() { // It returns an error if the tar contains any files which would escape to a // parent of dst, or if the archive contains any files whose type is not // a regular file or directory. +// +//nolint:gocognit,gocyclo func Untar(buffer io.Reader, dst string) error { gzr, err := gzip.NewReader(buffer) if err != nil { @@ -74,7 +76,7 @@ func Untar(buffer io.Reader, dst string) error { return fmt.Errorf("could not create directory '%s' %w", filepath.Dir(header.Name), err) } - f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) + f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) //#nosec:G115 if err != nil { return fmt.Errorf("could not create file '%s' %w", header.Name, err) } diff --git a/pkg/chaincode/packageID.go b/pkg/chaincode/packageID.go index af36f0b..c242385 100644 --- a/pkg/chaincode/packageID.go +++ b/pkg/chaincode/packageID.go @@ -42,6 +42,8 @@ type ChaincodePackageMetadata struct { // ParseChaincodePackage parses a set of bytes as a chaincode package // and returns the parsed package as a metadata struct and a code package +// +//nolint:gocognit,gocyclo func ParseChaincodePackage(source []byte) (*ChaincodePackageMetadata, []byte, error) { gzReader, err := gzip.NewReader(bytes.NewBuffer(source)) if err != nil { diff --git a/pkg/chaincode/signaturepolicy.go b/pkg/chaincode/signaturepolicy.go index e4a905b..0c2bbdb 100644 --- a/pkg/chaincode/signaturepolicy.go +++ b/pkg/chaincode/signaturepolicy.go @@ -136,6 +136,7 @@ func nOutOf(n int32, policies []*cb.SignaturePolicy) *cb.SignaturePolicy { } } +//nolint:gocognit,gocyclo func secondPass(args ...interface{}) (interface{}, error) { /* general sanity check, we expect at least 3 args */ if len(args) < 3 { @@ -153,10 +154,10 @@ func secondPass(args ...interface{}) (interface{}, error) { /* get the second argument, we expect an integer telling us how many of the remaining we expect to have*/ - var t int + var t int32 switch arg := args[1].(type) { case float64: - t = int(arg) + t = int32(arg) default: return nil, fmt.Errorf("unrecognized type, expected a number, got %s", reflect.TypeOf(args[1])) } @@ -165,7 +166,7 @@ func secondPass(args ...interface{}) (interface{}, error) { n := len(args) - 2 /* sanity check - t should be positive, permit equal to n+1, but disallow over n+1 */ - if t < 0 || t > n+1 { + if t < 0 || int(t) > n+1 { return nil, fmt.Errorf("invalid t-out-of-n predicate, t %d, n %d", t, n) } @@ -239,7 +240,7 @@ func secondPass(args ...interface{}) (interface{}, error) { } type policyContext struct { - IDNum int + IDNum int32 principals []*mb.MSPPrincipal } @@ -285,6 +286,8 @@ func NewApplicationPolicy(signaturePolicy, channelConfigPolicy string) (*peer.Ap // - ORG is a string (representing the MSP identifier) // - ROLE takes the value of any of the RoleXXX constants representing // the required role +// +//nolint:gocognit,gocyclo func signaturePolicyEnvelopeFromString(policy string) (*cb.SignaturePolicyEnvelope, error) { // first we translate the and/or business into outof gates intermediate, err := govaluate.NewEvaluableExpressionWithFunctions( @@ -438,7 +441,7 @@ func policyParse(rule *cb.SignaturePolicy, ids []string, buf *bytes.Buffer) { rules := p.NOutOf.GetRules() switch n { - case int32(len(rules)): + case int32(len(rules)): //#nosec:G115 buf.WriteString("AND(") case 1: buf.WriteString("OR(") diff --git a/pkg/network/network.go b/pkg/network/network.go index fbda6d8..2d8f3f6 100644 --- a/pkg/network/network.go +++ b/pkg/network/network.go @@ -217,16 +217,13 @@ func (client *GRPCClient) parseSecureOptions(opts SecureOptions) error { client.tlsConfig = &tls.Config{ VerifyPeerCertificate: opts.VerifyCertificate, MinVersion: tls.VersionTLS12} // TLS 1.2 only - if len(opts.ServerRootCAs) > 0 { - client.tlsConfig.RootCAs = x509.NewCertPool() - for _, certBytes := range opts.ServerRootCAs { - err := AddPemToCertPool(certBytes, client.tlsConfig.RootCAs) - if err != nil { - //commLogger.Debugf("error adding root certificate: %v", err) - return fmt.Errorf("error adding root certificate: %w", err) - } - } + + if serverRootCAs, err := newRootCACertPool(opts); err == nil { + client.tlsConfig.RootCAs = serverRootCAs + } else { + return err } + if opts.RequireClientCert { // make sure we have both Key and Certificate if opts.Key != nil && @@ -252,6 +249,23 @@ func (client *GRPCClient) parseSecureOptions(opts SecureOptions) error { return nil } +func newRootCACertPool(opts SecureOptions) (*x509.CertPool, error) { + if len(opts.ServerRootCAs) == 0 { + return nil, nil // Use the host's root CA set + } + + result := x509.NewCertPool() + + for _, certBytes := range opts.ServerRootCAs { + err := AddPemToCertPool(certBytes, result) + if err != nil { + return nil, fmt.Errorf("error adding root certificate: %w", err) + } + } + + return result, nil +} + // AddPemToCertPool adds PEM-encoded certs to a cert pool func AddPemToCertPool(pemCerts []byte, pool *x509.CertPool) error { certs, _, err := pemToX509Certs(pemCerts)