Skip to content

Advanced Secrets

voyz edited this page Nov 7, 2023 · 5 revisions

Docker Swarm with Docker Secrets

This section discusses running an instance of IBeam inside a locked Docker Swarm, and using the Docker Swarm facilities for managing secrets. Please refer to the following articles for in-depth details on Docker Swarm, locking the swarm, and using Docker secrets.

It's important to understand that if you fail to lock your swarm then it's possible for an attacker to read the encryption key for the swarm. That in turn would allow them to decrypt any secrets stored in your swarm.

Once you have a locked Docker Swarm instance initialized, you can create the secrets. On your host system create two secure (meaning not world-readable) files containing your Interactive Brokers account name and password:

  1. ib.account.txt
  2. ib.password.txt

Next, inject these secrets into the Docker Swarm by using the docker secret create command.

docker secret create IBEAM_ACCOUNT_v1 ib.account.txt
docker secret create IBEAM_PASSWORD_v1 ib.password.txt

Once you've initialized the secrets delete the original files from your host system.

Next, create an Inputs Directory with a conf.yaml file. The format of this file is discussed on the the Gateway Configuration page. Toward the end of the conf.yaml there is a block to define IPs to trust and reject, e.g.,

...
ips:
  allow:
    - 127.0.0.1
  deny:
    - 0-255.*.*.*

The example above grants access from the local loopback interface, 127.0.0.1, and denies all other addresses (0-255.*.*.*).

To deploy IBeam as a service named 'ibeam' we will use the docker service create command.

docker service create \
    --name ibeam \
    --network host \
    --publish published=5000,target=5000,mode=host \
    --publish published=5001,target=5001,mode=host \
    --secret source=IBEAM_ACCOUNT_v1,uid=1000,gid=1000,mode=0400 \
    --secret source=IBEAM_PASSWORD_v1,uid=1000,gid=1000,mode=0400 \
    --env IBEAM_SECRETS_SOURCE=fs \
    --env IBEAM_ACCOUNT=/run/secrets/IBEAM_ACCOUNT_v1 \
    --env IBEAM_PASSWORD=/run/secrets/IBEAM_PASSWORD_v1 \
    --mount type=bind,source=/path/to/inputs/directory,target=/srv/inputs,ro=true \
    voyz/ibeam:latest

Note that you need to change the /path/to/inputs/directory in the --mount parameter of this example to the actual filesystem path you created for your Inputs Directory.

Docker will prepare the ibeam container by writing the secrets into the container's tmpfs filesystem /run/secrets/. When IBeam starts it will read the file paths indicated via the environment variables IBEAM_ACCOUNT and IBEAM_PASSWORD.

You can verify that the container is running by using docker ps and docker logs.

If you examine the output of the docker ps command we run below, you will see at the far right it lists the name of the running container as ibeam.1.q4jovvg0bsu7svzak17lrm22e. We'll have to specify that full name when we call docker logs in the subsequent command.

$ docker ps
CONTAINER ID   IMAGE         COMMAND               CREATED          STATUS          PORTS                                       NAMES
bde337ce7216   test:latest   "/srv/ibeam/run.sh"   54 seconds ago   Up 52 seconds   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   ibeam.1.q4jovvg0bsu7svzak17lrm22e

$ docker logs ibeam.1.q4jovvg0bsu7svzak17lrm22e
2022-06-10 14:09:01,642|I| ############ Starting IBeam version 0.4.0 ############
2022-06-10 14:09:01,643|I| Custom conf.yaml found and will be used by the Gateway
2022-06-10 14:09:01,646|I| Secrets source: fs
2022-06-10 14:09:01,647|I| Gateway not found, starting new one...
...
2022-06-10 14:09:02,654|I| Gateway started with pid: 12
2022-06-10 14:09:03,826|I| No active sessions, logging in...
2022-06-10 14:09:15,845|I| Authentication process succeeded
2022-06-10 14:09:19,146|I| Gateway running and authenticated.
2022-06-10 14:09:19,167|I| Starting maintenance with interval 60 seconds

Once IBeam has started, verify the Gateway is running by sending a request with curl.

curl -X GET "https://localhost:5000/v1/api/one/user" -k

Docker Stack

You can also manage deployment of the IBeam service into Docker Swarm by using a docker stack managed through a docker-compose.yml file. Below is an example of a docker-compose.yml file specifying the same directives that we used when deploying the Docker service manually.

version: "3.7"

secrets:
  IBEAM_ACCOUNT_v1:
    external: true
  IBEAM_PASSWORD_v1:
    external: true

services:
  ibeam:
    image: "voyz/ibeam:latest"
    environment:
      IBEAM_SECRETS_SOURCE: "fs"
      IBEAM_ACCOUNT: "/run/secrets/IBEAM_ACCOUNT_v1"
      IBEAM_PASSWORD: "/run/secrets/IBEAM_PASSWORD_v1"
    ports:
      - published: 5000
        target: 5000
        mode: host
      - published: 5001
        target: 5001
        mode: host
    secrets:
      - source: "IBEAM_ACCOUNT_v1"
        uid: "1000"
        gid: "1000"
        mode: 0400
      - source: "IBEAM_PASSWORD_v1"
        uid: "1000"
        gid: "1000"
        mode: 0400
    volumes:
      - type: "bind"
        source: "inputs"
        target: "/srv/inputs"
        read_only: true

When accessed from the local host Docker Swarm will route traffic over a gateway interface, docker_gwbridge, that it sets up.

We need to modify the conf.yaml in our Inputs Directory to account for this address.

To determine the gateway interface address, use the docker network inspect command to look at the docker_gwbridge network.

docker network inspect docker_gwbridge
...
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
...

Here we can see the address is 172.18.0.1. Edit your conf.yaml file and add the address to the allow list.

...
ips:
  allow:
    - 127.0.0.1
    - 172.18.0.1
  deny:
    - 0-255.*.*.*

To deploy our Docker stack we will use the docker stack deploy command. Here we're going to name the stack ib.

docker stack deploy -c docker-compose.yml ib

Docker will create the IBeam container as a new service named ib_ibeam. You can verify that the container is running by using docker ps and docker logs.

If you examine the output of the docker ps command we run below, you will see at the far right it lists the name of the running container as ib_ibeam.1.rknycfzbs5i76euv9xfx6mbtw. We'll have to specify that full name when we call docker logs in the subsequent command.

$ docker ps
CONTAINER ID   IMAGE         COMMAND                  CREATED              STATUS              PORTS                                       NAMES
c5ed2dfe4757   ibcp:latest   "/bin/sh -c 'python …"   About a minute ago   Up About a minute   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   ib_ibeam.1.rknycfzbs5i76euv9xfx6mbtw

$ docker logs -f ib_ibeam.1.rknycfzbs5i76euv9xfx6mbtw
2022-06-10 14:24:26,906|I| ############ Starting IBeam version 0.4.0 ############
2022-06-10 14:24:26,646|I| Secrets source: fs
2022-06-10 14:24:26,909|I| Gateway not found, starting new one...
...
2022-06-10 14:24:27,915|I| Gateway started with pid: 11
2022-06-10 14:24:28,817|I| No active sessions, logging in...
2022-06-10 14:24:39,602|I| Authentication process succeeded
2022-06-10 14:24:42,726|I| Gateway running and authenticated.
2022-06-10 14:24:42,733|I| Starting maintenance with interval 60 seconds

Once IBeam has started, verify the Gateway is running by sending a request with curl.

curl -X GET "https://localhost:5000/v1/api/one/user" -k

GCP Secret Manager

If you're deploying IBeam on Google Cloud Platform, you can securely use the Service Account's credentials to access GCP Secret Manager.

This is possible since all types of GCP deployment (currently: Compute Engine, Kubernetes, Cloud Run and Cloud Functions) are running within a context that has a Service Account available. IBeam can query Google's metadata server when running on GCP for the Service Account's access token, and use that to read secrets from the Secret Manager.

This decouples the IBKR credentials from the IBeam deployment and as such is the more secure way of deploying IBeam, when compared to using environment variables.

Guide

  1. Enable GCP Secret Manager and add your IBKR credentials as secrets, eg. IBEAM_ACCOUNT, IBEAM_PASSWORD:

    image

    You'll later need to use the name of your secrets as IBeam environment variables.

  2. Ensure the Service Account used to deploy IBeam has the following credentials:

    1. Secret Manager Secret Accessor
      • resourcemanager.projects.get
      • resourcemanager.projects.list
      • secretmanager.versions.access
  3. Make sure you have available:

    1. [YOUR GCP PROJECT NAME]: the name of your GCP project
    2. [YOUR IBEAM ACCOUNT SECRET NAME]: secret containing your IBKR account name
    3. [YOUR IBEAM PASSWORD SECRET NAME]: secret containing your IBKR password
    4. (optionally) [YOUR IBEAM KEY SECRET NAME]: secret containing your IBKR password key
  4. When deploying IBeam, specify the following environment variables:

    1. IBEAM_SECRETS_SOURCE=gcp_secrets_manager
    2. IBEAM_GCP_SECRETS_URL=https://secretmanager.googleapis.com/v1/projects/[YOUR GCP PROJECT NAME]/secrets
    3. IBEAM_ACCOUNT=[YOUR IBEAM ACCOUNT SECRET NAME]/versions/1
    4. IBEAM_PASSWORD=[YOUR IBEAM PASSWORD SECRET NAME]/versions/1
    5. (optionally) IBEAM_KEY=[YOUR IBEAM KEY SECRET NAME]/versions/1

This should set your IBeam instance with an ability to read the IBKR credentials from the GCP Secret Manager.