0% found this document useful (0 votes)
35 views67 pages

TACACS+ NG Configuration Guide

Uploaded by

IurieBoscanean
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views67 pages

TACACS+ NG Configuration Guide

Uploaded by

IurieBoscanean
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

TACACS+ NG

Marc Huber
TACACS+ NG ii

COLLABORATORS

TITLE :

TACACS+ NG

ACTION NAME DATE SIGNATURE

WRITTEN BY Marc Huber August 18, 2024

REVISION HISTORY

NUMBER DATE DESCRIPTION NAME


TACACS+ NG iii

Contents

1 Introduction 1
1.1 Download . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

2 Definitions and Terms 1

3 Operation 2
3.1 Command line syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
3.2 Signals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
3.3 Event mechanism selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

4 Configuration 3
4.1 Sample Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
4.2 Configuration directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
4.2.1 Global options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
[Link] Limits and timeouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
[Link] DNS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
[Link] Process-specific options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
[Link] Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2.2 Realms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
[Link] Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.2.3 Realm attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
[Link] Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
[Link].1 Accounting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
[Link].2 Spoofing Syslog Packets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
[Link] User Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
[Link] Limits and timeouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
[Link].1 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
[Link].2 User back-end options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
[Link].3 TLS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
[Link] Miscellaneous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
[Link] Realm Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
[Link] Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
[Link] Networks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
[Link].1 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
[Link] Devices (Hosts) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
[Link].1 Timeouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
[Link].2 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
[Link].3 Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
TACACS+ NG iv

[Link].4 Banners and Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27


[Link].5 Workarounds for Client Bugs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
[Link].6 Inheritance and Hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
[Link].7 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
[Link].8 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
[Link] Time Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
[Link].1 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
[Link] Access Control Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
[Link].1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
[Link] Rewriting User Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
[Link] Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
[Link].1 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
[Link] Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
[Link].1 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
[Link] Profiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
[Link] Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
[Link] Configuring Non-local Users via MAVIS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
[Link] Configuring Local Users for MAVIS authentication . . . . . . . . . . . . . . . . . . . . . . . 44
[Link] Configuring User Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
[Link] Configuring Expiry Dates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
[Link] Configuring Authentication on the NAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
[Link] Configuring Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
[Link] Authorizing Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
[Link] The Authorization Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
[Link] Authorization Relies on Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
[Link] Configuring Service Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
[Link].1 The Authorization Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.3 MAVIS Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.3.1 LDAP Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
[Link] LDAP Custom Schema Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
[Link] Active Directory Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
[Link] Generic LDAP Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.3.2 PAM back-end . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.3.3 System Password Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.3.4 Shadow Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.3.5 RADIUS Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
[Link] Sample Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.3.6 Experimental Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.3.7 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
TACACS+ NG v

5 Debugging 55
5.1 Debugging Configuration Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.2 Trace Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

6 Frequently Asked Questions 56

7 Multi-tenant setups 58
7.1 AD, Realms and Tenants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

8 AAA rule tracing 59

9 Bugs 61

10 References 61

11 Copyrights and Acknowledgements 61


TACACS+ NG 1 / 62

1 Introduction

tac_plus-ng is a TACACS+ daemon. It provides networking components like routers and switches with authentication, authori-
sation and accounting services.
This version is a major rewrite of the original public Cisco source code and is in turn largely based on tac_plus, which comes
with the same distribution. Key features include:

• NAS specific device keys, prompts, enable passwords

• Rule-based permission assignment


• Flexible external back-ends for user profiles (e.g. via PERL scripts or C; LDAP (including ActiveDirectory), RADIUS and
others are included)
• Connection multiplexing (multiple concurrent NAS clients per process)

• Session multiplexing (multiple concurrent sessions per connection, single-connection)


• Scalable, no limit on users, clients or servers.
• CLI context aware.

• Full support for both IPv4 and IPv6


• Implements and auto-detects HAProxy protocol 2.
• Supports TLS
• Compliant to RFC8907

• Supports Linux VRFs


• Supports (non-standard) SSH Public Key Authentication (see the Wiki for reference)

1.1 Download

You can download the source code from the GitHub repository at [Link] On-line
documentation is available via [Link] too.

2 Definitions and Terms

The following chapters utilize a couple of terms that may need further explanation:

Client or NAC A Network Access Client, e.g. the source device of a ssh (or telnet) connection.
A Network Access Server or Device, e.g. a Cisco box, or any other client which makes
Device or NAS or NAD TACACS+ authentication and authorization requests, or generates TACACS+ accounting
packets.
A program which services network requests for authentication and authorization, verifies
Daemon
identities, grants or denies authorizations, and logs accounting records.
Strings of text in the form attribute=value, sent between a NAS and a TACACS+
AV pairs
daemon as part of the TACACS+ protocol.

Since a NAS is sometimes referred to as a server, and a daemon is also often referred to as a server, the term server has been
avoided here in favor of the less ambiguous terms NAS and Daemon.
TACACS+ NG 2 / 62

3 Operation

This section gives a brief and basic overview on how to run tac_plus-ng.
In earlier versions, tac_plus wasn’t a standalone program but had to be invoked by spawnd. This has changed, as spawnd func-
tionality is now part of the tac_plus binary. However, using a dedicated spawnd process is still possible and, more importantly,
the spawnd configuration options and documentation remain valid.
tac_plus may use auxiliary MAVIS back-end modules for authentication and authorization.

3.1 Command line syntax

The only mandatory argument is the path to the configuration file:


tac_plus-ng [ -P ] [ -d level ] [ -i child_id ] configuration-file [ id ]

If the program was compiled with CURL support, configuration-file may be an URL.
Keep the -P option in mind - it is imperative that the configuration file supplied is syntactically correct, as the daemon won’t
start if there are any parsing errors.
The -d switch enables debugging. You most likely don’t want to use this. Read the source if you need to.
The -i option is only honoured if the build-in spawnd functionality is used. In that case, it selects the configuration ID for
tac_plus, while the optional last argument id sets the ID of the spawnd configuration section.

3.2 Signals

Both the master (that’s the process running the spawnd code) and the child processes (running the tac_plus-ng code) intercept
the SIGHUP signal:

• The master process will restart upon reception of SIGHUP, re-reading the configuration file. The child processes will recognize
that the master process is no longer available. It will continue to serve the existing connections and terminate when idle.
• If SIGHUP is sent to a child process it will stop accepting new connections from its master process. It will continue to serve
the existing connections and terminate when idle.

Sending SIGUSR1 to the master process will cause it to abandon existing child processes (these will continue to serve the
existing connections only) and start new child processes.

3.3 Event mechanism selection

Several level-triggered event mechanisms are supported. By default, the one best suited for your operating system will be used.
However, you may set the environment variable IO_POLL_MECHANISM to select a specific one.
The following event mechanisms are supported (in order of preference):

• port (Sun Solaris 10 and higher only, IO_POLL_MECHANISM=32)


• kqueue (*BSD and Darwin only, IO_POLL_MECHANISM=1)
• /dev/poll (Sun Solaris only, IO_POLL_MECHANISM=2)
• epoll (Linux only, IO_POLL_MECHANISM=4)
• poll (IO_POLL_MECHANISM=8)
• select (IO_POLL_MECHANISM=16)

Environment variables can be set in the configuration file at top-level:


setenv IO_POLL_MECHANISM = 4
TACACS+ NG 3 / 62

4 Configuration

The daemon is configured using a text file. Let’s have a look at a sample configuration first, before digging into the various
configuration directives.

4.1 Sample Configuration

A single configuration file is sufficient for configuring quite everything: the spawnd connection broker, tac_plus-ng and the
MAVIS authentication and authorization back-end.
The daemon supports shebang syntax. If the configuration file is executable and starts with
#!/usr/local/sbin/tac_plus-ng

then it can be started directly.


The first step is to configure the spawnd portion to tell the daemon the addresses and TCP ports to listen on and to, eventually
pass realms:
id = spawnd {
listen { port = 49 }
listen { port = 4949 }
listen { address = ::0 port = 4950 realm = customer1 }
listen { address = [Link] port = 4951 realm = customer2 }
# listen { address = [Link] port = 4951 realm = customer2 tls = yes }
#
# See the spawnd configuration guide for further configuration options.
}

The thing that needs some explanation here is realms. A realm in tac_plus-ng summarizes a set of configuration options. Realms
inherit configurations from their parent realm, including the parent ruleset, which will be evaluated if the local ruleset doesn’t
exist or doesn’t return a verdict.
The default realm is internaly named default. Using realms is optional.
Now to the actual tac_plus-ng configuration which starts with
id = tac_plus-ng {
# This is the top-level realm, actually.

The second line above starts a comment. Comments can appear anywhere in the configuration file, starting with the # character
and extending to the end of the current line. Should you need to disable this special meaning of the # character, e.g. if you have
a password containing a # character, simply enclose the string containing it within double quotes.
Typically, the next step is to define log destinations and tell the daemon to use them. This sample logs to disk, but other
destinations (syslog, pipe) are available, too.
log authzlog { destination = /var/log/tac_plus/authz/%Y/%m/%[Link] }
log authclog { destination = /var/log/tac_plus/authc/%Y/%m/%[Link] }
log acctlog { destination = /var/log/tac_plus/acct/%Y/%m/%[Link] }
accounting log = acctlog
authentication log = authclog
authorization log = authzlog

Logs are interited to sub-realms and while sub-realms can define their own logging that won’t override the parent realm defini-
tions.
You can specify a retire limit to have the server auto-terminate and restart its worker processes:
retire limit = 1000
TACACS+ NG 4 / 62

Then, there’s the MAVIS part:


mavis module = groups {
resolve gids = yes
resolve gids attribute = TACMEMBER
groups filter = /^(guest|staff|ubuntu)$/
}

mavis module = external {


exec = /usr/local/sbin/pammavis "pammavis" "-s" "sshd"
}

user backend = mavis


login backend = mavis chpass
pap backend = mavis

which defines interaction with external external back-ends.


You can define network objects for later use in ACLs:
net outThere { address = [Link] address = [Link]/16 }

Networks can be hierarchic, too:


net all {
net north {
address = [Link]/16
}
net south {
address = [Link]/16 }
}

Now, define device objects for your network access devices. Just like realms and networks these can be hierarchic:
device world {
welcome banner = "\nHitherto shalt thou come, but no further. (Job 38.11)\n\n"
key = QaWsEdRfTgY
enable 15 = clear test
address = ::/0
device south {
address = [Link]/16
}
device west {
address = [Link]/16
}
}

device localhost {
address = [Link]
welcome banner = "Welcome home\n"
parent = world # for key and other definitions not set here
}

device rfc {
address = [Link]/12
welcome banner = "Welcome private\n"
key = labKey
}

Now, define some profiles. These will be assigned to users later:


profile readwrite {
script {
TACACS+ NG 5 / 62

if (service == shell) {
if (cmd == "")
set priv-lvl = 15
permit
}
}
}

profile getconfig {
script {
if (service == shell) {
if (cmd == "") {
set autocmd = "sho run"
set priv-lvl = 15
permit
}
}
}
}

profile engineering {
script {
if (service == shell) {
if (cmd == "") {
set priv-lvl = 7
permit
}
if (cmd =~ /^ping/) deny
permit
}
}
}

profile guest {
script {
if (service == shell) {
if (cmd == "") {
set priv-lvl = 1
permit
}
}
permit
}
}

Your can define groups to implement a role-based access control scheme ...
group admin {
group north # "admin" is a member
group south # of both
}

group engineering {
}

group guest {
}

... and add users:


user demo {
password login = clear demo
TACACS+ NG 6 / 62

member = engineering,admin
}

user readonly {
password login = clear readonly
member = guest
}

Finally, implement a rule-set to assign profiles to users:


ruleset {
rule from-localhost {
enabled = yes
script {
if (nas == localhost) {
if (group == admin) {
profile = admin
permit
}
if (group == engineering ) {
profile = engineering
permit
}
}
}
}
rule from-rfc {
enabled = yes
script {
if (nas == rfc) {
if (group == south) {
profile = admin
permit
}
if (group == engineering ) {
profile = engineering
permit
}
}
}
}
}
}

4.2 Configuration directives

Configuration options include

1. global options
2. realms
3. devices
4. time specifications
5. profiles
6. groups
7. users
TACACS+ NG 7 / 62

8. access lists
9. rules

The reasoning behind that non-random order is that parts of the configuration may use other parts, and these need to exist before
being used.
id = tac_plus-ng

{ TopLevelAttr }

RealmAttr

RealmDecl

Railroad diagram: TacPlusConfig

Including Files
Configuration files may refer to other configuration files:
include = file
will read and parse file. Shell wildcard patterns are expanded by glob(3). The include statement will be accepted virtually
everywhere (but not in comments or textual strings).

4.2.1 Global options

The global configuration section may contain the following configuration directives, plus the realm options detailed in the next
section. realm confiurations at global level are implicitely assigned to the default realm and will be inherited by sub-realms.

[Link] Limits and timeouts

A number of global limits and timeouts may be specified exclusively at global level:

• retire limit = n
The particular daemon instance will terminate after processing n requests. The spawnd instance will spawn a new instance if
necessary.
Default: unset
• retire timeout = s
The particular daemon instance will terminate after s seconds. spawnd will spawn a new instance if necessary.
Default: unset

Time units
Appending s, m, h or d to any timeout value will scale the value as expected.

[Link] DNS

tac_plus-ng can make use of both static and dynamic (via c-ares) DNS entries. Configuration options at global (and realm) level
are:

• dns preload address address = hostname


Preload DNS cache with address-to-hostname mapping.
TACACS+ NG 8 / 62

• dns preload file = filename


Preload DNS cache with address-to-hostname mappings from filename (see your hosts(5) manpage for syntax).
Example:
dns preload address [Link] = [Link]
dns preload file = /etc/hosts

device [Link] {
# "address = [Link]" is implied
key = mykey
}

• dns cache period = seconds


This option specifies the minimum DNS response caching time (default: 1800 seconds).

• dns servers = "string"


This option specifies the servers to use. This string will be evaluated by ares_set_servers_ports_csv(3), please see the corre-
sponding man page for details. The option isn’t available if compiled without DNS support.

The following configuration options are available at global, realm, device and net level.

• dns reverse-lookup [ nac | nas ] = ( yes | no )


This will perform a DNS reverse lookup on the NAC address, the NAS address or (if unspecified) both.
• dns timeout = seconds
This option specifies the maximum amount of time to wait for a DNS response.

[Link] Process-specific options

There are a couple of process-specific options available:

• coredump directory = directory


Dump cores to directory. You really shouldn’t need this.

[Link] Railroad Diagrams

umask = umask

syslog facility = string


level

working directory = directory

coredump directory = directory

retire limit = count

seconds = seconds

time zone = timeZone

Railroad diagram: GlobalDecl


TACACS+ NG 9 / 62

4.2.2 Realms

Bascially, realms are containers to logically separate configuration sets. At top-level, there’s the default realm (called default
internally). Realms pass on most configurations (e.g. logging, users (if there are no users defined in that realm scope), groups,
profiles) to their sub-realms.
Realm selection is intially based on spawnd configuration:
spawnd = {
listen { port = 49 } # implied realm is "default"
listen { port = 3939 } # implied realm is "default"
listen { port = 4949 realm = realmOne }
listen { port = 5959 realm = realmTwo }
}

If VRFs are used and no realm is specified in the spawnd section, the daemon will try to use the VRF name as realm and fall
back to the default realm if that "vrf realm" isn’t defined.
A realm can be selected based on device address, too:
device myDevices { address = [Link]/24 target-realm = realmOne }

The syntax to use (and define) realms is


realm realmName { ... }

at top configuration level. Realms cover devices, users, groups, profiles, rulesets, timespecs, MAVIS configurations other config-
uration options.

[Link] Railroad Diagrams

realm realmName { }

RealmAttr

Railroad diagram: RealmDecl

4.2.3 Realm attributes

The following options may be specified at realm level. This includes the default realm:

[Link] Logging

Logging options defined in the top-level default realm will be shared with sub-realms unless the sub-realm has its own logging
configuration. The software provides logs for

• Authentication
authentication log = log_destination

• Authorization
authorization log = log_destination

• Accounting
accounting log = log_destination
TACACS+ NG 10 / 62

• Connections
connection log = log_destination

Logs may be written to multiple destinations:


Valid log destinations are "named":
log mylog {
destination = [Link] # UDP syslog
# or one of the following:
# destination = [fe80::123:4567:89ab:cdef]:514 # IPv6 UDP, with non-standard UDP port
# destination = "/tmp/[Link]" # plain file, async writes
# destination = ">/tmp/[Link]" # plain file, sync writes
# destination = "|my_script.sh" # script
# destination = syslog # syslog(3)
#
syslog facility = MAIL # sets log facility
syslog level = DEBUG # sets log level
}
authentication log = mylog
accounting log = mylog
authorization log = mylog

Syslog
Logging non-session related output to syslogd(8) can be disabled using
syslog default = deny

Log destinations may contain strftime(3)-style character sequences, e.g.:


destination = /var/log/tac_plus/%Y/%m/%[Link]

to automate time-based log file switching. By default, the daemon will use your local time zone for time conversion. You can
switch to a different one by using the time zone option (see below).
A couple of other configuration options that may be useful in log context include:

• ( authentication | authorization | accounting ) format = string


This defines the logging format. strftime(3) conversions are recognized. The following variables are resolved:

values of cmd= and cmd-arg= attribute-value pairs, separated by


${cmd}, ${cmd,separator} whitespace or separator. Will be mapped to ${args} if service isn’t
shell
input attribute-value pairs (excluding service, separated by whitespace
${args}, ${args,separator}
or separator
${rargs}, ${rargs,separator} output attribute value pairs, separated by whitespace or separator
${[Link]}, ${nas}
Device IP address
[deprecated]
${client}, ${nac} Client IP address
${user} user name
${[Link]} user name before any rewrite operations
${profile} profile assigned to user
${service} service type (e.g. shell)
${result} typically permit or deny
TACACS+ NG 11 / 62

${[Link]}${port}
NAS port (console, tty, ...)
[deprecated]
${hint} added/replaced for authorization, informal text for accounting
${[Link]}, ${host}
Device name of matching device declaration
[deprecated]
${[Link]},
Client DNS reverse mapping
${nac-name} [deprecated]
${[Link]},
Device DNS reverse mapping
${nas-name} [deprecated]
A message ID, perhaps suitable for RFC5424 logs. These are listed
${msgid}
somewhere below.
${type} packet type (authen/author/acct)
${accttype} accounting type (start/stop/update)
${priority} syslog priority
${action} authentication info (e.g. pap login)
${privlvl} privilege level
${authen-action} login or chpass
${authen-type} Authentication packet type, e.g. AUTHEN/PASS, AUTHEN/FAIL
${authen-service} asciiascii/pap/chap/mschap/mschapv2
${authen-method} krb5/line/enable/local/tacacs+/guest/radius/krb4/rcmd
${rule} Name of the matching rule.
${label} Ruleset label, if any.
${config-file} Configuration file name
${config-line} Configuration file line number
${context} Context variable (set via context = ...)
Name of the current socket IPv4 vrf, supported on Linux (requires sysctl
${vrf}
net.ipv4.tcp_l3mdev_accept=1) and possibly OpenBSD.
${realm} realm name
${uid} UID from PAM backend
${gid} GID from PAM backend
${gids} GIDs from PAM backend
${home} Home directory from PAM backend
${shell} Shell from PAM backend
${dn} Raw dn backend value, typically from LDAP
The IDENTITY_SOURCE backend value (the identitySourceName of the
${identity-source}
originating MAVIS module)
${memberof} Raw memberOf backend value, typically from LDAP
${[Link]}, ${hostname}
Server host name
[deprecated]
${[Link]} Server address
${[Link]} Server TCP port
${[Link]} TLS Connection Version (requires LibTLS or OpenSSL)
${[Link]} TLS Connection Cipher (requires LibTLS or OpenSSL)
${[Link]} TLS Peer Certificate Issuer (requires LibTLS or OpenSSL)
${[Link]} TLS Peer Certificate Subject (requires LibTLS)
${[Link]} TLS Connection Cipher Strength (requires LibTLS or OpenSSL)
${[Link]} TLS peer certificate Common Name (requires LibTLS or OpenSSL)
${[Link]} TLS PSK identity (requires OpenSSL)

The built-in defaults as of writing this are:


# Accounting to file/pipe:
"%Y-%m-%d %H:%M:%S %z\t${nas}\t${user}\t${port}\t${nac}\t${accttype}\t${service}\t${cmd}\n ←-
"
# Accounting to UDP syslog:
"<${priority}>%Y-%m-%d %H:%M:%S %z ${hostname} ${nas}|${user}|${port}|${nac}|${accttype}|$ ←-
{service}|${cmd}"
# Accounting to syslog(3):
TACACS+ NG 12 / 62

"${nas}|${user}|${port}|${nac}|${accttype}|${service}|${cmd}"
# Authorization to file/pipe:
"%Y-%m-%d %H:%M:%S %z\t${nas}\t${user}\t${port}\t${nac}\t${profile}\t${result}\t${service ←-
}\t${cmd}\n"
# Authorization to UDP syslog:
"<${priority}>%Y-%m-%d %H:%M:%S %z ${hostname} ${nas}|${user}|${port}|${nac}|${profile}|${ ←-
result}|${service}|${cmd}"
# Authorization to syslog(3):
"${nas}|${user}|${port}|${nac}|${profile}|${result}|${service}|${cmd}"
# Authentication to file/pipe:
"%Y-%m-%d %H:%M:%S %z\t${nas}\t${user}\t${port}\t${nac}\t${action} ${hint}\n"
# Authentication to UDP syslog:
"<${priority}>%Y-%m-%d %H:%M:%S %z ${hostname} ${nas}|${user}|${port}|${nac}|${action} ${ ←-
hint}"
# Authentication to syslog(3):
"${nas}|${user}|${port}|${nac}|${action} ${hint}"
# Connections to file/pipe:
"%Y-%m-%d %H:%M:%S %z\t${accttype}\t${nas}\t${[Link]}\t${[Link]}\ ←-
t${[Link]}\n"
# Connections to UDP syslog:
"<${priority}>%Y-%m-%d %H:%M:%S %z ${hostname} ${accttype}|${nas}|${[Link]}|${ ←-
[Link]}|${[Link]}"
# Connections to syslog(3):
"${accttype}|${nas}|${[Link]}|${[Link]}|${[Link]}"

Message ID Description
AUTHZPASS authorization succeeded
AUTHZPASS-ADD authorization succeeded, attribute-value-pairs were added
AUTHZPASS-REPL authorization succeeded, attribute-value-pairs were replaced
AUTHZFAIL authorization failed
AUTHCFAIL generic authentication failure
AUTHCFAIL-ABORT authentication was aborted
AUTHCFAIL-BACKEND the authentication backend failed
AUTHCFAIL-BUG authentication failed due some programming error
AUTHCFAIL-DENY authentication was denied
AUTHCFAIL-WEAKPASSWORD the password used didn’t met minimum criteria
AUTHCFAIL-ACL access was denied due to ruleset or acl
AUTHCFAIL-DENY-RETRY the user tried the same wrong password once more
AUTHCFAIL-PASSWORD-NOT_TEXT the password isn’t specified as clear-text
AUTHCFAIL-BAD-CHALLENGE-LENGTHthe MSCHAP challenge length didn’t match
AUTHCFAIL-NOPASS there’s no passwort set for the user
AUTHCPASS authentication passed
ACCT-START accounting start
ACCT-STOP accounting stop
ACCT-UNKNOWN unknown (non-compliant) accounting data
ACCT-UPDATE accounting update/watchdog
CONN-REJECT connection was rejected
CONN-START connection was started
CONN-STOP connection was terminated

• time zone = time-zone


By default, the daemon uses your local system time zone to convert the internal system time to calendar time. This option sets
the TZ environment variable to the time-zone argument. See your local tzset man page for details.
• umask = mode
This sets the file creation mode mask. Example:
umask = 0640
TACACS+ NG 13 / 62

[Link].1 Accounting

All accounting records are written, as text, to the file (or command) specified with the accounting log directive.
Accounting records are text lines containing tab-separated fields. The first 6 fields are always the same. These are:

• timestamp
• NAS address
• username

• port
• NAC address
• record type

Following these, a variable number of fields are written, depending on the accounting record type. All are of the form attribute=valu
There will always be a task_id field.
Attributes, as sent by the NAS, might be:
unknown service start_time port elapsed_time status priv_level cmd protocol cmd-arg bytes_i
bytes_out paks_in paks_out address task_id callback-dialstring nocallback-verify callback-l
callback-rotary
More may appear,. randomly..
Example records (lines wrapped for legibility) are thus:
1995-07-13 13:35:28 -0500 [Link] chein tty5 [Link]
stop task_id=12028 service=exec port=5 elapsed_time=875
1995-07-13 13:37:04 -0500 [Link] lol tty18 [Link]
stop task_id=11613 service=exec port=18 elapsed_time=909
1995-07-13 14:09:02 -0500 [Link] billw tty18 [Link]
start task_id=17150 service=exec port=18
1995-07-13 14:09:02 -0500 [Link] billw tty18 [Link]
start task_id=17150 service=exec port=18

Elapsed time is in seconds, and is the field most people are usually interested in.

[Link].2 Spoofing Syslog Packets

The script [Link] (which comes bundled with this distribution, have a look at the tac_plus-ng/extra/
directory) may be used to make syslogd believe that logs come straight from your router, not from tac_plus-ng.
E.g., if your syslogd is listening on [Link], you may try:
access log = "|exec sudo /path/to/[Link] [Link]"

This may be useful if you want to keep logs in a common place.


In contrast to the older [Link] script [Link] will handle both IPv4 and IPv6 addresses and has
more configuration options.
Please read tac_plus-ng/extra/[Link] too, if you’re thinking about using this script.
TACACS+ NG 14 / 62

[Link] User Messages

User messages, e.g. the Username prompt, can be customized, both at device and realm level:
message USERNAME = "utilisateur"

Supported messages and their defaults:

ID Default value
ACCOUNT_EXPIRES "This account will expire soon."
BACKEND_FAILED "Authentication backend failure."
CHANGE_PASSWORD "Please change your password."
DENIED_BY_ACL "Denied by ACL"
ENABLE_PASSWORD "Enable Password: "
PASSWORD "Password: "
PASSWORD_ABORT "Password change dialog aborted."
PASSWORD_AGAIN "Retype new password: "
PASSWORD_CHANGE_DIALOG "Entering password change dialog"
PASSWORD_CHANGED "Password change succeeded."
PASSWORD_EXPIRED "Password has expired."
PASSWORD_EXPIRES "Password will expire on %c." (fed to strftime(3))
PASSWORD_INCORRECT "Password incorrect."
PASSWORD_MINREQ "Password doesn’t meet minimum requirements."
PASSWORD_NEW "New password: "
PASSWORD_NOMATCH "Passwords do not match."
PASSWORD_OLD "Old password: "
PERMISSION_DENIED "Permission denied."
RESPONSE "Response: "
RESPONSE_INCORRECT "Response incorrect."
USERNAME "Username: "
USER_ACCESS_VERIFICATION "User Access Verification"

[Link] Limits and timeouts

A number of global limits and timeouts may be specified at realm and global level:

• connection timeout = s
Terminate a connection to a NAS after an idle period of at least s seconds.
Default: 600

• context timeout = s
Clears context cache entries after s seconds of inactivity. Default: 3600 seconds.
Default: 3600
This configuration will be accepted at realm level, too.

• warning period = d
Set warning period for password expiry to d days.
Default: 14
• max-rounds = n
This sets an upper limit on the number of packet exchanges per session. Default: 40, acceptable range is from 1 to 127.
TACACS+ NG 15 / 62

[Link].1 Authentication

• password acl = acl


password acl may be used to perform simple compliance checks on user passwords. For example, to enforce a minimum
password length of 6 characters you may try
acl password-compliance {
if (password =~ /^....../)
permit
deny
}
password acl = password-compliance

Authentications using passwords that fail the check will be rejected.


• password max-attempts = integer
The max-attempts parameter limits the number of Password: prompts per TACACS+ session at login. It currently
defaults to 1, meaning that a typical login sequence with bad passwords would look like:
> telnet [Link]
Trying [Link]...
Connected to [Link].
Escape character is ’^]’.

Welcome. Authorized Use Only.

Username: admin
Password: ***
Password incorrect.

Welcome. Authorized Use Only.

Username: admin
Password: ****
Password incorrect.

Welcome. Authorized Use Only.

Username: admin
Password: *
Password incorrect.

Connection closed by foreign host.

Using, for example,


password max-attempts = 3

(the actual default in earlier versions was 4) would change this dialog to:
> telnet [Link]
Trying [Link]...
Connected to [Link].
Escape character is ’^]’.

Welcome. Authorized Use Only.

Username: admin
Password: ***
TACACS+ NG 16 / 62

Password incorrect.
Password: ****

Password incorrect.
Password: *****

Password incorrect. Go away.

Welcome. Authorized Use Only.

Username:

It’s at the NAS’s discretion to restart the authentication dialog with a new TACACS+ session or to close the (Telnet/SSH/...)
session to the user if TACACS+ authentication fails.
Thid directive can be used at device level, too.
• anonymous-enable = ( permit | deny )
Several broken TACACS+ implementations send no or an invalid username in enable packets. Setting this option to deny
tries to enforce user authentication before enabling. This option defaults to permit.
Alas, this may or may not work. In theory, the enable dialog should look somewhat like:
Router> enable
Username: me
Password: *******
Enable Password: **********
Router#

However, some implementations may resend the user password at the Enable Password: prompt. In that case you’ve got
only two options: Either try
enable = login

at user profile level, which will omit the secondary password query and let the user enable with his login password, or permit
anonymous enable (which is disabled by default) with
anonymous-enable = permit

in device context to use the enable passwords defined there.


• augmented-enable = ( permit | deny )
For outdated TACACS+ client implementations that send $enable$ instead of the real username in an enable request, this
will permit user specific authentication using a concatenation of username and login password, separated with a single space
character:
> enable
Password: myusername mypassword
#

enable [ level ] = login needs to be set in the users’ profile for this option to take effect.
Default: augmented-enable = deny
augmented-enable will only take effect if the NAS tries to authenticate a username matching the regex
^\$enab..\$$

(e.g.: $enable$, $enab15$). That matching criteria may be changed using an ACL:
acl custom_enable_acl { if (user =~ ^demo$) permit deny }
enable user acl = custom_enable_acl
TACACS+ NG 17 / 62

There are also experimental options for (non-standard) SSH public key authentication available. These may or may not supported
by your vender:

• ssh-key = public-ssh-key-in-OpenSSL-authorized_keys-format
Example: ssh-key = "AAAAB3NzO4S6C/SAu9E90P3n9dfbe3iNiK...STPC6V1fffa123OxmK3hhzwbl"
• ssh-key-hash = ssh-key-in-OpenSSL-format
There’s no use in specifying the hash if you’ve configured the public key, the daemon will care for that itself.
Example: ssh-key-hash = SHA256:kOkclqivcjludf/jdsfkyqpddffdk38U12+CkA8fBAC

[Link].2 User back-end options

These options are relevant for configuring the MAVIS user back-end:

• pap password [ default ] = ( login | pap )


When set to login, the PAP password default for new users will be set to use the login password.

• pap password mapping = ( login | pap )


When set to login, PAP authentication requests will be mapped to ASCII Login requests. You may wish to uses this for
NEXUS devices.
May be overridden at device level.
• user backend = mavis
Get user data from the MAVIS back-end. Without that directive, only locally defined users will be available and the MAVIS
back-end may be used for authenticating known users (with password = mavis or simlar) only.
• pap backend = mavis [ prefetch ]
Verify PAP passwords using the MAVIS back-end. This needs to be set to either mavis or prefetch in order to authenticate
PAP requests using the MAVIS back-end. If unset, the PAP password from the users’ profile will be used.
If prefetch is specified, the daemon will first retrieve the users’ profile from the back-end and then authenticate the user
based on information eventually found there.
This directive implies user backend = mavis.
• login backend = mavis [ prefetch ] [ chalresp [ noecho ] ] [ chpass ]
Verify Login passwords using the MAVIS back-end. This needs to be set to either mavis or prefetch in order to authenti-
cate login requests using the MAVIS back-end. If unset, the login password from the users’ profile will be used.
If prefetch is specified, the daemon will first retrieve the users’ profile from the back-end and then authenticate the user
based on information eventually found there.
This directive implies user backend = mavis.
For use with OPIE-enabled MAVIS modules, add the chalresp keyword (and, optionally, add noecho, unless you want the
typed-in response to display on the screen). Example:
login backend = mavis chalresp noecho

For non-local users, if the chpass attribute is set and the user provides an empty password at login, the user is given the
option to change his password. This requires appropriate support in the MAVIS back-end modules.

• mavis module = module { ... }


Load MAVIS module module. See the MAVIS documentation for configuration guidance.
• mavis path = path
Add path to the search-path for MAVIS modules.
TACACS+ NG 18 / 62

• mavis cache timeout = s


Cache MAVIS authentication data for s seconds. If s is set to a value smaller than 11, the dynamic user object is valid for the
current TACACS+ session only. Default is 120 seconds.
• mavis noauthcache
Disables password caching for MAVIS modules.
• mavis user filter = acl
Query MAVIS user back-end only if acl matches. Defaults to:
acl __internal__username_acl__ { if (user =~ "[]<>/()|=[]+") deny permit }
mavis user filter = __internal__username_acl__

[Link].3 TLS

TACACS+-over-TLS is not a standard. These features are experimental.


If compiled with OpenSSL or LibTLS support the following configuration options are available:

• tls cert-file = cert-file


Specifies the public part of a TLS server certificate in PEM format.
• tls key-file = key-file
Specifies the private part (the key) of a TLS server certificate in PEM format.
• tls passphrase = passphrase
Specifies the optional passphrase to decrypt key-file.
• tls accept expired = ( yes | no )
Accept expired certificates.
• tls verify-depth = depth
Sets TLS verification depth.
• tls cafile = cafile
Specifies a file with the CAs to use.
• tls alpn = ALPN-Protocol-ID
There’s currently no ALPN Protocol ID registered for TACACS-over-TLS, the official list is here: TLS Application-Layer
Protocol Negotiation (ALPN) Protocol IDs.
• tls auto-detect = ( yes | no )
Enable TLS auto-detection. Defaults to no.

If compiled with OpenSSL support, TLSv1.3 Preshared Keys and SNIs are supported:

• tls psk = ( yes | no )


This enables PSK support at realm level.
PSK identity and key can be declared at device level:
tls psk id = myid
tls psk key = 0123456789abcdef # in hex

• tls sni = SNI


This adds SNI to the server name list of the current realm. A TLS connection requesting SNI will automatically be mapped to
that realm.
TACACS+ NG 19 / 62

Example:
id = spawnd {
listen { port = 4949 realm = heck }
listen { port = 4950 realm = heck tls = yes }
spawn { instances min = 1 instances max = 32 }
id = tac_plus-ng {
...
realm heck {
tls cert-file = /somewhere/tac-ca/[Link]
tls key-file = /somewhere/tac-ca/[Link]
tls ca-file = /somewhere/tac-ca/[Link]
...
}
}
}

[Link] Miscellaneous

In realm context:

• haproxy auto-detect = ( yes | no )


Enable HAProxy protocol v2 auto-detection. Defaults to no.

In spawnd listen context,

• haproxy = ( yes | no )
will will tell tac_plus-ng to auto-detect that a connection is proxied via HAProxy protocol 2.
A suitable HAProxy configuration could look similar to:
frontend tacplus
bind *:49
mode tcp
default_backend backendtacplus

backend backendtacplus
balance source
server tacserver1 [Link]:4949 no-check send-proxy-v2

• tls = ( yes | no )
will tell tac_plus-ng whether the connection is TLS encrypted.
• vrf = ( vrf-name | vrf-number )
will tell spawnd listen to bind(2) to the requested VRF (vrf-name on Linux, vrf-number on OpenBSD).

Example:
id = spawnd {
...
listen {
port = 49
vrf = vrf-blue
tls = true
haproxy = true
}
....
}
TACACS+ NG 20 / 62

[Link] Realm Inheritance

Realms inherit quite some configuration from their parent realm:

Declaration of ... is taken from parent realm ...


acl if not found in current realm
dns forward mapping if not found in current realm
group if not found in current realm
device (IP lookup) if no device defined in current realm
device (name lookup) if not found in current realm
log always
mavis module if not set and no users defined in current realm
network if not found in current realm
profile if not found in current realm
ruleset if not set or undefined result in current realm
timespec if not found in current realm
user if not found in current realm
TACACS+ NG 21 / 62
TACACS+ NG 22 / 62

[Link] Railroad Diagrams

MavisDecl

warning period = days

single-connect = yes

no

mavis cache timeout = seconds

LogDecl

accouting log = logName


authorization

accounting

pap backend = mavis prefetch

password = pap

login

login backend = mavis prefetch

chalresp

chpass

password acl = aclName

warning period = seconds

connection timeout = seconds

context timeout = seconds

time zone = timezone

enable user acl = aclName

rewrite name = rewriteDecl

HostDecl

NetDecl

ACLDecl

GroupDecl

UserDecl

ruleset { rule }

realm subRealmName { RealmAttr }

parent = realmName

password max-attempts = number

password expiry warning = number

max-rounds = number

dns preload file = FileName

address = ipAddress

timeout = seconds

cache period = seconds

servers = quotedString

reverse-lookup nac nas = yes

no

message messageName = string

haproxy auto-detect = yes

no

Railroad diagram: RealmAttr


TACACS+ NG 23 / 62

aaa realm = realmName

authentication-fallback = permit

deny

period = seconds

anonymous-enable = yes

no

augmented-enable = yes

no

group realm = realmName

password max-attempts = count

backoff = seconds

acl = acl

max-rounds = count

login backend = mavis prefetch

chalresp noecho

chpass

pap backend = mavis

pap password default = login

pap
mapping

user backend = mavis prefetch

Railroad diagram: RealmAttrAuthen

[Link] Networks

Networks consist of IP addresses or other networks. They may overlap. Networks can be used in ACLs. The parent of a network
may be set either implicitly (by defining it it parent context) or explicitly.
net home {
address = [Link]/23
net dev {
address = [Link]
}
parent = ...

[Link].1 Railroad Diagrams

net netName { NetAttr }

Railroad diagram: NetDecl


TACACS+ NG 24 / 62

address = CIDR

file = filePath

net subNetName = { NetAttr }

parent = netName

Railroad diagram: NetAttr

[Link] Devices (Hosts)

The daemon will talk to known NAS addresses only. Connections from unknown addresses will be rejected.
If you want tac_plus-ng to encrypt its packets (and you almost certainly do want this, as there can be usernames and passwords
contained in there), then you’ll have to specify an (non-empty) encryption key. The identical key must also be configured on any
NAS which communicates with tac_plus.
To specify a global key, use a statement similar to
device world4 {
key = "your key here"
address = [Link]/0
}

(where world is not a keyword, but just some arbitrary character string).

Double Quotes
You only need double quotes on the daemon if your key contains spaces. Confusingly, even if your key does contain spaces,
you should never use double quotes when you configure the matching key on the NAS.
The daemon will reject connections from devices that have no encryption key defined.
Double quotes within double-quoted strings may be escaped using the backslash character \ (which can be escaped by itself),
e.g.:
key = "quo\\te me\"."

translates to the ASCII sequence


quo\te me".

Any CIDR range within a device definition needs to to be unique, and the most specific definition will match. The requirement
for unambiguousness is quite simply based on the fact that certain device object attributes (key, prompt, enable passwords) may
only exist once.
If compiled with TLS support, primary criteria for device object selection with TLS is no longer the NAS IP address but the certifi-
cate DNS SANs (OpenSSL only), the subject and/or the common name. E.g., CN=[Link],OU=org,OU=loca
will check for device objects named CN=[Link],OU=org,OU=local, OU=org,OU=local, OU=local
and then for [Link], [Link] and demo before falling back to IP based selection.
On the NAS, you also need to configure the same key. Do this by issuing the current variant of:
aaa new-model
tacacs-server host [Link] single-connection key your key here

The optional single-connection parameter specifies that multiple sessions may use the same TCP/IP connection to the
server.
Generally, the syntax for device declarations conforms to
device name { key-value pairs }
The key-value pairs permitted in device sections of the configuration file are explained below.
TACACS+ NG 25 / 62

• key [warn] ( YYYY-MM-DD | s ) ] = string


This sets the key used for encrypting the communication between server and NAS. Multiple keys may be set, making key
migration from one key to another pretty easy. If the warn keyword is specified, a warning message is logged when a NAS
actually uses the key. Optionally, the warn keyword accepts a date argument that specifies when the warnings should start to
appear in the logs.
During debugging, it may be convenient to temporarily switch off encryption by using an empty key:
key = ""

Be careful to remember to switch encryption back on again after you’ve finished debugging.
• address = cidr
Adds the address range specified by cidr to the current device definition.
internetAddress / maskLen

internetMask

Railroad diagram: CIDR


• address file = file
Add the addresses from file to the current device definition. Shell wildcard patterns are expanded by glob(3).
• single-connection ( may-close ) = ( yes | no )
This directive may be used to permit or deny the single-connection feature for a particular device object. The may-close
keyword tells the daemon to close the connection if it’s unused.

Caveat Emptor
There’s a slight chance that single-connection doesn’t work as expected. The single-connection implementation in your router
or even the one implemented in this daemon (or possibly both) may be buggy. If you’re noticing weird AAA behaviour that
can’t be explained otherwise, then try disabling single-connection on the router.

This configuration will be accepted at realm level, too.

• parent = deviceName
This sets the the parent devices. Definitions not found in the current device will be looked up there, recursively.
• device deviceName { DeviceAttr }
Devices can be defined in device context, too.

• script { tacAction }
Scripts can be used in device context. These are run before AAA and mey be used to permit or deny access, or to rewrite
usernames.
This configuration will be accepted at realm level (for the default host, too.

[Link].1 Timeouts

The connection timeout may be specified:

• connection timeout = s
Terminate a connection to this NAS after an idle period of at least s seconds. Defaults to the global option.
TACACS+ NG 26 / 62

[Link].2 Authentication

The following authentication related directives are available at device object level:

• pap password mapping = ( login | pap )


When set to login, PAP authentication requests will be mapped to ASCII Login requests. You may wish to uses this for
NEXUS devices.

• enable [ level ] = ( permit | deny | login | ( clear | crypt) password )


This directive may be used to set device specific enable passwords, to use the login password, or to permit (without password)
or refuse any enable attempt. level defaults to 15.
Enable passwords specified at device level have a lower precedence as those defined at user or profile level.

Password Hashes
You can use the openssl passwd utility to compute password hashes.

You can enable via TACACS+ by configuring on the NAS:


aaa authentication enable default group tacacs+ enable

• anonymous-enable = ( permit | deny )


Several broken TACACS+ implementations send no or an invalid username in enable packets. Setting this option to deny
enforces user authentication before enabling. Setting this option here has precedence over the global option.
This configuration will be accepted at realm level, too.
• augmented-enable = ( permit | deny )
For TACACS+ client implementations that send $enable$ instead of the real username in an enable request, this will permit
user specific authentication using a concatenation of username and login password, separated with a single space character.
Setting this option here has precedence over the global option.
enable [ level ] = login needs to be set in the users’ profile for this option to take effect.
This configuration will be accepted at realm level, too.
• password max-attempts = integer
The max-attempts parameter limits the number of Password: prompts per TACACS+ session at login. It currently
defaults to 1.
This configuration will be accepted at realm level, too.
• password expiry warning = number
A password expiry warning will be displayed to the user if less than number time is left. Appending s (that’s the default) or
any of m, h, d, w will scale the number up as expeced.
This configuration will be accepted at realm level, too.

[Link].3 Authorization

The following authorization related directives are available at device object level:

• permit if-authenticated = ( yes | no )


This will cause authorization for users unknown to the daemon to succeed (e.g. when logging in locally while the daemon is
down or while initially configuring TACACS+ support and messing up).
This configuration will be accepted at realm level, too.
TACACS+ NG 27 / 62

[Link].4 Banners and Messages

The daemon allows for various banners to be displayed to the user:

• welcome banner ( fallback ) = string


• motd banner = string
• reject banner = string
The reject banner gets displayed in place of the welcome message if a connection was rejected by an access ACL
defined at device, user or group level.
These configurations will be accepted at realm level, too.
• failed authentication banner = string
The failed authentication banner gets displayed upon final failure of an authentication attempt.
• message = string

The time when those texts get displayed largely depends on the actual login method:

Context Directive Telnet SSHv1 SSHv2


displayed before displayed before
device welcome banner not displayed
Username: Password:
displayed before
device reject banner not displayed not displayed
closing connection
displayed after displayed after
device motd banner not displayed
successful login successful login
displayed after displayed after
user or group message not displayed
motd banner motd banner

Neither the motd banner nor a message defined in the users’ profile will be displayed if hushlogin is set for the user.
Both banners and messages support the same conversions as logs, unless specified as user level.
Example:
device ... {
...
welcome banner = "Welcome. Today is %A.\n"
...
}

[Link].5 Workarounds for Client Bugs

The directive
bug compatibility = value
may improve compatibility with clients that violate the TACACS+ protocol. Currently, the following bit values (yes, you can use
bitwise OR here) are recognized:

Bit Value Description


According to RFC8907 the data field should be ignored for ASCII authentications. Alas, IOS-XR puts
0 1
the password exactly there. Set this if required.
1 2 Accept version 1 for authorization and accounting packets, seen with Palo Alto systems.
Accept key-based packet obfuscation for TLS (this violates
2 4
[Link]).
TACACS+ NG 28 / 62

Bit Value Description


3 8 Accept TACACS+ payloads lower than advertized in the TACACS+ header.

Example:
device ... {
...
bug compatibility = 2
...
}

This configuration will be accepted at realm level, too.

[Link].6 Inheritance and Hosts

For address based device lookups, the daemon looks for the most specific device definition. Values that aren’t defined (if any)
will be lookup up in the device’s parent, which may be either set implicitely by defining a device in the context of it’s parent
device, or expliitely, using the parent statement.

[Link].7 Railroad Diagrams

device deviceName { DeviceAttr }

Railroad diagram: DeviceDecl


TACACS+ NG 29 / 62

EnableExpr

key warn yyyy-mm-dd = string

secondsSinceTheEpoch

welcome banner fallback = string

motd banner = string

failed authentication banner = string

address = CIDR

file = filePath

single-connect may-close = yes

no

connection timeout = seconds

session timeout = seconds

ccntext timeout = seconds

authentication fallback = permit

deny

pap password mapping = login

pap

anonymous-enable = permit

deny

device subDeviceName { DeviceAttr }

parent = devicName

password max-attempts = number

password expiry warning = number

max-rounds = number

script { tacAction }

dns timeout = seconds

reverse-lookup nac nas yes

no

message messageName = string

tag = string

target-realm = realm

mavis backend = yes

no

Debug

Railroad diagram: DeviceAttr

enable level = clear cleartextPassword

7 obscuredPassword

crypt hashedPassword

login

permit

deny

Railroad diagram: EnableExpr


TACACS+ NG 30 / 62

[Link].8 Example

device = customer1 {
address = [Link]/8
key = "your key here"
welcome banner = "\nHitherto shalt thou come, but no further. (Job 38.11)\n\n"
enable 15 = clear whatever
}

device = test123 {
address = [Link]/28
address = [Link]/28
address = [Link]
# key/banners/enable will be inherited from [Link]/8 by default,
# unless you specify "inherit = no"
address file = /some/path/[Link]
welcome banner = "\nGo away.\n\n"
}

[Link] Time Ranges

timespec objects may be used for time based profile assignments. Both cron and Taylor-UUCP syntax are supported; see
you local crontab(5) and/or UUCP man pages for details. Syntax:
timespec = timespec_name { "entry" [ ... ] }
Example:
# Working hours are from Mo-Fr from 9 to 16:59, and
# on Saturdays from 9 to 12:59:
timespec workinghours {
"* 9-16 * * 1-5" # or: "* 9-16 * * Mon-Fri"
"* 9-12 * * 6" # or: "* 9-12 * * Sat"
}

timespec sunday { "* * * * 0" }

timespec example {
Wk2305-0855,Sa,Su2305-1655
Wk0905-2255,Su1705-2255
Any
}

[Link].1 Railroad Diagrams

timespec = timespecName { cronStyleTime }

uucpStyleTime

Railroad diagram: TimespecDecl

[Link] Access Control Lists

Access Control Lists (or, more exactly, Access Control Scripts) are the main component of ruleset evaluation.
Scripts may currently be used for ACLs, in host and profile declaration scope and in rule sets. If a script in a hierarchy doesn’t
return a final verdict (these are permit and deny), other scripts in the hierarchy may be evaluated. Default evaluation order is
TACACS+ NG 31 / 62

script-order host = bottom-up


script-order realm = bottom-up
script-order profile = bottom-up

but you may prefer to change that to top-down to have parent scripts executed first.
To provide an example for that: In
profile A {
script { ... }
profile B {
script { ... }
}

the script part from A will by default (bottom-up be evaluated, if the B script result isn’t final.
In contrast, for
script-order profile = top-down
profile A {
script { ... }
profile B {
script { ... }
}

the A part takes precedence and the B script will one be evaluated if the A result isn’t final.
skip parent-script = yes may be used (at profile, host and realm level) to ignore scripts defined at a higher hierarchy
level.
Scripting examples:

• acl acl_name { tac_action ... }


Example:
acl myacl123 {
if (nas == [Link] || nac = SomeHostName || nac-dns =~ /\\.example\\.com$/) deny
}

• script = { tac_action ... }


Example:
profile tunnelAdmin {
script {
if (service == shell) {
if (cmd == "") permit # required for shell startup
if (cmd =~ /^(no\s)?shutdown\s/) permit
if (cmd =~ /^interface Tunnel/) permit
deny
}
}
}

user joe {
password = ...
member = ops
}
ruleset {
rule opsRule {
script {
if (group == ops)
TACACS+ NG 32 / 62

profile = tunnelAdmin
permit
}
}
}

[Link].1 Syntax

A script consists of a series of actions:


if TacCond TacAction else TacAction

context = string

label = string

message = string

profile = profileName

rewrite user = rewriteRuleName

permit

deny

return

{ TacAction }

Railroad diagram: TacAction


The actions return, permit and deny are final. At the end of a script, return is implied, at which the daemon continues
processing the configured cmd statements in shell context) or standard ACLs (in ACL context). The assignment operations
(context =, message =) do make sense in shell context only.
Setting the context variable makes sense in shell context only. See the example in the corresponding section.
Attribute-related directives are:

• default attribute = ( permit | deny )


This directive specifies whether the daemon is to accept or reject unknown attributes sent by the NAS (default: deny).

• ( set | add | optional ) attribute = value


Defines mandatory and optional attribute-value pairs:
– set unconditionally returns a mandatory AV pair to the NAS
– optional returns a NAS-requested (and perhaps modified) optional AV pair to the NAS unless the attribute was already
in the mandatory list
– add returns an optional AV pair to the client even if the client didn’t request it (and it was neither in the mandatory nor
optional list)
Example:
set priv-lvl = 15

For a detailed description on mandatory and optional AV-pairs, see the "The Authorization Algorithm" section somewhere
below.
TACACS+ NG 33 / 62

Numbered Attributes
A %%d added to an attribute will will result in a numbered attribute, starting to count at 1 (%%n would start counting at 0). For
example,
set route#%%d = "[Link] [Link] [Link]"
set route#%%d = "[Link] [Link] [Link]"
set route#%%d = "[Link] [Link] [Link]"

results in
set route#1 = "[Link] [Link] [Link]"
set route#2 = "[Link] [Link] [Link]"
set route#3 = "[Link] [Link] [Link]"

Variables
The same variables supported for logging can be used as attribute values, too. Example: set uid = "${uid}"

• return
Use the current service definition as-is. This stops the daemon from checking for the same service in the groups the current
user (or group) is a member of.

Conditions:

Left-hand side Operators Right-hand side Comment


== !=
"string" String or REGEX Log variable substitutions will apply
=~ !~
acl == != ACL object name
== !=
arg[attr] String or REGEX arg[protocol], arg[service], ...
=~ !~
== !=
authen-action String or REGEX login, chpass
=~ !~
== !=
authen-method String or REGEX login, enable, ppp
=~ !~
== !=
authen-service String or REGEX none, line, enable, local, tacacs+
=~ !~
== !=
authen-type String or REGEX ascii, pap, chap, mschap, mschapv2, sshkey, sshcer
=~ !~
== != Net object name or IP
client Net incl. parents
=~ !~ address or string.
[Link] == != Client remote address
== !=
[Link] Client remote address
=~ !~
== != Client DNS PTR
[Link]
=~ !~ record
== !=
cmd String or REGEX shell command line
=~ !~
== !=
context String or REGEX current exec context
=~ !~
Device (host) object
device == != name, net object name incl. parents
or device address
== != Device (host) or net
[Link] incl. parents
=~ !~ object name
== !=
[Link] Device (host) address
=~ !~
TACACS+ NG 34 / 62

Left-hand side Operators Right-hand side Comment


== != Device (host) DNS
[Link]
=~ !~ PTR record
== !=
[Link] Device (host) tag
=~ !~
== !=
dn String or REGEX MAVIS dn attribute
=~ !~
== !=
identity-source String or REGEX authenticating/authorizing MAVIS module
=~ !~
== !=
member String or REGEX group membership
=~ !~
== !=
memberof String or REGEX MAVIS memberOf attribute
=~ !~
[deprecated, use
== != net object name (incl. parents), client remote address
nac client] String or
=~ !~ (rem_addr)
REGEX
[deprecated, use
== !=
nac-name [Link]] client DNS PTR
=~ !~
String or REGEX
[deprecated, use
== != device or net object name (incl. parents), matches NAC
nas device] String or
=~ !~ remote address (rem_addr)
REGEX
[deprecated, use
== !=
nas-name [Link]] NAS/NAD DNS PTR
=~ !~
String or REGEX
== !=
password String or REGEX session user password
=~ !~
[deprecated, use
== !=
port [Link]] session port (vty02, console, ...)
=~ !~
String or REGEX
== !=
priv-lvlp String or REGEX session privilege level (0 ... 15) reported by the device
=~ !~
== !=
protocol String or REGEX session protocol (ppp, ...)
=~ !~
realm == != Realm object name
== !=
[Link] Server address
=~ !~
== !=
[Link] Server host name
=~ !~
== !=
[Link] Server TCP port
=~ !~
== !=
service String or REGEX session service (shell, ...)
=~ !~
time == != Timespec object name
== !=
[Link] String or REGEX TLS specific data
=~ !~
== !=
[Link] String or REGEX TLS specific data
=~ !~
== !=
[Link] String or REGEX TLS specific data
=~ !~
== !=
[Link] String or REGEX TLS specific data
=~ !~
== !=
[Link] String or REGEX TLS specific data
=~ !~
== !=
[Link] String or REGEX TLS specific data
=~ !~
TACACS+ NG 35 / 62

Left-hand side Operators Right-hand side Comment


== !=
[Link] String or REGEX TLS specific data
=~ !~
== !=
type String or REGEX authen, author, acct
=~ !~
== !=
user String or REGEX session user
=~ !~

Railroad diagrams for conditions:


TACACS+ NG 36 / 62

! TacCond

( TacCond )

( TacCond && TacCond )

||

cmd == value
context !=

port =~ regEx
user !~

[Link]

password

vrf

type == authen

!= author

acct

device == value
!=

=~ regEx
!~

nac == netName
!=

nas == deviceName
!=
netName

nac-name =~ regEx
nas-name !~

time == timespecName
!=

acl == aclName
!=

realm == realmName
!=

=~ regEx
!~

member == == value
!= !=

=~ regEx
!~

dn == value
memberof !=

=~ regEx
!~

attr[ attrName ] == value


!=

=~ regEx
!~

authen-action authen-type authen-service authen-method == string


!=

[Link] == value
[Link] !=

[Link] =~ regEx
[Link] !~

[Link]

[Link]

[Link]
TACACS+ NG 37 / 62

cmd and context may be used in shell context only. tls_* conditions require libtls.

[Link] Rewriting User Names

A script may refer to a rewrite profile defined at realm level to rewrite user names. For example, the following will map both
admin and root to [Link], and convert all other usernames to lower-case:
rewrite rewriteRule {
rewrite /^admin$/ [Link]
rewrite /^root$/ [Link]
rewrite /^.*$/ \L$0
}

device ... {
...
script { rewrite user = rewriteRule }
...
}

You can limit the usage of a rewritten-to user with the rewritten-only directive, e.g.:
rewrite rewriteRule {
rewrite /^.*$/ nopassword
}
user nopassword {
password login = permit
password pap = login
member = ...
rewritten-only
}

[Link] Users

The basic form of a user declarations is


user username { ... }

A user or group declaration may contain key-value pairs and service declarations.
The following declarations are valid in user context only:

• password login [ fallback ] = ( ( clear | crypt ) password | mavis | permit | deny )


The login password authenticates shell log-ins to the server.
password login = crypt aFtFBT4e5muQE
password login = clear Ci5c0

For the argument after crypt you may use whatever hashes your crypt(3) implementation supports.
If the mavis keyword is used instead, the password will be looked up via the MAVIS back-end. It will not be cached. This
functionality may be useful if you want to authenticate at external systems, despite static user declarations in the configuration
file.
If you’re using password login = mavis, thefallback password will be used if there’s a MAVIS backend error.
• password pap [ fallback ] = ( ( clear | crypt ) password | login|mavis | permit | deny )
The pap authenticates PAP log-ins to the server. Just like with login, the password doesn’t need to be in clear text, but may
be hashed, or may be looked up via the MAVIS back-end. You can even map pap to login globally by configuring password
pap = login in realm context.
If you’re using password pap = mavis, thefallback password will be used if there’s a MAVIS backend error.
TACACS+ NG 38 / 62

• password chap = ( clear password | permit | deny )


For CHAP authentication, a cleartext password is required.
• password ms-chap = ( clear password | permit | deny )
For MS-CHAP authentication, a cleartext password is required.
• password { ... }
This directive allows for nested specification of passwords. Example:
user marc {
password {
login = clear myLoginPassword
pap = clear myPapPassword
}
}

• enable [ level ] = ( permit | deny | login | ( clear | crypt ) password )


This directive may be used to set user specific enable passwords, to use the login password, or to permit (without password)
or refuse any enable attempt. Enable secrets defined at user level have precedence over those defined at device level. level
defaults to 15.
The default privilege level for an ordinary user on the NAS is usually 1. When a user enables, she can reset this level to a value
between 0 and 15 by using the NAS enable command. If she doesn’t specify a level, the default level she enables to is 15.
• message = string
A message displayed to the user upon log-in.

• hushlogin = ( yes | no )
Setting hushlogin to yes keeps the daemon from displaying motd and user messages upon login.
• valid from = ( YYYY-MM-DD | s )
The user profile will be valid starting at the given date, which can be specified either in ISO8601 date format or as in seconds
since January 1, 1970, UTC.
• valid until = ( YYYY-MM-DD | s )
The user profile will be invalid after the given date.
• member = groupOne[,groupTwo]*
This specifies group membership. A user can be a member of multiple groups and groups can be members of a parent group.

[Link].1 Railroad Diagrams

user userName { UserAttr }

GroupAttr

ShellDecl

ServiceDecl

Railroad diagram: UserDecl


TACACS+ NG 39 / 62

groupName

member = groupName ,

EnableExpr

password login fallback = PasswordExprHash


pap

chap = PasswordExpr
ms-chap

password = PasswdExpr

password { login = PasswordExprHash }

pap

chap = PasswordExpr
ms-chap

Railroad diagram: ServiceDecl

[Link] Groups

A user can be a member of multiple groups. A user that is a member of a group that comes with a parent group is a member of
the latter, too. Group are defined using
group groupname { ... }

The following key-value pairs are valid for groups:

• member = groupOne[,groupTwo]*
This specifies group membership.
• parent = groupName
The parent of a group can be set explicitly.
• group groupName { GroupAttr }
Groups may be parents of other groups.

[Link].1 Railroad Diagrams

group groupName { GroupAttr }

Railroad diagram: GroupDecl


GroupDecl

parent = groupName

groupName

member = groupName ,

Railroad diagram: GroupAttr

[Link] Profiles

Profiles are collections of services that can be assigned to users via the policy rule-set. Syntax is
profile profileName { profileAttr }
TACACS+ NG 40 / 62

Profiles are collections of services available to a user. A couple of configuration attributes are service specific and only valid in
certain contexts:
SHELL (EXEC) Service
Shell startup should have an appropriate script definition
script {
if (service == "shell" && cmd == "")
permit
}

defined. Valid configuration directive within the curly brackets are:

• script { tacAction }
Commands can be permitted or denied using script syntax:
script {
if (service == "shell" && cmd == "")
permit
if (cmd =~ /^write term/) deny
if (cmd =~ /^configure /) deny
permit
}

• profile SubProfileName { ... }


This defines a new profile that inherits most values from its parent profile.
• parent = ParentProfileName
This sets ParentProfileName as parent profile. Parent profiles defined in parent realms are accepted, too.

Have a look at the authorization log in case you’re unsure what commands and arguments the router actually sends for verification.
E.g.,
Non-Shell Services
E.g. for PPP, protocol definitions may be used:
script {
if (service == "ppp" && protocol == "ip") {
set addr = [Link]
permit
}
}

The historical
default protocol = permit

will no longer be recognized but can be replaced with a simple


script {
permit
}

For a Juniper Networks-specific authorization service, use:


script {
if (service == junos-exec) {
set local-user-name = NOC
# see the Junos documentation for more attributes
}
}
TACACS+ NG 41 / 62

Likewise, for Raritan Dominion SX IP Console Servers:


script {
if (service == dominionsx) {
set port-list = "1 3 4 15"
set user-type = administator # or operator, or observer
}
}

Quotes
If your router expects double-quoted values (e.g. Cisco Nexus devices do), you can advise the parser to automatically add
these:
set shell:roles = "\"network-admin\""

and
set shell:roles = ’"network-admin"’
}

are equivalent, but the latter is more readable.

[Link] Railroad Diagrams

message permit = string


deny

debug

Railroad diagram: UserMessage


profile profileName { ProfileAttr }

Railroad diagram: ProfileDecl

EnableExpr

hushlogin = profile subProfileName { ProfileAttr } parent = profileName permit

deny

TacScript

Debug

Railroad diagram: ProfileAttr


PasswdExpr

login

crypt hashedPassword

Railroad diagram: PasswordExprHash


TACACS+ NG 42 / 62

alias name { configuration-snippets }

dns address CIDR = string

prefetch file = fileName

proctitle = string

umask = umask

retire limit = count

seconds = seconds

setenv name string

syslog level = EMERG

others

DEBUG

facility = AUTH

others

FTP

ident = string

default = seconds yes

no

Railroad diagram: TopLevelAttr

number

ALL

ACCT

ACL

AUTHEN

AUTHOR

CMD

CONFIG

HEX

LOG

LWRES

NONE

PACKET

PARSE

REGEX

USERINPUT

Railroad diagram: Debug


acl = not aclName

Railroad diagram: Acl

service = serviceName @ deviceName { }

ServiceAttr

Railroad diagram: ServiceDecl


TACACS+ NG 43 / 62

AttrDefault

ProtoDefault

ProtoDecl

Acl

AVPair

UserMessage

return

Railroad diagram: ServiceAttr


default attribute = permit

deny

Railroad diagram: AttrDefault


set add optional attribute = value

Railroad diagram: AVPair


service Acl = shell @ deviceName { }

ShellAttr

Railroad diagram: ShellDecl

AttrDefault

CmdDefault

Acl

AVPair

TacScript

return

Railroad diagram: ShellAttr

script { TacAction }

Railroad diagram: TacScript

cmd = command { permit commandArgRegex }

deny

UserMsg

Railroad diagram: ShellCommandDecl


default protocol = permit

deny

Railroad diagram: ProtoDefault


TACACS+ NG 44 / 62

protocol = protocolName

{ AttrDefault }

Acl

AVPair

UserMessage

Railroad diagram: ProtoDecl

[Link] Configuring Non-local Users via MAVIS

MAVIS configuration is optional. You don’t need it if you’re content with user configuration in the main configuration file.
MAVIS back-ends may dynamically create user entries, based, e.g., on LDAP information.
For PAP and LOGIN,
pap backend = mavis
login backend = mavis

in the global section delegate authentiation to the MAVIS sub-system. Statically defined users are still valid, and have a higher
precedence.
By default, MAVIS user data will be cached for 120 seconds. You may change that period using
cache timeout = seconds

in the global configuration section.

[Link] Configuring Local Users for MAVIS authentication

Under certain circumstances you may wish to keep the user definitions in the plain text configuration file, but authenticate against
some external system nevertheless, e.g. LDAP or RADIUS. To do so, just specify one of
login = mavis
pap = mavis
password = mavis

in the corresponding user definition.

[Link] Configuring User Authentication

User Authentication can be specified separately for PAP, CHAP, and normal logins. CHAP and global user authentication must
be given in clear text.
The following assigns the user mary five different passwords for inbound and outbound CHAP, inbound PAP, outbound PAP, and
normal login respectively:
user mary {
password chap = clear "chap password"
password pap = clear "inbound pap password"
password login = crypt XQj4892fjk
}

If
user backend = mavis
TACACS+ NG 45 / 62

is configured in the global section, users not found in the configuration file will be looked up by the MAVIS back-end. You
should consider using this option in conjuction with the more sophisticated back-ends (LDAP and ActiveDirectory, in particular),
or whenever you’re not willing to duplicate your pre-existing database user data to the configuration file. For users looked up by
the MAVIS back-end,
pap backend = mavis

and/or
login backend = mavis

(again, in the global section of the configuration file) will cause PAP and/or Login authentication to be performed by the MAVIS
back-end (e.g. by performing an LDAP bind), ignoring any corresponding password definitions in the users’ profile.
If you just want the users defined in your configuration file to authenticate using the MAVIS back-end, simply set the corre-
sponding PAP or Login password field to mavis (there’s no need to add the user backend = mavis directive in this
case):
user mary { login = mavis }

[Link] Configuring Expiry Dates

An entry of the form:


user lol {
valid until = YYYY-MM-DD
password login = clear "bite me"
}

will cause the user profile to become invalid, starting after the valid until date. Valid date formats are both ISO8601 and
the absolute number of seconds since 1970-01-01.
A expiry warning message is sent to the user when she logs in, by default starting at 14 days before the expiration date, but
configurable via the warning period directive.
Complementary to profile expiry,
valid from = YYYY-MM-DD

activates a profile at the given date.

[Link] Configuring Authentication on the NAS

On the NAS, to configure login authentication, try


aaa new-model
aaa authentication login default group tacacs+ local

(Alternatively, you can try a named authentication list instead of default. Please see the IOS documentation for details.)
TACACS+ NG 46 / 62

Don’t lock yourself out.


As soon as you issue this command, you will no longer be able to create new logins to your NAS without a functioning
TACACS+ daemon appropriately configured with usernames and password, so make sure you have this ready.
As a safety measure while setting up, you should configure an enable secret and make it the last resort authentication
method, so if your TACACS+ daemon fails to respond you will be able to use the NAS enable password to login. To do
this, configure:
aaa authentication login default group tacacs+ enable

or, to if you have local accounts:


aaa authentication login default group tacacs+ local

If all else fails, and you find yourself locked out of the NAS due to a configuration problem, the section on recovering
from lost passwords on Cisco’s CCO web page will help you dig your way out.

[Link] Configuring Authorization

Authorization must be configured on both the NAS and the daemon to operate correctly. By default, the NAS will allow every-
thing until you configure it to make authorization requests to the daemon.
On the daemon, the opposite is true: The daemon will, by default, deny authorization of anything that isn’t explicitly permitted.
Authorization allows the daemon to deny commands and services outright, or to modify commands and services on a per-user
basis. Authorization on the daemon is divided into two separate parts: commands and services.

[Link] Authorizing Commands

Exec commands are those commands which are typed at a NAS exec prompt. When authorization is requested by the NAS, the
entire command is sent to the tac_plus daemon for authorization.
Command authorization is configured by telling the ruleset to apply a profile to the user. See the Profile section for details.

[Link] The Authorization Process

Authorizing a single session can result in multiple requests being sent to the daemon. For example, in order to authorize a dialin
PPP user for IP, the following authorization requests will be made from the NAS:

1. An initial authorization request to startup PPP from the exec, using the AV pairs service=ppp, protocol=ip, will
be made (Note: this initial request will be omitted if you are autoselecting PPP, since you won’t know the username yet).
This request is really done to find the address for dumb PPP (or SLIP) clients who can’t do address negotiation. Instead,
they expect you to tell them what address to use before PPP starts up, via a text message e.g. "Entering PPP. Your address
is [Link]". They rely on parsing this address from the message to know their address.
2. Next, an authorization request is made from the PPP subsystem to see if PPP’s LCP layer is authorized. LCP parameters
can be set at this time (e.g. callback). This request contains the AV pairs service=ppp, protocol=lcp.

3. Next an authorization request to startup PPP’s IPCP layer is made using the AV pairs service=ppp, protocol=ipcp.
Any parameters returned by the daemon are cached.
4. Next, during PPP’s address negotiation phase, each time the remote peer requests a specific address, if that address isn’t in
the cache obtained in step 3, a new authorization request is made to see if the peers requested address is allowable. This
step can be repeated multiple times until both sides agree on the remote peer’s address or until the NAS (or client) decide
they’re never going to agree and they shut down PPP instead.
TACACS+ NG 47 / 62

[Link] Authorization Relies on Authentication

Since we pretty much rely on having a username in authorization requests to decide which addresses etc. to hand out, it is
important to know where the username for a PPP user comes from. There are generally 2 possible sources:

1. You force the user to authenticate by making her login to the exec and you use that login name in authorization requests.
This username isn’t propagated to PPP by default. To have this happen, you generally need to configure the if-needed
method, e.g.
aaa authentication login default tacacs+
aaa authentication ppp default if-needed

2. Alternatively, you can run an authentication protocol, PAP or CHAP (CHAP is much preferred), to identify the user. You
don’t need an explicit login step if you do this (so it’s the only possibility if you are using autoselect). This authentication
gets done before you see the first LCP authorization request of course. Typically you configure this by doing:
aaa authentication ppp default tacacs+
int async 1
ppp authentication chap

If you omit either of these authentication schemes, you will start to see authorization requests in which the username is missing.

[Link] Configuring Service Authorization

A list of AV pairs is placed in the daemon’s configuration file in order to authorize services. The daemon compares each NAS
AV pair to its configured AV pairs and either allows or denies the service. If the service is allowed, the daemon may add, change
or delete AV pairs before returning them to the NAS, thereby restricting what the user is permitted to do.

[Link].1 The Authorization Algorithm

The complete algorithm by which the daemon processes its configured AV pairs against the list the NAS sends, is given below.
Find the user (or group) entry for this service (and protocol), then for each AV pair sent from the NAS:

1. If the AV pair from the NAS is mandatory:


(a) look for an exact attribute,value match in the user’s mandatory list. If found, add the AV pair to the output.
(b) If an exact match doesn’t exist, look in the user’s optional list for the first attribute match. If found, add the NAS AV
pair to the output.
(c) If no attribute match exists, deny the command if the default is to deny, or,
(d) If the default is permit, add the NAS AV pair to the output.
2. If the AV pair from the NAS is optional:
(a) look for an exact attribute,value match in the user’s mandatory list. If found, add DAEMON’s AV pair to output.
(b) If not found, look for the first attribute match in the user’s mandatory list. If found, add DAEMON’s AV pair to
output.
(c) If no mandatory match exists, look for an exact attribute,value pair match among the daemon’s optional AV pairs. If
found add the DAEMON’s matching AV pair to the output.
(d) If no exact match exists, locate the first attribute match among the daemon’s optional AV pairs. If found add the
DAEMON’s matching AV pair to the output.
(e) If no match is found, delete the AV pair if the default is deny, or
(f) If the default is permit add the NAS AV pair to the output.
3. After all AV pairs have been processed, for each mandatory DAEMON AV pair, if there is no attribute match already in
the output list, add the AV pair (but add only ONE AV pair for each mandatory attribute).
4. After all AV pairs have been processed, for each optional unrequested DAEMON AV pair, if there is no attribute match
already in the output list, add that AV pair (but add only ONE AV pair for each optional attribute).
TACACS+ NG 48 / 62

4.3 MAVIS Backends

The distribution comes with various MAVIS modules, of which the external module is probably the most interesting, as it interacts
with simple Perl scripts to authenticate and authorize requests. You’ll find sample scripts in the mavis/perl directory. Have a
close look at them, as you may (or will) need to perform some trivial customizations to make them match your local environment.
You should really have a look at the MAVIS documentation. It gives examples for RADIUS and PAM authentication, too.

4.3.1 LDAP Backends

mavis_tacplus_ldap.pl is an authentication/authorization back-end for the external module. It interfaces to various


kinds of LDAP servers, e.g. OpenLDAP, Fedora DS and Active Directory. Its behaviour is controlled by a list of environmental
variables:

Variable Description
One of: generic, tacacs_schema, microsoft.
LDAP_SERVER_TYPE
Default: tacacs_schema
Space-separated list of LDAP URLs or IP addresses or device names
LDAP_HOSTS Examples:
"ldap01 ldap02", "ldaps://ads01:636 ldaps://ads02:636"
LDAP search scope (base, one, sub)
LDAP_SCOPE
Default: sub
Base DN of your LDAP server
LDAP_BASE
Example: dc=example,dc=com
LDAP search filter. Defaults:
• for LDAP_SERVER_TYPE=generic:
"(uid=%s)"

LDAP_FILTER • for LDAP_SERVER_TYPE=tacacs_schema:


"(&(uid=%s)(objectClass=tacacsAccount))"
• for LDAP_SERVER_TYPE=microsoft:
"(&(objectclass=user)(sAMAccountName=%s))"

LDAP search filter for password changes. Defaults:


• for LDAP_SERVER_TYPE=generic:
"(uid=%s)"

LDAP_FILTER_CHPW • for LDAP_SERVER_TYPE=tacacs_schema:


"(&(uid=%s)(objectClass=tacacsAccount)(!(tacacsFlag=staticpas
• for LDAP_SERVER_TYPE=microsoft:
"(&(objectclass=user)(sAMAccountName=%s))"

User to use for LDAP bind if server doesn’t permit anonymous searches.
LDAP_USER
Default: unset
Password for LDAP_USER
LDAP_PASSWD
Default: unset
An AD group starting with this prefix will be used as the user’s TACACS+ group
membership. The value of AD_GROUP_PREFIX will be stripped from the group
name.
AD_GROUP_PREFIX
Example: With AD_GROUP_PREFIX set to tacacs (which is actually the
default), an AD group membership of TacacsNOC will assign the user to the NOC
TACACS+ group. Note that TACACS+ group names are case-sensitive.
TACACS+ NG 49 / 62

Variable Description
If set, user needs to be in one of the AD_GROUP_PREFIX groups.
REQUIRE_AD_GROUP_PREFIX
Default: unset
If set, the server is required to support start_tls.
USE_TLS
Default: unset
This sets options for use with Net::LDAP start_tls and LDAPS, in Perl syntax.
Details can be found in the Net::LDAP documentation.
Default: unset
TLS_OPTIONS Example:
setenv TLS_OPTIONS = "sslversion => ’tlsv1_3’"

Permit password changes via this back-end.


FLAG_CHPW
Default: unset
Try to enforce a simplicistic password policy.
FLAG_PWPOLICY
Default: unset
Keep connection to LDAP server open.
FLAG_CACHE_CONNECTION
Default: unset
If searching for the user in LDAP fails, try the next MAVIS module (if any).
FLAG_FALLTHROUGH
Default: unset
Use the memberOf attribute for determining group membership. Setting
LDAP_SERVER_TYPE to microsoft implies this. May be used if you’re
FLAG_USE_MEMBEROF
running OpenLDAP with memberof overlay enabled.
Default: unset

[Link] LDAP Custom Schema Backend

For LDAP_SERVER_TYPE set to tacacs_schema, the program expects the LDAP server to support the experimental [Link]
included for OpenLDAP and Fedora-DS. The schema files are located in the mavis/perl directory.
The new schema allows for a auxiliary object class
objectClass: tacacsAccount

which introduces a couple of new attributes. A sample user entry could then look similar to the following LDIF snippet:
dn: uid=marc,ou=people,dc=example,dc=com
uid: marc
cn: Marc Huber
objectClass: posixAccount
objectClass: inetOrgPerson
objectClass: shadowAccount
objectClass: tacacsAccount
shadowMax: 10000
uidNumber: 1000
gecos: Marc Huber
givenName: Marc
sn: Huber
gidNumber: 500
shadowLastChange: 14012
loginShell: /bin/bash
homeDirectory: /Users/marc
mail: marc@[Link]
userPassword:: abcdefghijklmnopqrstuvwxyz=
tacacsClient: [Link]/24
tacacsClient: management
tacacsMember: readonly,readwrite
tacacsProfile: { valid until = 2010-01-30 chap = clear ahzoi5Ue }
TACACS+ NG 50 / 62

As tacacsProfile may (and most probably will) contain sensitive data, you should consider setting up LDAP ACLs to
restrict access.
You should be pretty familiar with OpenLDAP (or, for that matter, Fedora-DS) if you’re willing to go this route. For current
versions of OpenLDAP: Use ldapadd to add tacacs_schema.ldif to the cn=config tree. For older versions, add
[Link] to the list of included schema and objectClass definitions in [Link].

[Link] Active Directory Backend

If LDAP_SERVER_TYPE is set to microsoft, the script back-ends to AD servers. Sample configuration (you’ll find that one
in the extra directory, too):
#!/usr/local/sbin/tac_plus-ng
id = spawnd {
listen = { port = 49 }
spawn = {
instances min = 1
instances max = 10
}
background = yes
}

id = tac_plus-ng {
# access log = /var/log/tac_plus-ng/access/%Y%m%[Link]
# accounting log = /var/log/tac_plus-ng/acct/%Y%m%[Link]

mavis module = groups {


groups filter = /^(admins|guest|readonly)$/ # these are defined below
memberof filter = /^CN=tacacs_/ # use this as a prefix
}

mavis module = external {


setenv LDAP_SERVER_TYPE = "microsoft"
setenv LDAP_HOSTS = "[Link]:389"
setenv LDAP_BASE = "dc=example,dc=local"
setenv LDAP_USER = "tacacs@[Link]"
setenv LDAP_PASSWD = "password"
setenv TACACS_GROUP_PREFIX = "tacacs_"
setenv UNLIMIT_AD_GROUP_MEMBERSHIP = 1
#setenv REQUIRE_TACACS_GROUP_PREFIX = 1
exec = /usr/local/lib/mavis/mavis_tacplus_ldap.pl
}

login backend = mavis


user backend = mavis
pap backend = mavis

device world {
address = ::/0
welcome banner = "Welcome\n"
enable 15 = clear secret
key = demo
}

profile admins {
script {
if (service == shell) {
if (cmd == "")
set priv-lvl = 15
permit
}
TACACS+ NG 51 / 62

}
}

profile guest {
enable = deny
script {
if (service == shell) {
if (cmd == "")
set priv-lvl = 1
permit
}
}
}

group admins
group guest

user demo {
password login = clear demo
member = admins
}

user = readonly {
password login = clear readonly
member = guest
}
ruleset {
rule {
script {
if (memberof =~ /^CN=tacacs_admins,/) { profile = admins permit }
if (memberof =~ /^CN=tacacs_readonly,/) { profile = readonly permit }
}
}
rule {
script {
if (member == guest) { profile = guest permit }
}
}
}
}

[Link] Generic LDAP Backend

If LDAP_SERVER_TYPE is set to generic, the script won’t require any modification to your LDAP server, but only authenti-
cates users (with login = mavis, pap = mavis or password = mavis declaration) defined in the configuration file.
No authorization is done by this back-end.

4.3.2 PAM back-end

Example configuration for using Pluggable Authentication Modules:


id = spawnd { listen = { port = 49 } }

id = tac_plus {
mavis module = groups {
resolve gids = yes
resolve gids attribute = TACMEMBER
groups filter = /^(guest|staff)$/
}
TACACS+ NG 52 / 62

mavis module = external {


exec = /usr/local/sbin/pammavis "pammavis" "-s" "sshd"
}
user backend = mavis
login backend = mavis
device = global { address = [Link]/0 key = demo }

profile staff {
service shell {
script {
if (cmd == "") {
set priv-lvl = 15
permit
}
}
}
profile guest {
service shell {
script {
set priv-lvl = 15
if (cmd =~ ^/show /)
permit
deny
}
}
}
}

4.3.3 System Password Backends

mavis_tacplus_passwd.pl authenticates against your local password database. Alas, to use this functionality, the script
may have to run as root, as it needs access to the encrypted passwords. Primary and auxiliary UNIX group memberships will be
mapped to TACACS+ groups.
mavis_tacplus_opie.pl is based on mavis_tacplus_passwd.pl, but uses OPIE one-time passwords for authenti-
cation.

4.3.4 Shadow Backend

mavis_tacplus_shadow.pl may be used to keep user passwords out of the tac_plusconfiguration file, enabling users to
change their passwords via the password change dialog. Passwords are stored in an auxiliary, /etc/shadow-like ASCII file,
one user per line:
username:encryptedPassword:lastChange:minAge:maxAge:reserved

lastChange is the number of days since 1970-01-01 when the password was last changed, and minAge and maxAge deter-
mine whether the password may/may not/needs to be changed. Setting lastChange to 0 enforces a password change upon
first login.
Example shadow file:
marc:$1$q5/vUEsR$jVwHmEw8zAmgkjMShLBg/.:15218:0:99999:
newuser:$1$pQtQsMuj$GKpIr5r2GNaZNfDfnCBtw.:0:0:99999:
test:$1$pQtQsMuj$GKpIr5r2GNaZNfDfnCBtw.:15218:1:30:

Sample daemon configuration:


...
id = tac_plus {
TACACS+ NG 53 / 62

...
mavis module = external {
setenv SHADOWFILE = /path/to/shadow
# setenv FLAG_PWPOLICY=y
# setenv ci=/usr/bin/ci
#
# There are more modern password hashes available via mkpasswd:
# setenv MKPASSWD=/usr/bin/mkpasswd
# setenv MKPASSWDMETHOD=yescrypt
#
exec = /usr/local/lib/mavis/mavis_tacplus_shadow.pl
}
...
login backend = mavis chpass
...
user marc {
login = mavis
...
}
...
}
...

4.3.5 RADIUS Backends

mavis_tacplus_radius.pl authenticates against a RADIUS server. No authorization is done, unless the RADIUS_GROUP_ATTR
environment variable is set (see below). This module may, for example, be useful if you have static user account definitions in
the configuration file, but authentication passwords should be verified by RADIUS. Use the login = mavis or password
= mavis statement in the user profile for this to work.
If the Authen::Radius Perl module is installed, the value of the RADIUS attribute specified by RADIUS_GROUP_ATTR
will be used to create a TAC_MEMBER definition which uses the attribute value as group membership. E.g., an attribute value of
Administrator would result in a
member = Administrator

declaration for the authenticated user, enabling authorization and omitting the need for static users in the configuration file.
Keep in mind that authorization will only work well if either

• the tacplus_info_cache module is being used (it will cache authentication AV pairs locally, so subsequent authorizations
should work fine unless you’re switching to a tac_plus server running elsewhere).

or

• single-connection is used and


• mavis cache timeout is set to a sufficiently high value that covers the user’s (expected) maximum login time.

Alternatively to mavis_tacplus_radius.pl the pamradius program may called by the external module. Results
should be roughly equivalent.

[Link] Sample Configuration

## Use tacinfo_cache to cache authorization data to disk:


mavis module = tacinfo_cache {
directory = /tmp/tacinfo
}
TACACS+ NG 54 / 62

## You can use either the Perl module ...


#mavis module = external {
# exec = /usr/local/lib/mavis_tacplus_radius.pl
# setenv RADIUS_HOST = [Link]:1812 # could add more devices here, comma-separated
# setenv RADIUS_SECRET = "mysecret"
# setenv RADIUS_GROUP_ATTR = Class
# setenv RADIUS_PASSWORD_ATTR = Password # defaults to: User-Password
# }
## ... or the freeradius-client based code:
mavis module = external {
exec = /usr/local/sbin/radmavis "radmavis" "group_attribute=Class" "authserver ←-
=[Link]:1812:mysecret"
}

4.3.6 Experimental Backends

mavis_tacplus_sms.pl is a sample (skeleton) script to send One-Time Passwords via a SMS back-end.

4.3.7 Error Handling

If a back-end script fails due to an external problem (e.g. LDAP server unavailability), your router may or may not fall back to
local authentication (if configured). Chances are that the fallback doesn’t work. If you still want to be able to authenticate via
TACACS+ in that case, you can do so with a non-MAVIS user which will only be valid in case of a back-end error:
...
# set the time interval you want the user to be valid if the back-end fails:
authentication fallback period = 60 # that’s actually the default value
...
# add a local user for emergencies:
user = cisco {
...
fallback-only
...
}

To indicate that fallback mode is actually active, you may a display a different login prompt to your users:
device = ... {
...
welcome banner = "Welcome\n"
welcome banner fallback = "Welcome\nEmergency accounts are currently enabled.\n"
...
}

Fallback can be enabled/disabled globally an on a per-device basis. Default is enabled.


authentication fallback = permit
host ... {
...
authentication fallback = deny
...
}
TACACS+ NG 55 / 62

5 Debugging

5.1 Debugging Configuration Files

When creating configuration files, it is convenient to check their syntax using the -P flag to tac_plus; e.g:
tac_plus -P config-file

will syntax check the configuration file and print any error messages on the terminal.

5.2 Trace Options

Trace (or debugging) options may be specified in global, device, user and group context. The current debugging level is a
combination (read: OR) of all those. Generic syntax is:
debug = option ...

For example, getting command authorization to work in a predictable way can be tricky - the exact attributes the NAS sends to
the daemon may depend on the IOS version, and may in general not match your expectations. If your regular expressions don’t
work, add
debug = REGEX

where appropriate, and the daemon may log some useful information to syslog.
Multiple trace options may be specified. Example:
debug = REGEX CMD

Trace options may be removed by prefixing them with-. Example:


debug = ALL -PARSE

The debugging options available are summarized in the following table:

Bit Value Name Description


0 1 PARSE Configuration file parsing
1 2 AUTHOR Authorization related
2 4 AUTHEN Authentication related
3 8 ACCT Accounting related
4 16 CONFIG Configuration related
5 32 PACKET Packet dump
6 64 HEX Packet hex-dump
7 128 LOCK File locking
8 256 REGEX Regular expressions
9 512 ACL Access Control Lists
10 1024 RADIUS unused
11 2048 CMD Command lookups
12 4096 BUFFER Buffer handling
13 8192 PROC Procedural traces
14 16384 NET Network related
15 32768 PATH File system path related
16 65536 CONTROL Control connection related
17 131072 INDEX Directory index related
18 262144 AV Attribute-Value pair handling
19 524288 MAVIS MAVIS related
20 1048576 DNS DNS related
TACACS+ NG 56 / 62

Bit Value Name Description


21 2097152 USERINPUT Show user input (this may include passwords)
31 2147483648 NONE Disable debugging

Some of those debugging options are not used and trigger no output at all.

Debugging User Input


The daemon will (starting with snapshot 202012051554) by default no longer outputs user input from authentication packets
sent by the NAS. You can explicitly change this using the USERINPUT debug flag. Something like
debug = ALL

or using a numeric value will not work, it needs to be enabled explicitly, e.g.:
debug = ALL USERINPUT

Be prepared to see plain text user passwords if you enable this option.

6 Frequently Asked Questions

• Is there a Graphical User Interface of any kind?


No, unless your favourite text editor does qualify.

• I’m using the single-connection feature. How can I force my router to close the TCP connections to the TACACS+
server?
On IOS, show tcp brief will display the TCP connections. Search for the ones terminating at your server, and kill them
using clear tcp tcb .... Example:
Router#sho tcp brief | incl [Link].49
633BB794 [Link].17326 [Link].49 ESTAB
6287E4C4 [Link].24880 [Link].49 ESTAB
Router#clear tcp tcb 633BB794
[confirm]
[OK]
Router#clear tcp tcb 6287E4C4
[confirm]
[OK]
Router#

• Is there any way to avoid having clear text versions of the CHAP secrets in the configuration file?
CHAP requires that the server knows the cleartext password (or equivalently, something from which the server can generate
the cleartext password). Note that this is part of the definition of CHAP, not just the whim of some Cisco engineer who drank
too much coffee late one night.
If we encrypted the CHAP passwords in the database, then we’d need to keep a key around so that the server can decrypt them
when CHAP needs them. So this only ends up being a slight obfuscation and not much more secure than the original scheme.
In extended TACACS, the CHAP secrets were separated from the password file because the password file may be a system
password file and hence world readable. But with TACACS+’s native database, there is no such requirement, so we think the
best solution is to read-protect the files. Note that this is the same problem that a Kerberos server has. If your security is
compromised on the Kerberos server, then your database is wide open. Kerberos does encrypt the database, but if you want
your server to automatically restart, then you end up having to "kstash" the key in a file anyway and you’re back to the same
security problem.
So storing the cleartext password on the security server is really an absolute requirement of the CHAP protocols, not something
imposed by TACACS+.
TACACS+ NG 57 / 62

With the scheme choosen for newer TACACS+ protocol revisions, the NAS sends the challenge information to the TACACS+
daemon and the daemon uses the cleartext password to generate the response and returns that.
The original TACACS+ protocol included specific protocol knowledge for CHAP. Please note that this version of the daemon
implementation no longer supports SENDPASS, SENDAUTH and ARAP to comply to RFC8907.
However, the above doesn’t apply to PAP. You can keep an inbound PAP password DES- or MD5-encrypted, since all you need
to do with it is verify that the password the principal gave you is correct.
• How is the typical login authentication sequence done?
1. NAS sends START packet to daemon
2. Daemon send GETUSER containing login prompt to NAS
3. NAS prompts user for username
4. NAS sends packet to daemon
5. Daemon sends GETPASS containing password prompt to the NAS
6. NAS prompts user for password
7. NAS sends packet to daemon
8. Daemon sends accept, reject or error to NAS
• How do I limit the number of sessions a user can have?
With this version of the daemon you can’t.

• How can I configure time-outs on an interface via TACACS+?


Certain per-user/per-interface timeouts may be set by TACACS+ during authorization. As of 11.0, you can set an exec timeout.
As of 11.1 you can also set an exec idle timeout.
There are currently no settable timeouts for PPP or SLIP sessions, but there is a workaround which applies to ASYNC PPP/SLIP
idle timeouts started via exec sessions only: This workaround is to set an EXEC (idletime) timeout on an exec session which
is later used to start up PPP or SLIP (either via a TACACS+ autocommand or via the user explicitly invoking PPP or SLIP). In
this case, the exec idle timeout will correctly terminate an idle PPP or SLIP session. Note that this workaround cannot be used
for sessions which autoselect PPP or SLIP.
An idle timeout terminates a connection when the interface is idle for a given period of time (this is equivalent to the "session-
timeout" Cisco IOS configuration directive). The other timeouts are absolute. Of course, any timeouts set by TACACS+ apply
only to the current connection.
profile ... {
...
service shell {
set idletime = 5 # disconnect lol if there is no traffic for 5 minutes
set timeout = 60 # disconnect lol unconditionally after one hour
...
}
}

You also need to configure exec authorization on the NAS for the above timeouts, e.g.
aaa authorization exec default group tacacs+

Note that these timeouts only work for async lines, not for ISDN currently.
Note also that you cannot use the authorization if-authenticated option with these parameters, since that skips autho-
rization if the user has successfully authenticated.
• Can someone expand on the use of the optional keyword?
Most attributes are mandatory i.e. if the daemon sends them to the NAS, the NAS must obey them or deny the authorization.
This is the default. It is possible to mark attributes as optional, in which case a NAS which cannot support the attribute is free
to simply ignore it without causing the authorization to fail.
TACACS+ NG 58 / 62

This was intended to be useful in cutover situations where you have multiple NASes running different versions of IOS, some
of which support more attributes than others. If you make the new attributes optional, older NASes could ignore the optional
attributes while new NASes could apply them. Note that this weakens your security a little, since you are no longer guaranteed
that attributes are always applied on successful authorization, so it should be used judiciously.
• What about MSCHAP?
The daemon comes with mschap support. Mschap is configured the same way as chap, only using the mschap keyword in
place of the chap keyword.
MSCHAP requires DES support. Use the --with-ssl flag when configuring the package.
Marc Huber thinks that MSCHAP relevance is less than zero and expects it to be removed from the standard, as nobody uses
it anyway.

7 Multi-tenant setups

While using a dedictated tac_plus-ng installation per tenant is certainly possible it lacks some elegance. There are other ways:
A single daemon can tell tenants apart by

• device identity, either


– by NAD IP address or
– by certificate common name (currently irrelevant, as there’s no NAD support)
• realms, which are determined
– by tacacs+ destination port or
– by VRF (Linux, mostly)

Using the IP-based device identity should be sufficient for simple setups, but these don’t scale and don’t handle IP address
conflicts. Options to cope with the latter involve realms. A realm is most basicly a text string the tcp listener (spawnd) assigns to
a connection based on TCP destination port:
id = spawnd {
...
listen { port = 49001 realm = customer1 }
listen { port = 49002 realm = customer2 }
...
}

In case VRFs aren’t an option you can use HAProxy instances to transparently relay TACACS+ connections to tac_plus-ng:
id = spawnd {
...
listen { port = 49001 realm = customer1 haproxy = yes }
listen { port = 49002 realm = customer2 haproxy = yes }
....
}

tac_plus-ng will then take the NAD IP from the HAProxy protocol v2 header.
Otherwise, the "listen" directive can be limite to your locally defined VRFs:
id = spawnd {
...
listen { port = 49000 realm = customer1 vrf = blue }
listen { port = 49000 realm = customer2 vrf = red }
...
}
TACACS+ NG 59 / 62

On Linux, if you set net.ipv4.tcp_l3mdev_accept=1, you can even get away with
id = spawnd { ... listen { port = 49000 } ... }

and the daemon will use the VRF name your clients did connect from as realm name.

7.1 AD, Realms and Tenants

The suggested setup for giving customers limited access to NADs is:
id = tac_plus-ng {
mavis module = external { your AD configuration goes here }

profile ... { ... }

realm customer1 {

net custsrc { the IP ranges the end customer may log in from }

rewrite normalizeCustomerAccount {
rewrite /^.*$/ cust1-\L$0
}

net custnet { the IP ranges the end customer may log in from }

device customer1 {
....
script { if (nac == custsrc) rewrite user = normalizeCustomerAccount }
}

ruleset {
rule customer {
if (nac == custnet) {
if (member == ...) { profile = ... permit }
deny
}
if (member == ...) profile = ... permit
deny
}
}

In this example, you can easily share your LDAP (or AD) server between your own admin users and multiple tenants. The
daemon will automatically prefix the customer accounts with a prefix and convert them to lower case. Note that the username
rewriting happens using a script in device context. Rewriting won’t work in scripts anywhere else.

8 AAA rule tracing

The distribution includes the [Link] Perl script. It’s usually not installed automatically, due to some Perl dependencies
that need to be met. It requires a couple of CPAN Perl modules, and a custom one. Your OS distribution might provide re-built
packages for Net::IP and/or Net::TacacsPlus::Packet, so check for this first. For unavailable packages, you can do
a manual install using cpan. Example for Ubuntu:
sudu apt install libnet-ip-perl
sudo cpan install Net::TacacsPlus::Packet

Then cd to tac_plus-ng/perl and run make. [Link] should now be ready to use.
Usage information:
TACACS+ NG 60 / 62

$ This is a TACACS+ AAA validator for tac_plus-ng.

Usage: ./[Link] [ <Options> ] [ <attributes> ... ]

attributes are authorization or accounting AV pairs, default is:


"service=shell" "cmd*"

Options:
--help show this text
--defaults=<file> read default settings from <file>
--mode=<mode> authc, authz or acct [authz]
--username=<username> username [ubuntu]
--port=<port> port [vty0]
--remote=<client ip> remote client ip [[Link]]
--key=<key> encryption key [demo]
--realm=<realm> realm [default]
--nad=<address> NAD (router/switch/...) IP address [[Link]]
--authentype=<type> authen_type [ascii]
--authenmethod=<n> authen_method [tacacsplus]
--authenservice=<n> authen_method [login]
--exec=<path> executable path [/usr/local/sbin/tac_plus-ng]
--conf=<config> configuration file [/usr/local/etc/tac_plus-[Link]]
--id=<id> id for configuration selection [tac_plus-ng]

For authc the password can be set either via the environment variable
TACTRACEPASSWORD or the defaults file. Setting it via a CLI option isn’t
supported as the password would show up as clear text in the process listing.

Example:
# [Link] --conf extra/tac_plus-[Link]-ads --user user01
[Link] ---<start packet>---
[Link] session id: 00000001, data length: 46
[Link] AUTHOR, priv_lvl=0
[Link] authen_type=ascii (1)
[Link] authen_method=tacacs+ (6)
[Link] service=login (1)
[Link] user_len=6 port_len=4 rem_addr_len=9 arg_cnt=2
[Link] user (len: 6): user01
[Link] 0000 75 73 65 72 30 31 user01
[Link] port (len: 4): vty0
[Link] 0000 76 74 79 30 vty0
[Link] rem_addr (len: 9): [Link]
[Link] 0000 31 32 37 2e 30 2e 30 2e 31 127.0.0. 1
[Link] arg[0] (len: 13): service=shell
[Link] 0000 73 65 72 76 69 63 65 3d 73 68 65 6c 6c service= shell
[Link] arg[1] (len: 4): cmd*
[Link] 0000 63 6d 64 2a cmd*
[Link] ---<end packet>---
[Link] Start authorization request
[Link] looking for user user01 in MAVIS backend
[Link] user found by MAVIS backend, av pairs:
MEMBEROF "CN=tacacs_admins,OU=Groups,DC=example,DC=local","CN=tacacs_readwrite ←-
,OU=Groups,DC=example,DC=local"
USER user01
DN CN=user01,CN=Users,DC=example,DC=local
IPADDR [Link]
SERVERIP [Link]
REALM default
TACMEMBER "admins"
[Link] verdict for user user01 is ACK
TACACS+ NG 61 / 62

[Link] user ’user01’ found


[Link] evaluating ACL default#0
[Link] pcre2: ’^CN=tacacs_admins,’ <=> ’CN=tacacs_admins,OU=Groups,DC=example,DC=local’ ←-
= 1
[Link] line 79: [memberof] <pcre-regex> ’^CN=tacacs_admins,’ => true
[Link] line 79: [profile] ’admins’
[Link] line 79: [permit]
[Link] ACL default#0: match
[Link] user01@[Link]: ACL default#0: permit (profile: admins)
[Link] line 45: [service] = ’shell’ => true
[Link] line 47: [cmd] = ’’ => true
[Link] line 47: [set] ’priv-lvl=15’
[Link] line 48: [permit]
[Link] nas:service=shell (passed thru)
[Link] nas:cmd* (passed thru)
[Link] nas:absent srv:priv-lvl=15 -> add priv-lvl=15 (k)
[Link] added 1 args
[Link] Writing AUTHOR/PASS_ADD size=30
[Link] ---<start packet>---
[Link] session id: 00000001, data length: 18
[Link] AUTHOR/REPLY, status=1 (AUTHOR/PASS_ADD)
[Link] msg_len=0, data_len=0, arg_cnt=1
[Link] msg (len: 0):
[Link] data (len: 0):
[Link] arg[0] (len: 11): priv-lvl=15
[Link] 0000 70 72 69 76 2d 6c 76 6c 3d 31 35 priv-lvl =15
[Link] ---<end packet>---

9 Bugs

• This documentation isn’t well structured.


• The examples given are too IPv4-centric. However, the daemon handles IPv6 just fine.

• Some of the NAS configuration examples aren’t recently tested. Refer to the IOS documentation for IOS configuration syntax
guidance.

10 References

• [Link] - The TACACS+ Protocol (Version 1.78)

• RFC8907: The Terminal Access Controller Access-Control System Plus (TACACS+) Protocol

11 Copyrights and Acknowledgements

Please see the source for copyright and licensing information of individual files.

• The following applies if the software was compiled with OpenSSL support:
This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit ([Link]
This product includes cryptographic software written by Eric Young (eay@[Link]).

• MD4 algorithm:
The software uses the RSA Data Security, Inc. MD4 Message-Digest Algorithm.
TACACS+ NG 62 / 62

• MD5 algorithm:
The software uses the RSA Data Security, Inc. MD5 Message-Digest Algorithm.
• If the software was compiled with PCRE (Perl Compatible Regular Expressions) support, the following applies:
Regular expression support is provided by the PCRE library package, which is open source software, written by Philip Hazel,
and copyright by the University of Cambridge, England. ([Link]
• The original tac_plus code (which this software and considerable parts of the documentation are based on) is distributed
under the following license:
Copyright (c) 1995-1998 by Cisco systems, Inc.
Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that
this copyright and permission notice appear on all copies of the software and supporting documentation, the name of Cisco
Systems, Inc. not be used in advertising or publicity pertaining to distribution of the program without specific prior permission,
and notice be given in supporting documentation that modification, copying and distribution is by permission of Cisco Systems,
Inc.
Cisco Systems, Inc. makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS
PROVIDED ``AS IS” AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMI-
TATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
• The code written by Marc Huber is distributed under the following license:
Copyright (C) 1999-2022 Marc Huber ([Link]@[Link]). All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following dis-
claimer in the documentation and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment:
This product includes software developed by Marc Huber ([Link]@[Link]).
Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments
normally appear.

THIS SOFTWARE IS PROVIDED ``AS IS” AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITS AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS IN-
TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Common questions

Powered by AI

MAVIS modules expand tac_plus-ng's functionality by serving as a back-end for authentication, authorization, and accounting, facilitating a robust and flexible infrastructure for managing authentication processes. These modules enable integration with external systems, such as LDAP or RADIUS, providing a cohesive way to verify user credentials and manage user sessions dynamically, significantly enhancing the daemon’s capability to handle complex authentication scenarios .

Realms in tac_plus-ng provide a structure to distinctly manage tenant configurations by associating sets of rules and options per tenant, allowing for the separation and independent handling of authentication data. This setup is particularly beneficial when using VRFs (Virtual Routing and Forwarding), as it aligns network configurations with specific tenant realms, thus enhancing manageability and scalability in multi-tenant environments .

Integrating LDAP with tac_plus-ng using generic LDAP backends facilitates user authentication by enabling centralized management of user credentials without modifications to the LDAP server. The backend authenticates users defined in the tac_plus-ng configuration file against LDAP, simplifying the authentication process while leveraging existing directory infrastructures for consistency and efficiency .

The IO mechanisms in tac_plus-ng, such as 'port', 'kqueue', '/dev/poll', 'epoll', 'poll', and 'select', cater to different operating systems, optimizing performance by utilizing the most efficient method available for the environment. For instance, 'port' is optimal for Sun Solaris 10, while 'epoll' is best for Linux, providing efficient event notification mechanisms tailored to specific system capabilities .

The MAVIS shadow backend offers advantages in password management by storing passwords in a secure manner akin to traditional UNIX shadow files, thereby separating user authentication data from tac_plus-ng configuration files. This separation enhances security by allowing users to change their passwords independently and facilitates compliance with best practices for secure storage and handling of authentication credentials .

Specifying a retire limit in tac_plus-ng is significant for maintaining server performance and stability, as it sets an automatic termination and restart threshold for worker processes after handling a specified number of requests (e.g., 1000). This helps free up resources and prevents potential issues related to memory leaks or process hang-ups, thereby ensuring consistent operation .

Log destinations like syslog and pipe enhance tac_plus-ng’s configuration management by offering flexible logging options. The daemon can write logs to different destinations, allowing administrators to track authentication, authorization, and accounting activities effectively. This flexibility facilitates adaptation to various logging infrastructures while maintaining comprehensive records for audit and troubleshooting .

Realms in tac_plus-ng contribute to flexibility by allowing configuration to be modular and inherit properties, enhancing the capability to manage different sets of rules and attributes associated with user authentications. Realms can inherit settings from their parent realms, simplifying the configuration management by allowing shared configurations to apply across multiple realms while still enabling customization for specific cases .

Using optional attributes in TACACS+ during authorization can facilitate network configurations involving multiple NASes with different IOS versions. This allows newer NASes to apply more attributes, while older NASes can simply ignore optional attributes without failing the authorization process. However, it potentially weakens security since there's no guarantee that attributes are consistently applied across all devices upon successful authorization, necessitating cautious implementation .

The shebang syntax in the configuration file allows for the tac_plus-ng daemon to be executable directly if it starts with #!/usr/local/sbin/tac_plus-ng. This feature enhances usability by simplifying the starting process of the daemon, eliminating the need for additional commands, thereby streamlining administrative operations .

You might also like