Added new resources

Allows testing of network configurations
Closes #2

Signed-off-by: Russell Seymour <russell.seymour@turtlesystems.co.uk>
This commit is contained in:
Russell Seymour 2017-03-02 14:01:54 +00:00
parent 1f53a1649e
commit de1b7134ef
6 changed files with 225 additions and 12 deletions

View file

@ -15,6 +15,8 @@ Encoding:
Enabled: true
HashSyntax:
Enabled: true
ClassLength:
Max: 200
LineLength:
Enabled: false
EmptyLinesAroundBlockBody:

View file

@ -166,14 +166,23 @@ end
| | sku | The SKU being used |
| | size | The size of the machine |
| | location | Where the machine has been deployed |
| | boot_diagnostics? | Whether boot diagnostics have been enabled or not |
| | has_boot_diagnostics? | Whether boot diagnostics have been enabled or not |
| | nic_count | How many network cards are attached to the machine |
| | admin_username | The admin username that has been assigned to the machine |
| | computername | Computer name of the machine in the operating system. This maybe different to the VM name as seen in Azure |
| | hostname | Alias for computername |
| | password_authentication? | If password authentication is enabled. For Windows machines this is always true |
| | ssh_key_count | How many SSH public keys have been added to the machine. For Windows this is always 0 |
| | os_type | Tyep type of operating system. Linux or Windows |
| | os_type | The type of operating system. Linux or Windows |
| | private_ipaddresses | Returns an array of all the IP addresses for all the NICs on the machine |
| | has_public_ipaddress? | Whether the machine has been allocated an IP address or not |
| | domain_name_label | If the machine has a public IP address then return the domain name label it has been assigned |
For the resources that start with `has_` the following construct can be used
```ruby
it { should have_boot_diagnostics }
```
- `azure_vm_datadisks` - Resource to read the data disks for a machine and check that they are of the correct size etc

View file

@ -38,6 +38,9 @@ where
- `password_authentication?`
- `ssh_key_count`
- `os_type`
- `private_ipaddresses`
- `has_public_ipaddress?`
- `domain_name_label`
* `value` is the expected output from the matcher
For example:
@ -97,6 +100,10 @@ its('location') { should eq 'West Europe' }
Boolean test to see if boot diagnostics have been enabled on the machine
```ruby
it { should have_boot_diagnostics }
```
### nic_count
The number of network interface cards that have been attached to the machine
@ -135,6 +142,26 @@ This only applies to Linux machines and will always return `0` on Windows.
Generic test that returns either `Linux` or `Windows`.
### private_ipaddresses
Returns an array of all the private IP addresses that are assigned to the machine. This is because a machine can multiple NICs and each NIC can have multiple IP Configurations.
```ruby
its('private_ipaddresses') { should include '10.1.1.10' }
```
### has_public_ipaddress?
Returns boolean to state if the machine has been allocated a Public IP Address.
```ruby
it { should have_public_ip_address }
```
### domain_name_label
If a machine has been allocated a Public IP Addresse test to see what domain name label has been set.
## Examples
The following examples show how to use this InSpec audit resource.

View file

@ -19,7 +19,7 @@ class AzureVm < Inspec.resource(1)
end
"
attr_accessor :vm
attr_accessor :vm, :nics, :helpers
# Constructor to retrieve the VM from Azure
#
@ -30,13 +30,28 @@ class AzureVm < Inspec.resource(1)
# opts[:resource_group] Name of the resource group in which the host will be found
def initialize(opts)
opts = opts
helpers = Helpers.new
@helpers = Helpers.new
@vm = helpers.get_vm(opts[:name], opts[:resource_group])
# Ensure that the vm is an object
raise format('An error has occured: %s', vm) if vm.instance_of?(String)
# Parse the Network Interface Cards attached to the machine
@nics = parse_nics(vm.network_profile.network_interfaces)
end
filter = FilterTable.create
filter.add_accessor(:where)
.add_accessor(:entries)
.add(:accelerated_networking, field: 'enable_accelerated_networking')
.add(:ip_forwarding, field: 'enable_ip_forwarding')
.add(:location, field: 'location')
.add(:name, field: 'name')
.add(:primary, field: 'primary')
.add(:ip_configurations, field: 'ip_configurations')
filter.connect(self, :nics)
# Determine the SKU used to create the machine
#
# @return [String] Showing the sku, e.g. 16.04.0-LTS
@ -81,7 +96,7 @@ class AzureVm < Inspec.resource(1)
#
# @return [Boolean]
#
def boot_diagnostics?
def has_boot_diagnostics?
if vm.diagnostics_profile
vm.diagnostics_profile.boot_diagnostics.enabled
else
@ -156,4 +171,149 @@ class AzureVm < Inspec.resource(1)
def os_type
vm.storage_profile.os_disk.os_type
end
# Return an array of the private IP addresses so that it is possible
# to check if the machine has the correct assigned address
#
# @return [Array] Array of private ip addresses
#
def private_ipaddresses
# Create an array to hold the addresses
addresses = []
# Iterate around the filter that has been populated
entries.each do |entry|
entry.ip_configurations.each do |ip_config|
addresses << ip_config['private_ipaddress']
end
end
# return the array to the calling function
addresses
end
# Boolean test to check that the machine has a public IP address
#
# @return [boolean]
#
def has_public_ip_address?
# Define the test value
test = false
entries.each do |entry|
entry.ip_configurations.each do |ip_config|
if ip_config['public_ipaddress']['attached']
test = true
break
end
end
end
test
end
# Return the domain name label that has been assigned to the machine
#
# @return [String] The domain name label
#
def domain_name_label
label = nil
entries.each do |entry|
entry.ip_configurations.each do |ip_config|
if ip_config['public_ipaddress']['attached']
label = ip_config['public_ipaddress']['domain_name_label']
end
end
end
label
end
private
# Parse the array of NICs attached to the machine
#
# @return [Array] Array of all the NICs
#
def parse_nics(attached_nics)
# Iterate around the attached NICs
attached_nics.each.map do |attached_nic|
# Get the name of the resource group and the name of the NIC
# This is required as the card might be in a different resource group
nic_raw = attached_nic.id.split(%r{/})
nic_resource_group_name = nic_raw[4]
nic_name = nic_raw.last
# Interrogate Azure for the NIC details
nic = helpers.network_mgmt.client.network_interfaces.get(nic_resource_group_name, nic_name)
# Parse the NIC
parse_nic(nic)
end.compact
end
# Parse the indivdual NIC
#
# @return [Hash] Properties of the indvidual NIC
#
def parse_nic(nic)
# Create the hash table that contains all the information about the NIC
{
'enable_accelerated_networking' => nic.enable_accelerated_networking,
'enable_ip_forwarding' => nic.enable_ipforwarding,
'location' => nic.location,
'name' => nic.name,
'primary' => nic.primary,
# Parse all the IP configurations for the NIC
'ip_configurations' => parse_ip_configurations(nic.ip_configurations),
}
end
# Parse the array of IP configurations that are applied to the NIC
#
# @returns [Array] Array of all the IP configurations
#
def parse_ip_configurations(ip_configurations)
# Iterate around all of the IP configurations
ip_configurations.each.map do |ip_configuration|
parse_ip_configuration(ip_configuration)
end.compact
end
# Parse the IP configuration item
#
# @return [Hash] Hashtable of the ip_configuration attributes
#
def parse_ip_configuration(ip_configuration)
config = {
'name' => ip_configuration.name,
'primary' => ip_configuration.primary,
'private_ipaddress' => ip_configuration.private_ipaddress,
'public_ipaddress' => {
'attached' => !ip_configuration.public_ipaddress.nil?,
},
}
# if there is a public IP address attached get its details
if config['public_ipaddress']['attached']
# Get the name of the resource group and the name of the NIC
# This is required as the card might be in a different resource group
public_ip_raw = ip_configuration.public_ipaddress.id.split(%r{/})
public_ip_resource_group_name = public_ip_raw[4]
public_ip_name = public_ip_raw.last
# Interrogate Azure for the NIC details
public_ip = helpers.network_mgmt.client.public_ipaddresses.get(public_ip_resource_group_name, public_ip_name)
# update the config with the information about the public IP
config['public_ipaddress']['domain_name_label'] = public_ip.dns_settings.domain_name_label
config['public_ipaddress']['dns_fqdn'] = public_ip.dns_settings.fqdn
end
# return object
config
end
end

View file

@ -47,7 +47,7 @@ resource "azurerm_public_ip" "public_ip_1" {
location = "${var.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
public_ip_address_allocation = "dynamic"
domain_name_label = "linux-inspec-1"
domain_name_label = "linux-external-1"
}
# Create the virtual network for the machines
@ -66,7 +66,8 @@ resource "azurerm_subnet" "subnet" {
address_prefix = "10.1.1.0/24"
}
# Create the NIC for the machine
# Create the NIC for the internal machine
# Give the machine a static IP Address
resource "azurerm_network_interface" "nic1" {
name = "Inspec-NIC-1"
location = "${var.location}"
@ -75,7 +76,8 @@ resource "azurerm_network_interface" "nic1" {
ip_configuration {
name = "ipConfiguration1"
subnet_id = "${azurerm_subnet.subnet.id}"
private_ip_address_allocation = "dynamic"
private_ip_address_allocation = "static"
private_ip_address = "10.1.1.10"
}
}

View file

@ -1,10 +1,10 @@
title 'Virtual Machine Properties'
control 'azure-vm-1.0' do
control 'azure-vm-internal-1.0' do
impact 1.0
title 'Ensure VM was built with the correct Image and has the correct properties'
title 'Ensure Internal VM was built with the correct Image and has the correct properties'
# Ensure that the virtual machine has been created with the correct attributes
describe azure_virtual_machine(name: 'Linux-Internal-VM', resource_group: 'Inspec-Azure') do
@ -13,12 +13,25 @@ control 'azure-vm-1.0' do
its('offer') { should eq 'UbuntuServer' }
its('size') { should eq 'Standard_DS2_v2' }
its('location') { should eq 'westeurope' }
its('boot_diagnostics?') { should be false }
it { should_not have_boot_diagnostics }
its('nic_count') { should eq 1 }
its('admin_username') { should eq 'azure' }
its('password_authentication?') { should be true }
its('ssh_key_count') { should eq 0 }
its('os_type') { should eq 'Linux' }
its('os_type') { should eq 'Linux' }
its('private_ipaddresses') { should include '10.1.1.10' }
it { should_not have_public_ip_address }
end
end
control 'azure-vm-external-1.0' do
impact 1.0
title 'Ensure External VM has external access'
describe azure_virtual_machine(name: 'Linux-External-VM', resource_group: 'Inspec-Azure') do
it { should have_public_ip_address }
its('domain_name_label') { should eq 'linux-external-1' }
end
end