diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..df514015 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +# Before the docker CLI sends the context to the docker daemon, it looks for a file +# named .dockerignore in the root directory of the context. If this file exists, the +# CLI modifies the context to exclude files and directories that match patterns in it. +# +# You may want to specify which files to include in the context, rather than which +# to exclude. To achieve this, specify * as the first pattern, followed by one or +# more ! exception patterns. +# +# https://docs.docker.com/engine/reference/builder/#dockerignore-file + +# Exclude everything: +# +* + +# Now un-exclude required files and folders: +# +!go.mod +!go.sum +!main.go +!docker +!cmd +!common +!frontend +!parser +!walletrpc diff --git a/.env b/.env new file mode 100644 index 00000000..9d71d03b --- /dev/null +++ b/.env @@ -0,0 +1,13 @@ +ZCASHD_RPCUSER=zcashrpc +ZCASHD_RPCPASSWORD=${PASSWORD_ZCASHD} +ZCASHD_RPCPORT=38232 +ZCASHD_ALLOWIP=0.0.0.0/0 +ZCASHD_DATADIR=/srv/zcashd/.zcash +ZCASHD_PARMDIR=/srv/zcashd/.zcash-params +ZCASHD_GEN=0 +GF_SECURITY_ADMIN_USER=admin +GF_SECURITY_ADMIN_PASSWORD=${PASSWORD_GRAFANA} +LWD_GRPC_PORT=9067 +LWD_HTTP_PORT=9068 +ZCASHD_CONF_PATH=/srv/lightwalletd/zcash.conf +ZEBRA_RPC_PORT=8232 \ No newline at end of file diff --git a/.env.template b/.env.template index 91105242..9d71d03b 100644 --- a/.env.template +++ b/.env.template @@ -10,3 +10,4 @@ GF_SECURITY_ADMIN_PASSWORD=${PASSWORD_GRAFANA} LWD_GRPC_PORT=9067 LWD_HTTP_PORT=9068 ZCASHD_CONF_PATH=/srv/lightwalletd/zcash.conf +ZEBRA_RPC_PORT=8232 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 8598be4a..d944b2c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,74 @@ -FROM golang:1.22 AS lightwalletd_base +ARG APP_HOME=/srv/lightwalletd +ARG ZCASHD_CONF_PATH=$APP_HOME/zcash.conf -ADD . /go/src/github.com/zcash/lightwalletd -WORKDIR /go/src/github.com/zcash/lightwalletd +## +## Builder +## +# Create layer in case you want to modify local lightwalletd code +FROM golang:1.22 AS build -RUN make \ - && /usr/bin/install -c ./lightwalletd /usr/local/bin/ \ - && mkdir -p /var/lib/lightwalletd/db \ - && chown 2002:2002 /var/lib/lightwalletd/db +# Create and change to the app directory. +WORKDIR /app + +# Retrieve application dependencies. +# This allows the container build to reuse cached dependencies. +# Expecting to copy go.mod and if present go.sum. +COPY go.mod ./ +COPY go.sum ./ + +# Do not use `go get` as it updates the requirements listed in your go.mod file. +# `go mod download` does not add new requirements or update existing requirements. +RUN go mod download + +# Copy local code to the container image. +COPY . ./ + +# Build and install the binary. +RUN go build -v -o /usr/local/bin/lightwalletd + +## +## Runtime +## +FROM debian:bookworm-slim as runtime + +# Get the needed ARGs values +ARG APP_HOME +ARG ZCASHD_CONF_PATH +ARG LWD_GRPC_PORT=9067 +ARG LWD_HTTP_PORT=9068 ARG LWD_USER=lightwalletd ARG LWD_UID=2002 -RUN useradd --home-dir "/srv/$LWD_USER" \ - --shell /bin/bash \ - --create-home \ - --uid "$LWD_UID" \ - "$LWD_USER" +# Always run a container with a non-root user. Running as root inside the container is running as root in the Docker host +# If an attacker manages to break out of the container, they will have root access to the host +RUN groupadd --gid ${LWD_UID} ${LWD_USER} && \ + useradd --home-dir ${APP_HOME} --no-create-home --gid ${LWD_USER} --uid ${LWD_UID} --shell /bin/sh ${LWD_USER} + +# Create the directory for the database and certificates to keep backwards compatibility +RUN mkdir -p /var/lib/lightwalletd/db && \ + mkdir -p /secrets/lightwallted && \ + chown ${LWD_USER}:${LWD_USER} /var/lib/lightwalletd/db && \ + chown ${LWD_USER}:${LWD_USER} /secrets/lightwallted + +WORKDIR ${APP_HOME} + +COPY --from=build /usr/local/bin/lightwalletd /usr/local/bin +COPY ./docker/cert.key ./ +COPY ./docker/cert.pem ./ + +RUN set -ex; \ + { \ + echo "rpcuser=zcashrpc"; \ + echo "rpcpassword=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo ''`" \ + echo "rpcbind=zcashd"; \ + echo "rpcport=3434"; \ + } > "${ZCASHD_CONF_PATH}" + +EXPOSE ${LWD_GRPC_PORT} +EXPOSE ${LWD_HTTP_PORT} -WORKDIR "/srv/$LWD_USER" +USER ${LWD_USER} ENTRYPOINT ["lightwalletd"] -CMD ["--help"] +CMD ["--tls-cert=cert.pem", "--tls-key=cert.key", "--grpc-bind-addr=0.0.0.0:9067", "--http-bind-addr=0.0.0.0:9068", "--log-file=/dev/stdout", "--log-level=7"] diff --git a/Makefile b/Makefile index eb118bbd..fc9ba76b 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ coverage: # Generate code coverage report coverage_report: coverage - go tool cover -func=coverage.out + go tool cover -func=coverage.out # Generate code coverage report in HTML coverage_html: coverage @@ -102,31 +102,33 @@ lwd-api.html: walletrpc/compact_formats.proto walletrpc/service.proto # Generate docker image docker_img: - docker build -t zcash_lwd_base . + docker build --target runtime --tag zcash_lwd_base . # Run the above docker image in a container docker_img_run: - docker run -i --name zcashdlwd zcash_lwd_base + docker run --detach -it --name zcashdlwd zcash_lwd_base # Execute a bash process on zcashdlwdcontainer docker_img_bash: docker exec -it zcashdlwd bash +# TODO: Check these instructions as `zcashd` and `zcash-cli` binaries are not installed in the container # Start the zcashd process in the zcashdlwd container -docker_img_run_zcashd: - docker exec -i zcashdlwd zcashd -printtoconsole +# docker_img_run_zcashd: +# docker exec -i zcashdlwd zcashd -printtoconsole # Stop the zcashd process in the zcashdlwd container -docker_img_stop_zcashd: - docker exec -i zcashdlwd zcash-cli stop +# docker_img_stop_zcashd: +# docker exec -i zcashdlwd zcash-cli stop # Start the lightwalletd server in the zcashdlwd container docker_img_run_lightwalletd_insecure_server: docker exec -i zcashdlwd server --no-tls-very-insecure=true --conf-file /home/zcash/.zcash/zcash.conf --log-file /logs/server.log --bind-addr 127.0.0.1:18232 +# TODO: remove this command as its destructive for people using Docker for other purposes # Remove and delete ALL images and containers in Docker; assumes containers are stopped -docker_remove_all: - docker system prune -f +# docker_remove_all: +# docker system prune -f # Get dependencies dep: diff --git a/docker-compose.zebra.yml b/docker-compose.zebra.yml new file mode 100644 index 00000000..2657ad2c --- /dev/null +++ b/docker-compose.zebra.yml @@ -0,0 +1,90 @@ +name: zebra-lwd + +services: + lightwalletd: + image: electriccoinco/lightwalletd + platform: linux/amd64 + build: + context: . + target: runtime + depends_on: + zebra: + condition: service_started + restart: unless-stopped + deploy: + resources: + reservations: + cpus: "2" + memory: 4G + configs: + - source: lwd_config + target: ${ZCASHD_CONF_PATH} + uid: '2002' # Golang's container default user uid + gid: '2002' # Golang's container default group gid + volumes: + - litewalletd-data:/var/lib/lightwalletd/db + #! This setup with --no-tls-very-insecure is only for testing purposes + #! For production environments follow the guidelines here: https://github.com/zcash/lightwalletd#production-usage + command: > + --no-tls-very-insecure + --grpc-bind-addr=0.0.0.0:${LWD_GRPC_PORT} + --http-bind-addr=0.0.0.0:${LWD_HTTP_PORT} + --zcash-conf-path=${ZCASHD_CONF_PATH} + --data-dir=/var/lib/lightwalletd/db + --log-file=/dev/stdout + --log-level=7 + ports: + - "127.0.0.1:${LWD_GRPC_PORT}:${LWD_GRPC_PORT}" # gRPC + - "127.0.0.1:${LWD_HTTP_PORT}:${LWD_HTTP_PORT}" # HTTP + + zebra: + image: zfnd/zebra + platform: linux/amd64 + restart: unless-stopped + deploy: + resources: + reservations: + cpus: "4" + memory: 16G + environment: + - RPC_PORT=${ZEBRA_RPC_PORT} + logging: + options: + max-size: "10m" + max-file: "5" + #! Uncomment the `configs` mapping below to use the `zebrad.toml` config file from the host machine + #! NOTE: This will override the zebrad.toml in the image and make some variables irrelevant + # configs: + # - source: zebra_config + # target: /etc/zebrad/zebrad.toml + # uid: '2001' # Rust's container default user uid + # gid: '2001' # Rust's container default group gid + volumes: + - zebrad-cache:/var/cache/zebrad-cache + ports: + # Zebra uses the following default inbound and outbound TCP ports + - "127.0.0.1:8233:8233" # Mainnet Network (for peer connections) + - "127.0.0.1:8232:8232" # Opens an RPC endpoint (for wallet storing and mining) + # - "127.0.0.1:18233:18233" # Testnet Network + # - "127.0.0.1:9999:9999" # Metrics + # - "127.0.0.1:3000:3000" # Tracing + healthcheck: + start_period: 1m + interval: 15s + timeout: 10s + retries: 3 + test: ["CMD-SHELL", "curl --data-binary '{\"jsonrpc\":\"2.0\",\"id\":\"curltest\",\"method\":\"getinfo\",\"params\":[]}' -H 'Content-Type: application/json' http://127.0.0.1:8232 || exit 1"] + +configs: + zebra_config: + # Change the following line to point to a zebrad.toml on your host machine + file: ./docker/zebrad.toml + lwd_config: + # Change the following line to point to a zcash.conf on your host machine + file: ./docker/zebra.conf + +volumes: + litewalletd-data: + driver: local + zebrad-cache: + driver: local diff --git a/docker/zebra.conf b/docker/zebra.conf new file mode 100644 index 00000000..f8aa4990 --- /dev/null +++ b/docker/zebra.conf @@ -0,0 +1,5 @@ +rpcuser=zcashrpc +rpcpassword=notsecure +rpcbind=zebra +rpcport=8232 +testnet=0 diff --git a/docs/docker-compose-setup.md b/docs/docker-compose-setup.md index 45920415..2e3b59ab 100644 --- a/docs/docker-compose-setup.md +++ b/docs/docker-compose-setup.md @@ -25,7 +25,7 @@ Copy `.env.example` to `.env` and change any required parameters. |`ZCASHD_GEN`| should zcashd mine? `0` or `1` |`LWD_PORT`| port for lightwalletd to bind to| |`ZCASHD_CONF_PATH`| path for lightwalletd to pick up configuration| - +|`ZEBRA_RPC_PORT`| RPC port for Zebra| ## Populate secret env vars with random values @@ -136,3 +136,27 @@ Edit `docker-compose.yml` to look like ``` When you edit these lines in `docker-compose.yml`, stopping/starting the individual `lightwalletd` container doesn't actually make the changes happen—you have to stop/start the whole `docker-compose` ensemble of containers because the ports/network config stuff lives at that level and doesn't seem to be affected by individual container stop/starts. Also if you want to expose `lightwalletd` to the whole internet, you don't need to specify an IP address, `0.0.0.0` works as it should. + +## Running lightwalletd with Zebra + +### Using the docker-compose.zebra.yml + +You can start the `lightwalletd` with `zebra` by using the `docker-compose.zebra.yml` file. + +```bash +docker-compose -f docker-compose.zebra.yml up -d +``` + +If you'd like to build the lightwalletd image before starting the containers, you can do so with the following command: + +```bash +docker-compose -f docker-compose.zebra.yml up --build -d +``` + +Inside the `.env.template` there's a variable `ZEBRA_RPC_PORT`, defaulting to `8232`, which is the port that `zebra` will be listening on for RPC requests. This port is exposed to the host machine and can be accessed by the `lightwalletd` container. + +If you'd like to confirm if the RPC port is exposed and working as expected, you can run the following command: + +```bash +curl --data-binary '{"jsonrpc":"2.0","id":"curltest","method":"getinfo","params":[]}' -H 'Content-Type: application/json' http://127.0.0.1:8232 +```