2017-06-07 12:10:29 +00:00
|
|
|
# encoding: utf-8
|
|
|
|
# author: Takaaki Furukawa
|
|
|
|
|
|
|
|
require 'hashie/mash'
|
|
|
|
|
|
|
|
module Inspec::Resources
|
2017-12-07 19:22:55 +00:00
|
|
|
class Virtualization < Inspec.resource(1)
|
2017-06-07 12:10:29 +00:00
|
|
|
name 'virtualization'
|
|
|
|
desc 'Use the virtualization InSpec audit resource to test the virtualization platform on which the system is running'
|
|
|
|
example "
|
|
|
|
describe virtualization do
|
|
|
|
its('system') { should eq 'docker' }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe virtualization do
|
|
|
|
its('role') { should eq 'guest' }
|
|
|
|
end
|
|
|
|
|
|
|
|
control 'test' do
|
|
|
|
describe file('/var/tmp/foo') do
|
|
|
|
it { should be_file }
|
|
|
|
end
|
|
|
|
only_if { virtualization.system == 'docker' }
|
|
|
|
end
|
|
|
|
"
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
unless inspec.os.linux?
|
|
|
|
skip_resource 'The `virtualization` resource is not supported on your OS yet.'
|
|
|
|
else
|
|
|
|
collect_data_linux
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# add helper methods for easy access of properties
|
|
|
|
# allows users to use virtualization.role, virtualization.system
|
|
|
|
%w{role system}.each do |property|
|
|
|
|
define_method(property.to_sym) do
|
|
|
|
@virtualization_data[property.to_sym]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def params
|
|
|
|
collect_data_linux
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
'Virtualization Detection'
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def lxc_version_exists?
|
|
|
|
inspec.command('lxc-version').exist?
|
|
|
|
end
|
|
|
|
|
|
|
|
def docker_exists?
|
|
|
|
inspec.command('docker').exist?
|
|
|
|
end
|
|
|
|
|
|
|
|
def nova_exists?
|
|
|
|
inspec.command('nova').exist?
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect Xen
|
|
|
|
# /proc/xen is an empty dir for EL6 + Linode Guests + Paravirt EC2 instances
|
|
|
|
# Notes:
|
|
|
|
# - cpuid of guests, if we could get it, would also be a clue
|
|
|
|
# - may be able to determine if under paravirt from /dev/xen/evtchn (See OHAI-253)
|
|
|
|
# - Additional edge cases likely should not change the above assumptions
|
|
|
|
# but rather be additive - btm
|
|
|
|
def detect_xen
|
|
|
|
return false unless inspec.file('/proc/xen').exist?
|
|
|
|
@virtualization_data[:system] = 'xen'
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
|
|
|
|
# This file should exist on most Xen systems, normally empty for guests
|
|
|
|
if inspec.file('/proc/xen/capabilities').exist? &&
|
2017-11-21 07:49:41 +00:00
|
|
|
inspec.file('/proc/xen/capabilities').content =~ /control_d/i # rubocop:disable Layout/MultilineOperationIndentation
|
2017-06-07 12:10:29 +00:00
|
|
|
@virtualization_data[:role] = 'host'
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect Virtualbox from kernel module
|
|
|
|
def detect_virtualbox
|
|
|
|
return false unless inspec.file('/proc/modules').exist?
|
|
|
|
modules = inspec.file('/proc/modules').content
|
|
|
|
if modules =~ /^vboxdrv/
|
|
|
|
Inspec::Log.debug('Plugin Virtualization: /proc/modules contains vboxdrv. Detecting as vbox host')
|
|
|
|
@virtualization_data[:system] = 'vbox'
|
|
|
|
@virtualization_data[:role] = 'host'
|
|
|
|
elsif modules =~ /^vboxguest/
|
|
|
|
Inspec::Log.debug('Plugin Virtualization: /proc/modules contains vboxguest. Detecting as vbox guest')
|
|
|
|
@virtualization_data[:system] = 'vbox'
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# if nova binary is present we're on an openstack host
|
|
|
|
def detect_openstack
|
|
|
|
return false unless nova_exists?
|
|
|
|
@virtualization_data[:system] = 'openstack'
|
|
|
|
@virtualization_data[:role] = 'host'
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect paravirt KVM/QEMU from cpuinfo, report as KVM
|
|
|
|
def detect_kvm_from_cpuinfo
|
|
|
|
return false unless inspec.file('/proc/cpuinfo').content =~ /QEMU Virtual CPU|Common KVM processor|Common 32-bit KVM processor/
|
|
|
|
@virtualization_data[:system] = 'kvm'
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect KVM systems via /sys
|
|
|
|
# guests will have the hypervisor cpu feature that hosts don't have
|
|
|
|
def detect_kvm_from_sys
|
|
|
|
return false unless inspec.file('/sys/devices/virtual/misc/kvm').exist?
|
2017-11-21 07:49:41 +00:00
|
|
|
@virtualization_data[:system] = 'kvm'
|
2017-06-07 12:10:29 +00:00
|
|
|
if inspec.file('/proc/cpuinfo').content =~ /hypervisor/
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
else
|
|
|
|
@virtualization_data[:role] = 'host'
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect OpenVZ / Virtuozzo.
|
|
|
|
# http://wiki.openvz.org/BC_proc_entries
|
|
|
|
def detect_openvz
|
|
|
|
if inspec.file('/proc/bc/0').exist?
|
|
|
|
@virtualization_data[:system] = 'openvz'
|
|
|
|
@virtualization_data[:role] = 'host'
|
|
|
|
elsif inspec.file('/proc/vz').exist?
|
|
|
|
@virtualization_data[:system] = 'openvz'
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect Parallels virtual machine from pci devices
|
|
|
|
def detect_parallels
|
|
|
|
return false unless inspec.file('/proc/bus/pci/devices').content =~ /1ab84000/
|
|
|
|
@virtualization_data[:system] = 'parallels'
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect Linux-VServer
|
|
|
|
def detect_linux_vserver
|
|
|
|
return false unless inspec.file('/proc/self/status').exist?
|
|
|
|
proc_self_status = inspec.file('/proc/self/status').content
|
|
|
|
vxid = proc_self_status.match(/^(s_context|VxID):\s*(\d+)$/)
|
|
|
|
return false unless vxid && vxid[2]
|
|
|
|
@virtualization_data[:system] = 'linux-vserver'
|
|
|
|
if vxid[2] == '0'
|
|
|
|
@virtualization_data[:role] = 'host'
|
|
|
|
else
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect LXC/Docker
|
|
|
|
#
|
|
|
|
# /proc/self/cgroup will look like this inside a docker container:
|
|
|
|
# <index #>:<subsystem>:/lxc/<hexadecimal container id>
|
|
|
|
#
|
|
|
|
# /proc/self/cgroup could have a name including alpha/digit/dashes
|
|
|
|
# <index #>:<subsystem>:/lxc/<named container id>
|
|
|
|
#
|
|
|
|
# /proc/self/cgroup could have a non-lxc cgroup name indicating other uses
|
|
|
|
# of cgroups. This is probably not LXC/Docker.
|
|
|
|
# <index #>:<subsystem>:/Charlie
|
|
|
|
#
|
|
|
|
# A host which supports cgroups, and has capacity to host lxc containers,
|
|
|
|
# will show the subsystems and root (/) namespace.
|
|
|
|
# <index #>:<subsystem>:/
|
|
|
|
#
|
|
|
|
# Full notes, https://tickets.opscode.com/browse/OHAI-551
|
|
|
|
# Kernel docs, https://www.kernel.org/doc/Documentation/cgroups
|
|
|
|
def detect_lxc_docker
|
|
|
|
return false unless inspec.file('/proc/self/cgroup').exist?
|
|
|
|
cgroup_content = inspec.file('/proc/self/cgroup').content
|
|
|
|
if cgroup_content =~ %r{^\d+:[^:]+:/(lxc|docker)/.+$} ||
|
2017-11-21 07:49:41 +00:00
|
|
|
cgroup_content =~ %r{^\d+:[^:]+:/[^/]+/(lxc|docker)-.+$} # rubocop:disable Layout/MultilineOperationIndentation
|
2017-06-07 12:10:29 +00:00
|
|
|
@virtualization_data[:system] = $1 # rubocop:disable Style/PerlBackrefs
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
elsif lxc_version_exists? && cgroup_content =~ %r{\d:[^:]+:/$}
|
|
|
|
# lxc-version shouldn't be installed by default
|
|
|
|
# Even so, it is likely we are on an LXC capable host that is not being used as such
|
|
|
|
# So we're cautious here to not overwrite other existing values (OHAI-573)
|
|
|
|
unless @virtualization_data[:system] && @virtualization_data[:role]
|
|
|
|
@virtualization_data[:system] = 'lxc'
|
|
|
|
@virtualization_data[:role] = 'host'
|
|
|
|
end
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def detect_docker
|
|
|
|
return false unless inspec.file('/.dockerenv').exist? || inspec.file('/.dockerinit').exist?
|
|
|
|
@virtualization_data[:system] = 'docker'
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect LXD
|
|
|
|
# See https://github.com/lxc/lxd/blob/master/doc/dev-lxd.md
|
|
|
|
def detect_lxd
|
|
|
|
if inspec.file('/dev/lxd/sock').exist?
|
|
|
|
@virtualization_data[:system] = 'lxd'
|
|
|
|
@virtualization_data[:role] = 'guest'
|
|
|
|
elsif inspec.file('/var/lib/lxd/devlxd').exist?
|
|
|
|
@virtualization_data[:system] = 'lxd'
|
|
|
|
@virtualization_data[:role] = 'host'
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def collect_data_linux # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
|
|
|
# cache data in an instance var to avoid doing multiple detections for a single test
|
|
|
|
@virtualization_data ||= Hashie::Mash.new
|
|
|
|
return unless @virtualization_data.empty?
|
|
|
|
|
|
|
|
# each detect method will return true if it matched and was successfully
|
|
|
|
# able to populate @virtualization_data with stuff.
|
|
|
|
return if detect_xen
|
|
|
|
return if detect_virtualbox
|
|
|
|
return if detect_openstack
|
|
|
|
return if detect_kvm_from_cpuinfo
|
|
|
|
return if detect_kvm_from_sys
|
|
|
|
return if detect_openvz
|
|
|
|
return if detect_parallels
|
|
|
|
return if detect_linux_vserver
|
|
|
|
return if detect_lxc_docker
|
|
|
|
return if detect_docker
|
|
|
|
return if detect_lxd
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|