From 7b84f7fe3f91d96bc88e5d5c0b4b987890e39ecc Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Sun, 5 Mar 2017 14:19:00 +1100 Subject: [PATCH 01/18] wip: client ssl --- examples/ssl/certificate.yml | 66 ++++++++++++++++++++++++++++++++++++ examples/ssl/client/test.crt | 20 +++++++++++ examples/ssl/client/test.key | 27 +++++++++++++++ examples/ssl/server/test.crt | 20 +++++++++++ examples/ssl/server/test.key | 27 +++++++++++++++ examples/ssl/server/test.pem | 20 +++++++++++ protocol/http.go | 66 +++++++++++++++++++++++++++++++----- 7 files changed, 237 insertions(+), 9 deletions(-) create mode 100644 examples/ssl/certificate.yml create mode 100644 examples/ssl/client/test.crt create mode 100644 examples/ssl/client/test.key create mode 100644 examples/ssl/server/test.crt create mode 100644 examples/ssl/server/test.key create mode 100644 examples/ssl/server/test.pem diff --git a/examples/ssl/certificate.yml b/examples/ssl/certificate.yml new file mode 100644 index 0000000..027470e --- /dev/null +++ b/examples/ssl/certificate.yml @@ -0,0 +1,66 @@ +## Test configuration name. Used for reporting. +name: Network and HTTP screwer. + +## Test Description. Used for reporting +description: Slow network to mobile levels, and add 1s delay to all messages + +## Specify log output level +## +## Log Levels supported: +## Trace (0), Debug (1), Info (2, Default), Warn (3), Error (4), Fatal (5) +loglevel: 0 + +## Configure a proxy that will handle your requests, and forward +## to proxied host. +## +## Currently supports `tcp_proxy` and `http_proxy`. +proxy: + + ## HTTP Proxy: Configures an HTTP Proxy + ## + ## NOTE: SSL is currently not supported + - name: http_proxy + config: + host: 0.0.0.0 + port: 8000 + protocol: https + proxy_host: localhost + proxy_port: 8080 + proxy_protocol: https + # proxy_ssl_key: /Users/mfellows/go/src/github.com/mefellows/muxy/ssl/client/test.key + # proxy_ssl_cert: /Users/mfellows/go/src/github.com/mefellows/muxy/ssl/client/test.crt + proxy_client_ssl_key: /Users/mfellows/.pki/certs/cert-key.pem + proxy_client_ssl_cert: /Users/mfellows/.pki/certs/cert.pem + proxy_client_ssl_ca: /Users/mfellows/.pki/ca/ca.pem + insecure: true # allow insecure https + +## Middleware +## +## Middleware are plugins that are given the opportunity to intervene +## before a request is dispatched to the proxied system (PRE_DISPATCH event) +## and afterwards (POST_DISPATCH event). They are given a reference to +## the current context (HTTP Context or []bytes) and can mutate them. +## +## Middleware are executed in the order specified in this config. +## +middleware: + + ## HTTP Tamperer - Messes with Layer 7. + ## + ## Useful for messing with the HTTP protocol + ## + # - name: http_tamperer + # config: + # response: + # headers: + # content_length: "87" + # status: 500 # Override HTTP Status code + # body: | + # { "status": { "code": "API-200", "message": "Success" }, "certificateValue": "faoeu" } + + ## Request Logger - use this to see what's going in/out of the Proxy. + ## + ## + - name: logger + config: + hex_output: false # Display output as Hex instead of a string diff --git a/examples/ssl/client/test.crt b/examples/ssl/client/test.crt new file mode 100644 index 0000000..2f045c8 --- /dev/null +++ b/examples/ssl/client/test.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMjCCAhoCCQCserdrP4jhazANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJh +dTERMA8GA1UECAwIVmljdG9yaWExEjAQBgNVBAcMCU1lbGJvdXJuZTEMMAoGA1UE +CgwDTkFCMQ0wCwYDVQQLDARlMzAxMB4XDTE1MDMyNDAxNTMzNVoXDTE2MDgwNTAx +NTMzNVowZTELMAkGA1UEBhMCYXUxETAPBgNVBAgMCFZpY3RvcmlhMRIwEAYDVQQH +DAlNZWxib3VybmUxDDAKBgNVBAoMA05BQjENMAsGA1UECwwEZTMwMTESMBAGA1UE +AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDsy +DjQ5I9qjY7o/LigcOa8pwQMNrE8nTGTbhtCfh+ahdIwRWRfaJR8C1iMzimv/jKvc +YG50uOFKpbpPJ3QiqZQqK91tyaRxfoFWt0dDc8mXbLwp4zG/FHlkgHGj0z6jC++e +gaVn4MIQ5I3s1+Ixms4PGPaxXq0NGoRwwh6RPbMNQvJ0PbdFTQbv4GOH5ZSEG/nu +8+Q6plKHx+GKxxQ/dwDq29vDGzao3Uah4FdKor2Rh5HcbW7RD/Il4Ubjo6IYVVbP +wiEy6ZyiMF6eY2dLWZteyh055sZJxVug4lAAwhM/yJ50d6mq3gA0/dWNoXCQGcyb +l8w7SYC66KhJ4jeCUQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCuD0EA9mfC1Njy +0XPGtHw+mTov7JWCex461dGYsJvjvkEaUpBoiwMuwqff06ojEBsIyZDOeC9MgwlC +eEE4LNspCzb8OcWto0KN6AAwmNfOHDJ43zuxmeeZ/IbRuixPfy0yxS4QcJ72kMFm +4137ScN2E6gwl1W6Y9AQXjavAk7JvkqWfF2rO3GSlSyG3Ir1+Fh0ycIpKFS6YrZz +JOyjjA2oxLXb9PmhAOqaCaww1cpb07oG13nzPXLmv0e/3pcd/FeAOMP0VeuWV4Sm +4c/VtXNARydxZzqVtw7fjuXoWJCh3opRK2JVLXLb2uBrb4E4MTY2vZ/sKv+FV6aj +cd27lAMQ +-----END CERTIFICATE----- diff --git a/examples/ssl/client/test.key b/examples/ssl/client/test.key new file mode 100644 index 0000000..bd5920d --- /dev/null +++ b/examples/ssl/client/test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAsDsyDjQ5I9qjY7o/LigcOa8pwQMNrE8nTGTbhtCfh+ahdIwR +WRfaJR8C1iMzimv/jKvcYG50uOFKpbpPJ3QiqZQqK91tyaRxfoFWt0dDc8mXbLwp +4zG/FHlkgHGj0z6jC++egaVn4MIQ5I3s1+Ixms4PGPaxXq0NGoRwwh6RPbMNQvJ0 +PbdFTQbv4GOH5ZSEG/nu8+Q6plKHx+GKxxQ/dwDq29vDGzao3Uah4FdKor2Rh5Hc +bW7RD/Il4Ubjo6IYVVbPwiEy6ZyiMF6eY2dLWZteyh055sZJxVug4lAAwhM/yJ50 +d6mq3gA0/dWNoXCQGcybl8w7SYC66KhJ4jeCUQIDAQABAoIBAQCop04FF+djL7dU +FoamZo50ifS8mW55a5rhWlhY0ckKpyX2wqFLkS8cfWwagL+vhiGff029H5gm4rys +k/tyd4tAnOIq7pNF+VEAT0ksx09/PPrkfcLcgdwq/O24moi8/mHNZ3la+2/JQhAm +msiB5h+w1ejO7C/cumIi8YJz1AsNbnhKTncg+7Rt1pKgmYJhluhw1hQjq0Fq7Lin +7joiSwczmNV+IIrm+vYVKF1IySMHPK0ok+AI11sDanodsPt82jGFawhQC1EBzbyr +KkMnnr97WdKiA2VwBVxmyYNCfNg6h64j0E/XWlVIUhJTP98WIRKBW1jszOjuFUDR +g+DA8gURAoGBAOjiJTGN6P2HfUNuzDcejza6jbJRFASf4Vy+2xRhq9c7Vg9gert5 +aoBRII50qFEIYDMiGKTrW0ZclvjRMaYG21d4DkspjtBwZyN0llEAqZ/ccijCHksi +eQwppsnJoU3KhQgXXNj9JVKHKrC9FnSENLr+605tgsI+G3tO+UOApK/tAoGBAMG5 +cvi4cHLX9FOTh6NHxtTuQnkYWjRod7E/OgGCsHSMkQcrJA+2LprHAUcNslS3mmaX +ZYhKRybAxUxJgP8r2GOM2eWZ7rqRecepX888u4bNXXcLkTPbIrQcjstl9OnifYIo +KZaEQEjE2Lr4iU7g2TMgCjo6xg41zsPKh9Xf/id1AoGAJ5mMyYhf/fx0CGtmvlir +8Zp3TcMLrF2jbKnnhue02Lx2PdciB4711Sv2ZULg/CZ4dTlvB1weATDtWxH3Z0vz +MERx6cX/SuJSJ21DwjJipZROtS+NBymte6v5eIaYrymoxV9zolIpbocdc0Az+Uwh +y0pdqNBmU7FL6wPazuepGWECgYEArqNHigB7HoyfrVgpxoBGNl4zfob9ipFClX6y +A/qUp/ywIQ47DA7oJI+SD0PBp618e0+wMBUF32GYexUoPOCByfyH0fvawkWyytNd +k6zkQLmRsGe1FlJODqBP+fyHtPCAxH0AQLgoW3FZD/RNk9YO94/CqIujT9wh8U76 +9UtgCqECgYEA0mYTItD5nFihqC275I7tQ3ThFboE7ydcWGT/9YVl82SVHCNitJkY +CI+D5/EtDd8IkNDRkYzp0Lg2nE29GSsnzMo/qxe6LQX8yeIqUtGOmrswuLlPQx0k +SpvHVxuflgslNcguSOwNcdCAV2alLKIZiX1BvawjGDWT9mZlAH9tS3k= +-----END RSA PRIVATE KEY----- diff --git a/examples/ssl/server/test.crt b/examples/ssl/server/test.crt new file mode 100644 index 0000000..2f045c8 --- /dev/null +++ b/examples/ssl/server/test.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMjCCAhoCCQCserdrP4jhazANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJh +dTERMA8GA1UECAwIVmljdG9yaWExEjAQBgNVBAcMCU1lbGJvdXJuZTEMMAoGA1UE +CgwDTkFCMQ0wCwYDVQQLDARlMzAxMB4XDTE1MDMyNDAxNTMzNVoXDTE2MDgwNTAx +NTMzNVowZTELMAkGA1UEBhMCYXUxETAPBgNVBAgMCFZpY3RvcmlhMRIwEAYDVQQH +DAlNZWxib3VybmUxDDAKBgNVBAoMA05BQjENMAsGA1UECwwEZTMwMTESMBAGA1UE +AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDsy +DjQ5I9qjY7o/LigcOa8pwQMNrE8nTGTbhtCfh+ahdIwRWRfaJR8C1iMzimv/jKvc +YG50uOFKpbpPJ3QiqZQqK91tyaRxfoFWt0dDc8mXbLwp4zG/FHlkgHGj0z6jC++e +gaVn4MIQ5I3s1+Ixms4PGPaxXq0NGoRwwh6RPbMNQvJ0PbdFTQbv4GOH5ZSEG/nu +8+Q6plKHx+GKxxQ/dwDq29vDGzao3Uah4FdKor2Rh5HcbW7RD/Il4Ubjo6IYVVbP +wiEy6ZyiMF6eY2dLWZteyh055sZJxVug4lAAwhM/yJ50d6mq3gA0/dWNoXCQGcyb +l8w7SYC66KhJ4jeCUQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCuD0EA9mfC1Njy +0XPGtHw+mTov7JWCex461dGYsJvjvkEaUpBoiwMuwqff06ojEBsIyZDOeC9MgwlC +eEE4LNspCzb8OcWto0KN6AAwmNfOHDJ43zuxmeeZ/IbRuixPfy0yxS4QcJ72kMFm +4137ScN2E6gwl1W6Y9AQXjavAk7JvkqWfF2rO3GSlSyG3Ir1+Fh0ycIpKFS6YrZz +JOyjjA2oxLXb9PmhAOqaCaww1cpb07oG13nzPXLmv0e/3pcd/FeAOMP0VeuWV4Sm +4c/VtXNARydxZzqVtw7fjuXoWJCh3opRK2JVLXLb2uBrb4E4MTY2vZ/sKv+FV6aj +cd27lAMQ +-----END CERTIFICATE----- diff --git a/examples/ssl/server/test.key b/examples/ssl/server/test.key new file mode 100644 index 0000000..bd5920d --- /dev/null +++ b/examples/ssl/server/test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAsDsyDjQ5I9qjY7o/LigcOa8pwQMNrE8nTGTbhtCfh+ahdIwR +WRfaJR8C1iMzimv/jKvcYG50uOFKpbpPJ3QiqZQqK91tyaRxfoFWt0dDc8mXbLwp +4zG/FHlkgHGj0z6jC++egaVn4MIQ5I3s1+Ixms4PGPaxXq0NGoRwwh6RPbMNQvJ0 +PbdFTQbv4GOH5ZSEG/nu8+Q6plKHx+GKxxQ/dwDq29vDGzao3Uah4FdKor2Rh5Hc +bW7RD/Il4Ubjo6IYVVbPwiEy6ZyiMF6eY2dLWZteyh055sZJxVug4lAAwhM/yJ50 +d6mq3gA0/dWNoXCQGcybl8w7SYC66KhJ4jeCUQIDAQABAoIBAQCop04FF+djL7dU +FoamZo50ifS8mW55a5rhWlhY0ckKpyX2wqFLkS8cfWwagL+vhiGff029H5gm4rys +k/tyd4tAnOIq7pNF+VEAT0ksx09/PPrkfcLcgdwq/O24moi8/mHNZ3la+2/JQhAm +msiB5h+w1ejO7C/cumIi8YJz1AsNbnhKTncg+7Rt1pKgmYJhluhw1hQjq0Fq7Lin +7joiSwczmNV+IIrm+vYVKF1IySMHPK0ok+AI11sDanodsPt82jGFawhQC1EBzbyr +KkMnnr97WdKiA2VwBVxmyYNCfNg6h64j0E/XWlVIUhJTP98WIRKBW1jszOjuFUDR +g+DA8gURAoGBAOjiJTGN6P2HfUNuzDcejza6jbJRFASf4Vy+2xRhq9c7Vg9gert5 +aoBRII50qFEIYDMiGKTrW0ZclvjRMaYG21d4DkspjtBwZyN0llEAqZ/ccijCHksi +eQwppsnJoU3KhQgXXNj9JVKHKrC9FnSENLr+605tgsI+G3tO+UOApK/tAoGBAMG5 +cvi4cHLX9FOTh6NHxtTuQnkYWjRod7E/OgGCsHSMkQcrJA+2LprHAUcNslS3mmaX +ZYhKRybAxUxJgP8r2GOM2eWZ7rqRecepX888u4bNXXcLkTPbIrQcjstl9OnifYIo +KZaEQEjE2Lr4iU7g2TMgCjo6xg41zsPKh9Xf/id1AoGAJ5mMyYhf/fx0CGtmvlir +8Zp3TcMLrF2jbKnnhue02Lx2PdciB4711Sv2ZULg/CZ4dTlvB1weATDtWxH3Z0vz +MERx6cX/SuJSJ21DwjJipZROtS+NBymte6v5eIaYrymoxV9zolIpbocdc0Az+Uwh +y0pdqNBmU7FL6wPazuepGWECgYEArqNHigB7HoyfrVgpxoBGNl4zfob9ipFClX6y +A/qUp/ywIQ47DA7oJI+SD0PBp618e0+wMBUF32GYexUoPOCByfyH0fvawkWyytNd +k6zkQLmRsGe1FlJODqBP+fyHtPCAxH0AQLgoW3FZD/RNk9YO94/CqIujT9wh8U76 +9UtgCqECgYEA0mYTItD5nFihqC275I7tQ3ThFboE7ydcWGT/9YVl82SVHCNitJkY +CI+D5/EtDd8IkNDRkYzp0Lg2nE29GSsnzMo/qxe6LQX8yeIqUtGOmrswuLlPQx0k +SpvHVxuflgslNcguSOwNcdCAV2alLKIZiX1BvawjGDWT9mZlAH9tS3k= +-----END RSA PRIVATE KEY----- diff --git a/examples/ssl/server/test.pem b/examples/ssl/server/test.pem new file mode 100644 index 0000000..2f045c8 --- /dev/null +++ b/examples/ssl/server/test.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMjCCAhoCCQCserdrP4jhazANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJh +dTERMA8GA1UECAwIVmljdG9yaWExEjAQBgNVBAcMCU1lbGJvdXJuZTEMMAoGA1UE +CgwDTkFCMQ0wCwYDVQQLDARlMzAxMB4XDTE1MDMyNDAxNTMzNVoXDTE2MDgwNTAx +NTMzNVowZTELMAkGA1UEBhMCYXUxETAPBgNVBAgMCFZpY3RvcmlhMRIwEAYDVQQH +DAlNZWxib3VybmUxDDAKBgNVBAoMA05BQjENMAsGA1UECwwEZTMwMTESMBAGA1UE +AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDsy +DjQ5I9qjY7o/LigcOa8pwQMNrE8nTGTbhtCfh+ahdIwRWRfaJR8C1iMzimv/jKvc +YG50uOFKpbpPJ3QiqZQqK91tyaRxfoFWt0dDc8mXbLwp4zG/FHlkgHGj0z6jC++e +gaVn4MIQ5I3s1+Ixms4PGPaxXq0NGoRwwh6RPbMNQvJ0PbdFTQbv4GOH5ZSEG/nu +8+Q6plKHx+GKxxQ/dwDq29vDGzao3Uah4FdKor2Rh5HcbW7RD/Il4Ubjo6IYVVbP +wiEy6ZyiMF6eY2dLWZteyh055sZJxVug4lAAwhM/yJ50d6mq3gA0/dWNoXCQGcyb +l8w7SYC66KhJ4jeCUQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCuD0EA9mfC1Njy +0XPGtHw+mTov7JWCex461dGYsJvjvkEaUpBoiwMuwqff06ojEBsIyZDOeC9MgwlC +eEE4LNspCzb8OcWto0KN6AAwmNfOHDJ43zuxmeeZ/IbRuixPfy0yxS4QcJ72kMFm +4137ScN2E6gwl1W6Y9AQXjavAk7JvkqWfF2rO3GSlSyG3Ir1+Fh0ycIpKFS6YrZz +JOyjjA2oxLXb9PmhAOqaCaww1cpb07oG13nzPXLmv0e/3pcd/FeAOMP0VeuWV4Sm +4c/VtXNARydxZzqVtw7fjuXoWJCh3opRK2JVLXLb2uBrb4E4MTY2vZ/sKv+FV6aj +cd27lAMQ +-----END CERTIFICATE----- diff --git a/protocol/http.go b/protocol/http.go index 2dd5f08..a3f3b2e 100644 --- a/protocol/http.go +++ b/protocol/http.go @@ -1,7 +1,10 @@ package protocol import ( + "crypto/tls" + "crypto/x509" "fmt" + "io/ioutil" "net/http" "time" @@ -14,14 +17,19 @@ import ( // HTTPProxy implements the proxy interface for the HTTP protocol // nolint type HTTPProxy struct { - Port int `required:"true"` - Host string `required:"true" default:"localhost"` - Protocol string `default:"http" required:"true"` - ProxyHost string `required:"true" mapstructure:"proxy_host"` - ProxyPort int `required:"true" mapstructure:"proxy_port"` - ProxyProtocol string `required:"true" default:"http" mapstructure:"proxy_protocol"` - Insecure bool `required:"true" default:"false" mapstructure:"insecure"` - middleware []muxy.Middleware + Port int `required:"true"` + Host string `required:"true" default:"localhost"` + Protocol string `default:"http" required:"true"` + ProxyHost string `required:"true" mapstructure:"proxy_host"` + ProxyPort int `required:"true" mapstructure:"proxy_port"` + ProxyProtocol string `required:"true" default:"http" mapstructure:"proxy_protocol"` + Insecure bool `required:"true" default:"false" mapstructure:"insecure"` + ProxySslCertificate string `required:"false" mapstructure:"proxy_ssl_cert"` + ProxySslKey string `required:"false" mapstructure:"proxy_ssl_key"` + ProxyClientSslCert string `required:"false" mapstructure:"proxy_client_ssl_cert"` + ProxyClientSslKey string `required:"false" mapstructure:"proxy_client_ssl_key"` + ProxyClientSslCa string `required:"false" mapstructure:"proxy_client_ssl_ca"` + middleware []muxy.Middleware } func init() { @@ -42,12 +50,49 @@ func (p *HTTPProxy) Teardown() { // Proxy performs the proxy event func (p *HTTPProxy) Proxy() { log.Info("HTTP proxy listening on %s", log.Colorize(log.BLUE, fmt.Sprintf("%s://%s:%d", p.Protocol, p.Host, p.Port))) + + // TODO: Suggest we only try to configure SSL infra iff: + // a) https is requested; and + // b) custom certs/keys not provided pkiMgr, err := pki.New() checkHTTPServerError(err) + config, err := pkiMgr.GetClientTLSConfig() checkHTTPServerError(err) + + // Override SSL / TLS settings config.InsecureSkipVerify = p.Insecure + if p.ProxySslCertificate == "" { + p.ProxySslCertificate = pkiMgr.Config.ServerCertPath + } + + if p.ProxySslKey == "" { + p.ProxySslKey = pkiMgr.Config.ServerKeyPath + } + + // MASSL (client certiicate) setup + if p.ProxyClientSslCert != "" && p.ProxyClientSslKey != "" && p.ProxyClientSslCa != "" { + // Load client cert + cert, err := tls.LoadX509KeyPair(p.ProxyClientSslCert, p.ProxyClientSslKey) + if err != nil { + log.Fatal(err) + } + + // Load CA cert + caCert, err := ioutil.ReadFile(p.ProxyClientSslCa) + if err != nil { + log.Fatal(err) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + // Setup HTTPS client + config.Certificates = []tls.Certificate{cert} + config.RootCAs = caCertPool + config.BuildNameToCertificate() + } + mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { director := func(req *http.Request) { @@ -55,17 +100,20 @@ func (p *HTTPProxy) Proxy() { req.URL.Scheme = p.ProxyProtocol req.URL.Host = fmt.Sprintf("%s:%d", p.ProxyHost, p.ProxyPort) } + proxy := &ReverseProxy{Director: director, Middleware: p.middleware} proxy.Transport = &http.Transport{ Proxy: http.ProxyFromEnvironment, TLSClientConfig: config, TLSHandshakeTimeout: 10 * time.Second, } + proxy.ServeHTTP(w, r) }) + if p.Protocol == "https" { checkHTTPServerError(err) - checkHTTPServerError(http.ListenAndServeTLS(fmt.Sprintf("%s:%d", p.Host, p.Port), pkiMgr.Config.ClientCertPath, pkiMgr.Config.ClientKeyPath, mux)) + checkHTTPServerError(http.ListenAndServeTLS(fmt.Sprintf("%s:%d", p.Host, p.Port), p.ProxySslCertificate, p.ProxySslKey, mux)) } else { checkHTTPServerError(http.ListenAndServe(fmt.Sprintf("%s:%d", p.Host, p.Port), mux)) } From e5a567a85d7a21f8180d50367c98d067d5f3f7a8 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Sun, 5 Mar 2017 14:22:44 +1100 Subject: [PATCH 02/18] wip: example MASSL server to test client certs --- examples/ssl/massl-server/README | 6 +++ examples/ssl/massl-server/ca.pem | 19 ++++++++++ examples/ssl/massl-server/cert-key.pem | 27 +++++++++++++ examples/ssl/massl-server/cert.pem | 19 ++++++++++ examples/ssl/massl-server/client.p12 | Bin 0 -> 2397 bytes examples/ssl/massl-server/main.go | 44 ++++++++++++++++++++++ examples/ssl/massl-server/server-cert.pem | 19 ++++++++++ examples/ssl/massl-server/server-key.pem | 27 +++++++++++++ 8 files changed, 161 insertions(+) create mode 100644 examples/ssl/massl-server/README create mode 100644 examples/ssl/massl-server/ca.pem create mode 100644 examples/ssl/massl-server/cert-key.pem create mode 100644 examples/ssl/massl-server/cert.pem create mode 100644 examples/ssl/massl-server/client.p12 create mode 100644 examples/ssl/massl-server/main.go create mode 100644 examples/ssl/massl-server/server-cert.pem create mode 100644 examples/ssl/massl-server/server-key.pem diff --git a/examples/ssl/massl-server/README b/examples/ssl/massl-server/README new file mode 100644 index 0000000..7817770 --- /dev/null +++ b/examples/ssl/massl-server/README @@ -0,0 +1,6 @@ +# Test +curl --cacert ca.pem -E ./client.p12:password -v https://localhost:8080/hello + +# Create pkcs12 file +openssl pkcs12 -export -in cert.pem -inkey cert-key.pem -out client.p12 # use 'password' +curl --cacert ~/.pki/ca/ca.pem -E ./client.p12:password -v https://localhost:8080/hello diff --git a/examples/ssl/massl-server/ca.pem b/examples/ssl/massl-server/ca.pem new file mode 100644 index 0000000..0c5348a --- /dev/null +++ b/examples/ssl/massl-server/ca.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIRANd+eeBjb4D4mNW86NmUFk8wDQYJKoZIhvcNAQELBQAw +KjESMBAGA1UEChMJbG9jYWxob3N0MRQwEgYDVQQDEwtQa2kgQ0EgUm9vdDAeFw0x +NzAyMjgyMjIzMDBaFw0yMDAyMTMyMjIzMDBaMCoxEjAQBgNVBAoTCWxvY2FsaG9z +dDEUMBIGA1UEAxMLUGtpIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDS4qFc21Bh5fw4UftWS/MLxKkyJklX+045brxmYL05zGA/isF1QWSq +pZaXaXhFr68/LcXAHOAiNzJSHe9ezscnn7lLN0J+6v5wvW6UKoQhMdCZpWHsGFe5 +e4od6hWJm6rjh3qGx4ENgqXOZNukRMYbig7MKGE5htxcnvdImrPXAiRtuJ6Aa6bl +dBhkpOhQwHEey90NtcliRM6H1jYcCbhtlRStCVXsWiMjfpq9YIq+Wf/ece27Rvgy +DX3UVNkRTuS0ZeX+D3n4lyOMTzgT6Cn0OUU23D5TRCCkDCDxkXgmnT6Cri9x2WnX +AT7c2apUAx6ms9+AACE32ijqSg0Zx0+zAgMBAAGjIzAhMA4GA1UdDwEB/wQEAwIC +pDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCZ/d2+NWcU2bNy +/W4XrwOHuBGVWW6vB2HGDN8l+Ut3K6Gbc5sXrkmmoap2y9zZKZl9mybchqQUJ9Qo +U8zrhRJ5L74NRay9Jm+csRXbMBdSZtfJ8RRzZK7cr+fZ3DTd7tReSmV00nj7ciGj +O2s73/GZHab7FzbTSbEf/5ei0UMAlN4L89DxzJxfnvIg6wu7dXg/QPhU3Ws4Y4bj +5Dpl7pS2ZnVTh+cz39PgD+WkjubSx/CfOoo0bvwXKvg7vuE3HB65aP8tEZePSj4t +MKWLAxwTNSqq7FVDrYkpgsnG00BTefaViTRyEuMaBWc4IpJ+r+W2ODEtFTWVyiyJ +zXOYmm2Y +-----END CERTIFICATE----- diff --git a/examples/ssl/massl-server/cert-key.pem b/examples/ssl/massl-server/cert-key.pem new file mode 100644 index 0000000..f1bcc85 --- /dev/null +++ b/examples/ssl/massl-server/cert-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAuV2YLWXBXUyRp0FEIBr/Jp2EeOcm9XfQPZzZ6M21eH3Cky29 ++OD0ynINBdFHi7QB5fFdnTtg4jpH+q9CvvpGVcrW6tpjKic+RaN6abnSkz+RrXII +X4RtfV/W9wMz6V/h8nhzGWQID0iQkhooaIvTie2nQ+gwQm8wmCL1+KT2IRoELCvC +3VwrjDGde+dSOp+g9oWo4CkCce+wnSQQkI1Htctaq9EayAnrpPFupSTw0+AEOBJK +fGXg2yBN3EqUXBmF/jNcNsMqjV4LCheH6QYWSUxt24HoL9ycPdRnPeUKmzPi9dE9 +dNfXvPtoltE8e/D8zTNNGWcRgGR0lzfxuWeW/wIDAQABAoIBAEx13+Sx+W3bvKTq +FgjMOf0asl8QshBEyL/xaC1QVQ+LiGwfTSJQ4Ih1PQvuRH3K1ZGc2wmVSaRnd/Ne +wcB3CfYvgjFDve3QXC5rfX4I6WRVr2iFBhEoVeWGV+xyBMK6C0ByEMAjc/Oh8ghi +A9MEAlD9l6Y6K1Xr+XZ3zVAv81q5ZMEQsTERUkLA9lwDUpkQVipoLoKEVUeDiRvB +jH+t9/I+axyARyuEx0Vx4Dza4AOhyNdW9J9szlAo2dhV21vW12MLvKH9jx1U2iAw +vOBPe87xX40EIbiUkDrFKogdibFylQp/EWdsWFPM794b3D8/czfh2QYInghfoTzO +bk5+rrECgYEA8u5UBYHYhHV9cQNAZVmpDE1JwUBg34q5M11TuSs7dyy2iqiHI7MS +ysGsaOlf2bycKXQw10Ut+SVR0qnxjf2E/+cuCwnMySCaCPzat9UcGTMk3PqW7B4r +foYmUmgH9n9zc845/L/LNEtHkeLEAGOJL+jvwx4dKxf0C84TZmHUKscCgYEAw1Z4 +QI5L6OKIL1dGDUWLMJXkplxDSPH7XNwDg6zGa81T1NfgDCA+lyLqXEp9YqMSOk5N +4X+mTspazmgv3x6b6urGtIIRENZFLFgKqNfwDFkWDShwChF/8M7bzJsS4P/cNtr1 +lV0RHFERErRIE88v4ErXWwzDmOC/fJojJEW3OgkCgYEAzEuVKVR7C1nq9kFvxEvU +mF3e6sADN7rn6MRRhmVPCvf1Q0Ja87DC2vRo04l/bBLrmQj3kfHBqcayuuDkHS7Y +zIRT+kBxkarzHx/Vp8d2a9LQ621pwoPUvACA9cg6+hdQtlD1/xIkB4RPWeZEQrdy +RXI1P/dxPC5WtB7HvdADpz0CgYEApzvkgABTZPJsfXtOchZT8CikNPlQcacZ+Io0 +SAsnZSvI1bRsEHWaoHI4CwOLDWNnO5vGeYR7sYD09TmlonPmMN0HeYrRaYTIfAp0 +NdGJpkiu5Fz2buhEjLnM3AL3ysHCmwQitNmUyJVu9IB8JNmAt5nbfgwTeVMRHXAp +HejB0WECgYEA5hLQLsyqJY+BzzrvsC9RJ5y/U+P1KMFWnTyo/O8q2tihqSc9tlmk +Jun18bc6z9qzwiSrYqOpAsE6IJlG+Cf39tXytVCxpuBXIe529VZekl2tEYaqdiQ1 +0fbh7R+eGKRjtl+bXucciv+jok13oNWCTuTcpzbxWbNxenpMVYPwB5s= +-----END RSA PRIVATE KEY----- diff --git a/examples/ssl/massl-server/cert.pem b/examples/ssl/massl-server/cert.pem new file mode 100644 index 0000000..709cd29 --- /dev/null +++ b/examples/ssl/massl-server/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIRAP5kAANG+3/2fVhI4QOB3qQwDQYJKoZIhvcNAQELBQAw +KjESMBAGA1UEChMJbG9jYWxob3N0MRQwEgYDVQQDEwtQa2kgQ0EgUm9vdDAeFw0x +NzAyMjgyMjIzMDBaFw0yMDAyMTMyMjIzMDBaMBExDzANBgNVBAoTBmNsaWVudDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALldmC1lwV1MkadBRCAa/yad +hHjnJvV30D2c2ejNtXh9wpMtvfjg9MpyDQXRR4u0AeXxXZ07YOI6R/qvQr76RlXK +1uraYyonPkWjemm50pM/ka1yCF+EbX1f1vcDM+lf4fJ4cxlkCA9IkJIaKGiL04nt +p0PoMEJvMJgi9fik9iEaBCwrwt1cK4wxnXvnUjqfoPaFqOApAnHvsJ0kEJCNR7XL +WqvRGsgJ66TxbqUk8NPgBDgSSnxl4NsgTdxKlFwZhf4zXDbDKo1eCwoXh+kGFklM +bduB6C/cnD3UZz3lCpsz4vXRPXTX17z7aJbRPHvw/M0zTRlnEYBkdJc38blnlv8C +AwEAAaNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr +BgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkq +hkiG9w0BAQsFAAOCAQEASZe43Hi5HDj4odD7fjPHZcNOFUjc4GV5xR0P5sKS1yVO +3D5Gf9v4OVA7O3ejCLQP2lSq++EMUChAVpCJsfzXDwqe8YoHLnSS+EID+JpHEZ8P +PXXc59S/2HN7kEP447MRJCpFbOHy/axFkzZAlpQr7ffUhPQy3gnDWHDQ6uie6Od5 +bRX2B6Whgy9ey7PR3CMT8+SowCjhsKqE3y0vto+55XxMec35uKXI/djOZzje94fE +n7plsWLOgYu0aeOkVKLe6s/Q0/mjO7zicDHwMq0QvFIcMU/MgrZkvcq+2sg/6Rp0 +iKnKss7cdrxnA7fOsGK8mWP0U6GizCvVigI1zN5dZg== +-----END CERTIFICATE----- diff --git a/examples/ssl/massl-server/client.p12 b/examples/ssl/massl-server/client.p12 new file mode 100644 index 0000000000000000000000000000000000000000..a56d83eea8c32ada04cf8d8ee870056c2e77bc2b GIT binary patch literal 2397 zcmV-j38MBef(cmy0Ru3C2_FUtDuzgg_YDCD0ic2j5Cnn=3^0NT2rz;JzXk~^hDe6@ z4FLxRpn?OiFoFZD0s#Opf&--n2`Yw2hW8Bt2LUh~1_~;MNQUft83GGil5AhPU^e{0V2*)i?9`$EOjxf$6t5NNj!kERPI`Wp-Zpe z@)|A(<$(awv6&XAx`B~Uh3E94#ZjCtv(Q`?0eT^x*ayPJtXY2P+f&%B$TX-eB>gah zKcXzK;#Ra=X&~mApnUmvMkEmp-m5$J7CVO(^01G{{(rK9+6}JEUR)F0w>6|C4UfWV z+4wHdRsraZz`4Z7e?Q+m$9mBn=`eMB%j8A*4}!U!F6c zET|dDC*RrAhs-MmVE{1jLGQj}3JPW#Z-c4o)5cvU%SVwR62XkSBhitB*n4R?E<&cM z-p>faV<~HsxS=*js26@-5dN0&e`im^;YZebvuJ0a9;4_Hr10H-n@Yxiz1Y{k2DW*l zk(6&ISILwhJGhig9Fqs%PA)C*N_xg@58UePXL@Pf!^=Nm0pJ-=ew^1e;%m~<^cdi) zJgA34*v0@~djfhKfegJ0v-1@8GW9{Qt3bet0ykUU&=-W3L6gQ=hLsmOet0A%?L7(o z>3Vu0^H*V-)8J77+{~)6f4z0<62jTncXv8F60Gp|LwVQK)rj=&p5$((oit?sVFyCw zQ{dP!>;~C{9aF}!l!w(E+U^_#B8%aELPew{>tn-?sv8JT7k12ehpJV$?z*iD-l#cZ z#-*eyHP+N|jYED0w}oh16Zm5^=inmyFKB3)ORe7?l2Y*zn+~v2dAJ_HH3}US|CQzFf0$kKzg~c%&_q#7d7Pf`B=L13XW=+zHX;dY|{GlP`8ju zJ5f$j3LZzl=`}+I-sGW%%<%SqB_bP;Ig+GK_9XaO+nQ zzp{N_U`83G`g)u+Le|zg0+s;-j(K9$T#ZbMr9U;d4SBQ0QQ)DSclnB)jOOMEpSEIR z5=mAdd|IFLpyoD+iE&U$$FWV^;fAO?j7n2!KUK!xpsUSI4_n86_CGtkVk4scvz!hLsYir* z82w&LNQU4qa=Ihb0{*~)^cN7m$J+n*=C$<>YOF?g9O zcQGwklq5|B^G`#`W)cS!V@&~`tFFxZv67d1+UW=YmR9Y!+4Vf|0GqHnpnn59JF*o+ z{MB?7rOrK}Y0Xfs04}j$E*{=!_c%vhxi_s7*982kuc*3kWn$dw<-WXj3EwSO8 z7a@=E?^#kLWT%qRVP{G++D%l?0MZ4lm5qmt(E9|2Bhp%)n<`^_yM zw$%a9mRF7>3Q!rd8Dj>WEH_Z?CnKUDHmGn6#E+!%7`eCYhvWj4S)0?lnQoM%6&vo9 zr#FbAozI~FZH55LrOc%23~5pbLcLyIDF@J&3TEh`AT)i$xKoNWZ$ygBFT#2H2@a<$ z(qId75$?GDB%mX-nmfYVvl7lH9?%;X!7S^InfVSo7V(^#(IA2bLCWTbQ9aqfOnO~L znl4q+h4+orx5I`_bPvR3J8@`YY0>$Q5fXGdro;~kzrSNG0#rH}i@wz8Z7K}-+=nlN zUB3L309@WPO3gn40ssudi$s8%{yV5ZGbW>TbZ zIpGyesuNcyWhyJV$o@rl|A0BX#g*HD@|Sa326%+!dO;JTF%v1H(U;Mw%LNRqdkIvv zOrKtM{I?gu@EK#IUx|1MFyhIs~j6@p&e%fbdadjbCpIAd}mTrdeW;9 zSQ+ZpKC!BwUW~M+Dy!R7Qg7J|r%eieN`Qi3AEgw~*L&l$HN6|8Ibe+bt{m@v;H$pM z`Co1j8jP}bT$Ml487?4%uX&1T_;e}mApWu5QWH?x=7D1RA32^KrC0hnJr@)S%x0wK zs!TlD^DW!et=0txv8*Vrx>y|Ntl(}9 zxa?aCwVM%o)etQL&U&oF7a+;~;{q?>R{o!Gch!~M(q{UbSrH2=`3CzAP;n!;!QO3I zrSHFBK`hie0GYd0BnuX|cG>iDcNRAuw*nsNXCVy)UeIrm+0dJAmj5r{ z7JKD}hv{(jmG^}U%;Tme{(RKU3oPlGWjuFL7q+r?6_e+!@K({fgOfAUkW5vCyj2gD zC{=uZ;rIKjZiYi5c`{Ak71(7`tt&*PzKSs=Fe3&DDuzgg_YDCF6)_eB6!FpnfN~-> z6(Gk$3Wd|bu~HittuQe#AutIB1uG5%0vZJX1QcBZ_9Ra1u|r|9K)eDA4m4?y3ULGo P Date: Sun, 5 Mar 2017 21:00:32 +1100 Subject: [PATCH 03/18] chore(build): remove unused variable GIT_COMMIT --- scripts/build.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 22bb6dc..2988ecd 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -15,9 +15,6 @@ DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Change into that directory cd $DIR -# Get the git commit -GIT_COMMIT=$(git rev-parse HEAD) - # If its dev mode, only build for ourself if [ "${TF_DEV}x" != "x" ]; then XC_OS=${XC_OS:-$(go env GOOS)} From 4f9543f7cbfd48b73b824c60eba45f29db2561b0 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 20:52:13 +1100 Subject: [PATCH 04/18] feat(symptom): add tcp tamperer symptom --- protocol/tcp.go | 38 +++++++----- symptom/tcp_tamperer.go | 134 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 symptom/tcp_tamperer.go diff --git a/protocol/tcp.go b/protocol/tcp.go index 63709ac..3eb2abf 100644 --- a/protocol/tcp.go +++ b/protocol/tcp.go @@ -10,9 +10,8 @@ import ( "github.com/mefellows/plugo/plugo" ) -// TcpProxy implements a TCP proxy -// nolint -type TcpProxy struct { +// TCPProxy implements a TCP proxy +type TCPProxy struct { Port int `required:"true"` Host string `required:"true" default:"localhost"` ProxyHost string `required:"true" mapstructure:"proxy_host"` @@ -26,7 +25,7 @@ type TcpProxy struct { func init() { plugo.PluginFactories.Register(func() (interface{}, error) { - return &TcpProxy{}, nil + return &TCPProxy{}, nil }, "tcp_proxy") } @@ -37,16 +36,16 @@ func check(err error) { } // Setup sets up the TCP proxy -func (p *TcpProxy) Setup(middleware []muxy.Middleware) { +func (p *TCPProxy) Setup(middleware []muxy.Middleware) { p.middleware = middleware } // Teardown shuts down the TCP proxy -func (p *TcpProxy) Teardown() { +func (p *TCPProxy) Teardown() { } // Proxy runs the TCP proxy -func (p *TcpProxy) Proxy() { +func (p *TCPProxy) Proxy() { log.Trace("Checking connection: %s:%d", p.Host, p.Port) laddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", p.Host, p.Port)) check(err) @@ -57,7 +56,7 @@ func (p *TcpProxy) Proxy() { check(err) for { - log.Info("TCP proxy listening on %s", log.Colorize(log.BLUE, fmt.Sprintf("tcp://%s:%d", p.Host, p.Port))) + log.Info("TCP Proxy proxy listening on %s", log.Colorize(log.BLUE, fmt.Sprintf("tcp://%s:%d", p.Host, p.Port))) conn, err := listener.AcceptTCP() if err != nil { fmt.Printf("Failed to accept connection '%s'\n", err) @@ -112,13 +111,13 @@ func (p *proxy) err(s string, err error) { func (p *proxy) start() { - log.Trace("Starting TCP Proxy") + log.Trace("TCP Proxy Starting TCP Proxy") defer p.lconn.Close() //connect to remote rconn, err := net.DialTCP("tcp", nil, p.raddr) if err != nil { - p.err("Remote connection failed: %s", err) + p.err("TCP Proxy remote connection failed: %s", err) return } p.rconn = rconn @@ -129,14 +128,15 @@ func (p *proxy) start() { p.rconn.SetNoDelay(true) } //display both ends - log.Info("Opened %s >>> %s", p.lconn.RemoteAddr().String(), p.rconn.RemoteAddr().String()) + log.Info("TCP Proxy opened %s >>> %s", p.lconn.RemoteAddr().String(), p.rconn.RemoteAddr().String()) //bidirectional copy go p.pipe(p.lconn, p.rconn) go p.pipe(p.rconn, p.lconn) //wait for close... + <-p.errsig - log.Info("Closed (%d bytes sent, %d bytes received)", p.sentBytes, p.receivedBytes) + log.Info("TCP Proxy closed (%d bytes sent, %d bytes received)", p.sentBytes, p.receivedBytes) } func (p *proxy) pipe(src io.Reader, dst io.Writer) { @@ -149,7 +149,7 @@ func (p *proxy) pipe(src io.Reader, dst io.Writer) { n, readErr := src.Read(buff) if readErr != nil || n == 0 { if !islocal { - p.err("Read failed '%s'\n", readErr) + p.err("TCP Proxy read failed '%s'\n", readErr) } done = true } @@ -158,17 +158,21 @@ func (p *proxy) pipe(src io.Reader, dst io.Writer) { ctx := &muxy.Context{Bytes: b} for _, middleware := range p.middleware { + log.Trace("TCP Proxy applying middleware %v", middleware) if islocal { - middleware.HandleEvent(muxy.EventPreDispatch, ctx) - } else { middleware.HandleEvent(muxy.EventPostDispatch, ctx) + log.Trace("TCP Proxy overwriting bytes sent to target: %s", ctx.Bytes) + } else { + middleware.HandleEvent(muxy.EventPreDispatch, ctx) + log.Trace("TCP Proxy overwriting bytes sent to client: %s", ctx.Bytes) } + b = ctx.Bytes } n, err := dst.Write(b) if err != nil { - log.Error("Write failed: %s", err.Error()) - p.err("Write failed '%s'\n", err) + log.Error("TCP Proxy write failed: %s", err.Error()) + p.err("TCP Proxy write failed '%s'\n", err) return } diff --git a/symptom/tcp_tamperer.go b/symptom/tcp_tamperer.go new file mode 100644 index 0000000..528074d --- /dev/null +++ b/symptom/tcp_tamperer.go @@ -0,0 +1,134 @@ +package symptom + +import ( + "math/rand" + "time" + + "github.com/mefellows/muxy/log" + "github.com/mefellows/muxy/muxy" + "github.com/mefellows/plugo/plugo" +) + +// TCPRequestConfig contains details of the HTTP request to tamper with prior to +// sending on to the target system +type TCPRequestConfig struct { + // Body fixes the request message + Body string + + // Randomize request message + Randomize bool + + // Truncate the request message. Removes trailing char + Truncate bool +} + +// TCPResponseConfig contains details of the TCP response to tamper with prior to +// sending on to the initiating system +type TCPResponseConfig struct { + // Body fixes the response message + Body string + + // Randomize response message + Randomize bool + + // Truncate the response message. Removes trailing char + Truncate bool +} + +// TCPTampererSymptom is a plugin to mess with request/responses between +// a consumer and provider system +type TCPTampererSymptom struct { + Request TCPRequestConfig + Response TCPResponseConfig +} + +func init() { + plugo.PluginFactories.Register(func() (interface{}, error) { + return &TCPTampererSymptom{}, nil + }, "tcp_tamperer") +} + +// Setup sets up the plugin +func (m TCPTampererSymptom) Setup() { + log.Debug("TCP Tamperer Setup()") +} + +// Teardown shuts down the plugin +func (m TCPTampererSymptom) Teardown() { + log.Debug("TCP Tamperer Teardown()") +} + +// HandleEvent is a hook to allow the plugin to intervene with a request/response +// event +func (m *TCPTampererSymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) { + log.Trace("TCP Tamperer - Handle Event") + switch e { + case muxy.EventPreDispatch: + log.Debug("TCP Tamperer - Handle Event - pre dispatch") + m.MuckRequest(ctx) + case muxy.EventPostDispatch: + log.Debug("TCP Tamperer - Handle Event - post dispatch") + m.MuckResponse(ctx) + } +} + +// MuckRequest adds chaos to the request +func (m *TCPTampererSymptom) MuckRequest(ctx *muxy.Context) { + if m.Request.Truncate { + if len(ctx.Bytes) >= 2 { + ctx.Bytes = ctx.Bytes[:len(ctx.Bytes)-2] + } + } + if m.Request.Randomize { + ctx.Bytes = randStringBytesMaskImprSrc(len(ctx.Bytes)) + } + if m.Request.Body != "" { + ctx.Bytes = []byte(m.Request.Body) + } +} + +// MuckResponse adds chaos to the response +func (m *TCPTampererSymptom) MuckResponse(ctx *muxy.Context) { + if m.Response.Truncate { + // TODO: why 2 and 3? + if len(ctx.Bytes) >= 3 { + ctx.Bytes = ctx.Bytes[:len(ctx.Bytes)-3] + } + } + if m.Response.Randomize { + ctx.Bytes = randStringBytesMaskImprSrc(len(ctx.Bytes)) + } + if m.Response.Body != "" { + ctx.Bytes = []byte(m.Response.Body) + } +} + +// Randomly mess with bytes in an array +var src = rand.NewSource(time.Now().UnixNano()) + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + b[i] = letterBytes[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + + return b +} From 5900d7b70b30dbd3a005dceb82052b37573bd762 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 20:53:01 +1100 Subject: [PATCH 05/18] chore(formatting): update lint formatting in log lib --- log/log.go | 1 - 1 file changed, 1 deletion(-) diff --git a/log/log.go b/log/log.go index fbb9672..3d40ab1 100644 --- a/log/log.go +++ b/log/log.go @@ -9,7 +9,6 @@ import ( ) // Level to set -// nolint type Level int // Colour type to print in log messages From d208fde98c948b56133eaf39e63cbee3c479a087 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 20:54:00 +1100 Subject: [PATCH 06/18] chore(formatting): update lint formatting in http proxy and tamperer --- protocol/http.go | 1 - symptom/http_tamperer.go | 33 ++++++++++++++++----------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/protocol/http.go b/protocol/http.go index a3f3b2e..aeec32d 100644 --- a/protocol/http.go +++ b/protocol/http.go @@ -15,7 +15,6 @@ import ( ) // HTTPProxy implements the proxy interface for the HTTP protocol -// nolint type HTTPProxy struct { Port int `required:"true"` Host string `required:"true" default:"localhost"` diff --git a/symptom/http_tamperer.go b/symptom/http_tamperer.go index a3abc11..5300156 100644 --- a/symptom/http_tamperer.go +++ b/symptom/http_tamperer.go @@ -31,28 +31,27 @@ type ResponseConfig struct { Status int } -// HttpTampererSymptom is a plugin to mess with request/responses between +// HTTPTampererSymptom is a plugin to mess with request/responses between // a consumer and provider system -// nolint -type HttpTampererSymptom struct { +type HTTPTampererSymptom struct { Request RequestConfig Response ResponseConfig } func init() { plugo.PluginFactories.Register(func() (interface{}, error) { - return &HttpTampererSymptom{}, nil + return &HTTPTampererSymptom{}, nil }, "http_tamperer") } // Setup sets up the plugin -func (m HttpTampererSymptom) Setup() { - log.Debug("HTTP Error Setup()") +func (m HTTPTampererSymptom) Setup() { + log.Debug("HTTP Tamperer Setup()") } // Teardown shuts down the plugin -func (m HttpTampererSymptom) Teardown() { - log.Debug("HTTP Error Teardown()") +func (m HTTPTampererSymptom) Teardown() { + log.Debug("HTTP Tamperer Teardown()") } // Crude implementation of an io.ReadCloser @@ -77,7 +76,7 @@ func (r *responseBody) Read(p []byte) (int, error) { // HandleEvent is a hook to allow the plugin to intervene with a request/response // event -func (m *HttpTampererSymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) { +func (m *HTTPTampererSymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) { switch e { case muxy.EventPreDispatch: m.MuckRequest(ctx) @@ -87,7 +86,7 @@ func (m *HttpTampererSymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) } // MuckRequest adds chaos to the request -func (m *HttpTampererSymptom) MuckRequest(ctx *muxy.Context) { +func (m *HTTPTampererSymptom) MuckRequest(ctx *muxy.Context) { // Body if m.Request.Body != "" { @@ -96,20 +95,20 @@ func (m *HttpTampererSymptom) MuckRequest(ctx *muxy.Context) { log.Error(err.Error()) } *ctx.Request = *newreq - log.Debug("Spoofing HTTP Request Body with %s", log.Colorize(log.BLUE, m.Request.Body)) + log.Debug("HTTP Tamperer Spoofing HTTP Request Body With %s", log.Colorize(log.BLUE, m.Request.Body)) } // Set Cookies for _, c := range m.Request.Cookies { c.Expires = stringToDate(c.RawExpires) - log.Debug("Spoofing Request Cookie %s => %s", log.Colorize(log.LIGHTMAGENTA, c.Name), c.String()) + log.Debug("HTTP Tamperer Spoofing Request Cookie %s => %s", log.Colorize(log.LIGHTMAGENTA, c.Name), c.String()) ctx.Request.Header.Add("Cookie", c.String()) } // Set Headers for k, v := range m.Request.Headers { key := strings.ToTitle(strings.Replace(k, "_", "-", -1)) - log.Debug("Spoofing Request Header %s => %s", log.Colorize(log.LIGHTMAGENTA, key), v) + log.Debug("HTTP Tamperer Spoofing Request Header %s => %s", log.Colorize(log.LIGHTMAGENTA, key), v) ctx.Request.Header.Set(key, v) } @@ -120,7 +119,7 @@ func (m *HttpTampererSymptom) MuckRequest(ctx *muxy.Context) { } // MuckResponse adds chaos to the response -func (m *HttpTampererSymptom) MuckResponse(ctx *muxy.Context) { +func (m *HTTPTampererSymptom) MuckResponse(ctx *muxy.Context) { // Body if m.Response.Body != "" { @@ -141,21 +140,21 @@ func (m *HttpTampererSymptom) MuckResponse(ctx *muxy.Context) { ProtoMinor: ctx.Response.ProtoMinor, Body: cl, } - log.Debug("Injecting HTTP Response Body with %s", log.Colorize(log.BLUE, m.Response.Body)) + log.Debug("HTTP Tamperer Injecting HTTP Response Body with %s", log.Colorize(log.BLUE, m.Response.Body)) *ctx.Response = *r } // Set Cookies for _, c := range m.Response.Cookies { c.Expires = stringToDate(c.RawExpires) - log.Debug("Spoofing Response Cookie %s => %s", log.Colorize(log.LIGHTMAGENTA, c.Name), c.String()) + log.Debug("HTTP Tamperer Spoofing Response Cookie %s => %s", log.Colorize(log.LIGHTMAGENTA, c.Name), c.String()) ctx.Response.Header.Add("Set-Cookie", c.String()) } // Set Headers for k, v := range m.Response.Headers { key := strings.ToTitle(strings.Replace(k, "_", "-", -1)) - log.Debug("Spoofing Response Header %s => %s", log.Colorize(log.LIGHTMAGENTA, key), v) + log.Debug("HTTP Tamperer Spoofing Response Header %s => %s", log.Colorize(log.LIGHTMAGENTA, key), v) ctx.Response.Header.Add(key, v) } From b5e320d62422c59e35a58dba7abea0a12b024db6 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 20:54:39 +1100 Subject: [PATCH 07/18] feat(symptom): alias 'http_delay' => 'delay' and ability to delay request/response paths --- symptom/http_delay.go | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/symptom/http_delay.go b/symptom/http_delay.go index 431308a..1037163 100644 --- a/symptom/http_delay.go +++ b/symptom/http_delay.go @@ -8,39 +8,52 @@ import ( "github.com/mefellows/plugo/plugo" ) -// HttpDelaySymptom adds specified delays to HTTP requests -// nolint -type HttpDelaySymptom struct { - Delay int `required:"true" default:"2"` +// HTTPDelaySymptom adds specified delays to HTTP requests +// Update docs: these values should be in ms +type HTTPDelaySymptom struct { + RequestDelay int `required:"false" mapstructure:"request_delay"` + ResponseDelay int `required:"false" mapstructure:"response_delay"` + Delay int `required:"false" mapstructure:"delay"` } func init() { plugo.PluginFactories.Register(func() (interface{}, error) { - return &HttpDelaySymptom{}, nil + return &HTTPDelaySymptom{}, nil }, "http_delay") + plugo.PluginFactories.Register(func() (interface{}, error) { + return &HTTPDelaySymptom{}, nil + }, "delay") } // Setup sets up the delay plugin -func (m HttpDelaySymptom) Setup() { +func (m HTTPDelaySymptom) Setup() { log.Debug("HTTP Delay Setup()") } // Teardown shuts down the plugin -func (m HttpDelaySymptom) Teardown() { +func (m HTTPDelaySymptom) Teardown() { log.Debug("HTTP Delay Teardown()") } // HandleEvent takes a proxy event for the proxy to intercept and modify -func (m HttpDelaySymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) { +func (m HTTPDelaySymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) { switch e { case muxy.EventPreDispatch: - m.Muck(ctx) + if m.RequestDelay != 0 { + m.Muck(ctx, m.RequestDelay) + } + case muxy.EventPostDispatch: + if m.ResponseDelay != 0 { + m.Muck(ctx, m.ResponseDelay) + } else if m.Delay != 0 { // legacy behaviour + m.Muck(ctx, m.Delay*1000) // convert to ms + } } } // Muck injects chaos into the system -func (m *HttpDelaySymptom) Muck(ctx *muxy.Context) { - delay := time.Duration(m.Delay) * time.Second +func (m *HTTPDelaySymptom) Muck(ctx *muxy.Context, wait int) { + delay := time.Duration(wait) * time.Millisecond log.Debug("HTTP Delay Muck(), delaying for %v seconds\n", delay.Seconds()) for { From b409c9adc1e80e0f5119b616eefc5d4b35f9c3f0 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 20:55:05 +1100 Subject: [PATCH 08/18] chore(formatting): fix lint in reverse proxy --- protocol/reverse_proxy.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/protocol/reverse_proxy.go b/protocol/reverse_proxy.go index 1f33d8a..60af676 100644 --- a/protocol/reverse_proxy.go +++ b/protocol/reverse_proxy.go @@ -206,7 +206,13 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { defer res.Body.Close() // Fire Post-dispatch middleware event - ctx = &muxy.Context{req, res, rw, nil} // nolint + ctx = &muxy.Context{ + Request: req, + Response: res, + ResponseWriter: rw, + Bytes: nil, + } + for _, middleware := range p.Middleware { middleware.HandleEvent(muxy.EventPostDispatch, ctx) } From ccb86df235dc55812dc3613affa3efc513df2e1a Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 20:55:33 +1100 Subject: [PATCH 09/18] feat(symptom): alias 'http_delay' => 'delay' and ability to delay request/response paths --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a95c202..adb6341 100644 --- a/README.md +++ b/README.md @@ -88,10 +88,11 @@ Muxy is typically used in two ways: # Proxy plugins middleware: - # HTTP response delay plugin - - name: http_delay + # Message Delay request/response plugin + - name: delay config: - delay: 5 + request_delay: 1000 + response_delay: 500 # Log in/out messages - name: logger @@ -168,9 +169,10 @@ Example configuration snippet: ```yaml middleware: - - name: http_delay + - name: delay config: - delay: 1 # Delay in seconds to apply to response + request_delay: 1000 # Delay in ms to apply to request to target + response_delay: 500 # Delay in ms to apply to response from target ``` #### HTTP Tamperer From 8a76b585ed2cb30868c5bed64dbceec722e6d0a9 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 22:37:55 +1100 Subject: [PATCH 10/18] feat(ssl): example MASSL server and client certificates test with Muxy --- examples/ssl/README.md | 59 +++++++++++++++++++ examples/ssl/certificate.yml | 30 +++++----- examples/ssl/client-certs/ca.pem | 19 ++++++ .../cert-key.pem | 0 .../{massl-server => client-certs}/cert.pem | 0 examples/ssl/massl-server/main.go | 14 ++++- .../ssl/{client => proxy-server}/test.crt | 0 .../ssl/{client => proxy-server}/test.key | 0 examples/ssl/server/test.crt | 20 ------- examples/ssl/server/test.key | 27 --------- examples/ssl/server/test.pem | 20 ------- 11 files changed, 105 insertions(+), 84 deletions(-) create mode 100644 examples/ssl/README.md create mode 100644 examples/ssl/client-certs/ca.pem rename examples/ssl/{massl-server => client-certs}/cert-key.pem (100%) rename examples/ssl/{massl-server => client-certs}/cert.pem (100%) rename examples/ssl/{client => proxy-server}/test.crt (100%) rename examples/ssl/{client => proxy-server}/test.key (100%) delete mode 100644 examples/ssl/server/test.crt delete mode 100644 examples/ssl/server/test.key delete mode 100644 examples/ssl/server/test.pem diff --git a/examples/ssl/README.md b/examples/ssl/README.md new file mode 100644 index 0000000..082ab06 --- /dev/null +++ b/examples/ssl/README.md @@ -0,0 +1,59 @@ +# SSL Muxy Tests + +Tests the following features: + +* Run Proxy with HTTPS enabled +* Run Proxy with HTTPS enabled + custom certificate +* Proxy HTTPS target +* Proxy HTTPS target with invalid (untrusted) certificate +* Proxy HTTPS target requiring client certificates + + +### Start MASSL server + +``` +cd examples/ssl/massl-server +go run main.go +``` + +From this directory, you should be able to `curl` the server to ensure it's up: + +``` +curl --cacert ca.pem -E ./client.p12:password https://localhost:8080/hello +# responds with "hello, world!" +``` + +### Start Muxy + +``` +cd examples/ssl +muxy proxy --config certificate.yml +``` + +### cURL muxy + +``` +curl -k -v https://localhost:8000/hello +``` + +You should see "Server certificate: localhost" if the correct certificates are being used. + +### Add some chaos + +Now that you have things working, time to add some chaos - uncomment the `http_tamperer` +in `certificate.yml`: + +``` +## HTTP Tamperer - Messes with Layer 7. +## +## Useful for messing with the HTTP protocol +## +- name: http_tamperer + config: + request: + path: "/nothello" + body: "wow, new body!" # Override request body + response: + status: 201 # Override HTTP Status code + body: "my new body" # Override response body +``` diff --git a/examples/ssl/certificate.yml b/examples/ssl/certificate.yml index 027470e..d645e0f 100644 --- a/examples/ssl/certificate.yml +++ b/examples/ssl/certificate.yml @@ -1,5 +1,5 @@ ## Test configuration name. Used for reporting. -name: Network and HTTP screwer. +name: Serve SSL and Send client certificates ## Test Description. Used for reporting description: Slow network to mobile levels, and add 1s delay to all messages @@ -27,12 +27,12 @@ proxy: proxy_host: localhost proxy_port: 8080 proxy_protocol: https - # proxy_ssl_key: /Users/mfellows/go/src/github.com/mefellows/muxy/ssl/client/test.key - # proxy_ssl_cert: /Users/mfellows/go/src/github.com/mefellows/muxy/ssl/client/test.crt - proxy_client_ssl_key: /Users/mfellows/.pki/certs/cert-key.pem - proxy_client_ssl_cert: /Users/mfellows/.pki/certs/cert.pem - proxy_client_ssl_ca: /Users/mfellows/.pki/ca/ca.pem - insecure: true # allow insecure https + proxy_ssl_key: proxy-server/test.key + proxy_ssl_cert: proxy-server/test.crt + proxy_client_ssl_key: client-certs/cert-key.pem + proxy_client_ssl_cert: client-certs/cert.pem + proxy_client_ssl_ca: client-certs/ca.pem + # insecure: true # allow insecure https ## Middleware ## @@ -49,14 +49,14 @@ middleware: ## ## Useful for messing with the HTTP protocol ## - # - name: http_tamperer - # config: - # response: - # headers: - # content_length: "87" - # status: 500 # Override HTTP Status code - # body: | - # { "status": { "code": "API-200", "message": "Success" }, "certificateValue": "faoeu" } + - name: http_tamperer + config: + request: + path: "/nothello" + body: "wow, new body!" # Override request body + response: + status: 201 # Override HTTP Status code + body: "my new body" # Override response body ## Request Logger - use this to see what's going in/out of the Proxy. ## diff --git a/examples/ssl/client-certs/ca.pem b/examples/ssl/client-certs/ca.pem new file mode 100644 index 0000000..0c5348a --- /dev/null +++ b/examples/ssl/client-certs/ca.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIRANd+eeBjb4D4mNW86NmUFk8wDQYJKoZIhvcNAQELBQAw +KjESMBAGA1UEChMJbG9jYWxob3N0MRQwEgYDVQQDEwtQa2kgQ0EgUm9vdDAeFw0x +NzAyMjgyMjIzMDBaFw0yMDAyMTMyMjIzMDBaMCoxEjAQBgNVBAoTCWxvY2FsaG9z +dDEUMBIGA1UEAxMLUGtpIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDS4qFc21Bh5fw4UftWS/MLxKkyJklX+045brxmYL05zGA/isF1QWSq +pZaXaXhFr68/LcXAHOAiNzJSHe9ezscnn7lLN0J+6v5wvW6UKoQhMdCZpWHsGFe5 +e4od6hWJm6rjh3qGx4ENgqXOZNukRMYbig7MKGE5htxcnvdImrPXAiRtuJ6Aa6bl +dBhkpOhQwHEey90NtcliRM6H1jYcCbhtlRStCVXsWiMjfpq9YIq+Wf/ece27Rvgy +DX3UVNkRTuS0ZeX+D3n4lyOMTzgT6Cn0OUU23D5TRCCkDCDxkXgmnT6Cri9x2WnX +AT7c2apUAx6ms9+AACE32ijqSg0Zx0+zAgMBAAGjIzAhMA4GA1UdDwEB/wQEAwIC +pDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCZ/d2+NWcU2bNy +/W4XrwOHuBGVWW6vB2HGDN8l+Ut3K6Gbc5sXrkmmoap2y9zZKZl9mybchqQUJ9Qo +U8zrhRJ5L74NRay9Jm+csRXbMBdSZtfJ8RRzZK7cr+fZ3DTd7tReSmV00nj7ciGj +O2s73/GZHab7FzbTSbEf/5ei0UMAlN4L89DxzJxfnvIg6wu7dXg/QPhU3Ws4Y4bj +5Dpl7pS2ZnVTh+cz39PgD+WkjubSx/CfOoo0bvwXKvg7vuE3HB65aP8tEZePSj4t +MKWLAxwTNSqq7FVDrYkpgsnG00BTefaViTRyEuMaBWc4IpJ+r+W2ODEtFTWVyiyJ +zXOYmm2Y +-----END CERTIFICATE----- diff --git a/examples/ssl/massl-server/cert-key.pem b/examples/ssl/client-certs/cert-key.pem similarity index 100% rename from examples/ssl/massl-server/cert-key.pem rename to examples/ssl/client-certs/cert-key.pem diff --git a/examples/ssl/massl-server/cert.pem b/examples/ssl/client-certs/cert.pem similarity index 100% rename from examples/ssl/massl-server/cert.pem rename to examples/ssl/client-certs/cert.pem diff --git a/examples/ssl/massl-server/main.go b/examples/ssl/massl-server/main.go index d578a8b..3311391 100644 --- a/examples/ssl/massl-server/main.go +++ b/examples/ssl/massl-server/main.go @@ -3,18 +3,25 @@ package main import ( "crypto/tls" "crypto/x509" + "fmt" "io" "io/ioutil" "log" "net/http" ) -func HelloServer(w http.ResponseWriter, req *http.Request) { +func helloServer(w http.ResponseWriter, req *http.Request) { + log.Println("MASSL Server - /hello called") io.WriteString(w, "hello, world!\n") } +func fileNotFoundServer(w http.ResponseWriter, req *http.Request) { + log.Println("404: ", req.URL.Path, "not found") + io.WriteString(w, fmt.Sprint("404", req.URL.Path, " not found\n")) +} func main() { - http.HandleFunc("/hello", HelloServer) + http.HandleFunc("/hello", helloServer) + http.HandleFunc("/", fileNotFoundServer) caCert, err := ioutil.ReadFile("ca.pem") if err != nil { @@ -40,5 +47,8 @@ func main() { TLSConfig: tlsConfig, } + log.Println("MASSL Server Listening on port 8080") + log.Println("") + log.Println("curl --cacert ca.pem -E ./client.p12:password -v https://localhost:8080/hello") server.ListenAndServeTLS("server-cert.pem", "server-key.pem") //private cert } diff --git a/examples/ssl/client/test.crt b/examples/ssl/proxy-server/test.crt similarity index 100% rename from examples/ssl/client/test.crt rename to examples/ssl/proxy-server/test.crt diff --git a/examples/ssl/client/test.key b/examples/ssl/proxy-server/test.key similarity index 100% rename from examples/ssl/client/test.key rename to examples/ssl/proxy-server/test.key diff --git a/examples/ssl/server/test.crt b/examples/ssl/server/test.crt deleted file mode 100644 index 2f045c8..0000000 --- a/examples/ssl/server/test.crt +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDMjCCAhoCCQCserdrP4jhazANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJh -dTERMA8GA1UECAwIVmljdG9yaWExEjAQBgNVBAcMCU1lbGJvdXJuZTEMMAoGA1UE -CgwDTkFCMQ0wCwYDVQQLDARlMzAxMB4XDTE1MDMyNDAxNTMzNVoXDTE2MDgwNTAx -NTMzNVowZTELMAkGA1UEBhMCYXUxETAPBgNVBAgMCFZpY3RvcmlhMRIwEAYDVQQH -DAlNZWxib3VybmUxDDAKBgNVBAoMA05BQjENMAsGA1UECwwEZTMwMTESMBAGA1UE -AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDsy -DjQ5I9qjY7o/LigcOa8pwQMNrE8nTGTbhtCfh+ahdIwRWRfaJR8C1iMzimv/jKvc -YG50uOFKpbpPJ3QiqZQqK91tyaRxfoFWt0dDc8mXbLwp4zG/FHlkgHGj0z6jC++e -gaVn4MIQ5I3s1+Ixms4PGPaxXq0NGoRwwh6RPbMNQvJ0PbdFTQbv4GOH5ZSEG/nu -8+Q6plKHx+GKxxQ/dwDq29vDGzao3Uah4FdKor2Rh5HcbW7RD/Il4Ubjo6IYVVbP -wiEy6ZyiMF6eY2dLWZteyh055sZJxVug4lAAwhM/yJ50d6mq3gA0/dWNoXCQGcyb -l8w7SYC66KhJ4jeCUQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCuD0EA9mfC1Njy -0XPGtHw+mTov7JWCex461dGYsJvjvkEaUpBoiwMuwqff06ojEBsIyZDOeC9MgwlC -eEE4LNspCzb8OcWto0KN6AAwmNfOHDJ43zuxmeeZ/IbRuixPfy0yxS4QcJ72kMFm -4137ScN2E6gwl1W6Y9AQXjavAk7JvkqWfF2rO3GSlSyG3Ir1+Fh0ycIpKFS6YrZz -JOyjjA2oxLXb9PmhAOqaCaww1cpb07oG13nzPXLmv0e/3pcd/FeAOMP0VeuWV4Sm -4c/VtXNARydxZzqVtw7fjuXoWJCh3opRK2JVLXLb2uBrb4E4MTY2vZ/sKv+FV6aj -cd27lAMQ ------END CERTIFICATE----- diff --git a/examples/ssl/server/test.key b/examples/ssl/server/test.key deleted file mode 100644 index bd5920d..0000000 --- a/examples/ssl/server/test.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAsDsyDjQ5I9qjY7o/LigcOa8pwQMNrE8nTGTbhtCfh+ahdIwR -WRfaJR8C1iMzimv/jKvcYG50uOFKpbpPJ3QiqZQqK91tyaRxfoFWt0dDc8mXbLwp -4zG/FHlkgHGj0z6jC++egaVn4MIQ5I3s1+Ixms4PGPaxXq0NGoRwwh6RPbMNQvJ0 -PbdFTQbv4GOH5ZSEG/nu8+Q6plKHx+GKxxQ/dwDq29vDGzao3Uah4FdKor2Rh5Hc -bW7RD/Il4Ubjo6IYVVbPwiEy6ZyiMF6eY2dLWZteyh055sZJxVug4lAAwhM/yJ50 -d6mq3gA0/dWNoXCQGcybl8w7SYC66KhJ4jeCUQIDAQABAoIBAQCop04FF+djL7dU -FoamZo50ifS8mW55a5rhWlhY0ckKpyX2wqFLkS8cfWwagL+vhiGff029H5gm4rys -k/tyd4tAnOIq7pNF+VEAT0ksx09/PPrkfcLcgdwq/O24moi8/mHNZ3la+2/JQhAm -msiB5h+w1ejO7C/cumIi8YJz1AsNbnhKTncg+7Rt1pKgmYJhluhw1hQjq0Fq7Lin -7joiSwczmNV+IIrm+vYVKF1IySMHPK0ok+AI11sDanodsPt82jGFawhQC1EBzbyr -KkMnnr97WdKiA2VwBVxmyYNCfNg6h64j0E/XWlVIUhJTP98WIRKBW1jszOjuFUDR -g+DA8gURAoGBAOjiJTGN6P2HfUNuzDcejza6jbJRFASf4Vy+2xRhq9c7Vg9gert5 -aoBRII50qFEIYDMiGKTrW0ZclvjRMaYG21d4DkspjtBwZyN0llEAqZ/ccijCHksi -eQwppsnJoU3KhQgXXNj9JVKHKrC9FnSENLr+605tgsI+G3tO+UOApK/tAoGBAMG5 -cvi4cHLX9FOTh6NHxtTuQnkYWjRod7E/OgGCsHSMkQcrJA+2LprHAUcNslS3mmaX -ZYhKRybAxUxJgP8r2GOM2eWZ7rqRecepX888u4bNXXcLkTPbIrQcjstl9OnifYIo -KZaEQEjE2Lr4iU7g2TMgCjo6xg41zsPKh9Xf/id1AoGAJ5mMyYhf/fx0CGtmvlir -8Zp3TcMLrF2jbKnnhue02Lx2PdciB4711Sv2ZULg/CZ4dTlvB1weATDtWxH3Z0vz -MERx6cX/SuJSJ21DwjJipZROtS+NBymte6v5eIaYrymoxV9zolIpbocdc0Az+Uwh -y0pdqNBmU7FL6wPazuepGWECgYEArqNHigB7HoyfrVgpxoBGNl4zfob9ipFClX6y -A/qUp/ywIQ47DA7oJI+SD0PBp618e0+wMBUF32GYexUoPOCByfyH0fvawkWyytNd -k6zkQLmRsGe1FlJODqBP+fyHtPCAxH0AQLgoW3FZD/RNk9YO94/CqIujT9wh8U76 -9UtgCqECgYEA0mYTItD5nFihqC275I7tQ3ThFboE7ydcWGT/9YVl82SVHCNitJkY -CI+D5/EtDd8IkNDRkYzp0Lg2nE29GSsnzMo/qxe6LQX8yeIqUtGOmrswuLlPQx0k -SpvHVxuflgslNcguSOwNcdCAV2alLKIZiX1BvawjGDWT9mZlAH9tS3k= ------END RSA PRIVATE KEY----- diff --git a/examples/ssl/server/test.pem b/examples/ssl/server/test.pem deleted file mode 100644 index 2f045c8..0000000 --- a/examples/ssl/server/test.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDMjCCAhoCCQCserdrP4jhazANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJh -dTERMA8GA1UECAwIVmljdG9yaWExEjAQBgNVBAcMCU1lbGJvdXJuZTEMMAoGA1UE -CgwDTkFCMQ0wCwYDVQQLDARlMzAxMB4XDTE1MDMyNDAxNTMzNVoXDTE2MDgwNTAx -NTMzNVowZTELMAkGA1UEBhMCYXUxETAPBgNVBAgMCFZpY3RvcmlhMRIwEAYDVQQH -DAlNZWxib3VybmUxDDAKBgNVBAoMA05BQjENMAsGA1UECwwEZTMwMTESMBAGA1UE -AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDsy -DjQ5I9qjY7o/LigcOa8pwQMNrE8nTGTbhtCfh+ahdIwRWRfaJR8C1iMzimv/jKvc -YG50uOFKpbpPJ3QiqZQqK91tyaRxfoFWt0dDc8mXbLwp4zG/FHlkgHGj0z6jC++e -gaVn4MIQ5I3s1+Ixms4PGPaxXq0NGoRwwh6RPbMNQvJ0PbdFTQbv4GOH5ZSEG/nu -8+Q6plKHx+GKxxQ/dwDq29vDGzao3Uah4FdKor2Rh5HcbW7RD/Il4Ubjo6IYVVbP -wiEy6ZyiMF6eY2dLWZteyh055sZJxVug4lAAwhM/yJ50d6mq3gA0/dWNoXCQGcyb -l8w7SYC66KhJ4jeCUQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCuD0EA9mfC1Njy -0XPGtHw+mTov7JWCex461dGYsJvjvkEaUpBoiwMuwqff06ojEBsIyZDOeC9MgwlC -eEE4LNspCzb8OcWto0KN6AAwmNfOHDJ43zuxmeeZ/IbRuixPfy0yxS4QcJ72kMFm -4137ScN2E6gwl1W6Y9AQXjavAk7JvkqWfF2rO3GSlSyG3Ir1+Fh0ycIpKFS6YrZz -JOyjjA2oxLXb9PmhAOqaCaww1cpb07oG13nzPXLmv0e/3pcd/FeAOMP0VeuWV4Sm -4c/VtXNARydxZzqVtw7fjuXoWJCh3opRK2JVLXLb2uBrb4E4MTY2vZ/sKv+FV6aj -cd27lAMQ ------END CERTIFICATE----- From d82297e53be9314581295a6dbe6b94aba0ca1042 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 22:38:45 +1100 Subject: [PATCH 11/18] feat(delay): update example delay config --- examples/config.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/config.yml b/examples/config.yml index de77a5c..e09cfe6 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -96,13 +96,14 @@ middleware: config: hex_output: false # Display output as Hex instead of a string - ## HTTP Response delay + ## HTTP/TCP Response delay ## ## Simple middleware that delays an HTTP response up to `delay` seconds ## - - name: http_delay + - name: delay config: - delay: 1 # Delay in seconds to apply to response + request_delay: 100 # Delay in ms to apply to request + response_delay: 500 # Delay in ms to apply to response ## Network Shaper - Layer 4 Tamperer. ## From 26dd36465bfc911254f96f468a9d2c6faf774891 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 22:39:08 +1100 Subject: [PATCH 12/18] feat(tcp): fix tcp middleware execution --- protocol/tcp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol/tcp.go b/protocol/tcp.go index 3eb2abf..0a032a9 100644 --- a/protocol/tcp.go +++ b/protocol/tcp.go @@ -160,10 +160,10 @@ func (p *proxy) pipe(src io.Reader, dst io.Writer) { for _, middleware := range p.middleware { log.Trace("TCP Proxy applying middleware %v", middleware) if islocal { - middleware.HandleEvent(muxy.EventPostDispatch, ctx) + middleware.HandleEvent(muxy.EventPreDispatch, ctx) log.Trace("TCP Proxy overwriting bytes sent to target: %s", ctx.Bytes) } else { - middleware.HandleEvent(muxy.EventPreDispatch, ctx) + middleware.HandleEvent(muxy.EventPostDispatch, ctx) log.Trace("TCP Proxy overwriting bytes sent to client: %s", ctx.Bytes) } b = ctx.Bytes From 444b3091be8e7abdc6fd4185dd8febb381811a08 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 22:39:33 +1100 Subject: [PATCH 13/18] chore(formatting): update log formatting in delay symptom --- symptom/http_delay.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/symptom/http_delay.go b/symptom/http_delay.go index 1037163..e92c21e 100644 --- a/symptom/http_delay.go +++ b/symptom/http_delay.go @@ -8,7 +8,7 @@ import ( "github.com/mefellows/plugo/plugo" ) -// HTTPDelaySymptom adds specified delays to HTTP requests +// HTTPDelaySymptom adds specified delays to requests Symptom // Update docs: these values should be in ms type HTTPDelaySymptom struct { RequestDelay int `required:"false" mapstructure:"request_delay"` @@ -27,12 +27,12 @@ func init() { // Setup sets up the delay plugin func (m HTTPDelaySymptom) Setup() { - log.Debug("HTTP Delay Setup()") + log.Debug("Delay Symptom - Setup()") } // Teardown shuts down the plugin func (m HTTPDelaySymptom) Teardown() { - log.Debug("HTTP Delay Teardown()") + log.Debug("Delay Symptom - Teardown()") } // HandleEvent takes a proxy event for the proxy to intercept and modify @@ -54,7 +54,7 @@ func (m HTTPDelaySymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) { // Muck injects chaos into the system func (m *HTTPDelaySymptom) Muck(ctx *muxy.Context, wait int) { delay := time.Duration(wait) * time.Millisecond - log.Debug("HTTP Delay Muck(), delaying for %v seconds\n", delay.Seconds()) + log.Debug("Delay Symptom - Muck(), delaying for %v seconds", delay.Seconds()) for { select { From 27a0687beb264ab4ef7c48b9760282b7424c19e0 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 22:39:58 +1100 Subject: [PATCH 14/18] feat(tcp): add ability to tamper with tcp messages #13 --- examples/tcp/README.md | 26 +++++++++++++++++ examples/tcp/tcp.yml | 62 +++++++++++++++++++++++++++++++++++++++++ symptom/tcp_tamperer.go | 32 +++++++++++++-------- 3 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 examples/tcp/README.md create mode 100644 examples/tcp/tcp.yml diff --git a/examples/tcp/README.md b/examples/tcp/README.md new file mode 100644 index 0000000..8b3678e --- /dev/null +++ b/examples/tcp/README.md @@ -0,0 +1,26 @@ +# TCP Muxy Example + +Start up three shells, one to listen on a TCP port: + +_NOTE_: Ensure that [netcat](http://nc110.sourceforge.net/) is installed first. +``` +nc -k -l 8000 +``` + +...and the other to run `muxy`: + +``` +muxy proxy --config tcp.yml +``` + +...and the other to send some messages to: + +``` +telnet localhost 8001` +``` + +In this final shell, put in a message such as "hello". In the other shell, +you should see the message being overridden with "wow, new request!". + +In that shell, type in "goodbye!". Back in the original shell, you should see +this message overridden with "wow, new response!". diff --git a/examples/tcp/tcp.yml b/examples/tcp/tcp.yml new file mode 100644 index 0000000..302b5eb --- /dev/null +++ b/examples/tcp/tcp.yml @@ -0,0 +1,62 @@ +## Test configuration name. Used for reporting. +name: TCP Tampering Example + +## Test Description. Used for reporting +description: Tamper with TCP messages + +## Specify log output level +## +## Log Levels supported: +## Trace (0), Debug (1), Info (2, Default), Warn (3), Error (4), Fatal (5) +loglevel: 1 + +## Configure a proxy that will handle your requests, and forward +## to proxied host. +## +## Currently supports `tcp_proxy` and `http_proxy`. +proxy: + + # Configures a TCP proxy + # + # NOTE: TLS is currently not supported + - name: tcp_proxy + config: + host: 0.0.0.0 # Local address to bind to and accept connections. May be an IP/hostname + port: 8001 # Local port to bind to + proxy_host: 0.0.0.0 # Proxy server port + proxy_port: 8000 # Proxied server port + nagles_algorithm: true # Use Nagles algorithm? + packet_size: 64 # Size of each contiguous network packet to proxy + +## Middleware +## +## Middleware are plugins that are given the opportunity to intervene +## before a request is dispatched to the proxied system (PRE_DISPATCH event) +## and afterwards (POST_DISPATCH event). They are given a reference to +## the current context (HTTP Context or []bytes) and can mutate them. +## +## Middleware are executed in the order specified in this config. +## +middleware: + + ## TCP Tamperer - Messes with Layer 5. + ## + ## Useful for messing with the TCP protocol + ## + - name: tcp_tamperer + config: + request: + body: "wow, new request!" # Override request body + randomize: true # Replaces input mesage with a random string + truncate: true # Removes last character from the request message + response: + body: "wow, new response!" # Override response body + randomize: true # Replaces response mesage with a random string + truncate: true # Removes last character from the response message + + ## Request Logger - use this to see what's going in/out of the Proxy. + ## + ## + - name: logger + config: + hex_output: false # Display output as Hex instead of a string diff --git a/symptom/tcp_tamperer.go b/symptom/tcp_tamperer.go index 528074d..4739a07 100644 --- a/symptom/tcp_tamperer.go +++ b/symptom/tcp_tamperer.go @@ -74,33 +74,41 @@ func (m *TCPTampererSymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) { // MuckRequest adds chaos to the request func (m *TCPTampererSymptom) MuckRequest(ctx *muxy.Context) { + if m.Request.Body != "" { + log.Debug("TCP Tamperer - replacing body [%s] with [%s]", ctx.Bytes, m.Request.Body) + ctx.Bytes = []byte(m.Request.Body) + } + if m.Request.Randomize { + random := randStringBytesMaskImprSrc(len(ctx.Bytes)) + log.Debug("TCP Tamperer - randomizing body [%s] with [%s]", ctx.Bytes, random) + ctx.Bytes = random + } if m.Request.Truncate { if len(ctx.Bytes) >= 2 { + log.Debug("TCP Tamperer - randomizing body [%s] with [%s]", ctx.Bytes, ctx.Bytes[:len(ctx.Bytes)-2]) ctx.Bytes = ctx.Bytes[:len(ctx.Bytes)-2] } } - if m.Request.Randomize { - ctx.Bytes = randStringBytesMaskImprSrc(len(ctx.Bytes)) - } - if m.Request.Body != "" { - ctx.Bytes = []byte(m.Request.Body) - } } // MuckResponse adds chaos to the response func (m *TCPTampererSymptom) MuckResponse(ctx *muxy.Context) { + if m.Response.Body != "" { + log.Debug("TCP Tamperer - replacing body [%s] with [%s]", ctx.Bytes, m.Request.Body) + ctx.Bytes = []byte(m.Response.Body) + } + if m.Response.Randomize { + random := randStringBytesMaskImprSrc(len(ctx.Bytes)) + log.Debug("TCP Tamperer - randomizing body [%s] with [%s]", ctx.Bytes, random) + ctx.Bytes = random + } if m.Response.Truncate { // TODO: why 2 and 3? if len(ctx.Bytes) >= 3 { + log.Debug("TCP Tamperer - randomizing body [%s] with [%s]", ctx.Bytes, ctx.Bytes[:len(ctx.Bytes)-3]) ctx.Bytes = ctx.Bytes[:len(ctx.Bytes)-3] } } - if m.Response.Randomize { - ctx.Bytes = randStringBytesMaskImprSrc(len(ctx.Bytes)) - } - if m.Response.Body != "" { - ctx.Bytes = []byte(m.Response.Body) - } } // Randomly mess with bytes in an array From 22db561fe9baeaf0fc655f30f40013e257ef7257 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 22:40:24 +1100 Subject: [PATCH 15/18] chore(formatting): update log formatting in network_shape symptom --- symptom/network_shape.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/symptom/network_shape.go b/symptom/network_shape.go index 3f54a00..b6da0a4 100644 --- a/symptom/network_shape.go +++ b/symptom/network_shape.go @@ -41,7 +41,7 @@ func init() { // Setup sets up the plugin func (s *ShittyNetworkSymptom) Setup() { - log.Debug("Setting up ShittyNetworkSymptom: Enabling firewall") + log.Debug("ShittyNetworkSymptom - Setup()") ports := parsePorts(strings.Join(s.TargetPorts, ",")) targetIPv4, targetIPv6 := parseAddrs(strings.Join(append(s.TargetIps, s.TargetIps6...), ",")) @@ -75,12 +75,12 @@ func (s ShittyNetworkSymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) // Muck is where the plugin can do any context-specific chaos func (s *ShittyNetworkSymptom) Muck(ctx *muxy.Context) { - log.Debug("ShittyNetworkSymptom Mucking...") + log.Debug("ShittyNetworkSymptom - Mucking...") } // Teardown shuts down the plugin func (s *ShittyNetworkSymptom) Teardown() { - log.Debug("Tearing down ShittyNetworkSymptom") + log.Debug("ShittyNetworkSymptom - Teardown()") s.config.Stop = true supressOutput(func() { throttler.Run(&s.config) @@ -124,7 +124,7 @@ func parseLoss(loss string) float64 { } l, err := strconv.ParseFloat(val, 64) if err != nil { - log.Fatal("Incorrectly specified packet loss:", loss) + log.Fatal("ShittyNetworkSymptom - Incorrectly specified packet loss:", loss) } return l } @@ -152,7 +152,7 @@ func parseAddrs(addrs string) ([]string, []string) { parsedIPv6 = append(parsedIPv6, net.String()) } } else { - log.Fatal("Incorrectly specified target IP or CIDR:", adr) + log.Fatal("ShittyNetworkSymptom - Incorrectly specified target IP or CIDR:", adr) } } } @@ -171,13 +171,13 @@ func parsePorts(ports string) []string { if validRange(prt) { parsed = append(parsed, prt) } else { - log.Fatal("Incorrectly specified port range:", prt) + log.Fatal("ShittyNetworkSymptom - Incorrectly specified port range:", prt) } } else { //Isn't a range, check if just a single port if validPort(prt) { parsed = append(parsed, prt) } else { - log.Fatal("Incorrectly specified port:", prt) + log.Fatal("ShittyNetworkSymptom - Incorrectly specified port:", prt) } } } @@ -237,7 +237,7 @@ func parseProtos(protos string) []string { p == "icmp" { parsed = append(parsed, p) } else { - log.Fatal("Incorrectly specified protocol:", p) + log.Fatal("ShittyNetworkSymptom - Incorrectly specified protocol:", p) } } } From 9326a1d4b377adf6b489d1e204eab6f741d31785 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 22:40:53 +1100 Subject: [PATCH 16/18] feat(http): ability to modify request path in http proxy --- symptom/http_tamperer.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/symptom/http_tamperer.go b/symptom/http_tamperer.go index 5300156..c26a20c 100644 --- a/symptom/http_tamperer.go +++ b/symptom/http_tamperer.go @@ -2,6 +2,7 @@ package symptom import ( "bytes" + "fmt" "io" "net/http" @@ -20,6 +21,7 @@ type RequestConfig struct { Headers map[string]string Cookies []http.Cookie Body string + Path string } // ResponseConfig contains details of the HTTP response to tamper with prior to @@ -88,6 +90,12 @@ func (m *HTTPTampererSymptom) HandleEvent(e muxy.ProxyEvent, ctx *muxy.Context) // MuckRequest adds chaos to the request func (m *HTTPTampererSymptom) MuckRequest(ctx *muxy.Context) { + // Path + if m.Request.Path != "" { + log.Debug("HTTP Tamperer Spoofing HTTP URI from [%s] to [%s]", ctx.Request.URL.Path, log.Colorize(log.BLUE, m.Request.Path)) + ctx.Request.URL.Path = m.Request.Path + } + // Body if m.Request.Body != "" { newreq, err := http.NewRequest(ctx.Request.Method, ctx.Request.URL.String(), bytes.NewBuffer([]byte(m.Request.Body))) @@ -95,25 +103,26 @@ func (m *HTTPTampererSymptom) MuckRequest(ctx *muxy.Context) { log.Error(err.Error()) } *ctx.Request = *newreq - log.Debug("HTTP Tamperer Spoofing HTTP Request Body With %s", log.Colorize(log.BLUE, m.Request.Body)) + log.Debug("HTTP Tamperer Spoofing HTTP Request Body With [%s]", log.Colorize(log.BLUE, m.Request.Body)) } // Set Cookies for _, c := range m.Request.Cookies { c.Expires = stringToDate(c.RawExpires) - log.Debug("HTTP Tamperer Spoofing Request Cookie %s => %s", log.Colorize(log.LIGHTMAGENTA, c.Name), c.String()) + log.Debug("HTTP Tamperer Spoofing Request Cookie [%s]=> %s]", log.Colorize(log.LIGHTMAGENTA, c.Name), c.String()) ctx.Request.Header.Add("Cookie", c.String()) } // Set Headers for k, v := range m.Request.Headers { key := strings.ToTitle(strings.Replace(k, "_", "-", -1)) - log.Debug("HTTP Tamperer Spoofing Request Header %s => %s", log.Colorize(log.LIGHTMAGENTA, key), v) + log.Debug("HTTP Tamperer Spoofing Request Header [%s => %s]", log.Colorize(log.LIGHTMAGENTA, key), v) ctx.Request.Header.Set(key, v) } // This Writes all headers, setting status code - so call this last if m.Request.Method != "" { + log.Debug("HTTP Tamperer Spoofing Request Method from [%s] to [%s]", ctx.Request.Method, log.Colorize(log.LIGHTMAGENTA, m.Request.Method)) ctx.Request.Method = m.Request.Method } } @@ -140,26 +149,27 @@ func (m *HTTPTampererSymptom) MuckResponse(ctx *muxy.Context) { ProtoMinor: ctx.Response.ProtoMinor, Body: cl, } - log.Debug("HTTP Tamperer Injecting HTTP Response Body with %s", log.Colorize(log.BLUE, m.Response.Body)) + log.Debug("HTTP Tamperer Injecting HTTP Response Body with [%s]", log.Colorize(log.BLUE, m.Response.Body)) *ctx.Response = *r } // Set Cookies for _, c := range m.Response.Cookies { c.Expires = stringToDate(c.RawExpires) - log.Debug("HTTP Tamperer Spoofing Response Cookie %s => %s", log.Colorize(log.LIGHTMAGENTA, c.Name), c.String()) + log.Debug("HTTP Tamperer Spoofing Response Cookie [%s => %s]", log.Colorize(log.LIGHTMAGENTA, c.Name), c.String()) ctx.Response.Header.Add("Set-Cookie", c.String()) } // Set Headers for k, v := range m.Response.Headers { key := strings.ToTitle(strings.Replace(k, "_", "-", -1)) - log.Debug("HTTP Tamperer Spoofing Response Header %s => %s", log.Colorize(log.LIGHTMAGENTA, key), v) + log.Debug("HTTP Tamperer Spoofing Response Header [%s => %s]", log.Colorize(log.LIGHTMAGENTA, key), v) ctx.Response.Header.Add(key, v) } // This Writes all headers, setting status code - so call this last if m.Response.Status != 0 { + log.Debug("HTTP Tamperer Spoofing Response Code From [%d] to [%s]", ctx.Response.StatusCode, log.Colorize(log.LIGHTMAGENTA, fmt.Sprintf("%d", m.Response.Status))) ctx.Response.StatusCode = m.Response.Status ctx.Response.Status = http.StatusText(m.Response.Status) } From 9d0bf1714b7d9d836218b2fd2b6d4a2dd67a64bc Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 23:09:04 +1100 Subject: [PATCH 17/18] feat(http): ability to easily override host header to http(s) proxy target --- symptom/http_tamperer.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/symptom/http_tamperer.go b/symptom/http_tamperer.go index c26a20c..604287f 100644 --- a/symptom/http_tamperer.go +++ b/symptom/http_tamperer.go @@ -22,6 +22,7 @@ type RequestConfig struct { Cookies []http.Cookie Body string Path string + Host string } // ResponseConfig contains details of the HTTP response to tamper with prior to @@ -96,6 +97,12 @@ func (m *HTTPTampererSymptom) MuckRequest(ctx *muxy.Context) { ctx.Request.URL.Path = m.Request.Path } + // Host + if m.Request.Host != "" { + log.Debug("HTTP Tamperer Spoofing HTTP Host from [%s] to [%s]", ctx.Request.URL.Host, log.Colorize(log.BLUE, m.Request.Host)) + ctx.Request.Host = m.Request.Host + } + // Body if m.Request.Body != "" { newreq, err := http.NewRequest(ctx.Request.Method, ctx.Request.URL.String(), bytes.NewBuffer([]byte(m.Request.Body))) From 084203b33fadbe252a01ff01920e3dc39661f36b Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Tue, 7 Mar 2017 23:09:45 +1100 Subject: [PATCH 18/18] chore(docs): Update examples, recent changes to HTTP proxy and include TCP Tamperer --- README.md | 104 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index adb6341..29e5448 100644 --- a/README.md +++ b/README.md @@ -11,25 +11,38 @@ Muxy is a proxy that _mucks_ with your system and application context, operating If you are building a distributed system, Muxy can help you test your resilience and fault tolerance patterns. ### Contents - - * [Features](#features) - * [Installation](#installation) - * [Using Muxy](#using-muxy) - * [5 Minute Quick Start](#5-minute-example) - * [Muxy Components](#proxies-and-middlewares) - * [Proxies](#proxies) - * [HTTP Proxy](#http-proxy) - * [TCP Proxy](#tcp-proxy) - * [Middleware](#middleware) - * [HTTP Delay](#http-delay) - * [HTTP Tamperer](#http-tamperer) - * [Network Shaper](#network-shaper) - * [Logger](#logger) - * [YAML Configuration Reference](#configuration-reference) - * [Examples](#examples) - * [Go Hystrix](#hystrix) - * [Using Docker](#docker) - * [Extending Muxy](#extending-muxy) + + +- [Introduction](#introduction) + - [Contents](#contents) +- [Features](#features) +- [Installation](#installation) + - [On Mac OSX using Homebrew](#on-mac-osx-using-homebrew) + - [Using Go Get](#using-go-get) +- [Using Muxy](#using-muxy) + - [5-minute example](#5-minute-example) + - [Muxy as part of a test suite](#muxy-as-part-of-a-test-suite) + - [Notes](#notes) +- [Proxies and Middlewares](#proxies-and-middlewares) + - [Proxies](#proxies) + - [HTTP Proxy](#http-proxy) + - [TCP Proxy](#tcp-proxy) + - [Middleware](#middleware) + - [Delay](#delay) + - [HTTP Tamperer](#http-tamperer) + - [Network Shaper](#network-shaper) + - [TCP Tamperer](#tcp-tamperer) + - [Logger](#logger) +- [Configuration Reference](#configuration-reference) +- [Examples](#examples) + - [Hystrix](#hystrix) +- [Usage with Docker](#usage-with-docker) +- [Extending Muxy](#extending-muxy) + - [Proxies](#proxies) + - [Middleware](#middleware) +- [Contributing](#contributing) + + ## Features @@ -82,11 +95,15 @@ Muxy is typically used in two ways: config: host: 0.0.0.0 port: 8181 - proxy_host: onegeek.com.au + proxy_host: www.onegeek.com.au proxy_port: 80 # Proxy plugins middleware: + - name: http_tamperer + config: + request: + host: "www.onegeek.com.au" # Message Delay request/response plugin - name: delay @@ -96,6 +113,7 @@ Muxy is typically used in two ways: # Log in/out messages - name: logger + ``` 1. Run Muxy with your config: `muxy proxy --config ./config.yml` 1. Make a request to www.onegeek.com via the proxy: `time curl -v -H"Host: www.onegeek.com.au" http://localhost:8181/`. Compare that with a request direct to the website: `time curl -v www.onegeek.com.au` - it should be approximately 5s faster. @@ -122,7 +140,7 @@ It is also recommended to run within a container/virtual machine to avoid uninte ### Proxies #### HTTP Proxy -Simple HTTP Proxy that starts up on a local IP/Hostname and Port. +Simple HTTP(s) Proxy that starts up on a local IP/Hostname and Port. Example configuration snippet: @@ -130,12 +148,27 @@ Example configuration snippet: proxy: - name: http_proxy config: + ## Proxy host details host: 0.0.0.0 protocol: http port: 8181 + + ## Proxy target details proxy_host: 0.0.0.0 proxy_port: 8282 proxy_protocol: https + + ## Certificate to present to Muxy clients (i.e. server certs) + proxy_ssl_key: proxy-server/test.key + proxy_ssl_cert: proxy-server/test.crt + + ## Certificate to present to Muxy proxy targets (i.e. client certs) + proxy_client_ssl_key: client-certs/cert-key.pem + proxy_client_ssl_cert: client-certs/cert.pem + proxy_client_ssl_ca: client-certs/ca.pem + + ## Enable this to proxy targets we don't trust + # insecure: true # allow insecure https ``` #### TCP Proxy @@ -161,9 +194,10 @@ proxy: Middleware have the ability to intervene upon receiving a request (Pre-Dispatch) or before sending the response back to the client (Post-Dispatch). In some cases, such as the Network Shaper, the effect is applied _before any request is made_ (e.g. if the local network device configuration is altered). -#### HTTP Delay +#### Delay -A basic middleware that simply adds a delay of `delay` seconds. +A basic middleware that simply adds a delay of `delay` milliseconds to the request +or response. Example configuration snippet: @@ -186,6 +220,8 @@ middleware: - name: http_tamperer config: request: + host: "somehost" # Override Host header that's sent to target + path: "/" # Override the request path method: "GET" # Override request method headers: x_my_request: "foo" # Override request header @@ -247,6 +283,26 @@ middleware: - "udp" - "icmp" ``` + +#### TCP Tamperer + +The TCP Tamperer is a Layer 5 tamperer, modifying the messages in and around TCP +sessions. Crudely, you can set the body of inbound and outbound TCP packets, truncate +the last character of messages or randomise the text over the wire. + +``` +- name: tcp_tamperer + config: + request: + body: "wow, new request!" # Override request body + randomize: true # Replaces input message with a random string + truncate: true # Removes last character from the request message + response: + body: "wow, new response!" # Override response body + randomize: true # Replaces response message with a random string + truncate: true # Removes last character from the response message +``` + #### Logger Log the in/out messages, optionally requesting the output to be hex encoded. @@ -271,7 +327,7 @@ Refer to the [example](/examples/config.yml) YAML file for a full reference. Using the [Hystrix Go](https://github.com/afex/hystrix-go) library, we use Muxy to trigger a circuit breaker and return a canned response, ensuring we don't have downtime. View the [example](examples/hystrix). -## Docker +## Usage with Docker Download the [Docker image](https://github.com/mefellows/docker-muxy) by running: