mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-03 00:40:25 +02:00
envoy: log mtls failures
This implements limited listener-based access logging for downstream transport failures, only enabled when downstream_mtls.enforcement is set to 'reject_connection'. Client certificate details and the error message will be logged. Additionally, the new key 'client-certificate' can be set in the access_log_fields list in the configuration, which will add peer certificate properties (issuer, subject, SANs) to the existing per-request http logs.
This commit is contained in:
parent
d07cb616fd
commit
783b1127c7
3 changed files with 44 additions and 20 deletions
|
@ -139,15 +139,10 @@ func listenerAccessLog() []*envoy_config_accesslog_v3.AccessLog {
|
|||
}
|
||||
tcp := marshalAny(
|
||||
&envoy_extensions_access_loggers_grpc_v3.TcpGrpcAccessLogConfig{CommonConfig: cc})
|
||||
http := marshalAny(
|
||||
&envoy_extensions_access_loggers_grpc_v3.HttpGrpcAccessLogConfig{CommonConfig: cc})
|
||||
return []*envoy_config_accesslog_v3.AccessLog{
|
||||
{
|
||||
Name: "envoy.access_loggers.tcp_grpc",
|
||||
ConfigType: &envoy_config_accesslog_v3.AccessLog_TypedConfig{TypedConfig: tcp},
|
||||
}, {
|
||||
Name: "envoy.access_loggers.http_grpc",
|
||||
ConfigType: &envoy_config_accesslog_v3.AccessLog_TypedConfig{TypedConfig: http},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +157,9 @@ func (b *Builder) buildMainListener(
|
|||
li.ListenerFilters = append(li.ListenerFilters, ProxyProtocolFilter())
|
||||
}
|
||||
|
||||
li.AccessLog = listenerAccessLog() // XXX
|
||||
if cfg.Options.DownstreamMTLS.Enforcement == config.MTLSEnforcementRejectConnection {
|
||||
li.AccessLog = listenerAccessLog()
|
||||
}
|
||||
|
||||
if cfg.Options.InsecureServer {
|
||||
li.Address = buildAddress(cfg.Options.Addr, 80)
|
||||
|
|
|
@ -2,13 +2,11 @@ package controlplane
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
envoy_data_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3"
|
||||
envoy_service_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v3"
|
||||
"github.com/rs/zerolog"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
)
|
||||
|
@ -43,18 +41,18 @@ func accessLogListener(
|
|||
ctx context.Context, msg *envoy_service_accesslog_v3.StreamAccessLogsMessage,
|
||||
) {
|
||||
for _, entry := range msg.GetTcpLogs().GetLogEntry() {
|
||||
e, _ := protojson.Marshal(entry)
|
||||
log.Info(ctx).
|
||||
Str("service", "envoy").
|
||||
Interface("log", json.RawMessage(e)).
|
||||
Msg("listener connect (TCP log)")
|
||||
}
|
||||
for _, entry := range msg.GetHttpLogs().GetLogEntry() {
|
||||
e, _ := protojson.Marshal(entry)
|
||||
log.Info(ctx).
|
||||
Str("service", "envoy").
|
||||
Interface("log", json.RawMessage(e)).
|
||||
Msg("listener connect (HTTP log)")
|
||||
failure := entry.GetCommonProperties().GetDownstreamTransportFailureReason()
|
||||
if failure == "" {
|
||||
continue
|
||||
}
|
||||
e := log.Info(ctx).Str("service", "envoy")
|
||||
dict := zerolog.Dict()
|
||||
populateCertEventDict(entry.GetCommonProperties().GetTlsProperties().GetPeerCertificateProperties(), dict)
|
||||
e.Dict("client-certificate", dict)
|
||||
e.Str("ip", entry.GetCommonProperties().GetDownstreamRemoteAddress().GetSocketAddress().GetAddress())
|
||||
e.Str("tls-sni-hostname", entry.GetCommonProperties().GetTlsProperties().GetTlsSniHostname())
|
||||
e.Str("downstream-transport-failure-reason", failure)
|
||||
e.Msg("listener connection failure")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +119,34 @@ func populateLogEvent(
|
|||
return evt.Str(string(field), entry.GetCommonProperties().GetUpstreamCluster())
|
||||
case log.AccessLogFieldUserAgent:
|
||||
return evt.Str(string(field), entry.GetRequest().GetUserAgent())
|
||||
case log.AccessLogFieldClientCertificate:
|
||||
dict := zerolog.Dict()
|
||||
populateCertEventDict(entry.GetCommonProperties().GetTlsProperties().GetPeerCertificateProperties(), dict)
|
||||
return evt.Dict(string(field), dict)
|
||||
default:
|
||||
return evt
|
||||
}
|
||||
}
|
||||
|
||||
func populateCertEventDict(cert *envoy_data_accesslog_v3.TLSProperties_CertificateProperties, dict *zerolog.Event) {
|
||||
if cert.Issuer != "" {
|
||||
dict.Str("issuer", cert.Issuer)
|
||||
}
|
||||
if cert.Subject != "" {
|
||||
dict.Str("subject", cert.Subject)
|
||||
}
|
||||
if len(cert.SubjectAltName) > 0 {
|
||||
arr := zerolog.Arr()
|
||||
for _, san := range cert.SubjectAltName {
|
||||
// follow openssl GENERAL_NAME_print formatting
|
||||
// envoy only provides dns and uri SANs at the moment
|
||||
switch san := san.GetSan().(type) {
|
||||
case *envoy_data_accesslog_v3.TLSProperties_CertificateProperties_SubjectAltName_Dns:
|
||||
arr.Str("DNS:" + san.Dns)
|
||||
case *envoy_data_accesslog_v3.TLSProperties_CertificateProperties_SubjectAltName_Uri:
|
||||
arr.Str("URI:" + san.Uri)
|
||||
}
|
||||
}
|
||||
dict.Array("subjectAltName", arr)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ const (
|
|||
AccessLogFieldSize AccessLogField = "size"
|
||||
AccessLogFieldUpstreamCluster AccessLogField = "upstream-cluster"
|
||||
AccessLogFieldUserAgent AccessLogField = "user-agent"
|
||||
AccessLogFieldClientCertificate AccessLogField = "client-certificate"
|
||||
)
|
||||
|
||||
var defaultAccessLogFields = []AccessLogField{
|
||||
|
@ -64,6 +65,7 @@ var accessLogFieldLookup = map[AccessLogField]struct{}{
|
|||
AccessLogFieldSize: {},
|
||||
AccessLogFieldUpstreamCluster: {},
|
||||
AccessLogFieldUserAgent: {},
|
||||
AccessLogFieldClientCertificate: {},
|
||||
}
|
||||
|
||||
// Validate returns an error if the access log field is invalid.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue