mirror of
https://github.com/inspec/inspec
synced 2024-11-27 15:10:44 +00:00
Initial checkin of Inspec Azure resource pack
Signed-off-by: Russell Seymour <russell.seymour@turtlesystems.co.uk>
This commit is contained in:
commit
12c832604e
10 changed files with 410 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.envrc
|
||||||
|
.ruby-version
|
||||||
|
.direnv
|
||||||
|
Gemfile.lock
|
||||||
|
inspec.lock
|
72
.rubocop.yml
Normal file
72
.rubocop.yml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
AllCops:
|
||||||
|
Exclude:
|
||||||
|
- Gemfile
|
||||||
|
- Rakefile
|
||||||
|
- 'test/**/*'
|
||||||
|
- 'examples/**/*'
|
||||||
|
- 'vendor/**/*'
|
||||||
|
- 'lib/bundles/inspec-init/templates/**/*'
|
||||||
|
Documentation:
|
||||||
|
Enabled: false
|
||||||
|
AlignParameters:
|
||||||
|
Enabled: true
|
||||||
|
Encoding:
|
||||||
|
Enabled: true
|
||||||
|
HashSyntax:
|
||||||
|
Enabled: true
|
||||||
|
LineLength:
|
||||||
|
Enabled: false
|
||||||
|
EmptyLinesAroundBlockBody:
|
||||||
|
Enabled: false
|
||||||
|
MethodLength:
|
||||||
|
Max: 40
|
||||||
|
NumericLiterals:
|
||||||
|
MinDigits: 10
|
||||||
|
Metrics/CyclomaticComplexity:
|
||||||
|
Max: 10
|
||||||
|
Metrics/PerceivedComplexity:
|
||||||
|
Max: 11
|
||||||
|
Metrics/AbcSize:
|
||||||
|
Max: 33
|
||||||
|
Style/PercentLiteralDelimiters:
|
||||||
|
PreferredDelimiters:
|
||||||
|
'%': '{}'
|
||||||
|
'%i': ()
|
||||||
|
'%q': '{}'
|
||||||
|
'%Q': ()
|
||||||
|
'%r': '{}'
|
||||||
|
'%s': ()
|
||||||
|
'%w': '{}'
|
||||||
|
'%W': ()
|
||||||
|
'%x': ()
|
||||||
|
Style/AlignHash:
|
||||||
|
Enabled: false
|
||||||
|
Style/PredicateName:
|
||||||
|
Enabled: false
|
||||||
|
Style/ClassAndModuleChildren:
|
||||||
|
Enabled: false
|
||||||
|
Style/ConditionalAssignment:
|
||||||
|
Enabled: false
|
||||||
|
Style/BracesAroundHashParameters:
|
||||||
|
Enabled: false
|
||||||
|
Style/AndOr:
|
||||||
|
Enabled: false
|
||||||
|
Style/Not:
|
||||||
|
Enabled: false
|
||||||
|
Style/FileName:
|
||||||
|
Enabled: false
|
||||||
|
Style/TrailingCommaInLiteral:
|
||||||
|
EnforcedStyleForMultiline: comma
|
||||||
|
Style/TrailingCommaInArguments:
|
||||||
|
EnforcedStyleForMultiline: comma
|
||||||
|
Style/NegatedIf:
|
||||||
|
Enabled: false
|
||||||
|
Style/UnlessElse:
|
||||||
|
Enabled: false
|
||||||
|
BlockDelimiters:
|
||||||
|
Enabled: false
|
||||||
|
Style/SpaceAroundOperators:
|
||||||
|
Enabled: false
|
||||||
|
Style/IfUnlessModifier:
|
||||||
|
Enabled: false
|
16
Gemfile
Normal file
16
Gemfile
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'inifile'
|
||||||
|
gem 'azure_mgmt_compute'
|
||||||
|
gem 'azure_mgmt_resources'
|
||||||
|
gem 'bundle'
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
gem 'rake'
|
||||||
|
gem 'rubocop'
|
||||||
|
gem 'github_changelog_generator'
|
||||||
|
end
|
||||||
|
|
||||||
|
group :inspec do
|
||||||
|
gem 'inspec', '~> 1.1'
|
||||||
|
end
|
127
README.md
Normal file
127
README.md
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
# InSpec Azure Resource Pack
|
||||||
|
|
||||||
|
This resource pack provides resources for Azure Resources. It will ship with the required resources to write your own compliance rules:
|
||||||
|
|
||||||
|
```
|
||||||
|
├── README.md - this readme
|
||||||
|
├── controls - contains example controls
|
||||||
|
└── libraries - contains Azure resources
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get Started
|
||||||
|
|
||||||
|
This profile uses the Azure Ruby SDK and as such requires a Service Principal Name (SPN) to be created in the Azure subscription that is being tested.
|
||||||
|
|
||||||
|
This can be done on the command line or from the Azure Portal
|
||||||
|
|
||||||
|
- Azure CLI: https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authenticate-service-principal-cli
|
||||||
|
- PowerShell: https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authenticate-service-principal
|
||||||
|
- Azure Portal: https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal
|
||||||
|
|
||||||
|
The information from the SPN can be specified either in a file `~/.azure/credentials`, a different file or as environment variables.
|
||||||
|
|
||||||
|
### Credentials File
|
||||||
|
|
||||||
|
The simplest way is to create the file `~/.azure/credentials` with the following format. The profile is configured to look for this file by default do no settings are required.
|
||||||
|
|
||||||
|
```
|
||||||
|
[<SUBSCRIPTION_ID>]
|
||||||
|
client_id = "<CLIENT_ID>"
|
||||||
|
client_secret = "<CLIENT_SECRET>"
|
||||||
|
tenant_id = "<TENANT_ID>"
|
||||||
|
```
|
||||||
|
|
||||||
|
So to run the profile now it is as simple as running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
inspec exec inspec-azure
|
||||||
|
```
|
||||||
|
|
||||||
|
A different credentials file, with the same format, can be specified as an environment variable `AZURE_CREDS_FILE`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AZURE_CREDS_FILE="/path/to/another/file" inspec exec inspec-azure
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this file format supports multiple subscription_ids. By default `inspec-azure` will pick the first subscription in the file. However if another subscription should be used then specify it in the environment variable `AZURE_SUBSCRIPTION_ID`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AZURE_SUBSCRIPTION_ID="2fbdbb02-df2e-11e6-bf01-fe55135034f3" inspec exec inspec-azure
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment variables
|
||||||
|
|
||||||
|
It is possible to not have a credentials file at all and specify all of the required information as the following environment variables:
|
||||||
|
|
||||||
|
- `AZURE_SUBSCRIPTION_ID`
|
||||||
|
- `AZURE_CLIENT_ID`
|
||||||
|
- `AZURE_CLIENT_SECRET`
|
||||||
|
- `AZURE_TENANT_ID`
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AZURE_SUBSCRIPTION_ID="2fbdbb02-df2e-11e6-bf01-fe55135034f3" AZURE_CLIENT_ID="58dc4f6c-df2e-11e6-bf01-fe55135034f3" AZURE_CLIENT_SECRET="Jibr4iwwaaZwBb6W" AZURE_TENANT_ID="6ad89b58-df2e-11e6-bf01-fe55135034f3" inspec exec inspec-azure
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use the resources
|
||||||
|
|
||||||
|
Since this is an InSpec resource pack, it only defines InSpec resources. It includes example tests only. To easily use the Azure InSpec resources in your tests do the following:
|
||||||
|
|
||||||
|
### Create a new profile
|
||||||
|
|
||||||
|
```bash
|
||||||
|
inspec init profile my-profile
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adapt the `inspec.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: my-profile
|
||||||
|
title: My own Azure profile
|
||||||
|
version: 0.1.0
|
||||||
|
depends:
|
||||||
|
- name: azure
|
||||||
|
url: https://github.com/chef/inspec-azure/archive/master.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add controls
|
||||||
|
|
||||||
|
Since your profile depends on the resource pack, you can use those resources in your own profile:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
control 'azure-1' do
|
||||||
|
impact 1.0
|
||||||
|
title 'Checks that the machine was built from the correct image'
|
||||||
|
|
||||||
|
describe azurevm_image(host: 'example-01', resource_group: 'MyResourceGroup') do
|
||||||
|
its('sku') { should eq '16.04.0-LTS' }
|
||||||
|
its('publisher') { should ieq 'Canonical' }
|
||||||
|
its('offer') { should ieq 'UbuntuServer' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Resources
|
||||||
|
|
||||||
|
- `azurevm_image` - This resource reads information about a virtual machine in the specified resource group
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| ------ | --- |
|
||||||
|
| **Author:** | Russell Seymour (<russell@chef.io>) |
|
||||||
|
| **Copyright:** | Copyright (c) 2016 Chef Software Inc. |
|
||||||
|
| **License:** | Apache License, Version 2.0 |
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
38
Rakefile
Normal file
38
Rakefile
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env rake
|
||||||
|
|
||||||
|
require 'rake/testtask'
|
||||||
|
require 'rubocop/rake_task'
|
||||||
|
|
||||||
|
# Rubocop
|
||||||
|
desc 'Run Rubocop lint checks'
|
||||||
|
task :rubocop do
|
||||||
|
RuboCop::RakeTask.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# lint the project
|
||||||
|
desc 'Run robocop linter'
|
||||||
|
task lint: [:rubocop]
|
||||||
|
|
||||||
|
# run tests
|
||||||
|
task default: [:lint, 'test:check']
|
||||||
|
|
||||||
|
namespace :test do
|
||||||
|
# run inspec check to verify that the profile is properly configured
|
||||||
|
task :check do
|
||||||
|
dir = File.join(File.dirname(__FILE__))
|
||||||
|
sh("bundle exec inspec check #{dir}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Automatically generate a changelog for this project. Only loaded if
|
||||||
|
# the necessary gem is installed.
|
||||||
|
# use `rake changelog to=1.2.0`
|
||||||
|
begin
|
||||||
|
v = ENV['to']
|
||||||
|
require 'github_changelog_generator/task'
|
||||||
|
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
||||||
|
config.future_release = v
|
||||||
|
end
|
||||||
|
rescue LoadError
|
||||||
|
puts '>>>>> GitHub Changelog Generator not loaded, omitting tasks'
|
||||||
|
end
|
12
controls/example_vm_image.rb
Normal file
12
controls/example_vm_image.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
title 'Sample profile to test the Image SKU of a vm'
|
||||||
|
|
||||||
|
control 'azurevm-image-1.0' do
|
||||||
|
impact 1.0
|
||||||
|
title 'Ensure that the machine has an image SKU of 16.04.0-LTS'
|
||||||
|
describe azurevm_image(host: 'exmaple-01', resource_group: 'MyResourceGroup') do
|
||||||
|
its('sku') { should eq '16.04.0-LTS' }
|
||||||
|
its('publisher') { should eq 'Canonical' }
|
||||||
|
its('offer') { should eq 'UbuntuServer' }
|
||||||
|
end
|
||||||
|
end
|
5
inspec.yml
Normal file
5
inspec.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name: azure
|
||||||
|
title: Azure Resource Pack
|
||||||
|
maintainer: russell@turtlesystems.co.uk
|
||||||
|
summary: This resource pack provides compliance resources for Azure
|
||||||
|
version: 0.1.0
|
45
libraries/azure_conn.rb
Normal file
45
libraries/azure_conn.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
require 'ms_rest_azure'
|
||||||
|
require 'inifile'
|
||||||
|
|
||||||
|
class AzureConnection
|
||||||
|
attr_accessor :subscription_id
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
# If an INSPEC_AZURE_CREDS environment has been specified set the
|
||||||
|
# the credentials file to that, otherwise set the one in home
|
||||||
|
azure_creds_file = ENV['AZURE_CREDS_FILE']
|
||||||
|
if azure_creds_file.nil?
|
||||||
|
|
||||||
|
# The environment file has not be set, so default to one in the home directory
|
||||||
|
azure_creds_file = File.join(Dir.home, '.azure', 'credentials')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check to see if the credentials file exists
|
||||||
|
if File.file?(azure_creds_file)
|
||||||
|
@credentials = IniFile.load(File.expand_path(azure_creds_file))
|
||||||
|
else
|
||||||
|
warn format('%s was not found or not accessible', azure_creds_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def connection
|
||||||
|
# If a connection already exists then return it
|
||||||
|
return @conn if defined?(@conn)
|
||||||
|
|
||||||
|
# Determine if only one subscription is specified in the configuration file, if so use that
|
||||||
|
if @credentials.sections.length == 1
|
||||||
|
@subscription_id = @credentials.sections[0]
|
||||||
|
else
|
||||||
|
@subscription_id = ENV['AZURE_SUBSCRIPTION_ID']
|
||||||
|
end
|
||||||
|
|
||||||
|
# Determine the client_id, tenant_id and the client_secret
|
||||||
|
tenant_id = ENV['AZURE_TENANT_ID'] || @credentials[@subscription_id]['tenant_id']
|
||||||
|
client_id = ENV['AZURE_CLIENT_ID'] || @credentials[@subscription_id]['client_id']
|
||||||
|
client_secret = ENV['AZURE_CLIENT_SECRET'] || @credentials[@subscription_id]['client_secret']
|
||||||
|
|
||||||
|
# Create a new connection
|
||||||
|
token_provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret)
|
||||||
|
@conn = MsRest::TokenCredentials.new(token_provider)
|
||||||
|
end
|
||||||
|
end
|
77
libraries/azure_vm_image.rb
Normal file
77
libraries/azure_vm_image.rb
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
require 'azure_conn'
|
||||||
|
require 'azure_mgmt_compute'
|
||||||
|
|
||||||
|
require_relative 'common/resource_groups'
|
||||||
|
|
||||||
|
class AzureVmImage < Inspec.resource(1)
|
||||||
|
name 'azurevm_image'
|
||||||
|
|
||||||
|
desc "
|
||||||
|
This resource gathers information about which image the vm was created from
|
||||||
|
"
|
||||||
|
|
||||||
|
example "
|
||||||
|
describe azurevm_image_sku(host: 'acme-test-01', resource_group: 'ACME') do
|
||||||
|
its('sku') { should eq '16.04.0-LTS'}
|
||||||
|
end
|
||||||
|
"
|
||||||
|
|
||||||
|
# Load the configuration file on initialisation
|
||||||
|
def initialize(opts)
|
||||||
|
@opts = opts
|
||||||
|
end
|
||||||
|
|
||||||
|
def sku
|
||||||
|
vm = get_vm(@opts[:host], @opts[:resource_group])
|
||||||
|
|
||||||
|
if vm.instance_of?(String)
|
||||||
|
vm
|
||||||
|
else
|
||||||
|
vm.storage_profile.image_reference.sku
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def publisher
|
||||||
|
|
||||||
|
vm = get_vm(@opts[:host], @opts[:resource_group])
|
||||||
|
|
||||||
|
if vm.instance_of?(String)
|
||||||
|
vm
|
||||||
|
else
|
||||||
|
vm.storage_profile.image_reference.publisher
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def offer
|
||||||
|
vm = get_vm(@opts[:host], @opts[:resource_group])
|
||||||
|
|
||||||
|
if vm.instance_of?(String)
|
||||||
|
vm
|
||||||
|
else
|
||||||
|
vm.storage_profile.image_reference.offer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Retrieve the named virtual machine from Azure
|
||||||
|
def get_vm(name, rg_name)
|
||||||
|
# Azure connection
|
||||||
|
azure = AzureConnection.new
|
||||||
|
|
||||||
|
client = Azure::ARM::Compute::ComputeManagementClient.new(azure.connection)
|
||||||
|
client.subscription_id = azure.subscription_id
|
||||||
|
|
||||||
|
# Ensure that the resource group exists
|
||||||
|
rg = ResourceGroups.new(azure)
|
||||||
|
|
||||||
|
unless rg.exists(rg_name)
|
||||||
|
throw "The Resource group cannot be found: #{rg_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# get a vm from the named resource group
|
||||||
|
begin
|
||||||
|
client.virtual_machines.get(rg_name, name)
|
||||||
|
rescue => e
|
||||||
|
e.error_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
libraries/common/resource_groups.rb
Normal file
13
libraries/common/resource_groups.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
require 'azure_mgmt_resources'
|
||||||
|
|
||||||
|
class ResourceGroups
|
||||||
|
def initialize(azure)
|
||||||
|
@client = Azure::ARM::Resources::ResourceManagementClient.new(azure.connection)
|
||||||
|
@client.subscription_id = azure.subscription_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def exists(name)
|
||||||
|
@client.resource_groups.check_existence(name)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue