Merge remote-tracking branch 'origin/v2' into v2

pull/189/head
xiaojunnuo 2024-09-05 18:01:04 +08:00
commit af388ec39f
14 changed files with 124 additions and 42 deletions

View File

@ -1,34 +1,37 @@
version: '3.3'
services:
certd:
# 镜像 # ↓↓↓↓↓ --- 1、 镜像版本号,建议改成固定版本号【可选】
# 镜像 # ↓↓↓↓↓ --- 镜像版本号,建议改成固定版本号【可选】
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
container_name: certd # 容器名
restart: unless-stopped # 自动重启
volumes:
# ↓↓↓↓↓ ------------------------------------------------------- 2、 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下【可选】
# ↓↓↓↓↓ -------------------------------------------------------- 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下【可选】
- /data/certd:/app/data
ports: # 端口映射
# ↓↓↓↓ ----------------------------------------------------------3、如果端口有冲突可以修改第一个7001为其他不冲突的端口号【可选】
# ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突可以修改第一个7001为其他不冲突的端口号【可选】
- "7001:7001"
dns:
# 如果出现getaddrinfo ENOTFOUND等错误可以尝试修改或注释dns配置
- 223.5.5.5
- 223.6.6.6
# ↓↓↓↓ ----------------------------------------------------------如果你服务器部署在国外可以用8.8.8.8替换上面的dns【可选】
# ↓↓↓↓ ---------------------------------------------------------- 如果你服务器部署在国外可以用8.8.8.8替换上面的dns【可选】
# - 8.8.8.8
# - 8.8.4.4
environment: # 环境变量
- TZ=Asia/Shanghai
#- HTTPS_PROXY=http://xxxxxx:xx
#- HTTP_PROXY=http://xxxxxx:xx
# ↑↑↑↑↑ ------------------------------------- 这里可以设置http代理【可选】
- certd_system_resetAdminPasswd=false
# ↑↑↑↑↑---------------------------4、如果忘记管理员密码可以设置为true重启之后管理员密码将改成123456然后请及时修改回false【可选】
# ↑↑↑↑↑--------------------------- 如果忘记管理员密码可以设置为true重启之后管理员密码将改成123456然后请及时修改回false【可选】
- certd_cron_immediateTriggerOnce=false
# ↑↑↑↑↑---------------------------5、如果设置为true启动后所有配置了cron的流水线任务都将被立即触发一次【可选】
# ↑↑↑↑↑--------------------------- 如果设置为true启动后所有配置了cron的流水线任务都将被立即触发一次【可选】
- VITE_APP_ICP_NO=
# ↑↑↑↑↑ -----------------------------------------6、这里可以设置备案号【可选】
# ↑↑↑↑↑ ----------------------------------------- 这里可以设置备案号【可选】
#- certd_koa_key=./data/ssl/cert.key
#- certd_koa_cert=./data/ssl/cert.crt
# ↑↑↑↑↑ -----------------------------------------7、配置证书和key则表示https方式启动访问网址要使用 https://your.domain:7001【可选】
# ↑↑↑↑↑ ----------------------------------------- 配置证书和key则表示https方式启动访问网址要使用 https://your.domain:7001【可选】
# 设置环境变量即可自定义certd配置
# 服务端配置项见: packages/ui/certd-server/src/config/config.default.ts

20
init.sh Normal file
View File

@ -0,0 +1,20 @@
current_pwd=$(pwd)
echo "开始设置git配置"
read -p "请输入username" username
git config user.name $username
read -p "请输入email" email
git config user.email $email
git config credential.helper "store --file=$current_pwd/.git/credential.store"
echo "已设置记住git账号密码"
git config core.autocrlf input
echo "已设置auto crlf = input"
git config core.filemode false
echo "已设置忽略文件模式变化"
echo "git配置完成"

View File

@ -24,7 +24,7 @@
"license": "AGPL-3.0",
"dependencies": {
"axios": "^1.7.2",
"lodash": "^4.17.21"
"lodash-es": "^4.17.21"
},
"workspaces": [
"packages/**"

View File

@ -111,7 +111,7 @@ async function verifyDnsChallenge(authz, challenge, keyAuthorization, prefix = '
log(`DNS query finished successfully, found ${recordValues.length} TXT records`);
if (!recordValues.length || !recordValues.includes(keyAuthorization)) {
throw new Error(`Authorization not found in DNS TXT record: ${recordName}`);
throw new Error(`Authorization not found in DNS TXT record: ${recordName}need:${keyAuthorization},found:${recordValues}`);
}
log(`Key authorization match for ${challenge.type}/${recordName}, ACME challenge verified`);

View File

@ -66,6 +66,8 @@ async function spawn(opts: SpawnOption): Promise<string> {
cmd = item;
}
}
}else{
cmd = opts.cmd
}
log.info(`执行命令: ${cmd}`);
let stdout = "";

View File

@ -42,10 +42,35 @@ export class CertConvertPlugin extends AbstractTaskPlugin {
name: "a-input-password",
vModel: "value",
},
required: true,
required: false,
})
pfxPassword!: string;
@TaskInput({
title: "输出PFX",
value:true,
component: {
name: "a-switch",
vModel: "checked",
},
required: true,
})
pfxEnabled: boolean = true;
@TaskInput({
title: "输出DER",
value:true,
component: {
name: "a-switch",
vModel: "checked",
},
required: true,
})
derEnabled: boolean = true;
@TaskOutput({
title: "pfx格式证书",
type: "PfxCert",
@ -64,11 +89,19 @@ export class CertConvertPlugin extends AbstractTaskPlugin {
const certReader = new CertReader(this.cert);
const handle = async (opts: CertReaderHandleContext) => {
if(this.pfxEnabled){
// 调用openssl 转pfx
await this.convertPfx(opts);
}else{
this.logger.info("pfx证书已禁用");
}
if(this.pfxEnabled){
// 转der
await this.convertDer(opts);
}else{
this.logger.info("der证书已禁用");
}
};
await certReader.readCertFile({ logger: this.logger, handle });
@ -86,6 +119,11 @@ export class CertConvertPlugin extends AbstractTaskPlugin {
const pfxPath = path.join(os.tmpdir(), "/certd/tmp/", Math.floor(Math.random() * 1000000) + "", "cert.pfx");
const dir = path.dirname(pfxPath)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
let passwordArg = "-passout pass:";
if (this.pfxPassword) {
passwordArg = `-password pass:${this.pfxPassword}`;
@ -102,6 +140,13 @@ export class CertConvertPlugin extends AbstractTaskPlugin {
private async convertDer(opts: CertReaderHandleContext) {
const { reader, tmpCrtPath } = opts;
const derPath = path.join(os.tmpdir(), "/certd/tmp/", Math.floor(Math.random() * 1000000) + "", `cert.der`);
const dir = path.dirname(derPath)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
await this.exec(`openssl x509 -outform der -in ${tmpCrtPath} -out ${derPath}`);
this.derCert = derPath;

View File

@ -4,6 +4,7 @@ import os from "os";
import path from "path";
import { crypto } from "@certd/acme-client";
import { ILogger } from "@certd/pipeline";
import dayjs from "dayjs";
export type CertReaderHandleContext = { reader: CertReader; tmpCrtPath: string; tmpKeyPath: string };
export type CertReaderHandle = (ctx: CertReaderHandleContext) => Promise<void>;
@ -78,6 +79,7 @@ export class CertReader implements CertInfo {
const detail = this.getCrtDetail();
let domain = detail.detail.domains.commonName;
domain = domain.replace(".", "_").replace("*", "_");
return `${prefix}_${domain}_${applyTime}.${suffix}`;
const timeStr = dayjs(applyTime).format("YYYYMMDDHHmmss");
return `${prefix}_${domain}_${timeStr}.${suffix}`;
}
}

View File

@ -48,7 +48,7 @@
"cron-parser": "^4.9.0",
"dayjs": "^1.11.7",
"glob": "^10.4.5",
"https-proxy-agent": "^7.0.4",
"https-proxy-agent": "^7.0.5",
"iconv-lite": "^0.6.3",
"js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.0",

View File

@ -1,6 +1,7 @@
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { Autowire, ILogger } from '@certd/pipeline';
import { AliyunAccess } from '@certd/plugin-plus';
import { AliyunAccess, AliyunClient } from '@certd/plugin-plus';
@IsDnsProvider({
name: 'aliyun',
title: '阿里云',
@ -15,13 +16,14 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
logger!: ILogger;
async onInstance() {
const access: any = this.access;
const Core = await import('@alicloud/pop-core');
this.client = new Core.default({
this.client = new AliyunClient({logger:this.logger})
await this.client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
endpoint: 'https://alidns.aliyuncs.com',
apiVersion: '2015-01-09',
});
})
}
//
// async getDomainList() {
@ -99,6 +101,7 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
// Line: 'oversea' // 海外
};
const requestOption = {
method: 'POST',
};

View File

@ -1,5 +1,5 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, utils } from '@certd/pipeline';
import { AliyunAccess } from '@certd/plugin-plus';
import { AliyunAccess, AliyunClient } from '@certd/plugin-plus';
import { appendTimeSuffix } from '../../utils/index.js';
import { CertInfo } from '@certd/plugin-cert';
@ -200,14 +200,15 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractTaskPlugin {
}
async getClient(aliyunProvider: any, regionId: string) {
const Core = await import('@alicloud/pop-core');
return new Core.default({
const client = new AliyunClient({logger:this.logger})
await client.init({
accessKeyId: aliyunProvider.accessKeyId,
accessKeySecret: aliyunProvider.accessKeySecret,
endpoint: `https://cs.${regionId}.aliyuncs.com`,
apiVersion: '2015-12-15',
});
})
return client
}
async getKubeConfig(client: any, clusterId: string, isPrivateIpAddress = false) {

View File

@ -1,6 +1,6 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import dayjs from 'dayjs';
import { AliyunAccess } from "@certd/plugin-plus";
import { AliyunAccess, AliyunClient } from "@certd/plugin-plus";
@IsTaskPlugin({
name: 'DeployCertToAliyunCDN',
title: '部署证书至阿里云CDN',
@ -59,14 +59,14 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
}
async getClient(access: AliyunAccess) {
const Core = await import('@alicloud/pop-core');
return new Core.default({
const client = new AliyunClient({logger:this.logger})
await client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
endpoint: 'https://cdn.aliyuncs.com',
apiVersion: '2018-05-10',
});
})
return client
}
async buildParams() {

View File

@ -1,6 +1,6 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import dayjs from 'dayjs';
import { AliyunAccess } from "@certd/plugin-plus";
import { AliyunAccess, AliyunClient } from "@certd/plugin-plus";
@IsTaskPlugin({
name: 'DeployCertToAliyunDCDN',
title: '部署证书至阿里云DCDN',
@ -59,13 +59,14 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
}
async getClient(access: AliyunAccess) {
const sdk = await import('@alicloud/pop-core');
return new sdk.default({
const client = new AliyunClient({logger:this.logger})
await client.init({
accessKeyId: access.accessKeyId,
accessKeySecret: access.accessKeySecret,
endpoint: 'https://dcdn.aliyuncs.com',
apiVersion: '2018-01-15',
});
})
return client
}
async buildParams() {

View File

@ -1,6 +1,6 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
import { appendTimeSuffix, checkRet } from '../../utils/index.js';
import { AliyunAccess } from "@certd/plugin-plus";
import { AliyunAccess, AliyunClient } from "@certd/plugin-plus";
@IsTaskPlugin({
name: 'uploadCertToAliyun',
@ -86,13 +86,14 @@ export class UploadCertToAliyun extends AbstractTaskPlugin {
}
async getClient(aliyunProvider: AliyunAccess) {
const Core = await import('@alicloud/pop-core');
return new Core.default({
const client = new AliyunClient({logger:this.logger})
await client.init({
accessKeyId: aliyunProvider.accessKeyId,
accessKeySecret: aliyunProvider.accessKeySecret,
endpoint: 'https://cas.aliyuncs.com',
apiVersion: '2018-07-13',
});
})
return client
}
}
//注册插件

View File

@ -21,6 +21,7 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
component: {
placeholder: './tmp/cert.pem',
},
required: true,
})
crtPath!: string;
@TaskInput({
@ -29,6 +30,7 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
component: {
placeholder: './tmp/cert.key',
},
required: true,
})
keyPath!: string;
@TaskInput({
@ -36,7 +38,7 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'pi-output-selector',
from: 'CertApply',
from: ['CertApply','CertConvert'],
},
required: true,
})
@ -44,11 +46,13 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
@TaskOutput({
title: '证书保存路径',
type:"HostCrtPath"
})
hostCrtPath!: string;
@TaskOutput({
title: '私钥保存路径',
type:"HostKeyPath"
})
hostKeyPath!: string;