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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions