From 7135abb2c9fe0f7207f4b063b6cf43d40557dc73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?plaf=C3=BC?= <plafue@users.noreply.github.com>
Date: Fri, 27 Dec 2024 16:25:55 +0100
Subject: [PATCH] Add docker in docker example to advanced usage in docs
 (#4620)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Patrick Schratz <patrick.schratz@gmail.com>
---
 .cspell.json                                  |  1 +
 docs/docs/20-usage/90-advanced-usage.md       | 93 +++++++++++++++++++
 .../version-2.8/20-usage/90-advanced-usage.md | 90 ++++++++++++++++++
 3 files changed, 184 insertions(+)

diff --git a/.cspell.json b/.cspell.json
index 68a768057..419c4e0db 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -39,6 +39,7 @@
     "cpuset",
     "creativecommons",
     "Curr",
+    "CERTDIR",
     "datacenter",
     "DATASOURCE",
     "Debugf",
diff --git a/docs/docs/20-usage/90-advanced-usage.md b/docs/docs/20-usage/90-advanced-usage.md
index 910646629..e8a691de0 100644
--- a/docs/docs/20-usage/90-advanced-usage.md
+++ b/docs/docs/20-usage/90-advanced-usage.md
@@ -127,3 +127,96 @@ WOODPECKER_ENVIRONMENT=first_var:value1,second_var:value2
 ```
 
 Note that this tightly couples the server and app configurations (where the app is a completely separate application). But this is a good option for truly global variables which should apply to all steps in all pipelines for all apps.
+
+## Docker in docker (dind) setup
+
+:::warning
+This set up will only work on trusted repositories and for security reasons should only be used in private environments.
+See [project settings](./75-project-settings.md#trusted) to enable "trusted" mode.
+:::
+
+The snippet below shows how a step can communicate with the docker daemon running in a `docker:dind` service.
+
+:::note
+If your goal is to build/publish OCI images, consider using the [Docker Buildx Plugin](https://woodpecker-ci.org/plugins/Docker%20Buildx) instead.
+:::
+
+First we need to define a service running a docker with the `dind` tag.
+This service must run in `privileged` mode:
+
+```yaml
+services:
+  - name: docker
+    image: docker:dind # use 'docker:<major-version>-dind' or similar in production
+    privileged: true
+    ports:
+      - 2376
+```
+
+Next, we need to set up TLS communication between the `dind` service and the step that wants to communicate with the docker daemon (unauthenticated TCP connections have been deprecated [as of docker v27](https://github.com/docker/cli/blob/v27.4.0/docs/deprecated.md#unauthenticated-tcp-connections) and will result in an error in v28).
+
+This can be achieved by letting the daemon generate TLS certificates and share them with the client through an agent volume mount (`/opt/woodpeckerci/dind-certs` in the example below).
+
+```diff
+services:
+  - name: docker
+    image: docker:dind # use 'docker:<major-version>-dind' or similar in production
+    privileged: true
++    environment:
++      DOCKER_TLS_CERTDIR: /dind-certs
++    volumes:
++      - /opt/woodpeckerci/dind-certs:/dind-certs
+     ports:
+       - 2376
+```
+
+In the docker client step:
+
+1. Set the `DOCKER_*` environment variables shown below to configure the connection with the daemon.
+   These generic docker environment variables that are framework-agnostic (e.g. frameworks like [TestContainers](https://testcontainers.com/), [Spring Boot Docker Compose](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-docker-compose) do all respect them).
+2. Mount the volume to the location where the daemon has created the certificates (`/opt/woodpeckerci/dind-certs`)
+
+Test the connection with the docker client:
+
+```diff
+steps:
+  - name: test
+    image: docker:cli # in production use something like 'docker:<major version>-cli'
++    environment:
++      DOCKER_HOST: "tcp://docker:2376"
++      DOCKER_CERT_PATH: "/dind-certs/client"
++      DOCKER_TLS_VERIFY: "1"
++    volumes:
++      - /opt/woodpeckerci/dind-certs:/dind-certs
+    commands:
+      - docker version
+```
+
+This step should output the server and client version information if everything has been set up correctly.
+
+Full example:
+
+```yaml
+steps:
+  - name: test
+    image: docker:cli # use 'docker:<major-version>-cli' or similar in production
+    environment:
+      DOCKER_HOST: 'tcp://docker:2376'
+      DOCKER_CERT_PATH: '/dind-certs/client'
+      DOCKER_TLS_VERIFY: '1'
+    volumes:
+      - /opt/woodpeckerci/dind-certs:/dind-certs
+    commands:
+      - docker version
+
+services:
+  - name: docker
+    image: docker:dind # use 'docker:<major-version>-dind' or similar in production
+    privileged: true
+    environment:
+      DOCKER_TLS_CERTDIR: /dind-certs
+    volumes:
+      - /opt/woodpeckerci/dind-certs:/dind-certs
+    ports:
+      - 2376
+```
diff --git a/docs/versioned_docs/version-2.8/20-usage/90-advanced-usage.md b/docs/versioned_docs/version-2.8/20-usage/90-advanced-usage.md
index 065386fdf..c44c2baeb 100644
--- a/docs/versioned_docs/version-2.8/20-usage/90-advanced-usage.md
+++ b/docs/versioned_docs/version-2.8/20-usage/90-advanced-usage.md
@@ -127,3 +127,93 @@ WOODPECKER_ENVIRONMENT=first_var:value1,second_var:value2
 ```
 
 Note that this tightly couples the server and app configurations (where the app is a completely separate application). But this is a good option for truly global variables which should apply to all steps in all pipelines for all apps.
+
+## Docker in docker (dind) setup
+
+:::warning
+This set up will only work on trusted repositories and for security reasons should only be used in private environments.
+See [project settings](./75-project-settings.md#trusted) to enable trusted mode.
+:::
+
+The snippet below shows how a step can communicate with the docker daemon via a `docker:dind` service.
+
+:::note
+If your aim ist to build/publish OCI images, consider using the [Docker Buildx Plugin](https://woodpecker-ci.org/plugins/Docker%20Buildx) instead.
+:::
+
+First we need to define a servie running a docker with the `dind` tag. This service must run in privileged mode:
+
+```yaml
+services:
+  - name: docker
+    image: docker:27.4-dind
+    privileged: true
+    ports:
+      - 2376
+```
+
+Next we need to set up TLS communication between the `dind` service and the step that wants to communicate with the docker daemon (since Unauthenticated TCP connections have been deprecated [as of docker v26](https://github.com/docker/cli/blob/v27.4.0/docs/deprecated.md#unauthenticated-tcp-connections) and will ve removed in release v28).
+
+We can achieve this by letting the daemon generate TLS certificates for us and share them with the client via a volume mount in the agent (`/opt/woodpeckerci/dind-certs` in the example below).
+
+```diff
+services:
+  - name: docker
+    image: docker:27.4-dind
+    privileged: true
++    environment:
++      DOCKER_TLS_CERTDIR: /dind-certs
++    volumes:
++      - /opt/woodpeckerci/dind-certs:/dind-certs
+     ports:
+       - 2376
+```
+In the step that needs access to the daemon we need to:
+
+1. Set the `DOCKER_*` environment variables shown below, setting up the connection with the daemon. These are standardized environment variables that should work with the docker client used by your framework of choice (e.g. [TestContainers](https://testcontainers.com/), [Spring Boot Docker Compose](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-docker-compose) or similar).
+2. Mount the volume where the daemon has created the certificates (`/opt/woodpeckerci/dind-certs`)
+
+In this example we test the connection with the vanilla docker client:
+
+```diff
+steps:
+  - name: test
+    image: docker:27.4-cli
++    environment:
++      DOCKER_HOST: "tcp://docker:2376"
++      DOCKER_CERT_PATH: "/dind-certs/client"27.4-cli
++      DOCKER_TLS_VERIFY: "1"
++    volumes:
++      - /opt/woodpeckerci/dind-certs:/dind-certs
+    commands:
+      - docker version
+```
+
+This step should output version information of the client and the server if everything has been set correctly.
+
+Complete example:
+
+```yaml
+steps:
+  - name: test
+    image: docker:27.4-cli
+    environment:
+      DOCKER_HOST: "tcp://docker:2376"
+      DOCKER_CERT_PATH: "/dind-certs/client"27.4-cli
+      DOCKER_TLS_VERIFY: "1"
+    volumes:
+      - /opt/woodpeckerci/dind-certs:/dind-certs
+    commands:
+      - docker version
+
+services:
+  - name: docker
+    image: docker:27.4-dind
+    privileged: true
+    environment:
+      DOCKER_TLS_CERTDIR: /dind-certs
+    volumes:
+      - /opt/woodpeckerci/dind-certs:/dind-certs
+    ports:
+      - 2376
+```