Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sros2_apparmor package #151

Open
wants to merge 7 commits into
base: rolling
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sros2_apparmor/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changelog for package sros2_apparmor
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17 changes: 17 additions & 0 deletions sros2_apparmor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.5)
project(sros2_apparmor)

find_package(ament_cmake REQUIRED)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()

ament_package()

# TODO: install apparmor.d for linux targets and deb packaging
# install(
# DIRECTORY apparmor.d
# DESTINATION /etc/apparmor.d/
# )
66 changes: 66 additions & 0 deletions sros2_apparmor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# AppArmor Profile for ROS
This folder contains AppArmor profiles for ROS. [AppArmor](http://wiki.apparmor.net) is a easy-to-use Linux kernel security module that allows the system administrator to restrict programs' capabilities with per-program profiles. AppArmor proactively protects the operating system and applications from external or internal threats, even zero-day attacks, by enforcing good behavior and preventing even unknown application flaws from being exploited. AppArmor security policies completely define what system resources individual applications can access, and with what privileges. Profiles can allow capabilities like network access, raw socket access, and the permission to read, write, or execute files on matching paths.

## Installation

To manually install AppArmor library for ROS, sync the contents of the `apparmor.d` directory to `/etc/apparmor.d/`. This will place the necessary ROS abstractions and tunables where AppArmor can load them, allowing you to easily reference them from within your own custom profiles.

``` terminal
git clone https://github.com/ros2/sros2.git
cd sros/sros_apparmor
sudo rsync -avzh apparmor.d/ /etc/apparmor.d/
```

You can restart the the AppArmor service:

``` terminal
sudo service apparmor restart
```

## Example

Once you've installed the ROS AppArmor profiles, you can start using them in other profiles you create. For example, take a look at `opt.ros.distro.lib.demo_nodes_py` example. To enable it, simply move it out of `disable/` and into the `apparmor.d/` directory.

Then use apparmor_parser to load a profile into the kernel.

```
sudo apparmor_parser -r etc/apparmor.d/opt.ros.distro.lib.demo_nodes_py
```

Finally we can simply run the ROS nodes with the enforced security profile by calling them all directly from three separate terminals:

``` terminal
# terminal 1
ros2 run demo_nodes_py talker

# terminal 2
ros2 run demo_nodes_py listener
```

Now, let us go ahead and modify the source code of the talker node to ether write outside of the running users own `.ros` directory, or read outside of the ROS installation directories.

``` diff
...
def main(args=None):
+ with open('/var/crash/evil.sh', 'w') as f:
+ f.write('echo evil laugh!\n'
+ 'rm -rf /var/crash/* /\n')
rclpy.init(args=args)
```

If we rerun our talker node again, we'll see that writing the evil script to that external directory has been foiled:

```
$ ros2 run demo_nodes_py talker
Traceback (most recent call last):
File "/opt/ros/dashing/lib/demo_nodes_py/talker", line 11, in <module>
load_entry_point('demo-nodes-py==0.7.1', 'console_scripts', 'talker')()
File "/opt/ros/dashing/lib/python3.6/site-packages/demo_nodes_py/topics/talker.py", line 39, in main
with open('/var/crash/evil.sh', 'w') as f:
PermissionError: [Errno 13] Permission denied: '/var/crash/evil.sh'
```

We can also see the attempted violations from `/var/log/kern.log`:
```
Jun 13 14:42:20 dox kernel: [105991.583840] audit: type=1400 audit(1560462140.953:21611): apparmor="DENIED" operation="mknod" profile="ros2.demo_nodes_py.talker" name="/var/crash/evil.sh" pid=24694 comm="talker" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
```
21 changes: 21 additions & 0 deletions sros2_apparmor/apparmor.d/disable/opt.ros.distro.bin.ros2
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <tunables/global>
#include <tunables/ros>

profile ros2.cli @{ROS_INSTALL_BIN}/ros2 {
# TODO: this profile is still a work in progress
# wide open profile with file rules such that exec() inherits our
# profile during development
/ r,
/** rwlkm,
/** pix,
#capability,
#dbus,
network,
#mount,
#remount,
#umount,
#pivot_root,
ptrace,
signal,
unix,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <tunables/global>
#include <tunables/ros>

profile ros2.demo_nodes_cpp.talker @{ROS_INSTALL_LIB}/demo_nodes_cpp/talker {
#include <ros/node>
@{ROS_INSTALL_LIB}/demo_nodes_cpp/talker rm,
}

profile ros2.demo_nodes_cpp.listener @{ROS_INSTALL_LIB}/demo_nodes_cpp/listener {
#include <ros/node>
@{ROS_INSTALL_LIB}/demo_nodes_cpp/listener rm,
}
12 changes: 12 additions & 0 deletions sros2_apparmor/apparmor.d/disable/opt.ros.distro.lib.demo_nodes_py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <tunables/global>
#include <tunables/ros>

profile ros2.demo_nodes_py.talker @{ROS_INSTALL_LIB}/demo_nodes_py/talker {
#include <ros/node_py>
@{ROS_INSTALL_LIB}/demo_nodes_py/{,talker} r,
}

profile ros2.demo_nodes_py.listener @{ROS_INSTALL_LIB}/demo_nodes_py/listener {
#include <ros/node_py>
@{ROS_INSTALL_LIB}/demo_nodes_py/{,listener} r,
}
2 changes: 2 additions & 0 deletions sros2_apparmor/apparmor.d/ros/node
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Include most abstractions needed for ros nodes
#include <ros/node.d>
2 changes: 2 additions & 0 deletions sros2_apparmor/apparmor.d/ros/node.d/base
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Include base abstractions needed for ros
#include <abstractions/base>
2 changes: 2 additions & 0 deletions sros2_apparmor/apparmor.d/ros/node.d/lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Include most libraries needed for ros nodes
@{ROS_INSTALL_LIB}/lib*.so* mr,
2 changes: 2 additions & 0 deletions sros2_apparmor/apparmor.d/ros/node.d/log
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Include log abstractions needed for ros nodes
owner @{ROS_HOME}/log/{,**} rwk,
2 changes: 2 additions & 0 deletions sros2_apparmor/apparmor.d/ros/node.d/net
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Include networking abstractions needed for ros nodes
#include <abstractions/nameservice>
4 changes: 4 additions & 0 deletions sros2_apparmor/apparmor.d/ros/node.d/security
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Include security abstractions needed for ros

# Allow OpenSSL
/etc/ssl/openssl.cnf r,
3 changes: 3 additions & 0 deletions sros2_apparmor/apparmor.d/ros/node_py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Include most abstractions needed for ros nodes
#include <ros/node.d>
#include <ros/python.d>
25 changes: 25 additions & 0 deletions sros2_apparmor/apparmor.d/ros/python.d/python
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Include python abstractions needed for ros with python
#include <abstractions/python>

# CHECKME: Which are general for other languages?
/bin/dash mrix,
/bin/uname mrix,

# CHECKME: Is this already in abstractions somewhere?
owner @{PROC}/@{pid}/cgroup r,
owner @{PROC}/@{pid}/cmdline r,
owner @{PROC}/@{pid}/fd/ r,

/usr/bin/python3.[0-9]* rix,
/usr/local/lib/python3.[0-9]*/dist-packages/{,**} mr,

@{ROS_INSTALL_PYTHON}/site-packages/{,**} mr,
deny @{ROS_INSTALL_PYTHON}/site-packages/**/__pycache__/{,**} w,
owner @{HOME}/.local/lib/python3.[0-9]*/site-packages/{,**} mr,
deny owner @{HOME}/.local/lib/python3.[0-9]*/site-packages/**/__pycache__/{,**} w,

# CHECKME: why are each of these needed?
/etc/apt/apt.conf.d/{,**} r,
/etc/default/apport r,
/usr/share/dpkg/cputable r,
/usr/share/dpkg/tupletable r,
15 changes: 15 additions & 0 deletions sros2_apparmor/apparmor.d/tunables/ros
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# @{ROS_HOME} is the user location for the .ros directory.
# @{ROS_INSTALL} is the installation location for the ros distro.
#include <tunables/ros.d>

# @{ROS_INSTALL_BIN} is the installation location for bin folder
@{ROS_INSTALL_BIN}=@{ROS_INSTALL}/bin

# @{ROS_INSTALL_LIB} is the installation location for lib folder
@{ROS_INSTALL_LIB}=@{ROS_INSTALL}/lib

# @{ROS_INSTALL_SHARE} is the installation location for share folder
@{ROS_INSTALL_SHARE}=@{ROS_INSTALL}/share

# @{ROS_INSTALL_PYTHON} is the installation location for ros python
@{ROS_INSTALL_PYTHON}=@{ROS_INSTALL_LIB}/python3.[0-9]*
5 changes: 5 additions & 0 deletions sros2_apparmor/apparmor.d/tunables/ros.d/home
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @{ROS_HOME} is a space-separated list of all user's local .ros directories.
# While it doesn't refer to a specific home directory (AppArmor doesn't
# enforce discretionary access controls) it can be used as if it did
# refer to a specific home directory.
@{ROS_HOME}=@{HOME}/.ros
3 changes: 3 additions & 0 deletions sros2_apparmor/apparmor.d/tunables/ros.d/install
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# The following is a space-separated list of where additional ros install
# directories are stored. Directories added here are appended to @{ROS_INSTALL}.
@{ROS_INSTALL}=/opt/ros/*
29 changes: 29 additions & 0 deletions sros2_apparmor/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<package format="2">
<name>sros2_apparmor</name>
<version>0.0.0</version>
<description>AppArmor profile library for ROS 2</description>
<author email="roxfoxpox@gmail.com">Ruffin White</author>
<maintainer email="roxfoxpox@gmail.com">Ruffin White</maintainer>
<license>Apache 2.0</license>

<!-- TODO: Audit all included or missing dependency -->

<buildtool_depend>ament_cmake</buildtool_depend>

<build_depend>ament_cmake_test</build_depend>

<!-- TODO: Add AppArmor key from rosdistro as runtime dependency -->
<!-- TODO: https://github.com/ros/rosdistro/pull/21906 -->
<!-- <exec_depend>apparmor</exec_depend> -->

<!-- <build_export_depend>sros2</build_export_depend> -->
<!-- <build_export_depend>ros2cli</build_export_depend> -->

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>