How does the default statement influence the behavior of load command in CLI?

Hi,

Recently I did a test on the load command in CLI.

When I added a default statement for the data node such as a leaf in yang model. if I loaded configuration from a file which didn’t set a value for that node, I was required to have write permission on that node. Otherwise CLI returned the error “access denied”.

When no default statement for that data node, even without write permission on that node, I was still able to load configuration from the same file.

Now I am curious how the default statement influences the behavior of load command in CLI. Could you help me with asking the question? Thanks.

Best,
Li

Hi,
Are you using load merge, override, or replace?

Just replaced ‘developerLogLevel’ to ‘btrace’ in /etc/confd/confd.conf.
My Original query is… REST query is not working for non-admin users…

-bash-4.2$ curl -v -X POST -u test:password http://10.26.138.165:80/rest/operational-state/show-system-monitor/

  • About to connect() to 10.26.138.165 port 80 (#0)
  • Trying 10.26.138.165…
  • Connected to 10.26.138.165 (10.26.138.165) port 80 (#0)
  • Server auth using Basic with user ‘test’

POST /rest/operational-state/show-system-monitor/ HTTP/1.1
Authorization: Basic dGVzdDpwYXNzd29yZA==
User-Agent: curl/7.29.0
Host: 10.26.138.165
Accept: /

< HTTP/1.1 405 Method Not Allowed
< Date: Thu, 07 Jul 2022 12:43:21 GMT
< Server: NOS WWW
< Authentication-Token: TXV8cHNLYFJBUmk0ZUpZcEBXT0dZbT51eHtSTXw5V30=
< Allow: POST
< Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
< Content-Length: 0
< Content-Type: text/html
< Pragma: no-cache
<

  • Connection #0 to host 10.26.138.165 left intact
    -bash-4.2$

Hi, override and replace.

Which ConfD version are you using? If you can provide a YANG snippet and NACM rules that can be used to reproduce the issue, that would help pinpoint the issue.

I observed this result on ConfD 7.7.3.

The YANG model has a very simple schema tree.

  typedef small-int {
    type int32 {
      range "-100000 .. 100000";
    }
  }

  container new {
    tailf:callpoint simplecp;
    leaf int {
      type small-int;
      default 32;
    }
  }

The user belonging to group admin can access all YANG models but the above model since no NACM rule for it and by default access is denied.

    <write-default>deny</write-default>
    <read-default>deny</read-default>
    <groups>
      <group>
        <name>admin</name>
        <user-name>admin</user-name>
        <user-name>private</user-name>
      </group>
    </groups>
    <rule-list>
      <name>admin</name>
      <group>admin</group>
      <rule>
        <name>tailf-aaa-authentication</name>
        <module-name>tailf-aaa</module-name>
        <action>permit</action>
      </rule>
      <rule>
        <name>tailf-aaa-user</name>
        <module-name>tailf-aaa</module-name>
        <action>permit</action>
      </rule>
      <rule>
        <name>tailf-webui-user</name>
        <module-name>tailf-webui</module-name>
        <action>permit</action>
      </rule>
      <rule>
        <name>ietf-netconf-acm</name>
        <module-name>ietf-netconf-acm</module-name>
        <action>permit</action>
      </rule>
      <rule>
        <name>tailf-tls</name>
        <module-name>tailf-tls</module-name>
        <action>permit</action>
      </rule>
      <rule>
        <name>tailf-webui</name>
        <module-name>tailf-webui</module-name>
        <action>permit</action>
      </rule>
      <rule>
        <name>tailf-acm</name>
        <module-name>tailf-acm</module-name>
        <action>permit</action>
      </rule>
      <rule>
        <name>smp</name>
        <module-name>smp</module-name>
        <action>permit</action>
      </rule>
    </rule-list>

The CLI console of reproduction on native ConfD is shown as below.

node# show running-config | display xml | save file.xml
node# config 
Entering configuration mode terminal
node(config)# load override file.xml 
Loading.
Error: access denied

When CLI returned the error “access denied”, I found the below logs.

<DEBUG> 8-Jul-2022::06:09:47.887 node-150-132-197-18 confd[<0.337.0>]: devel-aaa User: admin[admin] rejected data access path /new:new/int op read due to no rule matched and /nacm/read-default is 'deny'
<DEBUG> 8-Jul-2022::06:09:47.887 node-150-132-197-18 confd[<0.337.0>]: devel-aaa User: admin[admin] rejected data access path /new:new/int op read due to no rule matched and /nacm/read-default is 'deny'
<DEBUG> 8-Jul-2022::06:09:59.895 node-150-132-197-18 confd[<0.376.0>]: devel-aaa User: admin[admin] rejected data access path /new:new op write due to no rule matched and /nacm/write-default is 'deny'

With the ConfD 7.8.3, 7.7.6 etc. releases sometime end of August I notice that there will be a correction to the behavior you describe:

The CLI command ‘load override file.xml’ would fail if the user didn’t have (NACM) delete access to the whole configuration. Now only the part of the configuration where the user has delete access will be attempted to be deleted, and the rest will be ignored.

I added the delete access for that model new and tested on ConfD 7.7.3 again, that error didn’t occur no longer. But I’m really confused why. I mean I don’t know the logic of current “load override”.

I’m sorry to forget telling you that no value was added for /new/int at initial in my test. I know the behavior of override is to firstly delete the whole configuration and then load the new configuration. According to what you explained, since the user didn’t have delete access to /new/int, that data node would be ignored and its value kept empty. When the user loaded override file.xml, since there was no value specified for /new/int in file.xml, it was expected that no change happened on /new/int and ConfD didn’t need to do any operations to /new/int. So I would have thought even if no NACM rule for the model new, “load override” could work fine. But the result was not as expected.

Could you make further clarification? Thanks.

Are you handling default values in your get_elem() (and get_next() etc) callbacks?
If not, that explains the behavior you observe. If you just provide the default value back to ConfD as if it was a non-default value ConfD will try to delete it when you for example use “load override” in the CLI.

Some psueudo code to illustrate how you should implement your get_elem() callback:

static int get_elem(struct confd_trans_ctx *tctx,
                    confd_hkeypath_t *keypath)
{
  confd_value_t v;

  if ("value exists and is not default") {
    confd_data_reply_value(tctx, &v);
  } else if ("value exists and is default"} {
    CONFD_SET_DEFAULT(&v);
    confd_data_reply_value(tctx, &v);
  } else if ("value does not exist but the node has a default value") {
    CONFD_SET_DEFAULT(&v);
    confd_data_reply_value(tctx, &v);
  } else { //value does not exist and the node has no default value
    confd_data_reply_not_found(tctx);
  } 
  return CONFD_OK;
}

In our implementation, no matter the node has a default value, get_elem just only calls confd_data_reply_not_found to return if value does not exist. So I think ConfD does not see a value on that node and will not try to delete it.

What is your setting for /confdConfig/defaultHandlingMode in confd.conf?
Do you have the CONFD_DAEMON_FLAG_NO_DEFAULTS flag set when you call confd_init_daemon() and/or confd_set_daemon_flags()?

/confdConfig/defaultHandlingMode is set to “explicit”.
Only CONFD_DAEMON_FLAG_BULK_GET_CONTAINER flag is set for confd_set_daemon_flags().

This should be pointed out in other parts of the documentation too, but from the confd_lib_dp(3) man page under CONFD_DAEMON_FLAG_NO_DEFAULTS:

By default, ConfD assumes that the daemon doesn’t know about default values, and thus whenever default values come into effect,
ConfD will issue set_elem() callbacks to set those values, even if they have not actually been set
by the northbound agent.

This means that if the data provider application, like in your case, reply with confd_data_reply_not_found() when there is a default value in the YANG model but it does not exist, ConfD will try to set it. But since the user does not have access to that leaf, permission to do so will be denied.

From the confd_lib_dp(3) man page under get_elem()

If the leaf does not exist the callback must call confd_data_reply_not_found(). If the leaf
has a default value defined in the data model, and no value has been set, the callback should use
confd_data_reply_value() or confd_data_reply_value_attrs() with a value of
type C_DEFAULT - this makes it possible for northbound agents to leave such leafs out of the data
returned to the user/manager (if requested).

With your settings, if you do not want to support providing a value of type C_DEFAULT for leafs and case statements that have a default value in the YANG model, and you want to avoid the “permission denied” issue, you need to at least initialize your data provider with the default value. ConfD will then from that point on use the set_value() callback to set the default value when the leaf/case is deleted.

I don’t get what you mean when you said

you need to at least initialize your data provider with the default value. ConfD will then from that point on use the set_value() callback to set the default value when the leaf/case is deleted.

I should also add CONFD_DAEMON_FLAG_NO_DEFAULTS for confd_set_daemon_flags(), then in my case ConfD will not set the default value for that leaf which defines a default value and its value doesn’t exist, right? Or could you explain further on that sentence? Thanks.

No, unless you plan on returning C_DEFAULT as described previously you should not set CONFD_DAEMON_FLAG_NO_DEFAULTS as the confd_lib_dp(3) man page describes.

For leafs and cases that have a default value in the YANG model, you either reply with an actual value or a value of type C_DEFAULT. You do not reply with confd_data_reply_not_found() for such leafs/cases