Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: address network policy generation inter-namespace bug #564

Merged
merged 13 commits into from
Aug 1, 2024
Merged
95 changes: 95 additions & 0 deletions src/pepr/operator/controllers/network/generate.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { describe, expect, it } from "@jest/globals";
import { kind } from "pepr";
import { Direction } from "../../crd";
import { generate } from "./generate";

describe("network policy generate", () => {
it("should generate correct network policy", async () => {
const policy = generate("test", {
description: "test",
direction: Direction.Ingress,
selector: { app: "test" },
remoteNamespace: "foo",
remoteSelector: { app: "foo" },
});

expect(policy.metadata?.name).toEqual("Ingress-test");
expect(policy.spec).toEqual({
ingress: [
{
from: [
{
namespaceSelector: {
matchLabels: {
"kubernetes.io/metadata.name": "foo",
},
},
podSelector: {
matchLabels: {
app: "foo",
},
},
},
],
ports: [],
},
],
podSelector: { matchLabels: { app: "test" } },
policyTypes: ["Ingress"],
} as kind.NetworkPolicy["spec"]);
});
});

describe("network policy generate", () => {
it("should generate correct network policy for just remoteNamespace", async () => {
const policy = generate("test", {
description: "test",
direction: Direction.Ingress,
selector: { app: "test" },
remoteNamespace: "foo",
});

expect(policy.metadata?.name).toEqual("Ingress-test");
expect(policy.spec).toEqual({
ingress: [
{
from: [
{
namespaceSelector: {
matchLabels: {
"kubernetes.io/metadata.name": "foo",
},
},
},
],
ports: [],
},
],
podSelector: { matchLabels: { app: "test" } },
policyTypes: ["Ingress"],
} as kind.NetworkPolicy["spec"]);
});
});

describe("network policy generate", () => {
it("should generate correct network policy for just remoteNamespace", async () => {
const policy = generate("test", {
description: "test",
direction: Direction.Egress,
selector: { app: "test" },
remoteNamespace: "",
});

expect(policy.metadata?.name).toEqual("Egress-test");
expect(policy.spec).toEqual({
egress: [
{
ports: [],
to: [{ namespaceSelector: {} }],
},
],
podSelector: { matchLabels: { app: "test" } },
policyTypes: ["Egress"],
} as kind.NetworkPolicy["spec"]);
});
});
99 changes: 47 additions & 52 deletions src/pepr/operator/controllers/network/generate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { V1LabelSelector, V1NetworkPolicyPeer, V1NetworkPolicyPort } from "@kubernetes/client-node";
import { V1NetworkPolicyPeer, V1NetworkPolicyPort } from "@kubernetes/client-node";
import { kind } from "pepr";

import { Allow, RemoteGenerated } from "../../crd";
Expand All @@ -7,6 +7,50 @@ import { cloudMetadata } from "./generators/cloudMetadata";
import { intraNamespace } from "./generators/intraNamespace";
import { kubeAPI } from "./generators/kubeAPI";

function getPeers(policy: Allow): V1NetworkPolicyPeer[] {
mjnagel marked this conversation as resolved.
Show resolved Hide resolved
let peers: V1NetworkPolicyPeer[] = [];

if (policy.remoteGenerated) {
switch (policy.remoteGenerated) {
case RemoteGenerated.KubeAPI:
peers = kubeAPI();
break;

case RemoteGenerated.CloudMetadata:
peers = cloudMetadata;
break;

case RemoteGenerated.IntraNamespace:
peers = [intraNamespace];
break;

case RemoteGenerated.Anywhere:
peers = [anywhere];
break;
}
} else if (policy.remoteNamespace !== undefined || policy.remoteSelector !== undefined) {
mjnagel marked this conversation as resolved.
Show resolved Hide resolved
const peer: V1NetworkPolicyPeer = {};

if (policy.remoteNamespace !== "") {
peer.namespaceSelector = {
matchLabels: { "kubernetes.io/metadata.name": policy.remoteNamespace || "" },
};
} else if (policy.remoteNamespace === "") {
mjnagel marked this conversation as resolved.
Show resolved Hide resolved
peer.namespaceSelector = {};
}

if (policy.remoteSelector !== undefined) {
peer.podSelector = {
matchLabels: policy.remoteSelector,
};
}

peers.push(peer);
}

return peers;
}

export function generate(namespace: string, policy: Allow): kind.NetworkPolicy {
// Generate a unique name for the NetworkPolicy
const name = generateName(policy);
Expand Down Expand Up @@ -35,57 +79,8 @@ export function generate(namespace: string, policy: Allow): kind.NetworkPolicy {
};
}

// Create the remote (peer) to match against
let peers: V1NetworkPolicyPeer[] = [];

// Add the remoteNamespace if they exist
if (policy.remoteNamespace !== undefined) {
const namespaceSelector: V1LabelSelector = {};

// Add the remoteNamespace to the namespaceSelector if it exists and is not "*", otherwise match all namespaces
if (policy.remoteNamespace !== "" && policy.remoteNamespace !== "*") {
namespaceSelector.matchLabels = {
"kubernetes.io/metadata.name": policy.remoteNamespace,
};
}

// Add the remoteNamespace to the peers
peers.push({ namespaceSelector });
}

// Add the remoteSelector if they exist
if (policy.remoteSelector) {
peers.push({
podSelector: {
matchLabels: policy.remoteSelector,
},
});
}

// Check if remoteGenerated is set
if (policy.remoteGenerated) {
// Add the remoteGenerated label
generated.metadata!.labels!["uds/generated"] = policy.remoteGenerated;

// Check if remoteGenerated is set
switch (policy.remoteGenerated) {
case RemoteGenerated.KubeAPI:
peers = kubeAPI();
break;

case RemoteGenerated.CloudMetadata:
peers = cloudMetadata;
break;

case RemoteGenerated.IntraNamespace:
peers.push(intraNamespace);
break;

case RemoteGenerated.Anywhere:
peers = [anywhere];
break;
}
}
// Create the network policy peers
const peers: V1NetworkPolicyPeer[] = getPeers(policy);

// Define the ports to allow from the ports property
const ports: V1NetworkPolicyPort[] = (policy.ports ?? []).map(port => ({ port }));
Expand Down