tacplus-ng: Denying empty commits on Junos

46 views
Skip to first unread message

Davide Parpinello

unread,
Mar 31, 2025, 11:17:52 AMMar 31
to Event-Driven Servers
Hello,
I'm trying to implement some rule to deny empty commit messages in my tacplus-ng server, but seems that all of my attempts are ignored. Mainly every user would be admin, as it's already filtered by LDAP, so I would like this to be applied to every user and every device.

Hoping to get some guidance :)

#!/usr/sbin/tac_plus-ng
id = spawnd {
        listen = { address = 0.0.0.0 port = 49 }
        spawn = {
                instances min = 1
                instances max = 10
        }
        background = yes
}

id = tac_plus-ng {
        mavis module = external {
                setenv LDAP_SERVER_TYPE = "generic"
                setenv LDAP_HOSTS = "<REDACTED>"
                setenv LDAP_BASE = "<REDACTED>"
                setenv LDAP_USER = "${LDAP_USER}"
                setenv LDAP_PASSWD = "${LDAP_PASSWD}"
                setenv LDAP_FILTER = "${LDAP_FILTER}"
                exec = /usr/local/lib/mavis/mavis_tacplus-ng_ldap.pl
        }

        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 }
        accounting log = acctlog
        authentication log = authclog
        authorization log = authzlog

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

        device = world {
                welcome banner = "\nAccess is restricted to authorized users\n\n"
                welcome banner fallback = "\nAccess is restricted to authorized users\n\nEmergency mode enabled: no LDAP auth\n\n"
                enable 15 = clear secret
                key = "${TAC_KEY}"
                device net {
                        address = 192.168.0.0/22
                }
        }

        ruleset {
            rule {
                script {
                    if (cmd =~ "^commit") {
                        if (!(cmd =~ "comment")) {
                            deny
                            message = "Commit rejected: Comment required (use 'commit comment \"message\"')"
                        }
                    }
                }
            }
            rule {
                script {
                    set priv-lvl = 15
                    permit
                }
            }
        }

        profile admins {
                script {
                        if (service == shell || service == junos-exec) {
                                if (cmd == "") { set priv-lvl = 15 }
                                if (cmd =~ "^commit") {
                                        deny
                                        message = "Commit rejected: Comment required (use 'commit comment \"message\"')"
                                }
                        }
                }
        }

        group admins

        user superadmin {
                password login = crypt "${SUPER_PASSWD}"
                member = admins
                profile = admins
                fallback-only
        }
}

Marc Huber

unread,
Mar 31, 2025, 12:59:52 PMMar 31
to event-driv...@googlegroups.com

Hi Davide,

last I've checked Junos supported mapping a TACACS+ user to a local user (or local authorization profile, I don't remember that well), but command authorization wasn't available at all. Are you certain that's now an option?

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/ae8bd20d-1a1a-4479-bc99-6ad414169f8cn%40googlegroups.com.

Davide Parpinello

unread,
Apr 1, 2025, 2:37:13 AMApr 1
to Event-Driven Servers
Hello Mark,

So I would need to test adding those. I would just wondering if I should use service junos-exec, or shell, because I recall it should be the former but the accounting logs in tacplus-ng says it's just shell.

Thanks

Marc Huber

unread,
Apr 1, 2025, 9:19:55 AMApr 1
to event-driv...@googlegroups.com
Hi Davide,

I don't have a Junos device available right now, so this is mostly from
memory: the service name defaults to junos-exec (and can be changed with
"set service-name").

            if (service == junos-exec) {
                set local-user-name = ...
                permit
            }

Cheers,

Marc

Davide Parpinello

unread,
Apr 8, 2025, 6:37:28 AMApr 8
to Event-Driven Servers
Hi Marc,
seems that I cannot find the correct place to add those directives in my config. Please see in my first message how the config looks, just remove my attempts to deny commands.

I don't understand if it should go in the ruleset, in the profile admins script, or maybe in the devices list.

Thanks
D.

Marc Huber

unread,
Apr 8, 2025, 10:48:22 AMApr 8
to event-driv...@googlegroups.com

Hi Davide,

that "set" should be part of a profile declaration:

    profile profile_name {
        script {
            if (service == junos-exec) {
                set local-user-name = local_user_name
                permit
            }
        }
    }

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.
Message has been deleted

Davide Parpinello

unread,
Apr 9, 2025, 8:25:36 AMApr 9
to Event-Driven Servers
Hi Marc, 
I tried to add those rules and did multiple tries changing parameters, but seems I can't get it working. I also noticed that the username doesn't become "remote" as I'm setting, and the tacplus-ng logs are getting this:

2025-04-09 11:39:23 +0000 <SWITCH_IP> <MY_LDAP_USERNAME>                <CLIENT_IP>            deny    junos-exec

This is my config at the moment, I'm wondering if some reason the profile doesn't get hooked to the user. I tried with both service == shell and service == junos-exec, but doesn't change.

#!/usr/sbin/tac_plus-ng
id = spawnd {
        listen = { address = 0.0.0.0 port = 49 }
        spawn = {
                instances min = 1
                instances max = 10
        }
        background = yes
}

id = tac_plus-ng {
        mavis module = external {
                setenv LDAP_SERVER_TYPE = "generic"
                setenv LDAP_HOSTS = "<SERVER>
                setenv LDAP_BASE = "<LDAPBASE>"
                setenv LDAP_USER = "${LDAP_USER}"
                setenv LDAP_PASSWD = "${LDAP_PASSWD}"
                setenv LDAP_FILTER = "${LDAP_FILTER}"
                exec = /usr/local/lib/mavis/mavis_tacplus-ng_ldap.pl
        }

        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 }
        accounting log = acctlog
        authentication log = authclog
        authorization log = authzlog

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

        device = world {
                welcome banner = "\n--ACCESS --\nAccess is restricted to authorized users\n\n"
                welcome banner fallback = "\n--ACCESS --\nAccess is restricted to authorized users\n\nEmergency mode enabled: no LDAP auth\n\n"
                enable 15 = clear secret
                key = "${TAC_KEY}"
                device net {
                        address = 192.168.1.0/24
                }
        }

        ruleset {
            rule {
                script {
                    set priv-lvl = 15
                    permit
                }
            }
        }

        profile admins {
                script {
                        if (service == shell || service == junos-exec) {
                                if (cmd == "") { set priv-lvl = 15 }
                        }
                        if (service == shell) {
                                set local-user-name = remote
                                set allow-commands = "(commit.*[a-zA-Z0-9].*)"
                                set allow-configuration-regexps = "(system commit-message)"
                                set deny-commands = "(^commit$)"
                                set deny-configuration-regexps = ""
                                permit
                        }
                }
        }

        user superadmin {
                password login = crypt "${SUPER_PASSWD}"
                profile = admins
                fallback-only
        }
}

Thanks a lot for your help as always :)

D.

Marc Huber

unread,
Apr 9, 2025, 12:17:00 PMApr 9
to event-driv...@googlegroups.com

Hi Davide,

I'd try


        profile admins {
                script {
                        if (service == shell) {
                                # Cisco

                                if (cmd == "") {
                                    set priv-lvl = 15
                                }
                                permit
                        }
                        if (service == junos-exec) {
                                # Junos

                                set local-user-name = remote
                                set allow-commands = "(commit.*[a-zA-Z0-9].*)"
                                set allow-configuration-regexps = "(system commit-message)"
                                set deny-commands = "(^commit$)"
                                set deny-configuration-regexps = ""
                                permit
                        }
                }
        }

I have no Junos system available, so this is based on guessing. And I didn't check your regular expressions.

Cheers,

Marc
Reply all
Reply to author
Forward
0 new messages