Module: KubeAutoAnalyzer
- Defined in:
- lib/kube_auto_analyzer.rb,
lib/kube_auto_analyzer/version.rb,
lib/kube_auto_analyzer/reporting.rb,
lib/kube_auto_analyzer/utility/network.rb,
lib/kube_auto_analyzer/vuln_checks/kubelet.rb,
lib/kube_auto_analyzer/api_checks/master_node.rb,
lib/kube_auto_analyzer/vuln_checks/api_server.rb,
lib/kube_auto_analyzer/agent_checks/file_checks.rb,
lib/kube_auto_analyzer/vuln_checks/service_token.rb,
lib/kube_auto_analyzer/agent_checks/process_checks.rb
Constant Summary collapse
- VERSION =
"0.0.3"
Instance Attribute Summary collapse
-
#execute ⇒ Object
Returns the value of attribute execute.
Class Method Summary collapse
- .check_files ⇒ Object
- .check_kubelet_process ⇒ Object
- .execute(commmand_line_opts) ⇒ Object
- .html_report ⇒ Object
- .is_port_open?(ip, port) ⇒ Boolean
- .report ⇒ Object
- .test_api_server ⇒ Object
- .test_controller_manager ⇒ Object
- .test_etcd ⇒ Object
- .test_insecure_api_external ⇒ Object
-
.test_insecure_api_internal ⇒ Object
This is somewhat awkward placement.
- .test_scheduler ⇒ Object
-
.test_service_token_internal ⇒ Object
This is somewhat awkward placement.
- .test_unauth_kubelet_external ⇒ Object
-
.test_unauth_kubelet_internal ⇒ Object
This is somewhat awkward placement.
Instance Attribute Details
#execute ⇒ Object
Returns the value of attribute execute.
2 3 4 |
# File 'lib/kube_auto_analyzer.rb', line 2 def execute @execute end |
Class Method Details
.check_files ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/kube_auto_analyzer/agent_checks/file_checks.rb', line 3 def self.check_files require 'json' @log.debug ("entering File check") target = .target_server @results[target]['worker_files'] = Hash.new #Run on any nodes that aren't NoSchedule #Doesn't necessarily mean worker nodes, but a reasonable facsimile for now. nodes = Array.new @client.get_nodes.each do |node| unless node.spec.taints.to_s =~ /NoSchedule/ nodes << node end end nodes.each do |nod| node_hostname = nod..labels['kubernetes.io/hostname'] container_name = "kaa" + node_hostname pod = Kubeclient::Resource.new pod. = {} pod..name = container_name pod..namespace = "default" pod.spec = {} pod.spec.restartPolicy = "Never" pod.spec.containers = {} pod.spec.containers = [{name: "kubeautoanalyzerfiletest", image: "raesene/kaa-agent:latest"}] pod.spec.volumes = [{name: 'etck8s', hostPath: {path: '/etc'}}] pod.spec.containers[0].volumeMounts = [{mountPath: '/etc', name: 'etck8s'}] pod.spec.containers[0].args = ["/file-checker.rb","/etc/kubernetes"] pod.spec.nodeselector = {} begin pod.spec.nodeselector['kubernetes.io/hostname'] = node_hostname @client.create_pod(pod) begin sleep(5) until @client.get_pod(container_name,"default")['status']['containerStatuses'][0]['state']['terminated']['reason'] == "Completed" rescue retry end files = JSON.parse(@client.get_pod_log(container_name,"default")) @results[target]['worker_files'][node_hostname] = files ensure @client.delete_pod(container_name,"default") end end @log.debug("Finished Worker File Check") end |
.check_kubelet_process ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/kube_auto_analyzer/agent_checks/process_checks.rb', line 3 def self.check_kubelet_process @log.debug("Entering Process Checks") target = .target_server @results[target]['kubelet_checks'] = Hash.new @results[target]['node_evidence'] = Hash.new nodes = Array.new @client.get_nodes.each do |node| unless node.spec.taints.to_s =~ /NoSchedule/ nodes << node end end nodes.each do |nod| node_hostname = nod..labels['kubernetes.io/hostname'] container_name = "kaa" + node_hostname pod = Kubeclient::Resource.new pod. = {} pod..name = container_name pod..namespace = "default" pod.spec = {} pod.spec.restartPolicy = "Never" pod.spec.containers = {} pod.spec.containers = [{name: "kaakubelettest", image: "raesene/kaa-agent:latest"}] pod.spec.containers[0].args = ["/process-checker.rb"] pod.spec.hostPID = true pod.spec.nodeselector = {} pod.spec.nodeselector['kubernetes.io/hostname'] = node_hostname begin @client.create_pod(pod) begin sleep(5) until @client.get_pod(container_name,"default")['status']['containerStatuses'][0]['state']['terminated']['reason'] == "Completed" rescue retry end processes = JSON.parse(@client.get_pod_log(container_name,"default")) #If we didn't get more than one process, we're probably not reading the host ones #So either it's a bug or we don't have rights if processes.length < 2 @log.debug("Process Check failed didn't get the node process list") @results[target]['kubelet_checks'][node_hostname]['Kubelet Not Found'] = "Error - couldn't see host process list" @client.delete_pod(container_name,"default") return end #puts processes kubelet_proc = '' processes.each do |proc| if proc =~ /kubelet/ kubelet_proc = proc end end @results[target]['kubelet_checks'][node_hostname] = Hash.new unless kubelet_proc.length > 1 @results[target]['kubelet_checks'][node_hostname]['Kubelet Not Found'] = "Error" @log.debug(processes) @client.delete_pod(container_name,"default") return end @results[target]['node_evidence'][node_hostname] = Hash.new @results[target]['node_evidence'][node_hostname]['kubelet'] = kubelet_proc #Checks unless kubelet_proc =~ /--allow-privileged=false/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.1 - Ensure that the --allow-privileged argument is set to false'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.1 - Ensure that the --allow-privileged argument is set to false'] = "Pass" end unless kubelet_proc =~ /--anonymous-auth=false/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.2 - Ensure that the --anonymous-auth argument is set to false'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.2 - Ensure that the --anonymous-auth argument is set to false'] = "Pass" end if kubelet_proc =~ /--authorization-mode\S*AlwaysAllow/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.3 - Ensure that the --authorization-mode argument is not set to AlwaysAllow'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.3 - Ensure that the --authorization-mode argument is not set to AlwaysAllow'] = "Pass" end unless kubelet_proc =~ /--client-ca-file/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.4 - Ensure that the --client-ca-file argument is set as appropriate'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.4 - Ensure that the --client-ca-file argument is set as appropriate'] = "Pass" end unless kubelet_proc =~ /--read-only-port=0/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.5 - Ensure that the --read-only-port argument is set to 0'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.5 - Ensure that the --read-only-port argument is set to 0'] = "Pass" end if kubelet_proc =~ /--streaming-connection-idle-timeout=0/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.6 - Ensure that the --streaming-connection-idle-timeout argument is not set to 0'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.6 - Ensure that the --streaming-connection-idle-timeout argument is not set to 0'] = "Pass" end unless kubelet_proc =~ /--protect-kernel-defaults=true/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.7 - Ensure that the --protect-kernel-defaults argument is set to true'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.7 - Ensure that the --protect-kernel-defaults argument is set to true'] = "Pass" end if kubelet_proc =~ /--make-iptables-util-chains=false/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.8 - Ensure that the --make-iptables-util-chains argument is set to true'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.8 - Ensure that the --make-iptables-util-chains argument is set to true'] = "Pass" end unless kubelet_proc =~ /--keep-terminated-pod-volumes=false/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.9 - that the --keep-terminated-pod-volumes argument is set to false'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.9 - Ensure that the --keep-terminated-pod-volumes argument is set to false'] = "Pass" end if kubelet_proc =~ /--hostname-override/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.10 - Ensure that the --hostname-override argument is not set'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.10 - Ensure that the --hostname-override argument is not set'] = "Pass" end unless kubelet_proc =~ /--event-qps=0/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.11 - Ensure that the --event-qps argument is set to 0'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.11 - Ensure that the --event-qps argument is set to 0'] = "Pass" end unless (kubelet_proc =~ /--tls-cert-file/) && (kubelet_proc =~ /--tls-private-key-file/) @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.12 - Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.12 - Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate'] = "Pass" end unless kubelet_proc =~ /--cadvisor-port=0/ @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.13 - Ensure that the --cadvisor-port argument is set to 0'] = "Fail" else @results[target]['kubelet_checks'][node_hostname]['CIS 2.1.13 - Ensure that the --cadvisor-port argument is set to 0'] = "Pass" end #Need an ensure block here to make sure that the pod is deleted after its run ensure @client.delete_pod(container_name,"default") end end end |
.execute(commmand_line_opts) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/kube_auto_analyzer.rb', line 14 def self.execute(commmand_line_opts) = commmand_line_opts require 'logger' begin require 'kubeclient' rescue LoadError puts "You need to install kubeclient for this, try 'gem install kubeclient'" exit end @base_dir = .report_directory if !File.exists?(@base_dir) Dir.mkdirs(@base_dir) end @log = Logger.new(@base_dir + '/kube-analyzer-log.txt') @log.level = Logger::DEBUG @log.debug("Log created at " + Time.now.to_s) @log.debug("Target API Server is " + .target_server) @report_file_name = @base_dir + '/' + .report_file #Remove the Text report for now as we're not using this option #@report_file = File.new(@report_file_name + '.txt','w+') @html_report_file = File.new(@report_file_name + '.html','w+') @log.debug("New Report File created #{@report_file_name}") @results = Hash.new #TODO: Expose this as an option rather than hard-code to off unless .config_file = { verify_ssl: OpenSSL::SSL::VERIFY_NONE} #TODO: Need to setup the other authentication options if .token.length > 1 = { bearer_token: .token} elsif .token_file.length > 1 = { bearer_token_file: .token_file} else #Not sure this will actually work for no auth. needed, try and ooold cluster to check = {} end @results[.target_server] = Hash.new @client = Kubeclient::Client.new .target_server, 'v1', auth_options: , ssl_options: else begin config = Kubeclient::Config.read(.config_file) rescue Errno::ENOENT puts "Config File could not be read, check the path?" exit end @client = Kubeclient::Client.new( config.context.api_endpoint, config.context.api_version, { ssl_options: config.context., auth_options: config.context. } ) #We didn't specify the target on the command line so lets get it from the config file .target_server = config.context.api_endpoint @log.debug("target is " + .target_server) @results[config.context.api_endpoint] = Hash.new end #Test response begin @client.get_pods.to_s rescue puts "Check of API connection failed." puts "try using kubectl with the same connection details" puts "to see what's going wrong." exit end test_api_server test_scheduler test_controller_manager test_etcd test_unauth_kubelet_external test_insecure_api_external if .agent_checks test_unauth_kubelet_internal test_insecure_api_internal test_service_token_internal check_files check_kubelet_process end html_report end |
.html_report ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/kube_auto_analyzer/reporting.rb', line 53 def self.html_report logo_path = File.join(__dir__, "data-logo.b64") logo = File.open(logo_path).read @log.debug("Starting HTML Report") @html_report_file << ' <!DOCTYPE html> <head> <title> Kubernetes Auto Analyzer Report</title> <meta charset="utf-8"> <style> body { font: normal 14px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; color: #C41230; background: #FFFFFF; } #kubernetes-analyzer { font-weight: bold; font-size: 48px; color: #C41230; } .master-node, .worker-node { background: #F5F5F5; border: 1px solid black; padding-left: 6px; } #api-server-results { font-weight: italic; font-size: 36px; color: #C41230; } table, th, td { border-collapse: collapse; border: 1px solid black; } th { font: bold 11px; color: #C41230; background: #999999; letter-spacing: 2px; text-transform: uppercase; text-align: left; padding: 6px 6px 6px 12px; } td { background: #FFFFFF; padding: 6px 6px 6px 12px; color: #333333; } </style> </head> <body> ' @html_report_file.puts '<img width="100" height="100" align="right"' + " src=#{logo} />" @html_report_file.puts "<h1>Kubernetes Auto Analyzer</h1>" @html_report_file.puts "<br><b>Server Reviewed : </b> #{@options.target_server}" @html_report_file.puts '<br><br><div class="master-node"><h2>Master Node Results</h2><br>' @html_report_file.puts "<h2>API Server</h2>" @html_report_file.puts "<table><thead><tr><th>Check</th><th>result</th></tr></thead>" @results[.target_server]['api_server'].each do |test, result| if result == "Fail" result = '<span style="color:red;">Fail</span>' elsif result == "Pass" result = '<span style="color:green;">Pass</span>' end @html_report_file.puts "<tr><td>#{test}</td><td>#{result}</td></tr>" end @html_report_file.puts "</table>" @html_report_file.puts "<br><br>" @html_report_file.puts "<br><br><h2>Scheduler</h2>" @html_report_file.puts "<table><thead><tr><th>Check</th><th>result</th></tr></thead>" @results[.target_server]['scheduler'].each do |test, result| if result == "Fail" result = '<span style="color:red;">Fail</span>' elsif result == "Pass" result = '<span style="color:green;">Pass</span>' end @html_report_file.puts "<tr><td>#{test}</td><td>#{result}</td></tr>" end @html_report_file.puts "</table>" @html_report_file.puts "<br><br>" @html_report_file.puts "<br><br><h2>Controller Manager</h2>" @html_report_file.puts "<table><thead><tr><th>Check</th><th>result</th></tr></thead>" @results[.target_server]['controller_manager'].each do |test, result| if result == "Fail" result = '<span style="color:red;">Fail</span>' elsif result == "Pass" result = '<span style="color:green;">Pass</span>' end @html_report_file.puts "<tr><td>#{test}</td><td>#{result}</td></tr>" end @html_report_file.puts "</table>" @html_report_file.puts "<br><br>" @html_report_file.puts "<br><br><h2>etcd</h2>" @html_report_file.puts "<table><thead><tr><th>Check</th><th>result</th></tr></thead>" @results[.target_server]['etcd'].each do |test, result| if result == "Fail" result = '<span style="color:red;">Fail</span>' elsif result == "Pass" result = '<span style="color:green;">Pass</span>' end @html_report_file.puts "<tr><td>#{test}</td><td>#{result}</td></tr>" end @html_report_file.puts "</table>" @html_report_file.puts "<br><br><h2>Evidence</h2><br>" @html_report_file.puts "<table><thead><tr><th>Area</th><th>Output</th></tr></thead>" @results[.target_server]['evidence'].each do |area, output| @html_report_file.puts "<tr><td>#{area}</td><td>#{output}</td></tr>" end #Close the master Node Div @html_report_file.puts "</table></div>" if .agent_checks @html_report_file.puts '<br><br><div class="worker-node"><h2>Worker Node Results</h2>' @results[.target_server]['kubelet_checks'].each do |node, results| @html_report_file.puts "<br><b>#{node} Kubelet Checks</b>" @html_report_file.puts "<table><thead><tr><th>Check</th><th>result</th></tr></thead>" results.each do |test, result| if result == "Fail" result = '<span style="color:red;">Fail</span>' elsif result == "Pass" result = '<span style="color:green;">Pass</span>' end @html_report_file.puts "<tr><td>#{test}</td><td>#{result}</td></tr>" end @html_report_file.puts "</table>" end @html_report_file.puts "<br><br><h2>Evidence</h2><br>" @html_report_file.puts "<table><thead><tr><th>Host</th><th>Area</th><th>Output</th></tr></thead>" @results[.target_server]['node_evidence'].each do |node, evidence| evidence.each do |area, data| @html_report_file.puts "<tr><td>#{node}</td><td>#{area}</td><td>#{data}</td></tr>" end end @html_report_file.puts "</table>" end #Close the Worker Node Div @html_report_file.puts '</div>' if .agent_checks @html_report_file.puts '<br><h2>File Permissions</h2>' @results[.target_server]['worker_files'].each do |node, results| @html_report_file.puts "<br><b>#{node}</b><br>" @html_report_file.puts "<table><thead><tr><th>file</th><th>user</th><th>group</th><th>permissions</th></thead>" results.each do |file| @html_report_file.puts "<tr><td>#{file[0]}</td><td>#{file[1]}</td><td>#{file[2]}</td><td>#{file[3]}</td></tr>" end @html_report_file.puts "</table>" end end @html_report_file.puts '<br><h2>Vulnerability Checks</h2>' @html_report_file.puts '<br><h3>External Unauthenticated Access to the Kubelet</h3>' @html_report_file.puts "<table><thead><tr><th>Node IP Address</th><th>Result</th></thead>" @results[.target_server]['vulns']['unauth_kubelet'].each do |node, result| unless (result =~ /Forbidden/ || result =~ /Not Open/) output = "Vulnerable" else output = result end @html_report_file.puts "<tr><td>#{node}</td><td>#{output}</td></tr>" end @html_report_file.puts "</table>" if .agent_checks @html_report_file.puts '<br><h3>Internal Unauthenticated Access to the Kubelet</h3>' @html_report_file.puts "<table><thead><tr><th>Node IP Address</th><th>Result</th></thead>" @results[.target_server]['vulns']['internal_kubelet'].each do |node, result| unless (result =~ /Forbidden/ || result =~ /Not Open/) output = "Vulnerable" else output = result end @html_report_file.puts "<tr><td>#{node}</td><td>#{output}</td></tr>" end @html_report_file.puts "</table>" end @html_report_file.puts '<br><h3>External Insecure API Port Exposed</h3>' @html_report_file.puts "<table><thead><tr><th>Node IP Address</th><th>Result</th></thead>" @results[.target_server]['vulns']['insecure_api_external'].each do |node, result| unless (result =~ /Forbidden/ || result =~ /Not Open/) output = "Vulnerable" else output = result end @html_report_file.puts "<tr><td>#{node}</td><td>#{output}</td></tr>" end @html_report_file.puts "</table>" if .agent_checks @html_report_file.puts '<br><h3>Internal Insecure API Port Exposed</h3>' @html_report_file.puts "<table><thead><tr><th>Node IP Address</th><th>Result</th></thead>" @results[.target_server]['vulns']['insecure_api_internal'].each do |node, result| unless (result =~ /Forbidden/ || result =~ /Not Open/) output = "Vulnerable" else output = result end @html_report_file.puts "<tr><td>#{node}</td><td>#{output}</td></tr>" end @html_report_file.puts "</table>" end if .agent_checks @html_report_file.puts '<br><h3>Default Service Token In Use</h3>' @html_report_file.puts "<table><thead><tr><th>API endpoint</th><th>Result</th></thead>" @results[.target_server]['vulns']['service_token'].each do |node, result| unless (result =~ /Forbidden/ || result =~ /Not Open/) output = "Vulnerable" else output = result end @html_report_file.puts "<tr><td>#{node}</td><td>#{output}</td></tr>" end @html_report_file.puts "</table>" end @html_report_file.puts "<br><br><h2>Vulnerability Evidence</h2><br>" @html_report_file.puts "<table><thead><tr><th>Vulnerability</th><th>Host</th><th>Output</th></tr></thead>" @results[.target_server]['vulns']['unauth_kubelet'].each do |node, result| @html_report_file.puts "<tr><td>External Unauthenticated Kubelet Access</td><td>#{node}</td><td>#{result}</td></tr>" end if .agent_checks @results[.target_server]['vulns']['internal_kubelet'].each do |node, result| @html_report_file.puts "<tr><td>Internal Unauthenticated Kubelet Access</td><td>#{node}</td><td>#{result}</td></tr>" end end @results[.target_server]['vulns']['insecure_api_external'].each do |node, result| @html_report_file.puts "<tr><td>External Insecure API Server Access</td><td>#{node}</td><td>#{result}</td></tr>" end if .agent_checks @results[.target_server]['vulns']['insecure_api_internal'].each do |node, result| @html_report_file.puts "<tr><td>Internal Insecure API Server Access</td><td>#{node}</td><td>#{result}</td></tr>" end end if .agent_checks @results[.target_server]['vulns']['service_token'].each do |node, result| @html_report_file.puts "<tr><td>Default Service Token In Use</td><td>#{node}</td><td>#{result}</td></tr>" end end @html_report_file.puts "</table>" #Closing the report off @html_report_file.puts '</body></html>' end |
.is_port_open?(ip, port) ⇒ Boolean
4 5 6 7 8 9 10 11 12 13 |
# File 'lib/kube_auto_analyzer/utility/network.rb', line 4 def self.is_port_open?(ip, port) begin Socket.tcp(ip, port, connect_timeout: 2) rescue Errno::ECONNREFUSED return false rescue Errno::ETIMEDOUT return false end true end |
.report ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/kube_auto_analyzer/reporting.rb', line 2 def self.report @log.debug("Starting Report") @report_file.puts "Kubernetes Analyzer" @report_file.puts "===================\n\n" @report_file.puts "**Server Reviewed** : #{@options.target_server}" @report_file.puts "\n\nAPI Server Results" @report_file.puts "----------------------\n\n" @results[.target_server]['api_server'].each do |test, result| @report_file.puts '* ' + test + ' - **' + result + '**' end @report_file.puts "\n\nScheduler Results" @report_file.puts "----------------------\n\n" @results[.target_server]['scheduler'].each do |test, result| @report_file.puts '* ' + test + ' - **' + result + '**' end @report_file.puts "\n\nController Manager Results" @report_file.puts "----------------------\n\n" @results[.target_server]['controller_manager'].each do |test, result| @report_file.puts '* ' + test + ' - **' + result + '**' end @report_file.puts "\n\netcd Results" @report_file.puts "----------------------\n\n" @results[.target_server]['etcd'].each do |test, result| @report_file.puts '* ' + test + ' - **' + result + '**' end if .agent_file_checks @report_file.puts "\n\nWorker Nodes File Permissions" @report_file.puts "----------------------\n\n" @log.debug("Class is #{@results[@options.target_server]['worker_files'].class}") @results[.target_server]['worker_files'].each do |node, results| @report_file.puts "\n\n#{node}\n" results.each do |file| @report_file.puts file.join(', ') end end end @report_file.puts "\n\nEvidence" @report_file.puts "---------------\n\n" @report_file.puts ' ' + @results[.target_server]['evidence']['API Server'].to_s @report_file.puts "---------------\n\n" @report_file.puts ' ' + @results[.target_server]['evidence']['Scheduler'].to_s @report_file.puts "---------------\n\n" @report_file.puts ' ' + @results[.target_server]['evidence']['Controller Manager'].to_s @report_file.puts "---------------\n\n" @report_file.puts ' ' + @results[.target_server]['evidence']['etcd'].to_s @report_file.close end |
.test_api_server ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/kube_auto_analyzer/api_checks/master_node.rb', line 3 def self.test_api_server @log.debug("Entering the test API Server Method") target = .target_server @log.debug("target is #{target}") @results[target]['api_server'] = Hash.new @results[target]['evidence'] = Hash.new pods = @client.get_pods pods.each do |pod| #Ok this is a bit naive as a means of hitting the API server but hey it's a start if pod['metadata']['name'] =~ /kube-apiserver/ @api_server = pod end end unless @api_server @results[target]['api_server']['API Server Pod Not Found'] = "Error" return end api_server_command_line = @api_server['spec']['containers'][0]['command'] #Check for Allow Privileged unless api_server_command_line.index{|line| line =~ /--allow-privileged=false/} @results[target]['api_server']['CIS 1.1.1 - Ensure that the --allow-privileged argument is set to false'] = "Fail" else @results[target]['api_server']['CIS 1.1.1 - Ensure that the --allow-privileged argument is set to false'] = "Pass" end #Check for Anonymous Auth unless api_server_command_line.index{|line| line =~ /--anonymous-auth=false/} @results[target]['api_server']['CIS 1.1.2 - Ensure that the --anonymous-auth argument is set to false'] = "Fail" else @results[target]['api_server']['CIS 1.1.2 - Ensure that the --anonymous-auth argument is set to false'] = "Pass" end #Check for Basic Auth if api_server_command_line.index{|line| line =~ /--basic-auth-file/} @results[target]['api_server']['CIS 1.1.3 - Ensure that the --basic-auth-file argument is not set'] = "Fail" else @results[target]['api_server']['CIS 1.1.3 - Ensure that the --basic-auth-file argument is not set'] = "Pass" end #Check for Insecure Allow Any Token if api_server_command_line.index{|line| line =~ /--insecure-allow-any-token/} @results[target]['api_server']['CIS 1.1.4 - Ensure that the --insecure-allow-any-token argument is not set'] = "Fail" else @results[target]['api_server']['CIS 1.1.4 - Ensure that the --insecure-allow-any-token argument is not set'] = "Pass" end #Check to confirm that Kubelet HTTPS isn't set to false if api_server_command_line.index{|line| line =~ /--kubelet-https=false/} @results[target]['api_server']['CIS 1.1.5 - Ensure that the --kubelet-https argument is set to true'] = "Fail" else @results[target]['api_server']['CIS 1.1.5 - Ensure that the --kubelet-https argument is set to true'] = "Pass" end #Check for Insecure Bind Address if api_server_command_line.index{|line| line =~ /--insecure-bind-address/} @results[target]['api_server']['CIS 1.1.6 - Ensure that the --insecure-bind-address argument is not set'] = "Fail" else @results[target]['api_server']['CIS 1.1.6 - Ensure that the --insecure-bind-address argument is not set'] = "Pass" end #Check for Insecure Bind port unless api_server_command_line.index{|line| line =~ /--insecure-port=0/} @results[target]['api_server']['CIS 1.1.7 - Ensure that the --insecure-port argument is set to 0'] = "Fail" else @results[target]['api_server']['CIS 1.1.7 - Ensure that the --insecure-port argument is set to 0'] = "Pass" end #Check Secure Port isn't set to 0 if api_server_command_line.index{|line| line =~ /--secure-port=0/} @results[target]['api_server']['CIS 1.1.8 - Ensure that the --secure-port argument is not set to 0'] = "Fail" else @results[target]['api_server']['CIS 1.1.8 - Ensure that the --secure-port argument is not set to 0'] = "Pass" end # unless api_server_command_line.index{|line| line =~ /--profiling=false/} @results[target]['api_server']['CIS 1.1.9 - Ensure that the --profiling argument is set to false'] = "Fail" else @results[target]['api_server']['CIS 1.1.9 - Ensure that the --profiling argument is set to false'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--repair-malformed-updates/} @results[target]['api_server']['CIS 1.1.10 - Ensure that the --repair-malformed-updates argument is set to false'] = "Fail" else @results[target]['api_server']['CIS 1.1.10 - Ensure that the --repair-malformed-updates argument is set to false'] = "Pass" end if api_server_command_line.index{|line| line =~ /--admission-control\S*AlwaysAdmit/} @results[target]['api_server']['CIS 1.1.11 - Ensure that the admission control policy is not set to AlwaysAdmit'] = "Fail" else @results[target]['api_server']['CIS 1.1.11 - Ensure that the admission control policy is not set to AlwaysAdmit'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--admission-control\S*AlwaysPullImages/} @results[target]['api_server']['CIS 1.1.12 - Ensure that the admission control policy is set to AlwaysPullImages'] = "Fail" else @results[target]['api_server']['CIS 1.1.12 - Ensure that the admission control policy is set to AlwaysPullImages'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--admission-control\S*DenyEscalatingExec/} @results[target]['api_server']['CIS 1.1.13 - Ensure that the admission control policy is set to DenyEscalatingExec'] = "Fail" else @results[target]['api_server']['CIS 1.1.13 - Ensure that the admission control policy is set to DenyEscalatingExec'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--admission-control\S*SecurityContextDeny/} @results[target]['api_server']['CIS 1.1.14 - Ensure that the admission control policy is set to SecurityContextDeny'] = "Fail" else @results[target]['api_server']['CIS 1.1.14 - Ensure that the admission control policy is set to SecurityContextDeny'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--admission-control\S*NamespaceLifecycle/} @results[target]['api_server']['CIS 1.1.15 - Ensure that the admission control policy is set to NamespaceLifecycle'] = "Fail" else @results[target]['api_server']['CIS 1.1.15 - Ensure that the admission control policy is set to NamespaceLifecycle'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--audit-log-path/} @results[target]['api_server']['CIS 1.1.16 - Ensure that the --audit-log-path argument is set as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.16 - Ensure that the --audit-log-path argument is set as appropriate'] = "Pass" end #TODO: This check needs to do something with the number of days but for now lets just check whether it's present. unless api_server_command_line.index{|line| line =~ /--audit-log-maxage/} @results[target]['api_server']['CIS 1.1.17 - Ensure that the --audit-log-maxage argument is set to 30 or as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.17 - Ensure that the --audit-log-maxage argument is set to 30 or as appropriate'] = "Pass" end #TODO: This check needs to do something with the number of backups but for now lets just check whether it's present. unless api_server_command_line.index{|line| line =~ /--audit-log-maxbackup/} @results[target]['api_server']['CIS 1.1.18 - Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.18 - Ensure that the --audit-log-maxbackup argument is set to 10 or as appropriate'] = "Pass" end #TODO: This check needs to do something with the size of backups but for now lets just check whether it's present. unless api_server_command_line.index{|line| line =~ /--audit-log-maxsize/} @results[target]['api_server']['CIS 1.1.19 - Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.19 - Ensure that the --audit-log-maxsize argument is set to 100 or as appropriate'] = "Pass" end if api_server_command_line.index{|line| line =~ /--authorization-mode\S*AlwaysAllow/} @results[target]['api_server']['CIS 1.1.20 - Ensure that the --authorization-mode argument is not set to AlwaysAllow'] = "Fail" else @results[target]['api_server']['CIS 1.1.20 - Ensure that the --authorization-mode argument is not set to AlwaysAllow'] = "Pass" end if api_server_command_line.index{|line| line =~ /--token-auth-file/} @results[target]['api_server']['CIS 1.1.21 - Ensure that the --token-auth-file argument is not set'] = "Fail" else @results[target]['api_server']['CIS 1.1.21 - Ensure that the --token-auth-file argument is not set'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--kubelet-certificate-authority/} @results[target]['api_server']['CIS 1.1.22 - Ensure that the --kubelet-certificate-authority argument is set as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.22 - Ensure that the --kubelet-certificate-authority argument is set as appropriate'] = "Pass" end unless (api_server_command_line.index{|line| line =~ /--kubelet-client-certificate/} && api_server_command_line.index{|line| line =~ /--kubelet-client-key/}) @results[target]['api_server']['CIS 1.1.23 - Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.23 - Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--service-account-lookup=true/} @results[target]['api_server']['CIS 1.1.24 - Ensure that the --service-account-lookup argument is set to true'] = "Fail" else @results[target]['api_server']['CIS 1.1.24 - Ensure that the --service-account-lookup argument is set to true'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--admission-control\S*PodSecurityPolicy/} @results[target]['api_server']['CIS 1.1.25 - Ensure that the admission control policy is set to PodSecurityPolicy'] = "Fail" else @results[target]['api_server']['CIS 1.1.25 - Ensure that the admission control policy is set to PodSecurityPolicy'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--service-account-key-file/} @results[target]['api_server']['CIS 1.1.26 - Ensure that the --service-account-key-file argument is set as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.26 - Ensure that the --service-account-key-file argument is set as appropriate'] = "Pass" end unless (api_server_command_line.index{|line| line =~ /--etcd-certfile/} && api_server_command_line.index{|line| line =~ /--etcd-keyfile/}) @results[target]['api_server']['CIS 1.1.27 - Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.27 - Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--admission-control\S*ServiceAccount/} @results[target]['api_server']['CIS 1.1.28 - Ensure that the admission control policy is set to ServiceAccount'] = "Fail" else @results[target]['api_server']['CIS 1.1.28 - Ensure that the admission control policy is set to ServiceAccount'] = "Pass" end unless (api_server_command_line.index{|line| line =~ /--tls-cert-file/} && api_server_command_line.index{|line| line =~ /--tls-private-key-file/}) @results[target]['api_server']['CIS 1.1.29 - Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.29 - Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--client-ca-file/} @results[target]['api_server']['CIS 1.1.30 - Ensure that the --client-ca-file argument is set as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.30 - Ensure that the --client-ca-file argument is set as appropriate'] = "Pass" end unless api_server_command_line.index{|line| line =~ /--etcd-cafile/} @results[target]['api_server']['CIS 1.1.31 - Ensure that the --etcd-cafile argument is set as appropriate'] = "Fail" else @results[target]['api_server']['CIS 1.1.31 - Ensure that the --etcd-cafile argument is set as appropriate'] = "Pass" end @results[target]['evidence']['API Server'] = api_server_command_line end |
.test_controller_manager ⇒ Object
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/kube_auto_analyzer/api_checks/master_node.rb', line 251 def self.test_controller_manager target = .target_server @results[target]['controller_manager'] = Hash.new pods = @client.get_pods pods.each do |pod| #Ok this is a bit naive as a means of hitting the API server but hey it's a start if pod['metadata']['name'] =~ /kube-controller-manager/ @controller_manager = pod end end unless @controller_manager @results[target]['controller_manager']['Controller Manager Pod Not Found'] = "Error" return end controller_manager_command_line = @controller_manager['spec']['containers'][0]['command'] unless controller_manager_command_line.index{|line| line =~ /--terminated-pod-gc-threshold/} @results[target]['controller_manager']['CIS 1.3.1 - Ensure that the --terminated-pod-gc-threshold argument is set as appropriate'] = "Fail" else @results[target]['controller_manager']['CIS 1.3.1 - Ensure that the --terminated-pod-gc-threshold argument is set as appropriate'] = "Pass" end unless controller_manager_command_line.index{|line| line =~ /--profiling=false/} @results[target]['controller_manager']['CIS 1.3.2 - Ensure that the --profiling argument is set to false'] = "Fail" else @results[target]['controller_manager']['CIS 1.3.2 - Ensure that the --profiling argument is set to false'] = "Pass" end if controller_manager_command_line.index{|line| line =~ /--insecure-experimental-approve-all-kubelet-csrs-for-group/} @results[target]['controller_manager']['CIS 1.3.3 - Ensure that the --insecure-experimental-approve-all-kubelet-csrs-for-group argument is not set'] = "Fail" else @results[target]['controller_manager']['CIS 1.3.3 - Ensure that the --insecure-experimental-approve-all-kubelet-csrs-for-group argument is not set'] = "Pass" end unless controller_manager_command_line.index{|line| line =~ /--use-service-account-credentials=true/} @results[target]['controller_manager']['CIS 1.3.4 - Ensure that the --use-service-account-credentials argument is set to true'] = "Fail" else @results[target]['controller_manager']['CIS 1.3.4 - Ensure that the --use-service-account-credentials argument is set to true'] = "Pass" end unless controller_manager_command_line.index{|line| line =~ /--service-account-private-key-file/} @results[target]['controller_manager']['CIS 1.3.5 - Ensure that the --service-account-private-key-file argument is set as appropriate'] = "Fail" else @results[target]['controller_manager']['CIS 1.3.5 - Ensure that the --service-account-private-key-file argument is set as appropriate'] = "Pass" end unless controller_manager_command_line.index{|line| line =~ /--root-ca-file/} @results[target]['controller_manager']['CIS 1.3.6 - Ensure that the --root-ca-file argument is set as appropriate'] = "Fail" else @results[target]['controller_manager']['CIS 1.3.6 - Ensure that the --root-ca-file argument is set as appropriate'] = "Pass" end @results[target]['evidence']['Controller Manager'] = controller_manager_command_line end |
.test_etcd ⇒ Object
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/kube_auto_analyzer/api_checks/master_node.rb', line 310 def self.test_etcd target = .target_server @results[target]['etcd'] = Hash.new pods = @client.get_pods pods.each do |pod| #Ok this is a bit naive as a means of hitting the API server but hey it's a start if pod['metadata']['name'] =~ /etcd/ @etcd = pod end end unless @etcd @results[target]['etcd']['etcd Pod Not Found'] = "Error" return end etcd_command_line = @etcd['spec']['containers'][0]['command'] unless (etcd_command_line.index{|line| line =~ /--cert-file/} && etcd_command_line.index{|line| line =~ /--key-file/}) @results[target]['etcd']['CIS 1.5.1 - Ensure that the --cert-file and --key-file arguments are set as appropriate'] = "Fail" else @results[target]['etcd']['CIS 1.5.1 - Ensure that the --cert-file and --key-file arguments are set as appropriate'] = "Pass" end unless etcd_command_line.index{|line| line =~ /--client-cert-auth=true/} @results[target]['etcd']['CIS 1.5.2 - Ensure that the --client-cert-auth argument is set to true'] = "Fail" else @results[target]['etcd']['CIS 1.5.2 - Ensure that the --client-cert-auth argument is set to true'] = "Pass" end if etcd_command_line.index{|line| line =~ /--auto-tls argument=true/} @results[target]['etcd']['CIS 1.5.3 - Ensure that the --auto-tls argument is not set to true'] = "Fail" else @results[target]['etcd']['CIS 1.5.3 - Ensure that the --auto-tls argument is not set to true'] = "Pass" end unless (etcd_command_line.index{|line| line =~ /--peer-cert-file/} && etcd_command_line.index{|line| line =~ /--peer-key-file/}) @results[target]['etcd']['CIS 1.5.4 - Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate'] = "Fail" else @results[target]['etcd']['CIS 1.5.4 - Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate'] = "Pass" end unless etcd_command_line.index{|line| line =~ /--peer-client-cert-auth=true/} @results[target]['etcd']['CIS 1.5.5 - Ensure that the --peer-client-cert-auth argument is set to true'] = "Fail" else @results[target]['etcd']['CIS 1.5.5 - Ensure that the --peer-client-cert-auth argument is set to true'] = "Pass" end if etcd_command_line.index{|line| line =~ /--peer-auto-tls argument=true/} @results[target]['etcd']['CIS 1.5.6 - Ensure that the --peer-auto-tls argument is not set to true'] = "Fail" else @results[target]['etcd']['CIS 1.5.6 - Ensure that the --peer-auto-tls argument is not set to true'] = "Pass" end @results[target]['evidence']['etcd'] = etcd_command_line end |
.test_insecure_api_external ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/kube_auto_analyzer/vuln_checks/api_server.rb', line 3 def self.test_insecure_api_external @log.debug("Doing the external Insecure API check") target = .target_server unless @results[target]['vulns'] @results[target]['vulns'] = Hash.new end @results[target]['vulns']['insecure_api_external'] = Hash.new #Check for whether the Insecure API port is visible outside the cluster nodes = Array.new @client.get_nodes.each do |node| nodes << node['status']['addresses'][0]['address'] end nodes.each do |nod| if is_port_open?(nod, 8080) begin pods_resp = RestClient::Request.execute(:url => "http://#{nod}:8080/api",:method => :get) rescue RestClient::Forbidden pods_resp = "Not Vulnerable - Request Forbidden" end @results[target]['vulns']['insecure_api_external'][nod] = pods_resp else @results[target]['vulns']['insecure_api_external'][nod] = "Not Vulnerable - Port Not Open" end end end |
.test_insecure_api_internal ⇒ Object
This is somewhat awkward placement. Deployment mechanism sits more with the agent checks But from a “what it’s looking for” perspective, as a weakness in API Server, it makes more sense here.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/kube_auto_analyzer/vuln_checks/api_server.rb', line 31 def self.test_insecure_api_internal require 'json' @log.debug("Doing the internal Insecure API Server check") target = .target_server @results[target]['vulns']['insecure_api_internal'] = Hash.new nodes = Array.new @client.get_nodes.each do |node| nodes << node['status']['addresses'][0]['address'] end container_name = "kaainsecureapitest" pod = Kubeclient::Resource.new pod. = {} pod..name = container_name pod..namespace = "default" pod.spec = {} pod.spec.restartPolicy = "Never" pod.spec.containers = {} pod.spec.containers = [{name: "kubeautoanalyzerapitest", image: "raesene/kaa-agent:latest"}] pod.spec.containers[0].args = ["/api-server-checker.rb",nodes.join(',')] begin @log.debug("About to start API Server check pod") @client.create_pod(pod) @log.debug("Executed the create pod") begin sleep(5) until @client.get_pod(container_name,"default")['status']['containerStatuses'][0]['state']['terminated']['reason'] == "Completed" rescue retry end @log.debug ("started Kube API Check pod") results = JSON.parse(@client.get_pod_log(container_name,"default")) results.each do |node, results| @results[target]['vulns']['insecure_api_internal'][node] = results end ensure @client.delete_pod(container_name,"default") end end |
.test_scheduler ⇒ Object
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/kube_auto_analyzer/api_checks/master_node.rb', line 225 def self.test_scheduler target = .target_server @results[target]['scheduler'] = Hash.new pods = @client.get_pods pods.each do |pod| #Ok this is a bit naive as a means of hitting the API server but hey it's a start if pod['metadata']['name'] =~ /kube-scheduler/ @scheduler = pod end end unless @scheduler @results[target]['scheduler']['Scheduler Pod Not Found'] = "Error" return end scheduler_command_line = @scheduler['spec']['containers'][0]['command'] unless scheduler_command_line.index{|line| line =~ /--profiling=false/} @results[target]['scheduler']['CIS 1.2.1 - Ensure that the --profiling argument is set to false'] = "Fail" else @results[target]['scheduler']['CIS 1.2.1 - Ensure that the --profiling argument is set to false'] = "Pass" end @results[target]['evidence']['Scheduler'] = scheduler_command_line end |
.test_service_token_internal ⇒ Object
This is somewhat awkward placement. Deployment mechanism sits more with the agent checks But from a “what it’s looking for” perspective, as a weakness in Kubelet, it makes more sense here.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/kube_auto_analyzer/vuln_checks/service_token.rb', line 5 def self.test_service_token_internal require 'json' @log.debug("Doing the internal Service Token check") target = .target_server @results[target]['vulns']['service_token'] = Hash.new api_server_url = @client.api_endpoint.to_s container_name = "kaakubeletunauthtest" pod = Kubeclient::Resource.new pod. = {} pod..name = container_name pod..namespace = "default" pod.spec = {} pod.spec.restartPolicy = "Never" pod.spec.containers = {} pod.spec.containers = [{name: "kubeautoanalyzerservicetokentest", image: "raesene/kaa-agent:latest"}] pod.spec.containers[0].args = ["/service-token-checker.rb",api_server_url] begin @log.debug("About to start Service Token Check pod") @client.create_pod(pod) @log.debug("Executed the create pod") begin sleep(5) until @client.get_pod(container_name,"default")['status']['containerStatuses'][0]['state']['terminated']['reason'] == "Completed" rescue retry end @log.debug ("started Service Token Check pod") results = JSON.parse(@client.get_pod_log(container_name,"default")) results.each do |node, results| @results[target]['vulns']['service_token'][api_server_url] = results end ensure @client.delete_pod(container_name,"default") end end |
.test_unauth_kubelet_external ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/kube_auto_analyzer/vuln_checks/kubelet.rb', line 3 def self.test_unauth_kubelet_external @log.debug("Doing the external kubelet check") target = .target_server unless @results[target]['vulns'] @results[target]['vulns'] = Hash.new end @results[target]['vulns']['unauth_kubelet'] = Hash.new #Check for whether the Kubelet port is visible outside the cluster nodes = Array.new @client.get_nodes.each do |node| nodes << node['status']['addresses'][0]['address'] end nodes.each do |nod| if is_port_open?(nod, 10250) begin pods_resp = RestClient::Request.execute(:url => "https://#{nod}:10250/runningpods",:method => :get, :verify_ssl => false) rescue RestClient::Forbidden pods_resp = "Not Vulnerable - Request Forbidden" end @results[target]['vulns']['unauth_kubelet'][nod] = pods_resp else @results[target]['vulns']['unauth_kubelet'][nod] = "Not Vulnerable - Port Not Open" end end end |
.test_unauth_kubelet_internal ⇒ Object
This is somewhat awkward placement. Deployment mechanism sits more with the agent checks But from a “what it’s looking for” perspective, as a weakness in Kubelet, it makes more sense here.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/kube_auto_analyzer/vuln_checks/kubelet.rb', line 31 def self.test_unauth_kubelet_internal require 'json' @log.debug("Doing the internal kubelet check") target = .target_server @results[target]['vulns']['internal_kubelet'] = Hash.new nodes = Array.new @client.get_nodes.each do |node| nodes << node['status']['addresses'][0]['address'] end container_name = "kaakubeletunauthtest" pod = Kubeclient::Resource.new pod. = {} pod..name = container_name pod..namespace = "default" pod.spec = {} pod.spec.restartPolicy = "Never" pod.spec.containers = {} pod.spec.containers = [{name: "kubeautoanalyzerkubelettest", image: "raesene/kaa-agent:latest"}] pod.spec.containers[0].args = ["/kubelet-checker.rb",nodes.join(',')] begin @log.debug("About to start Kubelet check pod") @client.create_pod(pod) @log.debug("Executed the create pod") begin sleep(5) until @client.get_pod(container_name,"default")['status']['containerStatuses'][0]['state']['terminated']['reason'] == "Completed" rescue retry end @log.debug ("started Kubelet Check pod") results = JSON.parse(@client.get_pod_log(container_name,"default")) results.each do |node, results| @results[target]['vulns']['internal_kubelet'][node] = results end ensure @client.delete_pod(container_name,"default") end end |