Skip to content

Commit e385da1

Browse files
authored
ECS json bug fix that corrects casing issues for entrypoint, image and environment values (#175)
1 parent e087d58 commit e385da1

File tree

5 files changed

+308
-0
lines changed

5 files changed

+308
-0
lines changed

sysdig/cfn_preprocess_template.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
This file contains definition for the following functions.
3+
4+
terraformPreModifications is used to modify the container definitions passed to the cfn patcher such that it modifies casing issues in any ECS json content so that it can be processed by the kilt patcher.
5+
6+
GetValueFromTemplate is used to obtain key, value information from JSON object
7+
*/
8+
9+
package sysdig
10+
11+
import (
12+
"context"
13+
"fmt"
14+
15+
"github.com/Jeffail/gabs/v2"
16+
"github.com/rs/zerolog/log"
17+
)
18+
19+
// GetValueFromTemplate can be used to obtain string value from JSON object
20+
func GetValueFromTemplate(what *gabs.Container) (string, *gabs.Container) {
21+
var result string
22+
var fallback *gabs.Container
23+
24+
switch v := what.Data().(type) {
25+
case string:
26+
result = v
27+
fallback = nil
28+
default:
29+
fallback = what
30+
result = "placeholder: " + what.String()
31+
}
32+
return result, fallback
33+
}
34+
35+
func terraformPreModifications(ctx context.Context, patchedStack []byte) ([]byte, error) {
36+
l := log.Ctx(ctx)
37+
template, err := gabs.ParseJSON(patchedStack)
38+
if err != nil {
39+
l.Error().Err(err).Msg("failed to parse input fragment")
40+
return nil, err
41+
}
42+
43+
// This code block is used when parsing ECS JSON format
44+
for _, resource := range template.S("Resources").ChildrenMap() {
45+
for _, container := range resource.S("Properties", "ContainerDefinitions").Children() {
46+
if container.Exists("image") {
47+
passthrough, _ := GetValueFromTemplate(container.S("image"))
48+
_, err = container.Set(passthrough, "Image")
49+
if err != nil {
50+
return nil, fmt.Errorf("Could not update Image field: %v", err)
51+
}
52+
53+
err = container.Delete("image")
54+
if err != nil {
55+
return nil, fmt.Errorf("could not delete image in the Container definition: %w", err)
56+
}
57+
}
58+
59+
if container.Exists("Environment") {
60+
for _, env := range container.S("Environment").Children() {
61+
if env.Exists("name") {
62+
name, _ := env.S("name").Data().(string)
63+
err = env.Delete("name")
64+
if err != nil {
65+
return nil, fmt.Errorf("Could not delete \"name\" key in Environment: %v", err)
66+
}
67+
_, err = env.Set(name, "Name")
68+
if err != nil {
69+
return nil, fmt.Errorf("Could not assign \"Name\" key in Environment: %v", err)
70+
}
71+
}
72+
if env.Exists("value") {
73+
value, _ := env.S("value").Data().(string)
74+
err = env.Delete("value")
75+
if err != nil {
76+
return nil, fmt.Errorf("Could not delete \"value\" key in Environment: %v", err)
77+
}
78+
_, err = env.Set(value, "Value")
79+
if err != nil {
80+
return nil, fmt.Errorf("Could not assign \"Value\" key in Environment: %v", err)
81+
}
82+
}
83+
}
84+
}
85+
86+
if container.Exists("entryPoint") {
87+
for _, arg := range container.S("entryPoint").Children() {
88+
passthrough, _ := GetValueFromTemplate(arg)
89+
err = container.ArrayAppend(passthrough, "EntryPoint")
90+
if err != nil {
91+
return nil, fmt.Errorf("Could not append entrypoint values to EntryPoint: %v", err)
92+
}
93+
}
94+
95+
err = container.Delete("entryPoint")
96+
if err != nil {
97+
return nil, fmt.Errorf("could not delete entryPoint in the Container definition: %w", err)
98+
}
99+
}
100+
}
101+
}
102+
103+
return template.Bytes(), err
104+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package sysdig
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"io/ioutil"
7+
"testing"
8+
9+
"github.com/falcosecurity/kilt/runtimes/cloudformation/cfnpatcher"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestECStransformation(t *testing.T) {
14+
inputfile, err := ioutil.ReadFile("testfiles/ECSinput.json")
15+
16+
if err != nil {
17+
t.Fatalf("Cannot find testfiles/ECSinput.json")
18+
}
19+
20+
recipeConfig := KiltRecipeConfig{
21+
SysdigAccessKey: "sysdig_access_key",
22+
AgentImage: "workload_agent_image",
23+
OrchestratorHost: "orchestrator_host",
24+
OrchestratorPort: "orchestrator_port",
25+
CollectorHost: "collector_host",
26+
CollectorPort: "collector_port",
27+
}
28+
29+
jsonConf, err := json.Marshal(&recipeConfig)
30+
if err != nil {
31+
t.Fatalf("Failed to serialize configuration: %v", err.Error())
32+
}
33+
34+
kiltConfig := &cfnpatcher.Configuration{
35+
Kilt: agentinoKiltDefinition,
36+
ImageAuthSecret: "image_auth_secret",
37+
OptIn: false,
38+
UseRepositoryHints: true,
39+
RecipeConfig: string(jsonConf),
40+
}
41+
42+
patchedOutput, err := patchFargateTaskDefinition(context.Background(), string(inputfile), kiltConfig)
43+
if err != nil {
44+
t.Fatalf("Cannot execute PatchFargateTaskDefinition : %v", err.Error())
45+
}
46+
47+
expectedOutput, err := ioutil.ReadFile("testfiles/ECSInstrumented.json")
48+
if err != nil {
49+
t.Fatalf("Cannot find testfiles/ECSinput.json")
50+
}
51+
52+
type cdef struct {
53+
Command []string `json:"Command"`
54+
EntryPoint []string `json:"EntryPoint"`
55+
Environment []map[string]string `json:"Environment"`
56+
Image string `json:"Image"`
57+
Linuxparameters interface{} `json:"LinuxParameters"`
58+
VolumesFrom []interface{} `json:"VolumesFrom"`
59+
LogConfiguration interface{} `json:"LogConfiguration"`
60+
Name string `json:"Name"`
61+
Image2 string `json:"image"`
62+
EntryPoint2 string `json:"entryPoint"`
63+
}
64+
65+
var patchedContainerDefinitions, expectedContainerDefinitions []cdef
66+
err = json.Unmarshal([]byte(*patchedOutput), &patchedContainerDefinitions)
67+
if err != nil {
68+
t.Fatalf("Error Unmarshaling patched Container definitions : %v", err.Error())
69+
}
70+
71+
err = json.Unmarshal([]byte(expectedOutput), &expectedContainerDefinitions)
72+
if err != nil {
73+
t.Fatalf("Error Unmarshaling expected Container definitions: %v", err.Error())
74+
}
75+
76+
assert.Equal(t, expectedContainerDefinitions[0].Name, patchedContainerDefinitions[0].Name)
77+
78+
// The order received from patchedOutput changes continuously hence it is important to check if the arrays of expected and actual are equal without order being correct. This check also
79+
// helps with checking if key/value is named "Name" and "Value" accordingly.
80+
assert.ElementsMatch(t, expectedContainerDefinitions[0].Environment, patchedContainerDefinitions[0].Environment)
81+
82+
// Check if Image key is correct
83+
assert.Equal(t, expectedContainerDefinitions[0].Image, patchedContainerDefinitions[0].Image)
84+
assert.Equal(t, patchedContainerDefinitions[0].Image2, "")
85+
86+
// Check if entryPoint key is correct
87+
assert.Equal(t, expectedContainerDefinitions[0].EntryPoint, patchedContainerDefinitions[0].EntryPoint)
88+
assert.Equal(t, patchedContainerDefinitions[0].EntryPoint2, "")
89+
}

sysdig/data_source_sysdig_fargate_workload_agent.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ type cfnStack struct {
100100
Resources map[string]cfnResource `json:"Resources"`
101101
}
102102

103+
// PatchFargateTaskDefinition modifies the container definitions
103104
func patchFargateTaskDefinition(ctx context.Context, containerDefinitions string, kiltConfig *cfnpatcher.Configuration) (patched *string, err error) {
104105
var cdefs []map[string]interface{}
105106
err = json.Unmarshal([]byte(containerDefinitions), &cdefs)
@@ -139,6 +140,9 @@ func patchFargateTaskDefinition(ctx context.Context, containerDefinitions string
139140
}
140141
}()
141142

143+
// ECS JSON modifications
144+
patchedStack, _ = terraformPreModifications(ctx, patchedStack)
145+
142146
patchedBytes, err := cfnpatcher.Patch(ctx, kiltConfig, patchedStack)
143147
if err != nil {
144148
return nil, err
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
[
2+
{
3+
"Command": [
4+
"watch",
5+
"-n60",
6+
"cat",
7+
"/etc/shadow",
8+
"/bin/sh"
9+
],
10+
"EntryPoint": [
11+
"/opt/draios/bin/instrument"
12+
],
13+
"Environment": [
14+
{
15+
"Name": "SYSDIG_ACCESS_KEY",
16+
"Value": "sysdig_access_key"
17+
},
18+
{
19+
"Name": "SYSDIG_LOGGING",
20+
"Value": ""
21+
},
22+
{
23+
"Name": "SYSDIG_ENDPOINT",
24+
"Value": "value"
25+
},
26+
{
27+
"Name": "pmet",
28+
"Value": "temp"
29+
},
30+
{
31+
"Name": "SYSDIG_ORCHESTRATOR",
32+
"Value": "orchestrator_host"
33+
},
34+
{
35+
"Name": "SYSDIG_ORCHESTRATOR_PORT",
36+
"Value": "orchestrator_port"
37+
},
38+
{
39+
"Name": "SYSDIG_COLLECTOR",
40+
"Value": "collector_host"
41+
},
42+
{
43+
"Name": "SYSDIG_COLLECTOR_PORT",
44+
"Value": "collector_port"
45+
}
46+
],
47+
"Image": "quay.io/rehman0288/busyboxplus:latest",
48+
"LinuxParameters": {
49+
"Capabilities": {
50+
"Add": [
51+
"SYS_PTRACE"
52+
]
53+
}
54+
},
55+
"VolumesFrom": [
56+
{
57+
"ReadOnly": true,
58+
"SourceContainer": "SysdigInstrumentation"
59+
}
60+
],
61+
"logConfiguration": {
62+
"logDriver": "awslogs",
63+
"options": {
64+
"awslogs-group": "test-log-group",
65+
"awslogs-region": "us-east-1",
66+
"awslogs-stream-prefix": "ecs"
67+
}
68+
},
69+
"name": "busybox"
70+
},
71+
{
72+
"EntryPoint": [
73+
"/opt/draios/bin/logwriter"
74+
],
75+
"Image": "workload_agent_image",
76+
"Name": "SysdigInstrumentation",
77+
"RepositoryCredentials": {
78+
"CredentialsParameter": "image_auth_secret"
79+
}
80+
}
81+
]

sysdig/testfiles/ECSinput.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[
2+
{
3+
"Environment": [
4+
{
5+
"name": "pmet",
6+
"value": "temp"
7+
},
8+
{
9+
"Name": "SYSDIG_ENDPOINT",
10+
"Value": "value"
11+
}
12+
],
13+
"entryPoint": [
14+
"watch",
15+
"-n60",
16+
"cat",
17+
"/etc/shadow"
18+
],
19+
"image": "quay.io/rehman0288/busyboxplus:latest",
20+
"logConfiguration": {
21+
"logDriver": "awslogs",
22+
"options": {
23+
"awslogs-group": "test-log-group",
24+
"awslogs-region": "us-east-1",
25+
"awslogs-stream-prefix": "ecs"
26+
}
27+
},
28+
"name": "busybox"
29+
}
30+
]

0 commit comments

Comments
 (0)