标签: ssl

  • nginx https配置模板

    server {
        listen 443 ssl http2 default_server;
        server_name  www.lwbj.cn;
        root /var/www/www.lwbj.cn;
        index  index.html index.htm index.php;
    
        #文件上传大小限制  必须要放在server下的server_name下
        client_max_body_size 200m;
    
        # 因为是默认的 https 站点,所以有可能是从 IP 进来的请求,那么把它跳转到域名
        if ($host != 'www.lwbj.cn') {
            rewrite ^/(.*)$ https://lwbj.cn/$1 permanent;
            break;
        }
    
        ssl_certificate /etc/nginx/ssl/www.lwbj.cn.pem;
        ssl_certificate_key /etc/nginx/ssl/www.lwbj.cn.key;
    
        #加上TLSv1,HTTPS检测会报PCI DSS不合规
        ssl_protocols  TLSv1.2 TLSv1.3;# Requires nginx >= 1.13.0 else use TLSv1.2
        ssl_prefer_server_ciphers on;
        ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
        ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
        ssl_session_timeout 10m;
        ssl_session_cache shared:SSL:10m;
        ssl_session_tickets off; # Requires nginx >= 1.5.9
        ssl_stapling on; # Requires nginx >= 1.3.7
        ssl_stapling_verify on; # Requires nginx => 1.3.7
    
        add_header X-Frame-Options SAMEORIGIN;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
    
        location / {
            try_files $uri $uri/ /index.php?$args;
        }
    
        # pass PHP scripts to FastCGI server
        location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        }
    }
    
  • acme.sh 用法

    安装 acme.sh

    curl https://get.acme.sh | sh -s email=your-email@outlook.com

    设置别名,方便使用:

    alias acme.sh=~/.acme.sh/acme.sh

    申请证书:

    acme.sh --issue -d www.lwbj.cn  --nginx

    acme.sh --issue -d www.lwbj.cn  --webroot /var/www/www.lwbj.cn

    部署证书:

    acme.sh --install-cert -d www.lwbj.cn --key-file /etc/nginx/ssl/www.lwbj.cn.key --fullchain-file /etc/nginx/ssl/www.lwbj.cn.pem --reloadcmd "service nginx force-reload"

    删除证书:

    acme.sh --remove -d www.lwbj.cn

    删除后记得到 /root/.acme.sh/ 目录下找到域名文件夹,一并删除。

    官方文档:https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E

  • Debian+Nginx+PHP HTTPS配置模板

    Nginx+PHP+HTTPS的配置模板,如果要部署其它网站,可以基于此进行修改:

    # 处理 HTTP 请求,所有请求都重定向到 https://lwbj.cn
    server {
        listen 80;
        server_name lwbj.cn www.lwbj.cn;  # 同时匹配带 www 和不带 www 的域名
    
        # 所有请求都重定向到 https://lwbj.cn
        return 301 https://lwbj.cn$request_uri;
    }
    
    # 处理不带 www 的 HTTPS 请求,重定向到带 www 的域名
    server {
        listen 443 ssl http2;
        server_name lwbj.cn;  # 不带 www 的域名
    
        ssl_certificate /etc/nginx/ssl/www.lwbj.cn.pem;  # 证书路径
        ssl_certificate_key /etc/nginx/ssl/www.lwbj.cn.key;  # 证书路径
    
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;
        ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
        ssl_ecdh_curve secp384r1;
        ssl_session_timeout 10m;
        ssl_session_cache shared:SSL:10m;
        ssl_session_tickets off;
        ssl_stapling on;
        ssl_stapling_verify on;
    
        # 重定向所有不带 www 的 HTTPS 请求到 www.lwbj.cn
        return 301 https://lwbj.cn$request_uri;
    }
    
    # 处理带 www 的 HTTPS 请求
    server {
        listen 443 ssl http2;
        root /var/www/www.lwbj.cn; # 确保这里是正确的根目录路径
        index index.html index.htm index.php index.nginx-debian.html;
    
        server_name  www.lwbj.cn;
        client_max_body_size 20m;
    
        ssl_certificate /etc/nginx/ssl/www.lwbj.cn.pem; # 确保证书路径正确
        ssl_certificate_key /etc/nginx/ssl/www.lwbj.cn.net.key; # 确保证书路径正确
    
        #加上TLSv1,HTTPS检测会报PCI DSS不合规
        ssl_protocols  TLSv1.2 TLSv1.3;# Requires nginx >= 1.13.0 else use TLSv1.2
        ssl_prefer_server_ciphers on;
        ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
        ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
        ssl_session_timeout 10m;
        ssl_session_cache shared:SSL:10m;
        ssl_session_tickets off; # Requires nginx >= 1.5.9
        ssl_stapling on; # Requires nginx >= 1.3.7
        ssl_stapling_verify on; # Requires nginx => 1.3.7
    
        location / {
            try_files $uri $uri/ /index.php?$args;
        }
    
        location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        }
    }
  • ACME.sh自动证书管理

    https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E

    按文档做就行。

    acme.sh 实现了 acme 协议, 可以从 letsencrypt 生成免费的证书.

    主要步骤:

    1. 安装 acme.sh
    2. 生成证书
    3. copy 证书到 nginx/apache 或者其他服务
    4. 更新证书
    5. 更新 acme.sh
    6. 出错怎么办, 如何调试

    下面详细介绍.

    1. 安装 acme.sh

    安装很简单, 一个命令:

    curl https://get.acme.sh | sh -s email=my@example.com

    普通用户和 root 用户都可以安装使用. 安装过程进行了以下几步:

    1. 把 acme.sh 安装到你的 home 目录下:

    ~/.acme.sh/

    并创建 一个 shell 的 alias, 例如 .bashrc,方便你的使用: alias acme.sh=~/.acme.sh/acme.sh

    1. 自动为你创建 cronjob, 每天 0:00 点自动检测所有的证书, 如果快过期了, 需要更新, 则会自动更新证书.

    更高级的安装选项请参考:  https://github.com/Neilpang/acme.sh/wiki/How-to-install

    安装过程不会污染已有的系统任何功能和文件, 所有的修改都限制在安装目录中: ~/.acme.sh/

    2. 生成证书

    acme.sh 实现了 acme 协议支持的所有验证协议. 一般有两种方式验证: http 和 dns 验证.

    1. http 方式需要在你的网站根目录下放置一个文件, 来验证你的域名所有权,完成验证. 然后就可以生成证书了.

    acme.sh --issue -d mydomain.com -d www.mydomain.com --webroot /home/wwwroot/mydomain.com/

    只需要指定域名, 并指定域名所在的网站根目录. acme.sh 会全自动的生成验证文件, 并放到网站的根目录, 然后自动完成验证. 最后会聪明的删除验证文件. 整个过程没有任何副作用.

    如果你用的 apache服务器, acme.sh 还可以智能的从 apache的配置中自动完成验证, 你不需要指定网站根目录:

    acme.sh --issue -d mydomain.com --apache

    如果你用的 nginx服务器, 或者反代, acme.sh 还可以智能的从 nginx的配置中自动完成验证, 你不需要指定网站根目录:

    acme.sh --issue -d mydomain.com --nginx

    注意, 无论是 apache 还是 nginx 模式, acme.sh在完成验证之后, 会恢复到之前的状态, 都不会私自更改你本身的配置. 好处是你不用担心配置被搞坏, 也有一个缺点, 你需要自己配置 ssl 的配置, 否则只能成功生成证书, 你的网站还是无法访问https. 但是为了安全, 你还是自己手动改配置吧.

    如果你还没有运行任何 web 服务, 80 端口是空闲的, 那么 acme.sh 还能假装自己是一个webserver, 临时听在80 端口, 完成验证:

    acme.sh --issue -d mydomain.com --standalone

    更高级的用法请参考:  https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert

    2. 手动 dns 方式, 手动在域名上添加一条 txt 解析记录, 验证域名所有权.

    这种方式的好处是, 你不需要任何服务器, 不需要任何公网 ip, 只需要 dns 的解析记录即可完成验证. 坏处是,如果不同时配置 Automatic DNS API,使用这种方式 acme.sh 将无法自动更新证书,每次都需要手动再次重新解析验证域名所有权。

    acme.sh --issue --dns -d mydomain.com \
     --yes-I-know-dns-manual-mode-enough-go-ahead-please

    然后, acme.sh 会生成相应的解析记录显示出来, 你只需要在你的域名管理面板中添加这条 txt 记录即可.

    等待解析完成之后, 重新生成证书:

    acme.sh --renew -d mydomain.com \
      --yes-I-know-dns-manual-mode-enough-go-ahead-please

    注意第二次这里用的是 --renew

    dns 方式的真正强大之处在于可以使用域名解析商提供的 api 自动添加 txt 记录完成验证.

    acme.sh 目前支持 cloudflare, dnspod, cloudxns, godaddy 以及 ovh 等数十种解析商的自动集成.

    以 dnspod 为例, 你需要先登录到 dnspod 账号, 生成你的 api id 和 api key, 都是免费的. 然后:

    export DP_Id="1234"
    export DP_Key="sADDsdasdgdsf"
    acme.sh --issue --dns dns_dp -d aa.com -d www.aa.com

    证书就会自动生成了. 这里给出的 api id 和 api key 会被自动记录下来, 将来你在使用 dnspod api 的时候, 就不需要再次指定了. 直接生成就好了:

    acme.sh --issue -d mydomain2.com --dns  dns_dp

    更详细的 api 用法:  https://github.com/Neilpang/acme.sh/blob/master/dnsapi/README.md

    3. copy/安装 证书

    前面证书生成以后, 接下来需要把证书 copy 到真正需要用它的地方.

    注意, 默认生成的证书都放在安装目录下: ~/.acme.sh/, 请不要直接使用此目录下的文件, 例如: 不要直接让 nginx/apache 的配置文件使用这下面的文件. 这里面的文件都是内部使用, 而且目录结构可能会变化.

    正确的使用方法是使用--install-cert 命令,并指定目标位置, 然后证书文件会被copy到相应的位置, 例如:

    Apache example:

    acme.sh --install-cert -d example.com \
    --cert-file      /path/to/certfile/in/apache/cert.pem  \
    --key-file       /path/to/keyfile/in/apache/key.pem  \
    --fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
    --reloadcmd     "service apache2 force-reload"

    Nginx example:

    acme.sh --install-cert -d example.com \
    --key-file       /path/to/keyfile/in/nginx/key.pem  \
    --fullchain-file /path/to/fullchain/nginx/cert.pem \
    --reloadcmd     "service nginx force-reload"

    (一个小提醒, 这里用的是 service nginx force-reload, 不是 service nginx reload, 据测试, reload 并不会重新加载证书, 所以用的 force-reload)

    Nginx 的配置 ssl_certificate 使用 /etc/nginx/ssl/fullchain.cer ,而非 /etc/nginx/ssl/<domain>.cer ,否则 SSL Labs 的测试会报 Chain issues Incomplete 错误。

    --install-cert命令可以携带很多参数, 来指定目标文件. 并且可以指定 reloadcmd, 当证书更新以后, reloadcmd会被自动调用,让服务器生效.

    详细参数请参考:  https://github.com/Neilpang/acme.sh#3-install-the-issued-cert-to-apachenginx-etc

    值得注意的是, 这里指定的所有参数都会被自动记录下来, 并在将来证书自动更新以后, 被再次自动调用.

    4. 查看已安装证书信息

    acme.sh --info -d example.com

    会输出如下内容:

    DOMAIN_CONF=/root/.acme.sh/example.com/example.com.conf
    Le_Domain=example.com
    Le_Alt=no
    Le_Webroot=dns_ali
    Le_PreHook=
    Le_PostHook=
    Le_RenewHook=
    Le_API=https://acme-v02.api.letsencrypt.org/directory
    Le_Keylength=
    Le_OrderFinalize=https://acme-v02.api.letsencrypt.org/acme/finalize/23xxxx150/781xxxx4310
    Le_LinkOrder=https://acme-v02.api.letsencrypt.org/acme/order/233xxx150/781xxxx4310
    Le_LinkCert=https://acme-v02.api.letsencrypt.org/acme/cert/04cbd28xxxxxx349ecaea8d07
    Le_CertCreateTime=1649358725
    Le_CertCreateTimeStr=Thu Apr  7 19:12:05 UTC 2022
    Le_NextRenewTimeStr=Mon Jun  6 19:12:05 UTC 2022
    Le_NextRenewTime=1654456325
    Le_RealCertPath=
    Le_RealCACertPath=
    Le_RealKeyPath=/etc/acme/example.com/privkey.pem
    Le_ReloadCmd=service nginx force-reload
    Le_RealFullChainPath=/etc/acme/example.com/chain.pem

    5. 更新证书

    目前证书在 60 天以后会自动更新, 你无需任何操作. 今后有可能会缩短这个时间, 不过都是自动的, 你不用关心.

    请确保 cronjob 正确安装, 看起来是类似这样的:

    crontab  -l
    56 * * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

    6. 关于修改ReloadCmd

    目前修改ReloadCmd没有专门的命令,可以通过重新安装证书来实现修改reloadCmd的目的。 此外,安装证书后,相关信息是保存在~/.acme.sh/example.com/example.conf文件下的,内容就是acme.sh --info -d example.com输出的信息,不过ReloadCmd在文件中使用了Base64编码。理论上可以通过直接修改该文件来修改ReloadCmd,且修改时,无需Base64编码,直接写命令原文acme.sh也可以识别。 不过,example.conf文件的位置和内容格式以后可能会改变!example.conf一直都是内部使用, 后面有可能会改为用 sqlite 或者mysql 格式存储. 所以一般不建议自己修改。

    7. 更新 acme.sh

    目前由于 acme 协议和 letsencrypt CA 都在频繁的更新, 因此 acme.sh 也经常更新以保持同步.

    升级 acme.sh 到最新版 :

    acme.sh --upgrade

    如果你不想手动升级, 可以开启自动升级:

    acme.sh --upgrade --auto-upgrade

    之后, acme.sh 就会自动保持更新了.

    你也可以随时关闭自动更新:

    acme.sh --upgrade --auto-upgrade  0

    8. 出错怎么办:

    如果出错, 请添加 debug log:

    acme.sh --issue  .....  --debug

    或者:

    acme.sh --issue  .....  --debug  2

    请参考:  https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh

    在DNS验证模式下如果debug中出现诸如”timed out”等字样可能是因为GFW拦截了相应请求,需要添加http(s) proxy环境变量。(请按照自己实际设定修改)

    export http_proxy="socks5h://localhost:1081" && export https_proxy="socks5h://localhost:1081"

    如果是使用docker则完整示例配置如下:

    docker run --rm  -it  \
      -v "/etc/acme":/acme.sh  \
      -e "CF_Token=[填入自己的信息]" \
      -e "CF_Account_ID=[填入自己的信息]" \
      -e "CF_Zone_ID=[填入自己的信息]" \
      -e http_proxy="socks5h://[代理A]:1234" \
      -e https_proxy="socks5h://[代理A]:1234" \
      --network container:[代理A]\
      neilpang/acme.sh \
      --issue -d example.com --dns dns_cf --debug

    上述例子中使用cloudflare的DNS来签发证书,并通过把acme.sh链接到容器[代理A],来转发curl请求(请按照自己实际设定修改)

    最后, 本文并非完全的使用说明, 还有很多高级的功能, 更高级的用法请参看其他 wiki 页面.

    https://github.com/Neilpang/acme.sh/wiki

  • 解决 Nginx SSL 证书报错

    闲来无事,查看了一下 nginx 的 error.log 日志文件,发现里面大量的报错信息:cannot load certificate “data:”: PEM_read_bio_X509_AUX() failed (SSL: error:0909006C:PEM routines:get_name:no start line:Expecting: TRUSTED CERTIFICATE) while SSL handshaking…

    是 ssl 证书报错了,想了一下,整个服务器只有这个博客使用了 https ,理所当然的从博客的 nginx 配置文件和证书文件入手,历时三天也没找到原因,甚至一度把证书都更换了,从腾讯云的 SSL 证书换到了阿里云的 SSL 证书,结果还是报错,说明证书没问题。

    就在快要放弃的时候,突然想到,之前为了屏蔽直接从 ip 来的请求,加了一些配置,屏蔽了http://iphttps://ip两种访问形式,而屏蔽 https+ip 也涉及到了 SSL 证书的问题,当时是通过 map 映射给了一个空证书,会不会是这个原因?

    于是,为了验证这个猜想,把博客站点的错误日志和其它站点的分开,单独统计。过了一夜,第二天打开日志文件查看,果然,博客的错误日志中并没有此错误,只有 error.log 里面有,这就说明错误不是博客站点的 SSL 证书引起的,那么只可能是之前屏蔽 https+ip 的配置引起的。

    找到了原因,那么解决起来就快了。

    先帖一下之前的配置:

    map "" $empty {
        default "";
    }
    server {
        listen 80 default_server;
        listen 443 ssl http2 default_server;
        listen [::]:80 default_server;
        listen [::]:443 ssl http2 default_server;
        server_name _;
    
        ssl_ciphers aNULL;
        ssl_certificate data:$empty;
        ssl_certificate_key data:$empty;
        return 444;
    }

    把上面的配置改成如下:

    # 禁止直接通过IP访问网站
    server {
        listen 80 default_server;
        server_name _;
        return 444;
    }

    删除了屏蔽 https+ip 的配置,只保留屏蔽 http+ip 的配置。然后在博客站点的 nginx 配置文件中添加如下代码:

    # 通过 default_server 把博客站点设置为默认的 https 站点
    listen 443 ssl http2 default_server;
    
    if ($host != 'wujie.me') {
        rewrite ^/(.*)$ https://wujie.me/$1 permanent;
        break;
    }

    把直接从非域名来的请求(包括从 ip 来的请求)跳转到域名就可以了。

    这样修改后,经过两天的观察,没有再出现错误,问题解决!