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