SelfSiteSsl.php
8.0 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
<?php
namespace App\Console\Commands;
use App\Repositories\BtRepository;
use Illuminate\Console\Command;
class SelfSiteSsl extends Command
{
protected $signature = 'self_site_ssl';
protected $description = '自建站项目自动更新证书';
protected $bt_repository;
protected $bt;
public function __construct()
{
parent::__construct();
$this->bt_repository = new BtRepository();
$this->bt = $this->bt_repository->getBtObject();
}
public function handle()
{
try {
$this->checkDomainSsl();
} catch (\Exception $e) {
$this->output($e->getMessage());
}
}
public function checkDomainSsl()
{
$end_day = date('Y-m-d H:i:s', time() + 3 * 24 * 3600);//3天后到期
$site_domain = env('DOMAIN', '');
$site_ip = env('SITE_IP', '');
if (!$site_domain) {
throw new \Exception('项目主站域名未配置');
}
if (!$site_ip) {
throw new \Exception('项目主站IP未配置');
}
$ssl_time = $this->getDomainSslTime($site_domain);
if ($ssl_time['to'] < $end_day) {
//主站证书即将到期
$site_list = $this->bt->WebSiteList($site_domain);
if (isset($site_list['data']) && $site_list['data'] && $site_list['data'][0]['status'] == 1) {
$site_id = $site_list['data'][0]['id'];
$host = $site_list['data'][0]['name'];
//获取站点可用于设置证书的域名
$site_domain = $this->bt->WebDoaminList($site_id);
$apply_ssl_domain_list = [];
foreach ($site_domain as $val) {
if (strpos($val['name'], '*') === false && $this->check_domain_record($val['name'], ['ip' => $site_ip])) {
$apply_ssl_domain_list[] = $val['name'];
}
}
if (empty($apply_ssl_domain_list)) {
throw new \Exception('主站所有域名都未解析在当前服务器');
}
//申请证书之前,还原主站配置
$config_before = file_get_contents(public_path('main_site_default.txt'));
$re_config_before = $this->bt->SaveFileBody('/www/server/panel/vhost/nginx/' . $host . '.conf', $config_before, 'utf-8', 1);
if (!($re_config_before['status'] ?? false)) {
throw new \Exception($re_config_before['msg'] ?? '还原主站nginx配置失败');
}
//设置站点证书
$this->setDomainSsl($site_id, $host, $apply_ssl_domain_list);
//申请证书之后,更新主站配置
$config_after = file_get_contents(public_path('main_site_config.txt'));
$re_config_after = $this->bt->SaveFileBody('/www/server/panel/vhost/nginx/' . $host . '.conf', $config_after, 'utf-8', 1);
if (!($re_config_after['status'] ?? false)) {
throw new \Exception($re_config_after['msg'] ?? '更新主站nginx配置失败');
}
$this->output('主站证书更新成功');
}
}
$amp_domain = env('AMP_DOMAIN', '');
if ($amp_domain) {
$amp_ssl_time = $this->getDomainSslTime($amp_domain);
if ($amp_ssl_time['to'] < $end_day) {
//AMP证书即将到期
$amp_site_list = $this->bt->WebSiteList($amp_domain);
if (isset($amp_site_list['data']) && $amp_site_list['data'] && $amp_site_list['data'][0]['status'] == 1) {
$amp_site_id = $amp_site_list['data'][0]['id'];
$amp_host = $amp_site_list['data'][0]['name'];
//设置站点证书
$this->setDomainSsl($amp_site_id, $amp_host, [$amp_host]);
$this->output('AMP站证书更新成功');
}
}
}
}
/**
* 检查域名解析师是否正确
* @param $domain
* @param $server_info
* @return bool
* @author Akun
* @date 2025/01/13 14:53
*/
public function check_domain_record($domain, $server_info)
{
try {
$records = dns_get_record($domain, DNS_A);
if (count($records) != 1) {
return false;
}
$record = $records[0];
if ($record['host'] == $server_info['domain'] || $record['ip'] == $server_info['ip']) {
return $domain;
} else {
return false;
}
} catch (\Exception $e) {
return false;
}
}
/**
* 设置域名证书
* @param $site_id
* @param $host
* @param $domain_list
* @param string $key
* @param string $cer
* @throws \Exception
* @author Akun
* @date 2025/01/13 14:53
*/
public function setDomainSsl($site_id, $host, $domain_list, $key = '', $cer = '')
{
if (empty($key) || empty($cer)) {
$ssl = $this->bt->GetSSL($host);
if (isset($ssl['cert_data']['notAfter']) && strtotime($ssl['cert_data']['notAfter']) - time() > 259200) {
// 如果已经申请了ssl证书, 并且证书有效期超过3天, 那么就使用已经申请好的证书
$key = $ssl['key'];
$cer = $ssl['csr'];
$is_set_status = !$ssl['status'];
} else {
$re_apply_cert = $this->bt->ApplyCert(json_encode($domain_list), $site_id);
if (!($re_apply_cert['status'] ?? false)) {
$apply_error_msg = '申请免费证书失败';
if (isset($re_apply_cert['msg'])) {
if (is_array($re_apply_cert['msg'])) {
$apply_error_msg = json_encode($re_apply_cert['msg']);
} else {
$apply_error_msg = $re_apply_cert['msg'];
}
}
throw new \Exception($apply_error_msg);
}
$key = $re_apply_cert['private_key'];
$cer = $re_apply_cert['cert'];
$is_set_status = true;
}
} else {
$is_set_status = true;
}
if ($key && $cer && $is_set_status) {
$re_set_ssl = $this->bt->SetSSL(1, $host, $key, $cer);
if (!($re_set_ssl['status'] ?? false)) {
throw new \Exception($re_set_ssl['msg'] ?? '设置证书失败');
}
}
}
/**
* 获取域名证书有效时间
* @param $domain
* @return string[]
* @author Akun
* @date 2024/08/29 9:59
*/
public function getDomainSslTime($domain)
{
$valid_from = '';
$valid_to = '';
try {
$context = stream_context_create([
'ssl' => [
'capture_peer_cert' => true,
'capture_peer_cert_chain' => false,
'verify_peer' => false,
'verify_peer_name' => false
],
]);
$stream = stream_socket_client('ssl://' . $domain . ':443', $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);
if ($stream) {
$remote_cert = stream_context_get_params($stream)['options']['ssl']['peer_certificate'];
if ($remote_cert) {
$valid_from = date('Y-m-d H:i:s', openssl_x509_parse($remote_cert)['validFrom_time_t']);
$valid_to = date('Y-m-d H:i:s', openssl_x509_parse($remote_cert)['validTo_time_t']);
}
}
fclose($stream);
} catch (\Exception $e) {
$valid_from = '';
$valid_to = '';
}
return ['from' => $valid_from, 'to' => $valid_to];
}
/**
* 输出处理日志
* @param $message
*/
public function output($message)
{
echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
}
}