后台管理网站的时候希望能够加密数据,特别是密码、加密文章之类的数据需要加密传输。为了安全咱也赶赶新潮在 VPS 上部署了 SSL 加密服务,以后登录和管理 WordPress  以及 phpMyAdmin 的时候就可以使用 HTTPS 加密链接访问了。木有money,只好用 OpenSSL 自己签发(Self-Signed)个证书了。

至于部署加密链接的好处,可以参考之前转发的一篇文章 Google 加密链接,这里就不啰嗦了。下面直接记录部署过程。

0. 准备工作

在安装之前还是要声明一下我的前期工作,也就是目前这个 VPS 的状态。

这个 VPS 上部署了 LAMP + phpMyAdmin 来提供服务。当然 VPS 使用的是 Linode 的,这个应该没有关系。因为是 CentOS,下面的操作记录就把参考资料中提到的其它 Linux 发行版的命令行剔除了,如有需要可参考文后提供的参考资料。

1. 安装 openssl

对于前面描述的需要,就不值当去购买商业 SSL 证书了,GoDaddy 的 SSL 证书标准版也要 $69.99/年呢,虽然第一年只要 $12.99(如果你不小心点了这个链接,注意看打开页面导航菜单下面的提示信息 :D)。所以下面安装的是自己用 openssl 生成的证书(自己签发,Self-Signed)。

以 root 身份登录 VPS,执行下述命令行,

yum install openssl

也许收到的提示是 Nothing to do,那说明早就安装好了。

2. 使用 Apache 自己签发一个 SSL 证书

  1. 首先为 Apache 安装 mod_ssl 模块,
    yum install mod_ssl
  2. 创建保存证书的目录,
    mkdir /etc/httpd/ssl
  3. 为了生成 2048 bit 的 SSL 证书,我们先用下面的命令设定加密等级:
    openssl genrsa -des3 -out mydomain.key 2048

    -des3 是我们选择的加密方式。-out 指定输入的文件为 mydomain.key。输出类似于:

    # openssl genrsa -des3 -out cnzhx.net.key 2048
    Generating RSA private key, 2048 bit long modulus
    ..............................................................+++
    ...............................................................................................................+++
    e is 65537 (0x10001)
    Enter pass phrase for cnzhx.net.key:
    Verifying - Enter pass phrase for cnzhx.net.key:
    

    这里我是用了自己的域名作为文件名。后面两行是提示让给这个 key 设置一个访问密码。不用太复杂,等会儿就要用到,也许也就用这一次。但是不输入密码就无法进行下去。

  4. 现在我们将密码去掉,不然每次使用密钥(key)都要输入密码也挺麻烦的:
    # cp cnzhx.net.key cnzhx.net.key.pass
    # openssl rsa -in cnzhx.net.key.pass -out cnzhx.net.key

    这里需要输入刚才设置的密码来确认。现在,有密码的那个已经被我们重命名为 cnzhx.net.key.pass 了,新的这个 cnzhx.net.key 是没有密码的。

  5. 然后使用下面的指令生成证书签发请求(certificate signing request (CSR))和证书密钥(certificate key),
    openssl req -new -nodes -key cnzhx.net.key -out cnzhx.net.csr

    运行上述命令后会被询问几个配置参数,如下所示。比如组织所在地、名称,服务器名称等。地址等可以留空。如果要留空的话可以直接输入一个 .(英文圆点)。因为这个证书将用于通用的 SSL 服务,这里使用 FQDN(fully qualified domain name,完全认证域名,比如我的 cnzhx.net )作为 “Common Name” 条目(注意下面空行前的最后两行)。例如,([ ] 中的内容是程序根据服务器配置自动填充的,可能有所不同,没关系)

    # openssl req -new -nodes -key cnzhx.net.key -out cnzhx.net.csr
    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) [XX]:CN
    State or Province Name (full name) []:CnZhx
    Locality Name (eg, city) [Default City]:CnZhx
    Organization Name (eg, company) [Default Company Ltd]:CnZhx
    Organizational Unit Name (eg, section) []:CnZhx
    Common Name (eg, your name or your server's hostname) []:cnzhx.net
    Email Address []:[email protected]
    
    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:
    An optional company name []:

    上面最后两个是附加信息,也可以不填,直接回车即可。
    需要特别注意的是:Common Name 这里输入的是 cnzhx.net,则这个证书只对 cnzhx.net 有效,而对 www.cnzhx.net 无效。实际上 www.cnzhx.net 应该归到 *.cnzhx.net 里面,重新针对 *.cnzhx.net 生成一个证书即可。即,Common Name 那里填入 *.cnzhx.net(支持通配符的)。如果需要的话,也可以针对所有的子域名生成一个“通配符”的:

    openssl req -new -nodes -key cnzhx.net.key -out sub.cnzhx.net.csr

    只不过,针对这个 sub.cnzhx.net.csr,请记得在 Common Name 那里输入 *.cnzhx.net(当然记得换成自己的域名)。我这里是懒得麻烦,直接用刚才那个 cnzhx.net.key 来签发自己的所有证书了。也可以针对不同的应用设置不同的 key,只是对我来说没有必要。我仅仅是为了加密传输中的数据,穿着衣服就行了,不需要太多伪装。
    不过,既然是自己签发的证书,这个其实也没所谓啦,反正是自个儿说了算,对 cnzhx.net 和所有子域名都使用同一个证书也没什么大不了的

  6. 下面使用刚才产生的证书签发请求(CSR)来生成一个证书,还是以我的根域名 cnzhx.net 为例。我这里指令和输出为:
    # openssl x509 -req -days 3650 -in cnzhx.net.csr -signkey cnzhx.net.key -out cnzhx.net.crt
    Signature ok
    subject=/C=CN/ST=CnZhx/L=CnZhx/O=CnZhx/OU=CnZhx/CN=cnzhx.net/[email protected]
    Getting Private key

    上面的命令中,

    1. x509 表示 X.509 Certificate Data Management;
    2. 使用 -days 来指定有效期,我写了 3650 天,可以随意更改,反正长些比较省事儿;
    3. 详细参数说明请参考 OpenSSL 文档
  7. 现在我们已经得到了需要的证书 cnzhx.net.crt,cnzhx.net.csr 已经没用了,可以删除。
    # rm cnzhx.net.csr -f
  8. 将生成的 cnzhx.net.key 和 cnzhx.net.crt 移动到前面创建的 Apache 服务器的 SSL 证书目录(前面创建的保存证书的目录),然后按照下面的介绍来配置服务器使用此证书即可。对了,刚才操作生成的 3 个文件都在刚才执行命令行的当前目录下,别找不到了。

3. 配置 Apache 使用自己签发的证书

Apache 目前还不支持使用基于名称的 SSL 虚拟主机配置。所以下面使用一个 IP 地址来给多个虚拟主机提供 SSL 服务。

编辑虚拟主机配置文件 vhost.conf(参考以前安装时的配置文件),当然也可以另外在相同目录下创建一个 vhostssl.conf 文件来单独放 SSL 服务的配置,设置希望启用 HTTPS 加密链接的网站的虚拟主机配置文件中的 VirtualHost。使用下面的模板来为每个需要使用 SSL 服务的网站创建配置,注意根据实际情况修改参数。# 之所以注释掉下面这行,是因为

# 安装 SSL 模块的时候已经
# 自动在 Apache 的配置中启用了这个监听端口
#
#<IfModule mod_ssl.c>
#    Listen 443
#</IfModule>
#<IfModule mod_gnutls.c>
#    Listen 443
#</IfModule>

<IfModule mod_ssl.c>
    # If you add NameVirtualHost *:443 here, you will also have to change
    # the VirtualHost statement in /etc/apache2/sites-available/default-ssl
    # to <VirtualHost *:443>
    # Server Name Indication for SSL named virtual hosts is currently not
    # supported by MSIE on Windows XP. 
    NameVirtualHost 12.34.56.78:443
    # ipv6 地址需要用方括号 [ ] 括起来 
    NameVirtualHost [2400:8900::f03c:91ff:fedf:9b24]:443

<VirtualHost 12.34.56.78:443 [2400:8900::f03c:91ff:fedf:9b24]:443>
     SSLEngine On
     SSLProtocol all -SSLv2 -SSLv3
     Header add Strict-Transport-Security “max-age=31536000″
     SSLHonorCipherOrder On
     SSLCompression off
     SSLCipherSuite ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:!kEDH:!aNULL:+HIGH:+MEDIUM
     SSLCertificateFile /etc/httpd/ssl/cnzhx.net.crt
     SSLCertificateKeyFile /etc/httpd/ssl/cnzhx.net.key
   ServerAdmin [email protected]
   ServerName cnzhx.net
   DocumentRoot /srv/www/cnzhx.net/public_html

   [...... 这里省略了很多其它配置没写 ......]

    ErrorLog /srv/www/cnzhx.net/logs/ssl.error.log
    CustomLog /srv/www/cnzhx.net/logs/ssl.access.log combined
</VirtualHost>

</IfModule>

我采用的是新建一个文件:/etc/httpd/conf.d/vhostssl.conf 来放置 SSL 的配置。做法很简单:

  1. 将 vhost.conf 中的所有内容复制到新建文件 vhostssl.conf 中;
  2. 然后修改:
    1. 注意将上面标记为红色的部分对应修改一下(原来后面的端口是 80);
    2. 然后按照每个虚拟主机对应修改上面标记为蓝色的部分;
    3. 其它部分保持不变。
  3. 如果有不想启用 SSL 服务的虚拟主机,在新配置文件中将其对应的所有配置删除。
  4. 如果有非正常端口访问的网站,比如我用的非正常端口访问的 phpMyAdmin,这个情况就有点儿复杂了。因为 HTTP 和 HTTPS 不能使用同一个端口,所以解决方法有两种,
    1. 方法一:新开一个端口用于 HTTPS 访问 phpMyAdmin,比如 2084,那么就需要在 vhostssl.conf 中 NameVirtualHost 12.34.56.78:443 下面增加两行
      Listen 2084
      NameVirtualHost 12.34.56.78:2084

      然后再对应修改 vhostssl.conf 中 phpMyAdmin 对应的部分(使用上面的新端口,而不是 443);

    2. 方法二:禁止使用 HTTP 非加密访问 phpMyAdmin,这样就可以把原来的端口 2082 用于 HTTPS 访问 phpMyAdmin,而不需要新用一个端口,剩下的与 A 的操作类似,但是特别要注意的是,此时必须删除 vhost.conf 中关于 phpMyAdmin 的配置
  5. 完成,先测试一下配置是否有参数错误,然后重启 Apache 服务
    service httpd configtest
    service httpd graceful

    即可使用。

不过此时使用 HTTPS 加密连接访问网站的话会总是提示收到提示说网站使用的安全证书不是由受信任的机构发布的,很烦人的。所以干脆将安全证书添加为“受信任的根证书”吧。

4. 配置 WordPress

可以将 WordPress 配置为必须使用 HTTPS 加密连接来访问登陆页面和管理后台。

修改 wp-config.php,在

/* That's all, stop editing! Happy blogging. */

之前,加上

define('FORCE_SSL_ADMIN', true);

可以使得后台强制加密访问;而加入一行

define('FORCE_SSL_LOGIN', true);

可以使登录页面强制加密访问。

我是两个语句都加了。

5. 参考资料

更新于 2013.06.18,改为 2048 bit 的 SSL 证书。

更新于 2014.12.12,增加一些 conf 文件中增加 SSL 安全性的设置。©

本文发表于水景一页。永久链接:<http://cnzhx.net/blog/ssl-on-lamp-on-vps/>。转载请保留此信息及相应链接。