mirror of
https://github.com/certd/certd.git
synced 2026-04-03 14:10:54 +08:00
perf: 支持aws route53 dns
This commit is contained in:
@@ -16,9 +16,8 @@ Certd® 是一个免费的全自动证书管理系统,让你的网站证书永
|
||||
|
||||
> 流水线数量现已调整为无限制,欢迎大家使用
|
||||
|
||||
官方开源地址:
|
||||
|
||||
| | |
|
||||
|官方开源地址: | |
|
||||
| ---- | ---- |
|
||||
| [Github](https://github.com/certd/certd)|  |
|
||||
| [Gitee](https://gitee.com/certd/certd) |  |
|
||||
|
||||
@@ -5,8 +5,8 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
|
||||
|
||||
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
|
||||
|
||||
官方开源地址:
|
||||
| | |
|
||||
|
||||
| 官方开源地址: | |
|
||||
| ---- | ---- |
|
||||
| [Github](https://github.com/certd/certd)|  |
|
||||
| [Gitee](https://gitee.com/certd/certd) |  |
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
services:
|
||||
certd:
|
||||
environment: # 环境变量
|
||||
- certd_system_resetAdminPasswd=false
|
||||
- certd_system_resetAdminPasswd=true
|
||||
```
|
||||
## 2. 重启容器
|
||||
```shell
|
||||
docker compose up -d
|
||||
docker logs -f --tail 500 certd
|
||||
# 观察日志,当日志中输出“重置1号管理员用户的密码完成”,即可操作下一步
|
||||
# 观察日志,当日志中输出“重置1号管理员用户密码完成”,即可操作下一步
|
||||
# 这里会打印1号管理员记录的用户名,如果你修改过管理员用户名,请注意查看此条日志
|
||||
```
|
||||
## 3. 恢复环境变量
|
||||
修改docker-compose.yaml,将`certd_system_resetAdminPasswd`改回`false`
|
||||
@@ -23,4 +24,6 @@ docker logs -f --tail 500 certd
|
||||
docker compose up -d
|
||||
```
|
||||
## 5. 默认密码登录
|
||||
使用`admin/123456`登录系统,请及时修改管理员密码
|
||||
使用`原管理员账号/123456`登录系统,请及时修改管理员密码
|
||||
> 默认管理员账号: admin
|
||||
> 如果忘记管理员账号,请查看修改密码时的启动日志,会打印管理员账号名
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=production node --optimize-for-size ./bootstrap.js",
|
||||
"dev-start": "cross-env NODE_ENV=dev & mwtsc --watch --run @midwayjs/mock/app",
|
||||
"dev-start": "cross-env NODE_ENV=dev & mwtsc --watch --run @midwayjs/mock/app",
|
||||
"dc": "cd ../../../ && pnpm run dev",
|
||||
"dev": "cross-env NODE_ENV=local & pnpm run dev-start",
|
||||
"dev-commlocal": "cross-env NODE_ENV=dev-commlocal mwtsc --watch --run @midwayjs/mock/app",
|
||||
@@ -44,6 +44,7 @@
|
||||
"@aws-sdk/client-acm": "^3.699.0",
|
||||
"@aws-sdk/client-cloudfront": "^3.699.0",
|
||||
"@aws-sdk/client-iam": "^3.699.0",
|
||||
"@aws-sdk/client-route-53": "^3.957.0",
|
||||
"@aws-sdk/client-s3": "^3.705.0",
|
||||
"@certd/acme-client": "^1.37.16",
|
||||
"@certd/basic": "^1.37.16",
|
||||
|
||||
@@ -31,6 +31,20 @@ process.on('uncaughtException', error => {
|
||||
}
|
||||
});
|
||||
|
||||
// function startHeapLog() {
|
||||
// function format(bytes: any) {
|
||||
// return (bytes / 1024 / 1024).toFixed(2) + ' MB';
|
||||
// }
|
||||
// function log() {
|
||||
// const mu = process.memoryUsage();
|
||||
// logger.info(`rss:${format(mu.rss)},heapUsed: ${format(mu.heapUsed)},heapTotal: ${format(mu.heapTotal)},external: ${format(mu.external)}`);
|
||||
// }
|
||||
// setInterval(log, 200);
|
||||
// log()
|
||||
// }
|
||||
|
||||
// startHeapLog();
|
||||
|
||||
@Configuration({
|
||||
detectorOptions: {
|
||||
ignore: [
|
||||
@@ -64,6 +78,9 @@ export class MainConfiguration {
|
||||
app: koa.Application;
|
||||
|
||||
async onReady() {
|
||||
|
||||
|
||||
|
||||
// add middleware
|
||||
// this.app.useMiddleware([ReportMiddleware]);
|
||||
// add filter
|
||||
|
||||
@@ -64,6 +64,18 @@ export class AwsAccess extends BaseAccess {
|
||||
helper: '请妥善保管您的安全访问密钥。您可以在AWS管理控制台的IAM中创建新的访问密钥。',
|
||||
})
|
||||
secretAccessKey = '';
|
||||
|
||||
@AccessInput({
|
||||
title: 'region',
|
||||
component: {
|
||||
name:"a-select",
|
||||
options: AwsRegions,
|
||||
},
|
||||
required: true,
|
||||
helper: '请选择您的默认AWS区域,默认us-east-1',
|
||||
options: AwsRegions,
|
||||
})
|
||||
region = '';
|
||||
}
|
||||
|
||||
new AwsAccess();
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
|
||||
import { AwsClient } from './libs/aws-client.js';
|
||||
import { AwsAccess } from './access.js';
|
||||
|
||||
|
||||
@IsDnsProvider({
|
||||
name: 'aws-route53',
|
||||
title: 'AWS Route53',
|
||||
desc: 'AWS Route53 DNS解析提供商',
|
||||
accessType: 'aws',
|
||||
icon: 'svg:icon-aws',
|
||||
order:0,
|
||||
})
|
||||
export class AwsRoute53Provider extends AbstractDnsProvider {
|
||||
|
||||
client: AwsClient;
|
||||
async onInstance() {
|
||||
const access: AwsAccess = this.ctx.access as AwsAccess
|
||||
this.client = new AwsClient({ access: access, logger: this.logger, region:access.region || 'us-east-1' });
|
||||
}
|
||||
|
||||
async createRecord(options: CreateRecordOptions): Promise<any> {
|
||||
const { fullRecord, value, type, domain } = options;
|
||||
this.logger.info('添加域名解析:', fullRecord, value, domain);
|
||||
// const domain = await this.matchDomain(fullRecord);
|
||||
|
||||
const {ZoneId,ZoneName} = await this.client.route53GetHostedZoneId(domain);
|
||||
this.logger.info(`获取到hostedZoneId:${ZoneId},name:${ZoneName},domain:${domain}`);
|
||||
|
||||
await this.client.route53ChangeRecord({
|
||||
hostedZoneId: ZoneId,
|
||||
fullRecord: fullRecord,
|
||||
type: type,
|
||||
value: value,
|
||||
action: 'CREATE',
|
||||
});
|
||||
return {
|
||||
hostedZoneId: ZoneId,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async removeRecord(options: RemoveRecordOptions<any>): Promise<any> {
|
||||
const { fullRecord, value,type } = options.recordReq;
|
||||
const record = options.recordRes;
|
||||
const hostedZoneId = record.hostedZoneId;
|
||||
|
||||
try{
|
||||
await this.client.route53ChangeRecord({
|
||||
hostedZoneId: hostedZoneId,
|
||||
fullRecord: fullRecord,
|
||||
type: type,
|
||||
value: value,
|
||||
action: 'DELETE',
|
||||
});
|
||||
}catch(e){
|
||||
this.logger.warn(`删除域名解析失败:${e.message} : ${hostedZoneId} ${fullRecord} ${value} ${type} `, );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new AwsRoute53Provider();
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './plugins/index.js';
|
||||
export * from './access.js';
|
||||
export * from './aws-route53-provider.js';
|
||||
@@ -1,40 +0,0 @@
|
||||
// 导入所需的 SDK 模块
|
||||
import { AwsAccess } from '../access.js';
|
||||
import { CertInfo } from '@certd/plugin-cert';
|
||||
|
||||
type AwsAcmClientOptions = { access: AwsAccess; region: string };
|
||||
|
||||
export class AwsAcmClient {
|
||||
options: AwsAcmClientOptions;
|
||||
access: AwsAccess;
|
||||
region: string;
|
||||
constructor(options: AwsAcmClientOptions) {
|
||||
this.options = options;
|
||||
this.access = options.access;
|
||||
this.region = options.region;
|
||||
}
|
||||
async importCertificate(certInfo: CertInfo) {
|
||||
// 创建 ACM 客户端
|
||||
const { ACMClient, ImportCertificateCommand } = await import('@aws-sdk/client-acm');
|
||||
const acmClient = new ACMClient({
|
||||
region: this.region, // 替换为您的 AWS 区域
|
||||
credentials: {
|
||||
accessKeyId: this.access.accessKeyId, // 从环境变量中读取
|
||||
secretAccessKey: this.access.secretAccessKey,
|
||||
},
|
||||
});
|
||||
|
||||
const cert = certInfo.crt.split('-----END CERTIFICATE-----')[0] + '-----END CERTIFICATE-----';
|
||||
// 构建上传参数
|
||||
const data = await acmClient.send(
|
||||
new ImportCertificateCommand({
|
||||
Certificate: Buffer.from(cert),
|
||||
PrivateKey: Buffer.from(certInfo.key),
|
||||
// CertificateChain: certificateChain, // 可选
|
||||
})
|
||||
);
|
||||
console.log('Upload successful:', data);
|
||||
// 返回证书 ARN(Amazon Resource Name)
|
||||
return data.CertificateArn;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// 导入所需的 SDK 模块
|
||||
import { AwsAccess } from '../access.js';
|
||||
import { CertInfo } from '@certd/plugin-cert';
|
||||
import {ILogger, utils} from '@certd/basic';
|
||||
type AwsClientOptions = { access: AwsAccess; region: string, logger:ILogger };
|
||||
|
||||
export class AwsClient {
|
||||
options: AwsClientOptions;
|
||||
access: AwsAccess;
|
||||
region: string;
|
||||
logger: ILogger;
|
||||
constructor(options: AwsClientOptions) {
|
||||
this.options = options;
|
||||
this.access = options.access;
|
||||
this.region = options.region;
|
||||
this.logger = options.logger;
|
||||
}
|
||||
async importCertificate(certInfo: CertInfo) {
|
||||
// 创建 ACM 客户端
|
||||
const { ACMClient, ImportCertificateCommand } = await import('@aws-sdk/client-acm');
|
||||
const acmClient = new ACMClient({
|
||||
region: this.region, // 替换为您的 AWS 区域
|
||||
credentials: {
|
||||
accessKeyId: this.access.accessKeyId, // 从环境变量中读取
|
||||
secretAccessKey: this.access.secretAccessKey,
|
||||
},
|
||||
});
|
||||
|
||||
const cert = certInfo.crt.split('-----END CERTIFICATE-----')[0] + '-----END CERTIFICATE-----';
|
||||
// 构建上传参数
|
||||
const data = await acmClient.send(
|
||||
new ImportCertificateCommand({
|
||||
Certificate: Buffer.from(cert),
|
||||
PrivateKey: Buffer.from(certInfo.key),
|
||||
// CertificateChain: certificateChain, // 可选
|
||||
})
|
||||
);
|
||||
console.log('Upload successful:', data);
|
||||
// 返回证书 ARN(Amazon Resource Name)
|
||||
return data.CertificateArn;
|
||||
}
|
||||
|
||||
|
||||
async route53ClientGet() {
|
||||
const { Route53Client } = await import('@aws-sdk/client-route-53');
|
||||
return new Route53Client({
|
||||
region: this.region,
|
||||
credentials: {
|
||||
accessKeyId: this.access.accessKeyId, // 从环境变量中读取
|
||||
secretAccessKey: this.access.secretAccessKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async route53GetHostedZoneId(name:string) :Promise<{ZoneId:string,ZoneName:string}> {
|
||||
const hostedZones = await this.route53ListHostedZones(name);
|
||||
const zoneId = hostedZones[0].Id.replace('/hostedzone/','');
|
||||
this.logger.info(`获取到hostedZoneId:${zoneId},name:${hostedZones[0].Name}`);
|
||||
return {
|
||||
ZoneId: zoneId,
|
||||
ZoneName: hostedZones[0].Name,
|
||||
};
|
||||
}
|
||||
async route53ListHostedZones(name:string) :Promise<{Id:string,Name:string}[]> {
|
||||
const { ListHostedZonesByNameCommand } =await import("@aws-sdk/client-route-53"); // ES Modules import
|
||||
|
||||
const client = await this.route53ClientGet();
|
||||
const input = { // ListHostedZonesByNameRequest
|
||||
DNSName: name,
|
||||
};
|
||||
const command = new ListHostedZonesByNameCommand(input);
|
||||
const response = await this.doRequest(()=>client.send(command));
|
||||
if (response.HostedZones.length === 0) {
|
||||
throw new Error(`找不到 HostedZone ${name}`);
|
||||
}
|
||||
this.logger.info(`获取到hostedZoneId:${JSON.stringify(response.HostedZones)}`);
|
||||
return response.HostedZones;
|
||||
}
|
||||
|
||||
async route53ChangeRecord(req:{
|
||||
hostedZoneId:string,fullRecord:string,type:string, value:string, action:"CREATE"|"DELETE"}){
|
||||
const { ChangeResourceRecordSetsCommand} =await import("@aws-sdk/client-route-53"); // ES Modules import
|
||||
// const { Route53Client, ChangeResourceRecordSetsCommand } = require("@aws-sdk/client-route-53"); // CommonJS import
|
||||
// import type { Route53ClientConfig } from "@aws-sdk/client-route-53";
|
||||
const client = await this.route53ClientGet();
|
||||
|
||||
const appendBody:any = {}
|
||||
if(req.action === 'CREATE'){
|
||||
appendBody.TTL = 60;
|
||||
}
|
||||
const input = { // ChangeResourceRecordSetsRequest
|
||||
HostedZoneId: req.hostedZoneId, // required
|
||||
ChangeBatch: { // ChangeBatch
|
||||
Changes: [ // Changes // required
|
||||
{ // Change
|
||||
Action: req.action as any , // required
|
||||
ResourceRecordSet: { // ResourceRecordSet
|
||||
Name: req.fullRecord+".", // required
|
||||
Type: req.type.toUpperCase() as any,
|
||||
ResourceRecords: [ // ResourceRecords
|
||||
{ // ResourceRecord
|
||||
Value: `"${req.value}"`, // required
|
||||
},
|
||||
],
|
||||
...appendBody
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
this.logger.info(`添加域名解析参数:${JSON.stringify(input)}`);
|
||||
const command = new ChangeResourceRecordSetsCommand(input);
|
||||
const response = await this.doRequest(()=>client.send(command));
|
||||
console.log('Add record successful:', JSON.stringify(response));
|
||||
await utils.sleep(3000);
|
||||
return response;
|
||||
/*
|
||||
// { // ChangeResourceRecordSetsResponse
|
||||
// ChangeInfo: { // ChangeInfo
|
||||
// Id: "STRING_VALUE", // required
|
||||
// Status: "PENDING" || "INSYNC", // required
|
||||
// SubmittedAt: new Date("TIMESTAMP"), // required
|
||||
// Comment: "STRING_VALUE",
|
||||
// },
|
||||
// };*/
|
||||
}
|
||||
|
||||
async doRequest<T>(call:()=>Promise<T>):Promise<T>{
|
||||
try{
|
||||
return await call();
|
||||
}catch(err){
|
||||
this.logger.error(`调用接口失败:${err.Error?.Message || err.message},requestId:${err.requestId}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
||||
import { AwsAccess, AwsRegions } from "../access.js";
|
||||
import { AwsAcmClient } from "../libs/aws-acm-client.js";
|
||||
import { AwsClient } from "../libs/aws-client.js";
|
||||
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
||||
import { optionsUtils } from "@certd/basic";
|
||||
|
||||
@@ -115,9 +115,10 @@ export class AwsDeployToCloudFront extends AbstractTaskPlugin {
|
||||
}
|
||||
|
||||
private async uploadToACM(access: AwsAccess, cert: CertInfo) {
|
||||
const acmClient = new AwsAcmClient({
|
||||
const acmClient = new AwsClient({
|
||||
access,
|
||||
region: this.region,
|
||||
logger: this.logger,
|
||||
});
|
||||
const awsCertARN = await acmClient.importCertificate(cert);
|
||||
this.logger.info('证书上传成功,id=', awsCertARN);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
|
||||
import { CertInfo } from '@certd/plugin-cert';
|
||||
import { AwsAccess, AwsRegions } from '../access.js';
|
||||
import { AwsAcmClient } from '../libs/aws-acm-client.js';
|
||||
import { AwsClient } from '../libs/aws-client.js';
|
||||
import { CertApplyPluginNames} from '@certd/plugin-cert';
|
||||
@IsTaskPlugin({
|
||||
name: 'AwsUploadToACM',
|
||||
@@ -59,9 +59,10 @@ export class AwsUploadToACM extends AbstractTaskPlugin {
|
||||
async execute(): Promise<void> {
|
||||
const { cert, accessId, region } = this;
|
||||
const access = await this.getAccess<AwsAccess>(accessId);
|
||||
const acmClient = new AwsAcmClient({
|
||||
const acmClient = new AwsClient({
|
||||
access,
|
||||
region,
|
||||
logger: this.logger,
|
||||
});
|
||||
this.awsCertARN = await acmClient.importCertificate(cert);
|
||||
this.logger.info('证书上传成功,id=', this.awsCertARN);
|
||||
|
||||
1252
pnpm-lock.yaml
generated
1252
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user