NOISSUE - Add ability to handle both tls cert file and raw cert data (#2871)

Signed-off-by: Jilks Smith <smithjilks@gmail.com>
This commit is contained in:
Smith Jilks
2025-08-08 16:48:36 +03:00
committed by GitHub
parent a0b3970ad6
commit bc7b988098
6 changed files with 77 additions and 40 deletions
+2 -2
View File
@@ -255,7 +255,7 @@ func proxyHTTP(ctx context.Context, cfg server.Config, logger *slog.Logger, sess
TargetPath: targetHTTPPath,
}
if cfg.CertFile != "" || cfg.KeyFile != "" {
tlsCert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
tlsCert, err := server.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
if err != nil {
return err
}
@@ -275,7 +275,7 @@ func proxyHTTP(ctx context.Context, cfg server.Config, logger *slog.Logger, sess
go func() {
errCh <- mp.Listen(ctx)
}()
logger.Info(fmt.Sprintf("%s service HTTPS server listening at %s:%s with TLS cert %s and key %s", svcName, cfg.Host, cfg.Port, cfg.CertFile, cfg.KeyFile))
logger.Info(fmt.Sprintf("%s service HTTPS server listening at %s:%s with TLS", svcName, cfg.Host, cfg.Port))
default:
go func() {
errCh <- mp.Listen(ctx)
+6 -11
View File
@@ -5,12 +5,11 @@ package grpcclient
import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"time"
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/server"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
@@ -112,22 +111,18 @@ func connect(cfg Config) (*grpc.ClientConn, security, error) {
tlsConfig := &tls.Config{}
// Loading root ca certificates file
rootCA, err := os.ReadFile(cfg.ServerCAFile)
rootCA, err := server.LoadRootCACerts(cfg.ServerCAFile)
if err != nil {
return nil, secure, fmt.Errorf("failed to load root ca file: %w", err)
return nil, secure, fmt.Errorf("failed to load root ca: %w", err)
}
if len(rootCA) > 0 {
capool := x509.NewCertPool()
if !capool.AppendCertsFromPEM(rootCA) {
return nil, secure, fmt.Errorf("failed to append root ca to tls.Config")
}
tlsConfig.RootCAs = capool
if rootCA != nil {
tlsConfig.RootCAs = rootCA
secure = withTLS
}
// Loading mtls certificates file
if cfg.ClientCert != "" || cfg.ClientKey != "" {
certificate, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey)
certificate, err := server.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey)
if err != nil {
return nil, secure, fmt.Errorf("failed to client certificate and key %w", err)
}
+4 -4
View File
@@ -65,7 +65,7 @@ func TestHandler(t *testing.T) {
Timeout: time.Second,
ServerCAFile: "invalid",
},
err: errors.New("failed to load root ca file: open invalid: no such file or directory"),
err: errors.New("failed to load root ca: failed to append root ca to tls.Config"),
},
{
desc: "failed with invalid server CA file as cert key",
@@ -74,7 +74,7 @@ func TestHandler(t *testing.T) {
Timeout: time.Second,
ServerCAFile: "../../docker/ssl/certs/supermq-server.key",
},
err: errors.New("failed to append root ca to tls.Config"),
err: errors.New("failed to load root ca: failed to append root ca to tls.Config"),
},
{
desc: "failed with invalid client cert",
@@ -85,7 +85,7 @@ func TestHandler(t *testing.T) {
ClientKey: "../../docker/ssl/certs/supermq-server.key",
ServerCAFile: "../../docker/ssl/certs/ca.crt",
},
err: errors.New("failed to client certificate and key open invalid: no such file or directory"),
err: errors.New("failed to client certificate and key tls: failed to find any PEM data in certificate input"),
},
{
desc: "failed with invalid client key",
@@ -96,7 +96,7 @@ func TestHandler(t *testing.T) {
ClientKey: "invalid",
ServerCAFile: "../../docker/ssl/certs/ca.crt",
},
err: errors.New("failed to client certificate and key open invalid: no such file or directory"),
err: errors.New("failed to client certificate and key tls: failed to find any PEM data in key input"),
},
}
+9 -21
View File
@@ -10,7 +10,6 @@ import (
"fmt"
"log/slog"
"net"
"os"
"time"
"github.com/absmach/supermq/pkg/server"
@@ -56,7 +55,7 @@ func (s *grpcServer) Start() error {
switch {
case s.Config.CertFile != "" || s.Config.KeyFile != "":
certificate, err := tls.LoadX509KeyPair(s.Config.CertFile, s.Config.KeyFile)
certificate, err := server.LoadX509KeyPair(s.Config.CertFile, s.Config.KeyFile)
if err != nil {
return fmt.Errorf("failed to load auth gRPC client certificates: %w", err)
}
@@ -67,32 +66,28 @@ func (s *grpcServer) Start() error {
var mtlsCA string
// Loading Server CA file
rootCA, err := loadCertFile(s.Config.ServerCAFile)
rootCA, err := server.LoadRootCACerts(s.Config.ServerCAFile)
if err != nil {
return fmt.Errorf("failed to load root ca file: %w", err)
}
if len(rootCA) > 0 {
if rootCA != nil {
if tlsConfig.RootCAs == nil {
tlsConfig.RootCAs = x509.NewCertPool()
}
if !tlsConfig.RootCAs.AppendCertsFromPEM(rootCA) {
return fmt.Errorf("failed to append root ca to tls.Config")
}
tlsConfig.RootCAs = rootCA
mtlsCA = fmt.Sprintf("root ca %s", s.Config.ServerCAFile)
}
// Loading Client CA File
clientCA, err := loadCertFile(s.Config.ClientCAFile)
clientCA, err := server.LoadRootCACerts(s.Config.ClientCAFile)
if err != nil {
return fmt.Errorf("failed to load client ca file: %w", err)
}
if len(clientCA) > 0 {
if clientCA != nil {
if tlsConfig.ClientCAs == nil {
tlsConfig.ClientCAs = x509.NewCertPool()
}
if !tlsConfig.ClientCAs.AppendCertsFromPEM(clientCA) {
return fmt.Errorf("failed to append client ca to tls.Config")
}
tlsConfig.ClientCAs = clientCA
mtlsCA = fmt.Sprintf("%s client ca %s", mtlsCA, s.Config.ClientCAFile)
}
creds = grpc.Creds(credentials.NewTLS(tlsConfig))
@@ -100,9 +95,9 @@ func (s *grpcServer) Start() error {
case mtlsCA != "":
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
creds = grpc.Creds(credentials.NewTLS(tlsConfig))
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS/mTLS cert %s , key %s and %s", s.Name, s.Address, s.Config.CertFile, s.Config.KeyFile, mtlsCA))
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS/mTLS", s.Name, s.Address))
default:
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS cert %s and key %s", s.Name, s.Address, s.Config.CertFile, s.Config.KeyFile))
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS cert", s.Name, s.Address))
}
default:
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s without TLS", s.Name, s.Address))
@@ -145,10 +140,3 @@ func (s *grpcServer) Stop() error {
return nil
}
func loadCertFile(certFile string) ([]byte, error) {
if certFile != "" {
return os.ReadFile(certFile)
}
return []byte{}, nil
}
+17 -2
View File
@@ -5,6 +5,7 @@ package http
import (
"context"
"crypto/tls"
"fmt"
"log/slog"
"net/http"
@@ -39,10 +40,23 @@ func (s *httpServer) Start() error {
s.Protocol = httpProtocol
switch {
case s.Config.CertFile != "" || s.Config.KeyFile != "":
certs, err := server.LoadX509KeyPair(s.Config.CertFile, s.Config.KeyFile)
if err != nil {
return err
}
if s.server.TLSConfig == nil {
s.server.TLSConfig = &tls.Config{}
}
tlsConf := s.server.TLSConfig.Clone()
tlsConf.Certificates = append(tlsConf.Certificates, certs)
s.server.TLSConfig = tlsConf
s.Protocol = httpsProtocol
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS cert %s and key %s", s.Name, s.Protocol, s.Address, s.Config.CertFile, s.Config.KeyFile))
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS", s.Name, s.Protocol, s.Address))
go func() {
errCh <- s.server.ListenAndServeTLS(s.Config.CertFile, s.Config.KeyFile)
errCh <- s.server.ListenAndServeTLS("", "")
}()
default:
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s without TLS", s.Name, s.Protocol, s.Address))
@@ -50,6 +64,7 @@ func (s *httpServer) Start() error {
errCh <- s.server.ListenAndServe()
}()
}
select {
case <-s.Ctx.Done():
return s.Stop()
+39
View File
@@ -4,6 +4,8 @@ package server
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"log/slog"
"os"
@@ -88,3 +90,40 @@ func StopSignalHandler(ctx context.Context, cancel context.CancelFunc, logger *s
return nil
}
}
func ReadFileOrData(input string) ([]byte, error) {
if _, err := os.Stat(input); err == nil {
return os.ReadFile(input)
}
return []byte(input), nil
}
func LoadX509KeyPair(certFile, keyFile string) (tls.Certificate, error) {
cert, err := ReadFileOrData(certFile)
if err != nil {
return tls.Certificate{}, fmt.Errorf("failed to read cert: %v", err)
}
key, err := ReadFileOrData(keyFile)
if err != nil {
return tls.Certificate{}, fmt.Errorf("failed to read key: %v", err)
}
return tls.X509KeyPair(cert, key)
}
func LoadRootCACerts(input string) (*x509.CertPool, error) {
pemData, err := ReadFileOrData(input)
if err != nil {
return nil, fmt.Errorf("failed to load root CA data: %w", err)
}
if len(pemData) > 0 {
capool := x509.NewCertPool()
if !capool.AppendCertsFromPEM(pemData) {
return nil, fmt.Errorf("failed to append root ca to tls.Config")
}
return capool, nil
}
return nil, nil
}