VIP Authorization

VIP authentication and authorization go hand in hand. When an agent authenticates to a VOLTTRON platform that agent proves its identity to the platform. Once authenticated, an agent is allowed to connect to the message bus. VIP authorization is about giving a platform owner the ability to limit the capabilities of authenticated agents.

Authorization Implementation

Authorization has client and server implementations. On the client side, authorization is used to connect the auth subsystem to the auth service. This provides the connection needed to manage capabilites and protected topics. The server authorization is used to manage pending credentials and certificates that are handled by authentication, as well as managing capabilties and protected topics.

BaseServerAuthorization: * approve_authorization * deny_authorization * delete_authorization * get_authorization * get_authorization_status * get_pending_authorizations * get_approved_authorizations * get_denied_authorizations * update_user_capabilites * load_protected_topics * update_protected_topics

BaseClientAuthorization * connect_remote_platform

There are two parts to authorization:

  1. Required capabilities (specified in agent’s code)

  2. Authorization entries (specified via volttron-ctl auth commands)

The following example will walk through how to specify required capabilities and grant those capabilities in authorization entries.

Single Capability

For this example suppose there is a temperature agent that can read and set the temperature of a particular room. The agent author anticipates that building managers will want to limit which agents can set the temperature.

In the temperature agent, a required capability is specified by using the RPC.allow decorator:

@RPC.export
def get_temperature():
   ...

@RPC.allow('CAP_SET_TEMP')
@RPC.export
def set_temperature(temp):
   ...

In the code above, any agent can call the get_temperature method, but only agents with the CAP_SET_TEMP capability can call set_temperature.

Note

Capabilities are arbitrary strings. This example follows the general style used for Linux capabilities, but it is up to the agent author.

Now that a required capability has been specified, suppose a VOLTTRON platform owner wants to allow a specific agent, say Alice Agent, to set the temperature.

The platform owner runs vctl auth add to add new authorization entries or vctl auth update to update an existing entry. If Alice Agent is installed on the platform, then it already has an authorization entry. Running vctl auth list shows the existing entries:

...
INDEX: 3
{
  "domain": null,
  "user_id": "AliceAgent",
  "roles": [],
  "enabled": true,
  "mechanism": "CURVE",
  "capabilities": [],
  "groups": [],
  "address": null,
  "credentials": "JydrFRRv-kdSejL6Ldxy978pOf8HkWC9fRHUWKmJfxc",
  "comments": null
}
...

Currently AliceAgent cannot set the temperature because it does not have the CAP_SET_TEMP capability. To grant this capability the platform owner runs vctl auth update 3:

(For any field type "clear" to clear the value.)
domain []:
address []:
user_id [AliceAgent]:
capabilities (delimit multiple entries with comma) []: CAP_SET_TEMP
roles (delimit multiple entries with comma) []:
groups (delimit multiple entries with comma) []:
mechanism [CURVE]:
credentials [JydrFRRv-kdSejL6Ldxy978pOf8HkWC9fRHUWKmJfxc]:
comments []:
enabled [True]:
updated entry at index 3

Now Alice Agent can call set_temperature via RPC. If other agents try to call that method they will get the following exception:

error: method "set_temperature" requires capabilities set(['CAP_SET_TEMP']),
but capability list [] was provided

Multiple Capabilities

Expanding on the temperature-agent example, the set_temperature method can require agents to have multiple capabilities:

@RPC.allow(['CAP_SET_TEMP', 'CAP_FOO_BAR'])
@RPC.export
def set_temperature():
   ...

This requires an agent to have both the CAP_SET_TEMP and the CAP_FOO_BAR capabilities. Multiple capabilities can also be specified by using multiple RPC.allow decorators:

@RPC.allow('CAP_SET_TEMP')
@RPC.allow('CAN_FOO_BAR')
@RPC.export
def temperature():
   ...

Capability with parameter restriction

Capabilities can also be used to restrict access to a rpc method only with certain parameter values. For example, if Agent A exposes a method bar which accepts parameter x.

AgentA’s capability enabled exported RPC method:

@RPC.export
@RPC.allow('can_call_bar')
def bar(self, x):
   return 'If you can see this, then you have the required capabilities'

You can restrict access to Agent A’s bar method to Agent B with x=1. To add this auth entry use the vctl auth add command as show below:

vctl auth add --capabilities '{"test1_cap2":{"x":1}}' --user_id AgentB --credential vELQORgWOUcXo69DsSmHiCCLesJPa4-CtVfvoNHwIR0

The auth.json file entry for the above command would be:

{
  "domain": null,
  "user_id": "AgentB",
  "roles": [],
  "enabled": true,
  "mechanism": "CURVE",
  "capabilities": {
    "test1_cap2": {
      "x": 1
    }
  },
  "groups": [],
  "address": null,
  "credentials": "vELQORgWOUcXo69DsSmHiCCLesJPa4-CtVfvoNHwIR0",
  "comments": null
}

Parameter values can also be regular expressions:

(volttron)volttron@volttron1:~/git/myvolttron$ vctl auth add
domain []:
address []:
user_id []:
capabilities (delimit multiple entries with comma) []: {'test1_cap2':{'x':'/.*'}}
roles (delimit multiple entries with comma) []:
groups (delimit multiple entries with comma) []:
mechanism [CURVE]:
credentials []: vELQORgWOUcXo69DsSmHiCCLesJPa4-CtVfvoNHwIR0
comments []:
enabled [True]:
added entry domain=None, address=None, mechanism='CURVE', credentials=u'vELQORgWOUcXo69DsSmHiCCLesJPa4-CtVfvoNHwIR0', user_id='b22e041d-ec21-4f78-b32e-ab7138c22373'

The auth.json file entry for the above command would be:

{
  "domain": null,
  "user_id": "90f8ef35-4407-49d8-8863-4220e95974c7",
  "roles": [],
  "enabled": true,
  "mechanism": "CURVE",
  "capabilities": {
    "test1_cap2": {
      "x": "/.*"
    }
  },
  "groups": [],
  "address": null,
  "credentials": "vELQORgWOUcXo69DsSmHiCCLesJPa4-CtVfvoNHwIR0",
  "comments": null
}

Protecting Pub/Sub Topics

VIP authorization enables VOLTTRON platform owners to protect pub/sub topics. More specifically, a platform owner can limit who can publish to a given topic. This protects subscribers on that platform from receiving messages (on the protected topic) from unauthorized agents.

Example

To protect a topic, add the topic name to $VOLTTRON_HOME/protected_topics.json. For example, the following protected-topics file declares that the topic foo is protected:

{
   "write-protect": [
      {"topic": "foo", "capabilities": ["can_publish_to_foo"]}
   ]
}

Note

The capability name can_publish_to_foo is not special; It can be any string, but it is easier to manage capabilities with meaningful names.

Now only agents with the capability can_publish_to_foo can publish to the topic foo. To add this capability to authenticated agents, run vctl auth update (or volttron-ctl auth add for new authentication entries), and enter can_publish_to_foo in the capabilities field:

capabilities (delimit multiple entries with comma) []: can_publish_to_foo

Agents that have the can_publish_to_foo capabilities can publish to topic foo. That is, such agents can call:

self.vip.pubsub.publish('pubsub', 'foo', message='Here is a message')

If unauthorized agents try to publish to topic foo they will get an exception:

to publish to topic "foo" requires capabilities ['can_publish_to_foo'], but capability list [] was provided

Regular Expressions

Topic names in $VOLTTRON_HOME/protected_topics.json can be specified as regular expressions. In order to use a regular expression, the topic name must begin and end with a “/”. For example:

{
   "write-protect": [
      {"topic": "/foo/*.*/", "capabilities": ["can_publish_to_foo"]}
   ]
}

This protects topics such as foo/bar and foo/anything.