Chef InSpec for your security and compliance automation

Oleksii Vasyliev, Railsware

Chef InSpec for your security and compliance automation

Brought to you by Alexey Vasiliev, Railsware

Oleksii Vasyliev

Security

Company security areas

"IT Security" vs "IT Compliance"

Information Security is the practice of exercising due diligence and due care to protect the confidentiality, integrity, and availability of critical business assets

IT Compliance is the process of meeting a third party’s requirements for digital security with the aim of enabling business operations in a particular market or with a particular customer

"IT Security" vs "IT Compliance"

Security pros

"IT Security" vs "IT Compliance"

Compliance pros

Security Compliance Benefits

Audit and Compliance Automation

Automation tools

Chef Inspec

Chef InSpec

InSpec is an open-source testing framework by Chef that enables you to specify compliance, security, and other policy requirements

Chef InSpec Support

Inspec Profile structure

$ tree nginx
nginx
├── README.md
├── controls
│ └── example.rb
├── inspec.lock
└── inspec.yml

inspec.yml

name: ssh
title: Basic SSH
maintainer: Alexey Vasiliev
copyright: Alexey Vasiliev
copyright_email: support@never.answer
license: MIT, All rights reserved
summary: Verify that SSH Server and SSH Client are configured securely
version: 1.0.0
supports:
  - os-family: linux
depends:
  - name: profile
    path: ../path/to/profile
inspec_version: "~> 2.1"

controls/example.rb (resources)

describe file('/tmp/test.txt') do
  it { should be_file }
end

"should" vs "expect" syntax

describe file('/tmp/test.txt') do
  it 'should be a file' do
    expect(subject).to(be_file)
  end
end

"should" vs "expect" syntax

describe 'test file' do
  subject { file('/tmp/test.txt') }
  it 'should be a file' do
    expect(subject).to(be_file)
  end
end

Check SSH Protocol

describe sshd_config do
  its('Protocol') { should eq('2') }
end
$ inspec exec test.rb
Profile: tests from test.rb (tests from test.rb)
Version: (not specified)
Target:  local://

  SSH Configuration
     ×  Protocol should eq "2"

     expected: "2"
          got: nil

     (compared using ==)

Test Summary: 0 successful, 1 failure, 0 skipped

Inspec specified locations

$ inspec exec /path/to/linux-baseline
$ inspec exec supermarket://username/linux-baseline
$ inspec exec git@github.com:dev-sec/linux-baseline.git
$ inspec exec https://api_token@github.com/dev-sec/linux-baseline.git
$ inspec exec https://webserver/linux-baseline.tar.gz
$ inspec exec https://webserver/linux-baseline.zip
          

Inspec target

$ inspec exec test.rb
$ inspec exec test.rb -t ssh://user@hostname -i /path/to/key
$ inspec exec test.rb -t winrm://Administrator@windowshost
  --password 'your-password'
$ inspec exec test.rb -t winrm://windowshost --user 'UserName@domain'
  --password 'your-password'
$ inspec exec test.rb -t docker://container_id
$ ...cloud platforms on next slides...
        

Inspec "OR" check

describe.one do
  describe ssh_config do
    its('Protocol') { should eq('3') }
  end
  describe ssh_config do
    its('Protocol') { should eq('2') }
  end
end
        

Inspec Controls

control 'Test *nix Super User' do
  impact 0.5
  title 'Superuser Test'
  desc 'Make sure the root user exists.'

  only_if do
    os.redhat? || os.debian? || os.linux? || os.darwin? || os.bsd?
  end

  describe user('root') do
    it { should exist }
  end
end
        

Inspec "impact"

Values are based off CVSS 3.0

Include controls

control 'ssh-2'
control 'ssh-11'
control 'ssh-23'
...
include_controls 'nginx-hardening'
        

Include controls

include_controls 'nginx-hardening' do
  skip_control 'nginx-3'
  skip_control 'nginx-232'
end
include_controls 'nginx-hardening' do
  control 'nginx-2'
  control 'nginx-4'
end
        

Modify included controls

include_controls 'nginx-hardening' do
  control 'nginx-2' do
    impact 0.5
  end
  control 'nginx-4' do
    impact 1.0
  end
end
        

DevSec: DevOps + Security

DevSec - Automatic Server Hardening

$ inspec supermarket info dev-sec/linux-baseline
name: linux-baseline
owner: dev-sec
url: https://github.com/dev-sec/linux-baseline

description: Linux compliance profile, used for Security + DevOps. More information is available at http://dev-sec.io

$ inspec supermarket exec dev-sec/linux-baseline -t ssh://root:password@target

$ inspec exec https://github.com/dev-sec/linux-baseline -t ssh://root:password@target

$ inspec exec https://github.com/dev-sec/linux-baseline -t ssh://root:password@target
    --controls package-08

Compliance Documentation

Compliance standards bodies

Example: CIS 5.5.4

control 'cis-ubuntu-lts-5.4.4' do
  impact 0.7
  title 'Ensure default user umask is 027 or more restrictive'
  desc 'The default umask determines the permissions'\
        'of files created by users.'
  describe file('/etc/bash.bashrc') do
    its('content') { should match /^umask 027/ }
  end
  describe file('/etc/profile') do
    its('content') { should match /^umask 027/ }
  end
end

Many teams use the Common Vulnerability Scoring System to assign the appropriate impact value to each test.

cis_554

Cloud Platforms

AWS example

describe aws_security_group("sg-12345678") do
  it { should exist }
end

$ inspec exec test.rb -t aws://profile
            

GCP example

describe google_compute_network(project: gcp_project_id, name: 'foo') do
 it { should exist }
 its('name') { should eq 'foo' }
 its('subnetworks.count') { should eq 1 }
 its('subnetworks.first') { should match 'bar' }
 its('routing_config.routing_mode') { should eq 'REGIONAL' }
 its('auto_create_subnetworks') { should be false }
end

$ inspec exec test.rb -t gcp://
                

InSpec + Terraform

resource "aws_security_group" "default" {
  name        = "terraform_example"
  description = "Used in the terraform"
  vpc_id      = "${aws_vpc.default.id}"

  ...
}

output "vpc_id" {
  value = "${aws_vpc.default.id}"
}

$ terraform output --json > inspec_tests/files/terraform.json 
# load data from Terraform output
content = inspec.profile.file("terraform.json")
params = JSON.parse(content)

# store vpc in variable
VPC_ID = params['vpc_id']['value']

# you can use the variable in various spaces

describe aws_vpc(vpc_id: VPC_ID) do
  its('cidr_block') { should cmp '10.0.0.0/16' }
end

describe aws_security_group(group_name: 'terraform_example') do
  it { should exist }
  its('group_name') { should eq 'terraform_example' }
  its('description') { should eq 'Used in the terraform' }
  its('vpc_id') { should eq VPC_ID }
end

Troubleshooting

Inspec Shell

$ inspec shell
$ inspec shell -t ssh://root@192.168.64.2:11022

Welcome to the interactive InSpec Shell
To find out how to use it, type: help

You are currently running on:

Name: mac_os_x
Families: darwin, bsd, unix, os
Release: 19.4.0
Arch: x86_64

inspec> 2 + 2
=> 4
inspec> quit
          

Conclusion

Conclusion

team

<Thank You!> Questions?

Contact information

QuestionsSlide