Skip to content

Commit

Permalink
plumb AMD certs to workload containers (#1549)
Browse files Browse the repository at this point in the history
confidential containers: Add AMD cert plumbing

Add logic to plumb AMD certificates to workload containers. The
assumption is that the certificates will be "fresh enough" for
necessary attestation and key release by the workflow and third
party services.

Additionally add error logging when UVM reference info file
is not found

Signed-off-by: Maksim An <maksiman@microsoft.com>
  • Loading branch information
anmaxvl committed Dec 6, 2022
1 parent 1233dd1 commit 734a0ed
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 20 deletions.
6 changes: 4 additions & 2 deletions internal/guest/runtime/hcsv2/uvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,10 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
// construction time.
if oci.ParseAnnotationsBool(ctx, settings.OCISpecification.Annotations, annotations.UVMSecurityPolicyEnv, false) {
secPolicyEnv := fmt.Sprintf("UVM_SECURITY_POLICY=%s", h.securityPolicyEnforcer.EncodedSecurityPolicy())
uvmReferenceInfo := fmt.Sprintf("UVM_REFERENCE_INFO=%s", h.uvmReferenceInfo)
settings.OCISpecification.Process.Env = append(settings.OCISpecification.Process.Env, secPolicyEnv, uvmReferenceInfo)
uvmReferenceInfoEnv := fmt.Sprintf("UVM_REFERENCE_INFO=%s", h.uvmReferenceInfo)
amdCertEnv := fmt.Sprintf("UVM_HOST_AMD_CERTIFICATE=%s", settings.OCISpecification.Annotations[annotations.HostAMDCertificate])
settings.OCISpecification.Process.Env = append(settings.OCISpecification.Process.Env,
secPolicyEnv, uvmReferenceInfoEnv, amdCertEnv)
}

// Create the BundlePath
Expand Down
2 changes: 1 addition & 1 deletion internal/oci/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func ProcessAnnotations(ctx context.Context, s *specs.Spec) (err error) {
// Named `Process` and not `Expand` since this function may be expanded (pun intended) to
// deal with other annotation issues and validation.

// Rathen than give up part of the way through on error, this just emits a warning (similar
// Rather than give up part of the way through on error, this just emits a warning (similar
// to the `parseAnnotation*` functions) and continues through, so the spec is not left in a
// (partially) unusable form.
// If multiple different errors are to be raised, they should be combined or, if they
Expand Down
25 changes: 21 additions & 4 deletions internal/uvm/security_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"path/filepath"

hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
"github.com/Microsoft/hcsshim/pkg/ctrdtaskapi"
Expand Down Expand Up @@ -42,6 +43,17 @@ func WithSecurityPolicyEnforcer(enforcer string) ConfidentialUVMOpt {
}
}

func base64EncodeFileContents(filePath string) (string, error) {
if filePath == "" {
return "", nil
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(content), 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.
Expand All @@ -50,11 +62,16 @@ func WithUVMReferenceInfo(referenceRoot string, referenceName string) Confidenti
if referenceName == "" {
return nil
}
content, err := os.ReadFile(filepath.Join(referenceRoot, referenceName))
if err != nil && !os.IsNotExist(err) {
return err
fullFilePath := filepath.Join(referenceRoot, referenceName)
encoded, err := base64EncodeFileContents(fullFilePath)
if err != nil {
if os.IsNotExist(err) {
log.G(ctx).WithField("filePath", fullFilePath).Debug("UVM reference info file not found")
return nil
}
return fmt.Errorf("failed to read UVM reference info file: %w", err)
}
r.EncodedUVMReference = base64.StdEncoding.EncodeToString(content)
r.EncodedUVMReference = encoded
return nil
}
}
Expand Down
5 changes: 3 additions & 2 deletions internal/uvm/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,12 @@ func (uvm *UtilityVM) Start(ctx context.Context) (err error) {
}

if uvm.confidentialUVMOptions != nil && uvm.OS() == "linux" {
if err := uvm.SetConfidentialUVMOptions(ctx,
copts := []ConfidentialUVMOpt{
WithSecurityPolicy(uvm.confidentialUVMOptions.SecurityPolicy),
WithSecurityPolicyEnforcer(uvm.confidentialUVMOptions.SecurityPolicyEnforcer),
WithUVMReferenceInfo(defaultLCOWOSBootFilesPath(), uvm.confidentialUVMOptions.UVMReferenceInfoFile),
); err != nil {
}
if err := uvm.SetConfidentialUVMOptions(ctx, copts...); err != nil {
return err
}
}
Expand Down
8 changes: 6 additions & 2 deletions pkg/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,17 @@ const (
// GuestStateFile specifies the path of the vmgs file to use if required. Only applies in SNP mode.
GuestStateFile = "io.microsoft.virtualmachine.lcow.gueststatefile"

// UVMSecurityPolicyEnv specifies if UVM_SECURITY_POLICY and
// UVM_REFERENCE_INFO variables should be injected for a container.
// UVMSecurityPolicyEnv specifies if UVM_SECURITY_POLICY, UVM_REFERENCE_INFO
// and UVM_HOST_AMD_CERTIFICATE variables should be injected for a container.
UVMSecurityPolicyEnv = "io.microsoft.virtualmachine.lcow.securitypolicy.env"

// UVMReferenceInfoFile specifies the filename of a signed UVM reference file to be passed to UVM.
UVMReferenceInfoFile = "io.microsoft.virtualmachine.lcow.uvm-reference-info-file"

// HostAMDCertificate specifies the filename of the AMD certificates to be passed to UVM.
// The certificate is expected to be located in the same directory as the shim executable.
HostAMDCertificate = "io.microsoft.virtualmachine.lcow.amd-certificate"

// DisableLCOWTimeSyncService is used to disable the chronyd time
// synchronization service inside the LCOW UVM.
DisableLCOWTimeSyncService = "io.microsoft.virtualmachine.lcow.timesync.disable"
Expand Down
24 changes: 15 additions & 9 deletions test/cri-containerd/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -790,9 +790,11 @@ func Test_RunContainer_WithPolicy_And_SecurityPolicyEnv_Annotation(t *testing.T)
alpineCmd,
sandboxRequest.Config,
)
certValue := "dummy-cert-value"
if setPolicyEnv {
containerRequest.Config.Annotations = map[string]string{
annotations.UVMSecurityPolicyEnv: "true",
annotations.HostAMDCertificate: certValue,
}
}
// setup logfile to capture stdout
Expand All @@ -812,19 +814,23 @@ func Test_RunContainer_WithPolicy_And_SecurityPolicyEnv_Annotation(t *testing.T)
if err != nil {
t.Fatalf("error reading log file: %s", err)
}
policyEnv := fmt.Sprintf("UVM_SECURITY_POLICY=%s", config.policy)
measurementEnv := "UVM_REFERENCE_INFO="
targetEnvs := []string{
fmt.Sprintf("UVM_SECURITY_POLICY=%s", config.policy),
"UVM_REFERENCE_INFO=",
fmt.Sprintf("UVM_HOST_AMD_CERTIFICATE=%s", certValue),
}
if setPolicyEnv {
// make sure that the expected environment variable was set
if !strings.Contains(string(content), policyEnv) || !strings.Contains(string(content), measurementEnv) {
t.Fatalf("UVM_SECURITY_POLICY and UVM_REFERENCE_INFO env vars should be set for init"+
" process:\n%s\n", string(content))
for _, env := range targetEnvs {
if !strings.Contains(string(content), env) {
t.Fatalf("missing init process environment variable: %s", env)
}
}
} else {
if strings.Contains(string(content), policyEnv) || strings.Contains(string(content), measurementEnv) {
t.Fatalf("UVM_SECURITY_POLICY and UVM_REFERENCE_INFO env vars shouldn't be set for init"+
" process:\n%s\n",
string(content))
for _, env := range targetEnvs {
if strings.Contains(string(content), env) {
t.Fatalf("environment variable should not be set for init process: %s", env)
}
}
}
})
Expand Down

0 comments on commit 734a0ed

Please sign in to comment.