This documentation corresponds to an older version of the product, is no longer updated, and may contain outdated information.
Please access the latest versions from https://cisco-tailf.gitbook.io/nso-docs and update your bookmarks. OK
This chapter describes how to use NSO's built-in authentication and authorization mechanisms. Users log into NSO through the CLI, NETCONF, RESTCONF, SNMP, or via the Web UI. In either case, users need to be authenticated. That is, a user needs to present credentials, such as a password or a public key in order to gain access. As an alternative for RESTCONF, users can be authenticated via token validation.
Once a user is authenticated, all operations performed by that user need to be authorized. That is, certain users may be allowed to perform certain tasks, whereas others are not. This is called authorization. We differentiate between authorization of commands and authorization of data access.
The NSO daemon manages device configuration including AAA information. In fact, NSO both manages AAA information and uses it. The AAA information describes which users may login, what passwords they have and what they are allowed to do.
This is solved in NSO by requiring a data model to be both
loaded and populated with data. NSO uses the YANG module
tailf-aaa.yang
for authentication, while
ietf-netconf-acm.yang
(NACM, RFC 8341)
as augmented by tailf-acm.yang
is used for
group assignment and authorization.
The NACM data model is targeted specifically towards access
control for NETCONF operations, and thus lacks some
functionality that is needed in NSO, in particular support for
authorization of CLI commands and the possibility to specify the
"context" (NETCONF/CLI/etc) that a given authorization rule
should apply to. This functionality is modeled by augmentation
of the NACM model, as defined in the
tailf-acm.yang
YANG module.
The ietf-netconf-acm.yang
and
tailf-acm.yang
modules can be found in
$NCS_DIR/src/ncs/yang
directory in the release, while
tailf-aaa.yang
can be found in
the $NCS_DIR/src/ncs/aaa
directory.
NACM options related to services are modeled by augmentation of the
NACM model, as defined in the tailf-ncs-acm.yang
YANG module.
The tailf-ncs-acm.yang
can be found in
$NCS_DIR/src/ncs/yang
directory in the release.
The complete AAA data model defines a set of users, a set of groups and a set of rules. The data model must be populated with data that is subsequently used by by NSO itself when it authenticates users and authorizes user data access. These YANG modules work exactly like all other fxs files loaded into the system with the exception that NSO itself uses them. The data belongs to the application, but NSO itself is the user of the data.
Since NSO requires a data model for the AAA information for its operation, it will report an error and fail to start if these data models can not be found.
NSO itself is configured through a configuration file -
ncs.conf
.
In that file we have the following
items related to authentication and authorization:
/ncs-config/aaa/ssh-server-key-dir
If SSH termination is enabled for NETCONF or the CLI, the
NSO built-in SSH server needs to have server keys. These
keys are generated by the NSO install script and by
default end up in
$NCS_DIR/etc/ncs/ssh
.
It is also possible to use OpenSSH to terminate NETCONF or the CLI. If OpenSSH is used to terminate SSH traffic, this setting has no effect.
/ncs-config/aaa/ssh-pubkey-authentication
If SSH termination is enabled for NETCONF or the CLI, this item controls how the NSO SSH daemon locates the user keys for public key authentication. See the section called “Public Key Login” for the details.
/ncs-config/aaa/local-authentication/enabled
The term "local user" refers to a user stored
under /aaa/authentication/users
. The alternative
is a user unknown to NSO, typically authenticated by PAM.
By default, NSO first checks local users before trying PAM or external authentication.
Local authentication is practical in test environments. It is also useful when we want to have one set of users that are allowed to login to the host with normal shell access and another set of users that are only allowed to access the system using the normal encrypted, fully authenticated, northbound interfaces of NSO.
If we always authenticate users through PAM, it may make
sense to set this configurable to false
. If we
disable local authentication, it implicitly means that we
must use either PAM authentication or "external
authentication". It also means that we can leave the entire
data trees under /aaa/authentication/users
and,
in the case of "external auth" also /nacm/groups
(for NACM) or /aaa/authentication/groups
(for
legacy tailf-aaa) empty.
/ncs-config/aaa/pam
NSO can authenticate users using PAM (Pluggable Authentication Modules). PAM is an integral part of most Unix-like systems.
PAM is a complicated - albeit powerful - subsystem. It may be easier to have all users stored locally on the host. However, if we want to store users in a central location, PAM can be used to access the remote information. PAM can be configured to perform most login scenarios including RADIUS and LDAP. One major drawback with PAM authentication is that there is no easy way to extract the group information from PAM. PAM authenticates users; it does not assign a user to a set of groups.
PAM authentication is thoroughly described later in this chapter.
/ncs-config/aaa/default-group
If this configuration parameter is defined and if the group of a user cannot be determined, a logged in user ends up in the given default group.
/ncs-config/aaa/external-authentication
NSO can authenticate users using an external executable. This is further described in the the section called “External authentication” section.
As an alternative, you may consider using package authentication.
/ncs-config/aaa/external-validation
NSO can authenticate users by validation of tokens using an external executable. This is further described in the the section called “External token validation” section. Where external authentication uses a username and password to authenticate a user, external validation uses a token. The validation script should use the token to authenticate a user and can, optionally, also return a new token to be returned with the result of the request. It is currently only supported for RESTCONF.
/ncs-config/aaa/external-challenge
NSO has support for multifactor authentication by sending challenges to a user. Challenges may be sent from any of the external authentication mechanisms but is currently only supported by JSONRPC and CLI over SSH. This is further described in the the section called “External multi factor authentication” section.
/ncs-config/aaa/package-authentication
NSO can authenticate users using package authentication. It extends the concept of external authentication by allowing multiple packages to be used for authentication instead of a single executable. This is further described in the the section called “Package Authentication” section.
/ncs-config/aaa/single-sign-on
With this setting enabled, NSO invokes Package
Authentication on all requests to HTTP endpoints with the
/sso
prefix.
This way, Package Authentication packages that require custom
endpoints can expose them under the /sso
base route.
For example, a SAMLv2 Single Sign-On (SSO) package needs to
process requests to an AssertionConsumerService endpoint,
such as /sso/saml/acs
, and therefore
requires enabling this setting.
This is a valid authentication method for webui and JSON-RPC interfaces and needs Package Authentication to be enabled as well.
/ncs-config/aaa/single-sign-on/enable-automatic-redirect
If only one Single Sign-On package is configured (a package with single-sign-on-url set in package-meta-data.xml) and also this setting is enabled, NSO automatically redirects all unauthenticated access attempts to the configured single-sign-on-url.
Depending on northbound management protocol, when a user session is created in NSO, it may or may not be authenticated. If the session is not yet authenticated, NSO's AAA subsystem is used to perform authentication and authorization, as described below. If the session already has been authenticated, NSO's AAA assigns groups to the user as described in the section called “Group Membership”, and performs authorization, as described in the section called “Authorization”.
The authentication part of the data model can be found in tailf-aaa.yang:
container authentication { tailf:info "User management"; container users { tailf:info "List of local users"; list user { key name; leaf name { type string; tailf:info "Login name of the user"; } leaf uid { type int32; mandatory true; tailf:info "User Identifier"; } leaf gid { type int32; mandatory true; tailf:info "Group Identifier"; } leaf password { type passwdStr; mandatory true; } leaf ssh_keydir { type string; mandatory true; tailf:info "Absolute path to directory where user's ssh keys may be found"; } leaf homedir { type string; mandatory true; tailf:info "Absolute path to user's home directory"; } } } }
AAA authentication is used in the following cases:
When the built-in SSH server is used for NETCONF and CLI sessions.
For Web UI sessions and REST access.
When the method
Maapi.Authenticate()
is used.
NSO's AAA authentication is not used in the following cases:
When NETCONF uses an external SSH daemon, such as OpenSSH.
In this case, the NETCONF session is initiated using the program netconf-subsys, as described in the section called “NETCONF Transport Protocols” in Northbound APIs .
When NETCONF uses TCP, as described in the section called “NETCONF Transport Protocols” in Northbound APIs , e.g. through the command netconf-console.
When the CLI uses an external SSH daemon, such as OpenSSH, or a telnet daemon.
In this case, the CLI session is initiated through the
command
ncs_cli.
An important special case here is when a user has logged in to
the host and invokes the command
ncs_cli from the shell.
In NSO deployments, it is crucial to consider this case. If non trusted
users have shell access to the host, the
NCS_IPC_ACCESS_FILE
feature as described in
the section called “Restricting access to the IPC port”
must be used.
When SNMP is used. SNMP has its own authentication mechanisms. See The NSO SNMP Agent in Northbound APIs .
When the method
Maapi.startUserSession()
is used
without a preceding call of
Maapi.authenticate()
.
When a user logs in over NETCONF or the CLI using the built-in SSH server, with public key login, the procedure is as follows.
The user presents a username in accordance with the SSH protocol.
The SSH server consults the settings for
/ncs-config/aaa/ssh-pubkey-authentication
and
/ncs-config/aaa/local-authentication/enabled
.
If
ssh-pubkey-authentication
is set to local
, and the SSH keys in
/aaa/authentication/users/user{$USER}/ssh_keydir
match the keys presented by the user, authentication
succeeds.
Otherwise, if
ssh-pubkey-authentication
is set to system
,
local-authentication
is enabled, and the SSH keys in
/aaa/authentication/users/user{$USER}/ssh_keydir
match the keys presented by the user, authentication
succeeds.
Otherwise, if
ssh-pubkey-authentication
is set to system
and the user
/aaa/authentication/users/user{$USER}
does not
exist, but the user does exist in the OS password database,
the keys in the user's $HOME/.ssh
directory are
checked. If these keys match the keys presented by the user,
authentication succeeds.
Otherwise, authentication fails.
In all cases the keys are expected to be stored in a file called
authorized_keys
(or
authorized_keys2
if
authorized_keys
does not exist), and in the
native OpenSSH format (i.e. as generated by the OpenSSH
ssh-keygen command).
If authentication succeeds, the user's group membership is
established as described in
the section called “Group Membership”.
This is exactly the same procedure that is used by the OpenSSH
server with the exception that the built-in SSH server
also may locate the directory containing the public keys for a
specific user by consulting the
/aaa/authentication/users
tree.
We need to provide a directory where SSH keys are kept for a
specific user, and give the absolute path to this directory for
the /aaa/authentication/users/user/ssh_keydir
leaf. If public key login is not desired at all for a user, the
value of the ssh_keydir
leaf should be set to
""
, i.e. the empty string. Similarly, if the
directory does not contain any SSH keys, public key logins for
that user will be disabled.
The built-in SSH daemon supports DSA, RSA and ED25519 keys. To generate and enable RSA keys of size 4096 bits for, say, user "bob", the following steps are required.
On the client machine, as user "bob", generate a private/public key pair as:
# ssh-keygen -b 4096 -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/bob/.ssh/id_rsa): Created directory '/home/bob/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/bob/.ssh/id_rsa. Your public key has been saved in /home/bob/.ssh/id_rsa.pub. The key fingerprint is: ce:1b:63:0a:f9:d4:1d:04:7a:1d:98:0c:99:66:57:65 bob@buzz # ls -lt ~/.ssh total 8 -rw------- 1 bob users 3247 Apr 4 12:28 id_rsa -rw-r--r-- 1 bob users 738 Apr 4 12:28 id_rsa.pub
Now we need to copy the public key to the target machine where the NETCONF or CLI SSH client runs.
Assume we have the following user entry:
<user> <name>bob</name> <uid>100</uid> <gid>10</gid> <password>$1$feedbabe$nGlMYlZpQ0bzenyFOQI3L1</password> <ssh_keydir>/var/system/users/bob/.ssh</ssh_keydir> <homedir>/var/system/users/bob</homedir> </user>
We need to copy the newly generated file
id_rsa.pub
, which is the public key, to
a file on the target machine called
/var/system/users/bob/.ssh/authorized_keys
Since the release of
OpenSSH 7.0
support of ssh-dss
host and user keys is disabled
by default. If you want to continue using these, you may
re-enable it using the following options for OpenSSH client:
HostKeyAlgorithms=+ssh-dss PubkeyAcceptedKeyTypes=+ssh-dss
You may find full instructions at OpenSSH Legacy Options webpage.
Password login is triggered in the following cases:
When a user logs in over NETCONF or the CLI using the built in SSH server, with a password. The user presents a username and a password in accordance with the SSH protocol.
When a user logs in using the Web UI. The Web UI asks for a username and password.
When the method Maapi.authenticate()
is used.
In this case, NSO
will by default try local authentication,
PAM, external authentication and package authentication in that order,
as described below. It is possible to change the order in which these are
tried, by modifying the
ncs.conf
.
parameter
/ncs-config/aaa/auth-order
.
See
ncs.conf(5) in Manual Pages
for details.
If /aaa/authentication/users/user{$USER}
exists and the presented password matches the encrypted
password in
/aaa/authentication/users/user{$USER}/password
the user is authenticated.
If the password does not match or if the user does not
exist in /aaa/authentication/users
, PAM login
is attempted, if enabled. See
the section called “PAM”
for details.
If all of the above fails and external authentication is enabled, the configured executable is invoked. See the section called “External authentication” for details.
If authentication succeeds, the user's group membership is established as described in the section called “Group Membership”.
On operating systems supporting PAM, NSO also supports PAM authentication. Using PAM authentication with NSO can be very convenient since it allows us to have the same set of users and groups having access to NSO as those that have access to the UNIX/Linux host itself.
If we use PAM, we do not have to have any users or any groups configured in the NSO aaa namespace at all. To configure PAM we typically need to do the following:
Remove all users and groups from the aaa initialization XML file.
Enable PAM in ncs.conf by adding:
<pam> <enabled>true</enabled> <service>common-auth</service> </pam>
to the aaa section in
ncs.conf. The service
name
specifies the PAM service, typically a file in the directory
/etc/pam.d
, but may alternatively be an
entry in a file /etc/pam.conf
, depending on
OS and version. Thus it is possible to have a different login
procedure to NSO
than to the host itself.
If pam is enabled and we want to use pam for login
the system may have to run as root. This depends on how
pam is configured locally. However the default "system-auth"
will typically require root since the pam libraries then read
/etc/shadow
. If we don't want to run
NSO as root,
the solution here is to change owner of a helper program called
$NCS_DIR/lib/ncs/lib/core/pam/priv/epam
and also set the setuid bit.
# cd $NCS_DIR/lib/ncs/lib/core/pam/priv/ # chown root:root epam # chmod u+s epam
PAM is the recommended way to authenticate NSO users.
As an example, say that we have user test in /etc/passwd
,
and furthermore:
# grep test /etc/group operator:x:37:test admin:x:1001:test
thus, the test user is part of the admin and the operator groups and logging in to NSO as the test user, through CLI ssh, Web UI, or netconf renders the following in the audit log.
<INFO> 28-Jan-2009::16:05:55.663 buzz ncs[14658]: audit user: test/0 logged in over ssh from 127.0.0.1 with authmeth:password <INFO> 28-Jan-2009::16:05:55.670 buzz ncs[14658]: audit user: test/5 assigned to groups: operator,admin <INFO> 28-Jan-2009::16:05:57.655 buzz ncs[14658]: audit user: test/5 CLI 'exit'
Thus, the test user was found and authenticated from
/etc/passwd
, and the crucial group assignment of the test user
was done from /etc/group
.
If we wish to be able to also manipulate the users, their
passwords etc on the device we can write a private YANG model
for that data, store that data in CDB, setup a normal CDB subscriber for
that data, and finally when our private user data is manipulated,
our CDB subscriber picks up the changes and changes the contents of
the relevant /etc
files.
A common situation is when we wish to have all authentication data stored remotely, not locally, for example on a remote RADIUS or LDAP server. This remote authentication server typically not only stores the users and their passwords, but also the group information.
If we wish to have not only the users, but also the group information stored on a remote server, the best option for NSO authentication is to use "external authentication".
If this feature is configured, NSO
will invoke
the executable configured in
/ncs-config/aaa/external-authentication/executable
in
ncs.conf
, and pass the username and
the clear text password on stdin
using the string notation:
"[user;password;]\n"
.
For example if user "bob" attempts to login over SSH
using the password "secret", and external authentication is
enabled, NSO
will invoke the configured executable and
write "[bob;secret;]\n"
on the stdin
stream
for the executable.
The task of the executable is then to authenticate the user and also establish the username-to-groups mapping.
For example the executable could be a RADIUS client
which utilizes some proprietary vendor attributes to
retrieve the groups of the user from the RADIUS server.
If authentication is successful, the program should write
"accept "
followed by a space-separated list of groups the
user is member of, and additional information as described below.
Again, assuming that Bob's password indeed
was "secret", and that Bob is member of the "admin" and the
"lamers" groups, the program should write
"accept admin lamers $uid $gid $supplementary_gids $HOME\n"
on
its standard output and then exit.
There is a general limit of 16000 bytes of output from the "externalauth" program
Thus the format of the output from an "externalauth" program when authentication is successful should be:
"accept $groups $uid $gid $supplementary_gids $HOME\n"
Where
$groups
is a space separated list of the group names
the user is a member of.
$uid
is the UNIX integer user id
NSO
should use
as default when executing commands for this user.
$gid
is the UNIX integer group id
NSO
should use
as default when executing commands for this user.
$supplementary_gids
is a (possibly empty)
space separated list of additional
UNIX group ids the user is also a member of.
$HOME
is the directory which should be used as
HOME for this user when NSO
executes commands on behalf
of this user.
It is further possible for the program to return a token on successful
authentication, by using
"accept_token"
instead of "accept"
:
"accept_token $groups $uid $gid $supplementary_gids $HOME $token\n"
Where $token
is an arbitrary string. NSO
will then, for some northbound interfaces, include this token in
responses.
It is also possible for the program to return additional
information on successful authentication, by using
"accept_info"
instead of "accept"
:
"accept_info $groups $uid $gid $supplementary_gids $HOME $info\n"
Where $info
is some arbitrary text. NSO
will then just append this text to the generated audit log message
(CONFD_EXT_LOGIN).
Yet another possibility is for the program to return a warning
that the user's password is about to expire, by using
"accept_warning"
instead of "accept"
:
"accept_warning $groups $uid $gid $supplementary_gids $HOME $warning\n"
Where $warning
is an appropriate warning message.
The message will be processed by NSO
according to the setting of /ncs-config/aaa/expiration-warning
in ncs.conf
.
There is also support for token variations of "accept_info"
and "accept_warning"
namely "accept_token_info"
and
"accept_token_warning"
.
Both "accept_token_info"
and "accept_token_warning"
expects the external program to output exactly the same as described above
with the addition of a token after $HOME:
"accept_token_info $groups $uid $gid $supplementary_gids $HOME $token $info\n"
"accept_token_warning $groups $uid $gid $supplementary_gids $HOME $token $warning\n"
If authentication failed, the program should write
"reject"
or "abort"
, possibly followed by a
reason for the rejection, and a trailing newline. For example
"reject Bad password\n"
or just "abort\n"
.
The difference between "reject"
and "abort"
is that with "reject"
, NSO
will try subsequent mechanisms configured for /ncs-config/aaa/auth-order
in ncs.conf
(if any), while with
"abort"
, the authentication fails immediately. Thus
"abort"
can prevent subsequent mechanisms from being
tried, but when external authentication is the last mechanism (as in
the default order), it has the same effect as
"reject"
.
Supported by some nortbound APIs, such as JSONRPC and CLI over SSH, the external authentication may also choose to issue a challenge:
"challenge $challenge-id $challenge-prompt\n"
The challenge-prompt may be multi line, why it must be base64 encoded
For more information on multi factor authentication, see the
the section called “External multi factor authentication” section.
When external authentication is used, the group list returned
by the external program is prepended by any possible
group information stored locally under the /aaa
tree.
Hence when we use
external authentication it is indeed possible to have the entire
/aaa/authentication
tree empty. The group assignment performed
by the external program will still be valid and the relevant groups will
be used by NSO
when the authorization rules are checked.
When username, password authentication is not feasible, authentication by token validation is possible. Currently only RESTCONF supports this mode of authentication. It shares all properties of external authentication, but instead of a username and password, it takes a token as input. The output is also almost the same, the only difference is that it is also expected to output a username.
If this feature is configured, NSO
will invoke
the executable configured in
/ncs-config/aaa/external-validation/executable
in
ncs.conf
, and pass the token on stdin
using the string notation:
"[token;]\n"
.
For example if user "bob" attempts to login over RESTCONF
using the token "topsecret", and external validation is
enabled, NSO
will invoke the configured executable and
write "[topsecret;]\n"
on the stdin
stream
for the executable.
The task of the executable is then to validate the token, thereby authenticating the user and also establish the username and username-to-groups mapping.
For example the executable could be a FUSION client
which utilizes some proprietary vendor attributes to
retrieve the username and groups of the user from the FUSION server.
If token validation is successful, the program should write
"accept "
followed by a space-separated list of groups the
user is member of, and additional information as described below.
Again, assuming that Bob's token indeed
was "topsecret", and that Bob is member of the "admin" and the
"lamers" groups, the program should write
"accept admin lamers $uid $gid $supplementary_gids $HOME $USER\n"
on
its standard output and then exit.
There is a general limit of 16000 bytes of output from the "externalvalidation" program
Thus the format of the output from an "externalvalidation" program when token validation authentication is successful should be:
"accept $groups $uid $gid $supplementary_gids $HOME $USER\n"
Where
$groups
is a space separated list of the group names
the user is a member of.
$uid
is the UNIX integer user id
NSO
should use
as default when executing commands for this user.
$gid
is the UNIX integer group id
NSO
should use
as default when executing commands for this user.
$supplementary_gids
is a (possibly empty)
space separated list of additional
UNIX group ids the user is also a member of.
$HOME
is the directory which should be used as
HOME for this user when NSO
executes commands on behalf
of this user.
$USER
is the user derived from mapping the token.
It is further possible for the program to return a new token on successful
token validation authentication, by using
"accept_token"
instead of "accept"
:
"accept_token $groups $uid $gid $supplementary_gids $HOME $USER $token\n"
Where $token
is an arbitrary string. NSO
will then, for some northbound interfaces, include this token in
responses.
It is also possible for the program to return additional
information on successful token validation authentication, by using
"accept_info"
instead of "accept"
:
"accept_info $groups $uid $gid $supplementary_gids $HOME $USER $info\n"
Where $info
is some arbitrary text. NSO
will then just append this text to the generated audit log message
(CONFD_EXT_LOGIN).
Yet another possibility is for the program to return a warning
that the user's password is about to expire, by using
"accept_warning"
instead of "accept"
:
"accept_warning $groups $uid $gid $supplementary_gids $HOME $USER $warning\n"
Where $warning
is an appropriate warning message.
The message will be processed by NSO
according to the setting of /ncs-config/aaa/expiration-warning
in ncs.conf
.
There is also support for token variations of "accept_info"
and "accept_warning"
namely "accept_token_info"
and
"accept_token_warning"
.
Both "accept_token_info"
and "accept_token_warning"
expects the external program to output exactly the same as described above
with the addition of a token after $USER:
"accept_token_info $groups $uid $gid $supplementary_gids $HOME $USER $token $info\n"
"accept_token_warning $groups $uid $gid $supplementary_gids $HOME $USER $token $warning\n"
If token validation authentication failed, the program should write
"reject"
or "abort"
, possibly followed by a
reason for the rejection, and a trailing newline. For example
"reject Bad password\n"
or just "abort\n"
.
The difference between "reject"
and "abort"
is that with "reject"
, NSO
will try subsequent mechanisms configured for /ncs-config/aaa/validation-order
in ncs.conf
(if any), while with
"abort"
, the token validation authentication fails immediately.
Thus "abort"
can prevent subsequent mechanisms from being
tried. Currently the only available token validation authentication
mechanism is the external one.
Supported by some nortbound APIs, such as JSONRPC and CLI over SSH, the external validation may also choose to issue a challenge:
"challenge $challenge-id $challenge-prompt\n"
The challenge-prompt may be multi line, why it must be base64 encoded
For more information on multi factor authentication, see the
the section called “External multi factor authentication” section.
When username, password or token authentication is not enough, a challenge may be sent from any of the external authentication mechanisms to the user. A challenge consists of a challenge id and a base64 encoded challenge prompt, and a user is supposed to send a response to the challenge. Currently only JSONRPC and CLI over SSH supports multi factor authentication. Responses to challenges of multi factor authentication has the same output as the token authentication mechanism.
If this feature is configured, NSO
will invoke the executable configured in
/ncs-config/aaa/external-challenge/executable
in
ncs.conf
, and pass the challenge id and response on stdin
using the string notation: "[challenge-id;response;]\n"
.
For example a user "bob" has received a challenge from external
authentication, external validation or external challenge and then attempts
to login over JSONRPC with a response to the challenge using
challenge id:"22efa",response:"ae457b". The external challenge mechanism is
enabled, NSO will invoke the configured executable and write
"[22efa;ae457b;]\n"
on the stdin
stream for the
executable.
The task of the executable is then to validate the challenge id, response combination, thereby authenticating the user and also establish the username and username-to-groups mapping.
For example the executable could be a RADIUS client
which utilizes some proprietary vendor attributes to
retrieve the username and groups of the user from the RADIUS server.
If challenge id, response validation is successful, the program should write
"accept "
followed by a space-separated list of groups the
user is member of, and additional information as described below.
Again, assuming that Bob's challenge id, response combination indeed
was "22efa", "ae457b" and that Bob is member of the "admin" and the
"lamers" groups, the program should write
"accept admin lamers $uid $gid $supplementary_gids $HOME $USER\n"
on its standard output and then exit.
There is a general limit of 16000 bytes of output from the "externalchallenge" program
Thus the format of the output from an "externalchallenge" program when challenge based authentication is successful should be:
"accept $groups $uid $gid $supplementary_gids $HOME $USER\n"
Where
$groups
is a space separated list of the group names
the user is a member of.
$uid
is the UNIX integer user id
NSO
should use
as default when executing commands for this user.
$gid
is the UNIX integer group id
NSO
should use
as default when executing commands for this user.
$supplementary_gids
is a (possibly empty)
space separated list of additional
UNIX group ids the user is also a member of.
$HOME
is the directory which should be used as
HOME for this user when NSO
executes commands on behalf
of this user.
$USER
is the user derived from mapping the
challenge id, response.
It is further possible for the program to return a token on successful
authentication, by using
"accept_token"
instead of "accept"
:
"accept_token $groups $uid $gid $supplementary_gids $HOME $USER $token\n"
Where $token
is an arbitrary string. NSO
will then, for some northbound interfaces, include this token in
responses.
It is also possible for the program to return additional
information on successful authentication, by using
"accept_info"
instead of "accept"
:
"accept_info $groups $uid $gid $supplementary_gids $HOME $USER $info\n"
Where $info
is some arbitrary text. NSO
will then just append this text to the generated audit log message
(CONFD_EXT_LOGIN).
Yet another possibility is for the program to return a warning
that the user's password is about to expire, by using
"accept_warning"
instead of "accept"
:
"accept_warning $groups $uid $gid $supplementary_gids $HOME $USER $warning\n"
Where $warning
is an appropriate warning message.
The message will be processed by NSO
according to the setting of /ncs-config/aaa/expiration-warning
in ncs.conf
.
There is also support for token variations of "accept_info"
and "accept_warning"
namely "accept_token_info"
and
"accept_token_warning"
.
Both "accept_token_info"
and "accept_token_warning"
expects the external program to output exactly the same as described above
with the addition of a token after $USER:
"accept_token_info $groups $uid $gid $supplementary_gids $HOME $USER $token $info\n"
"accept_token_warning $groups $uid $gid $supplementary_gids $HOME $USER $token $warning\n"
If authentication failed, the program should write
"reject"
or "abort"
, possibly followed by a
reason for the rejection, and a trailing newline. For example
"reject Bad challenge response\n"
or just "abort\n"
.
The difference between "reject"
and "abort"
is that with "reject"
, NSO
will try subsequent mechanisms configured for /ncs-config/aaa/challenge-order
in ncs.conf
(if any), while with
"abort"
, the challenge response authentication fails immediately.
Thus "abort"
can prevent subsequent mechanisms from being
tried. Currently the only available challenge response authentication
mechanism is the external one.
Supported by some nortbound APIs, such as JSONRPC and CLI over SSH, the external challenge may also choose to issue a new challenge:
"challenge $challenge-id $challenge-prompt\n"
The challenge-prompt may be multi line, so it must be base64 encoded
Note that when using challenges with the CLI over SSH, the
/ncs-config/cli/ssh/use-keyboard-interactive>
needs to be set to true for the challenges to be sent correctly to the client!
The configuration of the ssh client used may need to be given
the option to allow a higher number of allowed number of password prompts,
e.g. -o NumberOfPasswordPrompts
, else the default number may
introduce an unexpected behaviour when the client is presented with multiple
challenges.
The Package Authentication functionality allows for packages to handle the NSO authentication in a customized fashion. Authentication data can e.g. be stored remotely, and a script in the package is used to communicate with the remote system.
Compared to external authentication, the Package Authentication
mechanism allows specifying multiple packages to be invoked in the order
they are appear in the configuration. NSO provides
implementations for LDAP, SAMLv2, and TACACS+ protocols with packages
available in $NCS_DIR/packages/auth/
.
Additionally, you can implement your own authentication packages as
detailed below.
Authentication packages are NSO packages with the required content of
an executable file scripts/authenticate
. This
executable basically follows the same API, and limitations, as the external
auth script, but with a different input format and some additional
functionality. Other than these requirements, it is possible to customize the
package arbitrarily.
Package authentication is supported for Single Sign-On (see
Single Sign-On
in Web UI),
JSONRPC, and RESTCONF. Note that Single Sign-On and (non batch) JSON-RPC
allows all functionality while the RESTCONF interface will treat anything
other than a "accept_username "
reply from the package as if
authentication failed!
Package authentication is enabled by setting the
ncs.conf
options
/ncs-config/aaa/package-authentication/enabled
to true, and adding
the package by name in the
/ncs-config/aaa/package-authentication/packages
list. The order of
the configured packages is the order that the packages will be used when
attempting to authenticate a user. See
ncs.conf(5) in Manual Pages
for details.
If this feature is configured in ncs.conf
,
NSO will for each configured package invoke
script/authenticate
, and pass username, password,
original HTTP request (i.e. the user supplied "next" query parameter)
HTTP request, HTTP headers, HTTP body, client source
IP, client source port, northbound API context, and protocol on
stdin
using the string notation:
"[user;password;orig_request;request;headers;body;src-ip;src-port;ctx;proto;]\n"
.
The fields user, password, orig_request, request, headers, body are all base64 encoded.
If the body length exceeds the partial_post_size
of
the RESTCONF server, the body passed to the authenticate script will
only contain the string '==nso_package_authentication_partial_body=='.
The original request will be prefixed with the string '==nso_package_authentication_next==' before base64 encoded part. This means supplying the "next" query parameter value "/my-location" will pass the following string to the authentication script: '==nso_package_authentication_next==L215LWxvY2F0aW9u'.
For example if an unauthenticated user attempts to start a single
sign-on process over northbound HTTP based APIs with the cisco-nso-saml2-auth
package, package authentication is enabled and configured with packages, and
also single sign-on is enabled, NSO will, for each configured package,
invoke the executable scripts/authenticate
and write
"[;;;R0VUIC9zc28vc2FtbC9sb2dpbi8gSFRUUC8xLjE=;;;127.0.0.1;59226;webui;https;]\n"
.
on the stdin
stream for the executable.
For clarity, the base64 decoded contents sent to stdin
presented:
"[;;;GET /sso/saml/login/ HTTP/1.1;;;127.0.0.1;54321;webui;https;]\n"
.
The task of the package is then to authenticate the user and also establish the username-to-groups mapping.
For example the package could support a SAMLv2 authentication protocol
which communicates with an Identity Provider (IdP) for authentication. If
authentication is successful, the program should write either
"accept "
, or "accept_username "
, depending on if
the authentication is started with username or if an external entity handles
the entire authentication and supplies the username for a successful
authentication. (SAMLv2 uses accept_username, since the IdP handles the entire
authentication.) The "accept_username " is followed by a username and then
followed by a space-separated list of groups the user is member of, and
additional information as described below.
If authentication is successful and the authenticated user Bob is member of
the groups "admin" and "wheel", the program should write
"accept_username bob admin wheel 1000 1000 100 /home/bob\n"
on its standard output and then exit.
There is a general limit of 16000 bytes of output from the "packageauth" program.
Thus the format of the output from a "packageauth" program when authentication is successful should be either the same as from "externalauth" (see the section called “External authentication”) or the following:
"accept_username $USER $groups $uid $gid $supplementary_gids $HOME\n"
Where
$USER
is the user derived during the execution of the
"packageauth" program.
$groups
is a space separated list of the group names
the user is a member of.
$uid
is the UNIX integer user id NSO should use as
default when executing commands for this user.
$gid
is the UNIX integer group id NSO should use as
default when executing commands for this user.
$supplementary_gids
is a (possibly empty) space separated
list of additional UNIX group ids the user is also a member of.
$HOME
is the directory which should be used as HOME for
this user when NSO executes commands on behalf of this user.
In addition to the "externalauth" API, the authentication packages can also return the following responses:
unknown '
-
(reason
'reason
being plain-text) if they can't
handle authentication for the supplied input.
redirect '
-
(url
'url
being base64 encoded) for an HTTP
redirect.
content '
-
(content-type
' 'content
'content-type
being plain-text mime-type and
content
being base64 encoded) to relay
supplied content.
accept_username_redirect
-
which combines the url
$USER $groups $uid $gid $supplementary_gids $HOMEaccept_username
and redirect
.
It is also possible for the program to return additional
information on successful authentication, by using
"accept_info"
instead of "accept"
:
"accept_info $groups $uid $gid $supplementary_gids $HOME $info\n"
Where $info
is some arbitrary text. NSO
will then just append this text to the generated audit log message
(NCS_PACKAGE_AUTH_SUCCESS).
Yet another possibility is for the program to return a warning
that the user's password is about to expire, by using
"accept_warning"
instead of "accept"
:
"accept_warning $groups $uid $gid $supplementary_gids $HOME $warning\n"
Where $warning
is an appropriate warning message. The
message will be processed by NSO according to the setting of
/ncs-config/aaa/expiration-warning
in
ncs.conf
.
If authentication fails, the program should write
"reject"
or "abort"
, possibly followed by a
reason for the rejection, and a trailing newline. For example
"reject 'Bad password'\n"
or just "abort\n"
.
The difference between "reject"
and "abort"
is that with "reject"
, NSO
will try subsequent mechanisms configured for
/ncs-config/aaa/auth-order
, and packages configured for
/ncs-config/aaa/package-authentication/packages
in
ncs.conf
(if any), while with
"abort"
, the authentication fails immediately. Thus
"abort"
can prevent subsequent mechanisms from being
tried, but when external authentication is the last mechanism (as in
the default order), it has the same effect as
"reject"
.
When package authentication is used, the group list returned by the
package executable is prepended by any possible group information stored
locally under the /aaa
tree. Hence when package authentication is
used, it is indeed possible to have the entire /aaa/authentication
tree empty. The group assignment performed by the external program will still
be valid and the relevant groups will be used by NSO when the
authorization rules are checked.
Package authentication will invoke the
scripts/authenticate
when a user tries to authenticate
using CLI. In this case only the username, password, client source IP,
client source port, northbound API context, and protocol will be passed to
the script.
When serving a username/password request, script output other than accept, challenge or abort will be treated as if authentication failed.
When this is enabled,
/ncs-config/aaa/package-authentication/package-challenge/enabled
is set to true, packages will also be used to try to resolve challenges sent
to the server and is only supported by CLI over SSH. The script
script/challenge
will be invoked passing challenge id,
response, client source IP, client source port, northbound API context, and
protocol on stdin
using the string notation:
"[challengeid;response;src-ip;src-port;ctx;proto;]\n"
.
The output should follow that of the authenticate script.
The fields challengeid and response are base64 encoded when passed to the script.
NSO listens for client connections on the NSO IPC port.
See /ncs-config/ncs-ipc-address/ip
in ncs.conf.
Access to this port is by default not authenticated. That means that
all users with shell access to the host, can connect to this port.
So NSO deployment ends up in either of two cases, untrusted users
have, or have not shell access to the host(s) where NSO is deployed.
If all shell users on the deployment host(s) are trusted, no further action
is required, however if untrusted users do have shell access to the hosts,
access to the IPC port must be restricted,
see the section called “Restricting access to the IPC port”.
If IPC port access is not used, an untrusted shell user can simply invoke:
bob> ncs_cli --user admin
to impersonate as the admin
user, or invoke
bob> ncs_load > all.xml
to retrieve the entire configuration.
Once a user is authenticated, group membership must be established. A single user can be a member of several groups. Group membership is used by the authorization rules to decide which operations a certain user is allowed to perform. Thus the NSO AAA authorization model is entirely group based. This is also sometimes referred to as role based authorization.
All groups are stored under /nacm/groups
, and each group
contains a number of usernames. The
ietf-netconf-acm.yang
model defines a group
entry:
list group { key name; description "One NACM Group Entry. This list will only contain configured entries, not any entries learned from any transport protocols."; leaf name { type group-name-type; description "Group name associated with this entry."; } leaf-list user-name { type user-name-type; description "Each entry identifies the username of a member of the group associated with this entry."; } }
The tailf-acm.yang
model augments this
with a gid
leaf:
augment /nacm:nacm/nacm:groups/nacm:group { leaf gid { type int32; description "This leaf associates a numerical group ID with the group. When a OS command is executed on behalf of a user, supplementary group IDs are assigned based on 'gid' values for the groups that the use is a member of."; } }
A valid group entry could thus look like:
<group> <name>admin</name> <user-name>bob</user-name> <user-name>joe</user-name> <gid xmlns="http://tail-f.com/yang/acm">99</gid> </group>
The above XML data would then mean that users
bob
and joe
are members of the admin
group.
The users need not necessarily exist as actual users under
/aaa/authentication/users
in order to belong to
a group. If for example PAM authentication is used, it does not make
sense to have all users listed under
/aaa/authentication/users
.
By default, the user is assigned to groups by using any groups
provided by the northbound transport (e.g. via the
ncs_cli
or netconf-subsys
programs), by consulting data under
/nacm/groups
, by consulting the
/etc/group
file, and by using any additional
groups supplied by the authentication method. If
/nacm/enable-external-groups
is set to "false",
only the data under /nacm/groups
is consulted.
The resulting group assignment is the union of these
methods, if it is non-empty. Otherwise, the default group is
used, if configured (
/ncs-config/aaa/default-group
in
ncs.conf
).
A user entry has a UNIX uid and UNIX gid assigned to it. Groups may have optional group ids. When a user is logged in, and NSO tries to execute commands on behalf of that user, the uid/gid for the command execution is taken from the user entry. Furthermore, UNIX supplementary group ids are assigned according to the gids in the groups where the user is a member.
Once a user is authenticated and group membership is established, when the user starts to perform various actions, each action must be authorized. Normally the authorization is done based on rules configured in the AAA data model as described in this section.
The authorization procedure first checks the value of
/nacm/enable-nacm
. This leaf has a default of
true
, but if it is set to false
, all
access is permitted. Otherwise, the next step is to traverse the
rule-list
list:
list rule-list { key "name"; ordered-by user; description "An ordered collection of access control rules."; leaf name { type string { length "1..max"; } description "Arbitrary name assigned to the rule-list."; } leaf-list group { type union { type matchall-string-type; type group-name-type; } description "List of administrative groups that will be assigned the associated access rights defined by the 'rule' list. The string '*' indicates that all groups apply to the entry."; } // ... }
If the group
leaf-list in a rule-list
entry
matches any of the user's groups, the cmdrule
list
entries are examined for command authorization, while the
rule
entries are examined for rpc, notification, and
data authorization.
The tailf-acm.yang
module augments the
rule-list
entry in
ietf-netconf-acm.yang
with a
cmdrule
list:
augment /nacm:nacm/nacm:rule-list { list cmdrule { key "name"; ordered-by user; description "One command access control rule. Command rules control access to CLI commands and Web UI functions. Rules are processed in user-defined order until a match is found. A rule matches if 'context', 'command', and 'access-operations' match the request. If a rule matches, the 'action' leaf determines if access is granted or not."; leaf name { type string { length "1..max"; } description "Arbitrary name assigned to the rule."; } leaf context { type union { type nacm:matchall-string-type; type string; } default "*"; description "This leaf matches if it has the value '*' or if its value identifies the agent that is requesting access, i.e. 'cli' for CLI or 'webui' for Web UI."; } leaf command { type string; default "*"; description "Space-separated tokens representing the command. Refer to the Tail-f AAA documentation for further details."; } leaf access-operations { type union { type nacm:matchall-string-type; type nacm:access-operations-type; } default "*"; description "Access operations associated with this rule. This leaf matches if it has the value '*' or if the bit corresponding to the requested operation is set."; } leaf action { type nacm:action-type; mandatory true; description "The access control action associated with the rule. If a rule is determined to match a particular request, then this object is used to determine whether to permit or deny the request."; } leaf log-if-permit { type empty; description "If this leaf is present, access granted due to this rule is logged in the developer log. Otherwise, only denied access is logged. Mainly intended for debugging of rules."; } leaf comment { type string; description "A textual description of the access rule."; } } }
Each rule has seven leafs. The first is the name
list
key, the following three leafs are matching leafs. When
NSO tries to run a command it tries to
match the command towards the matching leafs and if all of
context
, command
, and
access-operations
match, the fifth field, i.e. the
action
, is applied.
name
is the name of the rule. The rules are
checked in order, with the ordering given by the the YANG
ordered-by user
semantics, i.e. independent of
the key values.
context
is either of the strings
cli
, webui
, or *
for a command rule. This means that we can differentiate
authorization rules for which access method is used. Thus if
command access is attempted through the CLI the context
will be the string cli
whereas for operations
via the Web UI, the context will be the string
webui
.
This is the actual command getting executed. If the rule
applies to one or several CLI commands, the string is a
space separated list of CLI command tokens, for example
request system reboot
. If the command
applies to Web UI operations, it is a space separated string
similar to a CLI string.
A string which consists of just "*" matches any command.
In general, we do not recommend using command rules to protect the configuration. Use rules for data access as described in the next section to control access to different parts of the data. Command rules should be used only for CLI commands and Web UI operations that cannot be expressed as data rules.
The individual tokens can be POSIX extended regular expressions. Each regular expression is implicitly anchored, i.e. an "^" is prepended and a "$" is appended to the regular expression.
access-operations
is used to match the
operation that NSO tries to perform. It
must be one or both of the "read" and "exec" values from
the access-operations-type
bits type
definition in ietf-netconf-acm.yang
,
or "*" to match any operation.
If all of the previous fields match, the rule as a whole
matches and the value of action
will be taken.
I.e. if a match is found, a decision is made whether to
permit or deny the request in its entirety. If
action
is permit
, the request is
permitted, if action
is deny
, the
request is denied and an entry written to the developer
log.
If this leaf is present, an entry is written to the
developer log for a matching request also when
action
is permit
. This is very
useful when debugging command rules.
An optional textual description of the rule.
For the rule processing to be written to the devel
log, the
/ncs-config/logs/developer-log-level
entry in
ncs.conf
must be set to trace
.
If no matching rule is found in any of the cmdrule
lists in any rule-list
entry that matches the user's
groups, this augmentation from
tailf-acm.yang
is relevant:
augment /nacm:nacm { leaf cmd-read-default { type nacm:action-type; default "permit"; description "Controls whether command read access is granted if no appropriate cmdrule is found for a particular command read request."; } leaf cmd-exec-default { type nacm:action-type; default "permit"; description "Controls whether command exec access is granted if no appropriate cmdrule is found for a particular command exec request."; } leaf log-if-default-permit { type empty; description "If this leaf is present, access granted due to one of /nacm/read-default, /nacm/write-default, or /nacm/exec-default /nacm/cmd-read-default, or /nacm/cmd-exec-default being set to 'permit' is logged in the developer log. Otherwise, only denied access is logged. Mainly intended for debugging of rules."; } }
If "read" access is requested, the value of
/nacm/cmd-read-default
determines whether access is
permitted or denied.
If "exec" access is requested, the value of
/nacm/cmd-exec-default
determines whether access is
permitted or denied.
If access is permitted due to one of these default leafs, the
/nacm/log-if-default-permit
has the same effect as the
log-if-permit
leaf for the cmdrule
lists.
The rules in the rule
list are used to control access
to rpc operations, notifications, and data nodes defined in YANG
models. Access to invocation of actions
(tailf:action
) is controlled with the same method
as access to data nodes, with a request for "exec"
access. ietf-netconf-acm.yang
defines a
rule
entry as:
list rule { key "name"; ordered-by user; description "One access control rule. Rules are processed in user-defined order until a match is found. A rule matches if 'module-name', 'rule-type', and 'access-operations' match the request. If a rule matches, the 'action' leaf determines if access is granted or not."; leaf name { type string { length "1..max"; } description "Arbitrary name assigned to the rule."; } leaf module-name { type union { type matchall-string-type; type string; } default "*"; description "Name of the module associated with this rule. This leaf matches if it has the value '*' or if the object being accessed is defined in the module with the specified module name."; } choice rule-type { description "This choice matches if all leafs present in the rule match the request. If no leafs are present, the choice matches all requests."; case protocol-operation { leaf rpc-name { type union { type matchall-string-type; type string; } description "This leaf matches if it has the value '*' or if its value equals the requested protocol operation name."; } } case notification { leaf notification-name { type union { type matchall-string-type; type string; } description "This leaf matches if it has the value '*' or if its value equals the requested notification name."; } } case data-node { leaf path { type node-instance-identifier; mandatory true; description "Data Node Instance Identifier associated with the data node controlled by this rule. Configuration data or state data instance identifiers start with a top-level data node. A complete instance identifier is required for this type of path value. The special value '/' refers to all possible data-store contents."; } } } leaf access-operations { type union { type matchall-string-type; type access-operations-type; } default "*"; description "Access operations associated with this rule. This leaf matches if it has the value '*' or if the bit corresponding to the requested operation is set."; } leaf action { type action-type; mandatory true; description "The access control action associated with the rule. If a rule is determined to match a particular request, then this object is used to determine whether to permit or deny the request."; } leaf comment { type string; description "A textual description of the access rule."; } }
tailf-acm
augments this with two additional
leafs:
augment /nacm:nacm/nacm:rule-list/nacm:rule { leaf context { type union { type nacm:matchall-string-type; type string; } default "*"; description "This leaf matches if it has the value '*' or if its value identifies the agent that is requesting access, e.g. 'netconf' for NETCONF, 'cli' for CLI, or 'webui' for Web UI."; } leaf log-if-permit { type empty; description "If this leaf is present, access granted due to this rule is logged in the developer log. Otherwise, only denied access is logged. Mainly intended for debugging of rules."; } }
Similar to the command access check, whenever a user through some agent tries to access an rpc, a notification, a data item, or an action, access is checked. For a rule to match, three or four leafs must match and when a match is found, the corresponding action is taken.
We have the following leafs in the rule
list entry.
name
is the name of the rule. The rules are
checked in order, with the ordering given by the the YANG
ordered-by user
semantics, i.e. independent of
the key values.
The module-name
string is the name of the YANG
module where the node being accessed is defined. The
special value *
(i.e. the default) matches
all modules.
Since the elements of the path to a given node may be
defined in different YANG modules when augmentation is
used, rules which have a value other than *
for the module-name
leaf may require that
additional processing is done before a decision to permit
or deny or the access can be taken. Thus if an XPath that
completely identifies the nodes that the rule should apply
to is given for the path
leaf (see below), it
may be best to leave the module-name
leaf unset.
This is a choice between three possible leafs that are
used for matching, in addition to the
module-name
:
The name of a rpc operation, or "*" to match any rpc.
The name of a notification, or "*" to match any notification.
A restricted XPath expression leading down into the populated XML tree. A rule with a path specified matches if it is equal to or shorter than the checked path. Several types of paths are allowed.
Tagpaths that are not containing any
keys. For example
/ncs/live-device/live-status
.
Instantiated key: as in
/devices/device[name="x1"]/config/interface
matches the interface configuration for managed
device "x1" It's possible to have partially
instantiated paths only containing some keys
instantiated - i.e combinations of tagpaths and
keypaths. Assuming a deeper tree, the path
/devices/device/config/interface[name="eth0"]
matches the "eth0" interface configuration on all
managed devices.
Wild card at end as in:
/services/web-site/*
does
not match the web site service instances, but
rather all children of the web site service
instances.
Thus the path in a rule is matched against the path in the attempted data access. If the attempted access has a path that is equal to or longer than the rule path - we have a match.
If none of the leafs rpc-name
,
notification-name
, or path
are set,
the rule matches for any rpc, notification, data, or
action access.
context
is either of the strings cli
,
netconf
, webui
, snmp
, or
*
for a data rule. Furthermore, when we initiate
user sessions from MAAPI, we can choose any string we want.
Similarly to command rules we can differentiate access depending on which agent is used to gain access.
access-operations
is used to match the operation
that NSO tries to perform. It must be
one or more of the "create", "read", "update", "delete" and
"exec" values from
the access-operations-type
bits type
definition in ietf-netconf-acm.yang
,
or "*" to match any operation.
This leaf has the same characteristics as the action
leaf for command access.
This leaf has the same characteristics as the
log-if-permit
leaf for command access.
An optional textual description of the rule.
If no matching rule is found in any of the rule
lists
in any rule-list
entry that matches the user's
groups, the data model node for which access is requested is
examined for presence of the NACM extensions:
If the nacm:default-deny-all
extension is
specified for the data model node, access is denied.
If the nacm:default-deny-write
extension is
specified for the data model node, and "create", "update",
or "delete" access is requested, access is denied.
If examination of the NACM extensions did not result in access
being denied, the value (permit
or
deny
) of the relevant default leaf is examined:
If "read" access is requested, the value of
/nacm/read-default
determines whether access is
permitted or denied.
If "create", "update", or "delete" access is requested, the
value of /nacm/write-default
determines whether
access is permitted or denied.
If "exec" access is requested, the value of
/nacm/exec-default
determines whether access is
permitted or denied.
If access is permitted due to one of these default leafs, this
augmentation from tailf-acm.yang
is
relevant:
augment /nacm:nacm { ... leaf log-if-default-permit { type empty; description "If this leaf is present, access granted due to one of /nacm/read-default, /nacm/write-default, /nacm/exec-default /nacm/cmd-read-default, or /nacm/cmd-exec-default being set to 'permit' is logged in the developer log. Otherwise, only denied access is logged. Mainly intended for debugging of rules."; } }
I.e. it has the same effect as the log-if-permit
leaf
for the rule
lists, but for the case
where the value of one of the default leafs permits the access.
When NSO executes a command, the command rules in the authorization database are searched, The rules are tried in order, as described above. When a rule matches the operation (command) that NSO is attempting, the action of the matching rule is applied - whether permit or deny.
When actual data access is attempted, the data rules are searched.
E.g. when a user attempts to execute delete aaa
in the CLI,
the user needs delete access to the entire tree /aaa
.
Another example is if a CLI user writes show configuration
aaa TAB
it suffices to have read access to at least one item
below /aaa
for the CLI to perform the TAB completion.
If no rule matches or an explicit deny rule is found, the CLI
will not TAB complete.
Yet another example is if a user tries to execute
delete aaa authentication users
, we need to perform a check
on the paths /aaa
and /aaa/authentication
before
attempting
to delete the sub tree. Say that we have a rule for
path /aaa/authentication/users
which is an permit rule and we
have a subsequent rule for path /aaa
which is a
deny rule. With this
rule set the user should indeed be allowed to delete the entire
/aaa/authentication/users
tree but not the /aaa
tree nor the /aaa/authentication
tree.
We have two variations on how the rules are processed. The easy case is when we actually try to read or write an item in the configuration database. The execution goes like:
foreach rule { if (match(rule, path)) { return rule.action; } }
The second case is when we execute TAB completion in the CLI. This is more complicated. The execution goes like:
rules = select_rules_that_may_match(rules, path); if (any_rule_is_permit(rules)) return permit; else return deny;
The idea being that as we traverse (through TAB) down the XML tree, as long as there is at least one rule that can possibly match later, once we have more data, we must continue.
For example assume we have:
"/system/config/foo" --> permit
"/system/config" --> deny
If we in the CLI stand at "/system/config"
and hit TAB
we want the CLI to show foo
as a completion, but none
of the other nodes that exist under /system/config
.
Whereas if we try to execute delete /system/config
the
request must be rejected.
By default, NACM rules are configured for entire
tailf:action
or YANG 1.1 action
statements,
but not for input
statement child leafs. To
override this behaviour, and enable NACM rules on input
leafs, set the following parameter to 'true':
/ncs-config/aaa/action-input-rules/enabled
.
When enabled all action input leafs given
to an action will be validated for NACM rules. If broad 'deny'
NACM rules are used, you might need to add 'permit' rules for
the affected action input leafs to allow actions to be
used with parameters.
By design NACM rules are ignored for changes done by services - FASTMAP, Reactive FASTMAP, or Nano services. The reasoning behind this is that a service package can be seen as a controlled way to provide limited access to devices for a user group that is not allowed to apply arbitrary changes on the devices.
However, there are NSO installations where this behavior is not
desired, and NSO administrators want to enforce NACM rules even on
changes done by services. For this purpose, the leaf called
/nacm/enforce-nacm-on-services
is provided. By default, it is
set to false
.
Note however that currently, even with this leaf set to true, there are limitations. Namely, the post-actions for nano-services are run in a user session without any access checks. Besides that, NACM rules are not enforced on the read operations performed in the service callbacks.
It might be desirable to deny everything for a user group and only allow access to a specific service. This pattern could be used to allow an operator to provision the service, but deny everything else. While this pattern works for a normal FASTMAP service, there are some caveats for stacked services, Reactive FASTMAP and Nano services. For these kinds of services, in addition to the service itself, access should be provided to the user group for the following paths:
In case of stacked services, the user group needs read and write access to the leaf "private/re-deploy-counter" under the bottom service. Otherwise, the user will not be able to re-deploy the service.
In case of Reactive FASTMAP or Nano services, the user group needs read and write access to the following:
/zombies
/side-effect-queue
/kickers
In deployments with many devices it can become cumbersome to
handle data authorization per device. To help with this there is
a rule type that works on device group membership (for more on
device groups, see the section called “Device Groups” in User Guide). To do
this, devices are added to different device-groups and the rule
type device-group-rule
is used.
The IETF NACM rule type is augmented with a new rule type named
device-group-rule
which contains a leafref to the
device-groups. See Example 2, “Device Group model augmentation”.
augment "/nacm:nacm/nacm:rule-list/nacm:rule/nacm:rule-type" { case device-group-rule { leaf device-group { type leafref { path "/ncs:devices/ncs:device-group/ncs:name"; } description "Which device group this rule applies to."; } } }
In Example 3, “Device group configuration” we configure two device-groups based on different regions, and add devices to them.
<devices> <device-group> <name>us_east</name> <device-name>cli0</device-name> <device-name>gen0</device-name> </device-group> <device-group> <name>us_west</name> <device-name>nc0</device-name> </device-group> </devices>
In Example 4, “NACM group configuration” we
configure an operator for the us_east
region:
<nacm> <groups> <group> <name>us_east</name> <user-name>us_east_oper</user-name> </group> </groups> </nacm>
In Example 5, “Device Group Authorization rules” we
configure the device group rules and refer to the device group
and the us_east
group.
<nacm> <rule-list> <name>us_east</name> <group>us_east</group> <rule> <name>us_east_read_permit</name> <device-group xmlns="http://tail-f.com/yang/ncs-acm/device-group-authorization">us_east</device-group> <access-operations>read</access-operations> <action>permit</action> </rule> <rule> <name>us_east_create_permit</name> <device-group xmlns="http://tail-f.com/yang/ncs-acm/device-group-authorization">us_east</device-group> <access-operations>create</access-operations> <action>permit</action> </rule> <rule> <name>us_east_update_permit</name> <device-group xmlns="http://tail-f.com/yang/ncs-acm/device-group-authorization">us_east</device-group> <access-operations>update</access-operations> <action>permit</action> </rule> <rule> <name>us_east_delete_permit</name> <device-group xmlns="http://tail-f.com/yang/ncs-acm/device-group-authorization">us_east</device-group> <access-operations>delete</access-operations> <action>permit</action> </rule> </rule-list> </nacm>
In summary device group authorization gives a more compact configuration for deployments where devices can be grouped and authorization can be done on a device group basis.
Modifications on the device-group subtree is recommended to be controlled by a limited set of users.
Assume that we have two groups, admin
and
oper
. We want admin
to be able to see
and and edit the XML tree rooted at /aaa
, but we do not
want users that are members of the oper
group to even
see the /aaa
tree. We would have the following
rule-list and rule entries. Note, here we use the XML data from
tailf-aaa.yang
to exemplify. The examples
apply to all data, for all data models loaded into the system.
<rule-list> <name>admin</name> <group>admin</group> <rule> <name>tailf-aaa</name> <module-name>tailf-aaa</module-name> <path>/</path> <access-operations>read create update delete</access-operations> <action>permit</action> </rule> </rule-list> <rule-list> <name>oper</name> <group>oper</group> <rule> <name>tailf-aaa</name> <module-name>tailf-aaa</module-name> <path>/</path> <access-operations>read create update delete</access-operations> <action>deny</action> </rule> </rule-list>
If we do not want the members of oper
to
be able to execute the NETCONF operation edit-config
, we
define the following rule-list and rule entries:
<rule-list> <name>oper</name> <group>oper</group> <rule> <name>edit-config</name> <rpc-name>edit-config</rpc-name> <context xmlns="http://tail-f.com/yang/acm">netconf</context> <access-operations>exec</access-operations> <action>deny</action> </rule> </rule-list>
To spell it out, the above defines four elements to match. If
NSO tries to perform a
netconf
operation, which is the operation
edit-config
, and the user which runs the command is
member of the oper
group, and finally it is an
exec
(execute) operation, we have a match. If so, the
action is deny
.
The path
leaf can be used to specify explicit
paths into the XML tree using XPath syntax. For example
the following:
<rule-list> <name>admin</name> <group>admin</group> <rule> <name>bob-password</name> <path>/aaa/authentication/users/user[name='bob']/password</path> <context xmlns="http://tail-f.com/yang/acm">cli</context> <access-operations>read update</access-operations> <action>permit</action> </rule> </rule-list>
Explicitly allows the admin
group to change the
password for precisely the bob
user when the user is
using the CLI. Had path
been
/aaa/authentication/users/user/password
the rule would
apply to all password elements for all users. Since the
path
leaf completely identifies the nodes that the rule
applies to, we do not need to give tailf-aaa
for the module-name
leaf.
NSO applies variable substitution, whereby
the username of the logged in user can be used in a
path
. Thus:
<rule-list> <name>admin</name> <group>admin</group> <rule> <name>user-password</name> <path>/aaa/authentication/users/user[name='$USER']/password</path> <context xmlns="http://tail-f.com/yang/acm">cli</context> <access-operations>read update</access-operations> <action>permit</action> </rule> </rule-list>
The above rule allows all users that are part of the
admin
group to change their own passwords only.
A member of oper
is able to execute NETCONF operation
action
if that member has exec
access on NETCONF
RPC action
operation, read
access on all
instances in the hierarchy of data nodes that identifies the specific
action in the datastore, and exec
access on the specific
action. For example an action is defined as below.
container test { action double { input { leaf number { type uint32; } } output { leaf result { type uint32; } } } }
To be able to execute double
action through NETCONF RPC, the
members of oper
need the following rule-list and
rule-entries.
<rule-list> <name>oper</name> <group>oper</group> <rule> <name>allow-netconf-rpc-action</name> <rpc-name>action</rpc-name> <context xmlns="http://tail-f.com/yang/acm">netconf</context> <access-operations>exec</access-operations> <action>permit</action> </rule> <rule> <name>allow-read-test</name> <path>/test</path> <access-operations>read</access-operations> <action>permit</action> </rule> <rule> <name>allow-exec-double</name> <path>/test/double</path> <access-operations>exec</access-operations> <action>permit</action> </rule> </rule-list>
Or, a simpler rule set as the following.
<rule-list> <name>oper</name> <group>oper</group> <rule> <name>allow-netconf-rpc-action</name> <rpc-name>action</rpc-name> <context xmlns="http://tail-f.com/yang/acm">netconf</context> <access-operations>exec</access-operations> <action>permit</action> </rule> <rule> <name>allow-exec-double</name> <path>/test</path> <access-operations>read exec</access-operations> <action>permit</action> </rule> </rule-list>
Finally if we wish members of the oper
group
to never be able to execute the request system
reboot
command, also available as a reboot
NETCONF rpc, we have:
<rule-list> <name>oper</name> <group>oper</group> <cmdrule xmlns="http://tail-f.com/yang/acm"> <name>request-system-reboot</name> <context>cli</context> <command>request system reboot</command> <access-operations>exec</access-operations> <action>deny</action> </cmdrule> <!-- The following rule is required since the user can --> <!-- do "edit system" --> <cmdrule xmlns="http://tail-f.com/yang/acm"> <name>request-reboot</name> <context>cli</context> <command>request reboot</command> <access-operations>exec</access-operations> <action>deny</action> </cmdrule> <rule> <name>netconf-reboot</name> <rpc-name>reboot</rpc-name> <context xmlns="http://tail-f.com/yang/acm">netconf</context> <access-operations>exec</access-operations> <action>deny</action> </rule> </rule-list>
Debugging the AAA rules can be hard. The best way to debug rules
that behave unexpectedly is to add the log-if-permit
leaf to some or all of the rules that have action
permit
. Whenever such a rule triggers a permit
action, an entry is written to the developer log.
Finally it is worth mentioning that when a user session is initially created it will gather the authorization rules that are relevant for that user session and keep these rules for the life of the user session. Thus when we update the AAA rules in e.g. the CLI the update will not apply to the current session - only to future user sessions.
NSO's AAA subsystem will cache the AAA information in order to speed up the authorization process. This cache must be updated whenever there is a change to the AAA information. The mechanism for this update depends on how the AAA information is stored, as described in the following two sections.
In order to start NSO, the data models for AAA must be
loaded. The defaults in the case that no actual
data is loaded for these models allow all read and exec access,
while write access is denied. Access may still be further
restricted by the NACM extensions, though - e.g. the
/nacm
container has nacm:default-deny-all
,
meaning that not even read access is allowed if no data is loaded.
The NSO installation ships with an XML initialization file containing
AAA configuration. The file is called aaa_init.xml
and
is, by default, copied to the CDB directory by the NSO install
scripts.
The local installation variant, targeting development only, defines two
users, admin
and oper
with passwords set to
admin
and oper
respectively for authentication.
The two users belong to user groups with NACM rules restricting their
authorization level.
The system installation aaa_init.xml
variant,
targeting production deployment, defines NACM rules only as users are, by
default, authenticated using PAM. The NACM rules target two user groups,
ncsadmin
and ncsoper
. Users belonging to the
ncsoper
group are limited to read-only access.
The default aaa_init.xml
file provided with the
NSO system installation must not be used as-is in a deployment
without reviewing and verifying that every NACM rule in the file matches
Normally the AAA data will be stored as configuration in CDB. This
allows for changes to be made through NSO's
transaction-based configuration
management. In this case the AAA cache will be updated automatically
when changes are made to the AAA data. If changing the AAA data via
NSO's configuration management is not
possible or desirable, it is alternatively possible to use the CDB
operational data store for AAA data. In this case the AAA cache can
be updated either explicitly e.g. by using the
maapi_aaa_reload()
function, see the
confd_lib_maapi(3) in Manual Pages
manual page, or by triggering
a subscription notification by using the "subscription lock" when
updating the CDB operational data store, see
Using CDB
in Development Guide.
Some applications may not want to expose the AAA data
to end users in the CLI or the Web UI. Two reasonable approaches exist
here and both rely on the tailf:export
statement.
If a module has tailf:export none
it will be
invisible to all agents. We can then either use a transform whereby
we define another AAA model and write a transform program which maps our
AAA data to the data which must exist in
tailf-aaa.yang
and
ietf-netconf-acm.yang
.
This way we can
choose to export and and expose an entirely different AAA model.
Yet another very easy way out, is to define a set of static AAA rules whereby a set of fixed users and fixed groups have fixed access to our configuration data. Possibly the only field we wish to manipulate is the password field.