Write enhanced fragment loading diagnostics and expose to container in the security context#2830
Write enhanced fragment loading diagnostics and expose to container in the security context#2830micromaomao wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR enhances security policy fragment injection diagnostics by persisting decoded fragment artifacts (COSE, payload, metadata, and per-request outcome markers) in a predictable guest location, and exposing those diagnostics to the workload container via the existing security-context mechanism (LCOW and WCOW).
Changes:
- Add per-fragment dump directory keyed by fragment SHA, writing
fragment.cose, decodedfragment,metadata.json, and{n}.succeed/{n}.failmarkers. - Introduce guest paths for fragment diagnostics (
/tmp/fragmentsfor LCOW,C:\InjectedFragmentsfor WCOW). - Mount/map the fragments diagnostics directory into the container under
<security-context-dir>/fragments.debugafter policy enforcement.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| pkg/securitypolicy/securitypolicy_options.go | Implements enhanced fragment dump (COSE/payload/metadata) and per-request success/failure markers; creates fragments directory during policy setup. |
| internal/guestpath/paths.go | Adds constants for LCOW/WCOW fragments diagnostics storage locations inside the UVM. |
| internal/guest/runtime/hcsv2/uvm.go | Adds a post-enforcement bind mount to expose fragments diagnostics into the LCOW container’s security-context directory. |
| internal/gcs-sidecar/handlers.go | Adds a post-enforcement mapped directory to expose fragments diagnostics into the WCOW container’s security-context directory. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if err := os.MkdirAll(fragmentsPath(), 0755); err != nil { | ||
| return fmt.Errorf("failed to create injected fragments directory: %w", err) | ||
| } |
| if err := os.MkdirAll(thisFragmentDir, 0755); err != nil { | ||
| return fmt.Errorf("failed to create injected fragment directory: %w", err) | ||
| } | ||
| if err := writeFileIfNotExists(filepath.Join(thisFragmentDir, "fragment.cose"), raw, 0644); err != nil { | ||
| return fmt.Errorf("failed to write fragment.cose: %w", err) | ||
| } |
| if err := writeFileIfNotExists(filepath.Join(thisFragmentDir, "fragment"), unpacked.Payload, 0644); err != nil { | ||
| return fmt.Errorf("failed to write injected fragment payload: %w", err) | ||
| } |
| // Global counter for fragment injection requests, used to create unique | ||
| // error / success marker files even when the same fragment is injected | ||
| // multiple times. | ||
| var FragmentRequestId atomic.Uint64 | ||
|
|
| if securityContextDir != "" { | ||
| settings.OCISpecification.Mounts = append(settings.OCISpecification.Mounts, specs.Mount{ | ||
| Destination: path.Join("/", filepath.Base(securityContextDir), "fragments.debug"), | ||
| Type: "bind", | ||
| Source: guestpath.LCOWFragmentsPath, | ||
| Options: []string{"bind", "ro"}, | ||
| }) | ||
| } |
| if securityContextDir != "" { | ||
| container.MappedDirectories = append(container.MappedDirectories, hcsschema.MappedDirectory{ | ||
| HostPath: guestpath.WCOWFragmentsPath, | ||
| ContainerPath: filepath.Join(`C:\`, filepath.Base(securityContextDir), "fragments.debug"), | ||
| ReadOnly: true, | ||
| }) | ||
| } |
988c0c2 to
4600cb1
Compare
|
We can have things we expect to "fail" e.g. old infra fragments, so call it x.deny instead of x.fail fragments.debug -> fragments.info, then add readme explaining that it is helpful but not an integrity guarantee |
Write the error string or a success marker, the decoded fragment content, and headers, and place them in a findable place on Windows and Linux. Assisted-by: GitHub-Copilot copilot-review Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
…\fragments.debug [cherry-pick of 43dcfafab onto main] Assisted-by: GitHub-Copilot copilot-review Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
…ragments.debug Assisted-by: GitHub-Copilot copilot-review Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
4600cb1 to
71a0f55
Compare
…d add a README We can have things we expect to "fail" e.g. old infra fragments with old svns that are always injected by the host for compatibility / rolling upgrade reasons. As such we rename x.fail to x.deny (even if it's not actually a deny but a genuine parse error etc) Assisted-by: GitHub-Copilot copilot-review Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (2)
pkg/securitypolicy/securitypolicy_options.go:46
FragmentRequestIdis exported and usesIdinstead of Go’s conventionalID. Since it’s only used internally in this file, make it unexported and rename tofragmentRequestIDto avoid exposing a global API surface unnecessarily.
// Global counter for fragment injection requests, used to create unique
// deny / success marker files even when the same fragment is injected
// multiple times.
var FragmentRequestId atomic.Uint64
pkg/securitypolicy/securitypolicy_options.go:231
- After renaming the counter to
fragmentRequestID, update this call site accordingly.
currReqId := FragmentRequestId.Add(1)
| func (s *SecurityOptions) EnsureFragmentDiagnosticsDir() error { | ||
| dir := fragmentsPath() | ||
| if err := os.MkdirAll(dir, 0755); err != nil { | ||
| return err | ||
| } | ||
| return os.WriteFile(filepath.Join(dir, "README"), fragmentsInfoREADME, 0644) | ||
| } |
| func writeFileIfNotExists(filename string, data []byte, perm os.FileMode) error { | ||
| file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) | ||
| if os.IsExist(err) { | ||
| return nil | ||
| } | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if _, err := file.Write(data); err != nil { | ||
| _ = file.Close() | ||
| return err | ||
| } | ||
| return file.Close() | ||
| } |
| markerName := fmt.Sprintf("%d.succeed", currReqId) | ||
| var markerContents []byte | ||
| if err != nil { | ||
| markerName = fmt.Sprintf("%d.deny", currReqId) | ||
| markerContents = []byte(err.Error()) | ||
| } |
| @@ -0,0 +1 @@ | |||
| The content of this directory is for informational purpose and should not be relied upon for security. | |||
This is useful as a debugging aid and not a security feature.
Enhance the existing fragment dumping code to also write the error string or a success marker, the decoded fragment content, and metadata, and place them in a findable place on Windows and Linux.
Example: