Skip to content

Commit aa40057

Browse files
authored
Merge pull request #1441 from anmaxvl/policy-duplicate-layers
Fix policy enforcement to handle identical layers.
2 parents 362e3d2 + e6ee566 commit aa40057

2 files changed

Lines changed: 212 additions & 181 deletions

File tree

pkg/securitypolicy/securitypolicy_test.go

Lines changed: 98 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -107,42 +107,14 @@ func Test_StandardSecurityPolicyEnforcer_From_Security_Policy_Conversion(t *test
107107
}
108108
}
109109

110-
// Do we correctly set up the data structures that are part of creating a new
111-
// StandardSecurityPolicyEnforcer
112-
func Test_StandardSecurityPolicyEnforcer_Devices_Initialization(t *testing.T) {
113-
f := func(p *generatedContainers) bool {
114-
policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString)
115-
116-
// there should be a device entry for each container
117-
if len(p.containers) != len(policy.Devices) {
118-
return false
119-
}
120-
121-
// in each device entry that corresponds to a container,
122-
// the array should have space for all the root hashes
123-
for i := 0; i < len(p.containers); i++ {
124-
if len(p.containers[i].Layers) != len(policy.Devices[i]) {
125-
return false
126-
}
127-
}
128-
129-
return true
130-
}
131-
132-
if err := quick.Check(f, &quick.Config{MaxCount: 1000}); err != nil {
133-
t.Errorf("Test_StandardSecurityPolicyEnforcer_Devices_Initialization failed: %v", err)
134-
}
135-
}
136-
137110
// Verify that StandardSecurityPolicyEnforcer.EnforceDeviceMountPolicy will
138111
// return an error when there's no matching root hash in the policy
139112
func Test_EnforceDeviceMountPolicy_No_Matches(t *testing.T) {
140113
f := func(p *generatedContainers) bool {
141114
policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString)
142115

143-
r := rand.New(rand.NewSource(time.Now().UnixNano()))
144-
target := generateMountTarget(r)
145-
rootHash := generateInvalidRootHash(r)
116+
target := generateMountTarget(testRand)
117+
rootHash := generateInvalidRootHash(testRand)
146118

147119
err := policy.EnforceDeviceMountPolicy(target, rootHash)
148120

@@ -161,9 +133,8 @@ func Test_EnforceDeviceMountPolicy_Matches(t *testing.T) {
161133
f := func(p *generatedContainers) bool {
162134
policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString)
163135

164-
r := rand.New(rand.NewSource(time.Now().UnixNano()))
165-
target := generateMountTarget(r)
166-
rootHash := selectRootHashFromContainers(p, r)
136+
target := generateMountTarget(testRand)
137+
rootHash := selectRootHashFromContainers(p, testRand)
167138

168139
err := policy.EnforceDeviceMountPolicy(target, rootHash)
169140

@@ -180,33 +151,16 @@ func Test_EnforceDeviceUmountPolicy_Removes_Device_Entries(t *testing.T) {
180151
f := func(p *generatedContainers) bool {
181152
policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString)
182153

183-
r := rand.New(rand.NewSource(time.Now().UnixNano()))
184-
target := generateMountTarget(r)
185-
rootHash := selectRootHashFromContainers(p, r)
154+
target := generateMountTarget(testRand)
155+
rootHash := selectRootHashFromContainers(p, testRand)
186156

187157
err := policy.EnforceDeviceMountPolicy(target, rootHash)
188158
if err != nil {
189159
return false
190160
}
191161

192-
// we set up an expected new data structure shape were
193-
// the target has been removed, but everything else is
194-
// the same
195-
setupCorrectlyDone := false
196-
expectedDevices := make([][]string, len(policy.Devices))
197-
for i, container := range policy.Devices {
198-
expectedDevices[i] = make([]string, len(container))
199-
for j, storedTarget := range container {
200-
if target == storedTarget {
201-
setupCorrectlyDone = true
202-
} else {
203-
expectedDevices[i][j] = storedTarget
204-
}
205-
}
206-
}
207-
if !setupCorrectlyDone {
208-
// somehow, setup failed. this should never happen without another test
209-
// also failing
162+
if v, ok := policy.Devices[target]; !ok || v != rootHash {
163+
t.Errorf("root hash is missing or doesn't match: actual=%q expected=%q", v, rootHash)
210164
return false
211165
}
212166

@@ -215,7 +169,7 @@ func Test_EnforceDeviceUmountPolicy_Removes_Device_Entries(t *testing.T) {
215169
return false
216170
}
217171

218-
return cmp.Equal(policy.Devices, expectedDevices)
172+
return cmp.Equal(policy.Devices, map[string]string{})
219173
}
220174

221175
if err := quick.Check(f, &quick.Config{MaxCount: 1000}); err != nil {
@@ -322,8 +276,6 @@ func Test_EnforceOverlayMountPolicy_Multiple_Instances_Same_Container(t *testing
322276
t.Fatalf("failed with %d containers", containersToCreate)
323277
}
324278
}
325-
326-
t.Logf("ok for %d\n", containersToCreate)
327279
}
328280
}
329281

@@ -421,7 +373,7 @@ func Test_EnforceCommandPolicy_NarrowingMatches(t *testing.T) {
421373
f := func(p *generatedContainers) bool {
422374
// create two additional containers that "share everything"
423375
// except that they have different commands
424-
testContainerOne := generateContainersContainer(testRand, 5)
376+
testContainerOne := generateContainersContainer(testRand, 1, 5)
425377
testContainerTwo := *testContainerOne
426378
testContainerTwo.Command = generateCommand(testRand)
427379
// add new containers to policy before creating enforcer
@@ -535,7 +487,7 @@ func Test_EnforceEnvironmentVariablePolicy_Matches(t *testing.T) {
535487
func Test_EnforceEnvironmentVariablePolicy_Re2Match(t *testing.T) {
536488
p := generateContainers(testRand, 1)
537489

538-
container := generateContainersContainer(testRand, 1)
490+
container := generateContainersContainer(testRand, 1, 1)
539491
// add a rule to re2 match
540492
re2MatchRule := EnvRuleConfig{
541493
Strategy: EnvVarRuleRegex,
@@ -605,12 +557,11 @@ func Test_EnforceEnvironmentVariablePolicy_NotAllMatches(t *testing.T) {
605557
// the container in our policy" functionality works correctly.
606558
func Test_EnforceEnvironmentVariablePolicy_NarrowingMatches(t *testing.T) {
607559
f := func(p *generatedContainers) bool {
608-
r := rand.New(rand.NewSource(time.Now().UnixNano()))
609560
// create two additional containers that "share everything"
610561
// except that they have different environment variables
611-
testContainerOne := generateContainersContainer(r, 5)
562+
testContainerOne := generateContainersContainer(testRand, 1, 5)
612563
testContainerTwo := *testContainerOne
613-
testContainerTwo.EnvRules = generateEnvironmentVariableRules(r)
564+
testContainerTwo.EnvRules = generateEnvironmentVariableRules(testRand)
614565
// add new containers to policy before creating enforcer
615566
p.containers = append(p.containers, testContainerOne, &testContainerTwo)
616567

@@ -623,9 +574,9 @@ func Test_EnforceEnvironmentVariablePolicy_NarrowingMatches(t *testing.T) {
623574

624575
// mount and overlay all our containers
625576
for index, container := range p.containers {
626-
containerID := generateContainerID(r)
577+
containerID := generateContainerID(testRand)
627578

628-
layerPaths, err := createValidOverlayForContainer(policy, container, r)
579+
layerPaths, err := createValidOverlayForContainer(policy, container, testRand)
629580
if err != nil {
630581
return false
631582
}
@@ -668,7 +619,7 @@ func Test_EnforceEnvironmentVariablePolicy_NarrowingMatches(t *testing.T) {
668619

669620
// enforce command policy for containerOne
670621
// this will narrow our list of possible ids down
671-
envVars := buildEnvironmentVariablesFromContainerRules(testContainerOne, r)
622+
envVars := buildEnvironmentVariablesFromContainerRules(testContainerOne, testRand)
672623
err := policy.enforceEnvironmentVariablePolicy(testContainerOneID, envVars)
673624
if err != nil {
674625
return false
@@ -738,6 +689,72 @@ func Test_WorkingDirectoryPolicy_NoMatches(t *testing.T) {
738689
}
739690
}
740691

692+
// Consequent layers
693+
func Test_Overlay_Duplicate_Layers(t *testing.T) {
694+
f := func(p *generatedContainers) bool {
695+
c1 := generateContainersContainer(testRand, 5, 5)
696+
numLayers := len(c1.Layers)
697+
// make sure first container has two identical layers
698+
c1.Layers[numLayers-3] = c1.Layers[numLayers-2]
699+
700+
policy := NewStandardSecurityPolicyEnforcer([]*securityPolicyContainer{c1}, ignoredEncodedPolicyString)
701+
702+
// generate mount targets
703+
mountTargets := make([]string, numLayers)
704+
for i := 0; i < numLayers; i++ {
705+
mountTargets[i] = randString(testRand, maxGeneratedMountTargetLength)
706+
}
707+
708+
// call into mount enforcement
709+
for i := 0; i < numLayers; i++ {
710+
if err := policy.EnforceDeviceMountPolicy(mountTargets[i], c1.Layers[i]); err != nil {
711+
t.Errorf("failed to enforce device mount policy: %s", err)
712+
return false
713+
}
714+
}
715+
716+
if len(policy.Devices) != numLayers {
717+
t.Errorf("the number of mounted devices %v don't match the expectation: targets=%v layers=%v",
718+
policy.Devices, mountTargets, c1.Layers)
719+
return false
720+
}
721+
722+
overlay := make([]string, numLayers)
723+
for i := 0; i < numLayers; i++ {
724+
overlay[i] = mountTargets[numLayers-i-1]
725+
}
726+
containerID := randString(testRand, 32)
727+
if err := policy.EnforceOverlayMountPolicy(containerID, overlay); err != nil {
728+
t.Errorf("failed to enforce overlay mount policy: %s", err)
729+
return false
730+
}
731+
732+
// validate the state of the ContainerIndexToContainerIds mapping
733+
if containerIDs, ok := policy.ContainerIndexToContainerIds[0]; !ok {
734+
t.Errorf("container index to containerIDs mapping was not set: %v", containerIDs)
735+
return false
736+
} else {
737+
if _, ok := containerIDs[containerID]; !ok {
738+
t.Errorf("containerID is missing from possible containerIDs set: %v", containerIDs)
739+
return false
740+
}
741+
}
742+
743+
for _, mountTarget := range mountTargets {
744+
if err := policy.EnforceDeviceUnmountPolicy(mountTarget); err != nil {
745+
t.Errorf("failed to enforce unmount policy: %s", err)
746+
return false
747+
}
748+
}
749+
750+
return true
751+
}
752+
753+
if err := quick.Check(f, &quick.Config{MaxCount: 1}); err != nil {
754+
t.Errorf("failed to run stuff: %s", err)
755+
}
756+
}
757+
741758
//
742759
// Setup and "fixtures" follow...
743760
//
@@ -844,21 +861,21 @@ func generateContainers(r *rand.Rand, upTo int32) *generatedContainers {
844861

845862
numContainers := (int)(atLeastOneAtMost(r, upTo))
846863
for i := 0; i < numContainers; i++ {
847-
containers = append(containers, generateContainersContainer(r, maxLayersInGeneratedContainer))
864+
containers = append(containers, generateContainersContainer(r, 1, maxLayersInGeneratedContainer))
848865
}
849866

850867
return &generatedContainers{
851868
containers: containers,
852869
}
853870
}
854871

855-
func generateContainersContainer(r *rand.Rand, size int32) *securityPolicyContainer {
872+
func generateContainersContainer(r *rand.Rand, minNumberOfLayers, maxNumberOfLayers int32) *securityPolicyContainer {
856873
c := securityPolicyContainer{}
857874
c.Command = generateCommand(r)
858875
c.EnvRules = generateEnvironmentVariableRules(r)
859876
c.WorkingDir = randVariableString(r, maxGeneratedCommandLength)
860-
layers := int(atLeastOneAtMost(r, size))
861-
for i := 0; i < layers; i++ {
877+
numLayers := int(atLeastNAtMostM(r, minNumberOfLayers, maxNumberOfLayers))
878+
for i := 0; i < numLayers; i++ {
862879
c.Layers = append(c.Layers, generateRootHash(r))
863880
}
864881

@@ -1072,21 +1089,27 @@ func randVariableString(r *rand.Rand, maxLen int32) string {
10721089
return randString(r, atLeastOneAtMost(r, maxLen))
10731090
}
10741091

1075-
func randString(r *rand.Rand, len int32) string {
1076-
var s strings.Builder
1077-
for i := 0; i < (int)(len); i++ {
1078-
s.WriteRune(0x00ff & r.Int31n(256))
1092+
func randString(r *rand.Rand, length int32) string {
1093+
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
1094+
sb := strings.Builder{}
1095+
sb.Grow(int(length))
1096+
for i := 0; i < (int)(length); i++ {
1097+
sb.WriteByte(charset[r.Intn(len(charset))])
10791098
}
10801099

1081-
return s.String()
1100+
return sb.String()
10821101
}
10831102

10841103
func randMinMax(r *rand.Rand, min int32, max int32) int32 {
10851104
return r.Int31n(max-min+1) + min
10861105
}
10871106

1107+
func atLeastNAtMostM(r *rand.Rand, min, max int32) int32 {
1108+
return randMinMax(r, min, max)
1109+
}
1110+
10881111
func atLeastOneAtMost(r *rand.Rand, most int32) int32 {
1089-
return randMinMax(r, 1, most)
1112+
return atLeastNAtMostM(r, 1, most)
10901113
}
10911114

10921115
func atMost(r *rand.Rand, most int32) int32 {

0 commit comments

Comments
 (0)