Skip to content

Commit 83973fe

Browse files
committed
Initial version
0 parents  commit 83973fe

File tree

7 files changed

+348
-0
lines changed

7 files changed

+348
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/dist
2+
/node_modules

README.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# GitHub Action for Aliyun certificate deployment
2+
3+
Deploy SSL certificate to Aliyun Certificates Service (and use in CDN).
4+
5+
# Usage
6+
7+
> If you need to issue SSL certificates automatically, you can use [my acme.sh action](https://github.com/marketplace/actions/issue-ssl-certificate).
8+
9+
This action will deploy your PEM-formatted SSL certificate to Aliyun Certificates Service. And then set to use this certificate in CDN (optional).
10+
11+
According to Aliyun API, both Access Keys and STS Token are accepted as credentials.
12+
13+
```yaml
14+
jobs:
15+
deploy-to-aliyun:
16+
name: Deploy certificate to Aliyun
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Check out
20+
uses: actions/checkout@v2
21+
with:
22+
# If you just commited and pushed your newly issued certificate to this repo in a previous job,
23+
# use `ref` to make sure checking out the newest commit in this job
24+
ref: ${{ github.ref }}
25+
- uses: Menci/deploy-certificate-to-aliyun@beta-v1
26+
with:
27+
# Use Access Key
28+
access-key-id: ${{ secrets.ALIYUN_ACCESS_KEY_ID }}
29+
access-key-secret: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }}
30+
# Or use STS Token
31+
# security-token: ${{ secrets.ALIYUN_SECURITY_TOKEN }}
32+
33+
# Specify PEM fullchain file
34+
fullchain-file: ${{ env.FILE_FULLCHAIN }}
35+
# Specify PEM private key file
36+
key-file: ${{ env.FILE_KEY }}
37+
38+
# The name in Aliyun Certificates Service
39+
# Will replace the old one with the same name
40+
certificate-name: My-SSL
41+
42+
# (Optional) Deploy to CDN
43+
cdn-domains: |
44+
cdn1.example.com
45+
cdn2.example.com
46+
```

action.yml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Deploy SSL certificate to Aliyun
2+
description: Deploy SSL certificate to Aliyun Certificates Service (and use in CDN).
3+
branding:
4+
icon: lock
5+
color: green
6+
inputs:
7+
access-key-id:
8+
description: The accessKeyId used to authenticate with Aliyun. Please specify `access-key-id` and `access-key-secret` or specify `security-token`.
9+
default: ''
10+
access-key-secret:
11+
description: The accessKeySecret used to authenticate with Aliyun. Please specify `access-key-id` and `access-key-secret` or specify `security-token`.
12+
default: ''
13+
security-token:
14+
description: The securityToken (STS) used to authenticate with Aliyun. Please specify `access-key-id` and `access-key-secret` or specify `security-token`.
15+
default: ''
16+
fullchain-file:
17+
description: The file path of the PEM fullchain certificate.
18+
required: true
19+
key-file:
20+
description: The file path of the PEM private key file.
21+
required: true
22+
certificate-name:
23+
description: Set the name of deployed certificate in Aliyun. Will replace the old certificate with the same name.
24+
required: true
25+
cdn-domains:
26+
description: Enter a list of CDN domains you want to deploy the certificate to.
27+
required: true
28+
runs:
29+
using: node12
30+
main: bootstrap.js

bootstrap.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const child_process = require("child_process");
2+
child_process.execSync("yarn", { stdio: "inherit", cwd: __dirname });
3+
4+
require("./index");

index.js

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
const fs = require("fs");
2+
3+
const core = require("@actions/core");
4+
5+
const AliyunClient = require('@alicloud/pop-core');
6+
7+
const input = {
8+
accessKeyId: core.getInput("access-key-id"),
9+
accessKeySecret: core.getInput("access-key-secret"),
10+
securityToken: core.getInput("security-token"),
11+
fullchainFile: core.getInput("fullchain-file"),
12+
keyFile: core.getInput("key-file"),
13+
certificateName: core.getInput("certificate-name"),
14+
cdnDomains: core.getInput("cdn-domains")
15+
};
16+
17+
/**
18+
* @param {string} endpoint
19+
* @param {string} apiVersion
20+
* @param {string} action
21+
* @param {Record<string, unknown>} params
22+
*/
23+
function callAliyunApi(endpoint, apiVersion, action, params) {
24+
return new AliyunClient({
25+
...input.accessKeyId && input.accessKeySecret ? {
26+
accessKeyId: input.accessKeyId,
27+
accessKeySecret: input.accessKeySecret
28+
} : {},
29+
...input.securityToken ? {
30+
securityToken: input.securityToken
31+
} : {},
32+
endpoint,
33+
apiVersion
34+
}).request(action, params, { method: "POST" });
35+
}
36+
37+
async function deletePreviouslyDeployedCertificate() {
38+
/**
39+
* @typedef CertificateListItem
40+
* @prop {number} id
41+
* @prop {string} name
42+
*
43+
* @typedef DescribeUserCertificateListResponse
44+
* @prop {number} TotalCount
45+
* @prop {CertificateListItem[]} CertificateList
46+
*/
47+
48+
/**
49+
* @param {(item: CertificateListItem) => Promise<void>} callback
50+
*/
51+
async function listCertificates(callback) {
52+
let currentItems = 0;
53+
54+
for (let i = 1; ; i++) {
55+
/**
56+
* @type {DescribeUserCertificateListResponse}
57+
*/
58+
const response = await callAliyunApi(
59+
"https://cas.aliyuncs.com", "2018-07-13",
60+
"DescribeUserCertificateList",
61+
{
62+
ShowSize: 50,
63+
CurrentPage: i
64+
}
65+
);
66+
67+
for (const item of response.CertificateList)
68+
await callback(item);
69+
70+
currentItems += response.CertificateList.length;
71+
if (currentItems === response.TotalCount) break;
72+
}
73+
}
74+
75+
let foundId = 0;
76+
await listCertificates(async item => {
77+
if (item.name === input.certificateName) {
78+
foundId = item.id;
79+
}
80+
});
81+
82+
if (foundId === 0) {
83+
console.log("Previously deployed certificate not found. Skipping delete.");
84+
return;
85+
}
86+
87+
console.log(`Found previously deployed certificate ${foundId}. Deleting.`);
88+
89+
await callAliyunApi(
90+
"https://cas.aliyuncs.com", "2018-07-13",
91+
"DeleteUserCertificate",
92+
{
93+
CertId: foundId
94+
}
95+
);
96+
}
97+
98+
async function deployCertificate() {
99+
const fullchain = fs.readFileSync(input.fullchainFile, "utf-8");
100+
const key = fs.readFileSync(input.keyFile, "utf-8");
101+
102+
await deletePreviouslyDeployedCertificate();
103+
104+
await callAliyunApi(
105+
"https://cas.aliyuncs.com", "2018-07-13",
106+
"CreateUserCertificate",
107+
{
108+
Cert: fullchain,
109+
Key: key,
110+
Name: input.certificateName
111+
}
112+
);
113+
}
114+
115+
async function deployCertificateToCdn() {
116+
const domains = Array.from(new Set(input.cdnDomains.split(/\s+/).filter(x => x)));
117+
118+
for (const domain of domains) {
119+
console.log(`Deploying certificate to CDN domain ${domain}.`);
120+
121+
await callAliyunApi(
122+
"https://cdn.aliyuncs.com", "2018-05-10",
123+
"SetDomainServerCertificate",
124+
{
125+
DomainName: domain,
126+
CertName: input.certificateName,
127+
CertType: "cas",
128+
ServerCertificateStatus: "on",
129+
ForceSet: "1"
130+
}
131+
);
132+
}
133+
}
134+
135+
async function main() {
136+
await deployCertificate();
137+
138+
if (input.cdnDomains) await deployCertificateToCdn();
139+
}
140+
141+
main().catch(error => {
142+
console.log(error.stack);
143+
core.setFailed(error);
144+
process.exit(1);
145+
});

package.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "deploy-certificate-to-aliyun",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"@actions/core": "^1.6.0",
14+
"@alicloud/pop-core": "^1.7.10"
15+
}
16+
}

yarn.lock

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
"@actions/core@^1.6.0":
6+
version "1.6.0"
7+
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.6.0.tgz#0568e47039bfb6a9170393a73f3b7eb3b22462cb"
8+
integrity sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==
9+
dependencies:
10+
"@actions/http-client" "^1.0.11"
11+
12+
"@actions/http-client@^1.0.11":
13+
version "1.0.11"
14+
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-1.0.11.tgz#c58b12e9aa8b159ee39e7dd6cbd0e91d905633c0"
15+
integrity sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==
16+
dependencies:
17+
tunnel "0.0.6"
18+
19+
"@alicloud/pop-core@^1.7.10":
20+
version "1.7.10"
21+
resolved "https://registry.yarnpkg.com/@alicloud/pop-core/-/pop-core-1.7.10.tgz#d0e221036dbb0ccde453dd09b1cfabd8341b7a69"
22+
integrity sha512-9/aLWgmgaAdB1ERNTpdOvF7wueLY5CDTRxKZr93x542iuYRA1NDpcKslFqLOy5CUOa0CbopET3JGaHSAz5qv9g==
23+
dependencies:
24+
debug "^3.1.0"
25+
httpx "^2.1.2"
26+
json-bigint "^1.0.0"
27+
kitx "^1.2.1"
28+
xml2js "^0.4.17"
29+
30+
"@types/node@^14":
31+
version "14.17.27"
32+
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.27.tgz#5054610d37bb5f6e21342d0e6d24c494231f3b85"
33+
integrity sha512-94+Ahf9IcaDuJTle/2b+wzvjmutxXAEXU6O81JHblYXUg2BDG+dnBy7VxIPHKAyEEDHzCMQydTJuWvrE+Aanzw==
34+
35+
bignumber.js@^9.0.0:
36+
version "9.0.1"
37+
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5"
38+
integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==
39+
40+
debug@^3.1.0:
41+
version "3.2.7"
42+
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
43+
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
44+
dependencies:
45+
ms "^2.1.1"
46+
47+
debug@^4.1.1:
48+
version "4.3.2"
49+
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
50+
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
51+
dependencies:
52+
ms "2.1.2"
53+
54+
httpx@^2.1.2:
55+
version "2.2.7"
56+
resolved "https://registry.yarnpkg.com/httpx/-/httpx-2.2.7.tgz#1e34198146e32ca3305a66c11209559e1cbeba09"
57+
integrity sha512-Wjh2JOAah0pdczfqL8NC5378G7jMt0Zcpn8U+yyxAiejjlagzSTQgJHuVvka2VNPQlKfoGehYRc79WKq9E4gDw==
58+
dependencies:
59+
"@types/node" "^14"
60+
debug "^4.1.1"
61+
62+
json-bigint@^1.0.0:
63+
version "1.0.0"
64+
resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1"
65+
integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==
66+
dependencies:
67+
bignumber.js "^9.0.0"
68+
69+
kitx@^1.2.1:
70+
version "1.3.0"
71+
resolved "https://registry.yarnpkg.com/kitx/-/kitx-1.3.0.tgz#ab3ee7c598d2b1d629fd55568f868c4440c200ea"
72+
integrity sha512-fhBqFlXd0GkKTB+8ayLfpzPUw+LHxZlPAukPNBD1Om7JMeInT+/PxCAf1yLagvD+VKoyWhXtJR68xQkX/a0wOQ==
73+
74+
75+
version "2.1.2"
76+
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
77+
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
78+
79+
ms@^2.1.1:
80+
version "2.1.3"
81+
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
82+
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
83+
84+
sax@>=0.6.0:
85+
version "1.2.4"
86+
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
87+
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
88+
89+
90+
version "0.0.6"
91+
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
92+
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
93+
94+
xml2js@^0.4.17:
95+
version "0.4.23"
96+
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
97+
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
98+
dependencies:
99+
sax ">=0.6.0"
100+
xmlbuilder "~11.0.0"
101+
102+
xmlbuilder@~11.0.0:
103+
version "11.0.1"
104+
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
105+
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==

0 commit comments

Comments
 (0)