mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-16 22:23:45 +08:00
mod
This commit is contained in:
183
极客时间专栏/透视HTTP协议/答疑篇/41 | Linux|Mac实验环境搭建与URI查询参数.md
Normal file
183
极客时间专栏/透视HTTP协议/答疑篇/41 | Linux|Mac实验环境搭建与URI查询参数.md
Normal file
@@ -0,0 +1,183 @@
|
||||
<audio id="audio" title="41 | Linux/Mac实验环境搭建与URI查询参数" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/53/09/53387d0bb500b74eea2e2b8ca622d009.mp3"></audio>
|
||||
|
||||
你好,我是Chrono。
|
||||
|
||||
先要说一声“抱歉”。由于工作比较紧张、项目实施频繁出差,导致原本预定的“答疑篇”迟迟没有进展,这次趁着“十一”长假,总算赶出了两期,集中回答几个同学们问得比较多的问题:Linux/Mac实验环境搭建([第7讲](https://time.geekbang.org/column/article/100124)),URI查询参数([第11讲](https://time.geekbang.org/column/article/102008)),还有DHE/ECDHE算法的原理([第26讲](https://time.geekbang.org/column/article/110354)),后续有时间可能还会再陆续补充完善。
|
||||
|
||||
很高兴在时隔一个多月后与你再次见面,废话不多说了,让我们开始吧。
|
||||
|
||||
## Linux上搭建实验环境
|
||||
|
||||
我们先来看一下如何在Linux上搭建课程的实验环境。
|
||||
|
||||
首先,需要安装OpenResty,但它在Linux上提供的不是zip压缩包,而是各种Linux发行版的预编译包,支持常见的Ubuntu、Debian、CentOS等等,而且[官网](http://openresty.org/cn/linux-packages.html)上有非常详细安装步骤。
|
||||
|
||||
以Ubuntu为例,只要“按部就班”地执行下面的几条命令就可以了,非常轻松:
|
||||
|
||||
```
|
||||
# 安装导入GPG公钥所需的依赖包:
|
||||
sudo apt-get -y install --no-install-recommends wget gnupg ca-certificates
|
||||
|
||||
|
||||
# 导入GPG密钥:
|
||||
wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
|
||||
|
||||
|
||||
# 安装add-apt-repository命令
|
||||
sudo apt-get -y install --no-install-recommends software-properties-common
|
||||
|
||||
|
||||
# 添加官方仓库:
|
||||
sudo add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main"
|
||||
|
||||
|
||||
# 更新APT索引:
|
||||
sudo apt-get update
|
||||
|
||||
|
||||
# 安装 OpenResty
|
||||
sudo apt-get -y install openresty
|
||||
|
||||
```
|
||||
|
||||
全部完成后,OpenResty会安装到“/usr/local/openresty”目录里,可以用它自带的命令行工具“resty”来验证是否安装成功:
|
||||
|
||||
```
|
||||
$resty -v
|
||||
resty 0.23
|
||||
nginx version: openresty/1.15.8.2
|
||||
built with OpenSSL 1.1.0k 28 May 2019
|
||||
|
||||
```
|
||||
|
||||
有了OpenResty,就可以从GitHub上获取http_study项目的源码了,用“git clone”是最简单快捷的方法:
|
||||
|
||||
```
|
||||
git clone https://github.com/chronolaw/http_study
|
||||
|
||||
```
|
||||
|
||||
在Git仓库的“www”目录,我为Linux环境补充了一个Shell脚本“run.sh”,作用和Windows下的start.bat、stop.bat差不多,可以简单地启停实验环境,后面可以接命令行参数start/stop/reload/list:
|
||||
|
||||
```
|
||||
cd http_study/www/ #脚本必须在www目录下运行,才能找到nginx.conf
|
||||
./run.sh start #启动实验环境
|
||||
./run.sh list #列出实验环境的Nginx进程
|
||||
./run.sh reload #重启实验环境
|
||||
./run.sh stop #停止实验环境
|
||||
|
||||
```
|
||||
|
||||
启动OpenResty之后,就可以用浏览器或者curl来验证课程里的各个测试URI,但之前不要忘记修改“/etc/hosts”添加域名解析,例如:
|
||||
|
||||
```
|
||||
curl -v "http://127.0.0.1/"
|
||||
curl -v "http://www.chrono.com/09-1"
|
||||
curl -k "https://www.chrono.com/24-1?key=1234"
|
||||
curl -v "http://www.chrono.com/41-1"
|
||||
|
||||
```
|
||||
|
||||
## Mac上搭建实验环境
|
||||
|
||||
看完了Linux,我们再来看一下Mac。
|
||||
|
||||
这里我用的是两个环境:Mac mini 和 MacBook Air,不过都是好几年前的“老古董”了,系统是10.13 High Sierra和10.14 Mojave(更早的版本没有测试,但应该也都可以)。
|
||||
|
||||
首先要保证Mac里有第三方包管理工具homebrew,可以用下面的命令安装:
|
||||
|
||||
```
|
||||
#先安装Mac的homebrew
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
|
||||
```
|
||||
|
||||
然后,要用homebrew安装OpenResty,但它在Mac上的安装过程和Linux不同,不是预编译包,而是要下载许多相关的源码(如OpenSSL),然后再用clang本地编译,大概要花上五六分钟的时间,整体上比较慢,要有点耐心。
|
||||
|
||||
```
|
||||
#使用homebrew安装OpenResty
|
||||
brew install openresty/brew/openresty
|
||||
|
||||
```
|
||||
|
||||
安装完OpenResty,后续的操作就和Linux一样了,“git clone”项目源码:
|
||||
|
||||
```
|
||||
git clone https://github.com/chronolaw/http_study
|
||||
|
||||
```
|
||||
|
||||
然后,进“http_study/www”目录,用脚本“run.sh”启停实验环境,用Safari或者curl测试。
|
||||
|
||||
## Linux/Mac下的抓包
|
||||
|
||||
Linux和Mac里都有图形界面版本的Wireshark,抓包的用法与Windows完全一样,简单易用。
|
||||
|
||||
所以,今天我主要介绍命令行形式的抓包。
|
||||
|
||||
命令行抓包最基本的方式就是著名的tcpdump,不过我用得不是很多,所以就尽可能地“藏拙”了。
|
||||
|
||||
简单的抓包使用“-i lo”指定抓取本地环回地址,“port”指定端口号,“-w”指定抓包的存放位置,抓包结束时用“Ctrl+C”中断:
|
||||
|
||||
```
|
||||
sudo tcpdump -i lo -w a.pcap
|
||||
sudo tcpdump -i lo port 443 -w a.pcap
|
||||
|
||||
```
|
||||
|
||||
抓出的包也可以用tcpdump直接查看,用“-r”指定包的名字:
|
||||
|
||||
```
|
||||
tcpdump -r a.pcap
|
||||
tcpdump -r 08-1.pcapng -A
|
||||
|
||||
```
|
||||
|
||||
不过在命令行界面下可以用一个更好的工具——tshark,它是Wireshark的命令行版本,用法和tcpdump差不多,但更易读,功能也更丰富一些。
|
||||
|
||||
```
|
||||
tshark -r 08-1.pcapng
|
||||
tshark -r 08-1.pcapng -V
|
||||
tshark -r 08-1.pcapng -O tcp|less
|
||||
tshark -r 08-1.pcapng -O http|less
|
||||
|
||||
```
|
||||
|
||||
tshark也支持使用keylogfile解密查看HTTPS的抓包,需要用“-o”参数指定log文件,例如:
|
||||
|
||||
```
|
||||
tshark -r 26-1.pcapng -O http -o ssl.keylog_file:26-1.log|less
|
||||
|
||||
```
|
||||
|
||||
tcpdump、tshark和Linux里的许多工具一样,参数繁多、功能强大,你可以课后再找些资料仔细研究,这里就不做过多地介绍了。
|
||||
|
||||
## URI的查询参数和头字段
|
||||
|
||||
在[第11讲](https://time.geekbang.org/column/article/102008)里我留了一个课下作业:
|
||||
|
||||
“URI的查询参数和头字段很相似,都是key-value形式,都可以任意自定义,那么它们在使用时该如何区别呢?”
|
||||
|
||||
从课程后的留言反馈来看,有的同学没理解这个问题的本意,误以为问题问的是这两者在表现上应该如何区分,比如查询参数是跟在“?”后面,头字段是请求头里的KV对。
|
||||
|
||||
这主要是怪我没有说清楚。这个问题实际上想问的是:查询参数和头字段两者的形式很相近,query是key-value,头字段也是key-value,它们有什么区别,在发送请求时应该如何正确地使用它们。
|
||||
|
||||
换个说法就是:应该在什么场景下恰当地自定义查询参数或者头字段来附加额外信息。
|
||||
|
||||
当然了,因为HTTP协议非常灵活,这个问题也不会有唯一的、标准的答案,我只能说说我自己的理解。
|
||||
|
||||
因为查询参数是与URI关联在一起的,所以它针对的就是资源(URI),是长期、稳定的。而头字段是与一次HTTP请求关联的,针对的是本次请求报文,所以是短期、临时的。简单来说,就是两者的作用域和时效性是不同的。
|
||||
|
||||
从这一点出发,我们就可以知道在哪些场合下使用查询参数和头字段更加合适。
|
||||
|
||||
比如,要获取一个JS文件,而它会有多个版本,这个“版本”就是资源的一种属性,应该用查询参数来描述。而如果要压缩传输、或者控制缓存的时间,这些操作并不是资源本身固有的特性,所以用头字段来描述更好。
|
||||
|
||||
除了查询参数和头字段,还可以用其他的方式来向URI发送附加信息,最常用的一种方式就是POST一个JSON结构,里面能够存放比key-value复杂得多的数据,也许你早就在实际工作中这么做了。
|
||||
|
||||
在这种情况下,就可以完全不使用查询参数和头字段,服务器从JSON里获取所有必需的数据,让URI和请求头保持干净、整洁(^_^)。
|
||||
|
||||
今天的答疑就先到这里,我们下期再见,到时候再讲ECDHE算法。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c1/f9/c17f3027ba3cfb45e391107a8cf04cf9.png" alt="unpreview">
|
||||
|
||||
|
||||
93
极客时间专栏/透视HTTP协议/答疑篇/42 | DHE|ECDHE算法的原理.md
Normal file
93
极客时间专栏/透视HTTP协议/答疑篇/42 | DHE|ECDHE算法的原理.md
Normal file
@@ -0,0 +1,93 @@
|
||||
<audio id="audio" title="42 | DHE/ECDHE算法的原理" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/66/7b/669bffe5b009bca02f827d434fec157b.mp3"></audio>
|
||||
|
||||
你好,我是Chrono。
|
||||
|
||||
在[第26讲](https://time.geekbang.org/column/article/110354)里,我介绍了TLS 1.2的握手过程,在Client Hello和Server Hello里用到了ECDHE算法做密钥交换,参数完全公开,但却能够防止黑客攻击,算出只有通信双方才能知道的秘密Pre-Master。
|
||||
|
||||
这是TLS握手的关键步骤,也让很多同学不太理解,“为什么数据都是不保密的,但中间人却无法破解呢?”
|
||||
|
||||
解答这个问题必须要涉及密码学,我原本觉得有点太深了,不想展开细讲,但后来发现大家都对这个很关心,有点“打破砂锅问到底”的精神。所以,这次我就试着从底层来解释一下。不过你要有点心理准备,这不是那么好懂的。
|
||||
|
||||
先从ECDHE算法的名字说起。ECDHE就是“短暂-椭圆曲线-迪菲-赫尔曼”算法(ephemeral Elliptic Curve Diffie–Hellman),里面的关键字是“短暂”“椭圆曲线”和“迪菲-赫尔曼”,我先来讲“迪菲-赫尔曼”,也就是DH算法。
|
||||
|
||||
## 离散对数
|
||||
|
||||
DH算法是一种非对称加密算法,只能用于密钥交换,它的数学基础是“**离散对数**”(Discrete logarithm)。
|
||||
|
||||
那么,什么是离散对数呢?
|
||||
|
||||
上中学的时候我们都学过初等代数,知道指数和对数,指数就是幂运算,对数是指数的逆运算,是已知底数和真数(幂结果),反推出指数。
|
||||
|
||||
例如,如果以10作为底数,那么指数运算是y=10^x,对数运算是y=logx,100的对数是2(10^2=100,log100=2),2的对数是0.301(log2≈0.301)。
|
||||
|
||||
对数运算的域是实数,取值是连续的,而“离散对数”顾名思义,取值是不连续的,数值都是整数,但运算具有与实数对数相似的性质。
|
||||
|
||||
离散对数里的一个核心操作是模运算,也就是取余数(mod,在C、Java、Lua等语言里的操作符是“%”)。
|
||||
|
||||
假设有模数17,底数5,那么“5的3次方再对17取余数得6”(5 ^ 3 % 17 = 6)就是在离散整数域上的一次指数运算(5 ^ 3 (mod 17) = 6)。反过来,以5为底,17为模数,6的离散对数就是3(Ind(5, 6) = 3 ( mod 17))。
|
||||
|
||||
这里的(17,5)是离散对数的公共参数,6是真数,3是对数。知道了对数,就可以用幂运算很容易地得到真数,但反过来,知道真数却很难推断出对数,于是就形成了一个“**单向函数**”。
|
||||
|
||||
在这个例子里,选择的模数17很小,使用穷举法从1到17暴力破解也能够计算得到6的离散对数是3。
|
||||
|
||||
但如果我们选择的是一个非常非常大的数,比如说是有1024位的超大素数,那么暴力破解的成本就非常高了,几乎没有什么有效的方法能够快速计算出离散对数,这就是DH算法的数学基础。
|
||||
|
||||
## DH算法
|
||||
|
||||
知道了离散对数,我们来看DH算法,假设Alice和Bob约定使用DH算法来交换密钥。
|
||||
|
||||
基于离散对数,Alice和Bob需要首先确定模数和底数作为算法的参数,这两个参数是公开的,用P和G来代称,简单起见我们还是用17和5(P=17,G=5)。
|
||||
|
||||
然后Alice和Bob各自选择一个随机整数作为**私钥**(必须在1和P-2之间),严格保密。比如Alice选择a=10,Bob选择b=5。
|
||||
|
||||
有了DH的私钥,Alice和Bob再计算幂作为**公钥**,也就是A = (G ^ a % P) = 9,B = (G ^ b % P) = 14,这里的A和B完全可以公开,因为根据离散对数的原理,从真数反向计算对数a和b是非常困难的。
|
||||
|
||||
交换DH公钥之后,Alice手里有五个数:P=17,G=5,a=10,A=9,B=14,然后执行一个运算:(B ^ a % P)= 8。
|
||||
|
||||
因为离散对数的幂运算有交换律,B ^ a = (G ^ b ) ^ a = (G ^ a) ^ b = A ^ b,所以Bob计算A ^ b % P也会得到同样的结果8,这个就是Alice和Bob之间的共享秘密,可以作为会话密钥使用,也就是TLS里的Pre-Master。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/4f/ef/4fd1b613d46334827b53a1f31fa4b3ef.png" alt="">
|
||||
|
||||
那么黑客在这个密钥交换的通信过程中能否实现攻击呢?
|
||||
|
||||
整个通信过程中,Alice和Bob公开了4个信息:P、G、A、B,其中P、G是算法的参数,A和B是公钥,而a、b是各自秘密保管的私钥,无法获取,所以黑客只能从已知的P、G、A、B下手,计算9或14的离散对数。
|
||||
|
||||
由离散对数的性质就可以知道,如果P非常大,那么他很难在短时间里破解出私钥a、b,所以Alice和Bob的通信是安全的(但在本例中数字小,计算难度很低)。
|
||||
|
||||
实验环境的URI“/42-1”演示了这个简单DH密钥交换过程,可以用浏览器直接访问,命令行下也可以用“resty www/lua/42-1.lua”直接运行。
|
||||
|
||||
## DHE算法
|
||||
|
||||
DH算法有两种实现形式,一种是已经被废弃的DH算法,也叫static DH算法,另一种是现在常用的DHE算法(有时候也叫EDH)。
|
||||
|
||||
static DH算法里有一方的私钥是静态的,通常是服务器方固定,即a不变。而另一方(也就是客户端)随机选择私钥,即b采用随机数。
|
||||
|
||||
于是DH交换密钥时就只有客户端的公钥会变,而服务器公钥不变,在长期通信时就增加了被破解的风险,使得拥有海量计算资源的攻击者获得了足够的时间,最终能够暴力破解出服务器私钥,然后计算得到所有的共享秘密Pre-Master,不具有“前向安全”。
|
||||
|
||||
而DHE算法的关键在于“E”表示的临时性上(ephemeral),每次交换密钥时双方的私钥都是随机选择、临时生成的,用完就扔掉,下次通信不会再使用,相当于“一次一密”。
|
||||
|
||||
所以,即使攻击者破解了某一次的私钥,其他通信过程的私钥仍然是安全的,不会被解密,实现了“前向安全”。
|
||||
|
||||
## ECDHE算法
|
||||
|
||||
现在如果你理解了DHE,那么理解ECDHE也就不那么困难了。
|
||||
|
||||
ECDHE算法,就是把DHE算法里整数域的离散对数,替换成了椭圆曲线上的离散对数。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b4/ba/b452ceb3cbfc5c644a3053f2054b1aba.jpg" alt="">
|
||||
|
||||
原来DHE算法里的是任意整数,而ECDHE则是把连续的椭圆曲线给“离散化”成整数,用椭圆曲线上的“**倍运算**”替换了DHE里的幂运算。
|
||||
|
||||
在ECDHE里,算法的公开参数是椭圆曲线C、基点G和模数P,私钥是倍数x,公钥是倍点xG,已知倍点xG要想计算出离散对数x是非常困难的。
|
||||
|
||||
在通信时Alice和Bob各自随机选择两个数字a和b作为私钥,计算A=aG、B=bG作为公钥,然后互相交换,用与DHE相同的算法,计算得到aB=abG=Ab,就是共享秘密Pre-Master。
|
||||
|
||||
因为椭圆曲线离散对数的计算难度比普通的离散对数更大,所以ECDHE的安全性比DHE还要高,更能够抵御黑客的攻击。
|
||||
|
||||
## 思考题
|
||||
|
||||
最后留一个思考题吧:为什么DH算法只能用于密钥交换,不能用于数字签名,如果你理解了DH算法的原理应该不难回答出来。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/07/af/0773f7b9a098a64cdbe1bf2a666f87af.png" alt="">
|
||||
|
||||
|
||||
227
极客时间专栏/透视HTTP协议/答疑篇/43 | 如何进行Docker实验环境搭建?.md
Normal file
227
极客时间专栏/透视HTTP协议/答疑篇/43 | 如何进行Docker实验环境搭建?.md
Normal file
@@ -0,0 +1,227 @@
|
||||
<audio id="audio" title="43 | 如何进行Docker实验环境搭建?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/38/c7/3855a80ef278a46a10d97d775e2e18c7.mp3"></audio>
|
||||
|
||||
你好,我是Chrono。
|
||||
|
||||
《透视HTTP协议》这个专栏正式完结已经一年多了,感谢你的支持与鼓励。
|
||||
|
||||
这一年的时间下来,我发现专栏“实验环境的搭建”确实是一个比较严重的问题:虽然我已经尽量把Windows、macOS、Linux里的搭建步骤写清楚了,但因为具体的系统环境千差万别,总会有各式各样奇怪的问题出现,比如端口冲突、目录权限等等。
|
||||
|
||||
所以,为了彻底解决这个麻烦,我特意制作了一个Docker镜像,里面是完整可用的HTTP实验环境,下面我就来详细说一下该怎么用。
|
||||
|
||||
## 安装Docker环境
|
||||
|
||||
因为我不知道你对Docker是否了解,所以第一步我还是先来简单介绍一下它。
|
||||
|
||||
Docker是一种虚拟化技术,基于Linux的容器机制(Linux Containers,简称LXC),你可以把它近似地理解成是一个“轻量级的虚拟机”,只消耗较少的资源就能实现对进程的隔离保护。
|
||||
|
||||
使用Docker可以把应用程序和它相关的各种依赖(如底层库、组件等)“打包”在一起,这就是Docker镜像(Docker image)。Docker镜像可以让应用程序不再顾虑环境的差异,在任意的系统中以容器的形式运行(当然必须要基于Docker环境),极大地增强了应用部署的灵活性和适应性。
|
||||
|
||||
Docker是跨平台的,支持Windows、macOS、Linux等操作系统,在Windows、macOS上只要下载一个安装包,然后简单点几下鼠标就可以完成安装。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/24/b2/2490f6a2a514710e0b683d6cdb4614b2.png" alt="">
|
||||
|
||||
下面我以Ubuntu为例,说一下在Linux上的安装方法。
|
||||
|
||||
你可以在Linux上用apt-get或者yum安装Docker,不过更好的方式是使用Docker官方提供的脚本,自动完成全套的安装步骤。
|
||||
|
||||
```
|
||||
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
|
||||
|
||||
```
|
||||
|
||||
因为Docker是国外网站,直接从官网安装速度可能比较慢。所以你还可以选择国内的镜像网站来加快速度,像这里我就使用“–mirror”选项指定了“某某云”。
|
||||
|
||||
Docker是C/S架构,安装之后还需要再执行一条命令启动它的服务。
|
||||
|
||||
```
|
||||
sudo service docker start #Ubuntu启动docker服务
|
||||
|
||||
```
|
||||
|
||||
此外,操作Docker必须要有sudo权限,你可以用“usermod”命令把当前的用户加入“Docker”组里。如果图省事,也可以用sudo命令直接切换成root用户来操作。
|
||||
|
||||
```
|
||||
sudo usermod -aG docker ${USER} #当前用户加入Docker组
|
||||
sudo su - #或者直接用root用户
|
||||
|
||||
```
|
||||
|
||||
这些做完后,你需要执行命令“**docker version**”“**docker info**”来检查是否安装成功。比如下面这张图,显示的就是我使用的Docker环境,版本是“18.06.3-ce”。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d3/91/d3ba248bf564yy289a197dab8635c991.png" alt="">
|
||||
|
||||
## 获取Docker镜像
|
||||
|
||||
如果你已经安装好了Docker运行环境,现在就可以从Docker Hub上获取课程相应的Docker镜像文件了,用的是“**docker pull**”命令。
|
||||
|
||||
```
|
||||
docker pull chronolaw/http_study
|
||||
|
||||
```
|
||||
|
||||
这个镜像里打包了操作系统Ubuntu 18.04和最新的Openresty 1.17.8.2,还有项目的全部示例代码。为了方便你学习,我还在里面加入了Vim、Git、Telnet、Curl、Tcpdump等实用工具。
|
||||
|
||||
由于镜像里的东西多,所以体积比较大,下载需要一些时间,你要有点耐心。镜像下载完成之后,你可以用“Docker images”来查看结果,列出目前本地的所有镜像文件。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/07/22/0779b1016002f4bdafc6a785c991ba22.png" alt="">
|
||||
|
||||
从图中你可以看到,这个镜像的名字是“chronolaw/http_study”,大小是645MB。
|
||||
|
||||
## 启动Docker容器
|
||||
|
||||
有了镜像文件,你就可以用“**docker run**”命令,从镜像启动一个容器了。
|
||||
|
||||
这里面就是我们完整的HTTP实验环境,不需要再操心这样、那样的问题了,做到了真正的“开箱即用”。
|
||||
|
||||
```
|
||||
docker run -it --rm chronolaw/http_study
|
||||
|
||||
```
|
||||
|
||||
对于上面这条命令,我还要稍微解释一下:“-it”参数表示开启一个交互式的Shell,默认使用的是bash;“–rm”参数表示容器是“用完即扔”,不保存容器实例,一旦退出Shell就会自动删除容器(但不会删除镜像),免去了管理容器的麻烦。
|
||||
|
||||
“docker run”之后,你就会像虚拟机一样进入容器的运行环境,这里就是Ubuntu 18.04,身份也自动变成了root用户,大概是下面这样的。
|
||||
|
||||
```
|
||||
docker run -it --rm chronolaw/http_study
|
||||
|
||||
root@8932f62c972:/#
|
||||
|
||||
```
|
||||
|
||||
项目的源码我放在了root用户目录下,你可以直接进入“**http_study/www**”目录,然后执行“**run.sh**”启动OpenResty服务(可参考[第41讲](https://time.geekbang.org/column/article/146833))。
|
||||
|
||||
```
|
||||
cd ~/http_study/www
|
||||
./run.sh start
|
||||
|
||||
```
|
||||
|
||||
不过因为Docker自身的限制,镜像里的hosts文件不能直接添加“[www.chrono.com](http://www.chrono.com)”等实验域名的解析。如果你想要在URI里使用域名,就必须在容器启动后手动修改hosts文件,用Vim或者cat都可以。
|
||||
|
||||
```
|
||||
vim /etc/hosts #手动编辑hosts文件
|
||||
cat ~/http_study/hosts >> /etc/hosts #cat追加到hosts末尾
|
||||
|
||||
```
|
||||
|
||||
另一种方式是在“docker run”的时候用“**–add-host**”参数,手动指定域名/IP的映射关系。
|
||||
|
||||
```
|
||||
docker run -it --rm --add-host=www.chrono.com:127.0.0.1 chronolaw/http_study
|
||||
|
||||
```
|
||||
|
||||
保险起见,我建议你还是用第一种方式比较好。也就是启动容器后,用“cat”命令,把实验域名的解析追加到hosts文件里,然后再启动OpenResty服务。
|
||||
|
||||
```
|
||||
docker run -it --rm chronolaw/http_study
|
||||
|
||||
cat ~/http_study/hosts >> /etc/hosts
|
||||
cd ~/http_study/www
|
||||
./run.sh start
|
||||
|
||||
```
|
||||
|
||||
## 在Docker容器里做实验
|
||||
|
||||
把上面的工作都做完之后,我们的实验环境就算是完美地运行起来了,现在你就可以在里面任意验证各节课里的示例了,我来举几个例子。
|
||||
|
||||
不过在开始之前,我要提醒你一点,因为这个Docker镜像是基于Linux的,没有图形界面,所以只能用命令行(比如telnet、curl)来访问HTTP服务。当然你也可以查一下资料,让容器对外暴露80等端口(比如使用参数“–net=host”),在外部用浏览器来访问,这里我就不细说了。
|
||||
|
||||
先来看最简单的,[第7讲](https://time.geekbang.org/column/article/100124)里的测试实验环境,用curl来访问localhost,会输出一个文本形式的HTML文件内容。
|
||||
|
||||
```
|
||||
curl http://localhost #访问本机的HTTP服务
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/30/1a/30671d607d6cb74076c467bab1b95b1a.png" alt="">
|
||||
|
||||
然后我们来看[第9讲](https://time.geekbang.org/column/article/100513),用telnet来访问HTTP服务,输入“**telnet 127.0.0.1 80**”,回车,就进入了telnet界面。
|
||||
|
||||
Linux下的telnet操作要比Windows的容易一些,你可以直接把HTTP请求报文复制粘贴进去,再按两下回车就行了,结束telnet可以用“Ctrl+C”。
|
||||
|
||||
```
|
||||
GET /09-1 HTTP/1.1
|
||||
Host: www.chrono.com
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8d/fa/8d40c02c57b13835c8dd92bda97fa3fa.png" alt="">
|
||||
|
||||
实验环境里测试HTTPS和HTTP/2也是毫无问题的,只要你按之前说的,正确修改了hosts域名解析,就可以用curl来访问,但要加上“**-k**”参数来忽略证书验证。
|
||||
|
||||
```
|
||||
curl https://www.chrono.com/23-1 -vk
|
||||
curl https://www.metroid.net:8443/30-1 -vk
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/yy/94/yyff754d5fbe34cd2dfdb002beb00094.png" alt="">
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/69/51/69163cc988f8b95cf906cd4dbcyy3151.png" alt="">
|
||||
|
||||
这里要注意一点,因为Docker镜像里的Openresty 1.17.8.2内置了OpenSSL1.1.1g,默认使用的是TLS1.3,所以如果你想要测试TLS1.2的话,需要使用参数“**–tlsv1.2**”。
|
||||
|
||||
```
|
||||
curl https://www.chrono.com/30-1 -k --tlsv1.2
|
||||
|
||||
```
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b6/aa/b62a2278b73a4f264a14a04f0058cbaa.png" alt="">
|
||||
|
||||
## 在Docker容器里抓包
|
||||
|
||||
到这里,课程中的大部分示例都可以运行了。最后我再示范一下在Docker容器里tcpdump抓包的用法。
|
||||
|
||||
首先,你要指定抓取的协议、地址和端口号,再用“-w”指定存储位置,启动tcpdump后按“Ctrl+Z”让它在后台运行。比如为了测试TLS1.3,就可以用下面的命令行,抓取HTTPS的443端口,存放到“/tmp”目录。
|
||||
|
||||
```
|
||||
tcpdump tcp port 443 -i lo -w /tmp/a.pcap
|
||||
|
||||
```
|
||||
|
||||
然后,我们执行任意的telnet或者curl命令,完成HTTP请求之后,输入“fg”恢复tcpdump,再按“Ctrl+C”,这样抓包就结束了。
|
||||
|
||||
对于HTTPS需要导出密钥的情形,你必须在curl请求的同时指定环境变量“SSLKEYLOGFILE”,不然抓包获取的数据无法解密,你就只能看到乱码了。
|
||||
|
||||
```
|
||||
SSLKEYLOGFILE=/tmp/a.log curl https://www.chrono.com/11-1 -k
|
||||
|
||||
```
|
||||
|
||||
我把完整的抓包过程截了个图,你可以参考一下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/40/40/40f3c47814a174a4d135316b7cfdcf40.png" alt="">
|
||||
|
||||
抓包生成的文件在容器关闭后就会消失,所以还要用“**docker cp**”命令及时从容器里拷出来(指定容器的ID,看提示符,或者用“docker ps -a”查看,也可以从GitHub仓库里获取43-1.pcap/43-1.log)。
|
||||
|
||||
```
|
||||
docker cp xxx:/tmp/a.pcap . #需要指定容器的ID
|
||||
docker cp xxx:/tmp/a.log . #需要指定容器的ID
|
||||
|
||||
```
|
||||
|
||||
现在有了pcap文件和log文件,我们就可以用Wireshark来看网络数据,细致地分析HTTP/HTTPS通信过程了(HTTPS还需要设置一下Wireshark,见[第26讲](https://time.geekbang.org/column/article/110354))。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1a/bf/1ab18685ca765e8050b58ee76abd3cbf.png" alt="">
|
||||
|
||||
在这个包里,你可以清楚地看到,通信时使用的是TLS1.3协议,服务器选择的密码套件是TLS_AES_256_GCM_SHA384。
|
||||
|
||||
掌握了tcpdump的用法之后,你也可以再参考[第27讲](https://time.geekbang.org/column/article/110718),改改Nginx配置文件,自己抓包仔细研究TLS1.3协议的“supported_versions”“key_share”“server_name”等各个扩展协议。
|
||||
|
||||
## 小结
|
||||
|
||||
今天讲了Docker实验环境的搭建,我再小结一下要点。
|
||||
|
||||
1. Docker是一种非常流行的虚拟化技术,可以近似地把它理解成是一个“轻量级的虚拟机”;
|
||||
1. 可以用“docker pull”命令从Docker Hub上获取课程相应的Docker镜像文件;
|
||||
1. 可以用“docker run”命令从镜像启动一个容器,里面是完整的HTTP实验环境,支持TLS1.3;
|
||||
1. 可以在Docker容器里任意验证各节课里的示例,但需要使用命令行形式的telnet或者curl;
|
||||
1. 抓包需要使用tcpdump,指定抓取的协议、地址和端口号;
|
||||
1. 对于HTTPS,需要指定环境变量“SSLKEYLOGFILE”导出密钥,再发送curl请求。
|
||||
|
||||
很高兴时隔一年后再次与你见面,今天就到这里吧,期待下次HTTP/3发布时的相会。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/26/e3/26bbe56074b40fd5c259f396ddcfd6e3.png" alt="">
|
||||
Reference in New Issue
Block a user