structurizr / cli

A command line utility for Structurizr.
https://docs.structurizr.com/cli
Apache License 2.0
491 stars 75 forks source link

Support reading command line options from the environment and Docker secrets #73

Closed Midnighter closed 2 years ago

Midnighter commented 2 years ago

I would like to request the following feature:

Currently, when interacting with a remote workspace, the workspace ID, API key, and API secret have to be supplied as command line options. This can be cumbersome and has the risk of easily exposing them. I would therefore like to suggest that you support reading option values from the environment and even better from Docker secrets, i.e., files. What I imagine is being able to use the cli with a docker-compose file that looks something like this:

1) Environment

services:
  cli:
    environment:
      - "WORKSPACE_ID=xyz"
      - "API_KEY=x-y-z"
      - "API_SECRET=u-v-w"
    image: structurizr/cli:latest
    volumes:
      - ".:/usr/local/structurizr"

2) Docker secrets (using the file-based version)

secrets:
  workspace_id:
    file: ./secrets/workspace_id.txt
  api_key:
    file: ./secrets/api_key.txt
  api_secret:
    file: ./secrets/api_secret.txt

services:
  cli:
    environment:
      - "WORKSPACE_ID_FILE=/run/secrets/workspace_id"
      - "API_KEY_FILE=/run/secrets/api_key"
      - "API_SECRET_FILE=/run/secrets/api_secret"
    image: structurizr/cli:latest
    secrets:
      - workspace_id
      - api_key
      - api_secret
    volumes:
      - ".:/usr/local/structurizr"
simonbrowndotje commented 2 years ago

The underlying Structurizr client library can configure itself from a structurizr.properties file (see https://github.com/structurizr/java/blob/master/structurizr-client/src/com/structurizr/api/StructurizrClient.java#L82), so perhaps replicating this behaviour would be the most obvious first step.

What benefit would this (and Docker environment variables/secrets) provide over doing something like the following though?

export STRUCTURIZR_WORKSPACE_ID=...
export STRUCTURIZR_API_KEY=...
export STRUCTURIZR_API_SECRET=...
docker run -it --rm -v $PWD:/usr/local/structurizr structurizr/cli pull -id $STRUCTURIZR_WORKSPACE_ID -key $STRUCTURIZR_API_KEY -secret $STRUCTURIZR_API_SECRET
Midnighter commented 2 years ago

It's only a mild suggestion. It very much depends where and how people run it. From a security point of view, exposing the secret directly in the command or having them in the environment are pretty much equally bad. They can be accidentally exposed via logs or crash reports that print the environment.

So secrets, essentially reading the values from files are the best solution here. The values are not exposed in the command call and they are also not in the environment where they can be accidentally exposed.

simonbrowndotje commented 2 years ago

The CLI is designed to be used on developer computers, and perhaps build servers, so I'm not sure there's a huge risk to exposing API keys/secrets. That said, and I'm not a Docker expert by any means, something like this will work without any code changes to the Java-based CLI application:

docker-compose.yml

version: "3.6"

services:

  pull:
    image: structurizr/cli:latest
    entrypoint: /scripts/pull.sh
    secrets:
      - workspace_id
      - api_key
      - api_secret
    volumes:
      - .:/scripts/

secrets:
  workspace_id:
    file: ./secrets/workspace_id.txt
  api_key:
    file: ./secrets/api_key.txt
  api_secret:
    file: ./secrets/api_secret.txt

pull.sh

#!/bin/bash
java -cp /usr/local/structurizr-cli:/usr/local/structurizr-cli/lib/* com.structurizr.cli.StructurizrCliApplication pull -id `cat /run/secrets/workspace_id` -key `cat /run/secrets/api_key` -secret `cat /run/secrets/api_secret`

I appreciate that the secrets are still present on the command line as parameters, but at least they're only available inside the Docker container now. And this approach still requires plaintext files in the directory you're starting docker from. From what I've read, I think you need to use Swarm to use the full secrets management facilities (docker secret ...), but that perhaps seems overkill for this use case?

Midnighter commented 2 years ago

That will likely work! I appreciate you thinking about this.