Understanding DevOps and Cloud Maturity Models: A Guide to Elevating Your IT Strategy
In today’s fast-paced technological landscape, DevOps and Cloud practices are integral to accelerating software delivery and optimizing cloud resources. But as
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.
Here are the important facts about that app:
/etc/nginx/sites-available/default
file/home/adminuser/myapp/index.js
fileWhen the app runs, it will simply return a greeting message:
1$ curl <IP-ADDRESS-OF-YOUR-VM>
2Hello World from host mondoo-demo-webserver-non-compliant!
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.
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 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:
1policies:
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.
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:
Drag-and-drop your policy bundle file into the Upload Policy dialogue.
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.
Now, your policy will be included on each check of your assets, as long as the filter attribute of the policy group matches it.
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:
You can also inspect the specific checks to see how they did:
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, 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.
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