by: Helge, published: Jun 23, 2024, updated: Jul 14, 2024, in

Samba Active Directory in a Docker Container: Installation Guide

This article explains how to install a Samba v4 Active Directory domain controller in a Docker container. This post is part of my series on home automation that shows how to install, configure, and run a home server with dockerized or virtualized services.

This article is part of a mini-series about running Samba Active Directory and a file server service in a Docker container on a home server:

Architecture Overview

In my (home) network I want one identity provider for all services. In other words: only one password for everything. For that, I need a single “source of truth”. Authelia, my identity and access management (IAM) server, relies on an LDAP server. Until recently, I used lldap, a lightweight LDAP implementation.

But now I’m planning to switch from ownCloud to Samba as my file storage solution. Samba file sharing cannot authenticate against lldap – but Samba can be installed as an Active Directory domain controller, a role that comes with LDAP built-in. This makes it possible to set up Samba Active Directory as a central IAM server that authenticates Windows clients accessing SMB file shares just as well as providing Open ID Connect SSO services to other types of apps.

Dockerized or Natively Installed?

I’m using Samba in a Docker container because of the flexibility that kind of setup provides even though a Samba Active Directory domain controller poses significant challenges to Docker aficionados:

  • A Samba AD DC needs true root access (because it uses the security.* namespace for file system ACLs).
  • A Samba AD DC needs a static IP address on the host network.
    • The DC’s external IP address must be the same as the container’s internal address because an AD DC manages its own DNS zones whose IP addresses (as seen by the DC) must be reachable from machines on the external network.
  • The main DNS server running on the host must be able to forward queries to the AD DC running in a container – and vice versa.

Preparation

I’m assuming that you’ve set up Docker on Proxmox as described in the previous articles in this series.

Note: File Server in Addition to Domain Controller

You’ll notice that this guide covers a file server container in addition to the domain contoller. The reason is that a Samba file server is closely related to and dependent on a Samba domain controller. Therefore, the Docker compose file and related configs cover both services. The file server configuration will be covered in a later article.

Variables

The container we’re about to set up is going to be an Active Directory domain controller (DC). We’re using the following names and IP addresses:

  • DC hostname: dc1
  • DC IP address: 192.168.0.5
  • FS hostname: fs1
  • FS IP address: 192.168.0.6
  • Main DNS address: 192.168.0.4
  • AD Domain FQDN: ad.internal (internal is the official TLD for private networks)
  • Host network interface: vmbr0 (this is the default bridge interface created by Proxmox)

Add ACL Support to ZFS

If you haven’t done so yet, enable Posix ACLs for the (ZFS) file system your container is running on:

zfs set acltype=posixacl rpool/encrypted
zfs set xattr=sa rpool/encrypted

Static IP Address on the Host Network for the Samba Container

We can connect our Samba container directly to the host network with a Macvlan Docker network. This makes it possible to assigin a static IP address on the host network to the container as follows (snippet from docker-compose.yml):

services:
  sambanet:
    networks:
      samba:
        ipv4_address: 192.168.0.5

networks:
  sambanet:
    driver: macvlan
    driver_opts:
      parent: vmbr0
    ipam:
      config:
        - subnet: "192.168.0.0/24"
          gateway: "192.168.0.1"
          ip_range: "192.168.0.5/32"

Disable Isolation & Enable Pings Between Container and Host

With the above Macvlan configuration, the container can access other machines on the network, but communication with services on the host is not possible.

To disable the container/host isolation and enable network communication between the two, we’re using a clever solution devised by Lars Kellogg-Stedman and slightly improved by Francis-perso:

On the host, we set up a virtual link and add a route that points to the container’s IP address. To make it persistent, we need to recreate the link after every boot. /etc/network/interfaces is a good place to do that. Edit the file and paste the following at the end:

post-up ip link add docker_samba link vmbr0 type macvlan mode bridge
post-up ip link set docker_samba up
post-up ip route add 192.168.0.5/32 dev docker_samba
post-up ip route add 192.168.0.6/32 dev docker_samba

Reboot the Docker host to apply the changes.

Dockerized Samba Domain Controller Installation

Dockerized Samba Directory Structure

This is what the directory structure will look like when we’re done:

rpool/
 └── encrypted/
     └── docker/
         └── samba/
             ├── config-dc1/
                 └── smb.conf
             ├── config-fs1/
                 ├── samba/
                     └── smb.conf
                 └── supervisor/
             ├── data-dc1/
             ├── data-fs1/
             ├── dockerfile/
             	  ├── Dockerfile
             	  ├── init-dc.sh
             	  ├── samba-provision.sh
             	  └── vars.sh
             ├── shares-fs1/
             ├── .env
             └── docker-compose.yml

We’re placing the configuration on the encrypted ZFS dataset (rpool/encrypted)

Create Directories

Create the directories:

mkdir -p /rpool/encrypted/docker/samba/config-dc1
mkdir -p /rpool/encrypted/docker/samba/config-fs1/samba
mkdir -p /rpool/encrypted/docker/samba/config-fs1/supervisor
mkdir -p /rpool/encrypted/docker/samba/data-dc1
mkdir -p /rpool/encrypted/docker/samba/data-fs1
mkdir -p /rpool/encrypted/docker/samba/dockerfile
mkdir -p /rpool/encrypted/docker/samba/shares-fs1

Samba Docker Files

Clone the files to build the Docker container image from my GitHub repository:

git init
git remote add origin https://github.com/helgeklein/samba-docker-home-server.git
git pull origin main

Samba .env File

Create an .env with the following content to specify variables for your setup:

DC_NAME=dc1
DC_IP=192.168.0.5
FS_NAME=fs1
FS_IP=192.168.0.6
CONTAINER_IP_RANGE=192.168.0.4/30
SUBNET=192.168.0.0/24
GATEWAY=192.168.0.1
MAIN_DNS_IP=192.168.0.4
HOST_INTERFACE=vmbr0
DOMAIN_FQDN=ad.internal
DOMAIN_DN=dc=ad,dc=internal

Build the Image & Start the Samba Container

Navigate into the directory with docker-compose.yml and run:

docker compose build --pull
docker compose up -d

Inspect the container logs for errors with the command docker compose logs --tail 30 --timestamps. You should see the following:

No Samba config file found at: /etc/samba/config/smb.conf
Please exec into the container and provision Samba by running the following commands:
   docker exec -it dc1 bash
   /usr/helpers/samba-provision.sh

Provision a New AD Domain

Follow the instructions above to provision a new Active Directory domain by running:

docker exec -it dc1 bash
/usr/helpers/samba-provision.sh

After the domain is provisioned, restart the container to give my init script the chance to do its magic at container start:

docker compose down
docker compose up -d

Inspect the container logs for errors with the command docker compose logs --tail 30 --timestamps. You should see the following:

samba version 4.19.5-Ubuntu started.
Copyright Andrew Tridgell and the Samba Team 1992-2023
Attempting to autogenerate TLS self-signed keys for https for hostname 'DC1.ad.internal'
prefork_child_pipe_handler: Parent 7, Child 19 exited with status 0
TLS self-signed keys generated OK

Change the Administrator Password

Change the randomly generated initial administrator password:

docker exec -it dc1 bash
samba-tool user setpassword Administrator

DNS Forwarding

Since we want to keep using our existing DNS server we need to tell it to forward queries for the AD domain name to the DC’s DNS server. Add the following to the Unbound configuration:

   # Disable DNSSEC for our AD domain
   domain-insecure: "ad.internal"
   # Disable rebind protection for our AD domain
   private-domain: "ad.internal"
   # Forward AD domain queries to the DC
   stub-zone:
      name: "ad.internal"
      stub-addr: 192.168.0.5

Verification

An Active Directory domain controller depends on multiple services that all need to operate correctly. Make sure to perform the below checks. Do not ignore “weird” behavior – fix anything you may find!

DNS

DNS name resolution is essential for Active Directory. As an old saying goes, at the root of every AD problem lies a DNS problem.

DNS Updates

Run the following to check if dynamic DNS updates are working:

docker exec -it dc1 bash
samba_dnsupdate --verbose --all-names
# The following errors are apparently "normal":
# ; TSIG error with server: tsig verify failure
# Failed nsupdate: 2

AD Domain Zone

List all records of the AD domain DNS zones:

docker exec -it dc1 bash
samba-tool dns query localhost ad.internal @ ALL -U administrator
samba-tool dns query localhost _msdcs.ad.internal @ ALL -U administrator

Name Resolution

Make sure the following names can be resolved quickly and correctly both on your Docker host and on the machines in your network:

# A records
ad.internal
dc1.ad.internal

# SRV records (on Linux, use: host -a querystring)
_ldap._tcp.dc._msdcs.ad.internal
_kerberos._udp.ad.internal

Kerberos

Test Kerberos in the DC container:

docker exec -it dc1 bash
kinit administrator
klist

File Shares

Make sure the domain controller’s default file shares are listed:

docker exec -it dc1 bash
smbclient -L localhost -N

Open Ports

Inspect the open ports and the processes/services that listen on them. With Macvlan networking, we can only see this in the container, not on the host:

docker exec -it dc1 bash
ss -nltp

Optimizations

Remove RFC2307 ID Mappings

As per the official recommendation, remove the following line in the DC’s config/smb.conf file:

 idmap_ldb:use rfc2307 = yes

Previous Article Unbound: Conditionally Include Only Existing (Docker) Interfaces
Next Article Samba Active Directory as Authelia's SSO Authentication Backend