lnmp 环境下使用 acme.sh 签发Let's Encrypt 出现错误解决记录
最近在使用 lnmp脚本工具包 的时候 里面的acme.sh脚本签发 Let’s Encrypt证书报错
Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: 28
根据网上搜索,说是参数换成强制wget就行了,经过测试发现一样不行,于是开始了折腾之路。
经过反复测试发现,在境外的主机上操作都是正常的,但是境内就不行,难道是网络缘故?
思来想去,还是把签发这个事放在境外吧。
于是新建一台境外实例,部署好lnmp,修改 php-fpm的配置文件 用户名和用户组都改成root (避免权限问题)然后 lnmp stop php-fpm
修改/etc/init.d/php-fpm 这个文件给php-fpm 添加一个启动参数
php_opts=" -R –allow-to-run-as-root –fpm-config $php_fpm_CONF –pid $php_fpm_PID"
修改/usr/local/php/etc/php.ini
在disable_functions 里面去掉exec
修改/usr/local/nginx/conf/fastcgi.conf
fastcgi_param PHP_ADMIN_VALUE “open_basedir=$document_root/:/tmp/:/proc/:/usr/local/nginx/conf/ssl/”;
添加:/usr/local/nginx/conf/ssl/
准备工作就绪(lnmp php-fpm start && lnmp nginx reload),假设这台机器的域名是 ca.com 解析放在阿里云
在终端添加ssl虚拟主机,使用lnmp dns ali 然后根据lnmp的提示,添加好主机
再添加要帮忙签的域名,使用 lnmp onlyssl ali ,例如这里添加 abc.com
然后在/usr/local/nginx/conf/ssl/ 目录下可以找到对应域名的证书,我们只需要把文件复制到需要的服务器上就可以了,一般来说scp就行,但我采用了PHP更新的方案,所以这里要上代码了
服务端,也就是ca.com服务器上部署
<?php
if(empty($_POST['domain']) || !is_valid_domain_name($_POST['domain']))
{
exit;
}
//获取域名的解析记录
$ip = dns_get_record($_POST['domain'],DNS_A);
if(isset($ip[0]) && isset($ip[0]['ip']))
{
$ip = $ip[0]['ip'];
}else
{
exit;
}
//判断访问来源和域名是否一致
if($ip !== $_SERVER['REMOTE_ADDR'])
{
echo 3;
exit;
}
//验证通过后开始打包证书文件
$base = '/usr/local/nginx/conf/ssl/';
$path = $base . $_POST['domain'] . '/';
if(file_exists($path))
{
exec("cd '{$path}' && tar -czf {$_POST['domain']}.tgz ca.cer fullchain.cer {$_POST['domain']}.cer {$_POST['domain']}.csr {$_POST['domain']}.csr.conf {$_POST['domain']}.key");
//计算hash
$new_md5 = md5_file($path.$_POST['domain'].'.tgz');
if($new_md5 !== $_POST['md5'])
{
send_file($path.$_POST['domain'].'.tgz');
}
echo 4;
}
//验证域名合法性
function is_valid_domain_name($domain_name)
{
return(
preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name) //valid chars check
&& preg_match("/^.{1,253}$/", $domain_name) //overall length check
&& preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name) //length of each label
);
}
//发送文件
function send_file($file)
{
$size = filesize($file);
header("Content-type: application/octet-stream");
header("Accept-Ranges: bytes");
$begin = 0;
$end = $size - 1;
header("Content-Length: " . ($end - $begin + 1));
header("Content-Disposition: attachment;filename=".basename($file));
header("Content-Range: bytes ".$begin."-".$end."/".$size);
echo file_get_contents($file);
}
然后在abc.com这台机器上(前提是部署好了lnmp网站,创建了ssl配置,由于是基础这里不在赘述)创建 dnsca.php文件 内容如下
<?php
if(!isset($argv[1])) exit;
if(is_valid_domain_name($argv[1]))
{
$domain_name = $argv[1];
}
//目录不在就新建
$path = '/usr/local/nginx/conf/ssl/'.$domain_name.'/';
if(!file_exists($path))
{
@mkdir($path,0777,true);
}
//文件hash
$now_hash = "0";
if(file_exists($path.$domain_name.'.tgz'))
{
$now_hash = md5_file($path.$domain_name.'.tgz');
}
$now_hash = "0";
exec(' cd \''.$path.'\' && wget --post-data "domain='.urlencode($domain_name).'&md5='.$now_hash.'" --no-check-certificate -O '.$domain_name.'.tgz https://ca.com/ca.php');
$size = filesize($path.$domain_name.'.tgz');
if($size>1000)
{
exec('cd \''.$path.'\' && tar -xzf '.$domain_name.'.tgz && lnmp nginx reload');
}
//验证域名合法性
function is_valid_domain_name($domain_name)
{
return(
preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name) //valid chars check
&& preg_match("/^.{1,253}$/", $domain_name) //overall length check
&& preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name) //length of each label
);
}
设置好文件可执行权限,然后执行
/usr/bin/php dnsca.php abc.com
也可以设置成 crontab 任务
PS
这个方案太麻烦了,我后面不用了,就是做个记录,现在是在服务器上装7zip 用 shell 定期加密打包证书文件并且移动到 http 目录下,然后需要下载的服务器用 wget 下回来以后再解压到对应目录,全程使用 shell 和 crontab 。由于证书使用通配符签发,也就不需要验证域名了,出于安全考虑,把 http 目录设置了 IP 白名单。