diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 5370532..bbe5431 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -25,8 +25,11 @@ jobs: run: docker login -u $DOCKER_USER -p $DOCKER_PASSWORD - name: docker build run: | - docker build . --file Dockerfile --tag $DOCKER_USER/bw-cli:latest \ - --tag $DOCKER_USER/bw-cli:${{ github.event.inputs.version }} \ - --tag $DOCKER_USER/bw-cli:v${{ github.event.inputs.version }} + docker build . --file Dockerfile --tag $DOCKER_USER/bw-cli:latest-busybox \| + --provenance=mode=max \ + --sbom=true \ + --tag $DOCKER_USER/bw-cli:busybox-latest \ + --tag $DOCKER_USER/bw-cli:busybox-${{ github.event.inputs.version }} \ + --tag $DOCKER_USER/bw-cli:busybox-v${{ github.event.inputs.version }} - name: docker push run: docker push -a $DOCKER_USER/bw-cli diff --git a/.gitignore b/.gitignore index 03bd412..90f5182 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.env +*.override.* diff --git a/Dockerfile b/Dockerfile index c656f87..ba562df 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,36 @@ -FROM --platform=linux/amd64 debian:latest +# syntax = docker/dockerfile:1.2 +############################################### +# Build stage # +############################################### +FROM --platform=linux/amd64 debian:latest as builder ENV DEBIAN_FRONTEND=noninteractive - WORKDIR /usr/local/bin -RUN apt update && apt install -y curl unzip libsecret-1-0 jq -COPY entrypoint.sh . -RUN export VER=$(curl -H "Accept: application/vnd.github+json" https://api.github.com/repos/bitwarden/clients/releases | jq -r 'sort_by(.published_at) | reverse | .[].name | select( index("CLI") )' | sed 's:.*CLI v::' | head -n 1) && \ + +RUN apt update && apt install -y curl unzip jq + +RUN VER=$(curl -H "Accept: application/vnd.github+json" https://api.github.com/repos/bitwarden/clients/releases | jq -r 'sort_by(.published_at) | reverse | .[].name | select( index("CLI") )' | sed 's:.*CLI v::' | head -n 1) && \ curl -LO "https://github.com/bitwarden/clients/releases/download/cli-v{$VER}/bw-linux-{$VER}.zip" \ && unzip *.zip && chmod +x ./bw -ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ] + +RUN mkdir /lib-bw && ldd ./bw | tr -s '[:blank:]' '\n' | grep '^/lib' | xargs -I % cp % /lib-bw +RUN mkdir /lib64-bw && ldd ./bw | tr -s '[:blank:]' '\n' | grep '^/lib64' | xargs -I % cp % /lib64-bw + +############################################### +# App stage # +############################################### +FROM --platform=linux/amd64 busybox:musl + +# copy binaries +COPY --from=builder /usr/local/bin/bw /usr/bin/bw + +# copy shared libraries +COPY --from=builder /lib-bw /lib +COPY --from=builder /lib64-bw /lib64 + +# copy ca-certificates +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +# entrypoint +COPY entrypoint.sh /usr/bin/entrypoint.sh + +ENTRYPOINT [ "/usr/bin/entrypoint.sh" ] diff --git a/README.md b/README.md index 4751abb..a5a01b5 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,27 @@ # bw-docker + The latest Bitwarden CLI in a Docker container. ## Instructions + ### Interactive CLI + Run `docker build -t bw-cli:latest .` Then, `docker run -v $HOME/.config/Bitwarden\ CLI/:/root/.config/Bitwarden\ CLI/ -it bw-cli:latest login` ### Serve API + Create a local [Vault Management API](https://bitwarden.com/help/vault-management-api/) instance: `docker-compose.yml` ```yaml -version: "3.3" services: bw_api: container_name: bw_api hostname: bw_api platform: linux/amd64 - image: tangowithfoxtrot/bw-cli:${TAG:-latest} + image: tangowithfoxtrot/bw-cli:${TAG:-busybox-lastest} # environment: # uncomment if you're passing $VAULT_PASSWORD as a secret to unlock the vault # UNLOCK_VAULT: true volumes: @@ -26,10 +29,4 @@ services: # - "$HOME/Library/Application Support/Bitwarden CLI:/root/.config/Bitwarden CLI" # macOS ports: - "127.0.0.1:${SERVE_PORT:-8087}:${SERVE_PORT:-8087}" - healthcheck: - test: curl -f http://localhost:${SERVE_PORT:-8087}/status || exit 1 - interval: 5s - timeout: 2s - retries: 3 - start_period: 5s ``` diff --git a/docker-compose.yml b/docker-compose.yml index c3d9924..487cfc5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,9 @@ -version: "3.3" services: bw_api: container_name: bw_api hostname: bw_api platform: linux/amd64 - image: tangowithfoxtrot/bw-cli:${TAG:-latest} + image: tangowithfoxtrot/bw-cli:${TAG:-busybox-latest} # build: # context: . # dockerfile: Dockerfile @@ -17,9 +16,3 @@ services: # - "$HOME/Library/Application Support/Bitwarden CLI:/root/.config/Bitwarden CLI" # macOS ports: - "127.0.0.1:${SERVE_PORT:-8087}:${SERVE_PORT:-8087}" - healthcheck: - test: curl -f http://localhost:${SERVE_PORT:-8087}/status || exit 1 - interval: 5s - timeout: 2s - retries: 3 - start_period: 5s diff --git a/entrypoint.sh b/entrypoint.sh index aacd362..2354397 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,38 +1,52 @@ -#!/usr/bin/env bash +#!/bin/sh # to enable interactive CLI usage -if [[ $# -gt 0 ]]; then +if [ $# -gt 0 ]; then bw "$@" exit $? fi -STATUS="$(bw status | jq -r '.status')" +STATUS="$(bw status --pretty | grep 'status' | sed 's/status": "//g' | grep -oE '(\w+)' || echo "Could not get vault status. Exiting..." && exit 1)" -if [[ -n "$MFA_CODE" ]]; then +if [ -n "$MFA_CODE" ]; then # shellcheck disable=SC2034 - export MFA_LOGIN="--method 0 --code $MFA_CODE" + MFA_LOGIN="--method 0 --code $MFA_CODE" fi -if [[ -n "$BW_CLIENTSECRET" ]]; then - export API_LOGIN="--apikey" +if [ -n "$BW_CLIENTSECRET" ]; then + API_LOGIN="--apikey" fi -if [[ "$STATUS" == "unauthenticated" ]]; then +if [ "$STATUS" != "authenticated" ]; then bw config server "$SERVER_HOST_URL" && echo - # shellcheck disable=SC2086 - bw login "$VAULT_EMAIL" "$VAULT_PASSWORD" $API_LOGIN $MFA_LOGIN && echo + # shellcheck disable=SC2086,SC2155 + BW_TMP_SESSION="$(bw login --raw "$VAULT_EMAIL" "$VAULT_PASSWORD" $API_LOGIN $MFA_LOGIN)" && echo fi -bw serve --hostname all --port "${SERVE_PORT:-8087}" & -BW_SERVE_PID=$! -echo "\`bw serve\` pid: $BW_SERVE_PID" +if [ "$UNLOCK_VAULT" = "true" ]; then + if [ -n "$BW_TMP_SESSION" ]; then + export BW_SESSION="$BW_TMP_SESSION" + # unset the temp session key + unset BW_TMP_SESSION + else + # shellcheck disable=SC2155 + export BW_SESSION="$(bw unlock --raw "$VAULT_PASSWORD")" + fi + sleep 1 + + if [ "$(bw status --pretty | grep 'status' | sed 's/status": "//g' | grep -oE '(\w+)')" = "unauthenticated" ]; then + echo "Could not authenticate with Bitwarden. Exiting..." && exit 1 + fi -if [[ "$UNLOCK_VAULT" == "true" ]]; then - while ! curl -sX POST -H "Content-Type: application/json" -d "{\"password\": \"$VAULT_PASSWORD\"}" "http://localhost:${SERVE_PORT:-8087}/unlock" >/dev/null; do - sleep 1 - done echo "Vault unlocked!" +else + # unset the temp session key + unset BW_TMP_SESSION fi +bw serve --hostname all --port "${SERVE_PORT:-8087}" & +BW_SERVE_PID=$! +echo "\`bw serve\` pid: $BW_SERVE_PID" + echo "Server can be reached at: http://localhost:${SERVE_PORT:-8087}/status" sleep infinity