Detect Kubernetes Vulnerabilities with InSpec

Earlier this week a critical security vulnerability was uncovered in the Kubernetes API server. The vulnerability, CVE-2018-1002105, uncovers an attack vector that would allow an unprivileged or unauthenticated user to escalate their privileges and run arbitrary commands with cluster-admin level access. The severity of this vulnerability is compounded by the fact that because these unauthorized requests are made over established connections to the API, there is no easy way to determine whether systems have been exploited by examining audit and server logs. Because of this, it’s imperative that organizations implementing Kubernetes identify and mitigate their exposure as quickly as possible. Thankfully, InSpec provides everything you need to identify whether your clusters are affected, and take appropriate action if so.

Ensure Your Kubernetes Server is Patched

Per the CVE, the best way to ensure your cluster is protected is to verify that you’re running a version that’s been patched to resolve this issue. The versions affected by the vulnerability are:

  • Kubernetes v1.0.x-1.9.x
  • Kubernetes v1.10.0-1.10.10 (fixed in v1.10.11)
  • Kubernetes v1.11.0-1.11.4 (fixed in v1.11.5)
  • Kubernetes v1.12.0-1.12.2 (fixed in v1.12.3)

Regardless of how you installed your cluster, the running version can be queried via the Kubernetes CLI by running kubectl version --short, which will return output similar to:

Client Version: v1.10.11
Server Version: v1.10.11

With InSpec, we can use the command resource to evaluate any arbitrary command’s output and compare it against our expectations. In this case, we can take the output of our kubectl command, and quickly validate whether our server is properly patched.

control 'kube_version_check' do                      
  impact 1.0
  title 'Check the Kubernetes server version'           
  describe command('kubectl version --short') do
    its('stdout') { should_not match /Server Version: v1\.[0-9]\./ }
    its('stdout') { should_not match /Server Version: v1\.10\.[0-9]$/ }
    its('stdout') { should_not match /Server Version: v1\.10\.10$/ }
    its('stdout') { should_not match /Server Version: v1\.11\.[0-4]$/ }
    its('stdout') { should_not match /Server Version: v1\.12\.[0-2]$/ }
  end
end

In the above example, we’re setting our expectation that the output of kubectl version --short should not return any versions in which the vulnerability is present.

NOTE: Since the InSpec DSL is a Ruby based language, Ruby code can be used within controls. Check out this alternate example which uses conditional logic to simplify scan output. (Thanks, Jerry!)

Mitigating Exposure in Unpatched Versions

While upgrading to the latest release is the ideal way to address this vulnerability, it’s not always feasible in the short term for every organization. To this end, the CVE outlines ways to mitigate exposure to the vulnerability if you’re forced to run an older version for the time being. Once again, InSpec can help us ensure that Kubernetes is configured securely even if we are running an affected version!

One recommendation is to ensure that aggregated API servers are not in use on any potentially affected cluster. Per the CVE, the following command will let you know whether aggregated API servers are in use, and will return the names of any API services if so:

kubectl get apiservices \
  -o 'jsonpath={range .items[?(@.spec.service.name!="")]}{.metadata.name}{"\n"}{end}'

Once again, we can use InSpec’s command resource to validate, this time expecting the above command to produce no output.

control 'aggregation_check' do
  impact 1.0
  title 'Check whether aggregated APIs are in use'
  describe command('kubectl get apiservices -o "jsonpath={range .items[?(@.spec.service.name!=\"\")]}{.metadata.name}{\"\\n\"}{end}"') do
    its('stdout') { should be_empty }
  end
end

Finally, the CVE further recommends ensuring that the API server configuration disallows anonymous requests, and that non-admin users don’t have exec/attach/portforward permissions. While these aren’t quite as simple to identify as the previous examples, they’re already covered in the CIS Benchmarks for Kubernetes published by the Center for Internet Security, specifically:

  • 1.1.1 Ensure that the –anonymous-auth argument is set to false
  • 1.1.12 Ensure that the admission control plugin DenyEscalatingExec is set

Thankfully, there’s already an InSpec profile available that covers these controls in the DevSec CIS Kubernetes Benchmarks! Rather than re-write these validations ourselves, we can execute the CIS benchmarks from the existing dev-sec profile…

  • …in its entirety:
    inspec exec https://github.com/dev-sec/cis-kubernetes-benchmark
  • …including only the CVE-specific controls:
    inspec exec \ 
      https://github.com/dev-sec/cis-kubernetes-benchmark \
      --controls=cis-kubernetes-benchmark-1.1.1 cis-kubernetes-benchmark-1.1.12
  • …or in addition to our own custom controls in a meta profile

Conclusion

Whatever route you take, InSpec gives you everything you need to quickly validate your environments and provides actionable insights into how to remediate any potentially exposed systems, allowing you to continue to innovate confident in your systems’ security.

What’s Next?

Nick Rycar

Nick is a Technical Product Marketing Manager working out of Chef HQ in Seattle. When he's not busy preparing product demos, he's torturing his colleagues with terrible puns and needlessly esoteric pop-culture trivia. Mostly he's just another confused New York transplant in the Pacific Northwest.