Introduction¶
Secure ROS is a “fork” of core ROS packages to enable secure communication among ROS nodes (not to be confused with SROS, http://wiki.ros.org/SROS/). The main goal of Secure ROS is to enable secure communication for regular users of ROS.
The modified packages are rosmaster, rosgraph, roscpp, rospy, xmlrpcpp and nodelet.
Secure ROS uses IPSec in transport mode and modified versions of the ROS master, rospy module and roscpp library to ensure secure communication. At run time, the user can specify authorized subscribers and publishers to topics, setters and getters to parameters and providers (servers) and requesters (clients) of services in the form of a YAML configuration file for the ROS master. Secure ROS allows only authorized nodes to connect to topics, services and parameters listed in the configuration file.
Secure ROS keeps ROS public API intact, allowing user level ROS packages to be used without modification. If no YAML configuration is provided, Secure ROS behaves like regular ROS (with no security).
The Secure ROS website is at http://secure-ros.csl.sri.com/.
Installation¶
Please install ROS first (see http://wiki.ros.org/ROS/Installation).
To install Secure ROS, download the debian package from http://secure-ros.csl.sri.com/download/ and install with dpkg. E.g.
dpkg -i secure-ros-<RELEASE>-secure-ros_<VERSION>_<ARCH>.deb
The Secure ROS package files are installed in /opt/secure_ros/<release>/
and includes modified versions of some ROS libraries and modules.
Usage¶
The user can use the features of Secure ROS by sourcing /opt/secure_ros/<release>/setup.bash
instead of /opt/ros/<release>/setup.bash
. This will automatically include all packages installed in /opt/ros/<release>/
.
To enable secure communication, you also need to provide an authorization configuration file (relative to the working directory) when starting the ROS master. E.g.
ROS_AUTH_FILE=ros_auth.yaml roscore
If the ROS_AUTH_FILE
environment variable is not defined, the Secure ROS master behaves like the regular ROS master. The configuration file is described in the next section. For further details, please see the example.
Authorization Configuration¶
A sample configuration file from the multi_pubsub example is provided below. The authorization configuration YAML file is a dictionary with the following keys: aliases
, topics
, nodes
, parameters
and services
. The topics
key is required but the others are optional. These key-value pairs are described in detail below.
aliases:
vm2: [192.168.10.202]
vm3: [192.168.10.203]
topics:
/chatter:
publishers: [vm2]
subscribers: [vm3]
/counter:
publishers: [vm2]
subscribers: [vm3]
nodes:
/talker: [vm2]
/listener: [vm3]
aliases
: The value of thealiases
key is a dict of (key,value) pairs where each keys is an alias and each value is the corresponding list of IP addresses. The IP address can also be a hostname that can be resolved by the master.topics
: The value of thetopics
key is a dict of (key,value) pairs where the keys are all the allowed topics excepting reserved topics. The reserved topics arerosout
androsout_agg
. Each topic value is another dictionary with two required keys:publishers
andsubscribers
. The value of thepublisher
andsubscriber
keys is a list of IP addresses that are the authorized publishers and subscribers to that topic respectively.nodes
: This is an optional key. The value of the key is a dict of (key,value) pairs where the keys are node names excepting reserved nodes. The reserved nodes arerosout
. Each value is list of the IP addresses from which that node can register. If a node is listed as a key, then that node may only register from one of the associated IP addresses. If a node is not listed as a key or if thenodes
key is not present, then any node may register from the list ofauthorized_ip_addresses
.parameters
: This is an optional key. The value of the key is a dict of (key,value) pairs where the keys are parameter names (or parameter prefixes) other than the reserved parameters. The value of each key is another dict with two keys:setters
(required) andgetters
(optional). The value of thesetters
andgetters
keys is a list of IP addresses that are the authorized setters and getters of that parameter respectively. If a parameter name is not listed, then that parameter may be set and accessed from any IP address inauthorized_ip_addresses
. If a parameter does not have thegetters
key, that parameter may be accessed from any IP address inauthorized_ip_addresses
.The reserved parameters are:
/run_id
,/rosversion
,/rosdistro
,/tcp_keepalive
,/use_sim_time
,/enable_statistics
,/statistics_window_min_elements
,/statistics_window_max_elements
,/statistics_window_min_size
,/statistics_window_max_size
services
: This is an optional key. The value of the key is a dict of (key,value) pairs. The keys are service names other than the reserved services. The value of each key is another dict with two keys:providers
(required) andrequesters
(optional). The value of theproviders
andrequesters
keys is a list of IP addresses that are the authorized providers and requesters of that service respectively. If a service name is not listed, then that service may be set and accessed from any IP address inauthorized_ip_addresses
. If a service does not have therequesters
key, that service may be accessed from any IP address inauthorized_ip_addresses
.
authorized_ip_addresses
is an internal set containing all the IP addresses that are listed in the authorization file. Any request from an IP address not on this list is, in general, discarded. This list is also used as an authorized list for nodes, parameters or services that are not explicitly listed in the file.
IPSec configuration¶
Secure ROS relies on IPSec to ensure that IP packets are not tampered or spoofed. Therefore, IPsec needs to be configured correctly in order for Secure ROS to work well against malicious attacks. We have provided the secure_ros_tools
python package in order to generate keys and configuration files for IPSec. The following steps need to be followed to create the IPsec configuration files.
Install the
secure_ros_tools
package.sudo apt-get install racoon ipsec-tools python-pip python3-pip sudo pip install git+https://github.com/SRI-CSL/secure_ros_tools.git
Create YAML file (e.g.
hosts.yaml
) with all hosts in the system as follows. In this example, there are three hosts.machine1: 192.168.10.201 machine2: 192.168.10.202 machine3: 192.168.10.203
Generate configuration files for IPsec.:
create_ipsec_conf -i hosts.yaml
This will create a folder for each machine with keys and configuration files. E.g. the files for
machine1
are located inoutput/machine1
. Atar.gz
file is also created for each machine.Copy the
tar.gz
files to the appropriate machines and untar them into the appropriate folder on the respective machines.E.g., on
machine1
, untar the contents ofmachine1.tgz
.sudo tar xzf machine1.tgz -C /
Examples¶
Simple talker listener example (simple_pubsub)¶
This example is a simple example to run Secure ROS using the built-in talker and listener example in a linux machine. The ROS master and nodes run on the same machine.
Requirements¶
Install ROS and corresponding version of Secure ROS on Ubuntu (ROS Indigo on 14.04 and ROS Kinetic on 16.04).
Using Secure ROS¶
You need to source Secure ROS in each terminal.
source /opt/secure_ros/kinetic/setup.bash
Authorization configuration¶
The authorization file (ros_auth_simple_pubsub.yaml
) is shown below. It allows certain nodes and all topics to be published to and subscribed to from your machine (machine1, IP address: 127.0.0.1
).
aliases:
machine1: [127.0.0.1]
topics:
/chatter:
publishers: [machine1]
subscribers: [machine1]
/counter:
publishers: [machine1]
subscribers: [machine1]
nodes:
/talker: [machine1]
/listener: [machine1]
Running the example¶
All the nodes are run on the same machine (machine1). Change the working directory to examples/simple_pubsub
in all the cases and start the master and nodes in separate consoles. The ROS_AUTH_FILE
path is resolved with respect to the current working directory. Note that if ROS_IP
is not set, the ROS master is bound to all the network devices. ROS_MASTER_URI
has the default value http://<hostname>:11311
which resolves to 127.0.0.1
in this case.
unset ROS_IP
unset ROS_MASTER_URI
source /opt/secure_ros/kinetic/setup.bash
Start the master node from the
simple_pubsub
directory.ROS_AUTH_FILE=ros_auth_simple_pubsub.yaml roscore
Start the talker node.
rosrun test_secure_ros talker.py
You may also run the C++ version instead of the python version (
rosrun test_secure_ros talker2
)Start the listener node.
rosrun test_secure_ros listener.py
You may also run the C++ version instead of the python version (
rosrun test_secure_ros listener2
)
Test Secure ROS¶
We test Secure ROS by querying the master locally and externally as described in the following sections.
Test locally¶
You need to source Secure ROS in each terminal. ROS_MASTER_URI
has the default value which is http://<hostname>:11311
and the requests are routed through the local network.
unset ROS_IP
unset ROS_MASTER_URI
source /opt/secure_ros/kinetic/setup.bash
Run the commands in ROS commands. You should have full access.
Test externally¶
If you set ROS_MASTER_URI
with the external IP address of your machine (e.g. http://192.168.10.201:11311
), then the requests are routed through the network and not loopback (127.0.0.1
).
You will need to replace the IP address 192.168.10.201
with your actual IP address.
source /opt/secure_ros/kinetic/setup.bash
export ROS_IP=192.168.10.201
export ROS_MASTER_URI=http://192.168.10.201:11311
Run the commands in ROS commands. Secure ROS will deny the requests since the external IP address (192.168.10.201
) is not an authorized IP address for any of these topics.
You may repeat this example with ros_auth_simple_pubsub2.yaml
where the external IP address has been added to the alias for machine1.
(You will need to replace the IP address 192.168.10.201
in ros_auth_simple_pubsub2.yaml
with your actual IP address.)
In this case, irrespective of whether you use the local (http://localhost:11311
) or external IP address (http://192.168.10.201:11311
) in the ROS_MASTER_URI
on machine1, you will be able to gain full access.
However, you will not be able to access the nodes from another machine (machine2, IP address e.g.: 192.168.10.202
).
On machine2 (or any other machine on the network), set ROS_MASTER_URI
.
source /opt/secure_ros/kinetic/setup.bash
export ROS_MASTER_URI=http://192.168.10.201:11311/
You will also need to set ROS_IP
if the hostname of machine2 cannot be resolved from machine1. Please see ROS Network set-up for details.
export ROS_IP=192.168.10.202
You may then run the commands in ROS commands. You should be unable to register with the master, subscribe, publish, or otherwise access information.
Test from network¶
You may modify the previous example by adding a second authorized machine (e.g. machine2 with IP address 192.168.10.202
) to the subscribers for topic /chatter
(ros_auth_simple_pubsub_network.yaml
). Run the following commands in each terminal in machine1. You only have to set ROS_IP
on all machines if all the hostnames cannot be resolved by all the machines as is standard ROS Network set-up.
source /opt/secure_ros/kinetic/setup.bash
export ROS_MASTER_URI=http://192.168.10.201:11311
export ROS_IP=192.168.10.201
To start the master node on machine1,
ROS_AUTH_FILE=ros_auth_simple_pubsub2.yaml roscore
Start the talker on machine1,
rosrun test_secure_ros talker.py
Start the listener on machine1,
rosrun test_secure_ros listener.py
On machine2, set ROS_MASTER_URI
.
source /opt/secure_ros/kinetic/setup.bash
export ROS_MASTER_URI=http://192.168.10.201:11311/
You will also need to set ROS_IP
if the hostname of machine2 cannot be resolved from machine1.
export ROS_IP=192.168.10.202
You should then be able to subscribe to /chatter
from machine2.
rostopic echo /chatter
However you will be unable to subscribe to /counter
from machine2.
rostopic echo /chatter
You will also have restricted access to information from the master on machine2. E.g. rostopic list will list only a subset of the topics (i.e. the topics that machine2 needs to know).
ROS commands¶
You may test Secure ROS by trying ROS command line tools.
Query rosmaster about topics
rostopic list
Subscribe to topics
rosrun test_secure_ros listener.py --anon
Publish to topics
rosrun test_secure_ros talker.py --anon
Call service (e.g.
/talker/get_loggers
)rosservice call /talker/get_loggers
Get parameters
rosparam list rosparam get /rosdistro
Set parameters
rosparam set /rosdistro "jade" rosparam get /rosdistro
Kill nodes
rosnode kill -a
Multi-machine talker listener example (multi_pubsub)¶
This example is a multi-machine example to run Secure ROS using the built-in talker and listener example. This example uses multiple VMs configured to use IPSec and requires Virtualbox, Vagrant and Ansible. Please see the section on Vagrant and ansible for details on installing and using vagrant and ansible.
The vagrant configuration for this example is loaded from the example.yaml
file
cpus: 1
memory: 1024
master_ip_address: 192.168.10.201
arch: amd64
pkg: ros-comm
machines:
pubsub1: 192.168.10.201
pubsub2: 192.168.10.202
pubsub3: 192.168.10.203
pubsub4: 192.168.10.204
The minimum number of Virtual Machines (VMs) required for the example is 3. pubsub4 may be used as a malicious machine to test Secure ROS and intercept ROS communication.
Requirements¶
The following requirements are needed to run the example.
- Connection to the internet on the host machine so that vagrant and ansible can fetch the images and debian packages.
- At least N=4 free cores on the host machine
- At least 4096MB RAM for the guest machines (1024 MB per guest)
If you don’t meet the requirements for N=4, you may try with N=3 (3 cores, 3072MB RAM) or use less RAM.
Starting the VMs¶
You may bring up the VMs using the following command.
vagrant up
Vagrant tasks¶
- Bring up the corresponding VMs as specified in the
example.yaml
configuration file. - Set up the networking as a private network along with appropriate IP addresses as specified in
example.yaml
. - Provision the VMs as specified in the
example.yaml
andsecure_ros.yaml
files.
Ansible playbooks¶
plays/install-secure-ros.yaml
: This installs the appropriate Secure ROS package from a deb file. If the deb file is not present, it is is created once (and used from that point).plays/build-secure-ros.yaml
: This builds the appropriate Secure ROS package and creates the deb file.plays/configure-secure-ros.yaml
: This sets up the Secure ROS environment with the appropriate bashrc files containing ROS_IP, ROS_MASTER_URI, location of the source file.plays/configure-racoon.yaml
: This configures the system security (IPsec) usingracoon
. The secure keys are created in each VM and the public keys are distributed to all VMs. The racoon and setkey configuration files are also created and the corresponding services are started. This information
Authorization configuration¶
The authorization configuration is in ros_auth_multi_pubsub.yaml
and its contents are presented below.
aliases:
vm2: [192.168.10.202]
vm3: [192.168.10.203]
topics:
/chatter:
publishers: [vm2]
subscribers: [vm3]
/counter:
publishers: [vm2]
subscribers: [vm3]
nodes:
/talker: [vm2]
/listener: [vm3]
Running the example¶
To run the nodes, run the following commands in the different VMs. Note that the correct environment is sourced from the files generated by ansible.
You will be able to ssh into each VM using vagrant from the root of the example folder (where the Vagrantfile is). For instance, to ssh into VM1, use the following command.:
vagrant ssh pubsub1
On VM1
ROS_AUTH_FILE=/vagrant/ros_auth_multi_pubsub.yaml roscore
On VM2
rosrun test_secure_ros talker.py
You may also run the C++ version instead of the python version.
rosrun test_secure_ros talker2
On VM3
rosrun test_secure_ros listener.py
You may also run the C++ version instead of the python version.
rosrun test_secure_ros listener2
Test example¶
You may test Secure ROS as follows.
Check IPSec: You may check the communication between VMs using
tcpdump
. Note that in Ubuntu 16.04, the network device may be namedenp0s8
or similar rather thaneth1
. You may find the name of the device usingifconfig
and checking the device name that has the IP address192.168.10.201
on the guest machine.:vagrant ssh pubsub2 sudo tcpdump -i eth1
You should see something like the following. The IP packets exchanged between pubsub2 and pubsub3 (192.168.10.203) are shown with AH (Authentication Headers) and ESP (Encapsulating Security Payload).
[vagrant@pubsub2 ~]$ sudo tcpdump -i eth1 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes 23:24:01.021656 IP pubsub2 > 192.168.10.203: AH(spi=0x0622f190,seq=0x4e): ESP(spi=0x0914f871,seq=0x4e), length 76 23:24:01.022429 IP 192.168.10.203 > pubsub2: AH(spi=0x076e48d8,seq=0x50): ESP(spi=0x0c784394,seq=0x50), length 68 23:24:02.021656 IP pubsub2 > 192.168.10.203: AH(spi=0x0622f190,seq=0x4f): ESP(spi=0x0914f871,seq=0x4f), length 100 23:24:02.022335 IP 192.168.10.203 > pubsub2: AH(spi=0x076e48d8,seq=0x51): ESP(spi=0x0c784394,seq=0x51), length 68
Query rosmaster about topics: You may query the rosmaster for topics from VM1. All topics are shown.
[vagrant@pubsub1 ~]$ rostopic list /chatter /counter /rosout /rosout_agg
You may query the rosmaster for topics from VM2. Only topics relevant to VM2 are shown.
[vagrant@pubsub2 ~]$ rostopic list /chatter /counter /rosout
You may query the rosmaster for topics from VM4. Zero topics are shown since VM4 is not an authorized IP machine.
[vagrant@pubsub4 ~]$ rostopic list
Starting a node from the wrong machine: Starting a new node with the same name as an existing node causes the master to send a shutdown signal to the existing node, but this is not allowed if the list of nodes with corresponding IP addresses is present in the authorization file as it is in the example.
E.g. Launch
/listener
from VM3, but with the node name/talker
. The node has permission to listen to the topics, but not to have the name/talker
.[vagrant@pubsub3 ~]$ rosrun test_secure_ros listener.py --name talker [FATAL] [WallTime: 1490114153.855925] unable to register publication [/rosout] with master: Not authorized [FATAL] [WallTime: 1490114153.863927] unable to register service [/talker/get_loggers] with master: Not authorized [FATAL] [WallTime: 1490114153.867944] unable to register service [/talker/set_logger_level] with master: Not authorized Started node /talker Subscribing to /chatter [FATAL] [WallTime: 1490114154.377988] unable to register subscription [/chatter] with master: Not authorized Subscribing to /counter [FATAL] [WallTime: 1490114154.886248] unable to register subscription [/counter] with master: Not authorized
On the master, you see that the node name registration is denied.
[auth][WARNING] registerPublisher( /talker, /rosout, http://192.168.10.203:48231/, 192.168.10.203 ) caller_id not authorized [auth][WARNING] registerSubscriber( /talker, /counter, http://192.168.10.203:48231/, 192.168.10.203 ) caller_id not authorized
Subscribing to topics: You can try to subscribe to a topic from an unauthorized machine. Note that if an IP address is an authorized publisher to a topic, then it is implicitly an authorized subscriber.
E.g. Run
listener.py
from VM2 with anonymous name.[vagrant@pubsub4 ~]$ rosrun test_secure_ros listener.py --anon FATAL] [WallTime: 1490826202.717182] unable to register publication [/rosout] with master: caller_id not authorized [FATAL] [WallTime: 1490826202.723819] unable to register service [/listener_11108_1490826202704/get_loggers] with master: caller_id not authorized [FATAL] [WallTime: 1490826202.727966] unable to register service [/listener_11108_1490826202704/set_logger_level] with master: caller_id not authorized Started node /listener_11108_1490826202704 Subscribing to /chatter [FATAL] [WallTime: 1490826203.239365] unable to register subscription [/chatter] with master: caller_id not authorized Subscribing to /counter [FATAL] [WallTime: 1490826203.750798] unable to register subscription [/counter] with master: caller_id not authorized
Publishing to topics:
You can try to publish to a topic from an unauthorized machine. E.g. Run
talker.py
from VM3 with anonymous name.vagrant@pubsub3 ~]$ rosrun test_secure_ros talker.py --anon ... Started node /talker_11475_1490827477247 Publishing to /chatter [FATAL] [WallTime: 1490827477.878853] unable to register publication [/chatter] with master: topic not authorized Publishing to /counter [FATAL] [WallTime: 1490827478.385780] unable to register publication [/counter] with master: topic not authorized
On the master.
[auth][WARNING] registerPublisher( /talker_11475_1490827477247, /chatter, http://192.168.10.203:37924/, 192.168.10.203 ) topic not authorized ... [auth][WARNING] registerPublisher( /talker_11475_1490827477247, /counter, http://192.168.10.203:37924/, 192.168.10.203 ) topic not authorized
Service client: You can try to call a service from an unauthorized machine (VM4). E.g. Call
/talker/get_loggers
from VM4.[vagrant@pubsub4 ~]$ rosservice call /talker/get_loggers ERROR: Unable to determine type of service [/talker/get_loggers].
On the master.
[auth][WARNING] lookupService( /rosservice, /talker/get_loggers, 192.168.10.204 ) service not authorized
Get parameters: You can try to get parameter from unauthorized machine (VM4).
[vagrant@pubsub4 ~]$ rosparam list
You can try to get parameter from authorized machine (VM2).
[vagrant@pubsub2 ~]$ rosparam get /rosdistro 'indigo '
Set parameters: You can try to set parameter from unauthorized machine (VM2).
[vagrant@pubsub2 ~]$ rosparam set /rosdistro "jade" ... rosgraph.masterapi.MasterError: parameter not authorized
You can try to set parameter from authorized machine (VM1).
[vagrant@pubsub1 ~]$ rosparam get /rosdistro 'indigo ' [vagrant@pubsub1 ~]$ rosparam set /rosdistro jade [vagrant@pubsub1 ~]$ rosparam get /rosdistro jade
Killing nodes: You can try to kill nodes from an unauthorized machine (only the master machine is allowed to kill nodes). First from VM3.
[vagrant@pubsub3 ~]$ rosnode kill -a killing: * /listener * /rosout * /talker ERROR: Failed to kill: * /listener * /talker [vagrant@pubsub3 ~]$ rosnode list /listener /rosout /talker
Next from VM1.
[vagrant@pubsub1 ~]$ rosnode kill -a killing: * /listener * /rosout * /talker killed [vagrant@pubsub1 ~]$ rosnode list /rosout
Packaging Secure ROS¶
This folder contains tools (vagrant and ansible) for creating Ubuntu debian packages for Secure ROS. Please see the section on Vagrant and ansible for details on installing and using vagrant and ansible.
Supported architectures and packages¶
The following architectures are supported.
- amd64 (64-bit PC)
- i386 (32-bit PC)
The following releases are supported
- ROS indigo (Ubuntu 14.04) on the indigo-devel branch.
- ROS kinetic (Ubuntu 16.04) on the kinetic-devel branch.
The secure-ros-<release>-ros-comm
package is created for each of the architectures. This includes modified versions of libraries (e.g libroscpp.so
) and python modules (roslaunch
, rospy
) which will be installed in /opt/secure_ros/<release>/
.
Generating the packages¶
The packages can be generated as follows.
vagrant up
The package is created in the root folder of the repository: deb/<arch>/secure-ros-<release>-ros-comm_<version>_<arch>.deb
. The build is preserved if the VMs are halted, but will need to be rebuilt if the VMs are destroyed.
Vagrant tasks¶
- Bring up the VMs as specified in the
secure_ros.yaml
configuration file. - Provision the VMs as specified in the
secure_ros.yaml
files (build, create and install the Secure ROS package).
Ansible playbooks¶
plays/install-secure-ros.yaml
: Please see Ansible playbooks.plays/build-secure-ros.yaml
: Please see Ansible playbooks.
Vagrant and ansible¶
We use ansible and vagrant to create and configure multiple VMs to run the examples and create debian package for Secure ROS.
Install Vagrant and ansible¶
We support Ubuntu (14.04, 16.04) and OS X. It is relatively easy to install on Ubuntu, while on Mac OS X, it is a bit more involved.
Ubuntu¶
Install virtualbox:
sudo apt-get install -y virtualbox
Install vagrant:
wget https://releases.hashicorp.com/vagrant/1.8.5/vagrant_1.8.5_x86_64.deb
sudo dpkg -i vagrant_1.8.5_x86_64.deb
Install ansible:
sudo apt-add-repository ppa:ansible/ansible -y
sudo apt-get update
sudo apt-get install ansible -y
OS X¶
Install virtualbox from https://www.virtualbox.org/wiki/Downloads.
Install vagrant from https://releases.hashicorp.com/vagrant.
Install ansible (this is a bit more involved. The instructions from https://valdhaus.co/writings/ansible-mac-osx/ are provided here for convenience. If you already have Xcode and pip
installed, you can skip those respective steps.
Install Xcode
Install
pip
sudo easy_install pip
Install ansible using
pip
sudo pip install ansible --quiet
Start up vagrant¶
Vagrant may be used to set up the VMs using two steps. These commands must be called from the example or package folders that contains Vagrantfile
.
To fire up and configure the VMs using vagrant
vagrant up
Trouble-shooting¶
The most common issues are as follows.
Make sure you have an internet connection.
Make sure that you have as many cores and RAM available as needed in the example.
There might be a bug in ansible that may cause it to fail. You may try again as follows.
vagrant provision
If it fails at the same spot, you may restart the VMs and try again.
vagrant halt vagrant up --no-provision vagrant provision