Skip to content

Commit 4b55375

Browse files
committed
Securitypolicy: Move fragment extraction
Move inject and load fragment into the securitypolicy pkg Signed-off-by: Mahati Chamarthy <[email protected]>
1 parent c9c7431 commit 4b55375

File tree

4 files changed

+78
-119
lines changed

4 files changed

+78
-119
lines changed

internal/gcs-sidecar/host.go

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,10 @@ package bridge
55

66
import (
77
"context"
8-
"crypto/sha256"
9-
"encoding/base64"
108
"fmt"
119
"io"
12-
"os"
13-
"path/filepath"
1410
"sync"
15-
"time"
1611

17-
"github.com/Microsoft/cosesign1go/pkg/cosesign1"
18-
didx509resolver "github.com/Microsoft/didx509go/pkg/did-x509-resolver"
1912
"github.com/Microsoft/hcsshim/internal/bridgeutils/gcserr"
2013
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
2114
"github.com/Microsoft/hcsshim/internal/log"
@@ -75,61 +68,15 @@ func NewHost(initialEnforcer securitypolicy.SecurityPolicyEnforcer) *Host {
7568
// security policy (done in the regoby LoadFragment)
7669
func (h *Host) InjectFragment(ctx context.Context, fragment *guestresource.LCOWSecurityPolicyFragment) (err error) {
7770
log.G(ctx).WithField("fragment", fmt.Sprintf("%+v", fragment)).Debug("GCS Host.InjectFragment")
78-
79-
raw, err := base64.StdEncoding.DecodeString(fragment.Fragment)
80-
if err != nil {
81-
return err
82-
}
83-
blob := []byte(fragment.Fragment)
84-
// keep a copy of the fragment, so we can manually figure out what went wrong
85-
// will be removed eventually. Give it a unique name to avoid any potential
86-
// race conditions.
87-
sha := sha256.New()
88-
sha.Write(blob)
89-
timestamp := time.Now()
90-
fragmentPath := fmt.Sprintf("fragment-%x-%d.blob", sha.Sum(nil), timestamp.UnixMilli())
91-
_ = os.WriteFile(filepath.Join(os.TempDir(), fragmentPath), blob, 0644)
92-
93-
unpacked, err := cosesign1.UnpackAndValidateCOSE1CertChain(raw)
94-
if err != nil {
95-
return fmt.Errorf("InjectFragment failed COSE validation: %w", err)
96-
}
97-
98-
payloadString := string(unpacked.Payload[:])
99-
issuer := unpacked.Issuer
100-
feed := unpacked.Feed
101-
chainPem := unpacked.ChainPem
102-
103-
log.G(ctx).WithFields(logrus.Fields{
104-
"issuer": issuer, // eg the DID:x509:blah....
105-
"feed": feed,
106-
"cty": unpacked.ContentType,
107-
"chainPem": chainPem,
108-
}).Debugf("unpacked COSE1 cert chain")
109-
110-
log.G(ctx).WithFields(logrus.Fields{
111-
"payload": payloadString,
112-
}).Tracef("unpacked COSE1 payload")
113-
114-
if len(issuer) == 0 || len(feed) == 0 { // must both be present
115-
return fmt.Errorf("either issuer and feed must both be provided in the COSE_Sign1 protected header")
116-
}
117-
118-
// Resolve returns a did doc that we don't need
119-
// we only care if there was an error or not
120-
_, err = didx509resolver.Resolve(unpacked.ChainPem, issuer, true)
71+
issuer, feed, payloadString, err := securitypolicy.ExtractAndVerifyFragment(ctx, fragment)
12172
if err != nil {
122-
log.G(ctx).Printf("Badly formed fragment - did resolver failed to match fragment did:x509 from chain with purported issuer %s, feed %s - err %s", issuer, feed, err.Error())
12373
return err
12474
}
125-
12675
// now offer the payload fragment to the policy
12776
err = h.securityPolicyEnforcer.LoadFragment(ctx, issuer, feed, payloadString)
12877
if err != nil {
129-
return fmt.Errorf("InjectFragment failed policy load: %w", err)
78+
return fmt.Errorf("error loading security policy fragment: %w", err)
13079
}
131-
log.G(ctx).Printf("passed fragment into the enforcer.")
132-
13380
return nil
13481
}
13582

internal/guest/runtime/hcsv2/uvm.go

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ package hcsv2
66
import (
77
"bufio"
88
"context"
9-
"crypto/sha256"
10-
"encoding/base64"
119
"encoding/json"
1210
"fmt"
1311
"io"
@@ -20,8 +18,6 @@ import (
2018
"syscall"
2119
"time"
2220

23-
"github.com/Microsoft/cosesign1go/pkg/cosesign1"
24-
didx509resolver "github.com/Microsoft/didx509go/pkg/did-x509-resolver"
2521
"github.com/Microsoft/hcsshim/internal/bridgeutils/gcserr"
2622
"github.com/Microsoft/hcsshim/internal/debug"
2723
"github.com/Microsoft/hcsshim/internal/guest/policy"
@@ -166,61 +162,15 @@ func (h *Host) SetConfidentialUVMOptions(ctx context.Context, r *guestresource.L
166162
// security policy (done in the regoby LoadFragment)
167163
func (h *Host) InjectFragment(ctx context.Context, fragment *guestresource.LCOWSecurityPolicyFragment) (err error) {
168164
log.G(ctx).WithField("fragment", fmt.Sprintf("%+v", fragment)).Debug("GCS Host.InjectFragment")
169-
170-
raw, err := base64.StdEncoding.DecodeString(fragment.Fragment)
171-
if err != nil {
172-
return err
173-
}
174-
blob := []byte(fragment.Fragment)
175-
// keep a copy of the fragment, so we can manually figure out what went wrong
176-
// will be removed eventually. Give it a unique name to avoid any potential
177-
// race conditions.
178-
sha := sha256.New()
179-
sha.Write(blob)
180-
timestamp := time.Now()
181-
fragmentPath := fmt.Sprintf("fragment-%x-%d.blob", sha.Sum(nil), timestamp.UnixMilli())
182-
_ = os.WriteFile(filepath.Join("/tmp", fragmentPath), blob, 0644)
183-
184-
unpacked, err := cosesign1.UnpackAndValidateCOSE1CertChain(raw)
185-
if err != nil {
186-
return fmt.Errorf("InjectFragment failed COSE validation: %w", err)
187-
}
188-
189-
payloadString := string(unpacked.Payload[:])
190-
issuer := unpacked.Issuer
191-
feed := unpacked.Feed
192-
chainPem := unpacked.ChainPem
193-
194-
log.G(ctx).WithFields(logrus.Fields{
195-
"issuer": issuer, // eg the DID:x509:blah....
196-
"feed": feed,
197-
"cty": unpacked.ContentType,
198-
"chainPem": chainPem,
199-
}).Debugf("unpacked COSE1 cert chain")
200-
201-
log.G(ctx).WithFields(logrus.Fields{
202-
"payload": payloadString,
203-
}).Tracef("unpacked COSE1 payload")
204-
205-
if len(issuer) == 0 || len(feed) == 0 { // must both be present
206-
return fmt.Errorf("either issuer and feed must both be provided in the COSE_Sign1 protected header")
207-
}
208-
209-
// Resolve returns a did doc that we don't need
210-
// we only care if there was an error or not
211-
_, err = didx509resolver.Resolve(unpacked.ChainPem, issuer, true)
165+
issuer, feed, payloadString, err := securitypolicy.ExtractAndVerifyFragment(ctx, fragment)
212166
if err != nil {
213-
log.G(ctx).Printf("Badly formed fragment - did resolver failed to match fragment did:x509 from chain with purported issuer %s, feed %s - err %s", issuer, feed, err.Error())
214167
return err
215168
}
216-
217169
// now offer the payload fragment to the policy
218170
err = h.securityPolicyEnforcer.LoadFragment(ctx, issuer, feed, payloadString)
219171
if err != nil {
220-
return fmt.Errorf("InjectFragment failed policy load: %w", err)
172+
return fmt.Errorf("error loading security policy fragment: %w", err)
221173
}
222-
log.G(ctx).Printf("passed fragment into the enforcer.")
223-
224174
return nil
225175
}
226176

pkg/securitypolicy/securitypolicyenforcer.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@ package securitypolicy
22

33
import (
44
"context"
5+
"crypto/sha256"
6+
"encoding/base64"
57
"fmt"
8+
"os"
9+
"path/filepath"
610
"syscall"
11+
"time"
712

13+
"github.com/Microsoft/cosesign1go/pkg/cosesign1"
14+
didx509resolver "github.com/Microsoft/didx509go/pkg/did-x509-resolver"
15+
"github.com/Microsoft/hcsshim/internal/log"
816
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
17+
"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
918
oci "github.com/opencontainers/runtime-spec/specs-go"
1019
"github.com/pkg/errors"
20+
"github.com/sirupsen/logrus"
1121
)
1222

1323
type createEnforcerFunc func(base64EncodedPolicy string, criMounts, criPrivilegedMounts []oci.Mount, maxErrorMessageLength int) (SecurityPolicyEnforcer, error)
@@ -121,7 +131,7 @@ type SecurityPolicyEnforcer interface {
121131
EnforceGetPropertiesPolicy(ctx context.Context) error
122132
EnforceDumpStacksPolicy(ctx context.Context) error
123133
EnforceRuntimeLoggingPolicy(ctx context.Context) (err error)
124-
LoadFragment(ctx context.Context, issuer string, feed string, code string) error
134+
LoadFragment(ctx context.Context, issuer string, feed string, rego string) error
125135
EnforceScratchMountPolicy(ctx context.Context, scratchPath string, encrypted bool) (err error)
126136
EnforceScratchUnmountPolicy(ctx context.Context, scratchPath string) (err error)
127137
GetUserInfo(spec *oci.Process, rootPath string) (IDName, []IDName, string, error)
@@ -142,6 +152,69 @@ func (s stringSet) contains(item string) bool {
142152
return contains
143153
}
144154

155+
// Fragment extends current security policy with additional constraints
156+
// from the incoming fragment. Note that it is base64 encoded over the bridge/
157+
//
158+
// There are three checking steps:
159+
// 1 - Unpack the cose document and check it was actually signed with the cert
160+
// chain inside its header
161+
// 2 - Check that the issuer field did:x509 identifier is for that cert chain
162+
// (ie fingerprint of a non leaf cert and the subject matches the leaf cert)
163+
// 3 - Check that this issuer/feed match the requirement of the user provided
164+
// security policy (done in the regoby LoadFragment)
165+
func ExtractAndVerifyFragment(ctx context.Context, fragment *guestresource.LCOWSecurityPolicyFragment) (issuer string, feed string, payloadString string, err error) {
166+
log.G(ctx).WithField("fragment", fmt.Sprintf("%+v", fragment)).Debug("VerifyAndExtractFragment")
167+
168+
raw, err := base64.StdEncoding.DecodeString(fragment.Fragment)
169+
if err != nil {
170+
return "", "", "", fmt.Errorf("failed to decode fragment: %w", err)
171+
}
172+
blob := []byte(fragment.Fragment)
173+
// keep a copy of the fragment, so we can manually figure out what went wrong
174+
// will be removed eventually. Give it a unique name to avoid any potential
175+
// race conditions.
176+
sha := sha256.New()
177+
sha.Write(blob)
178+
timestamp := time.Now()
179+
fragmentPath := fmt.Sprintf("fragment-%x-%d.blob", sha.Sum(nil), timestamp.UnixMilli())
180+
_ = os.WriteFile(filepath.Join(os.TempDir(), fragmentPath), blob, 0644)
181+
182+
unpacked, err := cosesign1.UnpackAndValidateCOSE1CertChain(raw)
183+
if err != nil {
184+
return "", "", "", fmt.Errorf("InjectFragment failed COSE validation: %w", err)
185+
}
186+
187+
payloadString = string(unpacked.Payload[:])
188+
issuer = unpacked.Issuer
189+
feed = unpacked.Feed
190+
chainPem := unpacked.ChainPem
191+
192+
log.G(ctx).WithFields(logrus.Fields{
193+
"issuer": issuer, // eg the DID:x509:blah....
194+
"feed": feed,
195+
"cty": unpacked.ContentType,
196+
"chainPem": chainPem,
197+
}).Debugf("unpacked COSE1 cert chain")
198+
199+
log.G(ctx).WithFields(logrus.Fields{
200+
"payload": payloadString,
201+
}).Tracef("unpacked COSE1 payload")
202+
203+
if len(issuer) == 0 || len(feed) == 0 { // must both be present
204+
return "", "", "", fmt.Errorf("either issuer and feed must both be provided in the COSE_Sign1 protected header")
205+
}
206+
207+
// Resolve returns a did doc that we don't need
208+
// we only care if there was an error or not
209+
_, err = didx509resolver.Resolve(unpacked.ChainPem, issuer, true)
210+
if err != nil {
211+
log.G(ctx).Printf("Badly formed fragment - did resolver failed to match fragment did:x509 from chain with purported issuer %s, feed %s - err %s", issuer, feed, err.Error())
212+
return "", "", "", err
213+
}
214+
215+
return issuer, feed, payloadString, nil
216+
}
217+
145218
// CreateSecurityPolicyEnforcer returns an appropriate enforcer for input
146219
// parameters. Returns an error if the requested `enforcer` implementation
147220
// isn't registered.

pkg/securitypolicy/securitypolicyenforcer_rego.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,6 @@ type regoEnforcer struct {
5959

6060
var _ SecurityPolicyEnforcer = (*regoEnforcer)(nil)
6161

62-
//nolint:unused
63-
/*func (sp SecurityPolicy) toInternal() (*securityPolicyInternal, error) {
64-
policy := new(securityPolicyInternal)
65-
var err error
66-
if policy.Containers, err = sp.Containers.toInternal(); err != nil {
67-
return nil, err
68-
}
69-
70-
return policy, nil
71-
}*/
72-
7362
func toStringSet(items []string) stringSet {
7463
s := make(stringSet)
7564
for _, item := range items {

0 commit comments

Comments
 (0)