├── entrypoint-scripts └── 10.setup-volume.sh ├── check-servers.sh ├── .github ├── dependabot.yml └── workflows │ └── docker-release.yml ├── start-servers.sh ├── LICENSE ├── Dockerfile └── README.md /entrypoint-scripts/10.setup-volume.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | if [ -z "${ENTRYPOINT_RUN_AS_ROOT:-}" ]; then 4 | chown -R $DOCKER_USER:$DOCKER_GROUP /db 5 | fi 6 | -------------------------------------------------------------------------------- /check-servers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | if [ "$ENABLE_IPv4" = "1" ]; then 4 | wget -4qSO /dev/null http://127.0.0.1:8080/adv 5 | fi 6 | 7 | if [ "$ENABLE_IPv6" = "1" ]; then 8 | wget -6qSO /dev/null http://[::1]:8080/adv 9 | fi 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "docker" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /start-servers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | TANG="system:'REMOTE_ADDR=\$SOCAT_PEERADDR tangd /db'" 4 | 5 | if [ "$ENABLE_IPv4" = "1" ]; then 6 | socat TCP4-LISTEN:8080,reuseaddr,fork "$TANG" & 7 | fi 8 | 9 | if [ "$ENABLE_IPv6" = "1" ]; then 10 | socat TCP6-LISTEN:8080,ipv6only=1,reuseaddr,fork "$TANG" & 11 | fi 12 | 13 | wait 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Saswat Padhi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.22.1 AS builder 2 | 3 | 4 | ARG JOSE_COMMIT_SHA=95d3c02907f29e36e5a56a280adc5a069296ea76 5 | ARG TANG_COMMIT_SHA=97cffa4e2b7e60c7f363571a2acdf596effac575 6 | 7 | 8 | RUN apk add --no-cache --update \ 9 | bash \ 10 | g++ gawk git gmp gzip \ 11 | http-parser-dev \ 12 | isl-dev \ 13 | jansson-dev \ 14 | meson mpc1-dev mpfr-dev musl-dev \ 15 | ninja \ 16 | openssl-dev \ 17 | tar \ 18 | zlib-dev 19 | 20 | RUN git clone https://github.com/latchset/jose.git \ 21 | && cd jose \ 22 | && git checkout ${JOSE_COMMIT_SHA} \ 23 | && mkdir build \ 24 | && cd build \ 25 | && meson .. --prefix=/usr/local \ 26 | && ninja install 27 | 28 | RUN git clone https://github.com/latchset/tang.git \ 29 | && cd tang \ 30 | && git checkout ${TANG_COMMIT_SHA} \ 31 | && mkdir build \ 32 | && cd build \ 33 | && meson .. --prefix=/usr/local \ 34 | && ninja install 35 | 36 | 37 | 38 | 39 | FROM padhihomelab/alpine-base:3.22.1_0.19.0_0.2 40 | 41 | 42 | COPY --from=builder \ 43 | /usr/local/bin/jose \ 44 | /usr/local/bin/jose 45 | COPY --from=builder \ 46 | /usr/local/lib/libjose.so.0 \ 47 | /usr/local/lib/libjose.so.0 48 | COPY --from=builder \ 49 | /usr/local/lib/libjose.so.0.0.0 \ 50 | /usr/local/lib/libjose.so.0.0.0 51 | 52 | COPY --from=builder \ 53 | /usr/local/libexec/tangd \ 54 | /usr/local/bin/tangd 55 | COPY --from=builder \ 56 | /usr/local/libexec/tangd-keygen \ 57 | /usr/local/bin/tangd-keygen 58 | 59 | COPY entrypoint-scripts \ 60 | /etc/docker-entrypoint.d/99-extra-scripts 61 | COPY check-servers.sh \ 62 | /usr/local/bin/check-servers 63 | COPY start-servers.sh \ 64 | /usr/local/bin/start-servers 65 | 66 | 67 | RUN chmod +x /etc/docker-entrypoint.d/99-extra-scripts/*.sh \ 68 | /usr/local/bin/check-servers \ 69 | /usr/local/bin/start-servers \ 70 | && apk add --no-cache --update \ 71 | http-parser \ 72 | jansson \ 73 | openssl \ 74 | socat \ 75 | wget \ 76 | zlib 77 | 78 | 79 | EXPOSE 8080 80 | VOLUME [ "/db" ] 81 | 82 | 83 | ENV ENABLE_IPv4=1 84 | ENV ENABLE_IPv6=0 85 | 86 | CMD "start-servers" 87 | 88 | 89 | HEALTHCHECK --start-period=5s --timeout=3s \ 90 | CMD "check-servers" 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker_tang 2 | 3 | [![build status](https://img.shields.io/github/actions/workflow/status/padhi-homelab/docker_tang/docker-release.yml?label=BUILD&branch=main&logo=github&logoWidth=24&style=flat-square)](https://github.com/padhi-homelab/docker_tang/actions) 4 | [![testing size](https://img.shields.io/docker/image-size/padhihomelab/tang/testing?label=SIZE%20%5Btesting%5D&logo=docker&logoColor=skyblue&logoWidth=24&style=flat-square)](https://hub.docker.com/r/padhihomelab/tang/tags) 5 | [![latest size](https://img.shields.io/docker/image-size/padhihomelab/tang/latest?label=SIZE%20%5Blatest%5D&logo=docker&logoColor=skyblue&logoWidth=24&style=flat-square)](https://hub.docker.com/r/padhihomelab/tang/tags) 6 | 7 | [![latest version](https://img.shields.io/docker/v/padhihomelab/tang/latest?label=LATEST&logo=linux-containers&logoWidth=20&labelColor=darkmagenta&color=gold&style=for-the-badge)](https://hub.docker.com/r/padhihomelab/tang/tags) 8 | [![image pulls](https://img.shields.io/docker/pulls/padhihomelab/tang?label=PULLS&logo=data:image/svg%2bxml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBmaWxsPSIjZmZmIj4KICAgIDxwYXRoIGQ9Ik0yMC41ODcsMTQuNjEzLDE4LDE3LjI0NlY5Ljk4QTEuOTc5LDEuOTc5LDAsMCwwLDE2LjAyLDhoLS4wNEExLjk3OSwxLjk3OSwwLDAsMCwxNCw5Ljk4djYuOTYzbC0uMjYtLjA0Mi0yLjI0OC0yLjIyN2EyLjA5MSwyLjA5MSwwLDAsMC0yLjY1Ny0uMjkzQTEuOTczLDEuOTczLDAsMCwwLDguNTgsMTcuNGw2LjA3NCw2LjAxNmEyLjAxNywyLjAxNywwLDAsMCwyLjgzMywwbDUuOTM0LTZhMS45NywxLjk3LDAsMCwwLDAtMi44MDZBMi4wMTYsMi4wMTYsMCwwLDAsMjAuNTg3LDE0LjYxM1oiLz4KICAgIDxwYXRoIGQ9Ik0xNiwwQTE2LDE2LDAsMSwwLDMyLDE2LDE2LDE2LDAsMCwwLDE2LDBabTAsMjhBMTIsMTIsMCwxLDEsMjgsMTYsMTIuMDEzLDEyLjAxMywwLDAsMSwxNiwyOFoiLz4KICA8L2c+Cjwvc3ZnPgo=&logoWidth=20&labelColor=teal&color=gold&style=for-the-badge)](https://hub.docker.com/r/padhihomelab/tang) 9 | 10 | --- 11 | 12 | A multiarch [tang] Docker image, based on [Alpine Linux]. 13 | 14 | | 386 | amd64 | arm/v6 | arm/v7 | arm64 | ppc64le | s390x | 15 | | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | 16 | | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | 17 | 18 | 19 | ## Usage 20 | 21 | ``` 22 | docker run -p 1234:8080 -it padhihomelab/tang 23 | ``` 24 | 25 | Runs `tang` server on port `1234`. 26 | 27 | You might also want to use: 28 | - the `--detach` to run the container in background 29 | - the `--rm` to cleanup the container filesystem after `docker` exits 30 | - a `docker compose` workflow instead (see: [services/tang]) 31 | 32 | ## IPv4 vs IPv6 33 | 34 | By default, the `tang` server only listens on IPv4. 35 | You may, however, also listen on IPv6 by creating a container 36 | with `ENABLE_IPv6=1` set: 37 | 38 | ``` 39 | docker run -e ENABLE_IPv6=1 ... 40 | ``` 41 | 42 | You may even disable listening on IPv4 entirely, 43 | and only listen on IPv6 instead: 44 | 45 | ``` 46 | docker run -e ENABLE_IPv4=0 -e ENABLE_IPv6=1 ... 47 | ``` 48 | 49 | ## Persistent Database 50 | 51 | To persist tang's database beyond the lifetime of a container, 52 | you could bind mount a host filesystem directory at `/db`: 53 | 54 | ``` 55 | docker run -v /path/to/tang/db:/db \ 56 | -p 1234:8080 \ 57 | -it padhihomelab/tang 58 | ``` 59 | 60 | This container drops `root` privilege at startup, 61 | and runs `tang` as an unprivileged user with `DOCKER_UID` id. 62 | So, the files in `/path/to/tang/db` that are written by `tang` 63 | are owned by the `$DOCKER_UID` user id within the container. 64 | For your host system user to be able to access them, 65 | you may override the `DOCKER_UID` variable when starting the container: 66 | 67 | ``` 68 | docker run -v /path/to/tang/db:/db \ 69 | -e DOCKER_UID=$(id -u) \ 70 | -p 1234:8080 \ 71 | -it padhihomelab/tang 72 | ``` 73 | 74 | For more information on this topic, 75 | please see the `alpine-base` image documentation: 76 | [Do I ever need to override the UID, GID etc.? Why are these variables exposed?](https://github.com/padhi-homelab/docker_alpine-base/#do-i-ever-need-to-override-the-uid-gid-etc-why-are-these-variables-exposed) 77 | 78 | 79 | 80 | [Alpine Linux]: https://alpinelinux.org/ 81 | [tang]: https://github.com/latchset/tang 82 | [services/tang]: https://github.com/padhi-homelab/services/tree/master/tang 83 | -------------------------------------------------------------------------------- /.github/workflows/docker-release.yml: -------------------------------------------------------------------------------- 1 | name: Docker CI Release 2 | 3 | on: 4 | pull_request: 5 | branches: main 6 | push: 7 | branches: main 8 | tags: 9 | - v* 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - 16 | name: "Git: Checkout" 17 | uses: actions/checkout@v6 18 | - 19 | name: "Docker: Setup QEMU" 20 | id: qemu 21 | uses: docker/setup-qemu-action@v3.7.0 22 | - 23 | name: "Docker: Setup Buildx" 24 | id: buildx 25 | uses: docker/setup-buildx-action@v3.11.1 26 | - 27 | name: "Docker: Available Platforms" 28 | run: echo ${{ steps.buildx.outputs.platforms }} 29 | - 30 | name: "Env: Prepare" 31 | id: prepare 32 | run: | 33 | PLATFORMS=linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6,linux/ppc64le,linux/s390x 34 | 35 | REPONAME=$(basename $GITHUB_REPOSITORY) 36 | DOCKER_IMAGE=padhihomelab/${REPONAME#docker_} 37 | GHCR_IMAGE=ghcr.io/padhi-homelab/${REPONAME#docker_} 38 | 39 | VERSION=testing 40 | if [[ $GITHUB_REF == refs/tags/* ]]; then 41 | VERSION=${GITHUB_REF#refs/tags/v} 42 | VERSION=${VERSION#_} 43 | fi 44 | 45 | TAGS="${DOCKER_IMAGE}:${VERSION},${GHCR_IMAGE}:${VERSION}" 46 | if [[ $VERSION =~ ^(((([0-9]{1,3}\.){,3}[0-9]{1,4})|(git\..*))_){,4}((([0-9]{1,3}\.){,3}[0-9]{1,4})|(git\..*))(-p[0-9]{1,2})?$ ]]; then 47 | TAGS="$TAGS,${DOCKER_IMAGE}:latest,${GHCR_IMAGE}:latest" 48 | fi 49 | 50 | echo ::set-output name=args::VERSION=${VERSION} \ 51 | --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ 52 | --build-arg VCS_REF=${GITHUB_SHA::8} 53 | echo ::set-output name=image::${DOCKER_IMAGE} 54 | echo ::set-output name=platforms::${PLATFORMS} 55 | echo ::set-output name=tags::${TAGS} 56 | echo ::set-output name=version::${VERSION} 57 | - 58 | name: "Docker: Buildx Build" 59 | uses: docker/build-push-action@v6.18.0 60 | with: 61 | platforms: ${{ steps.prepare.outputs.platforms }} 62 | push: false 63 | tags: ${{ steps.prepare.outputs.tags }} 64 | build-args: ${{ steps.prepare.outputs.args }} 65 | - 66 | name: "Docker: Load Image" 67 | uses: docker/build-push-action@v6.18.0 68 | with: 69 | load: true 70 | push: false 71 | tags: ${{ steps.prepare.outputs.tags }} 72 | build-args: ${{ steps.prepare.outputs.args }} 73 | - 74 | name: "Anchore Scan: Run" 75 | id: scan 76 | uses: anchore/scan-action@v7 77 | with: 78 | image: "${{ steps.prepare.outputs.image }}:${{ steps.prepare.outputs.version }}" 79 | acs-report-enable: true 80 | fail-build: false 81 | severity-cutoff: "critical" 82 | - 83 | name: "Anchore Scan: Report" 84 | uses: github/codeql-action/upload-sarif@v4 85 | with: 86 | sarif_file: ${{ steps.scan.outputs.sarif }} 87 | - 88 | name: "Docker Hub: Login" 89 | if: success() && github.event_name != 'pull_request' 90 | uses: docker/login-action@v3.6.0 91 | with: 92 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 93 | password: ${{ secrets.DOCKER_HUB_PASSWORD }} 94 | - 95 | name: "GitHub Container Registry: Login" 96 | if: success() && github.event_name != 'pull_request' 97 | uses: docker/login-action@v3.6.0 98 | with: 99 | registry: ghcr.io 100 | username: ${{ github.repository_owner }} 101 | password: ${{ secrets.GITHUB_TOKEN }} 102 | - 103 | name: "Docker: Buildx Push" 104 | if: success() && github.event_name != 'pull_request' 105 | uses: docker/build-push-action@v6.18.0 106 | with: 107 | platforms: ${{ steps.prepare.outputs.platforms }} 108 | push: true 109 | tags: ${{ steps.prepare.outputs.tags }} 110 | build-args: ${{ steps.prepare.outputs.args }} 111 | - 112 | name: "Docker Hub: Update description" 113 | if: success() && github.event_name != 'pull_request' 114 | uses: peter-evans/dockerhub-description@v5 115 | env: 116 | DOCKERHUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }} 117 | DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }} 118 | DOCKERHUB_REPOSITORY: ${{ steps.prepare.outputs.image }} 119 | - 120 | name: "Clean up" 121 | if: always() 122 | run: | 123 | rm -f ${HOME}/.docker/config.json 124 | --------------------------------------------------------------------------------