Write custom policies for Mondoo


Mondoo check results from the last post

This is the second post in a series about IT compliance with Mondoo. This post will focus on how to add your own custom policies to Mondoo.

Make sure to checkout the previous post before reading this one. It will make you familiar with Mondoo and the infrastructure we will be writing a policy for.

To demonstrate, we will be writing a policy which will check for attributes of the previously installed NodeJS app.

The NodeJS app

Here are the important facts about that app:

  • We need the Nginx, NodeJS and NPM packages installed on the VM
  • We use the nginx as a proxy for the nodejs app. Forwarding the NodeJS port 3000 to 80
  • Nginx site is configured in the /etc/nginx/sites-available/default file
  • The code of the NodeJS app lives inside the /home/adminuser/myapp/index.js file

When the app runs, it will simply return a greeting message:

2Hello World from host mondoo-demo-webserver-non-compliant!

What is a policy?

Policies are the specifications that cnspec uses when it scans a system. Think of a policy as a checklist that cnspec relies on to ensure that a system is compliant. In Mondoo and cnspec, these collections of compliance requirements are expressed as highly readable code. An example for policy could be: "Port 22 should be listening".

Learn more about policies here.

Mondoo Query Language

To express the rules of our policy, we need to write them in a language that Mondoo can understand. This language is called Mondoo Query Language or mql for short.

This is how a query for the SSH port example would look like:

1ports.listening.any( port == 22 )

You can test any query locally by using the cnspec shell:

1$ cnspec shell local
2cnspec> ports.listening.any( port == 22 )
3[ok] value: true

To make use of these queries, we need to put them into a policy file.

Writing a custom policy

Writing custom policies for Mondoo is quite easy. All the information goes into a YAML file. We call a file which collects policies a "policy bundle" in Mondoo. Once the file is written, we can upload it to Mondoo and have it automatically checked on each scan of our assets.

I prepared a custom policy for this blog post:

 2  - uid: webserver
 3    name: Webserver Baseline
 4    version: 1.0.0
 5    authors:
 6      - name: Infralovers GmbH
 7        email: team@infralovers.com
 8    docs:
 9      desc: Test suite for Infralovers blog post
10    tags:
11      mondoo.com/category: security
12      mondoo.com/platform: linux,ubuntu,debian
13    groups:
14      - title: SSH
15        filters: |
16          asset.family.contains("linux")          
17        checks:
18          - uid: ssh-01
19            title: Ensure SSH port is listening
20            mql: ports.listening.any( port == 22 )
21      - title: Webserver
22        filters: |
23          asset.family.contains("linux")          
24        checks:
25          - uid: webserver-01
26            title: Ensure webserver ports are listening
27            mql: |
28              ports.listening.any( port == 80 )
29              ports.listening.any( port == 3000 )              
30      - title: Packages
31        filters: |
32          asset.family.contains("linux")          
33        checks:
34          - uid: package-01
35            title: Ensure nginx is installed
36            mql: package('nginx').installed == true
37          - uid: package-02
38            title: Ensure nodejs is installed
39            mql: package('nodejs').installed == true
40          - uid: package-03
41            title: Ensure npm is installed
42            mql: package('npm').installed == true
43      - title: Files
44        filters: |
45          asset.family.contains("linux")          
46        checks:
47          - uid: file-01
48            title: Ensure nginx config is present
49            mql: file('/etc/nginx/sites-available/default').exists == true
50          - uid: file-02
51            title: Ensure application file is present
52            mql: file('/home/adminuser/myapp/index.js').exists == true

You can also find this custom policy in the companion Git repository to this blog post series. Look inside the "policies" directory to find it.

Let's analyse what is going on inside this policy: The first couple of lines define metadata for the policy. That is information on who wrote it, what version it is in etc.

Everything after the groups attribute is where it becomes interesting. I defined four different groups. It always make sense to separate checks toward different areas of concern into different groups. This makes it easier to maintain in the long run.

The first group has the title "SSH". Here we put all the checks concerning themselves with the SSH protocol. If you looks at the checks attribute, you can see that I am using a mql query to check if the SSH port, number 22, is listening:

ports.listening.any( port == 22 )

The filter attribute tells Mondoo on which assets to run this policy group. We have chosen asset.family.contains("linux"). This means that Mondoo will only run the associated policies on assets that are running a Linux operating system. If you do not define a filter, Mondoo will not run the check!

The second group "Webserver", checks for the necessary listening webserver ports: 80 and 3000.

Group three, checks if the required packages are installed and the fourth group check for the existence of the configuration files.

Uploading your policy to Mondoo

Once you have saved your file, go into your Mondoo dashboard. In the sidebar navigation, click on Registry. On the Registry page, click the purple plus button in the top-right corner:

Add a custom policy to Mondoo

Drag-and-drop your policy bundle file into the Upload Policy dialogue.

Drag and drop a custom policy to Mondoo

Once it is uploaded, search for the policy's name in the search bar on the Registry page. Once you found it, move your mouse cursor over it and click the enabled icon. It looks like a bar-chart with an arrow on top.

Search for a custom policy to Mondoo

Now, your policy will be included on each check of your assets, as long as the filter attribute of the policy group matches it.

Scanning your assets

To speed things up, you can trigger a scan on your asset manually:

1$ cnspec scan local

Check your asset. If you are following this blog post series it should be called mondoo-demo-webserver-non-compliant. You should see that your policy was checked by Mondoo:

Custom policy results in Mondoo

You can also inspect the specific checks to see how they did:

Custom policy results in Mondoo

Final thoughts

Not only does Mondoo come with lots of pre-defined policies, it also allows us to add our own. The MQL language is very powerful and let's us define policies for many different areas of concern. Compared to other policy-as-code tools, I feel that the syntax is easy to understand and the overhead is kept at a minimum.

As you can see, we can write compliance/security checks or even functional specifications. This will be especially helpful once we start writing policies with cnspec to verify e.g. Terraform code.

In the next post

In the next post, I will now finally try to remediate the compliancy issues that I discovered in these posts. I know, I originally wanted to that in this one. However, I thought I add some custom policies first.

Go Back explore our courses

We are here for you

You are interested in our courses or you simply have a question that needs answering? You can contact us at anytime! We will do our best to answer all your questions.

Contact us