norad_cli 0.1.12 → 0.1.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +2 -0
- data/README.md +61 -9
- data/WALKTHROUGH.md +12 -12
- data/lib/norad_cli/cli/sectest.rb +112 -43
- data/lib/norad_cli/support/results_server.rb +32 -0
- data/lib/norad_cli/support/sectest_container.rb +128 -0
- data/lib/norad_cli/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca54e887d7d704e64bbc278a2b4cc8afbbd0674c
|
4
|
+
data.tar.gz: 1201b3922139b899cc42785f6def1c58f9ea1f71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e28eaea3ca41e1e69c2021c1a770fa692b4f4d50b9343a021475fbb77855325e700023ba02328486bb7629b9c1c65759720475a7ede4f9fbaa78eb827b7a91a8
|
7
|
+
data.tar.gz: fc56350a7b03f7b7a037a8d3b692c6c2fb80e10020e793786f1f30d0389bf9effa228fdced9beba35b4932a924d000d24f76c50104527bacb70b305ab5f24748
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -13,12 +13,14 @@ Metrics/LineLength:
|
|
13
13
|
Metrics/MethodLength:
|
14
14
|
Exclude:
|
15
15
|
- 'lib/norad_cli/support/api_security_container_seed_script.rb'
|
16
|
+
- 'lib/norad_cli/support/sectest_container.rb'
|
16
17
|
- 'lib/norad_cli/cli/sectest.rb'
|
17
18
|
Metrics/AbcSize:
|
18
19
|
Exclude:
|
19
20
|
- 'lib/norad_cli/support/api_security_container_seed_script.rb'
|
20
21
|
- 'lib/norad_cli/cli/sectest.rb'
|
21
22
|
- 'lib/norad_cli/support/manifest_spec.rb'
|
23
|
+
- 'lib/norad_cli/support/sectest_container.rb'
|
22
24
|
Metrics/BlockLength:
|
23
25
|
Exclude:
|
24
26
|
- 'spec/**/*'
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ The above site contains all the necessary information to install Docker on a Mac
|
|
15
15
|
This utility can be installed simply by:
|
16
16
|
|
17
17
|
```
|
18
|
-
$ gem install
|
18
|
+
$ gem install norad_cli
|
19
19
|
```
|
20
20
|
|
21
21
|
Once installed, a norad executable should be available.
|
@@ -101,14 +101,66 @@ The sectest subcommand is where general security test tool development occurs.
|
|
101
101
|
```
|
102
102
|
$ norad help sectest
|
103
103
|
Commands:
|
104
|
-
norad sectest build
|
105
|
-
norad sectest build:all SECTESTNAME
|
106
|
-
norad sectest build:
|
107
|
-
norad sectest
|
108
|
-
norad sectest
|
109
|
-
norad sectest
|
110
|
-
norad sectest
|
111
|
-
norad sectest
|
104
|
+
norad sectest build # Build all sectest images and specs for the entire repository
|
105
|
+
norad sectest build:all SECTESTNAME # Build sectest images for SECTESTNAME and all testing images for SECTESTNAME
|
106
|
+
norad sectest build:image SECTESTNAME # Build the docker image for the security test SECTESTNAME
|
107
|
+
norad sectest build:specs SECTESTNAME # Build the spec images (test images) for the security test SECTESTNAME
|
108
|
+
norad sectest execute SECTESTNAME # Execute SECTESTNAME against an arbitrary target
|
109
|
+
norad sectest help [COMMAND] # Describe subcommands or one specific subcommand
|
110
|
+
norad sectest scaffold TESTNAME # Create a new security test with standard files + testing
|
111
|
+
norad sectest seed # Create the containers.rb seed to import into the api
|
112
|
+
norad sectest spec # Run all rspec tests for the entire repo (all sectests)
|
113
|
+
norad sectest spec:image SECTESTNAME # Run the rspec tests for SECTESTNAME
|
114
|
+
norad sectest validate # Validate all manifest.yml and readme.md
|
115
|
+
norad sectest validate:image SECTESTNAME # Validate SECTESTNAME manifest.yml and readme.md
|
116
|
+
```
|
117
|
+
|
118
|
+
### Sectest Execute Command
|
119
|
+
|
120
|
+
The execute command enables users to run any SECTESTNAME container against an arbitrary target. To see the available options for the subcommand, run:
|
121
|
+
|
122
|
+
```
|
123
|
+
$ norad sectest -h execute
|
124
|
+
Usage:
|
125
|
+
norad execute SECTESTNAME
|
126
|
+
|
127
|
+
Options:
|
128
|
+
-d, [--debug], [--no-debug] # Turn on debugging messages (e.g. Docker build logs to stdout)
|
129
|
+
-f, [--format], [--no-format] # Print the JSON results formatted
|
130
|
+
-r, [--registry=REGISTRY] # The Docker registry for Docker images
|
131
|
+
# Default: norad-registry.cisco.com:5000
|
132
|
+
-v, [--version=VERSION] # The version of the sectest container to build
|
133
|
+
# Default: latest
|
134
|
+
|
135
|
+
Execute SECTESTNAME against an arbitrary target
|
136
|
+
```
|
137
|
+
|
138
|
+
Since the execute command runs arbitrary SECTESTNAME containers, it must support dynamically setting options for those containers. Therefore, the command supports gathering and printing help with any SECTESTNAME container. To get the help for a container run:
|
139
|
+
|
140
|
+
```
|
141
|
+
$ norad sectest execute SECTESTNAME -h
|
142
|
+
```
|
143
|
+
|
144
|
+
The execute subcommand reads the SECTESTNAME's manifest.yml to determine all available options. An example of this behavior is:
|
145
|
+
|
146
|
+
```
|
147
|
+
$ norad sectest execute sec-ops-sudo -h
|
148
|
+
Usage:
|
149
|
+
norad execute sec-ops-sudo
|
150
|
+
|
151
|
+
Options:
|
152
|
+
-d, [--debug], [--no-debug] # Turn on debugging messages (e.g. Docker build logs to stdout)
|
153
|
+
-f, [--format], [--no-format] # Print the JSON results formatted
|
154
|
+
-r, [--registry=REGISTRY] # The Docker registry for Docker images
|
155
|
+
# Default: norad-registry.cisco.com:5000
|
156
|
+
-v, [--version=VERSION] # The version of the sectest container to build
|
157
|
+
# Default: latest
|
158
|
+
-t, [--target=TARGET] # The IP or FQDN of the host to test
|
159
|
+
-u, [--ssh-user=SSH_USER] # If the sectest requires authentication, then the username for authentication
|
160
|
+
-p, [--port=PORT] # The port to use for testing
|
161
|
+
-k, [--ssh-key=SSH_KEY] # If the sectest requires authentication, then the path to the ssh key file
|
162
|
+
|
163
|
+
Execute sec-ops-sudo against an arbitrary target
|
112
164
|
```
|
113
165
|
|
114
166
|
## Development
|
data/WALKTHROUGH.md
CHANGED
@@ -44,18 +44,18 @@ Now, for this example, we will be creating a security test using the ping utilit
|
|
44
44
|
```
|
45
45
|
$ norad help sectest
|
46
46
|
Commands:
|
47
|
-
norad sectest build
|
48
|
-
norad sectest build:all SECTESTNAME
|
49
|
-
norad sectest build:image SECTESTNAME
|
50
|
-
norad sectest build:specs SECTESTNAME
|
51
|
-
norad sectest execute SECTESTNAME
|
52
|
-
norad sectest help [COMMAND]
|
53
|
-
norad sectest scaffold TESTNAME
|
54
|
-
norad sectest seed
|
55
|
-
norad sectest spec
|
56
|
-
norad sectest spec:image SECTESTNAME
|
57
|
-
norad sectest validate
|
58
|
-
norad sectest validate:image SECTESTNAME
|
47
|
+
norad sectest build # Build all sectest images and specs for the entire repository
|
48
|
+
norad sectest build:all SECTESTNAME # Build sectest images for SECTESTNAME and all testing images for SECTESTNAME
|
49
|
+
norad sectest build:image SECTESTNAME # Build the docker image for the security test SECTESTNAME
|
50
|
+
norad sectest build:specs SECTESTNAME # Build the spec images (test images) for the security test SECTESTNAME
|
51
|
+
norad sectest execute SECTESTNAME # Execute SECTESTNAME against an arbitrary target
|
52
|
+
norad sectest help [COMMAND] # Describe subcommands or one specific subcommand
|
53
|
+
norad sectest scaffold TESTNAME # Create a new security test with standard files + testing
|
54
|
+
norad sectest seed # Create the containers.rb seed to import into the api
|
55
|
+
norad sectest spec # Run all rspec tests for the entire repo (all sectests)
|
56
|
+
norad sectest spec:image SECTESTNAME # Run the rspec tests for SECTESTNAME
|
57
|
+
norad sectest validate # Validate all manifest.yml and readme.md
|
58
|
+
norad sectest validate:image SECTESTNAME # Validate SECTESTNAME manifest.yml and readme.md
|
59
59
|
```
|
60
60
|
|
61
61
|
To start off, we use the scaffold command to create all of the required files to create a security test. The scaffold subcommand includes several options:
|
@@ -3,28 +3,74 @@ require 'thor'
|
|
3
3
|
require 'git'
|
4
4
|
require 'docker'
|
5
5
|
require 'norad_cli/support/api_security_container_seed_script'
|
6
|
+
require 'norad_cli/support/sectest_container'
|
6
7
|
require 'rspec'
|
8
|
+
require 'json'
|
9
|
+
require 'rainbow'
|
7
10
|
|
8
11
|
class Sectest < Thor
|
9
12
|
include Thor::Actions
|
10
13
|
|
14
|
+
@reserved_sectest_args = { target: ['-t', 'The IP or FQDN of the host to test'],
|
15
|
+
ssh_user: ['-u', 'If the sectest requires authentication, then the username for authentication'],
|
16
|
+
ssh_key: ['-k', 'If the sectest requires authentication, then the path to the ssh key file'],
|
17
|
+
port: ['-p', 'The port to use for testing'],
|
18
|
+
service_username: ['-e', 'FILL ME IN'],
|
19
|
+
service_password: ['-r', 'FILL ME IN'],
|
20
|
+
web_service_protocolweb_service_url_blacklist: ['-b', 'FILL ME IN'],
|
21
|
+
web_service_auth_type: ['-a', 'FILL ME IN'],
|
22
|
+
web_service_starting_page_path: ['-g', 'FILL ME IN'],
|
23
|
+
web_service_login_form_username_field_name: ['-l', 'FILL ME IN'],
|
24
|
+
web_service_login_form_password_field_name: ['-m', 'FILL ME IN'] }
|
25
|
+
|
11
26
|
def self.source_root
|
12
27
|
File.join(File.dirname(File.expand_path(__FILE__)), '../templates/')
|
13
28
|
end
|
14
29
|
|
30
|
+
# Loads a manifest file depending on the command
|
31
|
+
# rubocop:disable Style/GuardClause
|
32
|
+
def self.load_manifest
|
33
|
+
@sectest_manifest = {}
|
34
|
+
|
35
|
+
# Set defaults just in case no manifest.yml to overwrite
|
36
|
+
@sectest_manifest['registry'] = 'norad-registry.cisco.com:5000'
|
37
|
+
@sectest_manifest['version'] = 'latest'
|
38
|
+
|
39
|
+
# Dynamically add options and description based on the needs of the sectest container
|
40
|
+
if %w(build build:all build:image build:specs execute).include?(ARGV[1]) && ARGV[2] && !ARGV[2].start_with?('-', '--')
|
41
|
+
# Read in the program arguments
|
42
|
+
if File.exist?("sectests/#{ARGV[2]}/manifest.yml")
|
43
|
+
@sectest_manifest = YAML.safe_load(File.read("sectests/#{ARGV[2]}/manifest.yml"))
|
44
|
+
else
|
45
|
+
puts Rainbow("Error: #{ARGV[2]} sectest does not exist or it is missing sectests/#{ARGV[2]}/manifest.yml").red
|
46
|
+
puts Rainbow('Exiting...').red
|
47
|
+
exit(1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
# rubocop:enable Style/GuardClause
|
52
|
+
|
53
|
+
def initialize(*args)
|
54
|
+
super
|
55
|
+
|
56
|
+
# Check if the command is being run from the repository root (all commands must be)
|
57
|
+
root_dir?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Load the manifest file if necessary
|
61
|
+
# Correct set default registry and version
|
62
|
+
load_manifest
|
63
|
+
|
15
64
|
desc 'scaffold TESTNAME', 'Create a new security test with standard files + testing'
|
16
65
|
option :test_type, aliases: '-t', default: 'whole_host', desc: 'The security test type, Options: [authenticated|web_application|brute_force|ssl_crypto|ssh_crypto|whole_host]'
|
17
|
-
option :registry, aliases: '-r', default: '
|
18
|
-
option :version, aliases: '-v', default: '
|
66
|
+
option :registry, aliases: '-r', default: @sectest_manifest['registry'], desc: 'The Docker registry to store docker images'
|
67
|
+
option :version, aliases: '-v', default: @sectest_manifest['version'], desc: 'The version of the security test'
|
19
68
|
option :base_image, aliases: '-b', default: 'norad-registry.cisco.com:5000/norad:0.0.1', desc: 'Base Docker image to use (i.e. FROM field in the Dockerfile)'
|
20
69
|
option :configurable, type: :boolean, aliases: '-c', desc: 'Is the security test configurable (e.g. Qualys username and password)'
|
21
70
|
def scaffold(sectest_name)
|
22
71
|
# Grab the current directory
|
23
72
|
repo_dir = Dir.pwd
|
24
73
|
|
25
|
-
# Check for the root_dir
|
26
|
-
root_dir?
|
27
|
-
|
28
74
|
# Check for valid test types
|
29
75
|
if !%w(authenticated web_application brute_force ssl_crypto ssh_crypto whole_host).include?(options[:test_type])
|
30
76
|
say("#{options[:test_type]} is not a supported test type", :red)
|
@@ -58,12 +104,10 @@ class Sectest < Thor
|
|
58
104
|
end
|
59
105
|
|
60
106
|
desc 'build', 'Build all sectest images and specs for the entire repository'
|
61
|
-
option :
|
62
|
-
option :
|
107
|
+
option :debug, aliases: '-d', type: :boolean, default: true, desc: 'Turn on debugging messages (e.g. Docker build logs to stdout)'
|
108
|
+
option :registry, aliases: '-r', default: @sectest_manifest['registry'], desc: 'The Docker registry for Docker images'
|
109
|
+
option :version, aliases: '-v', default: @sectest_manifest['version'], desc: 'The version of the sectest container to build'
|
63
110
|
def build
|
64
|
-
# Check for the root_dir
|
65
|
-
root_dir?
|
66
|
-
|
67
111
|
# Error check to ensure this is a plugin directory
|
68
112
|
Dir.glob('sectests/*').select do |f|
|
69
113
|
if File.directory? f
|
@@ -75,12 +119,10 @@ class Sectest < Thor
|
|
75
119
|
|
76
120
|
# Define arguments and options
|
77
121
|
desc 'build:image SECTESTNAME', 'Build the docker image for the security test SECTESTNAME'
|
78
|
-
option :
|
79
|
-
option :
|
122
|
+
option :debug, aliases: '-d', type: :boolean, default: true, desc: 'Turn on debugging messages (e.g. Docker build logs to stdout)'
|
123
|
+
option :registry, aliases: '-r', default: @sectest_manifest['registry'], desc: 'The Docker registry for Docker images'
|
124
|
+
option :version, aliases: '-v', default: @sectest_manifest['version'], desc: 'The version of the sectest container to build'
|
80
125
|
define_method 'build:image' do |name|
|
81
|
-
# Check for the root_dir
|
82
|
-
root_dir?
|
83
|
-
|
84
126
|
imgs_to_build = {}
|
85
127
|
imgs_to_build["sectests/#{name}"] = "#{options[:registry]}/#{name}:#{options[:version]}"
|
86
128
|
|
@@ -102,19 +144,17 @@ class Sectest < Thor
|
|
102
144
|
imgs_to_build.keys.reverse_each do |img_dir|
|
103
145
|
say("Building image #{img_dir}...", :green)
|
104
146
|
Docker::Image.build_from_dir(img_dir, t: imgs_to_build[img_dir]) do |v|
|
105
|
-
$stdout.puts v
|
147
|
+
$stdout.puts v if options[:debug]
|
106
148
|
end
|
107
149
|
end
|
108
150
|
end
|
109
151
|
|
110
152
|
# Define arguments and options
|
111
153
|
desc 'build:specs SECTESTNAME', 'Build the spec images (test images) for the security test SECTESTNAME'
|
112
|
-
option :
|
113
|
-
option :
|
154
|
+
option :debug, aliases: '-d', type: :boolean, default: true, desc: 'Turn on debugging messages (e.g. Docker build logs to stdout)'
|
155
|
+
option :registry, aliases: '-r', default: @sectest_manifest['registry'], desc: 'The Docker registry for Docker images'
|
156
|
+
option :version, aliases: '-v', default: @sectest_manifest['version'], desc: 'The version of the sectest container to build'
|
114
157
|
define_method 'build:specs' do |name|
|
115
|
-
# Check for the root_dir
|
116
|
-
root_dir?
|
117
|
-
|
118
158
|
imgs_to_build = {}
|
119
159
|
imgs_to_build["#{File.expand_path(File.dirname(__FILE__))}/../templates/spec/support/Dockerfile.testserver"] = 'docker-images-test-results-server:latest'
|
120
160
|
imgs_to_build["#{File.expand_path(File.dirname(__FILE__))}/../templates/spec/support/Dockerfile.ubuntu_ssh"] = 'docker-images-test-ubuntu-ssh-server:latest'
|
@@ -131,7 +171,7 @@ class Sectest < Thor
|
|
131
171
|
say("Building image #{img_dir}...", :green)
|
132
172
|
docker_file = img_dir.split('/')[-1]
|
133
173
|
Docker::Image.build_from_dir(img_dir.gsub(docker_file, ''), dockerfile: docker_file, t: imgs_to_build[img_dir]) do |v|
|
134
|
-
$stdout.puts v
|
174
|
+
$stdout.puts v if options[:debug]
|
135
175
|
end
|
136
176
|
end
|
137
177
|
|
@@ -141,12 +181,10 @@ class Sectest < Thor
|
|
141
181
|
|
142
182
|
# Define arguments and options
|
143
183
|
desc 'build:all SECTESTNAME', 'Build sectest images for SECTESTNAME and all testing images for SECTESTNAME'
|
144
|
-
option :
|
145
|
-
option :
|
184
|
+
option :debug, aliases: '-d', type: :boolean, default: true, desc: 'Turn on debugging messages (e.g. Docker build logs to stdout)'
|
185
|
+
option :registry, aliases: '-r', default: @sectest_manifest['registry'], desc: 'The Docker registry for Docker images'
|
186
|
+
option :version, aliases: '-v', default: @sectest_manifest['version'], desc: 'The version of the sectest container to build'
|
146
187
|
define_method 'build:all' do |name|
|
147
|
-
# Check for the root_dir
|
148
|
-
root_dir?
|
149
|
-
|
150
188
|
# Build the sectest image
|
151
189
|
send('build:image', name)
|
152
190
|
|
@@ -154,13 +192,56 @@ class Sectest < Thor
|
|
154
192
|
send('build:specs', name)
|
155
193
|
end
|
156
194
|
|
195
|
+
# Dynamically add options and description based on the needs of the sectest container
|
196
|
+
if ARGV[1] == 'execute' && ARGV[2] && !ARGV[2].start_with?('-', '--')
|
197
|
+
desc "execute #{ARGV[2]}", "Execute #{ARGV[2]} against an arbitrary target"
|
198
|
+
|
199
|
+
# Dynamically create options
|
200
|
+
@sectest_manifest['prog_args'].scan(/{(.*?)}/).each do |ar|
|
201
|
+
if @reserved_sectest_args.key?(ar[0].to_sym)
|
202
|
+
option ar[0].to_sym, aliases: @reserved_sectest_args[ar[0].to_sym][0], desc: @reserved_sectest_args[ar[0].to_sym][1]
|
203
|
+
else
|
204
|
+
option ar[0].to_sym
|
205
|
+
end
|
206
|
+
end
|
207
|
+
else
|
208
|
+
desc 'execute SECTESTNAME', 'Execute SECTESTNAME against an arbitrary target'
|
209
|
+
end
|
210
|
+
option :debug, aliases: '-d', type: :boolean, default: false, desc: 'Turn on debugging messages (e.g. Docker build logs to stdout)'
|
211
|
+
option :format, aliases: '-f', type: :boolean, default: false, desc: 'Print the JSON results formatted'
|
212
|
+
option :registry, aliases: '-r', default: @sectest_manifest['registry'], desc: 'The Docker registry for Docker images'
|
213
|
+
option :version, aliases: '-v', default: @sectest_manifest['version'], desc: 'The version of the sectest container to build'
|
214
|
+
def execute(sectest_name)
|
215
|
+
# Ensure the results server is built by building the images specs (code reuse)
|
216
|
+
send('build:specs', sectest_name)
|
217
|
+
|
218
|
+
# Build the sectest image if necessary
|
219
|
+
send('build:image', sectest_name)
|
220
|
+
|
221
|
+
# Allocate an instance of the sectest
|
222
|
+
sectest_instance = NoradCli::SecTestContainer.new(ARGV[2], options)
|
223
|
+
|
224
|
+
# Start the test
|
225
|
+
sectest_instance.start
|
226
|
+
|
227
|
+
# Print any debugging
|
228
|
+
sectest_instance.output(options[:target]) if options[:debug]
|
229
|
+
|
230
|
+
# Get the results
|
231
|
+
results = sectest_instance.results
|
232
|
+
|
233
|
+
say('Results are:', :green)
|
234
|
+
formatted_results = options[:format] ? JSON.pretty_generate(JSON.parse(results)) : results
|
235
|
+
puts formatted_results
|
236
|
+
|
237
|
+
# Cleanup the sectest container
|
238
|
+
sectest_instance.shutdown
|
239
|
+
end
|
240
|
+
|
157
241
|
desc 'spec:image SECTESTNAME', 'Run the rspec tests for SECTESTNAME'
|
158
242
|
option :verbose, aliases: '-v', type: :boolean, desc: 'Turn on verbose logging'
|
159
243
|
option :debug, aliases: '-d', type: :boolean, desc: 'Turn on debugging'
|
160
244
|
define_method 'spec:image' do |name|
|
161
|
-
# Check for the root_dir
|
162
|
-
root_dir?
|
163
|
-
|
164
245
|
# Set environment variables
|
165
246
|
if options[:verbose]
|
166
247
|
ENV['ENABLE_LOGS'] = 'true'
|
@@ -189,9 +270,6 @@ class Sectest < Thor
|
|
189
270
|
option :verbose, aliases: '-v', type: :boolean, default: false, desc: 'Turn on verbose logging'
|
190
271
|
option :debug, aliases: '-d', type: :boolean, desc: 'Turn on debugging'
|
191
272
|
def spec
|
192
|
-
# Check for the root_dir
|
193
|
-
root_dir?
|
194
|
-
|
195
273
|
# Error check to ensure this is a plugin directory
|
196
274
|
Dir.glob('sectests/*').select do |f|
|
197
275
|
if File.directory? f
|
@@ -205,18 +283,12 @@ class Sectest < Thor
|
|
205
283
|
option :seedfile, aliases: '-s', type: :string, default: './containers.rb', desc: 'The name of the seed file to generate'
|
206
284
|
option :docsite, aliases: '-d', type: :string, default: 'https://norad.gitlab.io/docs/', desc: 'Set the documentation site'
|
207
285
|
def seed
|
208
|
-
# Check for the root_dir
|
209
|
-
root_dir?
|
210
|
-
|
211
286
|
# Generate the seed file
|
212
287
|
SeedGenerator.process_manifests(options[:seedfile], options[:docsite])
|
213
288
|
end
|
214
289
|
|
215
290
|
desc 'validate:image SECTESTNAME', 'Validate SECTESTNAME manifest.yml and readme.md'
|
216
291
|
define_method 'validate:image' do |name|
|
217
|
-
# Check for the root_dir
|
218
|
-
root_dir?
|
219
|
-
|
220
292
|
# Validate the readme file
|
221
293
|
ENV['sectest_name'] = name
|
222
294
|
RSpec.clear_examples
|
@@ -229,13 +301,10 @@ class Sectest < Thor
|
|
229
301
|
|
230
302
|
desc 'validate', 'Validate all manifest.yml and readme.md'
|
231
303
|
def validate
|
232
|
-
# Check for the root_dir
|
233
|
-
root_dir?
|
234
|
-
|
235
304
|
# Error check to ensure this is a plugin directory
|
236
305
|
Dir.glob('sectests/*').select do |f|
|
237
306
|
if File.directory? f
|
238
|
-
#
|
307
|
+
# Validate manifest and readme for the sectest
|
239
308
|
send('validate:image', f.split('/')[-1])
|
240
309
|
end
|
241
310
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module NoradCli
|
3
|
+
class ResultsServer
|
4
|
+
attr_accessor :container
|
5
|
+
|
6
|
+
def initialize(test_results_server_image)
|
7
|
+
@container = Docker::Container.create(
|
8
|
+
Image: test_results_server_image,
|
9
|
+
HostConfig: { PublishAllPorts: true }
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
@container.start
|
15
|
+
sleep 5 # sleep rather than wait since we are daemonizing a containe
|
16
|
+
refresh
|
17
|
+
end
|
18
|
+
|
19
|
+
def refresh
|
20
|
+
@container.refresh! # get more details
|
21
|
+
end
|
22
|
+
|
23
|
+
def shutdown
|
24
|
+
@container.stop
|
25
|
+
@container.delete(force: true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def host_port
|
29
|
+
@container.info['NetworkSettings']['Ports']['3000/tcp'].first['HostPort']
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'norad_cli/support/results_server.rb'
|
3
|
+
require 'net/http'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'rainbow'
|
6
|
+
|
7
|
+
module NoradCli
|
8
|
+
class SecTestContainer
|
9
|
+
attr_accessor :container
|
10
|
+
attr_accessor :assessment_id
|
11
|
+
attr_accessor :sectest_image
|
12
|
+
attr_accessor :results_server
|
13
|
+
|
14
|
+
def initialize(sectest_name, options)
|
15
|
+
# Generate a random assessment id
|
16
|
+
@assessment_id = SecureRandom.hex(32)
|
17
|
+
|
18
|
+
@sectest_image = "#{options['registry']}/#{sectest_name}:#{options[:version]}"
|
19
|
+
|
20
|
+
# Create a results server
|
21
|
+
@results_server = NoradCli::ResultsServer.new('docker-images-test-results-server')
|
22
|
+
|
23
|
+
ENV['ENABLE_LOGS'] = 'true'
|
24
|
+
env = [
|
25
|
+
'NORAD_ROOT=http://results:3000',
|
26
|
+
%(ASSESSMENT_PATHS=[{"id":"singletest", "assessment": "/results/#{@assessment_id}"}]),
|
27
|
+
'NORAD_SECRET=1234'
|
28
|
+
]
|
29
|
+
|
30
|
+
# Create the container
|
31
|
+
@container = Docker::Container.create(Image: @sectest_image,
|
32
|
+
Cmd: prog_args(sectest_name, options),
|
33
|
+
Env: env,
|
34
|
+
HostConfig: { Links: ["#{@results_server.container.id}:results"] })
|
35
|
+
end
|
36
|
+
|
37
|
+
# Format the prog_args appropriately for the container
|
38
|
+
def prog_args(sectest_name, options)
|
39
|
+
# Grab the program arguments (minus other function options)
|
40
|
+
# Options is a Thor::CoreExt::HashWithIndifferentAccess (except does not work)
|
41
|
+
prog_arg_hash = options.each_with_object({}) do |(k, v), hsh|
|
42
|
+
hsh[k.to_sym] = v unless k == 'debug' || k == 'registry' || k == 'version'
|
43
|
+
end
|
44
|
+
|
45
|
+
# Load the prog_arg format
|
46
|
+
sectest_options ||= YAML.safe_load(File.read("sectests/#{sectest_name}/manifest.yml"))
|
47
|
+
|
48
|
+
# Load an ssh key if necess
|
49
|
+
prog_arg_hash[:ssh_key] = load_ssh_key(prog_arg_hash[:ssh_key]) if prog_arg_hash.key?(:ssh_key)
|
50
|
+
|
51
|
+
# Fill out the prog_args and return
|
52
|
+
begin
|
53
|
+
format(sectest_options['prog_args'], prog_arg_hash).split(' ')
|
54
|
+
rescue KeyError
|
55
|
+
puts Rainbow('Error: The containers required arguments were not set.').red
|
56
|
+
puts Rainbow("Arguments in %{} should be set: #{sectest_options['prog_args']}").red
|
57
|
+
puts Rainbow("Arguments given: #{prog_arg_hash}").red
|
58
|
+
puts Rainbow("Run 'norad sectest execute #{sectest_name} -h' to see how to set arguments!").red
|
59
|
+
puts Rainbow('Exiting...').red
|
60
|
+
exit(1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def start
|
65
|
+
# Start the results server container
|
66
|
+
@results_server.start
|
67
|
+
|
68
|
+
# Start the sectest container
|
69
|
+
@container.start
|
70
|
+
@container.wait(60 * 10)
|
71
|
+
end
|
72
|
+
|
73
|
+
def output(target)
|
74
|
+
# Output container logs for debugging
|
75
|
+
@container.stop
|
76
|
+
c_state = @container.json['State']
|
77
|
+
|
78
|
+
# Print the entire state regardless of error or not to aid in debugging
|
79
|
+
puts Rainbow("[DEBUG] Container #{@sectest_image}'s Final State").green
|
80
|
+
puts Rainbow('-------------------------').green
|
81
|
+
c_state.each do |key, value|
|
82
|
+
puts Rainbow("#{key}: #{value}").green
|
83
|
+
end
|
84
|
+
|
85
|
+
puts Rainbow("\n[DEBUG] Logs for target #{@sectest_image} run against #{target}:").green
|
86
|
+
|
87
|
+
# Print logs regardless of ExitCode
|
88
|
+
puts Rainbow(@container.logs(stdout: true, stderr: true)).green
|
89
|
+
end
|
90
|
+
|
91
|
+
def results
|
92
|
+
# Get the results
|
93
|
+
url = "http://localhost:#{@results_server.host_port}/results?assessment_id=#{@assessment_id}"
|
94
|
+
uri = URI.parse(url)
|
95
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
96
|
+
request = Net::HTTP::Get.new(uri)
|
97
|
+
response = http.request(request)
|
98
|
+
if response.code == '200'
|
99
|
+
response.body
|
100
|
+
else
|
101
|
+
puts Rainbow('Error retrieving results\nExiting...').red
|
102
|
+
shutdown
|
103
|
+
exit(1)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def shutdown
|
108
|
+
# Stop the sectest image container and delete
|
109
|
+
@container.stop
|
110
|
+
@container.delete(force: true)
|
111
|
+
|
112
|
+
# Cleanup/Garbage collect the results server
|
113
|
+
@results_server.shutdown
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
# Replace ssh key file with encoded ssh key
|
119
|
+
def load_ssh_key(ssh_key_file)
|
120
|
+
if File.exist?(ssh_key_file)
|
121
|
+
Base64.strict_encode64(File.read(ssh_key_file))
|
122
|
+
else
|
123
|
+
puts Rainbow("Error: SSH Key file: #{ssh_key_file} does not exist!\nExiting..\n").red
|
124
|
+
exit(1)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
data/lib/norad_cli/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: norad_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Blake Hitchcock
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-03-
|
13
|
+
date: 2017-03-20 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: git
|
@@ -197,6 +197,8 @@ files:
|
|
197
197
|
- lib/norad_cli/support/api_security_container_seed_script.rb
|
198
198
|
- lib/norad_cli/support/manifest_spec.rb
|
199
199
|
- lib/norad_cli/support/readme_spec.rb
|
200
|
+
- lib/norad_cli/support/results_server.rb
|
201
|
+
- lib/norad_cli/support/sectest_container.rb
|
200
202
|
- lib/norad_cli/templates/.gitignore
|
201
203
|
- lib/norad_cli/templates/.rspec
|
202
204
|
- lib/norad_cli/templates/CONTRIBUTING.md
|
@@ -275,7 +277,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
275
277
|
version: '0'
|
276
278
|
requirements: []
|
277
279
|
rubyforge_project:
|
278
|
-
rubygems_version: 2.6.
|
280
|
+
rubygems_version: 2.6.11
|
279
281
|
signing_key:
|
280
282
|
specification_version: 4
|
281
283
|
summary: Command line interface for norad.
|