From 023e7dd4917ab3e4b200547003f38b301088ed90 Mon Sep 17 00:00:00 2001 From: Maksim An Date: Thu, 13 Jan 2022 18:49:09 -0800 Subject: [PATCH] Update securitypolicy struct names and docstrings During initial implementation a few things were skipped and the plan was to address them later. Some of the things include missing docstrings on exported security policy structs, methods and interfaces. This PR addresses above by splitting existing comments on the Policy struct and putting them closer to the actual implementation. Additionally fix inconsistencies with typical golang naming conventions. Signed-off-by: Maksim An --- internal/guest/runtime/hcsv2/uvm.go | 12 +- internal/guest/storage/overlay/overlay.go | 9 +- .../guest/storage/overlay/overlay_test.go | 8 +- internal/guest/storage/pmem/pmem.go | 4 +- internal/guest/storage/pmem/pmem_test.go | 8 +- internal/guest/storage/scsi/scsi.go | 4 +- internal/guest/storage/scsi/scsi_test.go | 8 +- .../test/policy/mountmonitorenforcer.go | 34 +++ .../mountmonitoringsecuritypolicyenforcer.go | 34 --- internal/tools/securitypolicy/main.go | 10 +- .../securitypolicy/enforcement.go | 222 ++++++++++-------- .../securitypolicy/policy.go | 34 +-- ...{securitypolicy_test.go => policy_test.go} | 116 ++++----- .../hcsshim/pkg/securitypolicy/enforcement.go | 222 ++++++++++-------- .../hcsshim/pkg/securitypolicy/policy.go | 34 +-- 15 files changed, 411 insertions(+), 348 deletions(-) create mode 100644 internal/guest/storage/test/policy/mountmonitorenforcer.go delete mode 100644 internal/guest/storage/test/policy/mountmonitoringsecuritypolicyenforcer.go rename test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicyenforcer.go => pkg/securitypolicy/enforcement.go (58%) rename test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicy.go => pkg/securitypolicy/policy.go (83%) rename pkg/securitypolicy/{securitypolicy_test.go => policy_test.go} (87%) rename pkg/securitypolicy/securitypolicyenforcer.go => test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/enforcement.go (58%) rename pkg/securitypolicy/securitypolicy.go => test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/policy.go (83%) diff --git a/internal/guest/runtime/hcsv2/uvm.go b/internal/guest/runtime/hcsv2/uvm.go index b1ec2c1001..e63f7ec608 100644 --- a/internal/guest/runtime/hcsv2/uvm.go +++ b/internal/guest/runtime/hcsv2/uvm.go @@ -51,7 +51,7 @@ type Host struct { // state required for the security policy enforcement policyMutex sync.Mutex - securityPolicyEnforcer securitypolicy.SecurityPolicyEnforcer + securityPolicyEnforcer securitypolicy.PolicyEnforcer securityPolicyEnforcerSet bool } @@ -62,7 +62,7 @@ func NewHost(rtime runtime.Runtime, vsock transport.Transport) *Host { rtime: rtime, vsock: vsock, securityPolicyEnforcerSet: false, - securityPolicyEnforcer: &securitypolicy.OpenDoorSecurityPolicyEnforcer{}, + securityPolicyEnforcer: &securitypolicy.OpenDoorEnforcer{}, } } @@ -217,7 +217,7 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM // security policy variable cannot be included in the security policy as its value is not available // security policy construction time. - if policyEnforcer, ok := (h.securityPolicyEnforcer).(*securitypolicy.StandardSecurityPolicyEnforcer); ok { + if policyEnforcer, ok := (h.securityPolicyEnforcer).(*securitypolicy.StandardEnforcer); ok { secPolicyEnv := fmt.Sprintf("SECURITY_POLICY=%s", policyEnforcer.EncodedSecurityPolicy) settings.OCISpecification.Process.Env = append(settings.OCISpecification.Process.Env, secPolicyEnv) } @@ -440,7 +440,7 @@ func newInvalidRequestTypeError(rt prot.ModifyRequestType) error { return errors.Errorf("the RequestType \"%s\" is not supported", rt) } -func modifyMappedVirtualDisk(ctx context.Context, rt prot.ModifyRequestType, mvd *prot.MappedVirtualDiskV2, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func modifyMappedVirtualDisk(ctx context.Context, rt prot.ModifyRequestType, mvd *prot.MappedVirtualDiskV2, securityPolicy securitypolicy.PolicyEnforcer) (err error) { switch rt { case prot.MreqtAdd: mountCtx, cancel := context.WithTimeout(ctx, time.Second*5) @@ -472,7 +472,7 @@ func modifyMappedDirectory(ctx context.Context, vsock transport.Transport, rt pr } } -func modifyMappedVPMemDevice(ctx context.Context, rt prot.ModifyRequestType, vpd *prot.MappedVPMemDeviceV2, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func modifyMappedVPMemDevice(ctx context.Context, rt prot.ModifyRequestType, vpd *prot.MappedVPMemDeviceV2, securityPolicy securitypolicy.PolicyEnforcer) (err error) { switch rt { case prot.MreqtAdd: return pmem.Mount(ctx, vpd.DeviceNumber, vpd.MountPath, vpd.MappingInfo, vpd.VerityInfo, securityPolicy) @@ -492,7 +492,7 @@ func modifyMappedVPCIDevice(ctx context.Context, rt prot.ModifyRequestType, vpci } } -func modifyCombinedLayers(ctx context.Context, rt prot.ModifyRequestType, cl *prot.CombinedLayersV2, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func modifyCombinedLayers(ctx context.Context, rt prot.ModifyRequestType, cl *prot.CombinedLayersV2, securityPolicy securitypolicy.PolicyEnforcer) (err error) { switch rt { case prot.MreqtAdd: layerPaths := make([]string, len(cl.Layers)) diff --git a/internal/guest/storage/overlay/overlay.go b/internal/guest/storage/overlay/overlay.go index 8c2de80268..9fd9c2c0ff 100644 --- a/internal/guest/storage/overlay/overlay.go +++ b/internal/guest/storage/overlay/overlay.go @@ -23,7 +23,14 @@ var ( // MountLayer first enforces the security policy for the container's layer paths // and then calls Mount to mount the layer paths as an overlayfs -func MountLayer(ctx context.Context, layerPaths []string, upperdirPath, workdirPath, rootfsPath string, readonly bool, containerId string, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func MountLayer( + ctx context.Context, + layerPaths []string, + upperdirPath, workdirPath, rootfsPath string, + readonly bool, + containerId string, + securityPolicy securitypolicy.PolicyEnforcer, +) (err error) { _, span := trace.StartSpan(ctx, "overlay::MountLayer") defer span.End() defer func() { oc.SetSpanStatus(span, err) }() diff --git a/internal/guest/storage/overlay/overlay_test.go b/internal/guest/storage/overlay/overlay_test.go index 828ee2a955..b8a1feb026 100644 --- a/internal/guest/storage/overlay/overlay_test.go +++ b/internal/guest/storage/overlay/overlay_test.go @@ -197,10 +197,10 @@ func Test_Security_Policy_Enforcement(t *testing.T) { } } -func openDoorSecurityPolicyEnforcer() securitypolicy.SecurityPolicyEnforcer { - return &securitypolicy.OpenDoorSecurityPolicyEnforcer{} +func openDoorSecurityPolicyEnforcer() securitypolicy.PolicyEnforcer { + return &securitypolicy.OpenDoorEnforcer{} } -func mountMonitoringSecurityPolicyEnforcer() *policy.MountMonitoringSecurityPolicyEnforcer { - return &policy.MountMonitoringSecurityPolicyEnforcer{} +func mountMonitoringSecurityPolicyEnforcer() *policy.MountMonitoringEnforcer { + return &policy.MountMonitoringEnforcer{} } diff --git a/internal/guest/storage/pmem/pmem.go b/internal/guest/storage/pmem/pmem.go index 8826e3d00d..417fba5b44 100644 --- a/internal/guest/storage/pmem/pmem.go +++ b/internal/guest/storage/pmem/pmem.go @@ -69,7 +69,7 @@ func mount(ctx context.Context, source, target string) (err error) { // // Note: both mappingInfo and verityInfo can be non-nil at the same time, in that case // linear target is created first and it becomes the data/hash device for verity target. -func Mount(ctx context.Context, device uint32, target string, mappingInfo *prot.DeviceMappingInfo, verityInfo *prot.DeviceVerityInfo, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func Mount(ctx context.Context, device uint32, target string, mappingInfo *prot.DeviceMappingInfo, verityInfo *prot.DeviceVerityInfo, securityPolicy securitypolicy.PolicyEnforcer) (err error) { mCtx, span := trace.StartSpan(ctx, "pmem::Mount") defer span.End() defer func() { oc.SetSpanStatus(span, err) }() @@ -123,7 +123,7 @@ func Mount(ctx context.Context, device uint32, target string, mappingInfo *prot. } // Unmount unmounts `target` and removes corresponding linear and verity targets when needed -func Unmount(ctx context.Context, devNumber uint32, target string, mappingInfo *prot.DeviceMappingInfo, verityInfo *prot.DeviceVerityInfo, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func Unmount(ctx context.Context, devNumber uint32, target string, mappingInfo *prot.DeviceMappingInfo, verityInfo *prot.DeviceVerityInfo, securityPolicy securitypolicy.PolicyEnforcer) (err error) { _, span := trace.StartSpan(ctx, "pmem::Unmount") defer span.End() defer func() { oc.SetSpanStatus(span, err) }() diff --git a/internal/guest/storage/pmem/pmem_test.go b/internal/guest/storage/pmem/pmem_test.go index be61d70b11..55d8d96c98 100644 --- a/internal/guest/storage/pmem/pmem_test.go +++ b/internal/guest/storage/pmem/pmem_test.go @@ -303,12 +303,12 @@ func Test_Security_Policy_Enforcement_Unmount_Calls(t *testing.T) { } } -func openDoorSecurityPolicyEnforcer() securitypolicy.SecurityPolicyEnforcer { - return &securitypolicy.OpenDoorSecurityPolicyEnforcer{} +func openDoorSecurityPolicyEnforcer() securitypolicy.PolicyEnforcer { + return &securitypolicy.OpenDoorEnforcer{} } -func mountMonitoringSecurityPolicyEnforcer() *policy.MountMonitoringSecurityPolicyEnforcer { - return &policy.MountMonitoringSecurityPolicyEnforcer{} +func mountMonitoringSecurityPolicyEnforcer() *policy.MountMonitoringEnforcer { + return &policy.MountMonitoringEnforcer{} } // device mapper tests diff --git a/internal/guest/storage/scsi/scsi.go b/internal/guest/storage/scsi/scsi.go index fbcebf3754..1fdac65248 100644 --- a/internal/guest/storage/scsi/scsi.go +++ b/internal/guest/storage/scsi/scsi.go @@ -49,7 +49,7 @@ const ( // // If `encrypted` is set to true, the SCSI device will be encrypted using // dm-crypt. -func Mount(ctx context.Context, controller, lun uint8, target string, readonly bool, encrypted bool, options []string, verityInfo *prot.DeviceVerityInfo, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func Mount(ctx context.Context, controller, lun uint8, target string, readonly bool, encrypted bool, options []string, verityInfo *prot.DeviceVerityInfo, securityPolicy securitypolicy.PolicyEnforcer) (err error) { spnCtx, span := trace.StartSpan(ctx, "scsi::Mount") defer span.End() defer func() { oc.SetSpanStatus(span, err) }() @@ -150,7 +150,7 @@ func Mount(ctx context.Context, controller, lun uint8, target string, readonly b // Unmount unmounts a SCSI device mounted at `target`. // // If `encrypted` is true, it removes all its associated dm-crypto state. -func Unmount(ctx context.Context, controller, lun uint8, target string, encrypted bool, verityInfo *prot.DeviceVerityInfo, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func Unmount(ctx context.Context, controller, lun uint8, target string, encrypted bool, verityInfo *prot.DeviceVerityInfo, securityPolicy securitypolicy.PolicyEnforcer) (err error) { ctx, span := trace.StartSpan(ctx, "scsi::Unmount") defer span.End() defer func() { oc.SetSpanStatus(span, err) }() diff --git a/internal/guest/storage/scsi/scsi_test.go b/internal/guest/storage/scsi/scsi_test.go index 853cb83df7..c7be782e1e 100644 --- a/internal/guest/storage/scsi/scsi_test.go +++ b/internal/guest/storage/scsi/scsi_test.go @@ -616,12 +616,12 @@ func Test_Security_Policy_Enforcement_Unmount_Calls(t *testing.T) { } } -func openDoorSecurityPolicyEnforcer() securitypolicy.SecurityPolicyEnforcer { - return &securitypolicy.OpenDoorSecurityPolicyEnforcer{} +func openDoorSecurityPolicyEnforcer() securitypolicy.PolicyEnforcer { + return &securitypolicy.OpenDoorEnforcer{} } -func mountMonitoringSecurityPolicyEnforcer() *policy.MountMonitoringSecurityPolicyEnforcer { - return &policy.MountMonitoringSecurityPolicyEnforcer{} +func mountMonitoringSecurityPolicyEnforcer() *policy.MountMonitoringEnforcer { + return &policy.MountMonitoringEnforcer{} } // dm-verity tests diff --git a/internal/guest/storage/test/policy/mountmonitorenforcer.go b/internal/guest/storage/test/policy/mountmonitorenforcer.go new file mode 100644 index 0000000000..fabf419d28 --- /dev/null +++ b/internal/guest/storage/test/policy/mountmonitorenforcer.go @@ -0,0 +1,34 @@ +package policy + +import ( + "github.com/Microsoft/hcsshim/pkg/securitypolicy" +) + +// For testing. Records the number of calls to each method so we can verify +// the expected interactions took place. +type MountMonitoringEnforcer struct { + DeviceMountCalls int + DeviceUnmountCalls int + OverlayMountCalls int +} + +var _ securitypolicy.PolicyEnforcer = (*MountMonitoringEnforcer)(nil) + +func (p *MountMonitoringEnforcer) EnforceDeviceMountPolicy(_ string, _ string) (err error) { + p.DeviceMountCalls++ + return nil +} + +func (p *MountMonitoringEnforcer) EnforceDeviceUnmountPolicy(_ string) (err error) { + p.DeviceUnmountCalls++ + return nil +} + +func (p *MountMonitoringEnforcer) EnforceOverlayMountPolicy(_ string, _ []string) (err error) { + p.OverlayMountCalls++ + return nil +} + +func (p *MountMonitoringEnforcer) EnforceCreateContainerPolicy(_ string, _ []string, _ []string) (err error) { + return nil +} diff --git a/internal/guest/storage/test/policy/mountmonitoringsecuritypolicyenforcer.go b/internal/guest/storage/test/policy/mountmonitoringsecuritypolicyenforcer.go deleted file mode 100644 index 3ba20684b4..0000000000 --- a/internal/guest/storage/test/policy/mountmonitoringsecuritypolicyenforcer.go +++ /dev/null @@ -1,34 +0,0 @@ -package policy - -import ( - "github.com/Microsoft/hcsshim/pkg/securitypolicy" -) - -// For testing. Records the number of calls to each method so we can verify -// the expected interactions took place. -type MountMonitoringSecurityPolicyEnforcer struct { - DeviceMountCalls int - DeviceUnmountCalls int - OverlayMountCalls int -} - -var _ securitypolicy.SecurityPolicyEnforcer = (*MountMonitoringSecurityPolicyEnforcer)(nil) - -func (p *MountMonitoringSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { - p.DeviceMountCalls++ - return nil -} - -func (p *MountMonitoringSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { - p.DeviceUnmountCalls++ - return nil -} - -func (p *MountMonitoringSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { - p.OverlayMountCalls++ - return nil -} - -func (p *MountMonitoringSecurityPolicyEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { - return nil -} diff --git a/internal/tools/securitypolicy/main.go b/internal/tools/securitypolicy/main.go index ad7e6d4498..e7554fe3f1 100644 --- a/internal/tools/securitypolicy/main.go +++ b/internal/tools/securitypolicy/main.go @@ -46,7 +46,7 @@ func main() { return err } - policy, err := func() (securitypolicy.SecurityPolicy, error) { + policy, err := func() (securitypolicy.Policy, error) { if config.AllowAll { return createOpenDoorPolicy(), nil } else { @@ -99,14 +99,14 @@ type Config struct { Containers []Container `toml:"container"` } -func createOpenDoorPolicy() securitypolicy.SecurityPolicy { - return securitypolicy.SecurityPolicy{ +func createOpenDoorPolicy() securitypolicy.Policy { + return securitypolicy.Policy{ AllowAll: true, } } -func createPolicyFromConfig(config Config) (securitypolicy.SecurityPolicy, error) { - p := securitypolicy.SecurityPolicy{ +func createPolicyFromConfig(config Config) (securitypolicy.Policy, error) { + p := securitypolicy.Policy{ Containers: securitypolicy.Containers{ Elements: map[string]securitypolicy.Container{}, }, diff --git a/test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicyenforcer.go b/pkg/securitypolicy/enforcement.go similarity index 58% rename from test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicyenforcer.go rename to pkg/securitypolicy/enforcement.go index 35807079d9..0d70841517 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicyenforcer.go +++ b/pkg/securitypolicy/enforcement.go @@ -10,87 +10,70 @@ import ( "github.com/google/go-cmp/cmp" ) -type SecurityPolicyEnforcer interface { +// PolicyEnforcer is an interface that encapsulates the logic necessary for +// enforcing a security policy +type PolicyEnforcer interface { EnforceDeviceMountPolicy(target string, deviceHash string) (err error) EnforceDeviceUnmountPolicy(unmountTarget string) (err error) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) } -func NewSecurityPolicyEnforcer(state SecurityPolicyState) (SecurityPolicyEnforcer, error) { +// NewSecurityPolicyEnforcer is a factory method that returns a corresponding +// PolicyEnforcer based on the State passed in +func NewSecurityPolicyEnforcer(state State) (PolicyEnforcer, error) { if state.SecurityPolicy.AllowAll { - return &OpenDoorSecurityPolicyEnforcer{}, nil + return &OpenDoorEnforcer{}, nil } else { containers, err := state.SecurityPolicy.Containers.toInternal() if err != nil { return nil, err } - return NewStandardSecurityPolicyEnforcer(containers, state.EncodedSecurityPolicy.SecurityPolicy), nil + return NewStandardEnforcer(containers, state.EncodedSecurityPolicy.SecurityPolicy), nil } } -type StandardSecurityPolicyEnforcer struct { +// StandardEnforcer enforces user provided security policy and tracks the +// internal state of all containers. +// +// Most of the work that this security policy enforcer does is around managing +// state needed to map from a container definition in the Policy to a specific +// container ID as we bring up each container. +// +// Implementation details are available in: +// - EnforceDeviceMountPolicy +// - EnforceDeviceUnmountPolicy +// - EnforceOverlayMountPolicy +// - EnforceCreateContainerPolicy +// - NewStandardEnforcer +type StandardEnforcer struct { // EncodedSecurityPolicy state is needed for key release EncodedSecurityPolicy string // Containers from the user supplied security policy. - Containers []securityPolicyContainer - // Devices and ContainerIndexToContainerIds are used to build up an - // understanding of the containers running with a UVM as they come up and - // map them back to a container definition from the user supplied - // SecurityPolicy // + // Containers that share the same base image, and perhaps further + // information, will have an entry per container instance in the + // SecurityPolicy. For example, a policy that has two containers that + // use Ubuntu 18.04 will have an entry for each even if they share the same + // command line. + Containers []container // Devices is a listing of targets seen when mounting a device // stored in a "per-container basis". As the UVM goes through its process of // bringing up containers, we have to piece together information about what // is going on. + Devices [][]string + // ContainerIndexToContainerIds is a mapping between a container defined in + // the policy to potential container IDs, that were created in the runtime // - // At the time that devices are being mounted, we do not know a container - // that they will be used for; only that there is a device with a given root - // hash that being mounted. We check to make sure that the root hash for the - // devices is a root hash that exists for 1 or more layers in any container - // in the supplied SecurityPolicy. Each "seen" layer is recorded in devices - // as it is mounted. So for example, if a root hash mount is found for the - // device being mounted and the first layer of the first container then we - // record the device target in Devices[0][0]. - // - // Later, when overlay filesystems created, we verify that the ordered layers - // for said overlay filesystem match one of the device orderings in Devices. - // When a match is found, the index in Devices is the same index in - // SecurityPolicy.Containers. Overlay filesystem creation is the first time we - // have a "container id" available to us. The container id identifies the - // container in question going forward. We record the mapping of Container - // index to container id so that when we have future operations like "run - // command" which come with a container id, we can find the corresponding - // container index and use that to look up the command in the appropriate - // SecurityPolicyContainer instance. + // Devices and ContainerIndexToContainerIds are used to build up an + // understanding of the containers running with a UVM as they come up and + // map them back to a container definition from the user supplied Policy // // As containers can have exactly the same base image and be "the same" at // the time we are doing overlay, the ContainerIndexToContainerIds in a // set of possible containers for a given container id. Go doesn't have a set // type so we are doing the idiomatic go thing of using a map[string]struct{} // to represent the set. - // - // Containers that share the same base image, and perhaps further - // information, will have an entry per container instance in the - // SecurityPolicy. For example, a policy that has two containers that - // use Ubuntu 18.04 will have an entry for each even if they share the same - // command line. - // - // Most of the work that this security policy enforcer does it around managing - // state needed to map from a container definition in the SecurityPolicy to - // a specfic container ID as we bring up each container. See - // enforceCommandPolicy where most of the functionality is handling the case - // were policy containers share an overlay and have to try to distinguish them - // based on the command line arguments. enforceEnvironmentVariablePolicy can - // further narrow based on environment variables if required. - // - // implementation details are available in: - // - EnforceDeviceMountPolicy - // - EnforceOverlayMountPolicy - // - enforceCommandPolicy - // - enforceEnvironmentVariablePolicy - // - NewStandardSecurityPolicyEnforcer - Devices [][]string ContainerIndexToContainerIds map[int]map[string]struct{} // Set of container IDs that we've allowed to start. Because Go doesn't have // sets as a built-in data structure, we are using a map @@ -99,21 +82,21 @@ type StandardSecurityPolicyEnforcer struct { mutex *sync.Mutex } -var _ SecurityPolicyEnforcer = (*StandardSecurityPolicyEnforcer)(nil) +var _ PolicyEnforcer = (*StandardEnforcer)(nil) -func NewStandardSecurityPolicyEnforcer(containers []securityPolicyContainer, encoded string) *StandardSecurityPolicyEnforcer { - // create new StandardSecurityPolicyEnforcer and add the expected containers - // to it - // fill out corresponding devices structure by creating a "same shapped" +// NewStandardEnforcer creates a new StandardEnforcer instance and adds the expected +// containers to it. +func NewStandardEnforcer(containers []container, encoded string) *StandardEnforcer { + // Fill out corresponding devices structure by creating a "same shaped" // devices listing that corresponds to our container root hash lists // the devices list will get filled out as layers are mounted devices := make([][]string, len(containers)) - for i, container := range containers { - devices[i] = make([]string, len(container.Layers)) + for i, c := range containers { + devices[i] = make([]string, len(c.Layers)) } - return &StandardSecurityPolicyEnforcer{ + return &StandardEnforcer{ EncodedSecurityPolicy: encoded, Containers: containers, Devices: devices, @@ -123,13 +106,13 @@ func NewStandardSecurityPolicyEnforcer(containers []securityPolicyContainer, enc } } -func (c Containers) toInternal() ([]securityPolicyContainer, error) { +func (c Containers) toInternal() ([]container, error) { containerMapLength := len(c.Elements) if c.Length != containerMapLength { return nil, fmt.Errorf("container numbers don't match in policy. expected: %d, actual: %d", c.Length, containerMapLength) } - internal := make([]securityPolicyContainer, containerMapLength) + internal := make([]container, containerMapLength) for i := 0; i < containerMapLength; i++ { iContainer, err := c.Elements[strconv.Itoa(i)].toInternal() @@ -144,23 +127,23 @@ func (c Containers) toInternal() ([]securityPolicyContainer, error) { return internal, nil } -func (c Container) toInternal() (securityPolicyContainer, error) { +func (c Container) toInternal() (container, error) { command, err := c.Command.toInternal() if err != nil { - return securityPolicyContainer{}, err + return container{}, err } envRules, err := c.EnvRules.toInternal() if err != nil { - return securityPolicyContainer{}, err + return container{}, err } layers, err := c.Layers.toInternal() if err != nil { - return securityPolicyContainer{}, err + return container{}, err } - return securityPolicyContainer{ + return container{ Command: command, EnvRules: envRules, Layers: layers, @@ -175,16 +158,16 @@ func (c CommandArgs) toInternal() ([]string, error) { return stringMapToStringArray(c.Elements), nil } -func (e EnvRules) toInternal() ([]securityPolicyEnvironmentVariableRule, error) { +func (e EnvRules) toInternal() ([]environmentVariableRule, error) { envRulesMapLength := len(e.Elements) if e.Length != envRulesMapLength { return nil, fmt.Errorf("env rule numbers don't match in policy. expected: %d, actual: %d", e.Length, envRulesMapLength) } - envRules := make([]securityPolicyEnvironmentVariableRule, envRulesMapLength) + envRules := make([]environmentVariableRule, envRulesMapLength) for i := 0; i < envRulesMapLength; i++ { eIndex := strconv.Itoa(i) - rule := securityPolicyEnvironmentVariableRule{ + rule := environmentVariableRule{ Strategy: e.Elements[eIndex].Strategy, Rule: e.Elements[eIndex].Rule, } @@ -213,7 +196,20 @@ func stringMapToStringArray(in map[string]string) []string { return out } -func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { +// EnforceDeviceMountPolicy for StandardEnforcer validates the target and +// its deviceHash against the read-only layers described in the security +// policy and updates internal state when the corresponding layer hash is +// found. +// +// At the time that devices are being mounted, we do not know a container +// that they will be used for; only that there is a device with a given root +// hash that being mounted. We check to make sure that the root hash for the +// devices is a root hash that exists for 1 or more layers in any container +// in the supplied Policy. Each "seen" layer is recorded in devices as it is +// mounted. So for example, if a root hash mount is found for the device being +// mounted and the first layer of the first container then we record the device +// target in Devices[0][0]. +func (pe *StandardEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { pe.mutex.Lock() defer pe.mutex.Unlock() @@ -227,8 +223,8 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string found := false - for i, container := range pe.Containers { - for ii, layer := range container.Layers { + for i, c := range pe.Containers { + for ii, layer := range c.Layers { if deviceHash == layer { pe.Devices[i][ii] = target found = true @@ -243,14 +239,16 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string return nil } -func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(unmountTarget string) (err error) { +// EnforceDeviceUnmountPolicy for StandardEnforcer finds the corresponding layers and resets +// the internal state. +func (pe *StandardEnforcer) EnforceDeviceUnmountPolicy(unmountTarget string) (err error) { pe.mutex.Lock() defer pe.mutex.Unlock() - for _, container := range pe.Devices { - for j, storedTarget := range container { + for _, targets := range pe.Devices { + for j, storedTarget := range targets { if unmountTarget == storedTarget { - container[j] = "" + targets[j] = "" } } } @@ -258,7 +256,20 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(unmountTarg return nil } -func (pe *StandardSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { +// EnforceOverlayMountPolicy for StandardEnforcer validates provided layerPaths +// against internal state. +// +// When overlay filesystems created, we verify that the ordered layers +// for said overlay filesystem match one of the device orderings in Devices. +// When a match is found, the index in Devices is the same index in +// Policy.Containers. Overlay filesystem creation is the first time we +// have a "container id" available to us. The container id identifies the +// container in question going forward. We record the mapping of Container +// index to container id so that when we have future operations like "run +// command" which come with a container id, we can find the corresponding +// container index and use that to look up the command in the appropriate +// Container instance. +func (pe *StandardEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { pe.mutex.Lock() defer pe.mutex.Unlock() @@ -298,7 +309,16 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID return nil } -func (pe *StandardSecurityPolicyEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { +// EnforceCreateContainerPolicy for StandardEnforcer validates the actual init +// process command line arguments and environment variables passed during +// container creation. The command line arguments must have an exact match +// where env vars must match a set of rules defined in the security policy. +// +// See enforceCommandPolicy where most of the functionality is handling the +// case were policy containers share an overlay and have to try to distinguish +// them based on the command line arguments. enforceEnvironmentVariablePolicy +// can further narrow based on environment variables if required. +func (pe *StandardEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { pe.mutex.Lock() defer pe.mutex.Unlock() @@ -326,7 +346,7 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceCreateContainerPolicy(container return nil } -func (pe *StandardSecurityPolicyEnforcer) enforceCommandPolicy(containerID string, argList []string) (err error) { +func (pe *StandardEnforcer) enforceCommandPolicy(containerID string, argList []string) (err error) { // Get a list of all the indexes into our security policy's list of // containers that are possible matches for this containerID based // on the image overlay layout @@ -357,7 +377,7 @@ func (pe *StandardSecurityPolicyEnforcer) enforceCommandPolicy(containerID strin return nil } -func (pe *StandardSecurityPolicyEnforcer) enforceEnvironmentVariablePolicy(containerID string, envList []string) (err error) { +func (pe *StandardEnforcer) enforceEnvironmentVariablePolicy(containerID string, envList []string) (err error) { // Get a list of all the indexes into our security policy's list of // containers that are possible matches for this containerID based // on the image overlay layout and command line @@ -385,7 +405,7 @@ func (pe *StandardSecurityPolicyEnforcer) enforceEnvironmentVariablePolicy(conta return nil } -func envIsMatchedByRule(envVariable string, rules []securityPolicyEnvironmentVariableRule) bool { +func envIsMatchedByRule(envVariable string, rules []environmentVariableRule) bool { for _, rule := range rules { switch rule.Strategy { case "string": @@ -404,7 +424,7 @@ func envIsMatchedByRule(envVariable string, rules []securityPolicyEnvironmentVar return false } -func (pe *StandardSecurityPolicyEnforcer) expandMatchesForContainerIndex(index int, idToAdd string) { +func (pe *StandardEnforcer) expandMatchesForContainerIndex(index int, idToAdd string) { _, keyExists := pe.ContainerIndexToContainerIds[index] if !keyExists { pe.ContainerIndexToContainerIds[index] = map[string]struct{}{} @@ -413,12 +433,12 @@ func (pe *StandardSecurityPolicyEnforcer) expandMatchesForContainerIndex(index i pe.ContainerIndexToContainerIds[index][idToAdd] = struct{}{} } -func (pe *StandardSecurityPolicyEnforcer) narrowMatchesForContainerIndex(index int, idToRemove string) { +func (pe *StandardEnforcer) narrowMatchesForContainerIndex(index int, idToRemove string) { delete(pe.ContainerIndexToContainerIds[index], idToRemove) } func equalForOverlay(a1 []string, a2 []string) bool { - // We've stored the layers from bottom to topl they are in layerPaths as + // We've stored the layers from bottom to top, they are in layerPaths as // top to bottom (the order a string gets concatenated for the unix mount // command). W do our check with that in mind. if len(a1) == len(a2) { @@ -435,7 +455,7 @@ func equalForOverlay(a1 []string, a2 []string) bool { } func possibleIndexesForID(containerID string, mapping map[int]map[string]struct{}) []int { - possibles := []int{} + var possibles []int for index, ids := range mapping { for id := range ids { if containerID == id { @@ -447,42 +467,50 @@ func possibleIndexesForID(containerID string, mapping map[int]map[string]struct{ return possibles } -type OpenDoorSecurityPolicyEnforcer struct{} +type OpenDoorEnforcer struct{} -var _ SecurityPolicyEnforcer = (*OpenDoorSecurityPolicyEnforcer)(nil) +var _ PolicyEnforcer = (*OpenDoorEnforcer)(nil) -func (p *OpenDoorSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { +// EnforceDeviceMountPolicy for OpenDoorEnforcer is a noop that allows everything +func (p *OpenDoorEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { return nil } -func (p *OpenDoorSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { +// EnforceDeviceUnmountPolicy for OpenDoorEnforcer is a noop that allows everything +func (p *OpenDoorEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { return nil } -func (p *OpenDoorSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { +// EnforceOverlayMountPolicy for OpenDoorEnforcer is a noop that allows everything +func (p *OpenDoorEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { return nil } -func (p *OpenDoorSecurityPolicyEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { +// EnforceCreateContainerPolicy for OpenDoorEnforcer is a noop that allows everything +func (p *OpenDoorEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { return nil } -type ClosedDoorSecurityPolicyEnforcer struct{} +type ClosedDoorEnforcer struct{} -var _ SecurityPolicyEnforcer = (*ClosedDoorSecurityPolicyEnforcer)(nil) +var _ PolicyEnforcer = (*ClosedDoorEnforcer)(nil) -func (p *ClosedDoorSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { +// EnforceDeviceMountPolicy for ClosedDoorEnforcer is a noop that rejects everything +func (p *ClosedDoorEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { return errors.New("mounting is denied by policy") } -func (p *ClosedDoorSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { +// EnforceDeviceUnmountPolicy for ClosedDoorEnforcer is a noop that rejects everything +func (p *ClosedDoorEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { return errors.New("unmounting is denied by policy") } -func (p *ClosedDoorSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { +// EnforceOverlayMountPolicy for ClosedDoorEnforcer is a noop that rejects everything +func (p *ClosedDoorEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { return errors.New("creating an overlay fs is denied by policy") } -func (p *ClosedDoorSecurityPolicyEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { +// EnforceCreateContainerPolicy for ClosedDoorEnforcer is a noop that rejects everything +func (p *ClosedDoorEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { return errors.New("running commands is denied by policy") } diff --git a/test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicy.go b/pkg/securitypolicy/policy.go similarity index 83% rename from test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicy.go rename to pkg/securitypolicy/policy.go index 3b381ccf2a..ae10a738fe 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicy.go +++ b/pkg/securitypolicy/policy.go @@ -14,12 +14,12 @@ const ( EnvVarRuleRegex EnvVarRule = "re2" ) -// Internal version of SecurityPolicyContainer -type securityPolicyContainer struct { +// Internal version of Container +type container struct { // The command that we will allow the container to execute Command []string `json:"command"` // The rules for determining if a given environment variable is allowed - EnvRules []securityPolicyEnvironmentVariableRule `json:"env_rules"` + EnvRules []environmentVariableRule `json:"env_rules"` // An ordered list of dm-verity root hashes for each layer that makes up // "a container". Containers are constructed as an overlay file system. The // order that the layers are overlayed is important and needs to be enforced @@ -27,34 +27,34 @@ type securityPolicyContainer struct { Layers []string `json:"layers"` } -// Internal versino of SecurityPolicyEnvironmentVariableRule -type securityPolicyEnvironmentVariableRule struct { +// Internal version of EnvRule +type environmentVariableRule struct { Strategy EnvVarRule `json:"type"` Rule string `json:"rule"` } -// SecurityPolicyState is a structure that holds user supplied policy to enforce +// State is a structure that holds user supplied policy to enforce // we keep both the encoded representation and the unmarshalled representation // because different components need to have access to either of these -type SecurityPolicyState struct { +type State struct { EncodedSecurityPolicy EncodedSecurityPolicy `json:"EncodedSecurityPolicy,omitempty"` - SecurityPolicy `json:"SecurityPolicy,omitempty"` + SecurityPolicy Policy `json:"SecurityPolicy,omitempty"` } -// EncodedSecurityPolicy is a JSON representation of SecurityPolicy that has +// EncodedSecurityPolicy is a JSON representation of Policy that has // been base64 encoded for storage in an annotation embedded within another // JSON configuration type EncodedSecurityPolicy struct { SecurityPolicy string `json:"SecurityPolicy,omitempty"` } -// Constructs SecurityPolicyState from base64Policy string. It first decodes +// NewSecurityPolicyState constructs State from base64Policy string. It first decodes // base64 policy and returns the structs security policy struct and encoded // security policy for given policy. The security policy is transmitted as json // in an annotation, so we first have to remove the base64 encoding that allows // the JSON based policy to be passed as a string. From there, we decode the -// JSONand setup our security policy struct -func NewSecurityPolicyState(base64Policy string) (*SecurityPolicyState, error) { +// JSON and setup our security policy struct +func NewSecurityPolicyState(base64Policy string) (*State, error) { // construct an encoded security policy that holds the base64 representation encodedSecurityPolicy := EncodedSecurityPolicy{ SecurityPolicy: base64Policy, @@ -70,25 +70,25 @@ func NewSecurityPolicyState(base64Policy string) (*SecurityPolicyState, error) { } // json unmarshall the decoded to a SecurityPolicy - securityPolicy := SecurityPolicy{} + securityPolicy := Policy{} err = json.Unmarshal(jsonPolicy, &securityPolicy) if err != nil { return nil, errors.Wrap(err, "unable to unmarshal JSON policy") } - return &SecurityPolicyState{ + return &State{ SecurityPolicy: securityPolicy, EncodedSecurityPolicy: encodedSecurityPolicy, }, nil } -type SecurityPolicy struct { +type Policy struct { // Flag that when set to true allows for all checks to pass. Currently used // to run with security policy enforcement "running dark"; checks can be in // place but the default policy that is created on startup has AllowAll set // to true, thus making policy enforcement effectively "off" from a logical // standpoint. Policy enforcement isn't actually off as the policy is "allow - // everything:. + // everything". AllowAll bool `json:"allow_all"` // One or more containers that are allowed to run Containers Containers `json:"containers"` @@ -127,7 +127,7 @@ type EnvRule struct { Rule string `json:"rule"` } -// Custom JSON marshalling to add `lenth` field that matches the number of +// Custom JSON marshalling to add `length` field that matches the number of // elements present in the `elements` field. func (c Containers) MarshalJSON() ([]byte, error) { type Alias Containers diff --git a/pkg/securitypolicy/securitypolicy_test.go b/pkg/securitypolicy/policy_test.go similarity index 87% rename from pkg/securitypolicy/securitypolicy_test.go rename to pkg/securitypolicy/policy_test.go index f9d1daea25..f117df85f9 100644 --- a/pkg/securitypolicy/securitypolicy_test.go +++ b/pkg/securitypolicy/policy_test.go @@ -33,7 +33,7 @@ const ( // Validate that our conversion from the external SecurityPolicy representation // to our internal format is done correctly. func Test_StandardSecurityPolicyEnforcer_From_Security_Policy_Conversion(t *testing.T) { - f := func(p *SecurityPolicy) bool { + f := func(p *Policy) bool { containers, err := p.Containers.toInternal() if err != nil { t.Logf("unexpected setup error. this might mean test fixture setup has a bug: %v", err) @@ -98,7 +98,7 @@ func Test_StandardSecurityPolicyEnforcer_From_Security_Policy_Conversion(t *test // StandardSecurityPolicyEnforcer func Test_StandardSecurityPolicyEnforcer_Devices_Initialization(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) // there should be a device entry for each container if len(p.containers) != len(policy.Devices) { @@ -125,7 +125,7 @@ func Test_StandardSecurityPolicyEnforcer_Devices_Initialization(t *testing.T) { // return an error when there's no matching root hash in the policy func Test_EnforceDeviceMountPolicy_No_Matches(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) target := generateMountTarget(r) @@ -147,7 +147,7 @@ func Test_EnforceDeviceMountPolicy_No_Matches(t *testing.T) { func Test_EnforceDeviceMountPolicy_Matches(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) target := generateMountTarget(r) @@ -166,7 +166,7 @@ func Test_EnforceDeviceMountPolicy_Matches(t *testing.T) { func Test_EnforceDeviceUmountPolicy_Removes_Device_Entries(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) target := generateMountTarget(r) @@ -216,7 +216,7 @@ func Test_EnforceDeviceUmountPolicy_Removes_Device_Entries(t *testing.T) { func Test_EnforceOverlayMountPolicy_No_Matches(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) containerID := generateContainerID(r) @@ -243,7 +243,7 @@ func Test_EnforceOverlayMountPolicy_No_Matches(t *testing.T) { func Test_EnforceOverlayMountPolicy_Matches(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) containerID := generateContainerID(r) @@ -270,7 +270,7 @@ func Test_EnforceOverlayMountPolicy_Overlay_Single_Container_Twice(t *testing.T) r := rand.New(rand.NewSource(time.Now().UnixNano())) p := generateContainers(r, 1) - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) containerID := generateContainerID(r) container := selectContainerFromContainers(p, r) @@ -298,11 +298,11 @@ func Test_EnforceOverlayMountPolicy_Overlay_Single_Container_Twice(t *testing.T) func Test_EnforceOverlayMountPolicy_Multiple_Instances_Same_Container(t *testing.T) { for containersToCreate := 2; containersToCreate <= maxContainersInGeneratedPolicy; containersToCreate++ { r := rand.New(rand.NewSource(time.Now().UnixNano())) - var containers []securityPolicyContainer + var containers []container for i := 1; i <= int(containersToCreate); i++ { arg := "command " + strconv.Itoa(i) - c := securityPolicyContainer{ + c := container{ Command: []string{arg}, Layers: []string{"1", "2"}, } @@ -310,7 +310,7 @@ func Test_EnforceOverlayMountPolicy_Multiple_Instances_Same_Container(t *testing containers = append(containers, c) } - policy := NewStandardSecurityPolicyEnforcer(containers, "") + policy := NewStandardEnforcer(containers, "") idsUsed := map[string]bool{} for i := 0; i < len(containers); i++ { @@ -345,7 +345,7 @@ func Test_EnforceOverlayMountPolicy_Overlay_Single_Container_Twice_With_Differen r := rand.New(rand.NewSource(time.Now().UnixNano())) p := generateContainers(r, 1) - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) var containerIDOne, containerIDTwo string @@ -373,13 +373,13 @@ func Test_EnforceOverlayMountPolicy_Overlay_Single_Container_Twice_With_Differen func Test_EnforceCommandPolicy_Matches(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) containerID := generateContainerID(r) - container := selectContainerFromContainers(p, r) + c := selectContainerFromContainers(p, r) - layerPaths, err := createValidOverlayForContainer(policy, container, r) + layerPaths, err := createValidOverlayForContainer(policy, c, r) if err != nil { return false } @@ -389,7 +389,7 @@ func Test_EnforceCommandPolicy_Matches(t *testing.T) { return false } - err = policy.enforceCommandPolicy(containerID, container.Command) + err = policy.enforceCommandPolicy(containerID, c.Command) // getting an error means something is broken return err == nil @@ -402,7 +402,7 @@ func Test_EnforceCommandPolicy_Matches(t *testing.T) { func Test_EnforceCommandPolicy_NoMatches(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) containerID := generateContainerID(r) @@ -450,7 +450,7 @@ func Test_EnforceCommandPolicy_NarrowingMatches(t *testing.T) { // add new containers to policy before creating enforcer p.containers = append(p.containers, testContainerOne, testContainerTwo) - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) testContainerOneID := "" testContainerTwoID := "" @@ -533,7 +533,7 @@ func Test_EnforceCommandPolicy_NarrowingMatches(t *testing.T) { func Test_EnforceEnvironmentVariablePolicy_Matches(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) containerID := generateContainerID(r) @@ -565,19 +565,19 @@ func Test_EnforceEnvironmentVariablePolicy_Re2Match(t *testing.T) { r := rand.New(rand.NewSource(time.Now().UnixNano())) p := generateContainers(r, 1) - container := generateContainersContainer(r, 1) + c := generateContainersContainer(r, 1) // add a rule to re2 match - re2MatchRule := securityPolicyEnvironmentVariableRule{ + re2MatchRule := environmentVariableRule{ Strategy: EnvVarRuleRegex, Rule: "PREFIX_.+=.+"} - container.EnvRules = append(container.EnvRules, re2MatchRule) - p.containers = append(p.containers, container) + c.EnvRules = append(c.EnvRules, re2MatchRule) + p.containers = append(p.containers, c) - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) containerID := generateContainerID(r) - layerPaths, err := createValidOverlayForContainer(policy, container, r) + layerPaths, err := createValidOverlayForContainer(policy, c, r) if err != nil { t.Fatalf("expected nil error got: %v", err) } @@ -598,7 +598,7 @@ func Test_EnforceEnvironmentVariablePolicy_Re2Match(t *testing.T) { func Test_EnforceEnvironmentVariablePolicy_NotAllMatches(t *testing.T) { f := func(p *generatedContainers) bool { - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) r := rand.New(rand.NewSource(time.Now().UnixNano())) containerID := generateContainerID(r) @@ -649,7 +649,7 @@ func Test_EnforceEnvironmentVariablePolicy_NarrowingMatches(t *testing.T) { // add new containers to policy before creating enforcer p.containers = append(p.containers, testContainerOne, testContainerTwo) - policy := NewStandardSecurityPolicyEnforcer(p.containers, ignoredEncodedPolicyString) + policy := NewStandardEnforcer(p.containers, ignoredEncodedPolicyString) testContainerOneID := "" testContainerTwoID := "" @@ -735,12 +735,12 @@ func Test_EnforceEnvironmentVariablePolicy_NarrowingMatches(t *testing.T) { // Setup and "fixtures" follow... // -func (*SecurityPolicy) Generate(r *rand.Rand, size int) reflect.Value { +func (*Policy) Generate(r *rand.Rand, size int) reflect.Value { // This fixture setup is used from 1 test. Given the limited scope it is // used from, all functionality is in this single function. That saves having // confusing fixture name functions where we have generate* for both internal // and external versions - p := &SecurityPolicy{ + p := &Policy{ Containers: Containers{ Elements: map[string]Container{}, }, @@ -799,7 +799,7 @@ func (*generatedContainers) Generate(r *rand.Rand, size int) reflect.Value { } func generateContainers(r *rand.Rand, upTo int32) *generatedContainers { - containers := []securityPolicyContainer{} + var containers []container numContainers := (int)(atLeastOneAtMost(r, upTo)) for i := 0; i < numContainers; i++ { @@ -811,8 +811,8 @@ func generateContainers(r *rand.Rand, upTo int32) *generatedContainers { } } -func generateContainersContainer(r *rand.Rand, size int32) securityPolicyContainer { - c := securityPolicyContainer{} +func generateContainersContainer(r *rand.Rand, size int32) container { + c := container{} c.Command = generateCommand(r) c.EnvRules = generateEnvironmentVariableRules(r) layers := int(atLeastOneAtMost(r, size)) @@ -838,12 +838,12 @@ func generateCommand(r *rand.Rand) []string { return args } -func generateEnvironmentVariableRules(r *rand.Rand) []securityPolicyEnvironmentVariableRule { - rules := []securityPolicyEnvironmentVariableRule{} +func generateEnvironmentVariableRules(r *rand.Rand) []environmentVariableRule { + var rules []environmentVariableRule numArgs := atLeastOneAtMost(r, maxGeneratedEnvironmentVariableRules) for i := 0; i < int(numArgs); i++ { - rule := securityPolicyEnvironmentVariableRule{ + rule := environmentVariableRule{ Strategy: "string", Rule: randVariableString(r, maxGeneratedEnvironmentVariableRuleLength), } @@ -869,7 +869,7 @@ func generateNeverMatchingEnvironmentVariable(r *rand.Rand) string { return randString(r, maxGeneratedEnvironmentVariableRuleLength+1) } -func buildEnvironmentVariablesFromContainerRules(c securityPolicyContainer, r *rand.Rand) []string { +func buildEnvironmentVariablesFromContainerRules(c container, r *rand.Rand) []string { vars := make([]string, 0) // Select some number of the valid, matching rules to be environment @@ -932,18 +932,18 @@ func generateContainerID(r *rand.Rand) string { return strconv.FormatInt(int64(id), 10) } -func selectContainerFromContainers(containers *generatedContainers, r *rand.Rand) securityPolicyContainer { +func selectContainerFromContainers(containers *generatedContainers, r *rand.Rand) container { numberOfContainersInPolicy := len(containers.containers) return containers.containers[r.Intn(numberOfContainersInPolicy)] } -func createValidOverlayForContainer(enforcer SecurityPolicyEnforcer, container securityPolicyContainer, r *rand.Rand) ([]string, error) { +func createValidOverlayForContainer(enforcer PolicyEnforcer, c container, r *rand.Rand) ([]string, error) { // storage for our mount paths - overlay := make([]string, len(container.Layers)) + overlay := make([]string, len(c.Layers)) - for i := 0; i < len(container.Layers); i++ { + for i := 0; i < len(c.Layers); i++ { mount := generateMountTarget(r) - err := enforcer.EnforceDeviceMountPolicy(mount, container.Layers[i]) + err := enforcer.EnforceDeviceMountPolicy(mount, c.Layers[i]) if err != nil { return overlay, err } @@ -954,24 +954,24 @@ func createValidOverlayForContainer(enforcer SecurityPolicyEnforcer, container s return overlay, nil } -func createInvalidOverlayForContainer(enforcer SecurityPolicyEnforcer, container securityPolicyContainer, r *rand.Rand) ([]string, error) { +func createInvalidOverlayForContainer(enforcer PolicyEnforcer, c container, r *rand.Rand) ([]string, error) { method := r.Intn(3) if method == 0 { - return invalidOverlaySameSizeWrongMounts(enforcer, container, r) + return invalidOverlaySameSizeWrongMounts(enforcer, c, r) } else if method == 1 { - return invalidOverlayCorrectDevicesWrongOrderSomeMissing(enforcer, container, r) + return invalidOverlayCorrectDevicesWrongOrderSomeMissing(enforcer, c, r) } else { - return invalidOverlayRandomJunk(enforcer, container, r) + return invalidOverlayRandomJunk(enforcer, c, r) } } -func invalidOverlaySameSizeWrongMounts(enforcer SecurityPolicyEnforcer, container securityPolicyContainer, r *rand.Rand) ([]string, error) { +func invalidOverlaySameSizeWrongMounts(enforcer PolicyEnforcer, c container, r *rand.Rand) ([]string, error) { // storage for our mount paths - overlay := make([]string, len(container.Layers)) + overlay := make([]string, len(c.Layers)) - for i := 0; i < len(container.Layers); i++ { + for i := 0; i < len(c.Layers); i++ { mount := generateMountTarget(r) - err := enforcer.EnforceDeviceMountPolicy(mount, container.Layers[i]) + err := enforcer.EnforceDeviceMountPolicy(mount, c.Layers[i]) if err != nil { return overlay, err } @@ -983,17 +983,17 @@ func invalidOverlaySameSizeWrongMounts(enforcer SecurityPolicyEnforcer, containe return overlay, nil } -func invalidOverlayCorrectDevicesWrongOrderSomeMissing(enforcer SecurityPolicyEnforcer, container securityPolicyContainer, r *rand.Rand) ([]string, error) { - if len(container.Layers) == 1 { +func invalidOverlayCorrectDevicesWrongOrderSomeMissing(enforcer PolicyEnforcer, c container, r *rand.Rand) ([]string, error) { + if len(c.Layers) == 1 { // won't work with only 1, we need to bail out to another method - return invalidOverlayRandomJunk(enforcer, container, r) + return invalidOverlayRandomJunk(enforcer, c, r) } // storage for our mount paths var overlay []string - for i := 0; i < len(container.Layers); i++ { + for i := 0; i < len(c.Layers); i++ { mount := generateMountTarget(r) - err := enforcer.EnforceDeviceMountPolicy(mount, container.Layers[i]) + err := enforcer.EnforceDeviceMountPolicy(mount, c.Layers[i]) if err != nil { return overlay, err } @@ -1006,7 +1006,7 @@ func invalidOverlayCorrectDevicesWrongOrderSomeMissing(enforcer SecurityPolicyEn return overlay, nil } -func invalidOverlayRandomJunk(enforcer SecurityPolicyEnforcer, container securityPolicyContainer, r *rand.Rand) ([]string, error) { +func invalidOverlayRandomJunk(enforcer PolicyEnforcer, c container, r *rand.Rand) ([]string, error) { // create "junk" for entry layersToCreate := r.Int31n(maxLayersInGeneratedContainer) overlay := make([]string, layersToCreate) @@ -1016,9 +1016,9 @@ func invalidOverlayRandomJunk(enforcer SecurityPolicyEnforcer, container securit } // setup entirely different and "correct" expected mounting - for i := 0; i < len(container.Layers); i++ { + for i := 0; i < len(c.Layers); i++ { mount := generateMountTarget(r) - err := enforcer.EnforceDeviceMountPolicy(mount, container.Layers[i]) + err := enforcer.EnforceDeviceMountPolicy(mount, c.Layers[i]) if err != nil { return overlay, err } @@ -1054,5 +1054,5 @@ func atMost(r *rand.Rand, most int32) int32 { // a type to hold a list of generated containers type generatedContainers struct { - containers []securityPolicyContainer + containers []container } diff --git a/pkg/securitypolicy/securitypolicyenforcer.go b/test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/enforcement.go similarity index 58% rename from pkg/securitypolicy/securitypolicyenforcer.go rename to test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/enforcement.go index 35807079d9..0d70841517 100644 --- a/pkg/securitypolicy/securitypolicyenforcer.go +++ b/test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/enforcement.go @@ -10,87 +10,70 @@ import ( "github.com/google/go-cmp/cmp" ) -type SecurityPolicyEnforcer interface { +// PolicyEnforcer is an interface that encapsulates the logic necessary for +// enforcing a security policy +type PolicyEnforcer interface { EnforceDeviceMountPolicy(target string, deviceHash string) (err error) EnforceDeviceUnmountPolicy(unmountTarget string) (err error) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) } -func NewSecurityPolicyEnforcer(state SecurityPolicyState) (SecurityPolicyEnforcer, error) { +// NewSecurityPolicyEnforcer is a factory method that returns a corresponding +// PolicyEnforcer based on the State passed in +func NewSecurityPolicyEnforcer(state State) (PolicyEnforcer, error) { if state.SecurityPolicy.AllowAll { - return &OpenDoorSecurityPolicyEnforcer{}, nil + return &OpenDoorEnforcer{}, nil } else { containers, err := state.SecurityPolicy.Containers.toInternal() if err != nil { return nil, err } - return NewStandardSecurityPolicyEnforcer(containers, state.EncodedSecurityPolicy.SecurityPolicy), nil + return NewStandardEnforcer(containers, state.EncodedSecurityPolicy.SecurityPolicy), nil } } -type StandardSecurityPolicyEnforcer struct { +// StandardEnforcer enforces user provided security policy and tracks the +// internal state of all containers. +// +// Most of the work that this security policy enforcer does is around managing +// state needed to map from a container definition in the Policy to a specific +// container ID as we bring up each container. +// +// Implementation details are available in: +// - EnforceDeviceMountPolicy +// - EnforceDeviceUnmountPolicy +// - EnforceOverlayMountPolicy +// - EnforceCreateContainerPolicy +// - NewStandardEnforcer +type StandardEnforcer struct { // EncodedSecurityPolicy state is needed for key release EncodedSecurityPolicy string // Containers from the user supplied security policy. - Containers []securityPolicyContainer - // Devices and ContainerIndexToContainerIds are used to build up an - // understanding of the containers running with a UVM as they come up and - // map them back to a container definition from the user supplied - // SecurityPolicy // + // Containers that share the same base image, and perhaps further + // information, will have an entry per container instance in the + // SecurityPolicy. For example, a policy that has two containers that + // use Ubuntu 18.04 will have an entry for each even if they share the same + // command line. + Containers []container // Devices is a listing of targets seen when mounting a device // stored in a "per-container basis". As the UVM goes through its process of // bringing up containers, we have to piece together information about what // is going on. + Devices [][]string + // ContainerIndexToContainerIds is a mapping between a container defined in + // the policy to potential container IDs, that were created in the runtime // - // At the time that devices are being mounted, we do not know a container - // that they will be used for; only that there is a device with a given root - // hash that being mounted. We check to make sure that the root hash for the - // devices is a root hash that exists for 1 or more layers in any container - // in the supplied SecurityPolicy. Each "seen" layer is recorded in devices - // as it is mounted. So for example, if a root hash mount is found for the - // device being mounted and the first layer of the first container then we - // record the device target in Devices[0][0]. - // - // Later, when overlay filesystems created, we verify that the ordered layers - // for said overlay filesystem match one of the device orderings in Devices. - // When a match is found, the index in Devices is the same index in - // SecurityPolicy.Containers. Overlay filesystem creation is the first time we - // have a "container id" available to us. The container id identifies the - // container in question going forward. We record the mapping of Container - // index to container id so that when we have future operations like "run - // command" which come with a container id, we can find the corresponding - // container index and use that to look up the command in the appropriate - // SecurityPolicyContainer instance. + // Devices and ContainerIndexToContainerIds are used to build up an + // understanding of the containers running with a UVM as they come up and + // map them back to a container definition from the user supplied Policy // // As containers can have exactly the same base image and be "the same" at // the time we are doing overlay, the ContainerIndexToContainerIds in a // set of possible containers for a given container id. Go doesn't have a set // type so we are doing the idiomatic go thing of using a map[string]struct{} // to represent the set. - // - // Containers that share the same base image, and perhaps further - // information, will have an entry per container instance in the - // SecurityPolicy. For example, a policy that has two containers that - // use Ubuntu 18.04 will have an entry for each even if they share the same - // command line. - // - // Most of the work that this security policy enforcer does it around managing - // state needed to map from a container definition in the SecurityPolicy to - // a specfic container ID as we bring up each container. See - // enforceCommandPolicy where most of the functionality is handling the case - // were policy containers share an overlay and have to try to distinguish them - // based on the command line arguments. enforceEnvironmentVariablePolicy can - // further narrow based on environment variables if required. - // - // implementation details are available in: - // - EnforceDeviceMountPolicy - // - EnforceOverlayMountPolicy - // - enforceCommandPolicy - // - enforceEnvironmentVariablePolicy - // - NewStandardSecurityPolicyEnforcer - Devices [][]string ContainerIndexToContainerIds map[int]map[string]struct{} // Set of container IDs that we've allowed to start. Because Go doesn't have // sets as a built-in data structure, we are using a map @@ -99,21 +82,21 @@ type StandardSecurityPolicyEnforcer struct { mutex *sync.Mutex } -var _ SecurityPolicyEnforcer = (*StandardSecurityPolicyEnforcer)(nil) +var _ PolicyEnforcer = (*StandardEnforcer)(nil) -func NewStandardSecurityPolicyEnforcer(containers []securityPolicyContainer, encoded string) *StandardSecurityPolicyEnforcer { - // create new StandardSecurityPolicyEnforcer and add the expected containers - // to it - // fill out corresponding devices structure by creating a "same shapped" +// NewStandardEnforcer creates a new StandardEnforcer instance and adds the expected +// containers to it. +func NewStandardEnforcer(containers []container, encoded string) *StandardEnforcer { + // Fill out corresponding devices structure by creating a "same shaped" // devices listing that corresponds to our container root hash lists // the devices list will get filled out as layers are mounted devices := make([][]string, len(containers)) - for i, container := range containers { - devices[i] = make([]string, len(container.Layers)) + for i, c := range containers { + devices[i] = make([]string, len(c.Layers)) } - return &StandardSecurityPolicyEnforcer{ + return &StandardEnforcer{ EncodedSecurityPolicy: encoded, Containers: containers, Devices: devices, @@ -123,13 +106,13 @@ func NewStandardSecurityPolicyEnforcer(containers []securityPolicyContainer, enc } } -func (c Containers) toInternal() ([]securityPolicyContainer, error) { +func (c Containers) toInternal() ([]container, error) { containerMapLength := len(c.Elements) if c.Length != containerMapLength { return nil, fmt.Errorf("container numbers don't match in policy. expected: %d, actual: %d", c.Length, containerMapLength) } - internal := make([]securityPolicyContainer, containerMapLength) + internal := make([]container, containerMapLength) for i := 0; i < containerMapLength; i++ { iContainer, err := c.Elements[strconv.Itoa(i)].toInternal() @@ -144,23 +127,23 @@ func (c Containers) toInternal() ([]securityPolicyContainer, error) { return internal, nil } -func (c Container) toInternal() (securityPolicyContainer, error) { +func (c Container) toInternal() (container, error) { command, err := c.Command.toInternal() if err != nil { - return securityPolicyContainer{}, err + return container{}, err } envRules, err := c.EnvRules.toInternal() if err != nil { - return securityPolicyContainer{}, err + return container{}, err } layers, err := c.Layers.toInternal() if err != nil { - return securityPolicyContainer{}, err + return container{}, err } - return securityPolicyContainer{ + return container{ Command: command, EnvRules: envRules, Layers: layers, @@ -175,16 +158,16 @@ func (c CommandArgs) toInternal() ([]string, error) { return stringMapToStringArray(c.Elements), nil } -func (e EnvRules) toInternal() ([]securityPolicyEnvironmentVariableRule, error) { +func (e EnvRules) toInternal() ([]environmentVariableRule, error) { envRulesMapLength := len(e.Elements) if e.Length != envRulesMapLength { return nil, fmt.Errorf("env rule numbers don't match in policy. expected: %d, actual: %d", e.Length, envRulesMapLength) } - envRules := make([]securityPolicyEnvironmentVariableRule, envRulesMapLength) + envRules := make([]environmentVariableRule, envRulesMapLength) for i := 0; i < envRulesMapLength; i++ { eIndex := strconv.Itoa(i) - rule := securityPolicyEnvironmentVariableRule{ + rule := environmentVariableRule{ Strategy: e.Elements[eIndex].Strategy, Rule: e.Elements[eIndex].Rule, } @@ -213,7 +196,20 @@ func stringMapToStringArray(in map[string]string) []string { return out } -func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { +// EnforceDeviceMountPolicy for StandardEnforcer validates the target and +// its deviceHash against the read-only layers described in the security +// policy and updates internal state when the corresponding layer hash is +// found. +// +// At the time that devices are being mounted, we do not know a container +// that they will be used for; only that there is a device with a given root +// hash that being mounted. We check to make sure that the root hash for the +// devices is a root hash that exists for 1 or more layers in any container +// in the supplied Policy. Each "seen" layer is recorded in devices as it is +// mounted. So for example, if a root hash mount is found for the device being +// mounted and the first layer of the first container then we record the device +// target in Devices[0][0]. +func (pe *StandardEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { pe.mutex.Lock() defer pe.mutex.Unlock() @@ -227,8 +223,8 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string found := false - for i, container := range pe.Containers { - for ii, layer := range container.Layers { + for i, c := range pe.Containers { + for ii, layer := range c.Layers { if deviceHash == layer { pe.Devices[i][ii] = target found = true @@ -243,14 +239,16 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string return nil } -func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(unmountTarget string) (err error) { +// EnforceDeviceUnmountPolicy for StandardEnforcer finds the corresponding layers and resets +// the internal state. +func (pe *StandardEnforcer) EnforceDeviceUnmountPolicy(unmountTarget string) (err error) { pe.mutex.Lock() defer pe.mutex.Unlock() - for _, container := range pe.Devices { - for j, storedTarget := range container { + for _, targets := range pe.Devices { + for j, storedTarget := range targets { if unmountTarget == storedTarget { - container[j] = "" + targets[j] = "" } } } @@ -258,7 +256,20 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(unmountTarg return nil } -func (pe *StandardSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { +// EnforceOverlayMountPolicy for StandardEnforcer validates provided layerPaths +// against internal state. +// +// When overlay filesystems created, we verify that the ordered layers +// for said overlay filesystem match one of the device orderings in Devices. +// When a match is found, the index in Devices is the same index in +// Policy.Containers. Overlay filesystem creation is the first time we +// have a "container id" available to us. The container id identifies the +// container in question going forward. We record the mapping of Container +// index to container id so that when we have future operations like "run +// command" which come with a container id, we can find the corresponding +// container index and use that to look up the command in the appropriate +// Container instance. +func (pe *StandardEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { pe.mutex.Lock() defer pe.mutex.Unlock() @@ -298,7 +309,16 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID return nil } -func (pe *StandardSecurityPolicyEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { +// EnforceCreateContainerPolicy for StandardEnforcer validates the actual init +// process command line arguments and environment variables passed during +// container creation. The command line arguments must have an exact match +// where env vars must match a set of rules defined in the security policy. +// +// See enforceCommandPolicy where most of the functionality is handling the +// case were policy containers share an overlay and have to try to distinguish +// them based on the command line arguments. enforceEnvironmentVariablePolicy +// can further narrow based on environment variables if required. +func (pe *StandardEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { pe.mutex.Lock() defer pe.mutex.Unlock() @@ -326,7 +346,7 @@ func (pe *StandardSecurityPolicyEnforcer) EnforceCreateContainerPolicy(container return nil } -func (pe *StandardSecurityPolicyEnforcer) enforceCommandPolicy(containerID string, argList []string) (err error) { +func (pe *StandardEnforcer) enforceCommandPolicy(containerID string, argList []string) (err error) { // Get a list of all the indexes into our security policy's list of // containers that are possible matches for this containerID based // on the image overlay layout @@ -357,7 +377,7 @@ func (pe *StandardSecurityPolicyEnforcer) enforceCommandPolicy(containerID strin return nil } -func (pe *StandardSecurityPolicyEnforcer) enforceEnvironmentVariablePolicy(containerID string, envList []string) (err error) { +func (pe *StandardEnforcer) enforceEnvironmentVariablePolicy(containerID string, envList []string) (err error) { // Get a list of all the indexes into our security policy's list of // containers that are possible matches for this containerID based // on the image overlay layout and command line @@ -385,7 +405,7 @@ func (pe *StandardSecurityPolicyEnforcer) enforceEnvironmentVariablePolicy(conta return nil } -func envIsMatchedByRule(envVariable string, rules []securityPolicyEnvironmentVariableRule) bool { +func envIsMatchedByRule(envVariable string, rules []environmentVariableRule) bool { for _, rule := range rules { switch rule.Strategy { case "string": @@ -404,7 +424,7 @@ func envIsMatchedByRule(envVariable string, rules []securityPolicyEnvironmentVar return false } -func (pe *StandardSecurityPolicyEnforcer) expandMatchesForContainerIndex(index int, idToAdd string) { +func (pe *StandardEnforcer) expandMatchesForContainerIndex(index int, idToAdd string) { _, keyExists := pe.ContainerIndexToContainerIds[index] if !keyExists { pe.ContainerIndexToContainerIds[index] = map[string]struct{}{} @@ -413,12 +433,12 @@ func (pe *StandardSecurityPolicyEnforcer) expandMatchesForContainerIndex(index i pe.ContainerIndexToContainerIds[index][idToAdd] = struct{}{} } -func (pe *StandardSecurityPolicyEnforcer) narrowMatchesForContainerIndex(index int, idToRemove string) { +func (pe *StandardEnforcer) narrowMatchesForContainerIndex(index int, idToRemove string) { delete(pe.ContainerIndexToContainerIds[index], idToRemove) } func equalForOverlay(a1 []string, a2 []string) bool { - // We've stored the layers from bottom to topl they are in layerPaths as + // We've stored the layers from bottom to top, they are in layerPaths as // top to bottom (the order a string gets concatenated for the unix mount // command). W do our check with that in mind. if len(a1) == len(a2) { @@ -435,7 +455,7 @@ func equalForOverlay(a1 []string, a2 []string) bool { } func possibleIndexesForID(containerID string, mapping map[int]map[string]struct{}) []int { - possibles := []int{} + var possibles []int for index, ids := range mapping { for id := range ids { if containerID == id { @@ -447,42 +467,50 @@ func possibleIndexesForID(containerID string, mapping map[int]map[string]struct{ return possibles } -type OpenDoorSecurityPolicyEnforcer struct{} +type OpenDoorEnforcer struct{} -var _ SecurityPolicyEnforcer = (*OpenDoorSecurityPolicyEnforcer)(nil) +var _ PolicyEnforcer = (*OpenDoorEnforcer)(nil) -func (p *OpenDoorSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { +// EnforceDeviceMountPolicy for OpenDoorEnforcer is a noop that allows everything +func (p *OpenDoorEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { return nil } -func (p *OpenDoorSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { +// EnforceDeviceUnmountPolicy for OpenDoorEnforcer is a noop that allows everything +func (p *OpenDoorEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { return nil } -func (p *OpenDoorSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { +// EnforceOverlayMountPolicy for OpenDoorEnforcer is a noop that allows everything +func (p *OpenDoorEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { return nil } -func (p *OpenDoorSecurityPolicyEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { +// EnforceCreateContainerPolicy for OpenDoorEnforcer is a noop that allows everything +func (p *OpenDoorEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { return nil } -type ClosedDoorSecurityPolicyEnforcer struct{} +type ClosedDoorEnforcer struct{} -var _ SecurityPolicyEnforcer = (*ClosedDoorSecurityPolicyEnforcer)(nil) +var _ PolicyEnforcer = (*ClosedDoorEnforcer)(nil) -func (p *ClosedDoorSecurityPolicyEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { +// EnforceDeviceMountPolicy for ClosedDoorEnforcer is a noop that rejects everything +func (p *ClosedDoorEnforcer) EnforceDeviceMountPolicy(target string, deviceHash string) (err error) { return errors.New("mounting is denied by policy") } -func (p *ClosedDoorSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { +// EnforceDeviceUnmountPolicy for ClosedDoorEnforcer is a noop that rejects everything +func (p *ClosedDoorEnforcer) EnforceDeviceUnmountPolicy(target string) (err error) { return errors.New("unmounting is denied by policy") } -func (p *ClosedDoorSecurityPolicyEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { +// EnforceOverlayMountPolicy for ClosedDoorEnforcer is a noop that rejects everything +func (p *ClosedDoorEnforcer) EnforceOverlayMountPolicy(containerID string, layerPaths []string) (err error) { return errors.New("creating an overlay fs is denied by policy") } -func (p *ClosedDoorSecurityPolicyEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { +// EnforceCreateContainerPolicy for ClosedDoorEnforcer is a noop that rejects everything +func (p *ClosedDoorEnforcer) EnforceCreateContainerPolicy(containerID string, argList []string, envList []string) (err error) { return errors.New("running commands is denied by policy") } diff --git a/pkg/securitypolicy/securitypolicy.go b/test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/policy.go similarity index 83% rename from pkg/securitypolicy/securitypolicy.go rename to test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/policy.go index 3b381ccf2a..ae10a738fe 100644 --- a/pkg/securitypolicy/securitypolicy.go +++ b/test/vendor/github.com/Microsoft/hcsshim/pkg/securitypolicy/policy.go @@ -14,12 +14,12 @@ const ( EnvVarRuleRegex EnvVarRule = "re2" ) -// Internal version of SecurityPolicyContainer -type securityPolicyContainer struct { +// Internal version of Container +type container struct { // The command that we will allow the container to execute Command []string `json:"command"` // The rules for determining if a given environment variable is allowed - EnvRules []securityPolicyEnvironmentVariableRule `json:"env_rules"` + EnvRules []environmentVariableRule `json:"env_rules"` // An ordered list of dm-verity root hashes for each layer that makes up // "a container". Containers are constructed as an overlay file system. The // order that the layers are overlayed is important and needs to be enforced @@ -27,34 +27,34 @@ type securityPolicyContainer struct { Layers []string `json:"layers"` } -// Internal versino of SecurityPolicyEnvironmentVariableRule -type securityPolicyEnvironmentVariableRule struct { +// Internal version of EnvRule +type environmentVariableRule struct { Strategy EnvVarRule `json:"type"` Rule string `json:"rule"` } -// SecurityPolicyState is a structure that holds user supplied policy to enforce +// State is a structure that holds user supplied policy to enforce // we keep both the encoded representation and the unmarshalled representation // because different components need to have access to either of these -type SecurityPolicyState struct { +type State struct { EncodedSecurityPolicy EncodedSecurityPolicy `json:"EncodedSecurityPolicy,omitempty"` - SecurityPolicy `json:"SecurityPolicy,omitempty"` + SecurityPolicy Policy `json:"SecurityPolicy,omitempty"` } -// EncodedSecurityPolicy is a JSON representation of SecurityPolicy that has +// EncodedSecurityPolicy is a JSON representation of Policy that has // been base64 encoded for storage in an annotation embedded within another // JSON configuration type EncodedSecurityPolicy struct { SecurityPolicy string `json:"SecurityPolicy,omitempty"` } -// Constructs SecurityPolicyState from base64Policy string. It first decodes +// NewSecurityPolicyState constructs State from base64Policy string. It first decodes // base64 policy and returns the structs security policy struct and encoded // security policy for given policy. The security policy is transmitted as json // in an annotation, so we first have to remove the base64 encoding that allows // the JSON based policy to be passed as a string. From there, we decode the -// JSONand setup our security policy struct -func NewSecurityPolicyState(base64Policy string) (*SecurityPolicyState, error) { +// JSON and setup our security policy struct +func NewSecurityPolicyState(base64Policy string) (*State, error) { // construct an encoded security policy that holds the base64 representation encodedSecurityPolicy := EncodedSecurityPolicy{ SecurityPolicy: base64Policy, @@ -70,25 +70,25 @@ func NewSecurityPolicyState(base64Policy string) (*SecurityPolicyState, error) { } // json unmarshall the decoded to a SecurityPolicy - securityPolicy := SecurityPolicy{} + securityPolicy := Policy{} err = json.Unmarshal(jsonPolicy, &securityPolicy) if err != nil { return nil, errors.Wrap(err, "unable to unmarshal JSON policy") } - return &SecurityPolicyState{ + return &State{ SecurityPolicy: securityPolicy, EncodedSecurityPolicy: encodedSecurityPolicy, }, nil } -type SecurityPolicy struct { +type Policy struct { // Flag that when set to true allows for all checks to pass. Currently used // to run with security policy enforcement "running dark"; checks can be in // place but the default policy that is created on startup has AllowAll set // to true, thus making policy enforcement effectively "off" from a logical // standpoint. Policy enforcement isn't actually off as the policy is "allow - // everything:. + // everything". AllowAll bool `json:"allow_all"` // One or more containers that are allowed to run Containers Containers `json:"containers"` @@ -127,7 +127,7 @@ type EnvRule struct { Rule string `json:"rule"` } -// Custom JSON marshalling to add `lenth` field that matches the number of +// Custom JSON marshalling to add `length` field that matches the number of // elements present in the `elements` field. func (c Containers) MarshalJSON() ([]byte, error) { type Alias Containers