pepr 0.42.2 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/build.d.ts +25 -11
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.helpers.d.ts +1 -8
- package/dist/cli/build.helpers.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +3 -0
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli.js +1292 -1247
- package/dist/controller.js +1 -1
- package/dist/fixtures/loader.d.ts.map +1 -1
- package/dist/lib/assets/assets.d.ts +30 -0
- package/dist/lib/assets/assets.d.ts.map +1 -0
- package/dist/lib/assets/deploy.d.ts +2 -2
- package/dist/lib/assets/deploy.d.ts.map +1 -1
- package/dist/lib/assets/helm.d.ts.map +1 -1
- package/dist/lib/assets/index.d.ts +4 -23
- package/dist/lib/assets/index.d.ts.map +1 -1
- package/dist/lib/assets/pods.d.ts +1 -1
- package/dist/lib/assets/pods.d.ts.map +1 -1
- package/dist/lib/assets/webhooks.d.ts +5 -2
- package/dist/lib/assets/webhooks.d.ts.map +1 -1
- package/dist/lib/assets/yaml/generateAllYaml.d.ts +9 -0
- package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -0
- package/dist/lib/assets/yaml/generateZarfYaml.d.ts +5 -0
- package/dist/lib/assets/yaml/generateZarfYaml.d.ts.map +1 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts +15 -0
- package/dist/lib/assets/yaml/overridesFile.d.ts.map +1 -0
- package/dist/lib/core/module.d.ts.map +1 -1
- package/dist/lib/enums.d.ts +4 -0
- package/dist/lib/enums.d.ts.map +1 -1
- package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
- package/dist/lib/processors/validate-processor.d.ts.map +1 -1
- package/dist/lib.js +37 -18
- package/dist/lib.js.map +4 -4
- package/package.json +5 -2
- package/src/cli/build.helpers.ts +8 -23
- package/src/cli/build.ts +73 -17
- package/src/cli/deploy.ts +8 -7
- package/src/cli/dev.ts +8 -7
- package/src/fixtures/loader.ts +2 -2
- package/src/lib/assets/assets.ts +189 -0
- package/src/lib/assets/deploy.ts +33 -31
- package/src/lib/assets/helm.ts +22 -4
- package/src/lib/assets/index.ts +40 -150
- package/src/lib/assets/pods.ts +3 -3
- package/src/lib/assets/webhooks.ts +23 -17
- package/src/lib/assets/yaml/generateAllYaml.ts +50 -0
- package/src/lib/assets/yaml/generateZarfYaml.ts +38 -0
- package/src/lib/assets/{yaml.ts → yaml/overridesFile.ts} +19 -109
- package/src/lib/core/module.ts +2 -1
- package/src/lib/enums.ts +6 -0
- package/src/lib/processors/mutate-processor.ts +2 -1
- package/src/lib/processors/validate-processor.ts +7 -1
- package/dist/lib/assets/yaml.d.ts +0 -6
- package/dist/lib/assets/yaml.d.ts.map +0 -1
package/src/lib/assets/helm.ts
CHANGED
|
@@ -99,8 +99,13 @@ export function watcherDeployTemplate(buildTimestamp: string): string {
|
|
|
99
99
|
- name: watcher
|
|
100
100
|
image: {{ .Values.watcher.image }}
|
|
101
101
|
imagePullPolicy: IfNotPresent
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
{{- if gt (len .Values.imagePullSecrets) 0 }}
|
|
103
|
+
imagePullSecrets:
|
|
104
|
+
{{- range .Values.imagePullSecrets }}
|
|
105
|
+
- name: {{ . }}
|
|
106
|
+
{{- end }}
|
|
107
|
+
{{- end }}
|
|
108
|
+
args:
|
|
104
109
|
- /app/node_modules/pepr/dist/controller.js
|
|
105
110
|
- {{ .Values.hash }}
|
|
106
111
|
readinessProbe:
|
|
@@ -115,6 +120,10 @@ export function watcherDeployTemplate(buildTimestamp: string): string {
|
|
|
115
120
|
{{- toYaml .Values.watcher.env | nindent 12 }}
|
|
116
121
|
- name: PEPR_WATCH_MODE
|
|
117
122
|
value: "true"
|
|
123
|
+
{{- if .Values.additionalIgnoredNamespaces }}
|
|
124
|
+
- name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
|
|
125
|
+
value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
|
|
126
|
+
{{- end }}
|
|
118
127
|
envFrom:
|
|
119
128
|
{{- toYaml .Values.watcher.envFrom | nindent 12 }}
|
|
120
129
|
securityContext:
|
|
@@ -179,8 +188,13 @@ export function admissionDeployTemplate(buildTimestamp: string): string {
|
|
|
179
188
|
- name: server
|
|
180
189
|
image: {{ .Values.admission.image }}
|
|
181
190
|
imagePullPolicy: IfNotPresent
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
{{- if gt (len .Values.imagePullSecrets) 0 }}
|
|
192
|
+
imagePullSecrets:
|
|
193
|
+
{{- range .Values.imagePullSecrets }}
|
|
194
|
+
- name: {{ . }}
|
|
195
|
+
{{- end }}
|
|
196
|
+
{{- end }}
|
|
197
|
+
args:
|
|
184
198
|
- /app/node_modules/pepr/dist/controller.js
|
|
185
199
|
- {{ .Values.hash }}
|
|
186
200
|
readinessProbe:
|
|
@@ -195,6 +209,10 @@ export function admissionDeployTemplate(buildTimestamp: string): string {
|
|
|
195
209
|
{{- toYaml .Values.admission.env | nindent 12 }}
|
|
196
210
|
- name: PEPR_WATCH_MODE
|
|
197
211
|
value: "false"
|
|
212
|
+
{{- if .Values.additionalIgnoredNamespaces }}
|
|
213
|
+
- name: PEPR_ADDITIONAL_IGNORED_NAMESPACES
|
|
214
|
+
value: "{{ join ", " .Values.additionalIgnoredNamespaces }}"
|
|
215
|
+
{{- end }}
|
|
198
216
|
envFrom:
|
|
199
217
|
{{- toYaml .Values.admission.envFrom | nindent 12 }}
|
|
200
218
|
securityContext:
|
package/src/lib/assets/index.ts
CHANGED
|
@@ -1,57 +1,59 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
3
|
|
|
4
|
-
import crypto from "crypto";
|
|
5
4
|
import { dumpYaml } from "@kubernetes/client-node";
|
|
6
5
|
import { kind } from "kubernetes-fluent-client";
|
|
7
|
-
import {
|
|
8
|
-
import { TLSOut, genTLS } from "../tls";
|
|
9
|
-
import { CapabilityExport } from "../types";
|
|
10
|
-
import { WebhookIgnore } from "../k8s";
|
|
11
|
-
import { deploy } from "./deploy";
|
|
12
|
-
import { loadCapabilities } from "./loader";
|
|
13
|
-
import { allYaml, zarfYaml, overridesFile, zarfYamlChart } from "./yaml";
|
|
14
|
-
import { namespaceComplianceValidator, replaceString } from "../helpers";
|
|
15
|
-
import { dedent } from "../helpers";
|
|
6
|
+
import { replaceString } from "../helpers";
|
|
16
7
|
import { resolve } from "path";
|
|
17
|
-
import {
|
|
18
|
-
chartYaml,
|
|
19
|
-
namespaceTemplate,
|
|
20
|
-
admissionDeployTemplate,
|
|
21
|
-
watcherDeployTemplate,
|
|
22
|
-
clusterRoleTemplate,
|
|
23
|
-
serviceMonitorTemplate,
|
|
24
|
-
} from "./helm";
|
|
25
|
-
import { promises as fs } from "fs";
|
|
26
|
-
import { webhookConfig } from "./webhooks";
|
|
27
|
-
import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
|
|
28
|
-
import { getWatcher, getModuleSecret } from "./pods";
|
|
29
|
-
|
|
30
|
-
import { clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
|
|
31
|
-
import { createDirectoryIfNotExists } from "../filesystemService";
|
|
8
|
+
import { ModuleConfig } from "../core/module";
|
|
32
9
|
|
|
33
10
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
-
function toYaml(obj: any): string {
|
|
11
|
+
export function toYaml(obj: any): string {
|
|
35
12
|
return dumpYaml(obj, { noRefs: true });
|
|
36
13
|
}
|
|
37
14
|
|
|
38
|
-
|
|
39
|
-
|
|
15
|
+
// Unit Test Me!!
|
|
16
|
+
export function createWebhookYaml(
|
|
17
|
+
name: string,
|
|
18
|
+
config: ModuleConfig,
|
|
40
19
|
webhookConfiguration: kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration,
|
|
41
20
|
): string {
|
|
42
21
|
const yaml = toYaml(webhookConfiguration);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"{{ .Values.admission.failurePolicy }}",
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
22
|
+
const replacements = [
|
|
23
|
+
{ search: name, replace: "{{ .Values.uuid }}" },
|
|
24
|
+
{
|
|
25
|
+
search: config.onError === "reject" ? "Fail" : "Ignore",
|
|
26
|
+
replace: "{{ .Values.admission.failurePolicy }}",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
search: `${config.webhookTimeout}` || "10",
|
|
30
|
+
replace: "{{ .Values.admission.webhookTimeout }}",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
search: `
|
|
34
|
+
- key: kubernetes.io/metadata.name
|
|
35
|
+
operator: NotIn
|
|
36
|
+
values:
|
|
37
|
+
- kube-system
|
|
38
|
+
- pepr-system
|
|
39
|
+
`,
|
|
40
|
+
replace: `
|
|
41
|
+
- key: kubernetes.io/metadata.name
|
|
42
|
+
operator: NotIn
|
|
43
|
+
values:
|
|
44
|
+
- kube-system
|
|
45
|
+
- pepr-system
|
|
46
|
+
{{- range .Values.additionalIgnoredNamespaces }}
|
|
47
|
+
- {{ . }}
|
|
48
|
+
{{- end }}
|
|
49
|
+
`,
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
return replacements.reduce((updatedYaml, { search, replace }) => replaceString(updatedYaml, search, replace), yaml);
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
|
|
56
|
+
export function helmLayout(basePath: string, unique: string): Record<string, Record<string, string>> {
|
|
55
57
|
const helm: Record<string, Record<string, string>> = {
|
|
56
58
|
dirs: {
|
|
57
59
|
chart: resolve(`${basePath}/${unique}-chart`),
|
|
@@ -90,115 +92,3 @@ function helmLayout(basePath: string, unique: string): Record<string, Record<str
|
|
|
90
92
|
|
|
91
93
|
return helm;
|
|
92
94
|
}
|
|
93
|
-
|
|
94
|
-
export class Assets {
|
|
95
|
-
readonly name: string;
|
|
96
|
-
readonly tls: TLSOut;
|
|
97
|
-
readonly apiToken: string;
|
|
98
|
-
readonly alwaysIgnore!: WebhookIgnore;
|
|
99
|
-
capabilities!: CapabilityExport[];
|
|
100
|
-
|
|
101
|
-
image: string;
|
|
102
|
-
buildTimestamp: string;
|
|
103
|
-
hash: string;
|
|
104
|
-
|
|
105
|
-
constructor(
|
|
106
|
-
readonly config: ModuleConfig,
|
|
107
|
-
readonly path: string,
|
|
108
|
-
readonly host?: string,
|
|
109
|
-
) {
|
|
110
|
-
this.name = `pepr-${config.uuid}`;
|
|
111
|
-
this.buildTimestamp = `${Date.now()}`;
|
|
112
|
-
this.alwaysIgnore = config.alwaysIgnore;
|
|
113
|
-
this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
|
|
114
|
-
this.hash = "";
|
|
115
|
-
// Generate the ephemeral tls things
|
|
116
|
-
this.tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
|
|
117
|
-
|
|
118
|
-
// Generate the api token for the controller / webhook
|
|
119
|
-
this.apiToken = crypto.randomBytes(32).toString("hex");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
setHash = (hash: string): void => {
|
|
123
|
-
this.hash = hash;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
deploy = async (force: boolean, webhookTimeout?: number): Promise<void> => {
|
|
127
|
-
this.capabilities = await loadCapabilities(this.path);
|
|
128
|
-
await deploy(this, force, webhookTimeout);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
zarfYaml = (path: string): string => zarfYaml(this, path);
|
|
132
|
-
|
|
133
|
-
zarfYamlChart = (path: string): string => zarfYamlChart(this, path);
|
|
134
|
-
|
|
135
|
-
allYaml = async (imagePullSecret?: string): Promise<string> => {
|
|
136
|
-
this.capabilities = await loadCapabilities(this.path);
|
|
137
|
-
// give error if namespaces are not respected
|
|
138
|
-
for (const capability of this.capabilities) {
|
|
139
|
-
namespaceComplianceValidator(capability, this.alwaysIgnore?.namespaces);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return allYaml(this, imagePullSecret);
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
/* eslint max-statements: ["warn", 21] */
|
|
146
|
-
generateHelmChart = async (basePath: string): Promise<void> => {
|
|
147
|
-
const helm = helmLayout(basePath, this.config.uuid);
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
await Promise.all(
|
|
151
|
-
Object.values(helm.dirs)
|
|
152
|
-
.sort((l, r) => l.split("/").length - r.split("/").length)
|
|
153
|
-
.map(async dir => await createDirectoryIfNotExists(dir)),
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
const code = await fs.readFile(this.path);
|
|
157
|
-
|
|
158
|
-
const pairs: [string, () => string][] = [
|
|
159
|
-
[helm.files.chartYaml, (): string => dedent(chartYaml(this.config.uuid, this.config.description || ""))],
|
|
160
|
-
[helm.files.namespaceYaml, (): string => dedent(namespaceTemplate())],
|
|
161
|
-
[helm.files.watcherServiceYaml, (): string => toYaml(watcherService(this.name))],
|
|
162
|
-
[helm.files.admissionServiceYaml, (): string => toYaml(service(this.name))],
|
|
163
|
-
[helm.files.tlsSecretYaml, (): string => toYaml(tlsSecret(this.name, this.tls))],
|
|
164
|
-
[helm.files.apiTokenSecretYaml, (): string => toYaml(apiTokenSecret(this.name, this.apiToken))],
|
|
165
|
-
[helm.files.storeRoleYaml, (): string => toYaml(storeRole(this.name))],
|
|
166
|
-
[helm.files.storeRoleBindingYaml, (): string => toYaml(storeRoleBinding(this.name))],
|
|
167
|
-
[helm.files.clusterRoleYaml, (): string => dedent(clusterRoleTemplate())],
|
|
168
|
-
[helm.files.clusterRoleBindingYaml, (): string => toYaml(clusterRoleBinding(this.name))],
|
|
169
|
-
[helm.files.serviceAccountYaml, (): string => toYaml(serviceAccount(this.name))],
|
|
170
|
-
[helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code, this.hash))],
|
|
171
|
-
];
|
|
172
|
-
await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content())));
|
|
173
|
-
|
|
174
|
-
await overridesFile(this, helm.files.valuesYaml);
|
|
175
|
-
|
|
176
|
-
const [mutateWebhook, validateWebhook] = await Promise.all([
|
|
177
|
-
webhookConfig(this, "mutate", this.config.webhookTimeout),
|
|
178
|
-
webhookConfig(this, "validate", this.config.webhookTimeout),
|
|
179
|
-
]);
|
|
180
|
-
|
|
181
|
-
if (validateWebhook || mutateWebhook) {
|
|
182
|
-
await fs.writeFile(helm.files.admissionDeploymentYaml, dedent(admissionDeployTemplate(this.buildTimestamp)));
|
|
183
|
-
await fs.writeFile(helm.files.admissionServiceMonitorYaml, dedent(serviceMonitorTemplate("admission")));
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (mutateWebhook) {
|
|
187
|
-
await fs.writeFile(helm.files.mutationWebhookYaml, createWebhookYaml(this, mutateWebhook));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (validateWebhook) {
|
|
191
|
-
await fs.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this, validateWebhook));
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const watchDeployment = getWatcher(this, this.hash, this.buildTimestamp);
|
|
195
|
-
if (watchDeployment) {
|
|
196
|
-
await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp)));
|
|
197
|
-
await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher")));
|
|
198
|
-
}
|
|
199
|
-
} catch (err) {
|
|
200
|
-
console.error(`Error generating helm chart: ${err.message}`);
|
|
201
|
-
process.exit(1);
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
}
|
package/src/lib/assets/pods.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { KubernetesObject, V1EnvVar } from "@kubernetes/client-node";
|
|
|
5
5
|
import { kind } from "kubernetes-fluent-client";
|
|
6
6
|
import { gzipSync } from "zlib";
|
|
7
7
|
import { secretOverLimit } from "../helpers";
|
|
8
|
-
import { Assets } from "
|
|
8
|
+
import { Assets } from "./assets";
|
|
9
9
|
import { ModuleConfig } from "../core/module";
|
|
10
10
|
import { Binding } from "../types";
|
|
11
11
|
|
|
@@ -109,7 +109,7 @@ export function getWatcher(
|
|
|
109
109
|
name: "watcher",
|
|
110
110
|
image,
|
|
111
111
|
imagePullPolicy: "IfNotPresent",
|
|
112
|
-
|
|
112
|
+
args: ["/app/node_modules/pepr/dist/controller.js", hash],
|
|
113
113
|
readinessProbe: {
|
|
114
114
|
httpGet: {
|
|
115
115
|
path: "/healthz",
|
|
@@ -248,7 +248,7 @@ export function getDeployment(
|
|
|
248
248
|
name: "server",
|
|
249
249
|
image,
|
|
250
250
|
imagePullPolicy: "IfNotPresent",
|
|
251
|
-
|
|
251
|
+
args: ["/app/node_modules/pepr/dist/controller.js", hash],
|
|
252
252
|
readinessProbe: {
|
|
253
253
|
httpGet: {
|
|
254
254
|
path: "/healthz",
|
|
@@ -9,17 +9,11 @@ import {
|
|
|
9
9
|
import { kind } from "kubernetes-fluent-client";
|
|
10
10
|
import { concat, equals, uniqWith } from "ramda";
|
|
11
11
|
|
|
12
|
-
import { Assets } from "
|
|
13
|
-
import { Event } from "../enums";
|
|
12
|
+
import { Assets } from "./assets";
|
|
13
|
+
import { Event, WebhookType } from "../enums";
|
|
14
14
|
import { Binding } from "../types";
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
key: "pepr.dev",
|
|
18
|
-
operator: "NotIn",
|
|
19
|
-
values: ["ignore"],
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
|
|
16
|
+
export const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
|
|
23
17
|
|
|
24
18
|
const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOperations | undefined => {
|
|
25
19
|
const { event, kind, isMutate, isValidate } = binding;
|
|
@@ -45,6 +39,21 @@ const validateRule = (binding: Binding, isMutateWebhook: boolean): V1RuleWithOpe
|
|
|
45
39
|
return ruleObject;
|
|
46
40
|
};
|
|
47
41
|
|
|
42
|
+
export function resolveIgnoreNamespaces(ignoredNSConfig: string[] = []): string[] {
|
|
43
|
+
const ignoredNSEnv = process.env.PEPR_ADDITIONAL_IGNORED_NAMESPACES;
|
|
44
|
+
if (!ignoredNSEnv) {
|
|
45
|
+
return ignoredNSConfig;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const namespaces = ignoredNSEnv.split(",").map(ns => ns.trim());
|
|
49
|
+
|
|
50
|
+
// add alwaysIgnore.namespaces to the list
|
|
51
|
+
if (ignoredNSConfig) {
|
|
52
|
+
namespaces.push(...ignoredNSConfig);
|
|
53
|
+
}
|
|
54
|
+
return namespaces.filter(ns => ns.length > 0);
|
|
55
|
+
}
|
|
56
|
+
|
|
48
57
|
export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean): Promise<V1RuleWithOperations[]> {
|
|
49
58
|
const { config, capabilities } = assets;
|
|
50
59
|
|
|
@@ -59,15 +68,15 @@ export async function generateWebhookRules(assets: Assets, isMutateWebhook: bool
|
|
|
59
68
|
return uniqWith(equals, rules);
|
|
60
69
|
}
|
|
61
70
|
|
|
62
|
-
export async function
|
|
71
|
+
export async function webhookConfigGenerator(
|
|
63
72
|
assets: Assets,
|
|
64
|
-
mutateOrValidate:
|
|
73
|
+
mutateOrValidate: WebhookType,
|
|
65
74
|
timeoutSeconds = 10,
|
|
66
75
|
): Promise<kind.MutatingWebhookConfiguration | kind.ValidatingWebhookConfiguration | null> {
|
|
67
|
-
const ignore = [
|
|
76
|
+
const ignore: V1LabelSelectorRequirement[] = [];
|
|
68
77
|
|
|
69
78
|
const { name, tls, config, apiToken, host } = assets;
|
|
70
|
-
const ignoreNS = concat(peprIgnoreNamespaces, config?.alwaysIgnore?.namespaces
|
|
79
|
+
const ignoreNS = concat(peprIgnoreNamespaces, resolveIgnoreNamespaces(config?.alwaysIgnore?.namespaces));
|
|
71
80
|
|
|
72
81
|
// Add any namespaces to ignore
|
|
73
82
|
if (ignoreNS) {
|
|
@@ -97,7 +106,7 @@ export async function webhookConfig(
|
|
|
97
106
|
};
|
|
98
107
|
}
|
|
99
108
|
|
|
100
|
-
const isMutate = mutateOrValidate ===
|
|
109
|
+
const isMutate = mutateOrValidate === WebhookType.MUTATE;
|
|
101
110
|
const rules = await generateWebhookRules(assets, isMutate);
|
|
102
111
|
|
|
103
112
|
// If there are no rules, return null
|
|
@@ -120,9 +129,6 @@ export async function webhookConfig(
|
|
|
120
129
|
namespaceSelector: {
|
|
121
130
|
matchExpressions: ignore,
|
|
122
131
|
},
|
|
123
|
-
objectSelector: {
|
|
124
|
-
matchExpressions: ignore,
|
|
125
|
-
},
|
|
126
132
|
rules,
|
|
127
133
|
// @todo: track side effects state
|
|
128
134
|
sideEffects: "None",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import crypto from "crypto";
|
|
5
|
+
import { Assets } from "../assets";
|
|
6
|
+
import { WebhookType } from "../../enums";
|
|
7
|
+
import { apiTokenSecret, service, tlsSecret, watcherService } from "../networking";
|
|
8
|
+
import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "../rbac";
|
|
9
|
+
import { dumpYaml, V1Deployment } from "@kubernetes/client-node";
|
|
10
|
+
import { getModuleSecret, getNamespace } from "../pods";
|
|
11
|
+
import { promises as fs } from "fs";
|
|
12
|
+
import { webhookConfigGenerator } from "../webhooks";
|
|
13
|
+
|
|
14
|
+
type deployments = { default: V1Deployment; watch: V1Deployment | null };
|
|
15
|
+
|
|
16
|
+
export async function generateAllYaml(assets: Assets, deployments: deployments): Promise<string> {
|
|
17
|
+
const { name, tls, apiToken, path, config } = assets;
|
|
18
|
+
const code = await fs.readFile(path);
|
|
19
|
+
const hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
20
|
+
|
|
21
|
+
const resources = [
|
|
22
|
+
getNamespace(assets.config.customLabels?.namespace),
|
|
23
|
+
clusterRole(name, assets.capabilities, config.rbacMode, config.rbac),
|
|
24
|
+
clusterRoleBinding(name),
|
|
25
|
+
serviceAccount(name),
|
|
26
|
+
apiTokenSecret(name, apiToken),
|
|
27
|
+
tlsSecret(name, tls),
|
|
28
|
+
deployments.default,
|
|
29
|
+
service(name),
|
|
30
|
+
watcherService(name),
|
|
31
|
+
getModuleSecret(name, code, hash),
|
|
32
|
+
storeRole(name),
|
|
33
|
+
storeRoleBinding(name),
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const webhooks = {
|
|
37
|
+
mutate: await webhookConfigGenerator(assets, WebhookType.MUTATE, assets.config.webhookTimeout),
|
|
38
|
+
validate: await webhookConfigGenerator(assets, WebhookType.VALIDATE, assets.config.webhookTimeout),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Add webhooks and watch deployment if they exist
|
|
42
|
+
const additionalResources = [webhooks.mutate, webhooks.validate, deployments.watch].filter(
|
|
43
|
+
resource => resource !== null && resource !== undefined,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
resources.push(...additionalResources);
|
|
47
|
+
|
|
48
|
+
// Convert the resources to a single YAML string
|
|
49
|
+
return resources.map(resource => dumpYaml(resource, { noRefs: true })).join("---\n");
|
|
50
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { dumpYaml } from "@kubernetes/client-node";
|
|
2
|
+
import { Assets } from "../assets";
|
|
3
|
+
|
|
4
|
+
type ConfigType = "manifests" | "charts";
|
|
5
|
+
|
|
6
|
+
export function generateZarfYamlGeneric(assets: Assets, path: string, type: ConfigType): string {
|
|
7
|
+
const manifestSettings = {
|
|
8
|
+
name: "module",
|
|
9
|
+
namespace: "pepr-system",
|
|
10
|
+
files: [path],
|
|
11
|
+
};
|
|
12
|
+
const chartSettings = {
|
|
13
|
+
name: "module",
|
|
14
|
+
namespace: "pepr-system",
|
|
15
|
+
version: `${assets.config.appVersion || "0.0.1"}`,
|
|
16
|
+
localPath: path,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const component = {
|
|
20
|
+
name: "module",
|
|
21
|
+
required: true,
|
|
22
|
+
images: [assets.image],
|
|
23
|
+
[type]: [type === "manifests" ? manifestSettings : chartSettings],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const zarfCfg = {
|
|
27
|
+
kind: "ZarfPackageConfig",
|
|
28
|
+
metadata: {
|
|
29
|
+
name: assets.name,
|
|
30
|
+
description: `Pepr Module: ${assets.config.description}`,
|
|
31
|
+
url: "https://github.com/defenseunicorns/pepr",
|
|
32
|
+
version: `${assets.config.appVersion || "0.0.1"}`,
|
|
33
|
+
},
|
|
34
|
+
components: [component],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return dumpYaml(zarfCfg, { noRefs: true });
|
|
38
|
+
}
|
|
@@ -1,24 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { genEnv } from "../pods";
|
|
2
|
+
import { ModuleConfig } from "../../core/module";
|
|
3
|
+
import { CapabilityExport } from "../../types";
|
|
4
4
|
import { dumpYaml } from "@kubernetes/client-node";
|
|
5
|
-
import
|
|
5
|
+
import { clusterRole } from "../rbac";
|
|
6
6
|
import { promises as fs } from "fs";
|
|
7
|
-
import { Assets } from ".";
|
|
8
|
-
import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking";
|
|
9
|
-
import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods";
|
|
10
|
-
import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac";
|
|
11
|
-
import { webhookConfig } from "./webhooks";
|
|
12
|
-
import { genEnv } from "./pods";
|
|
13
7
|
|
|
8
|
+
type CommonOverrideValues = {
|
|
9
|
+
apiToken: string;
|
|
10
|
+
capabilities: CapabilityExport[];
|
|
11
|
+
config: ModuleConfig;
|
|
12
|
+
hash: string;
|
|
13
|
+
name: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type ChartOverrides = CommonOverrideValues & {
|
|
17
|
+
image: string;
|
|
18
|
+
};
|
|
14
19
|
// Helm Chart overrides file (values.yaml) generated from assets
|
|
15
20
|
export async function overridesFile(
|
|
16
|
-
{ hash, name, image, config, apiToken, capabilities }:
|
|
21
|
+
{ hash, name, image, config, apiToken, capabilities }: ChartOverrides,
|
|
17
22
|
path: string,
|
|
23
|
+
imagePullSecrets: string[],
|
|
18
24
|
): Promise<void> {
|
|
19
25
|
const rbacOverrides = clusterRole(name, capabilities, config.rbacMode, config.rbac).rules;
|
|
20
26
|
|
|
21
27
|
const overrides = {
|
|
28
|
+
imagePullSecrets,
|
|
29
|
+
additionalIgnoredNamespaces: [],
|
|
22
30
|
rbac: rbacOverrides,
|
|
23
31
|
secrets: {
|
|
24
32
|
apiToken: Buffer.from(apiToken).toString("base64"),
|
|
@@ -169,101 +177,3 @@ export async function overridesFile(
|
|
|
169
177
|
|
|
170
178
|
await fs.writeFile(path, dumpYaml(overrides, { noRefs: true, forceQuotes: true }));
|
|
171
179
|
}
|
|
172
|
-
export function zarfYaml({ name, image, config }: Assets, path: string): string {
|
|
173
|
-
const zarfCfg = {
|
|
174
|
-
kind: "ZarfPackageConfig",
|
|
175
|
-
metadata: {
|
|
176
|
-
name,
|
|
177
|
-
description: `Pepr Module: ${config.description}`,
|
|
178
|
-
url: "https://github.com/defenseunicorns/pepr",
|
|
179
|
-
version: `${config.appVersion || "0.0.1"}`,
|
|
180
|
-
},
|
|
181
|
-
components: [
|
|
182
|
-
{
|
|
183
|
-
name: "module",
|
|
184
|
-
required: true,
|
|
185
|
-
manifests: [
|
|
186
|
-
{
|
|
187
|
-
name: "module",
|
|
188
|
-
namespace: "pepr-system",
|
|
189
|
-
files: [path],
|
|
190
|
-
},
|
|
191
|
-
],
|
|
192
|
-
images: [image],
|
|
193
|
-
},
|
|
194
|
-
],
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
return dumpYaml(zarfCfg, { noRefs: true });
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function zarfYamlChart({ name, image, config }: Assets, path: string): string {
|
|
201
|
-
const zarfCfg = {
|
|
202
|
-
kind: "ZarfPackageConfig",
|
|
203
|
-
metadata: {
|
|
204
|
-
name,
|
|
205
|
-
description: `Pepr Module: ${config.description}`,
|
|
206
|
-
url: "https://github.com/defenseunicorns/pepr",
|
|
207
|
-
version: `${config.appVersion || "0.0.1"}`,
|
|
208
|
-
},
|
|
209
|
-
components: [
|
|
210
|
-
{
|
|
211
|
-
name: "module",
|
|
212
|
-
required: true,
|
|
213
|
-
charts: [
|
|
214
|
-
{
|
|
215
|
-
name: "module",
|
|
216
|
-
namespace: "pepr-system",
|
|
217
|
-
version: `${config.appVersion || "0.0.1"}`,
|
|
218
|
-
localPath: path,
|
|
219
|
-
},
|
|
220
|
-
],
|
|
221
|
-
images: [image],
|
|
222
|
-
},
|
|
223
|
-
],
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
return dumpYaml(zarfCfg, { noRefs: true });
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export async function allYaml(assets: Assets, imagePullSecret?: string): Promise<string> {
|
|
230
|
-
const { name, tls, apiToken, path, config } = assets;
|
|
231
|
-
const code = await fs.readFile(path);
|
|
232
|
-
|
|
233
|
-
// Generate a hash of the code
|
|
234
|
-
assets.hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
235
|
-
|
|
236
|
-
const mutateWebhook = await webhookConfig(assets, "mutate", assets.config.webhookTimeout);
|
|
237
|
-
const validateWebhook = await webhookConfig(assets, "validate", assets.config.webhookTimeout);
|
|
238
|
-
const watchDeployment = getWatcher(assets, assets.hash, assets.buildTimestamp, imagePullSecret);
|
|
239
|
-
|
|
240
|
-
const resources = [
|
|
241
|
-
getNamespace(assets.config.customLabels?.namespace),
|
|
242
|
-
clusterRole(name, assets.capabilities, config.rbacMode, config.rbac),
|
|
243
|
-
clusterRoleBinding(name),
|
|
244
|
-
serviceAccount(name),
|
|
245
|
-
apiTokenSecret(name, apiToken),
|
|
246
|
-
tlsSecret(name, tls),
|
|
247
|
-
getDeployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret),
|
|
248
|
-
service(name),
|
|
249
|
-
watcherService(name),
|
|
250
|
-
getModuleSecret(name, code, assets.hash),
|
|
251
|
-
storeRole(name),
|
|
252
|
-
storeRoleBinding(name),
|
|
253
|
-
];
|
|
254
|
-
|
|
255
|
-
if (mutateWebhook) {
|
|
256
|
-
resources.push(mutateWebhook);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (validateWebhook) {
|
|
260
|
-
resources.push(validateWebhook);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (watchDeployment) {
|
|
264
|
-
resources.push(watchDeployment);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Convert the resources to a single YAML string
|
|
268
|
-
return resources.map(r => dumpYaml(r, { noRefs: true })).join("---\n");
|
|
269
|
-
}
|
package/src/lib/core/module.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { CapabilityExport, AdmissionRequest } from "../types";
|
|
|
9
9
|
import { setupWatch } from "../processors/watch-processor";
|
|
10
10
|
import { Log } from "../../lib";
|
|
11
11
|
import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
|
|
12
|
+
import { resolveIgnoreNamespaces } from "../assets/webhooks";
|
|
12
13
|
|
|
13
14
|
/** Custom Labels Type for package.json */
|
|
14
15
|
export interface CustomLabels {
|
|
@@ -113,7 +114,7 @@ export class PeprModule {
|
|
|
113
114
|
// Wait for the controller to be ready before setting up watches
|
|
114
115
|
if (isWatchMode() || isDevMode()) {
|
|
115
116
|
try {
|
|
116
|
-
setupWatch(capabilities, pepr?.alwaysIgnore?.namespaces);
|
|
117
|
+
setupWatch(capabilities, resolveIgnoreNamespaces(pepr?.alwaysIgnore?.namespaces));
|
|
117
118
|
} catch (e) {
|
|
118
119
|
Log.error(e, "Error setting up watch");
|
|
119
120
|
process.exit(1);
|
package/src/lib/enums.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { ModuleConfig } from "../core/module";
|
|
|
14
14
|
import { PeprMutateRequest } from "../mutate-request";
|
|
15
15
|
import { base64Encode, convertFromBase64Map, convertToBase64Map } from "../utils";
|
|
16
16
|
import { OnError } from "../../cli/init/enums";
|
|
17
|
+
import { resolveIgnoreNamespaces } from "../assets/webhooks";
|
|
17
18
|
|
|
18
19
|
export interface Bindable {
|
|
19
20
|
req: AdmissionRequest;
|
|
@@ -169,7 +170,7 @@ export async function mutateProcessor(
|
|
|
169
170
|
bind.binding,
|
|
170
171
|
bind.req,
|
|
171
172
|
bind.namespaces,
|
|
172
|
-
bind.config?.alwaysIgnore?.namespaces,
|
|
173
|
+
resolveIgnoreNamespaces(bind.config?.alwaysIgnore?.namespaces),
|
|
173
174
|
);
|
|
174
175
|
if (shouldSkip !== "") {
|
|
175
176
|
Log.debug(shouldSkip);
|
|
@@ -10,6 +10,7 @@ import Log from "../telemetry/logger";
|
|
|
10
10
|
import { convertFromBase64Map } from "../utils";
|
|
11
11
|
import { PeprValidateRequest } from "../validate-request";
|
|
12
12
|
import { ModuleConfig } from "../core/module";
|
|
13
|
+
import { resolveIgnoreNamespaces } from "../assets/webhooks";
|
|
13
14
|
|
|
14
15
|
export async function processRequest(
|
|
15
16
|
binding: Binding,
|
|
@@ -78,7 +79,12 @@ export async function validateProcessor(
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
// Continue to the next action without doing anything if this one should be skipped
|
|
81
|
-
const shouldSkip = shouldSkipRequest(
|
|
82
|
+
const shouldSkip = shouldSkipRequest(
|
|
83
|
+
binding,
|
|
84
|
+
req,
|
|
85
|
+
namespaces,
|
|
86
|
+
resolveIgnoreNamespaces(config?.alwaysIgnore?.namespaces),
|
|
87
|
+
);
|
|
82
88
|
if (shouldSkip !== "") {
|
|
83
89
|
Log.debug(shouldSkip);
|
|
84
90
|
continue;
|