Skip to content

Commit

Permalink
Set HCSSHIM_UVM_REFERENCE_INFO env for workload containers (microsoft…
Browse files Browse the repository at this point in the history
…#1499)

* Set HCSSHIM_UVM_REFERENCE_INFO env for workload containers

Workload containers need to be aware of the reference UVM measurement
they are currently running on. The expectation is that the signed UVM
measurement file will be a part of the package and located in the
same directory as the rest of the boot files (e.g. kernel, initrd
or VMGS). The file itself is a COSE_Sign1 document containing the
measurement and related information.
The content of the file will be plumbed to the UVM as
part of setting the security policy request and can later be
presented to the containers via an environment variable.

Signed-off-by: Maksim An <maksiman@microsoft.com>
  • Loading branch information
anmaxvl authored Sep 8, 2022
1 parent 06acf20 commit d0ba9f8
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 38 deletions.
4 changes: 2 additions & 2 deletions guest/prot/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,9 +578,9 @@ func UnmarshalContainerModifySettings(b []byte) (*ContainerModifySettings, error
}
msr.Settings = cc
case guestresource.ResourceTypeSecurityPolicy:
enforcer := &guestresource.LCOWSecurityPolicyEnforcer{}
enforcer := &guestresource.LCOWConfidentialOptions{}
if err := commonutils.UnmarshalJSONWithHresult(msrRawSettings, enforcer); err != nil {
return &request, errors.Wrap(err, "failed to unmarshal settings as LCOWSecurityPolicyEnforcer")
return &request, errors.Wrap(err, "failed to unmarshal settings as LCOWConfidentialOptions")
}
msr.Settings = enforcer
default:
Expand Down
37 changes: 23 additions & 14 deletions guest/runtime/hcsv2/uvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type Host struct {
policyMutex sync.Mutex
securityPolicyEnforcer securitypolicy.SecurityPolicyEnforcer
securityPolicyEnforcerSet bool
uvmReferenceInfo string
}

func NewHost(rtime runtime.Runtime, vsock transport.Transport) *Host {
Expand All @@ -72,14 +73,17 @@ func NewHost(rtime runtime.Runtime, vsock transport.Transport) *Host {
}
}

// SetSecurityPolicy takes a base64 encoded security policy
// and sets up our internal data structures we use to store
// said policy.
// SetConfidentialUVMOptions takes a security policy enforcer type, base64
// encoded security policy and base64 encoded UVM reference information to set
// up our internal data structures we use to store said policy. The signed
// UVM measurement can be presented to the workload containers via an
// environment variable if client requests so.
//
// 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 JSON and setup our security policy state
func (h *Host) SetSecurityPolicy(enforcerType, base64EncodedPolicy string) error {
func (h *Host) SetConfidentialUVMOptions(enforcerType string, base64EncodedPolicy string, base64UVMReference string) error {
h.policyMutex.Lock()
defer h.policyMutex.Unlock()
if h.securityPolicyEnforcerSet {
Expand Down Expand Up @@ -107,6 +111,7 @@ func (h *Host) SetSecurityPolicy(enforcerType, base64EncodedPolicy string) error

h.securityPolicyEnforcer = p
h.securityPolicyEnforcerSet = true
h.uvmReferenceInfo = base64UVMReference

return nil
}
Expand Down Expand Up @@ -289,16 +294,20 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
return nil, errors.Wrapf(err, "container creation denied due to policy")
}

// Export security policy as one of the process's environment variables so that application and sidecar
// containers can have access to it. The security policy is required by containers which need to extract
// init-time claims found in the security policy.
// Export security policy and signed UVM reference info as one of the
// process's environment variables so that application and sidecar
// containers can have access to it. The security policy is required
// by containers which need to extract init-time claims found in the
// security policy.
//
// We append the variable after the security policy enforcing logic completes to bypass it; the
// security policy variable cannot be included in the security policy as its value is not available
// security policy construction time.
// We append the variable after the security policy enforcing logic
// completes to bypass it; the security policy variable cannot be included
// in the security policy as its value is not available security policy
// construction time.
if oci.ParseAnnotationsBool(ctx, settings.OCISpecification.Annotations, annotations.SecurityPolicyEnv, false) {
secPolicyEnv := fmt.Sprintf("SECURITY_POLICY=%s", h.securityPolicyEnforcer.EncodedSecurityPolicy())
settings.OCISpecification.Process.Env = append(settings.OCISpecification.Process.Env, secPolicyEnv)
uvmReferenceInfo := fmt.Sprintf("HCSSHIM_UVM_REFERENCE_INFO=%s", h.uvmReferenceInfo)
settings.OCISpecification.Process.Env = append(settings.OCISpecification.Process.Env, secPolicyEnv, uvmReferenceInfo)
}

// Create the BundlePath
Expand Down Expand Up @@ -373,11 +382,11 @@ func (h *Host) modifyHostSettings(ctx context.Context, containerID string, req *
}
return c.modifyContainerConstraints(ctx, req.RequestType, req.Settings.(*guestresource.LCOWContainerConstraints))
case guestresource.ResourceTypeSecurityPolicy:
r, ok := req.Settings.(*guestresource.LCOWSecurityPolicyEnforcer)
r, ok := req.Settings.(*guestresource.LCOWConfidentialOptions)
if !ok {
return errors.New("the request's settings are not of type LCOWSecurityPolicyEnforcer")
return errors.New("the request's settings are not of type LCOWConfidentialOptions")
}
return h.SetSecurityPolicy(r.EnforcerType, r.EncodedSecurityPolicy)
return h.SetConfidentialUVMOptions(r.EnforcerType, r.EncodedSecurityPolicy, r.EncodedUVMReference)
default:
return errors.Errorf("the ResourceType \"%s\" is not supported for UVM", req.ResourceType)
}
Expand Down
1 change: 1 addition & 0 deletions oci/uvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ func SpecToUVMCreateOpts(ctx context.Context, s *specs.Spec, id, owner string) (
lopts.EnableScratchEncryption = ParseAnnotationsBool(ctx, s.Annotations, annotations.EncryptedScratchDisk, lopts.EnableScratchEncryption)
lopts.SecurityPolicy = parseAnnotationsString(s.Annotations, annotations.SecurityPolicy, lopts.SecurityPolicy)
lopts.SecurityPolicyEnforcer = parseAnnotationsString(s.Annotations, annotations.SecurityPolicyEnforcer, lopts.SecurityPolicyEnforcer)
lopts.UVMReferenceInfoFile = parseAnnotationsString(s.Annotations, annotations.UVMReferenceInfoFile, lopts.UVMReferenceInfoFile)
lopts.KernelBootOptions = parseAnnotationsString(s.Annotations, annotations.KernelBootOptions, lopts.KernelBootOptions)
lopts.DisableTimeSyncService = ParseAnnotationsBool(ctx, s.Annotations, annotations.DisableLCOWTimeSyncService, lopts.DisableTimeSyncService)
handleAnnotationPreferredRootFSType(ctx, s.Annotations, lopts)
Expand Down
3 changes: 2 additions & 1 deletion protocol/guestresource/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ type SignalProcessOptionsWCOW struct {
Signal guestrequest.SignalValueWCOW `json:",omitempty"`
}

type LCOWSecurityPolicyEnforcer struct {
type LCOWConfidentialOptions struct {
EnforcerType string `json:"EnforcerType,omitempty"`
EncodedSecurityPolicy string `json:"EncodedSecurityPolicy,omitempty"`
EncodedUVMReference string `json:"EncodedUVMReference,omitempty"`
}
2 changes: 1 addition & 1 deletion tools/uvmboot/lcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func runLCOW(ctx context.Context, options *uvm.OptionsLCOW, c *cli.Context) erro
return err
}

if err := vm.SetSecurityPolicy(ctx, "", options.SecurityPolicy); err != nil {
if err := vm.SetConfidentialUVMOptions(ctx, uvm.WithSecurityPolicy(options.SecurityPolicy)); err != nil {
return fmt.Errorf("could not set UVM security policy: %w", err)
}

Expand Down
9 changes: 8 additions & 1 deletion uvm/create_lcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,13 @@ const (
// UncompressedKernelFile is the default file name for an uncompressed
// kernel used to boot LCOW with KernelDirect.
UncompressedKernelFile = "vmlinux"
// In the SNP case both the kernel (bzImage) and initrd are stored in a vmgs (VM Guest State) file
// GuestStateFile is the default file name for a vmgs (VM Guest State) file
// which combines kernel and initrd and is used to boot from in the SNP case.
GuestStateFile = "kernelinitrd.vmgs"
// UVMReferenceInfoFile is the default file name for a COSE_Sign1
// reference UVM info, which can be made available to workload containers
// and can be used for validation purposes.
UVMReferenceInfoFile = "reference_info.cose"
)

// OptionsLCOW are the set of options passed to CreateLCOW() to create a utility vm.
Expand Down Expand Up @@ -104,6 +109,7 @@ type OptionsLCOW struct {
SecurityPolicy string // Optional security policy
SecurityPolicyEnabled bool // Set when there is a security policy to apply on actual SNP hardware, use this rathen than checking the string length
SecurityPolicyEnforcer string // Set which security policy enforcer to use (open door, standard or rego). This allows for better fallback mechanic.
UVMReferenceInfoFile string // Filename under `BootFilesPath` for (potentially signed) UVM image reference information.
UseGuestStateFile bool // Use a vmgs file that contains a kernel and initrd, required for SNP
GuestStateFile string // The vmgs file to load
DisableTimeSyncService bool // Disables the time synchronization service
Expand Down Expand Up @@ -155,6 +161,7 @@ func NewDefaultOptionsLCOW(id, owner string) *OptionsLCOW {
SecurityPolicyEnabled: false,
SecurityPolicyEnforcer: "",
SecurityPolicy: "",
UVMReferenceInfoFile: UVMReferenceInfoFile,
GuestStateFile: "",
DisableTimeSyncService: false,
}
Expand Down
79 changes: 60 additions & 19 deletions uvm/security_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,86 @@ package uvm

import (
"context"
"encoding/base64"
"fmt"
"os"
"path/filepath"

hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
"github.com/Microsoft/hcsshim/pkg/securitypolicy"
)

// SetSecurityPolicy tells the gcs instance in the UVM what policy to apply.
type ConfidentialUVMOpt func(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error

// WithSecurityPolicy sets the desired security policy for the resource.
func WithSecurityPolicy(policy string) ConfidentialUVMOpt {
return func(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error {
if policy == "" {
openDoorPolicy := securitypolicy.NewOpenDoorPolicy()
policyString, err := openDoorPolicy.EncodeToString()
if err != nil {
return err
}
policy = policyString
}
r.EncodedSecurityPolicy = policy
return nil
}
}

// WithSecurityPolicyEnforcer sets the desired enforcer type for the resource.
func WithSecurityPolicyEnforcer(enforcer string) ConfidentialUVMOpt {
return func(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error {
r.EnforcerType = enforcer
return nil
}
}

// WithUVMReferenceInfo reads UVM reference info file and base64 encodes the
// content before setting it for the resource. This is no-op if the
// `referenceName` is empty or the file doesn't exist.
func WithUVMReferenceInfo(referenceRoot string, referenceName string) ConfidentialUVMOpt {
return func(ctx context.Context, r *guestresource.LCOWConfidentialOptions) error {
if referenceName == "" {
return nil
}
content, err := os.ReadFile(filepath.Join(referenceRoot, referenceName))
if err != nil && !os.IsNotExist(err) {
return err
}
r.EncodedUVMReference = base64.StdEncoding.EncodeToString(content)
return nil
}
}

// SetConfidentialUVMOptions sends information required to run the UVM on
// SNP hardware, e.g., security policy and enforcer type, signed UVM reference
// information, etc.
//
// This has to happen before we start mounting things or generally changing
// the state of the UVM after is has been measured at startup
func (uvm *UtilityVM) SetSecurityPolicy(ctx context.Context, enforcer, policy string) error {
func (uvm *UtilityVM) SetConfidentialUVMOptions(ctx context.Context, opts ...ConfidentialUVMOpt) error {
if uvm.operatingSystem != "linux" {
return errNotSupported
}

if policy == "" {
openDoorPolicy := securitypolicy.NewOpenDoorPolicy()
policyString, err := openDoorPolicy.EncodeToString()
if err != nil {
return err
}
policy = policyString
}

uvm.m.Lock()
defer uvm.m.Unlock()

confOpts := &guestresource.LCOWConfidentialOptions{}
for _, o := range opts {
if err := o(ctx, confOpts); err != nil {
return err
}
}
modification := &hcsschema.ModifySettingRequest{
RequestType: guestrequest.RequestTypeAdd,
}

modification.GuestRequest = guestrequest.ModificationRequest{
ResourceType: guestresource.ResourceTypeSecurityPolicy,
RequestType: guestrequest.RequestTypeAdd,
Settings: guestresource.LCOWSecurityPolicyEnforcer{
EnforcerType: enforcer,
EncodedSecurityPolicy: policy,
GuestRequest: guestrequest.ModificationRequest{
ResourceType: guestresource.ResourceTypeSecurityPolicy,
RequestType: guestrequest.RequestTypeAdd,
Settings: *confOpts,
},
}

Expand Down

0 comments on commit d0ba9f8

Please sign in to comment.