Skip to content

LDAP directories

Taranac can connect to one or more directories — Active Directory, OpenLDAP, FreeIPA, Google Workspace Secure LDAP or any other LDAP server — and use them as a source of truth for identities. A directory connection does two things: it synchronises users, groups and computer objects into Taranac, and it acts as an authentication backend so directory accounts can sign in by binding against the server. Synced groups become first-class Taranac groups, so directory membership flows straight into RBAC, AAA and NAC policy.

You configure directories under Settings → Integrations → LDAP. Most deployments need only one connection; several are only required for genuinely separate directories — as here, where one installation holds an Active Directory domain, an OpenLDAP server and a FreeIPA realm side by side.

LDAP settings in Taranac: three connections — Active Directory, OpenLDAP and FreeIPA The LDAP settings page: each card is one directory connection, tagged with its type, with inline Test, Edit and Delete actions.

A connection has a type, chosen as the first step of creating it. The answer is not cosmetic: an integration built only for Active Directory binds by UPN, looks accounts up by sAMAccountName, tracks them by objectGUID and filters on userAccountControl — every one of which is false for OpenLDAP and FreeIPA. The type decides both what the directory can do and what the form is prefilled with.

Choosing the directory type when creating a configuration The type picker, the first step of Add Configuration. Each card states up front whether the directory can serve MS-CHAPv2 — worth knowing before the configuration exists, not after the first 802.1X login fails. (The shot predates 1.2.8: FreeIPA’s card now reads “Supports MS-CHAPv2 and PEAP-MSCHAPv2”.)

TypeMS-CHAPv2 / PEAPComputer syncdomain_suffixnetbios_nameSchema defaults
Microsoft Active Directory✅ via a domain joinrequiredrequiredsAMAccountName, objectClass=user / group
OpenLDAPoptionalRFC 2307: uid, posixAccount, groupOfNames
FreeIPA✅ via ipaNTHash (since 1.2.8)optionalRFC 2307 (same as OpenLDAP)
Google WorkspaceoptionalRFC 2307; the connection also requires a client certificate
Other LDAP directoryoptionalRFC 2307

The columns are different in kind, and the difference matters:

  • Capabilities — facts about the protocol you cannot override. The test is whether any configuration would make the directory answer. Holding a machine account is one: joining means a computer object, a secure channel and winbindd, and nothing an operator types gives OpenLDAP or Google Workspace one. Computer sync is one only on Google Workspace, whose Secure LDAP serves users and groups while devices sit behind the Admin SDK — no filter reaches them. The UI hides a tab the type cannot serve, and the API refuses the call independently (ldap.computer_sync_not_supported, domain.mschapv2_not_possible) — a hidden tab is a courtesy, not the control.
  • Defaults — guesses about a schema you can always override. Filters, attribute maps and the example values under each field are prefilled per type; being slightly wrong about your tree costs you one edit. An empty attribute map means the type’s own map, not Active Directory’s literals — since 1.2.2 there is no silent fallback to sAMAccountName.

MS-CHAPv2 is not one column but two mechanisms, which is why the table names which one each type gets. Until 1.2.8 the product held that only Active Directory could verify an MS-CHAPv2 password, and that was two facts confused for one. Active Directory’s unicodePwd cannot be read back by anyone, so a challenge there has to go to a domain controller — hence the join. What MS-CHAPv2 actually needs is the account’s NT hash, and FreeIPA publishes it as an ordinary attribute. See MS-CHAPv2 for directory accounts.

A configuration has five tabs: Users & Groups, Computers, EAP-TLS, MS-CHAPv2 and Sync History. Computers and MS-CHAPv2 are absent — not disabled — where the type cannot serve them; EAP-TLS is available for every type.

Upgrading an existing installation changes nothing: a configuration saved before directory types existed reads as Active Directory, and the Active Directory defaults are the values that always shipped.

Every synced user and group is tracked by an immutable identifier rather than by name or DN, so renaming or moving an object updates the existing Taranac record instead of creating a duplicate. Which attribute that is depends on the directory: objectGUID on Active Directory, entryUUID (RFC 4530) on OpenLDAP, Google Workspace and the generic type, and ipaUniqueID on FreeIPA.

Active Directory accepts user@domain and resolves it internally, so Taranac binds directly as the user. Everything else takes a distinguished name and answers a bare login with invalidDNSyntax — so for those types Taranac first resolves the DN with the service account, using the configuration’s own user_search_filter and user_search_bases, then binds as that DN.

That has a useful side effect: on OpenLDAP and FreeIPA, sign-in is scoped to the search bases you configured, which the direct UPN bind on Active Directory cannot be. If a login matches more than one entry the attempt is refused rather than guessed at, and an empty password is rejected outright (an empty simple bind is an anonymous bind, which most directories accept).

A standard RFC 2307 / inetOrgPerson tree: the login attribute is uid, groups are groupOfNames, and membership is a DN in member. The bind account is a full DN (cn=svc-taranac,ou=Services,…) — there is no UPN to type.

An OpenLDAP connection in Taranac An OpenLDAP connection. The type is shown next to the config ID and cannot be changed, and the search filters are prefilled for the RFC 2307 schema. There is a Computers tab but no MS-CHAPv2 one — machines are an ordinary search, but this type publishes no NT hash. Samba’s sambaNTPassword is the same idea in the RFC 2307 world; it exists only on a tree where somebody installed the Samba schema, so Taranac does not claim it — naming an attribute nobody has would turn a fixable permission problem into an unfixable schema one, and both would read as the same silence.

Defaults prefilled for this type:

FieldDefault
user_search_filter(&(objectClass=posixAccount)(uid={username}))
group_search_filter(objectClass=groupOfNames)
sync_user_filter(objectClass=posixAccount)
sync_group_filter(objectClass=groupOfNames)
user attributesuid, mail, givenName, sn, displayName, telephoneNumber
group attributescn, description, members from member (DN values)

The same defaults serve Google Workspace Secure LDAP and the generic type. Google additionally requires a client certificate on the connection.

FreeIPA (Red Hat’s identity manager, backed by 389 Directory Server) publishes the same schema shape as OpenLDAP — uid + posixAccount + inetOrgPerson for users, groupOfNames + posixGroup for groups — so its filters are identical. What differs is the layout: accounts do not live in an OU but under cn=users,cn=accounts,$SUFFIX, with groups under cn=groups,cn=accounts,$SUFFIX. That is what you actually type, which is why FreeIPA is its own type: the example values in every field follow the IPA tree.

A FreeIPA connection in Taranac A FreeIPA connection. Same filters as OpenLDAP, different containers: the bind account is uid=svc-taranac,cn=users,cn=accounts,… and the search bases sit under cn=accounts.

FreeIPA installs its own CA, independent of anything Active Directory signs. Each connection carries its own trust anchor, so an installation can hold directories with two different CAs at once — paste each server’s CA into tls_ca_cert on its own connection.

Since 1.2.8 FreeIPA is also the one non-AD type that can serve MS-CHAPv2, because it publishes the NT hash as an ordinary attribute — see MS-CHAPv2 for directory accounts.

  • Connection (config). One directory you connect to, identified by a short, immutable config_id. It holds the type, the servers, the bind account, search bases and filters, and all sync options.
  • Bind account. A service account Taranac uses to read the directory during sync and browsing (and, on non-AD types, to resolve a user’s DN at sign-in). Its credentials (bind_dn + bind_password) are stored encrypted.
  • Search base / filter. The subtree and LDAP filter that scope what sync and the directory browser see. Scoping here is how you decide which accounts and groups become Taranac objects.
  • auth_source. Synced users carry auth_source = ldap. They have no local password in Taranac; they authenticate against the directory. Locally created accounts are auth_source = local.
  • Domain suffix / NetBIOS name / priority. Identifiers used to route a login to the right connection: domain_suffix (e.g. corp.example.com) matches user@corp.example.com logins, netbios_name matches CORP\user (Active Directory only), and priority orders connections when the login is a bare username.

Open Settings → Integrations → LDAP → Add Configuration, pick the directory type, then fill in the connection form. Creating a configuration is a two-step flow — the type picker first, then the Connection and Search cards; the other tabs appear once the configuration exists. The essential fields:

FieldPurpose
config_idShort slug (lowercase, digits, -/_). Immutable once created.
display_nameFriendly name shown in the UI.
serversOne or more LDAP URIs, space- or newline-separated (e.g. ldaps://dc1.corp.example.com).
bind_dn / bind_passwordService-account credentials for read access — a UPN on Active Directory, a full DN elsewhere. Password is stored encrypted.
domain_suffixRequired on Active Directory, optional elsewhere: routes user@domain logins to this connection.
netbios_nameShort domain name (1–15 chars), e.g. CORP. Active Directory only; routes DOMAIN\user logins.
priority1–999. Lower numbers are tried first for bare-username logins.
use_start_tls / tls_validate / tls_ca_certTransport security: StartTLS, certificate validation, and — labelled Directory server CA certificate — the CA (PEM, pasted into the form) that signs the directory server’s certificate. Empty means the container’s system trust store.
connection_timeoutPer-server connect timeout in seconds. Default 10; the form accepts 1–120, the API up to 300.
pool_strategy / pool_active / pool_exhaustHow several servers are used: Round Robin (default), First Available or Random; active probing; and how long an exhausted pool waits (default 5 s).

When the connection details are saved, use the operations bar at the top of the Users & Groups tab:

  • Test Connection reports per-server reachability and latency, whether the bind succeeded, and a count for each search base — run this first. It names the stage that failed, so a name that does not resolve is reported as such rather than as a certificate problem, and a CA certificate that will not parse is described instead of returning a server error. An unreachable directory now fails here rather than hanging: the client used to read a server pool as “retry forever”, so the request simply held its worker.
  • Sync Preview performs a dry run and shows exactly what would be created, updated, disabled or deleted, plus any conflicts. Nothing is written. It previews the values currently in the form, not the saved configuration — so an edited filter can be answered for before it is committed.
  • Trigger Sync runs the sync for real.

The Computers tab carries its own pair, Computer Preview and Trigger Computer Sync.

An edited configuration reaches the daemons on their own schedule — no restart is needed. TACACS+ used to read the LDAP settings once at start-up and keep that copy for as long as it ran, so a correction made in the interface (most painfully a CA certificate) looked saved while authentication kept failing against the old values; since 1.2.4 it re-reads them on the same schedule RADIUS already used.

Scope is everything. Restrict the search bases to the subtrees you actually want in Taranac — point user search at an “Employees” OU (or cn=users,cn=accounts on FreeIPA) so service and disabled accounts never sync.

There are four filters, and they answer two different questions. Getting the pair confused is the commonest way to conclude that “the filter does not work”:

FieldCardQuestion it answers
user_search_filterSearchFinds one account at login. {username} is replaced with the login name. Not used by synchronisation.
group_search_filterSearchGroup lookup outside a sync run — it is the filter Test Connection’s group probe uses. Not used by synchronisation.
sync_user_filterSynchronizationDecides which accounts a sync run imports. The Active Directory default excludes disabled accounts.
sync_group_filterSynchronizationDecides which groups a sync run imports.

Browse opens the directory tree so you can pick bases instead of typing them. The browser understands non-AD schemas — group, groupOfNames, groupOfUniqueNames, posixGroup, ipaUserGroup for groups, user / person / inetOrgPerson / posixAccount for users, and the container objects FreeIPA nests its accounts in. It is also what fills Selective Group Sync and the computer search bases, with its own apply button for each. Picking groups by hand works on every type: until 1.2.4 the lookup used (objectClass=group) — a term only Active Directory understands — everywhere, so on OpenLDAP and FreeIPA the member list came back blank, the run finished green with zeroes across every counter, and nothing reported a fault, because LDAP does not treat “matched nothing” as an error.

Since 1.2.7 it holds a large directory. Levels are indented; a level holding four thousand machines no longer draws four thousand rows — leaves are capped at 200, with what was left out counted and said out loud, while containers are never cut, because cutting one makes everything below it unreachable while looking exactly like a container that is empty. The group-members preview is paged rather than showing the first 50 forever, and every shortened DN carries its full value on hover. The preview is not offered on an LDAP-group classification rule: it answers “which users would this group bring in”, and such a rule matches machines.

Sync runs in one of three modes:

ModeLabel in the formBehaviour
groups_with_usersGroups → UsersWalk the configured groups, resolve their members, and create/update those users. Users outside a synced group are ignored. Use this when group membership defines who belongs.
groups_and_usersGroups + All UsersThe above, and then additionally import every user matching the sync user filter — even those in no group.
users_onlyUsers OnlyCreate/update users from the user search base alone. Groups and memberships are not touched.

The form hides what a mode does not use rather than leaving it to look effective: the sync user filter disappears in Groups → Users, and the group filter, Selective Group Sync, nested groups and the RBAC block disappear in Users Only — each replaced by a line saying so. A per-run mode can also be passed to POST /ldap/{config_id}/sync; since 1.2.7 an unknown value is refused, naming the modes that exist, instead of quietly falling through to a user sync that reported success. computer_sync is told which route it actually wants — it is a real operation, on the Computers tab, not a sync mode.

The lifecycle for each object is create → update → deactivate → delete, governed by these options:

OptionEffect
sync_create_usersCreate a Taranac account for each directory user found. When off, sync only updates accounts that already exist.
sync_disable_missingSet users no longer present in the directory to inactive (is_active = false) instead of leaving them enabled.
sync_remove_stale_usersPermanently delete users that have disappeared from the directory.
sync_remove_stale_groupsPermanently delete groups that have disappeared from the directory.
sync_nested_groups (Follow nested groups)Import the members of groups nested inside a selected group. On by default, for existing configurations as well as new ones.
sync_group_dns (Selective Group Sync)Pick specific groups instead of relying on the sync group filter. Overrides the filter when set.
sync_enabled / sync_interval_minutesRun the sync automatically on a schedule. Default 60 minutes; the API accepts 5 minutes to 7 days.

Synced users are created with auth_source = ldap and no local password — authentication is delegated to the directory. Their group membership in Taranac mirrors the directory, and every action (created/updated/deactivated/deleted, per user and per group) is written to the audit log.

On a cluster the sync runs on the leader only. It is entirely shared-database work — bind, then upsert rows every node reads from the same primary — so running it per node would mean N concurrent binds against your directory every interval, N audit rows, racing upserts on the same users and N copies of any failure email.

Directories express a role as a group containing other groups far more often than as one flat list — it is the ordinary shape of an Active Directory tree, and FreeIPA has a dedicated object class for it. Since 1.2.4 the members of a nested group are imported and become members of the group you selected, however deep the nesting goes.

  • The nested group itself is not created in Taranac. Only the groups you actually selected are, because a group here carries a permission model and inventing one you never chose would be inventing permissions.
  • A nested group living outside your search bases is followed correctly: members are read one entry at a time rather than searched for inside those bases.
  • A loop — two groups containing each other — ends that branch instead of the run. Directories accept such loops, and one of them must not stop everyone else from synchronising.
  • Turn it off (Synchronization → Follow nested groups) and a nested group is named in the sync log rather than passed over in silence.
  • The number of nested groups a run went through is recorded in the audit log, so a sync that suddenly brings in far more people than expected can be explained without opening the directory.

A group object is never imported as if it were a person. In an Active Directory domain a group carries a login-style attribute, so before 1.2.4 a group contained in a synchronised group was created as an ordinary user account named after it. Objects that are not people — groups, OUs, containers — are now refused, and the sync log says which was which. Accounts created that way before the update are left alone rather than deleted; remove them from the user list.

“Members: 0” used to mean three different things and look like one: an empty group, membership held in an attribute Taranac does not read, or an entry that could not be read at all. Since 1.2.8 the first is left alone — empty is an answer — and the other two are named:

  • The members are DNs under a different attribute. member is empty but the entry lists them in uniqueMember. The message says so and names the attribute to set as the group member attribute for this directory. This is a configuration fix.
  • The members are bare login names. memberUid holds usernames rather than DNs (the RFC 2307 posixGroup style). That format is not read, and saying so is the whole of the fix available.
  • The entry could not be read at all is reported as such rather than as “group not found” for a group plainly sitting in the tree.

Sync Preview surfaces the same lines under Groups that will bring nobody, before a run rather than after it.

The membership of an imported group is decided by the directory — hand-editing it is refused. Until 1.2.4 the sync only ever removed members that had themselves been imported from that same directory, so anything else stayed for good, removable by neither the sync nor the interface; that is how a local account (directories ship their own admin) could become a permanent member of an imported group. Membership is now simply what the directory reports, and a removal is written to the audit log. This removes a membership, never an account.

Because synced groups are ordinary Taranac groups, they work everywhere groups work:

  • Admin RBAC. Assign an RBAC model to synced users via default_rbac_model_id (applied to all users of the connection) or group_rbac_mapping, which maps a specific directory group DN to a specific RBAC model. A group match takes precedence over the default.
  • AAA policy. Reference directory groups as conditions in TACACS+/RADIUS policy to grant device-admin privilege levels, command sets or RADIUS attributes by group — see AAA policy.
  • NAC policy. Use the same groups as conditions in 802.1X/MAB authorization to assign VLANs, ACLs or captive-portal flows — see NAC authentication.

This is the main reason to sync groups even when you do not need local admin accounts: it lets policy decisions key off directory membership without re-modelling your org chart inside Taranac.

The Computers tab imports the directory’s computer objects as NAC endpoints, enriching them with hostname, OS and the machine’s place in the tree. Turn on computer_sync_enabled, then configure computer_search_bases (required), computer_search_filter, the attribute holding the MAC (computer_mac_attr) and an attribute map, and preview and run it like user sync. Browse Computers opens the same tree the endpoint classification rules pick their locations from. A missing computer search base is now reported as the missing setting it is — it used to fail with “connection failure”, sending the operator to check the network, the controller and the certificates while the real cause sat only in the container log.

The lifecycle switches are separate from the user ones and default differently: computer_sync_create and computer_sync_update are on, computer_sync_deactivate_missing and computer_sync_delete_missing are off.

Since 1.2.7 this is no longer Active Directory’s alone. Computer sync runs on Active Directory, OpenLDAP, FreeIPA and the generic type; the one directory that cannot serve it is Google Workspace. It was never a protocol capability: an ordinary LDAP search whose every input is a field on this form, where the difference between directories is one filter.

TypeComputer filterMAC attributeName / hostname from
Active Directory(objectClass=computer)(none — empty)cn, dNSHostName
FreeIPA(objectClass=ipaHost)macAddressfqdn
OpenLDAP / other(objectClass=ieee802Device)macAddresscn

The Computers tab of a FreeIPA connection: filter (objectClass=ipaHost), MAC attribute macAddress, attributes mapped to fqdn and nsOsVersion The Computers tab on FreeIPA. Every field is prefilled from the directory type: (objectClass=ipaHost), macAddress, and a map pointing name and hostname at fqdn. On Active Directory the same tab opens with (objectClass=computer), an empty MAC attribute and dNSHostName.

Three cells are less obvious than they look, and each was measured rather than read off a schema:

  • ipaHost, not ieee802Device, on FreeIPA. IPA adds the ieee802device class to a host only once that host has a MAC, so filtering on the class would silently skip every machine without one — exactly the machines staging exists for, including the IPA master’s own object.
  • managedBy is deliberately unmapped on FreeIPA. It is present on every host and points at the host itself for anything self-managed, so mapping it would fill the field on every endpoint with its own DN. On Active Directory the same attribute names a person.
  • OpenLDAP has no operating-system attribute, so none is mapped. The stock schema set (core, cosine, nis, inetOrgPerson) defines none at all; a site that records an OS invented the attribute and only that site can name it — which is what the attribute map is for.
  • Computers that have a MAC attribute become NAC endpoints directly (match source mac_address).
  • Computers without one are held as pending computers (shown in a table on the tab). When that machine later authenticates by certificate (EAP-TLS), Taranac matches it by DNS hostname and binds the directory data to the resulting endpoint (match source dns_hostname).

Which of the two is the normal path is a property of the directory, and it is the practical difference an operator feels. Active Directory has no standard attribute for a MAC — that is the whole reason the pending table exists — so there staging is the rule and the direct path needs an attribute your organisation fills itself. FreeIPA (macAddress via ieee802device) and OpenLDAP (ieee802Device from nis.schema) publish one, so a machine there becomes an endpoint on the first sync, before it has ever authenticated, and staging is the exception.

An empty MAC attribute is a choice, not a blank. Everywhere else on this form empty means “use the type’s default”; in this one field it means “extract no MAC, stage the computer”. That is Active Directory’s own honest answer, and on FreeIPA or OpenLDAP it is how you turn the direct path off — so it is the one computer field where an empty value is stored rather than refilled.

Since 1.2.7 the sync can also import which directory groups each machine belongs to. That is the data behind the ldap_group classification rule, and nothing else reads it. It is its own switch (Group membership, off by default): it costs extra directory reads, and buys nothing until such a rule exists.

Membership arrives in a registry of directory groups plus a machine-to-group mapping, both written only by this sync. This is not the ordinary group table — those are user groups, carrying RBAC models, MFA requirements and enable passwords. View collected groups on the tab lists the registry: each group’s name and DN, and how many machines are in it.

The Groups block of the Computers tab: a Group membership toggle, memberOf as the membership attribute, ipahostgroup as the group class, and a View collected groups button The Groups block of the same tab. The membership attribute and the group class come from the directory type — ipahostgroup here is what keeps FreeIPA’s mirrored NIS netgroups out of the picker.

Three directories answer “which groups is this machine in?” three different ways, and the type knows which:

Active DirectoryFreeIPAOpenLDAP
Group object classgroupipahostgroupgroupOfNames
memberOf on a machinedirect onlytransitivedirect only
Nesting resolved bywalking memberOf upwardthe directory itselfwalking memberOf upward
Primary groupprimaryGroupIDno such conceptno such concept
  • Nesting is resolved at import, not while matching. The endpoint ends up holding a flat set of groups, so a rule written against an outer group matches a machine that is only a direct member of an inner one. 389 Directory Server expands the chain itself; OpenLDAP’s memberof overlay publishes direct membership only, so the chain is climbed by re-reading each group’s own memberOf. Active Directory behaves like OpenLDAP — its LDAP_MATCHING_RULE_IN_CHAIN would resolve the chain server-side, but that costs one search per machine against one read per distinct group, and it misses the primary group either way.
  • The group class is not decoration. FreeIPA mirrors every host group as a NIS netgroup carrying the same cn and a different identifier. One host in one group reported four entries on the test stand; without the class filter the registry doubles and the picker offers two identical names, one of them an artefact.
  • A machine’s primary group is invisible. In Active Directory the primary group (Domain Computers by default) is stored as a RID in primaryGroupID and is absent from memberOf by design. A rule written against Domain Computers will appear in the picker and match nothing.

Turning the switch off stops the collection; it does not delete what was already collected. Those rows stay and simply stop being refreshed — the switch does not promise a deletion, so it does not perform one.

The connection also stores the EAP-TLS settings used to validate machine and user certificates for that directory (CA certificate, identity matching, CRL/OCSP revocation checks) on the EAP-TLS tab — that tab is available for every type. See NAC PKI / EAP-TLS for the certificate side.

A directory connection can authenticate sign-ins — the web admin UI, and TACACS+ / RADIUS / 802.1X through the daemons. The flow for admin login:

  1. Taranac looks up the username among local users first.
  2. On a miss, it falls back to multi-domain LDAP discovery: it picks the matching connection(s) and tries to authenticate. (For AAA and NAC the connection is not discovered — it is the one the synced user record already belongs to.)
  3. The password is verified by binding as the user: directly as user@domain on Active Directory, or as the DN resolved by the service account on every other type.
How a typed login is routed to the right directory connection: by UPN domain_suffix, by NetBIOS name, or across all connections in priority order — then the bind that verifies the password.

Login routing depends on the format the user types:

Login formatRouted to
user@corp.example.com (UPN)The connection whose domain_suffix matches the domain.
CORP\user (NetBIOS)The connection whose netbios_name matches — Active Directory only.
user (bare)All enabled connections, tried in priority order.

On Active Directory, a bare or unqualified username is qualified to username@<domain_suffix> before binding (falling back to the domain parsed from a UPN-style bind_dn if no suffix is set).

Usernames are matched case-insensitively, mirroring how AD treats sAMAccountName/UPN binds — so CORP\jsmith and CORP\JSmith resolve to the same account. The original casing from the directory is preserved for display.

A failed sign-in says which failure it was. Active Directory answers a whole family of account states with the same LDAP result 49 (invalidCredentials) and distinguishes them only by a hex sub-code in the bind diagnostic. Taranac reads it, so the log records user not found, wrong username or password, not permitted to log on at this time, not permitted to log on at this workstation, password expired, account disabled, account expired, must change password before next logon or account locked out — each with the AD code — instead of a generic “invalid credentials”.

Directory users cannot change or reset their Taranac password — those operations are local-only and return an error for auth_source = ldap accounts. Password changes happen in the directory. (MFA enrollment, however, still applies to directory users; newly synced users can be auto-sent a setup link.)

Password verification by bind covers everything that hands Taranac the password itself. MS-CHAPv2 does not: it never sends the password, only a challenge/response over the account’s NT hashMD4(UTF-16LE(password)) — so it needs either a party that will compare the hash for you (a domain controller) or the hash itself.

MethodActive DirectoryFreeIPAOpenLDAP / Google / other
TACACS+ ASCII login
RADIUS PAP
802.1X — EAP-TTLS/PAP
802.1X — EAP-TLS✅ (no password involved)
802.1X — PEAP-MSCHAPv2✅ (node must be domain-joined)✅ (an ACI must grant the read)
RADIUS MS-CHAPv2 (e.g. MikroTik admin login)✅ (node must be domain-joined)✅ (an ACI must grant the read)

A PEAP attempt against a directory that cannot serve MS-CHAPv2 is refused by name rather than as a wrong password, and the refusal is not counted as a credential attempt — a rebooting domain controller does not look like password guessing. For those directories use EAP-TTLS/PAP or EAP-TLS for 802.1X; see EAP-TTLS with a directory.

MikroTik RouterOS proves an empty password before every interactive login. That probe is recognised, refused by name (empty_password_probe) and deliberately not counted as a credential attempt, so a RouterOS estate does not arm the brute-force guard by existing.

Some devices offer nothing else — a MikroTik RouterOS administrator login sends only MS-CHAPv2, has no PAP and no TACACS+ client — and 802.1X supplicants default to PEAP-MSCHAPv2 on Windows. Both take the same branch, and which branch a login takes is decided by the user’s own directory, from the configuration that account was synced from. Nothing is switched on by what the device sends: a device that sends PAP never enters it.

There are two mechanisms, and they have nothing in common beyond the answer they produce. Both are configured on the MS-CHAPv2 tab of the LDAP configuration, which is present only for a type that can serve one.

Active DirectoryFreeIPA
MechanismThe node holds a machine account and asks a domain controllerTaranac reads ipaNTHash from the directory and verifies the response itself
WhyunicodePwd is write-only and readable by nobody, Domain Admin includedipaNTHash is an ordinary attribute, generated for every user by the password plugin
Exclusive?Yes — one winbindd is one machine account is one joined forest, so at most one configuration may own itNo — verification is a stateless read, so as many configurations as you configure may have it on at once, alongside the one domain
Operator actionJoin the domainAdd one ACI granting the bind account the read
Ships withThe winbind service, in every installationNothing — no sidecar, no join, nothing delivered

A node may therefore hold a domain join and any number of hash-readable directories at the same time, and neither borrows the other’s verifier.

Add an LDAP configuration with the MS-CHAPv2 settings and the installation becomes a domain member using the account it already binds with — no computer object created by hand, no delegation requested from an AD administrator (a domain’s default machine-account quota is what makes this possible). The machine account name is derived so that a node re-joins its own object, and checked against the directory so two installations in one domain cannot overwrite each other. The automatic join happens once and never repeats.

  • Membership is reported per node. Each node holds its own machine account, so in a cluster every node joins separately; the status card lists one row per node with its machine account, whether winbind is running, and when it last reported.
  • The realm and the NetBIOS name are not entered twice. They are shown read-only, taken from domain_suffix and netbios_name on the Users & Groups tab — the directory and the domain are the same thing.
  • Join dialog. It asks the directory about the credentials and the target OU before spending them, so the commonest failures (wrong password, a mis-ordered OU path) are named in under a second instead of coming back as net ads join’s own diagnostics. The OU is written top-down — OU=Servers,OU=Taranac,DC=lab,DC=taranac,DC=pro is typed Taranac/Servers. Join credentials are used once, written to a temporary file the winbind service deletes before the join starts, and never written to the audit log.
  • Switching MS-CHAPv2 off does not leave the domain. The trust stays alive and its password keeps rotating, so switching it back on is instant.
  • A twelfth service, winbind, ships with every installation. Without a domain configured it starts, reports “not joined” and does nothing — a service you must first install is a service nobody enables, and MS-CHAPv2 is needed exactly by the administrators who discover the need at the moment their login fails.
  • An alert fires when the domain trust breaks — narrowly: MS-CHAPv2 is switched on and this node can no longer verify a domain password. A member with the feature deliberately switched off never pages anyone, and “never joined” is reported as an incomplete setup rather than a broken trust.
  • Failures are diagnosed, not guessed. Twenty-four NT_STATUS codes and nine join failures each map to a cause and a next action — expired account, logon-hours policy, a dead controller, NTLM disabled by policy, clock skew, a broken machine-account trust — and each carries whether it counts as a credential attempt.

Run checks on the tab answers what Taranac can answer itself: settings complete, configuration delivered to the winbind service, Kerberos configuration delivered, configuration delivered to RADIUS and to NAC, winbind reporting and running, the machine account working, MS-CHAPv2 switched on. Whether a domain controller is reachable is checked inside the winbind service, where the domain DNS lives.

Since 1.2.8. FreeIPA’s ipaNTHash is created for every user by the password plugin, byte for byte MD4(UTF-16LE(password))even where ipa-adtrust-install was never run. Whoever can read it can verify an MS-CHAPv2 response, so no domain, no machine account and no winbindd are involved. The tab shows two read-only facts instead of a realm: the Hash attribute (from the directory type) and Read by (the account this configuration already binds with).

The hash is read on each login and stored nowhere. Caching it would put a password-equivalent in a daemon’s memory and go stale the moment the person changes their password.

One operator action is always required. ipaNTHash sits in an explicit exclusion list even in the administrator’s own ACI — no bind can read it by default, admin included — so an ACI granting the bind account the read has to be added on the directory server. The tab generates the LDIF with your bind DN and search base already filled in, ready to copy:

dn: <your user search base>
changetype: modify
add: aci
aci: (targetattr = "ipaNTHash")(version 3.0; acl "Taranac MS-CHAPv2";
allow (read,search,compare) userdn = "ldap:///<your bind DN>";)

Apply it, then press Check the directory. Readability is established with a (ipaNTHash=*) search asking for 1.1 — LDAP’s “no attributes” — so what comes back is a DN and a count and not a single hash value crosses the wire. 389 Directory Server applies the ACI to the filter as well as to the result, which is what makes that possible.

MS-CHAPv2 is switched on automatically when you create a configuration of a hash-publishing type, for the same reason it is for the first Active Directory: the branch only runs when a NAS actually sends MS-CHAP attributes, so “on” costs an installation that never receives one nothing, while “off by default” costs every installation that does an unfindable switch. It never re-asserts itself — switching it off and re-saving is respected. The domain slot, by contrast, is claimed only for Active Directory and only when no other configuration already owns it; a second AD domain added later stays off and you choose.

Use…When
A directoryYou already run AD, OpenLDAP or FreeIPA and want a single source of truth; you want device-admin and NAC policy to follow directory group membership; you want joiners/leavers handled by directory lifecycle, not by hand.
Local usersBreak-glass and bootstrap accounts (so you can still log in if the directory is unreachable); service or automation identities that should not live in the directory; small deployments with no directory at all.

A common pattern: keep one or two local break-glass admins, drive everyone else from the directory, and map directory groups to RBAC models and policy conditions. See Users & groups and RBAC.

  • Device-admin by directory group. Sync the “Network Admins” group, map its DN to an RBAC model and reference it in TACACS+ policy to grant privilege 15 and a command set. Removing a person from the group removes their access at the next sync.
  • 802.1X with machine certificates. Run computer sync to seed endpoints and pending computers, enable EAP-TLS on the connection with your CA certificate, and authorize by directory computer group in NAC authentication — either through an endpoint group filled by an ldap_dn or ldap_group rule, or by certificate identity.
  • 802.1X against a Linux directory. Point the connection at OpenLDAP or FreeIPA, sync the groups you authorize on, and run EAP-TTLS/PAP (or EAP-TLS) — no domain, no Samba. On FreeIPA you can also run the Windows default, PEAP-MSCHAPv2, by granting the bind account a read on ipaNTHash — still no domain and no Samba.
  • Mixed estate. Add one connection per directory — an AD domain, an OpenLDAP server and a FreeIPA realm can coexist, each with its own type, trust anchor and search bases. priority orders bare-username logins across them.

Deleting a connection offers three cascade choices for the accounts it created:

OptionResult
Refuse if users existDeletion is blocked while synced users remain.
Disable usersSynced users are deactivated but kept.
Delete users and groupsSynced users and groups are removed.