轻松配置 https:Let‘s Encrypt 介绍及 Nginx Proxy Manager 实用操作教程
轻松配置 https:Let‘s Encrypt 介绍及 Nginx Proxy Manager 实用操作教程
前言
一般我们在本地进行 web 开发时用的都是 http 协议,而部署到服务器上之后为了安全都要配置 https,以保证客户端和服务器之间的通信内容得到加密,不会被泄露或篡改。
本文将介绍 https 协议的基本知识(如果有基础可以跳过),以及如何使用 Let’s Encrypt 给你的服务优雅地配置 https。
HTTPS
https本质上还是http,只不过https使用了ssl协议来加密通信内容。
ssl是一个介于TCP和应用层的协议,基于非对称加密算法,用于确保通信双方的交流是私密的
对称加密
加密解密使用同一把密钥,但是如何安全地传输这个密钥是个问题
非对称加密
有三条原则:
- 私钥加密,公钥解密
- 公钥加密,私钥解密
- 公钥加密,公钥无法解密
服务端给客户端发放公钥,客户端使用公钥加密请求,只有服务端的私钥能解密。服务端使用私钥加密响应,客户端使用公钥解密。
看起来很美好,但这样存在很一个很大的问题:如果有一个坏人(中间人)像下图这样操作,你们的通讯还是会被窃听甚至篡改的。
问题的本质在于:客户端不知道自己拿到的公钥是不是真实服务器的公钥
CA证书
于是,引入了可信任的第三方权威机构CA(Certificate Authority)
通过以上步骤可以保证客户端拿到的一定是真实服务器的公钥
混合加密
其实通过以上步骤已经可以保证加密通信了, 但是非对称加密的效率太低了, 而更高效的对称加密却不安全, 所以我们采用混合加密, 即使用非对称加密传输对称加密的密钥, 双方拿到对称加密的密钥之后使用对称加密进行通信
以下步骤(即ssl握手)发生在TCP三次握手之后:
通过以上方式,双方都拥有了相同的密钥,而且这个密钥是安全的,没有被任何中间人获取到。
联想:抓包软件
我们使用各种抓包软件时(如Fiddler),会发现无法解析HTTPS的包,要想实现就必须要在系统中安装它自己签发的CA证书。
学完HTTPS后就可以理解了,Fiddler相当于是你客户端和服务器之间的一个代理,Fiddler会经手你和服务器之间所有的流量,所以Fiddler可以向你展示抓包内容。
但是有了HTTPS之后,你和服务器使用的对称加密密钥只有你们两个知道,Fiddler就没办法解密通讯数据了。Fiddler要想解析就只能充当中间人,但是它的自签发证书是不被OS信任的,所以你要手动信任该证书。
Let’s Encrypt
上文提到,要想使用 https 协议,服务端必须向 ca 机构申请一个证书,这个证书是和域名绑定的,也就是说你必须要有一个域名,可以在阿里云等云服务提供商购买,也可以在 Freenom 免费申请一些小众后缀的域名。
假设我们有一个域名叫 baizhukui.com ,指向了一台服务器。我们想在这个服务器上运行一个 http 服务并配置 https,我们可以选择在域名提供商购买并下载证书文件,然后将其手动安装至服务器上。由于安装过程比较繁琐而且 ssl 证书是会过期的,所以我们更推荐另一种获取 ssl 证书的方式:acme(Automated Certificate Management Environment)。
acme 是一个协议,旨在自动化网站或网络应用程序的证书签发和管理过程。而 Let’s Encrypt 是一个证书颁发机构(CA),它采用了 ACME 协议来实现证书的自动化获取和更新。更重要的是,它是免费的!
我们一般开发的 web 应用都是 http 协议的,https 这种和业务无关的事情一般都会交给网关/反向代理去做,例如 nginx,traefik,nginx proxy manager 等。他们负责流量的加密解密,然后把明文数据转发给应用去处理。
traefik 和 nginx proxy manager(下文简称 npm)都是支持 Let’s Encrypt 的,我们只要做必要的配置,他们就可以自动地获取/更新/安装 ssl 证书。npm 的功能简单够用,图形化页面又很直观,所以我们就拿 npm 进行讲解。
Nginx Proxy Manager
安装
根据 npm 官方推荐,使用 docker 部署。为了便于互联,你的 http 服务也要以 docker 容器的形式部署。
用docker network create npm
新建一个名为npm
的网络,这是为了让网关和你的服务能够使用容器名互相访问,npm 和你的服务都需要加入此网络。
~/.docker/app/npm/docker-compose.yml
version: '3'
services:
npm:
image: 'jc21/nginx-proxy-manager:latest'
container_name: npm
restart: unless-stopped
ports:
- '80:80'
# gui端口
- '81:81'
- '443:443'
volumes:
- ./volume/data:/data
- ./volume/letsencrypt:/etc/letsencrypt
networks:
- npm
networks:
npm:
external: true
启动后访问服务器的 81 端口(记得开放端口),就可以看到 gui 页面了
默认账号:[email protected]
默认密码:changeme
登进去以后修改账号密码
反向代理并配置 https
点击添加 proxy host
保存,你就可以使用 https://baizhukui.com 访问你的服务了。
npm 的 ssl 配置中,dns challenge 是什么?为什么还要配 Credentials File Content?
ca 机构签发证书之前,必须检验你对于这个域名的所有权,dns challenge 是检验方式的一种。共有以下几种检验方式:
- DNS 验证:通过给这个域名添加一条 dns 解析规则,然后去查看这个解析规则是否生效。“添加 dns 解析规则”要通过你的域名提供商api来实现,比如访问阿里云的 api 就需要提供你账号的 access key 和 access secret。由于域名解析规则传播需要一定时间,所以生效比较慢。
- HTTP 验证:申请者在网站的特定路径下放置包含特定密钥或代码的文件,证明对该域名的控制权。CA 发出验证请求后会尝试访问该 URL 来检查文件的存在性。npm 的默认验证方式。简单快速。
- TLS-SNI 验证:申请者在服务器上配置特定的 TLS 证书,CA 发送验证请求并检查该证书以确认域名的控制权。
- Email 验证:CA 发送验证邮件到特定的注册邮箱地址,申请者必须通过邮件中的链接或回复特定的验证码来证明对域名的控制权。
Basic Auth 鉴权
对于一些没有提供鉴权手段但又要暴露在公网上的 http 服务(例如 loki)来说,“裸奔”总是令人内心不安的。还好 npm 提供了 basic auth 功能,要求接口访问者必须提供正确的账号密码。
这就是 access list 功能,我们可以添加一个。
保存,然后去proxy host 那里给对应的服务添加这条鉴权规则。
然后客户端在发起 http 请求时,要遵循以下规则:
- 客户端请求访问受保护的资源。
- 服务器返回 HTTP 状态码 401(未授权),并在响应头部的
WWW-Authenticate
字段中包含Basic
,表示使用基本认证。 - 客户端收到 401 后,会重新发送请求,但这次在请求头部中包含
Authorization
字段,其值为Basic
后接 base64 编码的用户名和密码组合,格式为username:password
。
例如,如果用户名是 user123
,密码是 pass456
,则经过 base64 编码后的字符串为 dXNlcjEyMzpwYXNzNDU2
。客户端请求头部的 Authorization
字段将是 Basic dXNlcjEyMzpwYXNzNDU2
。
当然,许多使用场景都会提供现成的 basic auth 功能,你只需要输入账号和密码就可以了。
注意,由于 basic auth 会明文传输你的账号和密码,所以必须配合 https 使用。否则就像装了防盗门但是总把钥匙挂在门上一样。
由于鉴权是通过 npm 实现的,我们不能允许有绕过 npm 的机会,所以记得关掉受保护容器的端口映射,强制所有流量都走 npm。否则就像装了防盗门但是旁边又开着窗户一样。