Initial commit: MAAS-deployable Samba Active Directory domain controller
Builds a Debian image that MAAS deploys to bare metal as an Active Directory
domain controller, with first-boot automation that either creates a domain or
joins an existing one without anyone logging in. To a Windows client the result
is an AD domain: same Kerberos, same LDAP, same Group Policy, same domain join.
The request was for "primary and backup domain controllers", which is NT 4
terminology. Active Directory has no such split — every DC holds a writable copy
and any of them can service a logon. What lives on one DC at a time are the five
FSMO roles, one confusingly named "PDC Emulator". The image therefore offers
provision (create the domain) and join (add another equal DC), which is the
distinction that actually exists.
Contents:
* customize-samba-ad.sh, run inside the packer-maas build VM: installs samba,
winbind, Kerberos, chrony and rsync, swaps the cloud kernel for the generic
one, and deletes every trace of a domain so one image can produce many DCs
* adc-maas-init, a first-boot state machine covering /etc/hosts, node-unique
identifiers, time, the resolver, provisioning or joining, the service
switchover and a self-test that proves Kerberos issues a ticket
* adc-sysvol-sync, a systemd timer implementing the SYSVOL workaround
* a MAAS curtin preseed, cloud-init examples, and the release pipeline
Two findings shaped the design.
Debian builds Samba against its bundled Heimdal rather than system MIT Kerberos
— samba-ad-dc does not depend on krb5-kdc — so the AD DC role is in Samba's
supported configuration, not the experimental MIT one. The build asserts this
and fails if it ever changes.
Samba implements neither DFS-R nor FRS, so SYSVOL — where Group Policy lives —
does not replicate between DCs. Left alone, a policy created on one DC never
reaches the others and clients behave differently depending on which DC answered
them, with nothing reporting an error. adc-sysvol-sync applies the Samba wiki's
rsync workaround: sync idmap.ldb once so SID-to-uid mappings agree, rsync the
tree, then samba-tool ntacl sysvolreset because rsync carries POSIX bits while
the Windows ACLs live in AD. Without an SSH key to the source DC it exits with
an explanation rather than letting Group Policy diverge quietly.
Also carried over from maas-proxmox, where they were verified on real hardware:
the curtin-hooks that skip the kernel install and pin interface names by MAC,
Type=simple on the first-boot unit to avoid the systemd ordering cycle, and
shipping networking.service disabled so MAAS owns the network on first boot.
Specific to this image, the Debian cloud kernel is replaced with the generic one
— a bare-metal node booted with the cloud kernel can come up with no disk.
Nothing here has been built or deployed. The README says so at the top and in a
Verified status section that separates what was checked from what was not.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
116
overlay/curtin/curtin-hooks
Executable file
116
overlay/curtin/curtin-hooks
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2026 Ilker Manap
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
# curtin-hooks - MAAS/curtin kurulum kancasi (Proxmox VE imaji icin)
|
||||
#
|
||||
# Curtin, hedef sistemde /curtin/curtin-hooks varsa dahili curthooks yerine
|
||||
# bu script'i calistirir (curtin.util.run_hook_if_exists).
|
||||
#
|
||||
# Amac: curtin'in APT uzerinden cekirdek kurmaya calismasini engellemek.
|
||||
# Proxmox cekirdegi (proxmox-default-kernel) imajin icinde hazir gelir;
|
||||
# curtin'in onu yeniden kurmasi gereksizdir ve dugumun deploy sirasinda
|
||||
# download.proxmox.com'a erisebilmesini zorunlu kilar (izole aglarda calismaz).
|
||||
#
|
||||
# Not: Bunu preseed'de "kernel: null" ile yapmak mumkun degil - MAAS ile
|
||||
# gelen curtin surumlerinin bir kismi bu degeri desteklemiyor ve
|
||||
# install_kernel icinde AttributeError ile patliyor. Kancayi kullanmak
|
||||
# curtin surumunden bagimsiz olarak calisir.
|
||||
#
|
||||
import shutil
|
||||
import os
|
||||
|
||||
from curtin.commands import curthooks
|
||||
from curtin.config import load_command_config
|
||||
from curtin.util import load_command_environment
|
||||
|
||||
|
||||
def disable_kernel_install():
|
||||
"""curtin'in cekirdek kurulum adimini etkisiz hale getir.
|
||||
|
||||
Fonksiyon adi curtin surumune gore degisiyor (install_kernel ->
|
||||
curthook_install_kernel), ikisini de yakaliyoruz.
|
||||
"""
|
||||
disabled = []
|
||||
for name in ("install_kernel", "curthook_install_kernel"):
|
||||
if hasattr(curthooks, name):
|
||||
setattr(curthooks, name, lambda *args, **kwargs: None)
|
||||
disabled.append(name)
|
||||
print("curtin-hooks: cekirdek kurulumu devre disi: %s" % (disabled or "hicbiri bulunamadi"))
|
||||
|
||||
|
||||
def pin_interface_names(config, target):
|
||||
"""MAAS'in bildigi arayuz adlarini hedef sistemde MAC uzerinden sabitle.
|
||||
|
||||
MAAS commissioning'i Ubuntu ephemeral ortaminda yapiyor ve arayuzu orada
|
||||
gordugu adla (or. enp6s18) kaydediyor. Deploy edilen Debian 13 / Proxmox
|
||||
ise udev'in farkli isimlendirme semasi yuzunden ayni karti ens18 diye
|
||||
adlandirabiliyor. Bu durumda cloud-init acilista arayuzu yeniden
|
||||
adlandirmaya calisiyor, su hatayi aliyor:
|
||||
|
||||
Failed to rename devices: [busy] Error renaming
|
||||
mac=... from ens18 to enp6s18
|
||||
|
||||
...ve arayuzu KAPALI birakiyor; dugum agini tamamen kaybediyor.
|
||||
|
||||
Cozum: MAC -> ad eslemesini systemd .link dosyasi olarak yaziyoruz, boylece
|
||||
udev karti en bastan MAAS'in bekledigi adla olusturuyor ve yeniden
|
||||
adlandirmaya hic gerek kalmiyor.
|
||||
"""
|
||||
net = config.get("network") or {}
|
||||
pairs = []
|
||||
|
||||
# netplan / v2 bicimi
|
||||
for name, cfg in (net.get("ethernets") or {}).items():
|
||||
cfg = cfg or {}
|
||||
mac = (cfg.get("match") or {}).get("macaddress")
|
||||
if mac:
|
||||
pairs.append((cfg.get("set-name") or name, mac))
|
||||
|
||||
# curtin v1 bicimi
|
||||
for entry in net.get("config") or []:
|
||||
if not isinstance(entry, dict) or entry.get("type") != "physical":
|
||||
continue
|
||||
if entry.get("name") and entry.get("mac_address"):
|
||||
pairs.append((entry["name"], entry["mac_address"]))
|
||||
|
||||
if not pairs:
|
||||
print("curtin-hooks: arayuz adi sabitlenmedi (ag yapilandirmasi bos)")
|
||||
return
|
||||
|
||||
link_dir = os.path.join(target, "etc/systemd/network")
|
||||
os.makedirs(link_dir, exist_ok=True)
|
||||
for name, mac in pairs:
|
||||
path = os.path.join(link_dir, "10-maas-%s.link" % name)
|
||||
with open(path, "w") as fh:
|
||||
fh.write(
|
||||
"# MAAS tarafindan beklenen arayuz adi - pve-maas imaji\n"
|
||||
"# curtin-hooks tarafindan olusturuldu.\n"
|
||||
"[Match]\n"
|
||||
"MACAddress=%s\n"
|
||||
"\n"
|
||||
"[Link]\n"
|
||||
"Name=%s\n" % (mac.lower(), name)
|
||||
)
|
||||
print("curtin-hooks: arayuz adi sabitlendi %s -> %s" % (mac, name))
|
||||
|
||||
|
||||
def cleanup():
|
||||
"""Kancayi hedef sistemden kaldir - iz birakma."""
|
||||
curtin_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
shutil.rmtree(curtin_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def main():
|
||||
state = load_command_environment()
|
||||
config = load_command_config(None, state)
|
||||
target = state["target"]
|
||||
|
||||
disable_kernel_install()
|
||||
curthooks.builtin_curthooks(config, target, state)
|
||||
pin_interface_names(config, target)
|
||||
cleanup()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user