# Creating a Security Test This document walks through the steps for creating a security test and integrating it into a Norad development box. It leverages the norad command line tool, which can be installed by doing: ``` $ gem install norad_cli ``` Other dependencies include a working [Norad Development Box](https://norad-dev-box-url) and [Docker](https://www.docker.com/community-edition). # Security Test Development Steps Assuming the dependencies described above are installed. Creating and testing a security test is straightforward. First, decided if you will be starting an entirely new repository to house security tests. If a new repository will be created, decided on a descriptive name, for instance asig-security, and use the norad repo create command: ``` $ norad repo create asig-security Initializing a new norad security test repository create asig-security/base create asig-security/spec License the repo under Apache 2? y Who is the copyright holder (Default: Cisco Systems, Inc.)? create asig-security/LICENSE create asig-security/.gitlab.ci.yml create asig-security/.gitignore create asig-security/CONTRIBUTING.md create asig-security/README.md ``` The command asks if the tests should be licensed under Apache 2 and for the copyright holder. When in doubt, answer no to the licensing question. The 'repo create' command creates a new git repository conforming to the Norad standards for layout and ci. If you will not be creating a new repostiory simply clone an existing Norad security test repo. ``` $ git clone git@norad-gitlab.cisco.com:norad/asig-hackathon-security-tests.git ``` Once a repository is selected, either by creating a new one or cloning an existing one, change into the root of that repository before starting test development. ``` $ cd asig-hackathon-security-tests ``` Now, for this example, we will be creating a security test using the ping utility. The norad CLI tool includes a subcommand, sectest, to aid developers with creating, testing, validating, and integrating a security tool into Norad. Help for the sectest subcommand is available: ``` $ norad help sectest Commands: norad sectest build # Build all sectest images and specs for the entire repository norad sectest build:all SECTESTNAME # Build sectest images for SECTESTNAME and all testing images for SECTESTNAME norad sectest build:image SECTESTNAME # Build the docker image for the security test SECTESTNAME norad sectest build:specs SECTESTNAME # Build the spec images (test images) for the security test SECTESTNAME norad sectest execute SECTESTNAME ARGUMENTS # Executes the specified security test SECTESTNAME w/ ARGUMENTS norad sectest help [COMMAND] # Describe subcommands or one specific subcommand norad sectest scaffold TESTNAME # Create a new security test with standard files + testing norad sectest seed # Create the containers.rb seed to import into the api norad sectest spec # Run all rspec tests for the entire repo (all sectests) norad sectest spec:image SECTESTNAME # Run the rspec tests for SECTESTNAME norad sectest validate # Validate all manifest.yml and readme.md norad sectest validate:image SECTESTNAME # Validate SECTESTNAME manifest.yml and readme.md ``` 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: ``` $ norad sectest scaffold --help Usage: norad scaffold TESTNAME Options: -t, [--test-type=TEST_TYPE] # The security test type, Options: [authenticated|web_application|brute_force|ssl_crypto|ssh_crypto|whole_host] # Default: whole_host -r, [--registry=REGISTRY] # The Docker registry to store docker images # Default: norad-registry.cisco.com:5000 -v, [--version=VERSION] # The version of the security test # Default: latest -b, [--base-image=BASE_IMAGE] # Base Docker image to use (i.e. FROM field in the Dockerfile) # Default: norad-registry.cisco.com:5000/norad:0.0.1 -c, [--configurable], [--no-configurable] # Is the security test configurable (e.g. Qualys username and password) Create a new security test with standard files + testing ``` First, the TESTNAME will be whatever name you would like to assign to the security test. For this example, we will use pinger. The TESTNAME should not collide with any folder names under the sectests folder. Next, you must decide on the security test's test-type, registry, version, base-image, and if it will be configurable options. The test-type defines ......., available test tests are: * authenticated: * web_application: * brute_force: * ssl_crypto: * ssh_crypto: * whole_host: For the pinger security test, we are simply checking the host, so the test-type be the default value, whole_host. The registry option permits the user to change where the security test will be stored, our test can use the default value (and most tests will). The version and base-image options allow you to set the initial version of the security test and a Docker image to start from. The default options are safe to use, unless, for the base image, you know of a different Docker image to use for a base. The configurable option sets whether the sectest will have additional command line options w/ defaults set in its manifest.yml. This new utility will not have any additional options, so there is no need to use the -c flag. Therefore, for the new pinger sectest, the scaffold command is: ``` $ norad sectest scaffold pinger create sectests/pinger/Dockerfile create sectests/pinger/README.md create sectests/pinger/manifest.yml create sectests/pinger/pinger-wrapper.rb create spec/pinger/pinger_spec.rb create spec/pinger/targets/Dockerfile.secure create spec/pinger/targets/Dockerfile.vulnerable ``` The scaffolding creates a new directory in sectests, pinger, housing the files necessary to create a new test. These files are: * pinger/Dockerfile: alter this file to install new software packages * pinger/README.md: describes the new security test and any options. This file is used to generate user facing documentation * pinger/manifest.yml: YAML file to describe the security test. This file is used for importing information into a Norad instance * pinger/pinger-wrapper.rb: A ruby script where execution begins when the security test is executed. Scaffolding also creates tests and test targets for the new security test. The test targets, a secure and a vulnerable target, are defined by the Dockerfiles in spec/pinger/targets/. The [rspec](http://rspec.info/) tests to run for the new security test (to check if it is working properly) are defined in the file spec/pinger/pinger_spec.rb. After a new security test is scaffolded, a developer alters the corresponding Dockerfile, (e.g. sectests/pinger/Dockerfile) to install any required dependencies. For instance, maybe you need to install a new tool (e.g. nmap) to develop the security test. In this case, the developer would add a new RUN command into the Dockerfile (sectests/pinger/Dockerfile): ``` RUN apt-get -y install nmap ``` Please refer to the [Dockerfile reference](https://docs.docker.com/engine/reference/builder/#parser-directives) for all the available commands. Once you have added all of the necessary commands to the new Dockerfile (sectests/pinger/Dockerfile), then you can try building the new sectest container with the norad sectest build command from the repo's root directory. For example, to build our test security tool: ``` $ norad sectest build:image pinger Building image pinger... {"stream":"Step 1/5 : FROM norad-registry.cisco.com:5000/norad:0.0.1\n"} {"stream":" ---\u003e e01961118951\n"} {"stream":"Step 2/5 : COPY pinger-wrapper.rb /pinger-wrapper.rb\n"} {"stream":" ---\u003e fde55e5e2b8a\n"} {"stream":"Removing intermediate container 6ab5c3c7f4c1\n"} {"stream":"Step 3/5 : RUN chmod 755 /pinger-wrapper.rb\n"} {"stream":" ---\u003e Running in 2431f94971e2\n"} {"stream":" ---\u003e 3294126d1b23\n"} {"stream":"Removing intermediate container 2431f94971e2\n"} {"stream":"Step 4/5 : WORKDIR /\n"} {"stream":" ---\u003e 92ef3e0a4eb9\n"} {"stream":"Removing intermediate container c83183b4a0f7\n"} {"stream":"Step 5/5 : ENTRYPOINT /pinger-wrapper.rb\n"} {"stream":" ---\u003e Running in 8b83ca556636\n"} {"stream":" ---\u003e 53b3a4bfbbf9\n"} {"stream":"Removing intermediate container 8b83ca556636\n"} {"stream":"Successfully built 53b3a4bfbbf9\n"} ``` The resulting image is stored on your machine and can be viewed by using docker image list command: ``` $ docker image list REPOSITORY TAG IMAGE ID CREATED SIZE norad-registry.cisco.com:5000/pinger latest 53b3a4bfbbf9 About a minute ago 496 MB ``` After that, update the manifest.yml (e.g. sectests/pinger/manifest.yml) and README.md (e.g. sectests/pinger/README.md) files. Updates to these files can be validated using the sectest validate commands: ``` $ norad sectest validate:image pinger Looking for README.md in: sectests/pinger... OK Testing for variant READMEs... No variants for this tool OK . Finished in 0.00142 seconds (files took 0.14096 seconds to load) 1 example, 0 failures Looking for README.md in: sectests/pinger... OK Testing for variant READMEs... No variants for this tool OK .Looking for valid manifest in: ... OK Testing for valid name... OK Testing for existence of version... OK Testing for existence of registry... OK Testing for valid prog args... OK Testing for test types... OK Testing for configurability... OK Testing for variants... No variants for this repo OK . Finished in 0.00587 seconds (files took 0.1604 seconds to load) 3 examples, 0 failures ``` Next, the security tools wrapper script should be updated to perform the actual security checks (e.g. sectests/pinger/pinger-wrapper.rb). The wrapper script is responsible for spawning any tools (e.g. ping, nmap, etc.), parsing the results, and posting them back to Norad. For the pinger example, we can use an already prepared wrapper script: ```ruby #!/usr/bin/env ruby require 'norad_beacon' def run(args) timeout = 3600 # set timeout for runner to 1 hour # Allocate a runner runner = NoradBeacon::Runner.new('ping', [ '-c', '1', args].flatten, timeout) # Execute the runner runner.execute(true) # Ensure the tool created results runner.parse_results do |fh| id = '1' title = 'Ping Test' description = 'This test will ping a host to determine if it is alive.' cvss = 'no_impact' raw_output = fh.read() + "Exit Code: #{runner.exit_code}" status = raw_output =~ /100% packet loss/ ? 'fail' : 'pass' # Add the result to the runner's result set # Note: Multiple results can be added, they will show up individually runner.result_set.add(NoradBeacon::Result.new(id, status, raw_output, title, description, cvss)) end rescue Exception => e puts "An exception occurred: #{e.inspect}" puts e.backtrace status = 'error' raw_output = 'Internal error occurred' title = 'Failed to run the tests' description = 'Internal error occurred' runner.result_set.add(NoradBeacon::Result.new('0', status, raw_output, title, description)) ensure # Save the results to Norad NoradBeacon::NoradAPI.post_results(runner.result_set) end run(ARGV) ``` For this walkthrough, copy the above code to sectests/pinger/pinger-wrapper.rb. The pinger docker image will need to rebuilt to incorporate the changes to the wrapper script. Simply rerun the norad sectest build:image pinger command. After the build succeeds, it is time to test the new security tests. There are two methods for testing. The first method is to run the security test container against a target, and the second is to use Norad's rspec testing system. To run against a target, use the command: FIXME: norad sectest execute SECTESTNAME ARGUMENTS The second method is more involved but necessary for the image to be merged into Norad's approved images. Norad's CI system runs validation tests against security tests to ensure the test performs as expected. The CI system automatically spins up a vulnerable image (defined in spec/SECTESTNAME/targets/Dockerfile.vulnerable) and a secure image (defined in spec/SECTESTNAME/targets/Dockerfile.secure) where SECTESTNAME is the name of the security test (e.g. pinger). The secure and vulnerable images are defined by the developer. For your testing, please define a vulnerable and a secure container by altering their respective Dockerfiles. When you are ready to build these two images, norad sectest build:specs command can be used: ``` $ norad sectest build:specs pinger ..... Building image spec/pinger/targets/Dockerfile.secure... {"stream":"Step 1/5 : FROM ubuntu:14.04\n"} {"stream":" ---\u003e 7c09e61e9035\n"} {"stream":"Step 2/5 : RUN apt-get -y update\n"} {"stream":" ---\u003e Using cache\n"} {"stream":" ---\u003e b2bc3253c8c3\n"} {"stream":"Step 3/5 : RUN apt-get -y install openssh-server\n"} {"stream":" ---\u003e Using cache\n"} {"stream":" ---\u003e b23b83d7dc9c\n"} {"stream":"Step 4/5 : RUN mkdir /var/run/sshd \u0026\u0026 chmod 0755 /var/run/sshd\n"} {"stream":" ---\u003e Using cache\n"} {"stream":" ---\u003e 3f7fe39c33ec\n"} {"stream":"Step 5/5 : CMD /usr/sbin/sshd -D\n"} {"stream":" ---\u003e Using cache\n"} {"stream":" ---\u003e 28da9b415f86\n"} {"stream":"Successfully built 28da9b415f86\n"} .... ``` The tests to run against the secure and vulnerable images are defined by the file spec/SECTESTNAME/SECTESTNAME_spec.rb (e.g. spec/pinger/pinger_spec.rb). These tests are written in (rspec)[http://rspec.info/], and the scaffolding command created some example tests for reference. For this example, the default rspec tests will work. For now, run the nroad cli testing command to view the results: ``` $ norad sectest spec:image pinger ``` At the end of the run, you will see: ``` Finished in 33.22 seconds (files took 0.14089 seconds to load) 6 examples, 1 failure Failed examples: rspec ./spec/pinger/pinger_spec.rb:24 # Pinger for vulnerable machine should report a failure ``` Digging into the reason why the test "Pinger for vulnerable machine should report a failure" failed, the console output shows: ``` expected: "fail" got: "pass" ``` Since this test is merely running a ping to check connectivity, the vulnerable host is up during the test, so the parsing logic in sectests/pinger/pinger-wrapper.rb, specifically: ```ruby raw_output = fh.read() + "Exit Code: #{runner.exit_code}" status = raw_output =~ /100% packet loss/ ? 'fail' : 'pass' ``` is marking the status incorrectly for the vulnerable machine. If you would like to see verbose logging from tests (includes stdout/stderr from the containers as wrapper scripts), run use the spec:image verbose flag: ``` $ norad sectest spec:image -v pinger ``` Once all of the tests have passed, it is time to generate a seed file to import into a development machine. A seed file can be generated by running: ``` $ norad sectest seed ``` The seed command generates a file, `containers.rb`, which can be used to import the tests into the Norad Web Application (e.g. [Norad Dev-Box](https://gitlab.com/norad/dev-box) or production machine). Follow the instructions below to import the file: * Upload `containers.rb` to your dev-box or production machine. * From the directory containing the `api` code run the following: $ rails runner path/to/your/containers.rb # Helpful Docker Commands * `docker images` - Determine the images available * `docker ps` - Show containers currently running * `docker kill 8b83ca556636` - Remove a currently running container # References * [Docker Command Line Reference](https://docs.docker.com/engine/reference/commandline/docker/) * [Dockerfile Reference](https://docs.docker.com/engine/reference/builder/) * [RSpec](http://rspec.info/)