Terminal Access Controller Access-Control System Plus (TACACS+) is a protocol developed by Cisco and released as an open standard beginning in 1993. Although derived from TACACS, TACACS+ is a separate protocol that handles authentication, authorization, and accounting (AAA) services.

Integrate a TACACS+ server into GNS3

tac_plus is a TACACS+ server for unix based systems. I’ve created a docker container (ehlers/tacacs) based on Debian Linux and the tac_plus server.

To add this docker container into GNS3 open the Docker preferences.
GNS3 project

With “New” add a new Docker VM template, choose the server type (most likely “Run this Docker VM on the GNS3 VM”) and then enter “ehlers/tacacs” as a new image.
GNS3 project

Then give it a name (e.g. tacacs), for the following questions stick with the defaults. Finally leave the preferences with “OK”.

It contains some additional utilities:

  • tac_parse - parse the configuration file, report errors
  • tac_start - starts tac_plus server process
  • tac_stop - stops tac_plus server process
  • tac_reload - tac_plus re-reads the configuration file

Furthermore it includes some tools to manage the TACACS+ password database:

  • tac_adduser - adds a user
  • tac_deluser - deletes a user
  • tac_passwd - changes the password of a user

GNS3 project

To test TACACS+ I’m using a simple GNS3 project.
GNS3 project

The IOS router gets a basic interface configuration:

interface Loopback0
 ip address 172.16.1.1 255.255.255.255
!
interface FastEthernet0/0
 ip address 192.168.1.1 255.255.255.0
 no shutdown
!
interface FastEthernet0/1
 ip address 10.1.1.1 255.255.255.0
 no shutdown
!

The IP setup of the Docker container is configured by right-click -> Configure -> Network configuration / Edit. The TACACS container gets IP 192.168.1.100/24, ipterm gets 10.1.1.100/24.

IOS configuration

This is a quite complete IOS configuration using TACACS+ for authentication, authorization and accounting. As command authorization and command accounting result in a lot of TACACS+ traffic, you should evaluate, if that’s really needed in your operational network.

service password-encryption
! fallback, if tacacs+ is not reachable
username cisco password cisco
enable secret enable-password
!
aaa new-model
aaa authentication login default group tacacs+ local
aaa authentication enable default group tacacs+ enable
!
aaa authorization config-commands
aaa authorization exec default group tacacs+ none
aaa authorization commands 1 default group tacacs+ none
aaa authorization commands 15 default group tacacs+ none
!
aaa accounting exec default start-stop group tacacs+
aaa accounting commands 1 default stop-only group tacacs+
aaa accounting commands 15 default stop-only group tacacs+
!
ip tacacs source-interface Loopback0
tacacs-server host 192.168.1.100 key tac-key
!
line vty 0 4
 login authentication default
!
! for testing only: no authentication on console
aaa authentication login no_auth none
line con 0
 login authentication no_auth

tac_plus configuration

This is an example configuration, that shows the basics. A documentation about the tac_plus configuration is availabe in the man page tac_plus.conf.5.

It’s very important, that the key configured in the devices matches the key in the configuration. Otherwise the TACACS+ communication won’t work.

root@tacacs-1:/etc/tacacs+# cat > tac_plus.conf
# Define where to log accounting data, this is the default.
accounting file = /var/log/tac_plus.acct

# This is the key that clients have to use to access TACACS+
key = tac-key

# Use /etc/tacacs+/passwd file to do authentication
default authentication = file /etc/tacacs+/passwd

# Don't allow interactive login wih enable user
user = $enab15$ {
}

# unprivileged default group
group = default {
	default service = deny

	service = exec {
		priv-lvl = 15
		idletime = 10
	}

	cmd = show {
		permit controllers
		permit interfaces
		permit "ip interface"
	}

	# enable authentication doesn't work with DEFAULT user
	# it always authenticates the special $enabN$ user (N = privilege level)
	#enable = file /etc/tacacs+/passwd
}

# admin group
group = admin {
	default service = permit

	service = exec {
		priv-lvl = 15
		idletime = 10
	}
	enable = file /etc/tacacs+/passwd
}

# admin users
user = admin1 {
	member = admin
}
user = admin2 {
	member = admin
}
user = admin3 {
	member = admin
}

# default user
user = DEFAULT {
	member = default
}
root@tacacs-1:/etc/tacacs+# tac_adduser '$enab15$'
Enter new password: <enable password>
Retype new password: <enable password>
root@tacacs-1:/etc/tacacs+# tac_adduser admin2
Enter new password: <admin2 password>
Retype new password: <admin2 password>
root@tacacs-1:/etc/tacacs+# tac_adduser test
Enter new password: <test password>
Retype new password: <test password>
root@tacacs-1:/etc/tacacs+# tac_reload
[ ok ] Reloading TACACS+ authentication daemon configuration files: tacacs+.
root@tacacs-1:/etc/tacacs+#

Most of the configuration should be pretty simple, but this “$enab15$” stuff needs some explanation.

Users, that are defined in the tac_plus.conf configuration files can use the “enable” statement to define a password file for using the user’s password also as the enable password. So in our example the user admin1, admin2 and admin3 use their password also as their enable password (tough that’s hardly needed, because their session starts in level 15).

For the DEFAULT user that doesn’t work. Instead it uses the password of the user “$enab<enable level>$” as the enable password. For enable level 15 (the default) it uses the user “$enab15$”. Therefore we need to add that user to the password database. But we don’t want, that anyone uses that username to login. So we disallow that at the beginning of the configuration.

Alternative tac_plus configuration with do_auth

do_auth use a simpler configuration language and allows a greater flexibility in TACACS+ authentication and authorization.

To use that copy tac_plus.conf.do_auth to tac_plus.conf and create a do_auth.ini. Details can be found in the do_auth project.

Here an example for do_auth.ini

[users]
$enab15$ =
default =
	fewcommands
admin1 =
	adminuser
admin2 =
	adminuser
admin3 =
	adminuser

[fewcommands]
host_allow =
	.*
device_permit =
	172\.16\.1\.1$
command_permit =
	show int.*
	show ip int.*
	show controllers.*

[adminuser]
host_allow =
	.*
device_permit =
	.*
command_permit =
	.*