mirror of
https://github.com/inspec/inspec
synced 2024-11-23 21:23:29 +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