From a4d3cdd612640403b05c5cc1993fd7d29933b3b1 Mon Sep 17 00:00:00 2001 From: Hanada Date: Sun, 11 May 2025 23:40:35 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=AF=81=E4=B9=A6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0AWS=20ACM=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/deploy/aws.php | 57 ++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/app/lib/deploy/aws.php b/app/lib/deploy/aws.php index 9aa40c4..a92220c 100644 --- a/app/lib/deploy/aws.php +++ b/app/lib/deploy/aws.php @@ -33,18 +33,9 @@ class aws implements DeployInterface if (empty($config['distribution_id'])) throw new Exception('分配ID不能为空'); $certInfo = openssl_x509_parse($fullchain, true); if (!$certInfo) throw new Exception('证书解析失败'); - $config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t']; - if (isset($info['cert_id']) && isset($info['cert_name']) && $info['cert_name'] == $config['cert_name']) { - $cert_id = $info['cert_id']; - $this->log('证书已上传:' . $cert_id); - } else { - $cert_id = $this->get_cert_id($fullchain, $privatekey); - $this->log('证书上传成功:' . $cert_id); - $info['cert_id'] = $cert_id; - $info['cert_name'] = $config['cert_name']; - usleep(500000); - } + $cert_id = $this->get_cert_id($fullchain, $privatekey, $info['cert_id']); + usleep(500000); $client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'cloudfront.amazonaws.com', 'cloudfront', '2020-05-31', 'us-east-1', $this->proxy); try { @@ -63,14 +54,49 @@ class aws implements DeployInterface $this->log('分配ID: ' . $config['distribution_id'] . ' 证书部署成功!'); } - private function get_cert_id($fullchain, $privatekey) + private function get_cert_id($fullchain, $privatekey, $cert_id = null) { - $cert = explode('-----END CERTIFICATE-----', $fullchain)[0] . '-----END CERTIFICATE-----'; + $client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'acm.us-east-1.amazonaws.com', 'acm', '', 'us-east-1', $this->proxy); + + if (!empty($cert_id)) { + try { + $data = $client->request('POST', 'CertificateManager.GetCertificate', [ + 'CertificateArn' => $cert_id + ]); + // 如果成功获取证书信息,说明证书存在,直接返回cert_id + if (isset($data['Certificate'])) { + $this->log('证书已存在:' . $cert_id); + return $cert_id; + } + } catch (Exception $e) { + $this->log('证书已被删除:' . $cert_id. ',准备重新上传'); + } + } + + $this->log('证书尚未存在,开始上传到AWS ACM'); + + $certificates = explode('-----END CERTIFICATE-----', $fullchain); + $cert = $certificates[0] . '-----END CERTIFICATE-----'; + $certificateChain = ''; + if (count($certificates) > 1) { + // 从第二个证书开始,重新拼接中间证书链 + for ($i = 1; $i < count($certificates); $i++) { + if (trim($certificates[$i]) !== '') { // 忽略空字符串(可能由末尾分割产生) + $certificateChain .= $certificates[$i] . '-----END CERTIFICATE-----'; + } + } + } + $param = [ 'Certificate' => base64_encode($cert), 'PrivateKey' => base64_encode($privatekey), ]; + // 如果有中间证书链,则添加到参数中 + if (!empty($certificateChain)) { + $param['CertificateChain'] = base64_encode($certificateChain); + } + $client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'acm.us-east-1.amazonaws.com', 'acm', '', 'us-east-1', $this->proxy); try { $data = $client->request('POST', 'CertificateManager.ImportCertificate', $param); @@ -78,6 +104,11 @@ class aws implements DeployInterface } catch (Exception $e) { throw new Exception('上传证书失败:' . $e->getMessage()); } + + $this->log('证书上传成功:' . $cert_id); + + $info['cert_id'] = $cert_id; + return $cert_id; } From dcc440c1f95c2fc2d85d923434e84f2ce5704d22 Mon Sep 17 00:00:00 2001 From: Hanada Date: Mon, 12 May 2025 00:02:16 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=9D=E6=AC=A1?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E8=AF=81=E4=B9=A6=E5=88=B0ACM=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/deploy/aws.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/lib/deploy/aws.php b/app/lib/deploy/aws.php index a92220c..73b0ccc 100644 --- a/app/lib/deploy/aws.php +++ b/app/lib/deploy/aws.php @@ -34,7 +34,8 @@ class aws implements DeployInterface $certInfo = openssl_x509_parse($fullchain, true); if (!$certInfo) throw new Exception('证书解析失败'); - $cert_id = $this->get_cert_id($fullchain, $privatekey, $info['cert_id']); + $cert_id = isset($info['cert_id']) ? $info['cert_id'] : null; + $cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_id, $config['cert_name']); usleep(500000); $client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'cloudfront.amazonaws.com', 'cloudfront', '2020-05-31', 'us-east-1', $this->proxy); From f8add88e3d9b6da02456a1dbc5197e0d4bc1703d Mon Sep 17 00:00:00 2001 From: Hanada Date: Mon, 12 May 2025 00:43:33 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E8=AF=81=E4=B9=A6=E5=88=B0AWS=20ACM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/DeployHelper.php | 14 ++++++++++++-- app/lib/deploy/aws.php | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/app/lib/DeployHelper.php b/app/lib/DeployHelper.php index 569eb37..851f765 100644 --- a/app/lib/DeployHelper.php +++ b/app/lib/DeployHelper.php @@ -1371,7 +1371,7 @@ class DeployHelper 'name' => 'AWS', 'class' => 2, 'icon' => 'aws.ico', - 'note' => '支持部署到Amazon CloudFront', + 'note' => '支持部署到Amazon CloudFront、AWS Certificate Manager', 'inputs' => [ 'AccessKeyId' => [ 'name' => 'AccessKeyId', @@ -1401,14 +1401,24 @@ class DeployHelper 'type' => 'select', 'options' => [ ['value'=>'cloudfront', 'label'=>'CloudFront'], + ['value'=>'acm', 'label'=>'AWS Certificate Manager'], ], - 'value' => 'cloudfront', + 'value' => 'acm', 'required' => true, ], 'distribution_id' => [ 'name' => '分配ID', 'type' => 'input', 'placeholder' => 'distributions id', + 'show' => 'product==\'cloudfront\'', + 'required' => true, + ], + 'acm_arn' => [ + 'name' => 'ACM ARN', + 'type' => 'input', + 'placeholder' => '', + 'show' => 'product==\'acm\'', + 'note' => '在AWS Certificate Manager控制台查看证书的ARN', 'required' => true, ], ], diff --git a/app/lib/deploy/aws.php b/app/lib/deploy/aws.php index 73b0ccc..1909668 100644 --- a/app/lib/deploy/aws.php +++ b/app/lib/deploy/aws.php @@ -29,6 +29,16 @@ class aws implements DeployInterface } public function deploy($fullchain, $privatekey, $config, &$info) + { + if ($config['product'] == 'acm') { + if (empty($config['acm_arn'])) throw new Exception('ACM ARN不能为空'); + $this->get_cert_id($fullchain, $privatekey, $config['acm_arn'], true); + } else { + $this->deploy_cloudfront($fullchain, $privatekey, $config, $info); + } + } + + private function deploy_cloudfront($fullchain, $privatekey, $config, &$info) { if (empty($config['distribution_id'])) throw new Exception('分配ID不能为空'); $certInfo = openssl_x509_parse($fullchain, true); @@ -55,8 +65,15 @@ class aws implements DeployInterface $this->log('分配ID: ' . $config['distribution_id'] . ' 证书部署成功!'); } - private function get_cert_id($fullchain, $privatekey, $cert_id = null) + private function get_cert_id($fullchain, $privatekey, $cert_id = null, $acm = false) { + if ($acm === true && $cert_id == null) { + throw new Exception('ACM ARN不能为空'); + } + + $certificates = explode('-----END CERTIFICATE-----', $fullchain); + $cert = $certificates[0] . '-----END CERTIFICATE-----'; + $client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'acm.us-east-1.amazonaws.com', 'acm', '', 'us-east-1', $this->proxy); if (!empty($cert_id)) { @@ -65,19 +82,20 @@ class aws implements DeployInterface 'CertificateArn' => $cert_id ]); // 如果成功获取证书信息,说明证书存在,直接返回cert_id - if (isset($data['Certificate'])) { - $this->log('证书已存在:' . $cert_id); + if (isset($data['Certificate']) && trim($data['Certificate']) == trim($cert)) { + $this->log('证书已是最新,ACM ARN:' . $cert_id); return $cert_id; + } else { + $this->log('证书已过期或被删除,准备更新或者重新上传'); } } catch (Exception $e) { + if ($acm === true) { + throw new Exception('获取证书信息失败,请检查ACM ARN是否正确:' . $e->getMessage()); + } $this->log('证书已被删除:' . $cert_id. ',准备重新上传'); } } - $this->log('证书尚未存在,开始上传到AWS ACM'); - - $certificates = explode('-----END CERTIFICATE-----', $fullchain); - $cert = $certificates[0] . '-----END CERTIFICATE-----'; $certificateChain = ''; if (count($certificates) > 1) { // 从第二个证书开始,重新拼接中间证书链 @@ -98,6 +116,11 @@ class aws implements DeployInterface $param['CertificateChain'] = base64_encode($certificateChain); } + // 如果是ACM,则添加ARN参数,用于更新证书 + if ($acm === true) { + $param['CertificateArn'] = $cert_id; + } + $client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'acm.us-east-1.amazonaws.com', 'acm', '', 'us-east-1', $this->proxy); try { $data = $client->request('POST', 'CertificateManager.ImportCertificate', $param);