换上了用Let's Encrypt Closed Beta自己签发的证书

先上图。两个小时的成果。
letsencrypt

在10月31的时候就收到了letsencrypt发的 Let’s Encrypt Closed Beta Invite‏ 说我的tms.im和www.tms.im已经加入内测白名单。可惜这封邮件被“智能”的丢进了垃圾邮件文件夹。导致我今天才看到。于是来折腾一下。

在开始之前先严重吐槽下,可能是由于内测或者该计划刚开始还不完善的原因。这个东西特别特别特别不亲民。我搞了两个多小时才签了一个证书出来。突然发现只签了tms.im没签www.tms.im。于是又用了40分钟的时间重新签了这两个。外加这货的有效期只有三个月,也就是说三个月以后我要重新签。在现在年付的付费证书已经这么便宜的情况下。到底letsencrypt有没有竞争力。

下面正文。

由于现在还是内测。官方在邮件里提供了一个内测用的API路径。

To use Let’s Encrypt’s official client to obtain your real
certificates, you will need to provide the production API URL on the
command line:

https://acme-v01.api.letsencrypt.org/directory

还提供了软件

git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt
./letsencrypt-auto --agree-dev-preview --server
https://acme-v01.api.letsencrypt.org/directory auth

但是装好软件以后运行./letsencrypt-auto报错。

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-Wa1Lwq/ConfigArgParse

去官方论坛https://community.letsencrypt.org/看了一下说只支持python2.7,不支持2.6。 好吧我centos6.7默认是2.6的python。也懒得搞成2.7了,于是另开了一台centos7的服务器来签发。

执行./letsencrypt-auto --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory auth -d tms.im -d www.tms.im

   报错The program nginx (process ID 7176) is already listening on TCP port 
    80. This will prevent us from binding to that port. Please stop the  
    nginx program temporarily and then try again.      

提醒我80端口被占用。验证个域名所有权还要关80端口额。还好只是个个人博客。如果我是生产用的域名和服务器怎么办。总不能停机去搞域名验证吧。

暂时停了80的nginx。终于进到了下一步。然后又开始报错

Failed authorization procedure. tms.im (dvsni): unauthorized :: The client lacks sufficient authorization :: Correct zName not found for TLS SNI challenge

只好切换用手动模式:后面加一个
-a manual

手动模式告诉我需要自己开一个 SimpleHTTPServer 来搞验证,并且给了我一堆指令。我以为它会自动开呢。用netstat看了一下,果然没有自动开。好吧我只好手动调用python开一个SimpleHTTPServer 。然后按照每次验证它给的路径和文件名和文件内容创建对应的验证文件。

当我以为这样就可以通过验证的时候,letsencrypt告诉我还是tooyoung

:: The client lacks sufficient authorization :: Invalid response from http://tms.im/.well-known/acme-challenge/g2eyBgAf3Z-NpzBAzTrvxopLxJ1PH1I8lt7sqiA01sQ [103.242.111.8]

什么。不是已经开好SimpleHTTPServer 了么。我的SimpleHTTPServer 里也有access log啊。我在浏览器打开也可以访问啊。再仔细一看IP是103.242.111.8。这,我不是改了DNS指向验证用的这台服务器了么,为什么非要去以前的服务器验证。

可能是远程API那边DNS更新比较慢吧。可是我用nslookup看了一下主要的几个DNS服务器都更新过了啊。鬼知道他们用的什么DNS,或者是就是有机制让你不能改IP验证?谁知道呢。只好在以前的服务器上也开了个SimpleHTTPServer,放入同样的验证文件。再进行验证。

这下终于生成成功了。最新的证书和私钥都在/etc/letsencrypt/live/$domain里面。内测阶段只能签三个月的证书。

/etc/letsencrypt/keys里面存的是所有的包括之前和最新的证书。

最好把整个/etc/letsencrypt目录都备份一下,里面是包括邮箱和token之类的东西。以后renew的时候用的。

折腾了两个多小时签发出第一张免费证书。letsencrypt还有很长的路要走啊。

新玩具

新玩具

跨IDC数据备份

为了数据安全性。除了做了主从和多机互备份以外,在异地容灾和数据备份方面也不容忽视。
这里是实现跨IDC数据备份的一种思路。分为文件备份和数据库备份两部分。文件备份可以保证用户上传的文件,图片和网站的代码不会丢失。而数据库备份则是数据保护的重中之重。
之所以采用跨IDC进行数据备份也是考虑到数据的安全性和同IDC的网络崩溃和主机崩溃的可能性。

文件备份

文件数量和大小决定了需要做到文件同步备份必须采用增量备份的方式。传统的方法是使用 inotify + rsync 来进行同步备份。
这里采用Sersync 来进行服务器间文件同步。

配置 Slave 上的 WWW 同步

Slave上安装rsync
然后配置文件写明,其中rsync.pass里面写入密码

log file = /var/log/rsyncd.log 
pidfile = /var/run/rsyncd.pid 
lock file = /var/run/rsync.lock  
secrets file = /etc/rsyncd/rsync.pass
[tongbu]
path = /home/wwwroot/
comment = tongbu
uid = root
gid = root
port=873
use chroot = no
read only = no
list = no
max connections = 200
timeout = 600 
auth users = tms
hosts allow = 主机的IP

最后启动Rsync的 rsync –daemon

启动 Master 上的 Sersync 做数据同步

主机上配置sersync

 <sersync>
	<localpath watch="/home/wwwroot">
	    <remote ip="远程IP" name="tongbu"/>
	</localpath>

其他配置略

第一次启动加上 -r 参数做首次同步,之后就不要再加  -r 参数了
./sersync2 -n 2 -d -r -o confxml.xml

文件备份到此结束。

数据库备份

数据库备份这里采用主从的方式,而这里的slave纯粹做备份使用。无业务访问他。
前期准备工作步骤如下:
1、修改备份服务器上my.cnf中的server-id

2、登录主服务器创建同步账号GRANT REPLICATION SLAVE ON . TO ‘slave’@‘Slave服务器IP’ IDENTIFIED BY ‘password’;

备份主服务器步骤:
1、FLUSH TABLES WITH READ LOCK;

2、cp -ar /var/lib/mysql /home/DATA/tmp

3、show master status;并且记录file和position

4、UNLOCK TABLES;

复制主服务器的/home/DATA/tmp到从服务器并且替换mysql的var目录

登录从服务器的mysql

CHANGE MASTER TO
 
    ->     MASTER_HOST='master_host_name',
 
    ->     MASTER_USER='replication_user_name',
 
    ->     MASTER_PASSWORD='replication_password',
 
    ->     MASTER_LOG_FILE='前面让你记录下的 master 状态显示的 logfile 名字',
 
    ->     MASTER_LOG_POS=前面记录下的 postion;

START SLAVE;

show slave status \G

这样Master —> Slave 就运行起来了。数据库备份结束。

为了进一步的安全考虑,采用crontab每隔一天做一次全量备份,在备份服务器进行。脚本如下

#!/bin/bash
DBName=数据库名
DBUser=备份用户
DBPasswd=备份用户的密码
BackupPath=/root/Dropbox/   #保存到同步目录

LogFile=/root/db.log
DBPath=/var/lib/mysql/ #备份的数据库目录
BackupMethod=mysqldump
#BackupMethod=mysqlhotcopy
#BackupMethod=tar

NewFile="$BackupPath"db$(date +%y%m%d).tgz
DumpFile="$BackupPath"db$(date +%y%m%d)
OldFile="$BackupPath"db$(date +%y%m%d --date='5 days ago').tgz  #自动删除5天前的备份
echo "-------------------------------------------" >> $LogFile
echo $(date +"%y-%m-%d %H:%M:%S") >> $LogFile
echo "--------------------------" >> $LogFile
#Delete Old File
if [ -f $OldFile ]
then
        rm -f $OldFile >> $LogFile 2>&1
        echo "[$OldFile]Delete Old File Success!" >> $LogFile
else
        echo "[$OldFile]No Old Backup File!" >> $LogFile
fi
if [ -f $NewFile ]
then
        echo "[$NewFile]The Backup File is exists,Can't Backup!" >> $LogFile
else
        case $BackupMethod in
        mysqldump)
                if [ -z $DBPasswd ]
                then
                        mysqldump -u $DBUser --opt $DBName > $DumpFile
                else
                        mysqldump -u $DBUser -p$DBPasswd --opt $DBName > $DumpFile
                fi
                tar czvf $NewFile $DumpFile >> $LogFile 2>&1
                echo "[$NewFile]Backup Success!" >> $LogFile
                rm -rf $DumpFile
                ;;
        mysqlhotcopy)
                rm -rf $DumpFile
                mkdir $DumpFile
                if [ -z $DBPasswd ]
                then
                        mysqlhotcopy -u $DBUser $DBName $DumpFile >> $LogFile 2>&1
                else
                        mysqlhotcopy -u $DBUser -p $DBPasswd $DBName $DumpFile >>$LogFile 2>&1
                fi
                tar czvf $NewFile $DumpFile >> $LogFile 2>&1
                echo "[$NewFile]Backup Success!" >> $LogFile
                rm -rf $DumpFile
                ;;
        *)
                service mysql stop >/dev/null 2>&1
                tar czvf $NewFile $DBPath$DBName >> $LogFile 2>&1
                service mysql start >/dev/null 2>&1
                echo "[$NewFile]Backup Success!" >> $LogFile
                ;;
        esac
fi
echo "-------------------------------------------" >> $LogFile

同时采用dropbox实时上传网盘。保证每日的数据库在云端存有一份备份。

利用prism.js实现代码高亮

发现一个小巧高效的代码高亮js,官网地址
http://prismjs.com/

Prism is a lightweight, extensible syntax highlighter, built with
modern web standards in mind. It’s a spin-off from Dabblet and is
tested there daily by thousands.

<link href="themes/prism.css" rel="stylesheet" />
<script src="prism.js"></script>

定制后加载到主题。
因为本站采用了instantclick做pjax加速,需要在触发pjax的时候同时触发高亮渲染。去instantclick官网的doc里找到

InstantClick fires four events to provide hooks into the lifecycle of
the page:

change : The page is changed, also triggered on initial page load and
if InstantClick is not supported. Replaces DOMContentLoaded.

Its callback can take an optional isInitialLoad argument, it’s a
Boolean that will be true when it’s the initial page change or when
InstantClick isn’t supported, and false when InstantClick changes the
page.

fetch : A page starts preloading.

receive : A page has been preloaded. You can modify its content.

wait : The user has clicked on a link, but the page isn’t preloaded
yet. Only triggers when the page isn’t displayed instantly.

这里采用on change触发InstantClick.on(‘change’, yourCallback);代码如下

InstantClick.on('change', function() {
    self.Prism.highlightAll(event);
});
InstantClick.init();

测试一下,一切正常。

DHL用自己的check digits规范把我坑了一下

这几天一直在搞条形码生成。因为有一个和DHL的合作项目要自动生成条形码打印出来。
而DHL给的条形码要求是interleaved 2of5.

查了一圈资料得知interleaved 2of5是没有check digits的
但是生成出来的条码很明显不对

后来在wikipedia发现A checksum can be added as last digit, which is calculated in the same way as UPC checksums.
于是用标准的checksums来计算check digit即

In the UPC-A system, the check digit is calculated as follows:

Add the digits in the odd-numbered positions (first, third, fifth,
etc.) together and multiply by three. Add the digits in the
even-numbered positions (second, fourth, sixth, etc.) to the result.
Find the result modulo 10 (i.e. the remainder when divided by 10… 10
goes into 58 5 times with 8 leftover). If the result is not zero,
subtract the result from ten.

但是生成出来的条码依旧和DHL给的范例不一样。反馈给DHL他们也说他们的机器扫描不成功。
后来以为是条码宽度问题。根据DHL给的要求把每条宽度设置为0.5mm到0.33mm之间。总宽度42mm
再次提交,DHL反馈说扫描出来了但是数字不对。首位多了0,末尾多了8.
万能的wikipedia又告诉我

Before the actual pairs there is a start code consisting of nnnn
(narrow bar-narrow space- narrow bar-narrow space), and after all
symbols there is the stop code consisting of Wnn (Wide bar-narrow
space-narrow bar).

Because digits are encoded by pairs, only an even number of digits can
be encoded. Typically an odd number of digits is encoded by adding a
“0” as first digit, but sometimes an odd number of digits is encoded
by using five narrow spaces in the last digit.

瞬间想到最后以为是不是barcode生成器自动给我添加的校验位。去查看了生成类的源代码发现

static public function compute($code, $crc, $type){
        if (! $crc) {
            if (strlen($code) % 2) $code = '0' . $code;
        } else {
            if ( ($type == 'int25') && (strlen($code) % 2 == 0) ) $code = '0' . $code;
            $odd = true;
            $sum = 0;
            for($i=strlen($code)-1; $i>-1; $i--){
                $v = intval($code[$i]);
                $sum += $odd ? 3 * $v : $v;
                $odd = ! $odd;
            }
            $code .= (string) ((10 - $sum % 10) % 10);
        }
        return($code);
    }

然而不传递checkdigit进去生成出来的依旧不对。
这时候想到DHL一开始发的Check digit calculation , Factor 4 and 9 see Specification。
wikipedia了一下。终于发现了

Identcode and Leitcode are variants of interleaved 2 of 5 with check
digits used by Deutsche Post.

一开始还不知道Deutsche Post是什么。查了一下 ,不就是DHL么。。。。
好坑啊。DHL还用自己的checkdigit规范。

果断去把生成类里的代码改成

$sum += $odd ? 4 * $v : 9 * $v;

又生成了一张。发现和DHL给的范例终于一模一样了。。

大功告成。
然而不知道为啥DHL用自己的checkdigit规范不用国际通用标准。真是好坑好坑。
另外,wikipedia真是个好东西。然而国内百度一下并没有任何资料。全都是卖东西的。