pepr 0.12.2 → 0.13.1
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/CODE_OF_CONDUCT.md +83 -0
- package/CONTRIBUTING.md +70 -0
- package/README.md +28 -30
- package/dist/cli.js +666 -692
- package/dist/controller.js +13 -81
- package/dist/lib/assets/deploy.d.ts +3 -0
- package/dist/lib/assets/deploy.d.ts.map +1 -0
- package/dist/lib/assets/index.d.ts +17 -0
- package/dist/lib/assets/index.d.ts.map +1 -0
- package/dist/lib/assets/loader.d.ts +8 -0
- package/dist/lib/assets/loader.d.ts.map +1 -0
- package/dist/lib/assets/networking.d.ts +6 -0
- package/dist/lib/assets/networking.d.ts.map +1 -0
- package/dist/lib/assets/pods.d.ts +8 -0
- package/dist/lib/assets/pods.d.ts.map +1 -0
- package/dist/lib/assets/rbac.d.ts +11 -0
- package/dist/lib/assets/rbac.d.ts.map +1 -0
- package/dist/lib/assets/webhooks.d.ts +6 -0
- package/dist/lib/assets/webhooks.d.ts.map +1 -0
- package/dist/lib/assets/yaml.d.ts +4 -0
- package/dist/lib/assets/yaml.d.ts.map +1 -0
- package/dist/lib/capability.d.ts +4 -9
- package/dist/lib/capability.d.ts.map +1 -1
- package/dist/lib/controller.d.ts +4 -15
- package/dist/lib/controller.d.ts.map +1 -1
- package/dist/lib/errors.d.ts +12 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/filter.d.ts +1 -1
- package/dist/lib/filter.d.ts.map +1 -1
- package/dist/lib/k8s/index.d.ts +2 -1
- package/dist/lib/k8s/index.d.ts.map +1 -1
- package/dist/lib/k8s/kinds.d.ts.map +1 -1
- package/dist/lib/k8s/types.d.ts +18 -14
- package/dist/lib/k8s/types.d.ts.map +1 -1
- package/dist/lib/k8s/upstream.d.ts +2 -2
- package/dist/lib/k8s/upstream.d.ts.map +1 -1
- package/dist/lib/logger.d.ts +8 -54
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/metrics.d.ts +10 -9
- package/dist/lib/metrics.d.ts.map +1 -1
- package/dist/lib/module.d.ts +4 -4
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/mutate-processor.d.ts +5 -0
- package/dist/lib/mutate-processor.d.ts.map +1 -0
- package/dist/lib/{request.d.ts → mutate-request.d.ts} +7 -7
- package/dist/lib/mutate-request.d.ts.map +1 -0
- package/dist/lib/types.d.ts +48 -55
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/validate-processor.d.ts +4 -0
- package/dist/lib/validate-processor.d.ts.map +1 -0
- package/dist/lib/validate-request.d.ts +54 -0
- package/dist/lib/validate-request.d.ts.map +1 -0
- package/dist/lib.d.ts +3 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +610 -354
- package/dist/lib.js.map +4 -4
- package/jest.config.json +4 -0
- package/journey/before.ts +21 -0
- package/journey/k8s.ts +81 -0
- package/journey/pepr-build.ts +69 -0
- package/journey/pepr-deploy.ts +133 -0
- package/journey/pepr-dev.ts +155 -0
- package/journey/pepr-format.ts +13 -0
- package/journey/pepr-init.ts +12 -0
- package/package.json +29 -27
- package/src/cli.ts +2 -11
- package/src/lib/assets/deploy.ts +179 -0
- package/src/lib/assets/index.ts +53 -0
- package/src/lib/assets/loader.ts +41 -0
- package/src/lib/assets/networking.ts +58 -0
- package/src/lib/assets/pods.ts +148 -0
- package/src/lib/assets/rbac.ts +57 -0
- package/src/lib/assets/webhooks.ts +139 -0
- package/src/lib/assets/yaml.ts +75 -0
- package/src/lib/capability.ts +80 -68
- package/src/lib/controller.ts +199 -99
- package/src/lib/errors.ts +20 -0
- package/src/lib/fetch.ts +1 -1
- package/src/lib/filter.ts +1 -3
- package/src/lib/k8s/index.ts +4 -1
- package/src/lib/k8s/kinds.ts +40 -0
- package/src/lib/k8s/types.ts +21 -15
- package/src/lib/k8s/upstream.ts +5 -1
- package/src/lib/logger.ts +14 -125
- package/src/lib/metrics.ts +86 -29
- package/src/lib/module.ts +32 -16
- package/src/lib/{processor.ts → mutate-processor.ts} +39 -28
- package/src/lib/{request.ts → mutate-request.ts} +26 -13
- package/src/lib/types.ts +54 -60
- package/src/lib/validate-processor.ts +76 -0
- package/src/lib/validate-request.ts +106 -0
- package/src/lib.ts +4 -2
- package/src/runtime/controller.ts +1 -1
- package/dist/lib/k8s/webhook.d.ts +0 -37
- package/dist/lib/k8s/webhook.d.ts.map +0 -1
- package/dist/lib/processor.d.ts +0 -5
- package/dist/lib/processor.d.ts.map +0 -1
- package/dist/lib/request.d.ts.map +0 -1
- package/src/lib/k8s/webhook.ts +0 -643
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import crypto from "crypto";
|
|
5
|
+
|
|
6
|
+
import { TLSOut, genTLS } from "../k8s/tls";
|
|
7
|
+
import { CapabilityExport, ModuleConfig } from "../types";
|
|
8
|
+
import { deploy } from "./deploy";
|
|
9
|
+
import { loadCapabilities } from "./loader";
|
|
10
|
+
import { allYaml, zarfYaml } from "./yaml";
|
|
11
|
+
|
|
12
|
+
export class Assets {
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly tls: TLSOut;
|
|
15
|
+
readonly apiToken: string;
|
|
16
|
+
capabilities!: CapabilityExport[];
|
|
17
|
+
image: string;
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
readonly config: ModuleConfig,
|
|
21
|
+
readonly path: string,
|
|
22
|
+
readonly host?: string,
|
|
23
|
+
) {
|
|
24
|
+
// Bind public methods
|
|
25
|
+
this.deploy = this.deploy.bind(this);
|
|
26
|
+
this.zarfYaml = this.zarfYaml.bind(this);
|
|
27
|
+
this.allYaml = this.allYaml.bind(this);
|
|
28
|
+
|
|
29
|
+
this.name = `pepr-${config.uuid}`;
|
|
30
|
+
|
|
31
|
+
this.image = `ghcr.io/defenseunicorns/pepr/controller:v${config.peprVersion}`;
|
|
32
|
+
|
|
33
|
+
// Generate the ephemeral tls things
|
|
34
|
+
this.tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
|
|
35
|
+
|
|
36
|
+
// Generate the api token for the controller / webhook
|
|
37
|
+
this.apiToken = crypto.randomBytes(32).toString("hex");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async deploy(webhookTimeout?: number) {
|
|
41
|
+
this.capabilities = await loadCapabilities(this.path);
|
|
42
|
+
await deploy(this, webhookTimeout);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
zarfYaml(path: string) {
|
|
46
|
+
return zarfYaml(this, path);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async allYaml() {
|
|
50
|
+
this.capabilities = await loadCapabilities(this.path);
|
|
51
|
+
return allYaml(this);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { fork } from "child_process";
|
|
5
|
+
|
|
6
|
+
import { CapabilityExport } from "../types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Read the capabilities from the module by running it in build mode
|
|
10
|
+
* @param path
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
export function loadCapabilities(path: string): Promise<CapabilityExport[]> {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
// Fork is needed with the PEPR_MODE env var to ensure the module is loaded in build mode and will send back the capabilities
|
|
16
|
+
const program = fork(path, {
|
|
17
|
+
env: {
|
|
18
|
+
...process.env,
|
|
19
|
+
LOG_LEVEL: "warn",
|
|
20
|
+
PEPR_MODE: "build",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Wait for the module to send back the capabilities
|
|
25
|
+
program.on("message", message => {
|
|
26
|
+
// Cast the message to the ModuleCapabilities type
|
|
27
|
+
const capabilities = message.valueOf() as CapabilityExport[];
|
|
28
|
+
|
|
29
|
+
// Iterate through the capabilities and generate the rules
|
|
30
|
+
for (const capability of capabilities) {
|
|
31
|
+
console.info(`Registered Pepr Capability "${capability.name}"`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
resolve(capabilities);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
program.on("error", error => {
|
|
38
|
+
reject(error);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { TLSOut } from "../k8s/tls";
|
|
5
|
+
import { Secret, Service } from "../k8s/upstream";
|
|
6
|
+
|
|
7
|
+
export function apiTokenSecret(name: string, apiToken: string): Secret {
|
|
8
|
+
return {
|
|
9
|
+
apiVersion: "v1",
|
|
10
|
+
kind: "Secret",
|
|
11
|
+
metadata: {
|
|
12
|
+
name: `${name}-api-token`,
|
|
13
|
+
namespace: "pepr-system",
|
|
14
|
+
},
|
|
15
|
+
type: "Opaque",
|
|
16
|
+
data: {
|
|
17
|
+
value: Buffer.from(apiToken).toString("base64"),
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function tlsSecret(name: string, tls: TLSOut): Secret {
|
|
23
|
+
return {
|
|
24
|
+
apiVersion: "v1",
|
|
25
|
+
kind: "Secret",
|
|
26
|
+
metadata: {
|
|
27
|
+
name: `${name}-tls`,
|
|
28
|
+
namespace: "pepr-system",
|
|
29
|
+
},
|
|
30
|
+
type: "kubernetes.io/tls",
|
|
31
|
+
data: {
|
|
32
|
+
"tls.crt": tls.crt,
|
|
33
|
+
"tls.key": tls.key,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function service(name: string): Service {
|
|
39
|
+
return {
|
|
40
|
+
apiVersion: "v1",
|
|
41
|
+
kind: "Service",
|
|
42
|
+
metadata: {
|
|
43
|
+
name,
|
|
44
|
+
namespace: "pepr-system",
|
|
45
|
+
},
|
|
46
|
+
spec: {
|
|
47
|
+
selector: {
|
|
48
|
+
app: name,
|
|
49
|
+
},
|
|
50
|
+
ports: [
|
|
51
|
+
{
|
|
52
|
+
port: 443,
|
|
53
|
+
targetPort: 3000,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { gzipSync } from "zlib";
|
|
5
|
+
|
|
6
|
+
import { Assets } from ".";
|
|
7
|
+
import { Deployment, Namespace, Secret } from "../k8s/upstream";
|
|
8
|
+
|
|
9
|
+
/** Generate the pepr-system namespace */
|
|
10
|
+
export const namespace: Namespace = {
|
|
11
|
+
apiVersion: "v1",
|
|
12
|
+
kind: "Namespace",
|
|
13
|
+
metadata: { name: "pepr-system" },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function deployment(assets: Assets, hash: string): Deployment {
|
|
17
|
+
const { name, image } = assets;
|
|
18
|
+
const app = name;
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
apiVersion: "apps/v1",
|
|
22
|
+
kind: "Deployment",
|
|
23
|
+
metadata: {
|
|
24
|
+
name,
|
|
25
|
+
namespace: "pepr-system",
|
|
26
|
+
labels: {
|
|
27
|
+
app,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
spec: {
|
|
31
|
+
replicas: 2,
|
|
32
|
+
selector: {
|
|
33
|
+
matchLabels: {
|
|
34
|
+
app,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
template: {
|
|
38
|
+
metadata: {
|
|
39
|
+
labels: {
|
|
40
|
+
app,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
spec: {
|
|
44
|
+
priorityClassName: "system-node-critical",
|
|
45
|
+
serviceAccountName: name,
|
|
46
|
+
containers: [
|
|
47
|
+
{
|
|
48
|
+
name: "server",
|
|
49
|
+
image,
|
|
50
|
+
imagePullPolicy: "IfNotPresent",
|
|
51
|
+
command: ["node", "/app/node_modules/pepr/dist/controller.js", hash],
|
|
52
|
+
readinessProbe: {
|
|
53
|
+
httpGet: {
|
|
54
|
+
path: "/healthz",
|
|
55
|
+
port: 3000,
|
|
56
|
+
scheme: "HTTPS",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
livenessProbe: {
|
|
60
|
+
httpGet: {
|
|
61
|
+
path: "/healthz",
|
|
62
|
+
port: 3000,
|
|
63
|
+
scheme: "HTTPS",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
ports: [
|
|
67
|
+
{
|
|
68
|
+
containerPort: 3000,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
env: [
|
|
72
|
+
{
|
|
73
|
+
name: "PEPR_PRETTY_LOG",
|
|
74
|
+
value: "false",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
resources: {
|
|
78
|
+
requests: {
|
|
79
|
+
memory: "64Mi",
|
|
80
|
+
cpu: "100m",
|
|
81
|
+
},
|
|
82
|
+
limits: {
|
|
83
|
+
memory: "256Mi",
|
|
84
|
+
cpu: "500m",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
volumeMounts: [
|
|
88
|
+
{
|
|
89
|
+
name: "tls-certs",
|
|
90
|
+
mountPath: "/etc/certs",
|
|
91
|
+
readOnly: true,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "api-token",
|
|
95
|
+
mountPath: "/app/api-token",
|
|
96
|
+
readOnly: true,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "module",
|
|
100
|
+
mountPath: `/app/load`,
|
|
101
|
+
readOnly: true,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
volumes: [
|
|
107
|
+
{
|
|
108
|
+
name: "tls-certs",
|
|
109
|
+
secret: {
|
|
110
|
+
secretName: `${name}-tls`,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "api-token",
|
|
115
|
+
secret: {
|
|
116
|
+
secretName: `${name}-api-token`,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "module",
|
|
121
|
+
secret: {
|
|
122
|
+
secretName: `${name}-module`,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function moduleSecret(name: string, data: Buffer, hash: string): Secret {
|
|
133
|
+
// Compress the data
|
|
134
|
+
const compressed = gzipSync(data);
|
|
135
|
+
const path = `module-${hash}.js.gz`;
|
|
136
|
+
return {
|
|
137
|
+
apiVersion: "v1",
|
|
138
|
+
kind: "Secret",
|
|
139
|
+
metadata: {
|
|
140
|
+
name: `${name}-module`,
|
|
141
|
+
namespace: "pepr-system",
|
|
142
|
+
},
|
|
143
|
+
type: "Opaque",
|
|
144
|
+
data: {
|
|
145
|
+
[path]: compressed.toString("base64"),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { ClusterRole, ClusterRoleBinding, ServiceAccount } from "../k8s/upstream";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Grants the controller access to cluster resources beyond the mutating webhook.
|
|
8
|
+
*
|
|
9
|
+
* @todo: should dynamically generate this based on resources used by the module. will also need to explore how this should work for multiple modules.
|
|
10
|
+
* @returns
|
|
11
|
+
*/
|
|
12
|
+
export function clusterRole(name: string): ClusterRole {
|
|
13
|
+
return {
|
|
14
|
+
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
15
|
+
kind: "ClusterRole",
|
|
16
|
+
metadata: { name },
|
|
17
|
+
rules: [
|
|
18
|
+
{
|
|
19
|
+
// @todo: make this configurable
|
|
20
|
+
apiGroups: ["*"],
|
|
21
|
+
resources: ["*"],
|
|
22
|
+
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"],
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function clusterRoleBinding(name: string): ClusterRoleBinding {
|
|
29
|
+
return {
|
|
30
|
+
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
31
|
+
kind: "ClusterRoleBinding",
|
|
32
|
+
metadata: { name },
|
|
33
|
+
roleRef: {
|
|
34
|
+
apiGroup: "rbac.authorization.k8s.io",
|
|
35
|
+
kind: "ClusterRole",
|
|
36
|
+
name,
|
|
37
|
+
},
|
|
38
|
+
subjects: [
|
|
39
|
+
{
|
|
40
|
+
kind: "ServiceAccount",
|
|
41
|
+
name,
|
|
42
|
+
namespace: "pepr-system",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function serviceAccount(name: string): ServiceAccount {
|
|
49
|
+
return {
|
|
50
|
+
apiVersion: "v1",
|
|
51
|
+
kind: "ServiceAccount",
|
|
52
|
+
metadata: {
|
|
53
|
+
name,
|
|
54
|
+
namespace: "pepr-system",
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
AdmissionregistrationV1WebhookClientConfig as AdmissionRegnV1WebhookClientCfg,
|
|
6
|
+
V1LabelSelectorRequirement,
|
|
7
|
+
V1RuleWithOperations,
|
|
8
|
+
} from "@kubernetes/client-node";
|
|
9
|
+
import { concat, equals, uniqWith } from "ramda";
|
|
10
|
+
|
|
11
|
+
import { Assets } from ".";
|
|
12
|
+
import { MutatingWebhookConfiguration, ValidatingWebhookConfiguration } from "../k8s/upstream";
|
|
13
|
+
import { Event } from "../types";
|
|
14
|
+
|
|
15
|
+
const peprIgnoreLabel: V1LabelSelectorRequirement = {
|
|
16
|
+
key: "pepr.dev",
|
|
17
|
+
operator: "NotIn",
|
|
18
|
+
values: ["ignore"],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"];
|
|
22
|
+
|
|
23
|
+
export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean) {
|
|
24
|
+
const { config, capabilities } = assets;
|
|
25
|
+
const rules: V1RuleWithOperations[] = [];
|
|
26
|
+
|
|
27
|
+
// Iterate through the capabilities and generate the rules
|
|
28
|
+
for (const capability of capabilities) {
|
|
29
|
+
console.info(`Module ${config.uuid} has capability: ${capability.name}`);
|
|
30
|
+
|
|
31
|
+
// Read the bindings and generate the rules
|
|
32
|
+
for (const binding of capability.bindings) {
|
|
33
|
+
const { event, kind, isMutate, isValidate } = binding;
|
|
34
|
+
|
|
35
|
+
// If the module doesn't have a callback for the event, skip it
|
|
36
|
+
if (isMutateWebhook && !isMutate) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!isMutateWebhook && !isValidate) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const operations: string[] = [];
|
|
45
|
+
|
|
46
|
+
// CreateOrUpdate is a Pepr-specific event that is translated to Create and Update
|
|
47
|
+
if (event === Event.CreateOrUpdate) {
|
|
48
|
+
operations.push(Event.Create, Event.Update);
|
|
49
|
+
} else {
|
|
50
|
+
operations.push(event);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Use the plural property if it exists, otherwise use lowercase kind + s
|
|
54
|
+
const resource = kind.plural || `${kind.kind.toLowerCase()}s`;
|
|
55
|
+
|
|
56
|
+
rules.push({
|
|
57
|
+
apiGroups: [kind.group],
|
|
58
|
+
apiVersions: [kind.version || "*"],
|
|
59
|
+
operations,
|
|
60
|
+
resources: [resource],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Return the rules with duplicates removed
|
|
66
|
+
return uniqWith(equals, rules);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function webhookConfig(
|
|
70
|
+
assets: Assets,
|
|
71
|
+
mutateOrValidate: "mutate" | "validate",
|
|
72
|
+
timeoutSeconds = 10,
|
|
73
|
+
): Promise<MutatingWebhookConfiguration | ValidatingWebhookConfiguration | null> {
|
|
74
|
+
const ignore = [peprIgnoreLabel];
|
|
75
|
+
|
|
76
|
+
const { name, tls, config, apiToken, host } = assets;
|
|
77
|
+
const ignoreNS = concat(peprIgnoreNamespaces, config.alwaysIgnore.namespaces || []);
|
|
78
|
+
|
|
79
|
+
// Add any namespaces to ignore
|
|
80
|
+
if (ignoreNS) {
|
|
81
|
+
ignore.push({
|
|
82
|
+
key: "kubernetes.io/metadata.name",
|
|
83
|
+
operator: "NotIn",
|
|
84
|
+
values: ignoreNS,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const clientConfig: AdmissionRegnV1WebhookClientCfg = {
|
|
89
|
+
caBundle: tls.ca,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// The URL must include the API Token
|
|
93
|
+
const apiPath = `/${mutateOrValidate}/${apiToken}`;
|
|
94
|
+
|
|
95
|
+
// If a host is specified, use that with a port of 3000
|
|
96
|
+
if (host) {
|
|
97
|
+
clientConfig.url = `https://${host}:3000${apiPath}`;
|
|
98
|
+
} else {
|
|
99
|
+
// Otherwise, use the service
|
|
100
|
+
clientConfig.service = {
|
|
101
|
+
name: name,
|
|
102
|
+
namespace: "pepr-system",
|
|
103
|
+
path: apiPath,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const isMutate = mutateOrValidate === "mutate";
|
|
108
|
+
const rules = await generateWebhookRules(assets, isMutate);
|
|
109
|
+
|
|
110
|
+
// If there are no rules, return null
|
|
111
|
+
if (rules.length < 1) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
apiVersion: "admissionregistration.k8s.io/v1",
|
|
117
|
+
kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
|
|
118
|
+
metadata: { name },
|
|
119
|
+
webhooks: [
|
|
120
|
+
{
|
|
121
|
+
name: `${name}.pepr.dev`,
|
|
122
|
+
admissionReviewVersions: ["v1", "v1beta1"],
|
|
123
|
+
clientConfig,
|
|
124
|
+
failurePolicy: "Ignore",
|
|
125
|
+
matchPolicy: "Equivalent",
|
|
126
|
+
timeoutSeconds,
|
|
127
|
+
namespaceSelector: {
|
|
128
|
+
matchExpressions: ignore,
|
|
129
|
+
},
|
|
130
|
+
objectSelector: {
|
|
131
|
+
matchExpressions: ignore,
|
|
132
|
+
},
|
|
133
|
+
rules,
|
|
134
|
+
// @todo: track side effects state
|
|
135
|
+
sideEffects: "None",
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
+
|
|
4
|
+
import { dumpYaml } from "@kubernetes/client-node";
|
|
5
|
+
import crypto from "crypto";
|
|
6
|
+
import { promises as fs } from "fs";
|
|
7
|
+
|
|
8
|
+
import { Assets } from ".";
|
|
9
|
+
import { apiTokenSecret, service, tlsSecret } from "./networking";
|
|
10
|
+
import { deployment, moduleSecret, namespace } from "./pods";
|
|
11
|
+
import { clusterRole, clusterRoleBinding, serviceAccount } from "./rbac";
|
|
12
|
+
import { webhookConfig } from "./webhooks";
|
|
13
|
+
|
|
14
|
+
export function zarfYaml({ name, image, config }: Assets, path: string) {
|
|
15
|
+
const zarfCfg = {
|
|
16
|
+
kind: "ZarfPackageConfig",
|
|
17
|
+
metadata: {
|
|
18
|
+
name,
|
|
19
|
+
description: `Pepr Module: ${config.description}`,
|
|
20
|
+
url: "https://github.com/defenseunicorns/pepr",
|
|
21
|
+
version: `${config.appVersion || "0.0.1"}`,
|
|
22
|
+
},
|
|
23
|
+
components: [
|
|
24
|
+
{
|
|
25
|
+
name: "module",
|
|
26
|
+
required: true,
|
|
27
|
+
manifests: [
|
|
28
|
+
{
|
|
29
|
+
name: "module",
|
|
30
|
+
namespace: "pepr-system",
|
|
31
|
+
files: [path],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
images: [image],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return dumpYaml(zarfCfg, { noRefs: true });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function allYaml(assets: Assets) {
|
|
43
|
+
const { name, tls, apiToken, path } = assets;
|
|
44
|
+
|
|
45
|
+
const code = await fs.readFile(path);
|
|
46
|
+
|
|
47
|
+
// Generate a hash of the code
|
|
48
|
+
const hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
49
|
+
|
|
50
|
+
const mutateWebhook = await webhookConfig(assets, "mutate");
|
|
51
|
+
const validateWebhook = await webhookConfig(assets, "validate");
|
|
52
|
+
|
|
53
|
+
const resources = [
|
|
54
|
+
namespace,
|
|
55
|
+
clusterRole(name),
|
|
56
|
+
clusterRoleBinding(name),
|
|
57
|
+
serviceAccount(name),
|
|
58
|
+
apiTokenSecret(name, apiToken),
|
|
59
|
+
tlsSecret(name, tls),
|
|
60
|
+
deployment(assets, hash),
|
|
61
|
+
service(name),
|
|
62
|
+
moduleSecret(name, code, hash),
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
if (mutateWebhook) {
|
|
66
|
+
resources.push(mutateWebhook);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (validateWebhook) {
|
|
70
|
+
resources.push(validateWebhook);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Convert the resources to a single YAML string
|
|
74
|
+
return resources.map(r => dumpYaml(r, { noRefs: true })).join("---\n");
|
|
75
|
+
}
|