@@ -22,9 +22,26 @@ PersistentVolumeClaim mounted at `/home/handshake/.handshake-node`.
2222Security-relevant defaults:
2323
2424- The container runs as the non-root ` handshake ` user baked into the upstream
25- image with ` runAsNonRoot: true ` , all Linux capabilities dropped, no
26- privilege escalation, ` readOnlyRootFilesystem: true ` , and the
27- ` RuntimeDefault ` seccomp profile.
25+ image (UID 100 / GID 101), pinned numerically via ` runAsUser ` /
26+ ` runAsGroup ` so kubelet can enforce ` runAsNonRoot: true ` without resolving
27+ the image's ` /etc/passwd ` . All Linux capabilities are dropped, no privilege
28+ escalation, ` readOnlyRootFilesystem: true ` , and the ` RuntimeDefault `
29+ seccomp profile.
30+ - An ` init-chown-data ` init container (busybox) fixes ownership of the
31+ mounted PVC on every start. It drops all Linux capabilities and adds back
32+ only ` CAP_CHOWN ` (to change owner/group) and ` CAP_DAC_READ_SEARCH ` (so
33+ root can traverse the ` 0700 ` directories the daemon creates under the
34+ data root on first start; without it, a restart of the pod would fail
35+ with ` Permission denied ` ). The container runs a selective
36+ ` find /home/handshake/.handshake-node \( -not -user <uid> -o -not -group <gid> \) -exec chown <uid>:<gid> {} + `
37+ against the mount root, so only entries whose owner or group already
38+ differ from the configured ` runAsUser ` / ` runAsGroup ` are touched. That
39+ keeps the operation idempotent across restarts and cheap on a warm PVC,
40+ and it never modifies file modes — the Brontide identity key's ` 0600 `
41+ survives, and a pre-populated ` persistence.existingClaim ` remains fully
42+ readable by the non-root main container. ` fsGroup ` is deliberately not
43+ used because kubelet's recursive ` fsGroup ` chmod adds group r/w to every
44+ file, which the daemon rejects for its Brontide identity key.
2845- The peer-to-peer Service is ` ClusterIP ` by default. Public exposure via
2946 ` NodePort ` or ` LoadBalancer ` is an explicit operator choice.
3047- The authenticated TLS RPC listener (port ` 12037 ` ) is not exposed unless
@@ -35,6 +52,10 @@ Security-relevant defaults:
3552- The Prometheus metrics endpoint (` 12039 ` ) and Stratum server (` 12040 ` ) are
3653 disabled by default. When enabled they are kept on private ` ClusterIP `
3754 Services.
55+ - Supported ` network ` values are ` main ` and ` regtest ` . The upstream
56+ ` handshake-node ` binary does not implement ` --testnet ` or ` --simnet ` ; the
57+ chart rejects those values at render time to avoid a ` CreateContainerError `
58+ loop from an unknown flag.
3859
3960## Prerequisites
4061
@@ -160,6 +181,9 @@ stratum:
160181 # Optional. Derived from stratum.service.port + allowPublic when unset.
161182 # listen: ""
162183 allowPublic: true
184+ # Required whenever stratum.enabled=true (even for private/loopback binds).
185+ # handshake-node v0.2.0-rc1 refuses to start if --stratumlisten is set
186+ # without --miningaddr.
163187 miningAddress: hs1qyourhandshakeaddress
164188 auth:
165189 existingSecret: handshake-node-stratum
@@ -168,10 +192,11 @@ stratum:
168192 type: ClusterIP
169193` ` `
170194
171- Public exposure requires `stratum.allowPublic=true` and both
172- ` miningAddress` and `auth.existingSecret` (validated at render time). The
173- Stratum Service is only rendered when `stratum.allowPublic=true` because
174- loopback listeners cannot be reached through a ClusterIP.
195+ ` stratum.miningAddress` is required whenever Stratum is enabled. Public
196+ exposure additionally requires `stratum.allowPublic=true` and
197+ ` stratum.auth.existingSecret` (all validated at render time). The Stratum
198+ Service is only rendered when `stratum.allowPublic=true` because loopback
199+ listeners cannot be reached through a ClusterIP.
175200
176201# # Service exposure
177202
@@ -195,6 +220,12 @@ service:
195220 type: NodePort
196221 port: 12038
197222 nodePort: 32038
223+ config:
224+ extra:
225+ # handshake-node does not derive its advertised address from the
226+ # Service; when using NodePort, set EXTERNALIP to the routable
227+ # "<node-ip>:<nodePort>" so peers can dial you back.
228+ EXTERNALIP: "203.0.113.10:32038"
198229` ` `
199230
200231# # Mainnet operations examples
@@ -204,7 +235,7 @@ Full mainnet node with private RPC, metrics enabled, and a beefier PVC:
204235` ` ` yaml
205236network: main
206237image:
207- tag: "0.1.1 -rc1"
238+ tag: "0.2.0 -rc1"
208239
209240persistence:
210241 size: 400Gi
215246 existingSecret: handshake-node-rpc
216247
217248config:
218- # Required whenever rpc.enabled=true. Substitute the Pod CIDR of the
219- # workloads that should be allowed to call RPC .
249+ # Required whenever rpc.enabled=true. Substitute the observed client
250+ # source CIDR (Pod CIDR unless CNI SNAT changes it to node IPs) .
220251 rpcallowip: "<trusted-rpc-client-cidr>"
221252
222253metrics:
@@ -262,8 +293,8 @@ See [`values.yaml`](values.yaml) for the full list of tunables. Key knobs:
262293| Key | Description | Default |
263294| -------------------------------- | ------------------------------------------------------------ | ------------------------------------ |
264295| `image.repository` | Image name | `ghcr.io/blinklabs-io/handshake-node` |
265- | `image.tag` | Image tag (never `latest`) | `0.1.1 -rc1` |
266- | `network` | Handshake network : ` main` , `regtest`, `simnet`, `testnet` | `main` |
296+ | `image.tag` | Image tag (never `latest`) | `0.2.0 -rc1` |
297+ | `network` | Handshake network : ` main` , `regtest` | `main` |
267298| `persistence.size` | PVC size | `200Gi` |
268299| `persistence.storageClass` | StorageClass name | cluster default |
269300| `rpc.enabled` | Enable authenticated RPC | `false` |
0 commit comments