mirror of
https://github.com/acmesh-official/acme.sh
synced 2025-11-24 09:49:50 +08:00
Compare commits
768 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50d7b8452d | ||
|
|
d88f8e862b | ||
|
|
fd72cced13 | ||
|
|
a6feb0a887 | ||
|
|
cd9c3a79e5 | ||
|
|
f3685bdef9 | ||
|
|
e2edf20833 | ||
|
|
31e39f1f6d | ||
|
|
d47c5eaf60 | ||
|
|
b8e515a738 | ||
|
|
0984585d58 | ||
|
|
f3e0eb705b | ||
|
|
04e0f87c03 | ||
|
|
8ccfcd272f | ||
|
|
ba69062a36 | ||
|
|
cea24b5f70 | ||
|
|
f3a71f62a1 | ||
|
|
4e4a6d8397 | ||
|
|
d8beaf727f | ||
|
|
c961c865ee | ||
|
|
34f25fa590 | ||
|
|
7af1155c11 | ||
|
|
c9d7daab70 | ||
|
|
981077516b | ||
|
|
7c488b5913 | ||
|
|
0e44f587a5 | ||
|
|
e593d86b80 | ||
|
|
339a8ad610 | ||
|
|
d1190febec | ||
|
|
5d2c5b01a8 | ||
|
|
0f954c20ff | ||
|
|
d6edff3182 | ||
|
|
dba26c3240 | ||
|
|
0ca5b7996c | ||
|
|
63e1d3610c | ||
|
|
b4325026b1 | ||
|
|
b072ef2e82 | ||
|
|
562a4c056e | ||
|
|
bb8248556d | ||
|
|
b22b085b50 | ||
|
|
a50252382a | ||
|
|
9bdb799b41 | ||
|
|
259cefab72 | ||
|
|
09ed421948 | ||
|
|
87f5ec5be5 | ||
|
|
f78b656f5f | ||
|
|
e6b940e247 | ||
|
|
50a9680f17 | ||
|
|
f2a6dc4dfd | ||
|
|
67e3dd36b3 | ||
|
|
6c4cc357b5 | ||
|
|
94965a418f | ||
|
|
40e6ba1100 | ||
|
|
0aed065a75 | ||
|
|
70b63a5ed4 | ||
|
|
1476a9ecf1 | ||
|
|
600a235140 | ||
|
|
ffa9f104db | ||
|
|
b807ec0956 | ||
|
|
5f8daeeb6d | ||
|
|
aec8307417 | ||
|
|
e96a1b01b9 | ||
|
|
d78ba322bf | ||
|
|
ab5c1b0a3a | ||
|
|
53fa16d39f | ||
|
|
05cf405cb5 | ||
|
|
5d833336d3 | ||
|
|
13be9d8d79 | ||
|
|
bb6326f4d4 | ||
|
|
c070407ab2 | ||
|
|
6b27bdadcd | ||
|
|
1c22c2f76a | ||
|
|
aa262d4124 | ||
|
|
646c0bfcb9 | ||
|
|
da68775472 | ||
|
|
bcf96608d1 | ||
|
|
dd17ac5045 | ||
|
|
59182dbc97 | ||
|
|
11df7187b3 | ||
|
|
0899803294 | ||
|
|
4742e0951c | ||
|
|
c3b1eb0837 | ||
|
|
542d7977db | ||
|
|
11927a768e | ||
|
|
ecd5bcec0c | ||
|
|
911a2084d4 | ||
|
|
839bf0e2c9 | ||
|
|
25555b8c3e | ||
|
|
cd7ac94f35 | ||
|
|
68aea3af9e | ||
|
|
cdb3216cc9 | ||
|
|
78f0201dfa | ||
|
|
2cd47b0240 | ||
|
|
be83a6a37a | ||
|
|
8ca99e85b0 | ||
|
|
f5b546b3c8 | ||
|
|
50c9d17830 | ||
|
|
d87af4788e | ||
|
|
0f21537f14 | ||
|
|
0533bde9f2 | ||
|
|
32b3717c32 | ||
|
|
d950f801af | ||
|
|
a8ab028b84 | ||
|
|
2aff36e74b | ||
|
|
db7e4bf940 | ||
|
|
ee20015d44 | ||
|
|
80941f8413 | ||
|
|
cf909db159 | ||
|
|
27dbe77fad | ||
|
|
38f2334360 | ||
|
|
6489a48e1f | ||
|
|
ca24d1762e | ||
|
|
2f4b84c074 | ||
|
|
6ae810a1fa | ||
|
|
3e3f695536 | ||
|
|
3ca93f4a4c | ||
|
|
f2d9930773 | ||
|
|
2fbf399156 | ||
|
|
5fe91d6577 | ||
|
|
753d0e7df7 | ||
|
|
e2cc350fbc | ||
|
|
07feb87dee | ||
|
|
1699e94f0f | ||
|
|
8a1e335bf5 | ||
|
|
5413bf8753 | ||
|
|
c4c5ecd03d | ||
|
|
3b67cf4378 | ||
|
|
f3c937f9ed | ||
|
|
671a699472 | ||
|
|
cc4fa1c6bd | ||
|
|
d11d476126 | ||
|
|
1ce06c7cdd | ||
|
|
800f02ba38 | ||
|
|
79964b0e85 | ||
|
|
0924d11bbf | ||
|
|
3cf85634eb | ||
|
|
ec01175d37 | ||
|
|
5bb518ff0f | ||
|
|
5415381cf4 | ||
|
|
129ae06354 | ||
|
|
058e5d5f4b | ||
|
|
2ec493f033 | ||
|
|
a33e8a8509 | ||
|
|
7b4be7be40 | ||
|
|
b90917a529 | ||
|
|
180f05f6f0 | ||
|
|
b2686e5b6d | ||
|
|
831b95f13a | ||
|
|
331b599a81 | ||
|
|
3ef817ebc2 | ||
|
|
d0f7c309ab | ||
|
|
e5079b9dad | ||
|
|
9e773c9143 | ||
|
|
fe600441c9 | ||
|
|
291b56db93 | ||
|
|
ec9975c3d9 | ||
|
|
9e57161941 | ||
|
|
20444bf253 | ||
|
|
e428c4402e | ||
|
|
15af89d51c | ||
|
|
ed95865696 | ||
|
|
8c71bd57e7 | ||
|
|
d204be4b2c | ||
|
|
4743171b4f | ||
|
|
1fb800f7a7 | ||
|
|
3a3b0dd5c1 | ||
|
|
2959281d42 | ||
|
|
e2c939fb97 | ||
|
|
51d0e4325e | ||
|
|
08b6cf0231 | ||
|
|
059147b74a | ||
|
|
c96ceeb1bd | ||
|
|
0a3b6c4813 | ||
|
|
8636a15695 | ||
|
|
96db9362c5 | ||
|
|
f81f93e9c3 | ||
|
|
a3a04b5f76 | ||
|
|
329174b6d9 | ||
|
|
b171aff418 | ||
|
|
1f6ffa3e88 | ||
|
|
24d2a8b9d5 | ||
|
|
4d8b99a307 | ||
|
|
f9085fedd7 | ||
|
|
c4236e58d1 | ||
|
|
eb91f9575a | ||
|
|
947d14ffeb | ||
|
|
b3f915b5b2 | ||
|
|
03d3f3afc3 | ||
|
|
2728170c01 | ||
|
|
c107f3632a | ||
|
|
7d1c5fca0b | ||
|
|
e71887e1a8 | ||
|
|
39d1eeda23 | ||
|
|
b482f3ce94 | ||
|
|
122cc48c29 | ||
|
|
c7c57cfa0e | ||
|
|
1f9ca20dfd | ||
|
|
5efbaa4849 | ||
|
|
c6f55bc4ab | ||
|
|
b28a3db3d6 | ||
|
|
b9ece28f68 | ||
|
|
79b4907cb9 | ||
|
|
dfbc244b00 | ||
|
|
b23d5e26e2 | ||
|
|
f1f3074306 | ||
|
|
f162ad193f | ||
|
|
5254f300f9 | ||
|
|
c0d0100ca8 | ||
|
|
21f201e371 | ||
|
|
bbc378ed8f | ||
|
|
e2d494321c | ||
|
|
c2df8043db | ||
|
|
df62150b5a | ||
|
|
eea52a5fa6 | ||
|
|
2d9015b840 | ||
|
|
187ef29914 | ||
|
|
c12be766e9 | ||
|
|
2168458f11 | ||
|
|
8cf0725593 | ||
|
|
8379f015d7 | ||
|
|
f08c01800f | ||
|
|
a62706678e | ||
|
|
e4468562d2 | ||
|
|
7316bf7bc5 | ||
|
|
3747b7d930 | ||
|
|
5be3f22d06 | ||
|
|
c572ce946b | ||
|
|
d69f0289ca | ||
|
|
048e5210f7 | ||
|
|
219e9115c0 | ||
|
|
b43416af97 | ||
|
|
6d84da588b | ||
|
|
569d6c557c | ||
|
|
58bb94d7c7 | ||
|
|
4f8f775e69 | ||
|
|
5eed02f7e9 | ||
|
|
983f1f28ca | ||
|
|
192ede5e64 | ||
|
|
803a7aa878 | ||
|
|
93f3098aec | ||
|
|
dbd94d047b | ||
|
|
be39ab32d1 | ||
|
|
8b1fb3cb0c | ||
|
|
9e04222ee6 | ||
|
|
72349507c4 | ||
|
|
79db8daddd | ||
|
|
b967f83f20 | ||
|
|
74a7592b4b | ||
|
|
486f23538f | ||
|
|
df86ff2191 | ||
|
|
41266f05e9 | ||
|
|
d2aa331838 | ||
|
|
670cb9d223 | ||
|
|
a205762bf0 | ||
|
|
2ffab66d97 | ||
|
|
10a6aec998 | ||
|
|
7cbe31baad | ||
|
|
8a09dc1b9b | ||
|
|
fa6e174651 | ||
|
|
8dee328eae | ||
|
|
340155e6a6 | ||
|
|
7fe19a030a | ||
|
|
11e0ccdc91 | ||
|
|
e55b2f4f8d | ||
|
|
d78c1f695e | ||
|
|
cfbb3e86b3 | ||
|
|
fa6234e417 | ||
|
|
9eeae9ad7e | ||
|
|
28688488ff | ||
|
|
41d804719f | ||
|
|
bc18168662 | ||
|
|
1fadae82c7 | ||
|
|
791c62ca64 | ||
|
|
4441a6ff59 | ||
|
|
63a7002477 | ||
|
|
a746139c53 | ||
|
|
697a060132 | ||
|
|
7834c25253 | ||
|
|
2e7cbfcff5 | ||
|
|
5b771039fc | ||
|
|
2f1bc5864f | ||
|
|
16d79ebaac | ||
|
|
e009ec8b93 | ||
|
|
738ece513f | ||
|
|
5a7b7b51c5 | ||
|
|
5bba9bddfd | ||
|
|
a3c0c7546d | ||
|
|
05d7ae4efa | ||
|
|
44483dba21 | ||
|
|
3498a5856a | ||
|
|
cd79b0343a | ||
|
|
abba00d595 | ||
|
|
88a8d7d901 | ||
|
|
d5c00071d3 | ||
|
|
c7a6cf5463 | ||
|
|
2ce2a15fc6 | ||
|
|
ce4be4e91e | ||
|
|
82dc2244c0 | ||
|
|
ab45b7783f | ||
|
|
4a56b2406b | ||
|
|
e0cd2cdb9c | ||
|
|
7575094cf3 | ||
|
|
40a3ae04b6 | ||
|
|
ac26f84170 | ||
|
|
fa574fe833 | ||
|
|
9a6e18ce80 | ||
|
|
8f9034fc8b | ||
|
|
3e5b102445 | ||
|
|
d8b1a8d439 | ||
|
|
54ae008dd7 | ||
|
|
9c1747581f | ||
|
|
ae4f7b6d7a | ||
|
|
c947322a69 | ||
|
|
14db45215f | ||
|
|
d20831e41a | ||
|
|
1bb902984b | ||
|
|
09f1c58872 | ||
|
|
7f944c2c8b | ||
|
|
1c02b85802 | ||
|
|
a2801649b4 | ||
|
|
15777732d3 | ||
|
|
b875037150 | ||
|
|
a6d583cb5e | ||
|
|
9882f3429a | ||
|
|
29d47c4de2 | ||
|
|
ce38ecb966 | ||
|
|
13ffa17048 | ||
|
|
a387682dfb | ||
|
|
f6ed197cd3 | ||
|
|
243593cdaa | ||
|
|
1e2d559859 | ||
|
|
20ea859183 | ||
|
|
86b24ea059 | ||
|
|
07af42476d | ||
|
|
87edf71e93 | ||
|
|
e3ea2ed420 | ||
|
|
8f9a1881a4 | ||
|
|
a8c6111197 | ||
|
|
3de8570022 | ||
|
|
a0311b0134 | ||
|
|
158a628c0e | ||
|
|
422e5026d6 | ||
|
|
797cbb9b20 | ||
|
|
be68fbd4f5 | ||
|
|
e440223b40 | ||
|
|
5766250288 | ||
|
|
870274ad9d | ||
|
|
9d6abcd9be | ||
|
|
432771dfe3 | ||
|
|
69925ce823 | ||
|
|
e9f9f515bd | ||
|
|
efd96153d8 | ||
|
|
c7b8f223ee | ||
|
|
a0636d5a87 | ||
|
|
c7b16249b8 | ||
|
|
031e885e4d | ||
|
|
796e2cc156 | ||
|
|
7af784adce | ||
|
|
a988a91e2e | ||
|
|
a8b564fa64 | ||
|
|
b97e140389 | ||
|
|
e51bef6d12 | ||
|
|
4bd31f4967 | ||
|
|
7ff7a7c527 | ||
|
|
c4a375b3a5 | ||
|
|
e3698edd19 | ||
|
|
e591d5cfe4 | ||
|
|
a2e62f8e1d | ||
|
|
4da493f3f3 | ||
|
|
dec90f7e5e | ||
|
|
2ea5b283a8 | ||
|
|
eb23549cd6 | ||
|
|
cd90062850 | ||
|
|
3ec72fcee9 | ||
|
|
b9091e14b3 | ||
|
|
5daefc0194 | ||
|
|
44edb2bd2f | ||
|
|
201aa24448 | ||
|
|
e799ef2977 | ||
|
|
0c538f7527 | ||
|
|
ca7202eb0a | ||
|
|
d5ec5f80ff | ||
|
|
0c944a03fe | ||
|
|
79a267ab08 | ||
|
|
f9a6988ece | ||
|
|
95e06de5de | ||
|
|
f530a5074b | ||
|
|
439580b91b | ||
|
|
4c2a384159 | ||
|
|
cfd0b040e4 | ||
|
|
b001840dee | ||
|
|
20a6ab3d1a | ||
|
|
21f16b50f3 | ||
|
|
f58e83ee87 | ||
|
|
a9b4a2a1ac | ||
|
|
5be1449db5 | ||
|
|
37d7f89c6d | ||
|
|
57e58ce76c | ||
|
|
1d9f76e2c8 | ||
|
|
56e0269e5e | ||
|
|
662df85e54 | ||
|
|
2be4a5e486 | ||
|
|
a43d697225 | ||
|
|
f06c1e6c78 | ||
|
|
b5d1918401 | ||
|
|
d018be5d36 | ||
|
|
67184d7b20 | ||
|
|
dfdd48b990 | ||
|
|
cae9cee295 | ||
|
|
067d586c1c | ||
|
|
d22b7938da | ||
|
|
29b7510957 | ||
|
|
9d548d81ac | ||
|
|
3c33cdfa3d | ||
|
|
288485b209 | ||
|
|
1cbf416b10 | ||
|
|
3b46d3dd74 | ||
|
|
c243829234 | ||
|
|
fd80faa389 | ||
|
|
e7d4352292 | ||
|
|
610e0f21d6 | ||
|
|
7e512bad96 | ||
|
|
98c4ce0c45 | ||
|
|
93fc48a2db | ||
|
|
d7eea12a6f | ||
|
|
2a1e06f8a9 | ||
|
|
02d54a783a | ||
|
|
5961d44339 | ||
|
|
fb3be8509d | ||
|
|
97be53741d | ||
|
|
0d2c26735e | ||
|
|
2d0a8ddb58 | ||
|
|
72518d4827 | ||
|
|
c6cd744186 | ||
|
|
ae2db62f1c | ||
|
|
8c76b8bc36 | ||
|
|
18256c4923 | ||
|
|
f345cc66cf | ||
|
|
5dbf664a6b | ||
|
|
d7c6679d70 | ||
|
|
cae203be71 | ||
|
|
8a29fbc850 | ||
|
|
00bcbd367f | ||
|
|
1befee5aca | ||
|
|
5982f4bcf0 | ||
|
|
2399476a21 | ||
|
|
9e45ac939b | ||
|
|
34f231f9f3 | ||
|
|
f940b2a58e | ||
|
|
0383c33558 | ||
|
|
3afa4b210d | ||
|
|
998783eb9d | ||
|
|
54d61bdc4a | ||
|
|
2d279c4c5c | ||
|
|
0fb206fe15 | ||
|
|
5d6fd8099f | ||
|
|
800e3f4599 | ||
|
|
35c61f52fe | ||
|
|
3db446633c | ||
|
|
e3b4a0213c | ||
|
|
9910ff5fa1 | ||
|
|
049be10406 | ||
|
|
9aa3be7f9f | ||
|
|
aba5c634ae | ||
|
|
9774b01b0e | ||
|
|
ecf0a710e1 | ||
|
|
c9febbdd87 | ||
|
|
caa2e45a8c | ||
|
|
d9130c9852 | ||
|
|
661f05837c | ||
|
|
a61fe418b2 | ||
|
|
b15cfc2c5a | ||
|
|
c575c9af3d | ||
|
|
81f27e9077 | ||
|
|
f78babfaa0 | ||
|
|
66990cf872 | ||
|
|
095fe2ed1b | ||
|
|
483ebc8141 | ||
|
|
b9311282eb | ||
|
|
d6f0c2b52b | ||
|
|
bb25febd70 | ||
|
|
19ab2a29ce | ||
|
|
24b4fe9867 | ||
|
|
aa7b82de04 | ||
|
|
3d826bed3a | ||
|
|
d2ae7e78ef | ||
|
|
656bd330f8 | ||
|
|
c2c8f32010 | ||
|
|
9f43c270e6 | ||
|
|
50827188ff | ||
|
|
6ae0f7f5c6 | ||
|
|
c583d6bb49 | ||
|
|
fdcb6b721c | ||
|
|
ef858ef062 | ||
|
|
fe04faf675 | ||
|
|
ad752b317d | ||
|
|
59649e9b1e | ||
|
|
6bf281f905 | ||
|
|
4dfc8b262c | ||
|
|
f08ffe9feb | ||
|
|
65de3110a9 | ||
|
|
f6dcd98995 | ||
|
|
950172dc01 | ||
|
|
5c48e139d4 | ||
|
|
150e9c8a48 | ||
|
|
b19ba13aff | ||
|
|
30bfc2cea7 | ||
|
|
30c2d84c6d | ||
|
|
fbd2038fa7 | ||
|
|
e4b8d9b9d6 | ||
|
|
6b50003697 | ||
|
|
a73c5b3355 | ||
|
|
eb59817e81 | ||
|
|
d404e92d16 | ||
|
|
86c017ec9d | ||
|
|
0407c4e0f7 | ||
|
|
0ba95a3dd4 | ||
|
|
0463b5d6cd | ||
|
|
3f4513b3a9 | ||
|
|
fb2029e717 | ||
|
|
48f02fb61b | ||
|
|
0c00e870c6 | ||
|
|
7da50703fb | ||
|
|
cfdaff5a46 | ||
|
|
18e46962c2 | ||
|
|
319e0ae3cf | ||
|
|
e69a7c38d9 | ||
|
|
ecd685755b | ||
|
|
276b51d927 | ||
|
|
251d1c5c9f | ||
|
|
028e17475f | ||
|
|
1ab630435d | ||
|
|
89002ed298 | ||
|
|
d0871bdae3 | ||
|
|
5ea6e9c9c0 | ||
|
|
267f283a31 | ||
|
|
3324c0ae79 | ||
|
|
811bff6db0 | ||
|
|
0c9546ccb0 | ||
|
|
7b16442656 | ||
|
|
b0070f03af | ||
|
|
08ee072f11 | ||
|
|
f3e4cea34f | ||
|
|
2d12b68952 | ||
|
|
1643b476eb | ||
|
|
5980ebc79a | ||
|
|
8371b030cf | ||
|
|
10afcaca2f | ||
|
|
2fb4b62330 | ||
|
|
cbcd7e0f86 | ||
|
|
df1c9d88a8 | ||
|
|
3aae1ae3d9 | ||
|
|
775bd1abd0 | ||
|
|
78009539d1 | ||
|
|
36246ad9ac | ||
|
|
e2053b22b4 | ||
|
|
dfdc402fbb | ||
|
|
73ba54a502 | ||
|
|
690a5e205d | ||
|
|
a6014bf04e | ||
|
|
d4d1f0f4a9 | ||
|
|
5fbc47eb3a | ||
|
|
6d4e903b08 | ||
|
|
66f08eb236 | ||
|
|
933c169da5 | ||
|
|
a8df88ab91 | ||
|
|
df9547ae39 | ||
|
|
d529eb6d00 | ||
|
|
fac1e367c9 | ||
|
|
31a5487cba | ||
|
|
43822d37a7 | ||
|
|
527dd31c70 | ||
|
|
e3c66532c5 | ||
|
|
22ea4004e1 | ||
|
|
d190a43e0a | ||
|
|
bd5e57d879 | ||
|
|
08928b486b | ||
|
|
75da0713d2 | ||
|
|
d35bf51780 | ||
|
|
ec603beeb0 | ||
|
|
7820467d59 | ||
|
|
fe09757cb3 | ||
|
|
1e6b68f5d1 | ||
|
|
f574e5813f | ||
|
|
30684246d2 | ||
|
|
8bdf5cf854 | ||
|
|
c93ec9331b | ||
|
|
0886e67df7 | ||
|
|
30de13b4df | ||
|
|
7a60c14b31 | ||
|
|
3ad08e9515 | ||
|
|
32fdc19697 | ||
|
|
c9c31c04c3 | ||
|
|
c3dd3ef0d7 | ||
|
|
fa989a554d | ||
|
|
4a4dacb52c | ||
|
|
dcf9cb581d | ||
|
|
1a6305c93f | ||
|
|
bdbf323fee | ||
|
|
7270f277a7 | ||
|
|
32dfc387c8 | ||
|
|
b3752e78b6 | ||
|
|
4c0d3f1b75 | ||
|
|
2e3cb75530 | ||
|
|
3e99ffe692 | ||
|
|
096d8992a1 | ||
|
|
0bbe6eef89 | ||
|
|
d0b748a4f2 | ||
|
|
58f41a194a | ||
|
|
d8d10bc478 | ||
|
|
61623d22d8 | ||
|
|
bc96082fa9 | ||
|
|
52677b0a88 | ||
|
|
e4a1958573 | ||
|
|
523c7682fa | ||
|
|
87ab2d9085 | ||
|
|
687cfcc2b1 | ||
|
|
c8e9a31ee5 | ||
|
|
2ce87fe264 | ||
|
|
13d7cae9e2 | ||
|
|
8814a348a8 | ||
|
|
5aa146a53f | ||
|
|
cc17973113 | ||
|
|
423966a505 | ||
|
|
fbad6a392d | ||
|
|
331c4bb669 | ||
|
|
63a195e5cc | ||
|
|
e22bcf7cb4 | ||
|
|
869578ce4a | ||
|
|
5fc5016d2c | ||
|
|
22e7ba3f7b | ||
|
|
dcf4f8f64e | ||
|
|
d07e8f91c4 | ||
|
|
054cb72e55 | ||
|
|
0e38c60da3 | ||
|
|
6d7eda3e8d | ||
|
|
ee1737a52e | ||
|
|
432c589722 | ||
|
|
c083e078d4 | ||
|
|
9003525210 | ||
|
|
bf233fbd48 | ||
|
|
cc7fdbd661 | ||
|
|
199067e8ab | ||
|
|
d65e3d9a6e | ||
|
|
ec9fc8cbf7 | ||
|
|
9aaf36cd0c | ||
|
|
c4d8fd83d4 | ||
|
|
a272ee4f59 | ||
|
|
7012b91f05 | ||
|
|
16679b572f | ||
|
|
6e18034333 | ||
|
|
b7ec6789b3 | ||
|
|
c835ab025a | ||
|
|
39c8f79f3e | ||
|
|
eae290992f | ||
|
|
bb276fc985 | ||
|
|
8d5618c44a | ||
|
|
8f63baf7e4 | ||
|
|
78768e985b | ||
|
|
2ee5d873db | ||
|
|
6cd3a8a549 | ||
|
|
5778811a18 | ||
|
|
4e1f39cdec | ||
|
|
2c554a4bbb | ||
|
|
8fb9a709b0 | ||
|
|
484d9d2ad8 | ||
|
|
1953957506 | ||
|
|
06e8b869b9 | ||
|
|
8f48168c73 | ||
|
|
d3595686f6 | ||
|
|
c839b2b039 | ||
|
|
acafa585f4 | ||
|
|
7939b419f7 | ||
|
|
61579ec329 | ||
|
|
1786a5e55a | ||
|
|
2ed01ff040 | ||
|
|
9bf69d30e0 | ||
|
|
218dc3390f | ||
|
|
6626371d87 | ||
|
|
a4270efac9 | ||
|
|
94dc5f330c | ||
|
|
c53da1ef72 | ||
|
|
01f54558b9 | ||
|
|
ca2a96b3f3 | ||
|
|
4d2f38b03a | ||
|
|
762978f8d8 | ||
|
|
bfdf1f482e | ||
|
|
990d46d659 | ||
|
|
620f86130c | ||
|
|
c0205b0ad5 | ||
|
|
ff3bce3287 | ||
|
|
2b3fc6656c | ||
|
|
4fa08fc78c | ||
|
|
5c5705789e | ||
|
|
ac0a1661f3 | ||
|
|
7cde2f8526 | ||
|
|
5bdad8448c | ||
|
|
63c6a3b089 | ||
|
|
230234e701 | ||
|
|
e4739512e6 | ||
|
|
14f3dbb71a | ||
|
|
0103f59f82 | ||
|
|
d9ded9f3f3 | ||
|
|
1f60d2bbb4 | ||
|
|
daf5650445 | ||
|
|
998c92d57e | ||
|
|
6de38fbf08 | ||
|
|
073ca4bf8d | ||
|
|
0cca212843 | ||
|
|
c8b6fe62a1 | ||
|
|
f321c3c7ea | ||
|
|
80a0a7b5c5 | ||
|
|
51e85716ee | ||
|
|
641989fdee | ||
|
|
e4f67c626a | ||
|
|
db0534ed97 | ||
|
|
2d39b3df88 | ||
|
|
a79b26af6c | ||
|
|
a1048c48e4 | ||
|
|
fc33dbb54e | ||
|
|
0a7c936442 | ||
|
|
c5f2df5c93 | ||
|
|
cada9dc0a0 | ||
|
|
cbd13a123f | ||
|
|
c1c7d87bc5 | ||
|
|
f4312b4428 | ||
|
|
00a506053c | ||
|
|
c191ab7c07 | ||
|
|
8663fb7e64 | ||
|
|
99dc89c051 | ||
|
|
158f22f733 | ||
|
|
eccec5f6d5 | ||
|
|
dceb3acace | ||
|
|
036e9d1074 | ||
|
|
6ed1c718b9 | ||
|
|
8f7ad693a8 | ||
|
|
eac18b1c99 | ||
|
|
6f93064114 | ||
|
|
b281789777 | ||
|
|
5ef501c5ec | ||
|
|
0662507116 | ||
|
|
b5eb4b904e | ||
|
|
93c9216b69 | ||
|
|
635695ec84 | ||
|
|
41e3eafa90 | ||
|
|
937cbf6df8 | ||
|
|
432c14b86a | ||
|
|
6ce00fcdb8 | ||
|
|
fb67522324 | ||
|
|
2b45dba57c | ||
|
|
b0515cf8c0 | ||
|
|
6cc11ffb7d | ||
|
|
65938b73e1 | ||
|
|
70a5587513 | ||
|
|
3d434e43ad | ||
|
|
a7b7355dcf | ||
|
|
63f5d07ed7 | ||
|
|
233e8a2085 | ||
|
|
770dc4b230 | ||
|
|
dca09dedac | ||
|
|
6fc1447fa6 | ||
|
|
fb6d3ab285 | ||
|
|
3ed4102a3c | ||
|
|
d53289d707 | ||
|
|
e8cce73a17 | ||
|
|
5c917113e8 | ||
|
|
a63b05a9e7 | ||
|
|
6cb415f57e |
25
.github/ISSUE_TEMPLATE.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试输出,我帮不了你.
|
||||
如何调试 https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh
|
||||
|
||||
If it is a bug report:
|
||||
- make sure you are able to repro it on the latest released version.
|
||||
You can install the latest version by: `acme.sh --upgrade`
|
||||
|
||||
- Search the existing issues.
|
||||
- Refer to the [WIKI](https://wiki.acme.sh).
|
||||
- Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh).
|
||||
|
||||
-->
|
||||
|
||||
Steps to reproduce
|
||||
------------------
|
||||
|
||||
Debug log
|
||||
-----------------
|
||||
|
||||
```
|
||||
acme.sh --issue ..... --debug 2
|
||||
```
|
||||
|
||||
|
||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<!--
|
||||
|
||||
Do NOT send pull request to `master` branch.
|
||||
|
||||
Please send to `dev` branch instead.
|
||||
|
||||
Any PR to `master` branch will NOT be merged.
|
||||
|
||||
-->
|
||||
53
.travis.yml
Normal file
53
.travis.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
language: shell
|
||||
sudo: required
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
global:
|
||||
- SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- debian-sid # Grab shellcheck from the Debian repo (o_O)
|
||||
packages:
|
||||
- shellcheck
|
||||
|
||||
install:
|
||||
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
|
||||
brew update && brew install openssl;
|
||||
brew info openssl;
|
||||
ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/;
|
||||
ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/;
|
||||
ln -s /usr/local/Cellar/openssl/1.0.2j/bin/openssl /usr/local/openssl;
|
||||
_old_path="$PATH";
|
||||
echo "PATH=$PATH";
|
||||
export PATH="";
|
||||
export OPENSSL_BIN="/usr/local/openssl";
|
||||
openssl version 2>&1 || true;
|
||||
$OPENSSL_BIN version 2>&1 || true;
|
||||
export PATH="$_old_path";
|
||||
fi
|
||||
|
||||
script:
|
||||
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
|
||||
- command -V openssl && openssl version
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then chmod +x ~/shfmt ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ~/shfmt -l -w -i 2 . ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi
|
||||
- cd ..
|
||||
- git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; fi
|
||||
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
|
||||
481
README.md
481
README.md
@@ -1,283 +1,388 @@
|
||||
# le: means simp`Le`
|
||||
Simplest shell script for LetsEncrypt free Certificate client
|
||||
# An ACME Shell script: acme.sh [](https://travis-ci.org/Neilpang/acme.sh)
|
||||
- An ACME protocol client written purely in Shell (Unix shell) language.
|
||||
- Full ACME protocol implementation.
|
||||
- Simple, powerful and very easy to use. You only need 3 minutes to learn it.
|
||||
- Bash, dash and sh compatible.
|
||||
- Simplest shell script for Let's Encrypt free certificate client.
|
||||
- Purely written in Shell with no dependencies on python or the official Let's Encrypt client.
|
||||
- Just one script to issue, renew and install your certificates automatically.
|
||||
- DOES NOT require `root/sudoer` access.
|
||||
|
||||
Simple and Powerful, you only need 3 minutes to learn.
|
||||
It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
|
||||
|
||||
Pure written in bash, no dependencies to python, acme-tiny or LetsEncrypt official client.
|
||||
Just one script, to issue, renew your certificates automatically.
|
||||
|
||||
Probably it's the smallest&easiest&smartest shell script to automatically issue & renew the free certificates from LetsEncrypt.
|
||||
|
||||
NOT require to be `root/sudoer`.
|
||||
|
||||
Wiki: https://github.com/Neilpang/le/wiki
|
||||
|
||||
#Tested OS
|
||||
1. Ubuntu [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
2. Debian [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
3. CentOS [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
4. Windows (cygwin with curl, openssl and crontab included) [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
5. FreeBSD with bash [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
6. pfsense with bash and curl
|
||||
7. openSUSE [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
8. Alpine Linux [](https://github.com/Neilpang/letest#here-are-the-latest-status) (with bash, curl. https://github.com/Neilpang/le/issues/94)
|
||||
9. Archlinux [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
10. fedora [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
11. Kali Linux [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
12. Oracle Linux [](https://github.com/Neilpang/letest#here-are-the-latest-status)
|
||||
13. Cloud Linux https://github.com/Neilpang/le/issues/111
|
||||
14. Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_le.sh
|
||||
Wiki: https://github.com/Neilpang/acme.sh/wiki
|
||||
|
||||
|
||||
For all the build status, check our daily build project:
|
||||
Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
|
||||
|
||||
https://github.com/Neilpang/letest.git
|
||||
|
||||
#Supported Mode
|
||||
1. Webroot mode
|
||||
2. Standalone mode
|
||||
3. Apache mode
|
||||
4. Dns mode
|
||||
# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
|
||||
|
||||
#How to install
|
||||
|
||||
1. Install online:
|
||||
# Tested OS
|
||||
|
||||
```
|
||||
curl https://raw.githubusercontent.com/Neilpang/le/master/le.sh | INSTALLONLINE=1 bash
|
||||
| NO | Status| Platform|
|
||||
|----|-------|---------|
|
||||
|1|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu
|
||||
|2|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian
|
||||
|3|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS
|
||||
|4|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included)
|
||||
|5|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD
|
||||
|6|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense
|
||||
|7|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE
|
||||
|8|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl)
|
||||
|9|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux
|
||||
|10|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora
|
||||
|11|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux
|
||||
|12|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux
|
||||
|13|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh
|
||||
|14|-----| Cloud Linux https://github.com/Neilpang/le/issues/111
|
||||
|15|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD
|
||||
|16|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia
|
||||
|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT)
|
||||
|18|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris
|
||||
|19|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux
|
||||
|20|[](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX
|
||||
|
||||
For all build statuses, check our [daily build project](https://github.com/Neilpang/acmetest):
|
||||
|
||||
https://github.com/Neilpang/acmetest
|
||||
|
||||
|
||||
# Supported modes
|
||||
|
||||
- Webroot mode
|
||||
- Standalone mode
|
||||
- Apache mode
|
||||
- DNS mode
|
||||
- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
|
||||
|
||||
|
||||
# 1. How to install
|
||||
|
||||
### 1. Install online
|
||||
|
||||
Check this project: https://github.com/Neilpang/get.acme.sh
|
||||
|
||||
```bash
|
||||
curl https://get.acme.sh | sh
|
||||
```
|
||||
|
||||
Or:
|
||||
```
|
||||
wget -O - https://raw.githubusercontent.com/Neilpang/le/master/le.sh | INSTALLONLINE=1 bash
|
||||
|
||||
```bash
|
||||
wget -O - https://get.acme.sh | sh
|
||||
```
|
||||
|
||||
|
||||
2. Or, Install from git:
|
||||
Clone this project:
|
||||
```
|
||||
git clone https://github.com/Neilpang/le.git
|
||||
cd le
|
||||
./le.sh install
|
||||
### 2. Or, Install from git
|
||||
|
||||
Clone this project and launch installation:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Neilpang/acme.sh.git
|
||||
cd ./acme.sh
|
||||
./acme.sh --install
|
||||
```
|
||||
|
||||
You don't have to be root then, although it is recommended.
|
||||
You `don't have to be root` then, although `it is recommended`.
|
||||
|
||||
Which does 3 jobs:
|
||||
* create and copy `le.sh` to your home dir: `~/.le`
|
||||
All the certs will be placed in this folder.
|
||||
* create alias : `le.sh=~/.le/le.sh` and `le=~/.le/le.sh`.
|
||||
* create everyday cron job to check and renew the cert if needed.
|
||||
Advanced Installation: https://github.com/Neilpang/acme.sh/wiki/How-to-install
|
||||
|
||||
After install, you must close current terminal and reopen again to make the alias take effect.
|
||||
The installer will perform 3 actions:
|
||||
|
||||
1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`.
|
||||
All certs will be placed in this folder too.
|
||||
2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`.
|
||||
3. Create daily cron job to check and renew the certs if needed.
|
||||
|
||||
Cron entry example:
|
||||
|
||||
```bash
|
||||
0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
|
||||
```
|
||||
|
||||
After the installation, you must close the current terminal and reopen it to make the alias take effect.
|
||||
|
||||
Ok, you are ready to issue certs now.
|
||||
|
||||
Ok, you are ready to issue cert now.
|
||||
Show help message:
|
||||
```
|
||||
root@v1:~# le.sh
|
||||
https://github.com/Neilpang/le
|
||||
v1.2.2
|
||||
Usage: le.sh [command] ...[args]....
|
||||
Available commands:
|
||||
|
||||
install:
|
||||
Install le.sh to your system.
|
||||
issue:
|
||||
Issue a cert.
|
||||
installcert:
|
||||
Install the issued cert to apache/nginx or any other server.
|
||||
renew:
|
||||
Renew a cert.
|
||||
renewAll:
|
||||
Renew all the certs.
|
||||
uninstall:
|
||||
Uninstall le.sh, and uninstall the cron job.
|
||||
version:
|
||||
Show version info.
|
||||
installcronjob:
|
||||
Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
|
||||
uninstallcronjob:
|
||||
Uninstall the cron job. The 'uninstall' command can do this automatically.
|
||||
toPkcs:
|
||||
Export the certificate and key to a pfx file.
|
||||
createAccountKey:
|
||||
Create an account private key, professional use.
|
||||
createDomainKey:
|
||||
Create an domain private key, professional use.
|
||||
createCSR:
|
||||
Create CSR , professional use.
|
||||
|
||||
|
||||
|
||||
root@v1:~/le# le issue
|
||||
Usage: le issue webroot|no|apache|dns a.com [www.a.com,b.com,c.com]|no [key-length]|no
|
||||
|
||||
|
||||
```
|
||||
|
||||
Set the param value to "no" means you want to ignore it.
|
||||
|
||||
For example, if you give "no" to "key-length", it will use default length 2048.
|
||||
|
||||
And if you give 'no' to 'cert-file-path', it will not copy the issued cert to the "cert-file-path".
|
||||
|
||||
In all the cases, the issued cert will be placed in "~/.le/domain.com/"
|
||||
|
||||
|
||||
# Just issue a cert:
|
||||
Example 1:
|
||||
Only one domain:
|
||||
```
|
||||
le issue /home/wwwroot/aa.com aa.com
|
||||
root@v1:~# acme.sh -h
|
||||
```
|
||||
|
||||
Example 2:
|
||||
Multiple domains in the same cert:
|
||||
# 2. Just issue a cert
|
||||
|
||||
```
|
||||
le issue /home/wwwroot/aa.com aa.com www.aa.com,cp.aa.com
|
||||
**Example 1:** Single domain.
|
||||
|
||||
```bash
|
||||
acme.sh --issue -d example.com -w /home/wwwroot/example.com
|
||||
```
|
||||
|
||||
First argument `/home/wwwroot/aa.com` is the web root folder, You must have `write` access to this folder.
|
||||
**Example 2:** Multiple domains in the same cert.
|
||||
|
||||
Second argument "aa.com" is the main domain you want to issue cert for.
|
||||
|
||||
Third argument is the additional domain list you want to use. Comma separated list, which is Optional.
|
||||
|
||||
You must point and bind all the domains to the same webroot dir:`/home/wwwroot/aa.com`
|
||||
|
||||
The cert will be placed in `~/.le/aa.com/`
|
||||
|
||||
The issued cert will be renewed every 80 days automatically.
|
||||
|
||||
# Install issued cert to apache/nginx etc.
|
||||
```
|
||||
le installcert aa.com /path/to/certfile/in/apache/nginx /path/to/keyfile/in/apache/nginx /path/to/ca/certfile/apache/nginx "service apache2|nginx reload"
|
||||
```bash
|
||||
acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com
|
||||
```
|
||||
|
||||
Install the issued cert/key to the production apache or nginx path.
|
||||
The parameter `/home/wwwroot/example.com` is the web root folder. You **MUST** have `write access` to this folder.
|
||||
|
||||
The cert will be renewed every 80 days by default (which is configurable), Once the cert is renewed, the apache/nginx will be automatically reloaded by the command: `service apache2 reload` or `service nginx reload`
|
||||
Second argument **"example.com"** is the main domain you want to issue the cert for.
|
||||
You must have at least one domain there.
|
||||
|
||||
You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`.
|
||||
|
||||
Generated/issued certs will be placed in `~/.acme.sh/example.com/`
|
||||
|
||||
The issued cert will be renewed automatically every **60** days.
|
||||
|
||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
||||
|
||||
|
||||
# Use Standalone server to issue cert (requires you be root/sudoer, or you have permission to listen tcp 80 port):
|
||||
Same usage as all above, just give `no` as the webroot.
|
||||
The tcp `80` port must be free to listen, otherwise you will be prompted to free the `80` port and try again.
|
||||
# 3. Install the issued cert to Apache/Nginx etc.
|
||||
|
||||
```
|
||||
le issue no aa.com www.aa.com,cp.aa.com
|
||||
After you issue a cert, you probably want to install/copy the cert to your Apache/Nginx or other servers.
|
||||
You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future.
|
||||
|
||||
**Apache** example:
|
||||
```bash
|
||||
acme.sh --install-cert -d example.com \
|
||||
--certpath /path/to/certfile/in/apache/cert.pem \
|
||||
--keypath /path/to/keyfile/in/apache/key.pem \
|
||||
--fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \
|
||||
--reloadcmd "service apache2 force-reload"
|
||||
```
|
||||
|
||||
# Use Apache mode (requires you be root/sudoer, since it is required to interact with apache server):
|
||||
If you are running a web server, apache or nginx, it is recommended to use the Webroot mode.
|
||||
Particularly, if you are running an apache server, you can use apache mode instead. Which doesn't write any file to your web root folder.
|
||||
|
||||
Just set string "apache" to the first argument, it will use apache plugin automatically.
|
||||
|
||||
```
|
||||
le issue apache aa.com www.aa.com,user.aa.com
|
||||
```
|
||||
All the other arguments are the same with previous.
|
||||
|
||||
|
||||
# Use DNS mode:
|
||||
Support the latest dns-01 challenge.
|
||||
|
||||
```
|
||||
le issue dns aa.com www.aa.com,user.aa.com
|
||||
**Nginx** example:
|
||||
```bash
|
||||
acme.sh --install-cert -d example.com \
|
||||
--keypath /path/to/keyfile/in/nginx/key.pem \
|
||||
--fullchainpath /path/to/fullchain/nginx/cert.pem \
|
||||
--reloadcmd "service nginx force-reload"
|
||||
```
|
||||
|
||||
You will get the output like bellow:
|
||||
Only the domain is required, all the other parameters are optional.
|
||||
|
||||
The ownership and permission info of existing files are preserved. You may want to precreate the files to have defined ownership and permission.
|
||||
|
||||
Install/copy the issued cert/key to the production Apache or Nginx path.
|
||||
|
||||
The cert will be `renewed every **60** days by default` (which is configurable). Once the cert is renewed, the Apache/Nginx service will be restarted automatically by the command: `service apache2 restart` or `service nginx restart`.
|
||||
|
||||
|
||||
# 4. Use Standalone server to issue cert
|
||||
|
||||
**(requires you to be root/sudoer or have permission to listen on port 80 (TCP))**
|
||||
|
||||
Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
|
||||
|
||||
```bash
|
||||
acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
|
||||
```
|
||||
|
||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
||||
|
||||
|
||||
# 5. Use Standalone TLS server to issue cert
|
||||
|
||||
**(requires you to be root/sudoer or have permission to listen on port 443 (TCP))**
|
||||
|
||||
acme.sh supports `tls-sni-01` validation.
|
||||
|
||||
Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
|
||||
|
||||
```bash
|
||||
acme.sh --issue --tls -d example.com -d www.example.com -d cp.example.com
|
||||
```
|
||||
|
||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
||||
|
||||
|
||||
# 6. Use Apache mode
|
||||
|
||||
**(requires you to be root/sudoer, since it is required to interact with Apache server)**
|
||||
|
||||
If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
|
||||
|
||||
Particularly, if you are running an Apache server, you should use Apache mode instead. This mode doesn't write any files to your web root folder.
|
||||
|
||||
Just set string "apache" as the second argument and it will force use of apache plugin automatically.
|
||||
|
||||
```
|
||||
acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
|
||||
```
|
||||
|
||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
||||
|
||||
|
||||
# 7. Use DNS mode:
|
||||
|
||||
Support the `dns-01` challenge.
|
||||
|
||||
```bash
|
||||
acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
|
||||
```
|
||||
|
||||
You should get an output like below:
|
||||
|
||||
```
|
||||
Add the following txt record:
|
||||
Domain:_acme-challenge.aa.com
|
||||
Domain:_acme-challenge.example.com
|
||||
Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c
|
||||
|
||||
Add the following txt record:
|
||||
Domain:_acme-challenge.www.aa.com
|
||||
Domain:_acme-challenge.www.example.com
|
||||
Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
Please add those txt records to the domains. Waiting for the dns to take effect.
|
||||
|
||||
Then just retry with 'renew' command:
|
||||
|
||||
```
|
||||
le renew aa.com
|
||||
|
||||
Then just rerun with `renew` argument:
|
||||
|
||||
```bash
|
||||
acme.sh --renew -d example.com
|
||||
```
|
||||
|
||||
Ok, it's finished.
|
||||
|
||||
|
||||
#Automatic dns api integeration
|
||||
# 8. Automatic DNS API integration
|
||||
|
||||
If your dns provider supports api access, we can use api to automatically issue certs.
|
||||
You don't have do anything manually.
|
||||
If your DNS provider supports API access, we can use that API to automatically issue the certs.
|
||||
|
||||
###Currently we support:
|
||||
You don't have to do anything manually!
|
||||
|
||||
1. Cloudflare.com api
|
||||
2. Dnspod.cn api
|
||||
3. Cloudxns.com api
|
||||
4. AWS Route 53, see: https://github.com/Neilpang/le/issues/65
|
||||
### Currently acme.sh supports:
|
||||
|
||||
More apis are coming soon....
|
||||
1. CloudFlare.com API
|
||||
1. DNSPod.cn API
|
||||
1. CloudXNS.com API
|
||||
1. GoDaddy.com API
|
||||
1. OVH, kimsufi, soyoustart and runabove API
|
||||
1. AWS Route 53
|
||||
1. PowerDNS.com API
|
||||
1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
|
||||
(DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
|
||||
1. LuaDNS.com API
|
||||
1. DNSMadeEasy.com API
|
||||
1. nsupdate API
|
||||
1. aliyun.com(阿里云) API
|
||||
1. ISPConfig 3.1 API
|
||||
1. Alwaysdata.com API
|
||||
1. Linode.com API
|
||||
1. FreeDNS (https://freedns.afraid.org/)
|
||||
|
||||
If your dns provider is not in the supported list above, you can write your own script api easily.
|
||||
**More APIs coming soon...**
|
||||
|
||||
For more details: [How to use dns api](dnsapi)
|
||||
If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project.
|
||||
|
||||
For more details: [How to use DNS API](dnsapi)
|
||||
|
||||
|
||||
# Issue ECC certificate:
|
||||
LetsEncrypt now can issue ECDSA certificate.
|
||||
And we also support it.
|
||||
# 9. Issue ECC certificates
|
||||
|
||||
`Let's Encrypt` can now issue **ECDSA** certificates.
|
||||
|
||||
And we support them too!
|
||||
|
||||
Just set the `length` parameter with a prefix `ec-`.
|
||||
|
||||
For example:
|
||||
|
||||
Single domain:
|
||||
```
|
||||
le issue /home/wwwroot/aa.com aa.com no ec-256
|
||||
### Single domain ECC cerfiticate
|
||||
|
||||
```bash
|
||||
acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256
|
||||
```
|
||||
|
||||
SAN multiple domains:
|
||||
```
|
||||
le issue /home/wwwroot/aa.com aa.com www.aa.com,cp.aa.com ec-256
|
||||
### SAN multi domain ECC certificate
|
||||
|
||||
```bash
|
||||
acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256
|
||||
```
|
||||
|
||||
Please look at the last parameter above.
|
||||
|
||||
Valid values are:
|
||||
|
||||
1. ec-256 (prime256v1, "ECDSA P-256")
|
||||
2. ec-384 (secp384r1, "ECDSA P-384")
|
||||
3. ec-521 (secp521r1, "ECDSA P-521", which is not supported by letsencrypt yet.)
|
||||
1. **ec-256 (prime256v1, "ECDSA P-256")**
|
||||
2. **ec-384 (secp384r1, "ECDSA P-384")**
|
||||
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
|
||||
|
||||
|
||||
# 10. How to renew the issued certs
|
||||
|
||||
#Under the Hood
|
||||
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
|
||||
|
||||
Speak ACME language with bash directly to Let's encrypt.
|
||||
However, you can also force to renew any cert:
|
||||
|
||||
```
|
||||
acme.sh --renew -d example.com --force
|
||||
```
|
||||
|
||||
or, for ECC cert:
|
||||
|
||||
```
|
||||
acme.sh --renew -d example.com --force --ecc
|
||||
```
|
||||
|
||||
|
||||
# 11. How to upgrade `acme.sh`
|
||||
|
||||
acme.sh is in constant development, so it's strongly recommended to use the latest code.
|
||||
|
||||
You can update acme.sh to the latest code:
|
||||
|
||||
```
|
||||
acme.sh --upgrade
|
||||
```
|
||||
|
||||
You can also enable auto upgrade:
|
||||
|
||||
```
|
||||
acme.sh --upgrade --auto-upgrade
|
||||
```
|
||||
|
||||
Then **acme.sh** will be kept up to date automatically.
|
||||
|
||||
Disable auto upgrade:
|
||||
|
||||
```
|
||||
acme.sh --upgrade --auto-upgrade 0
|
||||
```
|
||||
|
||||
|
||||
# 12. Issue a cert from an existing CSR
|
||||
|
||||
https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
|
||||
|
||||
|
||||
# Under the Hood
|
||||
|
||||
Speak ACME language using shell, directly to "Let's Encrypt".
|
||||
|
||||
TODO:
|
||||
|
||||
|
||||
#Acknowledgment
|
||||
# Acknowledgments
|
||||
|
||||
1. Acme-tiny: https://github.com/diafygi/acme-tiny
|
||||
2. ACME protocol: https://github.com/ietf-wg-acme/acme
|
||||
3. letsencrypt: https://github.com/letsencrypt/letsencrypt
|
||||
3. Certbot: https://github.com/certbot/certbot
|
||||
|
||||
|
||||
|
||||
#License & Other
|
||||
# License & Others
|
||||
|
||||
License is GPLv3
|
||||
|
||||
Please Star and Fork me.
|
||||
|
||||
Issues and pull requests are welcomed.
|
||||
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome.
|
||||
|
||||
|
||||
# Donate
|
||||
|
||||
1. PayPal: donate@acme.sh
|
||||
|
||||
[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list)
|
||||
|
||||
30
deploy/README.md
Normal file
30
deploy/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Using deploy api
|
||||
|
||||
Here are the scripts to deploy the certs/key to the server/services.
|
||||
|
||||
## 1. Deploy the certs to your cpanel host.
|
||||
|
||||
(cpanel deploy hook is not finished yet, this is just an example.)
|
||||
|
||||
Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
|
||||
|
||||
Then you can deploy now:
|
||||
|
||||
```sh
|
||||
export DEPLOY_CPANEL_USER=myusername
|
||||
export DEPLOY_CPANEL_PASSWORD=PASSWORD
|
||||
acme.sh --deploy -d example.com --deploy --deploy-hook cpanel
|
||||
```
|
||||
|
||||
## 2. Deploy ssl cert on kong proxy engine based on api.
|
||||
|
||||
Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
|
||||
|
||||
(TODO)
|
||||
|
||||
## 3. Deploy the cert to remote server through SSH access.
|
||||
|
||||
(TODO)
|
||||
|
||||
|
||||
|
||||
29
deploy/cpanel.sh
Normal file
29
deploy/cpanel.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#Here is the script to deploy the cert to your cpanel account by the cpanel APIs.
|
||||
|
||||
#returns 0 means success, otherwise error.
|
||||
|
||||
#export DEPLOY_CPANEL_USER=myusername
|
||||
#export DEPLOY_CPANEL_PASSWORD=PASSWORD
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#domain keyfile certfile cafile fullchain
|
||||
cpanel_deploy() {
|
||||
_cdomain="$1"
|
||||
_ckey="$2"
|
||||
_ccert="$3"
|
||||
_cca="$4"
|
||||
_cfullchain="$5"
|
||||
|
||||
_debug _cdomain "$_cdomain"
|
||||
_debug _ckey "$_ckey"
|
||||
_debug _ccert "$_ccert"
|
||||
_debug _cca "$_cca"
|
||||
_debug _cfullchain "$_cfullchain"
|
||||
|
||||
_err "Not implemented yet"
|
||||
return 1
|
||||
|
||||
}
|
||||
81
deploy/kong.sh
Executable file
81
deploy/kong.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# This deploy hook will deploy ssl cert on kong proxy engine based on api request_host parameter.
|
||||
# Note that ssl plugin should be available on Kong instance
|
||||
# The hook will match cdomain to request_host, in case of multiple domain it will always take the first
|
||||
# one (acme.sh behaviour).
|
||||
# If ssl config already exist it will update only cert and key not touching other parameter
|
||||
# If ssl config doesn't exist it will only upload cert and key and not set other parameter
|
||||
# Not that we deploy full chain
|
||||
# See https://getkong.org/plugins/dynamic-ssl/ for other options
|
||||
# Written by Geoffroi Genot <ggenot@voxbone.com>
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#domain keyfile certfile cafile fullchain
|
||||
kong_deploy() {
|
||||
_cdomain="$1"
|
||||
_ckey="$2"
|
||||
_ccert="$3"
|
||||
_cca="$4"
|
||||
_cfullchain="$5"
|
||||
_info "Deploying certificate on Kong instance"
|
||||
if [ -z "$KONG_URL" ]; then
|
||||
_debug "KONG_URL Not set, using default http://localhost:8001"
|
||||
KONG_URL="http://localhost:8001"
|
||||
fi
|
||||
|
||||
_debug _cdomain "$_cdomain"
|
||||
_debug _ckey "$_ckey"
|
||||
_debug _ccert "$_ccert"
|
||||
_debug _cca "$_cca"
|
||||
_debug _cfullchain "$_cfullchain"
|
||||
|
||||
#Get uuid linked to the domain
|
||||
uuid=$(_get "$KONG_URL/apis?request_host=$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
||||
if [ -z "$uuid" ]; then
|
||||
_err "Unable to get Kong uuid for domain $_cdomain"
|
||||
_err "Make sure that KONG_URL is correctly configured"
|
||||
_err "Make sure that a Kong api request_host match the domain"
|
||||
_err "Kong url: $KONG_URL"
|
||||
return 1
|
||||
fi
|
||||
#Save kong url if it's succesful (First run case)
|
||||
_saveaccountconf KONG_URL "$KONG_URL"
|
||||
#Generate DEIM
|
||||
delim="-----MultipartDelimeter$(date "+%s%N")"
|
||||
nl="\015\012"
|
||||
#Set Header
|
||||
_H1="Content-Type: multipart/form-data; boundary=$delim"
|
||||
#Generate data for request (Multipart/form-data with mixed content)
|
||||
#set name to ssl
|
||||
content="--$delim${nl}Content-Disposition: form-data; name=\"name\"${nl}${nl}ssl"
|
||||
#add key
|
||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"config.key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
|
||||
#Add cert
|
||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"config.cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
|
||||
#Close multipart
|
||||
content="$content${nl}--$delim--${nl}"
|
||||
#Convert CRLF
|
||||
content=$(printf %b "$content")
|
||||
#DEBUG
|
||||
_debug header "$_H1"
|
||||
_debug content "$content"
|
||||
#Check if ssl plugins is aready enabled (if not => POST else => PATCH)
|
||||
ssl_uuid=$(_get "$KONG_URL/apis/$uuid/plugins" | _egrep_o '"id":"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"[a-zA-Z0-9\-\,\"_\:]*"name":"ssl"' | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
||||
_debug ssl_uuid "$ssl_uuid"
|
||||
if [ -z "$ssl_uuid" ]; then
|
||||
#Post certificate to Kong
|
||||
response=$(_post "$content" "$KONG_URL/apis/$uuid/plugins" "" "POST")
|
||||
else
|
||||
#patch
|
||||
response=$(_post "$content" "$KONG_URL/apis/$uuid/plugins/$ssl_uuid" "" "PATCH")
|
||||
fi
|
||||
if ! [ "$(echo "$response" | _egrep_o "ssl")" = "ssl" ]; then
|
||||
_err "An error occured with cert upload. Check response:"
|
||||
_err "$response"
|
||||
return 1
|
||||
fi
|
||||
_debug response "$response"
|
||||
_info "Certificate successfully deployed"
|
||||
}
|
||||
28
deploy/myapi.sh
Executable file
28
deploy/myapi.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#Here is a sample custom api script.
|
||||
#This file name is "myapi.sh"
|
||||
#So, here must be a method myapi_deploy()
|
||||
#Which will be called by acme.sh to deploy the cert
|
||||
#returns 0 means success, otherwise error.
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#domain keyfile certfile cafile fullchain
|
||||
myapi_deploy() {
|
||||
_cdomain="$1"
|
||||
_ckey="$2"
|
||||
_ccert="$3"
|
||||
_cca="$4"
|
||||
_cfullchain="$5"
|
||||
|
||||
_debug _cdomain "$_cdomain"
|
||||
_debug _ckey "$_ckey"
|
||||
_debug _ccert "$_ccert"
|
||||
_debug _cca "$_cca"
|
||||
_debug _cfullchain "$_cfullchain"
|
||||
|
||||
_err "Not implemented yet"
|
||||
return 1
|
||||
|
||||
}
|
||||
317
dnsapi/README.md
317
dnsapi/README.md
@@ -1,86 +1,327 @@
|
||||
# How to use dns api
|
||||
# How to use DNS API
|
||||
|
||||
## Use CloudFlare domain api to automatically issue cert
|
||||
## 1. Use CloudFlare domain API to automatically issue cert
|
||||
|
||||
For now, we support clourflare integeration.
|
||||
|
||||
First you need to login to your clourflare account to get your api key.
|
||||
First you need to login to your CloudFlare account to get your API key.
|
||||
|
||||
```
|
||||
export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
|
||||
export CF_Email="xxxx@sss.com"
|
||||
|
||||
```
|
||||
|
||||
Ok, let's issue cert now:
|
||||
Ok, let's issue a cert now:
|
||||
```
|
||||
le.sh issue dns-cf aa.com www.aa.com
|
||||
acme.sh --issue --dns dns_cf -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `CF_Key` and `CF_Email` will be saved in `~/.le/account.conf`, when next time you use cloudflare api, it will reuse this key.
|
||||
The `CF_Key` and `CF_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
|
||||
## 2. Use DNSPod.cn domain API to automatically issue cert
|
||||
|
||||
## Use Dnspod.cn domain api to automatically issue cert
|
||||
|
||||
For now, we support dnspod.cn integeration.
|
||||
|
||||
First you need to login to your dnspod.cn account to get your api key and key id.
|
||||
First you need to login to your DNSPod account to get your API Key and ID.
|
||||
|
||||
```
|
||||
export DP_Id="1234"
|
||||
|
||||
export DP_Key="sADDsdasdgdsf"
|
||||
|
||||
```
|
||||
|
||||
Ok, let's issue cert now:
|
||||
Ok, let's issue a cert now:
|
||||
```
|
||||
le.sh issue dns-dp aa.com www.aa.com
|
||||
acme.sh --issue --dns dns_dp -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `DP_Id` and `DP_Key` will be saved in `~/.le/account.conf`, when next time you use dnspod.cn api, it will reuse this key.
|
||||
The `DP_Id` and `DP_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
|
||||
## Use Cloudxns.com domain api to automatically issue cert
|
||||
## 3. Use CloudXNS.com domain API to automatically issue cert
|
||||
|
||||
For now, we support Cloudxns.com integeration.
|
||||
|
||||
First you need to login to your Cloudxns.com account to get your api key and key secret.
|
||||
First you need to login to your CloudXNS account to get your API Key and Secret.
|
||||
|
||||
```
|
||||
export CX_Key="1234"
|
||||
|
||||
export CX_Secret="sADDsdasdgdsf"
|
||||
|
||||
```
|
||||
|
||||
Ok, let's issue cert now:
|
||||
Ok, let's issue a cert now:
|
||||
```
|
||||
le.sh issue dns-cx aa.com www.aa.com
|
||||
acme.sh --issue --dns dns_cx -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `CX_Key` and `CX_Secret` will be saved in `~/.le/account.conf`, when next time you use Cloudxns.com api, it will reuse this key.
|
||||
The `CX_Key` and `CX_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
|
||||
## 4. Use GoDaddy.com domain API to automatically issue cert
|
||||
|
||||
# Use custom api
|
||||
First you need to login to your GoDaddy account to get your API Key and Secret.
|
||||
|
||||
If your api is not supported yet, you can write your own dns api.
|
||||
https://developer.godaddy.com/keys/
|
||||
|
||||
Let's assume you want to name it 'myapi',
|
||||
|
||||
1. Create a bash script named `~/.le/dns-myapi.sh`,
|
||||
2. In the scrypt, you must have a function named `dns-myapi-add()`. Which will be called by le.sh to add dns records.
|
||||
3. Then you can use your api to issue cert like:
|
||||
Please create a Production key, instead of a Test key.
|
||||
|
||||
```
|
||||
le.sh issue dns-myapi aa.com www.aa.com
|
||||
export GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
export GD_Secret="asdfsdafdsfdsfdsfdsfdsafd"
|
||||
```
|
||||
|
||||
For more details, please check our sample script: [dns-myapi.sh](dns-myapi.sh)
|
||||
Ok, let's issue a cert now:
|
||||
```
|
||||
acme.sh --issue --dns dns_gd -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `GD_Key` and `GD_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
|
||||
## 5. Use PowerDNS embedded API to automatically issue cert
|
||||
|
||||
First you need to login to your PowerDNS account to enable the API and set your API-Token in the configuration.
|
||||
|
||||
https://doc.powerdns.com/md/httpapi/README/
|
||||
|
||||
```
|
||||
export PDNS_Url="http://ns.example.com:8081"
|
||||
export PDNS_ServerId="localhost"
|
||||
export PDNS_Token="0123456789ABCDEF"
|
||||
export PDNS_Ttl=60
|
||||
```
|
||||
|
||||
Ok, let's issue a cert now:
|
||||
```
|
||||
acme.sh --issue --dns dns_pdns -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `PDNS_Url`, `PDNS_ServerId`, `PDNS_Token` and `PDNS_Ttl` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
|
||||
## 6. Use OVH/kimsufi/soyoustart/runabove API to automatically issue cert
|
||||
|
||||
https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api
|
||||
|
||||
|
||||
## 7. Use nsupdate to automatically issue cert
|
||||
|
||||
First, generate a key for updating the zone
|
||||
```
|
||||
b=$(dnssec-keygen -a hmac-sha512 -b 512 -n USER -K /tmp foo)
|
||||
cat > /etc/named/keys/update.key <<EOF
|
||||
key "update" {
|
||||
algorithm hmac-sha512;
|
||||
secret "$(awk '/^Key/{print $2}' /tmp/$b.private)";
|
||||
};
|
||||
EOF
|
||||
rm -f /tmp/$b.{private,key}
|
||||
```
|
||||
|
||||
Include this key in your named configuration
|
||||
```
|
||||
include "/etc/named/keys/update.key";
|
||||
```
|
||||
|
||||
Next, configure your zone to allow dynamic updates.
|
||||
|
||||
Depending on your named version, use either
|
||||
```
|
||||
zone "example.com" {
|
||||
type master;
|
||||
allow-update { key "update"; };
|
||||
};
|
||||
```
|
||||
or
|
||||
```
|
||||
zone "example.com" {
|
||||
type master;
|
||||
update-policy {
|
||||
grant update subdomain example.com.;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Finally, make the DNS server and update Key available to `acme.sh`
|
||||
|
||||
```
|
||||
export NSUPDATE_SERVER="dns.example.com"
|
||||
export NSUPDATE_KEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=="
|
||||
```
|
||||
|
||||
Ok, let's issue a cert now:
|
||||
```
|
||||
acme.sh --issue --dns dns_nsupdate -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `NSUPDATE_SERVER` and `NSUPDATE_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
|
||||
## 8. Use LuaDNS domain API
|
||||
|
||||
Get your API token at https://api.luadns.com/settings
|
||||
|
||||
```
|
||||
export LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
export LUA_Email="xxxx@sss.com"
|
||||
```
|
||||
|
||||
To issue a cert:
|
||||
```
|
||||
acme.sh --issue --dns dns_lua -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `LUA_Key` and `LUA_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
|
||||
## 9. Use DNSMadeEasy domain API
|
||||
|
||||
Get your API credentials at https://cp.dnsmadeeasy.com/account/info
|
||||
|
||||
```
|
||||
export ME_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
export ME_Secret="qdfqsdfkjdskfj"
|
||||
```
|
||||
|
||||
To issue a cert:
|
||||
```
|
||||
acme.sh --issue --dns dns_me -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `ME_Key` and `ME_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
|
||||
## 10. Use Amazon Route53 domain API
|
||||
|
||||
https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API
|
||||
|
||||
```
|
||||
export AWS_ACCESS_KEY_ID=XXXXXXXXXX
|
||||
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX
|
||||
```
|
||||
|
||||
To issue a cert:
|
||||
```
|
||||
acme.sh --issue --dns dns_aws -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
## 11. Use Aliyun domain API to automatically issue cert
|
||||
|
||||
First you need to login to your Aliyun account to get your API key.
|
||||
[https://ak-console.aliyun.com/#/accesskey](https://ak-console.aliyun.com/#/accesskey)
|
||||
|
||||
```
|
||||
export Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
export Ali_Secret="jlsdflanljkljlfdsaklkjflsa"
|
||||
```
|
||||
|
||||
Ok, let's issue a cert now:
|
||||
```
|
||||
acme.sh --issue --dns dns_ali -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `Ali_Key` and `Ali_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
## 12. Use ISPConfig 3.1 API
|
||||
|
||||
This only works for ISPConfig 3.1 (and newer).
|
||||
|
||||
Create a Remote User in the ISPConfig Control Panel. The Remote User must have access to at least `DNS zone functions` and `DNS txt functions`.
|
||||
|
||||
```
|
||||
export ISPC_User="xxx"
|
||||
export ISPC_Password="xxx"
|
||||
export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
|
||||
export ISPC_Api_Insecure=1
|
||||
```
|
||||
If you have installed ISPConfig on a different port, then alter the 8080 accordingly.
|
||||
Leaver ISPC_Api_Insecure set to 1 if you have not a valid ssl cert for your installation. Change it to 0 if you have a valid ssl cert.
|
||||
|
||||
To issue a cert:
|
||||
```
|
||||
acme.sh --issue --dns dns_ispconfig -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `ISPC_User`, `ISPC_Password`, `ISPC_Api`and `ISPC_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
## 13. Use Alwaysdata domain API
|
||||
|
||||
First you need to login to your Alwaysdata account to get your API Key.
|
||||
|
||||
```sh
|
||||
export AD_API_KEY="myalwaysdataapikey"
|
||||
```
|
||||
|
||||
Ok, let's issue a cert now:
|
||||
|
||||
```sh
|
||||
acme.sh --issue --dns dns_ad -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `AD_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused
|
||||
when needed.
|
||||
|
||||
## 14. Use Linode domain API
|
||||
|
||||
First you need to login to your Linode account to get your API Key.
|
||||
[https://manager.linode.com/profile/api](https://manager.linode.com/profile/api)
|
||||
|
||||
Then add an API key with label *ACME* and copy the new key.
|
||||
|
||||
```sh
|
||||
export LINODE_API_KEY="..."
|
||||
```
|
||||
|
||||
Due to the reload time of any changes in the DNS records, we have to use the `dnssleep` option to wait at least 15 minutes for the changes to take effect.
|
||||
|
||||
Ok, let's issue a cert now:
|
||||
|
||||
```sh
|
||||
acme.sh --issue --dns dns_linode --dnssleep 900 -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
The `LINODE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||
|
||||
## 15. Use FreeDNS
|
||||
|
||||
FreeDNS (https://freedns.afraid.org/) does not provide an API to update DNS records (other than IPv4 and IPv6
|
||||
dynamic DNS addresses). The acme.sh plugin therefore retrieves and updates domain TXT records by logging
|
||||
into the FreeDNS website to read the HTML and posting updates as HTTP. The plugin needs to know your
|
||||
userid and password for the FreeDNS website.
|
||||
|
||||
```sh
|
||||
export FREEDNS_User="..."
|
||||
export FREEDNS_Password="..."
|
||||
```
|
||||
|
||||
You need only provide this the first time you run the acme.sh client with FreeDNS validation and then again
|
||||
whenever you change your password at the FreeDNS site. The acme.sh FreeDNS plugin does not store your userid
|
||||
or password but rather saves an authentication token returned by FreeDNS in `~/.acme.sh/account.conf` and
|
||||
reuses that when needed.
|
||||
|
||||
Now you can issue a certificate.
|
||||
|
||||
```sh
|
||||
acme.sh --issue --dns dns_freedns -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
Note that you cannot use acme.sh automatic DNS validation for FreeDNS public domains or for a subdomain that
|
||||
you create under a FreeDNS public domain. You must own the top level domain in order to automaitcally
|
||||
validate with acme.sh at FreeDNS.
|
||||
|
||||
# Use custom API
|
||||
|
||||
If your API is not supported yet, you can write your own DNS API.
|
||||
|
||||
Let's assume you want to name it 'myapi':
|
||||
|
||||
1. Create a bash script named `~/.acme.sh/dns_myapi.sh`,
|
||||
2. In the script you must have a function named `dns_myapi_add()` which will be called by acme.sh to add the DNS records.
|
||||
3. Then you can use your API to issue cert like this:
|
||||
|
||||
```
|
||||
acme.sh --issue --dns dns_myapi -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
|
||||
|
||||
|
||||
# Use lexicon DNS API
|
||||
|
||||
https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
|
||||
|
||||
171
dnsapi/dns-cf.sh
171
dnsapi/dns-cf.sh
@@ -1,171 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
#
|
||||
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
#
|
||||
#CF_Email="xxxx@sss.com"
|
||||
|
||||
|
||||
CF_Api="https://api.cloudflare.com/client/v4/"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns-cf-add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ] ; then
|
||||
_err "You don't specify cloudflare api key and email yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf CF_Key "$CF_Key"
|
||||
_saveaccountconf CF_Email "$CF_Email"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root $fulldomain ; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_debug "Getting txt records"
|
||||
_cf_rest GET "/zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
|
||||
|
||||
if [ "$?" != "0" ] || ! printf $response | grep \"success\":true > /dev/null ; then
|
||||
_err "Error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
count=$(printf $response | grep -o \"count\":[^,]* | cut -d : -f 2)
|
||||
|
||||
if [ "$count" == "0" ] ; then
|
||||
_info "Adding record"
|
||||
if _cf_rest POST "/zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
|
||||
if printf $response | grep $fulldomain > /dev/null ; then
|
||||
_info "Added, sleeping 10 seconds"
|
||||
sleep 10
|
||||
#todo: check if the record takes effect
|
||||
return 0
|
||||
else
|
||||
_err "Add txt record error."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
else
|
||||
_info "Updating record"
|
||||
record_id=$(printf $response | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
||||
_debug "record_id" $record_id
|
||||
|
||||
_cf_rest PUT "/zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}"
|
||||
if [ "$?" == "0" ]; then
|
||||
_info "Updated, sleeping 10 seconds"
|
||||
sleep 10
|
||||
#todo: check if the record takes effect
|
||||
return 0;
|
||||
fi
|
||||
_err "Update error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#################### Private functions bellow ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
# _domain_id=sdjkglgdfewsdfg
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
while [ '1' ] ; do
|
||||
h=$(printf $domain | cut -d . -f $i-100)
|
||||
if [ -z "$h" ] ; then
|
||||
#not valid
|
||||
return 1;
|
||||
fi
|
||||
|
||||
if ! _cf_rest GET "zones?name=$h" ; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if printf $response | grep \"name\":\"$h\" ; then
|
||||
_domain_id=$(printf "$response" | grep -o \"id\":\"[^\"]*\" | head -1 | cut -d : -f 2 | tr -d \")
|
||||
if [ "$_domain_id" ] ; then
|
||||
_sub_domain=$(printf $domain | cut -d . -f 1-$p)
|
||||
_domain=$h
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
let "i+=1"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
_cf_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
_debug $ep
|
||||
if [ "$3" ] ; then
|
||||
data="$3"
|
||||
_debug data "$data"
|
||||
response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json" --data $data)"
|
||||
else
|
||||
response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ] ; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug response "$response"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
_debug() {
|
||||
|
||||
if [ -z "$DEBUG" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -z "$2" ] ; then
|
||||
echo $1
|
||||
else
|
||||
echo "$1"="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
_info() {
|
||||
if [ -z "$2" ] ; then
|
||||
echo "$1"
|
||||
else
|
||||
echo "$1"="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
_err() {
|
||||
if [ -z "$2" ] ; then
|
||||
echo "$1" >&2
|
||||
else
|
||||
echo "$1"="$2" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
229
dnsapi/dns-dp.sh
229
dnsapi/dns-dp.sh
@@ -1,229 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Dnspod.cn Domain api
|
||||
#
|
||||
#DP_Id="1234"
|
||||
#
|
||||
#DP_Key="sADDsdasdgdsf"
|
||||
|
||||
|
||||
DP_Api="https://dnsapi.cn"
|
||||
|
||||
|
||||
#REST_API
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns-dp-add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$DP_Id" ] || [ -z "$DP_Key" ] ; then
|
||||
_err "You don't specify dnspod api key and key id yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
REST_API=$DP_Api
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf DP_Id "$DP_Id"
|
||||
_saveaccountconf DP_Key "$DP_Key"
|
||||
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root $fulldomain ; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
existing_records $_domain $_sub_domain
|
||||
_debug count "$count"
|
||||
if [ "$?" != "0" ] ; then
|
||||
_err "Error get existing records."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$count" == "0" ] ; then
|
||||
add_record $_domain $_sub_domain $txtvalue
|
||||
else
|
||||
update_record $_domain $_sub_domain $txtvalue
|
||||
fi
|
||||
}
|
||||
|
||||
#usage: root sub
|
||||
#return if the sub record already exists.
|
||||
#echos the existing records count.
|
||||
# '0' means doesn't exist
|
||||
existing_records() {
|
||||
_debug "Getting txt records"
|
||||
root=$1
|
||||
sub=$2
|
||||
|
||||
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if printf "$response" | grep 'No records' ; then
|
||||
count=0;
|
||||
return 0
|
||||
fi
|
||||
|
||||
if printf "$response" | grep "Action completed successful" >/dev/null ; then
|
||||
count=$(printf "$response" | grep '<type>TXT</type>' | wc -l)
|
||||
|
||||
record_id=$(printf "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1)
|
||||
return 0
|
||||
else
|
||||
_err "get existing records error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
count=0
|
||||
}
|
||||
|
||||
#add the txt record.
|
||||
#usage: root sub txtvalue
|
||||
add_record() {
|
||||
root=$1
|
||||
sub=$2
|
||||
txtvalue=$3
|
||||
fulldomain=$sub.$root
|
||||
|
||||
_info "Adding record"
|
||||
|
||||
if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if printf "$response" | grep "Action completed successful" ; then
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
return 1 #error
|
||||
}
|
||||
|
||||
#update the txt record
|
||||
#Usage: root sub txtvalue
|
||||
update_record() {
|
||||
root=$1
|
||||
sub=$2
|
||||
txtvalue=$3
|
||||
fulldomain=$sub.$root
|
||||
|
||||
_info "Updating record"
|
||||
|
||||
if ! _rest POST "Record.Modify" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认&record_id=$record_id"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if printf "$response" | grep "Action completed successful" ; then
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1 #error
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#################### Private functions bellow ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
# _domain_id=sdjkglgdfewsdfg
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
while [ '1' ] ; do
|
||||
h=$(printf $domain | cut -d . -f $i-100)
|
||||
if [ -z "$h" ] ; then
|
||||
#not valid
|
||||
return 1;
|
||||
fi
|
||||
|
||||
if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&domain=$h"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if printf "$response" | grep "Action completed successful" ; then
|
||||
_domain_id=$(printf "$response" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
||||
_debug _domain_id "$_domain_id"
|
||||
if [ "$_domain_id" ] ; then
|
||||
_sub_domain=$(printf $domain | cut -d . -f 1-$p)
|
||||
_debug _sub_domain $_sub_domain
|
||||
_domain=$h
|
||||
_debug _domain $_domain
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
let "i+=1"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
#Usage: method URI data
|
||||
_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
_debug $ep
|
||||
url="$REST_API/$ep"
|
||||
|
||||
_debug url "$url"
|
||||
|
||||
if [ "$3" ] ; then
|
||||
data="$3"
|
||||
_debug data "$data"
|
||||
response="$(curl --silent -X $m "$url" -d $data)"
|
||||
else
|
||||
response="$(curl --silent -X $m "$url" )"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ] ; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug response "$response"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
_debug() {
|
||||
|
||||
if [ -z "$DEBUG" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -z "$2" ] ; then
|
||||
echo $1
|
||||
else
|
||||
echo "$1"="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
_info() {
|
||||
if [ -z "$2" ] ; then
|
||||
echo "$1"
|
||||
else
|
||||
echo "$1"="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
_err() {
|
||||
if [ -z "$2" ] ; then
|
||||
echo "$1" >&2
|
||||
else
|
||||
echo "$1"="$2" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
#Here is a sample custom api script.
|
||||
#This file name is "dns-myapi.sh"
|
||||
#So, here must be a method dns-myapi-add()
|
||||
#Which will be called by le.sh to add the txt record to your api system.
|
||||
#returns 0 meanst success, otherwise error.
|
||||
|
||||
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns-myapi-add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_err "Not implemented!"
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#################### Private functions bellow ##################################
|
||||
|
||||
|
||||
_debug() {
|
||||
|
||||
if [ -z "$DEBUG" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -z "$2" ] ; then
|
||||
echo $1
|
||||
else
|
||||
echo "$1"="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
_info() {
|
||||
if [ -z "$2" ] ; then
|
||||
echo "$1"
|
||||
else
|
||||
echo "$1"="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
_err() {
|
||||
if [ -z "$2" ] ; then
|
||||
echo "$1" >&2
|
||||
else
|
||||
echo "$1"="$2" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
147
dnsapi/dns_ad.sh
Executable file
147
dnsapi/dns_ad.sh
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
#AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
|
||||
#This is the Alwaysdata api wrapper for acme.sh
|
||||
#
|
||||
#Author: Paul Koppen
|
||||
#Report Bugs here: https://github.com/wpk-/acme.sh
|
||||
|
||||
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_ad_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$AD_API_KEY" ]; then
|
||||
AD_API_KEY=""
|
||||
_err "You didn't specify the AD api key yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf AD_API_KEY "$AD_API_KEY"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_ad_tmpl_json="{\"domain\":$_domain_id,\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}"
|
||||
|
||||
if _ad_rest POST "record/" "$_ad_tmpl_json" && [ -z "$response" ]; then
|
||||
_info "txt record updated success."
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#fulldomain txtvalue
|
||||
dns_ad_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_debug "Getting txt records"
|
||||
_ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain"
|
||||
|
||||
if [ -n "$response" ]; then
|
||||
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
|
||||
_debug record_id "$record_id"
|
||||
if [ -z "$record_id" ]; then
|
||||
_err "Can not get record id to remove."
|
||||
return 1
|
||||
fi
|
||||
if _ad_rest DELETE "record/$record_id/" && [ -z "$response" ]; then
|
||||
_info "txt record deleted success."
|
||||
return 0
|
||||
fi
|
||||
_debug response "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
# _domain_id=12345
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
|
||||
if _ad_rest GET "domain/"; then
|
||||
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
_debug h "$h"
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")"
|
||||
if [ "$hostedzone" ]; then
|
||||
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_domain=$h
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#method uri qstr data
|
||||
_ad_rest() {
|
||||
mtd="$1"
|
||||
ep="$2"
|
||||
data="$3"
|
||||
|
||||
_debug mtd "$mtd"
|
||||
_debug ep "$ep"
|
||||
|
||||
export _H1="Accept: application/json"
|
||||
export _H2="Content-Type: application/json"
|
||||
|
||||
if [ "$mtd" != "GET" ]; then
|
||||
# both POST and DELETE.
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$AD_API_URL/$ep" "" "$mtd")"
|
||||
else
|
||||
response="$(_get "$AD_API_URL/$ep")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
187
dnsapi/dns_ali.sh
Executable file
187
dnsapi/dns_ali.sh
Executable file
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
Ali_API="https://alidns.aliyuncs.com/"
|
||||
|
||||
#Ali_Key="LTqIA87hOKdjevsf5"
|
||||
#Ali_Secret="0p5EYueFNq501xnCPzKNbx6K51qPH2"
|
||||
|
||||
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_ali_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
|
||||
Ali_Key=""
|
||||
Ali_Secret=""
|
||||
_err "You don't specify aliyun api key and secret yet."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and secret to the account conf file.
|
||||
_saveaccountconf Ali_Key "$Ali_Key"
|
||||
_saveaccountconf Ali_Secret "$Ali_Secret"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "Add record"
|
||||
_add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _ali_rest "Add record"
|
||||
}
|
||||
|
||||
dns_ali_rm() {
|
||||
fulldomain=$1
|
||||
_clean
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
_describe_records_query "$h"
|
||||
if ! _ali_rest "Get root" "ignore"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "PageNumber"; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_domain="$h"
|
||||
_debug _domain "$_domain"
|
||||
return 0
|
||||
fi
|
||||
p="$i"
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
_ali_rest() {
|
||||
signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
|
||||
signature=$(_ali_urlencode "$signature")
|
||||
url="$Ali_API?$query&Signature=$signature"
|
||||
|
||||
if ! response="$(_get "$url")"; then
|
||||
_err "Error <$1>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
|
||||
if [ -n "$message" ]; then
|
||||
_err "$message"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
|
||||
_ali_urlencode() {
|
||||
_str="$1"
|
||||
_str_len=${#_str}
|
||||
_u_i=1
|
||||
while [ "$_u_i" -le "$_str_len" ]; do
|
||||
_str_c="$(printf "%s" "$_str" | cut -c "$_u_i")"
|
||||
case $_str_c in [a-zA-Z0-9.~_-])
|
||||
printf "%s" "$_str_c"
|
||||
;;
|
||||
*)
|
||||
printf "%%%02X" "'$_str_c"
|
||||
;;
|
||||
esac
|
||||
_u_i="$(_math "$_u_i" + 1)"
|
||||
done
|
||||
}
|
||||
|
||||
_ali_nonce() {
|
||||
#_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
|
||||
#Not so good...
|
||||
date +"%s%N"
|
||||
}
|
||||
|
||||
_check_exist_query() {
|
||||
query=''
|
||||
query=$query'AccessKeyId='$Ali_Key
|
||||
query=$query'&Action=DescribeDomainRecords'
|
||||
query=$query'&DomainName='$1
|
||||
query=$query'&Format=json'
|
||||
query=$query'&RRKeyWord=_acme-challenge'
|
||||
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||
query=$query'&SignatureVersion=1.0'
|
||||
query=$query'&Timestamp='$(_timestamp)
|
||||
query=$query'&TypeKeyWord=TXT'
|
||||
query=$query'&Version=2015-01-09'
|
||||
}
|
||||
|
||||
_add_record_query() {
|
||||
query=''
|
||||
query=$query'AccessKeyId='$Ali_Key
|
||||
query=$query'&Action=AddDomainRecord'
|
||||
query=$query'&DomainName='$1
|
||||
query=$query'&Format=json'
|
||||
query=$query'&RR='$2
|
||||
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||
query=$query'&SignatureVersion=1.0'
|
||||
query=$query'&Timestamp='$(_timestamp)
|
||||
query=$query'&Type=TXT'
|
||||
query=$query'&Value='$3
|
||||
query=$query'&Version=2015-01-09'
|
||||
}
|
||||
|
||||
_delete_record_query() {
|
||||
query=''
|
||||
query=$query'AccessKeyId='$Ali_Key
|
||||
query=$query'&Action=DeleteDomainRecord'
|
||||
query=$query'&Format=json'
|
||||
query=$query'&RecordId='$1
|
||||
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||
query=$query'&SignatureVersion=1.0'
|
||||
query=$query'&Timestamp='$(_timestamp)
|
||||
query=$query'&Version=2015-01-09'
|
||||
}
|
||||
|
||||
_describe_records_query() {
|
||||
query=''
|
||||
query=$query'AccessKeyId='$Ali_Key
|
||||
query=$query'&Action=DescribeDomainRecords'
|
||||
query=$query'&DomainName='$1
|
||||
query=$query'&Format=json'
|
||||
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||
query=$query'&SignatureVersion=1.0'
|
||||
query=$query'&Timestamp='$(_timestamp)
|
||||
query=$query'&Version=2015-01-09'
|
||||
}
|
||||
|
||||
_clean() {
|
||||
_check_exist_query "$_domain"
|
||||
if ! _ali_rest "Check exist records" "ignore"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
records="$(echo "$response" -n | _egrep_o "\"RecordId\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
|
||||
printf "%s" "$records" \
|
||||
| while read -r record_id; do
|
||||
_delete_record_query "$record_id"
|
||||
_ali_rest "Delete record $record_id" "ignore"
|
||||
done
|
||||
}
|
||||
|
||||
_timestamp() {
|
||||
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
|
||||
}
|
||||
227
dnsapi/dns_aws.sh
Executable file
227
dnsapi/dns_aws.sh
Executable file
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
#
|
||||
#AWS_SECRET_ACCESS_KEY="xxxxxxx"
|
||||
|
||||
#This is the Amazon Route53 api wrapper for acme.sh
|
||||
|
||||
AWS_HOST="route53.amazonaws.com"
|
||||
AWS_URL="https://$AWS_HOST"
|
||||
|
||||
AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_aws_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
||||
AWS_ACCESS_KEY_ID=""
|
||||
AWS_SECRET_ACCESS_KEY=""
|
||||
_err "You don't specify aws route53 api key id and and api key secret yet."
|
||||
_err "Please create you key and try again. see $(__green $AWS_WIKI)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$AWS_SESSION_TOKEN" ]; then
|
||||
_saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
|
||||
_saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
|
||||
fi
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords><ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
|
||||
|
||||
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
|
||||
_info "txt record updated success."
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#fulldomain txtvalue
|
||||
dns_aws_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords><ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
|
||||
|
||||
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
|
||||
_info "txt record deleted success."
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
|
||||
if aws_rest GET "2013-04-01/hostedzone"; then
|
||||
_debug "response" "$response"
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "<Name>$h.</Name>"; then
|
||||
hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<.HostedZone>")"
|
||||
_debug hostedzone "$hostedzone"
|
||||
if [ -z "$hostedzone" ]; then
|
||||
_err "Error, can not get hostedzone."
|
||||
return 1
|
||||
fi
|
||||
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_domain=$h
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#method uri qstr data
|
||||
aws_rest() {
|
||||
mtd="$1"
|
||||
ep="$2"
|
||||
qsr="$3"
|
||||
data="$4"
|
||||
|
||||
_debug mtd "$mtd"
|
||||
_debug ep "$ep"
|
||||
_debug qsr "$qsr"
|
||||
_debug data "$data"
|
||||
|
||||
CanonicalURI="/$ep"
|
||||
_debug2 CanonicalURI "$CanonicalURI"
|
||||
|
||||
CanonicalQueryString="$qsr"
|
||||
_debug2 CanonicalQueryString "$CanonicalQueryString"
|
||||
|
||||
RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
|
||||
_debug2 RequestDate "$RequestDate"
|
||||
|
||||
#RequestDate="20161120T141056Z" ##############
|
||||
|
||||
export _H1="x-amz-date: $RequestDate"
|
||||
|
||||
aws_host="$AWS_HOST"
|
||||
CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n"
|
||||
SignedHeaders="host;x-amz-date"
|
||||
if [ -n "$AWS_SESSION_TOKEN" ]; then
|
||||
export _H2="x-amz-security-token: $AWS_SESSION_TOKEN"
|
||||
CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n"
|
||||
SignedHeaders="${SignedHeaders};x-amz-security-token"
|
||||
fi
|
||||
_debug2 CanonicalHeaders "$CanonicalHeaders"
|
||||
_debug2 SignedHeaders "$SignedHeaders"
|
||||
|
||||
RequestPayload="$data"
|
||||
_debug2 RequestPayload "$RequestPayload"
|
||||
|
||||
Hash="sha256"
|
||||
|
||||
CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)"
|
||||
_debug2 CanonicalRequest "$CanonicalRequest"
|
||||
|
||||
HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)"
|
||||
_debug2 HashedCanonicalRequest "$HashedCanonicalRequest"
|
||||
|
||||
Algorithm="AWS4-HMAC-SHA256"
|
||||
_debug2 Algorithm "$Algorithm"
|
||||
|
||||
RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
|
||||
_debug2 RequestDateOnly "$RequestDateOnly"
|
||||
|
||||
Region="us-east-1"
|
||||
Service="route53"
|
||||
|
||||
CredentialScope="$RequestDateOnly/$Region/$Service/aws4_request"
|
||||
_debug2 CredentialScope "$CredentialScope"
|
||||
|
||||
StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"
|
||||
|
||||
_debug2 StringToSign "$StringToSign"
|
||||
|
||||
kSecret="AWS4$AWS_SECRET_ACCESS_KEY"
|
||||
|
||||
#kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
|
||||
|
||||
_debug2 kSecret "$kSecret"
|
||||
|
||||
kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
|
||||
_debug2 kSecretH "$kSecretH"
|
||||
|
||||
kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
|
||||
_debug2 kDateH "$kDateH"
|
||||
|
||||
kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)"
|
||||
_debug2 kRegionH "$kRegionH"
|
||||
|
||||
kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)"
|
||||
_debug2 kServiceH "$kServiceH"
|
||||
|
||||
kSigningH="$(printf "aws4_request%s" | _hmac "$Hash" "$kServiceH" hex)"
|
||||
_debug2 kSigningH "$kSigningH"
|
||||
|
||||
signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)"
|
||||
_debug2 signature "$signature"
|
||||
|
||||
Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
|
||||
_debug2 Authorization "$Authorization"
|
||||
|
||||
_H3="Authorization: $Authorization"
|
||||
_debug _H3 "$_H3"
|
||||
|
||||
url="$AWS_URL/$ep"
|
||||
|
||||
if [ "$mtd" = "GET" ]; then
|
||||
response="$(_get "$url")"
|
||||
else
|
||||
response="$(_post "$data" "$url")"
|
||||
fi
|
||||
|
||||
_ret="$?"
|
||||
if [ "$_ret" = "0" ]; then
|
||||
if _contains "$response" "<ErrorResponse"; then
|
||||
_err "Response error:$response"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return "$_ret"
|
||||
}
|
||||
183
dnsapi/dns_cf.sh
Executable file
183
dnsapi/dns_cf.sh
Executable file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
#
|
||||
#CF_Email="xxxx@sss.com"
|
||||
|
||||
CF_Api="https://api.cloudflare.com/client/v4"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_cf_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
|
||||
CF_Key=""
|
||||
CF_Email=""
|
||||
_err "You don't specify cloudflare api key and email yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$CF_Email" "@"; then
|
||||
_err "It seems that the CF_Email=$CF_Email is not a valid email address."
|
||||
_err "Please check and retry."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf CF_Key "$CF_Key"
|
||||
_saveaccountconf CF_Email "$CF_Email"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_debug "Getting txt records"
|
||||
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
|
||||
|
||||
if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
|
||||
_err "Error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
|
||||
_debug count "$count"
|
||||
if [ "$count" = "0" ]; then
|
||||
_info "Adding record"
|
||||
if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
|
||||
if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
|
||||
_info "Added, OK"
|
||||
return 0
|
||||
else
|
||||
_err "Add txt record error."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
else
|
||||
_info "Updating record"
|
||||
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
|
||||
_debug "record_id" "$record_id"
|
||||
|
||||
_cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}"
|
||||
if [ "$?" = "0" ]; then
|
||||
_info "Updated, OK"
|
||||
return 0
|
||||
fi
|
||||
_err "Update error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
#fulldomain txtvalue
|
||||
dns_cf_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_debug "Getting txt records"
|
||||
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"
|
||||
|
||||
if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
|
||||
_err "Error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
|
||||
_debug count "$count"
|
||||
if [ "$count" = "0" ]; then
|
||||
_info "Don't need to remove."
|
||||
else
|
||||
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
|
||||
_debug "record_id" "$record_id"
|
||||
if [ -z "$record_id" ]; then
|
||||
_err "Can not get record id to remove."
|
||||
return 1
|
||||
fi
|
||||
if ! _cf_rest DELETE "zones/$_domain_id/dns_records/$record_id"; then
|
||||
_err "Delete record error."
|
||||
return 1
|
||||
fi
|
||||
_contains "$response" '"success":true'
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
# _domain_id=sdjkglgdfewsdfg
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
_debug h "$h"
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _cf_rest GET "zones?name=$h"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_domain=$h
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
_cf_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
data="$3"
|
||||
_debug "$ep"
|
||||
|
||||
export _H1="X-Auth-Email: $CF_Email"
|
||||
export _H2="X-Auth-Key: $CF_Key"
|
||||
export _H3="Content-Type: application/json"
|
||||
|
||||
if [ "$m" != "GET" ]; then
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$CF_Api/$ep" "" "$m")"
|
||||
else
|
||||
response="$(_get "$CF_Api/$ep")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
194
dnsapi/dns-cx.sh → dnsapi/dns_cx.sh
Normal file → Executable file
194
dnsapi/dns-cx.sh → dnsapi/dns_cx.sh
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# Cloudxns.com Domain api
|
||||
#
|
||||
@@ -6,56 +6,69 @@
|
||||
#
|
||||
#CX_Secret="sADDsdasdgdsf"
|
||||
|
||||
|
||||
CX_Api="https://www.cloudxns.net/api2"
|
||||
|
||||
|
||||
#REST_API
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns-cx-add() {
|
||||
dns_cx_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ] ; then
|
||||
|
||||
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then
|
||||
CX_Key=""
|
||||
CX_Secret=""
|
||||
_err "You don't specify cloudxns.com api key or secret yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
REST_API=$CX_Api
|
||||
|
||||
|
||||
REST_API="$CX_Api"
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf CX_Key "$CX_Key"
|
||||
_saveaccountconf CX_Secret "$CX_Secret"
|
||||
|
||||
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root $fulldomain ; then
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
existing_records $_domain $_sub_domain
|
||||
|
||||
existing_records "$_domain" "$_sub_domain"
|
||||
_debug count "$count"
|
||||
if [ "$?" != "0" ] ; then
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "Error get existing records."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$count" == "0" ] ; then
|
||||
add_record $_domain $_sub_domain $txtvalue
|
||||
if [ "$count" = "0" ]; then
|
||||
add_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||
else
|
||||
update_record $_domain $_sub_domain $txtvalue
|
||||
update_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||
fi
|
||||
|
||||
if [ "$?" == "0" ] ; then
|
||||
|
||||
if [ "$?" = "0" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#fulldomain
|
||||
dns_cx_rm() {
|
||||
fulldomain=$1
|
||||
REST_API="$CX_Api"
|
||||
if _get_root "$fulldomain"; then
|
||||
record_id=""
|
||||
existing_records "$_domain" "$_sub_domain"
|
||||
if ! [ "$record_id" = "" ]; then
|
||||
_rest DELETE "record/$record_id/$_domain_id" "{}"
|
||||
_info "Deleted record ${fulldomain}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
#usage: root sub
|
||||
#return if the sub record already exists.
|
||||
#echos the existing records count.
|
||||
@@ -64,24 +77,24 @@ existing_records() {
|
||||
_debug "Getting txt records"
|
||||
root=$1
|
||||
sub=$2
|
||||
|
||||
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100" ; then
|
||||
count=0
|
||||
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
|
||||
return 1
|
||||
fi
|
||||
count=0
|
||||
seg=$(printf "$response" | grep -o "{[^{]*host\":\"$_sub_domain[^}]*}")
|
||||
|
||||
seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}')
|
||||
_debug seg "$seg"
|
||||
if [ -z "$seg" ] ; then
|
||||
if [ -z "$seg" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if printf "$response" | grep '"type":"TXT"' > /dev/null ; then
|
||||
if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then
|
||||
count=1
|
||||
record_id=$(printf "$seg" | grep -o \"record_id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
||||
record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1)
|
||||
_debug record_id "$record_id"
|
||||
return 0
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
}
|
||||
|
||||
#add the txt record.
|
||||
@@ -90,14 +103,14 @@ add_record() {
|
||||
root=$1
|
||||
sub=$2
|
||||
txtvalue=$3
|
||||
fulldomain=$sub.$root
|
||||
|
||||
fulldomain="$sub.$root"
|
||||
|
||||
_info "Adding record"
|
||||
|
||||
|
||||
if ! _rest POST "record" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -107,21 +120,18 @@ update_record() {
|
||||
root=$1
|
||||
sub=$2
|
||||
txtvalue=$3
|
||||
fulldomain=$sub.$root
|
||||
|
||||
fulldomain="$sub.$root"
|
||||
|
||||
_info "Updating record"
|
||||
|
||||
if _rest PUT "record/$record_id" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}" ; then
|
||||
|
||||
if _rest PUT "record/$record_id" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#################### Private functions bellow ##################################
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
@@ -131,104 +141,76 @@ _get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
|
||||
if ! _rest GET "domain" ; then
|
||||
|
||||
if ! _rest GET "domain"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
while [ '1' ] ; do
|
||||
h=$(printf $domain | cut -d . -f $i-100)
|
||||
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
_debug h "$h"
|
||||
if [ -z "$h" ] ; then
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1;
|
||||
return 1
|
||||
fi
|
||||
|
||||
if printf "$response" | grep "$h." ; then
|
||||
seg=$(printf "$response" | grep -o "{[^{]*$h\.[^}]*\}" )
|
||||
if _contains "$response" "$h."; then
|
||||
seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}')
|
||||
_debug seg "$seg"
|
||||
_domain_id=$(printf "$seg" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
||||
_domain_id=$(printf "%s\n" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||
_debug _domain_id "$_domain_id"
|
||||
if [ "$_domain_id" ] ; then
|
||||
_sub_domain=$(printf $domain | cut -d . -f 1-$p)
|
||||
_debug _sub_domain $_sub_domain
|
||||
_domain=$h
|
||||
_debug _domain $_domain
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_domain="$h"
|
||||
_debug _domain "$_domain"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
let "i+=1"
|
||||
p="$i"
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
#Usage: method URI data
|
||||
_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
_debug $ep
|
||||
_debug ep "$ep"
|
||||
url="$REST_API/$ep"
|
||||
_debug url "$url"
|
||||
|
||||
cdate=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
cdate=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
|
||||
_debug cdate "$cdate"
|
||||
|
||||
|
||||
data="$3"
|
||||
_debug data "$data"
|
||||
|
||||
|
||||
sec="$CX_Key$url$data$cdate$CX_Secret"
|
||||
_debug sec "$sec"
|
||||
hmac=$(printf "$sec"| openssl md5 |cut -d " " -f 2)
|
||||
hmac=$(printf "%s" "$sec" | _digest md5 hex)
|
||||
_debug hmac "$hmac"
|
||||
|
||||
if [ "$3" ] ; then
|
||||
response="$(curl --silent -X $m "$url" -H "API-KEY: $CX_Key" -H "API-REQUEST-DATE: $cdate" -H "API-HMAC: $hmac" -H 'Content-Type: application/json' -d "$data")"
|
||||
|
||||
export _H1="API-KEY: $CX_Key"
|
||||
export _H2="API-REQUEST-DATE: $cdate"
|
||||
export _H3="API-HMAC: $hmac"
|
||||
export _H4="Content-Type: application/json"
|
||||
|
||||
if [ "$data" ]; then
|
||||
response="$(_post "$data" "$url" "" "$m")"
|
||||
else
|
||||
response="$(curl --silent -X $m "$url" -H "API-KEY: $CX_Key" -H "API-REQUEST-DATE: $cdate" -H "API-HMAC: $hmac" -H 'Content-Type: application/json')"
|
||||
response="$(_get "$url")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ] ; then
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug response "$response"
|
||||
if ! printf "$response" | grep '"message":"success"' > /dev/null ; then
|
||||
_debug2 response "$response"
|
||||
if ! _contains "$response" '"message":"success"'; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
_debug() {
|
||||
|
||||
if [ -z "$DEBUG" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -z "$2" ] ; then
|
||||
echo $1
|
||||
else
|
||||
echo "$1"="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
_info() {
|
||||
if [ -z "$2" ] ; then
|
||||
echo "$1"
|
||||
else
|
||||
echo "$1"="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
_err() {
|
||||
if [ -z "$2" ] ; then
|
||||
echo "$1" >&2
|
||||
else
|
||||
echo "$1"="$2" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
223
dnsapi/dns_dp.sh
Executable file
223
dnsapi/dns_dp.sh
Executable file
@@ -0,0 +1,223 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# Dnspod.cn Domain api
|
||||
#
|
||||
#DP_Id="1234"
|
||||
#
|
||||
#DP_Key="sADDsdasdgdsf"
|
||||
|
||||
REST_API="https://dnsapi.cn"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_dp_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then
|
||||
DP_Id=""
|
||||
DP_Key=""
|
||||
_err "You don't specify dnspod api key and key id yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf DP_Id "$DP_Id"
|
||||
_saveaccountconf DP_Key "$DP_Key"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
existing_records "$_domain" "$_sub_domain"
|
||||
_debug count "$count"
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "Error get existing records."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$count" = "0" ]; then
|
||||
add_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||
else
|
||||
update_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||
fi
|
||||
}
|
||||
|
||||
#fulldomain txtvalue
|
||||
dns_dp_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
||||
_err "Record.Lis error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" 'No records'; then
|
||||
_info "Don't need to remove."
|
||||
return 0
|
||||
fi
|
||||
|
||||
record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \")
|
||||
_debug record_id "$record_id"
|
||||
if [ -z "$record_id" ]; then
|
||||
_err "Can not get record id."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _rest POST "Record.Remove" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
|
||||
_err "Record.Remove error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_contains "$response" "Action completed successful"
|
||||
|
||||
}
|
||||
|
||||
#usage: root sub
|
||||
#return if the sub record already exists.
|
||||
#echos the existing records count.
|
||||
# '0' means doesn't exist
|
||||
existing_records() {
|
||||
_debug "Getting txt records"
|
||||
root=$1
|
||||
sub=$2
|
||||
|
||||
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" 'No records'; then
|
||||
count=0
|
||||
return 0
|
||||
fi
|
||||
|
||||
if _contains "$response" "Action completed successful"; then
|
||||
count=$(printf "%s" "$response" | grep -c '<type>TXT</type>' | tr -d ' ')
|
||||
record_id=$(printf "%s" "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1)
|
||||
_debug record_id "$record_id"
|
||||
return 0
|
||||
else
|
||||
_err "get existing records error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
count=0
|
||||
}
|
||||
|
||||
#add the txt record.
|
||||
#usage: root sub txtvalue
|
||||
add_record() {
|
||||
root=$1
|
||||
sub=$2
|
||||
txtvalue=$3
|
||||
fulldomain="$sub.$root"
|
||||
|
||||
_info "Adding record"
|
||||
|
||||
if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "Action completed successful"; then
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1 #error
|
||||
}
|
||||
|
||||
#update the txt record
|
||||
#Usage: root sub txtvalue
|
||||
update_record() {
|
||||
root=$1
|
||||
sub=$2
|
||||
txtvalue=$3
|
||||
fulldomain="$sub.$root"
|
||||
|
||||
_info "Updating record"
|
||||
|
||||
if ! _rest POST "Record.Modify" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认&record_id=$record_id"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "Action completed successful"; then
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1 #error
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
# _domain_id=sdjkglgdfewsdfg
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&domain=$h"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "Action completed successful"; then
|
||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||
_debug _domain_id "$_domain_id"
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_domain="$h"
|
||||
_debug _domain "$_domain"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p="$i"
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
#Usage: method URI data
|
||||
_rest() {
|
||||
m="$1"
|
||||
ep="$2"
|
||||
data="$3"
|
||||
_debug "$ep"
|
||||
url="$REST_API/$ep"
|
||||
|
||||
_debug url "$url"
|
||||
|
||||
if [ "$m" = "GET" ]; then
|
||||
response="$(_get "$url" | tr -d '\r')"
|
||||
else
|
||||
_debug2 data "$data"
|
||||
response="$(_post "$data" "$url" | tr -d '\r')"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
375
dnsapi/dns_freedns.sh
Executable file
375
dnsapi/dns_freedns.sh
Executable file
@@ -0,0 +1,375 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#This file name is "dns_freedns.sh"
|
||||
#So, here must be a method dns_freedns_add()
|
||||
#Which will be called by acme.sh to add the txt record to your api system.
|
||||
#returns 0 means success, otherwise error.
|
||||
#
|
||||
#Author: David Kerr
|
||||
#Report Bugs here: https://github.com/dkerr64/acme.sh
|
||||
#
|
||||
######## Public functions #####################
|
||||
|
||||
# Export FreeDNS userid and password in folowing variables...
|
||||
# FREEDNS_User=username
|
||||
# FREEDNS_Password=password
|
||||
# login cookie is saved in acme account config file so userid / pw
|
||||
# need to be set only when changed.
|
||||
|
||||
#Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_freedns_add() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
|
||||
_info "Add TXT record using FreeDNS"
|
||||
_debug "fulldomain: $fulldomain"
|
||||
_debug "txtvalue: $txtvalue"
|
||||
|
||||
if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then
|
||||
FREEDNS_User=""
|
||||
FREEDNS_Password=""
|
||||
if [ -z "$FREEDNS_COOKIE" ]; then
|
||||
_err "You did not specify the FreeDNS username and password yet."
|
||||
_err "Please export as FREEDNS_User / FREEDNS_Password and try again."
|
||||
return 1
|
||||
fi
|
||||
using_cached_cookies="true"
|
||||
else
|
||||
FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")"
|
||||
if [ -z "$FREEDNS_COOKIE" ]; then
|
||||
return 1
|
||||
fi
|
||||
using_cached_cookies="false"
|
||||
fi
|
||||
|
||||
_debug "FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)"
|
||||
|
||||
_saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE"
|
||||
|
||||
# split our full domain name into two parts...
|
||||
i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)"
|
||||
i="$(_math "$i" - 1)"
|
||||
top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)"
|
||||
i="$(_math "$i" - 1)"
|
||||
sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
|
||||
|
||||
# Sometimes FreeDNS does not reurn the subdomain page but rather
|
||||
# returns a page regarding becoming a premium member. This usually
|
||||
# happens after a period of inactivity. Immediately trying again
|
||||
# returns the correct subdomain page. So, we will try twice to
|
||||
# load the page and obtain our domain ID
|
||||
attempts=2
|
||||
while [ "$attempts" -gt "0" ]; do
|
||||
attempts="$(_math "$attempts" - 1)"
|
||||
|
||||
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
|
||||
if [ "$?" != "0" ]; then
|
||||
if [ "$using_cached_cookies" = "true" ]; then
|
||||
_err "Has your FreeDNS username and password channged? If so..."
|
||||
_err "Please export as FREEDNS_User / FREEDNS_Password and try again."
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Now convert the tables in the HTML to CSV. This litte gem from
|
||||
# http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
|
||||
subdomain_csv="$(echo "$htmlpage" \
|
||||
| grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
|
||||
| sed 's/^[\ \t]*//g' \
|
||||
| tr -d '\n' \
|
||||
| sed 's/<\/TR[^>]*>/\n/Ig' \
|
||||
| sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \
|
||||
| sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \
|
||||
| sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \
|
||||
| grep 'edit.php?' \
|
||||
| grep "$top_domain")"
|
||||
# The above beauty ends with striping out rows that do not have an
|
||||
# href to edit.php and do not have the top domain we are looking for.
|
||||
# So all we should be left with is CSV of table of subdomains we are
|
||||
# interested in.
|
||||
|
||||
# Now we have to read through this table and extract the data we need
|
||||
lines="$(echo "$subdomain_csv" | wc -l)"
|
||||
nl='
|
||||
'
|
||||
i=0
|
||||
found=0
|
||||
while [ "$i" -lt "$lines" ]; do
|
||||
i="$(_math "$i" + 1)"
|
||||
line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")"
|
||||
tmp="$(echo "$line" | cut -d ',' -f 1)"
|
||||
if [ $found = 0 ] && _startswith "$tmp" "<td>$top_domain"; then
|
||||
# this line will contain DNSdomainid for the top_domain
|
||||
DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')"
|
||||
found=1
|
||||
else
|
||||
# lines contain DNS records for all subdomains
|
||||
DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')"
|
||||
DNStype="$(echo "$line" | cut -d ',' -f 3)"
|
||||
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
|
||||
DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')"
|
||||
# Now get current value for the TXT record. This method may
|
||||
# not produce accurate results as the value field is truncated
|
||||
# on this webpage. To get full value we would need to load
|
||||
# another page. However we don't really need this so long as
|
||||
# there is only one TXT record for the acme chalenge subdomain.
|
||||
DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')"
|
||||
if [ $found != 0 ]; then
|
||||
break
|
||||
# we are breaking out of the loop at the first match of DNS name
|
||||
# and DNS type (if we are past finding the domainid). This assumes
|
||||
# that there is only ever one TXT record for the LetsEncrypt/acme
|
||||
# challenge subdomain. This seems to be a reasonable assumption
|
||||
# as the acme client deletes the TXT record on successful validation.
|
||||
fi
|
||||
else
|
||||
DNSname=""
|
||||
DNStype=""
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
_debug "DNSname: $DNSname DNStype: $DNStype DNSdomainid: $DNSdomainid DNSdataid: $DNSdataid"
|
||||
_debug "DNSvalue: $DNSvalue"
|
||||
|
||||
if [ -z "$DNSdomainid" ]; then
|
||||
# If domain ID is empty then something went wrong (top level
|
||||
# domain not found at FreeDNS).
|
||||
if [ "$attempts" = "0" ]; then
|
||||
# exhausted maximum retry attempts
|
||||
_debug "$htmlpage"
|
||||
_debug "$subdomain_csv"
|
||||
_err "Domain $top_domain not found at FreeDNS"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# break out of the 'retry' loop... we have found our domain ID
|
||||
break
|
||||
fi
|
||||
_info "Domain $top_domain not found at FreeDNS"
|
||||
_info "Retry loading subdomain page ($attempts attempts remaining)"
|
||||
done
|
||||
|
||||
if [ -z "$DNSdataid" ]; then
|
||||
# If data ID is empty then specific subdomain does not exist yet, need
|
||||
# to create it this should always be the case as the acme client
|
||||
# deletes the entry after domain is validated.
|
||||
_freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
|
||||
return $?
|
||||
else
|
||||
if [ "$txtvalue" = "$DNSvalue" ]; then
|
||||
# if value in TXT record matches value requested then DNS record
|
||||
# does not need to be updated. But...
|
||||
# Testing value match fails. Website is truncating the value field.
|
||||
# So for now we will always go down the else path. Though in theory
|
||||
# should never come here anyway as the acme client deletes
|
||||
# the TXT record on successful validation, so we should not even
|
||||
# have found a TXT record !!
|
||||
_info "No update necessary for $fulldomain at FreeDNS"
|
||||
return 0
|
||||
else
|
||||
# Delete the old TXT record (with the wrong value)
|
||||
_freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"
|
||||
if [ "$?" = "0" ]; then
|
||||
# And add in new TXT record with the value provided
|
||||
_freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
|
||||
fi
|
||||
return $?
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
#Usage: fulldomain txtvalue
|
||||
#Remove the txt record after validation.
|
||||
dns_freedns_rm() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
|
||||
_info "Delete TXT record using FreeDNS"
|
||||
_debug "fulldomain: $fulldomain"
|
||||
_debug "txtvalue: $txtvalue"
|
||||
|
||||
# Need to read cookie from conf file again in case new value set
|
||||
# during login to FreeDNS when TXT record was created.
|
||||
# acme.sh does not have a _readaccountconf() fuction
|
||||
FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")"
|
||||
_debug "FreeDNS login cookies: $FREEDNS_COOKIE"
|
||||
|
||||
# Sometimes FreeDNS does not reurn the subdomain page but rather
|
||||
# returns a page regarding becoming a premium member. This usually
|
||||
# happens after a period of inactivity. Immediately trying again
|
||||
# returns the correct subdomain page. So, we will try twice to
|
||||
# load the page and obtain our TXT record.
|
||||
attempts=2
|
||||
while [ "$attempts" -gt "0" ]; do
|
||||
attempts="$(_math "$attempts" - 1)"
|
||||
|
||||
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
|
||||
if [ "$?" != "0" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Now convert the tables in the HTML to CSV. This litte gem from
|
||||
# http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
|
||||
subdomain_csv="$(echo "$htmlpage" \
|
||||
| grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
|
||||
| sed 's/^[\ \t]*//g' \
|
||||
| tr -d '\n' \
|
||||
| sed 's/<\/TR[^>]*>/\n/Ig' \
|
||||
| sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \
|
||||
| sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \
|
||||
| sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \
|
||||
| grep 'edit.php?' \
|
||||
| grep "$fulldomain")"
|
||||
# The above beauty ends with striping out rows that do not have an
|
||||
# href to edit.php and do not have the domain name we are looking for.
|
||||
# So all we should be left with is CSV of table of subdomains we are
|
||||
# interested in.
|
||||
|
||||
# Now we have to read through this table and extract the data we need
|
||||
lines="$(echo "$subdomain_csv" | wc -l)"
|
||||
nl='
|
||||
'
|
||||
i=0
|
||||
found=0
|
||||
while [ "$i" -lt "$lines" ]; do
|
||||
i="$(_math "$i" + 1)"
|
||||
line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")"
|
||||
DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')"
|
||||
DNStype="$(echo "$line" | cut -d ',' -f 3)"
|
||||
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
|
||||
DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')"
|
||||
DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')"
|
||||
_debug "DNSvalue: $DNSvalue"
|
||||
# if [ "$DNSvalue" = "$txtvalue" ]; then
|
||||
# Testing value match fails. Website is truncating the value
|
||||
# field. So for now we will assume that there is only one TXT
|
||||
# field for the sub domain and just delete it. Currently this
|
||||
# is a safe assumption.
|
||||
_freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"
|
||||
return $?
|
||||
# fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# If we get this far we did not find a match (after two attempts)
|
||||
# Not necessarily an error, but log anyway.
|
||||
_debug2 "$subdomain_csv"
|
||||
_info "Cannot delete TXT record for $fulldomain/$txtvalue. Does not exist at FreeDNS"
|
||||
return 0
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
# usage: _freedns_login username password
|
||||
# print string "cookie=value" etc.
|
||||
# returns 0 success
|
||||
_freedns_login() {
|
||||
export _H1="Accept-Language:en-US"
|
||||
username="$1"
|
||||
password="$2"
|
||||
url="https://freedns.afraid.org/zc.php?step=2"
|
||||
|
||||
_debug "Login to FreeDNS as user $username"
|
||||
|
||||
htmlpage="$(_post "username=$(printf '%s' "$username" | _url_encode)&password=$(printf '%s' "$password" | _url_encode)&submit=Login&action=auth" "$url")"
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "FreeDNS login failed for user $username bad RC from _post"
|
||||
return 1
|
||||
fi
|
||||
|
||||
cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
|
||||
|
||||
# if cookies is not empty then logon successful
|
||||
if [ -z "$cookies" ]; then
|
||||
_debug "$htmlpage"
|
||||
_err "FreeDNS login failed for user $username. Check $HTTP_HEADER file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf "%s" "$cookies"
|
||||
return 0
|
||||
}
|
||||
|
||||
# usage _freedns_retrieve_subdomain_page login_cookies
|
||||
# echo page retrieved (html)
|
||||
# returns 0 success
|
||||
_freedns_retrieve_subdomain_page() {
|
||||
export _H1="Cookie:$1"
|
||||
export _H2="Accept-Language:en-US"
|
||||
url="https://freedns.afraid.org/subdomain/"
|
||||
|
||||
_debug "Retrieve subdmoain page from FreeDNS"
|
||||
|
||||
htmlpage="$(_get "$url")"
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "FreeDNS retrieve subdomins failed bad RC from _get"
|
||||
return 1
|
||||
elif [ -z "$htmlpage" ]; then
|
||||
_err "FreeDNS returned empty subdomain page"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug2 "$htmlpage"
|
||||
|
||||
printf "%s" "$htmlpage"
|
||||
return 0
|
||||
}
|
||||
|
||||
# usage _freedns_add_txt_record login_cookies domain_id subdomain value
|
||||
# returns 0 success
|
||||
_freedns_add_txt_record() {
|
||||
export _H1="Cookie:$1"
|
||||
export _H2="Accept-Language:en-US"
|
||||
domain_id="$2"
|
||||
subdomain="$3"
|
||||
value="$(printf '%s' "$4" | _url_encode)"
|
||||
url="http://freedns.afraid.org/subdomain/save.php?step=2"
|
||||
|
||||
htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")"
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "FreeDNS failed to add TXT record for $subdomain bad RC from _post"
|
||||
return 1
|
||||
elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then
|
||||
_debug "$htmlpage"
|
||||
_err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
|
||||
return 1
|
||||
elif _contains "$htmlpage" "security code was incorrect"; then
|
||||
_debug "$htmlpage"
|
||||
_err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested seurity code"
|
||||
_err "Note that you cannot use automatic DNS validation for FreeDNS public domains"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug2 "$htmlpage"
|
||||
_info "Added acme challenge TXT record for $fulldomain at FreeDNS"
|
||||
return 0
|
||||
}
|
||||
|
||||
# usage _freedns_delete_txt_record login_cookies data_id
|
||||
# returns 0 success
|
||||
_freedns_delete_txt_record() {
|
||||
export _H1="Cookie:$1"
|
||||
export _H2="Accept-Language:en-US"
|
||||
data_id="$2"
|
||||
url="https://freedns.afraid.org/subdomain/delete2.php"
|
||||
|
||||
htmlheader="$(_get "$url?data_id%5B%5D=$data_id&submit=delete+selected" "onlyheader")"
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "FreeDNS failed to delete TXT record for $data_id bad RC from _get"
|
||||
return 1
|
||||
elif ! _contains "$htmlheader" "200 OK"; then
|
||||
_debug "$htmlheader"
|
||||
_err "FreeDNS failed to delete TXT record $data_id"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Deleted acme challenge TXT record for $fulldomain at FreeDNS"
|
||||
return 0
|
||||
}
|
||||
117
dnsapi/dns_gd.sh
Executable file
117
dnsapi/dns_gd.sh
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#Godaddy domain api
|
||||
#
|
||||
#GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
#
|
||||
#GD_Secret="asdfsdfsfsdfsdfdfsdf"
|
||||
|
||||
GD_Api="https://api.godaddy.com/v1"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_gd_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then
|
||||
GD_Key=""
|
||||
GD_Secret=""
|
||||
_err "You don't specify godaddy api key and secret yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf GD_Key "$GD_Key"
|
||||
_saveaccountconf GD_Secret "$GD_Secret"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_info "Adding record"
|
||||
if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then
|
||||
if [ "$response" = "{}" ]; then
|
||||
_info "Added, sleeping 10 seconds"
|
||||
sleep 10
|
||||
#todo: check if the record takes effect
|
||||
return 0
|
||||
else
|
||||
_err "Add txt record error."
|
||||
_err "$response"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
|
||||
}
|
||||
|
||||
#fulldomain
|
||||
dns_gd_rm() {
|
||||
fulldomain=$1
|
||||
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _gd_rest GET "domains/$h"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" '"code":"NOT_FOUND"'; then
|
||||
_debug "$h not found"
|
||||
else
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_domain="$h"
|
||||
return 0
|
||||
fi
|
||||
p="$i"
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
_gd_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
data="$3"
|
||||
_debug "$ep"
|
||||
|
||||
export _H1="Authorization: sso-key $GD_Key:$GD_Secret"
|
||||
export _H2="Content-Type: application/json"
|
||||
|
||||
if [ "$data" ]; then
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$GD_Api/$ep" "" "$m")"
|
||||
else
|
||||
response="$(_get "$GD_Api/$ep")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
177
dnsapi/dns_ispconfig.sh
Executable file
177
dnsapi/dns_ispconfig.sh
Executable file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# ISPConfig 3.1 API
|
||||
# User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to:
|
||||
# - DNS zone Functions
|
||||
# - DNS txt Functions
|
||||
|
||||
# Report bugs to https://github.com/sjau/acme.sh
|
||||
|
||||
# Values to export:
|
||||
# export ISPC_User="remoteUser"
|
||||
# export ISPC_Password="remotePassword"
|
||||
# export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
|
||||
# export ISPC_Api_Insecure=1 # Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_ispconfig_add() {
|
||||
fulldomain="${1}"
|
||||
txtvalue="${2}"
|
||||
_debug "Calling: dns_ispconfig_add() '${fulldomain}' '${txtvalue}'"
|
||||
_ISPC_credentials && _ISPC_login && _ISPC_getZoneInfo && _ISPC_addTxt
|
||||
}
|
||||
|
||||
#Usage: dns_myapi_rm _acme-challenge.www.domain.com
|
||||
dns_ispconfig_rm() {
|
||||
fulldomain="${1}"
|
||||
_debug "Calling: dns_ispconfig_rm() '${fulldomain}'"
|
||||
_ISPC_credentials && _ISPC_login && _ISPC_rmTxt
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_ISPC_credentials() {
|
||||
if [ -z "${ISPC_User}" ] || [ -z "$ISPC_Password" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then
|
||||
ISPC_User=""
|
||||
ISPC_Password=""
|
||||
ISPC_Api=""
|
||||
ISPC_Api_Insecure=""
|
||||
_err "You haven't specified the ISPConfig Login data, URL and whether you want check the ISPC SSL cert. Please try again."
|
||||
return 1
|
||||
else
|
||||
_saveaccountconf ISPC_User "${ISPC_User}"
|
||||
_saveaccountconf ISPC_Password "${ISPC_Password}"
|
||||
_saveaccountconf ISPC_Api "${ISPC_Api}"
|
||||
_saveaccountconf ISPC_Api_Insecure "${ISPC_Api_Insecure}"
|
||||
# Set whether curl should use secure or insecure mode
|
||||
export HTTPS_INSECURE="${ISPC_Api_Insecure}"
|
||||
fi
|
||||
}
|
||||
|
||||
_ISPC_login() {
|
||||
_info "Getting Session ID"
|
||||
curData="{\"username\":\"${ISPC_User}\",\"password\":\"${ISPC_Password}\",\"client_login\":false}"
|
||||
curResult="$(_post "${curData}" "${ISPC_Api}?login")"
|
||||
_debug "Calling _ISPC_login: '${curData}' '${ISPC_Api}?login'"
|
||||
_debug "Result of _ISPC_login: '$curResult'"
|
||||
if _contains "${curResult}" '"code":"ok"'; then
|
||||
sessionID=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
|
||||
_info "Retrieved Session ID."
|
||||
_debug "Session ID: '${sessionID}'"
|
||||
else
|
||||
_err "Couldn't retrieve the Session ID."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_ISPC_getZoneInfo() {
|
||||
_info "Getting Zoneinfo"
|
||||
zoneEnd=false
|
||||
curZone="${fulldomain}"
|
||||
while [ "${zoneEnd}" = false ]; do
|
||||
# we can strip the first part of the fulldomain, since it's just the _acme-challenge string
|
||||
curZone="${curZone#*.}"
|
||||
# suffix . needed for zone -> domain.tld.
|
||||
curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"origin\":\"${curZone}.\"}}"
|
||||
curResult="$(_post "${curData}" "${ISPC_Api}?dns_zone_get")"
|
||||
_debug "Calling _ISPC_getZoneInfo: '${curData}' '${ISPC_Api}?login'"
|
||||
_debug "Result of _ISPC_getZoneInfo: '$curResult'"
|
||||
if _contains "${curResult}" '"id":"'; then
|
||||
zoneFound=true
|
||||
zoneEnd=true
|
||||
_info "Retrieved zone data."
|
||||
_debug "Zone data: '${curResult}'"
|
||||
fi
|
||||
if [ "${curZone#*.}" != "$curZone" ]; then
|
||||
_debug2 "$curZone still contains a '.' - so we can check next higher level"
|
||||
else
|
||||
zoneEnd=true
|
||||
_err "Couldn't retrieve zone data."
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
if [ "${zoneFound}" ]; then
|
||||
server_id=$(echo "${curResult}" | _egrep_o "server_id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
|
||||
_debug "Server ID: '${server_id}'"
|
||||
case "${server_id}" in
|
||||
'' | *[!0-9]*)
|
||||
_err "Server ID is not numeric."
|
||||
return 1
|
||||
;;
|
||||
*) _info "Retrieved Server ID" ;;
|
||||
esac
|
||||
zone=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
|
||||
_debug "Zone: '${zone}'"
|
||||
case "${zone}" in
|
||||
'' | *[!0-9]*)
|
||||
_err "Zone ID is not numeric."
|
||||
return 1
|
||||
;;
|
||||
*) _info "Retrieved Zone ID" ;;
|
||||
esac
|
||||
client_id=$(echo "${curResult}" | _egrep_o "sys_userid.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
|
||||
_debug "Client ID: '${client_id}'"
|
||||
case "${client_id}" in
|
||||
'' | *[!0-9]*)
|
||||
_err "Client ID is not numeric."
|
||||
return 1
|
||||
;;
|
||||
*) _info "Retrieved Client ID." ;;
|
||||
esac
|
||||
zoneFound=""
|
||||
zoneEnd=""
|
||||
fi
|
||||
}
|
||||
|
||||
_ISPC_addTxt() {
|
||||
curSerial="$(date +%s)"
|
||||
curStamp="$(date +'%F %T')"
|
||||
params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\""
|
||||
curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}}}"
|
||||
curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")"
|
||||
_debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'"
|
||||
_debug "Result of _ISPC_addTxt: '$curResult'"
|
||||
record_id=$(echo "${curResult}" | _egrep_o "\"response.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
|
||||
_debug "Record ID: '${record_id}'"
|
||||
case "${record_id}" in
|
||||
'' | *[!0-9]*)
|
||||
_err "Couldn't add ACME Challenge TXT record to zone."
|
||||
return 1
|
||||
;;
|
||||
*) _info "Added ACME Challenge TXT record to zone." ;;
|
||||
esac
|
||||
}
|
||||
|
||||
_ISPC_rmTxt() {
|
||||
# Need to get the record ID.
|
||||
curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"name\":\"${fulldomain}.\",\"type\":\"TXT\"}}"
|
||||
curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_get")"
|
||||
_debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_get'"
|
||||
_debug "Result of _ISPC_rmTxt: '$curResult'"
|
||||
if _contains "${curResult}" '"code":"ok"'; then
|
||||
record_id=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
|
||||
_debug "Record ID: '${record_id}'"
|
||||
case "${record_id}" in
|
||||
'' | *[!0-9]*)
|
||||
_err "Record ID is not numeric."
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
unset IFS
|
||||
_info "Retrieved Record ID."
|
||||
curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\"}"
|
||||
curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_delete")"
|
||||
_debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_delete'"
|
||||
_debug "Result of _ISPC_rmTxt: '$curResult'"
|
||||
if _contains "${curResult}" '"code":"ok"'; then
|
||||
_info "Removed ACME Challenge TXT record from zone."
|
||||
else
|
||||
_err "Couldn't remove ACME Challenge TXT record from zone."
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
78
dnsapi/dns_lexicon.sh
Executable file
78
dnsapi/dns_lexicon.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# dns api wrapper of lexicon for acme.sh
|
||||
|
||||
# https://github.com/AnalogJ/lexicon
|
||||
lexicon_cmd="lexicon"
|
||||
|
||||
wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_lexicon_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999)
|
||||
|
||||
if ! _exists "$lexicon_cmd"; then
|
||||
_err "Please install $lexicon_cmd first: $wiki"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$PROVIDER" ]; then
|
||||
PROVIDER=""
|
||||
_err "Please define env PROVIDER first: $wiki"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_savedomainconf PROVIDER "$PROVIDER"
|
||||
export PROVIDER
|
||||
|
||||
# e.g. busybox-ash does not know [:upper:]
|
||||
# shellcheck disable=SC2018,SC2019
|
||||
Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z')
|
||||
Lx_name_v=$(eval echo \$"$Lx_name")
|
||||
_debug "$Lx_name" "$Lx_name_v"
|
||||
if [ "$Lx_name_v" ]; then
|
||||
_saveaccountconf "$Lx_name" "$Lx_name_v"
|
||||
eval export "$Lx_name"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2018,SC2019
|
||||
Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z')
|
||||
Lx_token_v=$(eval echo \$"$Lx_token")
|
||||
_debug "$Lx_token" "$Lx_token_v"
|
||||
if [ "$Lx_token_v" ]; then
|
||||
_saveaccountconf "$Lx_token" "$Lx_token_v"
|
||||
eval export "$Lx_token"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2018,SC2019
|
||||
Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z')
|
||||
Lx_password_v=$(eval echo \$"$Lx_password")
|
||||
_debug "$Lx_password" "$Lx_password_v"
|
||||
if [ "$Lx_password_v" ]; then
|
||||
_saveaccountconf "$Lx_password" "$Lx_password_v"
|
||||
eval export "$Lx_password"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2018,SC2019
|
||||
Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z')
|
||||
Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken")
|
||||
_debug "$Lx_domaintoken" "$Lx_domaintoken_v"
|
||||
if [ "$Lx_domaintoken_v" ]; then
|
||||
eval export "$Lx_domaintoken"
|
||||
_saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v"
|
||||
fi
|
||||
|
||||
$lexicon_cmd "$PROVIDER" create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}"
|
||||
|
||||
}
|
||||
|
||||
#fulldomain
|
||||
dns_lexicon_rm() {
|
||||
fulldomain=$1
|
||||
|
||||
}
|
||||
183
dnsapi/dns_linode.sh
Executable file
183
dnsapi/dns_linode.sh
Executable file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#Author: Philipp Grosswiler <philipp.grosswiler@swiss-design.net>
|
||||
|
||||
LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action="
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_linode_add() {
|
||||
fulldomain="${1}"
|
||||
txtvalue="${2}"
|
||||
|
||||
if ! _Linode_API; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Using Linode"
|
||||
_debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "Domain does not exist."
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue"
|
||||
|
||||
if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then
|
||||
_resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
|
||||
_debug _resource_id "$_resource_id"
|
||||
|
||||
if [ -z "$_resource_id" ]; then
|
||||
_err "Error adding the domain resource."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Domain resource successfully added."
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#Usage: dns_linode_rm _acme-challenge.www.domain.com
|
||||
dns_linode_rm() {
|
||||
fulldomain="${1}"
|
||||
|
||||
if ! _Linode_API; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Using Linode"
|
||||
_debug "Calling: dns_linode_rm() '${fulldomain}'"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "Domain does not exist."
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_parameters="&DomainID=$_domain_id"
|
||||
|
||||
if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then
|
||||
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
|
||||
|
||||
resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")"
|
||||
if [ "$resource" ]; then
|
||||
_resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
|
||||
if [ "$_resource_id" ]; then
|
||||
_debug _resource_id "$_resource_id"
|
||||
|
||||
_parameters="&DomainID=$_domain_id&ResourceID=$_resource_id"
|
||||
|
||||
if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then
|
||||
_resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
|
||||
_debug _resource_id "$_resource_id"
|
||||
|
||||
if [ -z "$_resource_id" ]; then
|
||||
_err "Error deleting the domain resource."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Domain resource successfully deleted."
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_Linode_API() {
|
||||
if [ -z "$LINODE_API_KEY" ]; then
|
||||
LINODE_API_KEY=""
|
||||
|
||||
_err "You didn't specify the Linode API key yet."
|
||||
_err "Please create your key and try again."
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf LINODE_API_KEY "$LINODE_API_KEY"
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
# _domain_id=12345
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
|
||||
if _rest GET "domain.list"; then
|
||||
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
_debug h "$h"
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")"
|
||||
if [ "$hostedzone" ]; then
|
||||
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_domain=$h
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#method method action data
|
||||
_rest() {
|
||||
mtd="$1"
|
||||
ep="$2"
|
||||
data="$3"
|
||||
|
||||
_debug mtd "$mtd"
|
||||
_debug ep "$ep"
|
||||
|
||||
export _H1="Accept: application/json"
|
||||
export _H2="Content-Type: application/json"
|
||||
|
||||
if [ "$mtd" != "GET" ]; then
|
||||
# both POST and DELETE.
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$LINODE_API_URL$ep" "" "$mtd")"
|
||||
else
|
||||
response="$(_get "$LINODE_API_URL$ep$data")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
145
dnsapi/dns_lua.sh
Executable file
145
dnsapi/dns_lua.sh
Executable file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# bug reports to dev@1e.ca
|
||||
|
||||
#
|
||||
#LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
#
|
||||
#LUA_Email="user@luadns.net"
|
||||
|
||||
LUA_Api="https://api.luadns.com/v1"
|
||||
LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_lua_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then
|
||||
LUA_Key=""
|
||||
LUA_Email=""
|
||||
_err "You don't specify luadns api key and email yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf LUA_Key "$LUA_Key"
|
||||
_saveaccountconf LUA_Email "$LUA_Email"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_debug "Getting txt records"
|
||||
_LUA_rest GET "zones/${_domain_id}/records"
|
||||
|
||||
if ! _contains "$response" "\"id\":"; then
|
||||
_err "Error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ")
|
||||
_debug count "$count"
|
||||
if [ "$count" = "0" ]; then
|
||||
_info "Adding record"
|
||||
if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
|
||||
if _contains "$response" "$fulldomain"; then
|
||||
_info "Added"
|
||||
#todo: check if the record takes effect
|
||||
return 0
|
||||
else
|
||||
_err "Add txt record error."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
else
|
||||
_info "Updating record"
|
||||
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
|
||||
_debug "record_id" "$record_id"
|
||||
|
||||
_LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":$record_id,\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":$_domain_id,\"ttl\":120}"
|
||||
if [ "$?" = "0" ] && _contains "$response" "updated_at"; then
|
||||
_info "Updated!"
|
||||
#todo: check if the record takes effect
|
||||
return 0
|
||||
fi
|
||||
_err "Update error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
#fulldomain
|
||||
dns_lua_rm() {
|
||||
fulldomain=$1
|
||||
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
# _domain_id=sdjkglgdfewsdfg
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
if ! _LUA_rest GET "zones"; then
|
||||
return 1
|
||||
fi
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
_debug h "$h"
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "\"name\":\"$h\""; then
|
||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1)
|
||||
_debug _domain_id "$_domain_id"
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_domain="$h"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
_LUA_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
data="$3"
|
||||
_debug "$ep"
|
||||
|
||||
export _H1="Accept: application/json"
|
||||
export _H2="Authorization: Basic $LUA_auth"
|
||||
if [ "$data" ]; then
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$LUA_Api/$ep" "" "$m")"
|
||||
else
|
||||
response="$(_get "$LUA_Api/$ep")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
146
dnsapi/dns_me.sh
Executable file
146
dnsapi/dns_me.sh
Executable file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# bug reports to dev@1e.ca
|
||||
|
||||
# ME_Key=qmlkdjflmkqdjf
|
||||
# ME_Secret=qmsdlkqmlksdvnnpae
|
||||
|
||||
ME_Api=https://api.dnsmadeeasy.com/V2.0/dns/managed
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_me_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$ME_Key" ] || [ -z "$ME_Secret" ]; then
|
||||
ME_Key=""
|
||||
ME_Secret=""
|
||||
_err "You didn't specify DNSMadeEasy api key and secret yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf ME_Key "$ME_Key"
|
||||
_saveaccountconf ME_Secret "$ME_Secret"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_debug "Getting txt records"
|
||||
_me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT"
|
||||
|
||||
if ! _contains "$response" "\"totalRecords\":"; then
|
||||
_err "Error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2)
|
||||
_debug count "$count"
|
||||
if [ "$count" = "0" ]; then
|
||||
_info "Adding record"
|
||||
if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then
|
||||
if printf -- "%s" "$response" | grep \"id\": >/dev/null; then
|
||||
_info "Added"
|
||||
#todo: check if the record takes effect
|
||||
return 0
|
||||
else
|
||||
_err "Add txt record error."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
else
|
||||
_info "Updating record"
|
||||
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | head -n 1)
|
||||
_debug "record_id" "$record_id"
|
||||
|
||||
_me_rest PUT "$_domain_id/records/$record_id/" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"
|
||||
if [ "$?" = "0" ]; then
|
||||
_info "Updated"
|
||||
#todo: check if the record takes effect
|
||||
return 0
|
||||
fi
|
||||
_err "Update error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
#fulldomain
|
||||
dns_me_rm() {
|
||||
fulldomain=$1
|
||||
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
# _domain_id=sdjkglgdfewsdfg
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _me_rest GET "name?domainname=$h"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "\"name\":\"$h\""; then
|
||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | head -n 1 | cut -d : -f 2 | tr -d '}')
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_domain="$h"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
_me_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
data="$3"
|
||||
_debug "$ep"
|
||||
|
||||
cdate=$(date -u +"%a, %d %b %Y %T %Z")
|
||||
hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(printf "%s" "$ME_Secret" | _hex_dump | tr -d " ")" hex)
|
||||
|
||||
export _H1="x-dnsme-apiKey: $ME_Key"
|
||||
export _H2="x-dnsme-requestDate: $cdate"
|
||||
export _H3="x-dnsme-hmac: $hmac"
|
||||
|
||||
if [ "$data" ]; then
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$ME_Api/$ep" "" "$m")"
|
||||
else
|
||||
response="$(_get "$ME_Api/$ep")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
35
dnsapi/dns_myapi.sh
Executable file
35
dnsapi/dns_myapi.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#Here is a sample custom api script.
|
||||
#This file name is "dns_myapi.sh"
|
||||
#So, here must be a method dns_myapi_add()
|
||||
#Which will be called by acme.sh to add the txt record to your api system.
|
||||
#returns 0 means success, otherwise error.
|
||||
#
|
||||
#Author: Neilpang
|
||||
#Report Bugs here: https://github.com/Neilpang/acme.sh
|
||||
#
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_myapi_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_info "Using myapi"
|
||||
_debug fulldomain "$fulldomain"
|
||||
_debug txtvalue "$txtvalue"
|
||||
_err "Not implemented!"
|
||||
return 1
|
||||
}
|
||||
|
||||
#Usage: fulldomain txtvalue
|
||||
#Remove the txt record after validation.
|
||||
dns_myapi_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_info "Using myapi"
|
||||
_debug fulldomain "$fulldomain"
|
||||
_debug txtvalue "$txtvalue"
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
58
dnsapi/dns_nsupdate.sh
Executable file
58
dnsapi/dns_nsupdate.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: dns_nsupdate_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_nsupdate_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_checkKeyFile || return 1
|
||||
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
|
||||
# save the dns server and key to the account conf file.
|
||||
_saveaccountconf NSUPDATE_SERVER "${NSUPDATE_SERVER}"
|
||||
_saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}"
|
||||
_info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
|
||||
nsupdate -k "${NSUPDATE_KEY}" <<EOF
|
||||
server ${NSUPDATE_SERVER}
|
||||
update add ${fulldomain}. 60 in txt "${txtvalue}"
|
||||
send
|
||||
EOF
|
||||
if [ $? -ne 0 ]; then
|
||||
_err "error updating domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#Usage: dns_nsupdate_rm _acme-challenge.www.domain.com
|
||||
dns_nsupdate_rm() {
|
||||
fulldomain=$1
|
||||
_checkKeyFile || return 1
|
||||
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
|
||||
_info "removing ${fulldomain}. txt"
|
||||
nsupdate -k "${NSUPDATE_KEY}" <<EOF
|
||||
server ${NSUPDATE_SERVER}
|
||||
update delete ${fulldomain}. txt
|
||||
send
|
||||
EOF
|
||||
if [ $? -ne 0 ]; then
|
||||
_err "error updating domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_checkKeyFile() {
|
||||
if [ -z "${NSUPDATE_KEY}" ]; then
|
||||
_err "you must specify a path to the nsupdate key file"
|
||||
return 1
|
||||
fi
|
||||
if [ ! -r "${NSUPDATE_KEY}" ]; then
|
||||
_err "key ${NSUPDATE_KEY} is unreadable"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
295
dnsapi/dns_ovh.sh
Executable file
295
dnsapi/dns_ovh.sh
Executable file
@@ -0,0 +1,295 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#Applcation Key
|
||||
#OVH_AK="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||
#
|
||||
#Application Secret
|
||||
#OVH_AS="sdfsafsdfsdfdsfsdfsa"
|
||||
#
|
||||
#Consumer Key
|
||||
#OVH_CK="sdfsdfsdfsdfsdfdsf"
|
||||
|
||||
#OVH_END_POINT=ovh-eu
|
||||
|
||||
#'ovh-eu'
|
||||
OVH_EU='https://eu.api.ovh.com/1.0'
|
||||
|
||||
#'ovh-ca':
|
||||
OVH_CA='https://ca.api.ovh.com/1.0'
|
||||
|
||||
#'kimsufi-eu'
|
||||
KSF_EU='https://eu.api.kimsufi.com/1.0'
|
||||
|
||||
#'kimsufi-ca'
|
||||
KSF_CA='https://ca.api.kimsufi.com/1.0'
|
||||
|
||||
#'soyoustart-eu'
|
||||
SYS_EU='https://eu.api.soyoustart.com/1.0'
|
||||
|
||||
#'soyoustart-ca'
|
||||
SYS_CA='https://ca.api.soyoustart.com/1.0'
|
||||
|
||||
#'runabove-ca'
|
||||
RAV_CA='https://api.runabove.com/1.0'
|
||||
|
||||
wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api"
|
||||
|
||||
ovh_success="https://github.com/Neilpang/acme.sh/wiki/OVH-Success"
|
||||
|
||||
_ovh_get_api() {
|
||||
_ogaep="$1"
|
||||
|
||||
case "${_ogaep}" in
|
||||
|
||||
ovh-eu | ovheu)
|
||||
printf "%s" $OVH_EU
|
||||
return
|
||||
;;
|
||||
ovh-ca | ovhca)
|
||||
printf "%s" $OVH_CA
|
||||
return
|
||||
;;
|
||||
kimsufi-eu | kimsufieu)
|
||||
printf "%s" $KSF_EU
|
||||
return
|
||||
;;
|
||||
kimsufi-ca | kimsufica)
|
||||
printf "%s" $KSF_CA
|
||||
return
|
||||
;;
|
||||
soyoustart-eu | soyoustarteu)
|
||||
printf "%s" $SYS_EU
|
||||
return
|
||||
;;
|
||||
soyoustart-ca | soyoustartca)
|
||||
printf "%s" $SYS_CA
|
||||
return
|
||||
;;
|
||||
runabove-ca | runaboveca)
|
||||
printf "%s" $RAV_CA
|
||||
return
|
||||
;;
|
||||
|
||||
*)
|
||||
|
||||
_err "Unknown parameter : $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_ovh_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then
|
||||
OVH_AK=""
|
||||
OVH_AS=""
|
||||
_err "You don't specify OVH application key and application secret yet."
|
||||
_err "Please create you key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf OVH_AK "$OVH_AK"
|
||||
_saveaccountconf OVH_AS "$OVH_AS"
|
||||
|
||||
if [ -z "$OVH_END_POINT" ]; then
|
||||
OVH_END_POINT="ovh-eu"
|
||||
fi
|
||||
_info "Using OVH endpoint: $OVH_END_POINT"
|
||||
if [ "$OVH_END_POINT" != "ovh-eu" ]; then
|
||||
_saveaccountconf OVH_END_POINT "$OVH_END_POINT"
|
||||
fi
|
||||
|
||||
OVH_API="$(_ovh_get_api $OVH_END_POINT)"
|
||||
_debug OVH_API "$OVH_API"
|
||||
|
||||
if [ -z "$OVH_CK" ]; then
|
||||
_info "OVH consumer key is empty, Let's get one:"
|
||||
if ! _ovh_authentication; then
|
||||
_err "Can not get consumer key."
|
||||
fi
|
||||
#return and wait for retry.
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Checking authentication"
|
||||
|
||||
response="$(_ovh_rest GET "domain/")"
|
||||
if _contains "$response" "INVALID_CREDENTIAL"; then
|
||||
_err "The consumer key is invalid: $OVH_CK"
|
||||
_err "Please retry to create a new one."
|
||||
_clearaccountconf OVH_CK
|
||||
return 1
|
||||
fi
|
||||
_info "Consumer key is ok."
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_debug "Getting txt records"
|
||||
_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"
|
||||
|
||||
if _contains "$response" '\[\]' || _contains "$response" "This service does not exist"; then
|
||||
_info "Adding record"
|
||||
if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then
|
||||
if _contains "$response" "$txtvalue"; then
|
||||
_ovh_rest POST "domain/zone/$_domain/refresh"
|
||||
_debug "Refresh:$response"
|
||||
_info "Added, sleeping 10 seconds"
|
||||
sleep 10
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
else
|
||||
_info "Updating record"
|
||||
record_id=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 1)
|
||||
if [ -z "$record_id" ]; then
|
||||
_err "Can not get record id."
|
||||
return 1
|
||||
fi
|
||||
_debug "record_id" "$record_id"
|
||||
|
||||
if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then
|
||||
if _contains "$response" "null"; then
|
||||
_ovh_rest POST "domain/zone/$_domain/refresh"
|
||||
_debug "Refresh:$response"
|
||||
_info "Updated, sleeping 10 seconds"
|
||||
sleep 10
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
_err "Update error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
#fulldomain
|
||||
dns_ovh_rm() {
|
||||
fulldomain=$1
|
||||
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_ovh_authentication() {
|
||||
|
||||
_H1="X-Ovh-Application: $OVH_AK"
|
||||
_H2="Content-type: application/json"
|
||||
_H3=""
|
||||
_H4=""
|
||||
|
||||
_ovhdata='{"accessRules": [{"method": "GET","path": "/*"},{"method": "POST","path": "/*"},{"method": "PUT","path": "/*"},{"method": "DELETE","path": "/*"}],"redirection":"'$ovh_success'"}'
|
||||
|
||||
response="$(_post "$_ovhdata" "$OVH_API/auth/credential")"
|
||||
_debug3 response "$response"
|
||||
validationUrl="$(echo "$response" | _egrep_o "validationUrl\":\"[^\"]*\"" | _egrep_o "http.*\"" | tr -d '"')"
|
||||
if [ -z "$validationUrl" ]; then
|
||||
_err "Unable to get validationUrl"
|
||||
return 1
|
||||
fi
|
||||
_debug validationUrl "$validationUrl"
|
||||
|
||||
consumerKey="$(echo "$response" | _egrep_o "consumerKey\":\"[^\"]*\"" | cut -d : -f 2 | tr -d '"')"
|
||||
if [ -z "$consumerKey" ]; then
|
||||
_err "Unable to get consumerKey"
|
||||
return 1
|
||||
fi
|
||||
_debug consumerKey "$consumerKey"
|
||||
|
||||
OVH_CK="$consumerKey"
|
||||
_saveaccountconf OVH_CK "$OVH_CK"
|
||||
|
||||
_info "Please open this link to do authentication: $(__green "$validationUrl")"
|
||||
|
||||
_info "Here is a guide for you: $(__green "$wiki")"
|
||||
_info "Please retry after the authentication is done."
|
||||
|
||||
}
|
||||
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=2
|
||||
p=1
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _ovh_rest GET "domain/zone/$h"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$response" "This service does not exist" >/dev/null; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||
_domain="$h"
|
||||
return 0
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
_ovh_timestamp() {
|
||||
_H1=""
|
||||
_H2=""
|
||||
_H3=""
|
||||
_H4=""
|
||||
_H5=""
|
||||
_get "$OVH_API/auth/time" "" 30
|
||||
}
|
||||
|
||||
_ovh_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
data="$3"
|
||||
_debug "$ep"
|
||||
|
||||
_ovh_url="$OVH_API/$ep"
|
||||
_debug2 _ovh_url "$_ovh_url"
|
||||
_ovh_t="$(_ovh_timestamp)"
|
||||
_debug2 _ovh_t "$_ovh_t"
|
||||
_ovh_p="$OVH_AS+$OVH_CK+$m+$_ovh_url+$data+$_ovh_t"
|
||||
_debug _ovh_p "$_ovh_p"
|
||||
_ovh_hex="$(printf "%s" "$_ovh_p" | _digest sha1 hex)"
|
||||
_debug2 _ovh_hex "$_ovh_hex"
|
||||
|
||||
export _H1="X-Ovh-Application: $OVH_AK"
|
||||
export _H2="X-Ovh-Signature: \$1\$$_ovh_hex"
|
||||
_debug2 _H2 "$_H2"
|
||||
export _H3="X-Ovh-Timestamp: $_ovh_t"
|
||||
export _H4="X-Ovh-Consumer: $OVH_CK"
|
||||
export _H5="Content-Type: application/json;charset=utf-8"
|
||||
if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$_ovh_url" "" "$m")"
|
||||
else
|
||||
response="$(_get "$_ovh_url")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
184
dnsapi/dns_pdns.sh
Executable file
184
dnsapi/dns_pdns.sh
Executable file
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#PowerDNS Emdedded API
|
||||
#https://doc.powerdns.com/md/httpapi/api_spec/
|
||||
#
|
||||
#PDNS_Url="http://ns.example.com:8081"
|
||||
#PDNS_ServerId="localhost"
|
||||
#PDNS_Token="0123456789ABCDEF"
|
||||
#PDNS_Ttl=60
|
||||
|
||||
DEFAULT_PDNS_TTL=60
|
||||
|
||||
######## Public functions #####################
|
||||
#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
|
||||
#fulldomain
|
||||
#txtvalue
|
||||
dns_pdns_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if [ -z "$PDNS_Url" ]; then
|
||||
PDNS_Url=""
|
||||
_err "You don't specify PowerDNS address."
|
||||
_err "Please set PDNS_Url and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$PDNS_ServerId" ]; then
|
||||
PDNS_ServerId=""
|
||||
_err "You don't specify PowerDNS server id."
|
||||
_err "Please set you PDNS_ServerId and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$PDNS_Token" ]; then
|
||||
PDNS_Token=""
|
||||
_err "You don't specify PowerDNS token."
|
||||
_err "Please create you PDNS_Token and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$PDNS_Ttl" ]; then
|
||||
PDNS_Ttl="$DEFAULT_PDNS_TTL"
|
||||
fi
|
||||
|
||||
#save the api addr and key to the account conf file.
|
||||
_saveaccountconf PDNS_Url "$PDNS_Url"
|
||||
_saveaccountconf PDNS_ServerId "$PDNS_ServerId"
|
||||
_saveaccountconf PDNS_Token "$PDNS_Token"
|
||||
|
||||
if [ "$PDNS_Ttl" != "$DEFAULT_PDNS_TTL" ]; then
|
||||
_saveaccountconf PDNS_Ttl "$PDNS_Ttl"
|
||||
fi
|
||||
|
||||
_debug "Detect root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain "$_domain"
|
||||
|
||||
if ! set_record "$_domain" "$fulldomain" "$txtvalue"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#fulldomain
|
||||
dns_pdns_rm() {
|
||||
fulldomain=$1
|
||||
|
||||
_debug "Detect root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain "$_domain"
|
||||
|
||||
if ! rm_record "$_domain" "$fulldomain"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
set_record() {
|
||||
_info "Adding record"
|
||||
root=$1
|
||||
full=$2
|
||||
txtvalue=$3
|
||||
|
||||
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then
|
||||
_err "Set txt record error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! notify_slaves "$root"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
rm_record() {
|
||||
_info "Remove record"
|
||||
root=$1
|
||||
full=$2
|
||||
|
||||
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}"; then
|
||||
_err "Delete txt record error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! notify_slaves "$root"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
notify_slaves() {
|
||||
root=$1
|
||||
|
||||
if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root./notify"; then
|
||||
_err "Notify slaves error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _domain=domain.com
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=1
|
||||
|
||||
if _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones"; then
|
||||
_zones_response="$response"
|
||||
fi
|
||||
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||
if [ -z "$h" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$_zones_response" "\"name\": \"$h.\""; then
|
||||
_domain="$h"
|
||||
return 0
|
||||
fi
|
||||
|
||||
i=$(_math $i + 1)
|
||||
done
|
||||
_debug "$domain not found"
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_pdns_rest() {
|
||||
method=$1
|
||||
ep=$2
|
||||
data=$3
|
||||
|
||||
export _H1="X-API-Key: $PDNS_Token"
|
||||
|
||||
if [ ! "$method" = "GET" ]; then
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$PDNS_Url$ep" "" "$method")"
|
||||
else
|
||||
response="$(_get "$PDNS_Url$ep")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user