{"record":{"id":"58e6533793e66e5f","repo":"paperclipai/paperclip","slug":"plugin-kubernetes-egressmode-standard-cannot-enf","errorCode":null,"errorMessage":"[plugin-kubernetes] egressMode=standard cannot enforce FQDN-based egress rules; falling back to public-IPv4 (TCP 80/443) with private/link-local ranges excluded so the configured FQDNs (${totalFqdnsForWarn.join(\", \")}) remain reachable. Switch egressMode to \"cilium\" for exact FQDN allow-listing.","messagePattern":"\\[plugin-kubernetes\\] egressMode=standard cannot enforce FQDN-based egress rules; falling back to public-IPv4 \\(TCP 80/443\\) with private/link-local ranges excluded so the configured FQDNs \\((.+?)\\) remain reachable\\. Switch egressMode to \"cilium\" for exact FQDN allow-listing\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/plugins/sandbox-providers/kubernetes/src/plugin.ts","lineNumber":319,"sourceCode":"  ): Promise<PluginEnvironmentLease> {\n    const config = kubernetesProviderConfigSchema.parse(params.config);\n    const namespace = deriveTenantNamespace(config, params.companyId);\n\n    // The adapter for THIS run is the agent's adapter (params.adapterType) when\n    // supplied, so one environment can serve mixed harnesses; otherwise fall back\n    // to the environment's configured default adapter. getAdapterDefaults validates\n    // it is a registered adapter (throws otherwise), so a curated-out adapter fails\n    // the lease as before.\n    const effectiveAdapterType = resolveRunAdapterType(params.adapterType, config.adapterType);\n\n    // Emit a runtime warning if FQDNs are configured but egressMode=standard\n    // cannot enforce them. Mirrors the validateConfig warning so operators see\n    // it in paperclip-server logs even if they missed the validation step.\n    const adapterDefaultsForWarn = getAdapterDefaults(effectiveAdapterType, config.adapters);\n    const totalFqdnsForWarn = [...adapterDefaultsForWarn.allowFqdns, ...config.egressAllowFqdns];\n    if (config.egressMode === \"standard\" && totalFqdnsForWarn.length > 0) {\n      if (config.egressAllowCidrs.length === 0) {\n        console.warn(\n          `[plugin-kubernetes] egressMode=standard cannot enforce FQDN-based egress rules; falling back to public-IPv4 (TCP 80/443) with private/link-local ranges excluded so the configured FQDNs (${totalFqdnsForWarn.join(\", \")}) remain reachable. Switch egressMode to \"cilium\" for exact FQDN allow-listing.`,\n        );\n      } else {\n        console.warn(\n          `[plugin-kubernetes] egressMode=standard cannot enforce FQDN-based egress rules. The following FQDNs are reachable only via operator-supplied egressAllowCidrs: ${totalFqdnsForWarn.join(\", \")}. Switch egressMode to \"cilium\" for exact FQDN allow-listing.`,\n        );\n      }\n    }\n\n    const kc = createKubeConfig({\n      inCluster: config.inCluster,\n      kubeconfig: config.kubeconfig,\n    });\n    const clients = makeKubeClients(kc);\n\n    // Ensure the tenant namespace and all its RBAC / network policy resources\n    // exist before we try to create the Job.\n    const adapterDefaults = getAdapterDefaults(effectiveAdapterType, config.adapters);","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/paperclipai/paperclip/blob/120ae5428fa29bee300bcf806491cd4d965fbb7c/packages/plugins/sandbox-providers/kubernetes/src/plugin.ts#L301-L337","documentation":"The Kubernetes sandbox provider enforces egress with standard (Calico-style) NetworkPolicy, which can only express IP/CIDR rules — not FQDNs. When FQDN allow-lists are configured (adapter defaults plus egressAllowFqdns) and no operator CIDRs are supplied, the plugin falls back to allowing all public IPv4 TCP 80/443 (excluding private/link-local) so the FQDNs stay reachable. Isolation is therefore much broader than the configured allow-list intends; switch to egressMode=cilium for exact FQDN enforcement.","triggerScenarios":"Sandbox lease with config.egressMode === 'standard' and a non-empty FQDN set (adapterDefaults.allowFqdns + config.egressAllowFqdns) while config.egressAllowCidrs is empty — the warn is emitted per lease creation.","commonSituations":"Operator adds api.anthropic.com or similar to allowFqdns expecting allow-listing on a non-Cilium cluster; adapter curated defaults already carry FQDNs so the warn appears even without explicit operator config; security review discovers broad public HTTPS egress.","solutions":["Set egressMode to \"cilium\" on a Cilium-equipped cluster for exact FQDN allow-listing.","Alternatively supply egressAllowCidrs covering the FQDNs' resolved public IPs (this switches to the narrower CIDR-based warning).","Alternatively remove the FQDN entries if broad public IPv4 HTTPS egress is acceptable.","If the FQDNs come from adapter defaults, override the adapter's allowFqdns defaults to empty."],"exampleFix":"// before\n{ \"egressMode\": \"standard\", \"egressAllowFqdns\": [\"api.anthropic.com\"] }\n\n// after\n{ \"egressMode\": \"cilium\", \"egressAllowFqdns\": [\"api.anthropic.com\"] }","handlingStrategy":"validation","validationCode":"// Fail the deploy when FQDN allow-listing is required but unenforceable:\nfunction assertEgressConfig(config: SandboxProviderConfig): void {\n  const fqdns = [...adapterDefaults.allowFqdns, ...config.egressAllowFqdns];\n  if (config.egressMode === 'standard' && fqdns.length > 0 && config.egressAllowCidrs.length === 0) {\n    throw new Error(\n      `egressMode=standard silently broadens egress to public IPv4 80/443 for ${fqdns.join(', ')}; ` +\n      'use egressMode=cilium or supply egressAllowCidrs',\n    );\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat the matching validateConfig warning as a CI blocker, not a log line.","Standardize on Cilium clusters wherever FQDN allow-listing is a compliance requirement.","Document the public-IPv4 fallback semantics to security reviewers so nobody mistakes it for allow-listing.","Check adapter defaults for built-in allowFqdns — the warn can fire with zero operator config."],"tags":["kubernetes","egress","network-policy","fqdn","cilium","security"],"backgroundTag":"network-policy-fqdn-limitation","analyzedSha":"120ae5428fa29bee300bcf806491cd4d965fbb7c","analyzedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}