Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Set up Go
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
with:
go-version: '1.24'

- name: Run tests
run: go test ./...

govulncheck:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Set up Go
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
with:
go-version: '1.24'

- name: Run govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
30 changes: 23 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,55 @@ on:
permissions:
contents: write
packages: write
id-token: write
attestations: write

jobs:
goreleaser:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
with:
go-version: '1.23'
go-version: '1.24'

- name: Run govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

- name: Install cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v7.0.0
with:
distribution: goreleaser
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Generate SLSA build provenance attestation
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: 'dist/walship_*'
17 changes: 17 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ archives:
checksum:
name_template: 'checksums.txt'

signs:
- cmd: cosign
artifacts: checksum
output: true
args:
- "sign-blob"
- "--yes"
- "--output-signature=${signature}"
- "--output-certificate=${certificate}"
- "${artifact}"

sboms:
- id: archive
artifacts: archive
- id: source
artifacts: source

snapshot:
version_template: "{{ .Tag }}-next"

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.23-alpine AS builder
FROM golang:1.24-alpine AS builder

WORKDIR /app

Expand Down
65 changes: 60 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ FILE=walship_Linux_x86_64.tar.gz # pick the tarball for your OS/arch
curl -LO https://github.com/bft-labs/walship/releases/latest/download/$FILE
curl -LO https://github.com/bft-labs/walship/releases/latest/download/checksums.txt

# Verify (Linux)
# Verify checksum (Linux)
grep "$FILE" checksums.txt | sha256sum --check -

# Verify (macOS)
# Verify checksum (macOS)
grep "$FILE" checksums.txt | shasum -a 256 --check -

# Install
Expand All @@ -44,7 +44,43 @@ sudo mv walship /usr/local/bin/
```

Other platforms: see [Releases](https://github.com/bft-labs/walship/releases).
Checksums (`checksums.txt`) are published with each release.

### Verifying Release Integrity

Every release includes cryptographic signatures and build provenance so you can verify that the binary was built by our CI pipeline and has not been tampered with.

**1. Cosign signature verification (recommended)**

Requires [cosign](https://docs.sigstore.dev/cosign/system_config/installation/). This verifies that `checksums.txt` was signed by our GitHub Actions release workflow using keyless (OIDC) signing:

```bash
cosign verify-blob \
--signature checksums.txt.sig \
--certificate checksums.txt.pem \
--certificate-identity-regexp "^https://github.com/bft-labs/walship/" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checksums.txt
```

**2. SLSA build provenance verification**

Requires [GitHub CLI](https://cli.github.com/). This verifies SLSA Build Level 2 provenance — proof that the binary was produced by our CI from the correct source commit:

```bash
gh attestation verify ./walship -R bft-labs/walship
```

**3. SHA256 checksum verification**

```bash
# Linux
grep "$FILE" checksums.txt | sha256sum --check -

# macOS
grep "$FILE" checksums.txt | shasum -a 256 --check -
```

All three checks should pass before deploying the binary to a validator node.

## Quick Start (Not Recommended)

Expand All @@ -71,7 +107,8 @@ Description=Walship
After=network-online.target

[Service]
User=validator
User=walship
Group=walship
ExecStart=/usr/local/bin/walship \
--node-home /home/validator/.osmosisd \
--chain-binary-path /usr/local/bin/osmosisd \
Expand All @@ -80,11 +117,29 @@ ExecStart=/usr/local/bin/walship \
Restart=always
RestartSec=5

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
ReadOnlyPaths=/home/validator/.osmosisd/data/log.wal
ReadWritePaths=/home/validator/.osmosisd/data/log.wal/status.json

[Install]
WantedBy=multi-user.target
```

Adjust `User`, `--node-home`, `--chain-binary-path`, `--chain-id`, and `--auth-key` to match your environment. If you prefer not to keep the key in the unit file, you can supply `WALSHIP_AUTH_KEY` (and other flags) via an `EnvironmentFile`.
Adjust `User`, `Group`, `--node-home`, `--chain-binary-path`, `--chain-id`, `--auth-key`, and the `ReadOnlyPaths`/`ReadWritePaths` to match your environment. If you prefer not to keep the key in the unit file, you can supply `WALSHIP_AUTH_KEY` (and other flags) via an `EnvironmentFile`.

**Security notes:**
- We recommend running walship as a **dedicated `walship` user** (not the `validator` user) with only the minimum file permissions needed. Grant read access to the WAL directory and the chain binary only.
- The hardening directives (`NoNewPrivileges`, `ProtectSystem=strict`, etc.) prevent privilege escalation, block device access, and restrict the filesystem to read-only except for explicitly allowed paths.
- Create the dedicated user: `sudo useradd -r -s /usr/sbin/nologin walship && sudo usermod -aG validator walship`

Enable and start:

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/bft-labs/walship

go 1.22
go 1.24

require (
github.com/fsnotify/fsnotify v1.9.0
Expand Down
4 changes: 2 additions & 2 deletions internal/agent/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ func TestDefaultConfig(t *testing.T) {
if cfg.ServiceURL != DefaultServiceURL {
t.Errorf("ServiceURL = %v, want %v", cfg.ServiceURL, DefaultServiceURL)
}
if cfg.MaxBatchBytes != 4<<20 {
t.Errorf("MaxBatchBytes = %v, want 4MB", cfg.MaxBatchBytes)
if cfg.MaxBatchBytes != 16<<20 {
t.Errorf("MaxBatchBytes = %v, want 16MB", cfg.MaxBatchBytes)
}
}

Expand Down