Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions common/fabhttp/tls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package fabhttp_test

import (
"crypto/tls"
"crypto/x509"
"os"
"path/filepath"

"github.com/hyperledger/fabric/common/fabhttp"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("TLS", func() {
var httpTLS fabhttp.TLS
var tempDir string

BeforeEach(func() {
var err error
tempDir, err = os.MkdirTemp("", "opstls")
Expect(err).NotTo(HaveOccurred())

generateCertificates(tempDir)

httpTLS = fabhttp.TLS{
Enabled: true,
CertFile: filepath.Join(tempDir, "server-cert.pem"),
KeyFile: filepath.Join(tempDir, "server-key.pem"),
ClientCertRequired: true,
ClientCACertFiles: []string{
filepath.Join(tempDir, "client-ca.pem"),
},
}
})

AfterEach(func() {
os.RemoveAll(tempDir)
})

It("creates a valid TLS configuration", func() {
cert, err := tls.LoadX509KeyPair(
filepath.Join(tempDir, "server-cert.pem"),
filepath.Join(tempDir, "server-key.pem"),
)
Expect(err).NotTo(HaveOccurred())

pemBytes, err := os.ReadFile(filepath.Join(tempDir, "client-ca.pem"))
Expect(err).NotTo(HaveOccurred())

clientCAPool := x509.NewCertPool()
clientCAPool.AppendCertsFromPEM(pemBytes)

tlsConfig, err := httpTLS.Config()
Expect(err).NotTo(HaveOccurred())

// https://go-review.googlesource.com/c/go/+/229917
Expect(tlsConfig.ClientCAs.Equal(clientCAPool)).To(BeTrue())
tlsConfig.ClientCAs = nil

Expect(tlsConfig).To(Equal(&tls.Config{
MinVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{cert},
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
},
ClientAuth: tls.RequireAndVerifyClientCert,
}))
})

Context("when TLS is not enabled", func() {
BeforeEach(func() {
httpTLS.Enabled = false
})

It("returns a nil config", func() {
tlsConfig, err := httpTLS.Config()
Expect(err).NotTo(HaveOccurred())
Expect(tlsConfig).To(BeNil())
})
})

Context("when a client certificate is not required", func() {
BeforeEach(func() {
httpTLS.ClientCertRequired = false
})

It("requests a client cert with verification", func() {
tlsConfig, err := httpTLS.Config()
Expect(err).NotTo(HaveOccurred())
Expect(tlsConfig.ClientAuth).To(Equal(tls.VerifyClientCertIfGiven))
})
})

Context("when the server certificate cannot be constructed", func() {
BeforeEach(func() {
httpTLS.CertFile = "non-existent-file"
})

It("returns an error", func() {
_, err := httpTLS.Config()
Expect(err).To(MatchError("open non-existent-file: no such file or directory"))
})
})

Context("the client CA slice is empty", func() {
BeforeEach(func() {
httpTLS.ClientCACertFiles = nil
})

It("builds a TLS configuration without an empty CA pool", func() {
emptyPool := x509.NewCertPool()
tlsConfig, err := httpTLS.Config()
Expect(err).NotTo(HaveOccurred())
Expect(tlsConfig.ClientCAs.Equal(emptyPool)).To(BeTrue())
})
})

Context("when a client CA cert cannot be read", func() {
BeforeEach(func() {
httpTLS.ClientCACertFiles = []string{
"non-existent-file",
}
})

It("returns an error", func() {
_, err := httpTLS.Config()
Expect(err).To(MatchError("open non-existent-file: no such file or directory"))
})
})
})
16 changes: 11 additions & 5 deletions common/operations/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,26 @@ type Options struct {
Version string
}

func operationServiceURL(operationSubPath string, address string, logger *flogging.FabricLogger) string {
uRL, err := url.JoinPath("http://", address, operationSubPath)
func operationServiceURL(scheme string, operationSubPath string, address string, logger *flogging.FabricLogger) string {
uRL, err := url.JoinPath(scheme, address, operationSubPath)
if err != nil {
logger.Panicf("failed to construct metrics URL: %s", err)
}
return uRL
}

func PrometheusMetricsServiceURL(system *System, logger *flogging.FabricLogger) string {
return operationServiceURL("metrics", system.Addr(), logger)
if system.options.TLS.Enabled {
return operationServiceURL("https://", "metrics", system.Addr(), logger)
}
return operationServiceURL("http://", "metrics", system.Addr(), logger)
Comment on lines +83 to +86

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be implement further

}

func HealthCheckServiceURL(system *System, logger *flogging.FabricLogger) string {
return operationServiceURL("healthz", system.Addr(), logger)
if system.options.TLS.Enabled {
return operationServiceURL("https://", "healthz", system.Addr(), logger)
}
return operationServiceURL("http://", "healthz", system.Addr(), logger)
}

// NewOperationsSystem creates a new operations system with the provided configuration.
Expand All @@ -94,7 +100,7 @@ func NewOperationsSystem(ops Operations, metricsConfig Metrics) *System {
Logger: flogging.MustGetLogger("orderer.operations"),
ListenAddress: ops.ListenAddress,
TLS: fabhttp.TLS{
Enabled: false, // TLS is not currently supported for the operations server --- IGNORE ---
Enabled: ops.TLS.Enabled,
CertFile: ops.TLS.Certificate,
KeyFile: ops.TLS.PrivateKey,
ClientCertRequired: ops.TLS.ClientAuthRequired,
Expand Down
24 changes: 12 additions & 12 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ func (config *Configuration) ExtractRouterConfig(configBlock *common.Block) *nod
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey == "" {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.PrivateKey
}
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs == nil {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.RootCAs
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs == nil {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.RootCAs
}
}

Expand Down Expand Up @@ -314,7 +314,7 @@ func (config *Configuration) ExtractRouterConfig(configBlock *common.Block) *nod
Certificate: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.Certificate,
PrivateKey: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey,
ClientAuthRequired: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientAuthRequired,
RootCAs: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs,
ClientRootCAs: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs,
},
},
Metrics: &operations.Metrics{
Expand Down Expand Up @@ -348,8 +348,8 @@ func (config *Configuration) ExtractBatcherConfig(configBlock *common.Block) *no
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey == "" {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.PrivateKey
}
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs == nil {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.RootCAs
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs == nil {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.RootCAs
}
}

Expand Down Expand Up @@ -399,7 +399,7 @@ func (config *Configuration) ExtractBatcherConfig(configBlock *common.Block) *no
Certificate: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.Certificate,
PrivateKey: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey,
ClientAuthRequired: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientAuthRequired,
RootCAs: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs,
ClientRootCAs: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs,
},
},
Metrics: &operations.Metrics{
Expand Down Expand Up @@ -463,8 +463,8 @@ func (config *Configuration) ExtractConsenterConfig(configBlock *common.Block) *
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey == "" {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.PrivateKey
}
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs == nil {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.RootCAs
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs == nil {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.RootCAs
}
}

Expand Down Expand Up @@ -501,7 +501,7 @@ func (config *Configuration) ExtractConsenterConfig(configBlock *common.Block) *
Certificate: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.Certificate,
PrivateKey: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey,
ClientAuthRequired: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientAuthRequired,
RootCAs: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs,
ClientRootCAs: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs,
},
},
Metrics: &operations.Metrics{
Expand Down Expand Up @@ -543,8 +543,8 @@ func (config *Configuration) ExtractAssemblerConfig(configBlock *common.Block) *
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey == "" {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.PrivateKey
}
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs == nil {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.RootCAs
if config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs == nil {
config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs = config.LocalConfig.NodeLocalConfig.GeneralConfig.TLSConfig.RootCAs
}
}

Expand Down Expand Up @@ -583,7 +583,7 @@ func (config *Configuration) ExtractAssemblerConfig(configBlock *common.Block) *
Certificate: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.Certificate,
PrivateKey: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.PrivateKey,
ClientAuthRequired: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientAuthRequired,
RootCAs: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.RootCAs,
ClientRootCAs: config.LocalConfig.NodeLocalConfig.OperationsConfig.TLSConfig.ClientRootCAs,
},
},
Metrics: &operations.Metrics{
Expand Down
21 changes: 19 additions & 2 deletions testutil/network_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package testutil

import (
"context"
"crypto/tls"
"io"
"net"
"net/http"
Expand Down Expand Up @@ -53,8 +54,24 @@ func GetAvailablePort(t testing.TB) (port string, ll net.Listener) {

// FetchPrometheusMetricValue fetches the value of a Prometheus metric from the specified URL using the provided regular expression.
// It returns the metric value as an integer. If the metric is not found or cannot be converted to an integer, it returns -1.
func FetchPrometheusMetricValue(t *testing.T, re *regexp.Regexp, url string) int {
resp, err := http.Get(url)
func FetchPrometheusMetricValue(t *testing.T, re *regexp.Regexp, url string, tlsOpts ...func(config *tls.Config)) int {
require.NotEmpty(t, url, "URL cannot be empty")
require.NotNil(t, re, "Regular expression cannot be nil")

tlsClientConfig := &tls.Config{}

for _, opt := range tlsOpts {
opt(tlsClientConfig)
}

transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = tlsClientConfig

client := &http.Client{
Transport: transport,
Timeout: 10 * time.Second,
}
resp, err := client.Get(url)
require.NoError(t, err)

defer resp.Body.Close()
Expand Down