AI for DevOps Engineers - Part 1: The Building Blocks of DevOps AI
DevOps is a key success factor for modern software development and we have most definitely come across AI in one way or another. The intersection of AI and
We were already talking about mondoo in a previous article. Mondoo is brought to you by the team who gave you InSpec and the DevSec Project with a passion to make IT operations human readable and executable.
So, why are we revisiting this topic? Because we want to show you how to test your golden images with Mondoo based on another article we have written about HashiCorp Packer. And the reason for this is simple: Mondoo is a great tool to test your golden images for security vulnerabilities, and kitchen terraform is getting deprecated.
When you have defined a certain set of machine templates that you want to use for your infrastructure, you want to make sure that these templates are secure. This is where mondoo comes into play. Mondoo is a tool that allows you to test your golden images for security vulnerabilities, but also to test your infrastructure for compliance with certain security standards and organization standards. We can use that with custom policies to replace the deprecated kitchen terraform setup that was using inspec
for this purpose.
The missing link between these technologies is how to spin up your golden image to address the testing by Mondoo. This is where terratest comes into play. Terratest is a Go library that makes it easier to write automated tests for your infrastructure code. It is a great tool to spin up your golden image and test it with Mondoo. You also find here a great article about how to use terratest with terraform.
The actual go code to test a golden image with Mondoo and terratest is quite simple. You can see the full code in the following snippet:
1package test
2
3import (
4//...
5)
6
7func TestMondooBased(t *testing.T) {
8
9 expectedName := fmt.Sprintf("terratest-mondoo-%s", random.UniqueId())
10 cloud := os.Getenv("CLOUD")
11
12 terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
13 TerraformDir: fmt.Sprintf("./fixtures/%s/", cloud),
14 Vars: map[string]interface{}{
15 "vm_image_id": os.Getenv("PACKER_IMAGE_ID"),
16 "location": os.Getenv("PACKER_LOCATION"),
17 "subnet_id": os.Getenv("PACKER_SUBNET_ID"),
18 "vpc_id": os.Getenv("PACKER_VPC_ID"),
19 "vm_name": expectedName,
20 },
21 })
22
23 defer terraform.Destroy(t, terraformOptions)
24
25 terraform.InitAndApply(t, terraformOptions)
26
27 target_ipaddress := terraform.Output(t, terraformOptions, "target_ipaddress")
28
29 assert.NotEmpty(t, target_ipaddress)
30
31 runMondooTests(t, target_ipaddress, expectedName, "core")
32 runMondooTests(t, target_ipaddress, expectedName, os.Getenv("TESTNAME"))
33
34}
We have a test function that is using embedded test function to run certain tests. The runMondooTests
function is the one that is running the mondoo tests. In our example setup we have core
policyset that all machine templates must pass. And there also exist dedicated tests related to the actual provisioning of the machine template. And so in the end we are running the Mondoo tests for the core
policyset and the dedicated tests for the machine template - and both must be passed.
The actual fixtures
definied are still the same we used in the previous article. The core
policyset is defined in a file core.mql.yaml
and the dedicated tests are defined in a file ${TESTNAME}.mql.yaml
. And we just translated the tests from the kitchen terraform setup to the Mondoo setup.
The runMondooTests
function is defined as following snippet shows:
1func runMondooTests(t *testing.T, target_address string, expectedName string, profile string) {
2
3 policyBundleFile := fmt.Sprintf("./%s.mql.yaml", profile)
4 if _, err := os.Stat(policyBundleFile); err != nil {
5 t.Logf("policy bundle file not found: %s", policyBundleFile)
6 return
7 }
8 logFile := fmt.Sprintf("%s_%s.log", expectedName, profile)
9
10 var policyFilters []string
11
12 assetConfig := &inventory.Config{
13 Type: "ssh",
14 Host: target_address,
15 Port: int32(22),
16 Options: map[string]string{},
17 Credentials: []*vault.Credential{},
18 }
19
20 bundleLoader := policy.DefaultBundleLoader()
21 policyBundle, err := bundleLoader.BundleFromPaths(policyBundleFile)
22 if err != nil {
23 t.Error(errors.Wrap(err, "could not load policy bundle from "+policyBundleFile))
24 return
25 }
26
27 conf := inventory.New(inventory.WithAssets(&inventory.Asset{
28 Name: expectedName,
29 Connections: []*inventory.Config{assetConfig},
30 Annotations: map[string]string{},
31 Labels: map[string]string{},
32 }))
33
34 scanJob := &scan.Job{
35 Inventory: conf,
36 Bundle: policyBundle,
37 PolicyFilters: policyFilters,
38 ReportType: scan.ReportType_FULL,
39 }
40
41 scanService := scan.NewLocalScanner()
42 res, err := scanService.RunIncognito(context.Background(), scanJob)
43 if err != nil {
44 t.Error("scan failed: " + err.Error())
45 return
46 }
47
48 if res == nil || res.GetFull() == nil {
49 t.Error("scan failed: no result returned, use `debug: true` logging for more details")
50 return
51 }
52
53 report := res.GetFull()
54 handlerConf := reporter.HandlerConfig{
55 Format: "compact",
56 OutputTarget: logFile,
57 Incognito: true,
58 }
59 outputHandler, err := reporter.NewOutputHandler(handlerConf)
60 if err != nil {
61 t.Error("failed to create an output handler: " + err.Error())
62 return
63 }
64
65 buf := &bytes.Buffer{}
66 if x, ok := outputHandler.(*reporter.Reporter); ok {
67 x.WithOutput(buf)
68 }
69
70 if err := outputHandler.WriteReport(context.Background(), report); err != nil {
71 t.Error("failed to write report to output target: " + err.Error())
72 return
73 }
74
75 scoreThreshold := uint64(100)
76 if os.Getenv("SCORE_THRESHOLD") != "" {
77 scoreThreshold, _ = strconv.ParseUint(os.Getenv("SCORE_THRESHOLD"), 10, 32)
78 }
79
80 if report.GetWorstScore() < uint32(scoreThreshold) {
81 t.Errorf("scan %s has completed with %d score, does not pass score threshold %d", profile, report.GetWorstScore(), scoreThreshold)
82 return
83 }
84
85 t.Logf("scan %s has completed with %d score, passed score threshold %d", profile, report.GetWorstScore(), scoreThreshold)
86}
This function is running the Mondoo tests for a certain policyset. It is loading the policy bundle from a file, and then it is running the tests against the machine that was created by terratest setup. The result of the tests is written to a log file. And in the end the function is checking if the tests have passed or not, based on a certain score threshold.
The definition of machine templates is a crucial part of your infrastructure setup. You want to make sure that these templates are secure and compliant with certain standards. Mondoo is a great tool to test your golden images for security vulnerabilities, but also to test your infrastructure for compliance with certain security standards and organization standards. And terratest becomes very useful to spin up your golden image and test it with Mondoo. In this article we have shown you how to test your golden images with Mondoo and terratest. We hope this article was helpful to you and you learned something new.
Thank you for reading!
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