Linux 网络和流量加密完整指南(第一部分)

大家好!我是大聪明-PLUS

在本文中,我想重点讨论Linux系统上的流量加密。我们都明白保护隐私的重要性。在如今这个许多公司都在收集数据,黑客有时甚至会拦截我们网络流量的时代,这一点尤为重要。确保数据安全至关重要。例如,确保企业网络不被黑客窃听。如今,信息安全不再是一时风尚,而是迫在眉睫的必需品。网络犯罪日益猖獗,保护流量免遭拦截是个人和企业数字化生活中不可或缺的一部分。

本文将介绍 Linux 中主要的加密方法及其实现方式。我们将探讨协议级加密的工作原理、Linux 中的网络工作原理,以及端到端加密和虚拟专用网络 (VPN) 的概念。

本文对于关注隐私和数据保护的人士(包括企业)来说尤其具有参考价值。

这是第一部分,我们将在这里了解一些基础知识(协议、网络操作、编写用于 TCP 连接的 Python 脚本),在第二部分中,我们将探讨虚拟专用网络和代理,以及其他几个主题。


我们先来看看网络和网络协议的基本工作原理。如果您已经了解它们的工作原理,可以跳过本节,直接阅读下一节。

什么是网络?网络是由相互连接的设备(计算机、服务器、手机以及任何其他连接到网络的设备,甚至包括洗衣机)组成的集合,这些设备可以交换数据。网络使用网络协议------即数据交换的规则。以下是一些常见的网络协议:

  • HTTP------根据其定义,浏览器会向服务器生成一条消息;

  • DNS------根据其定义,浏览器通过网站的域名来识别网站的 IP 地址;

  • TCP------根据该协议,连接得以建立,数据传输的完整性得到保证;

  • IP------根据它,网络中的寻址是通过IP实现的;

  • 以太网------根据其定义,物理数据传输是在网络上的设备之间进行的。

  • FTP------顾名思义,计算机可以通过网络相互传输文件。

  • SMTP------顾名思义,服务器负责在网络上路由电子邮件。

  • UDP 是一种无连接协议,设计用于发送数据报(小数据块)。它不保证数据包的送达,也不保证丢失数据的恢复。然而,UDP 比 TCP 更简单、更快速。而 TCP 则提供数据包送达、顺序保证和错误恢复,这使得它比 UDP 更复杂。

传输控制协议/互联网协议(TCP/IP协议栈)简而言之,是一组在不同网络层级相互作用的协议,用于管理网络上的数据交换。每个协议都是一组用于管理数据交换的规则。因此,TCP/IP协议栈实际上是一组规则集。许多协议可以根据它们运行的网络层级进行分类。最常见的网络模型是OSI模型和TCP/IP模型。

在关键任务系统(例如 DNS)中,UDP 更为常用。速度比数据送达保证更重要。发送一个 TCP 数据包的时间,UDP 数据包可能已经被退回三次。而在其他一些应用中,数据完整性比速度更重要,因此会使用 TCP。

网络节点是计算机网络中的一个设备。节点可以分为终端节点和中间节点:

终端节点是指发送和/或接收数据的节点。简而言之,这些设备充当信息的接收者或来源。

中间节点是连接各个终端节点的节点。

例如,智能手机通过 Wi-Fi 向服务器发送请求。智能手机和服务器是端点,而 Wi-Fi 路由器是中间节点。

本世纪最大的问题是隐私。

最近,对个人数据的搜寻愈演愈烈。这种现象一直存在,人们试图控制每个人,但如今愈演愈烈。以众多服务为例------它们都会提供推荐,而这些推荐需要收集数TB的数据。谁在何时访问过?他们在寻找什么?来自哪个国家?等等等等。几乎可以生成一份关于每个人的完整报告。即使没有在线服务,你家附近的一家普通商店也能为你建立一份档案。你买了什么,何时买的。例如,一位中年男子早上走进商店,买了几瓶水和一些水果,两小时后又回来买了一瓶水。根据这些数据,你可以推断出他是一位运动员,每天早上跑步两小时,注重健康。你甚至可以推断出他的大致居住地。

企业和政府机构利用各种现代技术------从网站cookie到嵌入汽车和设备的传感器------收集前所未有的海量客户、公民和员工数据。他们的任务包括评估情绪和偏好、预测需求、提高生产力、检测欺诈、追踪位置、监测健康状况以及监控安全。

这些数据的收集大多出于正当目的。然而,没有人能够回答一个最紧迫的问题:界限在哪里?人们希望了解自己的个人信息是如何被处理和存储的,以及信息的保密程度如何。"被遗忘权"以及其他众多国际、国家和地方安全法规是否得到了尊重?

例如,如何保护公司的企业网络免受黑客攻击?如果黑客开始窃听网络流量,导致数百条关于公司及其员工的重要数据落入不法分子手中怎么办?如果这家公司是国有企业而不是私营企业呢?

确保安全对任何公司来说都是一项具有挑战性的任务,尤其是在云环境发展的情况下。

自2020年以来,远程办公已成为一种趋势,几乎每个人都在转向远程办公。在家办公意味着许多人会使用公司电脑和手机处理私人事务。然而,工作设备的安全性可能并不像看起来那么高。使用公司电脑和手机处理私人事务的人常常会担心:我的雇主能否看到我在家里的Wi-Fi网络上或在家时访问的网站?

正如我之前所说,在本文中,我们将尝试自行保护我们的网络流量。

交通监控的方法有哪些?

Linux 提供了大量用于网络操作和监控的工具。有时我们需要监控网络负载,以确定"幕后"发生了什么。

这类任务在负责监控整个公司网络的系统管理员中尤为常见。不过,它们对普通用户也很有用。

如果您正在在线阅读本文,想必您已经了解网络的概念。值得注意的是,几乎所有公司都有某种内部服务,并为此构建了所谓的局域网 (LAN)。不过,我们稍后再详细讨论局域网。为了让运行不同操作系统和程序的计算机能够相互通信,存在一种名为 OSI 的通用网络模型,它定义了相关标准。该模型将通信过程划分为多个步骤,称为"层",每一层都有其自身的规则。这些规则定义了通信的进行方式,被称为协议。您可能在其他地方见过一些协议的名称,例如 IP、DNS 和 HTTP。虽然标准的 OSI 模型假定有七层,但管理员通常只使用第二层、第三层和第四层,并将第五层、第六层和第七层合并为一层,通常称为第七层。

操作系统内核负责实现物理层、数据链路层、网络层和传输层,为应用程序提供与网络交互的统一接口。

让我们回到监控的话题。Linux 拥有丰富的网络实用程序和网络管理工具,涵盖了从net-tools网络iptables到终端的各种需求tcpdumpWireShark

我们来看一个网络监控工具iptraf。这是一个用 C 语言和 ncurses 库编写的小程序,用于监控计算机的网络活动。

复制代码
# Ubuntu/debian`
apt install iptraf

`# Arch`
pacman `-S` iptraf-ng

`# Centos/redhat`
yum install iptraf
`

安装完成后,运行该命令sudo iptraf-ng

ncurses界面将会打开。它包括流量监控器、接口设置、统计信息、监控器和过滤器以及配置功能。

例如,要查看每个网络连接和流量统计信息,请选择"IP流量监控"。然后选择网络接口。

接下来,您将看到系统正在使用的所有IP地址。您还将看到数据包数量、接收到的数据量以及其他信息。

您可以在下方看到有关此实用程序的简要帮助:

复制代码
`usage: iptraf-ng [options]
   or: iptraf-ng [options] `-B` [-i <iface> | `-d` <iface> | `-s` <iface> | `-z` <iface> | `-l` <iface> | `-g`]

    `-h`, `--help`            show this help message

    `-i` <iface>            `start` the IP traffic monitor (use `'-i all'` `for` all interfaces)
    `-d` <iface>            `start` the detailed statistics facility on an interface
    `-s` <iface>            `start` the TCP and UDP monitor on an interface
    `-z` <iface>            shows the packet size counts on an interface
    `-l` <iface>            `start` the LAN station monitor (use `'-l all'` `for` all LAN interfaces)
    `-g`                    `start` the general interface statistics

    `-B`                    run `in` background (use only with one of the above parameters
    `-f`                    `clear` all locks and counters
    `-t` <n>                run only `for` the specified <n> number of minutes
    `-L` <logfile>          specifies an alternate log file
`

我们再来看一个工具:[工具名称] iftop。这个实用程序可以让你实时监控网络使用情况、识别活动连接并分析网络活动。安装极其简单:

复制代码
# Ubuntu/debian`
apt install iftop

`# Arch`
pacman `-S` iftop

`# Centos/redhat`
yum install iftop`

您还可以将网络流量记录到日志中:

复制代码
#!/bin/bash`

`LOGFILE="$HOME/iftop_traffic.log"`
`INTERFACE="eth0"`

`sudo` iftop `-i` `$INTERFACE` > `"$LOGFILE"`

`echo` `"Log saved in $LOGFILE"`
`

以下是 iftop 工具的帮助信息:

复制代码
`Synopsis: iftop `-h` | [-npblNBP] [-i interface] [-f filter code]
                               [-F net/mask] [-G net6/mask6]

   `-h`                  display this message
   `-n`                  don`'t do hostname lookups`
`   -N                  don'`t convert port numbers to services
   `-p`                  run `in` promiscuous mode (show traffic between other
                       hosts on the same network segment)
   `-b`                  don`'t display a bar graph of traffic`
`   -B                  Display bandwidth in bytes`
`   -i interface        listen on named interface`
`   -f filter code      use filter code to select packets to count`
`                       (default: none, but only IP packets are counted)`
`   -F net/mask         show traffic flows in/out of IPv4 network`
`   -G net6/mask6       show traffic flows in/out of IPv6 network`
`   -l                  display and count link-local IPv6 traffic (default: off)`
`   -P                  show ports as well as hosts`
`   -m limit            sets the upper limit for the bandwidth scale`
`   -c config file      specifies an alternative configuration file`
`   -t                  use text interface without ncurses`

`   Sorting orders:`
`   -o 2s                Sort by first column (2s traffic average)`
`   -o 10s               Sort by second column (10s traffic average) [default]`
`   -o 40s               Sort by third column (40s traffic average)`
`   -o source            Sort by source address`
`   -o destination       Sort by destination address`

`   The following options are only available in combination with -t`
`   -s num              print one single text output afer num seconds, then quit`
`   -L num              number of lines to print`

`iftop, version 1.0pre4`
`copyright (c) 2002 Paul Warren <pdw@ex-parrot.com> and contributors`
`

❯ 协议级安全:SSL/TLS

为了在网络节点之间安全地传输数据,使用了一种名为 TLS(传输层安全协议)/ SSL(安全套接字层协议)的特殊安全协议。该协议可确保数据在传输过程中加密。

在深入探讨协议本身之前,有必要谈谈OSI(开放系统互连模型)网络模型。

OSI网络模型

OSI是开放系统互连(Open Systems Interconnection)模型。它是基础框架,描述了数据传输机制。它代表了一种组织网络内各种通信方面的标准方法。

TCP、HTTPS、UDP、FTP、ICMP 等数据传输协议都构建在第三层(特别是 IP 地址)之上。它们负责很多方面:保证数据传输、安全以及传输特定的高级数据结构。

OSI 模型是由国际标准化组织 (ISO) 的开发人员于 1984 年创建的。该模型是一个基本基础,每个决定研究网络工作原理的专家都应该了解它。

20 世纪 80 年代,它成为国际标准化组织 (ISO) 开放系统互连小组的成果。该模型未能完整描述网络,也未能获得互联网早期架构师的支持,后来在互联网工程任务组 (IETF) 的指导下,演变成了规范性较低的 TCP/IP 协议。

值得探讨一下 OSI 模型的各层结构。

SSL/TLS 的发展历程

安全套接层 (SSL) 协议由 Netscape 公司于 1995 年开发。其目的是确保互联网上客户端和服务器之间的数据传输安全。SSL 有多个版本,每个版本都比前一个版本更安全。

1999年,SSL被TLS(传输层安全协议)取代。然而,SSL这个术语仍然用来指代这两种协议。

使用 SSL/TLS 协议的网站的特点是其 URL 以"HTTPS"而不是"HTTP"开头,这表明连接是安全的。

TLS/SSL 的工作原理

TLS 和 SSL 被以下机构使用:

  • 用于身份验证的非对称加密。顺便一提,这种加密方式资源消耗较大,因此通常与对称加密结合使用。

  • 对称加密保护隐私。

  • 用于维护消息完整性的消息认证码。

该协议广泛应用于网络应用。事实上,它们本质上是相同的,因为 SSL 3.0 版本引入了其后续协议 TLS。然而,名称沿用至今,SSL 仍然最常用于指代 TLS。

当用户通过 HTTPS 访问网站时,浏览器会向服务器请求证书,服务器会发送包含公钥的 SSL 证书副本。浏览器随后会验证此证书。此外,浏览器还会检查证书的到期日期以及是否存在由受信任的证书颁发机构颁发的根证书。如果浏览器信任该证书,它会基于公钥生成一个预主密钥,并使用双方都支持的最高级别加密算法。服务器使用其私钥解密预主密钥,同意继续通信,并使用特定的加密方法创建一个共享密钥(主密钥)。此时,双方使用一个仅在当前会话中有效的对称密钥。会话结束后,密钥将被销毁,下次用户访问该网站时,握手过程将重新开始。

SSL 和 TLS 的工作原理基本相同。它们都基于 TCP/IP 协议建立安全通道,数据通过应用层协议(HTTP、DNS、FTP、SMTP 等)在该通道内传输。

应用层协议位于 TLS/SSL 通道中,而 TLS/SSL 通道又位于 TCP/IP 协议中。本质上,数据是通过 TCP/IP 协议传输的,但关键在于数据是加密的,只有建立连接的机器才能解密。

建立联系需要分几个阶段进行:

  1. 客户端与服务器建立连接并请求安全连接。这可以通过在 SSL/TLS 端口(默认端口 443)上建立连接来实现,也可以通过客户端在建立常规连接后额外请求安全连接来实现。

  2. 建立连接时,客户端会提供一个可用加密算法列表。服务器会将该列表与自身的算法列表进行比较,选择最安全的算法,然后将其发送给客户端。

  3. 服务器向客户端发送其数字证书(由证书颁发机构签名)和服务器的公钥。

  4. 客户端可以选择验证服务器的证书。它会联系受信任证书颁发机构的服务器,验证证书的有效性。建议客户端验证证书以防止 ARP 欺骗攻击。在这种类型的网络攻击中,攻击者可以伪装成客户端或服务器。

  5. 生成用于安全连接的会话密钥。生成过程如下:

  • 客户端生成一个随机数字序列;

  • 客户端使用服务器的公钥对其进行加密,并将结果发送给服务器;

  • 服务器使用私钥解密接收到的序列。

采用非对称加密算法,只有服务器才能解密序列。非对称加密基于两个密钥(私钥和公钥)。公钥用于加密消息,私钥用于解密消息。使用公钥解密密钥是不可能的。

  1. 这将建立一个加密连接。通过该连接传输的数据会被加密和解密,直到连接断开为止。
根证书

TLS 连接基于"根证书"的概念。根证书是由证书颁发机构颁发的证书,其签名确认了已签名证书的真实性。证书本身还包含服务器名称、到期日期和其他详细信息。当然,过期的证书无法使用。

证书签名请求 (CSR)

要获取已签名的服务器证书,您必须生成一个 CSR 文件,然后将其发送给 CA,CA 将返回已签名的证书。CSR 文件是使用生成的加密密钥生成的。

客户端证书

这些证书用于双向验证,客户端验证服务器的有效性,服务器也验证客户端未被欺骗。这种交互称为相互认证。这种认证方式可以增强安全性,或取代标准的登录名和密码认证。

哈希

哈希算法的目的是将SSL证书的全部内容转换为固定长度的比特串。哈希值使用证书颁发机构的私钥进行加密,该私钥作为签名包含在证书中。

我们来深入探讨一下哈希技术。用户密码、证书签名、校验和以及加密货币有什么共同之处?答案是:哈希。

哈希是基于哈希函数的。该函数可以将任意长度的数据转换为给定长度的数据,并且几乎可以保证数据安全。

哈希的基本原理:

  1. 哈希值始终是固定大小,例如 32 个字符。

  2. 完全相同的数据会生成相同的哈希值。但即使只改变一个字符,哈希值也会不同。

  3. 不同的值会产生不同的哈希值。

  4. 不可逆形成。

值得探讨一下哈希碰撞的概念。哈希碰撞是指两组不同的数据生成了相同的哈希值。因此,攻击者即使输入错误的密码,生成的哈希值仍然相同,从而能够获得访问权限。

哈希算法有很多种(例如 MD5、SHA1、SHA256、SHA5211、CRC-32)。每种算法都使用不同的底层算法,因此碰撞概率也不同。哈希算法可以分为三类:``

在SSL/TLS的上下文中,哈希算法的目的是将证书的全部内容转换为固定长度的字节字符串。证书颁发机构的私钥(签名)用于加密哈希值。

哈希算法还使用消息认证码(MAC)。这是为了验证传输数据的完整性。MAC 使用映射函数将消息数据表示为固定长度的值,然后对消息进行哈希处理。

TLS协议使用HMAC(哈希消息认证码)。它的独特之处在于,它直接使用共享密钥进行哈希运算。这意味着密钥成为数据的一部分,双方必须使用相同的密钥来验证数据的真实性。

目前支持的哈希算法是 SHA2,其中 SHA-256 最为常用。它相当安全,而且不像 SHA-512 那样庞大。SHA-512 的字长为 64 位,而 SHA-256 的字长为 32 位。其他参数也更大------每个周期的轮数为 80 轮(而不是 64 轮),消息被分割成 1024 位的数据块(而不是 512 位)。SHA1、MD5 等算法曾经被使用,但现在被认为存在安全漏洞。不过,偶尔还能见到 MD5 哈希值。


让我们尝试实现一个基于 TLS 的客户端-服务器应用程序作为示例(并了解什么是 Diffie-Hellman 协议)。

Diffie-Hellman算法

这是一种特殊的加密协议,允许多方通过安全通信信道获取共享密钥。生成的密钥用于通过对称加密算法对后续通信进行加密。然而,它的安全性并不像看起来那么高;它容易受到中间人攻击。该算法并非旨在解决这个问题。不过,该算法的主要优势之一是密钥的生成无需各方直接交换。

算法用于允许双方创建共享的秘密密钥(也称为"传输密钥"),然后使用该密钥对消息进行加密和解密。

用 Python 实现一个简单的 SSL 套接字应用程序

我们来尝试用 Python 实现 TLS 连接。在这个例子中,我将创建一个简单的聊天程序,其中一个客户端与服务器通信。我们不需要任何库,但我使用了 rich 库来显示美观的消息。

那么,我们首先导入所有需要的库:

复制代码
from` `pathlib` `import` `Path`
`from` `datetime` `import` `datetime`
`import` `socket`
`import` `ssl`
`import` `sys`
`import` `time`
`from` `rich` `import` `print`
`

如果需要安装,可以使用以下命令安装丰富的库pip3 install rich

之后,我们将编写一些基本函数:

复制代码
def` `print_msg`(`text`: `str`, `msg_type`: `str='info'`) `->` `None`:
    `"""`
`    Выводит детальное и красивое сообщение.`

`    :param text: Текст сообщения`
`    :param msg_type: Тип сообщения (по умолчанию info)`
`    """`
    `msg_type` `=` `msg_type`.`lower`()

    `if` `msg_type` `==` `'info'`:
        `message` `=` `f'[green]INFO::`{`datetime`.`now`()}`[/green] -- `{`text`}`'`
    `elif` `msg_type` `==` `'warning'`:
        `message` `=` `f'[yellow]WARNING::`{`datetime`.`now`()}`[/yellow] -- `{`text`}`'`
    `elif` `msg_type` `==` `'error'`:
        `message` `=` `f'[red]ERROR::`{`datetime`.`now`()}`[/red] -- `{`text`}`'`
    `elif` `msg_type` `==` `'debug'`:
        `message` `=` `f'[blue]DEBUG::`{`datetime`.`now`()}`[/blue] -- `{`text`}`'`
    `else`:
        `message` `=` `f'[cyan]`{`msg_type`.`upper`()}`::`{`datetime`.`now`()}`[/cyan] -- `{`text`}`'`

    `print`(`message`)


`def` `ping`() `->` `None`:
    `"""`
`    Функция для оперативной проверки, есть ли в системе подключение к интернету.`
`    """`
    `ip_lists` `=` [`"8.8.8.8"`, `'1.1.1.1'`]
    `port` `=` `53`

    `for` `ip` `in` `ip_lists`:
        `try`:
            `socket`.`setdefaulttimeout`(`3`)
            `sock` `=` `socket`.`socket`(`socket`.`AF_INET`, `socket`.`SOCK_STREAM`)

            `print_msg`(`f'Try ping `{`ip`}`...'`, `'debug'`)

            `server_address` `=` (`ip`, `port`)

            `sock`.`connect`(`server_address`)
        `except` `OSError` `as` `error`:
            `print_msg`(`f'OSError occured when ping `{`ip`}`: `{`error`}`'`, `'warning'`)
        `else`:
            `print_msg`(`f'Internet connection is enabled!'`, `'info'`)
            `sock`.`close`()
            `return` `True`

    `print_msg`(`f'Connection not acquired'`, `'error'`)
    `return` `False`


`def` `first_check`():
    `"""`
`    Первая проверка наличия подключения к сети.`
`    """`
    `if` `ping`():
        `live` `=` `" CONNECTION ACQUIRED "`
        `print`(`'-'` `*` `len`(`live`))
        `print`(`live`)
        `print`(`'-'` `*` `len`(`live`))
        `connection_acquired_time` `=` `datetime`.`now`()
        `acquiring_message` `=` `"connection acquired at: "` `+` `connection_acquired_time`.`strftime`(`"%Y-%m-%d %H:%M:%S"`)
        `print`(`acquiring_message`)

        `return` `True`
    `else`:
        `not_live` `=` `' CONNECTON NOT ACQUIRED '`
        `print`(`'-'` `*` `len`(`not_live`))
        `print`(`not_live`)
        `print`(`'-'` `*` `len`(`not_live`))

        `return` `False`
`

在这里,我实现了一个用于 ping 地址 8.8.8.8 和 1.1.1.1(以检查网络连接)的函数,一个用于显示"友好消息"和检查网络连接的函数。

现在我们的任务是实现服务器:

复制代码
class` `Server`:
    
    `def` `__init__`(`self`, `hostname`: `str`, `port`: `int`, `client_cert`: `str`, `server_key`: `str`, `server_cert`: `str`):
        
        `self`.`hostname` `=` `hostname`
        `self`.`port` `=` `port`

        `try`:
            `self`.`sock` `=` `socket`.`socket`(`socket`.`AF_INET`, `socket`.`SOCK_STREAM`, `0`)
            `self`.`sock`.`bind`((`self`.`hostname`, `self`.`port`))
            `print_msg`(`f'Binding TLS server address: `{`self`.`hostname`}`:`{`self`.`port`}`'`, `'info'`)
        `except` `Exception` `as` `ex`:
            `print_msg`(`f'An error occured when binding address: `{`ex`}`'`, `'error'`)
            `sys`.`exit`(`1`)
        `else`:
            `print_msg`(`f'Server address has been binded successfully.'`, `'debug'`)

        `try`:
            `self`.`client_cert` `=` `Path`(`client_cert`)
            `self`.`server_key` `=` `Path`(`server_key`)
            `self`.`server_cert` `=` `Path`(`server_cert`)
        `except` `TypeError` `as` `t_ex`:
            `print_msg`(`f'Type error when loading file paths: `{`t_ex`}`'`, `'error'`)
            `sys`.`exit`(`1`)

        `self`.`_check_paths`()

        `self`.`context` `=` `self`.`_create_ssl_context`()

    `def` `_check_paths`(`self`) `->` `bool`:
        
        
        `if` `not` `self`.`server_key`.`exists`() `or` `not` `self`.`server_cert`.`exists`():
            `print_msg`(`'Server keyfile or certfile has not exists. Exiting...'`, `'error'`)
        `elif` `not` `self`.`client_cert`.`exists`():
            `print_msg`(`'Client certfile is not exists'`, `'warning'`)
        `else`:
            `print_msg`(`'All files have been loaded successfully.'`)
            `return` `True`

        `sys`.`exit`(`1`)

    `def` `_create_ssl_context`(`self`) `->` `ssl`.`SSLContext`:
       
        `context` `=` `ssl`.`create_default_context`(`ssl`.`Purpose`.`CLIENT_AUTH`)
        
        `print_msg`(`f'Create SSL context (purpose: CLIENT_AUTH)'`, `'debug'`)

        `try`:
            `context`.`load_cert_chain`(`certfile=self`.`server_cert`, `keyfile=self`.`server_key`)
            `context`.`options` `|=` `ssl`.`OP_SINGLE_ECDH_USE`
            `context`.`options` `|=` `ssl`.`OP_NO_TLSv1` `|` `ssl`.`OP_NO_TLSv1_1` `|` `ssl`.`OP_NO_TLSv1_2`
            `print_msg`(`f'Setup SSL context: cert required; no TLSv1, TLSv1_1, no TLSv1_2.'`, `'debug'`)
        `except` `ssl`.`SSLError` `as` `ssl_ex`:
            `print_msg`(`f'SSL Context setup exception (SSlError): `{`ssl_ex`}`'`, `'error'`)
            `sys`.`exit`(`1`)
        `else`:
            `print_msg`(`f'SSL Context created successfully!'`, `'debug'`)

        `return` `context`

    `def` `get_message`(`self`, `conn`, `addr`):
        
        `print_msg`(`f"Wait message from `{`addr`}`..."`, `'info'`)
        `message` `=` `conn`.`recv`(`1024`).`decode`()
        `print_msg`(`f'Received message from `{`addr`}`: `{`message`}`'`, `'info'`)
        `print`(`f'`{`addr`[`0`]}`:`{`addr`[`1`]}` say: `{`message`}`'`)

    `def` `broadcast`(`self`, `conn`, `addr`):
        
        `print_msg`(`f'Start broadcast for `{`addr`}`'`, `'debug'`)
        `print`(`'Enter `exit` for stop broadcasting'`)

        `while` `True`:
            `self`.`get_message`(`conn`, `addr`)

            `cmd` `=` `input`(`f'`{`self`.`hostname`}`:`{`self`.`port`}` (server) $ '`).`lower`().`strip`()

            `if` `len`(`cmd`) `==` `"0"`:
                `continue`
            `elif` `cmd` `==` `'exit'`:
                `break`
            `else`:
                `conn`.`send`(`cmd`.`encode`())

        `print_msg`(`f'Stop broadcast for `{`addr`}`'`, `'debug'`)

    `def` `wrapping`(`self`):
        
        `print_msg`(`'Listen socket...'`, `'debug'`)
        `self`.`sock`.`listen`(`1`)
        
        `print_msg`(`'Wrap server-side socket...'`, `'debug'`)

        `try`:
            `with` `self`.`context`.`wrap_socket`(`self`.`sock`, `server_side=True`) `as` `wrapped_sock`:
                `wrapped_sock`.`settimeout`(`3600`)       
                `conn`, `addr` `=` `wrapped_sock`.`accept`() 
                `print_msg`(`f'New address connected: `{`addr`}`'`, `'info'`)
                `self`.`broadcast`(`conn`, `addr`)         
        `except` `KeyboardInterrupt`:
            `self`.`sock`.`close`()
            `print_msg`(`'KeyboardInterrupt was received. Shutdown server...'`, `'warning'`)
`

以及客户:

复制代码
#!/usr/bin/python3`
`from` `pathlib` `import` `Path`
`import` `socket`
`import` `ssl`
`import` `sys`
`from` `src`.`utils` `import` `print_msg`


`class` `Client`:
    `def` `__init__`(`self`, `hostname`: `str`, `port`: `int`, `client_key`: `str`, `client_cert`: `str`, `server_cert`: `str`):
        `self`.`hostname` `=` `hostname`
        `self`.`port` `=` `port`

        `try`:
            `self`.`sock` `=` `socket`.`socket`(`socket`.`AF_INET`, `socket`.`SOCK_STREAM`, `0`)
            `self`.`sock`.`connect`((`self`.`hostname`, `self`.`port`))
            `print_msg`(`f'Connecting to TLS server address: `{`self`.`hostname`}`:`{`self`.`port`}`'`, `'info'`)
        `except` `Exception` `as` `ex`:
            `print_msg`(`f'An error occured when connecting to address: `{`ex`}`'`, `'error'`)
            `sys`.`exit`(`1`)
        `else`:
            `print_msg`(`f'Client has been connected successfully.'`, `'debug'`)

        `try`:
            `self`.`client_cert` `=` `Path`(`client_cert`)
            `self`.`client_key` `=` `Path`(`client_key`)
            `self`.`server_cert` `=` `Path`(`server_cert`)
        `except` `TypeError` `as` `t_ex`:
            `print_msg`(`f'Type error when loading file paths: `{`t_ex`}`'`, `'error'`)
            `sys`.`exit`(`1`)

        `self`.`_check_paths`()

        `self`.`context` `=` `self`.`_create_ssl_context`()

    `def` `_check_paths`(`self`) `->` `bool`:
       
        
        `if` `not` `self`.`client_key`.`exists`() `or` `not` `self`.`client_cert`.`exists`():
            `print_msg`(`'Client keyfile or certfile has not exists. Exiting...'`, `'error'`)
        `elif` `not` `self`.`server_cert`.`exists`():
            `print_msg`(`'Server certfile is not exists'`, `'warning'`)
        `else`:
            `print_msg`(`'All files have been loaded successfully.'`)
            `return` `True`

        `sys`.`exit`(`1`)

    `def` `_create_ssl_context`(`self`) `->` `ssl`.`SSLContext`:
        
        `context` `=` `ssl`.`SSLContext`(`ssl`.`PROTOCOL_TLS`, `cafile=self`.`server_cert`)

        `print_msg`(`f'Create SSL context (PROTOCOL_TLS)'`, `'debug'`)

        `try`:
            `context`.`load_cert_chain`(`certfile=self`.`client_cert`, `keyfile=self`.`client_key`)
            `context`.`load_verify_locations`(`cafile=self`.`server_cert`)
            `context`.`options` `|=` `ssl`.`OP_SINGLE_ECDH_USE`
            `context`.`options` `|=` `ssl`.`OP_NO_TLSv1` `|` `ssl`.`OP_NO_TLSv1_1` `|` `ssl`.`OP_NO_TLSv1_2`
        `except` `ssl`.`SSLError` `as` `ssl_ex`:
            `print_msg`(`f'SSL Context setup exception (SSlError): `{`ssl_ex`}`'`, `'error'`)
            `sys`.`exit`(`1`)
        `else`:
            `print_msg`(`f'SSL Context created successfully!'`, `'debug'`)

        `return` `context`

    `def` `send_messages`(`self`):
        `with` `self`.`context`.`wrap_socket`(`self`.`sock`, `server_side=False`, `server_hostname=self`.`hostname`) `as` `wrapped_sock`:
            `self`.`wrapped_sock`.`settimeout`(`3600`)
            `print`(`f'`{`wrapped_sock`.`version`()}`; Type `exit` for disconnect'`)
            
            `while` `True`:
                `message` `=` `input`(`"Message > "`).`lower`().`strip`()

                `if` `len`(`message`) `==` `0`:
                    `continue`
                `elif` `message` `==` `'exit'`:
                    `sys`.`exit`(`1`)
                `else`:
                    `wrapped_sock`.`send`(`message`.`encode`())

                `receives` `=` `wrapped_sock`.`recv`(`1024`).`decode`()

                `print`(`f'`{`self`.`hostname`}`@`{`self`.`port`}` say: `{`receives`}`'`)
`

现在你可以进行测试了......但在此之前,你需要生成密钥和证书才能将客户端连接到服务器。可以使用openssl以下命令通过该实用程序完成此操作:

复制代码
openssl` req `-new` `-newkey` rsa:3072 `-days` `365` `-nodes` `-x509` `-keyout` client.key `-out` client.crt
`openssl` req `-new` `-newkey` rsa:3072 `-days` `365` `-nodes` `-x509` `-keyout` server.key `-out` server.crt
`

最后,你可以编写主函数:

复制代码
def` `main`():
    `if` `not` `first_check`():
        `print_msg`(`'Please, connect to network'`, `'note'`)
        `sys`.`exit`(`1`)

    `command` `=` `input`(`"Server or client? "`).`lower`()

    `if` `command` `==` `'server'`:
        `server` `=` `Server`(`"127.0.0.1"`, `8080`, `"client.crt"`, `"server.key"`, `"server.crt"`)
        `server`.`wrapping`()
    `else`:
        `client` `=` `Client`(`"127.0.0.1"`, `8080`, `"client.key"`, `'client.crt'`, `'server.crt'`)
        `client`.`send_messages`()


`if` `__name__` `==` `'__main__'`:
    `main`()
`

这是我们创建的小型客户端-服务器应用程序。您可以对其进行扩展,创建一个多线程的安全即时通讯工具。现在,让我们继续讨论本文第一部分的结论。

❯ 结论

这只是第一部分;在下一部分中,我们将探讨代理和 VPN 的概念。我们将学习如何仅用几条命令在服务器上创建自己的私有网络。我本想把所有内容都写在一篇文章里,但那样文章就太长了。因此,第二部分将在未来几天内发布,或者可能马上发布。如果您喜欢这个主题,请在评论区告诉我您是否希望看到更多相关文章。

希望您喜欢这篇文章。这是我耗时最长、最费力的项目之一,写作过程中我也学到了很多。这确实很有挑战性,我花了几个星期的时间研究这个主题。

任何批评指正我都非常欢迎。我知道我可能有所疏漏,或者描述得不够清楚(甚至有误)。我尽力检查了所有内容,但您或许仍然会发现一些不足之处。不过,人生就是一个不断自我提升的过程。

相关推荐
松涛和鸣9 小时前
DAY43 HTML Basics
linux·前端·网络·网络协议·tcp/ip·html
谁在夜里看海.9 小时前
【Linux-网络】HTTP的清风与HTTPS的密语
linux·网络·http·https
HIT_Weston9 小时前
82、【Ubuntu】【Hugo】搭建私人博客:文章目录(一)
linux·运维·ubuntu
CAU界编程小白9 小时前
Linux系统编程系列之文件fd
linux·文件
冉佳驹9 小时前
Linux ——— 文件操作与缓冲机制的核心原理
linux·重定向·用户级缓冲区·open的返回值·进程中的当前路径
牛奶咖啡139 小时前
Linux的ext4文件系统元数据故障恢复实践教程
linux·服务器·机械硬盘的结构·ext4文件系统的构成·ext4超级块故障的修复·ext4块组描述故障修复·ext4块组的构成
hhzz9 小时前
Docker 搭建 NextCloud + OnlyOffice 完整教程(Linux Centos7系统)
linux·docker·容器·onlyoffice·nextcloud
.普通人9 小时前
树莓派4Linux 可操作多个gpio口驱动编写
linux
01传说9 小时前
Linux-yum源切换阿里centos7 实战好用
linux·运维·服务器