Table of Contents
This chapter describes how to use ConfD's built-in authentication and authorization mechanisms. Users log into ConfD through the CLI, NETCONF, 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.
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 ConfD daemon manages device configuration including AAA information. In fact, ConfD 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 ConfD by requiring a data model to be both
loaded and populated with data. ConfD uses the YANG module
tailf-aaa.yang
for authentication, while
ietf-netconf-acm.yang
(NACM, RFC 6536)
as augmented by tailf-acm.yang
is used for
group assignment and authorization. For backwards compatibility,
it is alternatively possible to use the older revision 2011-09-22
of tailf-aaa.yang
for all of authentication,
group assignment, and authorization. This legacy version of
tailf-aaa can be found in the $CONFD_DIR/src/confd/aaa
directory, but its
usage is not further described here. Detailed information about
this can be found in versions of this document for
ConfD-5.3 and earlier.
The NACM data model is targeted specifically towards access
control for NETCONF operations, and thus lacks some
functionality that is needed in ConfD, 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
$CONFD_DIR/src/confd/yang
directory in the release, while
tailf-aaa.yang
can be found in
the $CONFD_DIR/src/confd/aaa
directory.
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 ConfD 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 ConfD itself uses them. The data belongs to the application, but ConfD itself is the user of the data.
Since ConfD 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.
ConfD itself is configured through a configuration file -
confd.conf
.
In that file we have the following
items related to authentication and authorization:
/confdConfig/aaa/sshServerKeyDir
If SSH termination is enabled for NETCONF or the CLI, the
ConfD
built-in SSH server needs to have server keys. These
keys are generated by the
ConfD
install script and by
default end up in
$CONFD_DIR/etc/confd/ssh
.
It is also possible to use OpenSSH to terminate NETCONF or the CLI. If OpenSSH is used to terminate SSH traffic, the SSH keys are not necessary.
/confdConfig/aaa/sshPubkeyAuthentication
If SSH termination is enabled for NETCONF or the CLI, this item controls how the ConfD SSH daemon locates the user keys for public key authentication. See Section 14.4.1, “Public Key Login” for the details.
/confdConfig/aaa/localAuthentication/enabled
The term "local user" refers to a user stored
under /aaa/authentication/users
. The alternative
is a user unknown to
ConfD,
typically authenticated by PAM.
By default, ConfD 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 ConfD.
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.
/confdConfig/aaa/pam
ConfD 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 also assign a user to a set of groups.
PAM authentication is thoroughly described later in this chapter.
/confdConfig/aaa/defaultGroup
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.
/confdConfig/aaa/aaaBridge
This key will be described in the Section 14.9, “Populating AAA using external data” section.
/confdConfig/aaa/externalAuthentication
ConfD can authenticate users using an external executable. This is further described later in the Section 14.4.4, “External authentication” section.
/confdConfig/aaa/authenticationCallback/enabled
If this is set to "true", ConfD will, as the last step of every authentication attempt, invoke an application callback. The callback can reject an otherwise successful authentication. See the section called “AUTHENTICATION CALLBACK” in the confd_lib_dp(3) manual page for the details about this.
/confdConfig/aaa/authorization/callback/enabled
If this is set to "true", ConfD will invoke application callbacks for authorization. The callbacks can partially or completely replace the logic described in Section 14.6, “Authorization”. See the section called “AUTHORIZATION CALLBACKS” in the confd_lib_dp(3) manual page for the details about this.
Depending on northbound management protocol, when a user session is created in ConfD, it may or may not be authenticated. If the session is not yet authenticated, ConfD's AAA subsystem is used to perform authentication and authorization, as described below. If the session already has been authenticated, ConfD's AAA assigns groups to the user as described in Section 14.5, “Group Membership”, and performs authorization, as described in Section 14.6, “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 function
maapi_authenticate()
is used.
The different authentication mechanisms that may be used in these cases are described below. Regardless of which mechanism that is used, ConfD can optionally invoke an application callback as the last step of the authentication process, see the section called “AUTHENTICATION CALLBACK” in confd_lib_dp(3). The callback is not used for the actual authentication, but it can reject an otherwise successful authentication.
ConfD'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 Section 15.3, “NETCONF Transport Protocols”.
When NETCONF uses TCP, as described in Section 15.3, “NETCONF Transport Protocols”, 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 confd_cli . An important special case here is when a user has logged in to the host and invokes the command confd_cli from the shell.
When SNMP is used. SNMP has its own authentication mechanisms. See Section 17.5.2, “USM and VACM and ConfD AAA” .
When the function
maapi_start_user_session()
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
/confdConfig/aaa/sshPubkeyAuthentication
and
/confdConfig/aaa/localAuthentication/enabled
.
If
sshPubkeyAuthentication
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
sshPubkeyAuthentication
is set to system
,
localAuthentication
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
sshPubkeyAuthentication
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
Section 14.5, “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 and RSA 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
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 function maapi_authenticate()
is used.
In this case, ConfD
will by default try local authentication,
PAM, and external authentication, in that order, as described
below. It is possible to change the order in which these are
tried, by modifying the
confd.conf
.
parameter /confdConfig/aaa/authOrder
.
See confd.conf(5)
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
????
for details.
If all of the above fails and external authentication is enabled, the configured executable is invoked. See ???? for details.
If authentication succeeds, the user's group membership is established as described in ????.
On operating systems supporting PAM, ConfD also supports PAM authentication. Using PAM authentication with ConfD can be very convenient since it allows us to have the same set of users and groups having access to ConfD 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 ConfD 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 confd.conf by adding:
<pam> <enabled>true</enabled> <service>common-auth</service> </pam>
to the aaa section in confd.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 ConfD
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
ConfD as root,
the solution here is to change owner of a helper program called
$CONFD_DIR/lib/confd/lib/core/pam/priv/epam
and also set the setuid bit.
# cd $CONFD_DIR/lib/confd/lib/core/pam/priv/ # chown root:root epam # chmod u+s epam
PAM is the recommended way to authenticate ConfD 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 ConfD 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 confd[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 confd[14658]: audit user: test/5 assigned to groups: operator,admin <INFO> 28-Jan-2009::16:05:57.655 buzz confd[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 ConfD authentication is to use "external authentication".
If this feature is configured, ConfD
will invoke
the executable configured in
/confdConfig/aaa/externalAuthentication/executable
in
confd.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, ConfD
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.
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
ConfD
should use
as default when executing commands for this user.
$gid
is the UNIX integer group id
ConfD
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 ConfD
executes commands on behalf
of this user.
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. ConfD
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 ConfD
according to the setting of /confdConfig/aaa/expirationWarning
in confd.conf
.
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"
, ConfD
will try subsequent mechanisms configured for /confdConfig/aaa/authOrder
in confd.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 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 ConfD
when the authorization rules are checked.
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 ConfD 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
confd_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 (/confdConfig/aaa/defaultGroup
in
confd.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 ConfD 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, but if needed we can also register application callbacks to partially or completely replace this logic, see the section called “AUTHORIZATION CALLBACKS” in confd_lib_dp(3).
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
ConfD 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.
It is important to understand that a command rule for the CLI applies to the string as entered by the user. The command rules are not aware of the data model. Thus it is not possible to have a rule like:
<cmdrule> <name>delete-eth0</name> <context>cli</context> <command>delete interfaces interface eth0</command> <access-operations>exec</access-operations> <action>deny</action> </cmdrule>
to protect a specific interface from removal in The Juniper CLI. The user can enter:
joe@host% edit interfaces joe@host% delete interface eth0
making the command rule above moot.
In the Cisco like CLIs it makes more sense to use command rules to protect data. This is due to the command oriented character of the Cisco CLIs.
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.
Another thing that is important for command rule
processing of CLI commands is the mode. If we enable the
feature /confdConfig/cli/modeInfoInAAA
, the
command rule matching will match on a string where the CLI
mode is prepended. This makes command rule processing more
useful in the Cisco style CLIs than in the Juniper style
CLI.
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 ConfD 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 /confdConfig/logs/developerLogLevel
entry in
confd.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 the rule applies to. The special value
*
matches all modules.
This is a choice between three possible leafs that are used for matching:
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 /interfaces/interface/mtu
.
Instantiated key: as in
/interfaces/interface[name="eth0"]/mask
matches the mask element only for the interface
name "eth0". 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
/hosts/host[name="venus"]/servers/server/ip
matches the "ip" element for all servers, but only
for the host named "venus".
Wild card at end as in:
/interfaces/interface/*
does not
match /interfaces/interface
but
rather all children of that path.
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 ConfD 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 ConfD 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 ConfD 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.
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
ConfD 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> <module-name>tailf-aaa</module-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.
ConfD 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> <module-name>tailf-aaa</module-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.
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.
ConfD'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 ConfD, 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 AAA data can either be stored in CDB or in an external daemon as described in the chapter Chapter 7, The external database API. The only new problem when we use CDB to store the AAA data is to initialize the AAA database. This can be done as described in the chapter Chapter 5, CDB - The ConfD XML Database, from an XML document containing real data.
ConfD
ships with a decent initialization document for the
AAA database. The file is called aaa_init.xml
and
is by default copied to the CDB directory by the
ConfD
install scripts. The file defines two users, admin
and
oper
with passwords set to admin
and
oper
respectively.
Normally the AAA data will be stored as configuration in CDB. This
allows for changes to be made through ConfD'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
ConfD'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
????
manual page, or by triggering
a subscription notification by using the "subscription lock" when
updating the CDB operational data store, see
????.
The confd_aaa_bridge
program
described here is deprecated. It does not support the NACM data
model. It may still be useful to study this as an example of an
external data provider for AAA data, however the implementation
follows the same principles as for other external data
providers.
An alternative to storing the AAA data in CDB is to
store it outside of ConfD. ConfD comes with an example
implementation of such a program. It is called
confd_aaa_bridge
and is fully described
in the man page confd_aaa_bridge(1).
The procedure here is precisely the same as with any other data model - with the exception that ConfD itself is a user of this data and reads it. Thus if using CDB to store the AAA data is not an option, the API for external databases must be used to populate the AAA tree.
The YANG model which describes this is the same
tailf-aaa.yang
but annotated with a
callpoint. It is shipped together with the example implementation
called confd_aaa_bridge
. When we compile the
tailf-aaa.yang
with the callpoint for external data
we name the resulting file aaa_bridge.fxs
.
The name of the fxs file is not significant, the
aaa_bridge.fxs
name is used here only to
distinguish it from the aaa_cdb.fxs
name that has
been used for the CDB version of legacy tailf-aaa. We can
equally well use the more "natural" name
tailf-aaa.fxs
in both cases.
We must additionally annotate
the ietf-netconf-acm.yang
module with a callpoint
and compile it, if the group assignment and authorization data is to
be provided by an external data provider. This annotation must be done
in addition to the annotation done by the
ietf-netconf-acm-ann.yang
module included in the
ConfD release.
The example program confd_aaa_bridge.c
which
is delivered
as source code in the ConfD release contains an example
implementation of external storage of the AAA data in an ad
hoc .ini style file. (This is only of interest for users that
do not use CDB to store any data at all.) See the UNIX man page
confd_aaa_bridge(1).
The confd_aaa_bridge
program implements a
configuration data provider, and thus changes to the AAA data can be
made through ConfD's configuration management just as when
the data is stored as configuration in CDB. And similar to the CDB
case, if changing the AAA data via ConfD's configuration
management is not
possible or desirable, it is alternatively possible to let the AAA
data be provided by an operational (i.e. read-only) external data
provider. In either case, when we use an external data provider for
AAA, the AAA cache must always be updated explicitly, e.g. by using
the maapi_aaa_reload()
function, see the confd_lib_maapi(3) manual page. For a
configuration data provider, the
confd_aaa_reload()
function may be more
convenient, see the confd_lib_dp(3)
manual page - this function is used by
confd_aaa_bridge
.
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.