Mikrotik -> Radius -> Mavis help

23 views
Skip to first unread message

David Fisher

unread,
Sep 18, 2025, 12:21:47 PM (7 days ago) Sep 18
to Event-Driven Servers
I'm wondering if anyone can help me get a MikroTik device working through tac_plus-ng?  I'm not quite sure what to even look into next...any assistance would be helpful!

I have been happily using tac_plus-ng for many years, it has been working great for all of my wide variety of devices.  Now I have been told, and have no input in, that we're adding a bunch of MikroTik devices to the network and they need to work.  I have my hands on the model we're folding into our fleet but I can't get it to authenticate.  I understand MikroTik devices don't support tacacs but rather solely use RADIUS.  I just updated to Version d7ba2ed3877a6e2a5fc442e8bd8e5e84ce074d8c and added what I thought was the necessary config changes but even with the most verbose logs all I can get out of tac_plus-ng is:

Log for Mikrotik:
8752: 11:47:03.862 e/28010000: 192.168.88.1 radius login for 'demo' from <IP> failed (password not set)

Whereas my regular devices work fine (so I know it's not the Mavis backend), please ignore the superfluous undefined groups the demo account is used for a lot of testing:

8752: 11:45:17.745 6/6555bc00: 10.102.255.2 looking for user demo in MAVIS backend
{ member = "boss_access",
demo:1: Group 'boss_access' not found.
8752: demo:1: Group 'boss_access' not found.
{ member = "boss_access","dhcp_access",
demo:1: Group 'dhcp_access' not found.
8752: demo:1: Group 'dhcp_access' not found.
{ member = "boss_access","dhcp_access","dns_access",
demo:1: Group 'dns_access' not found.
8752: demo:1: Group 'dns_access' not found.
{ member = "boss_access","dhcp_access","dns_access","gadmin",
demo:1: Group 'gadmin' not found.
8752: demo:1: Group 'gadmin' not found.
{ member = "boss_access","dhcp_access","dns_access","gadmin","go_access",
demo:1: Group 'go_access' not found.
8752: demo:1: Group 'go_access' not found.
{ member = "boss_access","dhcp_access","dns_access","gadmin","go_access","ipausers",
demo:1: Group 'ipausers' not found.
8752: demo:1: Group 'ipausers' not found.
{ member = "boss_access","dhcp_access","dns_access","gadmin","go_access","ipausers","nadmin","server_admin",
demo:1: Group 'server_admin' not found.
8752: demo:1: Group 'server_admin' not found.
{ member = "boss_access","dhcp_access","dns_access","gadmin","go_access","ipausers","nadmin","server_admin","webserver_access"
demo:1: Group 'webserver_access' not found.
8752: demo:1: Group 'webserver_access' not found.
8752: 11:45:17.872 6/6555bc00: 10.102.255.2 result for user demo is ACK [127 ms]
8752: 11:45:17.872 6/6555bc00: 10.102.255.2 shell login for 'demo' from <IP> on tty3 succeeded (profile=admin_profile)
8752: 11:45:17.960 7/8f039484: 10.102.255.2 Start authorization request
8752: 11:45:17.960 7/8f039484: 10.102.255.2 user 'demo' found
8752: 11:45:17.960 7/8f039484: 10.102.255.2 nas:service=shell (passed thru)
8752: 11:45:17.960 7/8f039484: 10.102.255.2 nas:cmd* (passed thru)
8752: 11:45:17.960 7/8f039484: 10.102.255.2 nas:absent srv:priv-lvl=15 -> add priv-lvl=15 (k)
8752: 11:45:17.960 7/8f039484: 10.102.255.2 added 1 args
8752: 11:45:18.016 8/2a8d6fb2: 10.102.255.2 Start accounting request

Then I'm logged in as demo with the right permissions.

My Mikrotik has the following settings:
/user aaa
set accounting=yes default-group=read exclude-groups="" interim-update=0s use-radius=yes
/radius add accounting-backup=no accounting-port=1813 address=<IP> authentication-port=1812 called-id="" certificate=none disabled=no domain="" protocol=udp realm="" \
    require-message-auth=yes-for-request-resp service=login timeout=300ms

And finally here is my full test config (some details removed):
id = spawnd {
    listen { port = 49 }
    listen { port = 1812 protocol = UDP }                      # RADIUS Access
    listen { port = 1813 protocol = UDP flag = accounting }    # RADIUS Accounting
}

id = tac_plus-ng {
    log authzlog { destination = /var/log/tac_plus/authz/%Y/%m_%d.log }
    log authclog { destination = /var/log/tac_plus/authc/%Y/%m_%d.log }
    log acctlog  { destination = /var/log/tac_plus/acct/%Y/%m_%d.log }
    log rad-accesslog { destination = /var/log/tac_plus/radius-access/%Y/%m_%d.log }
    log rad-acctlog   { destination = /var/log/tac_plus/radius-acct/%Y/%m_%d.log }
    accounting log = acctlog
    authentication log = authclog
    authorization log = authzlog
    radius.access   log = rad-accesslog
    radius.accounting log = rad-acctlog
    retire limit = 1000

    #ACL Lists
    include = /etc/tac_plus-ng/networks

    device world {
        welcome banner = "\nUNAUTHORIZED ACCESS TO THIS DEVICE IS PROHIBITED\n\nYou must have explicit, authorized permission to access or configure this device. \nUnauthorized attempts and actions to access or use this system may result in civil and/or criminal penalties. \nAll activities performed on this device are logged and monitored.\n\n"
        key = <tacacs key>
        enable 15 = clear password
        address = 0.0.0.0/0
        address = ::/0
        radius.key = test
    }

    device local {
        key = <tacacs key>
        enable 15 = clear password
        address = 10.0.0.0/8
        radius.key = test
    }

    # Define mavis LDAP backend
    mavis module = external {
       setenv LDAP_SERVER_TYPE = "generic"
       setenv LDAP_HOSTS = <HOST>
       setenv LDAP_BASE = <BASE>
       setenv LDAP_SCOPE = <SCOPE>
       setenv LDAP_USER = <USER>
       setenv LDAP_PASSWD = <PASSWORD>
       setenv UNLIMIT_AD_GROUP_MEMBERSHIP = 1
       setenv AD_GROUP_PREFIX = ""
       setenv REQUIRE_TACACS_GROUP_PREFIX = 0
       setenv FLAG_USE_MEMBEROF = 1
       setenv LDAP_MEMBEROF_REGEX = "^cn=net_([^,]+),cn=groups.*"
       exec = /usr/local/lib/mavis/mavis_tacplus_ldap.pl
    }

    login backend = mavis
    user backend  = mavis
    pap backend   = mavis

    #RADIUS dictionary (copied from radius-dict.cfg)
    radius.dictionary {
        attribute User-Name             1       string
        attribute User-Password         2       string
        attribute CHAP-Password         3       octets
        attribute NAS-IP-Address        4       ipv4addr
        attribute NAS-Port              5       integer
        attribute Framed-IP-Address     8       ipv4addr
        attribute Framed-IP-Netmask     9       ipv4addr
        attribute State                 24      octets
        attribute Class                 25      string

        attribute NAS-Port-Type         61      integer
        {
                Async                   0
                Sync                    1
                ISDN                    2
                ISDN-V120               3
                ISDN-V110               4
                Virtual                 5
                PIAFS                   6
                HDLC-Clear-Channel      7
                X.25                    8
                X.75                    9
                G.3-Fax                 10
                SDSL                    11
                ADSL-CAP                12
                ADSL-DMT                13
                IDSL                    14
                Ethernet                15
                xDSL                    16
                Cable                   17
                Wireless-Other          18
                Wireless-802.11         19
        }

        attribute Service-Type 6 integer
        {
                Login-User              1
                Framed-User             2
                Callback-Login-User     3
                Callback-Framed-User    4
                Outbound-User           5
                Administrative-User     6
                NAS-Prompt-User         7
                Authenticate-Only       8
                Callback-NAS-Prompt     9
                Call-Check              10
                Callback-Administrative 11
                Authorize-Only          17
                Framed-Management       18
        }

        attribute Login-IP-Host         14      ipv4addr
        attribute Login-Service         15      integer
        attribute Login-TCP-Port        16      integer
        attribute Reply-Message         18      string
        attribute Vendor-Specific       26      vsa
        attribute Called-Station-Id     30      string
        attribute Calling-Station-Id    31      string
        attribute NAS-Identifier        32      string

        attribute NAS-Port-Type         61      integer
        {
                Async                   0
                Sync                    1
                Virtual                 5
        }

        attribute Message-Authenticator 80      octets
        attribute NAS-Port-Id           87      string
        attribute NAS-IPv6-Address      95      ipv6addr
        attribute Framed-IPv6-Route     99      string
        attribute Framed-IPv6-Pool      100     string
        attribute Framed-IPv6-Address   168     ipv6addr

        attribute Acct-Status-Type      40      integer
        {
                Start                   1
                Stop                    2
                Interim-Update          3
                Accounting-On           7
                Accounting-Off          8
        }

        attribute Acct-Delay-Time       41      integer
        attribute Acct-Input-Octets     42      integer
        attribute Acct-Output-Octets    43      integer
        attribute Acct-Session-Id       44      string

        attribute Acct-Authentic        45      integer
        {
                RADIUS                  1
                Local                   2
                Remote                  3
        }
        attribute Acct-Session-Time     46      integer
        attribute Acct-Input-Packets    47      integer
        attribute Acct-Output-Packets   48      integer

        attribute Acct-Terminate-Cause  49      integer
        {
                User-Request            1
                Lost-Carrier            2
                Lost-Service            3
                Idle-Timeout            4
                Session-Timeout         5
                Admin-Reset             6
                Admin-Reboot            7
                Port-Error              8
                NAS-Error               9
                NAS-Request             10
                NAS-Reboot              11
                Port-Unneeded           12
                Port-Preempted          13
                Port-Suspended          14
                Service-Unavailable     15
                Callback                16
                User-Error              17
                Host-Request            18
        }

        attribute Acct-Multi-Session-Id 50      string
        attribute Acct-Link-Count       51      integer
        attribute Event-Timestamp       55      time
    }
    #Mikrotik specific dictionary (copied from wiki.mikrotik.com/Manual:RADIUS_Client/reference_dictionary)
    radius.dictionary mikrotik 14988 {
        attribute Mikrotik-Group       3   string
    }

    # Define groups
    group nadmin {
    }

    group tech {
    }

    # Define profiles
    profile admin_profile {
        script {
            if (service == junos-exec) {
                set local-user-name = <admin_username>
            }
            if (service == shell) {
                if (cmd == "") {
                    set priv-lvl = 15
                }
            }
            # When the session is RADIUS (RouterOS), place user in MikroTik group "full"
            if (aaa.protocol == radius) {
                set radius[mikrotik:Mikrotik-Group] = "full"
            }
            permit
        }
    }

    profile tech_profile {
        script {
            if (service == junos-exec) {
                set local-user-name = <tech_username>
            }
            if (service == shell) {
                if (cmd == "") {
                    set priv-lvl = 7
                }
            }
            # When the session is RADIUS (RouterOS), place user in MikroTik group "read"
            if (aaa.protocol == radius) {
                set radius[mikrotik:Mikrotik-Group] = "read"
            }
            permit
        }
    }

    # Ruleset to assign profiles to users
    ruleset {
        rule from-trusted-nets {
            enabled = yes
            script {
                if (group == nadmin) {
                    profile = admin_profile
                    permit
                }
            }
            enabled = yes
            script {
                if (group == tech) {
                    profile = tech_profile
                    permit
                }
            }
        }
    }
}

Marc Huber

unread,
Sep 18, 2025, 1:02:17 PM (6 days ago) Sep 18
to event-driv...@googlegroups.com

Hi,

the "password not set" might result from authen.c:do_radius_login() not actually seeing a RADIUS password attribute. Could you capture the initial RADIUS ACCEPT-REQUEST packet and share it? I understand that there might be privacy concerns, so sending the .pcap to my personal email is fine.

Cheers,

Marc

--
You received this message because you are subscribed to the Google Groups "Event-Driven Servers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to event-driven-ser...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/event-driven-servers/101fbee5-47cd-40a4-bef9-d4796124d845n%40googlegroups.com.

David Fisher

unread,
Sep 18, 2025, 2:14:14 PM (6 days ago) Sep 18
to Event-Driven Servers
I should have thought to include the capture, it's mostly internal lab stuff so I'll just paste it in here:
sudo tcpdump -v 'udp port 1812'
tcpdump: listening on enp1s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
13:52:31.582873 IP (tos 0x0, ttl 62, id 28949, offset 0, flags [DF], proto UDP (17), length 189)
    192.168.88.1.38660 > go1.<NETWORK>.net.radius: RADIUS, length: 161
        Access-Request (1), id: 0x37, Authenticator: 4581f87a1b6097582bf45448155ab3c4
          Service-Type Attribute (6), length: 6, Value: Login
          User-Name Attribute (1), length: 6, Value: demo
          Vendor-Specific Attribute (26), length: 24, Value: Vendor: Microsoft (311)
            Vendor Attribute: 11, Length: 16, Value: .._....tlk\.. ]3
          Vendor-Specific Attribute (26), length: 58, Value: Vendor: Microsoft (311)
            Vendor Attribute: 25, Length: 50, Value: .....w.UW..J..8.dV..........`.R..`...1..fY.C_....g
          Calling-Station-Id Attribute (31), length: 15, Value: <IP>
          NAS-Identifier Attribute (32), length: 8, Value: iLabMT
          NAS-IP-Address Attribute (4), length: 6, Value: 192.168.88.1
          Message-Authenticator Attribute (80), length: 18, Value: L..
13:52:31.583517 IP (tos 0x0, ttl 64, id 65511, offset 0, flags [DF], proto UDP (17), length 66)
    go1.<NETWORK>.net.radius > 192.168.88.1.38660: RADIUS, length: 38
        Access-Reject (3), id: 0x37, Authenticator: 33a586109ebe2b763cb4fb3fd81563fd
          Message-Authenticator Attribute (80), length: 18, Value: ...r.....A...&..
13:52:33.804784 IP (tos 0x0, ttl 62, id 29092, offset 0, flags [DF], proto UDP (17), length 189)
    192.168.88.1.49849 > go1.<NETWORK>.net.radius: RADIUS, length: 161
        Access-Request (1), id: 0x38, Authenticator: 0570ce6fded010f0e4c2fc7f8ba5e206
          Service-Type Attribute (6), length: 6, Value: Login
          User-Name Attribute (1), length: 6, Value: demo
          Vendor-Specific Attribute (26), length: 24, Value: Vendor: Microsoft (311)
            Vendor Attribute: 11, Length: 16, Value: ..!.W....F.....W
          Vendor-Specific Attribute (26), length: 58, Value: Vendor: Microsoft (311)
            Vendor Attribute: 25, Length: 50, Value: ...[.X7c._.q20..T.........FV....2.....0.=..MD`...W
          Calling-Station-Id Attribute (31), length: 15, Value: <IP>
          NAS-Identifier Attribute (32), length: 8, Value: iLabMT
          NAS-IP-Address Attribute (4), length: 6, Value: 192.168.88.1
          Message-Authenticator Attribute (80), length: 18, Value: .&.X..+. ..y;h..
13:52:33.805445 IP (tos 0x0, ttl 64, id 6044, offset 0, flags [DF], proto UDP (17), length 66)
    go1.<NETWORK>.net.radius > 192.168.88.1.49849: RADIUS, length: 38
        Access-Reject (3), id: 0x38, Authenticator: cfa5896bf5bd93ebf37d956b44fa024c
          Message-Authenticator Attribute (80), length: 18, Value: .-6l,. .2.....2.

Looks like it's using MS-CHAP2 (Microsoft attrib. 25 & 26) and to quote your own documentation "Marc Huber thinks that MSCHAP relevance is less than zero" LOL. Nevertheless I'd love to get this config working

Marc Huber

unread,
Sep 19, 2025, 7:34:58 AM (6 days ago) Sep 19
to event-driv...@googlegroups.com

Hi David,

alas, the only RADIUS authentication type implemented in tac_plus-ng is PAP. Your Mikrotoc devices tries (I'm guessing) MS-CHAP or something similiar. Reconfiguring the router to use PAP might be an option in case that's supported.

Cheers,

Marc

David Fisher

unread,
Sep 19, 2025, 7:48:00 AM (6 days ago) Sep 19
to Event-Driven Servers
Yeah, that's what I feared when seeing that TCP dump and the docs. Looking at Mikrotik support I see numerous unanswered pleas over many years asking for PAP support or tacacs support or anything other than MS-CHAP so looks like I'm building a parallel auth system.

As unfortunate as it is I appreciate you looking over my config and situation and confirming my suspicions for me

Thanks!

Marc Huber

unread,
Sep 21, 2025, 9:50:05 AM (4 days ago) Sep 21
to event-driv...@googlegroups.com

Hi,

as of today the tac_plus-ng RADIUS implementation recognizes MSCHAPv2. The MSCHAP user password needs to be set for authentication to work, and (due to the nature of CHAP algorithms) has be in clear text.

password ms-chap = clear demo

Cheers,

Marc

David Fisher

unread,
Sep 22, 2025, 10:25:24 AM (3 days ago) Sep 22
to Event-Driven Servers
Wow, thanks! That's above and beyond anything I expected. And it gets me into my test Mikrotik device with my predefined demo user at the right privilege level as expected:
704245: 08:42:38.293 0/67010000: 192.168.88.1 radius login for 'demo' from <TAC_SERVER_IP> succeeded (profile=tech_profile)

One issue though: It appears that something in this change breaks my mavis backend for all my other devices, even when I go back to an old pre-radius config (that works on an un-updated server) all mavis authenticated users are getting:
707667: 09:32:43.497 2/91dcfca7: <NETWORK_IP> looking for user dfisher realm default
707667: 09:32:43.497 2/91dcfca7: <NETWORK_IP> user lookup failed
707667: 09:32:43.497 2/91dcfca7: <NETWORK_IP> shell login for 'dfisher' from <TAC_SERVER_IP> on tty2 denied by ACL

Yet the predefined static user list works

Marc Huber

unread,
Sep 22, 2025, 2:18:36 PM (2 days ago) Sep 22
to event-driv...@googlegroups.com

Hi,

thanks for reporting, I think bd520e628e4e640df4a4ec94c8a4dde9b8d6d84f will fix that issue.

Thanks,

Marc

David Fisher

unread,
Sep 22, 2025, 2:56:25 PM (2 days ago) Sep 22
to Event-Driven Servers
Testing shows it works like a dream.  

Truly, much thanks for getting this working for me.

Reply all
Reply to author
Forward
0 new messages