EN

Documentazione di laboratorio

Laboratorio Exchange ibridoCom'è costruito, e perché così

Exchange 2019 CU15 · Proxmox VE 9 · pfSense 2.8
7 parti · 40 sezioni · 32 blocchi comando
Revisione agosto 2026
/ per cercare

Come leggere questo manuale

Questo non è un tutorial per costruire un laboratorio Exchange. È la documentazione di uno laboratorio: cosa contiene, perché ogni pezzo è fatto in quel modo, e cosa si è rotto durante la costruzione.

Se quello che cerchi sono i passi — dall'hypervisor vuoto all'ibrido funzionante, in ordine — sta nel manuale gemello: Costruire il laboratorio.

La parte che vale non sono i comandi — quelli stanno anche altrove — ma le motivazioni. Perché i segmenti sono /29 e non una rete piatta. Perché il bilanciatore lavora a livello 4 e non 7. Perché la sincronizzazione guarda una sola unità organizzativa. Ognuna di queste scelte esiste per riprodurre una condizione precisa di un ambiente di produzione, e senza quella condizione il laboratorio proverebbe qualcosa di diverso da quello che serve.

#Cosa contiene, e cosa no

Il documento originale copre anche l'accesso amministrativo al laboratorio, le credenziali e gli impegni verso il tenant su cui è appoggiato. Quella parte non è qui, e non ci sarà: è la mappa di come si entra in un ambiente che esiste davvero.

Restano fuori anche i riferimenti all'infrastruttura di produzione che il laboratorio riproduce. Il laboratorio nasce da un ambiente reale di un'organizzazione reale, e le sue caratteristiche — dominio, certificati, dimensionamento — non sono cose da pubblicare.

Tutti i nomi interni sono sostituiti: il dominio Active Directory è contoso.lab, gli indirizzi appartengono a intervalli generici, le persone si chiamano Mario Rossi. Le sostituzioni sono coerenti fra tutte le sezioni: se due pezzi parlano dello stesso oggetto, lo chiamano allo stesso modo.

#I tre livelli

Le sezioni portano un'etichetta che dice a chi servono. Il filtro in alto le usa.

LivelloA chi serveCosa copre
L1Chiunque debba capire l'insiemeConcetti, verifiche di sola lettura, diagnosi di base
L2Chi costruisce o mantieneConfigurazione dei servizi, manutenzione ordinaria
L3Chi ricostruisce da zeroInstallazioni, sottoscrizioni, regole di sincronizzazione

Parte 1 — Perché un laboratorio

Un laboratorio costa tempo e memoria. Vale la pena solo se risponde a domande che l'ambiente vero non permette di porre.

#L'origine: tre cose che non si potevano studiare altrove

L1

Il laboratorio nasce da tre situazioni concrete, tutte con la stessa forma: una domanda a cui non si poteva rispondere senza rischiare.

  • Un incidente di fallimenti TLS/SMTP su cui erano state formulate ipotesi che nessuno poteva verificare. Le ipotesi restavano ipotesi perché provarle voleva dire toccare la posta di tutti.
  • Un ripristino della password dell'amministratore locale su un server perimetrale, eseguito in emergenza. Una procedura che nessuno aveva mai provato prima di doverla eseguire.
  • La necessità di provare gli script di provisioning prima di eseguirli dove ogni errore ha conseguenze.

In generale: l'assenza di un posto dove poter sbagliare.

#Il criterio: fedeltà architetturale

L1

L'obiettivo dichiarato non era «un Exchange funzionante». Era una replica il più possibile aderente all'architettura reale, perché solo così i comportamenti osservati sono trasferibili.

La differenza è concreta. Un Exchange su rete piatta funziona benissimo e non riproduce il fenomeno per cui il server non vede l'indirizzo del vero mittente. Se l'incidente da studiare riguarda proprio quello, il laboratorio non serve a niente.

Ogni scelta successiva discende da questo criterio: quando una semplificazione avrebbe cancellato un fenomeno osservabile, la semplificazione è stata scartata.

#Cosa si riproduce, e cosa no

L2

Riprodotto fedelmente:

ElementoNel laboratorio
Exchange Server 2019 CU15Stessa versione, stessa build
Due server cassette in DAGLAB-MBX01 e LAB-MBX02
Reti a micro-segmenti /29 instradatiCinque segmenti dietro firewall
Bilanciatore che nasconde l'indirizzo sorgenteHAProxy L4 con SNAT
Due server perimetrali in workgroupLAB-EDG01 e LAB-EDG02 con EdgeSync
Connettori applicativiCoppia anonimo/autenticato
Ibrido con Entra ConnectSincronizzazione verso un tenant vero
Regole di sincronizzazione personalizzateFiltro su attributi
Struttura di OU per ciclo di vitaNove unità organizzative

Non riprodotto, e ogni volta per un motivo:

ElementoPerché no
Dieci databaseL'edizione Standard limita a cinque database per server. Con quattro database e copie incrociate si ottiene comunque un DAG bilanciato.
Hybrid Configuration WizardScrive connettori a livello di organizzazione in un tenant che ospita posta reale. Scelta consapevole di non eseguirlo.
PKI interna a più livelliIn produzione il certificato di servizio viene da una CA pubblica commerciale, non da una CA aziendale. Una PKI interna riprodurrebbe qualcosa che non esiste.
Bilanciatore commercialeSostituito da HAProxy in L4 con SNAT: comportamento di rete equivalente ai fini dell'indagine, costo e complessità molto inferiori.
Certificato pubblico di servizioNon ottenibile per un dominio di laboratorio. I certificati restano auto-firmati.

#I nomi di fantasia sono un meccanismo di sicurezza

L1

Tutti i nomi del laboratorio sono inventati e non hanno alcuna corrispondenza con nomi reali. Non è pudore: è una difesa.

Un comando destinato al laboratorio che nomina contoso.lab non può, per distrazione, colpire l'ambiente di produzione. Se i due ambienti condividessero i nomi, l'unica cosa che separerebbe un Remove-Mailbox di prova da un disastro sarebbe l'attenzione di chi lo digita — e l'attenzione, alle otto di sera, è una difesa debole.

Lo stesso principio vale per il prefisso LAB- in ogni nome macchina: nell'elenco dell'hypervisor, il laboratorio si riconosce a colpo d'occhio.

Parte 2 — Convivere con quello che c'è già

Il laboratorio gira su un host che ospita anche altro. La progettazione parte da lì: non deve poter danneggiare quello che c'era prima.

#I quattro recinti

L2

La protezione poggia su quattro meccanismi indipendenti. Indipendenti è la parola che conta: se uno cede, gli altri reggono.

  1. Rete. Tutte le macchine stanno su bridge Linux senza porte fisiche.
  2. Memoria e CPU. Memoria fissa, limiti espliciti, peso di scheduling ridotto.
  3. Storage. Provisioning sottile con discard, e una soglia oltre la quale ci si ferma.
  4. Convenzioni. Pool dedicato, tag, intervallo di identificativi riservato, prefisso nel nome.

#Bridge senza porte fisiche

L2

Un bridge Linux senza porte agganciate non ha alcun percorso verso le schede di rete dell'host. È isolato per costruzione, non per configurazione — che è una differenza sostanziale: una regola di firewall si può disattivare per errore, un cavo che non esiste no.

L'unico punto di contatto con la rete esterna è l'interfaccia WAN del firewall virtuale, che opera in NAT: il traffico esce mascherato dietro un solo indirizzo, e nessun servizio del laboratorio è raggiungibile dall'esterno se non attraverso inoltri di porta espliciti.

Verifica dell'isolamento:

ip -br a | grep vmbr        # i bridge del lab non devono avere IPv4
bridge link show | grep vmbr9   # nessuna riga = nessuna porta agganciata

#Memoria e CPU: perché 50 e non 512

L2

Ogni macchina del laboratorio ha memoria fissa — niente ballooning sui server Exchange — e due limiti espliciti:

qm set <vmid> --cpulimit 2     # tetto di 2 core effettivi
qm set <vmid> --cpuunits 50    # metà del peso predefinito

Il valore 50 è deliberato. Su cgroup v2 il peso predefinito è 100: mettendo 50, in caso di contesa lo scheduler favorisce le macchine preesistenti.

Sulla memoria non c'è margine da giocare: a pieno regime le macchine chiedono più RAM di quanta ne abbia l'host. Il laboratorio si usa quindi a gruppi di accensione, mai tutto insieme.

#La regola del datacenter

L1

Nessuna operazione a livello di datacenter o di nodo senza accordo esplicito. Riavvii dell'host, modifiche alla rete globale, interventi sullo storage condiviso: sono azioni che toccano tutti, e in un ambiente condiviso l'unica difesa è la regola sociale.

Parte 3 — La rete

Questa è la parte che si è deciso di non semplificare, e il resto del laboratorio poggia su questa scelta.

#I segmenti, e perché i /29

L2

Cinque segmenti dietro un firewall virtuale, ciascuno su un bridge senza porte fisiche.

SegmentoReteOspita
Gestione10.20.10.0/24Domain controller, server di sincronizzazione
Exchange10.20.20.8/29I due server cassette
Bilanciatore10.20.20.32/29HAProxy e il VIP
Client10.20.30.0/24Il client applicativo
DMZ10.20.40.0/29I due server perimetrali

La scelta dei /29 non è estetica. In produzione il bilanciatore e i server Exchange stanno su reti diverse e instradate, ed è questa la condizione che rende obbligatorio il SNAT.

Un laboratorio su rete piatta non riprodurrebbe il fenomeno. Con i /29 instradati sì, ed è verificabile nei log di protocollo.

#Il SNAT: perché Exchange non vede il vero mittente

L2

Il bilanciatore riceve le connessioni su un indirizzo virtuale e le inoltra ai server Exchange cambiando l'indirizzo sorgente con il proprio.

Non è una scelta di configurazione: è una necessità. Il bilanciatore non può presentarsi con l'indirizzo del client, perché il traffico di ritorno non tornerebbe da lui — tornerebbe direttamente al client, che non riconoscerebbe la connessione. Il prezzo è che Exchange perde la visibilità del vero mittente.

Il percorso completo di un messaggio in uscita:

Client applicativo  (10.20.30.10)
  → VIP del bilanciatore  (10.20.20.38)
  → HAProxy con SNAT      (la sorgente diventa 10.20.20.34)
  → Exchange cassette     (10.20.20.10 / .11)
  → EdgeSync → perimetro  (10.20.40.2 / .3)
  → Internet

La prova che il laboratorio è fedele sta in una riga del log di protocollo, dopo aver generato traffico attraverso il VIP:

2026-08-19T00:23:03.808Z,LAB-MBX02\Default Frontend LAB-MBX02,...,
10.20.20.11:25,10.20.20.34:37058,+,,

L'indirizzo remoto registrato è quello del bilanciatore, non del client. Gli Exchange accettano connessioni da un /29 diverso e non direttamente connesso: la condizione esatta della produzione.

#Il TTL come strumento di diagnosi

L1

Un dettaglio che vale più di molti strumenti. Nelle risposte al ping, il valore TTL distingue i percorsi:

TTL da un host WindowsSignificato
128Percorso diretto, nessun salto
127Un salto — cioè il passaggio dal firewall

Confrontare i TTL è il modo più rapido per capire se due macchine si parlano direttamente o attraverso l'instradamento. In un ambiente segmentato, questa è la prima domanda da porsi e l'ultima a cui si pensa.

#pfSense: le tre caselle che si sbagliano

L2

Tre impostazioni producono guasti che sembrano avere altre cause.

Block private networks e Block bogon networks sulla WAN. Quei filtri hanno senso su un'interfaccia affacciata a internet vero, dove traffico da indirizzi privati o riservati è per definizione anomalo. Quando la «WAN» è in realtà una LAN privata, il traffico amministrativo arriva da indirizzi privati o da intervalli classificati come bogon, e viene scartato prima di qualsiasi regola. I port forward risultano configurati e inerti.

Il campo DNS Hostname in System → General Setup. Serve alla verifica TLS del resolver e va lasciato vuoto. Inserirvi un indirizzo IP — errore facile, perché il campo è adiacente a quello degli indirizzi — impedisce la risoluzione pur essendo l'instradamento corretto.

Filter rule association su un inoltro di porta. Deve restare su Add associated filter rule. Con None, pfSense esegue il NAT ma blocca il traffico, e si passa mezz'ora a cercare la causa altrove.

#Aggiungere una scheda: non a caldo

L3

Le schede di rete non si agganciano a caldo su FreeBSD. La sequenza corretta parte da uno snapshot:

qm snapshot 1310 pre-modifica
qm set 1310 -net<N> virtio,bridge=vmbr9X,firewall=1
qm shutdown 1310 && qm start 1310

Poi, nell'interfaccia: Interfaces → Assignments → Add sulla nuova scheda, aprirla, Enable, descrizione, indirizzo statico, Save e Apply.

#Il firewall che riavvii, il testimone che cade

L2

Ogni riavvio del firewall manda offline il File Share Witness del DAG, perché la condivisione risiede sul domain controller, che sta oltre il firewall.

Non torna online da solo. È il punto in cui una manutenzione di rete apparentemente innocua lascia il cluster in uno stato degradato senza che nessuno se ne accorga. Il rimedio sta in Il quorum e il testimone.

Parte 4 — Active Directory e Exchange

#La foresta, e perché il dominio interno non basta

L2

Un solo domain controller, dominio contoso.lab, cestino di Active Directory attivo.

Il cestino non è un dettaglio: consente di ripristinare un oggetto cancellato per errore con tutti i suoi attributi. Senza, la cancellazione si propaga nel cloud e il recupero diventa laborioso.

Get-ADOptionalFeature -Filter * | ft Name,EnabledScopes -Auto

Ripristino di un oggetto eliminato:

Get-ADObject -Filter 'isDeleted -eq $true' -IncludeDeletedObjects -Properties * |
  ft Name,SamAccountName,LastKnownParent,whenChanged -Auto

Get-ADObject -Filter 'SamAccountName -eq "<utente>"' -IncludeDeletedObjects |
  Restore-ADObject

Alla foresta è stato aggiunto un suffisso UPN aggiuntivo su un dominio pubblico verificato:

Get-ADForest | Set-ADForest -UPNSuffixes @{add="lab.impicciando.it"}

È indispensabile: contoso.lab non è instradabile su internet, ed Entra Connect rifiuta di sincronizzare UPN non instradabili. Gli utenti destinati al cloud hanno quindi UPN sul dominio pubblico, mentre il samAccountName e l'accesso a Windows restano invariati.

#Le unità organizzative come recinto

L2

Nove unità organizzative che riflettono un ciclo di vita completo dell'utenza: operativi, sede, supporto, esterni, caselle di funzione, dismessi, disabilitati, in attesa di eliminazione — e SYNC.

SYNC ha una funzione diversa dalle altre: è il recinto della sincronizzazione. Entra Connect è configurato per considerare esclusivamente quella unità, quindi qualunque oggetto fuori da essa, per il tenant, non esiste.

La scelta è protettiva. Finché l'ambito resta ristretto, un errore nella configurazione della sincronizzazione può interessare al massimo gli oggetti di prova.

#Il CN deve coincidere col samAccountName

L1

Non è una preferenza estetica, ed è la trappola più costosa dell'intera struttura.

ComandoRisolve l'identità per
Add-MailboxPermissionsamAccountName
Add-ADPermissionnome dell'oggetto (CN)

Un utente creato con -Name "Ufficio Acquisti" e -SamAccountName "ufficio.acquisti" fa fallire il secondo comando con un laconico wasn't found, mentre il primo funziona regolarmente. Il risultato è uno script che assegna metà dei permessi e fallisce sull'altra metà — senza fermarsi.

Creazione corretta: il nome leggibile va nel DisplayName, che è quanto compare in rubrica.

New-ADUser -Name "mario.rossi" -DisplayName "Mario Rossi" -SamAccountName "mario.rossi"

Verifica e correzione degli oggetti non conformi:

Get-ADUser -SearchBase "OU=SYNC,DC=contoso,DC=lab" -Filter * -Properties Name,DisplayName |
  Where-Object { $_.Name -ne $_.SamAccountName } |
  ForEach-Object {
    Write-Host ("{0} -> {1}" -f $_.Name, $_.SamAccountName)
    Rename-ADObject -Identity $_.DistinguishedName -NewName $_.SamAccountName
  }

#La gerarchia oraria

L2

L'orario è il punto più delicato dell'intero laboratorio, e la ragione è raccontata per esteso in Nove ore di scarto.

Internet (time.windows.com, pool.ntp.org)
  │
  LAB-DC01  → radice della gerarchia, marcato /reliable:yes
  │
  ├── membri del dominio  → modalità domhier (automatica)
  └── macchine in workgroup → puntamento manuale al domain controller

Sul domain controller:

w32tm /config /manualpeerlist:"time.windows.com,0x8 pool.ntp.org,0x8" /syncfromflags:manual /reliable:yes /update
Set-Service w32time -StartupType Automatic
Restart-Service w32time
w32tm /resync /rediscover

Sui membri del dominio:

w32tm /config /syncfromflags:domhier /update
Restart-Service w32time
w32tm /query /source    # deve rispondere col nome del domain controller

Tutte le macchine devono avere lo stesso fuso orario dell'host che le ospita. Il motivo non riguarda la comodità di leggere l'ora, ma la correttezza dell'UTC dopo ogni riavvio.

#Il vincolo dell'edizione: cinque database

L2

L'edizione Standard consente un massimo di cinque database di cassette postali per server. L'installazione in modalità valutazione si comporta come Standard.

Il tentativo di andare oltre produce:

RcrExceedDbLimitException: ... maximum databases limit of 5

Due dettagli che si scoprono solo sbattendoci contro:

  • Il limite riguarda i database presenti sul server, copie comprese, non i database attivi. Quattro database con copia incrociata occupano quattro posti su ciascun nodo, non due.
  • L'installazione del secondo server cassette crea automaticamente un proprio database predefinito con nome generato, che consuma uno dei cinque posti. Va rimosso o rinominato prima di creare i database del progetto.

L'architettura risultante — due database attivi per nodo, con copia sull'altro — sta nel limite e dà un DAG pienamente funzionante e bilanciato.

DatabaseAttivo suCopia passiva su
DB01LAB-MBX01LAB-MBX02
DB02LAB-MBX02LAB-MBX01
DB03LAB-MBX01LAB-MBX02
DB04LAB-MBX02LAB-MBX01

Verifica dello stato:

Get-MailboxDatabaseCopyStatus * |
  ft Name,Status,ActiveCopy,CopyQueueLength,ReplayQueueLength,ContentIndexState -Auto

Mounted indica la copia attiva, Healthy una copia passiva allineata. CopyQueueLength è il numero di log ancora da copiare, ReplayQueueLength quelli copiati ma non ancora applicati: entrambi a zero significa replica in pari.

#Il DAG senza indirizzo IP

L2

Il DAG è configurato senza punto di accesso amministrativo — il modello consigliato da Exchange 2013 in poi. Il cluster sottostante non ha un nome di rete né un indirizzo IP propri.

La conseguenza pratica va conosciuta, perché sembra un guasto e non lo è: i comandi cluster non accettano -Cluster. Un tentativo produce un errore di RPC non disponibile.

Get-ClusterNode | ft Name,State -Auto   # corretto: si esegue su un nodo
Get-ClusterNode -Cluster DAG1           # errore, ed è atteso

#Il quorum e il testimone oltre il firewall

L2

Con due nodi e un testimone i voti sono tre e la maggioranza è due: il laboratorio sopravvive alla perdita di un nodo o del testimone, non di entrambi.

Il testimone risiede sul domain controller, che sta in un segmento diverso da quello dei nodi Exchange, oltre il firewall. Questa è una fragilità reale e si manifesta a ogni manutenzione del firewall.

Sintomo: QuorumGroup fallisce dicendo che il File Share Witness è offline, mentre FileShareQuorum risulta Passed.

La distinzione fra i due controlli è informativa: FileShareQuorum verifica che la configurazione del testimone sia corretta, QuorumGroup che la risorsa sia online. Durante un riavvio del firewall i nodi perdono l'accesso alla condivisione, la risorsa va in Failed e non torna su da sola.

Get-ClusterResource | ft Name,State -Auto
Start-ClusterGroup "Cluster Group"

Salute della replica:

Test-ReplicationHealth -Identity LAB-MBX01 | Where-Object Result -ne 'Passed'

Test-ReplicationHealth esegue diciannove controlli per server. Se il comando filtrato non stampa nulla, il DAG è integro.

#Manutenzione di un nodo

L3

La sequenza va eseguita dal nodo che resta attivo.

Set-ServerComponentState LAB-MBX01 -Component HubTransport -State Draining -Requester Maintenance
Invoke-Command -ComputerName LAB-MBX01 -ScriptBlock { Restart-Service MSExchangeTransport }
Redirect-Message -Server LAB-MBX01 -Target LAB-MBX02.contoso.lab
Suspend-ClusterNode -Name LAB-MBX01
Set-MailboxServer LAB-MBX01 -DatabaseCopyActivationDisabledAndMoveNow $true
Set-MailboxServer LAB-MBX01 -DatabaseCopyAutoActivationPolicy Blocked
Set-ServerComponentState LAB-MBX01 -Component ServerWideOffline -State Inactive -Requester Maintenance

Due passaggi che quella sequenza non completa da sola, e che vanno verificati.

I database attivi non sempre si spostano: il flag può incontrare il limite di quattro spostamenti all'ora per database imposto da Active Manager. Vanno allora spostati a mano.

Move-ActiveMailboxDatabase DB01 -ActivateOnServer LAB-MBX02 -Confirm:$false

E Suspend-ClusterNode mette il nodo in Paused ma non sposta il gruppo di cluster già presente: il Primary Active Manager resta dov'è finché non lo si sposta.

Move-ClusterGroup -Name "Cluster Group" -Node LAB-MBX02

Verifica di nodo vuoto: nessun Mounted sul nodo in manutenzione, PrimaryActiveManager sull'altro, nodo in Paused.

Dopo la manutenzione i database restano concentrati su un nodo. Riequilibrarli non è estetica: le preferenze di attivazione riflettono scelte di capacità, e si torna alla configurazione progettata.

#I connettori applicativi: due errori distinti

L2

Due connettori di ricezione riproducono la coppia che si trova in produzione, e la differenza fra i due spiega due errori che si incontrano nei log reali.

Connettore anonimoConnettore autenticato
Chi si connetteApplicativi che non sanno autenticarsiApplicativi con account di servizio
Porta25587
PermessiAnonymousUsers + diritto di relay esplicitoExchangeUsers
Controllo d'accessoSolo l'indirizzo sorgenteIndirizzo e credenziali
Errore tipico550 se l'indirizzo non è in elenco530 5.7.57 se non ci si autentica

Creazione del connettore anonimo, in due passi — il secondo è quello che si dimentica:

New-ReceiveConnector -Name "smtp-app-lab" -TransportRole FrontendTransport -Server LAB-MBX01 `
  -Bindings 0.0.0.0:25 `
  -RemoteIPRanges 10.20.20.10,10.20.20.11,10.20.20.34,10.20.30.10 `
  -PermissionGroups AnonymousUsers -AuthMechanism Tls -Enabled $true

Get-ReceiveConnector "LAB-MBX01\smtp-app-lab" |
  Add-ADPermission -User "NT AUTHORITY\ANONYMOUS LOGON" `
    -ExtendedRights "Ms-Exch-SMTP-Accept-Any-Recipient"

Il diritto Ms-Exch-SMTP-Accept-Any-Recipient è ciò che autorizza il relay verso domini esterni; RemoteIPRanges lo limita agli indirizzi elencati. Senza quel diritto, un invio verso l'esterno riceve 550 5.7.54.

Come Exchange sceglie il connettore. Più connettori possono condividere lo stesso binding; Exchange sceglie quello con l'intervallo di indirizzi sorgente più specifico. Ma la specificità vale solo entro la stessa famiglia di indirizzi: un connettore in ascolto su 0.0.0.0:25 con intervalli IPv4 non viene mai selezionato per una connessione IPv6. La storia per esteso sta in 550 5.7.54 con il connettore giusto.

I log di protocollo sono l'unico posto dove Exchange dichiara quale connettore ha accettato una sessione e da dove:

Get-ReceiveConnector -Server LAB-MBX01 | Set-ReceiveConnector -ProtocolLoggingLevel Verbose

Il secondo campo di ogni riga è il connettore che ha accettato la sessione, il quinto è l'indirizzo sorgente. Confrontarli è il modo più rapido per capire cosa è successo.

#Il trasporto perimetrale e EdgeSync

L3

Due server Windows fuori dal dominio, in DMZ, con il solo ruolo di trasporto perimetrale. Non contattano mai Active Directory: conservano una copia dei dati di configurazione in AD LDS, popolata da EdgeSync.

È questo isolamento a renderli adatti al perimetro — ed è anche la ragione per cui, se si perde la password dell'amministratore locale, non esiste un amministratore di dominio che possa entrare.

I tre punti dove questa fase fallisce.

Il suffisso DNS primario. Una macchina in workgroup non lo eredita dal dominio e va impostato a mano nel registro. Senza, il nome completo resta il solo nome NetBIOS e la sottoscrizione fallisce.

[System.Net.Dns]::GetHostEntry($env:COMPUTERNAME).HostName

L'orologio. EdgeSync usa certificati auto-firmati valutati rispetto all'ora corrente. Uno scarto di pochi minuti li rende non validi, e l'errore che ne risulta parla di connessione, non di tempo: The LDAP server is unavailable.

I servizi dopo la sottoscrizione. Anche con orario corretto e porta aperta, EdgeSync può non partire finché non si riavviano i servizi che valutano i certificati.

Restart-Service ADAM_MSExchange -Force
Restart-Service MSExchangeEdgeCredential
Restart-Service MSExchangeTransport

Come si legge l'esito della sottoscrizione:

StatoSignificato
SyncStatus : Normal, CookieRecords > 0Funzionante
SyncStatus : Inconclusive, tutti NotStartedAtteso subito dopo la creazione: il primo ciclo non è ancora avvenuto
CouldNotConnect, The LDAP server is unavailableOrario, oppure servizi da riavviare
Stati SkippedNormale: nulla di nuovo da sincronizzare

Un messaggio verso un dominio esterno inesistente che resta in coda in stato Retry è il risultato atteso: dimostra che il percorso interno → perimetro funziona.

#Il Direct Trust: perché una CA non serve

L3

I certificati che i server cassette e quelli perimetrali si scambiano sono auto-firmati, generati da Exchange. La fiducia non nasce dalla catena di certificazione: nasce dal fatto che il certificato è pubblicato nella directory — in Active Directory da un lato, in AD LDS dall'altro — e distribuito da EdgeSync.

La conseguenza pratica è che una CA non serve. Un certificato emesso da un'autorità non aggiungerebbe nulla alla verifica.

Il rinnovo, alla scadenza, si effettua rifacendo la sottoscrizione.

Get-ExchangeCertificate | ft Thumbprint,Subject,Issuer,NotAfter,Services -Auto

Dove Subject e Issuer coincidono, il certificato è auto-firmato.

#HAProxy in modalità L4

L2

Ventinove righe di configurazione, di cui due contano davvero.

frontend ft_smtp
    bind 10.20.20.38:25
    default_backend bk_smtp

backend bk_smtp
    balance roundrobin
    source 10.20.20.34
    server mbx01 10.20.20.10:25 check
    server mbx02 10.20.20.11:25 check

mode tcp — livello 4, nessuna terminazione TLS. L'handshake attraversa il bilanciatore intatto e arriva a Exchange, esattamente come fa il bilanciatore commerciale che riproduce. In modalità HTTP il bilanciatore terminerebbe la connessione, e il fenomeno TLS da studiare non sarebbe più osservabile.

source 10.20.20.34 — forza l'indirizzo sorgente verso i server. È il SNAT.

Verifica prima di riavviare — nessun output significa configurazione valida:

haproxy -c -f /etc/haproxy/haproxy.cfg

Prova del percorso completo, che deve rispondere col banner di uno dei due Exchange:

telnet 10.20.20.38 25

#Certificati: le lacune dichiarate

L2

Nel laboratorio esistono due tipi di certificato, e ne manca un terzo.

TipoEmittenteScopoRinnovo
Auto-firmato SMTPSé stessoDirect Trust cassette ↔ perimetroNuova sottoscrizione
Certificato di autenticazioneSé stessoAutenticazione OAuth fra serverCinque anni, silenzioso
Certificato di servizioClient e connessioni esterneAssente nel laboratorio

Il certificato di autenticazione merita attenzione perché scade in silenzio, e la sua scadenza rompe funzioni ibride come la condivisione della disponibilità senza messaggi evidenti. È un controllo che vale la pena inserire fra le verifiche periodiche, anche in produzione.

Get-ExchangeCertificate -Thumbprint (Get-AuthConfig).CurrentCertificateThumbprint |
  fl NotBefore,NotAfter,Subject

Il certificato di servizio non è ottenibile per un dominio di laboratorio, quindi i certificati restano auto-firmati. Va dichiarato quando si leggono i risultati: le prove di accesso client dall'esterno operano con un certificato che il client non considera attendibile. Questo non impedisce di verificare il percorso di rete — la connessione si stabilisce, il traffico passa — ma «il browser ha protestato» e «non ha funzionato» sono esiti diversi, e solo uno dei due è un guasto.

Per verificare se una catena è realmente verificabile e non solo formalmente corretta:

certutil -verify -urlfetch <file.cer>

-urlfetch scarica davvero CRL e certificati intermedi dagli indirizzi contenuti nel certificato.

Parte 5 — L'ibrido

#Il tenant, e la scelta di non eseguire l'HCW

L2

Il laboratorio si sincronizza verso un tenant Microsoft 365 vero, non di prova. È una scelta che aumenta la fedeltà e impone cautele.

La più importante: l'Hybrid Configuration Wizard non è stato eseguito. Scrive connettori a livello di organizzazione nel tenant, e quel tenant ospita posta reale. Rinunciarvi significa rinunciare a una parte del comportamento ibrido; eseguirlo avrebbe significato modificare la configurazione di posta di un ambiente in uso.

Il sottodominio di laboratorio è stato verificato automaticamente, senza record TXT: Microsoft eredita la prova di proprietà dal dominio padre, già verificato nello stesso tenant. In fase di aggiunta è stato deliberatamente saltato il passo dei servizi — nessun record MX, CNAME o SPF — perché al laboratorio serve solo che il dominio sia verificato, non che instradi posta.

#Entra Connect: il filtro per unità organizzativa

L2

È la protezione principale del tenant. Entra Connect considera esclusivamente una unità organizzativa: qualunque oggetto fuori da quella, per il tenant, non esiste.

VoceValore
Metodo di accessoPassword Hash Synchronization
Single sign-onDisattivato
FiltroUna sola OU
AncoraggiomS-DS-ConsistencyGuid
Funzioni opzionaliPassword Hash Sync, Exchange hybrid deployment
Ciclo automaticoAttivo, ogni 30 minuti

Il filtro va verificato a ogni riesecuzione della procedura guidata, che ripropone le impostazioni correnti ma non impedisce di modificarle per distrazione.

Set-ADSyncScheduler -SyncCycleEnabled $true

#Exchange hybrid deployment e la scrittura all'indietro

L2

Attivata dopo la configurazione iniziale, e la differenza è stata sostanziale.

Prima dell'attivazione, Exchange Online non riconosceva gli oggetti locali come destinatari validi. Dopo, gli stessi oggetti compaiono correttamente come MailUser, che è il modo in cui un ibrido rappresenta una casella che vive dall'altra parte.

La funzione abilita anche la scrittura all'indietro di alcuni attributi dal cloud verso Active Directory: stato dell'archivio, elenchi mittenti attendibili e bloccati, deleghe pubbliche, e indirizzi creati nel cloud.

L'effetto è osservabile: su un oggetto sincronizzato, in Active Directory, compare un indirizzo X500 con prefisso /o=ExchangeLabs — l'organizzazione di Exchange Online, non quella locale. Quell'indirizzo è nato nel cloud ed è tornato indietro. Serve a preservare la possibilità di rispondere a messaggi inviati quando la casella viveva altrove, e non va rimosso.

Dopo l'attivazione serve obbligatoriamente un ciclo completo:

Start-ADSyncSyncCycle -PolicyType Initial

#La regola di sincronizzazione personalizzata

L3

Sincronizza solo chi ha un certo valore di employeeType, oppure chi porta un attributo di estensione con un valore concordato. Scrive cloudFiltered, che è l'attributo con cui Entra Connect decide se un oggetto esiste per il tenant.

VoceValore
DirezioneInbound
Tipo oggettouserperson
Precedenza50
Attributo di destinazionecloudFiltered
Tipo di flussoExpression
IIF(IsPresent([employeeType]),
    IIF([employeeType]="Interno", False,
        IIF(IsPresent([extensionAttribute1]),
            IIF([extensionAttribute1]="SYNC365", False, True), True)),
    IIF(IsPresent([extensionAttribute1]),
        IIF([extensionAttribute1]="SYNC365", False, True), True))

Tabella di verità:

employeeTypeextensionAttribute1cloudFilteredEsito
InternoqualsiasiFalsesincronizza
altroSYNC365Falsesincronizza
altroaltro o assenteTrueescluso
assenteSYNC365Falsesincronizza
assenteassenteTrueescluso

Quattro cose da sapere prima di scriverla.

L'espressione va nel campo Source. Non esiste un campo separato: quando si imposta FlowType = Expression, la colonna Source diventa una casella di testo libera. È il punto in cui ci si blocca cercando un campo che non c'è.

La precedenza deve stare sotto 100. Da 100 in su ci sono le regole predefinite di Microsoft; una regola con precedenza superiore verrebbe sopravanzata e cloudFiltered non verrebbe mai scritto.

Niente operatori booleani. Il linguaggio delle espressioni è limitato: la struttura a IIF annidati è più prolissa ma sicura. IsPresent va sempre verificato prima di confrontare un attributo che potrebbe essere assente, altrimenti l'espressione fallisce a runtime proprio su quegli oggetti.

Dopo ogni modifica serve un ciclo completo. Un ciclo Delta valuta solo gli oggetti cambiati di recente; le regole nuove si applicano a quelli esistenti solo con Initial.

#La modalità staging

L2

È il modo corretto di sviluppare o modificare regole. In staging il motore importa, applica le regole e calcola tutte le differenze, ma non esporta nulla: si può sbagliare quante volte serve senza che un solo oggetto nel tenant se ne accorga.

Perché è indispensabile, e non è ovvio: un oggetto che esce dall'ambito di sincronizzazione è indistinguibile, per Entra Connect, da un oggetto sparito. Genera un'operazione di eliminazione nel cloud.

Durante la costruzione questo è stato verificato deliberatamente: togliendo l'attributo di ammissione a un utente già sincronizzato, in staging è comparsa un'operazione di Delete in attesa. Nessuna cancellazione è avvenuta.

La procedura, e il passo che protegge davvero:

  1. Attivare la modalità staging
  2. Modificare o creare la regola nell'editor
  3. Eseguire un ciclo Initial
  4. Ispezionare le operazioni in attesa (Pending Export)
  5. Correggere finché non compaiono eliminazioni indesiderate
  6. Solo allora disattivare lo staging
  7. Eseguire un altro ciclo Initial
  8. Verificare gli oggetti nel tenant

Il passo 5 è la regola: non si esce mai dallo staging con un'eliminazione in sospeso che non si vuole davvero.

#Il connector space: perché questo utente non si sincronizza

L2

Lo strumento che risponde in trenta secondi alla domanda più frequente.

Vista d'insieme — cosa verrebbe esportato: scheda Connectors → connettore del tenant → Search Connector SpaceScope: Pending Export → spuntare Add, Modify, DeleteSearch.

Vista di dettaglio — perché un oggetto è escluso: scheda Connectors → connettore della foresta → Search Connector SpaceScope: DN → il nome distinto dell'oggetto → selezionarlo → PreviewGenerate PreviewImport Attribute Flow.

Lì si vede ogni regola applicata all'oggetto, in ordine di precedenza, e il valore finale di ogni attributo.

Parte 6 — Metodo e diagnosi

#Tre principi

L1

Emersi dalla costruzione, in ordine di utilità.

Leggere il log, non il messaggio a schermo. Il messaggio della finestra è quasi sempre generico. La causa vera sta nel registro eventi o nel file di log dello strumento. L'errore più istruttivo dell'intero progetto — could not configure application based authentication — non aveva nulla a che vedere con l'autenticazione: nel registro eventi si leggeva un certificato con date incoerenti, e la causa era un orologio.

Confrontare due misure indipendenti. Un solo dato non dice se è giusto. Due dati che dovrebbero coincidere e non coincidono individuano il problema. È così che si è trovato lo sfasamento orario: due marche temporali nello stesso evento, a nove ore esatte di distanza.

Distinguere il livello che fallisce. TCP che connette ma LDAP che non risponde significa che il problema è sopra il trasporto, non nella rete. Continuare a controllare firewall e instradamenti, in quel caso, è tempo perso.

#Comandi di sola lettura

L1

Rete:

Test-NetConnection <ip> -Port <porta> -InformationLevel Quiet
Resolve-DnsName <nome>
Get-NetConnectionProfile

Identità e Kerberos:

nltest /sc_query:contoso.lab
Test-ComputerSecureChannel -Verbose
klist
w32tm /query /status
(Get-Date).ToUniversalTime()

Exchange e DAG:

Get-ClusterNode | ft Name,State,StatusInformation,DrainStatus -Auto
Get-MailboxDatabaseCopyStatus * | ft Name,Status,ActiveCopy,CopyQueueLength -Auto
Get-Queue -Server <server> | ft Identity,Status,MessageCount,NextHopDomain -Auto
Get-MessageTrackingLog -Server <server> -Sender <indirizzo> -Start (Get-Date).AddMinutes(-30) |
  fl Timestamp,EventId,Source,RecipientStatus,Recipients

Entra Connect, dove sta la causa vera dei fallimenti:

Get-WinEvent -LogName Application -MaxEvents 100 |
  Where-Object { $_.ProviderName -match "ADSync|Directory Synchronization|Entra" } |
  Select-Object TimeCreated,ProviderName,LevelDisplayName,Message | fl

#La ridondanza shadow non è un'anomalia

L2

Nei log di tracciamento compaiono eventi HARECEIVE, HAREDIRECT e code Shadow. Non sono guasti: sono il meccanismo con cui Exchange conserva una copia di ogni messaggio sull'altro nodo finché il primario non conferma la consegna.

Una coda Shadow con messaggi dentro è normale. Una coda Shadow che cresce e non si svuota indica un nodo che non conferma più le consegne, ed è un sintomo da indagare.

#Errori minori ma ricorrenti

L1

SintomoCausaRimedio
Cannot convert ArrayList passando più identità-Identity non accetta elenchiUsare la pipeline o ForEach-Object
Cannot convert ADPropertyValueCollectionGet-ADDomainController -Discover restituisce una collezionestring.HostName
too many movesLimite di quattro spostamenti orari per databaseAttendere
w32tm /resync /force: arguments were unexpected/force non è un parametro valido/rediscover
Restart-Service ADAM_MSExchange: dependent servicesHa servizi dipendenti-Force
Get-ADSyncScheduler: Synchronization Service is not runningIl servizio è fermoAtteso se lo si è appena arrestato
Lo scheduler è spento dopo la procedura guidataLa procedura lo disattivaSet-ADSyncScheduler -SyncCycleEnabled $true
Where-Object Result -ne Passed mostra tuttoSenza virgolette vale $null -ne 'Passed'Mettere le virgolette
Entra Connect: schermata di accesso vuotaIE Enhanced SecurityDisattivarla per gli amministratori

#Le quattro storie lunghe

L1

Quattro guasti hanno richiesto una diagnosi vera, e ognuno è raccontato per esteso altrove.

Il filo comune fra tutti e quattro: il messaggio d'errore indicava una direzione sbagliata. Non per cattiva progettazione — un componente può segnalare solo quello che vede — ma perché la causa stava sempre uno strato più sotto di dove il sintomo si manifestava.

Nessun risultato. Prova con il nome di un componente (DAG), un cmdlet (Set-Mailbox) o un concetto (quorum, SNAT, staging).

Documentazione di un laboratorio costruito per studiare un'infrastruttura Exchange 2019 ibrida. Nomi, indirizzi e domini interni sono sostituiti con valori generici. Laboratorio costruito con il supporto di Ilie.