pepr 0.1.7 → 0.1.9

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.
@@ -5,274 +5,329 @@ import {
5
5
  V1ClusterRole,
6
6
  V1ClusterRoleBinding,
7
7
  V1Deployment,
8
+ V1LabelSelectorRequirement,
8
9
  V1MutatingWebhookConfiguration,
10
+ V1Namespace,
9
11
  V1Secret,
10
12
  V1Service,
11
13
  V1ServiceAccount,
14
+ dumpYaml,
12
15
  } from "@kubernetes/client-node";
13
16
  import { gzipSync } from "zlib";
14
- import { tlsCA, tlsCert, tlsKey } from "./stub-tls";
15
17
  import { ModuleConfig } from "../types";
18
+ import { TLSOut, genTLS } from "./tls";
16
19
 
17
- const peprIgnore = {
20
+ const peprIgnore: V1LabelSelectorRequirement = {
18
21
  key: "pepr.dev",
19
22
  operator: "NotIn",
20
23
  values: ["ignore"],
21
24
  };
22
25
 
23
- // @todo: make all this 💩 real
26
+ export class Webhook {
27
+ private name: string;
28
+ private image: string;
29
+ private tls: TLSOut;
24
30
 
25
- /**
26
- * Grants the controller access to cluster resources beyond the mutating webhook.
27
- *
28
- * @todo: should dynamically generate this based on resources used by the module. will also need to explore how this should work for multiple modules.
29
- * @returns
30
- */
31
- export function role(config: ModuleConfig): V1ClusterRole {
32
- return {
33
- apiVersion: "rbac.authorization.k8s.io/v1",
34
- kind: "ClusterRole",
35
- metadata: {
36
- name: `pepr-${config.uuid}`,
37
- },
38
- rules: [
39
- {
40
- // @todo: make this configurable
41
- apiGroups: ["*"],
42
- resources: ["*"],
43
- verbs: ["create", "delete", "get", "list", "patch", "update", "watch"],
44
- },
45
- {
46
- apiGroups: ["admissionregistration.k8s.io/v1"],
47
- resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"],
48
- verbs: ["get", "list", "update"],
49
- },
50
- ],
51
- };
52
- }
53
-
54
- export function roleBinding(config: ModuleConfig): V1ClusterRoleBinding {
55
- const name = `pepr-${config.uuid}`;
56
- return {
57
- apiVersion: "rbac.authorization.k8s.io/v1",
58
- kind: "ClusterRoleBinding",
59
- metadata: { name },
60
- roleRef: {
61
- apiGroup: "rbac.authorization.k8s.io",
62
- kind: "ClusterRole",
63
- name,
64
- },
65
- subjects: [
66
- {
67
- kind: "ServiceAccount",
68
- name: "pepr",
69
- namespace: "pepr-system",
70
- },
71
- ],
72
- };
73
- }
74
-
75
- export function serviceAccoutn(): V1ServiceAccount {
76
- return {
77
- apiVersion: "v1",
78
- kind: "ServiceAccount",
79
- metadata: {
80
- name: "pepr",
81
- namespace: "pepr-system",
82
- },
83
- };
84
- }
85
-
86
- export function tlsSecret(): V1Secret {
87
- return {
88
- apiVersion: "v1",
89
- kind: "Secret",
90
- metadata: {
91
- name: "controller-tls",
92
- namespace: "pepr-system",
93
- },
94
- type: "kubernetes.io/tls",
95
- data: {
96
- "tls.crt": tlsCert(),
97
- "tls.key": tlsKey(),
98
- },
99
- };
100
- }
31
+ constructor(private readonly config: ModuleConfig) {
32
+ this.name = `pepr-${config.uuid}`;
101
33
 
102
- export function mutatingWebhook(config: ModuleConfig): V1MutatingWebhookConfiguration {
103
- const name = `pepr-${config.uuid}`;
34
+ this.image = `ghcr.io/defenseunicorns/pepr-controller:${config.version}`;
104
35
 
105
- const ignore = [peprIgnore];
106
- if (config.alwaysIgnore.kinds.length > 0) {
107
- // ignore.push({
108
- // key: "pepr.dev/kind",
109
- // operator: "NotIn",
110
- // values: config.alwaysIgnore.kinds,
111
- // });
36
+ // Generate the ephemeral tls things
37
+ this.tls = genTLS(this.name);
112
38
  }
113
39
 
114
- // Add any namespaces to ignore
115
- if (config.alwaysIgnore.namespaces.length > 0) {
116
- ignore.push({
117
- key: "kubernetes.io/metadata.name",
118
- operator: "NotIn",
119
- values: config.alwaysIgnore.namespaces,
120
- });
40
+ /** Generate the pepr-system namespace */
41
+ namespace(): V1Namespace {
42
+ return {
43
+ apiVersion: "v1",
44
+ kind: "Namespace",
45
+ metadata: { name: "pepr-system" },
46
+ };
121
47
  }
122
48
 
123
- return {
124
- apiVersion: "admissionregistration.k8s.io/v1",
125
- kind: "MutatingWebhookConfiguration",
126
- metadata: { name },
127
- webhooks: [
128
- {
129
- admissionReviewVersions: ["v1", "v1beta1"],
130
- clientConfig: {
131
- caBundle: tlsCA(),
132
- service: {
133
- name: "controller",
134
- namespace: "pepr-system",
135
- path: "/mutate",
136
- },
49
+ /**
50
+ * Grants the controller access to cluster resources beyond the mutating webhook.
51
+ *
52
+ * @todo: should dynamically generate this based on resources used by the module. will also need to explore how this should work for multiple modules.
53
+ * @returns
54
+ */
55
+ clusterRole(): V1ClusterRole {
56
+ return {
57
+ apiVersion: "rbac.authorization.k8s.io/v1",
58
+ kind: "ClusterRole",
59
+ metadata: { name: this.name },
60
+ rules: [
61
+ {
62
+ // @todo: make this configurable
63
+ apiGroups: ["*"],
64
+ resources: ["*"],
65
+ verbs: ["create", "delete", "get", "list", "patch", "update", "watch"],
137
66
  },
138
- failurePolicy: "Ignore",
139
- matchPolicy: "Equivalent",
67
+ ],
68
+ };
69
+ }
70
+
71
+ clusterRoleBinding(): V1ClusterRoleBinding {
72
+ const name = this.name;
73
+ return {
74
+ apiVersion: "rbac.authorization.k8s.io/v1",
75
+ kind: "ClusterRoleBinding",
76
+ metadata: { name },
77
+ roleRef: {
78
+ apiGroup: "rbac.authorization.k8s.io",
79
+ kind: "ClusterRole",
140
80
  name,
141
- timeoutSeconds: 15,
142
- namespaceSelector: {
143
- matchExpressions: ignore,
144
- },
145
- objectSelector: {
146
- matchExpressions: ignore,
81
+ },
82
+ subjects: [
83
+ {
84
+ kind: "ServiceAccount",
85
+ name,
86
+ namespace: "pepr-system",
147
87
  },
148
- // @todo: make this configurable
149
- rules: [
150
- {
151
- apiGroups: ["*"],
152
- apiVersions: ["*"],
153
- operations: ["CREATE", "UPDATE", "DELETE"],
154
- resources: ["*"],
155
- },
156
- ],
157
- // @todo: track side effects state
158
- sideEffects: "None",
88
+ ],
89
+ };
90
+ }
91
+
92
+ serviceAccount(): V1ServiceAccount {
93
+ return {
94
+ apiVersion: "v1",
95
+ kind: "ServiceAccount",
96
+ metadata: {
97
+ name: this.name,
98
+ namespace: "pepr-system",
159
99
  },
160
- ],
161
- };
162
- }
100
+ };
101
+ }
163
102
 
164
- export function deployment(config: ModuleConfig): V1Deployment {
165
- return {
166
- apiVersion: "apps/v1",
167
- kind: "Deployment",
168
- metadata: {
169
- name: "controller",
170
- namespace: "pepr-system",
171
- labels: {
172
- app: "controller",
103
+ tlsSecret(): V1Secret {
104
+ return {
105
+ apiVersion: "v1",
106
+ kind: "Secret",
107
+ metadata: {
108
+ name: `${this.name}-tls`,
109
+ namespace: "pepr-system",
173
110
  },
174
- },
175
- spec: {
176
- replicas: 2,
177
- selector: {
178
- matchLabels: {
179
- app: "controller",
111
+ type: "kubernetes.io/tls",
112
+ data: {
113
+ "tls.crt": this.tls.crt,
114
+ "tls.key": this.tls.key,
115
+ },
116
+ };
117
+ }
118
+
119
+ mutatingWebhook(): V1MutatingWebhookConfiguration {
120
+ const { name } = this;
121
+ const ignore = [peprIgnore];
122
+
123
+ // Add any namespaces to ignore
124
+ if (this.config.alwaysIgnore.namespaces.length > 0) {
125
+ ignore.push({
126
+ key: "kubernetes.io/metadata.name",
127
+ operator: "NotIn",
128
+ values: this.config.alwaysIgnore.namespaces,
129
+ });
130
+ }
131
+
132
+ return {
133
+ apiVersion: "admissionregistration.k8s.io/v1",
134
+ kind: "MutatingWebhookConfiguration",
135
+ metadata: { name },
136
+ webhooks: [
137
+ {
138
+ name: `${name}.pepr.dev`,
139
+ admissionReviewVersions: ["v1", "v1beta1"],
140
+ clientConfig: {
141
+ caBundle: this.tls.ca,
142
+ service: {
143
+ name: this.name,
144
+ namespace: "pepr-system",
145
+ path: "/mutate",
146
+ },
147
+ },
148
+ failurePolicy: "Ignore",
149
+ matchPolicy: "Equivalent",
150
+ timeoutSeconds: 15,
151
+ namespaceSelector: {
152
+ matchExpressions: ignore,
153
+ },
154
+ objectSelector: {
155
+ matchExpressions: ignore,
156
+ },
157
+ // @todo: make this configurable
158
+ rules: [
159
+ {
160
+ apiGroups: ["*"],
161
+ apiVersions: ["*"],
162
+ operations: ["CREATE", "UPDATE", "DELETE"],
163
+ resources: ["*"],
164
+ },
165
+ ],
166
+ // @todo: track side effects state
167
+ sideEffects: "None",
168
+ },
169
+ ],
170
+ };
171
+ }
172
+
173
+ deployment(): V1Deployment {
174
+ return {
175
+ apiVersion: "apps/v1",
176
+ kind: "Deployment",
177
+ metadata: {
178
+ name: this.name,
179
+ namespace: "pepr-system",
180
+ labels: {
181
+ app: this.name,
180
182
  },
181
183
  },
182
- template: {
183
- metadata: {
184
- labels: {
185
- app: "controller",
184
+ spec: {
185
+ replicas: 2,
186
+ selector: {
187
+ matchLabels: {
188
+ app: this.name,
186
189
  },
187
190
  },
188
- spec: {
189
- priorityClassName: "system-node-critical",
190
- serviceAccountName: `pepr-${config.uuid}`,
191
- containers: [
192
- {
193
- name: "server",
194
- image: "ghcr.io/defenseunicorns/pepr-controller:latest",
195
- imagePullPolicy: "IfNotPresent",
196
- livenessProbe: {
197
- httpGet: {
198
- path: "/healthz",
199
- port: 3000,
200
- scheme: "HTTPS",
201
- },
202
- },
203
- ports: [
204
- {
205
- containerPort: 3000,
206
- },
207
- ],
208
- resources: {
209
- requests: {
210
- memory: "64Mi",
211
- cpu: "100m",
191
+ template: {
192
+ metadata: {
193
+ labels: {
194
+ app: this.name,
195
+ },
196
+ },
197
+ spec: {
198
+ priorityClassName: "system-node-critical",
199
+ serviceAccountName: this.name,
200
+ containers: [
201
+ {
202
+ name: "server",
203
+ image: this.image,
204
+ imagePullPolicy: "IfNotPresent",
205
+ livenessProbe: {
206
+ httpGet: {
207
+ path: "/healthz",
208
+ port: 3000,
209
+ scheme: "HTTPS",
210
+ },
212
211
  },
213
- limits: {
214
- memory: "256Mi",
215
- cpu: "500m",
212
+ ports: [
213
+ {
214
+ containerPort: 3000,
215
+ },
216
+ ],
217
+ resources: {
218
+ requests: {
219
+ memory: "64Mi",
220
+ cpu: "100m",
221
+ },
222
+ limits: {
223
+ memory: "256Mi",
224
+ cpu: "500m",
225
+ },
216
226
  },
227
+ volumeMounts: [
228
+ {
229
+ name: "tls-certs",
230
+ mountPath: "/etc/certs",
231
+ readOnly: true,
232
+ },
233
+ ],
217
234
  },
218
- volumeMounts: [
219
- {
220
- name: "tls-certs",
221
- mountPath: "/etc/certs",
222
- readOnly: true,
235
+ ],
236
+ volumes: [
237
+ {
238
+ name: "tls-certs",
239
+ secret: {
240
+ secretName: `${this.name}-tls`,
223
241
  },
224
- ],
225
- },
226
- ],
227
- volumes: [
228
- {
229
- name: "tls-certs",
230
- secret: {
231
- secretName: "controller-tls",
232
242
  },
233
- },
234
- ],
243
+ ],
244
+ },
235
245
  },
236
246
  },
237
- },
238
- };
239
- }
247
+ };
248
+ }
249
+
250
+ service(): V1Service {
251
+ return {
252
+ apiVersion: "v1",
253
+ kind: "Service",
254
+ metadata: {
255
+ name: this.name,
256
+ namespace: "pepr-system",
257
+ },
258
+ spec: {
259
+ selector: {
260
+ app: this.name,
261
+ },
262
+ ports: [
263
+ {
264
+ port: 443,
265
+ targetPort: 3000,
266
+ },
267
+ ],
268
+ },
269
+ };
270
+ }
240
271
 
241
- export function service(): V1Service {
242
- return {
243
- apiVersion: "v1",
244
- kind: "Service",
245
- metadata: {
246
- name: "controller",
247
- namespace: "pepr-system",
248
- },
249
- spec: {
250
- selector: {
251
- app: "controller",
272
+ moduleSecret(data: string): V1Secret {
273
+ // Compress the data
274
+ const compressed = gzipSync(data);
275
+ return {
276
+ apiVersion: "v1",
277
+ kind: "Secret",
278
+ metadata: {
279
+ name: `${this.name}-module`,
280
+ namespace: "pepr-system",
252
281
  },
253
- ports: [
282
+ type: "Opaque",
283
+ data: {
284
+ module: compressed.toString("base64"),
285
+ },
286
+ };
287
+ }
288
+
289
+ zarfYaml(path: string) {
290
+ const zarfCfg = {
291
+ kind: "ZarfPackageConfig",
292
+ metadata: {
293
+ name: this.name,
294
+ description: `Pepr Module: ${this.config.description}`,
295
+ url: "https://github.com/defenseunicorns/pepr",
296
+ version: this.config.version,
297
+ },
298
+ components: [
254
299
  {
255
- port: 443,
256
- targetPort: 3000,
300
+ name: "module",
301
+ required: true,
302
+ manifests: [
303
+ {
304
+ name: "module",
305
+ namespace: "pepr-system",
306
+ files: [path],
307
+ },
308
+ ],
309
+ images: [this.image],
257
310
  },
258
311
  ],
259
- },
260
- };
261
- }
312
+ };
313
+
314
+ return dumpYaml(zarfCfg, { noRefs: true });
315
+ }
262
316
 
263
- export function moduleSecret(uuid: string, data: string): V1Secret {
264
- // Compress the data
265
- const compressed = gzipSync(data);
266
- return {
267
- apiVersion: "v1",
268
- kind: "Secret",
269
- metadata: {
270
- name: `module-${uuid}`,
271
- namespace: "pepr-system",
272
- },
273
- type: "Opaque",
274
- data: {
275
- module: compressed.toString("base64"),
276
- },
277
- };
317
+ allYaml(code: string) {
318
+ const resources = [
319
+ this.namespace(),
320
+ this.clusterRole(),
321
+ this.clusterRoleBinding(),
322
+ this.serviceAccount(),
323
+ this.tlsSecret(),
324
+ this.mutatingWebhook(),
325
+ this.deployment(),
326
+ this.service(),
327
+ this.moduleSecret(code),
328
+ ];
329
+
330
+ // Convert the resources to a single YAML string
331
+ return resources.map(r => dumpYaml(r, { noRefs: true })).join("---\n");
332
+ }
278
333
  }
package/src/lib/module.ts CHANGED
@@ -1,20 +1,21 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2023-Present The Pepr Authors
3
3
 
4
+ import R from "ramda";
4
5
  import { Capability } from "./capability";
5
6
  import { GroupVersionKind, Request, Response } from "./k8s";
6
7
  import logger from "./logger";
7
8
  import { processor } from "./processor";
8
- import { ModuleAdditionalCfg, ModuleConfig } from "./types";
9
+ import { ModuleConfig } from "./types";
10
+
11
+ const alwaysIgnore = {
12
+ namespaces: ["kube-system", "pepr-system"],
13
+ labels: [{ "pepr.dev": "ignore" }],
14
+ };
9
15
 
10
16
  export type PackageJSON = {
11
17
  description: string;
12
- pepr: {
13
- uuid: string;
14
- name: string;
15
- version: string;
16
- onError: string;
17
- };
18
+ pepr: ModuleConfig;
18
19
  };
19
20
 
20
21
  export class PeprModule {
@@ -35,17 +36,9 @@ export class PeprModule {
35
36
  *
36
37
  * @param config The configuration for the Pepr runtime
37
38
  */
38
- constructor({ description, pepr }: PackageJSON, additionalCfg: ModuleAdditionalCfg) {
39
- this._config = {
40
- // Hardcode default values
41
- alwaysIgnore: {
42
- namespaces: ["kube-system", "pepr-system"],
43
- labels: [{ "pepr.dev": "ignore" }],
44
- },
45
- ...pepr,
46
- ...additionalCfg,
47
- description,
48
- } as ModuleConfig;
39
+ constructor({ description, pepr }: PackageJSON) {
40
+ pepr.description = description;
41
+ this._config = R.mergeDeepWith(R.concat, pepr, alwaysIgnore);
49
42
  }
50
43
 
51
44
  Register = (capability: Capability) => {
package/src/lib/types.ts CHANGED
@@ -80,16 +80,18 @@ export type ModuleSigning = {
80
80
  authorizedKeys?: string[];
81
81
  };
82
82
 
83
- export type ModulePackageCfg = {
83
+ /** Global configuration for the Pepr runtime. */
84
+ export type ModuleConfig = {
85
+ /** The user-defined name for the module */
86
+ name: string;
87
+ /** The version of Pepr that the module was originally generated with */
88
+ version: string;
84
89
  /** A unique identifier for this Pepr module. This is automatically generated by Pepr. */
85
90
  uuid: string;
86
91
  /** A description of the Pepr module and what it does. */
87
- description: string;
92
+ description?: string;
88
93
  /** Reject K8s resource AdmissionRequests on error. */
89
- onError: ErrorBehavior;
90
- };
91
-
92
- export type ModuleAdditionalCfg = {
94
+ onError: ErrorBehavior | string;
93
95
  /** Configure global exclusions that will never be processed by Pepr. */
94
96
  alwaysIgnore: WebhookIgnore;
95
97
  /**
@@ -101,9 +103,6 @@ export type ModuleAdditionalCfg = {
101
103
  signing?: ModuleSigning;
102
104
  };
103
105
 
104
- /** Global configuration for the Pepr runtime. */
105
- export type ModuleConfig = ModulePackageCfg & ModuleAdditionalCfg;
106
-
107
106
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
107
  export type GenericClass = abstract new () => any;
109
108
 
package/tsconfig.json CHANGED
@@ -7,5 +7,6 @@
7
7
  "resolveJsonModule": true,
8
8
  "strict": false,
9
9
  "target": "ES2020"
10
- }
10
+ },
11
+ "include": ["src/**/*.ts"]
11
12
  }