Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/api-reference/dotnet/v1/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public enum BackendCapability
CaptureDenials = 1,
ProxyEnforcement = 2,
FilesystemDeniedPaths = 3,
/// <summary>
/// With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
/// loopback. Native Windows ingress support adds bidirectional access, including
/// container-hosted loopback servers.
/// </summary>
IngressHostLoopbackAllow = 4,
FilesystemEnumeratePaths = 5,
IdentitylessLoopbackProxy = 6,
Expand Down Expand Up @@ -954,6 +959,11 @@ public sealed class ProbeFacts
public bool BfsCompiledIn { get; init; }
public bool BaseContainerSupportsDenyPaths { get; init; }
public bool BaseContainerSupportsEnumeratePaths { get; init; }
/// <summary>
/// With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
/// loopback. Native Windows ingress support adds bidirectional access, including
/// container-hosted loopback servers.
/// </summary>
public bool BaseContainerSupportsIngressHostLoopbackAllow { get; init; }
public bool BaseContainerSupportsIdentitylessLoopbackProxy { get; init; }
public bool IsolationSessionAvailable { get; init; }
Expand Down
10 changes: 10 additions & 0 deletions docs/api-reference/node/v1/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export type BackendCapability =
| 'captureDenials'
| 'filesystemDeniedPaths'
| 'filesystemEnumeratePaths'
/**
* With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
* loopback. Native Windows ingress support adds bidirectional access, including
* container-hosted loopback servers.
*/
| 'ingressHostLoopbackAllow'
| 'proxyEnforcement'
| 'identitylessLoopbackProxy'
Expand Down Expand Up @@ -578,6 +583,11 @@ export interface ProbeFacts {
bfsCompiledIn: boolean;
baseContainerSupportsDenyPaths: boolean;
baseContainerSupportsEnumeratePaths: boolean;
/**
* With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
* loopback. Native Windows ingress support adds bidirectional access, including
* container-hosted loopback servers.
*/
baseContainerSupportsIngressHostLoopbackAllow: boolean;
baseContainerSupportsIdentitylessLoopbackProxy: boolean;
isolationSessionAvailable: boolean;
Expand Down
6 changes: 6 additions & 0 deletions docs/api-reference/rust/v1/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub enum BackendCapability {
CaptureDenials,
FilesystemDeniedPaths,
FilesystemEnumeratePaths,
/// With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
/// loopback. Native Windows ingress support adds bidirectional access, including
/// container-hosted loopback servers.
IngressHostLoopbackAllow,
ProxyEnforcement,
IdentitylessLoopbackProxy,
Expand Down Expand Up @@ -535,6 +538,9 @@ pub struct ProbeFacts {
pub bfs_compiled_in: bool,
pub base_container_supports_deny_paths: bool,
pub base_container_supports_enumerate_paths: bool,
/// With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
/// loopback. Native Windows ingress support adds bidirectional access, including
/// container-hosted loopback servers.
pub base_container_supports_ingress_host_loopback_allow: bool,
pub base_container_supports_identityless_loopback_proxy: bool,
pub isolation_session_available: bool,
Expand Down
31 changes: 10 additions & 21 deletions docs/backends/process-container/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ For the shared policy shape and defaults, see
[directional networking](../../schema.md#directional-networking-supported-contracts).
This guide covers how the Windows ProcessContainer backend enforces them.

ProcessContainer is available on Windows 11 24H2 and later. The initial
24H2 and 25H2 releases (builds 26100.9278 and 26200.9278, respectively) support
connections to medium-integrity host listeners. Builds 26100.9550 and
26200.9550 add support for hosting loopback servers inside the container and
connecting to low-integrity listeners. See
[Windows OS-version support](os-version-support.md) for supported releases.

## 1. What this backend targets at GA

On ProcessContainer paths with OS-scoped enforcement, each container gets two enforcement primitives scoped to its
Expand Down Expand Up @@ -102,26 +109,13 @@ common WFP endpoint enforcement. The proxy endpoint is the loopback address and
`runtimeConfig.networkProxy`. The packaged row covers both AppContainer and non-AppContainer proxies because their
client policy and package identity are the same; the enforcement table below separates their additional protections.

`ingress.hostLoopback` is bidirectional. `allowedProxyPeer` authorizes a package family or AppContainer profile without
`ingress.hostLoopback` requests access in both directions; the supported directions depend on the Windows build.
`allowedProxyPeer` authorizes a package family or AppContainer profile without
opening general host-loopback access, so identity-scoped paths keep `hostLoopback: "deny"`. Only an unpackaged
non-AppContainer proxy lacks an accepted peer identity and requires `hostLoopback: "allow"`. This is a weaker
development/testing compatibility deployment, not the shared policy's strict host-loopback-closure guarantee.
The shared setting requests both host-loopback directions, while WFP restricts client-container egress to the
configured proxy endpoint.
For direct-egress policies, MXC passes `ingress.default` and `ingress.hostLoopback` through the PSEC 1.1 ingress table when
`IsProcessSecurityEnvironmentVersionSupported` reports contract 1.1 or newer and
`QueryProcessSecurityEnvironmentSupport` advertises ingress support. Proxy policies omit that native table because
PSEC rejects it alongside a proxy. Their ingress default still selects `privateNetworkClientServer`, and their
host-loopback setting selects both the `networkLoopback` capability and the `MXC-Loopback` peer rather than an
identity-scoped proxy peer. The peer alone is insufficient: without the capability, the environment can be created
but its client cannot reach the host proxy.
The public ingress policy remains required. Direct-egress host-loopback allow
requires PSEC 1.1 and the ingress support flag. For an identity-less
`runtimeConfig.networkProxy` with explicit `hostLoopback: "allow"`, MXC also
supports a compatibility workaround on hosts that support PSEC 1.0 but not 1.1.
Hosts that support PSEC 1.1 or newer must still advertise the ingress support
flag; accepting a 1.0 payload does not enable this workaround.
Requests that do not allow host loopback use the PSEC 1.0 capability mapping.
Requests the PSEC contract cannot preserve continue to an AppContainer tier.

#### Discovering identity-less loopback proxy support
Expand All @@ -130,12 +124,7 @@ Backend discovery reports `identitylessLoopbackProxy` on the ProcessContainer
`base-container` tier when the host supports an identity-less proxy on loopback.
The Windows request probe also reports
`probes.baseContainerSupportsIdentitylessLoopbackProxy`, including when called
without a request. Support is available on PSEC 1.0-only hosts and on hosts with
PSEC 1.1 or newer that advertise ingress support. A host that reports
`baseContainerSupportsIngressHostLoopbackAllow` also supports an identity-less
loopback proxy. PSEC 1.1 or newer without ingress support does not qualify,
even if it also accepts PSEC 1.0 payloads. Version-query failures omit the
capability, set the probe fact to `false`, and add a warning.
without a request.

This does not grant general `ingressHostLoopbackAllow` support or report that the
current request uses an identity-less proxy. The request must set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ get fixed or wired.

| Backend | Status | Notes |
|---------|--------|-------|
| Process T1 | βœ… Good | Windows 24H2+ only. Runs the primitives suite, tier-gated to `base-container`. Includes supported directional networking phases (capability matrix, model-3 equivalence, explicit egress rules, host loopback, runtime proxy, reject surface). Remaining failures are genuine MXC bugs or harness limitations. |
| Process T1 | βœ… Good | Windows 24H2+ only. Runs the primitives suite, tier-gated to `base-container`. Includes supported directional networking phases (capability matrix, model-3 equivalence, explicit egress rules, host loopback, runtime proxy, reject surface). Host-loopback checks cover both ingress defaults; proxy checks verify IPv4 and IPv6 endpoint isolation without requiring native ingress support. Remaining failures are genuine MXC bugs or harness limitations. |
| Process T3 | βœ… Good | Windows 23H2 only. Runs the primitives suite tier-gated to `appcontainer-dacl`, plus `T3-Workloads.ps1` (real programs β€” pwsh, git, node, python, cmd) on top of the T3 primitives. The directional networking phases assert the documented *rejection* behavior here, since AppContainer cannot carry egress rules, proxy peer identity, or host-loopback configuration. |
| Bubblewrap | βœ… Good | |
| LXC | βœ… Good | Some networking tests fail on distros other than Ubuntu 24.04; seems to be an issue with MXC. The in-process streaming handle is covered at PR time by `lxc-e2e.yml` instead, because this matrix runs prebuilt binaries. |
Expand Down
5 changes: 1 addition & 4 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,7 @@ the development/testing configuration is:

This identity-less host-loopback deployment restricts client egress to the
configured proxy address and port, but does not verify which process owns that
endpoint. It supports PSEC 1.0-only hosts and hosts with PSEC 1.1 or newer that
advertise ingress support. PSEC 1.1 or newer without that flag rejects the
request, even if the host also accepts PSEC 1.0 payloads. For production
ProcessContainer deployments, identify a packaged proxy through
endpoint. For production ProcessContainer deployments, identify a packaged proxy through
`processContainer.network.allowedProxyPeer` instead; see
[proxy deployment choices](backends/process-container/networking.md#proxy-deployment-choices).
Bubblewrap and Seatbelt also support a caller-managed loopback proxy, but
Expand Down
9 changes: 8 additions & 1 deletion sdk/dotnet/Microsoft.Mxc.Sdk/V1/PlatformDiscovery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ public enum BackendCapability
FilesystemDeniedPaths = 3,

/// <summary>
/// Host-loopback allow enforcement at the reported tier.
/// With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
/// loopback. Native Windows ingress support adds bidirectional access, including
/// container-hosted loopback servers.
/// </summary>
IngressHostLoopbackAllow = 4,

Expand Down Expand Up @@ -151,6 +153,11 @@ public sealed class ProbeFacts
public bool BfsCompiledIn { get; init; }
public bool BaseContainerSupportsDenyPaths { get; init; }
public bool BaseContainerSupportsEnumeratePaths { get; init; }
/// <summary>
/// With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
/// loopback. Native Windows ingress support adds bidirectional access, including
/// container-hosted loopback servers.
/// </summary>
public bool BaseContainerSupportsIngressHostLoopbackAllow { get; init; }
/// <summary>Identity-less proxy support on loopback; requires explicit host-loopback allow.</summary>
public bool BaseContainerSupportsIdentitylessLoopbackProxy { get; init; }
Expand Down
10 changes: 10 additions & 0 deletions sdk/node/src/v1/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,11 @@ export type BackendCapability =
| 'captureDenials'
| 'filesystemDeniedPaths'
| 'filesystemEnumeratePaths'
/**
* With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
* loopback. Native Windows ingress support adds bidirectional access, including
* container-hosted loopback servers.
*/
| 'ingressHostLoopbackAllow'
| 'proxyEnforcement'
| 'identitylessLoopbackProxy'
Expand Down Expand Up @@ -756,6 +761,11 @@ export interface ProbeFacts {
bfsCompiledIn: boolean;
baseContainerSupportsDenyPaths: boolean;
baseContainerSupportsEnumeratePaths: boolean;
/**
* With explicit host-loopback allow, the baseline is policy-permitted stateful outbound
* loopback. Native Windows ingress support adds bidirectional access, including
* container-hosted loopback servers.
*/
baseContainerSupportsIngressHostLoopbackAllow: boolean;
/** Identity-less proxy support on loopback; requires explicit host-loopback allow. */
baseContainerSupportsIdentitylessLoopbackProxy: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ use crate::mxc_common::models::{
use crate::process_security_environment_spec::process_security_environment_layout::{
finish_process_security_environment_buffer, DestinationRuleT as PsecDestinationRuleT,
EndpointPolicyT as PsecEndpointPolicy, EndpointRuleT as PsecEndpointRuleT,
FilterAction as PsecFilterAction, IngressPolicyT as PsecIngressPolicy,
IpProtocol as PsecIpProtocol, IpSubnetT as PsecIpSubnetT, NetworkPolicyT as PsecNetworkPolicy,
PortRuleT as PsecPortRuleT, ProcessSecurityEnvironmentT as PsecProcessSecurityEnvironment,
ProxyInfoT as PsecProxyInfo, SchemaVersionT,
FilterAction as PsecFilterAction, IpProtocol as PsecIpProtocol, IpSubnetT as PsecIpSubnetT,
NetworkPolicyT as PsecNetworkPolicy, PortRuleT as PsecPortRuleT,
ProcessSecurityEnvironmentT as PsecProcessSecurityEnvironment, ProxyInfoT as PsecProxyInfo,
SchemaVersionT,
};

use crate::process_container_common::network_policy_helpers::{
Expand All @@ -30,7 +30,6 @@ pub(super) fn has_conflicting_proxy_identity(policy: &ContainerPolicy) -> bool {
pub(super) fn build_psec_v1_security_environment_spec(
request: &ExecutionRequest,
version: SecurityEnvironmentVersion,
supports_network_ingress: bool,
capture_network_decisions: bool,
) -> Vec<u8> {
assert_eq!(
Expand All @@ -39,10 +38,7 @@ pub(super) fn build_psec_v1_security_environment_spec(
);
let mut builder = flatbuffers::FlatBufferBuilder::with_capacity(1024);
let mut capabilities = effective_capabilities(request, capture_network_decisions);
if request.policy.network_proxy.is_enabled()
&& unrestricted_host_loopback_allowed(&request.policy)
{
// Proxy mode cannot carry the native ingress table that grants loopback.
if unrestricted_host_loopback_allowed(&request.policy) {
ensure_capability(&mut capabilities, "networkLoopback");
}
let ui_restrictions = crate::process_container_common::job_object::to_job_object_uilimit_mask(
Expand All @@ -64,10 +60,7 @@ pub(super) fn build_psec_v1_security_environment_spec(
fs_read_only: non_empty_paths(&request.policy.readonly_paths),
fs_deny: non_empty_paths(&request.policy.denied_paths),
fs_enumerate: non_empty_paths(&request.policy.enumerate_paths),
network_policy: Some(Box::new(build_psec_v1_network_policy(
&request.policy,
supports_network_ingress,
))),
network_policy: Some(Box::new(build_psec_v1_network_policy(&request.policy))),
};
let spec = spec.pack(&mut builder);
finish_process_security_environment_buffer(&mut builder, spec);
Expand Down Expand Up @@ -103,10 +96,7 @@ fn non_empty_paths(paths: &[String]) -> Option<Vec<String>> {
(!paths.is_empty()).then(|| paths.to_vec())
}

fn build_psec_v1_network_policy(
policy: &ContainerPolicy,
supports_network_ingress: bool,
) -> PsecNetworkPolicy {
fn build_psec_v1_network_policy(policy: &ContainerPolicy) -> PsecNetworkPolicy {
let mut network = PsecNetworkPolicy::default();
if policy.network_proxy.is_enabled() {
// Proxy and direct egress are mutually exclusive PSEC policy forms.
Expand All @@ -133,30 +123,10 @@ fn build_psec_v1_network_policy(
}
network.egress = Some(Box::new(egress));
}
// Proxy mode uses the private-network capability and peer for ingress.
// PSEC rejects a proxy combined with the native ingress table.
let ingress_policy = if supports_network_ingress && !policy.network_proxy.is_enabled() {
policy.network_ingress.as_ref()
} else {
None
};
network.allowed_appcontainer_peer = allowed_appcontainer_peer(policy);
if let Some(ingress_policy) = ingress_policy {
network.ingress = Some(Box::new(PsecIngressPolicy {
default_action: psec_filter_action(ingress_policy.default),
host_loopback: psec_filter_action(ingress_policy.host_loopback),
}));
}
network
}

fn psec_filter_action(action: NetworkAction) -> PsecFilterAction {
match action {
NetworkAction::Allow => PsecFilterAction::allow,
NetworkAction::Deny => PsecFilterAction::deny,
}
}

fn effective_egress_default(policy: &ContainerPolicy) -> NetworkAction {
policy
.network_egress
Expand Down
Loading
Loading