2021/12/04

tmkmsをdockerでビルドしてローカルのdocker-compose環境で利用してみる

tendermintcosmos-sdkrustdocker

概要

iqlusioninc/tmkmsのDockerfileを作成して、docker-composeで動かせるようにしました。

tmkmsとは

iqlusioninc/tmkmsはtendermintというコンセンサスアルゴリズムの署名管理に使うことができます。

主に以下の用途で使われます

  • プライベートキー保管のセキュリティ強化
  • 2重署名の回避

プライベートキー保管のセキュリティ強化

ハードウォレットのキーを署名に利用することができようになります。

プライベートキーをハードディスクやSSDなどのローカルデバイスではなく、セキュリティレベルの高いUSBデバイスであるハードウェアウォレット上に(のみ)保管できます。

これによってプライベートキーの読み取りへのハードルが上がり、ノードの運用者と鍵の管理者を分離することができるようになります。(もちろん単純にハッキングされた時の耐性が高くなるというメリットもあります。)

2重署名を回避できる

  • (tendermintでは)2重署名を行ってしまうと、コンセンサスを形成しようとしているネットワークに害を及ぼしてしまいます。(ビザンチンな振る舞いと認定されます)
  • cosmos-sdkなどのtendermintを利用しているブロックチェーンでは、そのような振る舞いが特定されるとペナルティ(stakingしているトークンを失う等)が課せられます。
  • また、cosmos-sdkでは2重署名だけでなく、nodeが停止して、コンセンサスに対する貢献行動が一定時間停止した場合にも似たようなペナルティが発生します。
  • 従って、コンセンサスを行うノード(バリデーターノードと呼びます)の可用性を高めようとする動機が生まれます。(複数サーバーで運用したり)
  • 複数サーバーで運用したりすると、どうしても2重署名しやすくなりますので、それを回避しようとするために使います。

Dockerfile

以下の内容で、docker imageをビルドします。

カスタマイズポイントとしては以下のようなものになります。

  • RUST_VERSION(1.54.0ですが、1.46以降であれば良いようです。)
  • TMKMS_VERSION(現状では0.10.1が最新)(ここを変更したらTMKMS_SRC_SHA256も変更する必要があります。)
  • ca-certificatesbashは入れる必要がありませんが、度々利用するので入れています。
# @see https://github.com/rust-lang/docker-rust
FROM alpine:3.14 as builder

# install utilities
RUN apk add --update alpine-sdk cmake clang libusb-dev
RUN apk add --no-cache ca-certificates

ENV RUSTUP_HOME=/usr/local/rustup \
    CARGO_HOME=/usr/local/cargo \
    PATH=/usr/local/cargo/bin:$PATH \
    RUST_VERSION=1.54.0 \
    TMKMS_VERSION=0.10.1 \
    TMKMS_SRC_SHA256=184f483c09a9cfcada58fe6aa3c0370dba0c8ff38df5d264fca5a1cc1033b602

RUN set -eux; \
    apkArch="$(apk --print-arch)"; \
    case "$apkArch" in \
        x86_64) rustArch='x86_64-unknown-linux-musl'; rustupSha256='bdf022eb7cba403d0285bb62cbc47211f610caec24589a72af70e1e900663be9' ;; \
        aarch64) rustArch='aarch64-unknown-linux-musl'; rustupSha256='89ce657fe41e83186f5a6cdca4e0fd40edab4fd41b0f9161ac6241d49fbdbbbe' ;; \
        *) echo >&2 "unsupported architecture: $apkArch"; exit 1 ;; \
    esac; \
    url="https://static.rust-lang.org/rustup/archive/1.24.3/${rustArch}/rustup-init"; \
    wget "$url"; \
    echo "${rustupSha256} *rustup-init" | sha256sum -c -; \
    chmod +x rustup-init; \
    ./rustup-init -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION --default-host ${rustArch}; \
    rm rustup-init; \
    chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \
    rustup --version; \
    cargo --version; \
    rustc --version;


# build from source
WORKDIR /tmkms

RUN set -ex; \
    wget https://github.com/iqlusioninc/tmkms/archive/refs/tags/v${TMKMS_VERSION}.tar.gz -O tmkms.tar.gz; \
    echo "${TMKMS_SRC_SHA256}  /tmkms/tmkms.tar.gz" | sha256sum -c -; \
    tar -zxvf "tmkms.tar.gz"; \
    rm -f tmkms.tar.gz; \
    mv tmkms-${TMKMS_VERSION} src; \
    cd src; \
    cargo build --release --features=softsign;


FROM alpine:3.14
COPY --from=builder /tmkms/src/target/release/tmkms /usr/local/bin/tmkms
RUN chmod +x /usr/local/bin/tmkms

# Install ca-certificates
RUN apk add --update ca-certificates

# install utilities
RUN apk add bash

ARG USER_ID
ARG GROUP_ID
ENV HOME /tmkms
ENV USER_ID ${USER_ID:-1000}
ENV GROUP_ID ${GROUP_ID:-1000}

# add our user and group first
RUN addgroup -g ${GROUP_ID} tmkms; \
    adduser -D -u ${USER_ID} -G tmkms -h /tmkms -s "/bin/bash" tmkms;

# install su-exec
RUN apk add --no-cache su-exec; \
    su-exec tmkms true;

WORKDIR /tmkms
EXPOSE 26658

CMD ["tmkms", "start"]

事前準備

docker-compose で動かす前に色々と準備します。

上記のDockerfileをビルドしてtmkms:latestのimageを作っておきます。

$ docker build -t tmkms:latest -f Dockerfile .

次に、事前にconfigファイルなどを作成します。

% docker run -it --rm -v $(pwd)/.docker-data/tmkms:/tmkms/.tmkms tmkms:latest bash
# container内で以下を実行します。
$ tmkms init -n cosmoshub /tmkms/.tmkms
# 必要に応じて、 `tmkms softsign import` したり `tmkms softsign keygen` したりします。
# また必要に応じて、/tmkms/.tmkms/tmkms.tomlも編集していきます。

/tmkms/.tmkms/tmkms.tomlを自分のノードの内容に応じて編集します。

[[chain]]
id = "my-chain"
key_format = { type = "bech32", account_key_prefix = "xxpub", consensus_key_prefix = "xxconspub" }
state_file = "/tmkms/.tmkms/state/my-chain-consensus.json"

[[providers.softsign]]
chain_ids = ["my-chain"]
key_type = "consensus"
path = "/tmkms/.tmkms/secrets/your-validator-secret.key"

[[validator]]
addr = "tcp://192.168.10.2:26658"
chain_id = "my-chain"
secret_key = "/tmkms/.tmkms/secrets/secret_connection.key"
protocol_version = "v0.34"
reconnect = true

docker-compose

以下のような感じになりました。

  • validatorに指定しているプログラムはtendermintベースの任意のバリデータノードプログラムを想定しています。(実際にはgaiadなどを指定します)
version: '3'
services:
  tmkms:
    container_name: tmkms
    image: "tmkms:latest"
    command: >
      tmkms start -c /tmkms/.tmkms/tmkms.toml
    ports:
      - "26659:26658"
    volumes:
      - .docker-data/tmkms:/tmkms/.tmkms
    networks:
      localnet:
        ipv4_address: 192.168.10.1
  validator:
    container_name: validator
    image: "your-sample-tendermint-validator:latest"
    command: >
      your-daemon start
    ports:
      - "26658:26658"
    volumes:
      - .docker-data/validator:/your-daemon/.your-daemon
    networks:
      localnet:
        ipv4_address: 192.168.10.2
    depends_on:
      - tmkms
networks:
  localnet:
    driver: bridge
    ipam:
      driver: default
      config:
        -
          subnet: 192.168.10.0/16

これらの設定で、ローカル環境でtmkmsを活用したバリデータノードのテスト環境の構築が確認できました。

以上です。