Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ function authserviceAuthorizationPolicy(
notValues: ["*"],
},
],
to: [
{
operation: {
notPorts: ["15020"],
notPaths: ["/stats/prometheus"],
},
},
],
},
],
selector: {
Expand Down Expand Up @@ -81,6 +89,14 @@ function jwtAuthZAuthorizationPolicy(
},
},
],
to: [
{
operation: {
notPorts: ["15020"],
notPaths: ["/stats/prometheus"],
},
},
],
},
],
},
Expand Down
105 changes: 91 additions & 14 deletions src/pepr/operator/controllers/network/authorizationPolicies.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { Direction, Gateway, RemoteGenerated, UDSPackage } from "../../crd";
import { Action, AuthorizationPolicy } from "../../crd/generated/istio/authorizationpolicy-v1beta1";
import { IstioState } from "../istio/namespace";
import { generateAuthorizationPolicies } from "./authorizationPolicies";

jest.mock("../../../logger", () => ({
Expand Down Expand Up @@ -58,7 +59,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "test-ns");
const policies = await generateAuthorizationPolicies(pkg, "test-ns", IstioState.Ambient);
expect(policies.length).toBe(1);
const policy = policies[0];
expect(policy.metadata?.name).toBe(
Expand All @@ -85,7 +86,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "test-ns");
const policies = await generateAuthorizationPolicies(pkg, "test-ns", IstioState.Ambient);
expect(policies.length).toBe(1);
const policy = policies[0];
expect(policy.metadata?.name).toBe("protect-kubeapi-test-ingress-kubeapi-test-kubeapi");
Expand All @@ -110,7 +111,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "test-ns");
const policies = await generateAuthorizationPolicies(pkg, "test-ns", IstioState.Ambient);
expect(policies.length).toBe(1);
const policy = policies[0];
expect(policy.metadata?.name).toBe("protect-kubenodes-test-ingress-kubenodes-test-kubenodes");
Expand Down Expand Up @@ -138,6 +139,7 @@ describe("authorization policy generation", () => {
const policies: AuthorizationPolicy[] = await generateAuthorizationPolicies(
pkg,
"curl-ns-remote-cidr",
IstioState.Ambient,
);
expect(policies).toHaveLength(1);
const policy = policies[0];
Expand Down Expand Up @@ -201,7 +203,11 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "authservice-test-app");
const policies = await generateAuthorizationPolicies(
pkg,
"authservice-test-app",
IstioState.Ambient,
);
// We expect exactly two policies: one for the expose rule and one for the allow rule.
expect(policies.length).toBe(2);

Expand Down Expand Up @@ -256,7 +262,11 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "test-tenant-app");
const policies = await generateAuthorizationPolicies(
pkg,
"test-tenant-app",
IstioState.Ambient,
);
expect(policies.length).toBe(2);
const names = policies.map(p => p.metadata?.name);
expect(new Set(names).size).toBe(2);
Expand All @@ -280,7 +290,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "loki");
const policies = await generateAuthorizationPolicies(pkg, "loki", IstioState.Ambient);
// With one allow rule (Ingress/IntraNamespace), expect one policy
expect(policies.length).toBe(1);
const policy = policies[0];
Expand Down Expand Up @@ -322,7 +332,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "neuvector");
const policies = await generateAuthorizationPolicies(pkg, "neuvector", IstioState.Ambient);
// With the current per-rule design we expect three policies
expect(policies.length).toBe(3);

Expand Down Expand Up @@ -394,7 +404,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "vector");
const policies = await generateAuthorizationPolicies(pkg, "vector", IstioState.Ambient);
expect(policies.length).toBe(1);
const policy = policies[0];
expect(policy.metadata?.name).toBe("protect-vector-ingress-prometheus-metrics");
Expand Down Expand Up @@ -429,7 +439,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "velero");
const policies = await generateAuthorizationPolicies(pkg, "velero", IstioState.Ambient);
// Expect one policy
expect(policies.length).toBe(1);
const policy = policies[0];
Expand All @@ -447,7 +457,7 @@ describe("authorization policy generation", () => {
);
});

test("should generate correct AuthorizationPolicies for Authservice", async () => {
test("should generate correct AuthorizationPolicies for Ambient Authservice", async () => {
const pkg: UDSPackage = {
metadata: { name: "authservice", namespace: "authservice", generation: 1 },
spec: {
Expand All @@ -467,7 +477,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "authservice");
const policies = await generateAuthorizationPolicies(pkg, "authservice", IstioState.Ambient);
// Expect two policies
expect(policies.length).toBe(2);
const nsPolicy = policies.find(
Expand All @@ -493,6 +503,73 @@ describe("authorization policy generation", () => {
);
});

test("should generate correct AuthorizationPolicies for Sidecar Authservice", async () => {
const pkg: UDSPackage = {
metadata: { name: "authservice", namespace: "authservice", generation: 1 },
spec: {
network: {
allow: [
{ direction: Direction.Ingress, remoteGenerated: RemoteGenerated.IntraNamespace },
{ direction: Direction.Egress, remoteGenerated: RemoteGenerated.IntraNamespace },
{
direction: Direction.Ingress,
selector: { "app.kubernetes.io/name": "authservice" },
remoteNamespace: "",
port: 10003,
description: "Protected Apps",
},
],
},
},
};

const policies = await generateAuthorizationPolicies(pkg, "authservice", IstioState.Sidecar);
// Expect three policies
expect(policies.length).toBe(3);
const nsPolicy = policies.find(
p => p.metadata?.name === "protect-authservice-ingress-all-pods-intranamespace",
);
expect(nsPolicy).toBeDefined();
expect(nsPolicy!.metadata?.namespace).toBe("authservice");
expect(nsPolicy!.spec?.action).toBe(Action.Allow);
expect(nsPolicy!.spec?.rules).toEqual(
expect.arrayContaining([{ from: [{ source: { namespaces: ["authservice"] } }] }]),
);

const workloadPolicy = policies.find(
p => p.metadata?.name === "protect-authservice-ingress-protected-apps",
);
expect(workloadPolicy).toBeDefined();
expect(workloadPolicy!.spec?.selector?.matchLabels).toEqual({
"app.kubernetes.io/name": "authservice",
});
expect(workloadPolicy!.spec?.action).toBe(Action.Allow);
expect(workloadPolicy!.spec?.rules).toEqual(
expect.arrayContaining([{ to: [{ operation: { ports: ["10003"] } }] }]),
);

const metricScrapingPolicy = policies.find(
p => p.metadata?.name === "protect-authservice-ingress-15020-metric-scraping",
);
expect(metricScrapingPolicy).toBeDefined();
expect(metricScrapingPolicy!.spec?.selector?.matchLabels).toEqual({});
expect(metricScrapingPolicy!.spec?.action).toBe(Action.Allow);
expect(metricScrapingPolicy!.spec?.rules).toEqual(
expect.arrayContaining([
{
from: [
{
source: {
principals: ["cluster.local/ns/monitoring/sa/kube-prometheus-stack-prometheus"],
},
},
],
to: [{ operation: { ports: ["15020"] } }],
},
]),
);
});

test("should generate correct AuthorizationPolicies for Prometheus-Stack", async () => {
const pkg: UDSPackage = {
metadata: { name: "prometheus-stack", namespace: "monitoring", generation: 1 },
Expand All @@ -519,7 +596,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "monitoring");
const policies = await generateAuthorizationPolicies(pkg, "monitoring", IstioState.Ambient);
// Expect three policies
expect(policies.length).toBe(3);
const nsPolicy = policies.find(
Expand Down Expand Up @@ -597,7 +674,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "grafana");
const policies = await generateAuthorizationPolicies(pkg, "grafana", IstioState.Ambient);
// Expect three policies: one from expose, one from allow, and one monitor policy
expect(policies.length).toBe(3);
const exposePolicy = policies.find(
Expand Down Expand Up @@ -763,7 +840,7 @@ describe("authorization policy generation", () => {
},
};

const policies = await generateAuthorizationPolicies(pkg, "keycloak");
const policies = await generateAuthorizationPolicies(pkg, "keycloak", IstioState.Ambient);
// We expect 6 policies
expect(policies.length).toBe(6);

Expand Down
21 changes: 21 additions & 0 deletions src/pepr/operator/controllers/network/authorizationPolicies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Rule,
Source,
} from "../../crd/generated/istio/authorizationpolicy-v1beta1";
import { IstioState } from "../istio/namespace";
import { getOwnerRef, purgeOrphans, sanitizeResourceName } from "../utils";
import { META_IP } from "./generators/cloudMetadata";
import { kubeAPI } from "./generators/kubeAPI";
Expand Down Expand Up @@ -206,6 +207,7 @@ function buildAuthPolicy(
export async function generateAuthorizationPolicies(
pkg: UDSPackage,
pkgNamespace: string,
istioMode: string,
): Promise<AuthorizationPolicy[]> {
const pkgName = pkg.metadata?.name ?? "unknown";
const generation = pkg.metadata?.generation?.toString() ?? "0";
Expand Down Expand Up @@ -261,6 +263,25 @@ export async function generateAuthorizationPolicies(
}
}

// In Sidecar mode, Prometheus lacks mTLS credentials, causing metric scrapes on port 15020 to fail.
// Add an AuthorizationPolicy to allow all traffic on port 15020 for the package's namespace.
Comment thread
chance-coleman marked this conversation as resolved.
Outdated
if (istioMode === IstioState.Sidecar) {
const extraPolicyName = sanitizeResourceName(
`protect-${pkgName}-ingress-15020-metric-scraping`,
Comment thread
chance-coleman marked this conversation as resolved.
Outdated
);
const extraPolicy = buildAuthPolicy(
extraPolicyName,
pkg,
{}, // empty selector to apply to all workloads in the namespace
{ principals: [PROMETHEUS_PRINCIPAL] },
["15020"],
);
policies.push(extraPolicy);
log.trace(
`Generated extra ambient allow authpol for port 15020: ${extraPolicy.metadata?.name}`,
);
}

// Apply policies concurrently.
for (const policy of policies) {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/pepr/operator/reconcilers/package-reconciler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function packageReconciler(pkg: UDSPackage) {
// Pass the effective Istio mode to the networkPolicies function
const netPol = await networkPolicies(pkg, namespace!, istioMode);

const authPol = await generateAuthorizationPolicies(pkg, namespace!);
const authPol = await generateAuthorizationPolicies(pkg, namespace!, istioMode);

let endpoints: string[] = [];
// Update the namespace to enable the expected Istio mode (sidecar or ambient)
Expand Down
Loading