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.

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 VOLLTRON platform owner wants to allow a specific agent, say AliceAgent, to set the temperature.

The platform owner runs volttron-ctl auth add to add new authorization entries or volttron-ctl auth update to update an existing entry. If AliceAgent is installed on the platform, then it already has an authorization entry. Running volttron-ctl 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 volttron-ctl 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 AliceAgent 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 AgentA 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 AgentA’s bar method to AgentB 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

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'

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
}