header image

内网穿透之frp

2018-06-23更新:

由于新版(0.18.0开始)和之前的版本不兼容,因此针对新版做了一些补充。补充的内容基于0.20.0版本。

之前讲过ngrok,但是那个需要自己编译,也已经很久很没有更新了(作者开发了ngrok 2,但是变成私有软件了)。所以这次我们换一个大概是中国人编写的frp,作者提供了详细的中文文档,上手十分简单。

frp的GitHub页面,我们可以在Releases页面下载到编译好的版本。提供各种架构的版本:darwin是macOS,linuxwindows就不用多说了,386就是32位平台,amd64就是64位平台,其他还有armmips等多种平台。如果没有还可以自己编译,但不在本文涉及范围。

下载解包之后,会得到几个文件,frps就是服务器端程序,frpc就是客户端程序,后缀ini的就是对应的配置文件,full的就是完整的配置文件。

服务器端配置

基本配置

frpsfrps.ini上传到服务器,让后放到某个目录下(我放到了/opt/frps/目录下)。然后修改frps.ini文件(你也可以先修改了再上传)。

基本上只要一条设置就够了(每条选项一行,=之前是选项名,之后是值,[]内是选项块的名字):

[common]
bind_port = 7000

bind_port就是绑定的端口,设置一个没有被占用的就行了。值为端口号的整数。
如果服务器有多个IP地址,我们还可以设置bind_addr来绑定特定的IP地址。值为点分十进制的IPv4地址(如1.2.3.4)。

2018-06-23更新:新版增加了IPv6支持,因此值还可以设置为IPv6地址(必须用方括号[],如[2400::1:23:456:7890])。

PS:由于Go语言的特点,0.0.0.0[::]是一样的效果,会同时监听IPv4和IPv6地址,但在用netstat里只能看到IPv6被监听,实际上是通过了IPv4-Mapped IPv6 Address同时兼听了IPv4地址的。

如果要开启kcp,我们还可以设置kcp_bind_port选项。值为端口号。
为了增加安全性,我们可以加上密码privilege_token选项。值为字符串。

2018-06-23更新:从0.17.0版开始,此选项不再支持,请改为token

其他更多选项及相关说明请参见frps_full.ini文件。

然后我们只要运行如下命令就可以了:

/opt/frps/frps -c /opt/frps/frps.ini

开机自动运行

当然,我们要让它开机自动运行,如果系统使用的是systemd来进行初始化,我们只需要编写一个service文件(保存为/lib/systemd/system/frps.service)就行了:

[Unit]
Description=frp server
After=network.target

[Service]
Type=simple
ExecStart=/opt/frps/frps -c /opt/frps/frps.ini
Restart=on-failure

[Install]
WantedBy=multi-user.target

然后照例启用和启动服务就行(逐条执行):

sudo systemctl enable frps
sudo systemctl start frps
sudo systemctl status frps

上面最后一条命令是查看服务状态的,如果是active就是成功运行了。

客户端配置

服务器端配置很简单,客户端配置就要复杂一点了。客户端里的配置文件一样是上面介绍的格式,下面我们以[]包含的不同块来分开介绍。注意,所有选项都在一个配置文件里。

如果是运行在NAS上的,同样上传到NAS之后使用(我上传到了/opt/frpc/目录)。

[common]

如同名字所说,这里都是一些公共配置,基本上如下:

[common]
server_addr = 1.2.3.4
server_port = 7000
admin_addr = 127.0.0.1
admin_port = 7400

server_addr就是我们的服务器IP地址,你也可以用域名,但是不推荐。
server_port就是我们在上面bind_port设置的端口号。
admin_addradmin_port是为了能够热加载配置而设置的,监听本机(127.0.0.1)的7400端口。
如果启用了密码,设置privilege_token和上面服务器设置的一样就行了。

2018-06-23更新:从0.17.0版开始,此选项不再支持,请改为token

如果上面设置了kcp_bind_port,我们还可以用protocol = kcp来启用kcp协议。
其他更多选项及相关说明请参见frps_full.ini文件。

公共设置就这些了,基本上这样就能用了。下面我们来设置具体的端口映射。

最基本的映射

基本的映射只要设置一个唯一的块名,然后下面做对应的设置就行了。比如我们设置SSH访问:

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 23333

[ssh]就是一个名称,我们自己设置就行了,用[]包上即可,下面的选项都是对应在这个块下的。一个ini可以包含多个设置。
type就是类型,大多数服务都是tcp的。除此之外还支持udphttp服务(当然,http也是tcp的一种,有这个只是为了方便虚拟主机的设置,详见GitHub上的说明),还有一个stcp我们下面会提到。
local_ip就是我们想转发到外网的本地主机的IP,127.0.0.1代表的就是本机。
local_port就是服务的端口了,22是ssh的默认端口。
remote_port就是我们想在服务器上打开的端口,比如我这里设置23333
如果需要加密,我们可以在每个映射下设置use_encryption = true
如果需要压缩,我们可以设置use_compression = true
其他更多选项及相关说明请参见frps_full.ini文件。

这样,当我们启动客户端之后,就可以通过访问服务器的23333端口来访问实际运行在本地主机的22端口上的SSH服务了。下面是一个Transmission远程访问的配置示例:

[transmission]
type = tcp
local_ip = 127.0.0.1
local_port = 9091
remote_port = 9091

当然,有时我们觉得这样直接把服务暴露给公网太不安全,那怎么办呢,上面提到过的stcp就是一个解决方案。

安全的映射

安全的映射我们需要两个客户端,一个运行在提供服务的主机上(简称A),一个运行在访问服务的主机上(简称B),通过服务器中转访问,服务器并不直接开放端口。

在提供服务的主机(A)上设置

我们还是以ssh服务为例:

[secret_ssh]
type = stcp
local_ip = 127.0.0.1
local_port = 22
sk = abcdef

前三项配置和前面的基本一样,就是type设置成了stcp。但最后我们不再设置remote_port,而是设置了一个sk,这是安全访问的密码,只有密码一致才能访问。

运行客户端之后,我们就可以通过另一个客户端来访问了。

在访问服务的主机(B)上设置

我们在另一台主机(B)上同样设置frpc.ini[common]部分是一样的,但是我们设置的并不是为了提供服务,而是访问服务,所以有所不同

[common]
server_addr = 1.2.3.4
server_port = 7000

[secret_ssh_vistor]
type = stcp
role = vistor
server_name = secret_ssh
sk = abcdef
bind_addr = 127.0.0.1
bind_port = 23333

type一样是stcp
多了个role = vistor表示我们是访客而不是提供服务的(吐个槽,作者这里错成vistor了,怎么想都是visitor才对嘛)。
2018-06-23更新:作者已经修正为了,请使用visitor作为正确的值。
然后server_name要和我们上面在主机A上设置的[]内的名字一样。
sk也要和上面一样。
bind_addr表示绑定在本机(B)上的IP地址,你可以绑定0.0.0.0或者具体的IP地址来再向局域网内提供访问。
bing_port就是绑定本机的端口了。

运行客户端之后,我们就可以访问本机(B)的23333端口,来访问主机A上的22端口的SSH服务了。

点对点映射

2018-06-23新增

新版增加了点对点映射,设置方法和安全映射差不多,仅type的值设置为xtcp

点对点映射时,连接不通过服务器,而是直接在两个主机(A和B)之间建立,但并不能支持所有网络环境,如果不能正常工作,还是需要使用安全映射。

自动运行客户端

我们可以先运行一下看看有没有什么错误。

/opt/frpc/frpc -c /opt/frpc/frpc.ini

因为我是运行在NAS上的,所以还是要自动运行。设置起来和服务器基本是一摸一样的,只不过把frps改成了frpc

如果系统使用的是systemd来进行初始化,我们只需要编写一个service文件(保存为/lib/systemd/system/frpc.service)就行了:

[Unit]
Description=frp client
After=network.target

[Service]
Type=simple
ExecStart=/opt/frpc/frpc -c /opt/frpc/frpc.ini
ExecReload=/opt/frpc/frpc -c /opt/frpc/frpc.ini --reload
Restart=on-failure

[Install]
WantedBy=multi-user.target

多了一个ExecReload选项是为了可以热加载配置,因为我们主要都是修改客户端配置,这样可以方便修改之后立即生效。

然后照例启用和启动服务就行(逐条执行):

sudo systemctl enable frpc
sudo systemctl start frpc
sudo systemctl status frpc

上面最后一条命令是查看服务状态的,如果是active就是成功运行了。如果我们需要热加载配置,只需要用命令sudo systemctl reload frpc就可以了。

补充

如果是运行在Windows上的,网上有很多资料如何自动运行,就不细说了。如果只是临时使用(比如是作为上面提到的安全映射里的主机B),那么只需要在命令行执行代码就行了(打开命令行的方式:在文件管理器的frpc.exe所在文件夹里按住Shift点右键,然后选择在此处打开命令行提示符/Powershell窗口)。

frpc -c frpc.ini

2018-06-23更新:新版可以直接用命令行参数启动,不需要建立ini文件。如:

frpc tcp -s "127.0.0.1:7000" -n ssh -l 22 -r 6000

具体参数含义和用法请使用frpc -h命令查看。

最后

感谢frp作者fatedier的工作,大家如果有问题咨询作者或者想捐款给作者,可以在GitHub上的说明的最后找到相关的联系方式。

Ngxin HTTPS代理Transmission Web UI

在上一篇文章中,我设置了用ngrok转发端口来实现对内网NAS的访问,但是如果转发用的是国内的VPS,会由于域名没有备案而被关停。解决方法一个是搞个备案过的域名。现在阿里云等云服务都有便宜的域名,所以这不是大问题。还有个方法就是绕开HTTP访问,比如我这里用的HTTPS。

方法简述

就是在NAS上建立一个Nginx服务,然后启用HTTPS(这部分就不详细介绍了,网上文章很多,我配置的是8443端口以免常用端口被检测),然后通过代理将访问路径/transmission/的请求转发到NAS的9091端口上。

初步方案

一开始就直接做了个转发,在Nginx的网站配置如下:

    location /transmission {
        proxy_pass: http://127.0.0.1:9091;
    }

但是这样却出现了问题,由于缺少Transmission的一个特有的请求头会造成409错误,而且不知为啥有静态文件无法获取。

改进方案

有这种需求的肯定不止我一个,所以马上就放狗搜了一下,还真找到了一篇文章,把上面的配置改成如下:

    location ^~ /transmission {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass_header X-Transmission-Session-Id;
        add_header   Front-End-Https   on;

        location /transmission/rpc {
            proxy_pass http://127.0.0.1:9091;
        }

        location /transmission/web/ {
            proxy_pass http://127.0.0.1:9091;
        }

        location /transmission/upload {
            proxy_pass http://127.0.0.1:9091;
        }

        location /transmission/web/style/ {
            alias /usr/share/transmission/web/style/;
        }

        location /transmission/web/javascript/ {
            alias /usr/share/transmission/web/javascript/;
        }

        location /transmission/web/images/ {
            alias /usr/share/transmission/web/images/;
        }

        location /transmission/ {
            return 301 https://$server_name/transmission/web;
        }
}

这样就行了,今后只要访问VPS的/transmission/(如http://example.com:8443/transmission)就和直接访问Transmission的9091端口一样了。

更进一步

但是这样还是能直接访问8443端口,能不能让这个端口不能直接访问呢,答案是可以。

我们需要用到cookie,cookie是什么就不多解释了。我们只要在Nginx的网站配置中最初的location之前添加如下内容就可以了。

    if ($http_cookie !~ "key=abcdef") {
        reutrn 444;
    }

上面配置的意思是,访问网站时的请求要包含一个名称是key、值是abcdef的cookie(这两个参数可以自己设定),否则就抛弃请求。444是一个特殊的错误码,Nginx遇到这个错误码会直接抛弃请求(就好像没有服务似的),而不会真正地返回错误。

这样,我们只要用一些特殊手段(Firefox或者Chrome的开发者工具或者一些附加组件/扩展程序)给网站添加一个名称是key、值是abcdef的cookie,就能访问了,而没有这个cookie时访问就好像服务不存在一样,彻底断绝检测到未备案网站的可能性。

内网穿透之ngrok [2017-10-13 UPDATED]

2017年10月13日更新:转发其他IP的端口

鉴于IPv4地址资源的枯竭,而电信公司又不部署IPv6,越来越多的宽带用户难以获得公网IP,导致NAS、私有云等应用难以实现,所以我们需要一种有效的能穿透NAT的工具。

省事的方法

目前有很多提供NAT穿透服务的网站,比如老牌的花生壳,以及NAT123等等。但缺点显而易见——免费的服务限制太多(限端口、限带宽),收费的又太贵。

还有一些基于这次提到ngrok提供服务的,虽然收费便宜点,但还是不能随心所欲。

ngrok介绍

ngrok是国外一个开发者基于GO语言写的服务,并且通过ngrok.com提供服务,不过1.0版本的软件是开源的(虽然停止了更新)。上面说到的基于ngrok和我们这次要架设的服务,都是基于1.0版本的。

准备工作

首先你要有自己的服务器(运行Linux的VPS),用来转发请求,然后你需要穿透NAT的设备要能够运行第三方的程序。最后你需要一台Linux的电脑(当然你在服务器上执行操作也可以,Windows 10的WSL也是不错的选择)。下面的代码会说明是在服务器本地机器还是需要穿透NAT的设备上执行。

所有的Linux操作都是基于Ubuntu或Debian,其他系统的操作大同小异,如果不会请留言。

你需要给你的服务器绑定一个域名,对于大多数人这不是问题了。如果你要用到子域名的话,再把子域名的泛解析绑定。

比如绑定了example.com的域名,如果要用子域名的话就要添加*的泛解析,DNS服务商都会有说明的,请详细阅读。

获取源代码

我们首先要在本地机器上部署编译环境:

sudo apt-get install build-essential golang mercurial git

然后获取源代码:

git clone https://github.com/inconshreveable/ngrok.git

这样会把源代码克隆岛ngrok目录下,我们切换一下目录:

cd ngrok

创建证书

因为ngrok的隧道是通过TLS传输数据的,我们可以用默认的证书,但这样谁都可以连接你的服务器了,所以我们要自己创建证书。

以下代码在本机一条一条地执行。第一条中的example.com记得改成你自己的域名。

NGROK_DOMAIN="example.com"
openssl genrsa -out base.key 2048
openssl req -new -x509 -nodes -key base.key -days 10000 -subj "/CN=$NGROK_DOMAIN" -out base.pem
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=$NGROK_DOMAIN" -out server.csr
openssl x509 -req -in server.csr -CA base.pem -CAkey base.key -CAcreateserial -days 10000 -out server.crt

我们需要的是server.keyserver.crtbase.pem三个文件。我们要放到对应的目录里:

cp server.key assets/server/tls/snakeoil.key
cp server.crt assets/server/tls/snakeoil.crt
cp base.pem assets/client/tls/ngrokroot.crt

修改代码

因为ngrok会默认监听IPv6端口,考虑到国内的网络环境,我们需要修改一下代码,否则将无法使用。主要修改两个地方,一个是src/ngrok/server/tunnel.go的109行net.ListenTCP后面的"tcp"改成"tcp4",另一处是src/ngrok/conn/conn.go的57行的net.Listen后的"tcp"同样改成"tcp4"

编译

编译很简单,我们首先通过GOOSGOARCH两个变量指定编译的目标系统和目标架构,目标系统有darwin(即OS X)、freebsdlinux,目标架构有386(32位系统)、amd64(64位系统)和arm(ARM嵌入式系统)。然后再通过serverclientrelease-serverrelease-client来指定编译的是客户端还是服务器端。有没有release的区别是,包含release的编译结果会把assets目录下的内容包括进去,从而可以独立执行。如果你今后还要更换证书,建议编译不包含release的版本。

以下代码都在本机执行。

编译服务器:

GOOS=linux GOARCH=amd64 make release-server

得到的服务器文件是bin/ngrokd

编译客户端,因为我的NAS是ARM架构的所以如下,各位请按自己的需求编译:

GOOS=linux GOARCH=arm make release-client

得到的客户端文件是bin/linux_arm/ngrok

不同系统或架构编译得到的文件位置可能不同,但都在bin目录下,并且文件名服务器是ngrokd客户端是ngrok,下面请根据实际情况操作。

配置服务器

首先把文件复制到服务器上。请选择你熟悉的手段。我是直接scp上传:

scp bin/ngrokd example.com:~

然后就要到服务器上操作了,通过SSH连接到服务器上。

首先把文件放到/opt/ngrokd目录下(这步可根据你的习惯来):

sudo mkdir /opt/ngrokd
sudo cp ngrokd /opt/ngrokd/

然后我们就要建立服务了。我使用的是Ubuntu 16.04 LTS,所以用systemd很简单,把如下内容保存到/lib/systemd/system/ngrokd.service这个文件(自己创建):

[Unit]
Description=ngrok server
After=network.target

[Service]
Type=simple
ExecStart=/opt/ngrokd/ngrokd -domain example.com -httpAddr "" -httpsAddr "" -tunnelAddr ":4443" -log "/var/log/ngrokd.log"
Restart=on-failure

[Install]
WantedBy=multi-user.target

其中ExecStart这一项后面就是我们ngrokd执行命令了。参数-domain表示服务器域名,请改成你自己的域名-httpAddr表示默认监听的HTTP端口,-httpsAddr表示默认监听的HTTPS端口,因为我用不到所以都设置成空字符串""来关闭监听,如果需要打开的话记得格式是:12345(冒号+端口号)这样的;-tunnelAddr表示服务器监听客户端连接的隧道端口号,格式和前面一样;-log表示日志文件位置;还有个-log-level用来控制日志记录的事件级别,选项有DEBUGINFOWARNINGERROR

如果编译的是不带release的版本,还可以通过-tlsCrt-tlsKey选项来指定证书文件的位置。

设置好之后,我们就要启用服务了,很简单:

sudo systemctl enable ngrokd

其中enable是命令表示启用服务(开机会自动启动),其他命令还有disable(禁用服务)、start(启动服务)、restart(重启服务)、stop(停止服务)、status(查看服务状态)等。所以我们就启动服务:

sudo systemctl start ngrokd

然后通过status查看服务,如果是active (runing)那么就OK了。

配置设备

先把从本机把客户端复制到我们的设备(这里我的是NAS)上。请选择你熟悉的手段。我还是一样使用scp

scp bin/linux_arm/ngrok 192.168.11.50:~

接下来我们就要到设备上操作了,同样通过SSH连接到设备上。

首先还是一样的把程序复制到/opt/ngrok目录下(这步同样可根据你的习惯来):

sudo mkdir /opt/ngrok
sudo cp ngrok /opt/ngrok/

然后还是一样的建立服务,因为我的NAS运行的是Debian,还是用systemd,操作和服务器上几乎一样。把如下内容保存到/lib/systemd/system/ngrok.service这个文件:

[Unit]
Description=ngrok client
After=network.target

[Service]
Type=simple
ExecStart=/opt/ngrok/ngrok -config "/opt/ngrok/ngrok.yml" -log "/var/log/ngrok.log" start transmission ssh
Restart=on-failure

[Install]
WantedBy=multi-user.target

同样还是看ExecStart后面的命令,这里加载了一个配置文件/opt/ngrok/ngrok.yml(之后我们会建立),-log记录日志和服务器端是类似的,最后用start transmission ssh启动了配置文件中的transmissonssh两个隧道。

那么配置文件是什么样呢?我们把如下内容保存到/opt/ngrok/ngrok.yml中:

server_addr: example.com:4443
trust_host_root_certs: false
tunnels:
  transmission:
    remote_port: 9091
    proto:
      tcp: 9091
  ssh:
    remote_port: 23333
    proto:
      tcp: 22

server_addr就是我们在服务器配置的域名和端口。

tunnels下就是隧道设置。

每一级前面多两个空格缩进的表示是上一级的子项。比如transmissionssh就是tunnels的子项,remote_portproto又是他们的子项,以此类推。

transmissionssh就是隧道的名称,每个对应一个隧道。上面的启动命令中start后面的就是这个。

remote_port表示远程对应的端口。

proto表示协议,支持的有tcphttphttps。因为我没有开启服务器上的http和https,所以我都是用的tcp(TCP也可以支持HTTP或HTTPS,之所以有独立的http和https选项是为了简化设置,直接对应服务器上的相应设置)。

tcp就是协议,后面跟着的是端口号。

所以上面的设置文件就表示,将服务器的9091端口的连接转发到设备的9091端口上,服务器的23333端口的连接转发到设备的22端口上。这样我们就可以通过访问服务器的相应端口来访问设备上的服务了。

2017年10月13日更新

还可以转发其他IP的端口,方法就是在proto下的tcp(或httphttps)后的端口号写成IP地址:端口号的格式(中间是英文冒号)。

如:

      tcp: 192.168.11.1:80

最后别忘了启用服务:

sudo systemctl enable ngrok
sudo systemctl start ngrok

最后

这样,我们就将设备上的9091端口和22端口的对应到了服务器的9091和23333端口上了。比如我要访问设备的SSH,就可以这样:

ssh -p23333 example.com

这样有个缺点,就是只能建立到运行ngrok的设备的隧道,如果你的设备不能运行ngrok,那就有点麻烦了。 2017年10月13日更新:其实是可以转发其他IP的端口的,详见上面的配置设备一节。

如果不能运行ngrok的设备提供的是HTTP或者HTTPS访问,还有一种变通的方法是在运行ngrok的设备(比如树莓派)上通过nginx的建立代理,代理到不能运行ngrok的设备上。

虽然有局限,但我们已经成功地穿透内网了。只希望IPv6快点到来,让我们不必再处理这些麻烦事。

删除Firefox过期的HPKP信息

前不久手贱,试了试HPKP,结果不知为突然出问题了。然后Firefox就彻底拒绝了我的网站。

HPKP,即The Public Key Pinning Extension for HTTP,是将证书的发行者的验证信息添加到一个HTTP头信息。用户代理在访问时,不光验证证书的有效性,还验证证书的发行者,从而避免中间人攻击。

出问题的原因很简单,证书的发行者不知为啥和原来有所不同(我用的是上一篇介绍的Let’s Encrypt,证书是自动颁发的),而HPKP又没有修改,所以Firefox就拒绝了。

但是不想折腾了,于是就删除了HPKP头信息,结果由于之前设置的过期时间太长,造成Firefox还是拒绝。很明显是Firefox缓存了相关信息。放狗找了一下,找到了一篇文章(见引用来源)。

只要把Firefox用户档案下面的SiteSecurityServiceState.txt删除就行了。

Linux是在用户的家目录下面,也就是~/.mozilla/firefox/xxxxxxxx.default/SiteSecurityServiceState.txt,其中xxxxxxxx是一串随机字符。

而Windows就在%APPDATA%\Mozilla\Firefox\Profiles\xxxxxxxx.default\SiteSecurityServiceState.txt%APPDATA%是一个系统变量,代表C:\Users\[username]\AppData\Roaming这个目录([username]是你的用户名)。

删除之前关闭Firefox,删除后再打开就正常了。

在Ubuntu上获取Let’s Encrypt免费证书 [2017-02-20 UPDATED]

2017年02月20日更新:第4节

Let’s Encrypt是电子前哨基金会(EFF)发布的免费SSL证书服务,受到微软、谷歌等大佬的支持,但是配置比较复杂(主要是证书有效事件较短,需要重新签发,虽然签发的过程都是在自己的机器上通过命令行实现),之前研究过一下,最终还是放弃了。

但最最近由消息称沃通的CA将有可能被Mozilla拒绝,所以还是重新弄起来吧。下面说一下步骤。

安装Certbot

Certbot是EFF开发的一个简单的工具,比起我之前研究的时候弄的ACME要简单得多,访问https://certbot.eff.org/ 之后,选择你的服务器(webserver)和操作系统(Operating System),就会给出简单的步骤了,我们还是一步一步来吧。

我的服务器是Nginx,操作系统是Ubuntu 16.04。由于自带了软件包,安装只需要一行命令即可:

sudo apt-get install letsencrypt

如果是其他版本的Ubuntu,只需要下载一个脚本就行了(下面的命令在需要下载到的目录里执行):

wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
./certbot-auto

签发证书

很简单,直接运行letsencrypt命令即可(使用其他版本的Ubuntu的把letsencrypt换成上面下载的certbot-auto,注意执行脚本需要完整路径/path/to/certbot-auto,在脚本所在目录下是就是./certbot-auto)。

sudo letsencrypt certonly --webroot -w /var/www -d example.com -d www.example.com -w /var/www/sub -d sub.example.com

第一次运行时会问你一些你的信息,以后就不会再询问了。

解释一下,certonly是它的子命令,表示只颁发证书。--webroot是它的插件,用于自动验证域名。-w是指定网站的目录,-d是指定目录对应的域名。一个-w后可以跟着多个-d就对应多个域名。可以用多个-w设置多个目录对应不同的域名。这些域名都在一个证书里。

需要注意的是,域名下的\.well-know\acme-challenge\目录下的文件必须要可以访问。这个是Let’s Encrypt用于验证域名所有权的。它会在上面-w参数设置的目录下建立临时文件,然后通过HTTP访问,比如上面的设置就会新建诸如/var/www/.well-known/acme-chanllenge/xxxxxxx(xxxxxxx是随机字符),然后通过http://example.com/.well-known/acme-chanllenge/xxxxxxx来验证。如果你的网站是纯HTTPS的,你可以用把这个地址重定向到https://example.com/.well-known/acme-chanllenge/xxxxxxx,可以成功通过验证。

看到Congratulations!就代表成功了,下面我们就可以配置Nginx了。

配置HTTP服务器

证书所在的地方是/etc/letsencrypt/下,archive里面是所有证书的存档,keys里面是所有证书,不过我们不用管,我们需要的证书在live下面,对应网站域名的目录下面就是了。一共有四个文件。

  • privkey.pem 这是私匙,对应Nginx的ssl_certificate_key选项,或者Apache2的SSLCertificateKeyFile选项。

  • cert.pem 服务器证书,这个只有Apache2低于2.4.8版本需要,对应SSLCertificateFile选项。

  • chain.pem 除服务器证书之外的所有证书,对于1.3.7版以上的Nginx对应ssl_trusted_certificate选项,对于低于2.4.8的Apache2对应SSLCertificateChainFile选项。

  • fullchain.pem 包括上面的服务器证书和其他证书,Nginx对应ssl_certificate选项,2.4.8版以上的Apache2对应SSLCertificateFile

所以对于我用的Nginx来说,只需要privkey.pemfullchain.pem这两个就够了。Apache2的话参考设置HTTPS的文章,对应设置上面提到的文件和选项就行了。

在Nginx的server下设置如下响应的ssl选项就行了,如:

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    root /var/www;
    index index.html;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        try_files $uri $uri/ =404;
    }
}

如果你是第一次设置HTTPS,记得在listen选项那里要监听443端口,并在后面加上ssl。如果要启用HTTP/2,请参考这篇文章

最好在原来的80端口上设置好跳转:

server {
    listen 80;
    server_name example.com www.example.com;
    rewrite ^(.*)$ https://example.com$1 permanent;
}

设置好了别忘了重启Nginx。

自动更新证书

更新证书很简单,直接如下命令就可以了:

sudo letsencrypt renew

可以用--dry-run选项来模拟更新证书,看看会不会出错。由于Ubuntu自带的版本有点旧,所以会出错……(certbot-auto的是最新的所以不会有问题)。这时候就需要加上--force-renew选项来强制更新。第一次运行可能还要你同意服务条款,所以根据提示加上--agree-tos(以后就不需要了)。

在正式更新的时候,要使证书生效还需要重启Nginx。如果是certbot-auto我们可以用--pre-hook--post-hook来设置更新前后的命令,用来停止和启动Nginx。

sudo /path/to/certbot-auto renew --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx"

但是Ubuntu自带的版本有点旧,所以就只好自己写脚本了。

#!/bin/sh
systemctl stop nginx
letsencrypt renew --force-renew
systemctl start nginx

2017年2月20日更新

上述命令中不能使用systemctl stop nginx来停止nginx,因为更新证书时同样需要验证,如果停止了服务就无法验证了。我们只需要在更新后重启nginx就行了。所以:

如果使用新版certbot-auto我们只需要:

sudo /path/to/certbot-auto renew --post-hook "systemctl start nginx"

自己编写脚本的话改成这样:

#!/bin/sh
letsencrypt renew
systemctl restart nginx

我们还可以在这个脚本中将输出重定向来生成日志文件,以便以后检查。

保存文件,这里我们保存到/opt/scripts/letsencrypt_renew

我们可以用crond或者systemd来自动更新证书,这里就简单点,用crond吧。用sudo crontab -e命令编辑root用户的crontab。在最后面新插入行并添加如下内容:

0 */12 * * * sh /opt/scripts/letsencrypt_renew

上面的意思是,每12小时的第0分执行脚本一次。因为Let’s Encrypt的推荐每12小时更新一次。更新操作并不一定会真的更新证书,只有在快到期的时候才会更新。

如果不想修改root的crontab,可以在/etc/cron.d下新建文件写入如下内容:

0 */12 * * * root sh /opt/scripts/letsencrypt_renew

比起crontab,就是在要执行的命令前面多了一个表示用户的root

更新证书是检测/etc/letsencrypt/renewal下的配置文件进行的,所以即使不管颁发多少个证书,上面的更新脚本都不用修改。

最后

如果要删除证书的话,把/etc/letsencrypt/下的archiveliverenewal中对应域名的文件或目录删除就可以了。

此外,还可以用-m选项来设置Email地址。还有更多的功能就去看看官方的文档吧:https://certbot.eff.org/docs/