mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-15 09:42:47 +02:00
TLS configuration
- handle the same AlPN list - use dev build tag for debugging purposes
This commit is contained in:
@ -2,7 +2,7 @@ package quic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@ -11,27 +11,16 @@ import (
|
|||||||
"github.com/quic-go/quic-go"
|
"github.com/quic-go/quic-go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
quictls "github.com/netbirdio/netbird/relay/tls"
|
||||||
nbnet "github.com/netbirdio/netbird/util/net"
|
nbnet "github.com/netbirdio/netbird/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
func Dial(ctx context.Context, address string) (net.Conn, error) {
|
||||||
dialTimeout = 30 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
func Dial(address string) (net.Conn, error) {
|
|
||||||
quicURL, err := prepareURL(address)
|
quicURL, err := prepareURL(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), dialTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
tlsConf := &tls.Config{
|
|
||||||
InsecureSkipVerify: true, // Set to true only for testing
|
|
||||||
NextProtos: []string{"netbird-relay"}, // Ensure this matches the server's ALPN
|
|
||||||
}
|
|
||||||
|
|
||||||
quicConfig := &quic.Config{
|
quicConfig := &quic.Config{
|
||||||
KeepAlivePeriod: 15 * time.Second,
|
KeepAlivePeriod: 15 * time.Second,
|
||||||
MaxIdleTimeout: 60 * time.Second,
|
MaxIdleTimeout: 60 * time.Second,
|
||||||
@ -50,8 +39,12 @@ func Dial(address string) (net.Conn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
session, err := quic.Dial(ctx, udpConn, udpAddr, tlsConf, quicConfig)
|
session, err := quic.Dial(ctx, udpConn, udpAddr, quictls.ClientQUICTLSConfig(), quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(ctx.Err(), context.Canceled) {
|
||||||
|
log.Infof("QUIC connection aborted to: %s", quicURL)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
log.Errorf("failed to dial to Relay server via QUIC '%s': %s", quicURL, err)
|
log.Errorf("failed to dial to Relay server via QUIC '%s': %s", quicURL, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package ws
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -16,7 +16,7 @@ import (
|
|||||||
nbnet "github.com/netbirdio/netbird/util/net"
|
nbnet "github.com/netbirdio/netbird/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Dial(address string) (net.Conn, error) {
|
func Dial(ctx context.Context, address string) (net.Conn, error) {
|
||||||
wsURL, err := prepareURL(address)
|
wsURL, err := prepareURL(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -32,8 +32,12 @@ func Dial(address string) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
parsedURL.Path = ws.URLPath
|
parsedURL.Path = ws.URLPath
|
||||||
|
|
||||||
wsConn, resp, err := websocket.Dial(context.Background(), parsedURL.String(), opts)
|
wsConn, resp, err := websocket.Dial(ctx, parsedURL.String(), opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(ctx.Err(), context.Canceled) {
|
||||||
|
log.Infof("WebSocket connection aborted to: %s", wsURL)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
log.Errorf("failed to dial to Relay server '%s': %s", wsURL, err)
|
log.Errorf("failed to dial to Relay server '%s': %s", wsURL, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -60,10 +64,6 @@ func httpClientNbDialer() *http.Client {
|
|||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
return customDialer.DialContext(ctx, network, addr)
|
return customDialer.DialContext(ctx, network, addr)
|
||||||
},
|
},
|
||||||
// Set up a TLS configuration that skips certificate verification
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
InsecureSkipVerify: true, // This accepts invalid TLS certificates
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
|
@ -2,19 +2,10 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/pem"
|
|
||||||
"math/big"
|
|
||||||
"net"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"go.opentelemetry.io/otel/metric"
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
|
||||||
nberrors "github.com/netbirdio/netbird/client/errors"
|
nberrors "github.com/netbirdio/netbird/client/errors"
|
||||||
@ -22,6 +13,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/relay/server/listener"
|
"github.com/netbirdio/netbird/relay/server/listener"
|
||||||
"github.com/netbirdio/netbird/relay/server/listener/quic"
|
"github.com/netbirdio/netbird/relay/server/listener/quic"
|
||||||
"github.com/netbirdio/netbird/relay/server/listener/ws"
|
"github.com/netbirdio/netbird/relay/server/listener/ws"
|
||||||
|
quictls "github.com/netbirdio/netbird/relay/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListenerConfig is the configuration for the listener.
|
// ListenerConfig is the configuration for the listener.
|
||||||
@ -64,19 +56,16 @@ func (r *Server) Listen(cfg ListenerConfig) error {
|
|||||||
}
|
}
|
||||||
r.listeners = append(r.listeners, wSListener)
|
r.listeners = append(r.listeners, wSListener)
|
||||||
|
|
||||||
quicListener := &quic.Listener{
|
tlsConfigQUIC, err := quictls.ServerQUICTLSConfig(cfg.TLSConfig)
|
||||||
Address: cfg.Address,
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.TLSConfig != nil {
|
quicListener := &quic.Listener{
|
||||||
quicListener.TLSConfig = cfg.TLSConfig
|
Address: cfg.Address,
|
||||||
} else {
|
TLSConfig: tlsConfigQUIC,
|
||||||
tlsConfig, err := generateTestTLSConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
quicListener.TLSConfig = tlsConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.listeners = append(r.listeners, quicListener)
|
r.listeners = append(r.listeners, quicListener)
|
||||||
|
|
||||||
errChan := make(chan error, len(r.listeners))
|
errChan := make(chan error, len(r.listeners))
|
||||||
@ -117,54 +106,3 @@ func (r *Server) Shutdown(ctx context.Context) error {
|
|||||||
func (r *Server) InstanceURL() string {
|
func (r *Server) InstanceURL() string {
|
||||||
return r.relay.instanceURL
|
return r.relay.instanceURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateTestTLSConfig creates a self-signed certificate for testing
|
|
||||||
func generateTestTLSConfig() (*tls.Config, error) {
|
|
||||||
log.Infof("generating test TLS config")
|
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
template := x509.Certificate{
|
|
||||||
SerialNumber: big.NewInt(1),
|
|
||||||
Subject: pkix.Name{
|
|
||||||
Organization: []string{"Test Organization"},
|
|
||||||
},
|
|
||||||
NotBefore: time.Now(),
|
|
||||||
NotAfter: time.Now().Add(time.Hour * 24 * 180), // Valid for 180 days
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
||||||
x509.ExtKeyUsageServerAuth,
|
|
||||||
},
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
DNSNames: []string{"localhost"},
|
|
||||||
IPAddresses: []net.IP{net.ParseIP("192.168.0.10")},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create certificate
|
|
||||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
certPEM := pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: certDER,
|
|
||||||
})
|
|
||||||
|
|
||||||
privateKeyPEM := pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "RSA PRIVATE KEY",
|
|
||||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
|
||||||
})
|
|
||||||
|
|
||||||
tlsCert, err := tls.X509KeyPair(certPEM, privateKeyPEM)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &tls.Config{
|
|
||||||
Certificates: []tls.Certificate{tlsCert},
|
|
||||||
NextProtos: []string{"netbird-relay"},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
3
relay/tls/alpn.go
Normal file
3
relay/tls/alpn.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package tls
|
||||||
|
|
||||||
|
const nbalpn = "nb-quic"
|
12
relay/tls/client_dev.go
Normal file
12
relay/tls/client_dev.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//go:build dev
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
func ClientQUICTLSConfig() *tls.Config {
|
||||||
|
return &tls.Config{
|
||||||
|
InsecureSkipVerify: true, // Debug mode allows insecure connections
|
||||||
|
NextProtos: []string{nbalpn}, // Ensure this matches the server's ALPN
|
||||||
|
}
|
||||||
|
}
|
11
relay/tls/client_prod.go
Normal file
11
relay/tls/client_prod.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
//go:build !dev
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
func ClientQUICTLSConfig() *tls.Config {
|
||||||
|
return &tls.Config{
|
||||||
|
NextProtos: []string{nbalpn},
|
||||||
|
}
|
||||||
|
}
|
37
relay/tls/doc.go
Normal file
37
relay/tls/doc.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Package tls provides utilities for configuring and managing
|
||||||
|
// Transport Layer Security (TLS) in server environments, with
|
||||||
|
// a focus on QUIC protocol support and testing configurations.
|
||||||
|
//
|
||||||
|
// The package includes functions for cloning and customizing
|
||||||
|
// TLS configurations as well as generating self-signed
|
||||||
|
// certificates for development and testing purposes.
|
||||||
|
//
|
||||||
|
// Key Features:
|
||||||
|
//
|
||||||
|
// - `ServerQUICTLSConfig`: Creates a server-side TLS configuration
|
||||||
|
// tailored for QUIC protocol with specified or default settings.
|
||||||
|
// QUIC requires a specific TLS configuration with proper ALPN
|
||||||
|
// (Application-Layer Protocol Negotiation) support, making the
|
||||||
|
// TLS settings crucial for establishing secure connections.
|
||||||
|
//
|
||||||
|
// - `ClientQUICTLSConfig`: Provides a client-side TLS configuration
|
||||||
|
// suitable for QUIC protocol. The configuration differs between
|
||||||
|
// development (insecure testing) and production (strict verification).
|
||||||
|
//
|
||||||
|
// - `generateTestTLSConfig`: Generates a self-signed TLS configuration
|
||||||
|
// for use in local development and testing scenarios.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// This package provides separate implementations for development
|
||||||
|
// and production environments. The development implementation
|
||||||
|
// (guarded by `//go:build dev`) supports testing configurations
|
||||||
|
// with self-signed certificates and insecure client connections.
|
||||||
|
// The production implementation (guarded by `//go:build !dev`)
|
||||||
|
// ensures that valid and secure TLS configurations are supplied
|
||||||
|
// and used.
|
||||||
|
//
|
||||||
|
// The QUIC protocol is highly reliant on properly configured TLS
|
||||||
|
// settings, and this package ensures that configurations meet the
|
||||||
|
// requirements for secure and efficient QUIC communication.
|
||||||
|
package tls
|
71
relay/tls/server_dev.go
Normal file
71
relay/tls/server_dev.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
//go:build dev
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ServerQUICTLSConfig(originTLSCfg *tls.Config) (*tls.Config, error) {
|
||||||
|
if originTLSCfg == nil {
|
||||||
|
log.Warnf("QUIC server will use self signed certificate for testing!")
|
||||||
|
return generateTestTLSConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := originTLSCfg.Clone()
|
||||||
|
cfg.NextProtos = []string{"nb"}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateTestTLSConfig creates a self-signed certificate for testing
|
||||||
|
func generateTestTLSConfig() (*tls.Config, error) {
|
||||||
|
log.Infof("generating test TLS config")
|
||||||
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
template := x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{"Test Organization"},
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(time.Hour * 24 * 180), // Valid for 180 days
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
|
x509.ExtKeyUsageServerAuth,
|
||||||
|
},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
DNSNames: []string{"localhost"},
|
||||||
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create certificate
|
||||||
|
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certPEM := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: certDER,
|
||||||
|
})
|
||||||
|
|
||||||
|
privateKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||||
|
})
|
||||||
|
|
||||||
|
tlsCert, err := tls.X509KeyPair(certPEM, privateKeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{tlsCert},
|
||||||
|
NextProtos: []string{nbalpn},
|
||||||
|
}, nil
|
||||||
|
}
|
17
relay/tls/server_prod.go
Normal file
17
relay/tls/server_prod.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//go:build !dev
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ServerQUICTLSConfig(originTLSCfg *tls.Config) (*tls.Config, error) {
|
||||||
|
if originTLSCfg == nil {
|
||||||
|
return nil, fmt.Errorf("valid TLS config is required for QUIC listener")
|
||||||
|
}
|
||||||
|
cfg := originTLSCfg.Clone()
|
||||||
|
cfg.NextProtos = []string{nbalpn}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
Reference in New Issue
Block a user