From 1e8139e8458fc9e2f1a9b22fa0efd02e9ead9bea Mon Sep 17 00:00:00 2001 From: Peter Kurfer Date: Wed, 30 Dec 2020 17:03:01 +0100 Subject: [PATCH] Add first draft of event stream --- Taskfile.yml | 7 +- api/{ => proto/internal/rpc}/endpoints.proto | 2 +- api/{ => proto/internal/rpc}/handlers.proto | 2 +- api/{ => proto/internal/rpc}/health.proto | 2 +- api/proto/pkg/audit/event_entity.proto | 61 +++++ api/proto/pkg/audit/http_details.proto | 32 +++ go.mod | 2 + go.sum | 51 ++-- pkg/audit/api.go | 28 +++ pkg/audit/event.go | 25 ++ pkg/audit/event_stream.go | 117 +++++++++ pkg/audit/event_stream_test.go | 235 +++++++++++++++++++ pkg/audit/http_details.go | 39 +++ pkg/audit/options.go | 54 +++++ pkg/audit/tls_details.go | 36 +++ pkg/logging/factory.go | 8 + pkg/logging/test_logger.go | 131 +++++++++++ 17 files changed, 808 insertions(+), 24 deletions(-) rename api/{ => proto/internal/rpc}/endpoints.proto (96%) rename api/{ => proto/internal/rpc}/handlers.proto (95%) rename api/{ => proto/internal/rpc}/health.proto (96%) create mode 100644 api/proto/pkg/audit/event_entity.proto create mode 100644 api/proto/pkg/audit/http_details.proto create mode 100644 pkg/audit/api.go create mode 100644 pkg/audit/event.go create mode 100644 pkg/audit/event_stream.go create mode 100644 pkg/audit/event_stream_test.go create mode 100644 pkg/audit/http_details.go create mode 100644 pkg/audit/options.go create mode 100644 pkg/audit/tls_details.go create mode 100644 pkg/logging/test_logger.go diff --git a/Taskfile.yml b/Taskfile.yml index 3e438ce..e58d684 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -5,7 +5,7 @@ vars: INETMOCK_PKG: gitlab.com/inetmock/inetmock/cmd/inetmock IMCTL_PKG: gitlab.com/inetmock/inetmock/cmd/imctl PROTO_FILES: - sh: find ./api/ -type f -name "*.proto" -printf "%f " + sh: find ./api/ -type f -name "*.proto" -printf "%p " env: GOOS: linux @@ -26,7 +26,7 @@ tasks: sources: - "**/*.proto" cmds: - - protoc --proto_path ./api/ --go_out=./internal/rpc --go_opt=paths=source_relative --go-grpc_out=./internal/rpc --go-grpc_opt=paths=source_relative {{ .PROTO_FILES }} + - protoc --proto_path ./api/proto/ --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative {{ .PROTO_FILES }} go-generate: sources: @@ -46,7 +46,8 @@ tasks: - generate cmds: - mkdir -p {{ .OUT_DIR }} - - go test -coverprofile={{ .OUT_DIR }}/cov-raw.out -covermode count -v ./... 2>&1 | go-junit-report > {{ .OUT_DIR }}/report.xml + - go test -coverprofile={{ .OUT_DIR }}/cov-raw.out -covermode count -v ./... 2>&1 | tee {{ .OUT_DIR }}/test_output + - cat {{ .OUT_DIR }}/test_output | go-junit-report -set-exit-code > {{ .OUT_DIR }}/report.xml - grep -v "generated" {{ .OUT_DIR }}/cov-raw.out > {{ .OUT_DIR }}/cov.out - gocover-cobertura < {{ .OUT_DIR }}/cov.out > {{ .OUT_DIR }}/coverage.xml - rm -f {{ .OUT_DIR }}/cov-raw.out diff --git a/api/endpoints.proto b/api/proto/internal/rpc/endpoints.proto similarity index 96% rename from api/endpoints.proto rename to api/proto/internal/rpc/endpoints.proto index 3981ff5..48d850b 100644 --- a/api/endpoints.proto +++ b/api/proto/internal/rpc/endpoints.proto @@ -5,7 +5,7 @@ option java_multiple_files = true; option java_package = "com.github.baez90.inetmock.rpc"; option java_outer_classname = "EndpointsProto"; -package inetmock; +package inetmock.rpc; service Endpoints { rpc GetEndpoints (GetEndpointsRequest) returns (GetEndpointsResponse) { diff --git a/api/handlers.proto b/api/proto/internal/rpc/handlers.proto similarity index 95% rename from api/handlers.proto rename to api/proto/internal/rpc/handlers.proto index 6440a99..44a3ff9 100644 --- a/api/handlers.proto +++ b/api/proto/internal/rpc/handlers.proto @@ -5,7 +5,7 @@ option java_multiple_files = true; option java_package = "com.github.baez90.inetmock.rpc"; option java_outer_classname = "HandlersProto"; -package inetmock; +package inetmock.rpc; service Handlers { rpc GetHandlers (GetHandlersRequest) returns (GetHandlersResponse) { diff --git a/api/health.proto b/api/proto/internal/rpc/health.proto similarity index 96% rename from api/health.proto rename to api/proto/internal/rpc/health.proto index 8c9b6b9..3ff1aee 100644 --- a/api/health.proto +++ b/api/proto/internal/rpc/health.proto @@ -5,7 +5,7 @@ option java_multiple_files = true; option java_package = "com.github.baez90.inetmock.rpc"; option java_outer_classname = "HealthProto"; -package inetmock; +package inetmock.rpc; service Health { rpc GetHealth (HealthRequest) returns (HealthResponse) { diff --git a/api/proto/pkg/audit/event_entity.proto b/api/proto/pkg/audit/event_entity.proto new file mode 100644 index 0000000..2939193 --- /dev/null +++ b/api/proto/pkg/audit/event_entity.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit"; +option java_outer_classname = "HandlerEventProto"; + +package inetmock.audit; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +enum TransportProtocol { + UNKNOWN_TRANSPORT = 0; + TCP = 1; + UDP = 2; +} + +enum AppProtocol { + UNKNOWN_APPLICATION = 0; + DNS = 1; + HTTP = 2; + HTTP_PROXY = 3; +} + +enum TLSVersion { + SSLv30 = 0; + TLS10 = 1; + TLS11 = 2; + TLS12 = 3; + TLS13 = 4; +} + +message TLSDetailsEntity { + TLSVersion version = 1; + string cipherSuite = 2; + string serverName = 3; +} + +message EventEntity { + int64 id = 1; + google.protobuf.Timestamp timestamp = 2; + TransportProtocol transport = 3; + AppProtocol application = 4; + + oneof sourceIP { + uint32 sourceIPv4 = 5; + uint64 sourceIPv6 = 6; + } + + oneof destinationIP { + uint32 destinationIPv4 = 7; + uint64 destinationIPv6 = 8; + } + + uint32 sourcePort = 9; + uint32 destinationPort = 10; + + TLSDetailsEntity tls = 11; + google.protobuf.Any protocolDetails = 12; +} \ No newline at end of file diff --git a/api/proto/pkg/audit/http_details.proto b/api/proto/pkg/audit/http_details.proto new file mode 100644 index 0000000..a771024 --- /dev/null +++ b/api/proto/pkg/audit/http_details.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit"; +option java_outer_classname = "HandlerEventProto"; + +package inetmock.audit; + +enum HTTPMethod { + GET = 0; + HEAD = 1; + POST = 2; + PUT = 3; + DELETE = 4; + CONNECT = 5; + OPTIONS = 6; + TRACE = 7; + PATCH = 8; +} + +message HTTPHeaderValue { + repeated string values = 1; +} + +message HTTPDetailsEntity { + HTTPMethod method = 1; + string host = 2; + string uri = 3; + string proto = 4; + map headers = 5; +} \ No newline at end of file diff --git a/go.mod b/go.mod index e17d974..993562b 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,11 @@ module gitlab.com/inetmock/inetmock go 1.15 require ( + github.com/bwmarrin/snowflake v0.3.0 github.com/golang/mock v1.4.4 github.com/golang/protobuf v1.4.2 github.com/google/uuid v1.1.2 + github.com/imdario/mergo v0.3.11 github.com/miekg/dns v1.1.31 github.com/olekukonko/tablewriter v0.0.4 github.com/prometheus/client_golang v1.7.1 diff --git a/go.sum b/go.sum index fec03c6..d1a0e24 100644 --- a/go.sum +++ b/go.sum @@ -30,13 +30,14 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= +github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -58,12 +59,13 @@ github.com/elazarl/goproxy/ext v0.0.0-20201021153353-00ad82a08272 h1:xOHQWEGsftk github.com/elazarl/goproxy/ext v0.0.0-20201021153353-00ad82a08272/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -83,7 +85,6 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -92,8 +93,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -108,8 +107,6 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -142,6 +139,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -154,6 +153,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -163,6 +163,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= +github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= @@ -182,6 +184,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks= +github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -193,9 +197,12 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -241,12 +248,18 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg= +github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -259,6 +272,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -272,9 +286,13 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -286,6 +304,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -327,8 +346,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -356,13 +373,12 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -420,8 +436,6 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgX google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d h1:HV9Z9qMhQEsdlvxNFELgQ11RkMzO3CMkjEySjCtuLes= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -429,12 +443,8 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1 h1:M8spwkmx0pHrPq+uMdl22w5CvJ/Y+oAJTIs9oGoCpOE= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -444,7 +454,6 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -458,6 +467,8 @@ gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153/go.mod h1:xzjpkye gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -466,6 +477,10 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/audit/api.go b/pkg/audit/api.go new file mode 100644 index 0000000..fa4ef7b --- /dev/null +++ b/pkg/audit/api.go @@ -0,0 +1,28 @@ +//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/audit/audit.mock.go -package=audit_mock + +package audit + +import ( + "errors" + "io" +) + +var ( + ErrSinkAlreadyRegistered = errors.New("sink with same name already registered") +) + +type Emitter interface { + Emit(ev Event) +} + +type Sink interface { + Name() string + OnSubscribe(evs <-chan Event) +} + +type EventStream interface { + io.Closer + Emitter + RegisterSink(s Sink) error + Sinks() []string +} diff --git a/pkg/audit/event.go b/pkg/audit/event.go new file mode 100644 index 0000000..e625734 --- /dev/null +++ b/pkg/audit/event.go @@ -0,0 +1,25 @@ +package audit + +import ( + "net" + "time" + + "google.golang.org/protobuf/proto" +) + +type EventDetails interface { + ProtoMessage() proto.Message +} + +type Event struct { + ID int64 + Timestamp time.Time + Transport TransportProtocol + Application AppProtocol + SourceIP net.IP + DestinationIP net.IP + SourcePort uint16 + DestinationPort uint16 + ProtocolDetails EventDetails + TLS *TLSDetails +} diff --git a/pkg/audit/event_stream.go b/pkg/audit/event_stream.go new file mode 100644 index 0000000..0019805 --- /dev/null +++ b/pkg/audit/event_stream.go @@ -0,0 +1,117 @@ +package audit + +import ( + "sync" + "time" + + "github.com/bwmarrin/snowflake" + "github.com/imdario/mergo" + "gitlab.com/inetmock/inetmock/pkg/logging" + "go.uber.org/zap" +) + +func init() { + snowflake.Epoch = time.Unix(0, 0).Unix() +} + +type eventStream struct { + logger logging.Logger + buffer chan *Event + sinks map[string]chan Event + lock sync.Locker + idGenerator *snowflake.Node + sinkBufferSize int + sinkConsumptionTimeout time.Duration +} + +func NewEventStream(logger logging.Logger, options ...EventStreamOption) (es EventStream, err error) { + cfg := newEventStreamCfg() + + for _, opt := range options { + opt(&cfg) + } + + var node *snowflake.Node + node, err = snowflake.NewNode(cfg.generatorIndex) + if err != nil { + return + } + + generatorIdx++ + underlying := &eventStream{ + logger: logger, + sinks: make(map[string]chan Event), + buffer: make(chan *Event, cfg.bufferSize), + sinkBufferSize: cfg.sinkBuffersize, + sinkConsumptionTimeout: cfg.sinkConsumptionTimeout, + idGenerator: node, + lock: &sync.Mutex{}, + } + + go underlying.distribute() + + es = underlying + + return +} + +func (e *eventStream) Emit(ev Event) { + defaultEvent := e.newDefaultEvent() + _ = mergo.Merge(defaultEvent, &ev) + select { + case e.buffer <- defaultEvent: + e.logger.Debug("pushed event to distribute loop") + default: + e.logger.Warn("buffer is full") + } +} + +func (e *eventStream) RegisterSink(s Sink) error { + name := s.Name() + + if _, exists := e.sinks[name]; exists { + return ErrSinkAlreadyRegistered + } + + downstream := make(chan Event, e.sinkBufferSize) + s.OnSubscribe(downstream) + e.sinks[name] = downstream + return nil +} + +func (e eventStream) Sinks() (sinks []string) { + for name := range e.sinks { + sinks = append(sinks, name) + } + return +} + +func (e *eventStream) Close() error { + close(e.buffer) + for _, downstream := range e.sinks { + close(downstream) + } + return nil +} + +func (e *eventStream) distribute() { + for ev := range e.buffer { + for name, s := range e.sinks { + e.logger.Debug("notify sink", zap.String("sink", name)) + select { + case s <- *ev: + e.logger.Debug("pushed event to sink channel") + case <-time.After(e.sinkConsumptionTimeout): + e.logger.Warn("sink consummation timed out") + } + } + } +} + +func (e *eventStream) newDefaultEvent() *Event { + id := e.idGenerator.Generate() + return &Event{ + ID: id.Int64(), + Timestamp: time.Now().UTC(), + } +} diff --git a/pkg/audit/event_stream_test.go b/pkg/audit/event_stream_test.go new file mode 100644 index 0000000..adf6c08 --- /dev/null +++ b/pkg/audit/event_stream_test.go @@ -0,0 +1,235 @@ +package audit_test + +import ( + "net" + "sync" + "testing" + "time" + + "gitlab.com/inetmock/inetmock/pkg/audit" + "gitlab.com/inetmock/inetmock/pkg/logging" +) + +var ( + defaultSink = &testSink{ + name: "test defaultSink", + } +) + +type testSink struct { + name string + consumer func(event audit.Event) +} + +func (t *testSink) Name() string { + return t.name +} + +func (t *testSink) OnSubscribe(evs <-chan audit.Event) { + go func() { + for ev := range evs { + if t.consumer != nil { + t.consumer(ev) + } + } + }() +} + +func wgMockSink(t testing.TB, wg *sync.WaitGroup) audit.Sink { + return &testSink{ + name: "WG mock sink", + consumer: func(event audit.Event) { + t.Logf("Got event = %v", event) + wg.Done() + }, + } +} + +func Test_eventStream_RegisterSink(t *testing.T) { + type args struct { + s audit.Sink + } + + tests := []struct { + name string + args args + setup func(e audit.EventStream) + wantErr bool + }{ + { + name: "Register test defaultSink", + args: args{ + s: defaultSink, + }, + wantErr: false, + }, + { + name: "Fail due to already registered defaultSink", + args: args{ + s: defaultSink, + }, + setup: func(e audit.EventStream) { + _ = e.RegisterSink(defaultSink) + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var err error + var e audit.EventStream + if e, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil { + t.Errorf("NewEventStream() error = %v", err) + } + + t.Cleanup(func() { + _ = e.Close() + }) + + if tt.setup != nil { + tt.setup(e) + } + + if err := e.RegisterSink(tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("RegisterSink() error = %v, wantErr %v", err, tt.wantErr) + } + + found := false + for _, s := range e.Sinks() { + if found = s == tt.args.s.Name(); found { + break + } + } + if !found { + t.Errorf("expected defaultSink name %s not found in registered sinks %v", tt.args.s.Name(), e.Sinks()) + } + }) + } +} + +func Test_eventStream_Emit(t *testing.T) { + type args struct { + evs []audit.Event + opts []audit.EventStreamOption + } + tests := []struct { + name string + args args + subscribe bool + }{ + { + name: "Expect to get a single event", + subscribe: true, + args: args{ + opts: []audit.EventStreamOption{audit.WithBufferSize(10)}, + evs: []audit.Event{ + { + Transport: audit.TransportProtocol_TCP, + Application: audit.AppProtocol_HTTP, + SourceIP: net.ParseIP("127.0.0.1"), + DestinationIP: net.ParseIP("127.0.0.1"), + SourcePort: 32344, + DestinationPort: 80, + }, + }, + }, + }, + { + name: "Expect to get multiple events", + subscribe: true, + args: args{ + opts: []audit.EventStreamOption{audit.WithBufferSize(10)}, + evs: []audit.Event{ + { + Transport: audit.TransportProtocol_TCP, + Application: audit.AppProtocol_HTTP, + SourceIP: net.ParseIP("127.0.0.1"), + DestinationIP: net.ParseIP("127.0.0.1"), + SourcePort: 32344, + DestinationPort: 80, + }, + { + Transport: audit.TransportProtocol_TCP, + Application: audit.AppProtocol_DNS, + SourceIP: net.ParseIP("::1"), + DestinationIP: net.ParseIP("::1"), + SourcePort: 32344, + DestinationPort: 80, + }, + }, + }, + }, + { + name: "Emit without subscribe sink", + args: args{ + opts: []audit.EventStreamOption{audit.WithBufferSize(0)}, + evs: []audit.Event{ + { + Transport: audit.TransportProtocol_TCP, + Application: audit.AppProtocol_HTTP, + SourceIP: net.ParseIP("127.0.0.1"), + DestinationIP: net.ParseIP("127.0.0.1"), + SourcePort: 32344, + DestinationPort: 80, + }, + }, + }, + subscribe: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var err error + var e audit.EventStream + if e, err = audit.NewEventStream(logging.CreateTestLogger(t), tt.args.opts...); err != nil { + t.Errorf("NewEventStream() error = %v", err) + } + + t.Cleanup(func() { + _ = e.Close() + }) + + emittedWaitGroup := &sync.WaitGroup{} + receivedWaitGroup := &sync.WaitGroup{} + + emittedWaitGroup.Add(len(tt.args.evs)) + + if tt.subscribe { + receivedWaitGroup.Add(len(tt.args.evs)) + if err := e.RegisterSink(wgMockSink(t, receivedWaitGroup)); err != nil { + t.Errorf("RegisterSink() error = %v", err) + } + } + + go func(evs []audit.Event, wg *sync.WaitGroup) { + for _, ev := range evs { + e.Emit(ev) + wg.Done() + } + }(tt.args.evs, emittedWaitGroup) + + select { + case <-waitGroupDone(emittedWaitGroup): + case <-time.After(100 * time.Millisecond): + t.Errorf("not all events emitted in time") + } + + select { + case <-waitGroupDone(receivedWaitGroup): + case <-time.After(5 * time.Second): + t.Errorf("did not get all expected events in time") + } + }) + } +} + +func waitGroupDone(wg *sync.WaitGroup) <-chan struct{} { + done := make(chan struct{}) + + go func(wg *sync.WaitGroup) { + wg.Wait() + close(done) + }(wg) + + return done +} diff --git a/pkg/audit/http_details.go b/pkg/audit/http_details.go new file mode 100644 index 0000000..dc5b096 --- /dev/null +++ b/pkg/audit/http_details.go @@ -0,0 +1,39 @@ +package audit + +import ( + "net/http" + "strings" + + "google.golang.org/protobuf/proto" +) + +type HTTPDetails struct { + Method string + Host string + URI string + Proto string + Headers http.Header +} + +func (d HTTPDetails) ProtoMessage() proto.Message { + var method = HTTPMethod_GET + if methodValue, known := HTTPMethod_value[strings.ToUpper(d.Method)]; known { + method = HTTPMethod(methodValue) + } + + headers := make(map[string]*HTTPHeaderValue) + + for k, v := range d.Headers { + headers[k] = &HTTPHeaderValue{ + Values: v, + } + } + + return &HTTPDetailsEntity{ + Method: method, + Host: d.Host, + Uri: d.URI, + Proto: d.Proto, + Headers: headers, + } +} diff --git a/pkg/audit/options.go b/pkg/audit/options.go new file mode 100644 index 0000000..24ee746 --- /dev/null +++ b/pkg/audit/options.go @@ -0,0 +1,54 @@ +package audit + +import "time" + +const ( + defaultEventStreamBufferSize = 100 + defaultSinkBufferSize = 0 + defaultSinkConsumptionTimeout = 50 * time.Millisecond +) + +var ( + generatorIdx int64 = 1 + WithBufferSize = func(bufferSize int) EventStreamOption { + return func(cfg *eventStreamCfg) { + cfg.bufferSize = bufferSize + } + } + WithGeneratorIndex = func(generatorIndex int64) EventStreamOption { + return func(cfg *eventStreamCfg) { + cfg.generatorIndex = generatorIndex + } + } + WithSinkBufferSize = func(bufferSize int) EventStreamOption { + return func(cfg *eventStreamCfg) { + cfg.sinkBuffersize = bufferSize + } + } + WithSinkConsumptionTimeout = func(timeout time.Duration) EventStreamOption { + return func(cfg *eventStreamCfg) { + cfg.sinkConsumptionTimeout = timeout + } + } +) + +type EventStreamOption func(cfg *eventStreamCfg) + +type eventStreamCfg struct { + bufferSize int + sinkBuffersize int + generatorIndex int64 + sinkConsumptionTimeout time.Duration +} + +func newEventStreamCfg() eventStreamCfg { + cfg := eventStreamCfg{ + generatorIndex: generatorIdx, + sinkBuffersize: defaultSinkBufferSize, + bufferSize: defaultEventStreamBufferSize, + sinkConsumptionTimeout: defaultSinkConsumptionTimeout, + } + generatorIdx++ + + return cfg +} diff --git a/pkg/audit/tls_details.go b/pkg/audit/tls_details.go new file mode 100644 index 0000000..fc9ae75 --- /dev/null +++ b/pkg/audit/tls_details.go @@ -0,0 +1,36 @@ +package audit + +import ( + "crypto/tls" + + "google.golang.org/protobuf/proto" +) + +type TLSDetails struct { + Version uint16 + CipherSuite uint16 + ServerName string +} + +func (d TLSDetails) ProtoMessage() proto.Message { + var version TLSVersion + + switch d.Version { + case tls.VersionTLS10: + version = TLSVersion_TLS10 + case tls.VersionTLS11: + version = TLSVersion_TLS11 + case tls.VersionTLS12: + version = TLSVersion_TLS12 + case tls.VersionTLS13: + version = TLSVersion_TLS13 + default: + version = TLSVersion_SSLv30 + } + + return &TLSDetailsEntity{ + Version: version, + CipherSuite: tls.CipherSuiteName(d.CipherSuite), + ServerName: d.ServerName, + } +} diff --git a/pkg/logging/factory.go b/pkg/logging/factory.go index 727d55b..961ee55 100644 --- a/pkg/logging/factory.go +++ b/pkg/logging/factory.go @@ -2,6 +2,7 @@ package logging import ( "strings" + "testing" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -48,6 +49,13 @@ func CreateLogger() (Logger, error) { } } +func CreateTestLogger(tb testing.TB) Logger { + return &testLogger{ + tb: tb, + encoder: zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()), + } +} + func MustCreateLogger() Logger { if logger, err := CreateLogger(); err != nil { panic(err) diff --git a/pkg/logging/test_logger.go b/pkg/logging/test_logger.go new file mode 100644 index 0000000..3ae9a93 --- /dev/null +++ b/pkg/logging/test_logger.go @@ -0,0 +1,131 @@ +package logging + +import ( + "runtime" + "runtime/debug" + "testing" + "time" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type testLogger struct { + name string + fields []zap.Field + tb testing.TB + encoder zapcore.Encoder +} + +func (t testLogger) Named(s string) Logger { + return testLogger{ + name: s, + tb: t.tb, + fields: t.fields, + } +} + +func (t testLogger) With(fields ...zap.Field) Logger { + return &testLogger{ + name: t.name, + fields: append(t.fields, fields...), + tb: t.tb, + } +} + +func (t testLogger) Debug(msg string, fields ...zap.Field) { + t.tb.Helper() + buf, err := t.encoder.EncodeEntry(zapcore.Entry{ + Level: zapcore.DebugLevel, + Time: time.Now(), + LoggerName: t.name, + Message: msg, + Caller: zapcore.NewEntryCaller(runtime.Caller(2)), + }, append(t.fields, fields...)) + + if err == nil { + t.tb.Log(buf.String()) + } +} + +func (t testLogger) Info(msg string, fields ...zap.Field) { + t.tb.Helper() + buf, err := t.encoder.EncodeEntry(zapcore.Entry{ + Level: zapcore.InfoLevel, + Time: time.Now(), + LoggerName: t.name, + Message: msg, + Caller: zapcore.NewEntryCaller(runtime.Caller(2)), + }, append(t.fields, fields...)) + + if err == nil { + t.tb.Log(buf.String()) + } +} + +func (t testLogger) Warn(msg string, fields ...zap.Field) { + t.tb.Helper() + buf, err := t.encoder.EncodeEntry(zapcore.Entry{ + Level: zapcore.WarnLevel, + Time: time.Now(), + LoggerName: t.name, + Message: msg, + Caller: zapcore.NewEntryCaller(runtime.Caller(2)), + }, append(t.fields, fields...)) + + if err == nil { + t.tb.Log(buf.String()) + } +} + +func (t testLogger) Error(msg string, fields ...zap.Field) { + t.tb.Helper() + buf, err := t.encoder.EncodeEntry(zapcore.Entry{ + Level: zapcore.ErrorLevel, + Time: time.Now(), + LoggerName: t.name, + Message: msg, + Caller: zapcore.NewEntryCaller(runtime.Caller(2)), + Stack: string(debug.Stack()), + }, append(t.fields, fields...)) + + if err == nil { + t.tb.Log(buf.String()) + } +} + +func (t testLogger) Panic(msg string, fields ...zap.Field) { + t.tb.Helper() + buf, err := t.encoder.EncodeEntry(zapcore.Entry{ + Level: zapcore.PanicLevel, + Time: time.Now(), + LoggerName: t.name, + Message: msg, + Caller: zapcore.NewEntryCaller(runtime.Caller(2)), + Stack: string(debug.Stack()), + }, append(t.fields, fields...)) + + if err == nil { + t.tb.Error(buf.String()) + } +} + +func (t testLogger) Fatal(msg string, fields ...zap.Field) { + t.tb.Helper() + buf, err := t.encoder.EncodeEntry(zapcore.Entry{ + Level: zapcore.FatalLevel, + Time: time.Now(), + LoggerName: t.name, + Message: msg, + Caller: zapcore.NewEntryCaller(runtime.Caller(2)), + Stack: string(debug.Stack()), + }, append(t.fields, fields...)) + + if err == nil { + t.tb.Error(buf.String()) + } +} + +func (t testLogger) Sync() error { + return nil +}