Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: TimJDFletcher/dockerfiles
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: timemachine-v2.6
Choose a base ref
...
head repository: TimJDFletcher/dockerfiles
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Loading
2 changes: 0 additions & 2 deletions samba-timemachine/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
vendor/
.bundle
2 changes: 0 additions & 2 deletions samba-timemachine/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
vendor/
.bundle
1 change: 1 addition & 0 deletions samba-timemachine/.trivyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CVE-2024-45337
36 changes: 23 additions & 13 deletions samba-timemachine/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
FROM debian:bookworm-20220801-slim
FROM debian:trixie-20241202-slim

ENV SAMBA_VERSION="2:4.16.4+dfsg-2"

ENV BACKUPDIR=/backups
ENV USER="timemachine" PASS="password" PUID="999" GID="999" LOG_LEVEL="1" QUOTA="1024"
ENV BACKUPDIR="/backups"
VOLUME /backups

ENV SAMBA_VERSION="2:4.21.2+dfsg-3"
RUN apt-get update && \
apt-get --no-install-recommends --yes install \
samba="$SAMBA_VERSION" \
samba-vfs-modules="$SAMBA_VERSION" \
iproute2 && \
rm -rf /var/lib/apt/lists/*
samba="${SAMBA_VERSION}" \
smbclient="${SAMBA_VERSION}" && \
rm -rf /var/lib/apt/lists/* /etc/samba/smb.conf

ADD samba.conf.tmpl /etc/samba/smb.conf.tmpl
ADD TimeMachine.quota.tmpl /etc/TimeMachine.quota.tmpl
ENV GOSS_VER="v0.4.9" GOSS_DST="/goss"
COPY goss/ /goss/
RUN apt-get update && \
apt-get --no-install-recommends --yes install curl ca-certificates && \
sh /goss/goss-installer && \
apt-get purge --yes curl ca-certificates && \
apt-get autoremove --yes && \
rm -rf /var/lib/apt/lists/*

EXPOSE 445/tcp
COPY samba.conf.tmpl /etc/samba/smb.conf.tmpl
COPY TimeMachine.quota.tmpl /etc/TimeMachine.quota.tmpl

ADD entrypoint /entrypoint
COPY entrypoint /entrypoint
RUN chmod 0755 /entrypoint

ENTRYPOINT ["/entrypoint"]
RUN ${GOSS_DST}/goss --gossfile ${GOSS_DST}/tests/goss-dockerfile-tests.yaml validate

EXPOSE 10445/tcp

ENTRYPOINT ["/entrypoint"]
7 changes: 0 additions & 7 deletions samba-timemachine/Gemfile

This file was deleted.

14 changes: 0 additions & 14 deletions samba-timemachine/Makefile

This file was deleted.

106 changes: 70 additions & 36 deletions samba-timemachine/README.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,108 @@
# samba-timemachine-docker

This is a docker container based on Ubuntu Focal running SAMBA and configured to provide Apple "Time Capsule" like backups.
This is a docker container based on Debian bookworm with SAMBA configured to provide Apple "Time Capsule" like backups.

The Docker Hub images support x86_64, Raspberry Pi 2/3/4 and other modern ARM based systems.
The Docker Hub [images](https://hub.docker.com/repository/docker/timjdfletcher/samba-timemachine/tags?page=1&ordering=last_updated)
support x86_64, Raspberry Pi 3/4 and other modern ARM64 based systems.

An example of how to use the container
# Networking

*BREAKING CHANGE in v2.9+* Changed default container listen port to 10445

The container by default listens on port 10445 to allow this container to run alongside an existing SAMBA server and to remove
the need for root access in the container

An example of how to use the container with raw docker

```bash
docker run -d -t \
-v /backups/timemachine:/backups \
-p 10445:445 \
--restart unless-stopped timjdfletcher/samba-timemachine:timemachine-v2.4
-p 10445:10445 \
--restart unless-stopped timjdfletcher/samba-timemachine:latest
```

This example maps the docker host port 10445 to the container port 445, so the container can be run alongside a normal SAMBA service.
The repo includes an example [docker compose](https://docs.docker.com/compose/) [file](./docker-compose.yml) that starts the container
with a local volume and healthchecks enabled.

# Discovery

The container only runs smbd, to enable discovery on your local network use multicast DNS such as avahi.
The container only runs smbd on a nonstandard port, to enable discovery on your local network a tool such as avahi to announce the server over mDNS.

I do this by running avahi-daemon on the docker host system, for debian systems install the package avahi-daemon:
I do this by running avahi-daemon on the docker host system, for debian type systems install the package avahi-daemon:

```bash
apt install avahi-daemon
```

To enable discovery copy the [service file](timemachine.service) to `/etc/avahi/services/`

There is also a docker [container](https://hub.docker.com/r/solidnerd/avahi) that can be used instead of installing avahi-daemon on the host.
I have not tested this recently.
And copy the example [service file](timemachine.service) to `/etc/avahi/services/`

# Settings

| Variable | Function | Default. |
| ------------|:-----------------------------------------------:|------------:|
| USER | Time Machine User | timemachine |
| PASS | User Password | password |
| PUID | UserID | 999 |
| PGID | GroupID | 999 |
| QUOTA | Time Machine Size in MB | 512000 |
| RANDOM_PASS | Generate a random password, printed in the logs | false |
| Variable | Function | Default |
|-------------|:-----------------------------------:|--------------:|
| `USER` | Time Machine Username | `timemachine` |
| `PASS` | Time Machine Password | `password` |
| `PUID` | Unix User ID for Time Machine user | `999` |
| `PGID` | Unix Group ID for Time Machine user | `999` |
| `LOG_LEVEL` | SAMBA logging level | `1` |
| `QUOTA` | Time Machine Quota in GB | `1024` |

The defaults are embedded in the Dockerfile

# Security

The container creates a user timemachine on startup, with by default a password of `password`, and then drops root.
The security design is simple and assumes that timemachine backups are encrypted before leaving the source macOS system.

The default configuration of the container creates a unix user called `timemachine` with uid and gid 999, and a matching SAMBA user called `timemachine` with a password of `password`.

A custom username can be passed to the container with the environment variable `USER`.

A password can be passed in with the environment variable `PASS`, or by setting the environment variable `RANDOM_PASS` to true the container generates a random password on startup.
A custom password can be passed to the container with the environment variable `PASS`.

# Storage
# Quota

I have had some problems using ZFS as a backing store for the container in Catalina, I'm not sure if this because of the slow SMR drive I was using or ZFS.
I have changed the backend storage to ext4 which has been working well.
*BREAKING CHANGE in v2.7+* Quota is now configured in Gigabytes

# Quotas
The container supports setting of quota to limit the max size of backups, it defaults to 1024GB (1TB).
I'm unclear if this works correctly in modern versions of macOS.

The container supports setting of quota to limit the max size of backups, it defaults to 512GB.
I'm unclear if this works correctly in macOS.
The SAMBA setting of `disk max size` is also configured to limit the reported size of the disk to the same as the configured quota.
This is a soft limit not a hard limit.

# Building the Docker image

To build the image you need to have docker and docker buildx available, this is included by default in docker desktop but for colime buildx needs to be [installed](https://github.com/abiosoft/colima/issues/44).

# Testing

Serverspec tests are included, to execute the tests use the run script: `./run test`
[Goss](https://github.com/goss-org/goss) tests are [included](goss/tests/), to execute the tests use the run script: `./run test`

Trivy is configured as well to test the container for known vulnerabilities.

# Debugging

The container can be started with SAMBA debugging flags for example: `--debuglevel=4`

There is a utility function in the run script that will print out macOS timemachine logs and then follow them to use it call:
`./run timemachineLogs`

# Storage notes

# Versions
Generally speaking timemachine backups are heavy metadata workloads.
I have had some performance problems using ZFS as a backing store for the container in Catalina.
I'm not sure if this because of the slow SMR drive I was using or by ZFS's copy on write design interacting badly with APFS.
I have changed the backend storage that I use to ext4 which has been working well.

Base image: [Ubuntu Focal](https://hub.docker.com/_/ubuntu?tab=tags&page=1&name=focal)
[SAMBA](https://packages.ubuntu.com/focal/samba)
[iproute2](https://packages.ubuntu.com/focal/iproute2)
# Software Used

# Docker image builds
* [Debian Trixie](https://hub.docker.com/_/debian/tags?page=1&name=trixie)
* [SAMBA](https://packages.debian.org/trixie/samba)
* [GOSS](https://github.com/goss-org/goss/releases)

Auto builds are disabled to allow for multiarch local builds.
# Areas for improvement

~~Repo is auto built here: https://hub.docker.com/r/timjdfletcher/samba-timemachine/~~
* Figure out how to run rootless (WIP)
* Backup directory ownership config
* User configuration, maybe bake the user into the container but how to support UID/GID mapping?
* Maybe just a hard set UID/GID ?
* Experiment with https://github.com/GoogleContainerTools/container-structure-test
4 changes: 2 additions & 2 deletions samba-timemachine/TimeMachine.quota.tmpl
Original file line number Diff line number Diff line change
@@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>GlobalQuota</key>
<integer>REPLACE_TM_SIZE</integer>
<integer>QUOTA_IN_BYTES</integer>
</dict>
</plist>
</plist>
54 changes: 54 additions & 0 deletions samba-timemachine/docker-compose-autoheal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: samba-timemachine
services:
autoheal:
container_name: autoheal
environment:
AUTOHEAL_INTERVAL: "10"
CURL_TIMEOUT: "30"
image: willfarrell/autoheal
network_mode: none
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
bind: {}
samba-timemachine:
container_name: samba-timemachine
environment:
LOG_LEVEL: "1"
PASS: password
PGID: "999"
PUID: "999"
QUOTA: "1024"
USER: tim
healthcheck:
test:
- CMD-SHELL
- /goss/goss --gossfile /goss/tests/goss-healthcheck-tests.yaml validate
timeout: 10s
interval: 1m0s
retries: 2
start_period: 20s
image: timjdfletcher/samba-timemachine:latest
labels:
autoheal: "true"
networks:
default: null
ports:
- mode: ingress
target: 445
published: "10445"
protocol: tcp
restart: unless-stopped
tty: true
volumes:
- type: volume
source: backups
target: /backups
volume: {}
networks:
default:
name: samba-timemachine
volumes:
backups:
name: samba-timemachine_backups
56 changes: 38 additions & 18 deletions samba-timemachine/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
version: '3.3'

name: samba-timemachine
services:
samba-timemachine:
image: samba-timemachine:tmp
container_name: samba-timemachine
environment:
PUID: ${PUID}
PGID: ${PGID}
USER: ${USER}
PASS: ${PASS}
QUOTA: ${QUOTA}
ports:
- 10445:445
volumes:
- backups:/backups:rw
restart: unless-stopped
tty: true
samba-timemachine:
container_name: samba-timemachine
environment:
PUID: "${PUID:-999}"
PGID: "${PGID:-999}"
USER: "${USER:-timemachine}"
PASS: "${PASS:-password}"
LOG_LEVEL: "${LOG_LEVEL:-1}"
QUOTA: "${QUOTA:-1024}"
healthcheck:
test:
- CMD-SHELL
- /goss/goss --gossfile /goss/tests/goss-healthcheck-tests.yaml validate
timeout: 10s
interval: 1m0s
retries: 2
start_period: 20s
image: timjdfletcher/samba-timemachine:${IMAGE_VERSION:-latest}
networks:
default: null
ports:
- mode: ingress
target: 445
published: 10445
protocol: tcp
restart: unless-stopped
tty: true
volumes:
- type: volume
source: backups
target: /backups
volume: {}
networks:
default:
name: samba-timemachine_default
volumes:
backups:
backups:
name: samba-timemachine_backups
Loading