Skip to content

Conversation

@nmoray
Copy link

@nmoray nmoray commented Jun 25, 2025

  • What I did
    Made use of Security Cipher module to decrypt the encrypted TACPLUS passkey

  • How I did it
    Implemented the feature by following HLD

  • How to verify it

1. Encrypt passkey:
sonic# config tacacs passkey nikhil --encrypt
sonic# show run al | jq .TACPLUS
{
  "global": {
    "auth_type": "login",
    "key_encrypt": "True",
    "passkey": "U2FsdGVkX19YTUcG+0r3+d78A5E0zTXEukS3ZNrfyFk=",
    "src_intf": "Loopback0"
  }
}
sonic# cat /etc/pam.d/common-auth-sonic
#THIS IS AN AUTO-GENERATED FILE
#
# /etc/pam.d/common-auth- authentication settings common to all services
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# here are the per-package modules (the "Primary" block)

auth	[success=done new_authtok_reqd=done default=ignore auth_err=die]	pam_tacplus.so server=<> secret=nikhil login=login timeout=5   try_first_pass
auth	[success=done new_authtok_reqd=done default=ignore auth_err=die]	pam_tacplus.so server=<> secret=nikhil login=login timeout=5   try_first_pass
auth	[success=1 default=ignore]	pam_unix.so nullok try_first_pass

#
# here's the fallback if no module succeeds
auth    requisite                       pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth    required                        pam_permit.so
# and here are more per-package modules (the "Additional" block)
sonic# cat /etc/cipher_pass.json
{
  "TACPLUS": {
    "table_info": [
      "TACPLUS|global"
    ],
    "password": "TEST1"
  }

2. Encrypt passkey and update existing password
sonic# config tacacs passkey nikhil --encrypt --rotate
Password:
sonic# cat /etc/cipher_pass.json
{
  "TACPLUS": {
    "table_info": [
      "TACPLUS|global"
    ],
    "password": "TEST3"
  }
sonic# cat /etc/pam.d/common-auth-sonic
#THIS IS AN AUTO-GENERATED FILE
#
# /etc/pam.d/common-auth- authentication settings common to all services
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# here are the per-package modules (the "Primary" block)

auth	[success=done new_authtok_reqd=done default=ignore auth_err=die]	pam_tacplus.so server=<> secret=nikhil login=login timeout=5   try_first_pass
auth	[success=done new_authtok_reqd=done default=ignore auth_err=die]	pam_tacplus.so server=<> secret=nikhil login=login timeout=5   try_first_pass
auth	[success=1 default=ignore]	pam_unix.so nullok try_first_pass

#
# here's the fallback if no module succeeds
auth    requisite                       pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth    required                        pam_permit.so
# and here are more per-package modules (the "Additional" block)
sonic# show run al | jq .TACPLUS
{
  "global": {
    "auth_type": "login",
    "key_encrypt": "True",
    "passkey": "U2FsdGVkX18/8nFrAiCe01pOqgPoUUZFaTpmtawdi8I=",
    "src_intf": "Loopback0"
  }
}

3. Revert back to plaintext passkey
sonic# config tacacs passkey ravi
sonic# cat /etc/pam.d/common-auth-sonic
#THIS IS AN AUTO-GENERATED FILE
#
# /etc/pam.d/common-auth- authentication settings common to all services
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# here are the per-package modules (the "Primary" block)

auth	[success=done new_authtok_reqd=done default=ignore auth_err=die]	pam_tacplus.so server=<> secret=ravi login=login timeout=5   try_first_pass
auth	[success=done new_authtok_reqd=done default=ignore auth_err=die]	pam_tacplus.so server=<> secret=ravi login=login timeout=5   try_first_pass
auth	[success=1 default=ignore]	pam_unix.so nullok try_first_pass

#
# here's the fallback if no module succeeds
auth    requisite                       pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth    required                        pam_permit.so
# and here are more per-package modules (the "Additional" block)
sonic# show run al | jq .TACPLUS
{
  "global": {
    "auth_type": "login",
    "key_encrypt": "False",
    "passkey": "ravi",
    "src_intf": "Loopback0"
  }
}
sonic# cat /etc/cipher_pass.json
{
  "TACPLUS": {
    "table_info": [],
    "password": null
  }

Note:: This PR is created by closing the old PR

@mssonicbld
Copy link

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@nmoray
Copy link
Author

nmoray commented Jun 26, 2025

@anders-nexthop please help in adding the reviewers.

@mssonicbld
Copy link

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link

@anders-nexthop anders-nexthop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One comment.

I am not very familiar with this code, but I'm wondering if this is the right place to decrypt the passkey? isn't this just going to generate more config files? would it be better to patch the actual TACACS server code to decrypt the passkey, so we aren't ever storing it in plaintext in config files?

server['ip'] = addr
server.update(self.tacplus_servers[addr])
if 'key_encrypt' in server and server['key_encrypt'] == 'True':
output = secure_cipher.decrypt_passkey("TACPLUS", server['passkey'])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldnt this be TACPLUS_SERVER?

@anders-nexthop
Copy link

@qiluo-msft please take a look at this review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants