Skip to content

Commit 16e77a4

Browse files
committed
Added sensu check and gem files.
1 parent 65c9d29 commit 16e77a4

12 files changed

Lines changed: 240 additions & 0 deletions

File tree

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/.bundle/
2+
/.yardoc
3+
/Gemfile.lock
4+
/_yardoc/
5+
/coverage/
6+
/doc/
7+
/pkg/
8+
/spec/reports/
9+
/tmp/
10+
/vendor/bundle

Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
source 'https://rubygems.org'
2+
3+
# Specify your gem's dependencies in quayio-scanner.gemspec
4+
gemspec

LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2017 Benjamin Meichsner
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

Rakefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
require 'bundler/gem_tasks'
2+
task default: :spec
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#! /usr/bin/env ruby
2+
#
3+
# check-container-vulnerabilities
4+
#
5+
# DESCRIPTION:
6+
#
7+
# This plugin attempts to fetch vulnerabilties for all running containers
8+
#
9+
# OUTPUT:
10+
# plain text
11+
#
12+
# PLATFORMS:
13+
# Linux
14+
#
15+
# DEPENDENCIES:
16+
# gem: sensu-plugin
17+
# gem: docker-api
18+
# gem: rest-client
19+
#
20+
# USAGE:
21+
# ./check-container-vulnerabilities.rb -d <docker-url> -t <quay-io-oauth-token>
22+
#
23+
24+
require 'sensu-plugin/check/cli'
25+
require 'quayio/scanner'
26+
27+
class CheckContainerVulnerabilities < Sensu::Plugin::Check::CLI
28+
option :docker_url,
29+
description: 'Docker URL',
30+
short: '-d URL',
31+
long: '--docker-url URL',
32+
default: 'unix:///var/run/docker.sock'
33+
34+
option :quayio_token,
35+
description: 'Quay.io oauth token',
36+
short: '-t TOKEN',
37+
long: '--quayio-token TOKEN'
38+
39+
def run
40+
result = Quayio::Scanner::Check.new(config[:docker_url],
41+
config[:quayio_token]).run
42+
43+
if result[0] == :ok
44+
ok result[1]
45+
else
46+
critical result[1]
47+
end
48+
end
49+
end

bin/console

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'bundler/setup'
4+
require 'quayio/scanner'
5+
6+
# You can add fixtures and/or initialization code here to make experimenting
7+
# with your gem easier. You can also use a different console, if you like.
8+
9+
# (If you use this, don't forget to add pry to your Gemfile!)
10+
# require "pry"
11+
# Pry.start
12+
13+
require 'irb'
14+
IRB.start(__FILE__)

bin/setup

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
set -vx
5+
6+
bundle install
7+
8+
# Do any other automated setup that you need to do here

lib/quayio/scanner.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
require 'quayio/scanner/version'
2+
require 'quayio/scanner/check'
3+
4+
module Quayio
5+
module Scanner
6+
end
7+
end

lib/quayio/scanner/check.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'quayio/scanner/image'
2+
require 'docker'
3+
4+
module Quayio
5+
module Scanner
6+
class Check < Struct.new(:docker_url, :quayio_token)
7+
def run
8+
Docker.url = docker_url
9+
containers = Docker::Container.all
10+
.map { |dc| dc.json['Config']['Image'] }
11+
.uniq
12+
13+
vulnerable_images = containers
14+
.map { |container| Image.new(container, quayio_token) }
15+
.select(&:vulnerable?)
16+
.map(&:name)
17+
18+
if vulnerable_images.empty?
19+
[:ok, "#{containers.size} Containers are ok"]
20+
else
21+
[:critical, "The images are insecure: #{vulnerable_images.join(', ')}"]
22+
end
23+
end
24+
end
25+
end
26+
end

lib/quayio/scanner/image.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
require 'rest-client'
2+
3+
module Quayio
4+
module Scanner
5+
class Image < Struct.new(:name, :quayio_token)
6+
def vulnerable?
7+
quayio? && image_exists? && scanned? && high_vulnerabilities_present?
8+
end
9+
10+
private
11+
12+
def quayio?
13+
name.match(%r{^quay.io\/})
14+
end
15+
16+
def image_exists?
17+
raw_image
18+
end
19+
20+
def scanned?
21+
raw_scan['status'] == 'scanned'
22+
end
23+
24+
def high_vulnerabilities_present?
25+
raw_scan['data']['Layer']['Features'].detect do |f|
26+
f['Vulnerabilities'] &&
27+
f['Vulnerabilities'].detect { |v| v['Severity'] == 'High' }
28+
end
29+
end
30+
31+
def repo
32+
name.split(':').first.gsub(%r{quay.io\/}, '')
33+
end
34+
35+
def tag
36+
name.split(':').last
37+
end
38+
39+
def raw_image
40+
return @raw_image if defined? @raw_image
41+
42+
@raw_image = begin
43+
JSON.parse(
44+
RestClient.get("https://quay.io/api/v1/repository/#{repo}/image",
45+
authorization: "Bearer #{quayio_token}", accept: :json)
46+
)['images'].detect { |i| i['tags'].include?(tag) }
47+
rescue RestClient::ExceptionWithResponse => err
48+
return nil if err.http_code == 404 # ignore unknown repos
49+
raise err
50+
end
51+
end
52+
53+
def raw_scan
54+
return @raw_scan if defined? @raw_scan
55+
56+
@raw_scan = begin
57+
JSON.parse(
58+
RestClient.get("https://quay.io/api/v1/repository/#{repo}/image/#{raw_image['id']}/security?vulnerabilities=true",
59+
authorization: "Bearer #{quayio_token}", accept: :json)
60+
)
61+
end
62+
end
63+
end
64+
end
65+
end

0 commit comments

Comments
 (0)