NW機器のTACACS+設定

およそ世間では情報が沢山あるので我が家の設定値のみ記載。

設定方針は次の通り。

  • 1号機は.20,2号機は.21,TACACSサーバがダウンしたらLocal認証(タイムアウトは5秒)
  • ログインできたら即特権(コマンド権限はTACACSサーバ側で握る)
  • 1号機を最初に記述,2号機を次に記述
  • aaaの設定でdefaultとして定義するものをよく見かけるが,自宅環境では不採用とし,各aaaの定義に名前を付ける。(仮にdefaultで定義すると全てのインタフェースに適用されるし,設定順番ミスるとそのままコマンドがはじかれるなどリスクがあると思う。ただし,line vty でのauthentication設定などは不要になるなど設定行は減る。)

Cisco IOS

!!! AAA有効
aaa new-model

!!! TACACSサーバ登録。aaa tacacs-server は非推奨になった模様。
tacacs server tacacs-sv1
 address ipv4 192.168.30.20
 key <tacacsサーバで設定したKey>
 timeout 5
tacacs server tacacs-sv2
 address ipv4 192.168.30.21
 key <tacacsサーバで設定したKey>
 timeout 5

!!! TACACSサーバグループ作成。
aaa group server tacacs+ TAC_SRV
 server name tacacs-sv1
 server name tacacs-sv2

!!! TACplusという名前でTAC_SRVで登録したTACACSサーバに問い合わせる。落ちてたらローカル認証。
aaa authentication login TACplus_authe group TAC_SRV local-case


!!! 実行権限をTACACSサーバに問い合わせる。落ちてたらローカル認証。
aaa authorization exec TACplus_autho_exec group TAC_SRV local

!!! 実行レベル15のコマンド実行権限をTACACSサーバに問い合わせる。落ちてたらローカル認証。
aaa authorization commands 15 TACplus_autho_cmd group TAC_SRV local

!!! コマンド実行ログをTACACSサーバへ送信。
aaa accounting commands 15 TACplus_acco start-stop group TAC_SRV



line vty 0 4
 exec-timeout 0 0
 authorization commands 15 TACplus_autho_cmd
 authorization exec TACplus_autho_exec
 logging synchronous
 login authentication TACplus_authe
 transport input ssh

Cisco ASA

ASAの場合はIOSとは若干違うが,基本的な定義の流れは同じ。

aaa-server TACplus protocol tacacs+
 reactivation-mode timed
 max-failed-attempts 2
aaa-server TACplus (mgmtside) host 192.168.30.20
 key *****
aaa-server TACplus (mgmtside) host 192.168.30.21
 key *****

!!! SSH接続時にTACACS→LOCALという順で認証
aaa authentication ssh console TACplus LOCAL

!!! 特権
aaa authentication enable console TACplus LOCAL

aaa authorization command TACplus LOCAL
aaa accounting command privilege 15 TACplus

!!! 
aaa authorization exec authentication-server auto-enable
aaa authentication login-history

以上。

TACACS+サーバ構築

概要

宅内環境の認証をTACACS化しようと,まずはTACACS+サーバを2台立てる。構成は次の通り。各機器の管理IFと同じセグメントにTACACSサーバを2台建てる。

OpenStack上にDebianイメージでインスタンスを1つ。冗長化のためESXi上にUbuntuのVMをたてて2号機とする。(なぜOSが違うかというと,ESXi上にDebian10のイメージがなかっただけで特に他意はなし)

TACACS+はtac_plusを利用する。元はこれ(https://www.shrubbery.net/tac_plus/)だと思うのだが,公開が終わっているっぽい。facebookがGithubでforkしたものを公開していたので,Ubuntu側ではここからソースをもってきてコンパイルしてインストールした。’Debianはリポジトリからインストールできた。)

インストール

Debian 10でのインストール方法

aptで一発。

# apt install tacacs+

Debain11でのインストール方法

11にはまだパッケージがなかったためソースからコンパイル。

# apt install git build-essential flex bison libwrap0-dev
$ git clone https://github.com/facebook/tac_plus.git
$ cd tac_plus/tacacs-F4.0.4.28
$ ./configure
$ make
# make install

続いて必要なファイルの準備。

サービス起動ファイル /etc/init.d/tacacs_plus

#!/bin/sh
### BEGIN INIT INFO
# Provides:          tacacs+
# Required-Start:    $network $local_fs $syslog $remote_fs
# Required-Stop:     $network $local_fs $remote_fs
# Should-Start:      $named
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: TACACS+ authentication daemon
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

DAEMON=/usr/local/sbin/tac_plus
NAME="tacacs+"
DESC="TACACS+ authentication daemon"
LOGDIR=/var/log/
STARTTIME=1

PIDFILE=/var/run/tac_plus.pid

test -x $DAEMON || exit 0

. /lib/lsb/init-functions

# Default options, these can be overriden by the information
# at /etc/default/$NAME
DAEMON_OPTS="-C /etc/tacacs+/tac_plus.conf"          # Additional options given to the server


LOGFILE=$LOGDIR/tac_plus.log  # Server logfile

# Include defaults if available
if [ -f /etc/default/$NAME ] ; then
        . /etc/default/$NAME
fi

# Check that the user exists (if we set a user)
# Does the user exist?
if [ -n "$DAEMONUSER" ] ; then
    if getent passwd | grep -q "^$DAEMONUSER:"; then
        # Obtain the uid and gid
        DAEMONUID=`getent passwd |grep "^$DAEMONUSER:" | awk -F : '{print $3}'`
        DAEMONGID=`getent passwd |grep "^$DAEMONUSER:" | awk -F : '{print $4}'`
    else
        log_failure_msg "The user $DAEMONUSER, required to run $NAME does not exist."
        exit 1
    fi
fi


set -e

running_pid() {
# Check if a given process pid's cmdline matches a given name
    pid=$1
    name=$2
    [ -z "$pid" ] && return 1
    [ ! -d /proc/$pid ] &&  return 1
    cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
    # Is this the expected server
    [ "$cmd" != "$name" ] &&  return 1
    return 0
}

running() {
# Check if the process is running looking at /proc
# (works for all users)

    # No pidfile, probably no daemon present
    [ ! -f "$PIDFILE" ] && return 1
    pid=`cat $PIDFILE`
    running_pid $pid $DAEMON || return 1
    return 0
}

start_server() {
# Start the process using the wrapper
    if check_config_quiet ; then
         start-stop-daemon --start --quiet --pidfile $PIDFILE \
                --exec $DAEMON -- $DAEMON_OPTS
         errcode=$?
         return $errcode
    else
         return $?
    fi

}

stop_server() {
    killproc -p $PIDFILE $DAEMON
    return $?
}

reload_server() {
    if check_config_quiet ; then
         [ ! -f "$PIDFILE" ] && return 1
         pid=`cat $PIDFILE` # This is the daemon's pid
         # Send a SIGHUP
         kill -1 $pid
         return $?
    else
         return $?
    fi
}

check_config() {
        $DAEMON -P $DAEMON_OPTS
        return $?
}

check_config_quiet() {
        $DAEMON -P $DAEMON_OPTS >/dev/null 2>&1
        return $?
}

force_stop() {
# Force the process to die killing it manually
        [ ! -e "$PIDFILE" ] && return
        if running ; then
                kill -15 $pid
        # Is it really dead?
                sleep "$DIETIME"s
                if running ; then
                        kill -9 $pid
                        sleep "$DIETIME"s
                        if running ; then
                                echo "Cannot kill $NAME (pid=$pid)!"
                                exit 1
                        fi
                fi
        fi
        rm -f $PIDFILE
}


case "$1" in
  start)
        log_daemon_msg "Starting $DESC " "$NAME"
        # Check if it's running first
        if running ;  then
            log_progress_msg "apparently already running"
            log_end_msg 0
            exit 0
        fi
        if start_server ; then
            # NOTE: Some servers might die some time after they start,
            # this code will detect this issue if STARTTIME is set
            # to a reasonable value
            [ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time
            if  running ;  then
                # It's ok, the server started and is running
                log_end_msg 0
            else
                # It is not running after we did start
                log_end_msg 1
            fi
        else
            # Either we could not start it
            log_end_msg 1
        fi
        ;;
  stop)
        log_daemon_msg "Stopping $DESC" "$NAME"
        if running ; then
            # Only stop the server if we see it running
                        errcode=0
            stop_server || errcode=$?
            log_end_msg $errcode
        else
            # If it's not running don't do anything
            log_progress_msg "apparently not running"
            log_end_msg 0
            exit 0
        fi
        ;;
  force-stop)
        # First try to stop gracefully the program
        $0 stop
        if running; then
            # If it's still running try to kill it more forcefully
            log_daemon_msg "Stopping (force) $DESC" "$NAME"
                        errcode=0
            force_stop || errcode=$?
            log_end_msg $errcode
        fi
        ;;
  restart|force-reload)
        log_daemon_msg "Restarting $DESC" "$NAME"
                errcode=0
        stop_server || errcode=$?
        # Wait some sensible amount, some server need this
        [ -n "$DIETIME" ] && sleep $DIETIME
        start_server || errcode=$?
        [ -n "$STARTTIME" ] && sleep $STARTTIME
        running || errcode=$?
        log_end_msg $errcode
        ;;
  status)

        log_daemon_msg "Checking status of $DESC" "$NAME"
        if running ;  then
            log_progress_msg "running"
            log_end_msg 0
        else
            log_progress_msg "apparently not running"
            log_end_msg 1
            exit 1
        fi
        ;;
  # Use this if the daemon cannot reload
  reload)
        log_daemon_msg "Reloading $DESC configuration files" "$NAME"
        if reload_server ; then
                if running ; then
                        log_end_msg 0
                else
                        log_progress_msg "$NAME not running"
                        log_end_msg 1
                fi
        else
                log_progress_msg "Reload failled"
                log_end_msg 1
        fi
        ;;
  check)
        check_config
        if [ X$? = "X0" ]
        then
                log_daemon_msg "Checking $DESC configuration files successful" "$NAME"
        else
                log_daemon_msg "Checking $DESC configuration files failed"
                exit 1
        fi
        ;;
  *)
        N=/etc/init.d/tacacs_plus
        echo "Usage: $N {start|stop|force-stop|restart|reload|force-reload|status|check}" >&2
        exit 1
        ;;
esac

exit 0

アカウンティングログファイル作成

# touch /var/log/tac_plus.acct

tacacs+設定ファイル編集(2台共通)

/etc/tacacs+/tac_plus

以下設定例。今回はとりあえず設定ファイル中にユーザを記述する設定方式をとったが,仕事ではやらないように注意。パスワードのハッシュ化は 「openssl passwd – 5」 (SHA256)で生成。

# アカウンティング用ログファイル指定
accounting file = /var/log/tac_plus.acct


# <サーバと通信するための鍵を記述>
key = tacacs_key

user = networkmanager {
        name = "Network Manager"
        member = admin
        login = des <ハッシュ化パスワード>
        enable = des <ハッシュ化enable>
}

user = networkoperator {
        name = "Read only user"
        member = operator
        login = des <ハッシュ化パスワード>
        enable = des <ハッシュ化enable>
}

# 管理者用グループ。Privilage15。
group = admin {
        default service = permit
        service = exec {
                priv-lvl = 15
        }

# オペレータ用グループ。showコマンドのみ利用可能
group = operator {
        default service = deny
        service = exec {
                priv-lvl = 15
        }
        cmd = show {
                permit .*
        }
        cmd = exit {
                permit .*
        }
}

TACACS+起動

# service tacacs_plus start

以上。

次はNW機器側の設定。