dcrd/contrib/docker
Dave Collins 563e89099e
build/contrib: Improve docker support.
This significantly reworks the docker support to provide build a
lightweight non-root distroless container image based on scratch.  It
employs a multi-stage build that downloads and builds the latest source
code, compresses the resulting binaries, and then produces the final
image based on scratch that only includes the Decred-specific binaries.

It must be noted that there are some still remaining TODO items in the
documentation as well as the Dockerfile that will need to be handled by
a future commit, but the changes are being submitted now to allow
another contributor to finish up those aspects.

The following is an overview of the changes:

- Removes existing Dockerfile and Dockerfile.alpine
- Introduces a new Dockerfile under contrib/docker with the following
  properties:
  - Runs as a non-root user
  - Uses a static UID:GID of 10000:10000
    - Note that using UIDs/GIDs below 10000 for container users is a
      security risk on several systems since a hypothetical attack which
      allows escalation outside of the container might otherwise
      coincide with an existing user's UID or existing group's GID which
      has additional permissions
  - The image is based on scratch image (aka completely empty) and only
    includes the Decred-specific binaries which means there is no shell
    or any other binaries available if an attacker were to somehow
    manage to find a remote execution vulnerability exploit in a Decred
    binary
- Introduces code to build an entrypoint for the image since it is based
  on scratch and thus has no shell for that purpose
- Adds contrib/docker/README.md
- Updates README.md in the main directory to account for changes
  - There is still outstanding work to be done here and thus has several
    TODOs
- Updates contrib/README.md to call out the new addition
2021-09-18 13:17:41 -05:00
..
entrypoint build/contrib: Improve docker support. 2021-09-18 13:17:41 -05:00
Dockerfile build/contrib: Improve docker support. 2021-09-18 13:17:41 -05:00
README.md build/contrib: Improve docker support. 2021-09-18 13:17:41 -05:00

Decred Full Node for Docker

ISC License

Overview

This provides all of the necessary files to build your own lightweight non-root container image based on scratch that provides dcrd, dcrctl, promptsecret and gencerts.

The approach used by the primary Dockerfile is to employ a multi-stage build that downloads and builds the latest source code, compresses the resulting binaries, and then produces a final image based on scratch that only includes the Decred-specific binaries.

Container Image Security Properties

The provided Dockerfile places a strong focus on security as follows:

  • Runs as a non-root user
  • Uses a static UID:GID of 10000:10000
    • Note that using UIDs/GIDs below 10000 for container users is a security risk on several systems since a hypothetical attack which allows escalation outside of the container might otherwise coincide with an existing user's UID or existing group's GID which has additional permissions
  • The image is based on scratch image (aka completely empty) and only includes the Decred-specific binaries which means there is no shell or any other binaries available if an attacker were to somehow manage to find a remote execution vulnerability exploit in a Decred binary

Container Environment Variables

  • DECRED_DATA (Default: /home/decred):
    The directory where data is stored inside the container. This typically does not need to be changed.

  • DCRD_NO_FILE_LOGGING (Default: true):
    Controls whether or not dcrd additionally logs to files under DECRED_DATA. Logging is only done via stdout by default in the container since that is standard practice for containers.

  • DCRD_ALT_DNSNAMES: (Default: None) Adds alternate server DNS names to the server certificate that is automtically generated for the RPC server. This is important when attempting to access the RPC from external sources because TLS is required and clients verify the server name matches the certificate.

Usage

Quick Start

The following are typical commands to get up and going quickly. The remaining sections describe things more in depth.

Note: These series of commands have you define and use environment variables in order to help make it clear exactly what every command line argument refers to. However, this means that if you close the shell, the commands will no longer work as written because those environment variables will no longer exist. You may wish to replace all instances of "${...}" with the associated concrete value.

  1. Build the base image with a tag to make it easy to reference later. These commands all use yourusername/dcrd for the image tag, but you should replace yourusername with your username or something else unique to you so you can easily identify it as being one of your images:

    $ DCRD_IMAGE_NAME="yourusername/dcrd"
    $ docker build -t "${DCRD_IMAGE_NAME}" -f contrib/docker/Dockerfile .
    

    NOTE: This MUST be run from the main directory of the dcrd code repo.

  2. Create a data volume and change its ownership to the user id of the user inside of the container so it has the necessary permissions to write to it:

    $ docker volume create decred-data
    $ DECRED_DATA_VOLUME=$(docker volume inspect decred-data -f '{{.Mountpoint}}')
    $ sudo chown -R 10000:10000 "${DECRED_DATA_VOLUME}"
    

    NOTE: The data volume only needs to be created once.

  3. Run dcrd on testnet in the background using the aforementioned data volume to store the blockchain and configuration data along with a name to make it easy to reference later:

    $ DCRD_CONTAINER_NAME="dcrd-testnet"
    $ docker run -d --read-only \
      --name "${DCRD_CONTAINER_NAME}" \
      -v decred-data:/home/decred \
      "${DCRD_IMAGE_NAME}" --testnet --altdnsnames "${DCRD_CONTAINER_NAME}"
    
  4. View the output logs of dcrd with the docker logs command:

    $ docker logs "${DCRD_CONTAINER_NAME}"
    

Starting and Stopping the Container

$ docker stop "${DCRD_CONTAINER_NAME}"
$ docker start "${DCRD_CONTAINER_NAME}"

Preliminaries

TODO: Explain about non-root permissions, network, etc

Basics

TODO: Finish documenting all the details here

  • Dockerfile
    Provides a user-contributed configuration file for building a container image that consists of dcrd, dcrctl, gencerts, and promptsecret along with exposed ports for dcrd's RPC server. It is based on scratch and runs as a non-root container.

TODO: It would probably be nice to provide some variants such as:

  • Dockerfile.release that either grabs the latest release code or checks out the latest release tag instead of building the master branch
  • Dockerfile.local that builds an image using the code in the build context instead of cloning and building the latest master branch

Interacting via RPC

TODO: With shared network...

Assuming the environment variables and configuration matches what was outlined in the quick start section:

$ docker run --rm --network container:"${DCRD_CONTAINER_NAME}" --read-only \
  -v decred-data:/home/decred \
  "${DCRD_IMAGE_NAME}" dcrctl --testnet getblockchaininfo

TODO: With user-defined network...

Assuming the environment variables and configuration matches what was outlined in the quick start section:

TODO: Would need to remove existing container and start new one with --network decred as follows...

NOTE: The network volume only needs to be created once.

$ docker network create decred
$ docker stop "${DCRD_CONTAINER_NAME}"
$ docker rm "${DCRD_CONTAINER_NAME}"
$ docker run -d --read-only \
  --network decred \
  --name "${DCRD_CONTAINER_NAME}" \
  -v decred-data:/home/decred \
  "${DCRD_IMAGE_NAME}" --testnet --altdnsnames "${DCRD_CONTAINER_NAME}"
$ docker run --rm --read-only \
  --network decred \
  -v decred-data:/home/decred \
  "${DCRD_IMAGE_NAME}" dcrctl --testnet --rpcserver "${DCRD_CONTAINER_NAME}" getblockchaininfo

TODO: Accessing the RPC server from remote services outside of a docker network

TODO: Needs to be running with port mapped...aka:

$ docker run -d --read-only \
  --name "${DCRD_CONTAINER_NAME}" \
  -v decred-data:/home/decred \
  -p 127.0.0.1:19109:19109 \
  "${DCRD_IMAGE_NAME}" --testnet --altdnsnames "${DCRD_CONTAINER_NAME}"

TODO: From other machine such as the host machine (not inside a docker container)... TODO: sudo required here because the data volume needs the perms of the uid/gid inside the container which the local user on the host won't have access too.. could alternatively use docker cp to copy the cert and conf file out of the container...

$ dcrdrpcuser=$(sudo cat "${DECRED_DATA_VOLUME}/.dcrd/dcrd.conf" | grep rpcuser= | cut -c9-)
$ dcrdrpcpass=$(sudo cat "${DECRED_DATA_VOLUME}/.dcrd/dcrd.conf" | grep rpcpass= | cut -c9-)
$ sudo curl --cacert "${DECRED_DATA_VOLUME}/.dcrd/rpc.cert" --user "${dcrdrpcuser}:${dcrdrpcpass}" \
  --data-binary '{"jsonrpc":"1.0","id":"1","method":"getbestblock","params":[]}' \
  https://127.0.0.1:19109

Troubleshooting / Common Issues

TODO

TODO: Write permission errors

TODO

TODO: Remote access certificate errors

TODO