近日笔者需要实现一个网络流量转发的需求。要求很简单:将浏览器的请求转发到一个指定的域名,需要提供一个https端口并且把http端口转发到https端口。由于笔者对nginx的认知有限,因此磕磕绊绊研究了一阵,将结果写出来,以供其他有类似需求的同学参考对照。
1. 使用docker拉起nginx服务
nginx的官方docker在这里。拉起nginx就一个命令:
sudo docker run -d -v /home/linmao/nginx/nginx.conf:/etc/nginx/nginx.conf -v /home/linmao/nginx/cert:/cert/ -p 8000:80 -p 4443:443 nginx
这里解释一下几个参数:
参数 | 说明 |
---|---|
-v /home/linmao/nginx/nginx.conf:/etc/nginx/nginx.conf | nginx 的配置文件 |
-v /home/linmao/nginx/cert:/cert/ | 适配https端的密钥 |
-p 8000:80 | 把本机的8000端口映射到nginx的http端口80 |
-p 4443:443 | 把本机的4443端口映射到nginx的https端443 |
2. 自签https证书
上边用到了https端点,需要用到ssl证书,到阿里云买一个证书要一年几百块,所以自己签一个,要多少有多少,缺点就是浏览器一看这证书感觉没什么权威性,会让用户先进一个安全警告页面。不过先凑合用着吧。下边的方法是笔者见过的最快生成证书的方法(仅需两个命令)。
首先,生成一个根密钥和csr,根据提示填写相应的信息:
$ openssl req -newkey rsa:2048 -nodes -keyout self_key.pem -out self_csr.pem
Generating a RSA private key
................................................+++++
.............................+++++
writing new private key to 'self_key.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Guangdong
Locality Name (eg, city) []:guangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:haha
Organizational Unit Name (eg, section) []:haha
Common Name (e.g. server FQDN or YOUR name) []:haha
Email Address []:haha@haha.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$ ls
self_csr.pem self_key.pem
接着给csr签名:
$ openssl x509 -signkey self_key.pem -in self_csr.pem -req -days 365 -out self_cert.pem
Signature ok
subject=C = CN, ST = Guangdong, L = guangzhou, O = haha, OU = haha, CN = haha, emailAddress = haha@haha.com
Getting Private key
$ ls
self_cert.pem self_csr.pem self_key.pem
根密钥就是self_key.pem,证书就是self_cert.pem,这两个要映射到nginx容器里给https端口使用,那个self_csr.pem可以扔了。
3. nginx配置文件
nginx.conf
work_processes 1;
events {
worker_connections 64;
}
http {
# 提供一个http端口
server {
# 监听80端口
listen 80;
# 重定向到https端口,这个操作会引起浏览器地址变成localhost,
# 但因为我们后边提供了https端口,因此并不会引起浏览器真的去请求localhost。
rewrite ^(.*) https://localhost$1 permanent;
}
# 提供一个https端口
server {
# 监听443端口,注意后边要加ssl
listen 443 ssl;
# 设置https所需的密钥
ssl_certificate /cert/self_cert.pem;
ssl_certificate_key /cert/self_cert_key.pem;
# 设置一个后端,可根据uri匹配不同的后端
# uri匹配规则 ~ /(.*)$
# ~: 以正则表达式进行匹配。
# ^/(.*)$: 匹配的正则表达式,匹配uri中的/xxxxxx。uri中后边这一段xxxxx可以通过$1获取。
location ~ ^/(.*)$ {
# 把流量转发到https://www.baidu.com/xxxxx。
proxy_pass https://www.baidu.com/$1;
# 后边这些配置可避免触发重定向,全都要写。
proxy_redirect off;
# 使用proxy_set_header可以修改请求头,nginx提供一些变量可以使用。
# $scheme:客户端请求nginx时使用的协议,可为http或https。
# $host: 客户端请求nginx时使用的ip地址或域名,取决于客户端通过ip还是域名发请求给nginx。
# $port: 客户端请求nginx时使用的端口。
# $http_host: 等同于$host:$port
# $proxy_host: 目标服务器,有些上游服务器会检查该头,把Host头改为$proxy_host可以避免触发一些安全机制。
proxy_set_header Host $proxy_host;
# $remote_addr:客户端ip地址或者上一个nginx的ip地址(如果请求是由nginx转发过来的)。
proxy_set_header X-Real-IP $remote_addr;
# $proxy_add_x_forwarded_for: 与X-Forwarded-For配合使用,
# 相当于把X-Forwarded-For中的内容再加上$remote_addr,形成一个跳转链。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
更多的nginx变量可以参考这里。启动之后就可以愉快地玩耍了。
999. Nginx踩坑记录
下边记录笔者爬出来的坑。