Skip to content

Validate SeiNode.spec.chainId with a Pattern regex (shell injection sink in seid-init) #236

@bdchatham

Description

@bdchatham

Problem

`SeiNodeSpec.ChainID` is validated only with `MinLength=1` (seinode_types.go:14-16). The value is interpolated unquoted via `%s` into bash `-c` scripts at:

  • `internal/noderesource/noderesource.go::buildSeidInitContainer` (the seid-init script)
  • `internal/task/bootstrap_resources.go::bootstrapSeidInitContainer` (the bootstrap variant)

A ChainID like `foo --home /etc; rm -rf /; echo ` passes admission and lands inside `bash -c` as an injection sink.

Surfaced during cross-review of #234 by the security-specialist agent. The PR doesn't widen this surface; it touches adjacent code, which is the moment to flag.

Impact

Anyone with `create/update seinodes` RBAC can execute arbitrary commands inside the seid-init container (uid 65532, with write access to the data PVC and any projected signing-key volumes in the bootstrap variant). The blast radius is the data PVC + any projected secrets mounted into the bootstrap pod.

Today, `create seinodes` is gated to platform operators on harbor. Engineer-cell namespaces have admin via `engineer-service-account` but typically don't author SeiNode CRs directly (they go through seictl + Flux). Risk is bounded but real — RBAC misconfiguration would open the path.

Relevant experts

  • security-specialist (threat model owner)
  • kubernetes-specialist (CRD validation marker authorship)

Proposed approach

Add a kubebuilder validation marker to `SeiNodeSpec.ChainID`:

```go
// +kubebuilder:validation:Pattern=^[a-zA-Z0-9_.-]+$
// +kubebuilder:validation:MaxLength=50
```

Pattern mirrors cosmos-sdk's accepted chain-ID character class. Length cap is conservative; real chain IDs are <30 chars.

Regenerate CRDs via `make manifests generate`. Existing chains: audit production for any chain ID that wouldn't match (`kubectl get seinodes -A -o jsonpath=...`). None expected — current convention is lowercase-alphanumeric.

Acceptance criteria

  • Pattern + MaxLength markers added to `ChainID`
  • CRD regenerated; `config/crd/` and `manifests/` updated
  • Existing chains audit confirms no breakage
  • Test: SeiNode with shell-metacharacter ChainID is rejected at admission

Out of scope

  • Quoting the ChainID inside the bash script (defense-in-depth, separate consideration)
  • Migrating the init scripts away from `bash -c` (larger refactor)

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions