目标:限制节点可以使用的主题。
教程级别:高级
时间:20 分钟
内容
背景
修改
permissions.xml
签署策略文件
启动节点
使用模板
背景
在继续之前,请确保您已完成设置安全教程。
权限非常灵活,可以用来控制 ROS 图中的许多行为。
在本教程中,我们演示了一项仅允许在默认 chatter
主题上发布消息的策略。这将防止,例如,在启动侦听器时重新映射主题或将相同的安全飞地用于其他目的。
为了执行此政策,我们需要更新 permissions.xml
文件并在启动节点之前重新签署它。这可以通过手动修改权限文件或使用 XML 模板来完成。
修改 permissions.xml
首先备份您的权限文件,然后打开 permissions.xml
进行编辑:
cd ~/sros2_demo/demo_keystore/enclaves/talker_listener/talker
mv permissions.p7s permissions.p7s~
mv permissions.xml permissions.xml~
vi permissions.xml
我们将修改 <allow_rule>
以适应 <publish>
和 <subscribe>
。此 XML 文件中的主题使用 DDS 命名格式,而不是 ROS 名称。有关在 ROS 和 DDS 之间映射主题名称的详细信息,请参阅主题和服务名称设计文档。
将以下 XML 内容粘贴到 permission.xml
,保存文件并退出文本编辑器。这显示了 chatter
和 rosout
ROS 主题分别重命名为 DDS rt/chatter
和 rt/rosout
主题。
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.omg.org/spec/DDS-SECURITY/20170901/omg_shared_ca_permissions.xsd">
<!-- 定义 DDS 安全配置文件,并指定 XML schema 的位置 -->
<permissions>
<!-- 权限定义的开始 -->
<grant name="/talker_listener/talker">
<!-- 定义一个权限授予,命名为 "/talker_listener/talker" -->
<subject_name>CN=/talker_listener/talker</subject_name>
<!-- 指定主体名称,即被授予权限的实体,CN 是通用名称 -->
<validity>
<not_before>2021-06-01T16:57:53</not_before>
<!-- 权限开始生效的时间 -->
<not_after>2031-05-31T16:57:53</not_after>
<!-- 权限失效的时间 -->
</validity>
<!-- 定义权限的有效期 -->
<allow_rule>
<!-- 允许规则的开始 -->
<domains>
<id>0</id>
<!-- 定义域 ID 为 0 -->
</domains>
<publish>
<!-- 定义发布权限 -->
<topics>
<topic>rt/chatter</topic>
<!-- 允许发布 rt/chatter 主题 -->
<topic>rt/rosout</topic>
<!-- 允许发布 rt/rosout 主题 -->
<topic>rt/parameter_events</topic>
<!-- 允许发布 rt/parameter_events 主题 -->
<topic>*/talker/*</topic>
<!-- 允许发布匹配 */talker/* 的主题 -->
</topics>
</publish>
<subscribe>
<!-- 定义订阅权限 -->
<topics>
<topic>rt/parameter_events</topic>
<!-- 允许订阅 rt/parameter_events 主题 -->
<topic>*/talker/*</topic>
<!-- 允许订阅匹配 */talker/* 的主题 -->
</topics>
</subscribe>
</allow_rule>
<!-- 第一个允许规则的结束 -->
<allow_rule>
<!-- 第二个允许规则的开始 -->
<domains>
<id>0</id>
<!-- 定义域 ID 为 0 -->
</domains>
<publish>
<!-- 定义发布权限 -->
<topics>
<topic>ros_discovery_info</topic>
<!-- 允许发布 ros_discovery_info 主题 -->
</topics>
</publish>
<subscribe>
<!-- 定义订阅权限 -->
<topics>
<topic>ros_discovery_info</topic>
<!-- 允许订阅 ros_discovery_info 主题 -->
</topics>
</subscribe>
</allow_rule>
<!-- 第二个允许规则的结束 -->
<default>DENY</default>
<!-- 默认情况下拒绝所有未明确允许的操作 -->
</grant>
<!-- 权限授予的结束 -->
</permissions>
<!-- 权限定义的结束 -->
</dds>
<!-- DDS 安全配置文件的结束 -->
此策略允许 talker 在 chatter
和 rosout
主题上发布。它还包括讲话者节点管理参数(rt/parameter_events)所需的发布和订阅权限(所有节点的要求)。发现(ros_discovery_info)权限保持原始模板不变。
签署策略文件
此命令将从更新的 XML 文件 permissions.xml
创建新的 S/MIME 签名策略文件 permissions.p7s
。该文件必须使用权限 CA 证书签名,这需要访问权限 CA 私钥。如果私钥已被保护,则可能需要根据您的安全计划采取额外步骤来解锁和使用它。
cxy@cxy-Ubuntu2404:~/sros2_demo/demo_keystore/enclaves/talker_listener/listener$ openssl smime -sign -text -in permissions.xml -out permissions.p7s \
--signer permissions_ca.cert.pem \
-inkey ~/sros2_demo/demo_keystore/private/permissions_ca.key.pem
启动节点
在更新权限到位后,我们可以使用之前教程中使用的相同命令成功启动节点:
export ROS_SECURITY_KEYSTORE=~/sros2_demo/demo_keystore
export ROS_SECURITY_ENABLE=true
export ROS_SECURITY_STRATEGY=Enforce
ros2 run demo_nodes_cpp talker --ros-args --enclave /talker_listener/talker
但是,尝试重新映射 chatter
主题会阻止节点启动(请注意,这需要将 ROS_SECURITY_STRATEGY
设置为 Enforce
)。
export ROS_SECURITY_KEYSTORE=~/sros2_demo/demo_keystore
export ROS_SECURITY_ENABLE=true
export ROS_SECURITY_STRATEGY=Enforce
ros2 run demo_nodes_cpp talker --ros-args --enclave /talker_listener/talker \
--remap chatter:=not_chatter
使用模板
安全策略很快就会变得混乱,因此 sros2
实用程序增加了从模板创建策略的功能。通过使用 sros2
存储库中提供的示例策略文件来执行此操作。让我们为 talker
和 listener
创建一个策略,仅使用 chatter
主题。
首先下载包含示例策略文件的 sros2
存储库:
git clone https://github.com/ros2/sros2.git /tmp/sros2
cxy@cxy-Ubuntu2404:~$ tree /tmp/sros2
/tmp/sros2
├── codecov.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── sros2
│ ├── CHANGELOG.rst
│ ├── package.xml
│ ├── pytest.ini
│ ├── resource
│ │ └── sros2
│ ├── setup.py
│ ├── sros2
│ │ ├── api
│ │ │ ├── _artifact_generation.py
│ │ │ ├── __init__.py
│ │ │ └── _policy.py
│ │ ├── command
│ │ │ ├── __init__.py
│ │ │ └── security.py
│ │ ├── errors.py
│ │ ├── __init__.py
│ │ ├── keystore
│ │ │ ├── _enclave.py
│ │ │ ├── __init__.py
│ │ │ ├── _keystore.py
│ │ │ └── _permission.py
│ │ ├── policy
│ │ │ ├── defaults
│ │ │ │ ├── dds
│ │ │ │ │ ├── governance.xml
│ │ │ │ │ └── __init__.py
│ │ │ │ ├── __init__.py
│ │ │ │ └── policy.xml
│ │ │ ├── __init__.py
│ │ │ ├── schemas
│ │ │ │ ├── dds
│ │ │ │ │ ├── governance.xsd
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── permissions.xsd
│ │ │ │ ├── __init__.py
│ │ │ │ └── policy.xsd
│ │ │ └── templates
│ │ │ ├── dds
│ │ │ │ ├── __init__.py
│ │ │ │ └── permissions.xsl
│ │ │ ├── __init__.py
│ │ │ └── policy.xsl
│ │ ├── _utilities.py
│ │ └── verb
│ │ ├── create_enclave.py
│ │ ├── create_keystore.py
│ │ ├── create_permission.py
│ │ ├── generate_artifacts.py
│ │ ├── generate_policy.py
│ │ ├── __init__.py
│ │ └── list_enclaves.py
│ ├── test
│ │ ├── conftest.py
│ │ ├── policies
│ │ │ ├── add_two_ints.policy.xml
│ │ │ ├── common
│ │ │ │ ├── lifecycle_node.xml
│ │ │ │ ├── node
│ │ │ │ │ ├── logging.xml
│ │ │ │ │ ├── parameters.xml
│ │ │ │ │ ├── time.xml
│ │ │ │ │ └── types.xml
│ │ │ │ └── node.xml
│ │ │ ├── invalid_policy_missing_topics_tag.xml
│ │ │ ├── minimal_action.policy.xml
│ │ │ ├── permissions
│ │ │ │ ├── add_two_ints
│ │ │ │ │ └── permissions.xml
│ │ │ │ ├── minimal_action
│ │ │ │ │ └── permissions.xml
│ │ │ │ ├── sample
│ │ │ │ │ └── permissions.xml
│ │ │ │ ├── single_context
│ │ │ │ │ └── permissions.xml
│ │ │ │ └── talker_listener
│ │ │ │ └── permissions.xml
│ │ │ ├── policy_to_permissions.py
│ │ │ ├── sample.policy.xml
│ │ │ ├── single_context.policy.xml
│ │ │ └── talker_listener.policy.xml
│ │ ├── sros2
│ │ │ ├── commands
│ │ │ │ └── security
│ │ │ │ └── verbs
│ │ │ │ ├── fixtures
│ │ │ │ │ ├── client_service_node.py
│ │ │ │ │ └── pub_sub_node.py
│ │ │ │ ├── test_create_enclave.py
│ │ │ │ ├── test_create_keystore.py
│ │ │ │ ├── test_create_permission.py
│ │ │ │ ├── test_generate_artifacts.py
│ │ │ │ ├── test_generate_policy_no_nodes.py
│ │ │ │ ├── test_generate_policy.py
│ │ │ │ ├── test_list_enclaves.py
│ │ │ │ ├── test_no_verb.py
│ │ │ │ └── utilities
│ │ │ │ ├── __init__.py
│ │ │ │ └── sros2_cli_test_case.py
│ │ │ ├── keystore
│ │ │ │ └── test_enclave.py
│ │ │ └── test_utilities.py
│ │ ├── test_copyright.py
│ │ ├── test_flake8.py
│ │ ├── test_mypy.py
│ │ ├── test_pep257.py
│ │ └── test_policy_to_permissions.py
│ └── xml_cache
│ ├── README.md
│ ├── xhtml-cache.xml
│ └── xml.xsd
├── sros2_cmake
│ ├── CHANGELOG.rst
│ ├── cmake
│ │ └── sros2_generate_artifacts.cmake
│ ├── CMakeLists.txt
│ ├── package.xml
│ ├── README.md
│ └── sros2_cmake-extras.cmake
├── SROS2_Linux.md
├── SROS2_MacOS.md
└── SROS2_Windows.md
35 directories, 92 files
然后使用 create_permission
动词指向示例策略以生成 XML 权限文件:
ros2 security create_permission demo_keystore \
/talker_listener/talker \
/tmp/sros2/sros2/test/policies/sample.policy.xml
ros2 security create_permission demo_keystore \
/talker_listener/listener \
/tmp/sros2/sros2/test/policies/sample.policy.xml
这些权限文件允许节点仅发布或订阅 chatter
主题,并启用参数所需的通信。
cxy@cxy-Ubuntu2404:~/sros2_demo$ ros2 security create_permission demo_keystore \
/talker_listener/talker \
/tmp/sros2/sros2/test/policies/sample.policy.xml
cxy@cxy-Ubuntu2404:~/sros2_demo$ ros2 security create_permission demo_keystore \
/talker_listener/listener \
/tmp/sros2/sros2/test/policies/sample.policy.xml
在一个启用了安全性的终端中,如前面的安全教程中所述,运行 talker
演示程序:
export ROS_SECURITY_KEYSTORE=~/sros2_demo/demo_keystore
export ROS_SECURITY_ENABLE=true
export ROS_SECURITY_STRATEGY=Enforce
ros2 run demo_nodes_cpp talker --ros-args -e /talker_listener/talker
在另一个终端中对 listener
程序执行相同的操作:
export ROS_SECURITY_KEYSTORE=~/sros2_demo/demo_keystore
export ROS_SECURITY_ENABLE=true
export ROS_SECURITY_STRATEGY=Enforce
ros2 run demo_nodes_py listener --ros-args -e /talker_listener/listener
此时,您的 talker
和 listener
节点将使用显式访问控制列表进行安全通信。然而, listener
节点尝试订阅 chatter
以外的主题将失败:
ros2 run demo_nodes_py listener --ros-args --enclave /talker_listener/listener \
--remap chatter:=not_chatter