From 24dbf021161662e20bcf9efdaabdd2cb06a73c5a Mon Sep 17 00:00:00 2001 From: matt durham Date: Tue, 12 Sep 2023 13:07:05 -0700 Subject: [PATCH] Flow Windows Certificate Store support added. --- CHANGELOG.md | 10 ++ .../flow/reference/config-blocks/http.md | 60 ++++++++- pkg/server/tls.go | 8 +- pkg/server/tls_certstore_windows.go | 118 +++++++++++------- pkg/server/tls_certstore_windows_test.go | 14 +-- service/http/handler.go | 13 ++ service/http/handler_windows.go | 25 ++++ service/http/http.go | 42 ++++++- service/http/tls.go | 89 ++++++++++++- 9 files changed, 314 insertions(+), 65 deletions(-) create mode 100644 service/http/handler.go create mode 100644 service/http/handler_windows.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 139fc6068baa..115383697600 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ internal API changes are not present. Main (unreleased) ----------------- + +> **BREAKING CHANGES**: This release has breaking changes. Please read entries +> carefully and consult the [upgrade guide][] for specific instructions. + +### Breaking changes + +- Static mode Windows Certificate Filter no longer restricted to TLS 1.2 and specific cipher suites. (@mattdurham) + ### Features - New Grafana Agent Flow components: @@ -24,6 +32,8 @@ Main (unreleased) - Clustering: Allow advertise interfaces to be configurable. (@wildum) +- Add support for `windows_certificate_filter` under http tls config block. (@mattdurham) + ### Other changes - Use Go 1.21.0 for builds. (@rfratto) diff --git a/docs/sources/flow/reference/config-blocks/http.md b/docs/sources/flow/reference/config-blocks/http.md index f19177c68b02..59a081fa1b66 100644 --- a/docs/sources/flow/reference/config-blocks/http.md +++ b/docs/sources/flow/reference/config-blocks/http.md @@ -38,11 +38,17 @@ inner blocks. The following blocks are supported inside the definition of `http`: -Hierarchy | Block | Description | Required ---------- | ----- | ----------- | -------- -tls | [tls][] | Define TLS settings for the HTTP server. | no +Hierarchy | Block | Description | Required +--------- |--------------------------------|---------------------------------------------------------------| -------- +tls | [tls][] | Define TLS settings for the HTTP server. | no +tls > windows_certificate_filter | [windows_certificate_filter][] | Configure Windows certificate store for all certificates. | no +tls > windows_certificate_filter > server | [server][] | Configure server certificates for Windows certificate filter. | no +tls > windows_certificate_filter > client | [client][] | Configure client certificates for Windows certificate filter. | no [tls]: #tls-block +[windows_certificate_filter]: #windows-certificate-filter-block +[server]: #server-block +[client]: #client-block ### tls block @@ -62,7 +68,7 @@ Grafana Agent. Name | Type | Description | Default | Required ---- | ---- | ----------- | ------- | -------- `cert_pem` | `string` | PEM data of the server TLS certificate. | `""` | conditionally -`cert_file` | `string` | Path to the server TLS certificate on disk. | `""` | conditionally +`cert_file` | `string` | Path to the server TLS certificate on disk. | `""` | conditionallyy `key_pem` | `string` | PEM data of the server TLS key. | `""` | conditionally `key_file` | `string` | Path to the server TLS key on disk. | `""` | conditionally `client_ca_pem` | `string` | PEM data of the client CA to validate requests against. | `""` | no @@ -146,3 +152,49 @@ The following versions are recognized: * `TLS12` for TLS 1.2 * `TLS11` for TLS 1.1 * `TLS10` for TLS 1.0 + + +### windows certificate filter block + +The `windows_certificate_filter` block is used to configure retrieving and client certificated from the built-in Windows +certificate store. This feature is not available on any platforms other than Windows. When using the `windows_certificate_filter` block +the following TLS settings are overridden and will cause an error when used. + +* `cert_pem` +* `cert_file` +* `key_pem` +* `key_file` +* `client_ca` +* `client_ca_file` + +{{% admonition type="warning" %}} +Also note TLS min and max may not be compatible with the certificate stored in the Windows certificate store. The `windows_certificate_filter` +will serve the found certificate even if it is not compatible with the specified TLS version. +{{% /admonition %}} + + +### server block + +The `server` block is used to find the certificate to check the signer. If multiple are found the `windows_certificate_filter` +will choose the newest one that is current. + +Name | Type | Description | Default | Required +---- |----------------|------------------------------------------------------------------------------------------|---------| -------- +`store` | `string` | Name of the system store to look for the Server Certificate ex LocalMachine, CurrentUser. | `""` | conditionally +`system_store` | `string` | Name of the store to look for the Server Certificate ex My, CA. | `""` | conditionally +`issuer_common_names` | `list(string)` | Issuer common names to check against. | | no +`template_id` | `string` | Server Template ID to match in ASN1 format ex "1.2.3". | `""` | no +`refresh_interval` | `string` | How often to check for a new server certificate. | `"5m"` | no + + + +### client block + +The `client` block is used to check the certificate presented to the server. + +Name | Type | Description | Default | Required +---- |----------------|--------------------------------------------------------|-----| -------- +`issuer_common_names` | `list(string)` | Issuer common names to check against. | | no +`subject_regex` | `string` | Regular expression to match Subject name. | `""` | no +`template_id` | `string` | Client Template ID to match in ASN1 format ex "1.2.3". | | no + diff --git a/pkg/server/tls.go b/pkg/server/tls.go index 3e784b01586e..8d09a68747f7 100644 --- a/pkg/server/tls.go +++ b/pkg/server/tls.go @@ -1,7 +1,6 @@ package server import ( - "context" "crypto/tls" "crypto/x509" "errors" @@ -154,8 +153,7 @@ type tlsListener struct { innerListener net.Listener - windowsCertHandler *winCertStoreHandler - cancelWindowsCert context.CancelFunc //nolint + windowsCertHandler *WinCertStoreHandler } // newTLSListener creates and configures a new tlsListener. @@ -182,8 +180,8 @@ func (l *tlsListener) Accept() (net.Conn, error) { // Close implements net.Listener and closes the tlsListener, preventing any new // connections from being formed. Existing connections will be kept alive. func (l *tlsListener) Close() error { - if l.cancelWindowsCert != nil { - l.cancelWindowsCert() + if l.windowsCertHandler != nil { + l.windowsCertHandler.Stop() } return l.innerListener.Close() } diff --git a/pkg/server/tls_certstore_windows.go b/pkg/server/tls_certstore_windows.go index a16fe0b1b522..109180593fe9 100644 --- a/pkg/server/tls_certstore_windows.go +++ b/pkg/server/tls_certstore_windows.go @@ -1,7 +1,6 @@ package server import ( - "context" "crypto" "crypto/tls" "crypto/x509" @@ -16,8 +15,8 @@ import ( "time" ) -// winCertStoreHandler handles the finding of certificates, validating them and injecting into the default TLS pipeline -type winCertStoreHandler struct { +// WinCertStoreHandler handles the finding of certificates, validating them and injecting into the default TLS pipeline +type WinCertStoreHandler struct { cfg WindowsCertificateFilter subjectRegEx *regexp.Regexp log log.Logger @@ -30,8 +29,31 @@ type winCertStoreHandler struct { // the client does NOT need the signer serverIdentity certstore.Identity clientAuth tls.ClientAuthType + shutdown chan struct{} +} - cancelContext context.Context +// NewWinCertStoreHandler creates a new WindowsCertificateFilter. Run should be called afterward. +func NewWinCertStoreHandler(cfg WindowsCertificateFilter, clientAuth tls.ClientAuthType, l log.Logger) (*WinCertStoreHandler, error) { + var subjectRegEx *regexp.Regexp + var err error + if cfg.Client != nil && cfg.Client.SubjectRegEx != "" { + subjectRegEx, err = regexp.Compile(cfg.Client.SubjectRegEx) + if err != nil { + return nil, fmt.Errorf("error compiling subject common name regular expression: %w", err) + } + } + cn := &WinCertStoreHandler{ + cfg: cfg, + subjectRegEx: subjectRegEx, + log: l, + shutdown: make(chan struct{}), + } + err = cn.refreshCerts() + if err != nil { + return nil, err + } + cn.clientAuth = clientAuth + return cn, nil } func (l *tlsListener) applyWindowsCertificateStore(c TLSConfig) error { @@ -56,20 +78,18 @@ func (l *tlsListener) applyWindowsCertificateStore(c TLSConfig) error { } } - // If there is an existing windows certhandler notify it to stop refreshing - if l.cancelWindowsCert != nil { - l.cancelWindowsCert() - l.cancelWindowsCert = nil + // If there is an existing windows certhandler stop it. + if l.windowsCertHandler != nil { + l.windowsCertHandler.Stop() } - cancelCtx := context.Background() - cancelCtx, cancelHandler := context.WithCancel(cancelCtx) - l.cancelWindowsCert = cancelHandler - cn := &winCertStoreHandler{ - cfg: *c.WindowsCertificateFilter, - subjectRegEx: subjectRegEx, - cancelContext: cancelCtx, - log: l.log, + + cn := &WinCertStoreHandler{ + cfg: *c.WindowsCertificateFilter, + subjectRegEx: subjectRegEx, + log: l.log, + shutdown: make(chan struct{}), } + err = cn.refreshCerts() if err != nil { return err @@ -77,24 +97,9 @@ func (l *tlsListener) applyWindowsCertificateStore(c TLSConfig) error { config := &tls.Config{ VerifyPeerCertificate: cn.VerifyPeer, - GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - cn.winMut.Lock() - defer cn.winMut.Unlock() - cert := &tls.Certificate{ - Certificate: [][]byte{cn.serverCert.Raw}, - PrivateKey: cn.serverSigner, - Leaf: cn.serverCert, - // These seem to be the safest to use, tested on Win10, Server 2016, 2019, 2022 - SupportedSignatureAlgorithms: []tls.SignatureScheme{ - tls.PKCS1WithSHA512, - tls.PKCS1WithSHA384, - tls.PKCS1WithSHA256, - }, - } - return cert, nil - }, - // Windows has broad support for 1.2, only 2022 has support for 1.3 - MaxVersion: tls.VersionTLS12, + GetCertificate: cn.CertificateHandler, + MaxVersion: uint16(c.MaxVersion), + MinVersion: uint16(c.MinVersion), } ca, err := getClientAuthFromString(c.ClientAuth) @@ -111,8 +116,31 @@ func (l *tlsListener) applyWindowsCertificateStore(c TLSConfig) error { return nil } +// Run runs the filter refresh. Stop should be called when done. +func (c *WinCertStoreHandler) Run() { + go c.startUpdateTimer() +} + +// Stop shuts down the refresh loop and frees the win32 handles. +func (c *WinCertStoreHandler) Stop() { + c.shutdown <- struct{}{} +} + +// CertificateHandler returns the certificate to handle peer check. +func (c *WinCertStoreHandler) CertificateHandler(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { + c.winMut.Lock() + defer c.winMut.Unlock() + + cert := &tls.Certificate{ + Certificate: [][]byte{c.serverCert.Raw}, + PrivateKey: c.serverSigner, + Leaf: c.serverCert, + } + return cert, nil +} + // VerifyPeer is called by the TLS pipeline, and specified in tls.config, this is where any custom verification happens -func (c *winCertStoreHandler) VerifyPeer(_ [][]byte, verifiedChains [][]*x509.Certificate) error { +func (c *WinCertStoreHandler) VerifyPeer(_ [][]byte, verifiedChains [][]*x509.Certificate) error { opts := x509.VerifyOptions{} clientCert := verifiedChains[0][0] @@ -150,7 +178,6 @@ func (c *winCertStoreHandler) VerifyPeer(_ [][]byte, verifiedChains [][]*x509.Ce // call the normal pipeline _, err := clientCert.Verify(opts) return err - } // this is the ASN1 Object Identifier for TemplateID @@ -162,7 +189,7 @@ type templateInformation struct { MinorVersion int } -func (c *winCertStoreHandler) startUpdateTimer() { +func (c *WinCertStoreHandler) startUpdateTimer() { refreshInterval := 5 * time.Minute c.winMut.Lock() if c.cfg.Server.RefreshInterval != 0 { @@ -171,7 +198,7 @@ func (c *winCertStoreHandler) startUpdateTimer() { c.winMut.Unlock() for { select { - case <-c.cancelContext.Done(): + case <-c.shutdown: if c.serverIdentity != nil { c.serverIdentity.Close() } @@ -189,9 +216,10 @@ func (c *winCertStoreHandler) startUpdateTimer() { } // refreshCerts is the main work item in certificate store, responsible for finding the right certificate -func (c *winCertStoreHandler) refreshCerts() (err error) { +func (c *WinCertStoreHandler) refreshCerts() (err error) { c.winMut.Lock() defer c.winMut.Unlock() + level.Debug(c.log).Log("msg", "refreshing Windows certificates") // Close the server identity if already set if c.serverIdentity != nil { @@ -226,12 +254,12 @@ func (c *winCertStoreHandler) refreshCerts() (err error) { return } -func (c *winCertStoreHandler) findServerIdentity() (certstore.Identity, error) { +func (c *WinCertStoreHandler) findServerIdentity() (certstore.Identity, error) { return c.findCertificate(c.cfg.Server.SystemStore, c.cfg.Server.Store, c.cfg.Server.IssuerCommonNames, c.cfg.Server.TemplateID, nil, c.getStore) } // getStore converts the string representation to the enum representation -func (c *winCertStoreHandler) getStore(systemStore string, storeName string) (certstore.Store, error) { +func (c *WinCertStoreHandler) getStore(systemStore string, storeName string) (certstore.Store, error) { st, err := certstore.StringToStoreType(systemStore) if err != nil { return nil, err @@ -246,7 +274,7 @@ func (c *winCertStoreHandler) getStore(systemStore string, storeName string) (ce type getStoreFunc func(systemStore, storeName string) (certstore.Store, error) // findCertificate applies the filters to get the server certificate -func (c *winCertStoreHandler) findCertificate(systemStore string, storeName string, commonNames []string, templateID string, subjectRegEx *regexp.Regexp, getStore getStoreFunc) (certstore.Identity, error) { +func (c *WinCertStoreHandler) findCertificate(systemStore string, storeName string, commonNames []string, templateID string, subjectRegEx *regexp.Regexp, getStore getStoreFunc) (certstore.Identity, error) { var store certstore.Store var validIdentity certstore.Identity var identities []certstore.Identity @@ -307,7 +335,7 @@ func (c *winCertStoreHandler) findCertificate(systemStore string, storeName stri return validIdentity, nil } -func (c *winCertStoreHandler) filterByIssuerCommonNames(input []certstore.Identity, commonNames []string) ([]certstore.Identity, error) { +func (c *WinCertStoreHandler) filterByIssuerCommonNames(input []certstore.Identity, commonNames []string) ([]certstore.Identity, error) { if len(commonNames) == 0 { return input, nil } @@ -327,7 +355,7 @@ func (c *winCertStoreHandler) filterByIssuerCommonNames(input []certstore.Identi return returnIdentities, nil } -func (c *winCertStoreHandler) filterByTemplateID(input []certstore.Identity, id string) ([]certstore.Identity, error) { +func (c *WinCertStoreHandler) filterByTemplateID(input []certstore.Identity, id string) ([]certstore.Identity, error) { if id == "" { return input, nil } @@ -360,7 +388,7 @@ func getTemplateID(cert *x509.Certificate) string { return "" } -func (c *winCertStoreHandler) filterBySubjectRegularExpression(input []certstore.Identity, regEx *regexp.Regexp) ([]certstore.Identity, error) { +func (c *WinCertStoreHandler) filterBySubjectRegularExpression(input []certstore.Identity, regEx *regexp.Regexp) ([]certstore.Identity, error) { if regEx == nil { return input, nil } diff --git a/pkg/server/tls_certstore_windows_test.go b/pkg/server/tls_certstore_windows_test.go index 63fec454ad8c..f888b896fbee 100644 --- a/pkg/server/tls_certstore_windows_test.go +++ b/pkg/server/tls_certstore_windows_test.go @@ -14,7 +14,7 @@ import ( ) func TestEasyFilter(t *testing.T) { - c := &winCertStoreHandler{ + c := &WinCertStoreHandler{ cfg: WindowsCertificateFilter{ Server: &WindowsServerFilter{ Store: "My", @@ -37,7 +37,7 @@ func TestEasyFilter(t *testing.T) { } func TestTemplateIDFilter(t *testing.T) { - c := &winCertStoreHandler{ + c := &WinCertStoreHandler{ cfg: WindowsCertificateFilter{ Server: &WindowsServerFilter{ Store: "My", @@ -61,7 +61,7 @@ func TestTemplateIDFilter(t *testing.T) { } func TestCommonName(t *testing.T) { - c := &winCertStoreHandler{ + c := &WinCertStoreHandler{ cfg: WindowsCertificateFilter{ Server: &WindowsServerFilter{ Store: "My", @@ -86,7 +86,7 @@ func TestCommonName(t *testing.T) { } func TestCommonName_Fail(t *testing.T) { - c := &winCertStoreHandler{ + c := &WinCertStoreHandler{ cfg: WindowsCertificateFilter{ Server: &WindowsServerFilter{ Store: "My", @@ -107,7 +107,7 @@ func TestCommonName_Fail(t *testing.T) { } func TestTemplateIDFilter_Fail(t *testing.T) { - c := &winCertStoreHandler{ + c := &WinCertStoreHandler{ cfg: WindowsCertificateFilter{ Server: &WindowsServerFilter{ Store: "My", @@ -127,7 +127,7 @@ func TestTemplateIDFilter_Fail(t *testing.T) { } func TestMatching2CertsGetMostRecent(t *testing.T) { - c := &winCertStoreHandler{ + c := &WinCertStoreHandler{ cfg: WindowsCertificateFilter{ Server: &WindowsServerFilter{ Store: "My", @@ -173,7 +173,7 @@ func (f fakeStore) Identities() ([]certstore.Identity, error) { return ids, nil } -func (f fakeStore) Import(data []byte, password string) error { +func (f fakeStore) Import(_ []byte, _ string) error { panic("should not be called") } diff --git a/service/http/handler.go b/service/http/handler.go new file mode 100644 index 000000000000..d8b57c2ae152 --- /dev/null +++ b/service/http/handler.go @@ -0,0 +1,13 @@ +//go:build !windows + +package http + +import ( + "crypto/tls" + "github.com/grafana/agent/pkg/server" +) + +// tlsConfig generates a tls.Config from args. +func (args *TLSArguments) winTlsConfig(_ *server.WinCertStoreHandler) (*tls.Config, error) { + panic("Windows Certificate filter is only available on Windows platforms.") +} diff --git a/service/http/handler_windows.go b/service/http/handler_windows.go new file mode 100644 index 000000000000..2cc9d606d8dd --- /dev/null +++ b/service/http/handler_windows.go @@ -0,0 +1,25 @@ +package http + +import ( + "crypto/tls" + "github.com/grafana/agent/pkg/server" +) + +// tlsConfig generates a tls.Config from args. +func (args *TLSArguments) winTlsConfig(win *server.WinCertStoreHandler) (*tls.Config, error) { + config := &tls.Config{ + MinVersion: uint16(args.MinVersion), + MaxVersion: uint16(args.MaxVersion), + ClientAuth: tls.ClientAuthType(args.ClientAuth), + VerifyPeerCertificate: win.VerifyPeer, + GetCertificate: win.CertificateHandler, + } + + for _, c := range args.CipherSuites { + config.CipherSuites = append(config.CipherSuites, uint16(c)) + } + for _, c := range args.CurvePreferences { + config.CurvePreferences = append(config.CurvePreferences, tls.CurveID(c)) + } + return config, nil +} diff --git a/service/http/http.go b/service/http/http.go index 82e214cf7172..3387628f5c5f 100644 --- a/service/http/http.go +++ b/service/http/http.go @@ -5,6 +5,7 @@ import ( "context" "crypto/tls" "fmt" + "github.com/grafana/agent/pkg/server" "net" "net/http" "path" @@ -55,6 +56,9 @@ type Service struct { gatherer prometheus.Gatherer opts Options + winMut sync.Mutex + win *server.WinCertStoreHandler + // publicLis and tcpLis are used to lazily enable TLS, since TLS is // optionally configurable at runtime. // @@ -131,6 +135,13 @@ func (s *Service) Run(ctx context.Context, host service.Host) error { ctx, cancel := context.WithCancel(ctx) defer cancel() + defer func() { + s.winMut.Lock() + defer s.winMut.Unlock() + if s.win != nil { + s.win.Stop() + } + }() netLis, err := net.Listen("tcp", s.opts.HTTPListenAddr) if err != nil { @@ -283,8 +294,21 @@ func (s *Service) componentHandler(host service.Host) http.HandlerFunc { func (s *Service) Update(newConfig any) error { newArgs := newConfig.(Arguments) + if newArgs.TLS.WindowsFilter != nil { + err := s.updateWindowsCertificateFilter(newArgs.TLS) + if err != nil { + return err + } + } + if newArgs.TLS != nil { - tlsConfig, err := newArgs.TLS.tlsConfig() + var tlsConfig *tls.Config + var err error + if newArgs.TLS.WindowsFilter != nil { + tlsConfig, err = newArgs.TLS.winTlsConfig(s.win) + } else { + tlsConfig, err = newArgs.TLS.tlsConfig() + } if err != nil { return err } @@ -327,6 +351,22 @@ func (s *Service) Data() any { } } +func (s *Service) updateWindowsCertificateFilter(tlsArgs *TLSArguments) error { + s.winMut.Lock() + defer s.winMut.Unlock() + // Stop if Window Handler is currently running. + if s.win != nil { + s.win.Stop() + } + handler, err := server.NewWinCertStoreHandler(tlsArgs.WindowsFilter.toYaml(), tls.ClientAuthType(tlsArgs.ClientAuth), s.log) + if err != nil { + return err + } + s.win = handler + s.win.Run() + return nil +} + // Data includes information associated with the HTTP service. type Data struct { // Address that the HTTP service is configured to listen on. diff --git a/service/http/tls.go b/service/http/tls.go index 79bd122849ac..3ece6015ec5b 100644 --- a/service/http/tls.go +++ b/service/http/tls.go @@ -5,7 +5,10 @@ import ( "crypto/x509" "encoding" "fmt" + "github.com/grafana/agent/pkg/server" + "github.com/grafana/regexp" "os" + "time" "github.com/grafana/river" "github.com/grafana/river/rivertypes" @@ -25,14 +28,94 @@ type TLSArguments struct { MinVersion TLSVersion `river:"min_version,attr,optional"` MaxVersion TLSVersion `river:"max_version,attr,optional"` - // TODO(rfratto): windows certificate filter. + // Windows Certificate Filter + WindowsFilter *WindowsCertificateFilter `river:"windows_certificate_filter,block,optional"` +} + +// WindowsCertificateFilter represents the configuration for accessing the Windows store +type WindowsCertificateFilter struct { + Server *WindowsServerFilter `river:"server,block"` + Client *WindowsClientFilter `river:"client,block"` +} + +// WindowsClientFilter is used to select a client root CA certificate +type WindowsClientFilter struct { + IssuerCommonNames []string `river:"issuer_common_names,attr,optional"` + SubjectRegEx string `river:"subject_regex,attr,optional"` + TemplateID string `river:"template_id,attr,optional"` +} + +// WindowsServerFilter is used to select a server certificate +type WindowsServerFilter struct { + Store string `river:"store,attr,optional"` + SystemStore string `river:"system_store,attr,optional"` + IssuerCommonNames []string `river:"issuer_common_names,attr,optional"` + TemplateID string `river:"template_id,attr,optional"` + RefreshInterval time.Duration `river:"refresh_interval,attr,optional"` +} + +func (wcf *WindowsCertificateFilter) toYaml() server.WindowsCertificateFilter { + return server.WindowsCertificateFilter{ + Server: &server.WindowsServerFilter{ + Store: wcf.Server.Store, + SystemStore: wcf.Server.SystemStore, + IssuerCommonNames: wcf.Server.IssuerCommonNames, + TemplateID: wcf.Server.TemplateID, + RefreshInterval: wcf.Server.RefreshInterval, + }, + Client: &server.WindowsClientFilter{ + IssuerCommonNames: wcf.Client.IssuerCommonNames, + SubjectRegEx: wcf.Client.SubjectRegEx, + TemplateID: wcf.Client.TemplateID, + }, + } +} + +var _ river.Defaulter = (*WindowsServerFilter)(nil) + +// SetToDefault sets the default for WindowsServerFilter +func (wcf *WindowsServerFilter) SetToDefault() { + wcf.RefreshInterval = 5 * time.Minute } var _ river.Validator = (*TLSArguments)(nil) -// Validate returns whether args is valid. It checks that mutually exclusive -// fields are not both set, and that required fields are set. +// Validate returns whether args is valid. func (args *TLSArguments) Validate() error { + if args.WindowsFilter == nil { + return args.validateTLS() + } + return args.validateWindowsCertificateFilterTLS() +} + +// validateWindowsCertificateFilterTLS validates the Windows Certificate filter details. +func (args *TLSArguments) validateWindowsCertificateFilterTLS() error { + switch { + case len(args.Cert) > 0: + case len(args.Key) > 0: + case len(args.CertFile) > 0: + case len(args.ClientCA) > 0: + case len(args.ClientCAFile) > 0: + case len(args.KeyFile) > 0: + return fmt.Errorf("cannot specify any key, certificate or CA when using windows certificate filter") + + } + if args.WindowsFilter.Server == nil { + return fmt.Errorf("windows_certificate_filter requires a server block defined") + } + var err error + if args.WindowsFilter.Client != nil && args.WindowsFilter.Client.SubjectRegEx != "" { + _, err = regexp.Compile(args.WindowsFilter.Client.SubjectRegEx) + if err != nil { + return fmt.Errorf("error compiling subject common name regular expression: %w", err) + } + } + return nil +} + +// validateTLS returns whether args is valid. It checks that mutually exclusive +// fields are not both set, and that required fields are set. +func (args *TLSArguments) validateTLS() error { if len(args.ClientCA) > 0 && len(args.ClientCAFile) > 0 { return fmt.Errorf("cannot specify both client_ca_pem and client_ca_file") }