Repository: cdk8s-team/cdk8s-plus
Pull request #1285 (2022-10-19 10:27:04)

feat(pod): isolate pod of all network connections (#1177)
# Backport

This will backport the following commits from `k8s-25/main` to `k8s-23/main`:
 - [feat(pod): isolate pod of all network connections (#1177)](https://github.com/cdk8s-team/cdk8s-plus/pull/1177)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool documentation](https://github.com/sqren/backport)


Patch:
diff --git a/src/cron-job.ts b/src/cron-job.ts
index bcf9ec32..e28bdbc1 100644
--- a/src/cron-job.ts
+++ b/src/cron-job.ts
@@ -152,6 +152,10 @@ export class CronJob extends workload.Workload {
       throw new Error('The \'ttlAfterFinished\' property cannot be set if \'successfulJobsRetained\' property or \'failedJobsRetained\' property is set. This would cause the retention of jobs to not work properly since it would delete the job based on its value.');
     }
 
+    if (this.isolate) {
+      this.connections.isolate();
+    }
+
     this.schedule = props.schedule;
     this.concurrencyPolicy = props.concurrencyPolicy ?? ConcurrencyPolicy.FORBID;
     this.startingDeadline = props.startingDeadline ?? Duration.seconds(10);
diff --git a/src/daemon-set.ts b/src/daemon-set.ts
index b010d15d..c62294b2 100644
--- a/src/daemon-set.ts
+++ b/src/daemon-set.ts
@@ -55,6 +55,9 @@ export class DaemonSet extends workload.Workload {
 
     this.minReadySeconds = props.minReadySeconds ?? 0;
 
+    if (this.isolate) {
+      this.connections.isolate();
+    }
   }
 
   /**
diff --git a/src/deployment.ts b/src/deployment.ts
index 60e32eb2..5ab994cd 100644
--- a/src/deployment.ts
+++ b/src/deployment.ts
@@ -158,6 +158,10 @@ export class Deployment extends workload.Workload {
 
     this.replicas = props.replicas ?? 2;
     this.strategy = props.strategy ?? DeploymentStrategy.rollingUpdate();
+
+    if (this.isolate) {
+      this.connections.isolate();
+    }
   }
 
   /**
diff --git a/src/job.ts b/src/job.ts
index 4a5cc933..a5a31e70 100644
--- a/src/job.ts
+++ b/src/job.ts
@@ -85,6 +85,9 @@ export class Job extends workload.Workload {
     this.backoffLimit = props.backoffLimit;
     this.ttlAfterFinished = props.ttlAfterFinished;
 
+    if (this.isolate) {
+      this.connections.isolate();
+    }
   }
 
   /**
diff --git a/src/pod.ts b/src/pod.ts
index b7693c19..9f853dd0 100644
--- a/src/pod.ts
+++ b/src/pod.ts
@@ -20,6 +20,8 @@ export abstract class AbstractPod extends base.Resource implements IPodSelector,
   public readonly dockerRegistryAuth?: secret.DockerConfigSecret;
   public readonly automountServiceAccountToken: boolean;
 
+  protected readonly isolate: boolean;
+
   private readonly _containers: container.Container[] = [];
   private readonly _initContainers: container.Container[] = [];
   private readonly _hostAliases: HostAlias[] = [];
@@ -36,6 +38,7 @@ export abstract class AbstractPod extends base.Resource implements IPodSelector,
     this.dns = new PodDns(props.dns);
     this.dockerRegistryAuth = props.dockerRegistryAuth;
     this.automountServiceAccountToken = props.automountServiceAccountToken ?? false;
+    this.isolate = props.isolate ?? false;
 
     if (props.containers) {
       props.containers.forEach(c => this.addContainer(c));
@@ -426,6 +429,13 @@ export interface AbstractPodProps extends base.ResourceProps {
    */
   readonly automountServiceAccountToken?: boolean;
 
+  /**
+   * Isolates the pod. This will prevent any ingress or egress connections to / from this pod.
+   * You can however allow explicit connections post instantiation by using the `.connections` property.
+   *
+   * @default false
+   */
+  readonly isolate?: boolean;
 }
 
 /**
@@ -541,6 +551,10 @@ export class Pod extends AbstractPod {
 
     this.scheduling = new PodScheduling(this);
     this.connections = new PodConnections(this);
+
+    if (this.isolate) {
+      this.connections.isolate();
+    }
   }
 
   public get podMetadata(): ApiObjectMetadataDefinition {
@@ -552,6 +566,7 @@ export class Pod extends AbstractPod {
    */
   public _toKube(): k8s.PodSpec {
     const scheduling = this.scheduling._toKube();
+
     return {
       ...this._toPodSpec(),
       affinity: scheduling.affinity,
@@ -1793,4 +1808,22 @@ export class PodConnections {
   private extractPorts(selector?: networkpolicy.INetworkPolicyPeer): networkpolicy.NetworkPolicyPort[] {
     return container.extractContainerPorts(selector).map(n => networkpolicy.NetworkPolicyPort.tcp(n.number));
   }
+
+  /**
+   * Sets the default network policy for Pod/Workload to have all egress and ingress connections as disabled
+   */
+  public isolate() {
+    new networkpolicy.NetworkPolicy(this.instance, 'DefaultDenyAll', {
+      selector: this.instance,
+      // the policy must be defined in the namespace of the pod
+      // so it can select it.
+      metadata: { namespace: this.instance.metadata.namespace },
+      egress: {
+        default: networkpolicy.NetworkPolicyTrafficDefault.DENY,
+      },
+      ingress: {
+        default: networkpolicy.NetworkPolicyTrafficDefault.DENY,
+      },
+    });
+  }
 }
diff --git a/src/stateful-set.ts b/src/stateful-set.ts
index abc013e4..4d4a9fa3 100644
--- a/src/stateful-set.ts
+++ b/src/stateful-set.ts
@@ -137,6 +137,10 @@ export class StatefulSet extends workload.Workload {
     this.minReady = props.minReady ?? Duration.seconds(0);
 
     this._service.select(this);
+
+    if (this.isolate) {
+      this.connections.isolate();
+    }
   }
 
   /**
diff --git a/src/workload.ts b/src/workload.ts
index a6eff5b7..e96db5bc 100644
--- a/src/workload.ts
+++ b/src/workload.ts
@@ -149,6 +149,7 @@ export abstract class Workload extends pod.AbstractPod {
     };
 
     const scheduling = this.scheduling._toKube();
+
     return {
       ...super._toPodSpec(),
       affinity: scheduling.affinity,


Test patch:
diff --git a/test/__snapshots__/cron-job.test.ts.snap b/test/__snapshots__/cron-job.test.ts.snap
index f81e4ae5..5d2428ab 100644
--- a/test/__snapshots__/cron-job.test.ts.snap
+++ b/test/__snapshots__/cron-job.test.ts.snap
@@ -1,5 +1,89 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`Can be isolated 1`] = `
+Array [
+  Object {
+    "apiVersion": "batch/v1",
+    "kind": "CronJob",
+    "metadata": Object {
+      "name": "test-cronjob-c812e113",
+    },
+    "spec": Object {
+      "concurrencyPolicy": "Forbid",
+      "failedJobsHistoryLimit": 1,
+      "jobTemplate": Object {
+        "spec": Object {
+          "template": Object {
+            "metadata": Object {
+              "labels": Object {
+                "cdk8s.io/metadata.addr": "test-CronJob-c80e4ce0",
+              },
+            },
+            "spec": Object {
+              "automountServiceAccountToken": false,
+              "containers": Array [
+                Object {
+                  "image": "foobar",
+                  "imagePullPolicy": "Always",
+                  "name": "main",
+                  "resources": Object {
+                    "limits": Object {
+                      "cpu": "1500m",
+                      "memory": "2048Mi",
+                    },
+                    "requests": Object {
+                      "cpu": "1000m",
+                      "memory": "512Mi",
+                    },
+                  },
+                  "securityContext": Object {
+                    "allowPrivilegeEscalation": false,
+                    "privileged": false,
+                    "readOnlyRootFilesystem": true,
+                    "runAsGroup": 26000,
+                    "runAsNonRoot": true,
+                    "runAsUser": 25000,
+                  },
+                },
+              ],
+              "dnsPolicy": "ClusterFirst",
+              "restartPolicy": "Never",
+              "securityContext": Object {
+                "fsGroupChangePolicy": "Always",
+                "runAsNonRoot": true,
+              },
+              "setHostnameAsFQDN": false,
+            },
+          },
+        },
+      },
+      "schedule": "0 0 * * *",
+      "startingDeadlineSeconds": 10,
+      "successfulJobsHistoryLimit": 3,
+      "suspend": false,
+    },
+  },
+  Object {
+    "apiVersion": "networking.k8s.io/v1",
+    "kind": "NetworkPolicy",
+    "metadata": Object {
+      "name": "test-cronjob-defaultdenyall-c81b49ed",
+    },
+    "spec": Object {
+      "podSelector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-CronJob-c80e4ce0",
+        },
+      },
+      "policyTypes": Array [
+        "Egress",
+        "Ingress",
+      ],
+    },
+  },
+]
+`;
+
 exports[`custom configuration 1`] = `
 Array [
   Object {
diff --git a/test/__snapshots__/daemon-set.test.ts.snap b/test/__snapshots__/daemon-set.test.ts.snap
index 015d560f..b8b6db91 100644
--- a/test/__snapshots__/daemon-set.test.ts.snap
+++ b/test/__snapshots__/daemon-set.test.ts.snap
@@ -1,5 +1,85 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`Can be isolated 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "DaemonSet",
+    "metadata": Object {
+      "name": "test-daemonset-c8482ea2",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-DaemonSet-c8f77186",
+        },
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-DaemonSet-c8f77186",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "foobar",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "networking.k8s.io/v1",
+    "kind": "NetworkPolicy",
+    "metadata": Object {
+      "name": "test-daemonset-defaultdenyall-c826616a",
+    },
+    "spec": Object {
+      "podSelector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-DaemonSet-c8f77186",
+        },
+      },
+      "policyTypes": Array [
+        "Egress",
+        "Ingress",
+      ],
+    },
+  },
+]
+`;
+
 exports[`custom 1`] = `
 Array [
   Object {
diff --git a/test/__snapshots__/deployment.test.ts.snap b/test/__snapshots__/deployment.test.ts.snap
index 20e5e40e..66876fb5 100644
--- a/test/__snapshots__/deployment.test.ts.snap
+++ b/test/__snapshots__/deployment.test.ts.snap
@@ -131,6 +131,95 @@ Array [
 ]
 `;
 
+exports[`Can be isolated 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "replicas": 5,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "foobar",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "networking.k8s.io/v1",
+    "kind": "NetworkPolicy",
+    "metadata": Object {
+      "name": "test-deployment-defaultdenyall-c8a461c9",
+    },
+    "spec": Object {
+      "podSelector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "policyTypes": Array [
+        "Egress",
+        "Ingress",
+      ],
+    },
+  },
+]
+`;
+
 exports[`expose captures all container ports 1`] = `
 Array [
   Object {
diff --git a/test/__snapshots__/job.test.ts.snap b/test/__snapshots__/job.test.ts.snap
new file mode 100644
index 00000000..b1d9d422
--- /dev/null
+++ b/test/__snapshots__/job.test.ts.snap
@@ -0,0 +1,75 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Can be isolated 1`] = `
+Array [
+  Object {
+    "apiVersion": "batch/v1",
+    "kind": "Job",
+    "metadata": Object {
+      "name": "test-job-c8ab3340",
+    },
+    "spec": Object {
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Job-c8ebb678",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "foobar",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Never",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "networking.k8s.io/v1",
+    "kind": "NetworkPolicy",
+    "metadata": Object {
+      "name": "test-job-defaultdenyall-c8a9d814",
+    },
+    "spec": Object {
+      "podSelector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Job-c8ebb678",
+        },
+      },
+      "policyTypes": Array [
+        "Egress",
+        "Ingress",
+      ],
+    },
+  },
+]
+`;
diff --git a/test/__snapshots__/pod.test.ts.snap b/test/__snapshots__/pod.test.ts.snap
index 063cc0ff..2502aa96 100644
--- a/test/__snapshots__/pod.test.ts.snap
+++ b/test/__snapshots__/pod.test.ts.snap
@@ -3794,6 +3794,74 @@ Array [
 ]
 `;
 
+exports[`connections | can isolate pod 1`] = `
+Array [
+  Object {
+    "apiVersion": "v1",
+    "kind": "Pod",
+    "metadata": Object {
+      "labels": Object {
+        "cdk8s.io/metadata.addr": "test-Pod-c815bc91",
+      },
+      "name": "test-pod-c890e1b8",
+    },
+    "spec": Object {
+      "automountServiceAccountToken": false,
+      "containers": Array [
+        Object {
+          "image": "pod",
+          "imagePullPolicy": "Always",
+          "name": "main",
+          "resources": Object {
+            "limits": Object {
+              "cpu": "1500m",
+              "memory": "2048Mi",
+            },
+            "requests": Object {
+              "cpu": "1000m",
+              "memory": "512Mi",
+            },
+          },
+          "securityContext": Object {
+            "allowPrivilegeEscalation": false,
+            "privileged": false,
+            "readOnlyRootFilesystem": true,
+            "runAsGroup": 26000,
+            "runAsNonRoot": true,
+            "runAsUser": 25000,
+          },
+        },
+      ],
+      "dnsPolicy": "ClusterFirst",
+      "restartPolicy": "Always",
+      "securityContext": Object {
+        "fsGroupChangePolicy": "Always",
+        "runAsNonRoot": true,
+      },
+      "setHostnameAsFQDN": false,
+    },
+  },
+  Object {
+    "apiVersion": "networking.k8s.io/v1",
+    "kind": "NetworkPolicy",
+    "metadata": Object {
+      "name": "test-pod-defaultdenyall-c882408f",
+    },
+    "spec": Object {
+      "podSelector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Pod-c815bc91",
+        },
+      },
+      "policyTypes": Array [
+        "Egress",
+        "Ingress",
+      ],
+    },
+  },
+]
+`;
+
 exports[`defaults 1`] = `
 Array [
   Object {
diff --git a/test/__snapshots__/statefulset.test.ts.snap b/test/__snapshots__/statefulset.test.ts.snap
new file mode 100644
index 00000000..bf7c1239
--- /dev/null
+++ b/test/__snapshots__/statefulset.test.ts.snap
@@ -0,0 +1,109 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Can be isolated 1`] = `
+Array [
+  Object {
+    "apiVersion": "v1",
+    "kind": "Service",
+    "metadata": Object {
+      "name": "test-service-c85b0531",
+    },
+    "spec": Object {
+      "externalIPs": Array [],
+      "ports": Array [
+        Object {
+          "port": 80,
+        },
+      ],
+      "selector": Object {
+        "cdk8s.io/metadata.addr": "test-StatefulSet-c809b559",
+      },
+      "type": "ClusterIP",
+    },
+  },
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "StatefulSet",
+    "metadata": Object {
+      "name": "test-statefulset-c8a6ec86",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "podManagementPolicy": "OrderedReady",
+      "replicas": 1,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-StatefulSet-c809b559",
+        },
+      },
+      "serviceName": "test-service-c85b0531",
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-StatefulSet-c809b559",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "foobar",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+      "updateStrategy": Object {
+        "rollingUpdate": Object {
+          "partition": 0,
+        },
+        "type": "RollingUpdate",
+      },
+    },
+  },
+  Object {
+    "apiVersion": "networking.k8s.io/v1",
+    "kind": "NetworkPolicy",
+    "metadata": Object {
+      "name": "test-statefulset-defaultdenyall-c8b814dd",
+    },
+    "spec": Object {
+      "podSelector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-StatefulSet-c809b559",
+        },
+      },
+      "policyTypes": Array [
+        "Egress",
+        "Ingress",
+      ],
+    },
+  },
+]
+`;
diff --git a/test/cron-job.test.ts b/test/cron-job.test.ts
index 4ea39612..48a5acb6 100644
--- a/test/cron-job.test.ts
+++ b/test/cron-job.test.ts
@@ -88,4 +88,22 @@ test('custom configuration', () => {
   expect(spec.jobTemplate.spec.activeDeadlineSeconds).toEqual(60);
   expect(spec.jobTemplate.spec.backoffLimit).toEqual(4);
   expect(spec.jobTemplate.spec.template.spec.containers[0].image).toEqual('image');
+});
+
+test('Can be isolated', () => {
+
+  const chart = Testing.chart();
+
+  new kplus.CronJob(chart, 'CronJob', {
+    containers: [{ image: 'foobar' }],
+    schedule: Cron.daily(),
+    isolate: true,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+
+  const networkPolicy = manifest[1].spec;
+  expect(networkPolicy.podSelector.matchLabels).toBeDefined;
+  expect(networkPolicy.policyTypes).toEqual(['Egress', 'Ingress']);
 });
\ No newline at end of file
diff --git a/test/daemon-set.test.ts b/test/daemon-set.test.ts
index 55c6ce29..10940441 100644
--- a/test/daemon-set.test.ts
+++ b/test/daemon-set.test.ts
@@ -94,3 +94,20 @@ test('can select by label', () => {
   expect(ds.matchLabels).toEqual(expectedSelector);
 
 });
+
+test('Can be isolated', () => {
+
+  const chart = Testing.chart();
+
+  new kplus.DaemonSet(chart, 'DaemonSet', {
+    containers: [{ image: 'foobar' }],
+    isolate: true,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+
+  const networkPolicy = manifest[1].spec;
+  expect(networkPolicy.podSelector.matchLabels).toBeDefined;
+  expect(networkPolicy.policyTypes).toEqual(['Egress', 'Ingress']);
+});
\ No newline at end of file
diff --git a/test/deployment.test.ts b/test/deployment.test.ts
index b4e015ac..67b333d0 100644
--- a/test/deployment.test.ts
+++ b/test/deployment.test.ts
@@ -33,6 +33,25 @@ test('A label selector is automatically allocated', () => {
 
 });
 
+
+test('Can be isolated', () => {
+
+  const chart = Testing.chart();
+
+  new kplus.Deployment(chart, 'Deployment', {
+    containers: [{ image: 'foobar' }],
+    replicas: 5,
+    isolate: true,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+
+  const networkPolicy = manifest[1].spec;
+  expect(networkPolicy.podSelector.matchLabels).toBeDefined;
+  expect(networkPolicy.policyTypes).toEqual(['Egress', 'Ingress']);
+});
+
 test('No selector is generated if "select" is false', () => {
 
   const chart = Testing.chart();
diff --git a/test/job.test.ts b/test/job.test.ts
index f00e6b01..0768e78b 100644
--- a/test/job.test.ts
+++ b/test/job.test.ts
@@ -91,3 +91,20 @@ test('Synthesizes spec lazily', () => {
   expect(container.image).toEqual('image');
 
 });
+
+test('Can be isolated', () => {
+
+  const chart = Testing.chart();
+
+  new kplus.Job(chart, 'Job', {
+    containers: [{ image: 'foobar' }],
+    isolate: true,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+
+  const networkPolicy = manifest[1].spec;
+  expect(networkPolicy.podSelector.matchLabels).toBeDefined;
+  expect(networkPolicy.policyTypes).toEqual(['Egress', 'Ingress']);
+});
\ No newline at end of file
diff --git a/test/pod.test.ts b/test/pod.test.ts
index 29f6e30b..32bab00a 100644
--- a/test/pod.test.ts
+++ b/test/pod.test.ts
@@ -781,6 +781,22 @@ describe('connections |', () => {
 
   });
 
+  test('can isolate pod', () => {
+    const chart = Testing.chart();
+
+    new kplus.Pod(chart, 'Pod', {
+      containers: [{ image: 'pod' }],
+      isolate: true,
+    });
+
+    const manifest = Testing.synth(chart);
+    expect(manifest).toMatchSnapshot();
+
+    const networkPolicy = manifest[1].spec;
+    expect(networkPolicy.podSelector.matchLabels).toBeDefined;
+    expect(networkPolicy.policyTypes).toEqual(['Egress', 'Ingress']);
+  });
+
   test('can allow to managed pod', () => {
 
     const chart = Testing.chart();
diff --git a/test/statefulset.test.ts b/test/statefulset.test.ts
index 6a84eb3b..96f71d8b 100644
--- a/test/statefulset.test.ts
+++ b/test/statefulset.test.ts
@@ -190,4 +190,26 @@ test('custom update strategy', () => {
   expect(set.strategy).toEqual(kplus.StatefulSetUpdateStrategy.onDelete());
   expect(spec.updateStrategy).toEqual({ type: 'OnDelete' });
 
+});
+
+test('Can be isolated', () => {
+
+  const chart = Testing.chart();
+
+  const service = new kplus.Service(chart, 'Service', {
+    ports: [{ port: 80 }],
+  });
+
+  new kplus.StatefulSet(chart, 'StatefulSet', {
+    containers: [{ image: 'foobar' }],
+    service: service,
+    isolate: true,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+
+  const networkPolicy = manifest[2].spec;
+  expect(networkPolicy.podSelector.matchLabels).toBeDefined;
+  expect(networkPolicy.policyTypes).toEqual(['Egress', 'Ingress']);
 });
\ No newline at end of file