nodejs+express+certbot实现网站https访问

什么是certbot?

    在开始这个教程前我们必须要了解的就是什么事certbot。

    certbot是Let's Encrypt为了用户方便生成https证书而开发的自动化程序,通过certbot我们可以生成四个文件分别用于为https加密所用。


安装certbot

    首先最保险的方法是你需要上Certbot的官网来选择您的服务器对应的操作系统版本:

snipaste_20170601_192431.png

我这里使用的语言是nodejs,操作系统是CentOS7,接着下面会出现教程教你如何安装certbot:

snipaste_20170601_192657.png

这里官网提供的方法是使用yum来安装Certbot

$ sudo yum install certbot

但是在CentOS7上,使用yum安装的certbot很可能会出现所安装的certbot和pyOpenSSL版本不对的情况(什么都别说,我就遇到了,可是把我折腾苦了),那么什么是版本不对呢?出现以下错误就说明你的yum安装的pyOpenSSL不对:

Traceback (most recent call last):
  File "/usr/bin/certbot", line 9, in <module>
    load_entry_point('certbot==0.13.0', 'console_scripts', 'certbot')()
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 378, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 2566, in load_entry_point
    return ep.load()
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 2260, in load    entry = __import__(self.module_name, globals(),globals(), ['__name__'])
  File "/usr/lib/python2.7/site-packages/certbot/main.py", line 17, in <module>
    from certbot import client  File "/usr/lib/python2.7/site-packages/certbot/client.py", line 10, in <module>
    from acme import client as acme_client
  File "/usr/lib/python2.7/site-packages/acme/client.py", line 31, in <module>
    requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3()  # type: ignore  File "/usr/lib/python2.7/site-packages/requests/packages/urllib3/contrib/pyopenssl.py", line 112, in inject_into_urllib3
    _validate_dependencies_met()
  File "/usr/lib/python2.7/site-packages/requests/packages/urllib3/contrib/pyopenssl.py", line 147, in _v
alidate_dependencies_met
    raise ImportError("'pyOpenSSL' module missing required functionality. "ImportError: 'pyOpenSSL' module missing required functionality. Try upgrading to v0.14 or newer.


使用pip安装绕过yum安装的版本不对问题



  1. 错误提示使用v0.14或者更新版本,先看看pip中的版本: pip search certbot,显示版本为0.14.0;

  2. 卸载yum安装的certbot和pyOpenSSL: yum remove certbot pyOpenSSL;

  3. 升级pip(如果不是最新版本,pip会提示你升级后再使用): pip install –upgrade pip;

  4. pip安装certbot: pip install certbot。 碰到了Python.h或者pyconfig.h找不到的错误,于是安装Python的devel包: yum install -y python-devel;再次运行命令,提示找不到opensslv.h头文件,于是安装OpenSSL的devel包: yum install -y openssl-devel;再次运行pip install certbot命令,成功安装certbot;

  5. 测试certbot是否可用: certbot certificates。输出正常,说明pip安装了最新版的certbot,并且能正确运行。


生成https证书

前面安装完certbot之后就到了最重要的地方了,就是生成https证书。

首先运行下列命令

$ certbot certonly

会出现两种选择,我们选择第二种

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

接着这里我们需要输入自己的域名:www.mydomain.com (注意这里有一个空格) mydomain.com,我们这里绑定的是两个域名,一个是带www的,一个是没有www的,如果你只需要其中一个的话这里可以只写一个

Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'to cancel):

这里要求我们输入网站的根目录

Select the webroot for milleros.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel):

如果前面输入域名的时候您输入的是两个则这里会再次提示输入网站根目录:

Select the webroot for yourdomain:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /root/www/static(你前面输入过的网站根目录)
-------------------------------------------------------------------------------

如果根目录一致的话我们就选择2.


输入完根目录之后certbot会自动为我们生成证书,生成成功会有以下提示:

Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/xxx.ml/fullchain.pem. Your cert will
   expire on 2017-06-07. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

注意:这里我遇到过一个很坑爹的问题,就是输入了网站根目录之后certbot提示我生成证书时认证失败:

Failed authorization procedure. example.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://example.com/.well-known/acme-ch    <head><title>404 Not Found</title></head>
    <body bgcolor="white">    <center><h1>404 Not Found</h1></center>    <hr><center>", www.example.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://www.example.com/.well-known/acme-challenge/k
    <head><title>404 Not Found</title></head>
    <body bgcolor="white">    <center><h1>404 Not Found</h1></center>    <hr><center>"
    
    IMPORTANT NOTES:
     - If you lose your account credentials, you can recover through       e-mails sent to example@example.com.
     - The following errors were reported by the server:
    
       Domain: example.com
       Type:   unauthorized
       Detail: Invalid response from
       http://example.com/.well-known/acme-challenge/wGNv57IGJjHQ9wyzzALktpNaPzfnTtN3m7u3QuO4p40:       "<html>
       <head><title>404 Not Found</title></head>
       <body bgcolor="white">       <center><h1>404 Not Found</h1></center>       <hr><center>"
    
       Domain: www.example.com
       Type:   unauthorized
       Detail: Invalid response from
       http://www.example.com/.well-known/acme-challenge/kFJ0CSuKOdgcT2xmciB4GGNCcnUPoIbpQmA9jOII_Bk:       "<html>
       <head><title>404 Not Found</title></head>
       <body bgcolor="white">       <center><h1>404 Not Found</h1></center>       <hr><center>"
    
       To fix these errors, please make sure that your domain name was
       entered correctly and the DNS A record(s) for that domain
       contain(s) the right IP address.
     - Your account credentials have been saved in your Certbot
       configuration directory at /etc/letsencrypt. You should make a
       secure backup of this folder now. This configuration directory will
       also contain certificates and private keys obtained by Certbot so
       making regular backups of this folder is ideal.

这里我研究了很久,网上很多人说可能是网站根目录下生成的文件夹.well-known 权限不够,但是我去根目录下看了,连那个文件夹都没看到,所以确定肯定不是权限问题,于是我在分析了一下错误信息里面包含404 not found的错误信息,可能是路由的问题,于是我在重复certbot生成https证书的过程中到了输入根目录的时候在结尾对加了一级/public(我用来存放客户端代码的文件夹),发现果然可以用了,顺利生成了https证书;


创建https服务器

    经过刚刚的certbot操作,我们已经生成了如下四个文件:

cert.pem
chain.pem
fullchain.pem
privkey.pem

首先我们需要在原先的http服务器上做如下改动:

var express = require('express');
var fs = require('fs');
var http = require('http');
var https = require('https');

//引入刚刚生成的https文件
var privateKey  = fs.readFileSync('/path/to/privkey.pem', 'utf-8');
var certificate = fs.readFileSync('/path/to/cert.pem', 'utf-8');
var ca = fs.readFileSync('/path/to/fullchain.pem', 'utf-8');
var credentials = {key: privateKey, cert: certificate ,ca:ca};

//实例化
expressvar app = express();

//创建http以及https服务
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

//监听端口
httpServer.listen(8321);
console.log('http server start on port 8321');
httpsServer.listen(8322);
console.log('http server start on port 8322');
//默认的http协议端口是80端口,https端口是443端口。

大功告成

然后我们可以直接使用app来进行各种路由操作,现在就大功告成啦:


只有https访问

如果您不想要http访问,只想要https访问,只需要修改我们之前的两条规则:

//var httpServer = http.createServer(app); //之前的创建http服务器
var httpServer = http.createServer(function(req,res){
	res.writeHead(301, {'Location': 'https://milleros.com/'});
	res.end();
}); //将http请求重定向至https
var httpsServer = https.createServer(credentials, app);

上面这段代码的重定向存在一点问题,就是当我们访问的地址不是主页时,而且请求http时则会跳转回https的主页,这显然不是我们想要的结果,所以我们把原先的代码改成这样

var express = require('express');
var fs = require('fs');
var http = require('http');
var https = require('https');

//引入刚刚生成的https文件
var privateKey  = fs.readFileSync('/path/to/privkey.pem', 'utf-8');
var certificate = fs.readFileSync('/path/to/cert.pem', 'utf-8');
var ca = fs.readFileSync('/path/to/fullchain.pem', 'utf-8');
var credentials = {key: privateKey, cert: certificate ,ca:ca};

//实例化
expressvar app = express();

//创建http以及https服务
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

//监听端口
httpServer.listen(8321);
console.log('http server start on port 8321');
httpsServer.listen(8322);
console.log('http server start on port 8322');
//默认的http协议端口是80端口,https端口是443端口。

//新增一个路由来拦截请求
app.use(function(req,res,next){
    console.log(req.protocol);
    if(req.protocol != "https"){
	res.writeHead(301, {'Location': 'https://milleros.com' + req.path});
	res.end();
    }
    next();//保证请求不被阻塞
});

这样就可以保证,网站中的每一个http请求都可以被重定向至https了。


结语

    linux是一个很复杂的操作系统(至少我这么认为),在我们平时进行的操作中经常会遇到各种各样的问题,有些问题在网络上我们可以很轻易的找到解决问题的方法,但是往往很多时候我们遇到的问题是很特殊的,网上几乎很难找到解决的办法,这个时候我们不应该着急,而应该静下心来仔细捋一捋思路,找找可能会导致问题出现的地方。

    正是因为linux的复杂性,很多时候我们误操作会导致很多不可预料的问题,像这次我就是因为pyOpenSSL版本不对的问题在寻求很多方法未果之后选择了对系统自带的Python进行升级,正是因为我这一疯狂的想法从而让我误删除了site-packages,这一误操作直接导致的就是yum和pip无法使用,从而让我不得不重置了整个linux系统。

    虽然对linux的误操作很容易造成不可估量的错误,但我还是鼓励大家大胆尝试,毕竟只有不停地跌倒才会有更多爬起来的经验,才能让我们更加的强大!


This is Miller

其它相关热文