mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 18:36:30 +02:00
* wip * cleanup * add test * use uuid for temp dir, derive root CA path from filemgr for tests * fix comment * fix double close * use latest notify
188 lines
5.8 KiB
Go
188 lines
5.8 KiB
Go
package controlplane
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"sync"
|
|
|
|
envoy_config_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3"
|
|
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
|
envoy_extensions_access_loggers_grpc_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3"
|
|
envoy_extensions_transport_sockets_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
|
envoy_service_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
|
|
"golang.org/x/net/nettest"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
|
|
"github.com/pomerium/pomerium/config"
|
|
"github.com/pomerium/pomerium/internal/log"
|
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
|
)
|
|
|
|
const (
|
|
clusterTypeURL = "type.googleapis.com/envoy.config.cluster.v3.Cluster"
|
|
listenerTypeURL = "type.googleapis.com/envoy.config.listener.v3.Listener"
|
|
)
|
|
|
|
func (srv *Server) buildDiscoveryResources() map[string][]*envoy_service_discovery_v3.Resource {
|
|
resources := map[string][]*envoy_service_discovery_v3.Resource{}
|
|
cfg := srv.currentConfig.Load()
|
|
for _, cluster := range srv.buildClusters(&cfg.Options) {
|
|
any, _ := anypb.New(cluster)
|
|
resources[clusterTypeURL] = append(resources[clusterTypeURL], &envoy_service_discovery_v3.Resource{
|
|
Name: cluster.Name,
|
|
Version: hex.EncodeToString(cryptutil.HashProto(cluster)),
|
|
Resource: any,
|
|
})
|
|
}
|
|
for _, listener := range srv.buildListeners(&cfg.Options) {
|
|
any, _ := anypb.New(listener)
|
|
resources[listenerTypeURL] = append(resources[listenerTypeURL], &envoy_service_discovery_v3.Resource{
|
|
Name: listener.Name,
|
|
Version: hex.EncodeToString(cryptutil.HashProto(listener)),
|
|
Resource: any,
|
|
})
|
|
}
|
|
return resources
|
|
}
|
|
|
|
func buildAccessLogs(options *config.Options) []*envoy_config_accesslog_v3.AccessLog {
|
|
lvl := options.ProxyLogLevel
|
|
if lvl == "" {
|
|
lvl = options.LogLevel
|
|
}
|
|
if lvl == "" {
|
|
lvl = "debug"
|
|
}
|
|
|
|
switch lvl {
|
|
case "trace", "debug", "info":
|
|
default:
|
|
// don't log access requests for levels > info
|
|
return nil
|
|
}
|
|
|
|
tc := marshalAny(&envoy_extensions_access_loggers_grpc_v3.HttpGrpcAccessLogConfig{
|
|
CommonConfig: &envoy_extensions_access_loggers_grpc_v3.CommonGrpcAccessLogConfig{
|
|
LogName: "ingress-http",
|
|
GrpcService: &envoy_config_core_v3.GrpcService{
|
|
TargetSpecifier: &envoy_config_core_v3.GrpcService_EnvoyGrpc_{
|
|
EnvoyGrpc: &envoy_config_core_v3.GrpcService_EnvoyGrpc{
|
|
ClusterName: "pomerium-control-plane-grpc",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
return []*envoy_config_accesslog_v3.AccessLog{{
|
|
Name: "envoy.access_loggers.http_grpc",
|
|
ConfigType: &envoy_config_accesslog_v3.AccessLog_TypedConfig{TypedConfig: tc},
|
|
}}
|
|
}
|
|
|
|
func buildAddress(hostport string, defaultPort int) *envoy_config_core_v3.Address {
|
|
host, strport, err := net.SplitHostPort(hostport)
|
|
if err != nil {
|
|
host = hostport
|
|
strport = fmt.Sprint(defaultPort)
|
|
}
|
|
port, err := strconv.Atoi(strport)
|
|
if err != nil {
|
|
port = defaultPort
|
|
}
|
|
if host == "" {
|
|
if nettest.SupportsIPv6() {
|
|
host = "::"
|
|
} else {
|
|
host = "0.0.0.0"
|
|
}
|
|
}
|
|
return &envoy_config_core_v3.Address{
|
|
Address: &envoy_config_core_v3.Address_SocketAddress{SocketAddress: &envoy_config_core_v3.SocketAddress{
|
|
Address: host,
|
|
PortSpecifier: &envoy_config_core_v3.SocketAddress_PortValue{PortValue: uint32(port)},
|
|
Ipv4Compat: true,
|
|
}},
|
|
}
|
|
}
|
|
|
|
func (srv *Server) envoyTLSCertificateFromGoTLSCertificate(cert *tls.Certificate) *envoy_extensions_transport_sockets_tls_v3.TlsCertificate {
|
|
envoyCert := &envoy_extensions_transport_sockets_tls_v3.TlsCertificate{}
|
|
var chain bytes.Buffer
|
|
for _, cbs := range cert.Certificate {
|
|
_ = pem.Encode(&chain, &pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: cbs,
|
|
})
|
|
}
|
|
envoyCert.CertificateChain = srv.filemgr.BytesDataSource("tls-crt.pem", chain.Bytes())
|
|
if cert.OCSPStaple != nil {
|
|
envoyCert.OcspStaple = srv.filemgr.BytesDataSource("ocsp-staple", cert.OCSPStaple)
|
|
}
|
|
if bs, err := x509.MarshalPKCS8PrivateKey(cert.PrivateKey); err == nil {
|
|
envoyCert.PrivateKey = srv.filemgr.BytesDataSource("tls-key.pem", pem.EncodeToMemory(
|
|
&pem.Block{
|
|
Type: "PRIVATE KEY",
|
|
Bytes: bs,
|
|
},
|
|
))
|
|
} else {
|
|
log.Warn().Err(err).Msg("failed to marshal private key for tls config")
|
|
}
|
|
for _, scts := range cert.SignedCertificateTimestamps {
|
|
envoyCert.SignedCertificateTimestamp = append(envoyCert.SignedCertificateTimestamp,
|
|
srv.filemgr.BytesDataSource("signed-certificate-timestamp", scts))
|
|
}
|
|
return envoyCert
|
|
}
|
|
|
|
var rootCABundle struct {
|
|
sync.Once
|
|
value string
|
|
}
|
|
|
|
func getRootCertificateAuthority() (string, error) {
|
|
rootCABundle.Do(func() {
|
|
// from https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ssl#arch-overview-ssl-enabling-verification
|
|
knownRootLocations := []string{
|
|
"/etc/ssl/certs/ca-certificates.crt",
|
|
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
|
|
"/etc/pki/tls/certs/ca-bundle.crt",
|
|
"/etc/ssl/ca-bundle.pem",
|
|
"/usr/local/etc/ssl/cert.pem",
|
|
"/etc/ssl/cert.pem",
|
|
}
|
|
for _, path := range knownRootLocations {
|
|
if _, err := os.Stat(path); err == nil {
|
|
rootCABundle.value = path
|
|
break
|
|
}
|
|
}
|
|
if rootCABundle.value == "" {
|
|
log.Error().Strs("known-locations", knownRootLocations).
|
|
Msgf("no root certificates were found in any of the known locations")
|
|
} else {
|
|
log.Info().Msgf("using %s as the system root certificate authority bundle", rootCABundle.value)
|
|
}
|
|
})
|
|
if rootCABundle.value == "" {
|
|
return "", fmt.Errorf("root certificates not found")
|
|
}
|
|
return rootCABundle.value, nil
|
|
}
|
|
|
|
func marshalAny(msg proto.Message) *anypb.Any {
|
|
any := new(anypb.Any)
|
|
_ = anypb.MarshalFrom(any, msg, proto.MarshalOptions{
|
|
AllowPartial: true,
|
|
Deterministic: true,
|
|
})
|
|
return any
|
|
}
|