Skip to content
Open
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
1 change: 1 addition & 0 deletions BOOKMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Desired state of the system, organized by capability domain.
| [Runner](specs/agents/runner.spec.md) | agents | Runner subprocess lifecycle, bridges, gRPC/HTTP endpoints |
| [MCP Server](specs/integrations/mcp-server.spec.md) | integrations | MCP tool definitions, sidecar and public endpoint modes |
| [Security](specs/security/security.spec.md) | security | Identity boundaries, credential authorization, per-session isolation, design decisions |
| [OpenShell Sandbox](specs/security/openshell-sandbox.spec.md) | security | Agent subprocess sandbox: network namespace, Landlock, seccomp, TLS proxy, OPA policy |

Feature specs remain in numbered directories under `specs/` (e.g., `specs/001-*/spec.md`).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ func runKubeMode(ctx context.Context, cfg *config.ControlPlaneConfig) error {
ImagePullSecret: cfg.ImagePullSecret,
PlatformMode: cfg.PlatformMode,
MPPConfigNamespace: cfg.MPPConfigNamespace,
OpenShellEnabled: cfg.OpenShellEnabled,
OpenShellPolicyName: cfg.OpenShellPolicyName,
}

conn, err := grpc.NewClient(cfg.GRPCServerAddr, grpc.WithTransportCredentials(grpcCredentials(cfg.GRPCUseTLS)))
Expand Down
4 changes: 4 additions & 0 deletions components/ambient-control-plane/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type ControlPlaneConfig struct {
HTTPSProxy string
NoProxy string
ImagePullSecret string
OpenShellEnabled bool
OpenShellPolicyName string
}

func Load() (*ControlPlaneConfig, error) {
Expand Down Expand Up @@ -91,6 +93,8 @@ func Load() (*ControlPlaneConfig, error) {
HTTPSProxy: os.Getenv("HTTPS_PROXY"),
NoProxy: os.Getenv("NO_PROXY"),
ImagePullSecret: os.Getenv("IMAGE_PULL_SECRET"),
OpenShellEnabled: os.Getenv("OPENSHELL_ENABLED") == "true",
OpenShellPolicyName: envOrDefault("OPENSHELL_POLICY_CONFIGMAP", "openshell-policy"),
}

if cfg.MCPAPIServerURL == "" {
Expand Down
14 changes: 14 additions & 0 deletions components/ambient-control-plane/internal/kubeclient/kubeclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ var NetworkPolicyGVR = schema.GroupVersionResource{
Resource: "networkpolicies",
}

var ConfigMapGVR = schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "configmaps",
}

type KubeClient struct {
dynamic dynamic.Interface
logger zerolog.Logger
Expand Down Expand Up @@ -327,6 +333,14 @@ func (kc *KubeClient) ListTenantNamespaces(ctx context.Context, namespace, label
return kc.dynamic.Resource(gvr).Namespace(namespace).List(ctx, opts)
}

func (kc *KubeClient) GetConfigMap(ctx context.Context, namespace, name string) (*unstructured.Unstructured, error) {
return kc.dynamic.Resource(ConfigMapGVR).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
}

func (kc *KubeClient) CreateConfigMap(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
return kc.dynamic.Resource(ConfigMapGVR).Namespace(obj.GetNamespace()).Create(ctx, obj, metav1.CreateOptions{})
}

func (kc *KubeClient) GetResource(ctx context.Context, gvr schema.GroupVersionResource, namespace, name string) (*unstructured.Unstructured, error) {
return kc.dynamic.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ type KubeReconcilerConfig struct {
ImagePullSecret string
PlatformMode string
MPPConfigNamespace string
OpenShellEnabled bool
OpenShellPolicyName string
}

type SimpleKubeReconciler struct {
Expand Down Expand Up @@ -178,6 +180,12 @@ func (r *SimpleKubeReconciler) provisionSession(ctx context.Context, session typ
}
}

if r.cfg.OpenShellEnabled {
if err := r.ensureOpenShellPolicy(ctx, namespace); err != nil {
return fmt.Errorf("ensuring openshell policy: %w", err)
}
}

if err := r.ensureServiceAccount(ctx, namespace, session, sessionLabel); err != nil {
return fmt.Errorf("ensuring service account: %w", err)
}
Expand Down Expand Up @@ -522,12 +530,7 @@ func (r *SimpleKubeReconciler) ensurePod(ctx context.Context, namespace string,
"memory": "4Gi",
},
},
"securityContext": map[string]interface{}{
"allowPrivilegeEscalation": false,
"capabilities": map[string]interface{}{
"drop": []interface{}{"ALL"},
},
},
"securityContext": r.buildRunnerSecurityContext(),
},
}

Expand Down Expand Up @@ -588,6 +591,14 @@ func (r *SimpleKubeReconciler) ensurePod(ctx context.Context, namespace string,
},
}

if r.cfg.OpenShellEnabled {
pod.Object["spec"].(map[string]interface{})["securityContext"] = map[string]interface{}{
"seccompProfile": map[string]interface{}{
"type": "Unconfined",
},
}
}
Comment on lines +594 to +600

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for seccomp or syscall requirements in OpenShell docs/code
rg -i 'seccomp|syscall' --type=md --type=yaml -C3

Repository: ambient-code/platform

Length of output: 24645


OpenShell requires pod-level seccompProfile.type: Unconfined (but scope the side effects)
OpenShell’s runner/security specs explicitly require seccompProfile: Unconfined at the pod level because the Supervisor applies its own seccomp-BPF filter; changing this to a targeted seccomp profile at the pod level would conflict with the documented design.

Since pod-level Unconfined affects all containers in the pod, confirm MCP/credential sidecars either (a) aren’t in this pod when OpenShellEnabled is on, or (b) have container-level securityContext.seccompProfile overrides to keep syscall filtering enabled for them (instead of inheriting Unconfined).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/ambient-control-plane/internal/reconciler/kube_reconciler.go`
around lines 594 - 600, When r.cfg.OpenShellEnabled is true the code sets a
pod-level securityContext.seccompProfile to Unconfined
(pod.Object["spec"].(map[string]interface{})["securityContext"] ...), which
affects all containers; update the logic to either (a) skip applying the
pod-level Unconfined if known MCP/credential sidecars are present, or (b) after
setting the pod-level Unconfined explicitly override those sidecars by iterating
pod.Object["spec"].(map[string]interface{})["containers"].([]interface{}) and
for containers matching the MCP/credential sidecar names set container-level
securityContext.seccompProfile to the intended restricted profile (e.g., type:
"RuntimeDefault" or the cluster default) so they do not inherit Unconfined;
reference r.cfg.OpenShellEnabled, pod.Object["spec"], securityContext,
seccompProfile, and the containers array when implementing the check/overrides.


if r.cfg.ImagePullSecret != "" {
pod.Object["spec"].(map[string]interface{})["imagePullSecrets"] = []interface{}{
map[string]interface{}{"name": r.cfg.ImagePullSecret},
Expand All @@ -602,6 +613,25 @@ func (r *SimpleKubeReconciler) ensurePod(ctx context.Context, namespace string,
return nil
}

func (r *SimpleKubeReconciler) buildRunnerSecurityContext() map[string]interface{} {
sc := map[string]interface{}{
"allowPrivilegeEscalation": false,
"capabilities": map[string]interface{}{
"drop": []interface{}{"ALL"},
},
}
if r.cfg.OpenShellEnabled {
sc["allowPrivilegeEscalation"] = true
sc["runAsUser"] = int64(0)
sc["runAsNonRoot"] = false
sc["capabilities"] = map[string]interface{}{
"drop": []interface{}{"ALL"},
"add": []interface{}{"NET_ADMIN", "SYS_ADMIN", "SYS_PTRACE", "SETUID", "SETGID", "CHOWN", "DAC_OVERRIDE"},
}
}
return sc
}

func (r *SimpleKubeReconciler) buildVolumes(extraVolumes []interface{}) []interface{} {
vols := []interface{}{
map[string]interface{}{
Expand All @@ -624,6 +654,14 @@ func (r *SimpleKubeReconciler) buildVolumes(extraVolumes []interface{}) []interf
},
})
}
if r.cfg.OpenShellEnabled {
vols = append(vols, map[string]interface{}{
"name": "openshell-policy",
"configMap": map[string]interface{}{
"name": r.cfg.OpenShellPolicyName,
},
})
}
vols = append(vols, extraVolumes...)
return vols
}
Expand All @@ -648,6 +686,13 @@ func (r *SimpleKubeReconciler) buildVolumeMounts() []interface{} {
"readOnly": true,
})
}
if r.cfg.OpenShellEnabled {
mounts = append(mounts, map[string]interface{}{
"name": "openshell-policy",
"mountPath": "/etc/openshell",
"readOnly": true,
})
}
return mounts
}

Expand Down Expand Up @@ -688,6 +733,48 @@ func (r *SimpleKubeReconciler) ensureVertexSecret(ctx context.Context, namespace
return nil
}

func (r *SimpleKubeReconciler) ensureOpenShellPolicy(ctx context.Context, namespace string) error {
policyName := r.cfg.OpenShellPolicyName

if _, err := r.nsKube().GetConfigMap(ctx, namespace, policyName); err == nil {
return nil
}

src, err := r.nsKube().GetConfigMap(ctx, r.cfg.CPRuntimeNamespace, policyName)
if err != nil {
return fmt.Errorf("reading openshell policy configmap %s/%s: %w", r.cfg.CPRuntimeNamespace, policyName, err)
}

data, _, _ := unstructured.NestedStringMap(src.Object, "data")
dataIface := make(map[string]interface{}, len(data))
for k, v := range data {
dataIface[k] = v
}

dst := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": policyName,
"namespace": namespace,
"labels": map[string]interface{}{
LabelManaged: "true",
LabelManagedBy: "ambient-control-plane",
},
},
"data": dataIface,
},
}

if _, err := r.nsKube().CreateConfigMap(ctx, dst); err != nil && !k8serrors.IsAlreadyExists(err) {
return fmt.Errorf("copying openshell policy configmap to %s: %w", namespace, err)
}

r.logger.Debug().Str("namespace", namespace).Str("configmap", policyName).Msg("openshell policy configmap copied")
return nil
}
Comment on lines +736 to +776

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add validation for required ConfigMap keys.

ensureOpenShellPolicy copies the policy ConfigMap data without verifying it contains the required keys (policy.rego, policy.yaml). If the source ConfigMap in CPRuntimeNamespace is malformed or incomplete, the runner will fail at runtime with an unclear error when the OpenShell supervisor tries to read the missing files.

Validate the keys exist after reading the source ConfigMap (around line 748) and return a clear error if they're missing.

🛡️ Proposed fix to validate required keys
 	data, _, _ := unstructured.NestedStringMap(src.Object, "data")
+	if _, ok := data["policy.rego"]; !ok {
+		return fmt.Errorf("openshell policy configmap %s/%s missing required key 'policy.rego'", r.cfg.CPRuntimeNamespace, policyName)
+	}
+	if _, ok := data["policy.yaml"]; !ok {
+		return fmt.Errorf("openshell policy configmap %s/%s missing required key 'policy.yaml'", r.cfg.CPRuntimeNamespace, policyName)
+	}
 	dataIface := make(map[string]interface{}, len(data))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (r *SimpleKubeReconciler) ensureOpenShellPolicy(ctx context.Context, namespace string) error {
policyName := r.cfg.OpenShellPolicyName
if _, err := r.nsKube().GetConfigMap(ctx, namespace, policyName); err == nil {
return nil
}
src, err := r.nsKube().GetConfigMap(ctx, r.cfg.CPRuntimeNamespace, policyName)
if err != nil {
return fmt.Errorf("reading openshell policy configmap %s/%s: %w", r.cfg.CPRuntimeNamespace, policyName, err)
}
data, _, _ := unstructured.NestedStringMap(src.Object, "data")
dataIface := make(map[string]interface{}, len(data))
for k, v := range data {
dataIface[k] = v
}
dst := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": policyName,
"namespace": namespace,
"labels": map[string]interface{}{
LabelManaged: "true",
LabelManagedBy: "ambient-control-plane",
},
},
"data": dataIface,
},
}
if _, err := r.nsKube().CreateConfigMap(ctx, dst); err != nil && !k8serrors.IsAlreadyExists(err) {
return fmt.Errorf("copying openshell policy configmap to %s: %w", namespace, err)
}
r.logger.Debug().Str("namespace", namespace).Str("configmap", policyName).Msg("openshell policy configmap copied")
return nil
}
func (r *SimpleKubeReconciler) ensureOpenShellPolicy(ctx context.Context, namespace string) error {
policyName := r.cfg.OpenShellPolicyName
if _, err := r.nsKube().GetConfigMap(ctx, namespace, policyName); err == nil {
return nil
}
src, err := r.nsKube().GetConfigMap(ctx, r.cfg.CPRuntimeNamespace, policyName)
if err != nil {
return fmt.Errorf("reading openshell policy configmap %s/%s: %w", r.cfg.CPRuntimeNamespace, policyName, err)
}
data, _, _ := unstructured.NestedStringMap(src.Object, "data")
if _, ok := data["policy.rego"]; !ok {
return fmt.Errorf("openshell policy configmap %s/%s missing required key 'policy.rego'", r.cfg.CPRuntimeNamespace, policyName)
}
if _, ok := data["policy.yaml"]; !ok {
return fmt.Errorf("openshell policy configmap %s/%s missing required key 'policy.yaml'", r.cfg.CPRuntimeNamespace, policyName)
}
dataIface := make(map[string]interface{}, len(data))
for k, v := range data {
dataIface[k] = v
}
dst := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": policyName,
"namespace": namespace,
"labels": map[string]interface{}{
LabelManaged: "true",
LabelManagedBy: "ambient-control-plane",
},
},
"data": dataIface,
},
}
if _, err := r.nsKube().CreateConfigMap(ctx, dst); err != nil && !k8serrors.IsAlreadyExists(err) {
return fmt.Errorf("copying openshell policy configmap to %s: %w", namespace, err)
}
r.logger.Debug().Str("namespace", namespace).Str("configmap", policyName).Msg("openshell policy configmap copied")
return nil
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/ambient-control-plane/internal/reconciler/kube_reconciler.go`
around lines 736 - 776, The ensureOpenShellPolicy function currently copies a
ConfigMap without validating required keys; after reading src via
r.nsKube().GetConfigMap (from r.cfg.CPRuntimeNamespace for policyName) inspect
the extracted data map and verify that "policy.rego" and "policy.yaml" keys
exist (or any other required keys), and if any are missing return a clear
fmt.Errorf identifying the missing key(s) and source (e.g., "openshell policy
configmap missing required key(s): ... in %s/%s"), before proceeding to
construct dst and call r.nsKube().CreateConfigMap.


func (r *SimpleKubeReconciler) buildEnv(ctx context.Context, session types.Session, sdk *sdkclient.Client, useMCPSidecar bool, credentialIDs map[string]string) []interface{} {
useVertex := "0"
if r.cfg.VertexEnabled {
Expand Down Expand Up @@ -770,6 +857,14 @@ func (r *SimpleKubeReconciler) buildEnv(ctx context.Context, session types.Sessi
env = append(env, envVar("NO_PROXY", r.cfg.NoProxy))
}

if r.cfg.OpenShellEnabled {
env = append(env,
envVar("OPENSHELL_ENABLED", "true"),
envVar("OPENSHELL_POLICY_RULES", "/etc/openshell/policy.rego"),
envVar("OPENSHELL_POLICY_DATA", "/etc/openshell/policy.yaml"),
)
}

return env
}

Expand Down
Loading
Loading