Windows Domain Controller & AD CS

This runbook covers deployment of Windows Server 2022 as a Domain Controller with Active Directory Certificate Services (AD CS) for the Domus Digitalis home lab.

Related Documentation

  • Linux certificate enrollment and ISE integration: See domus-ise-linux component (02-pki/certificate-enrollment)

1. Architecture

Component Configuration

Hostname

home-dc01

IP Address

10.50.1.50

Domain

inside.domusdigitalis.dev

NetBIOS

INSIDE

CA Name

HOME-ROOT-CA

Certificate Template

Linux-Workstation-Auth

2. VM Deployment

2.1. VM Specifications

Resource Value

RAM

8 GB (4 GB minimum)

vCPUs

2

Disk

60 GB

Network

Bridge to VLAN 1 (10.50.1.0/24)

ISO

Windows Server 2022 Standard (Desktop Experience)

2.2. KVM Installation

sudo virt-install \
    --name home-dc01 \
    --ram 8192 \
    --vcpus 2 \
    --disk path=/var/lib/libvirt/images/home-dc01.qcow2,size=60 \
    --os-variant win2k22 \
    --network bridge=br0 \
    --graphics vnc \
    --cdrom /path/to/windows_server_2022.iso

3. Post-Installation Configuration

3.1. Network Configuration

Set static IP:

New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress 10.50.1.50 -PrefixLength 24 -DefaultGateway 10.50.1.1

Set DNS (pfSense initially):

Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.50.1.1

Rename and restart:

Rename-Computer -NewName "home-dc01" -Restart

3.2. SSH Access with YubiKey (FIDO2)

Windows Server 2022 ships with OpenSSH 8.1, which doesn’t support sk-ssh-ed25519 (FIDO2). Upgrade to v9.5+ for YubiKey support.

3.2.1. Upgrade OpenSSH

Download OpenSSH:

Invoke-WebRequest -Uri "https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.5.0.0p1-Beta/OpenSSH-Win64-v9.5.0.0.msi" -OutFile "$env:TEMP\OpenSSH.msi"

Stop SSH and install:

Stop-Service sshd -Force
msiexec /i "$env:TEMP\OpenSSH.msi" /qn

Start service:

Start-Service sshd

3.2.2. Configure Authorized Keys

Create authorized_keys file:

$keys = @"
sk-ssh-ed25519@openssh.com AAAA... user@yubikey-primary
sk-ssh-ed25519@openssh.com AAAA... user@yubikey-secondary
ssh-ed25519 AAAA... user@fallback
"@
$keys | Out-File -Encoding UTF8 C:\ProgramData\ssh\administrators_authorized_keys

Set permissions:

icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"

3.2.3. Windows Firewall

Create SSH rule:

New-NetFirewallRule -DisplayName "OpenSSH Server" -Direction Inbound -Protocol TCP -LocalPort 22 -Action Allow

Enable firewall:

Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True

3.2.4. Make SSH Persistent After Reboot

Create startup script:

$script = @'
Start-Service sshd -ErrorAction SilentlyContinue
Set-Service -Name sshd -StartupType Automatic
if (-not (Get-NetFirewallRule -DisplayName "OpenSSH Server" -ErrorAction SilentlyContinue)) {
    New-NetFirewallRule -DisplayName "OpenSSH Server" -Direction Inbound -Protocol TCP -LocalPort 22 -Action Allow
}
'@
$script | Out-File -Encoding UTF8 "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\ssh-startup.ps1"

Set sshd to auto-start:

Set-Service -Name sshd -StartupType Automatic

3.2.5. SSH Client Configuration (Linux)

Add to ~/.ssh/config:

Host home-dc01
    HostName 10.50.1.50
    User Administrator
    IdentityFile ~/.ssh/id_ed25519_sk_rk_primary
    IdentityFile ~/.ssh/id_ed25519_sk_rk_secondary
    IdentityFile ~/.ssh/id_ed25519_fallback
    PreferredAuthentications publickey,password
    ControlMaster no

4. Active Directory Domain Services

4.1. Install AD DS Role

Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools

4.2. Promote to Domain Controller

Multi-line version:

$SafePassword = ConvertTo-SecureString "<DSRM-PASSWORD>" -AsPlainText -Force

Install-ADDSForest `
    -DomainName "inside.domusdigitalis.dev" `
    -DomainNetBIOSName "INSIDE" `
    -ForestMode "WinThreshold" `
    -DomainMode "WinThreshold" `
    -InstallDns `
    -SafeModeAdministratorPassword $SafePassword `
    -Force

SSH-friendly single line:

$SafePassword = ConvertTo-SecureString "<DSRM-PASSWORD>" -AsPlainText -Force; Install-ADDSForest -DomainName "inside.domusdigitalis.dev" -DomainNetBIOSName "INSIDE" -ForestMode "WinThreshold" -DomainMode "WinThreshold" -InstallDns -SafeModeAdministratorPassword $SafePassword -Force

After promotion, the server restarts. Log in as INSIDE\Administrator.

Update DNS to point to self:

Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.50.1.50,10.50.1.1

4.3. Create Service Accounts and Groups

Create ISE service account:

$IsePassword = ConvertTo-SecureString "<ISE-SVC-PASSWORD>" -AsPlainText -Force

New-ADUser -Name "svc-ise" `
    -SamAccountName "svc-ise" `
    -UserPrincipalName "svc-ise@inside.domusdigitalis.dev" `
    -AccountPassword $IsePassword `
    -Enabled $true `
    -PasswordNeverExpires $true `
    -Description "ISE AD Join Service Account"

Create security group:

New-ADGroup -Name "Linux-Cert-Enrollers" -GroupScope DomainLocal -Description "Authorized to enroll Linux workstation certificates"

Add administrator to group:

Add-ADGroupMember -Identity "Linux-Cert-Enrollers" -Members "Administrator"

5. Active Directory Certificate Services

5.1. Install AD CS Role

Install-WindowsFeature -Name ADCS-Cert-Authority,ADCS-Web-Enrollment -IncludeManagementTools

Verify installation:

Get-WindowsFeature ADCS* | Where-Object InstallState -eq 'Installed'

5.2. Configure Enterprise Root CA

Install-AdcsCertificationAuthority `
    -CAType EnterpriseRootCA `
    -CACommonName "HOME-ROOT-CA" `
    -KeyLength 4096 `
    -HashAlgorithmName SHA256 `
    -ValidityPeriod Years `
    -ValidityPeriodUnits 10 `
    -Force

Configure Web Enrollment:

Install-AdcsWebEnrollment -Force

Verify CA:

Get-Service certsvc
certutil -CAInfo

5.3. Export Root CA Certificate

Create certs directory:

New-Item -Path "C:\Certs" -ItemType Directory -Force

Get and export certificate:

$cert = (Get-ChildItem -Path Cert:\LocalMachine\CA | Where-Object { $_.Subject -like "*HOME-ROOT-CA*" })[0]
Export-Certificate -Cert $cert -FilePath C:\Certs\HOME-ROOT-CA.cer -Type CERT

Convert to PEM:

certutil -encode C:\Certs\HOME-ROOT-CA.cer C:\Certs\HOME-ROOT-CA.pem

Verify:

Get-ChildItem C:\Certs

6. Linux Workstation Certificate Template

The "Supply in Request" option is a known AD CS vulnerability (ESC1) if not properly secured. Restrict enrollment to the Linux-Cert-Enrollers group.

6.1. Create Template (GUI)

  1. Open Certificate Templates Console: certtmpl.msc

  2. Right-click Workstation Authentication > Duplicate Template

  3. Configure:

Setting Value

General: Template Name

Linux-Workstation-Auth

General: Validity

1 year

General: Renewal

6 weeks

Subject Name

Supply in the request

Extensions: Application Policies

Client Authentication

Security

Remove "Domain Computers" Enroll permission

Security

Add "Linux-Cert-Enrollers" with Enroll permission

6.2. Create Template (PowerShell)

$ConfigContext = (Get-ADRootDSE).configurationNamingContext
$TemplatePath = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext"

$NewTemplateCN = "Linux-Workstation-Auth"
$NewTemplateOID = "1.3.6.1.4.1.311.21.8." + [guid]::NewGuid().ToString().Replace("-",".")
$NewTemplate = @{
    "Name" = $NewTemplateCN
    "DisplayName" = "Linux-Workstation-Auth"
    "objectClass" = "pKICertificateTemplate"
    "flags" = 131680
    "revision" = 100
    "msPKI-Cert-Template-OID" = $NewTemplateOID
    "msPKI-Certificate-Name-Flag" = 1
    "msPKI-Enrollment-Flag" = 0
    "msPKI-Minimal-Key-Size" = 2048
    "msPKI-Private-Key-Flag" = 16842752
    "msPKI-RA-Signature" = 0
    "msPKI-Template-Minor-Revision" = 1
    "msPKI-Template-Schema-Version" = 2
    "pKICriticalExtensions" = "2.5.29.15"
    "pKIDefaultCSPs" = "1,Microsoft RSA SChannel Cryptographic Provider"
    "pKIDefaultKeySpec" = 1
    "pKIExpirationPeriod" = [byte[]](0x00,0x80,0x1A,0x06,0x00,0x00,0x00,0x00)
    "pKIExtendedKeyUsage" = "1.3.6.1.5.5.7.3.2"
    "pKIKeyUsage" = [byte[]](0xA0,0x00)
    "pKIMaxIssuingDepth" = 0
    "pKIOverlapPeriod" = [byte[]](0x00,0x80,0xA6,0x0A,0x02,0x00,0x00,0x00)
}
New-ADObject -Name $NewTemplateCN -Type pKICertificateTemplate -Path $TemplatePath -OtherAttributes $NewTemplate

Add template to CA:

Add-CATemplate -Name "Linux-Workstation-Auth" -Force

6.3. Publish Template

Add-CATemplate -Name "Linux-Workstation-Auth" -Force

Verify:

certutil -CATemplates | Select-String "Linux"

7. pfSense DNS Configuration

Before ISE can join AD, it must resolve inside.domusdigitalis.dev.

7.1. Automated via netapi

Change directory:

cd ~/atelier/_projects/personal/netapi-tui/02_AUTOMATA/pfsense/python

Load credentials:

eval "$(dsec source d000 dev/network)"

Run script:

uv run python add_dns_domain_override.py

7.2. Manual via pfSense GUI

  1. Navigate to Services > DNS Resolver > Domain Overrides

  2. Add:

    • Domain: inside.domusdigitalis.dev

    • IP Address: 10.50.1.50

  3. Save and Apply

7.3. Verify DNS

dig @10.50.1.1 home-dc01.inside.domusdigitalis.dev +short

Expected: 10.50.1.50

8. Verification Checklist

  • VM created and Windows Server installed

  • Static IP configured (10.50.1.50)

  • SSH with YubiKey working

  • AD DS promoted successfully

  • DNS resolving inside.domusdigitalis.dev

  • AD CS installed and configured

  • ROOT CA exported to C:\Certs\HOME-ROOT-CA.pem

  • Linux-Workstation-Auth template published

  • Service account svc-ise created

  • Security group Linux-Cert-Enrollers created

  • pfSense DNS override configured

9. Next Steps

After DC is operational, see domus-ise-linux component:

  1. Enroll Linux workstation certificates (02-pki/certificate-enrollment)

  2. Configure ISE Authentication Policy (03-ise-config/authentication-policy)

  3. Configure Linux EAP-TLS clients (04-linux-client/networkmanager-wired)