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

feat: investigate and restrict network policies #719

Merged
merged 12 commits into from
Sep 10, 2024
17 changes: 16 additions & 1 deletion src/authservice/chart/templates/uds-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,23 @@ spec:

# Egress must be allowed to the external facing Keycloak endpoint
- direction: Egress
remoteSelector:
app: tenant-ingressgateway
remoteNamespace: istio-tenant-gateway
description: "SSO Provider"

{{- if .Values.redis.uri }}
- direction: Egress
description: Redis Session Store
{{- if .Values.redis.internal.enabled }}
remoteSelector: {{ .Values.redis.internal.remoteSelector }}
remoteNamespace: {{ .Values.redis.internal.remoteNamespace }}
{{- else if .Values.redis.egressCidr }}
remoteCidr: {{ .Values.redis.egressCidr }}
{{- else }}
remoteGenerated: Anywhere
description: "SSO Provider & Redis Session Store"
{{- end }}
{{- end }}

- direction: Ingress
selector:
Expand Down
8 changes: 8 additions & 0 deletions src/authservice/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ image:

nameOverride: "authservice"

redis:
uri: "###ZARF_VAR_AUTHSERVICE_REDIS_URI###"
egressCidr: ""
internal:
enabled: false
remoteSelector: {}
remoteNamespace: ""

podAnnotations: {}

podSecurityContext: {}
Expand Down
28 changes: 18 additions & 10 deletions src/grafana/chart/templates/uds-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,31 @@ spec:
targetPort: 3000

allow:
- direction: Ingress
# Egress allowed to Loki
- direction: Egress
selector:
app.kubernetes.io/name: grafana
remoteNamespace: tempo
remoteNamespace: loki
remoteSelector:
app.kubernetes.io/name: tempo
port: 9090
description: "Tempo Datasource"
app.kubernetes.io/name: loki
description: "Loki Datasource"
port: 8080

# Egress allowed to Prometheus
- direction: Egress
selector:
app.kubernetes.io/name: grafana
remoteGenerated: Anywhere
remoteNamespace: monitoring
remoteSelector:
app.kubernetes.io/name: prometheus
description: "Prometheus Datasource"
port: 9090

# Egress allowed to Keyclaok
- direction: Egress
remoteNamespace: tempo
selector:
app.kubernetes.io/name: grafana
remoteNamespace: keycloak
remoteSelector:
app.kubernetes.io/name: tempo
port: 9411
description: "Tempo"
app.kubernetes.io/name: keycloak
description: "SSO Provider"
2 changes: 1 addition & 1 deletion src/keycloak/chart/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Check external PostgreSQL connection information. Fails when required values are
{{- else -}}{{fail "You must define \"username\", \"password\", \"database\", \"host\", and \"port\" for \"postgresql\"."}}
{{- end -}}
{{- default "true" "" }}
{{- else if not (empty (compact (values (omit .Values.postgresql "port")))) -}}
{{- else if not (empty (compact (values (omit .Values.postgresql "port" "internal")))) -}}
{{ fail "Cannot use an external PostgreSQL Database when devMode is enabled." -}}
{{- else -}}
{{ default "false" "" }}
Expand Down
11 changes: 10 additions & 1 deletion src/keycloak/chart/templates/uds-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ spec:
port: 8080

# Temp workaround for any cluster pod
# @todo: remove this once cluster pods is a remote generated target
# todo: remove this once cluster pods is a remote generated target
- description: "Keycloak backchannel access"
direction: Ingress
selector:
app.kubernetes.io/name: keycloak
remoteGenerated: Anywhere
port: 8080

UnicornChance marked this conversation as resolved.
Show resolved Hide resolved
# Keycloak OCSP to check certs cannot guarantee a static IP
- description: "OCSP Lookup"
direction: Egress
selector:
Expand All @@ -58,8 +59,16 @@ spec:
selector:
app.kubernetes.io/name: keycloak
port: {{ .Values.postgresql.port }}
{{- if .Values.postgresql.internal.enabled }}
remoteSelector: {{ .Values.postgresql.internal.remoteSelector }}
remoteNamespace: {{ .Values.postgresql.internal.remoteNamespace }}
{{- else if .Values.postgresql.egressCidr }}
remoteCidr: {{ .Values.postgresql.egressCidr }}
{{- else }}
remoteGenerated: Anywhere
{{- end }}
{{- end }}

{{- if .Values.autoscaling.enabled }}
# HA for keycloak
- direction: Ingress
Expand Down
6 changes: 6 additions & 0 deletions src/keycloak/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,12 @@ postgresql:
host: ""
# Port the database is listening on
port: 5432
egressCidr: ""
# Configure internal postgresql deployment, requires keycloak not be deployed in dev-mode
internal:
enabled: false
remoteSelector: {}
remoteNamespace: ""

serviceMonitor:
# If `true`, a ServiceMonitor resource for the prometheus-operator is created
Expand Down
17 changes: 9 additions & 8 deletions src/loki/chart/templates/uds-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,16 @@ spec:
- 8080
description: "Promtail Log Storage"

# Todo: wide open for now for pushing to s3
# Egress for S3 connections
- direction: Egress
selector:
app.kubernetes.io/name: loki
description: Storage
{{- if .Values.storage.internal.enabled }}
remoteSelector: {{ .Values.storage.internal.remoteSelector }}
remoteNamespace: {{ .Values.storage.internal.remoteNamespace }}
{{- else if .Values.storage.egressCidr }}
remoteCidr: {{ .Values.storage.egressCidr }}
{{- else }}
remoteGenerated: Anywhere

- direction: Egress
remoteNamespace: tempo
remoteSelector:
app.kubernetes.io/name: tempo
port: 9411
description: "Tempo"
{{- end }}
6 changes: 6 additions & 0 deletions src/loki/chart/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
storage:
internal:
enabled: false
remoteSelector: {}
remoteNamespace: ""
egressCidr: ""
12 changes: 4 additions & 8 deletions src/neuvector/chart/templates/uds-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@ spec:

# Access to SSO for OIDC
- direction: Egress
remoteGenerated: Anywhere
selector:
app: neuvector-controller-pod
remoteSelector:
app: tenant-ingressgateway
remoteNamespace: istio-tenant-gateway
description: "SSO Provider"

- direction: Egress
remoteGenerated: KubeAPI
Expand All @@ -79,10 +82,3 @@ spec:
app: neuvector-controller-pod
port: 30443
description: "Webhook"

- direction: Egress
remoteNamespace: tempo
remoteSelector:
app.kubernetes.io/name: tempo
port: 9411
description: "Tempo"
7 changes: 0 additions & 7 deletions src/pepr/operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,6 @@ spec:
app.kubernetes.io/name: grafana
remoteGenerated: Anywhere

- direction: Egress
remoteNamespace: tempo
remoteSelector:
app.kubernetes.io/name: tempo
port: 9411
description: "Tempo"

# SSO allows for the creation of Keycloak clients and with automatic secret generation
sso:
- name: Grafana Dashboard
Expand Down
58 changes: 58 additions & 0 deletions src/pepr/operator/controllers/network/generate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,61 @@ describe("network policy generate", () => {
policyTypes: ["Egress"],
} as kind.NetworkPolicy["spec"]);
});

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

expect(policy.metadata?.name).toEqual("Egress-test");
expect(policy.spec).toEqual({
egress: [
{
to: [
{
ipBlock: {
cidr: "192.168.0.0/16",
except: ["169.254.169.254/32"], // Include the except field here
},
},
],
ports: [],
},
],
podSelector: { matchLabels: { app: "test" } },
policyTypes: ["Egress"],
} as kind.NetworkPolicy["spec"]);
});

it("should generate correct network policy with remoteCidr for Ingress", async () => {
const policy = generate("test", {
description: "test",
direction: Direction.Ingress,
selector: { app: "test" },
remoteCidr: "10.0.0.0/8",
});

expect(policy.metadata?.name).toEqual("Ingress-test");
expect(policy.spec).toEqual({
ingress: [
{
from: [
{
ipBlock: {
cidr: "10.0.0.0/8",
except: ["169.254.169.254/32"], // Include the except field here
},
},
],
ports: [],
},
],
podSelector: { matchLabels: { app: "test" } },
policyTypes: ["Ingress"],
} as kind.NetworkPolicy["spec"]);
});
});
3 changes: 3 additions & 0 deletions src/pepr/operator/controllers/network/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { anywhere } from "./generators/anywhere";
import { cloudMetadata } from "./generators/cloudMetadata";
import { intraNamespace } from "./generators/intraNamespace";
import { kubeAPI } from "./generators/kubeAPI";
import { remoteCidr } from "./generators/remoteCidr";

function isWildcardNamespace(namespace: string) {
return namespace === "" || namespace === "*";
Expand Down Expand Up @@ -52,6 +53,8 @@ function getPeers(policy: Allow): V1NetworkPolicyPeer[] {
}

peers.push(peer);
} else if (policy.remoteCidr !== undefined) {
peers = [remoteCidr(policy.remoteCidr)];
UnicornChance marked this conversation as resolved.
Show resolved Hide resolved
}

return peers;
Expand Down
12 changes: 12 additions & 0 deletions src/pepr/operator/controllers/network/generators/remoteCidr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { V1NetworkPolicyPeer } from "@kubernetes/client-node";
import { META_IP } from "./cloudMetadata";

/** Matches a specific custom cidr EXCEPT the Cloud Meta endpoint */
export function remoteCidr(cidr: string): V1NetworkPolicyPeer {
return {
ipBlock: {
cidr,
except: [META_IP],
},
};
}
4 changes: 4 additions & 0 deletions src/pepr/operator/crd/generated/package-v1alpha1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ export interface Allow {
* A list of ports to allow (protocol is always TCP)
*/
ports?: number[];
/**
* Custom generated policy CIDR
*/
remoteCidr?: string;
/**
* Custom generated remote selector for the policy
*/
Expand Down
4 changes: 4 additions & 0 deletions src/pepr/operator/crd/sources/package/v1alpha1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ const allow = {
type: "string",
enum: ["KubeAPI", "IntraNamespace", "CloudMetadata", "Anywhere"],
},
remoteCidr: {
description: "Custom generated policy CIDR",
type: "string",
},
port: {
description: "The port to allow (protocol is always TCP)",
minimum: 1,
Expand Down
31 changes: 28 additions & 3 deletions src/pepr/operator/crd/validators/package-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,34 @@ export async function validator(req: PeprValidateRequest<UDSPackage>) {
const networkPolicyNames = new Set<string>();

for (const policy of networkPolicy) {
// remoteGenerated cannot be combined with remoteNamespace or remoteSelector
if (policy.remoteGenerated && (policy.remoteNamespace || policy.remoteSelector)) {
return req.Deny("remoteGenerated cannot be combined with remoteNamespace or remoteSelector");
// If 'remoteGenerated' is set, it cannot be combined with 'remoteNamespace', 'remoteSelector', or 'remoteCidr'.
if (
policy.remoteGenerated &&
(policy.remoteNamespace || policy.remoteSelector || policy.remoteCidr)
) {
return req.Deny(
"remoteGenerated cannot be combined with remoteNamespace, remoteSelector, or remoteCidr",
);
}

// If either 'remoteNamespace' or 'remoteSelector' is set, they cannot be combined with 'remoteGenerated' or 'remoteCidr'.
if (
(policy.remoteNamespace || policy.remoteSelector) &&
(policy.remoteGenerated || policy.remoteCidr)
) {
return req.Deny(
"remoteNamespace and remoteSelector cannot be combined with remoteGenerated or remoteCidr",
);
}

// If 'remoteCidr' is set, it cannot be combined with 'remoteGenerated', 'remoteNamespace', or 'remoteSelector'.
if (
policy.remoteCidr &&
(policy.remoteGenerated || policy.remoteNamespace || policy.remoteSelector)
) {
return req.Deny(
"remoteCidr cannot be combined with remoteGenerated, remoteNamespace, or remoteSelector",
);
}

// Ensure the policy name is unique
Expand Down
10 changes: 2 additions & 8 deletions src/prometheus-stack/chart/templates/uds-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ spec:
port: 10250
description: "Webhook"

# todo: lockdown egress to scrape targets
# Prometheus scrape targets
- direction: Egress
remoteNamespace: ""
remoteNamespace: "" # todo: restrict this overly permissive netpol
selector:
app.kubernetes.io/name: prometheus
description: "Metrics Scraping"
Expand All @@ -62,9 +62,3 @@ spec:
port: 9090
description: "Grafana Metrics Queries"

- direction: Egress
remoteNamespace: tempo
remoteSelector:
app.kubernetes.io/name: tempo
port: 9411
description: "Tempo"
7 changes: 0 additions & 7 deletions src/promtail/chart/templates/uds-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,6 @@ spec:
app.kubernetes.io/name: promtail
remoteGenerated: KubeAPI

- direction: Egress
remoteNamespace: tempo
remoteSelector:
app.kubernetes.io/name: tempo
port: 9411
description: "Tempo"

- direction: Egress
selector:
app.kubernetes.io/name: promtail
Expand Down
Loading
Loading