Python 的数据报传输层安全性。
PyDTLS 将数据报传输层安全性(DTLS - RFC 6347: http://tools.ietf.org/html/rfc6347)引入 Python 环境。简而言之,DTLS 以等同于 SSL/TLS 为 TCP 流内容所做的方式为 UDP 数据报有效负载带来安全性(加密、服务器身份验证、用户身份验证和消息身份验证)。
DTLS 现在在 Python 中非常容易使用。如果您熟悉 Python 标准库中的 ssl 模块,那么您已经知道如何操作了。它所需要的只是将数据报/UDP 套接字而不是流/TCP 套接字传递给wrap_socket函数。以下是如何设置连接的客户端:
[hidecontent type="logged" desc="隐藏内容:登录后可查看"]
import ssl
from socket import socket, AF_INET, SOCK_DGRAM
from dtls import do_patch
do_patch()
sock = ssl.wrap_socket(socket(AF_INET, SOCK_DGRAM))
sock.connect(('foo.bar.com', 1234))
sock.send('Hi there')
从 1.2.0 版开始,PyDTLS 除了支持 1.0 版外,还支持 DTLS 1.2 版。此版本还引入了使用椭圆曲线密码术和更细粒度配置选项的前向保密。
要从 PyPI 安装,请在任何支持的平台上输入:
pip install Dtls
PyDTLS 的主要设计目标是广泛的可用性。因此,它被构建为广泛兼容以下内容:
PyDTLS 需要 1.0.0 或更高版本的 OpenSSL 库。据报道,早期版本不提供稳定的 DTLS 支持。由于此版本的 OpenSSL 的打包发行版可用于许多流行的操作系统,因此在调用 PyDTLS 功能之前需要安装 OpenSSL-1.0.0。例如,在 Ubuntu 12.04 LTS 上,Python 解释器链接到 libcrypto.so.1.0.0 和 libssl.so.1.0.0,因此使用 PyDTLS 不需要进一步的安装步骤。
相比之下,在 Microsoft Windows 操作系统上安装 OpenSSL 就很不方便。出于这个原因,PyDTLS 的源代码分发版可用,其中包括用于 32 位和 64 位 Windows 的 OpenSSL dll。所有 dll 都已与 Microsoft C 运行时库的 Visual Studio 2008 版本链接,msvcr90.dll,CPython 2.7 使用的版本。因此,在装有 CPython 2.7 的机器上不需要安装 Microsoft 可再发行运行时包。与 PyDTLS 0.1.0 一起分发的 OpenSSL 版本是 1.0.1c。与 PyDTLS 1.2.0 一起分发的版本是提交 248cf959672041f38f4d80a4a09ee01d8ab04fe8(分支 OpenSSL_1_0_2-stable,1.0.2l-dev,包含对 DTLSv1_listen 的理想修复,在 1.0.2k 中不存在,这是 PyDTLS 1.2.0 发布时的稳定版本)。
PyDTLS 使用的 OpenSSL 版本可以根据sslconnection 的DTLS_OPENSSL_VERSION_NUMBER、DTLS_OPENSSL_VERSION 和 DTLS_OPENSSL_VERSION_INFO的值来确定。如果调用了do_patch (见下文),这些变量也可以通过ssl模块获得。请注意,PyDTLS 使用的 OpenSSL 版本可能与ssl模块使用的版本不同。
PyDTLS 的顶级包dtls通过 其sslconnection模块的SSLConnection类提供 DTLS 支持 。SSLConnection是在线文档化的,dtls/test/echo_seq.py 演示了如何使用此类通过侦听/接受/回显/关闭序列获取简单的回显服务器。相应的客户端可以类似于本文档顶部的代码片段,然后调用 unwrap方法关闭(或调用SSLConnection关闭方法,如果此类的实例也用于客户端) . 请注意,dtls 包不依赖于标准库的ssl模块,并且 因此, SSLConnection可用于ssl不可用或不兼容的环境中。
预计随着ssl模块成为一个已建立的、熟悉的 TLS 接口,它将成为访问 DTLS 的首选模块。为此,必须 在将 SOCK_DGRAM 类型的套接字传递给ssl 的wrap_socket函数或ssl 的SSLSocket构造函数之前调用dtls包的do_patch函数。
应该注意的是,一旦调用do_patch , dtls将引发ssl.SSLError类型的异常,而不是其默认的 dtls.err.SSLError。这允许用户在跨流和数据报套接字与ssl交互时,错误处理代码路径保持相同。
DTLS 协议将连接暗示为两个网络对等点之间的关联,其中整体关联状态由每个对等端点的握手状态表征(请参阅 RFC 6347)。OpenSSL 库在“SSL”类型实例(也称为 struct ssl_st)中记录此握手状态。在与此实例及其网络对等方完成握手后,可以通过引用唯一的“SSL”实例来安全地发送和接收数据报。连接是隐含的,因为流量可能被定向到或仅从网络对等点接收,其“SSL”实例已完成握手。底层网络协议(大多数情况下为 UDP)本身是无连接的这一事实并不重要。
此外,为了防止对 UDP DTLS 服务器的拒绝服务攻击,客户端必须在握手协议的早期经历 cookie 交换阶段,并且在服务器端资源提交给特定客户端之前(参见 RFC 的第 4.2.1 节) 6347). cookie 交换向服务器证明客户端确实可以在其握手启动 ClientHello 数据报所标记的源 IP 地址处接收 IP 流量。
PyDTLS在客户端通过connect方法,在服务器端通过accept方法实现这种连接建立。后者返回一个新的dtls.SSLConnection或ssl.SSLSocket 对象(取决于使用哪个接口,见上文),它“连接”到它的对等体。除了read和write方法(在两个接口级别)之外,还可以使用SSLSocket 的 send和recv方法;ssl禁止在连接的套接字上使用sendto和recvfrom。接受如预期的那样返回对等地址信息。请注意,在使用dtls的ssl接口时,必须在调用accept 之前调用listen。
在网络 io 层,只有来自其连接对等方的数据报必须传递给SSLConnection或SSLSocket对象(除非该对象在服务器端未连接,在这种情况下它可以处于侦听模式,初始服务器端套接字其它的作用是侦听传入的客户端连接请求)。
初始的服务器端侦听套接字对于执行此数据报路由功能没有用处。这是因为它必须保持未连接状态并准备好接收来自新的未知客户端的额外连接请求。
将传入数据报传递到正确连接的功能由dtls.demux包执行。当握手清除了 cookie 交换阶段时,SSLConnection请求来自多路分解器的新连接。demux 包的 osnet 模块提供了此请求的有效实现:它创建一个新套接字,该套接字绑定到与侦听套接字相同的网络接口和端口,但连接到对等方。UDP 堆栈(例如 Linux 中包含的堆栈)将传入数据报路由到此类已连接套接字,而不是绑定到同一端口的未连接套接字。
不幸的是,这不是 Microsoft Windows 上的行为。Windows UDP 将数据报路由到最早绑定到特定端口的当前存在的套接字(以及该套接字是否未连接,或连接到数据报的对等方或不同的对等方)。绑定到同一端口的其他套接字将不会接收流量,如果并且直到它们成为最早绑定的套接字,因为另一个套接字已关闭。
因此,demux 包提供并自动选择 Windows 平台上的模块 路由器。该模块在接收到新的连接请求时也会创建一个新的套接字;但不是将此套接字绑定到与侦听套接字相同的端口,而是绑定到一个新的临时端口。路由器然后将来自请求连接的对等方的数据报转发到相应的套接字。
为了效率起见,不对传出流量执行转发。相反,SSLConnection使用sendto引导来自原始侦听套接字的传出流量。在 OpenSSL 级别,这需要为“SSL”实例单独读取和写入数据报 BIO,一个分别处于“已连接”状态,一个处于“对等集”状态,并且与两个单独的网络套接字相关联。
从 PyDTLS 用户的角度来看,这种 demux 实现的选择和差异应该是透明的,除了可能的性能偏差之外。然而,这种透明度确实有一些限制:例如,当使用路由器时,接受方法可以返回None。当 SSLConnection检测到 demux 已将数据报转发到已知连接而不是通过accept启动到新对等点的连接时,就会发生这种情况。每当使用非阻塞套接字或带超时的套接字时,在这种情况下返回None很重要,因为作为转发数据报的结果,另一个套接字现在可能是可读的。接受必须返回,以便应用程序可以迭代其异步选择循环。
PyDTLS 实现了 SSL/TLS 关闭协议,因为它已针对 DTLS 进行了调整。SSLConnection 的 关闭和SSLSocket 的 解包 调用该协议。与 DTLS 握手的一般情况一样,应用程序必须准备好使用get_timeout和 handle_timeout方法,以及在套接字变为可读且异常携带 SSL_ERROR_WANT_READ 时重新调用关闭或 解包。(请参阅测试部分中有关异步 IO 的更多信息。)
SSLConnection 的 关闭和SSLSocket 的 解包返回一个(可能是新的)套接字,可用于与对等点的不安全通信,如ssl模块所述。在清除返回的套接字之前,多路分解基础结构仍用于此通信。请注意,当使用 路由器demux 时,返回的对象将是从socket.socket派生的对象。这是因为发送和接收路径仍必须定向到两个不同的操作系统套接字。此外,如果通过将此类套接字传递给ssl.wrap_socket或SSLConnection 构造函数来恢复安全通信,则会发生正确的事情。如果是osnet使用时,返回一个实际的socket.socket实例。
PyDTLS 套接字已经在以下使用模式下进行了测试:
在 OpenSSL 中使用多线程需要实现锁定回调。PyDTLS 确实实现了这一点,因此使用 PyDTLS 进行多线程编程在任何环境中都是安全的。然而,作为一个纯 Python 库,这些回调确实会带来一些开销。ssl模块 在其 C 扩展模块中实现了等效的锁定回调。不需要解释器重新进入,这种方法有望表现得更好。因此,PyDTLS 查询 OpenSSL 是否已经存在锁定回调,如果存在则不会覆盖它。因此,加载ssl可以提高性能,即使仅使用sslconnection接口也是如此。
请注意,加载顺序无关紧要:为了获得性能优势,可以在 dtls 包之前或之后加载ssl 。这是因为ssl不执行等效的现有锁定回调检查,如果 PyDTLS 回调已经安装,它将简单地覆盖它。但是当dtls操作已经在进行时,不应加载ssl ,此时某些锁可能处于其获取状态。
另请注意,此性能增强仅适用于 PyDTLS 加载与ssl相同的 OpenSSL 共享对象的平台 。例如,在 Ubuntu 12.04 上,情况就是如此,但在 Microsoft Windows 上,情况并非如此。
可以使用一个简单的回显服务器从项目根目录执行python -m dtls.test.echo_seq
。使用本文档顶部的代码片段可以访问 echo 服务器,使用“localhost”上的端口 28000。
单元测试套件可以从项目根目录使用 python -m dtls.test.unit [-v]
和执行python -m dtls.test.unit_wrapper
(用于客户端和服务器包装器)
模块test_ssl.py中几乎所有 Python 标准库的ssl单元测试都已移植到dtls.test.unit.py。所有测试都已调整为使用数据报套接字进行操作。在 Linux 上,每个测试执行四次,改变 IPv4 和 IPv6 之间的地址族以及osnet和router之间的多路分解。在 osnet不可用的 Windows 上,每个测试运行两次,一次使用 IPv4,一次使用 IPv6。
单元测试套件包括对上述每个兼容框架的测试。AsyncoreEchoServer类用作如何使用非阻塞数据报套接字并实现超时检测要求的示例。一般的 DTLS 和特别是 OpenSSL 需要在 DTLS 超时到期后与非阻塞套接字(或具有超时选项的套接字)一起使用时被回调,以在握手期间使用重新传输来处理数据包丢失。握手可能发生在任何读取或写入操作期间,即使在初始握手成功完成之后,以防对等方请求重新协商。
使用 -v 开关运行以详细模式执行所有单元测试。
dtls/test/test_perf.py 实现了一个交互式性能测试套件,用于比较 TCP、UDP、SSL 和 DTLS 的原始吞吐量。它可以通过环回接口在本地执行,也可以在远程客户端和服务器之间执行。在后一种情况下,只要通过交互式界面启动套件运行,测试作业就会发送到远程连接的客户端。运行 test_perf.py -h 以获取更多信息。
应该注意的是,比较不提供拥塞控制的协议(UDP 和 DTLS)与提供拥塞控制的协议(TCP 和 SSL)的性能是一项艰巨的任务。即使跨千兆位网络链路的原始吞吐量也可能会在没有拥塞控制和尽可能快地生成数据而没有节流的对等点(如本测试所做的那样)的情况下受到影响:当链路进入拥塞崩溃时,其吞吐量将显着下降。同样,环回是一个不完美的测试接口,因为它很少丢弃数据包,也从不复制或重新排序它们(因此抵消了 DTLS 相对于 SSL 的相对性能优势)。尽管如此,通过观察 test_perf.py 的操作可以获得一些有用的见解,包括存在一定量数据包丢失时的软件堆栈行为。
dtls包及其子包记录各种事件,主要是可以帮助调试的事件。特别是当日志记录级别至少设置为logging.DEBUG时,路由器会发出许多消息。dtls/test/echo_seq.py 在其操作期间激活此日志记录级别。
在最初发布时,PyDTLS 0.1.0 已经在 Ubuntu 12.04.1 LTS 32 位和 64 位以及 Microsoft Windows 7 32 位和 64 位上使用 CPython 2.7.3 进行了测试。欢迎使用带有附加平台端口的补丁。
从 1.2.0 版开始,PyDTLS 使用 CPython 2.7.13 在 Ubuntu 16.04 LTS 和 Microsoft Windows 10 上进行了测试。
[/hidecontent]