From 0f224e4fb8b5578fe70bdbedb52e69c74d9180c2 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Mon, 5 Feb 2024 14:27:06 +0100 Subject: [PATCH] Document socket-proxy permissions, return early when update failed on scaling down (#343) * Do not await containers when there was an error on scaling * Add test case for usage with socket proxy * Add documentation on required permissions for docker-socket-proxy * Add full list of used Docker APIs to doc * CONTAINER_START and CONTAINER_STOP is not needed --- cmd/backup/stop_restart.go | 4 +- docs/how-tos/use-custom-docker-host.md | 30 +++++++++- test/proxy/docker-compose.swarm.yml | 40 ++++++++++++++ test/proxy/docker-compose.yml | 36 ++++++++++++ test/proxy/run.sh | 76 ++++++++++++++++++++++++++ 5 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 test/proxy/docker-compose.swarm.yml create mode 100644 test/proxy/docker-compose.yml create mode 100755 test/proxy/run.sh diff --git a/cmd/backup/stop_restart.go b/cmd/backup/stop_restart.go index fa3219e..c538755 100644 --- a/cmd/backup/stop_restart.go +++ b/cmd/backup/stop_restart.go @@ -210,9 +210,9 @@ func (s *script) stopContainersAndServices() (func() error, error) { warnings, err := scaleService(s.cli, svc.serviceID, 0) if err != nil { scaleDownErrors.append(err) - } else { - scaledDownServices = append(scaledDownServices, svc) + return } + scaledDownServices = append(scaledDownServices, svc) for _, warning := range warnings { s.logger.Warn( fmt.Sprintf("The Docker API returned a warning when scaling down service %s: %s", svc.serviceID, warning), diff --git a/docs/how-tos/use-custom-docker-host.md b/docs/how-tos/use-custom-docker-host.md index 1f7a89e..20f44be 100644 --- a/docs/how-tos/use-custom-docker-host.md +++ b/docs/how-tos/use-custom-docker-host.md @@ -13,5 +13,33 @@ If you are interfacing with Docker via TCP, set `DOCKER_HOST` to the correct URL DOCKER_HOST=tcp://docker_socket_proxy:2375 ``` -In case you are using a socket proxy, it must support `GET` and `POST` requests to the `/containers` endpoint. If you are using Docker Swarm, it must also support the `/services` endpoint. If you are using pre/post backup commands, it must also support the `/exec` endpoint. +If you do this as you seek to restrict access to the Docker socket, this tool is potentially calling the following Docker APIs: +| API | When | +|-|-| +| `Info` | always | +| `ContainerExecCreate` | running commands from `exec-labels` | +| `ContainerExecAttach` | running commands from `exec-labels` | +| `ContainerExecInspect` | running commands from `exec-labels` | +| `ContainerList` | always | + `ServiceList` | Docker engine is running in Swarm mode | +| `ServiceInspect` | Docker engine is running in Swarm mode | +| `ServiceUpdate` | Docker engine is running in Swarm mode and `stop-during-backup` is used | +| `ConatinerStop` | `stop-during-backup` labels are applied to containers | +| `ContainerStart` | `stop-during-backup` labels are applied to container | + +--- + +In case you are using [`docker-socket-proxy`][proxy], this means following permissions are required: + +| Permission | When | +|-|-| +| INFO | always required | +| CONTAINERS | always required | +| POST | required when using `stop-during-backup` or `exec` labels | +| EXEC | required when using `exec`-labeled commands | +| SERVICES | required when Docker Engine is running in Swarm mode | +| NODES | required when labeling services `stop-during-backup` | +| TASKS | required when labeling services `stop-during-backup` | + +[proxy]: https://github.com/Tecnativa/docker-socket-proxy diff --git a/test/proxy/docker-compose.swarm.yml b/test/proxy/docker-compose.swarm.yml new file mode 100644 index 0000000..371b0e5 --- /dev/null +++ b/test/proxy/docker-compose.swarm.yml @@ -0,0 +1,40 @@ +# Copyright 2020-2021 - Offen Authors +# SPDX-License-Identifier: Unlicense + +version: '3.8' + +services: + backup: + image: offen/docker-volume-backup:${TEST_VERSION:-canary} + environment: + BACKUP_FILENAME: test.tar.gz + BACKUP_CRON_EXPRESSION: 0 0 5 31 2 ? + DOCKER_HOST: tcp://docker_socket_proxy:2375 + volumes: + - pg_data:/backup/pg_data:ro + - ${LOCAL_DIR:-local}:/archive + + docker_socket_proxy: + image: tecnativa/docker-socket-proxy:0.1 + environment: + INFO: ${ALLOW_INFO:-1} + CONTAINERS: ${ALLOW_CONTAINERS:-1} + SERVICES: ${ALLOW_SERVICES:-1} + POST: ${ALLOW_POST:-1} + TASKS: ${ALLOW_TASKS:-1} + NODES: ${ALLOW_NODES:-1} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + + pg: + image: postgres:14-alpine + environment: + POSTGRES_PASSWORD: example + volumes: + - pg_data:/var/lib/postgresql/data + deploy: + labels: + - docker-volume-backup.stop-during-backup=true + +volumes: + pg_data: diff --git a/test/proxy/docker-compose.yml b/test/proxy/docker-compose.yml new file mode 100644 index 0000000..641cc70 --- /dev/null +++ b/test/proxy/docker-compose.yml @@ -0,0 +1,36 @@ +# Copyright 2020-2021 - Offen Authors +# SPDX-License-Identifier: Unlicense + +version: '3.8' + +services: + backup: + image: offen/docker-volume-backup:${TEST_VERSION:-canary} + environment: + BACKUP_FILENAME: test.tar.gz + BACKUP_CRON_EXPRESSION: 0 0 5 31 2 ? + DOCKER_HOST: tcp://docker_socket_proxy:2375 + volumes: + - pg_data:/backup/pg_data:ro + - ${LOCAL_DIR:-local}:/archive + + docker_socket_proxy: + image: tecnativa/docker-socket-proxy:0.1 + environment: + INFO: ${ALLOW_INFO:-1} + CONTAINERS: ${ALLOW_CONTAINERS:-1} + POST: ${ALLOW_POST:-1} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + + pg: + image: postgres:14-alpine + environment: + POSTGRES_PASSWORD: example + volumes: + - pg_data:/var/lib/postgresql/data + labels: + - docker-volume-backup.stop-during-backup=true + +volumes: + pg_data: diff --git a/test/proxy/run.sh b/test/proxy/run.sh new file mode 100755 index 0000000..1747dc4 --- /dev/null +++ b/test/proxy/run.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) +. ../util.sh +current_test=$(basename $(pwd)) + +export LOCAL_DIR=$(mktemp -d) + +docker compose up -d --quiet-pull +sleep 5 + +# The default configuration in docker-compose.yml should +# successfully create a backup. +docker compose exec backup backup + +sleep 5 + +expect_running_containers "3" + +if [ ! -f "$LOCAL_DIR/test.tar.gz" ]; then + fail "Archive was not created" +fi +pass "Found relevant archive file." + +# Disabling POST should make the backup run fail +ALLOW_POST="0" docker compose up -d +sleep 5 + +set +e +docker compose exec backup backup +if [ $? = "0" ]; then + fail "Expected invocation to exit non-zero." +fi +set -e +pass "Invocation exited non-zero." + +docker compose down --volumes + +# Next, the test is run against a Swarm setup + +docker swarm init + +export LOCAL_DIR=$(mktemp -d) + +docker stack deploy --compose-file=docker-compose.swarm.yml test_stack + +sleep 20 + +# The default configuration in docker-compose.swarm.yml should +# successfully create a backup in Swarm mode. +docker exec $(docker ps -q -f name=backup) backup + +if [ ! -f "$LOCAL_DIR/test.tar.gz" ]; then + fail "Archive was not created" +fi + +pass "Found relevant archive file." + +sleep 5 +expect_running_containers "3" + +# Disabling POST should make the backup run fail +ALLOW_POST="0" docker stack deploy --compose-file=docker-compose.swarm.yml test_stack + +sleep 20 + +set +e +docker exec $(docker ps -q -f name=backup) backup +if [ $? = "0" ]; then + fail "Expected invocation to exit non-zero." +fi +set -e + +pass "Invocation exited non-zero."