Skip to content

Taranac 1.2.2

Latest

Choose a tag to compare

@gurkin33 gurkin33 released this 06 Aug 05:23

Added

  • A directory no longer has to be Active Directory. The integration was
    built for AD and said so only in its code: bind by UPN, look accounts up by
    sAMAccountName, track them by objectGUID, filter on userAccountControl.
    Every one of those is false for OpenLDAP, FreeIPA and Google Workspace Secure
    LDAP, which is why connecting one of them could not be made to work by
    configuration alone. A directory now has a type — Active Directory,
    OpenLDAP, FreeIPA, Google Workspace or generic — and the type supplies both
    what the protocol can do there (MS-CHAPv2 and computer sync are Active
    Directory only) and the schema guesses you are free to overwrite (search
    filters, attribute maps, the examples under each field). The type is chosen
    once, when the configuration is created, and cannot be changed afterwards: each
    directory imports accounts by its own login attribute and tracks them by its
    own identifier, so switching would mean converting users already synchronised.
    Existing installations are untouched — a configuration with no type reads
    as Active Directory, and the Active Directory defaults are the previous
    literals character for character.
  • Directory accounts can authenticate with MS-CHAPv2. Devices that offer
    nothing else — MikroTik RouterOS sends only MS-CHAPv2 for an administrator
    login, has no PAP and no TACACS+ client — could not authenticate against
    Taranac at all, which also made the RouterOS RADIUS template shipped in 1.2.1
    unreachable. Local accounts are verified in Taranac itself; an Active Directory
    account is verified by a domain controller, because its password hash is
    unreadable by design. The same now works for 802.1X endpoints: a directory
    account authenticates over PEAP/MSCHAPv2 and over EAP-TTLS. Nothing to switch
    on — the branch is chosen by what the device sends, and a device that sends PAP
    never enters it.
  • The node joins the domain by itself. 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 Active Directory 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.
  • An MS-CHAPv2 tab on the LDAP configuration — membership per node, the
    feature switch, and a join dialog that asks the directory about the credentials
    and the target organizational unit before spending them, so the commonest
    failures (wrong password, mis-ordered OU path) are named in under a second
    instead of coming back as net ads join's own diagnostics. At most one LDAP
    configuration may own the domain, and the refusal names the one that already
    does.
  • An alert 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.
  • Domain authentication 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, so a rebooting domain controller no longer looks like
    password guessing.
  • A twelfth service, winbind, ships with every installation. It owns the
    node's Active Directory machine account. 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.
  • Certificate enrollment (EST) works end to end. It was previously
    unreachable by any supported route: the frontend proxied only /api/, so
    /.well-known/est/ returned the web UI's HTML to an enrolling client, and the
    only working path was the API's bare port with an internal-only certificate. EST
    and the revocation lists are now published through the front door on the
    installation's own certificate, and the CRL distribution point is filled in
    automatically.
  • An approval queue for certificate requests. Turning on "require approval"
    used to stop issuance rather than defer it — the request was stored as a
    certificate with serial "0" and placeholder dates, no approval screen existed,
    and every client poll appended another row. Requests are now their own records,
    deduplicated by the request's fingerprint across polls, with a queue in the UI,
    and an approval group that says who gets notified (deciding is nac_pki.edit,
    as it always was).
  • Approve a certificate request from an email link. Members of the approval
    group get the request by mail with what is being issued and to whom. The link
    never acts on being opened: mail scanners, Safe Links and messenger previews
    follow links by themselves, so the GET only shows a page and the decision needs
    a deliberate confirmation.
  • Certificate renewal cannot be fooled by a revoked certificate. A renewal is
    checked against the revocation list before it is granted, so a certificate that
    has been revoked can no longer be used to obtain its own replacement. Renewal
    authenticated by the client's own certificate needs a listener that verifies
    client certificates, which this release does not yet ship — until then a
    renewal authenticates the same way an enrollment does.
  • Master key rotation — ./taranac rotate-key. Runs on a stopped system in a
    single transaction: the operator presents the current key, names the new one, a
    backup is taken, every encrypted column is re-encrypted, the system starts
    again. It can also be rehearsed on a dev stack — a tool with nowhere to practise
    is a tool nobody has practised.
  • A backup that a disaster recovery can actually use. The archive now carries
    .env, the compose file and config/, because the database's secrets are
    ciphertext whose key lives outside it; restoring asks for the five keys that
    matter. pg_dump gained a timeout, so a hung dump no longer occupies the single
    scheduler slot forever while the dashboard shows the last good archive.
  • The username filter matches a substring, case-insensitively. Filtering the
    NAC Auth Log for ad_user1 found nothing when the account had authenticated as
    ad_user1@example.com or EXAMPLE\ad_user1. Applied to the NAC Auth Log, NAC
    Sessions and the AAA logs alike, with trigram indexes on the seven journal
    tables so it stays fast on a large archive (53 ms → 1.5 ms on a 60k-row
    partition).
  • A VLAN filter on authorization profiles — "which profiles drop an endpoint
    into VLAN 40" no longer means paging through the list.
  • EAP-TTLS accepts all three of its inner methods. Native EAP-TTLS/MSCHAPv2 —
    what a supplicant sends with no EAP inside the tunnel — failed for everyone,
    including a local account with the correct password.

Changed

  • The UI downloads only the languages it will show. All seven bundled
    languages (3.58 MB of translations) were part of the entry chunk, so an
    installation running only English still fetched about 3.2 MB it would never
    display. The other languages now load when one is selected.
  • The Security menu section is gone. Authentication & Sessions moved to
    System, and the Crypto page — which existed for a single button that broke
    installations — was removed along with it (see the rotation tool above).
  • The RBAC list no longer shows a Type column or a System/Custom filter. No
    role in a running system is ever a system role, so the column read "Custom" on
    every row and one filter option returned everything while the other returned
    nothing. On upgrade: a custom report dataset built on the config_rbac_roles
    template that had picked the is_system column fails validation on the next run
    and needs its columns re-picked; no seeded dataset uses it.
  • The RADIUS "authentication type" dropdown is gone. It offered PAP / CHAP /
    MS-CHAPv2, and nothing read the value — the handler authenticated with PAP
    regardless. It advertised a capability the product did not have, which is worse
    than no control at all. MS-CHAPv2 now works off what the device actually sends,
    with nothing for an operator to choose.
  • EAP-TEAP is gone from the log filter and the policy schema, having never
    been implemented, and EAP-TTLS — which is fully supported — is finally rendered
    and filterable in the log views and the session drawer.
  • Syslog forwarding has a ceiling and reports what it drops. The forward
    window was unbounded, so after any downtime the whole missed range was loaded at
    once and sent one message per record; on a large installation that is not a
    slowdown but an outage, at the moment the system comes back.
  • The dashboard stops polling while its browser tab is hidden.

Fixed

  • Re-authentication no longer loses a session's identity, and a session the
    NAS keeps reporting is no longer archived behind the operator's back. An 802.1X
    re-authentication closes one session and opens the next milliseconds apart on
    the same port, which is what made the active session show LAB\anonymous.
  • A CoA "session not found" no longer kills a live session. The switch's 503
    answers "did I find a session matching your identifiers", not "is anybody on the
    network" — it usually means the port re-authenticated in place. Taranac used to
    archive the session and tell the operator the disconnect had succeeded, with no
    way to bring it back.
  • The orphaned-session probe no longer disconnects healthy sessions. It asked
    "are you still there?" with a real Disconnect-Request, and on hardware that
    sends no periodic interim accounting (the Cisco default) every healthy session
    eventually crossed the threshold and was torn down for being healthy. CoA is
    also sent to the port configured for the device instead of a fixed one.
  • Reauthentication timer reaches the hardware. The value was carried
    everywhere — model, UI, reports, daemon cache — and serialized nowhere: an
    operator typed 3600, saw the "seconds" hint, and got nothing. It is now rendered
    as Session-Timeout plus Termination-Action, per the vendor's own mapping.
  • A rejected reject reason is now the right one. An endpoint whose TLS
    handshake dies — typically because it does not trust the RADIUS server
    certificate — was logged as PEAP with auth_failed, sending the operator after
    credentials for a certificate problem. The EAP method is now taken from what the
    daemon reports, a rejected server certificate is named as such, and an account
    that cannot use PEAP is told to use EAP-TTLS instead of being reported as a bad
    password. A missing ntlm_auth helper is likewise named instead of surfacing as
    a wrong password.
  • A MikroTik administrator's successful login no longer writes a phantom
    failure.
    RouterOS probes the SSH "none" method by proving the empty password
    before prompting the human, so every successful login produced a counted
    bad_password — halving the configured brute-force threshold for exactly the
    devices that need MS-CHAPv2, and topping the "top failed authentications" widget
    with the customer's own administrators.
  • Failed RADIUS MFA challenges say what actually arrived instead of reporting
    an unusable fragment of the submitted value, so an expired code and a value that
    was never a code are no longer the same log line.
  • An unreachable directory fails instead of hanging. The LDAP client reads a
    boolean server pool as "retry forever", sleeping between rounds, so a directory
    that did not answer never produced an error — the request simply hung, holding
    the worker that made it. On every operation, including Test connection, which
    is exactly where an operator goes to find out what is wrong, and inside the
    RADIUS and TACACS+ daemons that loop sat in the thread serving the request.
  • A wrong CA file is named instead of returning a server error. The
    certificate is parsed when the connection is described rather than when it is
    opened, so a bad file surfaced as HTTP 500 on every operation; the connection
    report now says what is wrong with it.
  • Computer synchronisation says which setting is missing. With no computer
    search base configured it failed with "connection failure", which sent the
    operator to check the network, the controller and the certificates while the
    real cause sat only in the container log.
  • An empty attribute map no longer silently falls back to sAMAccountName,
    and creating a non-Active-Directory configuration no longer reports an error
    after having saved it.
  • Group matching on FortiGate. The seeded TACACS+ template named the AV-pair
    member_of; a FortiGate asks for memberof, so it was silently dropped and
    group matching could never succeed once an operator added a config match
    block. Migration 0019 renames it in existing templates and profiles too.
  • A network object of type FQDN can no longer be attached to a device. Such an
    object carries no network, so the device silently dropped out of the RADIUS
    clients list — it was not authenticated at all — and its TACACS+ rules lost
    their device restriction entirely.
  • Tables show an error instead of "no records". On a failed request the log
    pages rendered their empty state, so an operator in an incident read an empty
    authentication journal as "there were no authentications" — the worst kind of
    failure, silent and pointing the wrong way. Failures now say so, and a failed
    auto-refresh over live rows says the data is the last good one.
  • Escape closes only the topmost drawer. Pressing it in a third-level form
    closed all three levels and lost what had been typed; a confirmation dialog
    opened from a drawer now correctly takes the key.
  • A backup archive missing its declared content is no longer reported as
    successful.
    The host-files part was never collected on any deployment, and the
    only trace was a manifest field nobody reads — discovered at restore time, the
    worst possible moment. An incomplete archive is still written, but it is loud:
    audit, error log, and a backup:incomplete alert that clears only on the next
    complete archive.
  • The "Rotate Master Key" button no longer destroys the installation. It
    re-encrypted only the system settings, leaving device keys, enable passwords,
    the internal CA's private keys, NCM credentials, TOTP secrets and the license on
    the old key while swapping the key in memory — and answered 200. The endpoint
    now refuses, and rotation is done by the tool described above.

Security

  • nginx updated to 1.30.4, and it no longer announces its version. The web
    listeners ran 1.27.5 (edge, frontend) and 1.26.3 (captive portal) — branches
    that ended in 2025, on tags frozen ever since, so rebuilding those images had
    never brought a single fix. Nineteen and eighteen published advisories
    respectively apply to those versions, including a buffer overflow in map with
    a regular expression. None of them is reachable through the shipped
    configuration, which uses only proxy, TLS, gzip and return — but the list
    grows by itself, and an operator whose scanner flags it should not have to take
    our word for why it does not matter.
  • Certificate enrollment could be reached without any credential. A request
    carrying two invented headers and no credentials received HTTP 200 and a signed
    certificate: the enrollment endpoint trusted client-identity headers that no
    proxy in the deployment ever set or stripped, so anyone who could reach it
    controlled them. Reproduced on a stand, now a 401.
  • The token issued after the password but before the second factor was accepted
    as a session.
    It was signed with the same key and the same shape as a full
    access token, and nothing checked what it was for — so a stolen password alone
    was enough to enroll an attacker's own second factor over the victim's and then
    log in normally. Every token now declares its purpose and every endpoint accepts
    only the purposes it names, deny by default.
  • Removing someone else's second factor required nothing but the record's
    UUID.
    It now requires mfa.edit.
  • A group's RBAC model could be assigned by anyone who could edit groups. The
    model behind a privileged group was also visible to anyone who could view
    groups, so an operator could grant their own group a privileged model and simply
    log in again. Assigning a model, and membership of administrative groups, now
    require administrator rights.
  • The Telegram MFA webhook accepted unauthenticated requests. The secret check
    started with "if a secret was sent", so a request without the header passed it
    entirely, and a forged one could make the bot disclose which Taranac accounts
    are linked to a given Telegram account — into a chat of the caller's choosing.
    /mfa/verify is now also attempt-limited.
  • Fetching a CRL was an internal port scanner. Only the URL prefix was
    checked: no loopback or private-range block list, no re-check after a redirect,
    no size limit, TLS verification disabled outright, and the exception text handed
    back verbatim — and it re-runs on a schedule. It now validates the target,
    connects to the address it validated, verifies the certificate, caps the
    download, and reports failures without describing what it found.
  • A value containing a newline could append arbitrary directives to a generated
    daemon configuration.
    Escaping covered only quotes and backslashes, in all
    three generators. Control characters are now replaced: a mangled value breaks
    one device, a raw newline breaks the configuration of the whole fleet.
  • A TACACS+ rule whose device restriction resolved to nothing applied to every
    device.
    An emptied device group, or a device sitting on an FQDN object, turned
    a narrow rule into a fleet-wide one — and the generated ruleset is the only
    place the restriction is enforced, so nothing downstream caught it. The same
    rule already failed closed in RADIUS, which means one rule had opposite meanings
    in the two protocols.
  • A domain-qualified name is no longer an alias for a local account. Over
    802.1X, ANYTHING\alice and alice@whatever.invalid all authenticated as the
    local account alice, and the log named the bare account either way. The name
    is now tried exactly as sent, and a qualified name may only ever resolve to a
    directory account.