Skip to content
SAF Advanced InSpec Profile Developer CourseSAF Advanced InSpec Profile Developer Course
MITRE InSpec Advanced Profile Developer Course
Course
Resources
Installation
  • Course

    • 1. Course Overview
      • 2. Review the Fundamentals
        • 3. Practice the Fundamentals
          • 4. Tools for Automation
            • 5. Automate Security Testing
              • 6. Explore InSpec Resources
                • 7. Local vs Built-in Resources
                  • 8. Create a Custom Resource - The Git Example
                    • 9. Create a Custom Resource - The Docker Example
                      • 9.1. Create new profile and setup docker files
                        • 9.2. Develop controls to test/run profile
                          • 9.3. Rewrite test to utilize resource
                            • 9.4. Develop docker resource
                            • 10. Writing Plural Resources
                              • 11. Dissecting Resources
                                • 12. Exercise - Develop your own resources
                                  • 13. Add Your Resource to InSpec
                                    • 14. Custom Resource Examples from InSpec

                                    9. Create a Custom Resource - The Docker Example

                                    June 7, 2022About 3 min

                                    On This Page
                                    • 9.1. Create new profile and setup docker files
                                    • 9.2. Develop controls to test/run profile
                                    • 9.3. Rewrite test to utilize resource
                                    • 9.4. Develop docker resource

                                    Now let's try a more complicated example. Let's say we want to create a resource that can parse a docker-compose file.

                                    # 9.1. Create new profile and setup docker files

                                    First, let's write our docker compose file docker-compose.yml

                                    version: '3'
                                    services:
                                      workstation:
                                        container_name: workstation
                                        image: learnchef/inspec_workstation
                                        stdin_open: true
                                        tty: true
                                        links:
                                          - target
                                        volumes:
                                          - .:/root
                                      target:
                                        image: learnchef/inspec_target
                                        stdin_open: true
                                        tty: true
                                    

                                    We will continue writing our controls to check against this docker file:

                                    inspec init profile docker-workstations
                                    

                                    # 9.2. Develop controls to test/run profile

                                    In the controls/example.rb file, write the control:

                                    describe yaml('file_name') do
                                      its('setting') { should_not eq 'value' }
                                    end
                                    

                                    We need to replace the file_name above with the location of the docker-compose.yml file. We also need to change the setting to grab the tag we want to retrieve. Finally we need to change value with the actual value as shown in the docker compose file.

                                    describe yaml('/path/to/docker-compose.yml') do
                                      its(['services', 'workstation', 'image']) { should eq 'learnchef/inspec_workstation' }
                                      its(['services', 'workstation', 'volumes']) { should cmp '.:/root' }
                                    end
                                    

                                    Now if we test this control using the following command we should see all the tests pass.

                                    inspec exec docker-workstations
                                    

                                    # 9.3. Rewrite test to utilize resource

                                    Going back to the control, we will write it using a resource that doesn't exist called docker-compose-config that is going to take a path as a parameter.

                                    describe yaml('/path/to/docker-compose.yml') do
                                      its(['services', 'workstation', 'image']) { should eq 'learnchef/inspec_workstation' }
                                      its(['services', 'workstation', 'volumes']) { should cmp '.:/root' }
                                    end
                                    
                                    describe docker_compose_config('/path/to/docker-compose.yml') do
                                      its('services.workstation.image') { should eq 'learnchef/inspec_workstation' }
                                      its('services.workstation.volumes') { should cmp '.:/root' }
                                    end
                                    

                                    Now we should see an error if we go back to terminal and run the same command to execute a scan

                                    inspec exec docker-workstations
                                    

                                    We should get an error saying the docker_compose_config method does not yet exist. That's because we have not yet defined this resource.

                                    # 9.4. Develop docker resource

                                    In the libraries directory of the profile we will make a docker_compose_config.rb file, , the content of the file should look like this:

                                    # encoding: utf-8
                                    # copyright: 2019, The Authors
                                    
                                    class DockerComposeConfig < Inspec.resource(1)
                                    
                                      name 'docker_compose_config'
                                    
                                    end
                                    

                                    Using InSpec Init to Create the Resource

                                    Alternatively, you can use inspec init resource <your-resource-name> to create the template for your custom resource. However, make sure you check that the "lib" folder is renamed to "libraries", or that InSpec recognizes the location of your custom resource.

                                    Now when we save and run the profile again using:

                                    inspec exec docker-workstations
                                    

                                    We will get an error saying we gave it the wrong number of arguments: was given 1 but expected 0. This is because every class in Ruby that has a parameter must have an initialize function to accept that parameter.

                                    # encoding: utf-8
                                    # copyright: 2019, The Authors
                                    
                                    class DockerComposeConfig < Inspec.resource(1)
                                    
                                      name 'docker_compose_config'
                                    
                                      def initialize(path)
                                        @path = path
                                      end
                                    
                                    end
                                    

                                    Now let's run the profile once more.

                                    inspec exec docker-workstations
                                    

                                    This time the profile runs, but we get a message that the docker_compose_config resource does not have the services method. So let's define that method now:

                                    # encoding: utf-8
                                    # copyright: 2019, The Authors
                                    
                                    class DockerComposeConfig < Inspec.resource(1)
                                    
                                      name 'docker_compose_config'
                                    
                                      def initialize(path)
                                        @path = path
                                      end
                                    
                                      def services
                                    
                                      end
                                    
                                    end
                                    

                                    Start by just defining the services method. Then, let's run the profile once more.

                                    inspec exec docker-workstations
                                    

                                    Now we got a different failure that tells us a nil value was returned. So now we will go ahead and define the services method. We will use an already existing InSpec resource to parse the path file.

                                    # encoding: utf-8
                                    # copyright: 2019, The Authors
                                    
                                    class DockerComposeConfig < Inspec.resource(1)
                                    
                                      name 'docker_compose_config'
                                    
                                      def initialize(path)
                                        @path = path
                                        @yaml = inspec.yaml(path)
                                      end
                                    
                                      def services
                                        @yaml['services']
                                      end
                                    
                                    end
                                    

                                    Now let's run the profile once more.

                                    inspec exec docker-workstations
                                    

                                    You will notice that it parses it correctly, but instead of our result we end up getting a hash. We need to convert the hash into an object that appears like other objects so that we may use our dot notation. So we will wrap our hash in a Ruby class called a Hashie::Mash. This gives us a quick way to convert a hash into a Ruby object with a number of methods attached to it. You will have to import the Hashie library by running gem install hashie and import it in the resource file to be used. It and is written as follows:

                                    # encoding: utf-8
                                    # copyright: 2019, The Authors
                                    
                                    require "hashie/mash"
                                    
                                    class DockerComposeConfig < Inspec.resource(1)
                                    
                                      name 'docker_compose_config'
                                    
                                      def initialize(path)
                                        @path = path
                                        @yaml = inspec.yaml(path)
                                      end
                                    
                                      def services
                                        Hashie::Mash.new(@yaml['services'])
                                      end
                                    
                                    end
                                    

                                    Lets run the profile again.

                                    inspec exec docker-workstations
                                    

                                    Everything passed!

                                    Check your work

                                    Check your work with the InSpec video below that walks through this docker resource example!

                                    Edit this pageopen in new window
                                    Last update: 6/14/2022, 2:23:45 AM
                                    Contributors: Emily Rodriguez,Emily Rodriguez,Will Dower
                                    Prev
                                    8. Create a Custom Resource - The Git Example
                                    Next
                                    10. Writing Plural Resources
                                    Apache-2.0 | Copyright © 2022 - The MITRE Corporation
                                    Copyright © 2022 Aaron Lippold