@highstate/wireguard 0.4.3 → 0.4.5

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.
@@ -1,15 +1,45 @@
1
1
  import { wireguard } from '@highstate/library';
2
- import { forUnit } from '@highstate/pulumi';
3
- import { b as generateIdentityConfig } from '../shared-RypNC0-F.js';
2
+ import { forUnit, output } from '@highstate/pulumi';
3
+ import { b as generateIdentityConfig } from '../shared-BVOzbQ3O.js';
4
4
  import '@noble/curves/ed25519';
5
5
 
6
+ function text(array, ...values) {
7
+ const str = array.reduce(
8
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
9
+ (result, part, i) => result + part + (values[i] ? String(values[i]) : ""),
10
+ ""
11
+ );
12
+ return trimIndentation(str);
13
+ }
14
+ function trimIndentation(text2) {
15
+ const lines = text2.split("\n");
16
+ const indent = lines.filter((line) => line.trim() !== "").map((line) => line.match(/^\s*/)?.[0].length ?? 0).reduce((min, indent2) => Math.min(min, indent2), Infinity);
17
+ return lines.map((line) => line.slice(indent)).join("\n").trim();
18
+ }
19
+
6
20
  const { inputs, outputs } = forUnit(wireguard.config);
21
+ const configContent = output(inputs).apply(({ identity, peers }) => {
22
+ return generateIdentityConfig(identity, peers);
23
+ });
7
24
  var index = outputs({
8
- $representation: {
9
- showQRCode: true,
10
- content: inputs.apply(({ identity, peers }) => {
11
- return generateIdentityConfig(identity, peers);
12
- })
25
+ $pages: {
26
+ index: {
27
+ title: "WireGuard Configuration",
28
+ content: [
29
+ {
30
+ type: "markdown",
31
+ content: text`
32
+ You can use this configuration to setup an external WireGuard device via \`wg-quick\` command.
33
+ `
34
+ },
35
+ {
36
+ type: "qr",
37
+ content: configContent,
38
+ showContent: true,
39
+ language: "ini"
40
+ }
41
+ ]
42
+ }
13
43
  }
14
44
  });
15
45
 
@@ -1,32 +1,50 @@
1
1
  import { wireguard } from '@highstate/library';
2
- import { forUnit, getOrCreateSecret } from '@highstate/pulumi';
3
- import { g as generateKey, a as generatePresharedKey, c as convertPrivateKeyToPublicKey } from '../shared-RypNC0-F.js';
2
+ import { forUnit, getOrCreateSecret, toPromise } from '@highstate/pulumi';
3
+ import { g as generateKey, a as generatePresharedKey, c as convertPrivateKeyToPublicKey } from '../shared-BVOzbQ3O.js';
4
4
  import '@noble/curves/ed25519';
5
5
 
6
6
  const { name, args, inputs, secrets, outputs } = forUnit(wireguard.identity);
7
7
  const privateKey = getOrCreateSecret(secrets, "privateKey", generateKey);
8
8
  const presharedKeyPart = getOrCreateSecret(secrets, "presharedKeyPart", () => {
9
- return inputs.network.apply((network) => {
10
- return network?.presharedKeyMode === "secure" ? generatePresharedKey() : undefined;
9
+ return inputs.network?.apply((network) => {
10
+ return network?.presharedKeyMode === "secure" ? generatePresharedKey() : void 0;
11
11
  });
12
12
  });
13
+ const allowedIps = await toPromise(addK8sServiceIps(args.allowedIps ?? [args.address]));
14
+ const argsAllowedIpsString = args.allowedIps?.join(", ");
15
+ const calculatedAllowedIpsString = allowedIps.join(", ");
16
+ const calculatedEndpoint = args.externalIp && args.listenPort ? ` ${args.externalIp}:${args.listenPort}` : void 0;
13
17
  var index = outputs({
14
18
  identity: {
15
19
  name: args.peerName ?? name,
16
20
  network: inputs.network,
17
21
  address: args.address,
18
22
  privateKey,
19
- presharedKeyPart
23
+ presharedKeyPart,
24
+ k8sServices: inputs.k8sServices,
25
+ exitNode: args.exitNode ?? false,
26
+ listenPort: args.listenPort,
27
+ externalIp: args.externalIp
20
28
  },
21
29
  peer: {
22
30
  name: args.peerName ?? name,
23
31
  network: inputs.network,
24
32
  address: args.address,
25
33
  publicKey: privateKey.apply(convertPrivateKeyToPublicKey),
26
- allowedIps: args.allowedIps ?? [args.address],
27
- endpoint: args.endpoint,
34
+ allowedIps,
35
+ endpoint: args.endpoint ?? calculatedEndpoint,
28
36
  presharedKeyPart
37
+ },
38
+ $status: {
39
+ allowedIps: calculatedAllowedIpsString !== argsAllowedIpsString ? calculatedAllowedIpsString : void 0,
40
+ endpoint: calculatedEndpoint !== args.endpoint ? calculatedEndpoint : void 0
29
41
  }
30
42
  });
43
+ function addK8sServiceIps(allowedIps2) {
44
+ return inputs.k8sServices.apply((k8sServices) => {
45
+ const ips = k8sServices.map((service) => service.ip).filter((ip) => ip !== void 0);
46
+ return allowedIps2.concat(ips);
47
+ });
48
+ }
31
49
 
32
50
  export { index as default };
@@ -0,0 +1,135 @@
1
+ import { createProvider, createNamespace, mapMetadata, Deployment, NetworkPolicy } from '@highstate/k8s';
2
+ import { wireguard } from '@highstate/library';
3
+ import { forUnit, toPromise, output } from '@highstate/pulumi';
4
+ import { core } from '@pulumi/kubernetes';
5
+ import { b as generateIdentityConfig } from '../shared-BVOzbQ3O.js';
6
+ import '@noble/curves/ed25519';
7
+
8
+ const { name, args, inputs } = forUnit(wireguard.node);
9
+ const identity = await toPromise(inputs.identity);
10
+ const appName = args.appName ?? `wg-${name}`;
11
+ const containerPort = 51820;
12
+ const serviceType = args.serviceType ?? "ClusterIP";
13
+ const provider = await createProvider(inputs.k8sCluster);
14
+ const namespace = createNamespace(appName, provider, {
15
+ metadata: {
16
+ labels: {
17
+ "pod-security.kubernetes.io/enforce": "privileged"
18
+ }
19
+ }
20
+ });
21
+ const configSecret = new core.v1.Secret(
22
+ appName,
23
+ {
24
+ metadata: mapMetadata({ name: appName, namespace }),
25
+ stringData: {
26
+ "wg0.conf": output(inputs).apply(({ identity: identity2, peers }) => {
27
+ return generateIdentityConfig(identity2, peers, containerPort, [
28
+ "iptables -t nat -A POSTROUTING -j MASQUERADE"
29
+ ]);
30
+ })
31
+ }
32
+ },
33
+ { provider }
34
+ );
35
+ const deployment = new Deployment(
36
+ appName,
37
+ {
38
+ namespace,
39
+ container: {
40
+ image: "linuxserver/wireguard:latest",
41
+ environment: {
42
+ PUID: "1000",
43
+ PGID: "1000",
44
+ TZ: "Etc/UTC"
45
+ },
46
+ securityContext: {
47
+ capabilities: {
48
+ add: ["NET_ADMIN"]
49
+ }
50
+ },
51
+ port: {
52
+ containerPort,
53
+ protocol: "UDP"
54
+ },
55
+ volume: configSecret,
56
+ volumeMount: {
57
+ volume: configSecret,
58
+ mountPath: "/config/wg_confs"
59
+ }
60
+ },
61
+ service: {
62
+ type: serviceType,
63
+ externalIPs: identity.externalIp ? [identity.externalIp] : void 0,
64
+ port: {
65
+ port: containerPort,
66
+ protocol: "UDP",
67
+ nodePort: serviceType === "NodePort" ? identity.listenPort : void 0
68
+ }
69
+ }
70
+ },
71
+ { provider }
72
+ );
73
+ NetworkPolicy.create(
74
+ "allow-wireguard-ingress",
75
+ {
76
+ cni: inputs.k8sCluster.cni,
77
+ namespace,
78
+ description: "Allow encapsulated WireGuard traffic to the node from anywhere.",
79
+ selector: deployment.deployment.spec.selector,
80
+ ingressRule: {
81
+ fromAll: true,
82
+ toPort: { port: containerPort, protocol: "UDP" }
83
+ }
84
+ },
85
+ { provider }
86
+ );
87
+ if (identity.exitNode) {
88
+ NetworkPolicy.create(
89
+ "allow-all-egress",
90
+ {
91
+ cni: inputs.k8sCluster.cni,
92
+ namespace,
93
+ description: "Allow all egress traffic from the WireGuard node.",
94
+ selector: deployment.deployment.spec.selector,
95
+ egressRule: {
96
+ toAll: true
97
+ }
98
+ },
99
+ { provider }
100
+ );
101
+ }
102
+ for (const service of identity.k8sServices) {
103
+ NetworkPolicy.create(
104
+ `allow-egress-to-${service.namespace}-${service.name}`,
105
+ {
106
+ cni: inputs.k8sCluster.cni,
107
+ namespace,
108
+ description: `Allow egress traffic from the WireGuard node to ${service.namespace}/${service.name}.`,
109
+ selector: deployment.deployment.spec.selector,
110
+ egressRules: [
111
+ {
112
+ toNamespace: service.namespace,
113
+ toSelector: service.selector
114
+ },
115
+ // for compatibility with Cilium which cannot correctly detect the destination endpoint when the packet is redirected by the WireGuard node
116
+ ...service.serviceType === "ClusterIP" && service.ip ? [{ toCidr: `${service.ip}/32` }] : []
117
+ ]
118
+ },
119
+ { provider }
120
+ );
121
+ NetworkPolicy.create(
122
+ `allow-ingress-from-${appName}-to-${service.name}`,
123
+ {
124
+ cni: inputs.k8sCluster.cni,
125
+ namespace: service.namespace,
126
+ description: `Allow ingress traffic from the WireGuard node ${appName} to ${service.name}.`,
127
+ selector: service.selector,
128
+ ingressRule: {
129
+ fromNamespace: namespace,
130
+ fromSelector: deployment.deployment.spec.selector
131
+ }
132
+ },
133
+ { provider }
134
+ );
135
+ }
@@ -1,39 +1,24 @@
1
- import { ed25519 } from '@noble/curves/ed25519';
1
+ import { x25519 } from '@noble/curves/ed25519';
2
2
 
3
- const crypto = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
3
+ const crypto = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0;
4
4
 
5
- /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
6
- /**
7
- * Utilities for hex, bytes, CSPRNG.
8
- * @module
9
- */
10
- // We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
11
- // node.js versions earlier than v19 don't declare it in global scope.
12
- // For node.js, package.json#exports field mapping rewrites import
13
- // from `crypto` to `cryptoNode`, which imports native module.
14
- // Makes the utils un-importable in browsers without a bundler.
15
- // Once node.js 18 is deprecated (2025-04-30), we can just drop the import.
16
- /**
17
- * Secure PRNG. Uses `crypto.getRandomValues`, which defers to OS.
18
- */
19
5
  function randomBytes(bytesLength = 32) {
20
- if (crypto && typeof crypto.getRandomValues === 'function') {
21
- return crypto.getRandomValues(new Uint8Array(bytesLength));
22
- }
23
- // Legacy Node.js compatibility
24
- if (crypto && typeof crypto.randomBytes === 'function') {
25
- return crypto.randomBytes(bytesLength);
26
- }
27
- throw new Error('crypto.getRandomValues must be defined');
6
+ if (crypto && typeof crypto.getRandomValues === "function") {
7
+ return crypto.getRandomValues(new Uint8Array(bytesLength));
8
+ }
9
+ if (crypto && typeof crypto.randomBytes === "function") {
10
+ return crypto.randomBytes(bytesLength);
11
+ }
12
+ throw new Error("crypto.getRandomValues must be defined");
28
13
  }
29
14
 
30
15
  function generateKey() {
31
- const key = ed25519.utils.randomPrivateKey();
16
+ const key = x25519.utils.randomPrivateKey();
32
17
  return Buffer.from(key).toString("base64");
33
18
  }
34
19
  function convertPrivateKeyToPublicKey(privateKey) {
35
20
  const key = Buffer.from(privateKey, "base64");
36
- return Buffer.from(ed25519.getPublicKey(key)).toString("base64");
21
+ return Buffer.from(x25519.getPublicKey(key)).toString("base64");
37
22
  }
38
23
  function generatePresharedKey() {
39
24
  const key = randomBytes(32);
@@ -69,14 +54,25 @@ function generatePeerConfig(identity, peer) {
69
54
  }
70
55
  return lines.join("\n");
71
56
  }
72
- function generateIdentityConfig(identity, peers) {
57
+ function generateIdentityConfig(identity, peers, listenPort, postUp) {
73
58
  const lines = [
74
59
  "[Interface]",
75
60
  `# ${identity.name}`,
76
61
  `Address = ${identity.address}`,
77
- `PrivateKey = ${identity.privateKey}`
62
+ `PrivateKey = ${identity.privateKey}`,
63
+ "MTU = 1280"
78
64
  ];
79
- for (const peer of peers) {
65
+ if (listenPort) {
66
+ lines.push(`ListenPort = ${listenPort}`);
67
+ }
68
+ if (postUp) {
69
+ lines.push();
70
+ for (const command of postUp) {
71
+ lines.push(`PostUp = ${command}`);
72
+ }
73
+ }
74
+ const otherPeers = peers.filter((peer) => peer.name !== identity.name);
75
+ for (const peer of otherPeers) {
80
76
  lines.push("");
81
77
  lines.push(generatePeerConfig(identity, peer));
82
78
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highstate/wireguard",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -8,7 +8,8 @@
8
8
  "exports": {
9
9
  "./network": "./dist/network/index.js",
10
10
  "./identity": "./dist/identity/index.js",
11
- "./config": "./dist/config/index.js"
11
+ "./config": "./dist/config/index.js",
12
+ "./node": "./dist/node/index.js"
12
13
  },
13
14
  "publishConfig": {
14
15
  "access": "public"
@@ -17,15 +18,16 @@
17
18
  "build": "pkgroll --tsconfig=tsconfig.build.json"
18
19
  },
19
20
  "dependencies": {
20
- "@highstate/pulumi": "^0.4.3",
21
+ "@highstate/k8s": "^0.4.5",
22
+ "@highstate/pulumi": "^0.4.5",
21
23
  "@noble/curves": "^1.8.0",
22
- "@pulumi/command": "^1.0.1"
24
+ "@pulumi/kubernetes": "^4.18.0"
23
25
  },
24
26
  "peerDependencies": {
25
- "@highstate/library": "workspace:^"
27
+ "@highstate/library": "workspace:^0.4.4"
26
28
  },
27
29
  "devDependencies": {
28
30
  "pkgroll": "^2.5.1"
29
31
  },
30
- "gitHead": "5b99600bd04872a4817c97b26501e1f22c8a0780"
32
+ "gitHead": "afd601fdade1bcf31af58072eea3c08ee26349b8"
31
33
  }