torizon_audit 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/torizon_audit.rb +147 -0
  3. metadata +43 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 500ba44874ca4f8acf49a1b7f16d861353cf049be7be4b0518689a722d4277ed
4
+ data.tar.gz: dee0a4946b52cb837bc847bd87d91d6baf0b13672726400e6917891939ddef92
5
+ SHA512:
6
+ metadata.gz: fb17aabf9a1ef454d77df1afdd2a5f75254fd910b54862a063a05322192af17ff05f9dfc3b192ec728f04559385ba060d2537a4390df8ab0398a47f9c751863f
7
+ data.tar.gz: 9b8b2345dedd9f1a0f3d7140c16a9bfa46b8c6aee3885fc1217ea642253b65cf356d099d9d4391d54225ef7198b9d9976fd3d5bd68f25f5a16fc1d0930f7a242
@@ -0,0 +1,147 @@
1
+ require "bundler/setup"
2
+ require "optparse"
3
+ require "optparse/time"
4
+ require "json"
5
+ require "elasticsearch"
6
+
7
+
8
+ class Query
9
+ def self.timeFormat(t)
10
+ return t.strftime("%Y-%m-%dT%0k:%0M:%0SZ")
11
+ end
12
+
13
+ def self.matchQuery(key, value, analyzer="standard", operator="OR")
14
+ return {
15
+ "match" => {
16
+ key => {
17
+ "query" => value,
18
+ "analyzer" => analyzer,
19
+ "operator" => operator
20
+ }
21
+ }
22
+ }
23
+ end
24
+
25
+ def initialize(maxNumLogs = 10000)
26
+ @query = {
27
+ "from" => 0,
28
+ "size" => maxNumLogs,
29
+ "query" => {
30
+ "bool" => {
31
+ "filter" => [
32
+ {
33
+ "range" => {
34
+ "@timestamp" => {
35
+ "gte" => "2023-03-13T00:00:00.00Z",
36
+ "lte" => Query.timeFormat(Time.now),
37
+ "format" => "strict_date_optional_time"
38
+ }
39
+ }
40
+ },
41
+ ]
42
+ }
43
+ }
44
+ }
45
+ end
46
+ def timeRange(from, to)
47
+ @query["query"]["bool"]["filter"].find{|h| h.key?("range")}["range"]["@timestamp"]["gte"] = from
48
+ @query["query"]["bool"]["filter"].find{|h| h.key?("range")}["range"]["@timestamp"]["lte"] = to
49
+ end
50
+
51
+ def httpMethod(method)
52
+ @query["query"]["bool"]["filter"].append(Query.matchQuery("request_method", method))
53
+ end
54
+
55
+ def appName(name)
56
+ @query["query"]["bool"]["filter"].append(Query.matchQuery("kubernetes.labels.app", name))
57
+ end
58
+
59
+ def uri(path)
60
+ @query["query"]["bool"]["filter"].append(Query.matchQuery("uri", path, "standard", "AND"))
61
+ end
62
+
63
+ def originNamespace(namespace)
64
+ @query["query"]["bool"]["filter"].append(Query.matchQuery("origin_namespace", namespace))
65
+ end
66
+
67
+ def toJson()
68
+ return @query.to_json
69
+ end
70
+
71
+ def to_s
72
+ @query.to_s
73
+ end
74
+ end
75
+
76
+ # TODO:
77
+ # Keys rotated offline (tuf-reposerver, director, keyserver)
78
+ # PUT, POST, DELETE
79
+
80
+ class Audit
81
+ @@actionsMap = {
82
+ "api/v1/user_repo/targets$" => {"PUT" => "Upload signed targets.json"},
83
+ "api/v1/user_repo/targets/.*" => {"PUT" => "Create Tuf Target", "POST" => "Create TUF target", "PATCH" => "Modify TUF target", "DELETE" => "Delete TUF target"},
84
+ "api/v1/user_repo/trusted-delegations/.*/remote/refresh" => {"PUT" => "Refresh remote delegation"},
85
+ "api/v1/user_repo/trusted-delegations/.*" => {"PUT" => "Create or modify trusted-delegations"},
86
+ "api/v1/user_repo/delegations/.*" => {"PUT" => "Create or modify delegation metadata"},
87
+ "api/v1/multi_target_updates" => {"POST" => "Create multi-target-update"},
88
+ "api/v1/assignments" => {"POST" => "Create device assignment"},
89
+ "api/v1/admin/repo/offline-updates/.*" => {"POST" => "Create or modify lockbox", "PUT" => "Create or modify lockbox"},
90
+ "api/accounts/users/clients$" => {"POST" => "Create third-party API client"},
91
+ "api/accounts/users/clients/.*" => {"DELETE" => "Revoke third-party API client"},
92
+ "api/accounts/users/.*/keys/credentials.zip" => {"GET" => "Download credentials.zip"},
93
+ "api/accounts/organizations/share/.*/users$" => {"POST" => "Share organization with user"},
94
+ "api/accounts/organizations/share/.*/users/.*" => {"DELETE" => "Revoke organization access for user", "PUT" => "Change organization access level for user"},
95
+ "api/accounts/devices$" => {"POST" => "Provision device"},
96
+ "api/v1/device_groups$" => {"POST" => "Create device fleet"},
97
+ "api/v1/device_groups/.*/devices/.*" => {"POST" => "Add device to fleet", "DELETE" => "Remove device from fleet"},
98
+ "api/v1/device_groups/.*/rename" => {"PUT" => "Rename device fleet"},
99
+ "api/v1/device_groups/.*" => {"DELETE" => "Delete device fleet"},
100
+ "api/v1/devices/.*" => {"DELETE" => "Delete device"},
101
+ }
102
+
103
+ def initialize(client)
104
+ @client = client
105
+ end
106
+
107
+ def findAction(method, uri)
108
+ @@actionsMap.each {|uriPattern, methodActions|
109
+ if uri.match(uriPattern)
110
+ if methodActions.key?(method)
111
+ return methodActions[method]
112
+ end
113
+ end
114
+ }
115
+ return "Unknown Action"
116
+ end
117
+
118
+ def userActions(from, to, namespace, dropUnknownActions = true)
119
+ query = Query.new()
120
+ query.timeRange(Query.timeFormat(from), Query.timeFormat(to))
121
+ query.appName("ota-kong-kong")
122
+ query.originNamespace(namespace)
123
+
124
+ response = @client.search index: 'logstash*', body: query.toJson
125
+
126
+ actions = response["hits"]["hits"].map{ |obj|
127
+ src = obj["_source"]
128
+ {
129
+ "method" => src.key?("request_method") ? src["request_method"] : "",
130
+ "path" => src.key?("uri") ? src["uri"]: "",
131
+ "at" => src.key?("@timestamp") ? src["@timestamp"]: "",
132
+ "namespace" => src.key?("namespace") ? src["namespace"]: "",
133
+ "origin_namespace" => src.key?("origin_namespace") ? src["origin_namespace"]: "",
134
+ "status_code" => src.key?("status") ? src["status"]: "",
135
+ "ip" => src.key?("client_addr") ? src["client_addr"]: "",
136
+ "query" => src.key?("query_string") ? src["query_string"]: "",
137
+ "log_id" => obj.key?("_id") ? obj["_id"]: "",
138
+ "action" => self.findAction(src.key?("request_method") ? src["request_method"] : "", src.key?("uri") ? src["uri"]: "")
139
+ }
140
+ }
141
+ if dropUnknownActions
142
+ return actions.select{|req| req["action"] != "Unknown Action"}
143
+ end
144
+ return actions
145
+ end
146
+
147
+ end
metadata ADDED
@@ -0,0 +1,43 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: torizon_audit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ben Clouser
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-03-15 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Fetch user actions from torizon platform
14
+ email: ben.clouser@toradex.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/torizon_audit.rb
20
+ homepage:
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubygems_version: 3.3.25
40
+ signing_key:
41
+ specification_version: 4
42
+ summary: Fetch user actions from torizon platform
43
+ test_files: []