将Let'sencrypt装载到容器中实现自动申请自动续订所遇到的坑

准备把我的博客放在容器中,由于我的博客使用了 let’s encrypt,我希望把他也加入到容器中实现自动签发证书的自动化功能。不过在这个过程中我遇到了很多问题,现在就遇到的问题进行一些小小的总结。

签名错误

首先,遇到的第一个问题就是当执行 docker build 时 总是会卡在下载 acme.sh 认证这步,报错

Specified signature is not matched with our calculation. server string to sign ....

谷歌一番发现这个是阿里云的 API 报的错误,签名有问题,可是我在本地测试是 OK 的啊。肯定是少了什么东西,检查一番发现是容器内的时间是 UTC 的 要比 GMT 的时间少8小时。于是要解决这个办法就是在 dockerfile 中加入一句

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime 

这样就不会有问题了。

下载 acme.sh 出错

在我一开始之间使用以下命令去下载 acme.sh 时总是会build 失败

RUN wget -O -  https://get.acme.sh | sh

后来调整为

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O -  https://get.acme.sh | sh"]     

则没有问题,当然执行签发证书时候也需要这样写

RUN ["/bin/bash", "-c", "set -o pipefail && /root/.acme.sh/acme.sh --issue -d awen.me -d *.awen.me --dns dns_cf --keylength ec-256 --debug"]

否则可能会出现请求超时异常退出无法继续 build。

too many certificates already issued for exact set of domains

在申请证书时候,我们需要加上 –debug,否则如果出现异常我们无法判断是哪里出了问题

/root/.acme.sh/acme.sh --issue -d awen.me -d *.awen.me --dns dns_cf --keylength ec-256 --debug

当开启 debug 后,build 镜像提示

[Tue Jun 19 22:44:33 CST 2018] {"type":"urn:ietf:params:acme:error:rateLimited","detail":"Error finalizing order :: too many certificates already issued for exact set of domains: *.awen.me,awen.me: see https://letsencrypt.org/docs/rate-limits/","status": 429}
[Tue Jun 19 22:44:33 CST 2018] _on_issue_err
[Tue Jun 19 22:44:33 CST 2018] Please add '--debug' or '--log' to check more details.
[Tue Jun 19 22:44:33 CST 2018] See: https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh
[Tue Jun 19 22:44:33 CST 2018] socat doesn't exists.
[Tue Jun 19 22:44:33 CST 2018] Diagnosis versions:
openssl:openssl
OpenSSL 1.0.1t  3 May 2016
apache:
apache doesn't exists.
nginx:
nginx doesn't exists.
socat:
The command '/bin/bash -c set -o pipefail && /root/.acme.sh/acme.sh --issue -d awen.me -d *.awen.me --dns dns_cf --keylength ec-256 --debug' returned a non-zero code: 1

说明这个域名申请证书的次数过多了,你可以通过这个crt.sh是查看你的域名的申请次数,一般来说如果出现这个错误需要距离上次申请的日期顺延7天才可以。

为了减少每次 build 都去申请一次导致次数过多,可以先在本地申请,然后将本地的 /.acme.sh 目录下的证书目录拷贝过去以及保存 API 信息的 account.conf 文件拷贝到容器的/.acme.sh 目录下,这样后期只需要校验证书是否过期即可。

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O -  https://get.acme.sh | sh"]

  COPY account.conf /root/.acme.sh/account.conf
  COPY awen.me_ecc /root/.acme.sh/awen.me_ecc

计划任务

在安装 acme.sh 程序时如果没有安装 cron,则无法运行。安装 cron 很简单,类似下面这样

COPY crontab /var/spool/cron/crontabs/root

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime && \
    apt-get update && \
    apt-get install build-essential libpcre3 libpcre3-dev zlib1g-dev wget libtool automake  openssh-server supervisor cron -y && \

……

chown -R root:crontab /var/spool/cron/crontabs/root && \
    chmod 600 /var/spool/cron/crontabs/root && \
    touch /var/log/cron.log 

使容器保持运行状态

可以使用 supervisord ,安装

apt-get install supervisor

supervisord.conf 配置

[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D

[program:nginx]
command=/etc/init.d/nginx start

[program:crontab]
command=/etc/init.d/cron start

然后在dockerfile 中加入

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

acme.sh 无法验证

可以在本地先执行一遍,然后将/.acme.sh/account.conf 拷贝到容器的/.acme.sh/ 下

COPY account.conf /root/.acme.sh/account.conf    

完整的 dockerfile 文件参考

  FROM debian:jessie

MAINTAINER NGINX Docker Maintainers "[email protected]"

WORKDIR /opt

ENV DIR="/opt/nginx/" \
    ZLIB="zlib-1.2.11.tar.gz" \
    ZLIB_DIR="zlib-1.2.11" \
    PCRE="pcre-8.41.tar.gz" \
    PCRE_DIR="pcre-8.41" \
    OPENSSL="openssl-1.0.2o.tar.gz" \
    OPENSSL_DIR="openssl-1.0.2o" \ 
    NGINX="nginx-1.14.0.tar.gz" \
    NGINX_DIR="nginx-1.14.0" \
    NGX_BROTLI="ngx_brotli" 


COPY sources.list.jessie /etc/apt/sources.list
COPY nginx.sh /etc/init.d/nginx

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime && \
    apt-get update && \
    apt-get install build-essential libpcre3 libpcre3-dev zlib1g-dev wget libtool automake  openssh-server supervisor cron rsync  -y && \
    mkdir -p /opt/nginx && \
    cd $DIR && \
    wget -c -4 http://zlib.net/$ZLIB -O $DIR$ZLIB && \
    tar zxvf $ZLIB && \
    cd $ZLIB_DIR && \
    ./configure && \
    make && \
    make install && \
    cd $DIR && \
    wget -c -4 https://ftp.pcre.org/pub/pcre/$PCRE -O $DIR$PCRE && \
    tar zxvf $PCRE && \
    cd $PCRE_DIR && \
    ./configure && \
    make && \
    make install && \
    cd $DIR && \
    wget -c -4 https://www.openssl.org/source/$OPENSSL  -O $DIR$OPENSSL  && \
    tar zxvf $OPENSSL && \
    groupadd www && \
    useradd -s /sbin/nologin -g www www && \
    cd $DIR && \
    wget -c http://file201503.oss-cn-shanghai.aliyuncs.com/awen/ngx_brotli.tar.gz && \
    tar zxvf ngx_brotli.tar.gz && \
    wget -c -4 https://nginx.org/download/$NGINX -O $DIR$NGINX && \
    tar zxvf $NGINX && \
    cd $NGINX_DIR && \
    ./configure --prefix=/usr/local/nginx --user=www --group=www --with-pcre=$DIR$PCRE_DIR --with-http_v2_module --with-http_ssl_module --with-zlib=$DIR$ZLIB_DIR --with-openssl=$DIR$OPENSSL_DIR --add-module=$DIR$NGX_BROTLI && \
    make && \
    make install && \
    rm -rf /usr/local/nginx/conf/vhost && \
    mkdir /usr/local/nginx/conf/vhost  &&  \
    rm -rf /usr/local/nginx/conf/nginx && \
    mkdir /usr/local/nginx/ssl && \
    cd $DIR && \
    mkdir -p /var/run/sshd && \
    mkdir -p /var/log/supervisor && \
    mkdir -p /www/www && \
    mkdir -p /www/wwwlogs && \
    chown -R www:www /www/ && \
    rm -rf /opt/ && \ 
    apt-get  clean && \
    rm -rf /var/lib/apt/lists/* && \
    chmod +x /etc/init.d/nginx 

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O -  https://get.acme.sh | sh"] && \
    chown -R root:crontab /var/spool/cron/crontabs/root && \
    chmod 600 /var/spool/cron/crontabs/root && \
    touch /var/log/cron.log && \
    mkdir -p /root/.acme.sh/awen.me_ecc

COPY account.conf /root/.acme.sh/account.conf
COPY awen.me_ecc /root/.acme.sh/awen.me_ecc
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY awen.me.conf /usr/local/nginx/conf/vhost
COPY nginx.conf /usr/local/nginx/conf

RUN /root/.acme.sh/acme.sh  --installcert  -d  awen.me --ecc --keypath   /usr/local/nginx/ssl/awen.me.key --fullchainpath /usr/local/nginx/ssl/awen.me.cer --reloadcmd  "/etc/init.d/nginx restart"

EXPOSE 22 80 443
VOLUME ["/www/www","/www/wwwlogs"]

CMD ["/usr/bin/supervisord"]