From 6d2737b50134e52b58c5077e03d913ed3c1fa128 Mon Sep 17 00:00:00 2001 From: Peter Kurfer Date: Tue, 26 Jan 2021 18:42:07 +0100 Subject: [PATCH] Move HTTPS handling to http_mock handler --- assets/demoCA/ca.key | 5 -- assets/demoCA/ca.pem | 12 ----- cmd/inetmock/ca.go | 26 +--------- config.yaml | 19 ++++---- internal/app/app.go | 48 +++++++++++++++---- internal/endpoint/endpoint_manager.go | 2 - internal/endpoint/handler/http/audit.go | 5 +- .../endpoint/handler/http/mock/handler.go | 21 ++++++-- .../handler/http/mock/protocol_options.go | 4 ++ .../http/mock/protocol_options_test.go | 28 +++++++++++ pkg/audit/event_stream_test.go | 4 +- pkg/audit/sink/log_sink.go | 5 +- pkg/audit/sink/writer_sink_test.go | 4 +- pkg/audit/tls_details.go | 38 ++++++--------- 14 files changed, 124 insertions(+), 97 deletions(-) delete mode 100644 assets/demoCA/ca.key delete mode 100644 assets/demoCA/ca.pem diff --git a/assets/demoCA/ca.key b/assets/demoCA/ca.key deleted file mode 100644 index b3aa03e..0000000 --- a/assets/demoCA/ca.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTTz25fFLS2WO4hXD -162B059HEe+MAQtV4iGXf7HfKCihRANCAAT3D181Tzrz6i9Mx75pmyAsg+itojO9 -sHXZSswmfsh46IVK46m0hXNHgPvD2WYW5m1PHvRl3B0vDo/2Y6sOU/Q9 ------END PRIVATE KEY----- diff --git a/assets/demoCA/ca.pem b/assets/demoCA/ca.pem deleted file mode 100644 index c603b9c..0000000 --- a/assets/demoCA/ca.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB3DCCAYKgAwIBAgIQHQIFIEcNZjsDP+wDtGPMXzAKBggqhkjOPQQDAjBOMRAw -DgYDVQQGEwdnZXJtYW55MREwDwYDVQQHEwhEb3J0bXVuZDERMA8GA1UEChMISU5l -dE1vY2sxFDASBgNVBAMTC0lOZXRNb2NrIENBMB4XDTIwMDYxNTEwNTEzNloXDTIw -MDYxNTEwNTEzNlowTjEQMA4GA1UEBhMHZ2VybWFueTERMA8GA1UEBxMIRG9ydG11 -bmQxETAPBgNVBAoTCElOZXRNb2NrMRQwEgYDVQQDEwtJTmV0TW9jayBDQTBZMBMG -ByqGSM49AgEGCCqGSM49AwEHA0IABPcPXzVPOvPqL0zHvmmbICyD6K2iM72wddlK -zCZ+yHjohUrjqbSFc0eA+8PZZhbmbU8e9GXcHS8Oj/Zjqw5T9D2jQjBAMA4GA1Ud -DwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDwYDVR0T -AQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiBecJsOL7ej0kCkWOnoQJpW3JuY -KQIxQBT+XXPKEJj14AIhANG4twTloC3amz8Y7Zn3DVtvjXlTgg8YwjBFG+JioQOe ------END CERTIFICATE----- diff --git a/cmd/inetmock/ca.go b/cmd/inetmock/ca.go index 62f2279..b28ad0f 100644 --- a/cmd/inetmock/ca.go +++ b/cmd/inetmock/ca.go @@ -8,7 +8,6 @@ import ( "github.com/spf13/cobra" "gitlab.com/inetmock/inetmock/pkg/cert" "gitlab.com/inetmock/inetmock/pkg/config" - "gitlab.com/inetmock/inetmock/pkg/logging" "go.uber.org/zap" ) @@ -34,6 +33,7 @@ var ( certOutPath, curveName string ) +//nolint:lll func init() { generateCaCmd = &cobra.Command{ Use: "generate-ca", @@ -50,7 +50,7 @@ func init() { generateCaCmd.Flags().StringSliceVar(&caCertOptions.Locality, generateCaLocalityName, nil, "Locality information to append to certificate") generateCaCmd.Flags().StringSliceVar(&caCertOptions.StreetAddress, generateCaStreetAddressName, nil, "Street address information to append to certificate") generateCaCmd.Flags().StringSliceVar(&caCertOptions.PostalCode, generateCaPostalCodeName, nil, "Postal code information to append to certificate") - generateCaCmd.Flags().StringVar(&certOutPath, generateCACertOutPath, "", "Path where CA files should be stored") + generateCaCmd.Flags().StringVar(&certOutPath, generateCACertOutPath, "", "Path where CA files should be stored") generateCaCmd.Flags().StringVar(&curveName, generateCACurveName, "", "Name of the curve to use, if empty ED25519 is used, other valid values are [P224, P256,P384,P521]") generateCaCmd.Flags().DurationVar(¬Before, generateCANotBeforeRelative, 17520*time.Hour, "Relative time value since when in the past the CA certificate should be valid. The value has a time unit, the greatest time unit is h for hour.") generateCaCmd.Flags().DurationVar(¬After, generateCANotAfterRelative, 17520*time.Hour, "Relative time value until when in the future the CA certificate should be valid. The value has a time unit, the greatest time unit is h for hour.") @@ -108,25 +108,3 @@ func runGenerateCA(_ *cobra.Command, _ []string) { } logger.Info("completed certificate generation") } - -func getDurationFlag(cmd *cobra.Command, flagName string, logger logging.Logger) (val time.Duration, err error) { - if val, err = cmd.Flags().GetDuration(flagName); err != nil { - logger.Error( - "failed to parse parse flag", - zap.String("flag", flagName), - zap.Error(err), - ) - } - return -} - -func getStringFlag(cmd *cobra.Command, flagName string, logger logging.Logger) (val string, err error) { - if val, err = cmd.Flags().GetString(flagName); err != nil { - logger.Error( - "failed to parse parse flag", - zap.String("flag", flagName), - zap.Error(err), - ) - } - return -} diff --git a/config.yaml b/config.yaml index c71558e..7ce1cdc 100644 --- a/config.yaml +++ b/config.yaml @@ -69,6 +69,15 @@ endpoints: - 8080 options: <<: *httpResponseRules + https: + handler: http_mock + listenAddress: 0.0.0.0 + ports: + - 443 + - 8443 + options: + tls: true + <<: *httpResponseRules proxy: handler: http_proxy listenAddress: 0.0.0.0 @@ -78,16 +87,6 @@ endpoints: target: ipAddress: 127.0.0.1 port: 80 - httpsDowngrade: - handler: tls_interceptor - listenAddress: 0.0.0.0 - ports: - - 443 - - 8443 - options: - target: - ipAddress: 127.0.0.1 - port: 80 plainDns: handler: dns_mock listenAddress: 0.0.0.0 diff --git a/internal/app/app.go b/internal/app/app.go index 1a8b6db..48c137b 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -108,35 +108,67 @@ func (a *app) MustRun() { } func (a *app) Logger() logging.Logger { - return a.ctx.Value(loggerKey).(logging.Logger) + val := a.ctx.Value(loggerKey) + if val == nil { + return nil + } + return val.(logging.Logger) } func (a *app) Config() config.Config { - return a.ctx.Value(configKey).(config.Config) + val := a.ctx.Value(configKey) + if val == nil { + return nil + } + return val.(config.Config) } func (a *app) CertStore() cert.Store { - return a.ctx.Value(certStoreKey).(cert.Store) + val := a.ctx.Value(certStoreKey) + if val == nil { + return nil + } + return val.(cert.Store) } func (a *app) Checker() health.Checker { - return a.ctx.Value(healthCheckerKey).(health.Checker) + val := a.ctx.Value(healthCheckerKey) + if val == nil { + return nil + } + return val.(health.Checker) } func (a *app) EndpointManager() endpoint.EndpointManager { - return a.ctx.Value(endpointManagerKey).(endpoint.EndpointManager) + val := a.ctx.Value(endpointManagerKey) + if val == nil { + return nil + } + return val.(endpoint.EndpointManager) } func (a *app) Audit() audit.Emitter { - return a.ctx.Value(eventStreamKey).(audit.Emitter) + val := a.ctx.Value(eventStreamKey) + if val == nil { + return nil + } + return val.(audit.Emitter) } func (a *app) EventStream() audit.EventStream { - return a.ctx.Value(eventStreamKey).(audit.EventStream) + val := a.ctx.Value(eventStreamKey) + if val == nil { + return nil + } + return val.(audit.EventStream) } func (a *app) HandlerRegistry() api.HandlerRegistry { - return a.ctx.Value(handlerRegistryKey).(api.HandlerRegistry) + val := a.ctx.Value(handlerRegistryKey) + if val == nil { + return nil + } + return val.(api.HandlerRegistry) } func (a *app) Context() context.Context { diff --git a/internal/endpoint/endpoint_manager.go b/internal/endpoint/endpoint_manager.go index 2da4be2..174a9d3 100644 --- a/internal/endpoint/endpoint_manager.go +++ b/internal/endpoint/endpoint_manager.go @@ -149,8 +149,6 @@ func startEndpoint(ep Endpoint, ctx api.PluginContext, logger logging.Logger) (s success = false } - close(startSuccessful) - return } diff --git a/internal/endpoint/handler/http/audit.go b/internal/endpoint/handler/http/audit.go index 9d92817..3a8d993 100644 --- a/internal/endpoint/handler/http/audit.go +++ b/internal/endpoint/handler/http/audit.go @@ -1,6 +1,7 @@ package http import ( + "crypto/tls" "net/http" "gitlab.com/inetmock/inetmock/pkg/audit" @@ -24,8 +25,8 @@ func EventFromRequest(request *http.Request, app audit.AppProtocol) audit.Event if request.TLS != nil { ev.TLS = &audit.TLSDetails{ - Version: request.TLS.Version, - CipherSuite: request.TLS.CipherSuite, + Version: audit.TLSVersionToEntity(request.TLS.Version).String(), + CipherSuite: tls.CipherSuiteName(request.TLS.CipherSuite), ServerName: request.TLS.ServerName, } } diff --git a/internal/endpoint/handler/http/mock/handler.go b/internal/endpoint/handler/http/mock/handler.go index f4ad94d..a4a8805 100644 --- a/internal/endpoint/handler/http/mock/handler.go +++ b/internal/endpoint/handler/http/mock/handler.go @@ -2,6 +2,7 @@ package mock import ( "context" + "crypto/tls" "errors" "fmt" "net/http" @@ -50,11 +51,16 @@ func (p *httpHandler) Start(ctx api.PluginContext, config config.HandlerConfig) ConnContext: imHttp.StoreConnPropertiesInContext, } + if options.TLS { + p.server.TLSConfig = ctx.CertStore().TLSConfig() + p.server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) + } + for _, rule := range options.Rules { router.setupRoute(rule) } - go p.startServer() + go p.startServer(options.TLS) return } @@ -73,8 +79,17 @@ func (p *httpHandler) Shutdown(ctx context.Context) (err error) { return } -func (p *httpHandler) startServer() { - if err := p.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { +func (p *httpHandler) startServer(tls bool) { + var listen func() error + if tls { + listen = func() error { + return p.server.ListenAndServeTLS("", "") + } + } else { + listen = p.server.ListenAndServe + } + + if err := listen(); err != nil && !errors.Is(err, http.ErrServerClosed) { p.logger.Error( "failed to start http listener", zap.Error(err), diff --git a/internal/endpoint/handler/http/mock/protocol_options.go b/internal/endpoint/handler/http/mock/protocol_options.go index cca7a4d..0a6099a 100644 --- a/internal/endpoint/handler/http/mock/protocol_options.go +++ b/internal/endpoint/handler/http/mock/protocol_options.go @@ -50,6 +50,7 @@ func (tr targetRule) Response() string { } type httpOptions struct { + TLS bool Rules []targetRule } @@ -62,6 +63,7 @@ func loadFromConfig(config *viper.Viper) (options httpOptions, err error) { } tmpRules := struct { + TLS bool Rules []tmpCfg }{} @@ -69,6 +71,8 @@ func loadFromConfig(config *viper.Viper) (options httpOptions, err error) { return } + options.TLS = tmpRules.TLS + for _, i := range tmpRules.Rules { var rulePattern *regexp.Regexp var matchTargetValue RequestMatchTarget diff --git a/internal/endpoint/handler/http/mock/protocol_options_test.go b/internal/endpoint/handler/http/mock/protocol_options_test.go index e56bc7e..abad022 100644 --- a/internal/endpoint/handler/http/mock/protocol_options_test.go +++ b/internal/endpoint/handler/http/mock/protocol_options_test.go @@ -95,6 +95,34 @@ rules: }, wantErr: false, }, + { + name: "Parse config with header matcher and TLS true", + args: args{ + config: ` +tls: true +rules: +- pattern: "^application/octet-stream$" + target: Content-Type + matcher: Header + response: ./assets/fakeFiles/sample.exe +`, + }, + wantOptions: httpOptions{ + TLS: true, + Rules: []targetRule{ + { + pattern: regexp.MustCompile("^application/octet-stream$"), + response: func() string { + p, _ := filepath.Abs("./assets/fakeFiles/sample.exe") + return p + }(), + requestMatchTarget: RequestMatchTargetHeader, + targetKey: "Content-Type", + }, + }, + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/audit/event_stream_test.go b/pkg/audit/event_stream_test.go index f89c981..e6b0e3d 100644 --- a/pkg/audit/event_stream_test.go +++ b/pkg/audit/event_stream_test.go @@ -27,8 +27,8 @@ var ( SourcePort: 32344, DestinationPort: 80, TLS: &audit.TLSDetails{ - Version: tls.VersionTLS13, - CipherSuite: tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + Version: audit.TLSVersionToEntity(tls.VersionTLS13).String(), + CipherSuite: tls.CipherSuiteName(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA), ServerName: "localhost", }, ProtocolDetails: details.HTTP{ diff --git a/pkg/audit/sink/log_sink.go b/pkg/audit/sink/log_sink.go index fb3c131..52e9a5f 100644 --- a/pkg/audit/sink/log_sink.go +++ b/pkg/audit/sink/log_sink.go @@ -1,8 +1,6 @@ package sink import ( - "crypto/tls" - "gitlab.com/inetmock/inetmock/pkg/audit" "gitlab.com/inetmock/inetmock/pkg/logging" "go.uber.org/zap" @@ -34,7 +32,8 @@ func (l logSink) OnSubscribe(evs <-chan audit.Event, _ audit.CloseHandle) { if ev.TLS != nil { eventLogger = eventLogger.With( zap.String("tls_server_name", ev.TLS.ServerName), - zap.String("tls_cipher_suite", tls.CipherSuiteName(ev.TLS.CipherSuite)), + zap.String("tls_cipher_suite", ev.TLS.CipherSuite), + zap.String("tls_version", ev.TLS.Version), ) } diff --git a/pkg/audit/sink/writer_sink_test.go b/pkg/audit/sink/writer_sink_test.go index 0791369..cb955b9 100644 --- a/pkg/audit/sink/writer_sink_test.go +++ b/pkg/audit/sink/writer_sink_test.go @@ -27,8 +27,8 @@ var ( SourcePort: 32344, DestinationPort: 80, TLS: &audit.TLSDetails{ - Version: tls.VersionTLS13, - CipherSuite: tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + Version: audit.TLSVersionToEntity(tls.VersionTLS13).String(), + CipherSuite: tls.CipherSuiteName(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA), ServerName: "localhost", }, ProtocolDetails: details.HTTP{ diff --git a/pkg/audit/tls_details.go b/pkg/audit/tls_details.go index a984007..e4565ff 100644 --- a/pkg/audit/tls_details.go +++ b/pkg/audit/tls_details.go @@ -1,8 +1,6 @@ package audit -import ( - "crypto/tls" -) +import "crypto/tls" var ( tlsToEntity = map[uint16]TLSVersion{ @@ -12,45 +10,37 @@ var ( tls.VersionTLS12: TLSVersion_TLS12, tls.VersionTLS13: TLSVersion_TLS13, } - entityToTls = map[TLSVersion]uint16{ - TLSVersion_SSLv30: tls.VersionSSL30, - TLSVersion_TLS10: tls.VersionTLS10, - TLSVersion_TLS11: tls.VersionTLS11, - TLSVersion_TLS12: tls.VersionTLS12, - TLSVersion_TLS13: tls.VersionTLS13, - } - cipherSuiteIDLookup = func(name string) uint16 { - for _, cs := range tls.CipherSuites() { - if cs.Name == name { - return cs.ID - } - } - return 0 - } ) type TLSDetails struct { - Version uint16 - CipherSuite uint16 + Version string + CipherSuite string ServerName string } +func TLSVersionToEntity(version uint16) TLSVersion { + if v, known := tlsToEntity[version]; known { + return v + } + return TLSVersion_SSLv30 +} + func NewTLSDetailsFromProto(entity *TLSDetailsEntity) *TLSDetails { if entity == nil { return nil } return &TLSDetails{ - Version: entityToTls[entity.GetVersion()], - CipherSuite: cipherSuiteIDLookup(entity.GetCipherSuite()), + Version: entity.GetVersion().String(), + CipherSuite: entity.GetCipherSuite(), ServerName: entity.GetServerName(), } } func (d TLSDetails) ProtoMessage() *TLSDetailsEntity { return &TLSDetailsEntity{ - Version: tlsToEntity[d.Version], - CipherSuite: tls.CipherSuiteName(d.CipherSuite), + Version: TLSVersion(TLSVersion_value[d.Version]), + CipherSuite: d.CipherSuite, ServerName: d.ServerName, } }