Compare commits
2745 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
35632f2109 | ||
|
821adcf178 | ||
|
8bf9482bc0 | ||
|
fe8ad3548b | ||
|
4f17bc0d86 | ||
|
1a43c81840 | ||
|
d058ac6174 | ||
|
a4e7806d21 | ||
|
a00323412b | ||
|
838a20ea95 | ||
|
5c78a5e4fe | ||
|
30ed4af38d | ||
|
6a9304dd1c | ||
|
1782eeb785 | ||
|
1aabb7d6de | ||
|
d4b8f9700b | ||
|
2ebecf1aa0 | ||
|
7031df4948 | ||
|
e0381dd757 | ||
|
9b2eae24d2 | ||
|
7362e8de4d | ||
|
87beb0a5f2 | ||
|
fc69cea4f7 | ||
|
ad4780a1ac | ||
|
138ab6dbbd | ||
|
26c2fc21c8 | ||
|
fef74c3bca | ||
|
c390f1bfee | ||
|
f6698d4a84 | ||
|
4193196c8b | ||
|
da06dc3728 | ||
|
a0b8be5941 | ||
|
fe971680ea | ||
|
254eb8f304 | ||
|
c9d15901d1 | ||
|
59b3f5fb19 | ||
|
dbe9dd47ce | ||
|
8e35f8c3aa | ||
|
80970a0ac6 | ||
|
432f6ac4d7 | ||
|
eaf11009d1 | ||
|
df93fb773f | ||
|
f6887a4dac | ||
|
1c58c4c409 | ||
|
a972901438 | ||
|
25703296a6 | ||
|
7ebe97b931 | ||
|
76719d1bf5 | ||
|
cb113437f6 | ||
|
e6b3e42d61 | ||
|
c20b0169a9 | ||
|
89342bcb75 | ||
|
e036eea362 | ||
|
2044d633e9 | ||
|
8cb684e6bd | ||
|
997bd3392f | ||
|
ad44c87746 | ||
|
45ea2f82ba | ||
|
df6aa99ec2 | ||
|
114eb6288d | ||
|
2ea37e6a0d | ||
|
ea2330b49f | ||
|
610bb2b85c | ||
|
fca6e9b932 | ||
|
fc7f86104e | ||
|
d057a9bb6d | ||
|
167aba6f26 | ||
|
9ecd84080b | ||
|
22d260f4e6 | ||
|
ea94477cd4 | ||
|
522c953860 | ||
|
f86ee84457 | ||
|
2d282597ca | ||
|
8635d89cc8 | ||
|
dc341ef9c1 | ||
|
beb31ab2fa | ||
|
e0214a2c2a | ||
|
5275daa66c | ||
|
fb27261568 | ||
|
5a20ce81e3 | ||
|
1454b85671 | ||
|
fc4dba4468 | ||
|
adff28dade | ||
|
c345ecae63 | ||
|
e6461380c6 | ||
|
deb2a3e415 | ||
|
c71dcd7611 | ||
|
c1e05664ed | ||
|
0f1e5f4fa4 | ||
|
0fa20da990 | ||
|
02fb40c507 | ||
|
dc6ea97877 | ||
|
031d53b04f | ||
|
3006c90fb8 | ||
|
9cec2688ed | ||
|
65c3dc21f4 | ||
|
cefa7d940a | ||
|
fab292d2de | ||
|
00bbe68f78 | ||
|
fa3591f4f2 | ||
|
10cfc6838d | ||
|
42e78f9a3e | ||
|
0122eabd44 | ||
|
435bb3f1d3 | ||
|
1d59d43286 | ||
|
fe30bf7d09 | ||
|
5f68ad4e19 | ||
|
3fc39aad33 | ||
|
a94653ba77 | ||
|
d8637b2c0f | ||
|
f1b6016157 | ||
|
de99d6d9fc | ||
|
bb8386ab85 | ||
|
ca6226359b | ||
|
adfafe5c54 | ||
|
a8d8fefceb | ||
|
3bb5943b20 | ||
|
57da04b5ec | ||
|
e711d168df | ||
|
1700f064b3 | ||
|
de902166a8 | ||
|
2f08bd1965 | ||
|
13c68cd799 | ||
|
c7d78f4594 | ||
|
2f1ca949f0 | ||
|
a6488ff9ac | ||
|
cb5eae888d | ||
|
b6f7710621 | ||
|
3e36f05a8c | ||
|
bcb7e5f2c8 | ||
|
3cefcd8204 | ||
|
4cd1871816 | ||
|
7aaa9583fa | ||
|
dd582c0306 | ||
|
5cded5b53e | ||
|
0a64567822 | ||
|
7ca861805d | ||
|
6b6faa8129 | ||
|
f84577bcda | ||
|
37f9fd3498 | ||
|
11aef82993 | ||
|
9861e2d724 | ||
|
7ae0d0caa3 | ||
|
93d2c9a3f0 | ||
|
60569fdd83 | ||
|
0f61e9c15e | ||
|
d81fc155cb | ||
|
60f7750d77 | ||
|
9bdfd8f4fe | ||
|
945b7de76f | ||
|
3c35eadbc4 | ||
|
ab86e056a2 | ||
|
a2bc79ddd5 | ||
|
5789e80d74 | ||
|
65868ab8a7 | ||
|
89586530a5 | ||
|
0d93145834 | ||
|
617f4acfd6 | ||
|
fe4113d623 | ||
|
9a0e15cced | ||
|
e0c63d58b2 | ||
|
f7f8ea9b97 | ||
|
58cad98cd8 | ||
|
74ffbb2172 | ||
|
cc9c85cc1a | ||
|
a8f252e45d | ||
|
b821836dc4 | ||
|
f7e09af5c9 | ||
|
304cb56337 | ||
|
6b7b5caf54 | ||
|
fd461fe015 | ||
|
2797d2c535 | ||
|
f440656572 | ||
|
b1d019146a | ||
|
1dbc58d4e0 | ||
|
47ccb28482 | ||
|
744dea00ca | ||
|
373c2b379c | ||
|
957bbab440 | ||
|
bd48c99383 | ||
|
52d1d421a3 | ||
|
c3cc13595d | ||
|
adc8031e34 | ||
|
0974c74a89 | ||
|
c64aae6f39 | ||
|
b7b1714637 | ||
|
d8525493a1 | ||
|
30d0945855 | ||
|
ffde1f8343 | ||
|
96c35b41ed | ||
|
dc2979926f | ||
|
ff357dd3fb | ||
|
f35e15204d | ||
|
3ae4ba3300 | ||
|
0d8a314bcf | ||
|
e7cfde1904 | ||
|
aa41df4e7d | ||
|
d73953af3d | ||
|
54eec82311 | ||
|
dab244ad25 | ||
|
dbe7cb8dbb | ||
|
e814cccc44 | ||
|
1078fdc157 | ||
|
4bf4259dda | ||
|
03b53cbb60 | ||
|
7ef1340e2a | ||
|
c0b87adee5 | ||
|
4a8c2251e0 | ||
|
d1df5f3021 | ||
|
e1acea52f8 | ||
|
2e9f159225 | ||
|
2fcda9a73a | ||
|
78ba205f4d | ||
|
f44dec2c8d | ||
|
8c07af6fc7 | ||
|
cd01104de9 | ||
|
28f438a6bd | ||
|
9ff89b570f | ||
|
bc90376489 | ||
|
43b5ea801f | ||
|
9863e7ea6e | ||
|
ebaa39b03f | ||
|
fa3d7ad14b | ||
|
c51104f956 | ||
|
84795ff4d9 | ||
|
cc5c722e29 | ||
|
4fcddd1893 | ||
|
c8604255e4 | ||
|
492826a7f2 | ||
|
5e64781d65 | ||
|
2d4b900e33 | ||
|
d2481f5790 | ||
|
49f6104f03 | ||
|
2728d2aa6e | ||
|
0588fc6b7c | ||
|
e3cd52cab4 | ||
|
b2c6b9a320 | ||
|
39fa40ab12 | ||
|
0bf87bf4af | ||
|
92d37f6eaf | ||
|
d3b022fe17 | ||
|
79640f6b7d | ||
|
2cbdf274b1 | ||
|
6af5293315 | ||
|
bd3a2b1bb5 | ||
|
ff090d2f74 | ||
|
68e3a12a91 | ||
|
50eda6b678 | ||
|
192ec598a3 | ||
|
5b449999a5 | ||
|
afed62f6de | ||
|
59d1e16f9c | ||
|
dbe0d477d6 | ||
|
7248560169 | ||
|
f840f7d75b | ||
|
6e14a073ff | ||
|
cf3839ecec | ||
|
aa8cf76fb1 | ||
|
10b4bb598a | ||
|
de14d59bb3 | ||
|
d76272f0ea | ||
|
e04093efe2 | ||
|
bd6bbba948 | ||
|
37e4f35c93 | ||
|
0084cb7403 | ||
|
99e5c159a7 | ||
|
802121d54a | ||
|
160b2e95c9 | ||
|
7ec692cdef | ||
|
3dca67112d | ||
|
f8dac5905c | ||
|
48e4e41e05 | ||
|
22374b81de | ||
|
b9157e29cb | ||
|
bd247c35f2 | ||
|
7da9a45c61 | ||
|
122dfa12ac | ||
|
1905830b20 | ||
|
bfb41ce123 | ||
|
85e3ecfe0b | ||
|
9e073c954d | ||
|
b79c3f5cc4 | ||
|
ad5acb80fe | ||
|
7b7c834b08 | ||
|
42827be7c3 | ||
|
7022d27b8e | ||
|
ab911f1ce9 | ||
|
a6a1de50c8 | ||
|
97723fbbc9 | ||
|
2e58cf1168 | ||
|
3ca97d7258 | ||
|
9786dccdee | ||
|
a3612f53dd | ||
|
112257c49e | ||
|
d8e2b96bce | ||
|
6992659ba9 | ||
|
05696d443a | ||
|
f59a925897 | ||
|
afacdfcb95 | ||
|
8cb1b6b5d5 | ||
|
f7d9d53ad2 | ||
|
f4315e2c6f | ||
|
f0ac566c93 | ||
|
50f6a459cf | ||
|
179c80ae6d | ||
|
6e72f161a6 | ||
|
f71d8d7348 | ||
|
a12a3640a7 | ||
|
3b7bc5a56a | ||
|
e09d45c844 | ||
|
36fc321096 | ||
|
98a7a01dbb | ||
|
0f7be90500 | ||
|
7aaf4432d4 | ||
|
884a8995b4 | ||
|
bb42595275 | ||
|
a4bd89c938 | ||
|
f364d4fbef | ||
|
f899d0d8ed | ||
|
074cf00a7c | ||
|
15d10eeebc | ||
|
bea71f3411 | ||
|
eb99803b53 | ||
|
a60d0c4108 | ||
|
1cc3a13c49 | ||
|
feffbba6de | ||
|
6ea09444ec | ||
|
f1f486dacf | ||
|
fec4af3194 | ||
|
5342c7c82b | ||
|
8454ffa331 | ||
|
199977be6a | ||
|
00dbc3881f | ||
|
d93a5b2d20 | ||
|
8ca5ca6594 | ||
|
fe890c62f4 | ||
|
e15513bfdd | ||
|
dbe569c0d9 | ||
|
f2e1b589b5 | ||
|
377a37e4c9 | ||
|
6e163208b4 | ||
|
87a7bde618 | ||
|
37b0498699 | ||
|
59f976dc48 | ||
|
8565a853a8 | ||
|
dfd49e46ad | ||
|
73bbaced62 | ||
|
0c8870cb7f | ||
|
1a90f66f73 | ||
|
558e706bde | ||
|
1a08be0a3f | ||
|
29b2960805 | ||
|
f7f3a0bf0d | ||
|
ae4c186f55 | ||
|
af534a73fc | ||
|
772bbdc862 | ||
|
86521ec443 | ||
|
e3c4c9265d | ||
|
b3f8612e61 | ||
|
27b1dd04c4 | ||
|
46a876445f | ||
|
9bb58e47a7 | ||
|
b8447fcab8 | ||
|
3abcfd8fa9 | ||
|
f4ff2d5d2e | ||
|
09b41aa667 | ||
|
87dc4fe388 | ||
|
eed8a7f078 | ||
|
c18364c755 | ||
|
04946e992e | ||
|
5533782152 | ||
|
3f42487f0a | ||
|
8bdcd22486 | ||
|
b32d22731b | ||
|
b788cc24d1 | ||
|
94948f6d34 | ||
|
e5b76ed4c4 | ||
|
29a2920a2c | ||
|
089d35708b | ||
|
9b0b5bce9f | ||
|
ef20a0128f | ||
|
3039e4eb6d | ||
|
9143cd1485 | ||
|
13d31ecb7f | ||
|
a936b2f1f6 | ||
|
8d00f489cd | ||
|
b793dbf977 | ||
|
d52b38777a | ||
|
56cf93dff2 | ||
|
67d84cadad | ||
|
b384a24c0e | ||
|
66a68edbe6 | ||
|
dcf3d7234e | ||
|
0da839cce3 | ||
|
b6f62ac446 | ||
|
15ee036db1 | ||
|
6db8ae451a | ||
|
a7f3d413ef | ||
|
3b7be478aa | ||
|
7f39cdc856 | ||
|
80006f4730 | ||
|
a51025fe8f | ||
|
c42ed9c693 | ||
|
c48c8d07de | ||
|
4d4b6edbc2 | ||
|
2014ca9feb | ||
|
a9f631f404 | ||
|
ba468bb5e4 | ||
|
cf86d57a9f | ||
|
9e958f4e32 | ||
|
c7f6f20c9d | ||
|
1984f44ffe | ||
|
02de281e40 | ||
|
ae035deb92 | ||
|
edd1b60c3d | ||
|
55a3709bd1 | ||
|
d1fc01a407 | ||
|
65293f81d9 | ||
|
0afb0f7958 | ||
|
dd958872a8 | ||
|
62a2ce1d35 | ||
|
b556908cab | ||
|
e69a19db5c | ||
|
d86414febb | ||
|
832318fab1 | ||
|
80ad62ff56 | ||
|
ee50f254df | ||
|
cc0be6cd90 | ||
|
a7455d7edd | ||
|
b7c370fff7 | ||
|
8fd3a64e35 | ||
|
3761fb4377 | ||
|
0472f5da6a | ||
|
09041fb81d | ||
|
299a157409 | ||
|
53ede7b0d8 | ||
|
cd13aee3e7 | ||
|
8b3acb719e | ||
|
2961a90e7f | ||
|
db8a2d0c65 | ||
|
a6b5f0c9d4 | ||
|
8d136c6a25 | ||
|
4d94270cde | ||
|
e0d96bcb39 | ||
|
0d7b831661 | ||
|
0c9e4f67a8 | ||
|
da2c386b60 | ||
|
4770364d42 | ||
|
db3f131dfc | ||
|
d7f58c64f8 | ||
|
41b6aebe7c | ||
|
7d50332246 | ||
|
0d0478245f | ||
|
f680ede980 | ||
|
b7caf7a016 | ||
|
891198e4f3 | ||
|
38c5910be4 | ||
|
327e2fb0a4 | ||
|
c20c219990 | ||
|
4c30250782 | ||
|
caf23f9a04 | ||
|
beab808b76 | ||
|
6c8920f63e | ||
|
d108072bfb | ||
|
8cc7c5349a | ||
|
fb33ea2a0b | ||
|
63fca33b04 | ||
|
0548ad2fc6 | ||
|
623d615cd7 | ||
|
126df9647b | ||
|
2e2e7cd054 | ||
|
0ebc9f7a44 | ||
|
a8fba65cbd | ||
|
0b0476e196 | ||
|
f99d6dac08 | ||
|
cd2b7cd943 | ||
|
af759f2330 | ||
|
51be15f66d | ||
|
110e25e784 | ||
|
8414100d0b | ||
|
a3f4cb154e | ||
|
e6e22a1ca1 | ||
|
bb5f3cc326 | ||
|
7623025b90 | ||
|
56c98e9295 | ||
|
b937665b90 | ||
|
a7bc2293c0 | ||
|
0d25f7612b | ||
|
84e4181ed7 | ||
|
f66a29d1c3 | ||
|
dbd3881cea | ||
|
df753e2619 | ||
|
7eb6bbe65f | ||
|
a570fda1cb | ||
|
3b06fa6523 | ||
|
dcdbe2fbb8 | ||
|
dc1f36da43 | ||
|
cbb7082afd | ||
|
6ee72e119c | ||
|
05dbd395e6 | ||
|
69e7360cc3 | ||
|
7ef2533b98 | ||
|
97f87c4229 | ||
|
42a5cd961d | ||
|
bf00d3157f | ||
|
cf3ff4c136 | ||
|
7fe06adcfd | ||
|
ae3e5dbf2c | ||
|
20304590b4 | ||
|
2d8c0c0131 | ||
|
70f4cad2ca | ||
|
1f777a94a7 | ||
|
7560c64f46 | ||
|
bddde60522 | ||
|
e9366f8c76 | ||
|
4dba84d09e | ||
|
0cce2d6098 | ||
|
acbd8bce21 | ||
|
dea8a08b64 | ||
|
dde1bab1a8 | ||
|
799e402077 | ||
|
ce629e8e70 | ||
|
20cfc4ac66 | ||
|
132d5e8253 | ||
|
cb8b341fb4 | ||
|
982c54b605 | ||
|
67f543332a | ||
|
88ac4086c2 | ||
|
15f96b7239 | ||
|
0ea84ad799 | ||
|
1522b713da | ||
|
fe6b27bb59 | ||
|
df14b15397 | ||
|
7dd12044de | ||
|
06e12a30e7 | ||
|
d3fefd223d | ||
|
c0639c6608 | ||
|
05a2eb3df4 | ||
|
d4befeb536 | ||
|
f7f1168aad | ||
|
d6cf15368a | ||
|
a5fbf3fb80 | ||
|
3cf8f78745 | ||
|
59dab6eac7 | ||
|
553d861b8a | ||
|
7b5d94d062 | ||
|
cb021efaee | ||
|
c2344f3717 | ||
|
f537c606f7 | ||
|
c8f48a4a90 | ||
|
c2ad1b4e46 | ||
|
ba9d146d6c | ||
|
a5b04a0328 | ||
|
01249d0cb9 | ||
|
71c273fbcb | ||
|
aa9cbf7c55 | ||
|
429b18ed48 | ||
|
2690c05781 | ||
|
e3b688c9d8 | ||
|
41b6f18a5d | ||
|
5a59c39036 | ||
|
a02dd18ad7 | ||
|
16bdc7d0a3 | ||
|
40002e8040 | ||
|
6748c55c04 | ||
|
deb63b4adf | ||
|
015a9b9271 | ||
|
ab2305e259 | ||
|
b99c998057 | ||
|
6c0a7144f6 | ||
|
bf50fce5bd | ||
|
8718b156c4 | ||
|
7bbdd1f839 | ||
|
ffed1a4afa | ||
|
1bfd3642e8 | ||
|
577f4e0cc3 | ||
|
e2f05f3fc9 | ||
|
27f30631ed | ||
|
1ccfa96c2e | ||
|
a2c64e79ff | ||
|
7b623f85cd | ||
|
ed63eb6833 | ||
|
b8d0d3c242 | ||
|
fe1bfe9ae1 | ||
|
7154c9ee5d | ||
|
057c95bd1c | ||
|
0cafc00c4f | ||
|
75d2898efd | ||
|
764a4c99fa | ||
|
160513c671 | ||
|
a2af26635f | ||
|
63869deeb2 | ||
|
91e387e8b9 | ||
|
7d13146859 | ||
|
699d2b7e7e | ||
|
257de15c73 | ||
|
5a0225d033 | ||
|
bd2d0e6ad3 | ||
|
0c0d1d4e52 | ||
|
eab9603921 | ||
|
c07db3aa14 | ||
|
a19f7481b2 | ||
|
2905edce35 | ||
|
04a5d794ac | ||
|
264b9819ff | ||
|
4610204c83 | ||
|
ecf1f17cf4 | ||
|
9f942a6b65 | ||
|
f4ed1b32b8 | ||
|
ec0e871592 | ||
|
7a756ebc4d | ||
|
16dc21afff | ||
|
3a1c6d84f0 | ||
|
e684abdacd | ||
|
e275cb1efd | ||
|
60315e5b91 | ||
|
43b1a4bf5a | ||
|
cdb238e41c | ||
|
3871e44d9c | ||
|
5a51454d13 | ||
|
d102943a32 | ||
|
a0b27ddbd8 | ||
|
b950b04e89 | ||
|
c9a55f395b | ||
|
424da01878 | ||
|
3c933158c8 | ||
|
7221d488e5 | ||
|
1c16931e26 | ||
|
0a4b70dbd2 | ||
|
666c716bda | ||
|
be477d7ae3 | ||
|
6c8a623b88 | ||
|
ff8de34415 | ||
|
f8ca6d9833 | ||
|
dbab519004 | ||
|
e888c96591 | ||
|
59519f0493 | ||
|
e02f07d356 | ||
|
2c90d220b8 | ||
|
9feeba8d4b | ||
|
45090fc897 | ||
|
d761bdc1b1 | ||
|
287a8c76b5 | ||
|
a7dd86de71 | ||
|
c541a2e5de | ||
|
41dbf1ddac | ||
|
70ed6b96d1 | ||
|
ef26075a1c | ||
|
91c87446be | ||
|
dd207e1f02 | ||
|
e947870da9 | ||
|
f0b5f592dc | ||
|
773a2a6cfe | ||
|
c2a7e384ba | ||
|
4a8b35ef5f | ||
|
5cd0db32df | ||
|
ef01de6149 | ||
|
4e9749f655 | ||
|
abb7a1fd47 | ||
|
60bcee8c1d | ||
|
8ded524236 | ||
|
0a47f48191 | ||
|
8601267b90 | ||
|
6767e0c52d | ||
|
5141d1775d | ||
|
374af996d9 | ||
|
dcc9624c15 | ||
|
b3df1a2bf8 | ||
|
3fc3c02a4f | ||
|
874ddf9a32 | ||
|
69aeb70cc3 | ||
|
47e60cefe3 | ||
|
4ea7f3cda5 | ||
|
03288b521a | ||
|
4b0a7a6e1f | ||
|
383287adcd | ||
|
5c00afc6fe | ||
|
025e0e8093 | ||
|
35fb1f8585 | ||
|
9fb14eef0e | ||
|
5d6d0c6176 | ||
|
dd707242ef | ||
|
4f32f1285a | ||
|
a6ecdbae29 | ||
|
4904d100ff | ||
|
7a1f94bc20 | ||
|
f9320fff8f | ||
|
3dde83d8a0 | ||
|
f3e77a989c | ||
|
f3539b0354 | ||
|
4e5d4b9695 | ||
|
864315f6d1 | ||
|
713b7338ea | ||
|
53117b2f4c | ||
|
90623142e1 | ||
|
43503a20e5 | ||
|
ec53b27dfe | ||
|
238ecfc539 | ||
|
b888792940 | ||
|
e3d1ab52f8 | ||
|
f9c2874c35 | ||
|
2304f005e3 | ||
|
b95f836256 | ||
|
c94f9f21af | ||
|
fc336e3733 | ||
|
fc1df9f9a5 | ||
|
5a604bfdee | ||
|
281951a86b | ||
|
35ec3adadc | ||
|
b9256a1ba7 | ||
|
734c9a1aa5 | ||
|
6502a71083 | ||
|
f2634b44cd | ||
|
dcf9c467c3 | ||
|
3dcacc1f8d | ||
|
7cb81b0f35 | ||
|
8155ba5224 | ||
|
7169060425 | ||
|
ddb9dd4e45 | ||
|
2a05f24cb6 | ||
|
039e4c662d | ||
|
70351677a1 | ||
|
33cadfb97d | ||
|
5fbaeda217 | ||
|
b44ba0d21a | ||
|
b42532afe9 | ||
|
da6a335b87 | ||
|
0e8fef73bb | ||
|
7122a960fa | ||
|
d5b649a1a4 | ||
|
233c724b2d | ||
|
e1eb001872 | ||
|
5899d7034f | ||
|
23c3e9482f | ||
|
dd980d9dca | ||
|
d4ed50a915 | ||
|
d986c7d126 | ||
|
67a2a4f249 | ||
|
a6e87e7e08 | ||
|
33da8a7f62 | ||
|
68c533f777 | ||
|
671eecf203 | ||
|
2454ac8ef1 | ||
|
20f097faa4 | ||
|
5dba8b493d | ||
|
eb27013fba | ||
|
4f8d1c5c9d | ||
|
74168c3e05 | ||
|
cece848801 | ||
|
7ddbeaa078 | ||
|
2d4aa7ff8b | ||
|
79e044ac31 | ||
|
15ae5a5135 | ||
|
5684b7c329 | ||
|
7e96120353 | ||
|
cc36421fe5 | ||
|
683aa727d5 | ||
|
ea07b495ac | ||
|
8d211c3524 | ||
|
ab8df82563 | ||
|
8ba9c4ab97 | ||
|
2f70b8682e | ||
|
86dd4ea480 | ||
|
204e5f4418 | ||
|
044a9bb6d3 | ||
|
a31143328e | ||
|
2bb29a105c | ||
|
0013d98d04 | ||
|
916743f44b | ||
|
edebe65d95 | ||
|
c43fcd0af6 | ||
|
9a5c2b88dc | ||
|
aaee0414c8 | ||
|
d0c2fb9761 | ||
|
4f076c6924 | ||
|
51d4d1451a | ||
|
7b9d76dc65 | ||
|
f91aeea91c | ||
|
4cabf84be9 | ||
|
8a1f038a80 | ||
|
c9cab9ab74 | ||
|
bd78120bd5 | ||
|
0de3bf0ac7 | ||
|
2d144a8b43 | ||
|
1a140a5515 | ||
|
1ea8cfbfb0 | ||
|
64fda95186 | ||
|
7843c0c1b0 | ||
|
a3784854a7 | ||
|
8b7c000f47 | ||
|
5b8d7a3f29 | ||
|
328dbd57d4 | ||
|
a8c448f4cb | ||
|
de0419228f | ||
|
d8a4e47a13 | ||
|
bc920949cb | ||
|
0717f8591c | ||
|
affb65219e | ||
|
3e628f2678 | ||
|
ddabc38e3f | ||
|
c0097497be | ||
|
78198bfdaf | ||
|
2fb9c923f4 | ||
|
c485011ed1 | ||
|
0b8ae68213 | ||
|
0e73128f40 | ||
|
927c003d22 | ||
|
bd73823828 | ||
|
11582bc7d3 | ||
|
20ed4f96ba | ||
|
19790e9011 | ||
|
3a29e03458 | ||
|
e2eb685d76 | ||
|
86cb28fe34 | ||
|
bcc9679339 | ||
|
e5aeff50dc | ||
|
5f44c195e9 | ||
|
e4387e4aad | ||
|
b1b336804d | ||
|
9985c43817 | ||
|
190cac394c | ||
|
ae71a5abf6 | ||
|
df199c5788 | ||
|
c1ba4f1b55 | ||
|
afc0097b12 | ||
|
4e9f971c91 | ||
|
b1cc28bbda | ||
|
80d30bdd30 | ||
|
f27566669b | ||
|
29f12ddaf4 | ||
|
ed15ff0515 | ||
|
a8f71f79fe | ||
|
68c2478e0e | ||
|
be07fe9e9f | ||
|
4d8b661d51 | ||
|
ca0981645f | ||
|
bc7e02b47a | ||
|
55a55e9f74 | ||
|
b41d40da40 | ||
|
ba3e088b23 | ||
|
1b59b0b739 | ||
|
093cfcdf42 | ||
|
41801a60ad | ||
|
5ff0957861 | ||
|
6913b8beb5 | ||
|
c8d17bc363 | ||
|
2cbf1259a8 | ||
|
59dc513ac3 | ||
|
87b110bb86 | ||
|
1b3e1a7abe | ||
|
0afabc60ae | ||
|
2d4ea720eb | ||
|
9c757bbe6e | ||
|
40e0d72105 | ||
|
eba788e8c9 | ||
|
444b111a62 | ||
|
a364ab4ea7 | ||
|
be7840c827 | ||
|
a46e51e8db | ||
|
789ebb8990 | ||
|
e3cd96bf19 | ||
|
7746042adc | ||
|
dfe80556d3 | ||
|
b3529dc748 | ||
|
543c4423a2 | ||
|
3bd4d32b8d | ||
|
56a686d3e0 | ||
|
2f97c789dd | ||
|
04ca808e80 | ||
|
9b79743c5d | ||
|
a386826808 | ||
|
668894fc4d | ||
|
dc882e6279 | ||
|
d6eebf82be | ||
|
799f509ba9 | ||
|
688a234127 | ||
|
095697900b | ||
|
6ccf617d62 | ||
|
b3b4811b2c | ||
|
966e4246e5 | ||
|
9377c4f3ad | ||
|
b876128635 | ||
|
c0da801580 | ||
|
149310e1ec | ||
|
4e625c18dc | ||
|
a5d5113be3 | ||
|
7900c493af | ||
|
76fe5d8831 | ||
|
37031721dd | ||
|
3cc283cbee | ||
|
35cf98fff2 | ||
|
ca41ea2d5c | ||
|
daffc4e6a4 | ||
|
5f3cb9019b | ||
|
13c7182948 | ||
|
4951b58b21 | ||
|
7be7586971 | ||
|
edc76795d4 | ||
|
6d64098288 | ||
|
4351110082 | ||
|
6652138d3e | ||
|
c8d0d475e4 | ||
|
39b25029fc | ||
|
b5cabd6d8e | ||
|
b169a5c707 | ||
|
4f816c06b0 | ||
|
b5f49d9563 | ||
|
274fd5ab8b | ||
|
e48d7de763 | ||
|
f426940bd2 | ||
|
6c11dd12d7 | ||
|
c2b14d3075 | ||
|
8a144ebfee | ||
|
5440fcdf54 | ||
|
66b2d496af | ||
|
444a0282d7 | ||
|
f897ab4eb8 | ||
|
993c187e37 | ||
|
3ce67b282f | ||
|
606e59a5d0 | ||
|
0f26b1eafb | ||
|
d2a9d731ed | ||
|
80e6b1fc01 | ||
|
58a89edad7 | ||
|
bfe47eb152 | ||
|
32adc38e94 | ||
|
1584971908 | ||
|
025bebb3e2 | ||
|
4047adcc35 | ||
|
d4cf03c9fd | ||
|
5ba2068fc2 | ||
|
3ce7d410c8 | ||
|
74f28021e7 | ||
|
f90cbb636a | ||
|
c8929ca0cb | ||
|
9fb5bb620d | ||
|
ed58f32052 | ||
|
6d5743c506 | ||
|
a551619de6 | ||
|
873b113cb3 | ||
|
2280e66d73 | ||
|
2133897bbe | ||
|
bee5cb55a1 | ||
|
0a0838b616 | ||
|
b376dfa1e6 | ||
|
7ac0577b34 | ||
|
f16e060e87 | ||
|
9aaae24583 | ||
|
915ced7b92 | ||
|
5a36b9075f | ||
|
b5a7f46ecc | ||
|
4381657c5e | ||
|
d440b2f2b2 | ||
|
5b42aea9e7 | ||
|
e1d7a6b9ac | ||
|
38778f8adc | ||
|
8b7a86bd17 | ||
|
619bae745b | ||
|
2cf72bad30 | ||
|
e6959f093c | ||
|
8d783e8e1f | ||
|
f03098551e | ||
|
6887805402 | ||
|
7f9074adbf | ||
|
64847afc3f | ||
|
c4df8090e2 | ||
|
fe3523f47a | ||
|
199d846acb | ||
|
b07e479840 | ||
|
9bf37fde02 | ||
|
1054325b2d | ||
|
ef8cb11707 | ||
|
c23c40df8a | ||
|
77d606df34 | ||
|
e717c9dba2 | ||
|
3d312e2140 | ||
|
18de21f723 | ||
|
6aebaf6f47 | ||
|
641f6977a9 | ||
|
a092a2fa43 | ||
|
84c2b0c3d7 | ||
|
24ce7c1991 | ||
|
8be3465f94 | ||
|
227eac10f1 | ||
|
1cbd5485e7 | ||
|
610c3cf681 | ||
|
96d45cc341 | ||
|
8ba7d02fdb | ||
|
ef8a199a5a | ||
|
db83643c1e | ||
|
9b6f775276 | ||
|
69040dd668 | ||
|
14b5914233 | ||
|
a0eabd2298 | ||
|
9b27298d54 | ||
|
3075b4515a | ||
|
39bc417706 | ||
|
e11e32cd52 | ||
|
019a7bd66b | ||
|
4d89ce5d50 | ||
|
c31027b284 | ||
|
f17ec7a4f5 | ||
|
deec6aab1a | ||
|
3e8d9a1987 | ||
|
5e465a298f | ||
|
515c9e7811 | ||
|
03c8309703 | ||
|
2c28d6b10c | ||
|
df79443ed8 | ||
|
2b891f7f1d | ||
|
e4ed0b1884 | ||
|
c8c1c09189 | ||
|
08ae8cc3cb | ||
|
201673ca8a | ||
|
29e23ac9ce | ||
|
00483e8cdd | ||
|
83da01a2e1 | ||
|
7cd6ff054b | ||
|
6be2bb2289 | ||
|
439defca42 | ||
|
8a85bb2989 | ||
|
5e7519183d | ||
|
40e7eca1ee | ||
|
481f02de88 | ||
|
6a90856f0e | ||
|
dcbbee8adb | ||
|
225adcc836 | ||
|
0f607413d0 | ||
|
922553032b | ||
|
b49999721c | ||
|
de4c4eedd8 | ||
|
bcc984fc09 | ||
|
d53262fab6 | ||
|
532e44bcea | ||
|
3fb67629c1 | ||
|
6145465823 | ||
|
fb5091a388 | ||
|
d6c68f1a84 | ||
|
0d05f9ba80 | ||
|
a300df0020 | ||
|
a50158cbeb | ||
|
7278fd25e5 | ||
|
6fb8c0ec4c | ||
|
07cedc55e2 | ||
|
ae3cc81f03 | ||
|
97a45e3b02 | ||
|
451b290b79 | ||
|
499ea07934 | ||
|
afa06267a2 | ||
|
d4a6d9c076 | ||
|
c3f6112443 | ||
|
3411b736dd | ||
|
c603b9c40b | ||
|
1e2c5d038f | ||
|
9d6d96adf3 | ||
|
8d574ecb34 | ||
|
9ebb2ac2e4 | ||
|
7b935eec5d | ||
|
b209f66654 | ||
|
b98b4951b4 | ||
|
227d62a5dc | ||
|
7fae5553a8 | ||
|
6ead019873 | ||
|
6d2ab4d270 | ||
|
a6b58bc88d | ||
|
ea3c37d754 | ||
|
e58b00d9a2 | ||
|
b75e90f8c9 | ||
|
e82f3439c3 | ||
|
0bc8e3bee5 | ||
|
13f80acb2d | ||
|
8fe813acff | ||
|
0ed4fc6a12 | ||
|
b37bf06de8 | ||
|
27bbf0ccaf | ||
|
ed56d52af3 | ||
|
fb457968ec | ||
|
0bea2e2b94 | ||
|
72d02f442e | ||
|
bcf63b5d27 | ||
|
e88442cb46 | ||
|
930609e875 | ||
|
8752d08ce9 | ||
|
c46ceb06b4 | ||
|
92e4ecce3b | ||
|
8a2f673903 | ||
|
ac0dd90c37 | ||
|
9470850258 | ||
|
2982e9943e | ||
|
80e13bc24a | ||
|
9a677534a7 | ||
|
af08d67fad | ||
|
a2901d61ea | ||
|
aaae83efec | ||
|
7369298638 | ||
|
a761bd20fa | ||
|
01ace11293 | ||
|
205e95a246 | ||
|
2c2a43e1ec | ||
|
0c9a6da623 | ||
|
888d91d14a | ||
|
2c0cc87b4c | ||
|
ee0fadf247 | ||
|
9fb89d7fd2 | ||
|
af5c36e4ad | ||
|
a5f943e227 | ||
|
f8532ba812 | ||
|
fac4e151cc | ||
|
f3a0a25380 | ||
|
5a237795ea | ||
|
3a99a77104 | ||
|
5ce8050e46 | ||
|
5ae3a020bd | ||
|
af193291fa | ||
|
b39df5cef0 | ||
|
dc61c9e277 | ||
|
10a15e1188 | ||
|
a2bb6a4f1f | ||
|
38a19fa574 | ||
|
9ec4b59afb | ||
|
20877146df | ||
|
2a2d556551 | ||
|
0f762d98a4 | ||
|
36752cb6a8 | ||
|
a7f2d89e3f | ||
|
90b65c6618 | ||
|
190ec0c14c | ||
|
7250a300df | ||
|
34bb00450d | ||
|
63dadd8983 | ||
|
67c990e8cf | ||
|
0292e20c86 | ||
|
9088c8741a | ||
|
faedea2120 | ||
|
e1a0f5706d | ||
|
a78a4e6716 | ||
|
6bbf927f57 | ||
|
4c32bc8e22 | ||
|
df671a77f6 | ||
|
dca9def42c | ||
|
edee7ea284 | ||
|
658d09ed84 | ||
|
c41de8f270 | ||
|
9d2ee2127d | ||
|
e49ece8793 | ||
|
188274277a | ||
|
c0cb3945f1 | ||
|
e07795e8f0 | ||
|
bda454fe9c | ||
|
856e77053e | ||
|
62dad721fc | ||
|
2ce145f359 | ||
|
7e7291ace9 | ||
|
61c853a3c1 | ||
|
4346139d65 | ||
|
1476f83ba7 | ||
|
c959d64099 | ||
|
e67d26caeb | ||
|
86c3fa0df0 | ||
|
75ae57e194 | ||
|
d42feae0af | ||
|
45971b8083 | ||
|
8e9bbd1bb3 | ||
|
ec10a3eab4 | ||
|
49deb4af24 | ||
|
10f171b6e4 | ||
|
735db1a12b | ||
|
b2f4cc2dc5 | ||
|
d43b587d17 | ||
|
6b07a955f2 | ||
|
e23c02575d | ||
|
b1bf634136 | ||
|
459faf4dfb | ||
|
8cdceb83b2 | ||
|
6aa1ec0802 | ||
|
0727f7054b | ||
|
37cc611e3f | ||
|
c39e6c4423 | ||
|
1566656af3 | ||
|
737eba57bd | ||
|
d32cedd7dc | ||
|
2b6aa26703 | ||
|
95f1336060 | ||
|
f46ee93597 | ||
|
56d799f449 | ||
|
6251652c93 | ||
|
6a2c9a0dc1 | ||
|
4dd709b543 | ||
|
aa9f5b8c4a | ||
|
f485f3fdb5 | ||
|
c6a0ec64cb | ||
|
e0b179e5f3 | ||
|
dac7a3d272 | ||
|
beed123fb0 | ||
|
267e582827 | ||
|
69c02cae76 | ||
|
4f386663e7 | ||
|
eaae0547f2 | ||
|
cc5cfc7525 | ||
|
795dee85ef | ||
|
dbd5bef038 | ||
|
dd2a420578 | ||
|
eeee30ca03 | ||
|
82e8792737 | ||
|
6b18b3df34 | ||
|
fcebed19b9 | ||
|
af5f6176b5 | ||
|
424cc46db0 | ||
|
ce47ccecc4 | ||
|
d940f17390 | ||
|
6351b5d0dc | ||
|
9c4ac24a66 | ||
|
bf66df2a29 | ||
|
962ce380cd | ||
|
9769afb944 | ||
|
052b45a510 | ||
|
84a96e862e | ||
|
3105235a7a | ||
|
71a32477e4 | ||
|
342bce2168 | ||
|
86d8cbc4d2 | ||
|
6b63bd6a44 | ||
|
225707c877 | ||
|
66da6f18e3 | ||
|
bdf8bf391c | ||
|
f66d9e1a22 | ||
|
ccd3d96942 | ||
|
b203f2abaa | ||
|
5490a2f3ba | ||
|
ba44235471 | ||
|
be556f9e36 | ||
|
5e5ba11601 | ||
|
e384df30fa | ||
|
4635dacf7f | ||
|
18e4d270d9 | ||
|
3bcb91f6ae | ||
|
5e574a355d | ||
|
640c7c5fa3 | ||
|
eb6395a62c | ||
|
2b2845aa07 | ||
|
54d8c66f3e | ||
|
95bbf1b190 | ||
|
ee2dab51f3 | ||
|
f63409eed9 | ||
|
ad8940ad73 | ||
|
b8bfb5a56c | ||
|
3f58823430 | ||
|
e488220bfc | ||
|
e6e0771496 | ||
|
fe77d43fa0 | ||
|
737a7a2db2 | ||
|
6ae8d10132 | ||
|
7d249b6d3b | ||
|
a532b82771 | ||
|
e8756482aa | ||
|
35d6da785b | ||
|
dbdcbd4b9e | ||
|
a2b6f49c5c | ||
|
a31ed4a723 | ||
|
b2fc84c98e | ||
|
927369b06d | ||
|
401fd37e35 | ||
|
759cdf10c5 | ||
|
6e7ce1eec1 | ||
|
2a65955e88 | ||
|
00b6c6a437 | ||
|
21ef3b0ecf | ||
|
5b0d6a1375 | ||
|
0510da0853 | ||
|
365d22d076 | ||
|
1760169ef9 | ||
|
32ea224933 | ||
|
38a067e203 | ||
|
ab6f1b6df7 | ||
|
b36802edff | ||
|
1d2af0f291 | ||
|
40e8c5e2b0 | ||
|
e11d0d37ee | ||
|
ea4266538a | ||
|
aa7bf9169f | ||
|
a95e83ab6e | ||
|
86daaf4bf2 | ||
|
d5b4f02932 | ||
|
15deec6c53 | ||
|
17b1875151 | ||
|
bfda8f0b8a | ||
|
7bb0ff986b | ||
|
f3cfef4021 | ||
|
3184c3c21b | ||
|
9264737985 | ||
|
c9b353a689 | ||
|
fda6502f33 | ||
|
0fdac82b93 | ||
|
6b3d6d5211 | ||
|
cb89ee39f5 | ||
|
be827be742 | ||
|
8fcecd59a0 | ||
|
83b49b23e4 | ||
|
f5ee618986 | ||
|
8339b88180 | ||
|
f2958818c8 | ||
|
608547c62c | ||
|
20f604948f | ||
|
f72a4f966d | ||
|
bd00db4292 | ||
|
68debc474a | ||
|
6a7f993a9a | ||
|
ae25931b37 | ||
|
84fe6654cc | ||
|
d2d023cca7 | ||
|
64908e0080 | ||
|
d4e1899747 | ||
|
7f9b8d68ac | ||
|
5b1e849bde | ||
|
fba6de76b1 | ||
|
8419b42e83 | ||
|
5f38c15b1f | ||
|
c3b72baa8e | ||
|
16d0416f22 | ||
|
b9aa4f4478 | ||
|
8d3ad3a8c1 | ||
|
5a689ce897 | ||
|
35e22703af | ||
|
41a2d0e06c | ||
|
4d95e35c06 | ||
|
4e553f34ba | ||
|
b910726c43 | ||
|
64e3cab6ab | ||
|
f3196396a2 | ||
|
148336929d | ||
|
2f3ec3a77f | ||
|
8d7a487013 | ||
|
622464ff5e | ||
|
b45a44e405 | ||
|
a196958bd6 | ||
|
f101418658 | ||
|
aa05a1e81d | ||
|
384bc62f25 | ||
|
46e62f1a9a | ||
|
c5c2014081 | ||
|
c668c603cc | ||
|
8f3b7c179e | ||
|
ea18c47011 | ||
|
ced7110a78 | ||
|
92f13eb8bf | ||
|
1312ef7e50 | ||
|
e992979113 | ||
|
d317b49940 | ||
|
6be53468c5 | ||
|
046094bdcb | ||
|
1064c270d9 | ||
|
2447fccf1e | ||
|
c43c711f72 | ||
|
f354e6de69 | ||
|
08d60fcbf2 | ||
|
4cda54774a | ||
|
613475ac26 | ||
|
12615c46f8 | ||
|
20d23fcb92 | ||
|
b335840f97 | ||
|
6d84f59e6b | ||
|
c5efec678e | ||
|
83cb89e4f7 | ||
|
6bdf689d0f | ||
|
e164362069 | ||
|
72e3f33f28 | ||
|
ccfd907914 | ||
|
5a44e63cad | ||
|
d96cca3822 | ||
|
06580bf0e4 | ||
|
b21bd64764 | ||
|
6b97dc6734 | ||
|
4e3f328a02 | ||
|
8380ca2fdd | ||
|
ec678bc6d2 | ||
|
2b5e2d4760 | ||
|
89abad7980 | ||
|
d84da5bdbf | ||
|
5cc1d9521c | ||
|
a199fc6113 | ||
|
655e34b166 | ||
|
5ea3a02d6a | ||
|
15c68c9594 | ||
|
08438608d1 | ||
|
2da94e0fdf | ||
|
63165764dc | ||
|
103810ce20 | ||
|
f59356b96b | ||
|
4a8511f680 | ||
|
cd6698c688 | ||
|
c7ca9d7e36 | ||
|
d70b759cb9 | ||
|
ae3dda0f8f | ||
|
e229ba5945 | ||
|
dcc50093bb | ||
|
0831690f79 | ||
|
98ef51514f | ||
|
ac9993394c | ||
|
849c3fd9c9 | ||
|
da58fcbfce | ||
|
fa3cd9736f | ||
|
a0c5d17539 | ||
|
224cd04673 | ||
|
52243d0870 | ||
|
d519873fa4 | ||
|
a76dcd4ba1 | ||
|
2d07185300 | ||
|
518e1df257 | ||
|
e0def66959 | ||
|
772d970074 | ||
|
ba7d85145a | ||
|
fb73dceab0 | ||
|
13fd83e0ba | ||
|
719ba75fcc | ||
|
13ab98440c | ||
|
1752004301 | ||
|
536a5f7cff | ||
|
a69aece23a | ||
|
77d3815baa | ||
|
e225e17386 | ||
|
3290208749 | ||
|
3106187aac | ||
|
eae490b5b1 | ||
|
bcce77508a | ||
|
e9bdf02cfc | ||
|
77f659c9b9 | ||
|
c66e157a14 | ||
|
2c927277e2 | ||
|
29fe1c86da | ||
|
1ae9c48370 | ||
|
078a8b40e9 | ||
|
9daeae1695 | ||
|
014e016058 | ||
|
f41f93af3a | ||
|
51539521b1 | ||
|
522dec34a5 | ||
|
ea6d76cce6 | ||
|
7c7d61f61e | ||
|
184dde92a2 | ||
|
7e43c794fd | ||
|
c7285967d6 | ||
|
41f4baadb9 | ||
|
20082ec9fb | ||
|
8dae8c52c0 | ||
|
068076c0d5 | ||
|
c0ae44a41b | ||
|
593e8e1f63 | ||
|
707cf35f0a | ||
|
30f11d0e16 | ||
|
53d6ab6c23 | ||
|
280e44304a | ||
|
79fac4466e | ||
|
0e9f09e582 | ||
|
25d0fdf8ff | ||
|
1d089d4541 | ||
|
1c78663378 | ||
|
7666022840 | ||
|
946c8b498a | ||
|
406ca66c8d | ||
|
ed971df93a | ||
|
74c054b2a5 | ||
|
017a10189c | ||
|
f1c361855e | ||
|
3d72df4666 | ||
|
6f732a9957 | ||
|
447bf77dfe | ||
|
6621ef6a0b | ||
|
e327c8758e | ||
|
8a08de5691 | ||
|
3d7375be8b | ||
|
da754e9a71 | ||
|
cc9ec806b2 | ||
|
72cf037c0d | ||
|
8ae08b29e4 | ||
|
4967fa020f | ||
|
54f2640ef2 | ||
|
56246592c7 | ||
|
1ff5d71e12 | ||
|
67c42c5911 | ||
|
d0b514890a | ||
|
c0285fbc15 | ||
|
a438c841e1 | ||
|
19d7c2b336 | ||
|
afb6c70909 | ||
|
d9af496b13 | ||
|
a55cf40b1b | ||
|
b19008d1b8 | ||
|
6f88c81616 | ||
|
43cb230f19 | ||
|
fd6a59202d | ||
|
e353f66eaa | ||
|
9293bcfb1c | ||
|
d154118600 | ||
|
c2273d2c8e | ||
|
495ba01d8e | ||
|
f627a02886 | ||
|
5f9daa6640 | ||
|
9edda556de | ||
|
3891a52aeb | ||
|
1e5e3353f3 | ||
|
7aa4b8247c | ||
|
37339ddafc | ||
|
dcb51683c5 | ||
|
74a4a788b1 | ||
|
7909273a21 | ||
|
130e8dbd40 | ||
|
40e2ebed95 | ||
|
bf9b33acec | ||
|
7710a33b6c | ||
|
af3ea2d4fd | ||
|
52a3255936 | ||
|
5ab9ca1c0d | ||
|
461f602992 | ||
|
46180435cc | ||
|
7f2699c6da | ||
|
aede5c486b | ||
|
fb2407386f | ||
|
aa59c46c4c | ||
|
07afc4953a | ||
|
c5557fc488 | ||
|
8c14150536 | ||
|
c127903127 | ||
|
0881cf1379 | ||
|
d0a16b0ec0 | ||
|
290beb90a7 | ||
|
0a4ef17135 | ||
|
e0c32ce700 | ||
|
e65144a105 | ||
|
ae40445dba | ||
|
25a8240d12 | ||
|
1a4a180e8c | ||
|
aea10a3b93 | ||
|
f855862ade | ||
|
91d37c7875 | ||
|
5707b93110 | ||
|
81b2d0732f | ||
|
ec0538d251 | ||
|
eb0c629fad | ||
|
509d3f6d30 | ||
|
c576af7c6f | ||
|
f3682f0e8e | ||
|
1c58913eeb | ||
|
cfbc294832 | ||
|
64ad8b1dac | ||
|
fb079f9e50 | ||
|
39b09f8f87 | ||
|
53d26e5c5c | ||
|
e21f3e6c73 | ||
|
edd46eb3d1 | ||
|
e71238571a | ||
|
2867ec509e | ||
|
d853a9ebbe | ||
|
e0d2fa98f3 | ||
|
cc90f83463 | ||
|
9ea1238e1b | ||
|
1530abbd1a | ||
|
3bfcd18a03 | ||
|
6b7db22981 | ||
|
8adb8a6986 | ||
|
37e3e2f9c2 | ||
|
3c7be32ef5 | ||
|
051775b9b4 | ||
|
e0d5b91388 | ||
|
4dd2027428 | ||
|
42ab98b830 | ||
|
2b2bce6457 | ||
|
69ee816541 | ||
|
2cbf3f7e15 | ||
|
923eece3f5 | ||
|
d4fb313ff0 | ||
|
7dce465c06 | ||
|
5a30f5c00e | ||
|
fd406af962 | ||
|
9e5ae30372 | ||
|
0fe3538331 | ||
|
b0f5ad75ae | ||
|
a290f63a15 | ||
|
d078ce794e | ||
|
06f51a5c34 | ||
|
db96778064 | ||
|
d8918ea156 | ||
|
3503474bb8 | ||
|
eb1606b086 | ||
|
1330a092fa | ||
|
d3c74cfb45 | ||
|
d044545520 | ||
|
d1cdc1c6a0 | ||
|
dc8d91ea39 | ||
|
ae5a6d330d | ||
|
fe0bee21b0 | ||
|
c090c19bfe | ||
|
8fbec785e8 | ||
|
06fb3d9476 | ||
|
fe3e8a7bb6 | ||
|
ce2ff25edd | ||
|
65a1b892e3 | ||
|
e272fde95e | ||
|
d48bff0e20 | ||
|
6e61c34f0f | ||
|
2d9506eb54 | ||
|
b1988c7b67 | ||
|
fb5d72c29b | ||
|
ac148ce0e9 | ||
|
016dca654e | ||
|
6502bdecbe | ||
|
91a739af6e | ||
|
e232565971 | ||
|
472488ebe8 | ||
|
8de3698b23 | ||
|
a694b46914 | ||
|
c384ed960c | ||
|
2386d2e299 | ||
|
cc7e1a72c1 | ||
|
5cc0fa7c98 | ||
|
4ce848ab51 | ||
|
2e34e11b02 | ||
|
8eda5f36fb | ||
|
3dbe5d872b | ||
|
96a95ba9fe | ||
|
f594ed659e | ||
|
5c4bfbbd95 | ||
|
8733635638 | ||
|
928aa74e89 | ||
|
8fdfe673e8 | ||
|
494a6e6090 | ||
|
89bb7e6b0e | ||
|
52cfb9a041 | ||
|
3817ddef41 | ||
|
0f494c9dd6 | ||
|
3ff97ecf45 | ||
|
a10c0b516b | ||
|
5eb1469dbf | ||
|
9a90fe3794 | ||
|
1917c4b04a | ||
|
2b01d4a203 | ||
|
c5100219d1 | ||
|
a730a08161 | ||
|
4bb8e3a121 | ||
|
eacc00f786 | ||
|
17f5e557ed | ||
|
c33e5bc40f | ||
|
06d1a98ad2 | ||
|
afe6f4030e | ||
|
6e49c4ffe0 | ||
|
28d9f00610 | ||
|
8f688e5e13 | ||
|
5a182eddbf | ||
|
4b615cb3a9 | ||
|
b36247a091 | ||
|
6e77756d6a | ||
|
585c0c3818 | ||
|
6ff75f9a9f | ||
|
906ef43c00 | ||
|
4528957235 | ||
|
9d448a42a7 | ||
|
979e10f9d5 | ||
|
31f65b89bb | ||
|
86639dbc02 | ||
|
bf8c33703c | ||
|
ffe7ef4764 | ||
|
30dae70e2b | ||
|
557a747d55 | ||
|
fe273b3829 | ||
|
2439bb30e8 | ||
|
e48b6bd22d | ||
|
1de9ffacb0 | ||
|
93fd6170a3 | ||
|
b7c3e6099c | ||
|
d8163e9835 | ||
|
12b1916599 | ||
|
9d8cdb5976 | ||
|
29a7c1938a | ||
|
8636d3139e | ||
|
987571ce91 | ||
|
6f4c5fcc87 | ||
|
a7ca010d4e | ||
|
a836842a7e | ||
|
f8c11a324a | ||
|
052c9be111 | ||
|
854e520528 | ||
|
05737b85eb | ||
|
c8a2308739 | ||
|
ed46a078f9 | ||
|
4f7c2bf8c3 | ||
|
0e341726d2 | ||
|
a4f9746d3a | ||
|
556c546b2e | ||
|
aa479948f9 | ||
|
fa3cee9d58 | ||
|
0021fb8a33 | ||
|
c8c727e6c6 | ||
|
b8494ab3cc | ||
|
2eda03f5de | ||
|
3a38358946 | ||
|
e7fc697e57 | ||
|
6c9845b9f3 | ||
|
9e146a8a5a | ||
|
433d9bfb02 | ||
|
94917e315e | ||
|
ced6852735 | ||
|
8dc55f417d | ||
|
3d338bba3c | ||
|
631398f700 | ||
|
7984d8cdfb | ||
|
783a6110ef | ||
|
5d4d53c3a1 | ||
|
3014955ece | ||
|
0481f20c6b | ||
|
76309601eb | ||
|
84dd864886 | ||
|
7924e01b15 | ||
|
dadc70630b | ||
|
effc37a702 | ||
|
deac3fc918 | ||
|
e6dea4c92c | ||
|
075e992fa0 | ||
|
565ca81b30 | ||
|
58c4eaaf86 | ||
|
77e8008752 | ||
|
2ba10fcbc7 | ||
|
4956a58026 | ||
|
92332fc385 | ||
|
9366f4b40e | ||
|
f49e8ec5ad | ||
|
cd33647087 | ||
|
71ebcac7f2 | ||
|
f06aee21eb | ||
|
5fbbc17376 | ||
|
a9d8830106 | ||
|
d21e6235ad | ||
|
289f79bbb0 | ||
|
768e9f4c09 | ||
|
62c776d90c | ||
|
464022bea2 | ||
|
61549b4a74 | ||
|
18df3dc07a | ||
|
3725724c54 | ||
|
500a005aac | ||
|
584cc6de2e | ||
|
2e5a6e21cf | ||
|
b79f63db78 | ||
|
7ed7a57d92 | ||
|
1eaf7c89b7 | ||
|
1fff8dd306 | ||
|
c3a3d02bea | ||
|
a9261970dd | ||
|
b7a3fe05a4 | ||
|
ab6b9006b7 | ||
|
d9a8b057c3 | ||
|
2ec6215b1c | ||
|
c59a8c9644 | ||
|
10de4b6b7b | ||
|
0be214e79e | ||
|
d6083c68fd | ||
|
22f7ac22d5 | ||
|
a00046f9b2 | ||
|
8a24275ba9 | ||
|
ca841252bd | ||
|
54195b16ad | ||
|
cb90167c76 | ||
|
ac4ae85a4a | ||
|
48b2a271cc | ||
|
596807055e | ||
|
cee20c4eb9 | ||
|
15fb47cb3d | ||
|
2635dfef96 | ||
|
7d7789ae96 | ||
|
cc69285420 | ||
|
99d3a283ef | ||
|
9b532584d6 | ||
|
7576eb38d9 | ||
|
8440d013f8 | ||
|
174c87a192 | ||
|
32b62d6d4f | ||
|
a0c2d312e9 | ||
|
5127a9ae3c | ||
|
b5653a1c06 | ||
|
671bd1022e | ||
|
94bba4ac9c | ||
|
fe1136aa95 | ||
|
8950ffcc5e | ||
|
2bc627970e | ||
|
44615c6fa2 | ||
|
00f55ea0bc | ||
|
be43cebf7d | ||
|
f38317d01f | ||
|
da839aae66 | ||
|
768e00ff1a | ||
|
40631f465e | ||
|
f665c73bb1 | ||
|
be067466fe | ||
|
3c309df6dd | ||
|
b7e6d98647 | ||
|
48942de75e | ||
|
fbcbc10174 | ||
|
342b48105f | ||
|
5f3e7f02cc | ||
|
bfccf29ccf | ||
|
1e3bb1f02b | ||
|
0dcf6771e7 | ||
|
062503c523 | ||
|
c3d7f2f170 | ||
|
faf6c16717 | ||
|
3d79d78134 | ||
|
35cab4ee73 | ||
|
a0edb8f2ad | ||
|
5cfe5e312b | ||
|
13f6ec04d5 | ||
|
2edc4a79b9 | ||
|
4661185719 | ||
|
895da5cbf0 | ||
|
ef9147512b | ||
|
abe05456f7 | ||
|
f9dfd3b348 | ||
|
9449501537 | ||
|
f3b5d5ab7b | ||
|
8ee5726e0c | ||
|
effa7fd57d | ||
|
6c039d2ad0 | ||
|
2dd8527566 | ||
|
fe811ce32e | ||
|
9fee0805c4 | ||
|
1987c32761 | ||
|
0ed2659698 | ||
|
9878856dfe | ||
|
ed01fd4edf | ||
|
9474933070 | ||
|
f3987b453c | ||
|
dcb4cb3a1e | ||
|
198b840059 | ||
|
42583cf3bb | ||
|
2e15371d61 | ||
|
339ff8ca77 | ||
|
268eaddad8 | ||
|
bb3cc1130b | ||
|
64ae3280e1 | ||
|
95235d69c2 | ||
|
d639c7be39 | ||
|
d7cafe25ff | ||
|
996f53373e | ||
|
6f55370ad4 | ||
|
fd511966a7 | ||
|
c4ddddd434 | ||
|
83a4db3b31 | ||
|
e35ef75949 | ||
|
d9dec6fe6b | ||
|
69bdbaed41 | ||
|
30f359e642 | ||
|
29d0a1714e | ||
|
fcb97f802f | ||
|
9ad05e640d | ||
|
449f00f960 | ||
|
8e64329d05 | ||
|
4284777556 | ||
|
81c496d96c | ||
|
65c06da275 | ||
|
5d0657c49a | ||
|
f6f6550bfb | ||
|
e01fb50359 | ||
|
28ce1c1249 | ||
|
7db592d27a | ||
|
b8e5c0d898 | ||
|
067c1771d0 | ||
|
349429b76e | ||
|
cc8f2afce9 | ||
|
2e97b20f94 | ||
|
1a163243ec | ||
|
75660e6f21 | ||
|
199ca77c2a | ||
|
1e2d2abbdf | ||
|
11b980f574 | ||
|
3274f9f155 | ||
|
f90f8824bb | ||
|
c7116d40ca | ||
|
6ef66399f8 | ||
|
2b8561f27d | ||
|
b20d8f195b | ||
|
6cf0eb9e1d | ||
|
c349e9aabe | ||
|
6ee38ceaba | ||
|
c60613fbcb | ||
|
bcc1b7b48a | ||
|
b19cb0805c | ||
|
7dfc5a78ba | ||
|
a077132d82 | ||
|
8ed6be6307 | ||
|
c490dd1563 | ||
|
d866b3df1f | ||
|
97f3fb4496 | ||
|
7530266330 | ||
|
6a53f356d2 | ||
|
075dc1e4e9 | ||
|
97b87d4ce4 | ||
|
e203e98375 | ||
|
9fcd104065 | ||
|
178e0ba87c | ||
|
cc40110d7e | ||
|
d58fb2bbc0 | ||
|
812333e9ae | ||
|
92bbdce435 | ||
|
bc62d49fc9 | ||
|
fe54d5b8ae | ||
|
7cc30c268b | ||
|
df60a2248a | ||
|
aa85d0ffeb | ||
|
1db963361c | ||
|
8a55b20284 | ||
|
beec349bc5 | ||
|
b025ed6057 | ||
|
27a54bcbaa | ||
|
6b20993d2a | ||
|
9ab16bdbb3 | ||
|
23088bc897 | ||
|
054a62de60 | ||
|
5aff548794 | ||
|
ff8fe7e018 | ||
|
c6617ebc9f | ||
|
15fa0c264f | ||
|
25468f55ff | ||
|
2340c55d76 | ||
|
13c1f4ab19 | ||
|
a160b798ca | ||
|
71f00a9efd | ||
|
967096f01c | ||
|
7616e94fd3 | ||
|
27ec69fb97 | ||
|
182d150eaa | ||
|
098ef976f7 | ||
|
ea724e343b | ||
|
85736d697c | ||
|
576a146ed2 | ||
|
69c5291e52 | ||
|
4875ef045a | ||
|
369cfc2413 | ||
|
491842ea34 | ||
|
9801876a2f | ||
|
9c28a04c65 | ||
|
596a1764ef | ||
|
8e09e1b248 | ||
|
d5674c85d7 | ||
|
ed6649b1d3 | ||
|
02baa778c5 | ||
|
5fd0e5add2 | ||
|
23eccb2f20 | ||
|
3c523fb824 | ||
|
5dbfc2786d | ||
|
c61495df52 | ||
|
6ad5ea1696 | ||
|
39fa27a2dc | ||
|
348bae53fe | ||
|
6bc00fc5e5 | ||
|
54c0f015f9 | ||
|
ea3e6dae93 | ||
|
80e52c73b0 | ||
|
92dbe6cdf8 | ||
|
0781e8cf12 | ||
|
5c893f0f39 | ||
|
e05dc99006 | ||
|
f864416e39 | ||
|
472dbd641c | ||
|
05141b4f52 | ||
|
b71a088da7 | ||
|
d76fb566a2 | ||
|
24a40af103 | ||
|
4a60292f82 | ||
|
784b914e07 | ||
|
7eea866869 | ||
|
4ab5456a98 | ||
|
8881a9f40e | ||
|
4db7f6f59c | ||
|
edbf8509e1 | ||
|
a017fbadd3 | ||
|
201f4b7e4a | ||
|
c9ff536e24 | ||
|
238efb02c6 | ||
|
f7e12b629f | ||
|
7cbca7fc1e | ||
|
be7b87cda3 | ||
|
07979a13fb | ||
|
9073c4554f | ||
|
8694e0ad19 | ||
|
60fe987a5f | ||
|
86256162de | ||
|
db24ca3dc1 | ||
|
5e3aa2db1d | ||
|
b74a501fac | ||
|
490a7d4a78 | ||
|
b147195189 | ||
|
b561666d80 | ||
|
a0b5305e3e | ||
|
e7a6c17260 | ||
|
c8ee9e6447 | ||
|
f2d350002e | ||
|
fabd26f85b | ||
|
17dcf7d2e5 | ||
|
6f62995c96 | ||
|
f405f4bbc4 | ||
|
98124de362 | ||
|
1e4ea90021 | ||
|
053f4a9a2e | ||
|
2c7d2230b3 | ||
|
040e0d8387 | ||
|
b5c382f929 | ||
|
0c9c1ae673 | ||
|
da0e0bdaec | ||
|
1f5b6a6a35 | ||
|
d25b2890be | ||
|
d73438a397 | ||
|
e91538a554 | ||
|
8a3b514372 | ||
|
7eca955b79 | ||
|
4242354c03 | ||
|
8728389c88 | ||
|
6651801b3f | ||
|
9190ce3701 | ||
|
90e2064d72 | ||
|
943d419f98 | ||
|
551316bcb6 | ||
|
9dd5089940 | ||
|
06e7ebbdeb | ||
|
a83b16e12a | ||
|
91a8b97cf4 | ||
|
41754c92c3 | ||
|
e544995b83 | ||
|
45cf5c4c0f | ||
|
900eedfc2e | ||
|
faaa7bfa3a | ||
|
70366a98bd | ||
|
e88180b4d5 | ||
|
b639683ac1 | ||
|
918c8f9295 | ||
|
c2214cd4b5 | ||
|
7d7e5bac12 | ||
|
185b558561 | ||
|
4632035581 | ||
|
d7f81dff23 | ||
|
c849738c6f | ||
|
3cd7a2e6d6 | ||
|
0d4904f05d | ||
|
0b539a5977 | ||
|
395fdc9d61 | ||
|
3b3d7eff3c | ||
|
763c05313b | ||
|
9f80df3fcb | ||
|
f170ee9e59 | ||
|
339218508d | ||
|
f0c710b245 | ||
|
e66337a1db | ||
|
e087bccd33 | ||
|
8017774bf3 | ||
|
5f4d08ada5 | ||
|
1ad450d753 | ||
|
f1692b3436 | ||
|
2a9c56d9e3 | ||
|
39a5688464 | ||
|
e4e6173eff | ||
|
cf7334eb7d | ||
|
fdb96e91f1 | ||
|
844c5027ea | ||
|
8d0e485120 | ||
|
281ee1a853 | ||
|
a0bf29e600 | ||
|
d66c430e46 | ||
|
f511a52705 | ||
|
a674e410e0 | ||
|
fc63445c80 | ||
|
0c15655574 | ||
|
328b6d1cc6 | ||
|
b67d663a38 | ||
|
dd6c5c9eea | ||
|
2e87e64bd1 | ||
|
08c210d833 | ||
|
a9403013df | ||
|
05477c1a03 | ||
|
fcb6198a82 | ||
|
410d0bc125 | ||
|
abc62b9348 | ||
|
6fbf33c8f4 | ||
|
0ceb750dc7 | ||
|
a48c22d14f | ||
|
1521199e44 | ||
|
2910be82a4 | ||
|
07fdb087dc | ||
|
58150f5dcd | ||
|
e7a6ff39f9 | ||
|
b086afb272 | ||
|
7decf76883 | ||
|
d81369d63a | ||
|
c0fbe8237b | ||
|
58923b2846 | ||
|
d9f9477a52 | ||
|
966c744992 | ||
|
ddc91ce7c3 | ||
|
7e5107d90f | ||
|
c1668c9bdb | ||
|
7ddc2ccf1a | ||
|
3cd85fb395 | ||
|
b805ea9bf6 | ||
|
edd76f595a | ||
|
c131b63852 | ||
|
a70f377388 | ||
|
50f76446a9 | ||
|
15ce3ec41a | ||
|
b7b01999d9 | ||
|
956114fc42 | ||
|
bb3a986859 | ||
|
50fefc3bb0 | ||
|
c5eea2e7c5 | ||
|
19555a98ed | ||
|
9021f006f0 | ||
|
2d5f14388e | ||
|
ab47bf6451 | ||
|
d8bd45c2bd | ||
|
de692d3dcc | ||
|
4adb525513 | ||
|
72235a5f72 | ||
|
b6508cccec | ||
|
d9e7cf659e | ||
|
cf500cd817 | ||
|
f1338aca84 | ||
|
284de4ac5d | ||
|
19c4345162 | ||
|
d5d38b3331 | ||
|
bd04638d27 | ||
|
2f0d0e7c7a | ||
|
0b531e9fbc | ||
|
e3ebd582ec | ||
|
95ef046d0a | ||
|
21fd46d66b | ||
|
b3a801df11 | ||
|
a6d22e3b22 | ||
|
0415c050a5 | ||
|
014396cf11 | ||
|
70488f9c56 | ||
|
0052ab7148 | ||
|
f725040dd5 | ||
|
f131863642 | ||
|
8791047005 | ||
|
836a293f16 | ||
|
1177cc3f29 | ||
|
269847d19d | ||
|
df22f68088 | ||
|
d83d8552b8 | ||
|
365aa69afd | ||
|
578c338d40 | ||
|
389518e1b8 | ||
|
d42ff227f1 | ||
|
737e9e48ca | ||
|
f96d91cb6c | ||
|
85503655ab | ||
|
8d811760a9 | ||
|
4e0de22375 | ||
|
7cfbf100eb | ||
|
edbe026b49 | ||
|
1e967eceef | ||
|
7d20db93d3 | ||
|
fb05a42d2e | ||
|
9490ae1440 | ||
|
e932be0fb3 | ||
|
70b49980cb | ||
|
5e6ee5fd48 | ||
|
a2d872a9f0 | ||
|
e8bcde31b7 | ||
|
40cda9220a | ||
|
a494683bc8 | ||
|
2ba6a85eca | ||
|
0e58158a59 | ||
|
af740592c9 | ||
|
c9452c9f31 | ||
|
494a1603e4 | ||
|
5207e111e1 | ||
|
06995fb080 | ||
|
1f5cafc2d1 | ||
|
f190de39a6 | ||
|
272ab746a6 | ||
|
fce0bf6e59 | ||
|
5957786b2c | ||
|
4f3f4e23e4 | ||
|
5f5096e1d4 | ||
|
4b35aef728 | ||
|
6a0ed51f5e | ||
|
67360e93b8 | ||
|
f18f4c69f2 | ||
|
41435578d2 | ||
|
7f33ae3bee | ||
|
645135bf56 | ||
|
eb9005ad74 | ||
|
14089f8c6a | ||
|
49094120d9 | ||
|
fe4111a9f5 | ||
|
61613bee98 | ||
|
3ce967d8e5 | ||
|
aad9afad59 | ||
|
8cbbd022fc | ||
|
9b23cd6d19 | ||
|
d94f241d3c | ||
|
bb7c11adf1 | ||
|
5c295254bf | ||
|
236e8cc95c | ||
|
421973e0d9 | ||
|
e2a5af1cf7 | ||
|
65aa7b1084 | ||
|
f8c8330258 | ||
|
f5411ac9ab | ||
|
f31debc09c | ||
|
6654d7a919 | ||
|
44743b5f6f | ||
|
f80276584f | ||
|
cda41debfc | ||
|
2c50d01c26 | ||
|
01ebb6576d | ||
|
aaca0b6f76 | ||
|
84e1f3649f | ||
|
0ab2cfaf8b | ||
|
0545d6f083 | ||
|
d804228956 | ||
|
f7d70df2ba | ||
|
4daef52991 | ||
|
a329547682 | ||
|
f02af8d481 | ||
|
dd6c067832 | ||
|
dbc435506c | ||
|
f00e289014 | ||
|
1dffaba266 | ||
|
958a2f4274 | ||
|
21718a69d3 | ||
|
c650ae0e19 | ||
|
9ed435d04a | ||
|
fed6a0c24e | ||
|
44b9a8e7ed | ||
|
c16757b03a | ||
|
5d0dde5c15 | ||
|
f60356e8c7 | ||
|
cdf8f78962 | ||
|
4539d236df | ||
|
8718ac0c4b | ||
|
94787d537a | ||
|
bb8cff967e | ||
|
eca57beec1 | ||
|
58b4eb04f9 | ||
|
a9d46297c4 | ||
|
e9edecf34a | ||
|
71a5f0e84e | ||
|
fb22ee94d9 | ||
|
f03904ebce | ||
|
6cc9f49d97 | ||
|
f5f0680ec7 | ||
|
60e04b9065 | ||
|
025da92450 | ||
|
5de4aa091b | ||
|
1fe8235a85 | ||
|
0ab14399ae | ||
|
15dded712c | ||
|
8837f7e6e8 | ||
|
e8defd821a | ||
|
5d6effeff5 | ||
|
427c278012 | ||
|
a78a09f594 | ||
|
59fd48cfe2 | ||
|
cc78ab4855 | ||
|
063562261e | ||
|
70619dd0b7 | ||
|
63031fb278 | ||
|
7f924a56b3 | ||
|
114f2a1465 | ||
|
5d2777634a | ||
|
2d5b4a0003 | ||
|
adfa1704e2 | ||
|
ab3fd6be8f | ||
|
47702d075e | ||
|
341f000b9c | ||
|
0deea53931 | ||
|
8b3d792bec | ||
|
b82c48b66f | ||
|
fa91516dce | ||
|
4954b44d8e | ||
|
d132e51ac7 | ||
|
243b6ae985 | ||
|
8780ba3626 | ||
|
694194be2f | ||
|
c7f61f8b80 | ||
|
3a7c7fe4e8 | ||
|
668967a719 | ||
|
d15c14ab93 | ||
|
52b81608a1 | ||
|
048f754d83 | ||
|
748cb28017 | ||
|
d5ef3a3f8c | ||
|
e768e285ce | ||
|
a102d775b2 | ||
|
65e82b03ad | ||
|
80a636bd14 | ||
|
a4c57ee363 | ||
|
94bf54e7e0 | ||
|
b18ce5ade0 | ||
|
99793bb2c4 | ||
|
093936e594 | ||
|
036a37e351 | ||
|
d904df57ca | ||
|
d507979ec1 | ||
|
9bbcfead67 | ||
|
81036894c0 | ||
|
9044adecb5 | ||
|
9190fdd42c | ||
|
eab35605e4 | ||
|
28b65a7e7b | ||
|
5d1d2308b4 | ||
|
cf5952f508 | ||
|
3b0d7bc4ad | ||
|
22f8ab110e | ||
|
8f2d085d28 | ||
|
c3d7f5b28b | ||
|
45e6000619 | ||
|
7cd00a6760 | ||
|
cecc53fed3 | ||
|
58c2c70146 | ||
|
ad9f488df6 | ||
|
b19799bc72 | ||
|
1209b9b86e | ||
|
da957a3caf | ||
|
1bfd0f0149 | ||
|
3ff48b8559 | ||
|
6ba1eda96f | ||
|
dddfe07867 | ||
|
054f67eeb8 | ||
|
65c59c8c30 | ||
|
24925a1739 | ||
|
c49b40ee95 | ||
|
c06db30a65 | ||
|
0ed6fef49b | ||
|
9bad11ec79 | ||
|
74cdcde449 | ||
|
1613461504 | ||
|
0043f3558c | ||
|
a9c4b8dd1a | ||
|
5d00edc896 | ||
|
3bad815982 | ||
|
08cc7587ab | ||
|
458c0db3a8 | ||
|
a995333081 | ||
|
8eb608a839 | ||
|
1041c9f9fc | ||
|
1564742b76 | ||
|
b887fd153d | ||
|
d083674fb1 | ||
|
ed7a945261 | ||
|
ef5ffa939f | ||
|
d842ccb287 | ||
|
233893f122 | ||
|
2febdfc363 | ||
|
2c971a2598 | ||
|
b4c3c20e5e | ||
|
b6fbb012ad | ||
|
7f4db5a731 | ||
|
4dfdfa0b7d | ||
|
dbf659c575 | ||
|
cd8e04471c | ||
|
93de1e4903 | ||
|
2df43c9e2b | ||
|
5ace44493a | ||
|
a57ba3d81c | ||
|
6298112531 | ||
|
25afca55f6 | ||
|
52a16c917f | ||
|
eef9a60037 | ||
|
e158b5ccf6 | ||
|
f03d7efb5e | ||
|
da7b1fb014 | ||
|
62378d063e | ||
|
5fac282ee0 | ||
|
6eff873a07 | ||
|
1fb306c9d3 | ||
|
c064b3896a | ||
|
8400d1e60e | ||
|
5530e74382 | ||
|
47a38a8977 | ||
|
47883a94a6 | ||
|
ca19bbd366 | ||
|
7595808d26 | ||
|
37d22a144a | ||
|
dc697a6862 | ||
|
5398bac533 | ||
|
9984a168cb | ||
|
286f3713b0 | ||
|
ff9be30f86 | ||
|
34cebe8c0c | ||
|
200cd5972a | ||
|
808d1af578 | ||
|
d0995665a3 | ||
|
6132af8ecb | ||
|
2e9c4914a8 | ||
|
bf0d513e5b | ||
|
2be435ff32 | ||
|
20ba820253 | ||
|
44fd332965 | ||
|
0453d656d6 | ||
|
cb7e38577d | ||
|
4fa59ea04e | ||
|
bc2ed602e7 | ||
|
a1c4d159dd | ||
|
598f29b78e | ||
|
f61f2d6e5e | ||
|
f38df4df11 | ||
|
1e34ccbe2e | ||
|
2a8746f6b0 | ||
|
554e083f3d | ||
|
5d881a8b0f | ||
|
3d81641139 | ||
|
c25b4ba099 | ||
|
fd64c20807 | ||
|
80f1034dd6 | ||
|
15b841da06 | ||
|
902c08e9c9 | ||
|
ea652c023e | ||
|
3c79bb77db | ||
|
d8dbb85946 | ||
|
20702d26ec | ||
|
7d7e9501fa | ||
|
efef76d9cf | ||
|
e1e1ee31f0 | ||
|
142ca58d38 | ||
|
3b01bf7bda | ||
|
30416f54d1 | ||
|
f21ef0d2e9 | ||
|
39ced21a6f | ||
|
0f24417cb3 | ||
|
f84a87f2a2 | ||
|
dada57e5c4 | ||
|
d437d6fde9 | ||
|
69b11575e3 | ||
|
72e1a1b2e9 | ||
|
8ba573d196 | ||
|
4593231049 | ||
|
de9eac760b | ||
|
12ad8d52ae | ||
|
22f9a3b467 | ||
|
b64f0ba83f | ||
|
f73a494407 | ||
|
46ee74ed16 | ||
|
806b746fc0 | ||
|
cc820e97c6 | ||
|
283b04df73 | ||
|
6420d1239f | ||
|
04771e5a4a | ||
|
3d9608faa0 | ||
|
b7b4ae4262 | ||
|
b73b078705 | ||
|
887fa8649b | ||
|
f6172d7273 | ||
|
c70681712d | ||
|
80ca6de531 | ||
|
c6c395cd0f | ||
|
4831064623 | ||
|
d4660a23c0 | ||
|
e184a1b9e6 | ||
|
f8662c9bc2 | ||
|
463df9e4ba | ||
|
c768581829 | ||
|
754f7a7891 | ||
|
21450a08c2 | ||
|
c355b25bb1 | ||
|
1fe3d80838 | ||
|
930e16b64a | ||
|
2077a70d03 | ||
|
cbdb8bd9b9 | ||
|
5dcb417676 | ||
|
71bc993e3d | ||
|
c2812896f8 | ||
|
d43227ede4 | ||
|
8554ae38ed | ||
|
da656caf1e | ||
|
51fc853228 | ||
|
7a30cb9de7 | ||
|
1b475cf9f3 | ||
|
719b690451 | ||
|
3cdc523dec | ||
|
eb49127b9e | ||
|
d07172a528 | ||
|
79637097ba | ||
|
1259341095 | ||
|
5d3bc95ac5 | ||
|
de25232a73 | ||
|
95769de464 | ||
|
52a168b961 | ||
|
b3b00b6700 | ||
|
8e8cda132c | ||
|
6459ccb185 | ||
|
548f83c3ad | ||
|
555e0de9e4 | ||
|
cc4bce283f | ||
|
8189a34d14 | ||
|
5d88ad554f | ||
|
2cc50a2b65 | ||
|
33670a5bd0 | ||
|
64f8a222cb | ||
|
d9a9695fe0 | ||
|
8e6c4e1aca | ||
|
490fbfc13e | ||
|
4b45973361 | ||
|
4c27e08e3d | ||
|
94eb80597b | ||
|
d610eb15d8 | ||
|
4f53cd1704 | ||
|
d795fac37a | ||
|
ce6b71a58b | ||
|
09f74a9af8 | ||
|
552a49a680 | ||
|
97741398fb | ||
|
f8b225e70e | ||
|
57b4eda014 | ||
|
fc3a181779 | ||
|
9541ea6a9f | ||
|
f716f6060e | ||
|
dc0cca8c83 | ||
|
4f303de00c | ||
|
05aa26e619 | ||
|
1c4b831922 | ||
|
6613ae57b0 | ||
|
b6552aff75 | ||
|
3c98fae4f2 | ||
|
2028e4c8ad | ||
|
5c7feba77b | ||
|
23f2677052 | ||
|
6b67511748 | ||
|
36e0feea43 | ||
|
69392f67e8 | ||
|
e7d130cc11 | ||
|
6e3ba3ca45 | ||
|
0f54cf83f4 | ||
|
fb209cdfde | ||
|
431c53efcf | ||
|
79ad0ff56b | ||
|
d4dad58b0d | ||
|
baff032e3b | ||
|
26309f51e3 | ||
|
f8f53a6bd9 | ||
|
ac3667c765 | ||
|
96180e7555 | ||
|
024619676b | ||
|
5aa0f547cf | ||
|
b1ce6ffcc7 | ||
|
f01936ca4f | ||
|
70fdb1042f | ||
|
58cfc0d0cf | ||
|
4eff3b6a24 | ||
|
e5f69f0815 | ||
|
ef7b51beb7 | ||
|
8494ac8f3d | ||
|
8dea519235 | ||
|
0712e98904 | ||
|
c7ccddbcb9 | ||
|
efd3e8067b | ||
|
c6f7b7f35f | ||
|
f3dd1603db | ||
|
be7688a4df | ||
|
8e2f11389d | ||
|
346454c21b | ||
|
c822870cf8 | ||
|
9666cf680e | ||
|
a88622c1be | ||
|
c3fbc36ce7 | ||
|
f174d7dd39 | ||
|
38a8721a91 | ||
|
8aedf26a87 | ||
|
b2ff9240ac | ||
|
7a3c61b744 | ||
|
8dd1df71cc | ||
|
b59b0f0386 | ||
|
f59f484c01 | ||
|
a44ea0ddf0 | ||
|
2214507db0 | ||
|
a138425417 | ||
|
99c47dd50a | ||
|
594b83e7a6 | ||
|
76d0ef0851 | ||
|
549ebbb462 | ||
|
341656ddb9 | ||
|
5a3c3b4876 | ||
|
375b8dceb7 | ||
|
f37546e173 | ||
|
239d53426a | ||
|
3ccac629bc | ||
|
e8e6feeb0f | ||
|
c22705a593 | ||
|
ec7889dfa8 | ||
|
563d59526f | ||
|
0ffd5de6fc | ||
|
5014f83b86 | ||
|
953a9b1768 | ||
|
c641b61b26 | ||
|
d87e507865 | ||
|
ec1f9841b2 | ||
|
84b0f29d87 | ||
|
b23e05dbc5 | ||
|
37978b4fe5 | ||
|
ef15e55947 | ||
|
c282dd086f | ||
|
aac9f089d9 | ||
|
adce8f52e8 | ||
|
20b64c8900 | ||
|
66d781a226 | ||
|
867ec010ab | ||
|
ce0c6da9fa | ||
|
e22c5ea800 | ||
|
16bfb1727f | ||
|
f1f14040b8 | ||
|
9cb328966c | ||
|
0c76890572 | ||
|
18fc42e63b | ||
|
fc8d9df516 | ||
|
afdf8a78c0 | ||
|
0b3ae1f972 | ||
|
6a5ee72722 | ||
|
c6ec8bc0d9 | ||
|
51cfd996eb | ||
|
6d0e4bed4b | ||
|
05247dc4a4 | ||
|
43011f3bfa | ||
|
38854bd876 | ||
|
a9726bd52f | ||
|
4216c9e8f7 | ||
|
a8d670fc0d | ||
|
cbacc779fc | ||
|
896778cead | ||
|
04b0c62bf9 | ||
|
63a779baa8 | ||
|
2d1a776db7 | ||
|
3d2df3ba93 | ||
|
eb6238781d | ||
|
6140a3c26b | ||
|
6eaf2d67b7 | ||
|
35b34c43ed | ||
|
f67a9d2de1 | ||
|
05acf28e0d | ||
|
5698bec621 | ||
|
3ec495225f | ||
|
fee9baca89 | ||
|
bec26ce754 | ||
|
343d7df57c | ||
|
dca6a4bbd5 | ||
|
a32b95544b | ||
|
2422e0b481 | ||
|
3441bd0e7c | ||
|
05ced9fbc4 | ||
|
b7c3df455e | ||
|
d7affad059 | ||
|
34c3da9117 | ||
|
a22d3b2390 | ||
|
7c09bdc6e0 | ||
|
bc291141b1 | ||
|
c1b089d1c3 | ||
|
1271f97b66 | ||
|
582c77805c | ||
|
9eb5f65b8f | ||
|
00f01a9889 | ||
|
671edc33e1 | ||
|
1253357a39 | ||
|
6df31eb7f5 | ||
|
9299a83b17 | ||
|
a6614abd24 | ||
|
4c9d99040c | ||
|
6ef8adc863 | ||
|
a00300f88a | ||
|
274393ac64 | ||
|
1339b9422d | ||
|
ed9e196bf6 | ||
|
bba5376a36 | ||
|
df3575217a | ||
|
c504506455 | ||
|
2a28772312 | ||
|
d04c6dd3ac | ||
|
e48daffad9 | ||
|
58642286c9 | ||
|
6d62ae226a | ||
|
14f6f9ec94 | ||
|
e10f447b5b | ||
|
1d1f61613c | ||
|
b85c1a8861 | ||
|
430956d304 | ||
|
c0449a3ed2 | ||
|
18ad01533b | ||
|
288049dc95 | ||
|
573c8f3b13 | ||
|
e85d7a7be5 | ||
|
7015215f26 | ||
|
ffa5472b31 | ||
|
e00f0b4cf1 | ||
|
dc5c220e8f | ||
|
933d49b0b0 | ||
|
9af85f5a7e | ||
|
7ec52145e8 | ||
|
aa6112482d | ||
|
d035cdcff9 | ||
|
7ad3ddef2a | ||
|
38e08bb91f | ||
|
252a21e2ae | ||
|
ba7db3edda | ||
|
f64b061a28 | ||
|
f323ced4ca | ||
|
c06ec7c6ba | ||
|
835f9aad91 | ||
|
a4ec9f8b44 | ||
|
47c33d0344 | ||
|
f500c7abcb | ||
|
95dd7b5323 | ||
|
65c950e1a4 | ||
|
e484f32b1a | ||
|
bc396e7a90 | ||
|
ce182f43db | ||
|
9382d52d55 | ||
|
dd156d0689 | ||
|
ac9f6e3a41 | ||
|
1e7534b9d7 | ||
|
72d800ed10 | ||
|
54143ae6d4 | ||
|
bfa6e52470 | ||
|
ec654d2355 | ||
|
dfb4883c93 | ||
|
4bf1f579f5 | ||
|
f0d6d46766 | ||
|
4a81205e04 | ||
|
0ac37981cb | ||
|
400c31d031 | ||
|
54b38086e5 | ||
|
e0deca33d0 | ||
|
0b2b8b960b | ||
|
9826b8ae69 | ||
|
2ce9fb9760 | ||
|
ae66c6f0b4 | ||
|
1ef7fd3659 | ||
|
aeed287122 | ||
|
ea6a3c0963 | ||
|
8902a5c5cd | ||
|
3021c5cfad | ||
|
16db9a7337 | ||
|
68f66ca101 | ||
|
c34aadfbf7 | ||
|
cb4a2cf029 | ||
|
26b5180bf7 | ||
|
11bfb1e5fd | ||
|
e431df06ab | ||
|
cbf0ceacd5 | ||
|
8c634d8323 | ||
|
32d7bd5ab1 | ||
|
861df49670 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -3,7 +3,7 @@
|
|||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
patreon: # Replace with a single Patreon username
|
patreon: # Replace with a single Patreon username
|
||||||
open_collective: acmesh
|
open_collective: acmesh
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: neilpang
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
liberapay: # Replace with a single Liberapay username
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
@ -2,7 +2,7 @@
|
|||||||
我很忙, 每天可能只有 几秒钟 时间看你的 issue, 如果不按照我的要求写 issue, 你可能不会得到任何回复, 石沉大海.
|
我很忙, 每天可能只有 几秒钟 时间看你的 issue, 如果不按照我的要求写 issue, 你可能不会得到任何回复, 石沉大海.
|
||||||
|
|
||||||
请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试信息. 我做不了什么.
|
请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试信息. 我做不了什么.
|
||||||
如何调试 https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh
|
如何调试 https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
|
||||||
|
|
||||||
If it is a bug report:
|
If it is a bug report:
|
||||||
- make sure you are able to repro it on the latest released version.
|
- make sure you are able to repro it on the latest released version.
|
||||||
@ -10,7 +10,7 @@ You can install the latest version by: `acme.sh --upgrade`
|
|||||||
|
|
||||||
- Search the existing issues.
|
- Search the existing issues.
|
||||||
- Refer to the [WIKI](https://wiki.acme.sh).
|
- Refer to the [WIKI](https://wiki.acme.sh).
|
||||||
- Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh).
|
- Debug info [Debug](https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh).
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -3,7 +3,7 @@
|
|||||||
Please send to `dev` branch instead.
|
Please send to `dev` branch instead.
|
||||||
Any PR to `master` branch will NOT be merged.
|
Any PR to `master` branch will NOT be merged.
|
||||||
|
|
||||||
2. For dns api support, read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
|
2. For dns api support, read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
|
||||||
You will NOT get any review without passing this guide. You also need to fix the CI errors.
|
You will NOT get any review without passing this guide. You also need to fix the CI errors.
|
||||||
|
|
||||||
-->
|
-->
|
515
.github/workflows/DNS.yml
vendored
Normal file
515
.github/workflows/DNS.yml
vendored
Normal file
@ -0,0 +1,515 @@
|
|||||||
|
name: DNS
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'dnsapi/*.sh'
|
||||||
|
- '.github/workflows/DNS.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'dev'
|
||||||
|
paths:
|
||||||
|
- 'dnsapi/*.sh'
|
||||||
|
- '.github/workflows/DNS.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
CheckToken:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
hasToken: ${{ steps.step_one.outputs.hasToken }}
|
||||||
|
steps:
|
||||||
|
- name: Set the value
|
||||||
|
id: step_one
|
||||||
|
run: |
|
||||||
|
if [ "${{secrets.TokenName1}}" ] ; then
|
||||||
|
echo "::set-output name=hasToken::true"
|
||||||
|
else
|
||||||
|
echo "::set-output name=hasToken::false"
|
||||||
|
fi
|
||||||
|
- name: Check the value
|
||||||
|
run: echo ${{ steps.step_one.outputs.hasToken }}
|
||||||
|
|
||||||
|
Fail:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: CheckToken
|
||||||
|
if: "contains(needs.CheckToken.outputs.hasToken, 'false')"
|
||||||
|
steps:
|
||||||
|
- name: "Read this: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Test"
|
||||||
|
run: |
|
||||||
|
echo "Read this: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Test"
|
||||||
|
if [ "${{github.repository_owner}}" != "acmesh-official" ]; then
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
|
||||||
|
Docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: CheckToken
|
||||||
|
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Set env file
|
||||||
|
run: |
|
||||||
|
cd ../acmetest
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}" >> docker.env
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}" >> docker.env
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}" >> docker.env
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}" >> docker.env
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
echo "${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}" >> docker.env
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run acmetest
|
||||||
|
run: cd ../acmetest && ./rundocker.sh testall
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MacOS:
|
||||||
|
runs-on: macos-latest
|
||||||
|
needs: Docker
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install tools
|
||||||
|
run: brew install socat
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
needs: MacOS
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- name: Set git to use LF
|
||||||
|
run: |
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install cygwin base packages with chocolatey
|
||||||
|
run: |
|
||||||
|
choco config get cacheLocation
|
||||||
|
choco install --no-progress cygwin
|
||||||
|
shell: cmd
|
||||||
|
- name: Install cygwin additional packages
|
||||||
|
run: |
|
||||||
|
C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s https://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git
|
||||||
|
shell: cmd
|
||||||
|
- name: Set ENV
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
echo PATH=C:\tools\cygwin\bin;C:\tools\cygwin\usr\bin >> %GITHUB_ENV%
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FreeBSD:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: Windows
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
prepare: pkg install -y socat curl
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
OpenBSD:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: FreeBSD
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/openbsd-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
prepare: pkg_add socat curl libiconv
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NetBSD:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: OpenBSD
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/netbsd-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
prepare: |
|
||||||
|
/usr/sbin/pkg_add curl socat
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DragonFlyBSD:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: NetBSD
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/dragonflybsd-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
prepare: |
|
||||||
|
pkg install -y curl socat libnghttp2
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Solaris:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: DragonFlyBSD
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Solaris doesn't accept the expired ISRG X1 root
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/solaris-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
copyback: false
|
||||||
|
prepare: pkgutil -y -i socat
|
||||||
|
run: |
|
||||||
|
pkg set-mediator -v -I default@1.1 openssl
|
||||||
|
export PATH=/usr/gnu/bin:$PATH
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
Omnios:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: Solaris
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Omnios doesn't accept the expired ISRG X1 root
|
||||||
|
TokenName1: ${{ secrets.TokenName1}}
|
||||||
|
TokenName2: ${{ secrets.TokenName2}}
|
||||||
|
TokenName3: ${{ secrets.TokenName3}}
|
||||||
|
TokenName4: ${{ secrets.TokenName4}}
|
||||||
|
TokenName5: ${{ secrets.TokenName5}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/omnios-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
copyback: false
|
||||||
|
prepare: pkg install socat
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.TokenName1}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName2}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName3}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName4}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
|
||||||
|
fi
|
||||||
|
if [ "${{ secrets.TokenName5}}" ] ; then
|
||||||
|
export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
|
||||||
|
fi
|
||||||
|
cd ../acmetest
|
||||||
|
./letest.sh
|
||||||
|
|
||||||
|
|
71
.github/workflows/DragonFlyBSD.yml
vendored
Normal file
71
.github/workflows/DragonFlyBSD.yml
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
name: DragonFlyBSD
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/DragonFlyBSD.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/DragonFlyBSD.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
DragonFlyBSD:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vmactions/cf-tunnel@v0
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/dragonflybsd-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: |
|
||||||
|
pkg install -y curl socat libnghttp2
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
76
.github/workflows/FreeBSD.yml
vendored
Normal file
76
.github/workflows/FreeBSD.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
name: FreeBSD
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/FreeBSD.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/FreeBSD.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
FreeBSD:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
ACME_USE_WGET: 1
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vmactions/cf-tunnel@v0
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: pkg install -y socat curl wget
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
48
.github/workflows/Linux.yml
vendored
Normal file
48
.github/workflows/Linux.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
name: Linux
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Linux.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Linux.yml'
|
||||||
|
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Linux:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "mageia", "gentoo/stage3"]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: |
|
||||||
|
cd .. \
|
||||||
|
&& git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
|
||||||
|
&& cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./rundocker.sh testplat ${{ matrix.os }}
|
||||||
|
|
||||||
|
|
||||||
|
|
60
.github/workflows/MacOS.yml
vendored
Normal file
60
.github/workflows/MacOS.yml
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
name: MacOS
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/MacOS.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/MacOS.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
MacOS:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: macos-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install tools
|
||||||
|
run: brew install socat
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: |
|
||||||
|
cd .. \
|
||||||
|
&& git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
|
||||||
|
&& cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& sudo --preserve-env ./letest.sh
|
||||||
|
|
||||||
|
|
71
.github/workflows/NetBSD.yml
vendored
Normal file
71
.github/workflows/NetBSD.yml
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
name: NetBSD
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/NetBSD.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/NetBSD.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
NetBSD:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vmactions/cf-tunnel@v0
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/netbsd-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: |
|
||||||
|
/usr/sbin/pkg_add curl socat
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
75
.github/workflows/Omnios.yml
vendored
Normal file
75
.github/workflows/Omnios.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
name: Omnios
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Omnios.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Omnios.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Omnios:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
ACME_USE_WGET: 1
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vmactions/cf-tunnel@v0
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/omnios-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: pkg install socat wget
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
76
.github/workflows/OpenBSD.yml
vendored
Normal file
76
.github/workflows/OpenBSD.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
name: OpenBSD
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/OpenBSD.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/OpenBSD.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
OpenBSD:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
ACME_USE_WGET: 1
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vmactions/cf-tunnel@v0
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/openbsd-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: pkg_add socat curl wget libnghttp2
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
72
.github/workflows/PebbleStrict.yml
vendored
Normal file
72
.github/workflows/PebbleStrict.yml
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
name: PebbleStrict
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/PebbleStrict.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/PebbleStrict.yml'
|
||||||
|
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
PebbleStrict:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TestingDomain: example.com
|
||||||
|
TestingAltDomains: www.example.com
|
||||||
|
TEST_ACME_Server: https://localhost:14000/dir
|
||||||
|
HTTPS_INSECURE: 1
|
||||||
|
Le_HTTPPort: 5002
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_CA: "Pebble Intermediate CA"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install tools
|
||||||
|
run: sudo apt-get install -y socat
|
||||||
|
- name: Run Pebble
|
||||||
|
run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker compose up -d
|
||||||
|
- name: Set up Pebble
|
||||||
|
run: curl --request POST --data '{"ip":"10.30.50.1"}' http://localhost:8055/set-default-ipv4
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: cd ../acmetest && ./letest.sh
|
||||||
|
|
||||||
|
PebbleStrict_IPCert:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TestingDomain: 1.23.45.67
|
||||||
|
TEST_ACME_Server: https://localhost:14000/dir
|
||||||
|
HTTPS_INSECURE: 1
|
||||||
|
Le_HTTPPort: 5002
|
||||||
|
Le_TLSPort: 5001
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_CA: "Pebble Intermediate CA"
|
||||||
|
TEST_IPCERT: 1
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install tools
|
||||||
|
run: sudo apt-get install -y socat
|
||||||
|
- name: Run Pebble
|
||||||
|
run: |
|
||||||
|
docker run --rm -itd --name=pebble \
|
||||||
|
-e PEBBLE_VA_ALWAYS_VALID=1 \
|
||||||
|
-p 14000:14000 -p 15000:15000 letsencrypt/pebble:latest pebble -config /test/config/pebble-config.json -strict
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: cd ../acmetest && ./letest.sh
|
75
.github/workflows/Solaris.yml
vendored
Normal file
75
.github/workflows/Solaris.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
name: Solaris
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Solaris.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Solaris.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Solaris:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
ACME_USE_WGET: 1
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: vmactions/cf-tunnel@v0
|
||||||
|
id: tunnel
|
||||||
|
with:
|
||||||
|
protocol: http
|
||||||
|
port: 8080
|
||||||
|
- name: Set envs
|
||||||
|
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- uses: vmactions/solaris-vm@v1
|
||||||
|
with:
|
||||||
|
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||||
|
nat: |
|
||||||
|
"8080": "80"
|
||||||
|
prepare: pkgutil -y -i socat curl wget
|
||||||
|
copyback: false
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& ./letest.sh
|
||||||
|
|
||||||
|
|
103
.github/workflows/Ubuntu.yml
vendored
Normal file
103
.github/workflows/Ubuntu.yml
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
name: Ubuntu
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Ubuntu.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Ubuntu.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Ubuntu:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
ACME_USE_WGET: 1
|
||||||
|
- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
- TEST_ACME_Server: "https://localhost:9000/acme/acme/directory"
|
||||||
|
CA_ECDSA: "Smallstep Intermediate CA"
|
||||||
|
CA: "Smallstep Intermediate CA"
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
NO_REVOKE: 1
|
||||||
|
- TEST_ACME_Server: "https://localhost:9000/acme/acme/directory"
|
||||||
|
CA_ECDSA: "Smallstep Intermediate CA"
|
||||||
|
CA: "Smallstep Intermediate CA"
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: ""
|
||||||
|
NO_REVOKE: 1
|
||||||
|
TEST_IPCERT: 1
|
||||||
|
TestingDomain: "172.17.0.1"
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
NO_ECC_384: ${{ matrix.NO_ECC_384 }}
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
NO_REVOKE: ${{ matrix.NO_REVOKE }}
|
||||||
|
TEST_IPCERT: ${{ matrix.TEST_IPCERT }}
|
||||||
|
TestingDomain: ${{ matrix.TestingDomain }}
|
||||||
|
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install tools
|
||||||
|
run: sudo apt-get install -y socat wget
|
||||||
|
- name: Start StepCA
|
||||||
|
if: ${{ matrix.TEST_ACME_Server=='https://localhost:9000/acme/acme/directory' }}
|
||||||
|
run: |
|
||||||
|
docker run --rm -d \
|
||||||
|
-p 9000:9000 \
|
||||||
|
-e "DOCKER_STEPCA_INIT_NAME=Smallstep" \
|
||||||
|
-e "DOCKER_STEPCA_INIT_DNS_NAMES=localhost,$(hostname -f)" \
|
||||||
|
-e "DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT=true" \
|
||||||
|
-e "DOCKER_STEPCA_INIT_PASSWORD=test" \
|
||||||
|
--name stepca \
|
||||||
|
smallstep/step-ca:0.23.1
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
docker exec stepca bash -c "echo test >test" \
|
||||||
|
&& docker exec stepca step ca provisioner add acme --type ACME --admin-subject step --admin-password-file=/home/step/test \
|
||||||
|
&& docker exec stepca kill -1 1 \
|
||||||
|
&& docker exec stepca cat /home/step/certs/root_ca.crt | sudo bash -c "cat - >>/etc/ssl/certs/ca-certificates.crt"
|
||||||
|
- name: Clone acmetest
|
||||||
|
run: |
|
||||||
|
cd .. \
|
||||||
|
&& git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
|
||||||
|
&& cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
run: |
|
||||||
|
cd ../acmetest \
|
||||||
|
&& sudo --preserve-env ./letest.sh
|
||||||
|
|
||||||
|
|
78
.github/workflows/Windows.yml
vendored
Normal file
78
.github/workflows/Windows.yml
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
name: Windows
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Windows.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Windows.yml'
|
||||||
|
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Windows:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||||
|
CA_ECDSA: ""
|
||||||
|
CA: ""
|
||||||
|
CA_EMAIL: ""
|
||||||
|
TEST_PREFERRED_CHAIN: (STAGING)
|
||||||
|
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||||
|
# CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
|
||||||
|
# CA: "ZeroSSL RSA Domain Secure Site CA"
|
||||||
|
# CA_EMAIL: "githubtest@acme.sh"
|
||||||
|
# TEST_PREFERRED_CHAIN: ""
|
||||||
|
runs-on: windows-latest
|
||||||
|
env:
|
||||||
|
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||||
|
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||||
|
CA: ${{ matrix.CA }}
|
||||||
|
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
#The 80 port is used by Windows server, we have to use a custom port, tunnel will also use this port.
|
||||||
|
Le_HTTPPort: 8888
|
||||||
|
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||||
|
steps:
|
||||||
|
- name: Set git to use LF
|
||||||
|
run: |
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install cygwin base packages with chocolatey
|
||||||
|
run: |
|
||||||
|
choco config get cacheLocation
|
||||||
|
choco install --no-progress cygwin
|
||||||
|
shell: cmd
|
||||||
|
- name: Install cygwin additional packages
|
||||||
|
run: |
|
||||||
|
C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s https://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git,xxd
|
||||||
|
shell: cmd
|
||||||
|
- name: Set ENV
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
echo PATH=C:\tools\cygwin\bin;C:\tools\cygwin\usr\bin;%PATH% >> %GITHUB_ENV%
|
||||||
|
- name: Check ENV
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
echo "PATH=%PATH%"
|
||||||
|
- name: Clone acmetest
|
||||||
|
shell: cmd
|
||||||
|
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||||
|
- name: Run acmetest
|
||||||
|
shell: cmd
|
||||||
|
run: cd ../acmetest && bash.exe -c ./letest.sh
|
||||||
|
|
||||||
|
|
||||||
|
|
84
.github/workflows/dockerhub.yml
vendored
Normal file
84
.github/workflows/dockerhub.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
name: Build DockerHub
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '**.sh'
|
||||||
|
- "Dockerfile"
|
||||||
|
- '.github/workflows/dockerhub.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKER_IMAGE: neilpang/acme.sh
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
CheckToken:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
hasToken: ${{ steps.step_one.outputs.hasToken }}
|
||||||
|
env:
|
||||||
|
DOCKER_PASSWORD : ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
steps:
|
||||||
|
- name: Set the value
|
||||||
|
id: step_one
|
||||||
|
run: |
|
||||||
|
if [ "$DOCKER_PASSWORD" ] ; then
|
||||||
|
echo "hasToken=true" >>$GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "hasToken=false" >>$GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
- name: Check the value
|
||||||
|
run: echo ${{ steps.step_one.outputs.hasToken }}
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: CheckToken
|
||||||
|
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
|
||||||
|
steps:
|
||||||
|
- name: checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5.5.1
|
||||||
|
with:
|
||||||
|
images: ${DOCKER_IMAGE}
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: login to docker hub
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||||
|
- name: build and push the image
|
||||||
|
run: |
|
||||||
|
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||||
|
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||||
|
DOCKER_IMAGE_TAG=${GITHUB_REF#refs/heads/}
|
||||||
|
|
||||||
|
if [[ $DOCKER_IMAGE_TAG == master ]]; then
|
||||||
|
DOCKER_IMAGE_TAG=latest
|
||||||
|
AUTO_UPGRADE=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOCKER_LABELS=()
|
||||||
|
while read -r label; do
|
||||||
|
DOCKER_LABELS+=(--label "${label}")
|
||||||
|
done <<<"${DOCKER_METADATA_OUTPUT_LABELS}"
|
||||||
|
|
||||||
|
docker buildx build \
|
||||||
|
--tag ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \
|
||||||
|
"${DOCKER_LABELS[@]}" \
|
||||||
|
--output "type=image,push=true" \
|
||||||
|
--build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \
|
||||||
|
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x .
|
19
.github/workflows/issue.yml
vendored
Normal file
19
.github/workflows/issue.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: "Update issues"
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
comment:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.issues.createComment({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body: "Please upgrade to the latest code and try again first. Maybe it's already fixed. ```acme.sh --upgrade``` If it's still not working, please provide the log with `--debug 2`, otherwise, nobody can help you."
|
||||||
|
|
||||||
|
})
|
30
.github/workflows/pr_dns.yml
vendored
Normal file
30
.github/workflows/pr_dns.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Check dns api
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
paths:
|
||||||
|
- 'dnsapi/*.sh'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
welcome:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body: `**Welcome**
|
||||||
|
First thing: don't send PR to the master branch, please send to the dev branch instead.
|
||||||
|
Please make sure you've read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
|
||||||
|
Then reply on this message, otherwise, your code will not be reviewed or merged.
|
||||||
|
We look forward to reviewing your Pull request shortly ✨
|
||||||
|
注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试.
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
30
.github/workflows/pr_notify.yml
vendored
Normal file
30
.github/workflows/pr_notify.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Check notify api
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
branches:
|
||||||
|
- 'dev'
|
||||||
|
paths:
|
||||||
|
- 'notify/*.sh'
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
welcome:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body: `**Welcome**
|
||||||
|
Please make sure you've read our [Code-of-conduct](../wiki/Code-of-conduct) and add the usage here: [notify](../wiki/notify).
|
||||||
|
Then reply on this message, otherwise, your code will not be reviewed or merged.
|
||||||
|
We look forward to reviewing your Pull request shortly ✨
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
38
.github/workflows/shellcheck.yml
vendored
Normal file
38
.github/workflows/shellcheck.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
name: Shellcheck
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '**.sh'
|
||||||
|
- '.github/workflows/shellcheck.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '**.sh'
|
||||||
|
- '.github/workflows/shellcheck.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ShellCheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Shellcheck
|
||||||
|
run: sudo apt-get install -y shellcheck
|
||||||
|
- name: DoShellcheck
|
||||||
|
run: shellcheck -V && shellcheck -e SC2181 -e SC2089 **/*.sh && echo "shellcheck OK"
|
||||||
|
|
||||||
|
shfmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install shfmt
|
||||||
|
run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt
|
||||||
|
- name: shfmt
|
||||||
|
run: ~/shfmt -l -w -i 2 . ; git diff --exit-code && echo "shfmt OK"
|
38
.travis.yml
38
.travis.yml
@ -1,38 +0,0 @@
|
|||||||
language: shell
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64
|
|
||||||
|
|
||||||
|
|
||||||
install:
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
|
|
||||||
brew update && brew install socat;
|
|
||||||
export PATH="/usr/local/opt/openssl@1.1/bin:$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 && chmod +x ~/shfmt && ~/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 -e SC2181 **/*.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" ./rundocker.sh testplat ubuntu:latest ; fi
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
|
|
27
Dockerfile
27
Dockerfile
@ -1,27 +1,32 @@
|
|||||||
FROM alpine:3.10
|
FROM alpine:3.17
|
||||||
|
|
||||||
RUN apk update -f \
|
RUN apk --no-cache add -f \
|
||||||
&& apk --no-cache add -f \
|
|
||||||
openssl \
|
openssl \
|
||||||
|
openssh-client \
|
||||||
coreutils \
|
coreutils \
|
||||||
bind-tools \
|
bind-tools \
|
||||||
curl \
|
curl \
|
||||||
|
sed \
|
||||||
socat \
|
socat \
|
||||||
tzdata \
|
tzdata \
|
||||||
oath-toolkit-oathtool \
|
oath-toolkit-oathtool \
|
||||||
tar \
|
tar \
|
||||||
&& rm -rf /var/cache/apk/*
|
libidn \
|
||||||
|
jq \
|
||||||
|
cronie
|
||||||
|
|
||||||
ENV LE_CONFIG_HOME /acme.sh
|
ENV LE_CONFIG_HOME /acme.sh
|
||||||
|
|
||||||
ENV AUTO_UPGRADE 1
|
ARG AUTO_UPGRADE=1
|
||||||
|
|
||||||
|
ENV AUTO_UPGRADE $AUTO_UPGRADE
|
||||||
|
|
||||||
#Install
|
#Install
|
||||||
ADD ./ /install_acme.sh/
|
COPY ./ /install_acme.sh/
|
||||||
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
|
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
|
||||||
|
|
||||||
|
|
||||||
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab -
|
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null#> /proc/1/fd/1 2>/proc/1/fd/2#' | crontab -
|
||||||
|
|
||||||
RUN for verb in help \
|
RUN for verb in help \
|
||||||
version \
|
version \
|
||||||
@ -37,6 +42,7 @@ RUN for verb in help \
|
|||||||
revoke \
|
revoke \
|
||||||
remove \
|
remove \
|
||||||
list \
|
list \
|
||||||
|
info \
|
||||||
showcsr \
|
showcsr \
|
||||||
install-cronjob \
|
install-cronjob \
|
||||||
uninstall-cronjob \
|
uninstall-cronjob \
|
||||||
@ -51,17 +57,18 @@ RUN for verb in help \
|
|||||||
deactivate \
|
deactivate \
|
||||||
deactivate-account \
|
deactivate-account \
|
||||||
set-notify \
|
set-notify \
|
||||||
|
set-default-ca \
|
||||||
|
set-default-chain \
|
||||||
; do \
|
; do \
|
||||||
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
|
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
|
||||||
; done
|
; done
|
||||||
|
|
||||||
RUN printf "%b" '#!'"/usr/bin/env sh\n \
|
RUN printf "%b" '#!'"/usr/bin/env sh\n \
|
||||||
if [ \"\$1\" = \"daemon\" ]; then \n \
|
if [ \"\$1\" = \"daemon\" ]; then \n \
|
||||||
trap \"echo stop && killall crond && exit 0\" SIGTERM SIGINT \n \
|
exec crond -n -s -m off \n \
|
||||||
crond && while true; do sleep 1; done;\n \
|
|
||||||
else \n \
|
else \n \
|
||||||
exec -- \"\$@\"\n \
|
exec -- \"\$@\"\n \
|
||||||
fi" >/entry.sh && chmod +x /entry.sh
|
fi\n" >/entry.sh && chmod +x /entry.sh
|
||||||
|
|
||||||
VOLUME /acme.sh
|
VOLUME /acme.sh
|
||||||
|
|
||||||
|
173
README.md
173
README.md
@ -1,81 +1,108 @@
|
|||||||
# An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)
|
# An ACME Shell script: acme.sh
|
||||||
|
|
||||||
|
[![FreeBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)
|
||||||
|
[![OpenBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)
|
||||||
|
[![NetBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)
|
||||||
|
[![MacOS](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)
|
||||||
|
[![Ubuntu](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)
|
||||||
|
[![Windows](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)
|
||||||
|
[![Solaris](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)
|
||||||
|
[![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)
|
||||||
|
[![Omnios](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)
|
||||||
|
|
||||||
|
![Shellcheck](https://github.com/acmesh-official/acme.sh/workflows/Shellcheck/badge.svg)
|
||||||
|
![PebbleStrict](https://github.com/acmesh-official/acme.sh/workflows/PebbleStrict/badge.svg)
|
||||||
|
![DockerHub](https://github.com/acmesh-official/acme.sh/workflows/Build%20DockerHub/badge.svg)
|
||||||
|
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/acmesh" alt="Financial Contributors on Open Collective"><img src="https://opencollective.com/acmesh/all/badge.svg?label=financial+contributors" /></a>
|
||||||
|
[![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
[![Docker stars](https://img.shields.io/docker/stars/neilpang/acme.sh.svg)](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
|
||||||
|
[![Docker pulls](https://img.shields.io/docker/pulls/neilpang/acme.sh.svg)](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/acmesh" alt="Financial Contributors on Open Collective"><img src="https://opencollective.com/acmesh/all/badge.svg?label=financial+contributors" /></a> [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
- An ACME protocol client written purely in Shell (Unix shell) language.
|
- An ACME protocol client written purely in Shell (Unix shell) language.
|
||||||
- Full ACME protocol implementation.
|
- Full ACME protocol implementation.
|
||||||
- Support ACME v1 and ACME v2
|
- Support ECDSA certs
|
||||||
- Support ACME v2 wildcard certs
|
- Support SAN and wildcard certs
|
||||||
- Simple, powerful and very easy to use. You only need 3 minutes to learn it.
|
- Simple, powerful and very easy to use. You only need 3 minutes to learn it.
|
||||||
- Bash, dash and sh compatible.
|
- Bash, dash and sh compatible.
|
||||||
- Simplest shell script for Let's Encrypt free certificate client.
|
- Purely written in Shell with no dependencies on python.
|
||||||
- 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.
|
- Just one script to issue, renew and install your certificates automatically.
|
||||||
- DOES NOT require `root/sudoer` access.
|
- DOES NOT require `root/sudoer` access.
|
||||||
- Docker friendly
|
- Docker ready
|
||||||
- IPv6 support
|
- IPv6 ready
|
||||||
- Cron job notifications for renewal or error etc.
|
- Cron job notifications for renewal or error etc.
|
||||||
|
|
||||||
It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
|
It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates.
|
||||||
|
|
||||||
Wiki: https://github.com/Neilpang/acme.sh/wiki
|
Wiki: https://github.com/acmesh-official/acme.sh/wiki
|
||||||
|
|
||||||
For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker)
|
For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/acmesh-official/acme.sh/wiki/Run-acme.sh-in-docker)
|
||||||
|
|
||||||
Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
|
Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
|
||||||
|
|
||||||
|
|
||||||
# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
|
# [中文说明](https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
|
||||||
|
|
||||||
# Who:
|
# Who:
|
||||||
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
|
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
|
||||||
- [ruby-china.org](https://ruby-china.org/topics/31983)
|
- [ruby-china.org](https://ruby-china.org/topics/31983)
|
||||||
- [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer))
|
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
|
||||||
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
|
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
|
||||||
- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
|
|
||||||
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
|
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
|
||||||
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
|
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
|
||||||
- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html)
|
- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html)
|
||||||
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
|
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
|
||||||
- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh)
|
|
||||||
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
|
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
|
||||||
- [CentOS Web Panel](http://centos-webpanel.com/)
|
- [CentOS Web Panel](https://control-webpanel.com)
|
||||||
- [lnmp.org](https://lnmp.org/)
|
- [lnmp.org](https://lnmp.org/)
|
||||||
- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials)
|
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
|
||||||
|
|
||||||
# Tested OS
|
# Tested OS
|
||||||
|
|
||||||
| NO | Status| Platform|
|
| NO | Status| Platform|
|
||||||
|----|-------|---------|
|
|----|-------|---------|
|
||||||
|1|[![](https://neilpang.github.io/acmetest/status/ubuntu-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu
|
|1|[![MacOS](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)|Mac OSX
|
||||||
|2|[![](https://neilpang.github.io/acmetest/status/debian-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian
|
|2|[![Windows](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)|Windows (cygwin with curl, openssl and crontab included)
|
||||||
|3|[![](https://neilpang.github.io/acmetest/status/centos-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS
|
|3|[![FreeBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)|FreeBSD
|
||||||
|4|[![](https://neilpang.github.io/acmetest/status/windows-cygwin.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included)
|
|4|[![Solaris](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)|Solaris
|
||||||
|5|[![](https://neilpang.github.io/acmetest/status/freebsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD
|
|5|[![Ubuntu](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)| Ubuntu
|
||||||
|6|[![](https://neilpang.github.io/acmetest/status/pfsense.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense
|
|6|NA|pfsense
|
||||||
|7|[![](https://neilpang.github.io/acmetest/status/opensuse-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE
|
|7|[![OpenBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
|
||||||
|8|[![](https://neilpang.github.io/acmetest/status/alpine-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl)
|
|8|[![NetBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
|
||||||
|9|[![](https://neilpang.github.io/acmetest/status/base-archlinux.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux
|
|9|[![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
|
||||||
|10|[![](https://neilpang.github.io/acmetest/status/fedora-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora
|
|10|[![Omnios](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)|Omnios
|
||||||
|11|[![](https://neilpang.github.io/acmetest/status/kalilinux-kali-linux-docker.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux
|
|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
|
||||||
|12|[![](https://neilpang.github.io/acmetest/status/oraclelinux-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux
|
|12|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
|
||||||
|13|[![](https://neilpang.github.io/acmetest/status/proxmox.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh
|
|13|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
|
||||||
|14|-----| Cloud Linux https://github.com/Neilpang/le/issues/111
|
|14|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
|
||||||
|15|[![](https://neilpang.github.io/acmetest/status/openbsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD
|
|15|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
|
||||||
|16|[![](https://neilpang.github.io/acmetest/status/mageia.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia
|
|16|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
|
||||||
|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT)
|
|17|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
|
||||||
|18|[![](https://neilpang.github.io/acmetest/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris
|
|18|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
|
||||||
|19|[![](https://neilpang.github.io/acmetest/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux
|
|19|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
|
||||||
|20|[![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX
|
|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
|
||||||
|
|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
|
||||||
|
|22|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
|
||||||
|
|23|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
|
||||||
|
|24|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
|
||||||
|
|
||||||
For all build statuses, check our [weekly build project](https://github.com/Neilpang/acmetest):
|
|
||||||
|
|
||||||
https://github.com/Neilpang/acmetest
|
Check our [testing project](https://github.com/acmesh-official/acmetest):
|
||||||
|
|
||||||
|
https://github.com/acmesh-official/acmetest
|
||||||
|
|
||||||
# Supported CA
|
# Supported CA
|
||||||
|
|
||||||
- Letsencrypt.org CA(default)
|
- [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA)(default)
|
||||||
- [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA)
|
- Letsencrypt.org CA
|
||||||
|
- [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA)
|
||||||
|
- [SSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/SSL.com-CA)
|
||||||
|
- [Google.com Public CA](https://github.com/acmesh-official/acme.sh/wiki/Google-Public-CA)
|
||||||
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
|
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
|
||||||
|
- Any other [RFC8555](https://tools.ietf.org/html/rfc8555)-compliant CA
|
||||||
|
|
||||||
# Supported modes
|
# Supported modes
|
||||||
|
|
||||||
@ -85,24 +112,24 @@ https://github.com/Neilpang/acmetest
|
|||||||
- Apache mode
|
- Apache mode
|
||||||
- Nginx mode
|
- Nginx mode
|
||||||
- DNS mode
|
- DNS mode
|
||||||
- [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode)
|
- [DNS alias mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode)
|
||||||
- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
|
- [Stateless mode](https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode)
|
||||||
|
|
||||||
|
|
||||||
# 1. How to install
|
# 1. How to install
|
||||||
|
|
||||||
### 1. Install online
|
### 1. Install online
|
||||||
|
|
||||||
Check this project: https://github.com/Neilpang/get.acme.sh
|
Check this project: https://github.com/acmesh-official/get.acme.sh
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl https://get.acme.sh | sh
|
curl https://get.acme.sh | sh -s email=my@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Or:
|
Or:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget -O - https://get.acme.sh | sh
|
wget -O - https://get.acme.sh | sh -s email=my@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -111,14 +138,14 @@ wget -O - https://get.acme.sh | sh
|
|||||||
Clone this project and launch installation:
|
Clone this project and launch installation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/Neilpang/acme.sh.git
|
git clone https://github.com/acmesh-official/acme.sh.git
|
||||||
cd ./acme.sh
|
cd ./acme.sh
|
||||||
./acme.sh --install
|
./acme.sh --install -m my@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
You `don't have to be root` then, although `it is recommended`.
|
You `don't have to be root` then, although `it is recommended`.
|
||||||
|
|
||||||
Advanced Installation: https://github.com/Neilpang/acme.sh/wiki/How-to-install
|
Advanced Installation: https://github.com/acmesh-official/acme.sh/wiki/How-to-install
|
||||||
|
|
||||||
The installer will perform 3 actions:
|
The installer will perform 3 actions:
|
||||||
|
|
||||||
@ -180,7 +207,7 @@ The certs will be placed in `~/.acme.sh/example.com/`
|
|||||||
|
|
||||||
The certs will be renewed automatically every **60** days.
|
The certs will be renewed automatically every **60** days.
|
||||||
|
|
||||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
|
|
||||||
# 3. Install the cert to Apache/Nginx etc.
|
# 3. Install the cert to Apache/Nginx etc.
|
||||||
@ -226,7 +253,7 @@ Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to
|
|||||||
acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
|
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
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# 5. Use Standalone ssl server to issue cert
|
# 5. Use Standalone ssl server to issue cert
|
||||||
|
|
||||||
@ -238,14 +265,14 @@ Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted t
|
|||||||
acme.sh --issue --alpn -d example.com -d www.example.com -d cp.example.com
|
acme.sh --issue --alpn -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
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
|
|
||||||
# 6. Use Apache mode
|
# 6. Use Apache mode
|
||||||
|
|
||||||
**(requires you to be root/sudoer, since it is required to interact with Apache server)**
|
**(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`.
|
If you are running a web server, it is recommended to use the `Webroot mode`.
|
||||||
|
|
||||||
Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder.
|
Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder.
|
||||||
|
|
||||||
@ -257,15 +284,15 @@ acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
|
|||||||
|
|
||||||
**This apache mode is only to issue the cert, it will not change your apache config files.
|
**This apache mode is only to issue the cert, it will not change your apache config files.
|
||||||
You will need to configure your website config files to use the cert by yourself.
|
You will need to configure your website config files to use the cert by yourself.
|
||||||
We don't want to mess your apache server, don't worry.**
|
We don't want to mess with your apache server, don't worry.**
|
||||||
|
|
||||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# 7. Use Nginx mode
|
# 7. Use Nginx mode
|
||||||
|
|
||||||
**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
|
**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
|
||||||
|
|
||||||
If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
|
If you are running a web server, it is recommended to use the `Webroot mode`.
|
||||||
|
|
||||||
Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
|
Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
|
||||||
|
|
||||||
@ -281,9 +308,9 @@ acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
|
|||||||
|
|
||||||
**This nginx mode is only to issue the cert, it will not change your nginx config files.
|
**This nginx mode is only to issue the cert, it will not change your nginx config files.
|
||||||
You will need to configure your website config files to use the cert by yourself.
|
You will need to configure your website config files to use the cert by yourself.
|
||||||
We don't want to mess your nginx server, don't worry.**
|
We don't want to mess with your nginx server, don't worry.**
|
||||||
|
|
||||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
|
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
|
||||||
|
|
||||||
# 8. Automatic DNS API integration
|
# 8. Automatic DNS API integration
|
||||||
|
|
||||||
@ -293,13 +320,13 @@ You don't have to do anything manually!
|
|||||||
|
|
||||||
### Currently acme.sh supports most of the dns providers:
|
### Currently acme.sh supports most of the dns providers:
|
||||||
|
|
||||||
https://github.com/Neilpang/acme.sh/wiki/dnsapi
|
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
|
||||||
|
|
||||||
# 9. Use DNS manual mode:
|
# 9. Use DNS manual mode:
|
||||||
|
|
||||||
See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first.
|
See: https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode first.
|
||||||
|
|
||||||
If your dns provider doesn't support any api access, you can add the txt record by your hand.
|
If your dns provider doesn't support any api access, you can add the txt record by hand.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
|
acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
|
||||||
@ -333,10 +360,6 @@ Ok, it's done.
|
|||||||
|
|
||||||
# 10. Issue ECC certificates
|
# 10. Issue ECC certificates
|
||||||
|
|
||||||
`Let's Encrypt` can now issue **ECDSA** certificates.
|
|
||||||
|
|
||||||
And we support them too!
|
|
||||||
|
|
||||||
Just set the `keylength` parameter with a prefix `ec-`.
|
Just set the `keylength` parameter with a prefix `ec-`.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@ -357,10 +380,12 @@ Please look at the `keylength` parameter above.
|
|||||||
|
|
||||||
Valid values are:
|
Valid values are:
|
||||||
|
|
||||||
1. **ec-256 (prime256v1, "ECDSA P-256")**
|
1. **ec-256 (prime256v1, "ECDSA P-256", which is the default key type)**
|
||||||
2. **ec-384 (secp384r1, "ECDSA P-384")**
|
2. **ec-384 (secp384r1, "ECDSA P-384")**
|
||||||
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
|
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
|
||||||
|
4. **2048 (RSA2048)**
|
||||||
|
5. **3072 (RSA3072)**
|
||||||
|
6. **4096 (RSA4096)**
|
||||||
|
|
||||||
|
|
||||||
# 11. Issue Wildcard certificates
|
# 11. Issue Wildcard certificates
|
||||||
@ -430,12 +455,12 @@ acme.sh --upgrade --auto-upgrade 0
|
|||||||
|
|
||||||
# 15. Issue a cert from an existing CSR
|
# 15. Issue a cert from an existing CSR
|
||||||
|
|
||||||
https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
|
https://github.com/acmesh-official/acme.sh/wiki/Issue-a-cert-from-existing-CSR
|
||||||
|
|
||||||
|
|
||||||
# 16. Send notifications in cronjob
|
# 16. Send notifications in cronjob
|
||||||
|
|
||||||
https://github.com/Neilpang/acme.sh/wiki/notify
|
https://github.com/acmesh-official/acme.sh/wiki/notify
|
||||||
|
|
||||||
|
|
||||||
# 17. Under the Hood
|
# 17. Under the Hood
|
||||||
@ -455,8 +480,8 @@ TODO:
|
|||||||
|
|
||||||
### Code Contributors
|
### Code Contributors
|
||||||
|
|
||||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
This project exists thanks to all the people who contribute.
|
||||||
<a href="https://github.com/Neilpang/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a>
|
<a href="https://github.com/acmesh-official/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a>
|
||||||
|
|
||||||
### Financial Contributors
|
### Financial Contributors
|
||||||
|
|
||||||
@ -481,13 +506,15 @@ Support this project with your organization. Your logo will show up here with a
|
|||||||
<a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a>
|
<a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a>
|
||||||
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
|
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 19. License & Others
|
# 19. License & Others
|
||||||
|
|
||||||
License is GPLv3
|
License is GPLv3
|
||||||
|
|
||||||
Please Star and Fork me.
|
Please Star and Fork me.
|
||||||
|
|
||||||
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome.
|
[Issues](https://github.com/acmesh-official/acme.sh/issues) and [pull requests](https://github.com/acmesh-official/acme.sh/pulls) are welcome.
|
||||||
|
|
||||||
|
|
||||||
# 20. Donate
|
# 20. Donate
|
||||||
@ -495,4 +522,4 @@ Your donation makes **acme.sh** better:
|
|||||||
|
|
||||||
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
|
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
|
||||||
|
|
||||||
[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list)
|
[Donate List](https://github.com/acmesh-official/acme.sh/wiki/Donate-list)
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
deploy hook usage:
|
deploy hook usage:
|
||||||
|
|
||||||
https://github.com/Neilpang/acme.sh/wiki/deployhooks
|
https://github.com/acmesh-official/acme.sh/wiki/deployhooks
|
||||||
|
|
||||||
|
88
deploy/ali_cdn.sh
Normal file
88
deploy/ali_cdn.sh
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034,SC2154
|
||||||
|
|
||||||
|
# Script to create certificate to Alibaba Cloud CDN
|
||||||
|
#
|
||||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
|
||||||
|
#
|
||||||
|
# This deployment required following variables
|
||||||
|
# export Ali_Key="ALIACCESSKEY"
|
||||||
|
# export Ali_Secret="ALISECRETKEY"
|
||||||
|
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
|
||||||
|
#
|
||||||
|
# To specify the CDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
|
||||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn.example.com"
|
||||||
|
# If you have multiple CDN domains using the same certificate, just
|
||||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
|
||||||
|
#
|
||||||
|
# For DCDN, see ali_dcdn deploy hook
|
||||||
|
|
||||||
|
Ali_CDN_API="https://cdn.aliyuncs.com/"
|
||||||
|
|
||||||
|
ali_cdn_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"
|
||||||
|
|
||||||
|
# Load dnsapi/dns_ali.sh to reduce the duplicated codes
|
||||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
|
||||||
|
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
if ! . "$dnsapi_ali"; then
|
||||||
|
_err "Error loading file $dnsapi_ali. Please check your API file and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_prepare_ali_credentials || return 1
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_ALI_CDN_DOMAIN
|
||||||
|
if [ "$DEPLOY_ALI_CDN_DOMAIN" ]; then
|
||||||
|
_savedeployconf DEPLOY_ALI_CDN_DOMAIN "$DEPLOY_ALI_CDN_DOMAIN"
|
||||||
|
else
|
||||||
|
DEPLOY_ALI_CDN_DOMAIN="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# read cert and key files and urlencode both
|
||||||
|
_cert=$(_url_encode upper-hex <"$_cfullchain")
|
||||||
|
_key=$(_url_encode upper-hex <"$_ckey")
|
||||||
|
|
||||||
|
_debug2 _cert "$_cert"
|
||||||
|
_debug2 _key "$_key"
|
||||||
|
|
||||||
|
## update domain ssl config
|
||||||
|
for domain in $DEPLOY_ALI_CDN_DOMAIN; do
|
||||||
|
_set_cdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
|
||||||
|
if _ali_rest "Set CDN domain SSL certificate for $domain" "" POST; then
|
||||||
|
_info "Domain $domain certificate has been deployed successfully"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# domain pub pri
|
||||||
|
_set_cdn_domain_ssl_certificate_query() {
|
||||||
|
endpoint=$Ali_CDN_API
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=SetCdnDomainSSLCertificate'
|
||||||
|
query=$query'&CertType=upload'
|
||||||
|
query=$query'&DomainName='$1
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&SSLPri='$3
|
||||||
|
query=$query'&SSLProtocol=on'
|
||||||
|
query=$query'&SSLPub='$2
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_timestamp)
|
||||||
|
query=$query'&Version=2018-05-10'
|
||||||
|
}
|
88
deploy/ali_dcdn.sh
Normal file
88
deploy/ali_dcdn.sh
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034,SC2154
|
||||||
|
|
||||||
|
# Script to create certificate to Alibaba Cloud DCDN
|
||||||
|
#
|
||||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
|
||||||
|
#
|
||||||
|
# This deployment required following variables
|
||||||
|
# export Ali_Key="ALIACCESSKEY"
|
||||||
|
# export Ali_Secret="ALISECRETKEY"
|
||||||
|
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
|
||||||
|
#
|
||||||
|
# To specify the DCDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
|
||||||
|
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn.example.com"
|
||||||
|
# If you have multiple CDN domains using the same certificate, just
|
||||||
|
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn1.example.com dcdn2.example.com"
|
||||||
|
#
|
||||||
|
# For regular CDN, see ali_cdn deploy hook
|
||||||
|
|
||||||
|
Ali_DCDN_API="https://dcdn.aliyuncs.com/"
|
||||||
|
|
||||||
|
ali_dcdn_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"
|
||||||
|
|
||||||
|
# Load dnsapi/dns_ali.sh to reduce the duplicated codes
|
||||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
|
||||||
|
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
if ! . "$dnsapi_ali"; then
|
||||||
|
_err "Error loading file $dnsapi_ali. Please check your API file and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_prepare_ali_credentials || return 1
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_ALI_DCDN_DOMAIN
|
||||||
|
if [ "$DEPLOY_ALI_DCDN_DOMAIN" ]; then
|
||||||
|
_savedeployconf DEPLOY_ALI_DCDN_DOMAIN "$DEPLOY_ALI_DCDN_DOMAIN"
|
||||||
|
else
|
||||||
|
DEPLOY_ALI_DCDN_DOMAIN="$_cdomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# read cert and key files and urlencode both
|
||||||
|
_cert=$(_url_encode upper-hex <"$_cfullchain")
|
||||||
|
_key=$(_url_encode upper-hex <"$_ckey")
|
||||||
|
|
||||||
|
_debug2 _cert "$_cert"
|
||||||
|
_debug2 _key "$_key"
|
||||||
|
|
||||||
|
## update domain ssl config
|
||||||
|
for domain in $DEPLOY_ALI_DCDN_DOMAIN; do
|
||||||
|
_set_dcdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
|
||||||
|
if _ali_rest "Set DCDN domain SSL certificate for $domain" "" POST; then
|
||||||
|
_info "Domain $domain certificate has been deployed successfully"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# domain pub pri
|
||||||
|
_set_dcdn_domain_ssl_certificate_query() {
|
||||||
|
endpoint=$Ali_DCDN_API
|
||||||
|
query=''
|
||||||
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
|
query=$query'&Action=SetDcdnDomainSSLCertificate'
|
||||||
|
query=$query'&CertType=upload'
|
||||||
|
query=$query'&DomainName='$1
|
||||||
|
query=$query'&Format=json'
|
||||||
|
query=$query'&SSLPri='$3
|
||||||
|
query=$query'&SSLProtocol=on'
|
||||||
|
query=$query'&SSLPub='$2
|
||||||
|
query=$query'&SignatureMethod=HMAC-SHA1'
|
||||||
|
query=$query"&SignatureNonce=$(_ali_nonce)"
|
||||||
|
query=$query'&SignatureVersion=1.0'
|
||||||
|
query=$query'&Timestamp='$(_timestamp)
|
||||||
|
query=$query'&Version=2018-01-15'
|
||||||
|
}
|
92
deploy/cleverreach.sh
Normal file
92
deploy/cleverreach.sh
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# Here is the script to deploy the cert to your CleverReach Account using the CleverReach REST API.
|
||||||
|
# Your OAuth needs the right scope, please contact CleverReach support for that.
|
||||||
|
#
|
||||||
|
# Written by Jan-Philipp Benecke <github@bnck.me>
|
||||||
|
# Public domain, 2020
|
||||||
|
#
|
||||||
|
# Following environment variables must be set:
|
||||||
|
#
|
||||||
|
#export DEPLOY_CLEVERREACH_CLIENT_ID=myid
|
||||||
|
#export DEPLOY_CLEVERREACH_CLIENT_SECRET=mysecret
|
||||||
|
|
||||||
|
cleverreach_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_rest_endpoint="https://rest.cleverreach.com"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_CLEVERREACH_CLIENT_ID
|
||||||
|
_getdeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET
|
||||||
|
_getdeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID
|
||||||
|
|
||||||
|
if [ -z "${DEPLOY_CLEVERREACH_CLIENT_ID}" ]; then
|
||||||
|
_err "CleverReach Client ID is not found, please define DEPLOY_CLEVERREACH_CLIENT_ID."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "${DEPLOY_CLEVERREACH_CLIENT_SECRET}" ]; then
|
||||||
|
_err "CleverReach client secret is not found, please define DEPLOY_CLEVERREACH_CLIENT_SECRET."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_CLEVERREACH_CLIENT_ID "${DEPLOY_CLEVERREACH_CLIENT_ID}"
|
||||||
|
_savedeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET "${DEPLOY_CLEVERREACH_CLIENT_SECRET}"
|
||||||
|
_savedeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
|
||||||
|
|
||||||
|
_info "Obtaining a CleverReach access token"
|
||||||
|
|
||||||
|
_data="{\"grant_type\": \"client_credentials\", \"client_id\": \"${DEPLOY_CLEVERREACH_CLIENT_ID}\", \"client_secret\": \"${DEPLOY_CLEVERREACH_CLIENT_SECRET}\"}"
|
||||||
|
_auth_result="$(_post "$_data" "$_rest_endpoint/oauth/token.php" "" "POST" "application/json")"
|
||||||
|
|
||||||
|
_debug _data "$_data"
|
||||||
|
_debug _auth_result "$_auth_result"
|
||||||
|
|
||||||
|
_regex=".*\"access_token\":\"\([-._0-9A-Za-z]*\)\".*$"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_access_token=$(echo "$_auth_result" | _json_decode | sed -n "s/$_regex/\1/p")
|
||||||
|
|
||||||
|
_debug _subclient "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
|
||||||
|
|
||||||
|
if [ -n "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
|
||||||
|
_info "Obtaining token for sub-client ${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
|
||||||
|
export _H1="Authorization: Bearer ${_access_token}"
|
||||||
|
_subclient_token_result="$(_get "$_rest_endpoint/v3/clients/$DEPLOY_CLEVERREACH_SUBCLIENT_ID/token")"
|
||||||
|
_access_token=$(echo "$_subclient_token_result" | sed -n "s/\"//p")
|
||||||
|
|
||||||
|
_debug _subclient_token_result "$_access_token"
|
||||||
|
|
||||||
|
_info "Destroying parent token at CleverReach, as it not needed anymore"
|
||||||
|
_destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
|
||||||
|
_debug _destroy_result "$_destroy_result"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Uploading certificate and key to CleverReach"
|
||||||
|
|
||||||
|
_certData="{\"cert\":\"$(_json_encode <"$_cfullchain")\", \"key\":\"$(_json_encode <"$_ckey")\"}"
|
||||||
|
export _H1="Authorization: Bearer ${_access_token}"
|
||||||
|
_add_cert_result="$(_post "$_certData" "$_rest_endpoint/v3/ssl" "" "POST" "application/json")"
|
||||||
|
|
||||||
|
if [ -z "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
|
||||||
|
_info "Destroying token at CleverReach, as it not needed anymore"
|
||||||
|
_destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
|
||||||
|
_debug _destroy_result "$_destroy_result"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! echo "$_add_cert_result" | grep '"error":' >/dev/null; then
|
||||||
|
_info "Uploaded certificate successfully"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_debug _add_cert_result "$_add_cert_result"
|
||||||
|
_err "Unable to update certificate"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
98
deploy/consul.sh
Normal file
98
deploy/consul.sh
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to hashicorp consul using curl
|
||||||
|
# (https://www.consul.io/)
|
||||||
|
#
|
||||||
|
# it requires following environment variables:
|
||||||
|
#
|
||||||
|
# CONSUL_PREFIX - this contains the prefix path in consul
|
||||||
|
# CONSUL_HTTP_ADDR - consul requires this to find your consul server
|
||||||
|
#
|
||||||
|
# additionally, you need to ensure that CONSUL_HTTP_TOKEN is available
|
||||||
|
# to access the consul server
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
consul_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"
|
||||||
|
|
||||||
|
# validate required env vars
|
||||||
|
_getdeployconf CONSUL_PREFIX
|
||||||
|
if [ -z "$CONSUL_PREFIX" ]; then
|
||||||
|
_err "CONSUL_PREFIX needs to be defined (contains prefix path in vault)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedeployconf CONSUL_PREFIX "$CONSUL_PREFIX"
|
||||||
|
|
||||||
|
_getdeployconf CONSUL_HTTP_ADDR
|
||||||
|
if [ -z "$CONSUL_HTTP_ADDR" ]; then
|
||||||
|
_err "CONSUL_HTTP_ADDR needs to be defined (contains consul connection address)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedeployconf CONSUL_HTTP_ADDR "$CONSUL_HTTP_ADDR"
|
||||||
|
|
||||||
|
CONSUL_CMD=$(command -v consul)
|
||||||
|
|
||||||
|
# force CLI, but the binary does not exist => error
|
||||||
|
if [ -n "$USE_CLI" ] && [ -z "$CONSUL_CMD" ]; then
|
||||||
|
_err "Cannot find the consul binary!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# use the CLI first
|
||||||
|
if [ -n "$USE_CLI" ] || [ -n "$CONSUL_CMD" ]; then
|
||||||
|
_info "Found consul binary, deploying with CLI"
|
||||||
|
consul_deploy_cli "$CONSUL_CMD" "$CONSUL_PREFIX"
|
||||||
|
else
|
||||||
|
_info "Did not find consul binary, deploying with API"
|
||||||
|
consul_deploy_api "$CONSUL_HTTP_ADDR" "$CONSUL_PREFIX" "$CONSUL_HTTP_TOKEN"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
consul_deploy_api() {
|
||||||
|
CONSUL_HTTP_ADDR="$1"
|
||||||
|
CONSUL_PREFIX="$2"
|
||||||
|
CONSUL_HTTP_TOKEN="$3"
|
||||||
|
|
||||||
|
URL="$CONSUL_HTTP_ADDR/v1/kv/$CONSUL_PREFIX"
|
||||||
|
export _H1="X-Consul-Token: $CONSUL_HTTP_TOKEN"
|
||||||
|
|
||||||
|
if [ -n "$FABIO" ]; then
|
||||||
|
_post "$(cat "$_cfullchain")" "$URL/${_cdomain}-cert.pem" '' "PUT" || return 1
|
||||||
|
_post "$(cat "$_ckey")" "$URL/${_cdomain}-key.pem" '' "PUT" || return 1
|
||||||
|
else
|
||||||
|
_post "$(cat "$_ccert")" "$URL/${_cdomain}/cert.pem" '' "PUT" || return 1
|
||||||
|
_post "$(cat "$_ckey")" "$URL/${_cdomain}/cert.key" '' "PUT" || return 1
|
||||||
|
_post "$(cat "$_cca")" "$URL/${_cdomain}/chain.pem" '' "PUT" || return 1
|
||||||
|
_post "$(cat "$_cfullchain")" "$URL/${_cdomain}/fullchain.pem" '' "PUT" || return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
consul_deploy_cli() {
|
||||||
|
CONSUL_CMD="$1"
|
||||||
|
CONSUL_PREFIX="$2"
|
||||||
|
|
||||||
|
if [ -n "$FABIO" ]; then
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-cert.pem" @"$_cfullchain" || return 1
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-key.pem" @"$_ckey" || return 1
|
||||||
|
else
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
|
||||||
|
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
|
||||||
|
fi
|
||||||
|
}
|
@ -3,18 +3,29 @@
|
|||||||
# Uses command line uapi. --user option is needed only if run as root.
|
# Uses command line uapi. --user option is needed only if run as root.
|
||||||
# Returns 0 when success.
|
# Returns 0 when success.
|
||||||
#
|
#
|
||||||
|
# Configure DEPLOY_CPANEL_AUTO_<...> options to enable or restrict automatic
|
||||||
|
# detection of deployment targets through UAPI (if not set, defaults below are used.)
|
||||||
|
# - ENABLED : 'true' for multi-site / wildcard capability; otherwise single-site mode.
|
||||||
|
# - NOMATCH : 'true' to allow deployment to sites that do not match the certificate.
|
||||||
|
# - INCLUDE : Comma-separated list - sites must match this field.
|
||||||
|
# - EXCLUDE : Comma-separated list - sites must NOT match this field.
|
||||||
|
# INCLUDE/EXCLUDE both support non-lexical, glob-style matches using '*'
|
||||||
|
#
|
||||||
# Please note that I am no longer using Github. If you want to report an issue
|
# Please note that I am no longer using Github. If you want to report an issue
|
||||||
# or contact me, visit https://forum.webseodesigners.com/web-design-seo-and-hosting-f16/
|
# or contact me, visit https://forum.webseodesigners.com/web-design-seo-and-hosting-f16/
|
||||||
#
|
#
|
||||||
# Written by Santeri Kannisto <santeri.kannisto@webseodesigners.com>
|
# Written by Santeri Kannisto <santeri.kannisto@webseodesigners.com>
|
||||||
# Public domain, 2017-2018
|
# Public domain, 2017-2018
|
||||||
|
#
|
||||||
#export DEPLOY_CPANEL_USER=myusername
|
# export DEPLOY_CPANEL_USER=myusername
|
||||||
|
# export DEPLOY_CPANEL_AUTO_ENABLED='true'
|
||||||
|
# export DEPLOY_CPANEL_AUTO_NOMATCH='false'
|
||||||
|
# export DEPLOY_CPANEL_AUTO_INCLUDE='*'
|
||||||
|
# export DEPLOY_CPANEL_AUTO_EXCLUDE=''
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
#domain keyfile certfile cafile fullchain
|
#domain keyfile certfile cafile fullchain
|
||||||
|
|
||||||
cpanel_uapi_deploy() {
|
cpanel_uapi_deploy() {
|
||||||
_cdomain="$1"
|
_cdomain="$1"
|
||||||
_ckey="$2"
|
_ckey="$2"
|
||||||
@ -22,6 +33,9 @@ cpanel_uapi_deploy() {
|
|||||||
_cca="$4"
|
_cca="$4"
|
||||||
_cfullchain="$5"
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
# re-declare vars inherited from acme.sh but not passed to make ShellCheck happy
|
||||||
|
: "${Le_Alt:=""}"
|
||||||
|
|
||||||
_debug _cdomain "$_cdomain"
|
_debug _cdomain "$_cdomain"
|
||||||
_debug _ckey "$_ckey"
|
_debug _ckey "$_ckey"
|
||||||
_debug _ccert "$_ccert"
|
_debug _ccert "$_ccert"
|
||||||
@ -32,25 +46,120 @@ cpanel_uapi_deploy() {
|
|||||||
_err "The command uapi is not found."
|
_err "The command uapi is not found."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# declare useful constants
|
||||||
|
uapi_error_response='status: 0'
|
||||||
|
|
||||||
# read cert and key files and urlencode both
|
# read cert and key files and urlencode both
|
||||||
_cert=$(_url_encode <"$_ccert")
|
_cert=$(_url_encode <"$_ccert")
|
||||||
_key=$(_url_encode <"$_ckey")
|
_key=$(_url_encode <"$_ckey")
|
||||||
|
|
||||||
_debug _cert "$_cert"
|
_debug2 _cert "$_cert"
|
||||||
_debug _key "$_key"
|
_debug2 _key "$_key"
|
||||||
|
|
||||||
if [ "$(id -u)" = 0 ]; then
|
if [ "$(id -u)" = 0 ]; then
|
||||||
if [ -z "$DEPLOY_CPANEL_USER" ]; then
|
_getdeployconf DEPLOY_CPANEL_USER
|
||||||
|
# fallback to _readdomainconf for old installs
|
||||||
|
if [ -z "${DEPLOY_CPANEL_USER:=$(_readdomainconf DEPLOY_CPANEL_USER)}" ]; then
|
||||||
_err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
|
_err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
|
_debug DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
|
||||||
_response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
|
_savedeployconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
|
||||||
|
|
||||||
|
_uapi_user="$DEPLOY_CPANEL_USER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load all AUTO envars and set defaults - see above for usage
|
||||||
|
__cpanel_initautoparam ENABLED 'true'
|
||||||
|
__cpanel_initautoparam NOMATCH 'false'
|
||||||
|
__cpanel_initautoparam INCLUDE '*'
|
||||||
|
__cpanel_initautoparam EXCLUDE ''
|
||||||
|
|
||||||
|
# Auto mode
|
||||||
|
if [ "$DEPLOY_CPANEL_AUTO_ENABLED" = "true" ]; then
|
||||||
|
# call API for site config
|
||||||
|
_response=$(uapi DomainInfo list_domains)
|
||||||
|
# exit if error in response
|
||||||
|
if [ -z "$_response" ] || [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
|
||||||
|
_err "Error in deploying certificate - cannot retrieve sitelist:"
|
||||||
|
_err "\n$_response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# parse response to create site list
|
||||||
|
sitelist=$(__cpanel_parse_response "$_response")
|
||||||
|
_debug "UAPI sites found: $sitelist"
|
||||||
|
|
||||||
|
# filter sitelist using configured domains
|
||||||
|
# skip if NOMATCH is "true"
|
||||||
|
if [ "$DEPLOY_CPANEL_AUTO_NOMATCH" = "true" ]; then
|
||||||
|
_debug "DEPLOY_CPANEL_AUTO_NOMATCH is true"
|
||||||
|
_info "UAPI nomatch mode is enabled - Will not validate sites are valid for the certificate"
|
||||||
|
else
|
||||||
|
_debug "DEPLOY_CPANEL_AUTO_NOMATCH is false"
|
||||||
|
d="$(echo "${Le_Alt}," | sed -e "s/^$_cdomain,//" -e "s/,$_cdomain,/,/")"
|
||||||
|
d="$(echo "$_cdomain,$d" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\[\^\.\]\*/g')"
|
||||||
|
sitelist="$(echo "$sitelist" | grep -ix "$d")"
|
||||||
|
_debug2 "Matched UAPI sites: $sitelist"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# filter sites that do not match $DEPLOY_CPANEL_AUTO_INCLUDE
|
||||||
|
_info "Applying sitelist filter DEPLOY_CPANEL_AUTO_INCLUDE: $DEPLOY_CPANEL_AUTO_INCLUDE"
|
||||||
|
sitelist="$(echo "$sitelist" | grep -ix "$(echo "$DEPLOY_CPANEL_AUTO_INCLUDE" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\.\*/g')")"
|
||||||
|
_debug2 "Remaining sites: $sitelist"
|
||||||
|
|
||||||
|
# filter sites that match $DEPLOY_CPANEL_AUTO_EXCLUDE
|
||||||
|
_info "Applying sitelist filter DEPLOY_CPANEL_AUTO_EXCLUDE: $DEPLOY_CPANEL_AUTO_EXCLUDE"
|
||||||
|
sitelist="$(echo "$sitelist" | grep -vix "$(echo "$DEPLOY_CPANEL_AUTO_EXCLUDE" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\.\*/g')")"
|
||||||
|
_debug2 "Remaining sites: $sitelist"
|
||||||
|
|
||||||
|
# counter for success / failure check
|
||||||
|
successes=0
|
||||||
|
if [ -n "$sitelist" ]; then
|
||||||
|
sitetotal="$(echo "$sitelist" | wc -l)"
|
||||||
|
_debug "$sitetotal sites to deploy"
|
||||||
|
else
|
||||||
|
sitetotal=0
|
||||||
|
_debug "No sites to deploy"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# for each site: call uapi to publish cert and log result. Only return failure if all fail
|
||||||
|
for site in $sitelist; do
|
||||||
|
# call uapi to publish cert, check response for errors and log them.
|
||||||
|
if [ -n "$_uapi_user" ]; then
|
||||||
|
_response=$(uapi --user="$_uapi_user" SSL install_ssl domain="$site" cert="$_cert" key="$_key")
|
||||||
|
else
|
||||||
|
_response=$(uapi SSL install_ssl domain="$site" cert="$_cert" key="$_key")
|
||||||
|
fi
|
||||||
|
if [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
|
||||||
|
_err "Error in deploying certificate to $site:"
|
||||||
|
_err "$_response"
|
||||||
|
else
|
||||||
|
successes=$((successes + 1))
|
||||||
|
_debug "$_response"
|
||||||
|
_info "Succcessfully deployed to $site"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Raise error if all updates fail
|
||||||
|
if [ "$sitetotal" -gt 0 ] && [ "$successes" -eq 0 ]; then
|
||||||
|
_err "Could not deploy to any of $sitetotal sites via UAPI"
|
||||||
|
_debug "successes: $successes, sitetotal: $sitetotal"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Successfully deployed certificate to $successes of $sitetotal sites via UAPI"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# "classic" mode - will only try to deploy to the primary domain; will not check UAPI first
|
||||||
|
if [ -n "$_uapi_user" ]; then
|
||||||
|
_response=$(uapi --user="$_uapi_user" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
|
||||||
else
|
else
|
||||||
_response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
|
_response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
|
||||||
fi
|
fi
|
||||||
error_response="status: 0"
|
|
||||||
if test "${_response#*$error_response}" != "$_response"; then
|
if [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
|
||||||
_err "Error in deploying certificate:"
|
_err "Error in deploying certificate:"
|
||||||
_err "$_response"
|
_err "$_response"
|
||||||
return 1
|
return 1
|
||||||
@ -59,4 +168,44 @@ cpanel_uapi_deploy() {
|
|||||||
_debug response "$_response"
|
_debug response "$_response"
|
||||||
_info "Certificate successfully deployed"
|
_info "Certificate successfully deployed"
|
||||||
return 0
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
######## Private functions #####################
|
||||||
|
|
||||||
|
# Internal utility to process YML from UAPI - looks at main_domain, sub_domains, addon domains and parked domains
|
||||||
|
#[response]
|
||||||
|
__cpanel_parse_response() {
|
||||||
|
if [ $# -gt 0 ]; then resp="$*"; else resp="$(cat)"; fi
|
||||||
|
|
||||||
|
echo "$resp" |
|
||||||
|
sed -En \
|
||||||
|
-e 's/\r$//' \
|
||||||
|
-e 's/^( *)([_.[:alnum:]]+) *: *(.*)/\1,\2,\3/p' \
|
||||||
|
-e 's/^( *)- (.*)/\1,-,\2/p' |
|
||||||
|
awk -F, '{
|
||||||
|
level = length($1)/2;
|
||||||
|
section[level] = $2;
|
||||||
|
for (i in section) {if (i > level) {delete section[i]}}
|
||||||
|
if (length($3) > 0) {
|
||||||
|
prefix="";
|
||||||
|
for (i=0; i < level; i++)
|
||||||
|
{ prefix = (prefix)(section[i])("/") }
|
||||||
|
printf("%s%s=%s\n", prefix, $2, $3);
|
||||||
|
}
|
||||||
|
}' |
|
||||||
|
sed -En -e 's/^result\/data\/(main_domain|sub_domains\/-|addon_domains\/-|parked_domains\/-)=(.*)$/\2/p'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load parameter by prefix+name - fallback to default if not set, and save to config
|
||||||
|
#pname pdefault
|
||||||
|
__cpanel_initautoparam() {
|
||||||
|
pname="$1"
|
||||||
|
pdefault="$2"
|
||||||
|
pkey="DEPLOY_CPANEL_AUTO_$pname"
|
||||||
|
|
||||||
|
_getdeployconf "$pkey"
|
||||||
|
[ -n "$(eval echo "\"\$$pkey\"")" ] || eval "$pkey=\"$pdefault\""
|
||||||
|
_debug2 "$pkey" "$(eval echo "\"\$$pkey\"")"
|
||||||
|
_savedeployconf "$pkey" "$(eval echo "\"\$$pkey\"")"
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/path/to/fullchain.pem"
|
#DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/path/to/fullchain.pem"
|
||||||
#DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
|
#DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
|
||||||
|
|
||||||
_DEPLOY_DOCKER_WIKI="https://github.com/Neilpang/acme.sh/wiki/deploy-to-docker-containers"
|
_DEPLOY_DOCKER_WIKI="https://github.com/acmesh-official/acme.sh/wiki/deploy-to-docker-containers"
|
||||||
|
|
||||||
_DOCKER_HOST_DEFAULT="/var/run/docker.sock"
|
_DOCKER_HOST_DEFAULT="/var/run/docker.sock"
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ docker_deploy() {
|
|||||||
_getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD
|
_getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD
|
||||||
_debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
_debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
||||||
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
|
if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
|
||||||
_savedeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
|
_savedeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" "base64"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_cid="$(_get_id "$DEPLOY_DOCKER_CONTAINER_LABEL")"
|
_cid="$(_get_id "$DEPLOY_DOCKER_CONTAINER_LABEL")"
|
||||||
@ -273,15 +273,27 @@ _check_curl_version() {
|
|||||||
_minor="$(_getfield "$_cversion" 2 '.')"
|
_minor="$(_getfield "$_cversion" 2 '.')"
|
||||||
_debug2 "_minor" "$_minor"
|
_debug2 "_minor" "$_minor"
|
||||||
|
|
||||||
if [ "$_major$_minor" -lt "740" ]; then
|
if [ "$_major" -ge "8" ]; then
|
||||||
|
#ok
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ "$_major" = "7" ]; then
|
||||||
|
if [ "$_minor" -lt "40" ]; then
|
||||||
_err "curl v$_cversion doesn't support unit socket"
|
_err "curl v$_cversion doesn't support unit socket"
|
||||||
|
_err "Please upgrade to curl 7.40 or later."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [ "$_major$_minor" -lt "750" ]; then
|
if [ "$_minor" -lt "50" ]; then
|
||||||
_debug "Use short host name"
|
_debug "Use short host name"
|
||||||
export _CURL_NO_HOST=1
|
export _CURL_NO_HOST=1
|
||||||
else
|
else
|
||||||
export _CURL_NO_HOST=
|
export _CURL_NO_HOST=
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
|
else
|
||||||
|
_err "curl v$_cversion doesn't support unit socket"
|
||||||
|
_err "Please upgrade to curl 7.40 or later."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,8 @@ exim4_deploy() {
|
|||||||
cp "$_exim4_conf" "$_backup_conf"
|
cp "$_exim4_conf" "$_backup_conf"
|
||||||
|
|
||||||
_info "Modify exim4 conf: $_exim4_conf"
|
_info "Modify exim4 conf: $_exim4_conf"
|
||||||
if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" \
|
if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" &&
|
||||||
&& _setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then
|
_setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then
|
||||||
_info "Set config success!"
|
_info "Set config success!"
|
||||||
else
|
else
|
||||||
_err "Config exim4 server error, please report bug to us."
|
_err "Config exim4 server error, please report bug to us."
|
||||||
@ -109,6 +109,5 @@ exim4_deploy() {
|
|||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,47 +28,59 @@ fritzbox_deploy() {
|
|||||||
_debug _cfullchain "$_cfullchain"
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
if ! _exists iconv; then
|
if ! _exists iconv; then
|
||||||
|
if ! _exists uconv; then
|
||||||
if ! _exists perl; then
|
if ! _exists perl; then
|
||||||
_err "iconv or perl not found"
|
_err "iconv or uconv or perl not found"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
_fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}"
|
# Clear traces of incorrectly stored values
|
||||||
_fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}"
|
_clearaccountconf DEPLOY_FRITZBOX_USERNAME
|
||||||
_fritzbox_url="${DEPLOY_FRITZBOX_URL}"
|
_clearaccountconf DEPLOY_FRITZBOX_PASSWORD
|
||||||
|
_clearaccountconf DEPLOY_FRITZBOX_URL
|
||||||
|
|
||||||
_debug _fritzbox_url "$_fritzbox_url"
|
# Read config from saved values or env
|
||||||
_debug _fritzbox_username "$_fritzbox_username"
|
_getdeployconf DEPLOY_FRITZBOX_USERNAME
|
||||||
_secure_debug _fritzbox_password "$_fritzbox_password"
|
_getdeployconf DEPLOY_FRITZBOX_PASSWORD
|
||||||
if [ -z "$_fritzbox_username" ]; then
|
_getdeployconf DEPLOY_FRITZBOX_URL
|
||||||
|
|
||||||
|
_debug DEPLOY_FRITZBOX_URL "$DEPLOY_FRITZBOX_URL"
|
||||||
|
_debug DEPLOY_FRITZBOX_USERNAME "$DEPLOY_FRITZBOX_USERNAME"
|
||||||
|
_secure_debug DEPLOY_FRITZBOX_PASSWORD "$DEPLOY_FRITZBOX_PASSWORD"
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_FRITZBOX_USERNAME" ]; then
|
||||||
_err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME."
|
_err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [ -z "$_fritzbox_password" ]; then
|
if [ -z "$DEPLOY_FRITZBOX_PASSWORD" ]; then
|
||||||
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
|
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [ -z "$_fritzbox_url" ]; then
|
if [ -z "$DEPLOY_FRITZBOX_URL" ]; then
|
||||||
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
|
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}"
|
# Save current values
|
||||||
_saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}"
|
_savedeployconf DEPLOY_FRITZBOX_USERNAME "$DEPLOY_FRITZBOX_USERNAME"
|
||||||
_saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}"
|
_savedeployconf DEPLOY_FRITZBOX_PASSWORD "$DEPLOY_FRITZBOX_PASSWORD"
|
||||||
|
_savedeployconf DEPLOY_FRITZBOX_URL "$DEPLOY_FRITZBOX_URL"
|
||||||
|
|
||||||
# Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate
|
# Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate
|
||||||
export HTTPS_INSECURE=1
|
export HTTPS_INSECURE=1
|
||||||
|
|
||||||
_info "Log in to the FRITZ!Box"
|
_info "Log in to the FRITZ!Box"
|
||||||
_fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
|
_fritzbox_challenge="$(_get "${DEPLOY_FRITZBOX_URL}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
|
||||||
if _exists iconv; then
|
if _exists iconv; then
|
||||||
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')"
|
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | iconv -f ASCII -t UTF16LE | _digest md5 hex)"
|
||||||
|
elif _exists uconv; then
|
||||||
|
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | uconv -f ASCII -t UTF16LE | _digest md5 hex)"
|
||||||
else
|
else
|
||||||
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | md5sum | awk '{print $1}')"
|
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | _digest md5 hex)"
|
||||||
fi
|
fi
|
||||||
_fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
|
_fritzbox_sid="$(_get "${DEPLOY_FRITZBOX_URL}/login_sid.lua?sid=0000000000000000&username=${DEPLOY_FRITZBOX_USERNAME}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
|
||||||
|
|
||||||
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
|
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
|
||||||
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
|
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
|
||||||
@ -100,7 +112,7 @@ fritzbox_deploy() {
|
|||||||
_info "Upload certificate to the FRITZ!Box"
|
_info "Upload certificate to the FRITZ!Box"
|
||||||
|
|
||||||
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
|
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
|
||||||
_post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL
|
_post "$(cat "${_post_request}")" "${DEPLOY_FRITZBOX_URL}/cgi-bin/firmwarecfg" | grep SSL
|
||||||
|
|
||||||
retval=$?
|
retval=$?
|
||||||
if [ $retval = 0 ]; then
|
if [ $retval = 0 ]; then
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
# Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/).
|
# Here is the script to deploy the cert to G-Core CDN service (https://gcore.com/) using the G-Core Labs API (https://apidocs.gcore.com/cdn).
|
||||||
# Returns 0 when success.
|
# Returns 0 when success.
|
||||||
#
|
#
|
||||||
# Written by temoffey <temofffey@gmail.com>
|
# Written by temoffey <temofffey@gmail.com>
|
||||||
# Public domain, 2019
|
# Public domain, 2019
|
||||||
|
# Update by DreamOfIce <admin@dreamofice.cn> in 2023
|
||||||
|
|
||||||
#export DEPLOY_GCORE_CDN_USERNAME=myusername
|
#export DEPLOY_GCORE_CDN_USERNAME=myusername
|
||||||
#export DEPLOY_GCORE_CDN_PASSWORD=mypassword
|
#export DEPLOY_GCORE_CDN_PASSWORD=mypassword
|
||||||
@ -56,9 +57,9 @@ gcore_cdn_deploy() {
|
|||||||
_request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}"
|
_request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}"
|
||||||
_debug _request "$_request"
|
_debug _request "$_request"
|
||||||
export _H1="Content-Type:application/json"
|
export _H1="Content-Type:application/json"
|
||||||
_response=$(_post "$_request" "https://api.gcdn.co/auth/signin")
|
_response=$(_post "$_request" "https://api.gcore.com/auth/jwt/login")
|
||||||
_debug _response "$_response"
|
_debug _response "$_response"
|
||||||
_regex=".*\"token\":\"\([-._0-9A-Za-z]*\)\".*$"
|
_regex=".*\"access\":\"\([-._0-9A-Za-z]*\)\".*$"
|
||||||
_debug _regex "$_regex"
|
_debug _regex "$_regex"
|
||||||
_token=$(echo "$_response" | sed -n "s/$_regex/\1/p")
|
_token=$(echo "$_response" | sed -n "s/$_regex/\1/p")
|
||||||
_debug _token "$_token"
|
_debug _token "$_token"
|
||||||
@ -69,23 +70,26 @@ gcore_cdn_deploy() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Find CDN resource with cname $_cdomain"
|
_info "Find CDN resource with cname $_cdomain"
|
||||||
export _H2="Authorization:Token $_token"
|
export _H2="Authorization:Bearer $_token"
|
||||||
_response=$(_get "https://api.gcdn.co/resources")
|
_response=$(_get "https://api.gcore.com/cdn/resources")
|
||||||
|
_debug _response "$_response"
|
||||||
|
_regex="\"primary_resource\":null},"
|
||||||
|
_debug _regex "$_regex"
|
||||||
|
_response=$(echo "$_response" | sed "s/$_regex/$_regex\n/g")
|
||||||
_debug _response "$_response"
|
_debug _response "$_response"
|
||||||
_regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})"
|
|
||||||
_regex="^.*\"cname\":\"$_cdomain\".*$"
|
_regex="^.*\"cname\":\"$_cdomain\".*$"
|
||||||
_debug _regex "$_regex"
|
_debug _regex "$_regex"
|
||||||
_resource=$(echo "$_response" | sed 's/},{/},\n{/g' | _egrep_o "$_regex")
|
_resource=$(echo "$_response" | _egrep_o "$_regex")
|
||||||
_debug _resource "$_resource"
|
_debug _resource "$_resource"
|
||||||
_regex=".*\"id\":\([0-9]*\),.*$"
|
_regex=".*\"id\":\([0-9]*\).*$"
|
||||||
_debug _regex "$_regex"
|
_debug _regex "$_regex"
|
||||||
_resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
_resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
||||||
_debug _resourceId "$_resourceId"
|
_debug _resourceId "$_resourceId"
|
||||||
_regex=".*\"sslData\":\([0-9]*\)}.*$"
|
_regex=".*\"sslData\":\([0-9]*\).*$"
|
||||||
_debug _regex "$_regex"
|
_debug _regex "$_regex"
|
||||||
_sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
_sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
||||||
_debug _sslDataOld "$_sslDataOld"
|
_debug _sslDataOld "$_sslDataOld"
|
||||||
_regex=".*\"originGroup\":\([0-9]*\),.*$"
|
_regex=".*\"originGroup\":\([0-9]*\).*$"
|
||||||
_debug _regex "$_regex"
|
_debug _regex "$_regex"
|
||||||
_originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
_originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
|
||||||
_debug _originGroup "$_originGroup"
|
_debug _originGroup "$_originGroup"
|
||||||
@ -99,9 +103,9 @@ gcore_cdn_deploy() {
|
|||||||
_date=$(date "+%d.%m.%Y %H:%M:%S")
|
_date=$(date "+%d.%m.%Y %H:%M:%S")
|
||||||
_request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}"
|
_request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}"
|
||||||
_debug _request "$_request"
|
_debug _request "$_request"
|
||||||
_response=$(_post "$_request" "https://api.gcdn.co/sslData")
|
_response=$(_post "$_request" "https://api.gcore.com/cdn/sslData")
|
||||||
_debug _response "$_response"
|
_debug _response "$_response"
|
||||||
_regex=".*\"id\":\([0-9]*\),.*$"
|
_regex=".*\"id\":\([0-9]*\).*$"
|
||||||
_debug _regex "$_regex"
|
_debug _regex "$_regex"
|
||||||
_sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p")
|
_sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p")
|
||||||
_debug _sslDataAdd "$_sslDataAdd"
|
_debug _sslDataAdd "$_sslDataAdd"
|
||||||
@ -114,7 +118,7 @@ gcore_cdn_deploy() {
|
|||||||
_info "Update CDN resource"
|
_info "Update CDN resource"
|
||||||
_request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}"
|
_request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}"
|
||||||
_debug _request "$_request"
|
_debug _request "$_request"
|
||||||
_response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT")
|
_response=$(_post "$_request" "https://api.gcore.com/cdn/resources/$_resourceId" '' "PUT")
|
||||||
_debug _response "$_response"
|
_debug _response "$_response"
|
||||||
_regex=".*\"sslData\":\([0-9]*\).*$"
|
_regex=".*\"sslData\":\([0-9]*\).*$"
|
||||||
_debug _regex "$_regex"
|
_debug _regex "$_regex"
|
||||||
@ -130,7 +134,7 @@ gcore_cdn_deploy() {
|
|||||||
_info "Not found old SSL certificate"
|
_info "Not found old SSL certificate"
|
||||||
else
|
else
|
||||||
_info "Delete old SSL certificate"
|
_info "Delete old SSL certificate"
|
||||||
_response=$(_post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE")
|
_response=$(_post '' "https://api.gcore.com/cdn/sslData/$_sslDataOld" '' "DELETE")
|
||||||
_debug _response "$_response"
|
_debug _response "$_response"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ gitlab_deploy() {
|
|||||||
|
|
||||||
error_response="error"
|
error_response="error"
|
||||||
|
|
||||||
if test "${_response#*$error_response}" != "$_response"; then
|
if test "${_response#*"$error_response"}" != "$_response"; then
|
||||||
_err "Error in deploying certificate:"
|
_err "Error in deploying certificate:"
|
||||||
_err "$_response"
|
_err "$_response"
|
||||||
return 1
|
return 1
|
||||||
|
@ -36,6 +36,19 @@
|
|||||||
# Note: This functionality requires HAProxy was compiled against
|
# Note: This functionality requires HAProxy was compiled against
|
||||||
# a version of OpenSSL that supports this.
|
# a version of OpenSSL that supports this.
|
||||||
#
|
#
|
||||||
|
# export DEPLOY_HAPROXY_HOT_UPDATE="yes"
|
||||||
|
# export DEPLOY_HAPROXY_STATS_SOCKET="UNIX:/run/haproxy/admin.sock"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Deploy the certificate over the HAProxy stats socket without
|
||||||
|
# needing to reload HAProxy. Default is "no".
|
||||||
|
#
|
||||||
|
# Require the socat binary. DEPLOY_HAPROXY_STATS_SOCKET variable uses the socat
|
||||||
|
# address format.
|
||||||
|
#
|
||||||
|
# export DEPLOY_HAPROXY_MASTER_CLI="UNIX:/run/haproxy-master.sock"
|
||||||
|
#
|
||||||
|
# OPTIONAL: To use the master CLI with DEPLOY_HAPROXY_HOT_UPDATE="yes" instead
|
||||||
|
# of a stats socket, use this variable.
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
@ -46,6 +59,7 @@ haproxy_deploy() {
|
|||||||
_ccert="$3"
|
_ccert="$3"
|
||||||
_cca="$4"
|
_cca="$4"
|
||||||
_cfullchain="$5"
|
_cfullchain="$5"
|
||||||
|
_cmdpfx=""
|
||||||
|
|
||||||
# Some defaults
|
# Some defaults
|
||||||
DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy"
|
DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy"
|
||||||
@ -53,11 +67,8 @@ haproxy_deploy() {
|
|||||||
DEPLOY_HAPROXY_BUNDLE_DEFAULT="no"
|
DEPLOY_HAPROXY_BUNDLE_DEFAULT="no"
|
||||||
DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
|
DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
|
||||||
DEPLOY_HAPROXY_RELOAD_DEFAULT="true"
|
DEPLOY_HAPROXY_RELOAD_DEFAULT="true"
|
||||||
|
DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT="no"
|
||||||
if [ -f "${DOMAIN_CONF}" ]; then
|
DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT="UNIX:/run/haproxy/admin.sock"
|
||||||
# shellcheck disable=SC1090
|
|
||||||
. "${DOMAIN_CONF}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
_debug _cdomain "${_cdomain}"
|
_debug _cdomain "${_cdomain}"
|
||||||
_debug _ckey "${_ckey}"
|
_debug _ckey "${_ckey}"
|
||||||
@ -66,6 +77,8 @@ haproxy_deploy() {
|
|||||||
_debug _cfullchain "${_cfullchain}"
|
_debug _cfullchain "${_cfullchain}"
|
||||||
|
|
||||||
# PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
|
# PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_PEM_PATH
|
||||||
|
_debug2 DEPLOY_HAPROXY_PEM_PATH "${DEPLOY_HAPROXY_PEM_PATH}"
|
||||||
if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then
|
if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then
|
||||||
Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}"
|
Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}"
|
||||||
_savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}"
|
_savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}"
|
||||||
@ -82,14 +95,23 @@ haproxy_deploy() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
|
# PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_PEM_NAME
|
||||||
|
_debug2 DEPLOY_HAPROXY_PEM_NAME "${DEPLOY_HAPROXY_PEM_NAME}"
|
||||||
if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then
|
if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then
|
||||||
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}"
|
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}"
|
||||||
_savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}"
|
_savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}"
|
||||||
elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then
|
elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then
|
||||||
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
|
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
|
||||||
|
# We better not have '*' as the first character
|
||||||
|
if [ "${Le_Deploy_haproxy_pem_name%%"${Le_Deploy_haproxy_pem_name#?}"}" = '*' ]; then
|
||||||
|
# removes the first characters and add a _ instead
|
||||||
|
Le_Deploy_haproxy_pem_name="_${Le_Deploy_haproxy_pem_name#?}"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
|
# BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_BUNDLE
|
||||||
|
_debug2 DEPLOY_HAPROXY_BUNDLE "${DEPLOY_HAPROXY_BUNDLE}"
|
||||||
if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then
|
if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then
|
||||||
Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}"
|
Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}"
|
||||||
_savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}"
|
_savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}"
|
||||||
@ -98,6 +120,8 @@ haproxy_deploy() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
|
# ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_ISSUER
|
||||||
|
_debug2 DEPLOY_HAPROXY_ISSUER "${DEPLOY_HAPROXY_ISSUER}"
|
||||||
if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then
|
if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then
|
||||||
Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}"
|
Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}"
|
||||||
_savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}"
|
_savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}"
|
||||||
@ -106,6 +130,8 @@ haproxy_deploy() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
|
# RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_RELOAD
|
||||||
|
_debug2 DEPLOY_HAPROXY_RELOAD "${DEPLOY_HAPROXY_RELOAD}"
|
||||||
if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then
|
if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then
|
||||||
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}"
|
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}"
|
||||||
_savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}"
|
_savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}"
|
||||||
@ -113,6 +139,36 @@ haproxy_deploy() {
|
|||||||
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
|
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# HOT_UPDATE is optional. If not provided then assume "${DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_HOT_UPDATE
|
||||||
|
_debug2 DEPLOY_HAPROXY_HOT_UPDATE "${DEPLOY_HAPROXY_HOT_UPDATE}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_HOT_UPDATE}" ]; then
|
||||||
|
Le_Deploy_haproxy_hot_update="${DEPLOY_HAPROXY_HOT_UPDATE}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_hot_update "${Le_Deploy_haproxy_hot_update}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_hot_update}" ]; then
|
||||||
|
Le_Deploy_haproxy_hot_update="${DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# STATS_SOCKET is optional. If not provided then assume "${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_STATS_SOCKET
|
||||||
|
_debug2 DEPLOY_HAPROXY_STATS_SOCKET "${DEPLOY_HAPROXY_STATS_SOCKET}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_STATS_SOCKET}" ]; then
|
||||||
|
Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_stats_socket "${Le_Deploy_haproxy_stats_socket}"
|
||||||
|
elif [ -z "${Le_Deploy_haproxy_stats_socket}" ]; then
|
||||||
|
Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# MASTER_CLI is optional. No defaults are used. When the master CLI is used,
|
||||||
|
# all commands are sent with a prefix.
|
||||||
|
_getdeployconf DEPLOY_HAPROXY_MASTER_CLI
|
||||||
|
_debug2 DEPLOY_HAPROXY_MASTER_CLI "${DEPLOY_HAPROXY_MASTER_CLI}"
|
||||||
|
if [ -n "${DEPLOY_HAPROXY_MASTER_CLI}" ]; then
|
||||||
|
Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_MASTER_CLI}"
|
||||||
|
_savedomainconf Le_Deploy_haproxy_stats_socket "${Le_Deploy_haproxy_stats_socket}"
|
||||||
|
_cmdpfx="@1 " # command prefix used for master CLI only.
|
||||||
|
fi
|
||||||
|
|
||||||
# Set the suffix depending if we are creating a bundle or not
|
# Set the suffix depending if we are creating a bundle or not
|
||||||
if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
|
if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
|
||||||
_info "Bundle creation requested"
|
_info "Bundle creation requested"
|
||||||
@ -137,12 +193,13 @@ haproxy_deploy() {
|
|||||||
_issuer="${_pem}.issuer"
|
_issuer="${_pem}.issuer"
|
||||||
_ocsp="${_pem}.ocsp"
|
_ocsp="${_pem}.ocsp"
|
||||||
_reload="${Le_Deploy_haproxy_reload}"
|
_reload="${Le_Deploy_haproxy_reload}"
|
||||||
|
_statssock="${Le_Deploy_haproxy_stats_socket}"
|
||||||
|
|
||||||
_info "Deploying PEM file"
|
_info "Deploying PEM file"
|
||||||
# Create a temporary PEM file
|
# Create a temporary PEM file
|
||||||
_temppem="$(_mktemp)"
|
_temppem="$(_mktemp)"
|
||||||
_debug _temppem "${_temppem}"
|
_debug _temppem "${_temppem}"
|
||||||
cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
|
cat "${_ccert}" "${_cca}" "${_ckey}" | grep . >"${_temppem}"
|
||||||
_ret="$?"
|
_ret="$?"
|
||||||
|
|
||||||
# Check that we could create the temporary file
|
# Check that we could create the temporary file
|
||||||
@ -190,7 +247,7 @@ haproxy_deploy() {
|
|||||||
_info "Updating OCSP stapling info"
|
_info "Updating OCSP stapling info"
|
||||||
_debug _ocsp "${_ocsp}"
|
_debug _ocsp "${_ocsp}"
|
||||||
_info "Extracting OCSP URL"
|
_info "Extracting OCSP URL"
|
||||||
_ocsp_url=$(openssl x509 -noout -ocsp_uri -in "${_pem}")
|
_ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}")
|
||||||
_debug _ocsp_url "${_ocsp_url}"
|
_debug _ocsp_url "${_ocsp_url}"
|
||||||
|
|
||||||
# Only process OCSP if URL was present
|
# Only process OCSP if URL was present
|
||||||
@ -203,38 +260,41 @@ haproxy_deploy() {
|
|||||||
# Only process the certificate if we have a .issuer file
|
# Only process the certificate if we have a .issuer file
|
||||||
if [ -r "${_issuer}" ]; then
|
if [ -r "${_issuer}" ]; then
|
||||||
# Check if issuer cert is also a root CA cert
|
# Check if issuer cert is also a root CA cert
|
||||||
_subjectdn=$(openssl x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
_subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
||||||
_debug _subjectdn "${_subjectdn}"
|
_debug _subjectdn "${_subjectdn}"
|
||||||
_issuerdn=$(openssl x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
_issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
||||||
_debug _issuerdn "${_issuerdn}"
|
_debug _issuerdn "${_issuerdn}"
|
||||||
_info "Requesting OCSP response"
|
_info "Requesting OCSP response"
|
||||||
# Request the OCSP response from the issuer and store it
|
|
||||||
if [ "${_subjectdn}" = "${_issuerdn}" ]; then
|
|
||||||
# If the issuer is a CA cert then our command line has "-CAfile" added
|
# If the issuer is a CA cert then our command line has "-CAfile" added
|
||||||
openssl ocsp \
|
if [ "${_subjectdn}" = "${_issuerdn}" ]; then
|
||||||
-issuer "${_issuer}" \
|
_cafile_argument="-CAfile \"${_issuer}\""
|
||||||
-cert "${_pem}" \
|
|
||||||
-url "${_ocsp_url}" \
|
|
||||||
-header Host "${_ocsp_host}" \
|
|
||||||
-respout "${_ocsp}" \
|
|
||||||
-verify_other "${_issuer}" \
|
|
||||||
-no_nonce \
|
|
||||||
-CAfile "${_issuer}" \
|
|
||||||
| grep -q "${_pem}: good"
|
|
||||||
_ret=$?
|
|
||||||
else
|
else
|
||||||
# Issuer is not a root CA so no "-CAfile" option
|
_cafile_argument=""
|
||||||
openssl ocsp \
|
|
||||||
-issuer "${_issuer}" \
|
|
||||||
-cert "${_pem}" \
|
|
||||||
-url "${_ocsp_url}" \
|
|
||||||
-header Host "${_ocsp_host}" \
|
|
||||||
-respout "${_ocsp}" \
|
|
||||||
-verify_other "${_issuer}" \
|
|
||||||
-no_nonce \
|
|
||||||
| grep -q "${_pem}: good"
|
|
||||||
_ret=$?
|
|
||||||
fi
|
fi
|
||||||
|
_debug _cafile_argument "${_cafile_argument}"
|
||||||
|
# if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
|
||||||
|
_openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2)
|
||||||
|
_debug _openssl_version "${_openssl_version}"
|
||||||
|
_openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
|
||||||
|
_openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
|
||||||
|
if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
|
||||||
|
_header_sep="="
|
||||||
|
else
|
||||||
|
_header_sep=" "
|
||||||
|
fi
|
||||||
|
# Request the OCSP response from the issuer and store it
|
||||||
|
_openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \
|
||||||
|
-issuer \"${_issuer}\" \
|
||||||
|
-cert \"${_pem}\" \
|
||||||
|
-url \"${_ocsp_url}\" \
|
||||||
|
-header Host${_header_sep}\"${_ocsp_host}\" \
|
||||||
|
-respout \"${_ocsp}\" \
|
||||||
|
-verify_other \"${_issuer}\" \
|
||||||
|
${_cafile_argument} \
|
||||||
|
| grep -q \"${_pem}: good\""
|
||||||
|
_debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"
|
||||||
|
eval "${_openssl_ocsp_cmd}"
|
||||||
|
_ret=$?
|
||||||
else
|
else
|
||||||
# Non fatal: No issuer file was present so no OCSP stapling file created
|
# Non fatal: No issuer file was present so no OCSP stapling file created
|
||||||
_err "OCSP stapling in use but no .issuer file was present"
|
_err "OCSP stapling in use but no .issuer file was present"
|
||||||
@ -257,6 +317,76 @@ haproxy_deploy() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "${Le_Deploy_haproxy_hot_update}" = "yes" ]; then
|
||||||
|
# set the socket name for messages
|
||||||
|
if [ -n "${_cmdpfx}" ]; then
|
||||||
|
_socketname="master CLI"
|
||||||
|
else
|
||||||
|
_socketname="stats socket"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update certificate over HAProxy stats socket or master CLI.
|
||||||
|
if _exists socat; then
|
||||||
|
# look for the certificate on the stats socket, to chose between updating or creating one
|
||||||
|
_socat_cert_cmd="echo '${_cmdpfx}show ssl cert' | socat '${_statssock}' - | grep -q '^${_pem}$'"
|
||||||
|
_debug _socat_cert_cmd "${_socat_cert_cmd}"
|
||||||
|
eval "${_socat_cert_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_newcert="1"
|
||||||
|
_info "Creating new certificate '${_pem}' over HAProxy ${_socketname}."
|
||||||
|
# certificate wasn't found, it's a new one. We should check if the crt-list exists and creates/inserts the certificate.
|
||||||
|
_socat_crtlist_show_cmd="echo '${_cmdpfx}show ssl crt-list' | socat '${_statssock}' - | grep -q '^${Le_Deploy_haproxy_pem_path}$'"
|
||||||
|
_debug _socat_crtlist_show_cmd "${_socat_crtlist_show_cmd}"
|
||||||
|
eval "${_socat_crtlist_show_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Couldn't find '${Le_Deploy_haproxy_pem_path}' in haproxy 'show ssl crt-list'"
|
||||||
|
return "${_ret}"
|
||||||
|
fi
|
||||||
|
# create a new certificate
|
||||||
|
_socat_new_cmd="echo '${_cmdpfx}new ssl cert ${_pem}' | socat '${_statssock}' - | grep -q 'New empty'"
|
||||||
|
_debug _socat_new_cmd "${_socat_new_cmd}"
|
||||||
|
eval "${_socat_new_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Couldn't create '${_pem}' in haproxy"
|
||||||
|
return "${_ret}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_info "Update existing certificate '${_pem}' over HAProxy ${_socketname}."
|
||||||
|
fi
|
||||||
|
_socat_cert_set_cmd="echo -e '${_cmdpfx}set ssl cert ${_pem} <<\n$(cat "${_pem}")\n' | socat '${_statssock}' - | grep -q 'Transaction created'"
|
||||||
|
_debug _socat_cert_set_cmd "${_socat_cert_set_cmd}"
|
||||||
|
eval "${_socat_cert_set_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Can't update '${_pem}' in haproxy"
|
||||||
|
return "${_ret}"
|
||||||
|
fi
|
||||||
|
_socat_cert_commit_cmd="echo '${_cmdpfx}commit ssl cert ${_pem}' | socat '${_statssock}' - | grep -q '^Success!$'"
|
||||||
|
_debug _socat_cert_commit_cmd "${_socat_cert_commit_cmd}"
|
||||||
|
eval "${_socat_cert_commit_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Can't commit '${_pem}' in haproxy"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
if [ "${_newcert}" = "1" ]; then
|
||||||
|
# if this is a new certificate, it needs to be inserted into the crt-list`
|
||||||
|
_socat_cert_add_cmd="echo '${_cmdpfx}add ssl crt-list ${Le_Deploy_haproxy_pem_path} ${_pem}' | socat '${_statssock}' - | grep -q 'Success!'"
|
||||||
|
_debug _socat_cert_add_cmd "${_socat_cert_add_cmd}"
|
||||||
|
eval "${_socat_cert_add_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Can't update '${_pem}' in haproxy"
|
||||||
|
return "${_ret}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "'socat' is not available, couldn't update over ${_socketname}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
# Reload HAProxy
|
# Reload HAProxy
|
||||||
_debug _reload "${_reload}"
|
_debug _reload "${_reload}"
|
||||||
eval "${_reload}"
|
eval "${_reload}"
|
||||||
@ -267,6 +397,7 @@ haproxy_deploy() {
|
|||||||
else
|
else
|
||||||
_info "Reload successful"
|
_info "Reload successful"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
# If certificate already exist it will update only cert and key not touching other parameter
|
# If certificate already exists it will update only cert and key, not touching other parameters
|
||||||
# If certificate doesn't exist it will only upload cert and key and not set other parameter
|
# If certificate doesn't exist it will only upload cert and key, and not set other parameters
|
||||||
# Note that we deploy full chain
|
# Note that we deploy full chain
|
||||||
# Written by Geoffroi Genot <ggenot@voxbone.com>
|
# Written by Geoffroi Genot <ggenot@voxbone.com>
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ kong_deploy() {
|
|||||||
#Generate data for request (Multipart/form-data with mixed content)
|
#Generate data for request (Multipart/form-data with mixed content)
|
||||||
if [ -z "$ssl_uuid" ]; then
|
if [ -z "$ssl_uuid" ]; then
|
||||||
#set sni to domain
|
#set sni to domain
|
||||||
content="--$delim${nl}Content-Disposition: form-data; name=\"snis\"${nl}${nl}$_cdomain"
|
content="--$delim${nl}Content-Disposition: form-data; name=\"snis[]\"${nl}${nl}$_cdomain"
|
||||||
fi
|
fi
|
||||||
#add key
|
#add key
|
||||||
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
|
||||||
|
280
deploy/lighttpd.sh
Normal file
280
deploy/lighttpd.sh
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script for acme.sh to deploy certificates to lighttpd
|
||||||
|
#
|
||||||
|
# The following variables can be exported:
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_PEM_NAME="${domain}.pem"
|
||||||
|
#
|
||||||
|
# Defines the name of the PEM file.
|
||||||
|
# Defaults to "<domain>.pem"
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_PEM_PATH="/etc/lighttpd"
|
||||||
|
#
|
||||||
|
# Defines location of PEM file for Lighttpd.
|
||||||
|
# Defaults to /etc/lighttpd
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_RELOAD="systemctl reload lighttpd"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Reload command used post deploy
|
||||||
|
# This defaults to be a no-op (ie "true").
|
||||||
|
# It is strongly recommended to set this something that makes sense
|
||||||
|
# for your distro.
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_ISSUER="yes"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Places CA file as "${DEPLOY_LIGHTTPD_PEM}.issuer"
|
||||||
|
# Note: Required for OCSP stapling to work
|
||||||
|
#
|
||||||
|
# export DEPLOY_LIGHTTPD_BUNDLE="no"
|
||||||
|
#
|
||||||
|
# OPTIONAL: Deploy this certificate as part of a multi-cert bundle
|
||||||
|
# This adds a suffix to the certificate based on the certificate type
|
||||||
|
# eg RSA certificates will have .rsa as a suffix to the file name
|
||||||
|
# Lighttpd will load all certificates and provide one or the other
|
||||||
|
# depending on client capabilities
|
||||||
|
# Note: This functionality requires Lighttpd was compiled against
|
||||||
|
# a version of OpenSSL that supports this.
|
||||||
|
#
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
lighttpd_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
# Some defaults
|
||||||
|
DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT="/etc/lighttpd"
|
||||||
|
DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT="${_cdomain}.pem"
|
||||||
|
DEPLOY_LIGHTTPD_BUNDLE_DEFAULT="no"
|
||||||
|
DEPLOY_LIGHTTPD_ISSUER_DEFAULT="yes"
|
||||||
|
DEPLOY_LIGHTTPD_RELOAD_DEFAULT="true"
|
||||||
|
|
||||||
|
_debug _cdomain "${_cdomain}"
|
||||||
|
_debug _ckey "${_ckey}"
|
||||||
|
_debug _ccert "${_ccert}"
|
||||||
|
_debug _cca "${_cca}"
|
||||||
|
_debug _cfullchain "${_cfullchain}"
|
||||||
|
|
||||||
|
# PEM_PATH is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_PEM_PATH
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_PEM_PATH "${DEPLOY_LIGHTTPD_PEM_PATH}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_PEM_PATH}" ]; then
|
||||||
|
Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_pem_path "${Le_Deploy_lighttpd_pem_path}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_pem_path}" ]; then
|
||||||
|
Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure PEM_PATH exists
|
||||||
|
if [ -d "${Le_Deploy_lighttpd_pem_path}" ]; then
|
||||||
|
_debug "PEM_PATH ${Le_Deploy_lighttpd_pem_path} exists"
|
||||||
|
else
|
||||||
|
_err "PEM_PATH ${Le_Deploy_lighttpd_pem_path} does not exist"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PEM_NAME is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_PEM_NAME
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_PEM_NAME "${DEPLOY_LIGHTTPD_PEM_NAME}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_PEM_NAME}" ]; then
|
||||||
|
Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_pem_name "${Le_Deploy_lighttpd_pem_name}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_pem_name}" ]; then
|
||||||
|
Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# BUNDLE is optional. If not provided then assume "${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_BUNDLE
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_BUNDLE "${DEPLOY_LIGHTTPD_BUNDLE}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_BUNDLE}" ]; then
|
||||||
|
Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_bundle "${Le_Deploy_lighttpd_bundle}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_bundle}" ]; then
|
||||||
|
Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ISSUER is optional. If not provided then assume "${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_ISSUER
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_ISSUER "${DEPLOY_LIGHTTPD_ISSUER}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_ISSUER}" ]; then
|
||||||
|
Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_issuer "${Le_Deploy_lighttpd_issuer}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_issuer}" ]; then
|
||||||
|
Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# RELOAD is optional. If not provided then assume "${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}"
|
||||||
|
_getdeployconf DEPLOY_LIGHTTPD_RELOAD
|
||||||
|
_debug2 DEPLOY_LIGHTTPD_RELOAD "${DEPLOY_LIGHTTPD_RELOAD}"
|
||||||
|
if [ -n "${DEPLOY_LIGHTTPD_RELOAD}" ]; then
|
||||||
|
Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD}"
|
||||||
|
_savedomainconf Le_Deploy_lighttpd_reload "${Le_Deploy_lighttpd_reload}"
|
||||||
|
elif [ -z "${Le_Deploy_lighttpd_reload}" ]; then
|
||||||
|
Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the suffix depending if we are creating a bundle or not
|
||||||
|
if [ "${Le_Deploy_lighttpd_bundle}" = "yes" ]; then
|
||||||
|
_info "Bundle creation requested"
|
||||||
|
# Initialise $Le_Keylength if its not already set
|
||||||
|
if [ -z "${Le_Keylength}" ]; then
|
||||||
|
Le_Keylength=""
|
||||||
|
fi
|
||||||
|
if _isEccKey "${Le_Keylength}"; then
|
||||||
|
_info "ECC key type detected"
|
||||||
|
_suffix=".ecdsa"
|
||||||
|
else
|
||||||
|
_info "RSA key type detected"
|
||||||
|
_suffix=".rsa"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_suffix=""
|
||||||
|
fi
|
||||||
|
_debug _suffix "${_suffix}"
|
||||||
|
|
||||||
|
# Set variables for later
|
||||||
|
_pem="${Le_Deploy_lighttpd_pem_path}/${Le_Deploy_lighttpd_pem_name}${_suffix}"
|
||||||
|
_issuer="${_pem}.issuer"
|
||||||
|
_ocsp="${_pem}.ocsp"
|
||||||
|
_reload="${Le_Deploy_lighttpd_reload}"
|
||||||
|
|
||||||
|
_info "Deploying PEM file"
|
||||||
|
# Create a temporary PEM file
|
||||||
|
_temppem="$(_mktemp)"
|
||||||
|
_debug _temppem "${_temppem}"
|
||||||
|
cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
|
||||||
|
_ret="$?"
|
||||||
|
|
||||||
|
# Check that we could create the temporary file
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned during PEM file creation"
|
||||||
|
[ -f "${_temppem}" ] && rm -f "${_temppem}"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move PEM file into place
|
||||||
|
_info "Moving new certificate into place"
|
||||||
|
_debug _pem "${_pem}"
|
||||||
|
cat "${_temppem}" >"${_pem}"
|
||||||
|
_ret=$?
|
||||||
|
|
||||||
|
# Clean up temp file
|
||||||
|
[ -f "${_temppem}" ] && rm -f "${_temppem}"
|
||||||
|
|
||||||
|
# Deal with any failure of moving PEM file into place
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned while moving new certificate into place"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update .issuer file if requested
|
||||||
|
if [ "${Le_Deploy_lighttpd_issuer}" = "yes" ]; then
|
||||||
|
_info "Updating .issuer file"
|
||||||
|
_debug _issuer "${_issuer}"
|
||||||
|
cat "${_cca}" >"${_issuer}"
|
||||||
|
_ret="$?"
|
||||||
|
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} returned while copying issuer/CA certificate into place"
|
||||||
|
return ${_ret}
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
[ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option
|
||||||
|
if [ -z "${Le_OCSP_Staple}" ]; then
|
||||||
|
Le_OCSP_Staple="0"
|
||||||
|
fi
|
||||||
|
if [ "${Le_OCSP_Staple}" = "1" ]; then
|
||||||
|
_info "Updating OCSP stapling info"
|
||||||
|
_debug _ocsp "${_ocsp}"
|
||||||
|
_info "Extracting OCSP URL"
|
||||||
|
_ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}")
|
||||||
|
_debug _ocsp_url "${_ocsp_url}"
|
||||||
|
|
||||||
|
# Only process OCSP if URL was present
|
||||||
|
if [ "${_ocsp_url}" != "" ]; then
|
||||||
|
# Extract the hostname from the OCSP URL
|
||||||
|
_info "Extracting OCSP URL"
|
||||||
|
_ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3)
|
||||||
|
_debug _ocsp_host "${_ocsp_host}"
|
||||||
|
|
||||||
|
# Only process the certificate if we have a .issuer file
|
||||||
|
if [ -r "${_issuer}" ]; then
|
||||||
|
# Check if issuer cert is also a root CA cert
|
||||||
|
_subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
||||||
|
_debug _subjectdn "${_subjectdn}"
|
||||||
|
_issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
|
||||||
|
_debug _issuerdn "${_issuerdn}"
|
||||||
|
_info "Requesting OCSP response"
|
||||||
|
# If the issuer is a CA cert then our command line has "-CAfile" added
|
||||||
|
if [ "${_subjectdn}" = "${_issuerdn}" ]; then
|
||||||
|
_cafile_argument="-CAfile \"${_issuer}\""
|
||||||
|
else
|
||||||
|
_cafile_argument=""
|
||||||
|
fi
|
||||||
|
_debug _cafile_argument "${_cafile_argument}"
|
||||||
|
# if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
|
||||||
|
_openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2)
|
||||||
|
_debug _openssl_version "${_openssl_version}"
|
||||||
|
_openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
|
||||||
|
_openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
|
||||||
|
if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
|
||||||
|
_header_sep="="
|
||||||
|
else
|
||||||
|
_header_sep=" "
|
||||||
|
fi
|
||||||
|
# Request the OCSP response from the issuer and store it
|
||||||
|
_openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \
|
||||||
|
-issuer \"${_issuer}\" \
|
||||||
|
-cert \"${_pem}\" \
|
||||||
|
-url \"${_ocsp_url}\" \
|
||||||
|
-header Host${_header_sep}\"${_ocsp_host}\" \
|
||||||
|
-respout \"${_ocsp}\" \
|
||||||
|
-verify_other \"${_issuer}\" \
|
||||||
|
${_cafile_argument} \
|
||||||
|
| grep -q \"${_pem}: good\""
|
||||||
|
_debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"
|
||||||
|
eval "${_openssl_ocsp_cmd}"
|
||||||
|
_ret=$?
|
||||||
|
else
|
||||||
|
# Non fatal: No issuer file was present so no OCSP stapling file created
|
||||||
|
_err "OCSP stapling in use but no .issuer file was present"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Non fatal: No OCSP url was found int the certificate
|
||||||
|
_err "OCSP update requested but no OCSP URL was found in certificate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Non fatal: Check return code of openssl command
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Updating OCSP stapling failed with return code ${_ret}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# An OCSP file was already present but certificate did not have OCSP extension
|
||||||
|
if [ -f "${_ocsp}" ]; then
|
||||||
|
_err "OCSP was not requested but .ocsp file exists."
|
||||||
|
# Could remove the file at this step, although Lighttpd just ignores it in this case
|
||||||
|
# rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reload Lighttpd
|
||||||
|
_debug _reload "${_reload}"
|
||||||
|
eval "${_reload}"
|
||||||
|
_ret=$?
|
||||||
|
if [ "${_ret}" != "0" ]; then
|
||||||
|
_err "Error code ${_ret} during reload"
|
||||||
|
return ${_ret}
|
||||||
|
else
|
||||||
|
_info "Reload successful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
@ -20,14 +20,25 @@ mailcow_deploy() {
|
|||||||
_debug _cca "$_cca"
|
_debug _cca "$_cca"
|
||||||
_debug _cfullchain "$_cfullchain"
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
_mailcow_path="${DEPLOY_MAILCOW_PATH}"
|
_getdeployconf DEPLOY_MAILCOW_PATH
|
||||||
|
_getdeployconf DEPLOY_MAILCOW_RELOAD
|
||||||
|
|
||||||
if [ -z "$_mailcow_path" ]; then
|
_debug DEPLOY_MAILCOW_PATH "$DEPLOY_MAILCOW_PATH"
|
||||||
|
_debug DEPLOY_MAILCOW_RELOAD "$DEPLOY_MAILCOW_RELOAD"
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_MAILCOW_PATH" ]; then
|
||||||
_err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH."
|
_err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_ssl_path="${_mailcow_path}/data/assets/ssl/"
|
_savedeployconf DEPLOY_MAILCOW_PATH "$DEPLOY_MAILCOW_PATH"
|
||||||
|
[ -n "$DEPLOY_MAILCOW_RELOAD" ] && _savedeployconf DEPLOY_MAILCOW_RELOAD "$DEPLOY_MAILCOW_RELOAD"
|
||||||
|
|
||||||
|
_ssl_path="$DEPLOY_MAILCOW_PATH"
|
||||||
|
if [ -f "$DEPLOY_MAILCOW_PATH/generate_config.sh" ]; then
|
||||||
|
_ssl_path="$DEPLOY_MAILCOW_PATH/data/assets/ssl/"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -d "$_ssl_path" ]; then
|
if [ ! -d "$_ssl_path" ]; then
|
||||||
_err "Cannot find mailcow ssl path: $_ssl_path"
|
_err "Cannot find mailcow ssl path: $_ssl_path"
|
||||||
return 1
|
return 1
|
||||||
@ -46,7 +57,7 @@ mailcow_deploy() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DEFAULT_MAILCOW_RELOAD="cd ${_mailcow_path} && docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow"
|
DEFAULT_MAILCOW_RELOAD="docker restart \$(docker ps --quiet --filter name=nginx-mailcow --filter name=dovecot-mailcow --filter name=postfix-mailcow)"
|
||||||
_reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}"
|
_reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}"
|
||||||
|
|
||||||
_info "Run reload: $_reload"
|
_info "Run reload: $_reload"
|
||||||
|
156
deploy/openmediavault.sh
Normal file
156
deploy/openmediavault.sh
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# This deploy hook is tested on OpenMediaVault 5.x. It supports both local and remote deployment.
|
||||||
|
# The way it works is that if a cert with the matching domain name is not found, it will firstly create a dummy cert to get its uuid, and then replace it with your cert.
|
||||||
|
#
|
||||||
|
# DEPLOY_OMV_WEBUI_ADMIN - This is OMV web gui admin account. Default value is admin. It's required as the user parameter (-u) for the omv-rpc command.
|
||||||
|
# DEPLOY_OMV_HOST and DEPLOY_OMV_SSH_USER are optional. They are used for remote deployment through ssh (support public key authentication only). Per design, OMV web gui admin doesn't have ssh permission, so another account is needed for ssh.
|
||||||
|
#
|
||||||
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
openmediavault_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"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_OMV_WEBUI_ADMIN
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_OMV_WEBUI_ADMIN" ]; then
|
||||||
|
DEPLOY_OMV_WEBUI_ADMIN="admin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_OMV_WEBUI_ADMIN "$DEPLOY_OMV_WEBUI_ADMIN"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_OMV_HOST
|
||||||
|
_getdeployconf DEPLOY_OMV_SSH_USER
|
||||||
|
|
||||||
|
if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then
|
||||||
|
_info "[OMV deploy-hook] Deploy certificate remotely through ssh."
|
||||||
|
_savedeployconf DEPLOY_OMV_HOST "$DEPLOY_OMV_HOST"
|
||||||
|
_savedeployconf DEPLOY_OMV_SSH_USER "$DEPLOY_OMV_SSH_USER"
|
||||||
|
else
|
||||||
|
_info "[OMV deploy-hook] Deploy certificate locally."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then
|
||||||
|
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{\"start\": 0, \"limit\": -1}' | jq -r '.data[] | select(.name==\"/CN='$_cdomain'\") | .uuid'"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
_debug _command "$_command"
|
||||||
|
|
||||||
|
if [ -z "$_uuid" ]; then
|
||||||
|
_info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{\"cn\": \"test.example.com\", \"size\": 4096, \"days\": 3650, \"c\": \"\", \"st\": \"\", \"l\": \"\", \"o\": \"\", \"ou\": \"\", \"email\": \"\"}' | jq -r '.uuid'"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
_debug _command "$_command"
|
||||||
|
|
||||||
|
if [ -z "$_uuid" ]; then
|
||||||
|
_err "[OMV deploy-hook] An error occured while creating the certificate"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid"
|
||||||
|
_fullchain=$(jq <"$_cfullchain" -aRs .)
|
||||||
|
_key=$(jq <"$_ckey" -aRs .)
|
||||||
|
|
||||||
|
_debug _fullchain "$_fullchain"
|
||||||
|
_debug _key "$_key"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Updating key and certificate in openmediavault"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Asking nginx to reload"
|
||||||
|
_command="nginx -s reload"
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
_uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{"start": 0, "limit": -1}' | jq -r '.data[] | select(.name=="/CN='$_cdomain'") | .uuid')
|
||||||
|
if [ -z "$_uuid" ]; then
|
||||||
|
_info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
_uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{"cn": "test.example.com", "size": 4096, "days": 3650, "c": "", "st": "", "l": "", "o": "", "ou": "", "email": ""}' | jq -r '.uuid')
|
||||||
|
|
||||||
|
if [ -z "$_uuid" ]; then
|
||||||
|
_err "[OMB deploy-hook] An error occured while creating the certificate"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid"
|
||||||
|
_fullchain=$(jq <"$_cfullchain" -aRs .)
|
||||||
|
_key=$(jq <"$_ckey" -aRs .)
|
||||||
|
|
||||||
|
_debug _fullchain "$_fullchain"
|
||||||
|
_debug _key "$_key"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Updating key and certificate in openmediavault"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'"
|
||||||
|
_result=$(eval "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')"
|
||||||
|
_result=$(eval "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)"
|
||||||
|
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'"
|
||||||
|
_result=$(eval "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
_info "[OMV deploy-hook] Asking nginx to reload"
|
||||||
|
_command="nginx -s reload"
|
||||||
|
_result=$(eval "$_command")
|
||||||
|
|
||||||
|
_debug _command "$_command"
|
||||||
|
_debug _result "$_result"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
262
deploy/openstack.sh
Normal file
262
deploy/openstack.sh
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# OpenStack Barbican deploy hook
|
||||||
|
#
|
||||||
|
# This requires you to have OpenStackClient and python-barbicanclient
|
||||||
|
# installed.
|
||||||
|
#
|
||||||
|
# You will require Keystone V3 credentials loaded into your environment, which
|
||||||
|
# could be either password or v3applicationcredential type.
|
||||||
|
#
|
||||||
|
# Author: Andy Botting <andy@andybotting.com>
|
||||||
|
|
||||||
|
openstack_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"
|
||||||
|
|
||||||
|
if ! _exists openstack; then
|
||||||
|
_err "OpenStack client not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_openstack_credentials || return $?
|
||||||
|
|
||||||
|
_info "Generate import pkcs12"
|
||||||
|
_import_pkcs12="$(_mktemp)"
|
||||||
|
if ! _openstack_to_pkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca"; then
|
||||||
|
_err "Error creating pkcs12 certificate"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _import_pkcs12 "$_import_pkcs12"
|
||||||
|
_base64_pkcs12=$(_base64 "multiline" <"$_import_pkcs12")
|
||||||
|
|
||||||
|
secretHrefs=$(_openstack_get_secrets)
|
||||||
|
_debug secretHrefs "$secretHrefs"
|
||||||
|
_openstack_store_secret || return $?
|
||||||
|
|
||||||
|
if [ -n "$secretHrefs" ]; then
|
||||||
|
_info "Cleaning up existing secret"
|
||||||
|
_openstack_delete_secrets || return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Certificate successfully deployed"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_store_secret() {
|
||||||
|
if ! openstack secret store --name "$_cdomain." -t 'application/octet-stream' -e base64 --payload "$_base64_pkcs12"; then
|
||||||
|
_err "Failed to create OpenStack secret"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_delete_secrets() {
|
||||||
|
echo "$secretHrefs" | while read -r secretHref; do
|
||||||
|
_info "Deleting old secret $secretHref"
|
||||||
|
if ! openstack secret delete "$secretHref"; then
|
||||||
|
_err "Failed to delete OpenStack secret"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_get_secrets() {
|
||||||
|
if ! secretHrefs=$(openstack secret list -f value --name "$_cdomain." | cut -d' ' -f1); then
|
||||||
|
_err "Failed to list secrets"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "$secretHrefs"
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_to_pkcs() {
|
||||||
|
# The existing _toPkcs command can't allow an empty password, due to sh
|
||||||
|
# -z test, so copied here and forcing the empty password.
|
||||||
|
_cpfx="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
|
||||||
|
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:"
|
||||||
|
}
|
||||||
|
|
||||||
|
_openstack_credentials() {
|
||||||
|
_debug "Check OpenStack credentials"
|
||||||
|
|
||||||
|
# If we have OS_AUTH_URL already set in the environment, then assume we want
|
||||||
|
# to use those, otherwise use stored credentials
|
||||||
|
if [ -n "$OS_AUTH_URL" ]; then
|
||||||
|
_debug "OS_AUTH_URL env var found, using environment"
|
||||||
|
else
|
||||||
|
_debug "OS_AUTH_URL not found, loading stored credentials"
|
||||||
|
OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}"
|
||||||
|
OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}"
|
||||||
|
OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}"
|
||||||
|
OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}"
|
||||||
|
OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}"
|
||||||
|
OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}"
|
||||||
|
OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}"
|
||||||
|
OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}"
|
||||||
|
OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}"
|
||||||
|
OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}"
|
||||||
|
OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}"
|
||||||
|
OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}"
|
||||||
|
OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check each var and either save or clear it depending on whether its set.
|
||||||
|
# The helps us clear out old vars in the case where a user may want
|
||||||
|
# to switch between password and app creds
|
||||||
|
_debug "OS_AUTH_URL" "$OS_AUTH_URL"
|
||||||
|
if [ -n "$OS_AUTH_URL" ]; then
|
||||||
|
export OS_AUTH_URL
|
||||||
|
_saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL"
|
||||||
|
else
|
||||||
|
unset OS_AUTH_URL
|
||||||
|
_clearaccountconf SAVED_OS_AUTH_URL
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION"
|
||||||
|
if [ -n "$OS_IDENTITY_API_VERSION" ]; then
|
||||||
|
export OS_IDENTITY_API_VERSION
|
||||||
|
_saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION"
|
||||||
|
else
|
||||||
|
unset OS_IDENTITY_API_VERSION
|
||||||
|
_clearaccountconf SAVED_OS_IDENTITY_API_VERSION
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE"
|
||||||
|
if [ -n "$OS_AUTH_TYPE" ]; then
|
||||||
|
export OS_AUTH_TYPE
|
||||||
|
_saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE"
|
||||||
|
else
|
||||||
|
unset OS_AUTH_TYPE
|
||||||
|
_clearaccountconf SAVED_OS_AUTH_TYPE
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID"
|
||||||
|
if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then
|
||||||
|
export OS_APPLICATION_CREDENTIAL_ID
|
||||||
|
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID"
|
||||||
|
else
|
||||||
|
unset OS_APPLICATION_CREDENTIAL_ID
|
||||||
|
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID
|
||||||
|
fi
|
||||||
|
|
||||||
|
_secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET"
|
||||||
|
if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
|
||||||
|
export OS_APPLICATION_CREDENTIAL_SECRET
|
||||||
|
_saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET"
|
||||||
|
else
|
||||||
|
unset OS_APPLICATION_CREDENTIAL_SECRET
|
||||||
|
_clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_USERNAME" "$OS_USERNAME"
|
||||||
|
if [ -n "$OS_USERNAME" ]; then
|
||||||
|
export OS_USERNAME
|
||||||
|
_saveaccountconf_mutable OS_USERNAME "$OS_USERNAME"
|
||||||
|
else
|
||||||
|
unset OS_USERNAME
|
||||||
|
_clearaccountconf SAVED_OS_USERNAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
_secure_debug "OS_PASSWORD" "$OS_PASSWORD"
|
||||||
|
if [ -n "$OS_PASSWORD" ]; then
|
||||||
|
export OS_PASSWORD
|
||||||
|
_saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD"
|
||||||
|
else
|
||||||
|
unset OS_PASSWORD
|
||||||
|
_clearaccountconf SAVED_OS_PASSWORD
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME"
|
||||||
|
if [ -n "$OS_PROJECT_NAME" ]; then
|
||||||
|
export OS_PROJECT_NAME
|
||||||
|
_saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME"
|
||||||
|
else
|
||||||
|
unset OS_PROJECT_NAME
|
||||||
|
_clearaccountconf SAVED_OS_PROJECT_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_PROJECT_ID" "$OS_PROJECT_ID"
|
||||||
|
if [ -n "$OS_PROJECT_ID" ]; then
|
||||||
|
export OS_PROJECT_ID
|
||||||
|
_saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID"
|
||||||
|
else
|
||||||
|
unset OS_PROJECT_ID
|
||||||
|
_clearaccountconf SAVED_OS_PROJECT_ID
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME"
|
||||||
|
if [ -n "$OS_USER_DOMAIN_NAME" ]; then
|
||||||
|
export OS_USER_DOMAIN_NAME
|
||||||
|
_saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME"
|
||||||
|
else
|
||||||
|
unset OS_USER_DOMAIN_NAME
|
||||||
|
_clearaccountconf SAVED_OS_USER_DOMAIN_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID"
|
||||||
|
if [ -n "$OS_USER_DOMAIN_ID" ]; then
|
||||||
|
export OS_USER_DOMAIN_ID
|
||||||
|
_saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID"
|
||||||
|
else
|
||||||
|
unset OS_USER_DOMAIN_ID
|
||||||
|
_clearaccountconf SAVED_OS_USER_DOMAIN_ID
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME"
|
||||||
|
if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then
|
||||||
|
export OS_PROJECT_DOMAIN_NAME
|
||||||
|
_saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME"
|
||||||
|
else
|
||||||
|
unset OS_PROJECT_DOMAIN_NAME
|
||||||
|
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID"
|
||||||
|
if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then
|
||||||
|
export OS_PROJECT_DOMAIN_ID
|
||||||
|
_saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID"
|
||||||
|
else
|
||||||
|
unset OS_PROJECT_DOMAIN_ID
|
||||||
|
_clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then
|
||||||
|
# Application Credential auth
|
||||||
|
if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
|
||||||
|
_err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID"
|
||||||
|
_err "and OS_APPLICATION_CREDENTIAL_SECRET must be set."
|
||||||
|
_err "Please check your credentials and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Password auth
|
||||||
|
if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then
|
||||||
|
_err "OpenStack username or password not found."
|
||||||
|
_err "Please check your credentials and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then
|
||||||
|
_err "When using password authentication, OS_PROJECT_NAME or"
|
||||||
|
_err "OS_PROJECT_ID must be set."
|
||||||
|
_err "Please check your credentials and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
234
deploy/panos.sh
Normal file
234
deploy/panos.sh
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy certificates to Palo Alto Networks PANOS via API
|
||||||
|
# Note PANOS API KEY and IP address needs to be set prior to running.
|
||||||
|
# The following variables exported from environment will be used.
|
||||||
|
# If not set then values previously saved in domain.conf file are used.
|
||||||
|
#
|
||||||
|
# Firewall admin with superuser and IP address is required.
|
||||||
|
#
|
||||||
|
# REQURED:
|
||||||
|
# export PANOS_HOST=""
|
||||||
|
# export PANOS_USER="" #User *MUST* have Commit and Import Permissions in XML API for Admin Role
|
||||||
|
# export PANOS_PASS=""
|
||||||
|
#
|
||||||
|
# OPTIONAL
|
||||||
|
# export PANOS_TEMPLATE="" #Template Name of panorama managed devices
|
||||||
|
#
|
||||||
|
# The script will automatically generate a new API key if
|
||||||
|
# no key is found, or if a saved key has expired or is invalid.
|
||||||
|
|
||||||
|
# This function is to parse the XML response from the firewall
|
||||||
|
parse_response() {
|
||||||
|
type=$2
|
||||||
|
if [ "$type" = 'keygen' ]; then
|
||||||
|
status=$(echo "$1" | sed 's/^.*\(['\'']\)\([a-z]*\)'\''.*/\2/g')
|
||||||
|
if [ "$status" = "success" ]; then
|
||||||
|
panos_key=$(echo "$1" | sed 's/^.*\(<key>\)\(.*\)<\/key>.*/\2/g')
|
||||||
|
_panos_key=$panos_key
|
||||||
|
else
|
||||||
|
message="PAN-OS Key could not be set."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
status=$(echo "$1" | tr -d '\n' | sed 's/^.*"\([a-z]*\)".*/\1/g')
|
||||||
|
message=$(echo "$1" | tr -d '\n' | sed 's/.*\(<result>\|<msg>\|<line>\)\([^<]*\).*/\2/g')
|
||||||
|
_debug "Firewall message: $message"
|
||||||
|
if [ "$type" = 'keytest' ] && [ "$status" != "success" ]; then
|
||||||
|
_debug "**** API Key has EXPIRED or is INVALID ****"
|
||||||
|
unset _panos_key
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#This function is used to deploy to the firewall
|
||||||
|
deployer() {
|
||||||
|
content=""
|
||||||
|
type=$1 # Types are keytest, keygen, cert, key, commit
|
||||||
|
panos_url="https://$_panos_host/api/"
|
||||||
|
|
||||||
|
#Test API Key by performing a lookup
|
||||||
|
if [ "$type" = 'keytest' ]; then
|
||||||
|
_debug "**** Testing saved API Key ****"
|
||||||
|
_H1="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
# Get Version Info to test key
|
||||||
|
content="type=version&key=$_panos_key"
|
||||||
|
## Exclude all scopes for the empty commit
|
||||||
|
#_exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network><shared-object>exclude</shared-object>"
|
||||||
|
#content="type=commit&action=partial&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate API Key
|
||||||
|
if [ "$type" = 'keygen' ]; then
|
||||||
|
_debug "**** Generating new API Key ****"
|
||||||
|
_H1="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
content="type=keygen&user=$_panos_user&password=$_panos_pass"
|
||||||
|
# content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deploy Cert or Key
|
||||||
|
if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
|
||||||
|
_debug "**** Deploying $type ****"
|
||||||
|
#Generate DELIM
|
||||||
|
delim="-----MultipartDelimiter$(date "+%s%N")"
|
||||||
|
nl="\015\012"
|
||||||
|
#Set Header
|
||||||
|
export _H1="Content-Type: multipart/form-data; boundary=$delim"
|
||||||
|
if [ "$type" = 'cert' ]; then
|
||||||
|
panos_url="${panos_url}?type=import"
|
||||||
|
content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\ncertificate"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_cdomain"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
|
||||||
|
if [ "$_panos_template" ]; then
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$type" = 'key' ]; then
|
||||||
|
panos_url="${panos_url}?type=import"
|
||||||
|
content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\nprivate-key"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_cdomain"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n123456"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cdomain.key")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
|
||||||
|
if [ "$_panos_template" ]; then
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
#Close multipart
|
||||||
|
content="$content${nl}--$delim--${nl}${nl}"
|
||||||
|
#Convert CRLF
|
||||||
|
content=$(printf %b "$content")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Commit changes
|
||||||
|
if [ "$type" = 'commit' ]; then
|
||||||
|
_debug "**** Committing changes ****"
|
||||||
|
export _H1="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
#Check for force commit - will commit ALL uncommited changes to the firewall. Use with caution!
|
||||||
|
if [ "$FORCE" ]; then
|
||||||
|
_debug "Force switch detected. Committing ALL changes to the firewall."
|
||||||
|
cmd=$(printf "%s" "<commit><partial><force><admin><member>$_panos_user</member></admin></force></partial></commit>" | _url_encode)
|
||||||
|
else
|
||||||
|
_exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network>"
|
||||||
|
cmd=$(printf "%s" "<commit><partial>$_exclude_scope<admin><member>$_panos_user</member></admin></partial></commit>" | _url_encode)
|
||||||
|
fi
|
||||||
|
content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
response=$(_post "$content" "$panos_url" "" "POST")
|
||||||
|
parse_response "$response" "$type"
|
||||||
|
# Saving response to variables
|
||||||
|
response_status=$status
|
||||||
|
_debug response_status "$response_status"
|
||||||
|
if [ "$response_status" = "success" ]; then
|
||||||
|
_debug "Successfully deployed $type"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Deploy of type $type failed. Try deploying with --debug to troubleshoot."
|
||||||
|
_debug "$message"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# This is the main function that will call the other functions to deploy everything.
|
||||||
|
panos_deploy() {
|
||||||
|
_cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
|
||||||
|
_ckey="$2"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
# VALID FILE CHECK
|
||||||
|
if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
|
||||||
|
_err "Unable to find a valid key and/or cert. If this is an ECDSA/ECC cert, use the --ecc flag when deploying."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_HOST
|
||||||
|
if [ "$PANOS_HOST" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_HOST. Saving to file."
|
||||||
|
_savedeployconf PANOS_HOST "$PANOS_HOST" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_HOST from file."
|
||||||
|
_getdeployconf PANOS_HOST
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS USER
|
||||||
|
if [ "$PANOS_USER" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_USER. Saving to file."
|
||||||
|
_savedeployconf PANOS_USER "$PANOS_USER" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_USER from file."
|
||||||
|
_getdeployconf PANOS_USER
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_PASS
|
||||||
|
if [ "$PANOS_PASS" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_PASS. Saving to file."
|
||||||
|
_savedeployconf PANOS_PASS "$PANOS_PASS" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_PASS from file."
|
||||||
|
_getdeployconf PANOS_PASS
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_KEY
|
||||||
|
_getdeployconf PANOS_KEY
|
||||||
|
if [ "$PANOS_KEY" ]; then
|
||||||
|
_debug "Detected saved key."
|
||||||
|
_panos_key=$PANOS_KEY
|
||||||
|
else
|
||||||
|
_debug "No key detected"
|
||||||
|
unset _panos_key
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PANOS_TEMPLATE
|
||||||
|
if [ "$PANOS_TEMPLATE" ]; then
|
||||||
|
_debug "Detected ENV variable PANOS_TEMPLATE. Saving to file."
|
||||||
|
_savedeployconf PANOS_TEMPLATE "$PANOS_TEMPLATE" 1
|
||||||
|
else
|
||||||
|
_debug "Attempting to load variable PANOS_TEMPLATE from file."
|
||||||
|
_getdeployconf PANOS_TEMPLATE
|
||||||
|
fi
|
||||||
|
|
||||||
|
#Store variables
|
||||||
|
_panos_host=$PANOS_HOST
|
||||||
|
_panos_user=$PANOS_USER
|
||||||
|
_panos_pass=$PANOS_PASS
|
||||||
|
_panos_template=$PANOS_TEMPLATE
|
||||||
|
|
||||||
|
#Test API Key if found. If the key is invalid, the variable _panos_key will be unset.
|
||||||
|
if [ "$_panos_host" ] && [ "$_panos_key" ]; then
|
||||||
|
_debug "**** Testing API KEY ****"
|
||||||
|
deployer keytest
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for valid variables
|
||||||
|
if [ -z "$_panos_host" ]; then
|
||||||
|
_err "No host found. If this is your first time deploying, please set PANOS_HOST in ENV variables. You can delete it after you have successfully deployed the certs."
|
||||||
|
return 1
|
||||||
|
elif [ -z "$_panos_user" ]; then
|
||||||
|
_err "No user found. If this is your first time deploying, please set PANOS_USER in ENV variables. You can delete it after you have successfully deployed the certs."
|
||||||
|
return 1
|
||||||
|
elif [ -z "$_panos_pass" ]; then
|
||||||
|
_err "No password found. If this is your first time deploying, please set PANOS_PASS in ENV variables. You can delete it after you have successfully deployed the certs."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
# Generate a new API key if no valid API key is found
|
||||||
|
if [ -z "$_panos_key" ]; then
|
||||||
|
_debug "**** Generating new PANOS API KEY ****"
|
||||||
|
deployer keygen
|
||||||
|
_savedeployconf PANOS_KEY "$_panos_key" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Confirm that a valid key was generated
|
||||||
|
if [ -z "$_panos_key" ]; then
|
||||||
|
_err "Unable to generate an API key. The user and pass may be invalid or not authorized to generate a new key. Please check the PANOS_USER and PANOS_PASS credentials and try again"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
deployer cert
|
||||||
|
deployer key
|
||||||
|
deployer commit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
123
deploy/peplink.sh
Normal file
123
deploy/peplink.sh
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to deploy cert to Peplink Routers
|
||||||
|
#
|
||||||
|
# The following environment variables must be set:
|
||||||
|
#
|
||||||
|
# PEPLINK_Hostname - Peplink hostname
|
||||||
|
# PEPLINK_Username - Peplink username to login
|
||||||
|
# PEPLINK_Password - Peplink password to login
|
||||||
|
#
|
||||||
|
# The following environmental variables may be set if you don't like their
|
||||||
|
# default values:
|
||||||
|
#
|
||||||
|
# PEPLINK_Certtype - Certificate type to target for replacement
|
||||||
|
# defaults to "webadmin", can be one of:
|
||||||
|
# * "chub" (ContentHub)
|
||||||
|
# * "openvpn" (OpenVPN CA)
|
||||||
|
# * "portal" (Captive Portal SSL)
|
||||||
|
# * "webadmin" (Web Admin SSL)
|
||||||
|
# * "webproxy" (Proxy Root CA)
|
||||||
|
# * "wwan_ca" (Wi-Fi WAN CA)
|
||||||
|
# * "wwan_client" (Wi-Fi WAN Client)
|
||||||
|
# PEPLINK_Scheme - defaults to "https"
|
||||||
|
# PEPLINK_Port - defaults to "443"
|
||||||
|
#
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
_peplink_get_cookie_data() {
|
||||||
|
grep -i "\W$1=" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
|
||||||
|
}
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
peplink_deploy() {
|
||||||
|
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
|
||||||
|
# Get Hostname, Username and Password, but don't save until we successfully authenticate
|
||||||
|
_getdeployconf PEPLINK_Hostname
|
||||||
|
_getdeployconf PEPLINK_Username
|
||||||
|
_getdeployconf PEPLINK_Password
|
||||||
|
if [ -z "${PEPLINK_Hostname:-}" ] || [ -z "${PEPLINK_Username:-}" ] || [ -z "${PEPLINK_Password:-}" ]; then
|
||||||
|
_err "PEPLINK_Hostname & PEPLINK_Username & PEPLINK_Password must be set"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 PEPLINK_Hostname "$PEPLINK_Hostname"
|
||||||
|
_debug2 PEPLINK_Username "$PEPLINK_Username"
|
||||||
|
_secure_debug2 PEPLINK_Password "$PEPLINK_Password"
|
||||||
|
|
||||||
|
# Optional certificate type, scheme, and port for Peplink
|
||||||
|
_getdeployconf PEPLINK_Certtype
|
||||||
|
_getdeployconf PEPLINK_Scheme
|
||||||
|
_getdeployconf PEPLINK_Port
|
||||||
|
|
||||||
|
# Don't save the certificate type until we verify it exists and is supported
|
||||||
|
_savedeployconf PEPLINK_Scheme "$PEPLINK_Scheme"
|
||||||
|
_savedeployconf PEPLINK_Port "$PEPLINK_Port"
|
||||||
|
|
||||||
|
# Default vaules for certificate type, scheme, and port
|
||||||
|
[ -n "${PEPLINK_Certtype}" ] || PEPLINK_Certtype="webadmin"
|
||||||
|
[ -n "${PEPLINK_Scheme}" ] || PEPLINK_Scheme="https"
|
||||||
|
[ -n "${PEPLINK_Port}" ] || PEPLINK_Port="443"
|
||||||
|
|
||||||
|
_debug2 PEPLINK_Certtype "$PEPLINK_Certtype"
|
||||||
|
_debug2 PEPLINK_Scheme "$PEPLINK_Scheme"
|
||||||
|
_debug2 PEPLINK_Port "$PEPLINK_Port"
|
||||||
|
|
||||||
|
_base_url="$PEPLINK_Scheme://$PEPLINK_Hostname:$PEPLINK_Port"
|
||||||
|
_debug _base_url "$_base_url"
|
||||||
|
|
||||||
|
# Login, get the auth token from the cookie
|
||||||
|
_info "Logging into $PEPLINK_Hostname:$PEPLINK_Port"
|
||||||
|
encoded_username="$(printf "%s" "$PEPLINK_Username" | _url_encode)"
|
||||||
|
encoded_password="$(printf "%s" "$PEPLINK_Password" | _url_encode)"
|
||||||
|
response=$(_post "func=login&username=$encoded_username&password=$encoded_password" "$_base_url/cgi-bin/MANGA/api.cgi")
|
||||||
|
auth_token=$(_peplink_get_cookie_data "bauth" <"$HTTP_HEADER")
|
||||||
|
_debug3 response "$response"
|
||||||
|
_debug auth_token "$auth_token"
|
||||||
|
|
||||||
|
if [ -z "$auth_token" ]; then
|
||||||
|
_err "Unable to authenticate to $PEPLINK_Hostname:$PEPLINK_Port using $PEPLINK_Scheme."
|
||||||
|
_err "Check your username and password."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_H1="Cookie: $auth_token"
|
||||||
|
export _H1
|
||||||
|
_debug2 H1 "${_H1}"
|
||||||
|
|
||||||
|
# Now that we know the hostnameusername and password are good, save them
|
||||||
|
_savedeployconf PEPLINK_Hostname "$PEPLINK_Hostname"
|
||||||
|
_savedeployconf PEPLINK_Username "$PEPLINK_Username"
|
||||||
|
_savedeployconf PEPLINK_Password "$PEPLINK_Password"
|
||||||
|
|
||||||
|
_info "Generate form POST request"
|
||||||
|
|
||||||
|
encoded_key="$(_url_encode <"$_ckey")"
|
||||||
|
encoded_fullchain="$(_url_encode <"$_cfullchain")"
|
||||||
|
body="cert_type=$PEPLINK_Certtype&cert_uid=§ion=CERT_modify&key_pem=$encoded_key&key_pem_passphrase=&key_pem_passphrase_confirm=&cert_pem=$encoded_fullchain"
|
||||||
|
_debug3 body "$body"
|
||||||
|
|
||||||
|
_info "Upload $PEPLINK_Certtype certificate to the Peplink"
|
||||||
|
|
||||||
|
response=$(_post "$body" "$_base_url/cgi-bin/MANGA/admin.cgi")
|
||||||
|
_debug3 response "$response"
|
||||||
|
|
||||||
|
if echo "$response" | grep 'Success' >/dev/null; then
|
||||||
|
# We've verified this certificate type is valid, so save it
|
||||||
|
_savedeployconf PEPLINK_Certtype "$PEPLINK_Certtype"
|
||||||
|
_info "Certificate was updated"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Unable to update certificate, error code $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
132
deploy/proxmoxve.sh
Normal file
132
deploy/proxmoxve.sh
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Deploy certificates to a proxmox virtual environment node using the API.
|
||||||
|
#
|
||||||
|
# Environment variables that can be set are:
|
||||||
|
# `DEPLOY_PROXMOXVE_SERVER`: The hostname of the proxmox ve node. Defaults to
|
||||||
|
# _cdomain.
|
||||||
|
# `DEPLOY_PROXMOXVE_SERVER_PORT`: The port number the management interface is on.
|
||||||
|
# Defaults to 8006.
|
||||||
|
# `DEPLOY_PROXMOXVE_NODE_NAME`: The name of the node we'll be connecting to.
|
||||||
|
# Defaults to the host portion of the server
|
||||||
|
# domain name.
|
||||||
|
# `DEPLOY_PROXMOXVE_USER`: The user we'll connect as. Defaults to root.
|
||||||
|
# `DEPLOY_PROXMOXVE_USER_REALM`: The authentication realm the user authenticates
|
||||||
|
# with. Defaults to pam.
|
||||||
|
# `DEPLOY_PROXMOXVE_API_TOKEN_NAME`: The name of the API token created for the
|
||||||
|
# user account. Defaults to acme.
|
||||||
|
# `DEPLOY_PROXMOXVE_API_TOKEN_KEY`: The API token. Required.
|
||||||
|
|
||||||
|
proxmoxve_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug2 _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
# "Sane" defaults.
|
||||||
|
_getdeployconf DEPLOY_PROXMOXVE_SERVER
|
||||||
|
if [ -z "$DEPLOY_PROXMOXVE_SERVER" ]; then
|
||||||
|
_target_hostname="$_cdomain"
|
||||||
|
else
|
||||||
|
_target_hostname="$DEPLOY_PROXMOXVE_SERVER"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXVE_SERVER "$DEPLOY_PROXMOXVE_SERVER"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXVE_SERVER "$_target_hostname"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PROXMOXVE_SERVER_PORT
|
||||||
|
if [ -z "$DEPLOY_PROXMOXVE_SERVER_PORT" ]; then
|
||||||
|
_target_port="8006"
|
||||||
|
else
|
||||||
|
_target_port="$DEPLOY_PROXMOXVE_SERVER_PORT"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXVE_SERVER_PORT "$DEPLOY_PROXMOXVE_SERVER_PORT"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXVE_SERVER_PORT "$_target_port"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PROXMOXVE_NODE_NAME
|
||||||
|
if [ -z "$DEPLOY_PROXMOXVE_NODE_NAME" ]; then
|
||||||
|
_node_name=$(echo "$_target_hostname" | cut -d. -f1)
|
||||||
|
else
|
||||||
|
_node_name="$DEPLOY_PROXMOXVE_NODE_NAME"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXVE_NODE_NAME "$DEPLOY_PROXMOXVE_NODE_NAME"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXVE_NODE_NAME "$_node_name"
|
||||||
|
|
||||||
|
# Complete URL.
|
||||||
|
_target_url="https://${_target_hostname}:${_target_port}/api2/json/nodes/${_node_name}/certificates/custom"
|
||||||
|
_debug TARGET_URL "$_target_url"
|
||||||
|
|
||||||
|
# More "sane" defaults.
|
||||||
|
_getdeployconf DEPLOY_PROXMOXVE_USER
|
||||||
|
if [ -z "$DEPLOY_PROXMOXVE_USER" ]; then
|
||||||
|
_proxmoxve_user="root"
|
||||||
|
else
|
||||||
|
_proxmoxve_user="$DEPLOY_PROXMOXVE_USER"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXVE_USER "$DEPLOY_PROXMOXVE_USER"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXVE_USER "$_proxmoxve_user"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PROXMOXVE_USER_REALM
|
||||||
|
if [ -z "$DEPLOY_PROXMOXVE_USER_REALM" ]; then
|
||||||
|
_proxmoxve_user_realm="pam"
|
||||||
|
else
|
||||||
|
_proxmoxve_user_realm="$DEPLOY_PROXMOXVE_USER_REALM"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXVE_USER_REALM "$DEPLOY_PROXMOXVE_USER_REALM"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXVE_USER_REALM "$_proxmoxve_user_realm"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_PROXMOXVE_API_TOKEN_NAME
|
||||||
|
if [ -z "$DEPLOY_PROXMOXVE_API_TOKEN_NAME" ]; then
|
||||||
|
_proxmoxve_api_token_name="acme"
|
||||||
|
else
|
||||||
|
_proxmoxve_api_token_name="$DEPLOY_PROXMOXVE_API_TOKEN_NAME"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_NAME "$DEPLOY_PROXMOXVE_API_TOKEN_NAME"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXVE_API_TOKEN_NAME "$_proxmoxve_api_token_name"
|
||||||
|
|
||||||
|
# This is required.
|
||||||
|
_getdeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY
|
||||||
|
if [ -z "$DEPLOY_PROXMOXVE_API_TOKEN_KEY" ]; then
|
||||||
|
_err "API key not provided."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
_proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
|
||||||
|
_savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
|
||||||
|
fi
|
||||||
|
_debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY "$_proxmoxve_api_token_key"
|
||||||
|
|
||||||
|
# PVE API Token header value. Used in "Authorization: PVEAPIToken".
|
||||||
|
_proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
|
||||||
|
_debug2 "Auth Header" "$_proxmoxve_header_api_token"
|
||||||
|
|
||||||
|
# Ugly. I hate putting heredocs inside functions because heredocs don't
|
||||||
|
# account for whitespace correctly but it _does_ work and is several times
|
||||||
|
# cleaner than anything else I had here.
|
||||||
|
#
|
||||||
|
# This dumps the json payload to a variable that should be passable to the
|
||||||
|
# _psot function.
|
||||||
|
_json_payload=$(
|
||||||
|
cat <<HEREDOC
|
||||||
|
{
|
||||||
|
"certificates": "$(tr '\n' ':' <"$_cfullchain" | sed 's/:/\\n/g')",
|
||||||
|
"key": "$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')",
|
||||||
|
"node":"$_node_name",
|
||||||
|
"restart":"1",
|
||||||
|
"force":"1"
|
||||||
|
}
|
||||||
|
HEREDOC
|
||||||
|
)
|
||||||
|
_debug2 Payload "$_json_payload"
|
||||||
|
|
||||||
|
_info "Push certificates to server"
|
||||||
|
export HTTPS_INSECURE=1
|
||||||
|
export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
|
||||||
|
_post "$_json_payload" "$_target_url" "" POST "application/json"
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,8 @@
|
|||||||
# export QINIU_AK="QINIUACCESSKEY"
|
# export QINIU_AK="QINIUACCESSKEY"
|
||||||
# export QINIU_SK="QINIUSECRETKEY"
|
# export QINIU_SK="QINIUSECRETKEY"
|
||||||
# export QINIU_CDN_DOMAIN="cdn.example.com"
|
# export QINIU_CDN_DOMAIN="cdn.example.com"
|
||||||
|
# If you have more than one domain, just
|
||||||
|
# export QINIU_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
|
||||||
|
|
||||||
QINIU_API_BASE="https://api.qiniu.com"
|
QINIU_API_BASE="https://api.qiniu.com"
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ qiniu_deploy() {
|
|||||||
sslcert_access_token="$(_make_access_token "$sslcert_path")"
|
sslcert_access_token="$(_make_access_token "$sslcert_path")"
|
||||||
_debug sslcert_access_token "$sslcert_access_token"
|
_debug sslcert_access_token "$sslcert_access_token"
|
||||||
export _H1="Authorization: QBox $sslcert_access_token"
|
export _H1="Authorization: QBox $sslcert_access_token"
|
||||||
sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64 "multiline")
|
sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64)
|
||||||
|
|
||||||
if ! _contains "$sslcert_response" "certID"; then
|
if ! _contains "$sslcert_response" "certID"; then
|
||||||
_err "Error in creating certificate:"
|
_err "Error in creating certificate:"
|
||||||
@ -67,21 +69,23 @@ qiniu_deploy() {
|
|||||||
_debug certId "$_certId"
|
_debug certId "$_certId"
|
||||||
|
|
||||||
## update domain ssl config
|
## update domain ssl config
|
||||||
update_path="/domain/$QINIU_CDN_DOMAIN/httpsconf"
|
|
||||||
update_body="{\"certid\":$_certId,\"forceHttps\":false}"
|
update_body="{\"certid\":$_certId,\"forceHttps\":false}"
|
||||||
|
for domain in $QINIU_CDN_DOMAIN; do
|
||||||
|
update_path="/domain/$domain/httpsconf"
|
||||||
update_access_token="$(_make_access_token "$update_path")"
|
update_access_token="$(_make_access_token "$update_path")"
|
||||||
_debug update_access_token "$update_access_token"
|
_debug update_access_token "$update_access_token"
|
||||||
export _H1="Authorization: QBox $update_access_token"
|
export _H1="Authorization: QBox $update_access_token"
|
||||||
update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64 "multiline")
|
update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64)
|
||||||
|
|
||||||
if _contains "$update_response" "error"; then
|
if _contains "$update_response" "error"; then
|
||||||
_err "Error in updating domain httpsconf:"
|
_err "Error in updating domain $domain httpsconf:"
|
||||||
_err "$update_response"
|
_err "$update_response"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug update_response "$update_response"
|
_debug update_response "$update_response"
|
||||||
_info "Certificate successfully deployed"
|
_info "Domain $domain certificate has been deployed successfully"
|
||||||
|
done
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
# ```sh
|
# ```sh
|
||||||
# export ROUTER_OS_USERNAME=certuser
|
# export ROUTER_OS_USERNAME=certuser
|
||||||
# export ROUTER_OS_HOST=router.example.com
|
# export ROUTER_OS_HOST=router.example.com
|
||||||
|
# export ROUTER_OS_PORT=22
|
||||||
#
|
#
|
||||||
# acme.sh --deploy -d ftp.example.com --deploy-hook routeros
|
# acme.sh --deploy -d ftp.example.com --deploy-hook routeros
|
||||||
# ```
|
# ```
|
||||||
@ -48,6 +49,16 @@
|
|||||||
# One optional thing to do as well is to create a script that updates
|
# One optional thing to do as well is to create a script that updates
|
||||||
# all the required services and run that script in a single command.
|
# all the required services and run that script in a single command.
|
||||||
#
|
#
|
||||||
|
# To adopt parameters to `scp` and/or `ssh` set the optional
|
||||||
|
# `ROUTER_OS_SSH_CMD` and `ROUTER_OS_SCP_CMD` variables accordingly,
|
||||||
|
# see ssh(1) and scp(1) for parameters to those commands.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# ```ssh
|
||||||
|
# export ROUTER_OS_SSH_CMD="ssh -i /acme.sh/.ssh/router.example.com -o UserKnownHostsFile=/acme.sh/.ssh/known_hosts"
|
||||||
|
# export ROUTER_OS_SCP_CMD="scp -i /acme.sh/.ssh/router.example.com -o UserKnownHostsFile=/acme.sh/.ssh/known_hosts"
|
||||||
|
# ````
|
||||||
|
#
|
||||||
# returns 0 means success, otherwise error.
|
# returns 0 means success, otherwise error.
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
@ -59,6 +70,7 @@ routeros_deploy() {
|
|||||||
_ccert="$3"
|
_ccert="$3"
|
||||||
_cca="$4"
|
_cca="$4"
|
||||||
_cfullchain="$5"
|
_cfullchain="$5"
|
||||||
|
_err_code=0
|
||||||
|
|
||||||
_debug _cdomain "$_cdomain"
|
_debug _cdomain "$_cdomain"
|
||||||
_debug _ckey "$_ckey"
|
_debug _ckey "$_ckey"
|
||||||
@ -66,46 +78,127 @@ routeros_deploy() {
|
|||||||
_debug _cca "$_cca"
|
_debug _cca "$_cca"
|
||||||
_debug _cfullchain "$_cfullchain"
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_HOST
|
||||||
|
|
||||||
if [ -z "$ROUTER_OS_HOST" ]; then
|
if [ -z "$ROUTER_OS_HOST" ]; then
|
||||||
_debug "Using _cdomain as ROUTER_OS_HOST, please set if not correct."
|
_debug "Using _cdomain as ROUTER_OS_HOST, please set if not correct."
|
||||||
ROUTER_OS_HOST="$_cdomain"
|
ROUTER_OS_HOST="$_cdomain"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_USERNAME
|
||||||
|
|
||||||
if [ -z "$ROUTER_OS_USERNAME" ]; then
|
if [ -z "$ROUTER_OS_USERNAME" ]; then
|
||||||
_err "Need to set the env variable ROUTER_OS_USERNAME"
|
_err "Need to set the env variable ROUTER_OS_USERNAME"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_PORT
|
||||||
|
|
||||||
|
if [ -z "$ROUTER_OS_PORT" ]; then
|
||||||
|
_debug "Using default port 22 as ROUTER_OS_PORT, please set if not correct."
|
||||||
|
ROUTER_OS_PORT=22
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_SSH_CMD
|
||||||
|
|
||||||
|
if [ -z "$ROUTER_OS_SSH_CMD" ]; then
|
||||||
|
_debug "Use default ssh setup."
|
||||||
|
ROUTER_OS_SSH_CMD="ssh -p $ROUTER_OS_PORT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_SCP_CMD
|
||||||
|
|
||||||
|
if [ -z "$ROUTER_OS_SCP_CMD" ]; then
|
||||||
|
_debug "USe default scp setup."
|
||||||
|
ROUTER_OS_SCP_CMD="scp -P $ROUTER_OS_PORT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_getdeployconf ROUTER_OS_ADDITIONAL_SERVICES
|
||||||
|
|
||||||
if [ -z "$ROUTER_OS_ADDITIONAL_SERVICES" ]; then
|
if [ -z "$ROUTER_OS_ADDITIONAL_SERVICES" ]; then
|
||||||
_debug "Not enabling additional services"
|
_debug "Not enabling additional services"
|
||||||
ROUTER_OS_ADDITIONAL_SERVICES=""
|
ROUTER_OS_ADDITIONAL_SERVICES=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Trying to push key '$_ckey' to router"
|
_savedeployconf ROUTER_OS_HOST "$ROUTER_OS_HOST"
|
||||||
scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key"
|
_savedeployconf ROUTER_OS_USERNAME "$ROUTER_OS_USERNAME"
|
||||||
_info "Trying to push cert '$_cfullchain' to router"
|
_savedeployconf ROUTER_OS_PORT "$ROUTER_OS_PORT"
|
||||||
scp "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer"
|
_savedeployconf ROUTER_OS_SSH_CMD "$ROUTER_OS_SSH_CMD"
|
||||||
DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=admin policy=ftp,read,write,password,sensitive
|
_savedeployconf ROUTER_OS_SCP_CMD "$ROUTER_OS_SCP_CMD"
|
||||||
source=\"## generated by routeros deploy script in acme.sh
|
_savedeployconf ROUTER_OS_ADDITIONAL_SERVICES "$ROUTER_OS_ADDITIONAL_SERVICES"
|
||||||
\n/certificate remove [ find name=$_cdomain.cer_0 ]
|
|
||||||
\n/certificate remove [ find name=$_cdomain.cer_1 ]
|
# push key to routeros
|
||||||
\ndelay 1
|
if ! _scp_certificate "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key"; then
|
||||||
\n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\"
|
return $_err_code
|
||||||
\n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\"
|
fi
|
||||||
\ndelay 1
|
|
||||||
\n/file remove $_cdomain.cer
|
# push certificate chain to routeros
|
||||||
\n/file remove $_cdomain.key
|
if ! _scp_certificate "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer"; then
|
||||||
\ndelay 2
|
return $_err_code
|
||||||
\n/ip service set www-ssl certificate=$_cdomain.cer_0
|
fi
|
||||||
\n$ROUTER_OS_ADDITIONAL_SERVICES
|
|
||||||
|
DEPLOY_SCRIPT_CMD=":do {/system script remove \"LECertDeploy-$_cdomain\" } on-error={ }; \
|
||||||
|
/system script add name=\"LECertDeploy-$_cdomain\" owner=$ROUTER_OS_USERNAME \
|
||||||
|
comment=\"generated by routeros deploy script in acme.sh\" \
|
||||||
|
source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
|
||||||
|
\n/certificate remove [ find name=$_cdomain.cer_1 ];\
|
||||||
|
\n/certificate remove [ find name=$_cdomain.cer_2 ];\
|
||||||
|
\ndelay 1;\
|
||||||
|
\n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\";\
|
||||||
|
\n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\";\
|
||||||
|
\ndelay 1;\
|
||||||
|
\n:do {/file remove $_cdomain.cer; } on-error={ }\
|
||||||
|
\n:do {/file remove $_cdomain.key; } on-error={ }\
|
||||||
|
\ndelay 2;\
|
||||||
|
\n/ip service set www-ssl certificate=$_cdomain.cer_0;\
|
||||||
|
\n$ROUTER_OS_ADDITIONAL_SERVICES;\
|
||||||
\n\"
|
\n\"
|
||||||
"
|
"
|
||||||
# shellcheck disable=SC2029
|
|
||||||
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "$DEPLOY_SCRIPT_CMD"
|
if ! _ssh_remote_cmd "$DEPLOY_SCRIPT_CMD"; then
|
||||||
# shellcheck disable=SC2029
|
return $_err_code
|
||||||
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "/system script run \"LE Cert Deploy - $_cdomain\""
|
fi
|
||||||
# shellcheck disable=SC2029
|
|
||||||
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "/system script remove \"LE Cert Deploy - $_cdomain\""
|
if ! _ssh_remote_cmd "/system script run \"LECertDeploy-$_cdomain\""; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _ssh_remote_cmd "/system script remove \"LECertDeploy-$_cdomain\""; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# inspired by deploy/ssh.sh
|
||||||
|
_ssh_remote_cmd() {
|
||||||
|
_cmd="$1"
|
||||||
|
_secure_debug "Remote commands to execute: $_cmd"
|
||||||
|
_info "Submitting sequence of commands to routeros"
|
||||||
|
# quotations in bash cmd below intended. Squash travis spellcheck error
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
$ROUTER_OS_SSH_CMD "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "$_cmd"
|
||||||
|
_err_code="$?"
|
||||||
|
|
||||||
|
if [ "$_err_code" != "0" ]; then
|
||||||
|
_err "Error code $_err_code returned from routeros"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $_err_code
|
||||||
|
}
|
||||||
|
|
||||||
|
_scp_certificate() {
|
||||||
|
_src="$1"
|
||||||
|
_dst="$2"
|
||||||
|
_secure_debug "scp '$_src' to '$_dst'"
|
||||||
|
_info "Push key '$_src' to routeros"
|
||||||
|
|
||||||
|
$ROUTER_OS_SCP_CMD "$_src" "$_dst"
|
||||||
|
_err_code="$?"
|
||||||
|
|
||||||
|
if [ "$_err_code" != "0" ]; then
|
||||||
|
_err "Error code $_err_code returned from scp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $_err_code
|
||||||
|
}
|
||||||
|
477
deploy/ssh.sh
477
deploy/ssh.sh
@ -12,15 +12,19 @@
|
|||||||
# Only a username is required. All others are optional.
|
# Only a username is required. All others are optional.
|
||||||
#
|
#
|
||||||
# The following examples are for QNAP NAS running QTS 4.2
|
# The following examples are for QNAP NAS running QTS 4.2
|
||||||
# export DEPLOY_SSH_CMD="" # defaults to ssh
|
# export DEPLOY_SSH_CMD="" # defaults to "ssh -T"
|
||||||
# export DEPLOY_SSH_USER="admin" # required
|
# export DEPLOY_SSH_USER="admin" # required
|
||||||
# export DEPLOY_SSH_SERVER="qnap" # defaults to domain name
|
# export DEPLOY_SSH_SERVER="host1 host2:8022 192.168.0.1:9022" # defaults to domain name, support multiple servers with optional port
|
||||||
# export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
|
# export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
|
||||||
# export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem"
|
# export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem"
|
||||||
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
|
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
|
||||||
# export DEPLOY_SSH_FULLCHAIN=""
|
# export DEPLOY_SSH_FULLCHAIN=""
|
||||||
# export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart"
|
# export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart"
|
||||||
# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes
|
# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes or previously saved value
|
||||||
|
# export DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy" # path on remote system. Defaults to .acme_ssh_deploy
|
||||||
|
# export DEPLOY_SSH_MULTI_CALL="" # yes or no, default to no or previously saved value
|
||||||
|
# export DEPLOY_SSH_USE_SCP="" yes or no, default to no
|
||||||
|
# export DEPLOY_SSH_SCP_CMD="" defaults to "scp -q"
|
||||||
#
|
#
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
@ -31,15 +35,7 @@ ssh_deploy() {
|
|||||||
_ccert="$3"
|
_ccert="$3"
|
||||||
_cca="$4"
|
_cca="$4"
|
||||||
_cfullchain="$5"
|
_cfullchain="$5"
|
||||||
_cmdstr=""
|
_deploy_ssh_servers=""
|
||||||
_homedir='~'
|
|
||||||
_backupprefix="$_homedir/.acme_ssh_deploy/$_cdomain-backup"
|
|
||||||
_backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
|
|
||||||
|
|
||||||
if [ -f "$DOMAIN_CONF" ]; then
|
|
||||||
# shellcheck disable=SC1090
|
|
||||||
. "$DOMAIN_CONF"
|
|
||||||
fi
|
|
||||||
|
|
||||||
_debug _cdomain "$_cdomain"
|
_debug _cdomain "$_cdomain"
|
||||||
_debug _ckey "$_ckey"
|
_debug _ckey "$_ckey"
|
||||||
@ -48,136 +44,163 @@ ssh_deploy() {
|
|||||||
_debug _cfullchain "$_cfullchain"
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
# USER is required to login by SSH to remote host.
|
# USER is required to login by SSH to remote host.
|
||||||
|
_migratedeployconf Le_Deploy_ssh_user DEPLOY_SSH_USER
|
||||||
|
_getdeployconf DEPLOY_SSH_USER
|
||||||
|
_debug2 DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
|
||||||
if [ -z "$DEPLOY_SSH_USER" ]; then
|
if [ -z "$DEPLOY_SSH_USER" ]; then
|
||||||
if [ -z "$Le_Deploy_ssh_user" ]; then
|
|
||||||
_err "DEPLOY_SSH_USER not defined."
|
_err "DEPLOY_SSH_USER not defined."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
else
|
_savedeployconf DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
|
||||||
Le_Deploy_ssh_user="$DEPLOY_SSH_USER"
|
|
||||||
_savedomainconf Le_Deploy_ssh_user "$Le_Deploy_ssh_user"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# SERVER is optional. If not provided then use _cdomain
|
# SERVER is optional. If not provided then use _cdomain
|
||||||
if [ -n "$DEPLOY_SSH_SERVER" ]; then
|
_migratedeployconf Le_Deploy_ssh_server DEPLOY_SSH_SERVER
|
||||||
Le_Deploy_ssh_server="$DEPLOY_SSH_SERVER"
|
_getdeployconf DEPLOY_SSH_SERVER
|
||||||
_savedomainconf Le_Deploy_ssh_server "$Le_Deploy_ssh_server"
|
_debug2 DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
|
||||||
elif [ -z "$Le_Deploy_ssh_server" ]; then
|
if [ -z "$DEPLOY_SSH_SERVER" ]; then
|
||||||
Le_Deploy_ssh_server="$_cdomain"
|
DEPLOY_SSH_SERVER="$_cdomain"
|
||||||
fi
|
fi
|
||||||
|
_savedeployconf DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
|
||||||
|
|
||||||
# CMD is optional. If not provided then use ssh
|
# CMD is optional. If not provided then use ssh
|
||||||
if [ -n "$DEPLOY_SSH_CMD" ]; then
|
_migratedeployconf Le_Deploy_ssh_cmd DEPLOY_SSH_CMD
|
||||||
Le_Deploy_ssh_cmd="$DEPLOY_SSH_CMD"
|
_getdeployconf DEPLOY_SSH_CMD
|
||||||
_savedomainconf Le_Deploy_ssh_cmd "$Le_Deploy_ssh_cmd"
|
_debug2 DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
|
||||||
elif [ -z "$Le_Deploy_ssh_cmd" ]; then
|
if [ -z "$DEPLOY_SSH_CMD" ]; then
|
||||||
Le_Deploy_ssh_cmd="ssh"
|
DEPLOY_SSH_CMD="ssh -T"
|
||||||
fi
|
fi
|
||||||
|
_savedeployconf DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
|
||||||
|
|
||||||
# BACKUP is optional. If not provided then default to yes
|
# BACKUP is optional. If not provided then default to previously saved value or yes.
|
||||||
if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then
|
_migratedeployconf Le_Deploy_ssh_backup DEPLOY_SSH_BACKUP
|
||||||
Le_Deploy_ssh_backup="no"
|
_getdeployconf DEPLOY_SSH_BACKUP
|
||||||
elif [ -z "$Le_Deploy_ssh_backup" ]; then
|
_debug2 DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
|
||||||
Le_Deploy_ssh_backup="yes"
|
if [ -z "$DEPLOY_SSH_BACKUP" ]; then
|
||||||
|
DEPLOY_SSH_BACKUP="yes"
|
||||||
fi
|
fi
|
||||||
_savedomainconf Le_Deploy_ssh_backup "$Le_Deploy_ssh_backup"
|
_savedeployconf DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
|
||||||
|
|
||||||
_info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server"
|
# BACKUP_PATH is optional. If not provided then default to previously saved value or .acme_ssh_deploy
|
||||||
|
_migratedeployconf Le_Deploy_ssh_backup_path DEPLOY_SSH_BACKUP_PATH
|
||||||
|
_getdeployconf DEPLOY_SSH_BACKUP_PATH
|
||||||
|
_debug2 DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
|
||||||
|
if [ -z "$DEPLOY_SSH_BACKUP_PATH" ]; then
|
||||||
|
DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy"
|
||||||
|
fi
|
||||||
|
_savedeployconf DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
|
||||||
|
|
||||||
|
# MULTI_CALL is optional. If not provided then default to previously saved
|
||||||
|
# value (which may be undefined... equivalent to "no").
|
||||||
|
_migratedeployconf Le_Deploy_ssh_multi_call DEPLOY_SSH_MULTI_CALL
|
||||||
|
_getdeployconf DEPLOY_SSH_MULTI_CALL
|
||||||
|
_debug2 DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
|
||||||
|
if [ -z "$DEPLOY_SSH_MULTI_CALL" ]; then
|
||||||
|
DEPLOY_SSH_MULTI_CALL="no"
|
||||||
|
fi
|
||||||
|
_savedeployconf DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
|
||||||
|
|
||||||
# KEYFILE is optional.
|
# KEYFILE is optional.
|
||||||
# If provided then private key will be copied to provided filename.
|
# If provided then private key will be copied to provided filename.
|
||||||
|
_migratedeployconf Le_Deploy_ssh_keyfile DEPLOY_SSH_KEYFILE
|
||||||
|
_getdeployconf DEPLOY_SSH_KEYFILE
|
||||||
|
_debug2 DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
|
||||||
if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
|
if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
|
||||||
Le_Deploy_ssh_keyfile="$DEPLOY_SSH_KEYFILE"
|
_savedeployconf DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
|
||||||
_savedomainconf Le_Deploy_ssh_keyfile "$Le_Deploy_ssh_keyfile"
|
|
||||||
fi
|
|
||||||
if [ -n "$Le_Deploy_ssh_keyfile" ]; then
|
|
||||||
if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
|
||||||
# backup file we are about to overwrite.
|
|
||||||
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir >/dev/null;"
|
|
||||||
fi
|
|
||||||
# copy new certificate into file.
|
|
||||||
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;"
|
|
||||||
_info "will copy private key to remote file $Le_Deploy_ssh_keyfile"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# CERTFILE is optional.
|
# CERTFILE is optional.
|
||||||
# If provided then certificate will be copied or appended to provided filename.
|
# If provided then certificate will be copied or appended to provided filename.
|
||||||
|
_migratedeployconf Le_Deploy_ssh_certfile DEPLOY_SSH_CERTFILE
|
||||||
|
_getdeployconf DEPLOY_SSH_CERTFILE
|
||||||
|
_debug2 DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
|
||||||
if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
|
if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
|
||||||
Le_Deploy_ssh_certfile="$DEPLOY_SSH_CERTFILE"
|
_savedeployconf DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
|
||||||
_savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile"
|
|
||||||
fi
|
|
||||||
if [ -n "$Le_Deploy_ssh_certfile" ]; then
|
|
||||||
_pipe=">"
|
|
||||||
if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then
|
|
||||||
# if filename is same as previous file then append.
|
|
||||||
_pipe=">>"
|
|
||||||
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
|
||||||
# backup file we are about to overwrite.
|
|
||||||
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir >/dev/null;"
|
|
||||||
fi
|
|
||||||
# copy new certificate into file.
|
|
||||||
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;"
|
|
||||||
_info "will copy certificate to remote file $Le_Deploy_ssh_certfile"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# CAFILE is optional.
|
# CAFILE is optional.
|
||||||
# If provided then CA intermediate certificate will be copied or appended to provided filename.
|
# If provided then CA intermediate certificate will be copied or appended to provided filename.
|
||||||
|
_migratedeployconf Le_Deploy_ssh_cafile DEPLOY_SSH_CAFILE
|
||||||
|
_getdeployconf DEPLOY_SSH_CAFILE
|
||||||
|
_debug2 DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
|
||||||
if [ -n "$DEPLOY_SSH_CAFILE" ]; then
|
if [ -n "$DEPLOY_SSH_CAFILE" ]; then
|
||||||
Le_Deploy_ssh_cafile="$DEPLOY_SSH_CAFILE"
|
_savedeployconf DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
|
||||||
_savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile"
|
|
||||||
fi
|
|
||||||
if [ -n "$Le_Deploy_ssh_cafile" ]; then
|
|
||||||
_pipe=">"
|
|
||||||
if [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_keyfile" ] \
|
|
||||||
|| [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_certfile" ]; then
|
|
||||||
# if filename is same as previous file then append.
|
|
||||||
_pipe=">>"
|
|
||||||
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
|
||||||
# backup file we are about to overwrite.
|
|
||||||
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir >/dev/null;"
|
|
||||||
fi
|
|
||||||
# copy new certificate into file.
|
|
||||||
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;"
|
|
||||||
_info "will copy CA file to remote file $Le_Deploy_ssh_cafile"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# FULLCHAIN is optional.
|
# FULLCHAIN is optional.
|
||||||
# If provided then fullchain certificate will be copied or appended to provided filename.
|
# If provided then fullchain certificate will be copied or appended to provided filename.
|
||||||
|
_migratedeployconf Le_Deploy_ssh_fullchain DEPLOY_SSH_FULLCHAIN
|
||||||
|
_getdeployconf DEPLOY_SSH_FULLCHAIN
|
||||||
|
_debug2 DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
|
||||||
if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
|
if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
|
||||||
Le_Deploy_ssh_fullchain="$DEPLOY_SSH_FULLCHAIN"
|
_savedeployconf DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
|
||||||
_savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain"
|
|
||||||
fi
|
|
||||||
if [ -n "$Le_Deploy_ssh_fullchain" ]; then
|
|
||||||
_pipe=">"
|
|
||||||
if [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_keyfile" ] \
|
|
||||||
|| [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_certfile" ] \
|
|
||||||
|| [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_cafile" ]; then
|
|
||||||
# if filename is same as previous file then append.
|
|
||||||
_pipe=">>"
|
|
||||||
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
|
||||||
# backup file we are about to overwrite.
|
|
||||||
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir >/dev/null;"
|
|
||||||
fi
|
|
||||||
# copy new certificate into file.
|
|
||||||
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;"
|
|
||||||
_info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# REMOTE_CMD is optional.
|
# REMOTE_CMD is optional.
|
||||||
# If provided then this command will be executed on remote host.
|
# If provided then this command will be executed on remote host.
|
||||||
|
_migratedeployconf Le_Deploy_ssh_remote_cmd DEPLOY_SSH_REMOTE_CMD
|
||||||
|
_getdeployconf DEPLOY_SSH_REMOTE_CMD
|
||||||
|
_debug2 DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
|
||||||
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
|
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
|
||||||
Le_Deploy_ssh_remote_cmd="$DEPLOY_SSH_REMOTE_CMD"
|
_savedeployconf DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
|
||||||
_savedomainconf Le_Deploy_ssh_remote_cmd "$Le_Deploy_ssh_remote_cmd"
|
|
||||||
fi
|
|
||||||
if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then
|
|
||||||
_cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd;"
|
|
||||||
_info "Will execute remote command $Le_Deploy_ssh_remote_cmd"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$_cmdstr" ]; then
|
# USE_SCP is optional. If not provided then default to previously saved
|
||||||
_err "No remote commands to excute. Failed to deploy certificates to remote server"
|
# value (which may be undefined... equivalent to "no").
|
||||||
return 1
|
_getdeployconf DEPLOY_SSH_USE_SCP
|
||||||
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
|
_debug2 DEPLOY_SSH_USE_SCP "$DEPLOY_SSH_USE_SCP"
|
||||||
|
if [ -z "$DEPLOY_SSH_USE_SCP" ]; then
|
||||||
|
DEPLOY_SSH_USE_SCP="no"
|
||||||
|
fi
|
||||||
|
_savedeployconf DEPLOY_SSH_USE_SCP "$DEPLOY_SSH_USE_SCP"
|
||||||
|
|
||||||
|
# SCP_CMD is optional. If not provided then use scp
|
||||||
|
_getdeployconf DEPLOY_SSH_SCP_CMD
|
||||||
|
_debug2 DEPLOY_SSH_SCP_CMD "$DEPLOY_SSH_SCP_CMD"
|
||||||
|
if [ -z "$DEPLOY_SSH_SCP_CMD" ]; then
|
||||||
|
DEPLOY_SSH_SCP_CMD="scp -q"
|
||||||
|
fi
|
||||||
|
_savedeployconf DEPLOY_SSH_SCP_CMD "$DEPLOY_SSH_SCP_CMD"
|
||||||
|
|
||||||
|
if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
|
||||||
|
DEPLOY_SSH_MULTI_CALL="yes"
|
||||||
|
_info "Using scp as alternate method for copying files. Multicall Mode is implicit"
|
||||||
|
elif [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
_info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host"
|
||||||
|
else
|
||||||
|
_info "Required commands batched and sent in single call to remote host"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_deploy_ssh_servers="$DEPLOY_SSH_SERVER"
|
||||||
|
for DEPLOY_SSH_SERVER in $_deploy_ssh_servers; do
|
||||||
|
_ssh_deploy
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_ssh_deploy() {
|
||||||
|
_err_code=0
|
||||||
|
_cmdstr=""
|
||||||
|
_backupprefix=""
|
||||||
|
_backupdir=""
|
||||||
|
_local_cert_file=""
|
||||||
|
_local_ca_file=""
|
||||||
|
_local_full_file=""
|
||||||
|
|
||||||
|
case $DEPLOY_SSH_SERVER in
|
||||||
|
*:*)
|
||||||
|
_host=${DEPLOY_SSH_SERVER%:*}
|
||||||
|
_port=${DEPLOY_SSH_SERVER##*:}
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_host=$DEPLOY_SSH_SERVER
|
||||||
|
_port=
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
_info "Deploy certificates to remote server $DEPLOY_SSH_USER@$_host:$_port"
|
||||||
|
|
||||||
|
if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
|
||||||
|
_backupprefix="$DEPLOY_SSH_BACKUP_PATH/$_cdomain-backup"
|
||||||
|
_backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
|
||||||
# run cleanup on the backup directory, erase all older
|
# run cleanup on the backup directory, erase all older
|
||||||
# than 180 days (15552000 seconds).
|
# than 180 days (15552000 seconds).
|
||||||
_cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
|
_cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
|
||||||
@ -188,18 +211,252 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
|
|||||||
_cmdstr="mkdir -p $_backupdir; $_cmdstr"
|
_cmdstr="mkdir -p $_backupdir; $_cmdstr"
|
||||||
_info "Backup of old certificate files will be placed in remote directory $_backupdir"
|
_info "Backup of old certificate files will be placed in remote directory $_backupdir"
|
||||||
_info "Backup directories erased after 180 days."
|
_info "Backup directories erased after 180 days."
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_secure_debug "Remote commands to execute: " "$_cmdstr"
|
if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
|
||||||
_info "Submitting sequence of commands to remote server by ssh"
|
if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
|
||||||
|
# backup file we are about to overwrite.
|
||||||
|
_cmdstr="$_cmdstr cp $DEPLOY_SSH_KEYFILE $_backupdir >/dev/null;"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# copy new key into file.
|
||||||
|
if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
|
||||||
|
# scp the file
|
||||||
|
if ! _scp_remote_cmd "$_ckey" "$DEPLOY_SSH_KEYFILE"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# ssh echo to the file
|
||||||
|
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE;"
|
||||||
|
_info "will copy private key to remote file $DEPLOY_SSH_KEYFILE"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
|
||||||
|
_pipe=">"
|
||||||
|
if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
|
||||||
|
# if filename is same as previous file then append.
|
||||||
|
_pipe=">>"
|
||||||
|
elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
|
||||||
|
# backup file we are about to overwrite.
|
||||||
|
_cmdstr="$_cmdstr cp $DEPLOY_SSH_CERTFILE $_backupdir >/dev/null;"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# copy new certificate into file.
|
||||||
|
if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
|
||||||
|
# scp the file
|
||||||
|
_local_cert_file=$(_mktemp)
|
||||||
|
if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
|
||||||
|
cat "$_ckey" >>"$_local_cert_file"
|
||||||
|
fi
|
||||||
|
cat "$_ccert" >>"$_local_cert_file"
|
||||||
|
if ! _scp_remote_cmd "$_local_cert_file" "$DEPLOY_SSH_CERTFILE"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# ssh echo to the file
|
||||||
|
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $DEPLOY_SSH_CERTFILE;"
|
||||||
|
_info "will copy certificate to remote file $DEPLOY_SSH_CERTFILE"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEPLOY_SSH_CAFILE" ]; then
|
||||||
|
_pipe=">"
|
||||||
|
if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ] ||
|
||||||
|
[ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
|
||||||
|
# if filename is same as previous file then append.
|
||||||
|
_pipe=">>"
|
||||||
|
elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
|
||||||
|
# backup file we are about to overwrite.
|
||||||
|
_cmdstr="$_cmdstr cp $DEPLOY_SSH_CAFILE $_backupdir >/dev/null;"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# copy new certificate into file.
|
||||||
|
if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
|
||||||
|
# scp the file
|
||||||
|
_local_ca_file=$(_mktemp)
|
||||||
|
if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
|
||||||
|
cat "$_ckey" >>"$_local_ca_file"
|
||||||
|
fi
|
||||||
|
if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
|
||||||
|
cat "$_ccert" >>"$_local_ca_file"
|
||||||
|
fi
|
||||||
|
cat "$_cca" >>"$_local_ca_file"
|
||||||
|
if ! _scp_remote_cmd "$_local_ca_file" "$DEPLOY_SSH_CAFILE"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# ssh echo to the file
|
||||||
|
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $DEPLOY_SSH_CAFILE;"
|
||||||
|
_info "will copy CA file to remote file $DEPLOY_SSH_CAFILE"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
|
||||||
|
_pipe=">"
|
||||||
|
if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ] ||
|
||||||
|
[ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ] ||
|
||||||
|
[ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
|
||||||
|
# if filename is same as previous file then append.
|
||||||
|
_pipe=">>"
|
||||||
|
elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
|
||||||
|
# backup file we are about to overwrite.
|
||||||
|
_cmdstr="$_cmdstr cp $DEPLOY_SSH_FULLCHAIN $_backupdir >/dev/null;"
|
||||||
|
if [ "$DEPLOY_SSH_FULLCHAIN" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# copy new certificate into file.
|
||||||
|
if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
|
||||||
|
# scp the file
|
||||||
|
_local_full_file=$(_mktemp)
|
||||||
|
if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ]; then
|
||||||
|
cat "$_ckey" >>"$_local_full_file"
|
||||||
|
fi
|
||||||
|
if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ]; then
|
||||||
|
cat "$_ccert" >>"$_local_full_file"
|
||||||
|
fi
|
||||||
|
if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
|
||||||
|
cat "$_cca" >>"$_local_full_file"
|
||||||
|
fi
|
||||||
|
cat "$_cfullchain" >>"$_local_full_file"
|
||||||
|
if ! _scp_remote_cmd "$_local_full_file" "$DEPLOY_SSH_FULLCHAIN"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# ssh echo to the file
|
||||||
|
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $DEPLOY_SSH_FULLCHAIN;"
|
||||||
|
_info "will copy fullchain to remote file $DEPLOY_SSH_FULLCHAIN"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# cleanup local files if any
|
||||||
|
if [ -f "$_local_cert_file" ]; then
|
||||||
|
rm -f "$_local_cert_file"
|
||||||
|
fi
|
||||||
|
if [ -f "$_local_ca_file" ]; then
|
||||||
|
rm -f "$_local_ca_file"
|
||||||
|
fi
|
||||||
|
if [ -f "$_local_full_file" ]; then
|
||||||
|
rm -f "$_local_full_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
|
||||||
|
_cmdstr="$_cmdstr $DEPLOY_SSH_REMOTE_CMD;"
|
||||||
|
_info "Will execute remote command $DEPLOY_SSH_REMOTE_CMD"
|
||||||
|
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
_cmdstr=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if commands not all sent in multiple calls then all commands sent in a single SSH call now...
|
||||||
|
if [ -n "$_cmdstr" ]; then
|
||||||
|
if ! _ssh_remote_cmd "$_cmdstr"; then
|
||||||
|
return $_err_code
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# cleanup in case all is ok
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#cmd
|
||||||
|
_ssh_remote_cmd() {
|
||||||
|
_cmd="$1"
|
||||||
|
|
||||||
|
_ssh_cmd="$DEPLOY_SSH_CMD"
|
||||||
|
if [ -n "$_port" ]; then
|
||||||
|
_ssh_cmd="$_ssh_cmd -p $_port"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_secure_debug "Remote commands to execute: $_cmd"
|
||||||
|
_info "Submitting sequence of commands to remote server by $_ssh_cmd"
|
||||||
|
|
||||||
# quotations in bash cmd below intended. Squash travis spellcheck error
|
# quotations in bash cmd below intended. Squash travis spellcheck error
|
||||||
# shellcheck disable=SC2029
|
# shellcheck disable=SC2029
|
||||||
$Le_Deploy_ssh_cmd -T "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmdstr'"
|
$_ssh_cmd "$DEPLOY_SSH_USER@$_host" sh -c "'$_cmd'"
|
||||||
_ret="$?"
|
_err_code="$?"
|
||||||
|
|
||||||
if [ "$_ret" != "0" ]; then
|
if [ "$_err_code" != "0" ]; then
|
||||||
_err "Error code $_ret returned from $Le_Deploy_ssh_cmd"
|
_err "Error code $_err_code returned from ssh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return $_ret
|
return $_err_code
|
||||||
|
}
|
||||||
|
|
||||||
|
# cmd scp
|
||||||
|
_scp_remote_cmd() {
|
||||||
|
_src=$1
|
||||||
|
_dest=$2
|
||||||
|
|
||||||
|
_scp_cmd="$DEPLOY_SSH_SCP_CMD"
|
||||||
|
if [ -n "$_port" ]; then
|
||||||
|
_scp_cmd="$_scp_cmd -P $_port"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_secure_debug "Remote copy source $_src to destination $_dest"
|
||||||
|
_info "Submitting secure copy by $_scp_cmd"
|
||||||
|
|
||||||
|
$_scp_cmd "$_src" "$DEPLOY_SSH_USER"@"$_host":"$_dest"
|
||||||
|
_err_code="$?"
|
||||||
|
|
||||||
|
if [ "$_err_code" != "0" ]; then
|
||||||
|
_err "Error code $_err_code returned from scp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $_err_code
|
||||||
}
|
}
|
||||||
|
445
deploy/synology_dsm.sh
Normal file
445
deploy/synology_dsm.sh
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# ACME.sh 3rd party deploy plugin for Synology DSM
|
||||||
|
################################################################################
|
||||||
|
# Authors: Brian Hartvigsen (creator), https://github.com/tresni
|
||||||
|
# Martin Arndt (contributor), https://troublezone.net/
|
||||||
|
# Updated: 2023-07-03
|
||||||
|
# Issues: https://github.com/acmesh-official/acme.sh/issues/2727
|
||||||
|
################################################################################
|
||||||
|
# Usage (shown values are the examples):
|
||||||
|
# 1. Set required environment variables:
|
||||||
|
# - use automatically created temp admin user to authenticate
|
||||||
|
# export SYNO_USE_TEMP_ADMIN=1
|
||||||
|
# - or provide your own admin user credential to authenticate
|
||||||
|
# 1. export SYNO_USERNAME="adminUser"
|
||||||
|
# 2. export SYNO_PASSWORD="adminPassword"
|
||||||
|
# 2. Set optional environment variables
|
||||||
|
# - common optional variables
|
||||||
|
# - export SYNO_SCHEME="http" - defaults to "http"
|
||||||
|
# - export SYNO_HOSTNAME="localhost" - defaults to "localhost"
|
||||||
|
# - export SYNO_PORT="5000" - defaults to "5000"
|
||||||
|
# - export SYNO_CREATE=1 - to allow creating the cert if it doesn't exist
|
||||||
|
# - export SYNO_CERTIFICATE="" - to replace a specific cert by its
|
||||||
|
# description
|
||||||
|
# - temp admin optional variables
|
||||||
|
# - export SYNO_LOCAL_HOSTNAME=1 - if set to 1, force to treat hostname is
|
||||||
|
# targeting current local machine (since
|
||||||
|
# this method only locally supported)
|
||||||
|
# - exsiting admin 2FA-OTP optional variables
|
||||||
|
# - export SYNO_OTP_CODE="XXXXXX" - if set, script won't require to
|
||||||
|
# interactive input the OTP code
|
||||||
|
# - export SYNO_DEVICE_NAME="CertRenewal" - if set, script won't require to
|
||||||
|
# interactive input the device name
|
||||||
|
# - export SYNO_DEVICE_ID="" - (deprecated, auth with OTP code instead)
|
||||||
|
# required for omitting 2FA-OTP
|
||||||
|
# 3. Run command:
|
||||||
|
# acme.sh --deploy --deploy-hook synology_dsm -d example.com
|
||||||
|
################################################################################
|
||||||
|
# Dependencies:
|
||||||
|
# - curl
|
||||||
|
# - synouser & synogroup & synosetkeyvalue (Required for SYNO_USE_TEMP_ADMIN=1)
|
||||||
|
################################################################################
|
||||||
|
# Return value:
|
||||||
|
# 0 means success, otherwise error.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
########## Public functions ####################################################
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
synology_dsm_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
|
||||||
|
# Get username and password, but don't save until we authenticated successfully
|
||||||
|
_migratedeployconf SYNO_Username SYNO_USERNAME
|
||||||
|
_migratedeployconf SYNO_Password SYNO_PASSWORD
|
||||||
|
_migratedeployconf SYNO_Device_ID SYNO_DEVICE_ID
|
||||||
|
_migratedeployconf SYNO_Device_Name SYNO_DEVICE_NAME
|
||||||
|
_getdeployconf SYNO_USERNAME
|
||||||
|
_getdeployconf SYNO_PASSWORD
|
||||||
|
_getdeployconf SYNO_DEVICE_ID
|
||||||
|
_getdeployconf SYNO_DEVICE_NAME
|
||||||
|
|
||||||
|
# Prepare to use temp admin if SYNO_USE_TEMP_ADMIN is set
|
||||||
|
_getdeployconf SYNO_USE_TEMP_ADMIN
|
||||||
|
_check2cleardeployconfexp SYNO_USE_TEMP_ADMIN
|
||||||
|
_debug2 SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
|
||||||
|
|
||||||
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
|
||||||
|
if ! _exists synouser || ! _exists synogroup || ! _exists synosetkeyvalue; then
|
||||||
|
_err "Missing required tools to creat temp admin user, please set SYNO_USERNAME and SYNO_PASSWORD instead."
|
||||||
|
_err "Notice: temp admin user authorization method only supports local deployment on DSM."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if synouser --help 2>&1 | grep -q 'Permission denied'; then
|
||||||
|
_err "For creating temp admin user, the deploy script must be run as root."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "$SYNO_USERNAME" ] || _savedeployconf SYNO_USERNAME ""
|
||||||
|
[ -n "$SYNO_PASSWORD" ] || _savedeployconf SYNO_PASSWORD ""
|
||||||
|
|
||||||
|
_debug "Setting temp admin user credential..."
|
||||||
|
SYNO_USERNAME=sc-acmesh-tmp
|
||||||
|
SYNO_PASSWORD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
|
||||||
|
# Set 2FA-OTP settings to empty consider they won't be needed.
|
||||||
|
SYNO_DEVICE_ID=
|
||||||
|
SYNO_DEVICE_NAME=
|
||||||
|
SYNO_OTP_CODE=
|
||||||
|
else
|
||||||
|
_debug2 SYNO_USERNAME "$SYNO_USERNAME"
|
||||||
|
_secure_debug2 SYNO_PASSWORD "$SYNO_PASSWORD"
|
||||||
|
_debug2 SYNO_DEVICE_NAME "$SYNO_DEVICE_NAME"
|
||||||
|
_secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$SYNO_USERNAME" ] || [ -z "$SYNO_PASSWORD" ]; then
|
||||||
|
_err "You must set either SYNO_USE_TEMP_ADMIN, or set both SYNO_USERNAME and SYNO_PASSWORD."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Optional scheme, hostname and port for Synology DSM
|
||||||
|
_migratedeployconf SYNO_Scheme SYNO_SCHEME
|
||||||
|
_migratedeployconf SYNO_Hostname SYNO_HOSTNAME
|
||||||
|
_migratedeployconf SYNO_Port SYNO_PORT
|
||||||
|
_getdeployconf SYNO_SCHEME
|
||||||
|
_getdeployconf SYNO_HOSTNAME
|
||||||
|
_getdeployconf SYNO_PORT
|
||||||
|
|
||||||
|
# Default values for scheme, hostname and port
|
||||||
|
# Defaulting to localhost and http, because it's localhost…
|
||||||
|
[ -n "$SYNO_SCHEME" ] || SYNO_SCHEME=http
|
||||||
|
[ -n "$SYNO_HOSTNAME" ] || SYNO_HOSTNAME=localhost
|
||||||
|
[ -n "$SYNO_PORT" ] || SYNO_PORT=5000
|
||||||
|
_savedeployconf SYNO_SCHEME "$SYNO_SCHEME"
|
||||||
|
_savedeployconf SYNO_HOSTNAME "$SYNO_HOSTNAME"
|
||||||
|
_savedeployconf SYNO_PORT "$SYNO_PORT"
|
||||||
|
_debug2 SYNO_SCHEME "$SYNO_SCHEME"
|
||||||
|
_debug2 SYNO_HOSTNAME "$SYNO_HOSTNAME"
|
||||||
|
_debug2 SYNO_PORT "$SYNO_PORT"
|
||||||
|
|
||||||
|
# Get the certificate description, but don't save it until we verify it's real
|
||||||
|
_migratedeployconf SYNO_Certificate SYNO_CERTIFICATE "base64"
|
||||||
|
_getdeployconf SYNO_CERTIFICATE
|
||||||
|
_check2cleardeployconfexp SYNO_CERTIFICATE
|
||||||
|
_debug SYNO_CERTIFICATE "${SYNO_CERTIFICATE:-}"
|
||||||
|
|
||||||
|
# shellcheck disable=SC1003 # We are not trying to escape a single quote
|
||||||
|
if printf "%s" "$SYNO_CERTIFICATE" | grep '\\'; then
|
||||||
|
_err "Do not use a backslash (\) in your certificate description"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Getting API version..."
|
||||||
|
_base_url="$SYNO_SCHEME://$SYNO_HOSTNAME:$SYNO_PORT"
|
||||||
|
_debug _base_url "$_base_url"
|
||||||
|
response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
|
||||||
|
api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([^"]*\)".*/\1/p')
|
||||||
|
api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
|
||||||
|
_debug3 response "$response"
|
||||||
|
_debug3 api_path "$api_path"
|
||||||
|
_debug3 api_version "$api_version"
|
||||||
|
|
||||||
|
# Login, get the session ID and SynoToken from JSON
|
||||||
|
_info "Logging into $SYNO_HOSTNAME:$SYNO_PORT..."
|
||||||
|
encoded_username="$(printf "%s" "$SYNO_USERNAME" | _url_encode)"
|
||||||
|
encoded_password="$(printf "%s" "$SYNO_PASSWORD" | _url_encode)"
|
||||||
|
|
||||||
|
# ## START ## - DEPRECATED, for backward compatibility
|
||||||
|
_getdeployconf SYNO_TOTP_SECRET
|
||||||
|
|
||||||
|
if [ -n "$SYNO_TOTP_SECRET" ]; then
|
||||||
|
_info "WARNING: Usage of SYNO_TOTP_SECRET is deprecated!"
|
||||||
|
_info " See synology_dsm.sh script or ACME.sh Wiki page for details:"
|
||||||
|
_info " https://github.com/acmesh-official/acme.sh/wiki/Synology-NAS-Guide"
|
||||||
|
if ! _exists oathtool; then
|
||||||
|
_err "oathtool could not be found, install oathtool to use SYNO_TOTP_SECRET"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
DEPRECATED_otp_code="$(oathtool --base32 --totp "$SYNO_TOTP_SECRET" 2>/dev/null)"
|
||||||
|
|
||||||
|
if [ -z "$SYNO_DEVICE_ID" ]; then
|
||||||
|
_getdeployconf SYNO_DID
|
||||||
|
[ -n "$SYNO_DID" ] || SYNO_DEVICE_ID="$SYNO_DID"
|
||||||
|
fi
|
||||||
|
if [ -n "$SYNO_DEVICE_ID" ]; then
|
||||||
|
_H1="Cookie: did=$SYNO_DEVICE_ID"
|
||||||
|
export _H1
|
||||||
|
_debug3 H1 "${_H1}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$DEPRECATED_otp_code&device_name=certrenewal&device_id=$SYNO_DEVICE_ID" "$_base_url/webapi/$api_path?enable_syno_token=yes")
|
||||||
|
_debug3 response "$response"
|
||||||
|
# ## END ## - DEPRECATED, for backward compatibility
|
||||||
|
# If SYNO_DEVICE_ID or SYNO_OTP_CODE is set, we treat current account enabled 2FA-OTP.
|
||||||
|
# Notice that if SYNO_USE_TEMP_ADMIN=1, both variables will be unset
|
||||||
|
else
|
||||||
|
if [ -n "$SYNO_DEVICE_ID" ] || [ -n "$SYNO_OTP_CODE" ]; then
|
||||||
|
response='{"error":{"code":403}}'
|
||||||
|
# Assume the current account disabled 2FA-OTP, try to log in right away.
|
||||||
|
else
|
||||||
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
|
||||||
|
_getdeployconf SYNO_LOCAL_HOSTNAME
|
||||||
|
_debug SYNO_LOCAL_HOSTNAME "${SYNO_LOCAL_HOSTNAME:-}"
|
||||||
|
if [ "$SYNO_LOCAL_HOSTNAME" != "1" ] && [ "$SYNO_LOCAL_HOSTNAME" == "$SYNO_HOSTNAME" ]; then
|
||||||
|
if [ "$SYNO_HOSTNAME" != "localhost" ] && [ "$SYNO_HOSTNAME" != "127.0.0.1" ]; then
|
||||||
|
_err "SYNO_USE_TEMP_ADMIN=1 only support local deployment, though if you are sure that the hostname $SYNO_HOSTNAME is targeting to your **current local machine**, execute 'export SYNO_LOCAL_HOSTNAME=1' then rerun."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_debug "Creating temp admin user in Synology DSM..."
|
||||||
|
if synogroup --help | grep -q '\-\-memberadd '; then
|
||||||
|
_temp_admin_create "$SYNO_USERNAME" "$SYNO_PASSWORD"
|
||||||
|
synogroup --memberadd administrators "$SYNO_USERNAME" >/dev/null
|
||||||
|
elif synogroup --help | grep -q '\-\-member '; then
|
||||||
|
# For supporting DSM 6.x which only has `--member` parameter.
|
||||||
|
cur_admins=$(synogroup --get administrators | awk -F '[][]' '/Group Members/,0{if(NF>1)printf "%s ", $2}')
|
||||||
|
if [ -n "$cur_admins" ]; then
|
||||||
|
_temp_admin_create "$SYNO_USERNAME" "$SYNO_PASSWORD"
|
||||||
|
_secure_debug3 admin_users "$cur_admins$SYNO_USERNAME"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
synogroup --member administrators $cur_admins $SYNO_USERNAME >/dev/null
|
||||||
|
else
|
||||||
|
_err "The tool synogroup may be broken, please set SYNO_USERNAME and SYNO_PASSWORD instead."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Unsupported synogroup tool detected, please set SYNO_USERNAME and SYNO_PASSWORD instead."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# havig a workaround to temporary disable enforce 2FA-OTP, will restore
|
||||||
|
# it soon (after a single request), though if any accident occurs like
|
||||||
|
# unexpected interruption, this setting can be easily reverted manually.
|
||||||
|
otp_enforce_option=$(synogetkeyvalue /etc/synoinfo.conf otp_enforce_option)
|
||||||
|
if [ -n "$otp_enforce_option" ] && [ "${otp_enforce_option:-"none"}" != "none" ]; then
|
||||||
|
synosetkeyvalue /etc/synoinfo.conf otp_enforce_option none
|
||||||
|
_info "Enforcing 2FA-OTP has been disabled to complete temp admin authentication."
|
||||||
|
_info "Notice: it will be restored soon, if not, you can restore it manually via Control Panel."
|
||||||
|
_info "previous_otp_enforce_option" "$otp_enforce_option"
|
||||||
|
else
|
||||||
|
otp_enforce_option=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes")
|
||||||
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ] && [ -n "$otp_enforce_option" ]; then
|
||||||
|
synosetkeyvalue /etc/synoinfo.conf otp_enforce_option "$otp_enforce_option"
|
||||||
|
_info "Restored previous enforce 2FA-OTP option."
|
||||||
|
fi
|
||||||
|
_debug3 response "$response"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
|
||||||
|
_debug2 error_code "$error_code"
|
||||||
|
# Account has 2FA-OTP enabled, since error 403 reported.
|
||||||
|
# https://global.download.synology.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu/DSM_Login_Web_API_Guide_enu.pdf
|
||||||
|
if [ "$error_code" == "403" ]; then
|
||||||
|
if [ -z "$SYNO_DEVICE_NAME" ]; then
|
||||||
|
printf "Enter device name or leave empty for default (CertRenewal): "
|
||||||
|
read -r SYNO_DEVICE_NAME
|
||||||
|
[ -n "$SYNO_DEVICE_NAME" ] || SYNO_DEVICE_NAME="CertRenewal"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$SYNO_DEVICE_ID" ]; then
|
||||||
|
# Omit OTP code with SYNO_DEVICE_ID.
|
||||||
|
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_DEVICE_NAME&device_id=$SYNO_DEVICE_ID")
|
||||||
|
_secure_debug3 response "$response"
|
||||||
|
else
|
||||||
|
# Require the OTP code if still unset.
|
||||||
|
if [ -z "$SYNO_OTP_CODE" ]; then
|
||||||
|
printf "Enter OTP code for user '%s': " "$SYNO_USERNAME"
|
||||||
|
read -r SYNO_OTP_CODE
|
||||||
|
fi
|
||||||
|
_secure_debug SYNO_OTP_CODE "${SYNO_OTP_CODE:-}"
|
||||||
|
|
||||||
|
if [ -z "$SYNO_OTP_CODE" ]; then
|
||||||
|
response='{"error":{"code":404}}'
|
||||||
|
else
|
||||||
|
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_DEVICE_NAME&otp_code=$SYNO_OTP_CODE")
|
||||||
|
_secure_debug3 response "$response"
|
||||||
|
|
||||||
|
id_property='device_id'
|
||||||
|
[ "${api_version}" -gt '6' ] || id_property='did'
|
||||||
|
SYNO_DEVICE_ID=$(echo "$response" | grep "$id_property" | sed -n 's/.*"'$id_property'" *: *"\([^"]*\).*/\1/p')
|
||||||
|
_secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
|
||||||
|
_debug2 error_code "$error_code"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$error_code" ]; then
|
||||||
|
if [ "$error_code" == "403" ] && [ -n "$SYNO_DEVICE_ID" ]; then
|
||||||
|
_cleardeployconf SYNO_DEVICE_ID
|
||||||
|
_err "Failed to authenticate with SYNO_DEVICE_ID (may expired or invalid), please try again in a new terminal window."
|
||||||
|
elif [ "$error_code" == "404" ]; then
|
||||||
|
_err "Failed to authenticate with provided 2FA-OTP code, please try again in a new terminal window."
|
||||||
|
elif [ "$error_code" == "406" ]; then
|
||||||
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
|
||||||
|
_err "Failed with unexcepted error, please report this by providing full log with '--debug 3'."
|
||||||
|
else
|
||||||
|
_err "Enforce auth with 2FA-OTP enabled, please configure the user to enable 2FA-OTP to continue."
|
||||||
|
fi
|
||||||
|
elif [ "$error_code" == "400" ]; then
|
||||||
|
_err "Failed to authenticate, no such account or incorrect password."
|
||||||
|
elif [ "$error_code" == "401" ]; then
|
||||||
|
_err "Failed to authenticate with a non-existent account."
|
||||||
|
elif [ "$error_code" == "408" ] || [ "$error_code" == "409" ] || [ "$error_code" == "410" ]; then
|
||||||
|
_err "Failed to authenticate, the account password has expired or must be changed."
|
||||||
|
else
|
||||||
|
_err "Failed to authenticate with error: $error_code."
|
||||||
|
fi
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sid=$(echo "$response" | grep "sid" | sed -n 's/.*"sid" *: *"\([^"]*\).*/\1/p')
|
||||||
|
token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
|
||||||
|
_debug "Session ID" "$sid"
|
||||||
|
_debug SynoToken "$token"
|
||||||
|
if [ -z "$sid" ] || [ -z "$token" ]; then
|
||||||
|
# Still can't get necessary info even got no errors, may Synology have API updated?
|
||||||
|
_err "Unable to authenticate to $_base_url, you may report this by providing full log with '--debug 3'."
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_H1="X-SYNO-TOKEN: $token"
|
||||||
|
export _H1
|
||||||
|
_debug2 H1 "${_H1}"
|
||||||
|
|
||||||
|
# Now that we know the username and password are good, save them if not in temp admin mode.
|
||||||
|
if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
|
||||||
|
_cleardeployconf SYNO_USERNAME
|
||||||
|
_cleardeployconf SYNO_PASSWORD
|
||||||
|
_cleardeployconf SYNO_DEVICE_ID
|
||||||
|
_cleardeployconf SYNO_DEVICE_NAME
|
||||||
|
_savedeployconf SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
|
||||||
|
_savedeployconf SYNO_LOCAL_HOSTNAME "$SYNO_HOSTNAME"
|
||||||
|
else
|
||||||
|
_savedeployconf SYNO_USERNAME "$SYNO_USERNAME"
|
||||||
|
_savedeployconf SYNO_PASSWORD "$SYNO_PASSWORD"
|
||||||
|
_savedeployconf SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
|
||||||
|
_savedeployconf SYNO_DEVICE_NAME "$SYNO_DEVICE_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Getting certificates in Synology DSM..."
|
||||||
|
response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1&_sid=$sid" "$_base_url/webapi/entry.cgi")
|
||||||
|
_debug3 response "$response"
|
||||||
|
escaped_certificate="$(printf "%s" "$SYNO_CERTIFICATE" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')"
|
||||||
|
_debug escaped_certificate "$escaped_certificate"
|
||||||
|
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p")
|
||||||
|
_debug2 id "$id"
|
||||||
|
|
||||||
|
error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
|
||||||
|
_debug2 error_code "$error_code"
|
||||||
|
if [ -n "$error_code" ]; then
|
||||||
|
if [ "$error_code" -eq 105 ]; then
|
||||||
|
_err "Current user is not administrator and does not have sufficient permission for deploying."
|
||||||
|
else
|
||||||
|
_err "Failed to fetch certificate info: $error_code, please try again or contact Synology to learn more."
|
||||||
|
fi
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_migratedeployconf SYNO_Create SYNO_CREATE
|
||||||
|
_getdeployconf SYNO_CREATE
|
||||||
|
_debug2 SYNO_CREATE "$SYNO_CREATE"
|
||||||
|
|
||||||
|
if [ -z "$id" ] && [ -z "$SYNO_CREATE" ]; then
|
||||||
|
_err "Unable to find certificate: $SYNO_CERTIFICATE and $SYNO_CREATE is not set."
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# We've verified this certificate description is a thing, so save it
|
||||||
|
_savedeployconf SYNO_CERTIFICATE "$SYNO_CERTIFICATE" "base64"
|
||||||
|
|
||||||
|
_info "Generating form POST request..."
|
||||||
|
nl="\0015\0012"
|
||||||
|
delim="--------------------------$(_utc_date | tr -d -- '-: ')"
|
||||||
|
content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\0012"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\0012"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_CERTIFICATE}"
|
||||||
|
if echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then
|
||||||
|
_debug2 default "This is the default certificate"
|
||||||
|
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}true"
|
||||||
|
else
|
||||||
|
_debug2 default "This is NOT the default certificate"
|
||||||
|
fi
|
||||||
|
content="$content${nl}--$delim--${nl}"
|
||||||
|
content="$(printf "%b_" "$content")"
|
||||||
|
content="${content%_}" # protect trailing \n
|
||||||
|
|
||||||
|
_info "Upload certificate to the Synology DSM."
|
||||||
|
response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token&_sid=$sid" "" "POST" "multipart/form-data; boundary=${delim}")
|
||||||
|
_debug3 response "$response"
|
||||||
|
|
||||||
|
if ! echo "$response" | grep '"error":' >/dev/null; then
|
||||||
|
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
|
||||||
|
_info "Restart HTTP services succeeded."
|
||||||
|
else
|
||||||
|
_info "Restart HTTP services failed."
|
||||||
|
fi
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
_logout
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
|
||||||
|
_err "Unable to update certificate, got error response: $response."
|
||||||
|
_logout
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
_logout() {
|
||||||
|
# Logout CERT user only to not occupy a permanent session, e.g. in DSM's "Connected Users" widget (based on previous variables)
|
||||||
|
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=logout&_sid=$sid")
|
||||||
|
_debug3 response "$response"
|
||||||
|
}
|
||||||
|
|
||||||
|
_temp_admin_create() {
|
||||||
|
_username="$1"
|
||||||
|
_password="$2"
|
||||||
|
synouser --del "$_username" >/dev/null 2>/dev/null
|
||||||
|
synouser --add "$_username" "$_password" "" 0 "scruelt@hotmail.com" 0 >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
_temp_admin_cleanup() {
|
||||||
|
_flag=$1
|
||||||
|
_username=$2
|
||||||
|
|
||||||
|
if [ -n "${_flag}" ]; then
|
||||||
|
_debug "Cleanuping temp admin info..."
|
||||||
|
synouser --del "$_username" >/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#_cleardeployconf key
|
||||||
|
_cleardeployconf() {
|
||||||
|
_cleardomainconf "SAVED_$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# key
|
||||||
|
_check2cleardeployconfexp() {
|
||||||
|
_key="$1"
|
||||||
|
_clear_key="CLEAR_$_key"
|
||||||
|
# Clear saved settings if explicitly requested
|
||||||
|
if [ -n "$(eval echo \$"$_clear_key")" ]; then
|
||||||
|
_debug2 "$_key: value cleared from config, exported value will be ignored."
|
||||||
|
_cleardeployconf "$_key"
|
||||||
|
eval "$_key"=
|
||||||
|
export "$_key"=
|
||||||
|
eval SAVED_"$_key"=
|
||||||
|
export SAVED_"$_key"=
|
||||||
|
fi
|
||||||
|
}
|
223
deploy/truenas.sh
Normal file
223
deploy/truenas.sh
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a scipt to deploy the cert to your TrueNAS using the REST API.
|
||||||
|
# https://www.truenas.com/docs/hub/additional-topics/api/rest_api.html
|
||||||
|
#
|
||||||
|
# Written by Frank Plass github@f-plass.de
|
||||||
|
# https://github.com/danb35/deploy-freenas/blob/master/deploy_freenas.py
|
||||||
|
# Thanks to danb35 for your template!
|
||||||
|
#
|
||||||
|
# Following environment variables must be set:
|
||||||
|
#
|
||||||
|
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI"
|
||||||
|
#
|
||||||
|
# The following environmental variables may be set if you don't like their
|
||||||
|
# default values:
|
||||||
|
#
|
||||||
|
# DEPLOY_TRUENAS_HOSTNAME - defaults to localhost
|
||||||
|
# DEPLOY_TRUENAS_SCHEME - defaults to http, set alternatively to https
|
||||||
|
#
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
truenas_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"
|
||||||
|
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_APIKEY
|
||||||
|
|
||||||
|
if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
|
||||||
|
_err "TrueNAS API key not found, please set the DEPLOY_TRUENAS_APIKEY environment variable."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
|
||||||
|
|
||||||
|
# Optional hostname, scheme for TrueNAS
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_HOSTNAME
|
||||||
|
_getdeployconf DEPLOY_TRUENAS_SCHEME
|
||||||
|
|
||||||
|
# default values for hostname and scheme
|
||||||
|
[ -n "${DEPLOY_TRUENAS_HOSTNAME}" ] || DEPLOY_TRUENAS_HOSTNAME="localhost"
|
||||||
|
[ -n "${DEPLOY_TRUENAS_SCHEME}" ] || DEPLOY_TRUENAS_SCHEME="http"
|
||||||
|
|
||||||
|
_debug2 DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
|
||||||
|
_debug2 DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
|
||||||
|
|
||||||
|
_api_url="$DEPLOY_TRUENAS_SCHEME://$DEPLOY_TRUENAS_HOSTNAME/api/v2.0"
|
||||||
|
_debug _api_url "$_api_url"
|
||||||
|
|
||||||
|
_H1="Authorization: Bearer $DEPLOY_TRUENAS_APIKEY"
|
||||||
|
_secure_debug3 _H1 "$_H1"
|
||||||
|
|
||||||
|
_info "Testing Connection TrueNAS"
|
||||||
|
_response=$(_get "$_api_url/system/state")
|
||||||
|
_info "TrueNAS system state: $_response."
|
||||||
|
|
||||||
|
if [ -z "$_response" ]; then
|
||||||
|
_err "Unable to authenticate to $_api_url."
|
||||||
|
_err 'Check your connection settings are correct, e.g.'
|
||||||
|
_err 'DEPLOY_TRUENAS_HOSTNAME="192.168.x.y" or DEPLOY_TRUENAS_HOSTNAME="truenas.example.com".'
|
||||||
|
_err 'DEPLOY_TRUENAS_SCHEME="https" or DEPLOY_TRUENAS_SCHEME="http".'
|
||||||
|
_err "Verify your TrueNAS API key is valid and set correctly, e.g. DEPLOY_TRUENAS_APIKEY=xxxx...."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
|
||||||
|
|
||||||
|
_info "Getting current active certificate from TrueNAS"
|
||||||
|
_response=$(_get "$_api_url/system/general")
|
||||||
|
_active_cert_id=$(echo "$_response" | grep -B2 '"name":' | grep 'id' | tr -d -- '"id: ,')
|
||||||
|
_active_cert_name=$(echo "$_response" | grep '"name":' | sed -n 's/.*: "\(.\{1,\}\)",$/\1/p')
|
||||||
|
_param_httpsredirect=$(echo "$_response" | grep '"ui_httpsredirect":' | sed -n 's/.*": \(.\{1,\}\),$/\1/p')
|
||||||
|
_debug Active_UI_Certificate_ID "$_active_cert_id"
|
||||||
|
_debug Active_UI_Certificate_Name "$_active_cert_name"
|
||||||
|
_debug Active_UI_http_redirect "$_param_httpsredirect"
|
||||||
|
|
||||||
|
if [ "$DEPLOY_TRUENAS_SCHEME" = "http" ] && [ "$_param_httpsredirect" = "true" ]; then
|
||||||
|
_info "HTTP->HTTPS redirection is enabled"
|
||||||
|
_info "Setting DEPLOY_TRUENAS_SCHEME to 'https'"
|
||||||
|
DEPLOY_TRUENAS_SCHEME="https"
|
||||||
|
_api_url="$DEPLOY_TRUENAS_SCHEME://$DEPLOY_TRUENAS_HOSTNAME/api/v2.0"
|
||||||
|
_savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Uploading new certificate to TrueNAS"
|
||||||
|
_certname="Letsencrypt_$(_utc_date | tr ' ' '_' | tr -d -- ':')"
|
||||||
|
_debug3 _certname "$_certname"
|
||||||
|
|
||||||
|
_certData="{\"create_type\": \"CERTIFICATE_CREATE_IMPORTED\", \"name\": \"${_certname}\", \"certificate\": \"$(_json_encode <"$_cfullchain")\", \"privatekey\": \"$(_json_encode <"$_ckey")\"}"
|
||||||
|
_add_cert_result="$(_post "$_certData" "$_api_url/certificate" "" "POST" "application/json")"
|
||||||
|
|
||||||
|
_debug3 _add_cert_result "$_add_cert_result"
|
||||||
|
|
||||||
|
_info "Fetching list of installed certificates"
|
||||||
|
_cert_list=$(_get "$_api_url/system/general/ui_certificate_choices")
|
||||||
|
_cert_id=$(echo "$_cert_list" | grep "$_certname" | sed -n 's/.*"\([0-9]\{1,\}\)".*$/\1/p')
|
||||||
|
|
||||||
|
_debug3 _cert_id "$_cert_id"
|
||||||
|
|
||||||
|
_info "Current activate certificate ID: $_cert_id"
|
||||||
|
_activateData="{\"ui_certificate\": \"${_cert_id}\"}"
|
||||||
|
_activate_result="$(_post "$_activateData" "$_api_url/system/general" "" "PUT" "application/json")"
|
||||||
|
|
||||||
|
_debug3 _activate_result "$_activate_result"
|
||||||
|
|
||||||
|
_info "Checking if WebDAV certificate is the same as the TrueNAS web UI"
|
||||||
|
_webdav_list=$(_get "$_api_url/webdav")
|
||||||
|
_webdav_cert_id=$(echo "$_webdav_list" | grep '"certssl":' | tr -d -- '"certsl: ,')
|
||||||
|
|
||||||
|
if [ "$_webdav_cert_id" = "$_active_cert_id" ]; then
|
||||||
|
_info "Updating the WebDAV certificate"
|
||||||
|
_debug _webdav_cert_id "$_webdav_cert_id"
|
||||||
|
_webdav_data="{\"certssl\": \"${_cert_id}\"}"
|
||||||
|
_activate_webdav_cert="$(_post "$_webdav_data" "$_api_url/webdav" "" "PUT" "application/json")"
|
||||||
|
_webdav_new_cert_id=$(echo "$_activate_webdav_cert" | _json_decode | grep '"certssl":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
|
||||||
|
if [ "$_webdav_new_cert_id" -eq "$_cert_id" ]; then
|
||||||
|
_info "WebDAV certificate updated successfully"
|
||||||
|
else
|
||||||
|
_err "Unable to set WebDAV certificate"
|
||||||
|
_debug3 _activate_webdav_cert "$_activate_webdav_cert"
|
||||||
|
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
|
||||||
|
else
|
||||||
|
_info "WebDAV certificate is not configured or is not the same as TrueNAS web UI"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Checking if FTP certificate is the same as the TrueNAS web UI"
|
||||||
|
_ftp_list=$(_get "$_api_url/ftp")
|
||||||
|
_ftp_cert_id=$(echo "$_ftp_list" | grep '"ssltls_certificate":' | tr -d -- '"certislfa:_ ,')
|
||||||
|
|
||||||
|
if [ "$_ftp_cert_id" = "$_active_cert_id" ]; then
|
||||||
|
_info "Updating the FTP certificate"
|
||||||
|
_debug _ftp_cert_id "$_ftp_cert_id"
|
||||||
|
_ftp_data="{\"ssltls_certificate\": \"${_cert_id}\"}"
|
||||||
|
_activate_ftp_cert="$(_post "$_ftp_data" "$_api_url/ftp" "" "PUT" "application/json")"
|
||||||
|
_ftp_new_cert_id=$(echo "$_activate_ftp_cert" | _json_decode | grep '"ssltls_certificate":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
|
||||||
|
if [ "$_ftp_new_cert_id" -eq "$_cert_id" ]; then
|
||||||
|
_info "FTP certificate updated successfully"
|
||||||
|
else
|
||||||
|
_err "Unable to set FTP certificate"
|
||||||
|
_debug3 _activate_ftp_cert "$_activate_ftp_cert"
|
||||||
|
_debug3 _ftp_new_cert_id "$_ftp_new_cert_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug3 _activate_ftp_cert "$_activate_ftp_cert"
|
||||||
|
else
|
||||||
|
_info "FTP certificate is not configured or is not the same as TrueNAS web UI"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Checking if S3 certificate is the same as the TrueNAS web UI"
|
||||||
|
_s3_list=$(_get "$_api_url/s3")
|
||||||
|
_s3_cert_id=$(echo "$_s3_list" | grep '"certificate":' | tr -d -- '"certifa:_ ,')
|
||||||
|
|
||||||
|
if [ "$_s3_cert_id" = "$_active_cert_id" ]; then
|
||||||
|
_info "Updating the S3 certificate"
|
||||||
|
_debug _s3_cert_id "$_s3_cert_id"
|
||||||
|
_s3_data="{\"certificate\": \"${_cert_id}\"}"
|
||||||
|
_activate_s3_cert="$(_post "$_s3_data" "$_api_url/s3" "" "PUT" "application/json")"
|
||||||
|
_s3_new_cert_id=$(echo "$_activate_s3_cert" | _json_decode | grep '"certificate":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
|
||||||
|
if [ "$_s3_new_cert_id" -eq "$_cert_id" ]; then
|
||||||
|
_info "S3 certificate updated successfully"
|
||||||
|
else
|
||||||
|
_err "Unable to set S3 certificate"
|
||||||
|
_debug3 _activate_s3_cert "$_activate_s3_cert"
|
||||||
|
_debug3 _s3_new_cert_id "$_s3_new_cert_id"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug3 _activate_s3_cert "$_activate_s3_cert"
|
||||||
|
else
|
||||||
|
_info "S3 certificate is not configured or is not the same as TrueNAS web UI"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Checking if any chart release Apps is using the same certificate as TrueNAS web UI. Tool 'jq' is required"
|
||||||
|
if _exists jq; then
|
||||||
|
_info "Query all chart release"
|
||||||
|
_release_list=$(_get "$_api_url/chart/release")
|
||||||
|
_related_name_list=$(printf "%s" "$_release_list" | jq -r "[.[] | {name,certId: .config.ingress?.main.tls[]?.scaleCert} | select(.certId==$_active_cert_id) | .name ] | unique")
|
||||||
|
_release_length=$(printf "%s" "$_related_name_list" | jq -r "length")
|
||||||
|
_info "Found $_release_length related chart release in list: $_related_name_list"
|
||||||
|
for i in $(seq 0 $((_release_length - 1))); do
|
||||||
|
_release_name=$(echo "$_related_name_list" | jq -r ".[$i]")
|
||||||
|
_info "Updating certificate from $_active_cert_id to $_cert_id for chart release: $_release_name"
|
||||||
|
#Read the chart release configuration
|
||||||
|
_chart_config=$(printf "%s" "$_release_list" | jq -r ".[] | select(.name==\"$_release_name\")")
|
||||||
|
#Replace the old certificate id with the new one in path .config.ingress.main.tls[].scaleCert. Then update .config.ingress
|
||||||
|
_updated_chart_config=$(printf "%s" "$_chart_config" | jq "(.config.ingress?.main.tls[]? | select(.scaleCert==$_active_cert_id) | .scaleCert ) |= $_cert_id | .config.ingress ")
|
||||||
|
_update_chart_result="$(_post "{\"values\" : { \"ingress\" : $_updated_chart_config } }" "$_api_url/chart/release/id/$_release_name" "" "PUT" "application/json")"
|
||||||
|
_debug3 _update_chart_result "$_update_chart_result"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
_info "Tool 'jq' does not exists, skip chart release checking"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Deleting old certificate"
|
||||||
|
_delete_result="$(_post "" "$_api_url/certificate/id/$_active_cert_id" "" "DELETE" "application/json")"
|
||||||
|
|
||||||
|
_debug3 _delete_result "$_delete_result"
|
||||||
|
|
||||||
|
_info "Reloading TrueNAS web UI"
|
||||||
|
_restart_UI=$(_get "$_api_url/system/general/ui_restart")
|
||||||
|
_debug2 _restart_UI "$_restart_UI"
|
||||||
|
|
||||||
|
if [ -n "$_add_cert_result" ] && [ -n "$_activate_result" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Certificate update was not succesful, please try again with --debug"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
252
deploy/unifi.sh
252
deploy/unifi.sh
@ -1,12 +1,52 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
#Here is a script to deploy cert to unifi server.
|
# Here is a script to deploy cert on a Unifi Controller or Cloud Key device.
|
||||||
|
# It supports:
|
||||||
|
# - self-hosted Unifi Controller
|
||||||
|
# - Unifi Cloud Key (Gen1/2/2+)
|
||||||
|
# - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only)
|
||||||
|
# - Unifi Dream Machine
|
||||||
|
# This has not been tested on other "all-in-one" devices such as
|
||||||
|
# UDM Pro or Unifi Express.
|
||||||
|
#
|
||||||
|
# OS Version v2.0.0+
|
||||||
|
# Network Application version 7.0.0+
|
||||||
|
# OS version ~3.1 removed java and keytool from the UnifiOS.
|
||||||
|
# Using PKCS12 format keystore appears to work fine.
|
||||||
|
#
|
||||||
|
# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359
|
||||||
|
|
||||||
#returns 0 means success, otherwise error.
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
# The deploy-hook automatically detects standard Unifi installations
|
||||||
|
# for each of the supported environments. Most users should not need
|
||||||
|
# to set any of these variables, but if you are running a self-hosted
|
||||||
|
# Controller with custom locations, set these as necessary before running
|
||||||
|
# the deploy hook. (Defaults shown below.)
|
||||||
|
#
|
||||||
|
# Settings for Unifi Controller:
|
||||||
|
# Location of Java keystore or unifi.keystore.jks file:
|
||||||
#DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore"
|
#DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore"
|
||||||
|
# Keystore password (built into Unifi Controller, not a user-set password):
|
||||||
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
|
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
|
||||||
|
# Command to restart Unifi Controller:
|
||||||
#DEPLOY_UNIFI_RELOAD="service unifi restart"
|
#DEPLOY_UNIFI_RELOAD="service unifi restart"
|
||||||
|
#
|
||||||
|
# Settings for Unifi Cloud Key Gen1 (nginx admin pages):
|
||||||
|
# Directory where cloudkey.crt and cloudkey.key live:
|
||||||
|
#DEPLOY_UNIFI_CLOUDKEY_CERTDIR="/etc/ssl/private"
|
||||||
|
# Command to restart maintenance pages and Controller
|
||||||
|
# (same setting as above, default is updated when running on Cloud Key Gen1):
|
||||||
|
#DEPLOY_UNIFI_RELOAD="service nginx restart && service unifi restart"
|
||||||
|
#
|
||||||
|
# Settings for UnifiOS (Cloud Key Gen2):
|
||||||
|
# Directory where unifi-core.crt and unifi-core.key live:
|
||||||
|
#DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/"
|
||||||
|
# Command to restart unifi-core:
|
||||||
|
#DEPLOY_UNIFI_RELOAD="systemctl restart unifi-core"
|
||||||
|
#
|
||||||
|
# At least one of DEPLOY_UNIFI_KEYSTORE, DEPLOY_UNIFI_CLOUDKEY_CERTDIR,
|
||||||
|
# or DEPLOY_UNIFI_CORE_CONFIG must exist to receive the deployed certs.
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
@ -24,77 +64,203 @@ unifi_deploy() {
|
|||||||
_debug _cca "$_cca"
|
_debug _cca "$_cca"
|
||||||
_debug _cfullchain "$_cfullchain"
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
if ! _exists keytool; then
|
_getdeployconf DEPLOY_UNIFI_KEYSTORE
|
||||||
_err "keytool not found"
|
_getdeployconf DEPLOY_UNIFI_KEYPASS
|
||||||
return 1
|
_getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR
|
||||||
fi
|
_getdeployconf DEPLOY_UNIFI_CORE_CONFIG
|
||||||
|
_getdeployconf DEPLOY_UNIFI_RELOAD
|
||||||
|
|
||||||
DEFAULT_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore"
|
_debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
||||||
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-$DEFAULT_UNIFI_KEYSTORE}"
|
_debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
||||||
DEFAULT_UNIFI_KEYPASS="aircontrolenterprise"
|
_debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
||||||
_unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-$DEFAULT_UNIFI_KEYPASS}"
|
_debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
||||||
DEFAULT_UNIFI_RELOAD="service unifi restart"
|
_debug2 DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
||||||
_reload="${DEPLOY_UNIFI_RELOAD:-$DEFAULT_UNIFI_RELOAD}"
|
|
||||||
|
|
||||||
|
# Space-separated list of environments detected and installed:
|
||||||
|
_services_updated=""
|
||||||
|
|
||||||
|
# Default reload commands accumulated as we auto-detect environments:
|
||||||
|
_reload_cmd=""
|
||||||
|
|
||||||
|
# Unifi Controller environment (self hosted or any Cloud Key) --
|
||||||
|
# auto-detect by file /usr/lib/unifi/data/keystore
|
||||||
|
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}"
|
||||||
|
if [ -f "$_unifi_keystore" ]; then
|
||||||
_debug _unifi_keystore "$_unifi_keystore"
|
_debug _unifi_keystore "$_unifi_keystore"
|
||||||
if [ ! -f "$_unifi_keystore" ]; then
|
if ! _exists keytool; then
|
||||||
if [ -z "$DEPLOY_UNIFI_KEYSTORE" ]; then
|
_do_keytool=0
|
||||||
_err "unifi keystore is not found, please define DEPLOY_UNIFI_KEYSTORE"
|
_info "Installing certificate for Unifi Controller (PKCS12 keystore)."
|
||||||
return 1
|
|
||||||
else
|
else
|
||||||
_err "It seems that the specified unifi keystore is not valid, please check."
|
_do_keytool=1
|
||||||
return 1
|
_info "Installing certificate for Unifi Controller (Java keystore)"
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
if [ ! -w "$_unifi_keystore" ]; then
|
if [ ! -w "$_unifi_keystore" ]; then
|
||||||
_err "The file $_unifi_keystore is not writable, please change the permission."
|
_err "The file $_unifi_keystore is not writable, please change the permission."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Generate import pkcs12"
|
_unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-aircontrolenterprise}"
|
||||||
|
|
||||||
|
_debug "Generate import pkcs12"
|
||||||
_import_pkcs12="$(_mktemp)"
|
_import_pkcs12="$(_mktemp)"
|
||||||
|
_debug "_toPkcs $_import_pkcs12 $_ckey $_ccert $_cca $_unifi_keypass unifi root"
|
||||||
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root
|
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root
|
||||||
|
# shellcheck disable=SC2181
|
||||||
if [ "$?" != "0" ]; then
|
if [ "$?" != "0" ]; then
|
||||||
_err "Oops, error creating import pkcs12, please report bug to us."
|
_err "Error generating pkcs12. Please re-run with --debug and report a bug."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Modify unifi keystore: $_unifi_keystore"
|
# Save the existing keystore in case something goes wrong.
|
||||||
|
mv -f "${_unifi_keystore}" "${_unifi_keystore}"_original
|
||||||
|
_info "Previous keystore saved to ${_unifi_keystore}_original."
|
||||||
|
|
||||||
|
if [ "$_do_keytool" -eq 1 ]; then
|
||||||
|
_debug "Import into keystore: $_unifi_keystore"
|
||||||
if keytool -importkeystore \
|
if keytool -importkeystore \
|
||||||
-deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
|
-deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
|
||||||
-srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
|
-srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
|
||||||
-alias unifi -noprompt; then
|
-alias unifi -noprompt; then
|
||||||
_info "Import keystore success!"
|
_debug "Import keystore success!"
|
||||||
rm "$_import_pkcs12"
|
|
||||||
else
|
else
|
||||||
_err "Import unifi keystore error, please report bug to us."
|
_err "Error importing into Unifi Java keystore."
|
||||||
|
_err "Please re-run with --debug and report a bug."
|
||||||
|
_info "Restoring original keystore."
|
||||||
|
mv -f "${_unifi_keystore}"_original "${_unifi_keystore}"
|
||||||
rm "$_import_pkcs12"
|
rm "$_import_pkcs12"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
_debug "Copying new keystore to $_unifi_keystore"
|
||||||
|
cp -f "$_import_pkcs12" "$_unifi_keystore"
|
||||||
|
fi
|
||||||
|
|
||||||
_info "Run reload: $_reload"
|
# Update unifi service for certificate cipher compatibility
|
||||||
if eval "$_reload"; then
|
if ${ACME_OPENSSL_BIN:-openssl} pkcs12 \
|
||||||
|
-in "$_import_pkcs12" \
|
||||||
|
-password pass:aircontrolenterprise \
|
||||||
|
-nokeys | ${ACME_OPENSSL_BIN:-openssl} x509 -text \
|
||||||
|
-noout | grep -i "signature" | grep -iq ecdsa >/dev/null 2>&1; then
|
||||||
|
cp -f /usr/lib/unifi/data/system.properties /usr/lib/unifi/data/system.properties_original
|
||||||
|
_info "Updating system configuration for cipher compatibility."
|
||||||
|
_info "Saved original system config to /usr/lib/unifi/data/system.properties_original"
|
||||||
|
sed -i '/unifi\.https\.ciphers/d' /usr/lib/unifi/data/system.properties
|
||||||
|
echo "unifi.https.ciphers=ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256" >>/usr/lib/unifi/data/system.properties
|
||||||
|
sed -i '/unifi\.https\.sslEnabledProtocols/d' /usr/lib/unifi/data/system.properties
|
||||||
|
echo "unifi.https.sslEnabledProtocols=TLSv1.3,TLSv1.2" >>/usr/lib/unifi/data/system.properties
|
||||||
|
_info "System configuration updated."
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm "$_import_pkcs12"
|
||||||
|
|
||||||
|
# Restarting unifi-core will bring up unifi, doing it out of order results in
|
||||||
|
# a certificate error, and breaks wifiman.
|
||||||
|
# Restart if we aren't doing unifi-core, otherwise stop for later restart.
|
||||||
|
if systemctl -q is-active unifi; then
|
||||||
|
if [ ! -f "${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}/unifi-core.key" ]; then
|
||||||
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi"
|
||||||
|
else
|
||||||
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl stop unifi"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_services_updated="${_services_updated} unifi"
|
||||||
|
_info "Install Unifi Controller certificate success!"
|
||||||
|
elif [ "$DEPLOY_UNIFI_KEYSTORE" ]; then
|
||||||
|
_err "The specified DEPLOY_UNIFI_KEYSTORE='$DEPLOY_UNIFI_KEYSTORE' is not valid, please check."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cloud Key environment (non-UnifiOS -- nginx serves admin pages) --
|
||||||
|
# auto-detect by file /etc/ssl/private/cloudkey.key:
|
||||||
|
_cloudkey_certdir="${DEPLOY_UNIFI_CLOUDKEY_CERTDIR:-/etc/ssl/private}"
|
||||||
|
if [ -f "${_cloudkey_certdir}/cloudkey.key" ]; then
|
||||||
|
_info "Installing certificate for Cloud Key Gen1 (nginx admin pages)"
|
||||||
|
_debug _cloudkey_certdir "$_cloudkey_certdir"
|
||||||
|
if [ ! -w "$_cloudkey_certdir" ]; then
|
||||||
|
_err "The directory $_cloudkey_certdir is not writable; please check permissions."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# Cloud Key expects to load the keystore from /etc/ssl/private/unifi.keystore.jks.
|
||||||
|
# Normally /usr/lib/unifi/data/keystore is a symlink there (so the keystore was
|
||||||
|
# updated above), but if not, we don't know how to handle this installation:
|
||||||
|
if ! cmp -s "$_unifi_keystore" "${_cloudkey_certdir}/unifi.keystore.jks"; then
|
||||||
|
_err "Unsupported Cloud Key configuration: keystore not found at '${_cloudkey_certdir}/unifi.keystore.jks'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat "$_cfullchain" >"${_cloudkey_certdir}/cloudkey.crt"
|
||||||
|
cat "$_ckey" >"${_cloudkey_certdir}/cloudkey.key"
|
||||||
|
(cd "$_cloudkey_certdir" && tar -cf cert.tar cloudkey.crt cloudkey.key unifi.keystore.jks)
|
||||||
|
|
||||||
|
if systemctl -q is-active nginx; then
|
||||||
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }service nginx restart"
|
||||||
|
fi
|
||||||
|
_info "Install Cloud Key Gen1 certificate success!"
|
||||||
|
_services_updated="${_services_updated} nginx"
|
||||||
|
elif [ "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" ]; then
|
||||||
|
_err "The specified DEPLOY_UNIFI_CLOUDKEY_CERTDIR='$DEPLOY_UNIFI_CLOUDKEY_CERTDIR' is not valid, please check."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# UnifiOS environment -- auto-detect by /data/unifi-core/config/unifi-core.key:
|
||||||
|
_unifi_core_config="${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}"
|
||||||
|
if [ -f "${_unifi_core_config}/unifi-core.key" ]; then
|
||||||
|
_info "Installing certificate for UnifiOS"
|
||||||
|
_debug _unifi_core_config "$_unifi_core_config"
|
||||||
|
if [ ! -w "$_unifi_core_config" ]; then
|
||||||
|
_err "The directory $_unifi_core_config is not writable; please check permissions."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save the existing certs in case something goes wrong.
|
||||||
|
cp -f "${_unifi_core_config}"/unifi-core.crt "${_unifi_core_config}"/unifi-core_original.crt
|
||||||
|
cp -f "${_unifi_core_config}"/unifi-core.key "${_unifi_core_config}"/unifi-core_original.key
|
||||||
|
_info "Previous certificate and key saved to ${_unifi_core_config}/unifi-core_original.crt/key."
|
||||||
|
|
||||||
|
cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt"
|
||||||
|
cat "$_ckey" >"${_unifi_core_config}/unifi-core.key"
|
||||||
|
|
||||||
|
if systemctl -q is-active unifi-core; then
|
||||||
|
_reload_cmd="${_reload_cmd:+$_reload_cmd && }systemctl restart unifi-core"
|
||||||
|
fi
|
||||||
|
_info "Install UnifiOS certificate success!"
|
||||||
|
_services_updated="${_services_updated} unifi-core"
|
||||||
|
elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then
|
||||||
|
_err "The specified DEPLOY_UNIFI_CORE_CONFIG='$DEPLOY_UNIFI_CORE_CONFIG' is not valid, please check."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$_services_updated" ]; then
|
||||||
|
# None of the Unifi environments were auto-detected, so no deployment has occurred
|
||||||
|
# (and none of DEPLOY_UNIFI_{KEYSTORE,CLOUDKEY_CERTDIR,CORE_CONFIG} were set).
|
||||||
|
_err "Unable to detect Unifi environment in standard location."
|
||||||
|
_err "(This deploy hook must be run on the Unifi device, not a remote machine.)"
|
||||||
|
_err "For non-standard Unifi installations, set DEPLOY_UNIFI_KEYSTORE,"
|
||||||
|
_err "DEPLOY_UNIFI_CLOUDKEY_CERTDIR, and/or DEPLOY_UNIFI_CORE_CONFIG as appropriate."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_reload_cmd="${DEPLOY_UNIFI_RELOAD:-$_reload_cmd}"
|
||||||
|
if [ -z "$_reload_cmd" ]; then
|
||||||
|
_err "Certificates were installed for services:${_services_updated},"
|
||||||
|
_err "but none appear to be active. Please set DEPLOY_UNIFI_RELOAD"
|
||||||
|
_err "to a command that will restart the necessary services."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "Reload services (this may take some time): $_reload_cmd"
|
||||||
|
if eval "$_reload_cmd"; then
|
||||||
_info "Reload success!"
|
_info "Reload success!"
|
||||||
if [ "$DEPLOY_UNIFI_KEYSTORE" ]; then
|
|
||||||
_savedomainconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
|
||||||
else
|
|
||||||
_cleardomainconf DEPLOY_UNIFI_KEYSTORE
|
|
||||||
fi
|
|
||||||
if [ "$DEPLOY_UNIFI_KEYPASS" ]; then
|
|
||||||
_savedomainconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
|
||||||
else
|
|
||||||
_cleardomainconf DEPLOY_UNIFI_KEYPASS
|
|
||||||
fi
|
|
||||||
if [ "$DEPLOY_UNIFI_RELOAD" ]; then
|
|
||||||
_savedomainconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
|
||||||
else
|
|
||||||
_cleardomainconf DEPLOY_UNIFI_RELOAD
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
else
|
else
|
||||||
_err "Reload error"
|
_err "Reload error"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
|
||||||
|
|
||||||
|
# Successful, so save all (non-default) config:
|
||||||
|
_savedeployconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
|
||||||
|
_savedeployconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
|
||||||
|
_savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
|
||||||
|
_savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
|
||||||
|
_savedeployconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
131
deploy/vault.sh
Normal file
131
deploy/vault.sh
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Here is a script to deploy cert to hashicorp vault using curl
|
||||||
|
# (https://www.vaultproject.io/)
|
||||||
|
#
|
||||||
|
# it requires following environment variables:
|
||||||
|
#
|
||||||
|
# VAULT_PREFIX - this contains the prefix path in vault
|
||||||
|
# VAULT_ADDR - vault requires this to find your vault server
|
||||||
|
# VAULT_SAVE_TOKEN - set to anything if you want to save the token
|
||||||
|
# VAULT_RENEW_TOKEN - set to anything if you want to renew the token to default TTL before deploying
|
||||||
|
# VAULT_KV_V2 - set to anything if you are using v2 of the kv engine
|
||||||
|
#
|
||||||
|
# additionally, you need to ensure that VAULT_TOKEN is avialable
|
||||||
|
# to access the vault server
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
vault_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"
|
||||||
|
|
||||||
|
# validate required env vars
|
||||||
|
_getdeployconf VAULT_PREFIX
|
||||||
|
if [ -z "$VAULT_PREFIX" ]; then
|
||||||
|
_err "VAULT_PREFIX needs to be defined (contains prefix path in vault)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedeployconf VAULT_PREFIX "$VAULT_PREFIX"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_ADDR
|
||||||
|
if [ -z "$VAULT_ADDR" ]; then
|
||||||
|
_err "VAULT_ADDR needs to be defined (contains vault connection address)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_savedeployconf VAULT_ADDR "$VAULT_ADDR"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_SAVE_TOKEN
|
||||||
|
_savedeployconf VAULT_SAVE_TOKEN "$VAULT_SAVE_TOKEN"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_RENEW_TOKEN
|
||||||
|
_savedeployconf VAULT_RENEW_TOKEN "$VAULT_RENEW_TOKEN"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_KV_V2
|
||||||
|
_savedeployconf VAULT_KV_V2 "$VAULT_KV_V2"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_TOKEN
|
||||||
|
if [ -z "$VAULT_TOKEN" ]; then
|
||||||
|
_err "VAULT_TOKEN needs to be defined"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -n "$VAULT_SAVE_TOKEN" ]; then
|
||||||
|
_savedeployconf VAULT_TOKEN "$VAULT_TOKEN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_migratedeployconf FABIO VAULT_FABIO_MODE
|
||||||
|
|
||||||
|
# JSON does not allow multiline strings.
|
||||||
|
# So replacing new-lines with "\n" here
|
||||||
|
_ckey=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$2")
|
||||||
|
_ccert=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$3")
|
||||||
|
_cca=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$4")
|
||||||
|
_cfullchain=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$5")
|
||||||
|
|
||||||
|
export _H1="X-Vault-Token: $VAULT_TOKEN"
|
||||||
|
|
||||||
|
if [ -n "$VAULT_RENEW_TOKEN" ]; then
|
||||||
|
URL="$VAULT_ADDR/v1/auth/token/renew-self"
|
||||||
|
_info "Renew the Vault token to default TTL"
|
||||||
|
if ! _post "" "$URL" >/dev/null; then
|
||||||
|
_err "Failed to renew the Vault token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain"
|
||||||
|
|
||||||
|
if [ -n "$VAULT_FABIO_MODE" ]; then
|
||||||
|
_info "Writing certificate and key to $URL in Fabio mode"
|
||||||
|
if [ -n "$VAULT_KV_V2" ]; then
|
||||||
|
_post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL" >/dev/null || return 1
|
||||||
|
else
|
||||||
|
_post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL" >/dev/null || return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -n "$VAULT_KV_V2" ]; then
|
||||||
|
_info "Writing certificate to $URL/cert.pem"
|
||||||
|
_post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem" >/dev/null || return 1
|
||||||
|
_info "Writing key to $URL/cert.key"
|
||||||
|
_post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key" >/dev/null || return 1
|
||||||
|
_info "Writing CA certificate to $URL/ca.pem"
|
||||||
|
_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/ca.pem" >/dev/null || return 1
|
||||||
|
_info "Writing full-chain certificate to $URL/fullchain.pem"
|
||||||
|
_post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem" >/dev/null || return 1
|
||||||
|
else
|
||||||
|
_info "Writing certificate to $URL/cert.pem"
|
||||||
|
_post "{\"value\": \"$_ccert\"}" "$URL/cert.pem" >/dev/null || return 1
|
||||||
|
_info "Writing key to $URL/cert.key"
|
||||||
|
_post "{\"value\": \"$_ckey\"}" "$URL/cert.key" >/dev/null || return 1
|
||||||
|
_info "Writing CA certificate to $URL/ca.pem"
|
||||||
|
_post "{\"value\": \"$_cca\"}" "$URL/ca.pem" >/dev/null || return 1
|
||||||
|
_info "Writing full-chain certificate to $URL/fullchain.pem"
|
||||||
|
_post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem" >/dev/null || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# To make it compatible with the wrong ca path `chain.pem` which was used in former versions
|
||||||
|
if _contains "$(_get "$URL/chain.pem")" "-----BEGIN CERTIFICATE-----"; then
|
||||||
|
_err "The CA certificate has moved from chain.pem to ca.pem, if you don't depend on chain.pem anymore, you can delete it to avoid this warning"
|
||||||
|
_info "Updating CA certificate to $URL/chain.pem for backward compatibility"
|
||||||
|
if [ -n "$VAULT_KV_V2" ]; then
|
||||||
|
_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem" >/dev/null || return 1
|
||||||
|
else
|
||||||
|
_post "{\"value\": \"$_cca\"}" "$URL/chain.pem" >/dev/null || return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
@ -8,6 +8,8 @@
|
|||||||
#
|
#
|
||||||
# VAULT_PREFIX - this contains the prefix path in vault
|
# VAULT_PREFIX - this contains the prefix path in vault
|
||||||
# VAULT_ADDR - vault requires this to find your vault server
|
# VAULT_ADDR - vault requires this to find your vault server
|
||||||
|
# VAULT_SAVE_TOKEN - set to anything if you want to save the token
|
||||||
|
# VAULT_RENEW_TOKEN - set to anything if you want to renew the token to default TTL before deploying
|
||||||
#
|
#
|
||||||
# additionally, you need to ensure that VAULT_TOKEN is avialable or
|
# additionally, you need to ensure that VAULT_TOKEN is avialable or
|
||||||
# `vault auth` has applied the appropriate authorization for the vault binary
|
# `vault auth` has applied the appropriate authorization for the vault binary
|
||||||
@ -33,29 +35,70 @@ vault_cli_deploy() {
|
|||||||
_debug _cfullchain "$_cfullchain"
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
# validate required env vars
|
# validate required env vars
|
||||||
|
_getdeployconf VAULT_PREFIX
|
||||||
if [ -z "$VAULT_PREFIX" ]; then
|
if [ -z "$VAULT_PREFIX" ]; then
|
||||||
_err "VAULT_PREFIX needs to be defined (contains prefix path in vault)"
|
_err "VAULT_PREFIX needs to be defined (contains prefix path in vault)"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
_savedeployconf VAULT_PREFIX "$VAULT_PREFIX"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_ADDR
|
||||||
if [ -z "$VAULT_ADDR" ]; then
|
if [ -z "$VAULT_ADDR" ]; then
|
||||||
_err "VAULT_ADDR needs to be defined (contains vault connection address)"
|
_err "VAULT_ADDR needs to be defined (contains vault connection address)"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
_savedeployconf VAULT_ADDR "$VAULT_ADDR"
|
||||||
|
|
||||||
VAULT_CMD=$(which vault)
|
_getdeployconf VAULT_SAVE_TOKEN
|
||||||
|
_savedeployconf VAULT_SAVE_TOKEN "$VAULT_SAVE_TOKEN"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_RENEW_TOKEN
|
||||||
|
_savedeployconf VAULT_RENEW_TOKEN "$VAULT_RENEW_TOKEN"
|
||||||
|
|
||||||
|
_getdeployconf VAULT_TOKEN
|
||||||
|
if [ -z "$VAULT_TOKEN" ]; then
|
||||||
|
_err "VAULT_TOKEN needs to be defined"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -n "$VAULT_SAVE_TOKEN" ]; then
|
||||||
|
_savedeployconf VAULT_TOKEN "$VAULT_TOKEN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_migratedeployconf FABIO VAULT_FABIO_MODE
|
||||||
|
|
||||||
|
VAULT_CMD=$(command -v vault)
|
||||||
if [ ! $? ]; then
|
if [ ! $? ]; then
|
||||||
_err "cannot find vault binary!"
|
_err "cannot find vault binary!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$FABIO" ]; then
|
if [ -n "$VAULT_RENEW_TOKEN" ]; then
|
||||||
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}" cert=@"$_cfullchain" key=@"$_ckey" || return 1
|
_info "Renew the Vault token to default TTL"
|
||||||
|
if ! $VAULT_CMD token renew; then
|
||||||
|
_err "Failed to renew the Vault token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$VAULT_FABIO_MODE" ]; then
|
||||||
|
_info "Writing certificate and key to ${VAULT_PREFIX}/${_cdomain} in Fabio mode"
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}" cert=@"$_cfullchain" key=@"$_ckey" || return 1
|
||||||
else
|
else
|
||||||
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
|
_info "Writing certificate to ${VAULT_PREFIX}/${_cdomain}/cert.pem"
|
||||||
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
|
||||||
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
|
_info "Writing key to ${VAULT_PREFIX}/${_cdomain}/cert.key"
|
||||||
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
|
||||||
|
_info "Writing CA certificate to ${VAULT_PREFIX}/${_cdomain}/ca.pem"
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/ca.pem" value=@"$_cca" || return 1
|
||||||
|
_info "Writing full-chain certificate to ${VAULT_PREFIX}/${_cdomain}/fullchain.pem"
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
|
||||||
|
|
||||||
|
# To make it compatible with the wrong ca path `chain.pem` which was used in former versions
|
||||||
|
if $VAULT_CMD kv get "${VAULT_PREFIX}/${_cdomain}/chain.pem" >/dev/null; then
|
||||||
|
_err "The CA certificate has moved from chain.pem to ca.pem, if you don't depend on chain.pem anymore, you can delete it to avoid this warning"
|
||||||
|
_info "Updating CA certificate to ${VAULT_PREFIX}/${_cdomain}/chain.pem for backward compatibility"
|
||||||
|
$VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -65,9 +65,9 @@ vsftpd_deploy() {
|
|||||||
cp "$_vsftpd_conf" "$_backup_conf"
|
cp "$_vsftpd_conf" "$_backup_conf"
|
||||||
|
|
||||||
_info "Modify vsftpd conf: $_vsftpd_conf"
|
_info "Modify vsftpd conf: $_vsftpd_conf"
|
||||||
if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" \
|
if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" &&
|
||||||
&& _setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" \
|
_setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" &&
|
||||||
&& _setopt "$_vsftpd_conf" "ssl_enable" "=" "YES"; then
|
_setopt "$_vsftpd_conf" "ssl_enable" "=" "YES"; then
|
||||||
_info "Set config success!"
|
_info "Set config success!"
|
||||||
else
|
else
|
||||||
_err "Config vsftpd server error, please report bug to us."
|
_err "Config vsftpd server error, please report bug to us."
|
||||||
@ -106,5 +106,5 @@ vsftpd_deploy() {
|
|||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
DNS api usage:
|
DNS api usage:
|
||||||
|
|
||||||
|
|
||||||
https://github.com/Neilpang/acme.sh/wiki/dnsapi
|
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
|
||||||
|
|
||||||
|
268
dnsapi/dns_1984hosting.sh
Executable file
268
dnsapi/dns_1984hosting.sh
Executable file
@ -0,0 +1,268 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_1984hosting_info='1984.hosting
|
||||||
|
Domains: 1984.is
|
||||||
|
Site: 1984.hosting
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_1984hosting
|
||||||
|
Options:
|
||||||
|
One984HOSTING_Username Username
|
||||||
|
One984HOSTING_Password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2851
|
||||||
|
Author: Adrian Fedoreanu
|
||||||
|
'
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Add a text record.
|
||||||
|
dns_1984hosting_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Add TXT record using 1984Hosting."
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _1984hosting_login; then
|
||||||
|
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone."
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain '$fulldomain'."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
_debug "Add TXT record $fulldomain with value '$txtvalue'."
|
||||||
|
value="$(printf '%s' "$txtvalue" | _url_encode)"
|
||||||
|
url="https://1984.hosting/domains/entry/"
|
||||||
|
|
||||||
|
postdata="entry=new"
|
||||||
|
postdata="$postdata&type=TXT"
|
||||||
|
postdata="$postdata&ttl=900"
|
||||||
|
postdata="$postdata&zone=$_domain"
|
||||||
|
postdata="$postdata&host=$_sub_domain"
|
||||||
|
postdata="$postdata&rdata=%22$value%22"
|
||||||
|
_debug2 postdata "$postdata"
|
||||||
|
|
||||||
|
_authpost "$postdata" "$url"
|
||||||
|
if _contains "$_response" '"haserrors": true'; then
|
||||||
|
_err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post."
|
||||||
|
return 1
|
||||||
|
elif _contains "$_response" "html>"; then
|
||||||
|
_err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file."
|
||||||
|
return 1
|
||||||
|
elif _contains "$_response" '"auth": false'; then
|
||||||
|
_err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Added acme challenge TXT record for $fulldomain at 1984Hosting."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Remove the txt record after validation.
|
||||||
|
dns_1984hosting_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Delete TXT record using 1984Hosting."
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _1984hosting_login; then
|
||||||
|
_err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone."
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain '$fulldomain'."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug "Delete $fulldomain TXT record."
|
||||||
|
|
||||||
|
url="https://1984.hosting/domains"
|
||||||
|
if ! _get_zone_id "$url" "$_domain"; then
|
||||||
|
_err "Invalid zone '$_domain'."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_htmlget "$url/$_zone_id" "$txtvalue"
|
||||||
|
entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
|
||||||
|
_debug2 entry_id "$entry_id"
|
||||||
|
if [ -z "$entry_id" ]; then
|
||||||
|
_err "Error getting TXT entry_id for $1."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_authpost "entry=$entry_id" "$url/delentry/"
|
||||||
|
if ! _contains "$_response" '"ok": true'; then
|
||||||
|
_err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
_1984hosting_login() {
|
||||||
|
if ! _check_credentials; then return 1; fi
|
||||||
|
|
||||||
|
if _check_cookies; then
|
||||||
|
_debug "Already logged in."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Login to 1984Hosting as user $One984HOSTING_Username."
|
||||||
|
username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
|
||||||
|
password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
|
||||||
|
url="https://1984.hosting/api/auth/"
|
||||||
|
|
||||||
|
_get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
|
||||||
|
csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
|
||||||
|
sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
|
||||||
|
|
||||||
|
if [ -z "$csrftoken" ] || [ -z "$sessionid" ]; then
|
||||||
|
_err "One or more cookies are empty: '$csrftoken', '$sessionid'."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export _H1="Cookie: $csrftoken; $sessionid"
|
||||||
|
export _H2="Referer: https://1984.hosting/accounts/login/"
|
||||||
|
csrf_header=$(echo "$csrftoken" | sed 's/csrftoken=//' | _head_n 1)
|
||||||
|
export _H3="X-CSRFToken: $csrf_header"
|
||||||
|
|
||||||
|
response="$(_post "username=$username&password=$password&otpkey=" $url)"
|
||||||
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
if _contains "$response" '"loggedin": true'; then
|
||||||
|
One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
|
||||||
|
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
|
||||||
|
export One984HOSTING_SESSIONID_COOKIE
|
||||||
|
export One984HOSTING_CSRFTOKEN_COOKIE
|
||||||
|
_saveaccountconf_mutable One984HOSTING_Username "$One984HOSTING_Username"
|
||||||
|
_saveaccountconf_mutable One984HOSTING_Password "$One984HOSTING_Password"
|
||||||
|
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_credentials() {
|
||||||
|
One984HOSTING_Username="${One984HOSTING_Username:-$(_readaccountconf_mutable One984HOSTING_Username)}"
|
||||||
|
One984HOSTING_Password="${One984HOSTING_Password:-$(_readaccountconf_mutable One984HOSTING_Password)}"
|
||||||
|
if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
|
||||||
|
One984HOSTING_Username=""
|
||||||
|
One984HOSTING_Password=""
|
||||||
|
_clearaccountconf_mutable One984HOSTING_Username
|
||||||
|
_clearaccountconf_mutable One984HOSTING_Password
|
||||||
|
_err "You haven't specified 1984Hosting username or password yet."
|
||||||
|
_err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_cookies() {
|
||||||
|
One984HOSTING_SESSIONID_COOKIE="${One984HOSTING_SESSIONID_COOKIE:-$(_readaccountconf_mutable One984HOSTING_SESSIONID_COOKIE)}"
|
||||||
|
One984HOSTING_CSRFTOKEN_COOKIE="${One984HOSTING_CSRFTOKEN_COOKIE:-$(_readaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE)}"
|
||||||
|
if [ -z "$One984HOSTING_SESSIONID_COOKIE" ] || [ -z "$One984HOSTING_CSRFTOKEN_COOKIE" ]; then
|
||||||
|
_debug "No cached cookie(s) found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_authget "https://1984.hosting/api/auth/"
|
||||||
|
if _contains "$_response" '"ok": true'; then
|
||||||
|
_debug "Cached cookies still valid."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Cached cookies no longer valid. Clearing cookies."
|
||||||
|
One984HOSTING_SESSIONID_COOKIE=""
|
||||||
|
One984HOSTING_CSRFTOKEN_COOKIE=""
|
||||||
|
_clearaccountconf_mutable One984HOSTING_SESSIONID_COOKIE
|
||||||
|
_clearaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# _acme-challenge.www.domain.com
|
||||||
|
# Returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain="$1"
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
|
|
||||||
|
# not valid
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_authget "https://1984.hosting/domains/zonestatus/$h/?cached=no"
|
||||||
|
if _contains "$_response" '"ok": true'; 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
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _get_zone_id url domain.com
|
||||||
|
# Returns zone id for domain.com
|
||||||
|
_get_zone_id() {
|
||||||
|
url=$1
|
||||||
|
domain=$2
|
||||||
|
_htmlget "$url" "$domain"
|
||||||
|
_zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
|
||||||
|
_debug2 _zone_id "$_zone_id"
|
||||||
|
if [ -z "$_zone_id" ]; then
|
||||||
|
_err "Error getting _zone_id for $2."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add extra headers to request
|
||||||
|
_authget() {
|
||||||
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
_response=$(_get "$1" | _normalizeJson)
|
||||||
|
_debug2 _response "$_response"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Truncate huge HTML response
|
||||||
|
_htmlget() {
|
||||||
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
_response=$(_get "$1" | grep "$2")
|
||||||
|
if _contains "$_response" "@$2"; then
|
||||||
|
_response=$(echo "$_response" | grep -v "[@]" | _head_n 1)
|
||||||
|
fi
|
||||||
|
_debug2 _response "$_response"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add extra headers to request
|
||||||
|
_authpost() {
|
||||||
|
url="https://1984.hosting/domains"
|
||||||
|
_get_zone_id "$url" "$_domain"
|
||||||
|
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
|
||||||
|
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
|
||||||
|
export _H2="Referer: https://1984.hosting/domains/$_zone_id"
|
||||||
|
export _H3="X-CSRFToken: $csrf_header"
|
||||||
|
_response="$(_post "$1" "$2" | _normalizeJson)"
|
||||||
|
_debug2 _response "$_response"
|
||||||
|
}
|
69
dnsapi/dns_acmedns.sh
Normal file → Executable file
69
dnsapi/dns_acmedns.sh
Normal file → Executable file
@ -1,31 +1,70 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#
|
# shellcheck disable=SC2034
|
||||||
#Author: Wolfgang Ebner
|
dns_acmedns_info='acme-dns Server API
|
||||||
#Report Bugs here: https://github.com/webner/acme.sh
|
The acme-dns is a limited DNS server with RESTful API to handle ACME DNS challenges.
|
||||||
#
|
Site: github.com/joohoi/acme-dns
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_acmedns
|
||||||
|
Options:
|
||||||
|
ACMEDNS_USERNAME Username. Optional.
|
||||||
|
ACMEDNS_PASSWORD Password. Optional.
|
||||||
|
ACMEDNS_SUBDOMAIN Subdomain. Optional.
|
||||||
|
ACMEDNS_BASE_URL API endpoint. Default: "https://auth.acme-dns.io".
|
||||||
|
Issues: github.com/dampfklon/acme.sh
|
||||||
|
Author: Wolfgang Ebner, Sven Neubuaer
|
||||||
|
'
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
#Usage: dns_acmedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_acmedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
dns_acmedns_add() {
|
dns_acmedns_add() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
_info "Using acme-dns"
|
_info "Using acme-dns"
|
||||||
_debug fulldomain "$fulldomain"
|
_debug "fulldomain $fulldomain"
|
||||||
_debug txtvalue "$txtvalue"
|
_debug "txtvalue $txtvalue"
|
||||||
|
|
||||||
ACMEDNS_UPDATE_URL="${ACMEDNS_UPDATE_URL:-$(_readaccountconf_mutable ACMEDNS_UPDATE_URL)}"
|
#for compatiblity from account conf
|
||||||
ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readaccountconf_mutable ACMEDNS_USERNAME)}"
|
ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readaccountconf_mutable ACMEDNS_USERNAME)}"
|
||||||
|
_clearaccountconf_mutable ACMEDNS_USERNAME
|
||||||
ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readaccountconf_mutable ACMEDNS_PASSWORD)}"
|
ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readaccountconf_mutable ACMEDNS_PASSWORD)}"
|
||||||
|
_clearaccountconf_mutable ACMEDNS_PASSWORD
|
||||||
ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readaccountconf_mutable ACMEDNS_SUBDOMAIN)}"
|
ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readaccountconf_mutable ACMEDNS_SUBDOMAIN)}"
|
||||||
|
_clearaccountconf_mutable ACMEDNS_SUBDOMAIN
|
||||||
|
|
||||||
if [ "$ACMEDNS_UPDATE_URL" = "" ]; then
|
ACMEDNS_BASE_URL="${ACMEDNS_BASE_URL:-$(_readdomainconf ACMEDNS_BASE_URL)}"
|
||||||
ACMEDNS_UPDATE_URL="https://auth.acme-dns.io/update"
|
ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readdomainconf ACMEDNS_USERNAME)}"
|
||||||
|
ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readdomainconf ACMEDNS_PASSWORD)}"
|
||||||
|
ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readdomainconf ACMEDNS_SUBDOMAIN)}"
|
||||||
|
|
||||||
|
if [ "$ACMEDNS_BASE_URL" = "" ]; then
|
||||||
|
ACMEDNS_BASE_URL="https://auth.acme-dns.io"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_saveaccountconf_mutable ACMEDNS_UPDATE_URL "$ACMEDNS_UPDATE_URL"
|
ACMEDNS_UPDATE_URL="$ACMEDNS_BASE_URL/update"
|
||||||
_saveaccountconf_mutable ACMEDNS_USERNAME "$ACMEDNS_USERNAME"
|
ACMEDNS_REGISTER_URL="$ACMEDNS_BASE_URL/register"
|
||||||
_saveaccountconf_mutable ACMEDNS_PASSWORD "$ACMEDNS_PASSWORD"
|
|
||||||
_saveaccountconf_mutable ACMEDNS_SUBDOMAIN "$ACMEDNS_SUBDOMAIN"
|
if [ -z "$ACMEDNS_USERNAME" ] || [ -z "$ACMEDNS_PASSWORD" ]; then
|
||||||
|
response="$(_post "" "$ACMEDNS_REGISTER_URL" "" "POST")"
|
||||||
|
_debug response "$response"
|
||||||
|
ACMEDNS_USERNAME=$(echo "$response" | sed -n 's/^{.*\"username\":[ ]*\"\([^\"]*\)\".*}/\1/p')
|
||||||
|
_debug "received username: $ACMEDNS_USERNAME"
|
||||||
|
ACMEDNS_PASSWORD=$(echo "$response" | sed -n 's/^{.*\"password\":[ ]*\"\([^\"]*\)\".*}/\1/p')
|
||||||
|
_debug "received password: $ACMEDNS_PASSWORD"
|
||||||
|
ACMEDNS_SUBDOMAIN=$(echo "$response" | sed -n 's/^{.*\"subdomain\":[ ]*\"\([^\"]*\)\".*}/\1/p')
|
||||||
|
_debug "received subdomain: $ACMEDNS_SUBDOMAIN"
|
||||||
|
ACMEDNS_FULLDOMAIN=$(echo "$response" | sed -n 's/^{.*\"fulldomain\":[ ]*\"\([^\"]*\)\".*}/\1/p')
|
||||||
|
_info "##########################################################"
|
||||||
|
_info "# Create $fulldomain CNAME $ACMEDNS_FULLDOMAIN DNS entry #"
|
||||||
|
_info "##########################################################"
|
||||||
|
_info "Press enter to continue... "
|
||||||
|
read -r _
|
||||||
|
fi
|
||||||
|
|
||||||
|
_savedomainconf ACMEDNS_BASE_URL "$ACMEDNS_BASE_URL"
|
||||||
|
_savedomainconf ACMEDNS_USERNAME "$ACMEDNS_USERNAME"
|
||||||
|
_savedomainconf ACMEDNS_PASSWORD "$ACMEDNS_PASSWORD"
|
||||||
|
_savedomainconf ACMEDNS_SUBDOMAIN "$ACMEDNS_SUBDOMAIN"
|
||||||
|
|
||||||
export _H1="X-Api-User: $ACMEDNS_USERNAME"
|
export _H1="X-Api-User: $ACMEDNS_USERNAME"
|
||||||
export _H2="X-Api-Key: $ACMEDNS_PASSWORD"
|
export _H2="X-Api-Key: $ACMEDNS_PASSWORD"
|
||||||
@ -48,8 +87,8 @@ dns_acmedns_rm() {
|
|||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
_info "Using acme-dns"
|
_info "Using acme-dns"
|
||||||
_debug fulldomain "$fulldomain"
|
_debug "fulldomain $fulldomain"
|
||||||
_debug txtvalue "$txtvalue"
|
_debug "txtvalue $txtvalue"
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
|
18
dnsapi/dns_acmeproxy.sh
Normal file → Executable file
18
dnsapi/dns_acmeproxy.sh
Normal file → Executable file
@ -1,9 +1,17 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
## Acmeproxy DNS provider to be used with acmeproxy (http://github.com/mdbraber/acmeproxy)
|
dns_acmeproxy_info='AcmeProxy Server API
|
||||||
## API integration by Maarten den Braber
|
AcmeProxy can be used to as a single host in your network to request certificates through a DNS API.
|
||||||
##
|
Clients can connect with the one AcmeProxy host so you do not need to store DNS API credentials on every single host.
|
||||||
## Report any bugs via https://github.com/mdbraber/acme.sh
|
Site: github.com/mdbraber/acmeproxy
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_acmeproxy
|
||||||
|
Options:
|
||||||
|
ACMEPROXY_ENDPOINT API Endpoint
|
||||||
|
ACMEPROXY_USERNAME Username
|
||||||
|
ACMEPROXY_PASSWORD Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2251
|
||||||
|
Author: Maarten den Braber
|
||||||
|
'
|
||||||
|
|
||||||
dns_acmeproxy_add() {
|
dns_acmeproxy_add() {
|
||||||
fulldomain="${1}"
|
fulldomain="${1}"
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#ACTIVE24_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
dns_active24_info='Active24.com
|
||||||
|
Site: Active24.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_active24
|
||||||
|
Options:
|
||||||
|
ACTIVE24_Token API Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2059
|
||||||
|
Author: Milan Pála
|
||||||
|
'
|
||||||
|
|
||||||
ACTIVE24_Api="https://api.active24.com"
|
ACTIVE24_Api="https://api.active24.com"
|
||||||
|
|
||||||
@ -76,10 +83,10 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
i=2
|
i=1
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug "h" "$h"
|
_debug "h" "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -87,7 +94,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "\"$h\"" >/dev/null; then
|
if _contains "$response" "\"$h\"" >/dev/null; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#
|
dns_ad_info='AlwaysData.com
|
||||||
#AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
Site: AlwaysData.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ad
|
||||||
#This is the Alwaysdata api wrapper for acme.sh
|
Options:
|
||||||
#
|
AD_API_KEY API Key
|
||||||
#Author: Paul Koppen
|
Issues: github.com/acmesh-official/acme.sh/pull/503
|
||||||
#Report Bugs here: https://github.com/wpk-/acme.sh
|
Author: Paul Koppen
|
||||||
|
'
|
||||||
|
|
||||||
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
|
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
|
||||||
|
|
||||||
@ -94,7 +95,7 @@ _get_root() {
|
|||||||
if _ad_rest GET "domain/"; then
|
if _ad_rest GET "domain/"; then
|
||||||
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
|
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -105,7 +106,7 @@ _get_root() {
|
|||||||
if [ "$hostedzone" ]; then
|
if [ "$hostedzone" ]; then
|
||||||
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
|
_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
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_ali_info='AlibabaCloud.com
|
||||||
|
Domains: Aliyun.com
|
||||||
|
Site: AlibabaCloud.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ali
|
||||||
|
Options:
|
||||||
|
Ali_Key API Key
|
||||||
|
Ali_Secret API Secret
|
||||||
|
'
|
||||||
|
|
||||||
Ali_API="https://alidns.aliyuncs.com/"
|
# NOTICE:
|
||||||
|
# This file is referenced by Alibaba Cloud Services deploy hooks
|
||||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
|
||||||
|
# Be careful when modifying this file, especially when making breaking changes for common functions
|
||||||
|
|
||||||
#Ali_Key="LTqIA87hOKdjevsf5"
|
Ali_DNS_API="https://alidns.aliyuncs.com/"
|
||||||
#Ali_Secret="0p5EYueFNq501xnCPzKNbx6K51qPH2"
|
|
||||||
|
|
||||||
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
dns_ali_add() {
|
dns_ali_add() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
|
_prepare_ali_credentials || return 1
|
||||||
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
|
|
||||||
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_mutable Ali_Key "$Ali_Key"
|
|
||||||
_saveaccountconf_mutable Ali_Secret "$Ali_Secret"
|
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone"
|
||||||
if ! _get_root "$fulldomain"; then
|
if ! _get_root "$fulldomain"; then
|
||||||
@ -46,14 +46,74 @@ dns_ali_rm() {
|
|||||||
_clean
|
_clean
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Alibaba Cloud common functions below ####################
|
||||||
|
|
||||||
|
_prepare_ali_credentials() {
|
||||||
|
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
|
||||||
|
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
|
||||||
|
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_mutable Ali_Key "$Ali_Key"
|
||||||
|
_saveaccountconf_mutable Ali_Secret "$Ali_Secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
# act ign mtd
|
||||||
|
_ali_rest() {
|
||||||
|
act="$1"
|
||||||
|
ign="$2"
|
||||||
|
mtd="${3:-GET}"
|
||||||
|
|
||||||
|
signature=$(printf "%s" "$mtd&%2F&$(printf "%s" "$query" | _url_encode upper-hex)" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
|
||||||
|
signature=$(printf "%s" "$signature" | _url_encode upper-hex)
|
||||||
|
url="$endpoint?Signature=$signature"
|
||||||
|
|
||||||
|
if [ "$mtd" = "GET" ]; then
|
||||||
|
url="$url&$query"
|
||||||
|
response="$(_get "$url")"
|
||||||
|
else
|
||||||
|
response="$(_post "$query" "$url" "" "$mtd" "application/x-www-form-urlencoded")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_ret="$?"
|
||||||
|
_debug2 response "$response"
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "Error <$act>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ign" ]; then
|
||||||
|
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
|
||||||
|
if [ "$message" ]; then
|
||||||
|
_err "$message"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_ali_nonce() {
|
||||||
|
#_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
|
||||||
|
#Not so good...
|
||||||
|
date +"%s%N" | sed 's/%N//g'
|
||||||
|
}
|
||||||
|
|
||||||
|
_timestamp() {
|
||||||
|
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ####################
|
||||||
|
|
||||||
_get_root() {
|
_get_root() {
|
||||||
domain=$1
|
domain=$1
|
||||||
i=2
|
i=1
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
return 1
|
return 1
|
||||||
@ -65,7 +125,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "PageNumber"; then
|
if _contains "$response" "PageNumber"; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
@ -77,52 +137,10 @@ _get_root() {
|
|||||||
return 1
|
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
|
|
||||||
|
|
||||||
_debug2 response "$response"
|
|
||||||
if [ -z "$2" ]; then
|
|
||||||
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
|
|
||||||
if [ "$message" ]; then
|
|
||||||
_err "$message"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_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() {
|
_check_exist_query() {
|
||||||
_qdomain="$1"
|
_qdomain="$1"
|
||||||
_qsubdomain="$2"
|
_qsubdomain="$2"
|
||||||
|
endpoint=$Ali_DNS_API
|
||||||
query=''
|
query=''
|
||||||
query=$query'AccessKeyId='$Ali_Key
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
query=$query'&Action=DescribeDomainRecords'
|
query=$query'&Action=DescribeDomainRecords'
|
||||||
@ -138,6 +156,7 @@ _check_exist_query() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_add_record_query() {
|
_add_record_query() {
|
||||||
|
endpoint=$Ali_DNS_API
|
||||||
query=''
|
query=''
|
||||||
query=$query'AccessKeyId='$Ali_Key
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
query=$query'&Action=AddDomainRecord'
|
query=$query'&Action=AddDomainRecord'
|
||||||
@ -154,6 +173,7 @@ _add_record_query() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_delete_record_query() {
|
_delete_record_query() {
|
||||||
|
endpoint=$Ali_DNS_API
|
||||||
query=''
|
query=''
|
||||||
query=$query'AccessKeyId='$Ali_Key
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
query=$query'&Action=DeleteDomainRecord'
|
query=$query'&Action=DeleteDomainRecord'
|
||||||
@ -167,6 +187,7 @@ _delete_record_query() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_describe_records_query() {
|
_describe_records_query() {
|
||||||
|
endpoint=$Ali_DNS_API
|
||||||
query=''
|
query=''
|
||||||
query=$query'AccessKeyId='$Ali_Key
|
query=$query'AccessKeyId='$Ali_Key
|
||||||
query=$query'&Action=DescribeDomainRecords'
|
query=$query'&Action=DescribeDomainRecords'
|
||||||
@ -181,6 +202,7 @@ _describe_records_query() {
|
|||||||
|
|
||||||
_clean() {
|
_clean() {
|
||||||
_check_exist_query "$_domain" "$_sub_domain"
|
_check_exist_query "$_domain" "$_sub_domain"
|
||||||
|
# do not correct grammar here
|
||||||
if ! _ali_rest "Check exist records" "ignore"; then
|
if ! _ali_rest "Check exist records" "ignore"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -196,7 +218,3 @@ _clean() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_timestamp() {
|
|
||||||
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
|
|
||||||
}
|
|
||||||
|
185
dnsapi/dns_alviy.sh
Normal file
185
dnsapi/dns_alviy.sh
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_alviy_info='Alviy.com
|
||||||
|
Site: Alviy.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_alviy
|
||||||
|
Options:
|
||||||
|
Alviy_token API token. Get it from the https://cloud.alviy.com/token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/5115
|
||||||
|
'
|
||||||
|
|
||||||
|
Alviy_Api="https://cloud.alviy.com/api/v1"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_alviy_add _acme-challenge.www.domain.com "content"
|
||||||
|
dns_alviy_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
|
||||||
|
if [ -z "$Alviy_token" ]; then
|
||||||
|
Alviy_token=""
|
||||||
|
_err "Please specify Alviy token."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and email to the account conf file.
|
||||||
|
_saveaccountconf_mutable Alviy_token "$Alviy_token"
|
||||||
|
|
||||||
|
_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 existing records"
|
||||||
|
if _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_info "This record already exists, skipping"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_add_data="{\"content\":\"$txtvalue\",\"type\":\"TXT\"}"
|
||||||
|
_debug2 _add_data "$_add_data"
|
||||||
|
_info "Adding record"
|
||||||
|
if _alviy_rest POST "zone/$_domain/domain/$fulldomain/" "$_add_data"; then
|
||||||
|
_debug "Checking updated records of '${fulldomain}'"
|
||||||
|
|
||||||
|
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_err "TXT record '${txtvalue}' for '${fulldomain}', value wasn't set!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
_err "Add txt record error, value '${txtvalue}' for '${fulldomain}' was not set."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_sleep 10
|
||||||
|
_info "Added TXT record '${txtvalue}' for '${fulldomain}'."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain
|
||||||
|
dns_alviy_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
|
||||||
|
|
||||||
|
_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"
|
||||||
|
|
||||||
|
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
|
||||||
|
_info "The record does not exist, skip"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_add_data=""
|
||||||
|
uuid=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | tr "," "\n" | grep uuid | cut -d \" -f4)
|
||||||
|
# delete record
|
||||||
|
_debug "Delete TXT record for '${fulldomain}'"
|
||||||
|
if ! _alviy_rest DELETE "zone/$_domain/record/$uuid" "{\"confirm\":1}"; then
|
||||||
|
_err "Cannot delete empty TXT record for '$fulldomain'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_info "The record '$fulldomain'='$txtvalue' deleted"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=3
|
||||||
|
a="init"
|
||||||
|
while [ -n "$a" ]; do
|
||||||
|
a=$(printf "%s" "$domain" | cut -d . -f $i-)
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
n=$((i - 3))
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f $n-)
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
#not valid
|
||||||
|
_alviy_rest GET "zone/$domain/"
|
||||||
|
_debug "can't get host from $domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _alviy_rest GET "zone/$h/"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" '"code":"NOT_FOUND"'; then
|
||||||
|
_debug "$h not found"
|
||||||
|
else
|
||||||
|
s=$((n - 1))
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f -$s)
|
||||||
|
_domain="$h"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_alviy_txt_exists() {
|
||||||
|
zone=$1
|
||||||
|
domain=$2
|
||||||
|
content_data=$3
|
||||||
|
_debug "Getting existing records"
|
||||||
|
|
||||||
|
if ! _alviy_rest GET "zone/$zone/domain/$domain/TXT/"; then
|
||||||
|
_info "The record does not exist"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "$3"; then
|
||||||
|
_info "The record has other value"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# GOOD code return - TRUE function
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_alviy_rest() {
|
||||||
|
method=$1
|
||||||
|
path="$2"
|
||||||
|
content_data="$3"
|
||||||
|
_debug "$path"
|
||||||
|
|
||||||
|
export _H1="Authorization: Bearer $Alviy_token"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$content_data" ] || [ "$method" = "DELETE" ]; then
|
||||||
|
_debug "data ($method): " "$content_data"
|
||||||
|
response="$(_post "$content_data" "$Alviy_Api/$path" "" "$method")"
|
||||||
|
else
|
||||||
|
response="$(_get "$Alviy_Api/$path")"
|
||||||
|
fi
|
||||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
|
||||||
|
if [ "$_code" = "401" ]; then
|
||||||
|
_err "It seems that your api key or secret is not correct."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_code" != "200" ]; then
|
||||||
|
_err "API call error ($method): $path Response code $_code"
|
||||||
|
fi
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error on rest call ($method): $path. Response:"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
152
dnsapi/dns_anx.sh
Normal file
152
dnsapi/dns_anx.sh
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_anx_info='Anexia.com CloudDNS
|
||||||
|
Site: Anexia.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_anx
|
||||||
|
Options:
|
||||||
|
ANX_Token API Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3238
|
||||||
|
'
|
||||||
|
|
||||||
|
ANX_API='https://engine.anexia-it.com/api/clouddns/v1'
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
dns_anx_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using ANX CDNS API"
|
||||||
|
|
||||||
|
ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if [ "$ANX_Token" ]; then
|
||||||
|
_saveaccountconf_mutable ANX_Token "$ANX_Token"
|
||||||
|
else
|
||||||
|
_err "You didn't specify a ANEXIA Engine API token."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Always add records, wildcard need two records with the same name
|
||||||
|
_anx_rest POST "zone.json/${_domain}/records" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"rdata\":\"$txtvalue\"}"
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_anx_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using ANX CDNS API"
|
||||||
|
|
||||||
|
ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}"
|
||||||
|
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_get_record_id
|
||||||
|
|
||||||
|
if _is_uuid "$_record_id"; then
|
||||||
|
if ! _anx_rest DELETE "zone.json/${_domain}/records/$_record_id"; then
|
||||||
|
_err "Delete record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_info "No record found."
|
||||||
|
fi
|
||||||
|
echo "$response" | tr -d " " | grep \"status\":\"OK\" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_is_uuid() {
|
||||||
|
pattern='^\{?[A-Z0-9a-z]{8}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{12}\}?$'
|
||||||
|
if echo "$1" | _egrep_o "$pattern" >/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_record_id() {
|
||||||
|
_debug subdomain "$_sub_domain"
|
||||||
|
_debug domain "$_domain"
|
||||||
|
|
||||||
|
if _anx_rest GET "zone.json/${_domain}/records?name=$_sub_domain&type=TXT"; then
|
||||||
|
_debug response "$response"
|
||||||
|
if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then
|
||||||
|
_record_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"identifier\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
else
|
||||||
|
_record_id=''
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_err "Search existing record"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_anx_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Authorization: Token $ANX_Token"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "${ANX_API}/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "${ANX_API}/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
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
|
||||||
|
|
||||||
|
_anx_rest GET "zone.json/${h}"
|
||||||
|
if _contains "$response" "\"name\":\"$h\""; 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
|
||||||
|
}
|
177
dnsapi/dns_artfiles.sh
Normal file
177
dnsapi/dns_artfiles.sh
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_artfiles_info='ArtFiles.de
|
||||||
|
Site: ArtFiles.de
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_artfiles
|
||||||
|
Options:
|
||||||
|
AF_API_USERNAME API Username
|
||||||
|
AF_API_PASSWORD API Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/4718
|
||||||
|
Author: Martin Arndt <https://troublezone.net/>
|
||||||
|
'
|
||||||
|
|
||||||
|
########## API configuration ###################################################
|
||||||
|
|
||||||
|
AF_API_SUCCESS='status":"OK'
|
||||||
|
AF_URL_DCP='https://dcp.c.artfiles.de/api/'
|
||||||
|
AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
|
||||||
|
AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html'
|
||||||
|
|
||||||
|
########## Public functions ####################################################
|
||||||
|
|
||||||
|
# Adds a new TXT record for given ACME challenge value & domain.
|
||||||
|
# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
|
||||||
|
dns_artfiles_add() {
|
||||||
|
domain="$1"
|
||||||
|
txtValue="$2"
|
||||||
|
_info 'Using ArtFiles.de DNS addition API…'
|
||||||
|
_debug 'Domain' "$domain"
|
||||||
|
_debug 'txtValue' "$txtValue"
|
||||||
|
|
||||||
|
_set_credentials
|
||||||
|
_saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
|
||||||
|
_saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
|
||||||
|
|
||||||
|
_set_headers
|
||||||
|
_get_zone "$domain"
|
||||||
|
_dns 'GET'
|
||||||
|
if ! _contains "$response" 'TXT'; then
|
||||||
|
_err 'Retrieving TXT records failed.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_clean_records
|
||||||
|
_dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")"
|
||||||
|
if ! _contains "$response" "$AF_API_SUCCESS"; then
|
||||||
|
_err 'Adding ACME challenge value failed.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Removes the existing TXT record for given ACME challenge value & domain.
|
||||||
|
# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
|
||||||
|
dns_artfiles_rm() {
|
||||||
|
domain="$1"
|
||||||
|
txtValue="$2"
|
||||||
|
_info 'Using ArtFiles.de DNS removal API…'
|
||||||
|
_debug 'Domain' "$domain"
|
||||||
|
_debug 'txtValue' "$txtValue"
|
||||||
|
|
||||||
|
_set_credentials
|
||||||
|
_set_headers
|
||||||
|
_get_zone "$domain"
|
||||||
|
if ! _dns 'GET'; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "$txtValue"; then
|
||||||
|
_err 'Retrieved TXT records are missing given ACME challenge value.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_clean_records
|
||||||
|
response="$(printf -- '%s' "$response" | sed '/_acme-challenge "'"$txtValue"'"/d')"
|
||||||
|
_dns 'SET' "$response"
|
||||||
|
if ! _contains "$response" "$AF_API_SUCCESS"; then
|
||||||
|
_err 'Removing ACME challenge value failed.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
########## Private functions ###################################################
|
||||||
|
|
||||||
|
# Cleans awful TXT records response of ArtFiles's API & pretty prints it.
|
||||||
|
# Usage: _clean_records
|
||||||
|
_clean_records() {
|
||||||
|
_info 'Cleaning TXT records…'
|
||||||
|
# Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
|
||||||
|
# usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
|
||||||
|
# from '\"' & turn '\n' into real LF characters.
|
||||||
|
# Yup, awful API to use - but that's all we got to get this working, so… ;)
|
||||||
|
_debug2 'Raw ' "$response"
|
||||||
|
response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
|
||||||
|
_debug2 'Clean' "$response"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Executes an HTTP GET or POST request for getting or setting DNS records,
|
||||||
|
# containing given payload upon POST.
|
||||||
|
# Usage: _dns [GET | SET] [payload]
|
||||||
|
_dns() {
|
||||||
|
_info 'Executing HTTP request…'
|
||||||
|
action="$1"
|
||||||
|
payload="$(printf -- '%s' "$2" | _url_encode)"
|
||||||
|
url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
|
||||||
|
|
||||||
|
if [ "$action" = 'SET' ]; then
|
||||||
|
_debug2 'Payload' "$payload"
|
||||||
|
response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')"
|
||||||
|
else
|
||||||
|
response="$(_get "$url" '' 10)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _contains "$response" "$AF_API_SUCCESS"; then
|
||||||
|
_err "DNS API error: $response"
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug 'Response' "$response"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gets the root domain zone for given domain.
|
||||||
|
# Usage: _get_zone _acme-challenge.www.example.com
|
||||||
|
_get_zone() {
|
||||||
|
fqdn="$1"
|
||||||
|
domains="$(_get "$AF_URL_DOMAINS" '' 10)"
|
||||||
|
_info 'Getting domain zone…'
|
||||||
|
_debug2 'FQDN' "$fqdn"
|
||||||
|
_debug2 'Domains' "$domains"
|
||||||
|
|
||||||
|
while _contains "$fqdn" "."; do
|
||||||
|
if _contains "$domains" "$fqdn"; then
|
||||||
|
domain="$fqdn"
|
||||||
|
_info "Found root domain zone: $domain"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
fqdn="${fqdn#*.}"
|
||||||
|
_debug2 'FQDN' "$fqdn"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$domain" = "$fqdn" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_err 'Couldn'\''t find root domain zone.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sets the credentials for accessing ArtFiles's API
|
||||||
|
# Usage: _set_credentials
|
||||||
|
_set_credentials() {
|
||||||
|
_info 'Setting credentials…'
|
||||||
|
AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
|
||||||
|
AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
|
||||||
|
if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
|
||||||
|
_err 'Missing ArtFiles.de username and/or password.'
|
||||||
|
_err 'Please ensure both are set via export command & try again.'
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Adds the HTTP Authorization & Content-Type headers to a follow-up request.
|
||||||
|
# Usage: _set_headers
|
||||||
|
_set_headers() {
|
||||||
|
_info 'Setting headers…'
|
||||||
|
encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
|
||||||
|
export _H1="Authorization: Basic $encoded"
|
||||||
|
export _H2='Content-Type: application/json'
|
||||||
|
}
|
156
dnsapi/dns_arvan.sh
Normal file
156
dnsapi/dns_arvan.sh
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_arvan_info='ArvanCloud.ir
|
||||||
|
Site: ArvanCloud.ir
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_arvan
|
||||||
|
Options:
|
||||||
|
Arvan_Token API Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2796
|
||||||
|
Author: Vahid Fardi
|
||||||
|
'
|
||||||
|
|
||||||
|
ARVAN_API_URL="https://napi.arvancloud.ir/cdn/4.0/domains"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_arvan_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_arvan_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using Arvan"
|
||||||
|
|
||||||
|
Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
|
||||||
|
|
||||||
|
if [ -z "$Arvan_Token" ]; then
|
||||||
|
_err "You didn't specify \"Arvan_Token\" token yet."
|
||||||
|
_err "You can get yours from here https://npanel.arvancloud.ir/profile/api-keys"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#save the api token to the account conf file.
|
||||||
|
_saveaccountconf_mutable Arvan_Token "$Arvan_Token"
|
||||||
|
|
||||||
|
_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"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
if _arvan_rest POST "$_domain/dns-records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":{\"text\":\"$txtvalue\"},\"ttl\":120}"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "response id is $response"
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
elif _contains "$response" "Record Data is duplicate"; then
|
||||||
|
_info "Already exists, OK"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_arvan_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using Arvan"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
|
||||||
|
|
||||||
|
_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"
|
||||||
|
_arvan_rest GET "${_domain}/dns-records"
|
||||||
|
if ! printf "%s" "$response" | grep \"current_page\":1 >/dev/null; then
|
||||||
|
_err "Error on Arvan Api"
|
||||||
|
_err "Please create a github issue with debbug log"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_record_id=$(echo "$response" | _egrep_o ".\"id\":\"[^\"]*\",\"type\":\"txt\",\"name\":\"_acme-challenge\",\"value\":{\"text\":\"$txtvalue\"}" | cut -d : -f 2 | cut -d , -f 1 | tr -d \")
|
||||||
|
if ! _arvan_rest "DELETE" "${_domain}/dns-records/${_record_id}"; then
|
||||||
|
_err "Error on Arvan Api"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug "$response"
|
||||||
|
_contains "$response" 'dns record deleted'
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### 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 ! _arvan_rest GET "$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if _contains "$response" "\"domain\":\"$h\""; then
|
||||||
|
_domain_id=$(echo "$response" | cut -d : -f 3 | cut -d , -f 1 | 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
|
||||||
|
}
|
||||||
|
|
||||||
|
_arvan_rest() {
|
||||||
|
mtd="$1"
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
|
||||||
|
token_trimmed=$(echo "$Arvan_Token" | tr -d '"')
|
||||||
|
export _H1="Authorization: $token_trimmed"
|
||||||
|
|
||||||
|
if [ "$mtd" = "DELETE" ]; then
|
||||||
|
#DELETE Request shouldn't have Content-Type
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
|
||||||
|
elif [ "$mtd" = "POST" ]; then
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
export _H3="Accept: application/json"
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
|
||||||
|
else
|
||||||
|
response="$(_get "$ARVAN_API_URL/$ep$data")"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
177
dnsapi/dns_aurora.sh
Normal file
177
dnsapi/dns_aurora.sh
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_aurora_info='versio.nl AuroraDNS
|
||||||
|
Domains: pcextreme.nl
|
||||||
|
Site: versio.nl
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_aurora
|
||||||
|
Options:
|
||||||
|
AURORA_Key API Key
|
||||||
|
AURORA_Secret API Secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3459
|
||||||
|
Author: Jasper Zonneveld
|
||||||
|
'
|
||||||
|
|
||||||
|
AURORA_Api="https://api.auroradns.eu"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_aurora_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}"
|
||||||
|
AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}"
|
||||||
|
|
||||||
|
if [ -z "$AURORA_Key" ] || [ -z "$AURORA_Secret" ]; then
|
||||||
|
AURORA_Key=""
|
||||||
|
AURORA_Secret=""
|
||||||
|
_err "You didn't specify an Aurora api key and secret yet."
|
||||||
|
_err "You can get yours from here https://cp.pcextreme.nl/auroradns/users."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and secret to the account conf file.
|
||||||
|
_saveaccountconf_mutable AURORA_Key "$AURORA_Key"
|
||||||
|
_saveaccountconf_mutable AURORA_Secret "$AURORA_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"
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
if _aurora_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Added, OK"
|
||||||
|
return 0
|
||||||
|
elif _contains "$response" "RecordExistsError"; then
|
||||||
|
_info "Already exists, OK"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain txtvalue
|
||||||
|
dns_aurora_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}"
|
||||||
|
AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_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 records"
|
||||||
|
_aurora_rest GET "zones/${_domain_id}/records"
|
||||||
|
|
||||||
|
if ! _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Don't need to remove."
|
||||||
|
else
|
||||||
|
records=$(echo "$response" | _normalizeJson | tr -d "[]" | sed "s/},{/}|{/g" | tr "|" "\n")
|
||||||
|
if [ "$(echo "$records" | wc -l)" -le 2 ]; then
|
||||||
|
_err "Can not parse records."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
record_id=$(echo "$records" | grep "\"type\": *\"TXT\"" | grep "\"name\": *\"$_sub_domain\"" | grep "\"content\": *\"$txtvalue\"" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
|
_debug "record_id" "$record_id"
|
||||||
|
if [ -z "$record_id" ]; then
|
||||||
|
_err "Can not get record id to remove."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _aurora_rest DELETE "zones/$_domain_id/records/$record_id"; then
|
||||||
|
_err "Delete record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### 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=1
|
||||||
|
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 ! _aurora_rest GET "zones/$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"name\": \"$h\""; then
|
||||||
|
_domain_id=$(echo "$response" | _normalizeJson | tr -d "{}" | tr "," "\n" | grep "\"id\": *\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
|
_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
|
||||||
|
}
|
||||||
|
|
||||||
|
_aurora_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
key_trimmed=$(echo "$AURORA_Key" | tr -d '"')
|
||||||
|
secret_trimmed=$(echo "$AURORA_Secret" | tr -d '"')
|
||||||
|
|
||||||
|
timestamp=$(date -u +"%Y%m%dT%H%M%SZ")
|
||||||
|
signature=$(printf "%s/%s%s" "$m" "$ep" "$timestamp" | _hmac sha256 "$(printf "%s" "$secret_trimmed" | _hex_dump | tr -d " ")" | _base64)
|
||||||
|
authorization=$(printf "AuroraDNSv1 %s" "$(printf "%s:%s" "$key_trimmed" "$signature" | _base64)")
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json; charset=UTF-8"
|
||||||
|
export _H2="X-AuroraDNS-Date: $timestamp"
|
||||||
|
export _H3="Authorization: $authorization"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$AURORA_Api/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$AURORA_Api/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
@ -1,16 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
|
# shellcheck disable=SC2034
|
||||||
|
dns_autodns_info='InternetX autoDNS
|
||||||
# This is the InternetX autoDNS xml api wrapper for acme.sh
|
InternetX autoDNS XML API
|
||||||
# Author: auerswald@gmail.com
|
Site: InternetX.com/autodns/
|
||||||
# Created: 2018-01-14
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_autodns
|
||||||
#
|
Options:
|
||||||
# export AUTODNS_USER="username"
|
AUTODNS_USER Username
|
||||||
# export AUTODNS_PASSWORD="password"
|
AUTODNS_PASSWORD Password
|
||||||
# export AUTODNS_CONTEXT="context"
|
AUTODNS_CONTEXT Context
|
||||||
#
|
Author: <auerswald@gmail.com>
|
||||||
# Usage:
|
'
|
||||||
# acme.sh --issue --dns dns_autodns -d example.com
|
|
||||||
|
|
||||||
AUTODNS_API="https://gateway.autodns.com"
|
AUTODNS_API="https://gateway.autodns.com"
|
||||||
|
|
||||||
@ -111,7 +110,7 @@ _get_autodns_zone() {
|
|||||||
p=1
|
p=1
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
|
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
@ -129,7 +128,7 @@ _get_autodns_zone() {
|
|||||||
if _contains "$autodns_response" "<summary>1</summary>" >/dev/null; then
|
if _contains "$autodns_response" "<summary>1</summary>" >/dev/null; then
|
||||||
_zone="$(echo "$autodns_response" | _egrep_o '<name>[^<]*</name>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
_zone="$(echo "$autodns_response" | _egrep_o '<name>[^<]*</name>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
_system_ns="$(echo "$autodns_response" | _egrep_o '<system_ns>[^<]*</system_ns>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
_system_ns="$(echo "$autodns_response" | _egrep_o '<system_ns>[^<]*</system_ns>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_aws_info='Amazon AWS Route53 domain API
|
||||||
|
Site: docs.aws.amazon.com/route53/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_aws
|
||||||
|
Options:
|
||||||
|
AWS_ACCESS_KEY_ID API Key ID
|
||||||
|
AWS_SECRET_ACCESS_KEY API Secret
|
||||||
|
'
|
||||||
|
|
||||||
#
|
# All `_sleep` commands are included to avoid Route53 throttling, see
|
||||||
#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
# https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
|
||||||
#
|
|
||||||
#AWS_SECRET_ACCESS_KEY="xxxxxxx"
|
|
||||||
|
|
||||||
#This is the Amazon Route53 api wrapper for acme.sh
|
|
||||||
|
|
||||||
AWS_HOST="route53.amazonaws.com"
|
AWS_HOST="route53.amazonaws.com"
|
||||||
AWS_URL="https://$AWS_HOST"
|
AWS_URL="https://$AWS_HOST"
|
||||||
|
|
||||||
AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API"
|
AWS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Amazon-Route53-API"
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
@ -21,6 +25,7 @@ dns_aws_add() {
|
|||||||
|
|
||||||
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
|
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
|
||||||
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
|
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
|
||||||
|
AWS_DNS_SLOWRATE="${AWS_DNS_SLOWRATE:-$(_readaccountconf_mutable AWS_DNS_SLOWRATE)}"
|
||||||
|
|
||||||
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
||||||
_use_container_role || _use_instance_role
|
_use_container_role || _use_instance_role
|
||||||
@ -29,7 +34,7 @@ dns_aws_add() {
|
|||||||
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
||||||
AWS_ACCESS_KEY_ID=""
|
AWS_ACCESS_KEY_ID=""
|
||||||
AWS_SECRET_ACCESS_KEY=""
|
AWS_SECRET_ACCESS_KEY=""
|
||||||
_err "You haven't specifed the aws route53 api key id and and api key secret yet."
|
_err "You haven't specified the aws route53 api key id and and api key secret yet."
|
||||||
_err "Please create your key and try again. see $(__green $AWS_WIKI)"
|
_err "Please create your key and try again. see $(__green $AWS_WIKI)"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -38,11 +43,13 @@ dns_aws_add() {
|
|||||||
if [ -z "$_using_role" ]; then
|
if [ -z "$_using_role" ]; then
|
||||||
_saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
|
_saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
|
||||||
_saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
|
_saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
|
||||||
|
_saveaccountconf_mutable AWS_DNS_SLOWRATE "$AWS_DNS_SLOWRATE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone"
|
||||||
if ! _get_root "$fulldomain"; then
|
if ! _get_root "$fulldomain"; then
|
||||||
_err "invalid domain"
|
_err "invalid domain"
|
||||||
|
_sleep 1
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
@ -51,6 +58,7 @@ dns_aws_add() {
|
|||||||
|
|
||||||
_info "Getting existing records for $fulldomain"
|
_info "Getting existing records for $fulldomain"
|
||||||
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
|
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
|
||||||
|
_sleep 1
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -63,6 +71,7 @@ dns_aws_add() {
|
|||||||
|
|
||||||
if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then
|
if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then
|
||||||
_info "The TXT record already exists. Skipping."
|
_info "The TXT record already exists. Skipping."
|
||||||
|
_sleep 1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -72,9 +81,16 @@ dns_aws_add() {
|
|||||||
|
|
||||||
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
|
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
|
||||||
_info "TXT record updated successfully."
|
_info "TXT record updated successfully."
|
||||||
return 0
|
if [ -n "$AWS_DNS_SLOWRATE" ]; then
|
||||||
|
_info "Slow rate activated: sleeping for $AWS_DNS_SLOWRATE seconds"
|
||||||
|
_sleep "$AWS_DNS_SLOWRATE"
|
||||||
|
else
|
||||||
|
_sleep 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_sleep 1
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +101,7 @@ dns_aws_rm() {
|
|||||||
|
|
||||||
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
|
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
|
||||||
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
|
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
|
||||||
|
AWS_DNS_SLOWRATE="${AWS_DNS_SLOWRATE:-$(_readaccountconf_mutable AWS_DNS_SLOWRATE)}"
|
||||||
|
|
||||||
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
|
||||||
_use_container_role || _use_instance_role
|
_use_container_role || _use_instance_role
|
||||||
@ -93,6 +110,7 @@ dns_aws_rm() {
|
|||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone"
|
||||||
if ! _get_root "$fulldomain"; then
|
if ! _get_root "$fulldomain"; then
|
||||||
_err "invalid domain"
|
_err "invalid domain"
|
||||||
|
_sleep 1
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
@ -101,6 +119,7 @@ dns_aws_rm() {
|
|||||||
|
|
||||||
_info "Getting existing records for $fulldomain"
|
_info "Getting existing records for $fulldomain"
|
||||||
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
|
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
|
||||||
|
_sleep 1
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -109,6 +128,7 @@ dns_aws_rm() {
|
|||||||
_debug "_resource_record" "$_resource_record"
|
_debug "_resource_record" "$_resource_record"
|
||||||
else
|
else
|
||||||
_debug "no records exist, skip"
|
_debug "no records exist, skip"
|
||||||
|
_sleep 1
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -116,50 +136,45 @@ dns_aws_rm() {
|
|||||||
|
|
||||||
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
|
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
|
||||||
_info "TXT record deleted successfully."
|
_info "TXT record deleted successfully."
|
||||||
return 0
|
if [ -n "$AWS_DNS_SLOWRATE" ]; then
|
||||||
|
_info "Slow rate activated: sleeping for $AWS_DNS_SLOWRATE seconds"
|
||||||
|
_sleep "$AWS_DNS_SLOWRATE"
|
||||||
|
else
|
||||||
|
_sleep 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_sleep 1
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
_get_root() {
|
_get_root() {
|
||||||
domain=$1
|
domain=$1
|
||||||
i=2
|
i=1
|
||||||
p=1
|
p=1
|
||||||
|
|
||||||
if aws_rest GET "2013-04-01/hostedzone"; then
|
# iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100 | sed 's/\./\\./g')
|
||||||
_debug2 "Checking domain: $h"
|
_debug "Checking domain: $h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
|
_error "invalid domain"
|
||||||
_debug "IsTruncated"
|
|
||||||
_nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
|
||||||
_debug "NextMarker" "$_nextMarker"
|
|
||||||
if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then
|
|
||||||
_debug "Truncated request OK"
|
|
||||||
i=2
|
|
||||||
p=1
|
|
||||||
continue
|
|
||||||
else
|
|
||||||
_err "Truncated request error."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
#not valid
|
|
||||||
_err "Invalid domain"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# iterate over paginated result for list_hosted_zones
|
||||||
|
aws_rest GET "2013-04-01/hostedzone"
|
||||||
|
while true; do
|
||||||
if _contains "$response" "<Name>$h.</Name>"; then
|
if _contains "$response" "<Name>$h.</Name>"; then
|
||||||
hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
|
hostedzone="$(echo "$response" | tr -d '\n' | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
|
||||||
_debug hostedzone "$hostedzone"
|
_debug hostedzone "$hostedzone"
|
||||||
if [ "$hostedzone" ]; then
|
if [ "$hostedzone" ]; then
|
||||||
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
|
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@ -167,10 +182,19 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
|
||||||
|
_debug "IsTruncated"
|
||||||
|
_nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
|
_debug "NextMarker" "$_nextMarker"
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
_debug "Checking domain: $h - Next Page "
|
||||||
|
aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"
|
||||||
|
done
|
||||||
p=$i
|
p=$i
|
||||||
i=$(_math "$i" + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
fi
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,24 +208,40 @@ _use_container_role() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_use_instance_role() {
|
_use_instance_role() {
|
||||||
_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
|
_instance_role_name_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
|
||||||
_debug "_url" "$_url"
|
|
||||||
if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then
|
if _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 401; then
|
||||||
|
_debug "Using IMDSv2"
|
||||||
|
_token_url="http://169.254.169.254/latest/api/token"
|
||||||
|
export _H1="X-aws-ec2-metadata-token-ttl-seconds: 21600"
|
||||||
|
_token="$(_post "" "$_token_url" "" "PUT")"
|
||||||
|
_secure_debug3 "_token" "$_token"
|
||||||
|
if [ -z "$_token" ]; then
|
||||||
|
_debug "Unable to fetch IMDSv2 token from instance metadata"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
export _H1="X-aws-ec2-metadata-token: $_token"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 200; then
|
||||||
_debug "Unable to fetch IAM role from instance metadata"
|
_debug "Unable to fetch IAM role from instance metadata"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_aws_role=$(_get "$_url" "" 1)
|
|
||||||
_debug "_aws_role" "$_aws_role"
|
_instance_role_name=$(_get "$_instance_role_name_url" "" 1)
|
||||||
_use_metadata "$_url$_aws_role"
|
_debug "_instance_role_name" "$_instance_role_name"
|
||||||
|
_use_metadata "$_instance_role_name_url$_instance_role_name" "$_token"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_use_metadata() {
|
_use_metadata() {
|
||||||
|
export _H1="X-aws-ec2-metadata-token: $2"
|
||||||
_aws_creds="$(
|
_aws_creds="$(
|
||||||
_get "$1" "" 1 \
|
_get "$1" "" 1 |
|
||||||
| _normalizeJson \
|
_normalizeJson |
|
||||||
| tr '{,}' '\n' \
|
tr '{,}' '\n' |
|
||||||
| while read -r _line; do
|
while read -r _line; do
|
||||||
_key="$(echo "${_line%%:*}" | tr -d '"')"
|
_key="$(echo "${_line%%:*}" | tr -d '\"')"
|
||||||
_value="${_line#*:}"
|
_value="${_line#*:}"
|
||||||
_debug3 "_key" "$_key"
|
_debug3 "_key" "$_key"
|
||||||
_secure_debug3 "_value" "$_value"
|
_secure_debug3 "_value" "$_value"
|
||||||
@ -210,8 +250,8 @@ _use_metadata() {
|
|||||||
SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;;
|
SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;;
|
||||||
Token) echo "AWS_SESSION_TOKEN=$_value" ;;
|
Token) echo "AWS_SESSION_TOKEN=$_value" ;;
|
||||||
esac
|
esac
|
||||||
done \
|
done |
|
||||||
| paste -sd' ' -
|
paste -sd' ' -
|
||||||
)"
|
)"
|
||||||
_secure_debug "_aws_creds" "$_aws_creds"
|
_secure_debug "_aws_creds" "$_aws_creds"
|
||||||
|
|
||||||
|
208
dnsapi/dns_azion.sh
Normal file
208
dnsapi/dns_azion.sh
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_azion_info='Azion.om
|
||||||
|
Site: Azion.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_azion
|
||||||
|
Options:
|
||||||
|
AZION_Email Email
|
||||||
|
AZION_Password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3555
|
||||||
|
'
|
||||||
|
|
||||||
|
AZION_Api="https://api.azionapi.net"
|
||||||
|
|
||||||
|
######## Public functions ########
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_azion_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_debug "Detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Domain not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
|
||||||
|
_info "Add or update record"
|
||||||
|
_get_record "$_domain_id" "$_sub_domain"
|
||||||
|
if [ "$record_id" ]; then
|
||||||
|
_payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [$answers_list, \"$txtvalue\"], \"ttl\": 20}"
|
||||||
|
if _azion_rest PUT "intelligent_dns/$_domain_id/records/$record_id" "$_payload"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Record updated."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [\"$txtvalue\"], \"ttl\": 20}"
|
||||||
|
if _azion_rest POST "intelligent_dns/$_domain_id/records" "$_payload"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Record added."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Failed to add or update record."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_azion_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_debug "Detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Domain not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
|
||||||
|
_info "Removing record"
|
||||||
|
_get_record "$_domain_id" "$_sub_domain"
|
||||||
|
if [ "$record_id" ]; then
|
||||||
|
if _azion_rest DELETE "intelligent_dns/$_domain_id/records/$record_id"; then
|
||||||
|
_info "Record removed."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Failed to remove record."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_info "Record not found or already removed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
# Usage: _acme-challenge.www.domain.com
|
||||||
|
# returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
p=1
|
||||||
|
|
||||||
|
if ! _azion_rest GET "intelligent_dns"; 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" "\"domain\":\"$h\""; then
|
||||||
|
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"domain\":\"$h\"" | _egrep_o "\"id\":[0-9]*" | _head_n 1 | 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")
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_record() {
|
||||||
|
_domain_id=$1
|
||||||
|
_record=$2
|
||||||
|
|
||||||
|
if ! _azion_rest GET "intelligent_dns/$_domain_id/records"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"entry\":\"$_record\""; then
|
||||||
|
_json_record=$(echo "$response" | tr '{' "\n" | grep "\"entry\":\"$_record\"")
|
||||||
|
if [ "$_json_record" ]; then
|
||||||
|
record_id=$(echo "$_json_record" | _egrep_o "\"record_id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
answers_list=$(echo "$_json_record" | _egrep_o "\"answers_list\":\[.*\]" | _head_n 1 | cut -d : -f 2 | tr -d \[\])
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_token() {
|
||||||
|
AZION_Email="${AZION_Email:-$(_readaccountconf_mutable AZION_Email)}"
|
||||||
|
AZION_Password="${AZION_Password:-$(_readaccountconf_mutable AZION_Password)}"
|
||||||
|
|
||||||
|
if ! _contains "$AZION_Email" "@"; then
|
||||||
|
_err "It seems that the AZION_Email is not a valid email address. Revalidate your environments."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$AZION_Email" ] || [ -z "$AZION_Password" ]; then
|
||||||
|
_err "You didn't specified a AZION_Email/AZION_Password to generate Azion token."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable AZION_Email "$AZION_Email"
|
||||||
|
_saveaccountconf_mutable AZION_Password "$AZION_Password"
|
||||||
|
|
||||||
|
_basic_auth=$(printf "%s:%s" "$AZION_Email" "$AZION_Password" | _base64)
|
||||||
|
_debug _basic_auth "$_basic_auth"
|
||||||
|
|
||||||
|
export _H1="Accept: application/json; version=3"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
export _H3="Authorization: Basic $_basic_auth"
|
||||||
|
|
||||||
|
response="$(_post "" "$AZION_Api/tokens" "" "POST")"
|
||||||
|
if _contains "$response" "\"token\":\"" >/dev/null; then
|
||||||
|
_azion_token=$(echo "$response" | _egrep_o "\"token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
|
export AZION_Token="$_azion_token"
|
||||||
|
else
|
||||||
|
_err "Failed to generate Azion token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_azion_rest() {
|
||||||
|
_method=$1
|
||||||
|
_uri="$2"
|
||||||
|
_data="$3"
|
||||||
|
|
||||||
|
if [ -z "$AZION_Token" ]; then
|
||||||
|
_get_token
|
||||||
|
fi
|
||||||
|
_debug2 token "$AZION_Token"
|
||||||
|
|
||||||
|
export _H1="Accept: application/json; version=3"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
export _H3="Authorization: token $AZION_Token"
|
||||||
|
|
||||||
|
if [ "$_method" != "GET" ]; then
|
||||||
|
_debug _data "$_data"
|
||||||
|
response="$(_post "$_data" "$AZION_Api/$_uri" "" "$_method")"
|
||||||
|
else
|
||||||
|
response="$(_get "$AZION_Api/$_uri")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $_method $_uri $_data"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
@ -1,31 +1,55 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_azure_info='Azure
|
||||||
|
Site: Azure.microsoft.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_azure
|
||||||
|
Options:
|
||||||
|
AZUREDNS_SUBSCRIPTIONID Subscription ID
|
||||||
|
AZUREDNS_TENANTID Tenant ID
|
||||||
|
AZUREDNS_APPID App ID. App ID of the service principal
|
||||||
|
AZUREDNS_CLIENTSECRET Client Secret. Secret from creating the service principal
|
||||||
|
AZUREDNS_MANAGEDIDENTITY Use Managed Identity. Use Managed Identity assigned to a resource instead of a service principal. "true"/"false"
|
||||||
|
'
|
||||||
|
|
||||||
WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Azure-DNS"
|
wiki=https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
# Used to add txt record
|
# Used to add txt record
|
||||||
#
|
#
|
||||||
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate
|
# Ref: https://learn.microsoft.com/en-us/rest/api/dns/record-sets/create-or-update?view=rest-dns-2018-05-01&tabs=HTTP
|
||||||
#
|
#
|
||||||
|
|
||||||
dns_azure_add() {
|
dns_azure_add() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
|
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
|
||||||
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
|
||||||
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
|
||||||
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
|
||||||
|
|
||||||
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
|
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
AZUREDNS_APPID=""
|
AZUREDNS_APPID=""
|
||||||
AZUREDNS_CLIENTSECRET=""
|
AZUREDNS_CLIENTSECRET=""
|
||||||
_err "You didn't specify the Azure Subscription ID "
|
_err "You didn't specify the Azure Subscription ID"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
#save subscription id to account conf file.
|
||||||
|
_saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID"
|
||||||
|
|
||||||
|
AZUREDNS_MANAGEDIDENTITY="${AZUREDNS_MANAGEDIDENTITY:-$(_readaccountconf_mutable AZUREDNS_MANAGEDIDENTITY)}"
|
||||||
|
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
|
||||||
|
_info "Using Azure managed identity"
|
||||||
|
#save managed identity as preferred authentication method, clear service principal credentials from conf file.
|
||||||
|
_saveaccountconf_mutable AZUREDNS_MANAGEDIDENTITY "$AZUREDNS_MANAGEDIDENTITY"
|
||||||
|
_saveaccountconf_mutable AZUREDNS_TENANTID ""
|
||||||
|
_saveaccountconf_mutable AZUREDNS_APPID ""
|
||||||
|
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET ""
|
||||||
|
else
|
||||||
|
_info "You didn't ask to use Azure managed identity, checking service principal credentials"
|
||||||
|
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
||||||
|
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
||||||
|
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
||||||
|
|
||||||
if [ -z "$AZUREDNS_TENANTID" ]; then
|
if [ -z "$AZUREDNS_TENANTID" ]; then
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
@ -53,13 +77,15 @@ dns_azure_add() {
|
|||||||
_err "You didn't specify the Azure Client Secret"
|
_err "You didn't specify the Azure Client Secret"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
#save account details to account conf file.
|
|
||||||
_saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID"
|
#save account details to account conf file, don't opt in for azure manages identity check.
|
||||||
|
_saveaccountconf_mutable AZUREDNS_MANAGEDIDENTITY "false"
|
||||||
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
|
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
|
||||||
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
|
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
|
||||||
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
|
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
|
||||||
|
fi
|
||||||
|
|
||||||
accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
||||||
|
|
||||||
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
||||||
_err "invalid domain"
|
_err "invalid domain"
|
||||||
@ -109,17 +135,13 @@ dns_azure_add() {
|
|||||||
# Usage: fulldomain txtvalue
|
# Usage: fulldomain txtvalue
|
||||||
# Used to remove the txt record after validation
|
# Used to remove the txt record after validation
|
||||||
#
|
#
|
||||||
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete
|
# Ref: https://learn.microsoft.com/en-us/rest/api/dns/record-sets/delete?view=rest-dns-2018-05-01&tabs=HTTP
|
||||||
#
|
#
|
||||||
dns_azure_rm() {
|
dns_azure_rm() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
|
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
|
||||||
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
|
||||||
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
|
||||||
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
|
||||||
|
|
||||||
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
|
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
@ -129,6 +151,15 @@ dns_azure_rm() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AZUREDNS_MANAGEDIDENTITY="${AZUREDNS_MANAGEDIDENTITY:-$(_readaccountconf_mutable AZUREDNS_MANAGEDIDENTITY)}"
|
||||||
|
if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
|
||||||
|
_info "Using Azure managed identity"
|
||||||
|
else
|
||||||
|
_info "You didn't ask to use Azure managed identity, checking service principal credentials"
|
||||||
|
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
|
||||||
|
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
|
||||||
|
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
|
||||||
|
|
||||||
if [ -z "$AZUREDNS_TENANTID" ]; then
|
if [ -z "$AZUREDNS_TENANTID" ]; then
|
||||||
AZUREDNS_SUBSCRIPTIONID=""
|
AZUREDNS_SUBSCRIPTIONID=""
|
||||||
AZUREDNS_TENANTID=""
|
AZUREDNS_TENANTID=""
|
||||||
@ -155,8 +186,9 @@ dns_azure_rm() {
|
|||||||
_err "You didn't specify the Azure Client Secret"
|
_err "You didn't specify the Azure Client Secret"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
|
||||||
|
|
||||||
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
|
||||||
_err "invalid domain"
|
_err "invalid domain"
|
||||||
@ -172,7 +204,7 @@ dns_azure_rm() {
|
|||||||
_azure_rest GET "$acmeRecordURI" "" "$accesstoken"
|
_azure_rest GET "$acmeRecordURI" "" "$accesstoken"
|
||||||
timestamp="$(_time)"
|
timestamp="$(_time)"
|
||||||
if [ "$_code" = "200" ]; then
|
if [ "$_code" = "200" ]; then
|
||||||
vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v "$txtvalue")"
|
vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v -- "$txtvalue")"
|
||||||
values=""
|
values=""
|
||||||
comma=""
|
comma=""
|
||||||
for v in $vlist; do
|
for v in $vlist; do
|
||||||
@ -220,7 +252,7 @@ _azure_rest() {
|
|||||||
export _H2="accept: application/json"
|
export _H2="accept: application/json"
|
||||||
export _H3="Content-Type: application/json"
|
export _H3="Content-Type: application/json"
|
||||||
# clear headers from previous request to avoid getting wrong http code on timeouts
|
# clear headers from previous request to avoid getting wrong http code on timeouts
|
||||||
:>"$HTTP_HEADER"
|
: >"$HTTP_HEADER"
|
||||||
_debug "$ep"
|
_debug "$ep"
|
||||||
if [ "$m" != "GET" ]; then
|
if [ "$m" != "GET" ]; then
|
||||||
_secure_debug2 "data $data"
|
_secure_debug2 "data $data"
|
||||||
@ -235,10 +267,10 @@ _azure_rest() {
|
|||||||
if [ "$_code" = "401" ]; then
|
if [ "$_code" = "401" ]; then
|
||||||
# we have an invalid access token set to expired
|
# we have an invalid access token set to expired
|
||||||
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "0"
|
_saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "0"
|
||||||
_err "access denied make sure your Azure settings are correct. See $WIKI"
|
_err "Access denied. Invalid access token. Make sure your Azure settings are correct. See: $wiki"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# See https://docs.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific#general-rest-and-retry-guidelines for retryable HTTP codes
|
# See https://learn.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific#general-rest-and-retry-guidelines for retryable HTTP codes
|
||||||
if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "408" ] || [ "$_code" = "500" ] || [ "$_code" = "503" ] || [ "$_code" = "504" ]; then
|
if [ "$_ret" != "0" ] || [ -z "$_code" ] || [ "$_code" = "408" ] || [ "$_code" = "500" ] || [ "$_code" = "503" ] || [ "$_code" = "504" ]; then
|
||||||
_request_retry_times="$(_math "$_request_retry_times" + 1)"
|
_request_retry_times="$(_math "$_request_retry_times" + 1)"
|
||||||
_info "REST call error $_code retrying $ep in $_request_retry_times s"
|
_info "REST call error $_code retrying $ep in $_request_retry_times s"
|
||||||
@ -256,11 +288,12 @@ _azure_rest() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token
|
## Ref: https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#request-an-access-token
|
||||||
_azure_getaccess_token() {
|
_azure_getaccess_token() {
|
||||||
tenantID=$1
|
managedIdentity=$1
|
||||||
clientID=$2
|
tenantID=$2
|
||||||
clientSecret=$3
|
clientID=$3
|
||||||
|
clientSecret=$4
|
||||||
|
|
||||||
accesstoken="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
|
accesstoken="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
|
||||||
expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
|
expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
|
||||||
@ -278,9 +311,16 @@ _azure_getaccess_token() {
|
|||||||
fi
|
fi
|
||||||
_debug "getting new bearer token"
|
_debug "getting new bearer token"
|
||||||
|
|
||||||
|
if [ "$managedIdentity" = true ]; then
|
||||||
|
# https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
|
||||||
|
export _H1="Metadata: true"
|
||||||
|
response="$(_get http://169.254.169.254/metadata/identity/oauth2/token\?api-version=2018-02-01\&resource=https://management.azure.com/)"
|
||||||
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
|
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
else
|
||||||
export _H1="accept: application/json"
|
export _H1="accept: application/json"
|
||||||
export _H2="Content-Type: application/x-www-form-urlencoded"
|
export _H2="Content-Type: application/x-www-form-urlencoded"
|
||||||
|
|
||||||
body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials"
|
body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials"
|
||||||
_secure_debug2 "data $body"
|
_secure_debug2 "data $body"
|
||||||
response="$(_post "$body" "https://login.microsoftonline.com/$tenantID/oauth2/token" "" "POST")"
|
response="$(_post "$body" "https://login.microsoftonline.com/$tenantID/oauth2/token" "" "POST")"
|
||||||
@ -289,9 +329,10 @@ _azure_getaccess_token() {
|
|||||||
response="$(echo "$response" | _normalizeJson)"
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
expires_on=$(echo "$response" | _egrep_o "\"expires_on\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$accesstoken" ]; then
|
if [ -z "$accesstoken" ]; then
|
||||||
_err "no acccess token received. Check your Azure settings see $WIKI"
|
_err "No acccess token received. Check your Azure settings. See: $wiki"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [ "$_ret" != "0" ]; then
|
if [ "$_ret" != "0" ]; then
|
||||||
@ -311,15 +352,18 @@ _get_root() {
|
|||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
|
|
||||||
## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list
|
## Ref: https://learn.microsoft.com/en-us/rest/api/dns/zones/list?view=rest-dns-2018-05-01&tabs=HTTP
|
||||||
## returns up to 100 zones in one response therefore handling more results is not not implemented
|
## returns up to 100 zones in one response. Handling more results is not implemented
|
||||||
## (ZoneListResult with continuation token for the next page of results)
|
## (ZoneListResult with continuation token for the next page of results)
|
||||||
## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways
|
##
|
||||||
|
## TODO: handle more than 100 results, as per:
|
||||||
|
## https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-dns-limits
|
||||||
|
## The new limit is 250 Public DNS zones per subscription, while the old limit was only 100
|
||||||
##
|
##
|
||||||
_azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?\$top=500&api-version=2017-09-01" "" "$accesstoken"
|
_azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?\$top=500&api-version=2017-09-01" "" "$accesstoken"
|
||||||
# Find matching domain name in Json response
|
# Find matching domain name in Json response
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug2 "Checking domain: $h"
|
_debug2 "Checking domain: $h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -334,7 +378,7 @@ _get_root() {
|
|||||||
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias
|
#create the record at the domain apex (@) if only the domain name was provided as --domain-alias
|
||||||
_sub_domain="@"
|
_sub_domain="@"
|
||||||
else
|
else
|
||||||
_sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(echo "$domain" | cut -d . -f 1-"$p")
|
||||||
fi
|
fi
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
|
88
dnsapi/dns_bookmyname.sh
Normal file
88
dnsapi/dns_bookmyname.sh
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_bookmyname_info='BookMyName.com
|
||||||
|
Site: BookMyName.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_bookmyname
|
||||||
|
Options:
|
||||||
|
BOOKMYNAME_USERNAME Username
|
||||||
|
BOOKMYNAME_PASSWORD Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3209
|
||||||
|
Author: Neilpang
|
||||||
|
'
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# BookMyName urls:
|
||||||
|
# https://BOOKMYNAME_USERNAME:BOOKMYNAME_PASSWORD@www.bookmyname.com/dyndns/?hostname=_acme-challenge.domain.tld&type=txt&ttl=300&do=add&value="XXXXXXXX"'
|
||||||
|
# https://BOOKMYNAME_USERNAME:BOOKMYNAME_PASSWORD@www.bookmyname.com/dyndns/?hostname=_acme-challenge.domain.tld&type=txt&ttl=300&do=remove&value="XXXXXXXX"'
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
#good: update done, cid 123456, domain id 456789, type txt, ip XXXXXXXX
|
||||||
|
#good: remove done 1, cid 123456, domain id 456789, ttl 300, type txt, ip XXXXXXXX
|
||||||
|
|
||||||
|
# Be careful, BMN DNS servers can be slow to pick up changes; using dnssleep is thus advised.
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
# export BOOKMYNAME_USERNAME="ABCDE-FREE"
|
||||||
|
# export BOOKMYNAME_PASSWORD="MyPassword"
|
||||||
|
# /usr/local/ssl/acme.sh/acme.sh --dns dns_bookmyname --dnssleep 600 --issue -d domain.tld
|
||||||
|
|
||||||
|
#Usage: dns_bookmyname_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_bookmyname_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using bookmyname"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
BOOKMYNAME_USERNAME="${BOOKMYNAME_USERNAME:-$(_readaccountconf_mutable BOOKMYNAME_USERNAME)}"
|
||||||
|
BOOKMYNAME_PASSWORD="${BOOKMYNAME_PASSWORD:-$(_readaccountconf_mutable BOOKMYNAME_PASSWORD)}"
|
||||||
|
|
||||||
|
if [ -z "$BOOKMYNAME_USERNAME" ] || [ -z "$BOOKMYNAME_PASSWORD" ]; then
|
||||||
|
BOOKMYNAME_USERNAME=""
|
||||||
|
BOOKMYNAME_PASSWORD=""
|
||||||
|
_err "You didn't specify BookMyName username and password yet."
|
||||||
|
_err "Please specify them and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_saveaccountconf_mutable BOOKMYNAME_USERNAME "$BOOKMYNAME_USERNAME"
|
||||||
|
_saveaccountconf_mutable BOOKMYNAME_PASSWORD "$BOOKMYNAME_PASSWORD"
|
||||||
|
|
||||||
|
uri="https://${BOOKMYNAME_USERNAME}:${BOOKMYNAME_PASSWORD}@www.bookmyname.com/dyndns/"
|
||||||
|
data="?hostname=${fulldomain}&type=TXT&ttl=300&do=add&value=${txtvalue}"
|
||||||
|
result="$(_get "${uri}${data}")"
|
||||||
|
_debug "Result: $result"
|
||||||
|
|
||||||
|
if ! _startswith "$result" 'good: update done, cid '; then
|
||||||
|
_err "Can't add $fulldomain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_bookmyname_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using bookmyname"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
BOOKMYNAME_USERNAME="${BOOKMYNAME_USERNAME:-$(_readaccountconf_mutable BOOKMYNAME_USERNAME)}"
|
||||||
|
BOOKMYNAME_PASSWORD="${BOOKMYNAME_PASSWORD:-$(_readaccountconf_mutable BOOKMYNAME_PASSWORD)}"
|
||||||
|
|
||||||
|
uri="https://${BOOKMYNAME_USERNAME}:${BOOKMYNAME_PASSWORD}@www.bookmyname.com/dyndns/"
|
||||||
|
data="?hostname=${fulldomain}&type=TXT&ttl=300&do=remove&value=${txtvalue}"
|
||||||
|
result="$(_get "${uri}${data}")"
|
||||||
|
_debug "Result: $result"
|
||||||
|
|
||||||
|
if ! _startswith "$result" 'good: remove done 1, cid '; then
|
||||||
|
_info "Can't remove $fulldomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
245
dnsapi/dns_bunny.sh
Normal file
245
dnsapi/dns_bunny.sh
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_bunny_info='Bunny.net
|
||||||
|
Site: Bunny.net/dns/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_bunny
|
||||||
|
Options:
|
||||||
|
BUNNY_API_KEY API Key
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/4296
|
||||||
|
Author: <nosilver4u@ewww.io>
|
||||||
|
'
|
||||||
|
|
||||||
|
##################### Public functions #####################
|
||||||
|
|
||||||
|
## Create the text record for validation.
|
||||||
|
## Usage: fulldomain txtvalue
|
||||||
|
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
|
||||||
|
dns_bunny_add() {
|
||||||
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
BUNNY_API_KEY="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
|
||||||
|
# Check if API Key is set
|
||||||
|
if [ -z "$BUNNY_API_KEY" ]; then
|
||||||
|
BUNNY_API_KEY=""
|
||||||
|
_err "You did not specify Bunny.net API key."
|
||||||
|
_err "Please export BUNNY_API_KEY and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Using Bunny.net dns validation - add record"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
## save the env vars (key and domain split location) for later automated use
|
||||||
|
_saveaccountconf_mutable BUNNY_API_KEY "$BUNNY_API_KEY"
|
||||||
|
|
||||||
|
## split the domain for Bunny API
|
||||||
|
if ! _get_base_domain "$fulldomain"; then
|
||||||
|
_err "domain not found in your account for addition"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
|
||||||
|
## Set the header with our post type and auth key
|
||||||
|
export _H1="Accept: application/json"
|
||||||
|
export _H2="AccessKey: $BUNNY_API_KEY"
|
||||||
|
export _H3="Content-Type: application/json"
|
||||||
|
PURL="https://api.bunny.net/dnszone/$_domain_id/records"
|
||||||
|
PBODY='{"Id":'$_domain_id',"Type":3,"Name":"'$_sub_domain'","Value":"'$txtvalue'","ttl":120}'
|
||||||
|
|
||||||
|
_debug PURL "$PURL"
|
||||||
|
_debug PBODY "$PBODY"
|
||||||
|
|
||||||
|
## the create request - POST
|
||||||
|
## args: BODY, URL, [need64, httpmethod]
|
||||||
|
response="$(_post "$PBODY" "$PURL" "" "PUT")"
|
||||||
|
|
||||||
|
## check response
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error in response: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
## finished correctly
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
## Remove the txt record after validation.
|
||||||
|
## Usage: fulldomain txtvalue
|
||||||
|
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
|
||||||
|
dns_bunny_rm() {
|
||||||
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
BUNNY_API_KEY="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
|
||||||
|
# Check if API Key Exists
|
||||||
|
if [ -z "$BUNNY_API_KEY" ]; then
|
||||||
|
BUNNY_API_KEY=""
|
||||||
|
_err "You did not specify Bunny.net API key."
|
||||||
|
_err "Please export BUNNY_API_KEY and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Using Bunny.net dns validation - remove record"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
## split the domain for Bunny API
|
||||||
|
if ! _get_base_domain "$fulldomain"; then
|
||||||
|
_err "Domain not found in your account for TXT record removal"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
|
||||||
|
## Set the header with our post type and key auth key
|
||||||
|
export _H1="Accept: application/json"
|
||||||
|
export _H2="AccessKey: $BUNNY_API_KEY"
|
||||||
|
## get URL for the list of DNS records
|
||||||
|
GURL="https://api.bunny.net/dnszone/$_domain_id"
|
||||||
|
|
||||||
|
## 1) Get the domain/zone records
|
||||||
|
## the fetch request - GET
|
||||||
|
## args: URL, [onlyheader, timeout]
|
||||||
|
domain_list="$(_get "$GURL")"
|
||||||
|
|
||||||
|
## check response
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error in domain_list response: $domain_list"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 domain_list "$domain_list"
|
||||||
|
|
||||||
|
## 2) search through records
|
||||||
|
## check for what we are looking for: "Type":3,"Value":"$txtvalue","Name":"$_sub_domain"
|
||||||
|
record="$(echo "$domain_list" | _egrep_o "\"Id\"\s*\:\s*\"*[0-9]+\"*,\s*\"Type\"[^}]*\"Value\"\s*\:\s*\"$txtvalue\"[^}]*\"Name\"\s*\:\s*\"$_sub_domain\"")"
|
||||||
|
|
||||||
|
if [ -n "$record" ]; then
|
||||||
|
|
||||||
|
## We found records
|
||||||
|
rec_ids="$(echo "$record" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
|
||||||
|
_debug rec_ids "$rec_ids"
|
||||||
|
if [ -n "$rec_ids" ]; then
|
||||||
|
echo "$rec_ids" | while IFS= read -r rec_id; do
|
||||||
|
## delete the record
|
||||||
|
## delete URL for removing the one we dont want
|
||||||
|
DURL="https://api.bunny.net/dnszone/$_domain_id/records/$rec_id"
|
||||||
|
|
||||||
|
## the removal request - DELETE
|
||||||
|
## args: BODY, URL, [need64, httpmethod]
|
||||||
|
response="$(_post "" "$DURL" "" "DELETE")"
|
||||||
|
|
||||||
|
## check response (sort of)
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error in remove response: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
## finished correctly
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
##################### Private functions below #####################
|
||||||
|
|
||||||
|
## Split the domain provided into the "base domain" and the "start prefix".
|
||||||
|
## This function searches for the longest subdomain in your account
|
||||||
|
## for the full domain given and splits it into the base domain (zone)
|
||||||
|
## and the prefix/record to be added/removed
|
||||||
|
## USAGE: fulldomain
|
||||||
|
## EG: "_acme-challenge.two.three.four.domain.com"
|
||||||
|
## returns
|
||||||
|
## _sub_domain="_acme-challenge.two"
|
||||||
|
## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists
|
||||||
|
## _domain_id=234
|
||||||
|
## if only "domain.com" exists it will return
|
||||||
|
## _sub_domain="_acme-challenge.two.three.four"
|
||||||
|
## _domain="domain.com"
|
||||||
|
## _domain_id=234
|
||||||
|
_get_base_domain() {
|
||||||
|
# args
|
||||||
|
fulldomain="$(echo "$1" | _lower_case)"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
|
||||||
|
# domain max legal length = 253
|
||||||
|
MAX_DOM=255
|
||||||
|
page=1
|
||||||
|
|
||||||
|
## get a list of domains for the account to check thru
|
||||||
|
## Set the headers
|
||||||
|
export _H1="Accept: application/json"
|
||||||
|
export _H2="AccessKey: $BUNNY_API_KEY"
|
||||||
|
_debug BUNNY_API_KEY "$BUNNY_API_KEY"
|
||||||
|
## get URL for the list of domains
|
||||||
|
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
|
||||||
|
DOMURL="https://api.bunny.net/dnszone"
|
||||||
|
|
||||||
|
## while we dont have a matching domain we keep going
|
||||||
|
while [ -z "$found" ]; do
|
||||||
|
## get the domain list (current page)
|
||||||
|
domain_list="$(_get "$DOMURL")"
|
||||||
|
|
||||||
|
## check response
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error in domain_list response: $domain_list"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 domain_list "$domain_list"
|
||||||
|
|
||||||
|
i=1
|
||||||
|
while [ "$i" -gt 0 ]; do
|
||||||
|
## get next longest domain
|
||||||
|
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
|
||||||
|
## check we got something back from our cut (or are we at the end)
|
||||||
|
if [ -z "$_domain" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
## we got part of a domain back - grep it out
|
||||||
|
found="$(echo "$domain_list" | _egrep_o "\"Id\"\s*:\s*\"*[0-9]+\"*,\s*\"Domain\"\s*\:\s*\"$_domain\"")"
|
||||||
|
## check if it exists
|
||||||
|
if [ -n "$found" ]; then
|
||||||
|
## exists - exit loop returning the parts
|
||||||
|
sub_point=$(_math "$i" - 1)
|
||||||
|
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
|
||||||
|
_domain_id="$(echo "$found" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
found=""
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
## increment cut point $i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$found" ]; then
|
||||||
|
page=$(_math "$page" + 1)
|
||||||
|
nextpage="https://api.bunny.net/dnszone?page=$page"
|
||||||
|
## Find the next page if we don't have a match.
|
||||||
|
hasnextpage="$(echo "$domain_list" | _egrep_o "\"HasMoreItems\"\s*:\s*true")"
|
||||||
|
if [ -z "$hasnextpage" ]; then
|
||||||
|
_err "No record and no nextpage in Bunny.net domain search."
|
||||||
|
found=""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 nextpage "$nextpage"
|
||||||
|
DOMURL="$nextpage"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
## We went through the entire domain zone list and didn't find one that matched.
|
||||||
|
## If we ever get here, something is broken in the code...
|
||||||
|
_err "Domain not found in Bunny.net account, but we should never get here!"
|
||||||
|
found=""
|
||||||
|
return 1
|
||||||
|
}
|
@ -1,12 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#
|
dns_cf_info='CloudFlare
|
||||||
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
Site: CloudFlare.com
|
||||||
#
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cf
|
||||||
#CF_Email="xxxx@sss.com"
|
Options:
|
||||||
|
CF_Key API Key
|
||||||
#CF_Token="xxxx"
|
CF_Email Your account email
|
||||||
#CF_Account_ID="xxxx"
|
OptionsAlt:
|
||||||
|
CF_Token API Token
|
||||||
|
CF_Account_ID Account ID
|
||||||
|
CF_Zone_ID Zone ID. Optional.
|
||||||
|
'
|
||||||
|
|
||||||
CF_Api="https://api.cloudflare.com/client/v4"
|
CF_Api="https://api.cloudflare.com/client/v4"
|
||||||
|
|
||||||
@ -19,12 +23,21 @@ dns_cf_add() {
|
|||||||
|
|
||||||
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
|
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
|
||||||
CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
|
CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
|
||||||
|
CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
|
||||||
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
|
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
|
||||||
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
|
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
|
||||||
|
|
||||||
if [ "$CF_Token" ]; then
|
if [ "$CF_Token" ]; then
|
||||||
|
if [ "$CF_Zone_ID" ]; then
|
||||||
|
_savedomainconf CF_Token "$CF_Token"
|
||||||
|
_savedomainconf CF_Account_ID "$CF_Account_ID"
|
||||||
|
_savedomainconf CF_Zone_ID "$CF_Zone_ID"
|
||||||
|
else
|
||||||
_saveaccountconf_mutable CF_Token "$CF_Token"
|
_saveaccountconf_mutable CF_Token "$CF_Token"
|
||||||
_saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
|
_saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
|
||||||
|
_clearaccountconf_mutable CF_Zone_ID
|
||||||
|
_clearaccountconf CF_Zone_ID
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
|
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
|
||||||
CF_Key=""
|
CF_Key=""
|
||||||
@ -42,6 +55,14 @@ dns_cf_add() {
|
|||||||
#save the api key and email to the account conf file.
|
#save the api key and email to the account conf file.
|
||||||
_saveaccountconf_mutable CF_Key "$CF_Key"
|
_saveaccountconf_mutable CF_Key "$CF_Key"
|
||||||
_saveaccountconf_mutable CF_Email "$CF_Email"
|
_saveaccountconf_mutable CF_Email "$CF_Email"
|
||||||
|
|
||||||
|
_clearaccountconf_mutable CF_Token
|
||||||
|
_clearaccountconf_mutable CF_Account_ID
|
||||||
|
_clearaccountconf_mutable CF_Zone_ID
|
||||||
|
_clearaccountconf CF_Token
|
||||||
|
_clearaccountconf CF_Account_ID
|
||||||
|
_clearaccountconf CF_Zone_ID
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone"
|
||||||
@ -56,7 +77,7 @@ dns_cf_add() {
|
|||||||
_debug "Getting txt records"
|
_debug "Getting txt records"
|
||||||
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
|
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
|
||||||
|
|
||||||
if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
|
if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
|
||||||
_err "Error"
|
_err "Error"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -91,6 +112,7 @@ dns_cf_rm() {
|
|||||||
|
|
||||||
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
|
CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
|
||||||
CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
|
CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
|
||||||
|
CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
|
||||||
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
|
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
|
||||||
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
|
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
|
||||||
|
|
||||||
@ -106,17 +128,17 @@ dns_cf_rm() {
|
|||||||
_debug "Getting txt records"
|
_debug "Getting txt records"
|
||||||
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"
|
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"
|
||||||
|
|
||||||
if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
|
if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
|
||||||
_err "Error"
|
_err "Error: $response"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
|
count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ")
|
||||||
_debug count "$count"
|
_debug count "$count"
|
||||||
if [ "$count" = "0" ]; then
|
if [ "$count" = "0" ]; then
|
||||||
_info "Don't need to remove."
|
_info "Don't need to remove."
|
||||||
else
|
else
|
||||||
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
|
record_id=$(echo "$response" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
_debug "record_id" "$record_id"
|
_debug "record_id" "$record_id"
|
||||||
if [ -z "$record_id" ]; then
|
if [ -z "$record_id" ]; then
|
||||||
_err "Can not get record id to remove."
|
_err "Can not get record id to remove."
|
||||||
@ -126,7 +148,7 @@ dns_cf_rm() {
|
|||||||
_err "Delete record error."
|
_err "Delete record error."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_contains "$response" '"success":true'
|
echo "$response" | tr -d " " | grep \"success\":true >/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -141,8 +163,30 @@ _get_root() {
|
|||||||
domain=$1
|
domain=$1
|
||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
|
|
||||||
|
# Use Zone ID directly if provided
|
||||||
|
if [ "$CF_Zone_ID" ]; then
|
||||||
|
if ! _cf_rest GET "zones/$CF_Zone_ID"; then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
if echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
|
||||||
|
_domain=$(echo "$response" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
|
||||||
|
if [ "$_domain" ]; then
|
||||||
|
_cutlength=$((${#domain} - ${#_domain} - 1))
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
|
||||||
|
_domain_id=$CF_Zone_ID
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -160,9 +204,9 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then
|
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then
|
||||||
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
202
dnsapi/dns_clouddns.sh
Executable file
202
dnsapi/dns_clouddns.sh
Executable file
@ -0,0 +1,202 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_clouddns_info='vshosting.cz CloudDNS
|
||||||
|
Site: github.com/vshosting/clouddns
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_clouddns
|
||||||
|
Options:
|
||||||
|
CLOUDDNS_EMAIL Email
|
||||||
|
CLOUDDNS_PASSWORD Password
|
||||||
|
CLOUDDNS_CLIENT_ID Client ID
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2699
|
||||||
|
Author: Radek Sprta <sprta@vshosting.cz>
|
||||||
|
'
|
||||||
|
|
||||||
|
CLOUDDNS_API='https://admin.vshosting.cloud/clouddns'
|
||||||
|
CLOUDDNS_LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login'
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_clouddns_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_debug "fulldomain" "$fulldomain"
|
||||||
|
|
||||||
|
CLOUDDNS_CLIENT_ID="${CLOUDDNS_CLIENT_ID:-$(_readaccountconf_mutable CLOUDDNS_CLIENT_ID)}"
|
||||||
|
CLOUDDNS_EMAIL="${CLOUDDNS_EMAIL:-$(_readaccountconf_mutable CLOUDDNS_EMAIL)}"
|
||||||
|
CLOUDDNS_PASSWORD="${CLOUDDNS_PASSWORD:-$(_readaccountconf_mutable CLOUDDNS_PASSWORD)}"
|
||||||
|
|
||||||
|
if [ -z "$CLOUDDNS_PASSWORD" ] || [ -z "$CLOUDDNS_EMAIL" ] || [ -z "$CLOUDDNS_CLIENT_ID" ]; then
|
||||||
|
CLOUDDNS_CLIENT_ID=""
|
||||||
|
CLOUDDNS_EMAIL=""
|
||||||
|
CLOUDDNS_PASSWORD=""
|
||||||
|
_err "You didn't specify a CloudDNS password, email and client ID yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! _contains "$CLOUDDNS_EMAIL" "@"; then
|
||||||
|
_err "It seems that the CLOUDDNS_EMAIL=$CLOUDDNS_EMAIL is not a valid email address."
|
||||||
|
_err "Please check and retry."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# Save CloudDNS client id, email and password to config file
|
||||||
|
_saveaccountconf_mutable CLOUDDNS_CLIENT_ID "$CLOUDDNS_CLIENT_ID"
|
||||||
|
_saveaccountconf_mutable CLOUDDNS_EMAIL "$CLOUDDNS_EMAIL"
|
||||||
|
_saveaccountconf_mutable CLOUDDNS_PASSWORD "$CLOUDDNS_PASSWORD"
|
||||||
|
|
||||||
|
_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"
|
||||||
|
|
||||||
|
# Add TXT record
|
||||||
|
data="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"value\":\"$txtvalue\",\"domainId\":\"$_domain_id\"}"
|
||||||
|
if _clouddns_api POST "record-txt" "$data"; then
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_info "Added, OK"
|
||||||
|
elif _contains "$response" '"code":4136'; then
|
||||||
|
_info "Already exists, OK"
|
||||||
|
else
|
||||||
|
_err "Add TXT record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Publishing record changes"
|
||||||
|
_clouddns_api PUT "domain/$_domain_id/publish" "{\"soaTtl\":300}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: rm _acme-challenge.www.domain.com
|
||||||
|
dns_clouddns_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
_debug "fulldomain" "$fulldomain"
|
||||||
|
|
||||||
|
CLOUDDNS_CLIENT_ID="${CLOUDDNS_CLIENT_ID:-$(_readaccountconf_mutable CLOUDDNS_CLIENT_ID)}"
|
||||||
|
CLOUDDNS_EMAIL="${CLOUDDNS_EMAIL:-$(_readaccountconf_mutable CLOUDDNS_EMAIL)}"
|
||||||
|
CLOUDDNS_PASSWORD="${CLOUDDNS_PASSWORD:-$(_readaccountconf_mutable CLOUDDNS_PASSWORD)}"
|
||||||
|
|
||||||
|
_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"
|
||||||
|
|
||||||
|
# Get record ID
|
||||||
|
_clouddns_api GET "domain/$_domain_id"
|
||||||
|
if _contains "$response" "lastDomainRecordList"; then
|
||||||
|
re="\"lastDomainRecordList\".*\"id\":\"([^\"}]*)\"[^}]*\"name\":\"$fulldomain.\","
|
||||||
|
_last_domains=$(echo "$response" | _egrep_o "$re")
|
||||||
|
re2="\"id\":\"([^\"}]*)\"[^}]*\"name\":\"$fulldomain.\","
|
||||||
|
_record_id=$(echo "$_last_domains" | _egrep_o "$re2" | _head_n 1 | cut -d : -f 2 | cut -d , -f 1 | tr -d "\"")
|
||||||
|
_debug _record_id "$_record_id"
|
||||||
|
else
|
||||||
|
_err "Could not retrieve record ID"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Removing record"
|
||||||
|
if _clouddns_api DELETE "record/$_record_id"; then
|
||||||
|
if _contains "$response" "\"error\":"; then
|
||||||
|
_err "Could not remove record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Publishing record changes"
|
||||||
|
_clouddns_api PUT "domain/$_domain_id/publish" "{\"soaTtl\":300}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# Usage: _get_root _acme-challenge.www.domain.com
|
||||||
|
# Returns:
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
|
||||||
|
# Get domain root
|
||||||
|
data="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$CLOUDDNS_CLIENT_ID\"}]}"
|
||||||
|
_clouddns_api "POST" "domain/search" "$data"
|
||||||
|
domain_slice="$domain"
|
||||||
|
while [ -z "$domain_root" ]; do
|
||||||
|
if _contains "$response" "\"domainName\":\"$domain_slice\.\""; then
|
||||||
|
domain_root="$domain_slice"
|
||||||
|
_debug domain_root "$domain_root"
|
||||||
|
fi
|
||||||
|
domain_slice="$(echo "$domain_slice" | cut -d . -f 2-)"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Get domain id
|
||||||
|
data="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$CLOUDDNS_CLIENT_ID\"}, \
|
||||||
|
{\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}"
|
||||||
|
_clouddns_api "POST" "domain/search" "$data"
|
||||||
|
if _contains "$response" "\"id\":\""; then
|
||||||
|
re='domainType\":\"[^\"]*\",\"id\":\"([^\"]*)\",' # Match domain id
|
||||||
|
_domain_id=$(echo "$response" | _egrep_o "$re" | _head_n 1 | cut -d : -f 3 | tr -d "\",")
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | sed "s/.$domain_root//")
|
||||||
|
_domain="$domain_root"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_err 'Domain name not found on your CloudDNS account'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: _clouddns_api GET domain/search '{"data": "value"}'
|
||||||
|
# Returns:
|
||||||
|
# response='{"message": "api response"}'
|
||||||
|
_clouddns_api() {
|
||||||
|
method=$1
|
||||||
|
endpoint="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug endpoint "$endpoint"
|
||||||
|
|
||||||
|
if [ -z "$CLOUDDNS_TOKEN" ]; then
|
||||||
|
_clouddns_login
|
||||||
|
fi
|
||||||
|
_debug CLOUDDNS_TOKEN "$CLOUDDNS_TOKEN"
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Authorization: Bearer $CLOUDDNS_TOKEN"
|
||||||
|
|
||||||
|
if [ "$method" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$CLOUDDNS_API/$endpoint" "" "$method" | tr -d '\t\r\n ')"
|
||||||
|
else
|
||||||
|
response="$(_get "$CLOUDDNS_API/$endpoint" | tr -d '\t\r\n ')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error $endpoint"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Returns:
|
||||||
|
# CLOUDDNS_TOKEN=dslfje2rj23l
|
||||||
|
_clouddns_login() {
|
||||||
|
login_data="{\"email\": \"$CLOUDDNS_EMAIL\", \"password\": \"$CLOUDDNS_PASSWORD\"}"
|
||||||
|
response="$(_post "$login_data" "$CLOUDDNS_LOGIN_API" "" "POST" "Content-Type: application/json")"
|
||||||
|
|
||||||
|
if _contains "$response" "\"accessToken\":\""; then
|
||||||
|
CLOUDDNS_TOKEN=$(echo "$response" | _egrep_o "\"accessToken\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
|
export CLOUDDNS_TOKEN
|
||||||
|
else
|
||||||
|
echo 'Could not get CloudDNS access token; check your credentials'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
@ -1,12 +1,18 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_cloudns_info='ClouDNS.net
|
||||||
|
Site: ClouDNS.net
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cloudns
|
||||||
|
Options:
|
||||||
|
CLOUDNS_AUTH_ID Regular auth ID
|
||||||
|
CLOUDNS_SUB_AUTH_ID Sub auth ID
|
||||||
|
CLOUDNS_AUTH_PASSWORD Auth Password
|
||||||
|
Author: Boyan Peychev <boyan@cloudns.net>
|
||||||
|
'
|
||||||
|
|
||||||
# Author: Boyan Peychev <boyan at cloudns dot net>
|
|
||||||
# Repository: https://github.com/ClouDNS/acme.sh/
|
|
||||||
|
|
||||||
#CLOUDNS_AUTH_ID=XXXXX
|
|
||||||
#CLOUDNS_SUB_AUTH_ID=XXXXX
|
|
||||||
#CLOUDNS_AUTH_PASSWORD="YYYYYYYYY"
|
|
||||||
CLOUDNS_API="https://api.cloudns.net"
|
CLOUDNS_API="https://api.cloudns.net"
|
||||||
|
DOMAIN_TYPE=
|
||||||
|
DOMAIN_MASTER=
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
@ -61,15 +67,24 @@ dns_cloudns_rm() {
|
|||||||
host="$(echo "$1" | sed "s/\.$zone\$//")"
|
host="$(echo "$1" | sed "s/\.$zone\$//")"
|
||||||
record=$2
|
record=$2
|
||||||
|
|
||||||
|
_dns_cloudns_get_zone_info "$zone"
|
||||||
|
|
||||||
|
_debug "Type" "$DOMAIN_TYPE"
|
||||||
|
_debug "Cloud Master" "$DOMAIN_MASTER"
|
||||||
|
if _contains "$DOMAIN_TYPE" "cloud"; then
|
||||||
|
zone=$DOMAIN_MASTER
|
||||||
|
fi
|
||||||
|
_debug "ZONE" "$zone"
|
||||||
|
|
||||||
_dns_cloudns_http_api_call "dns/records.json" "domain-name=$zone&host=$host&type=TXT"
|
_dns_cloudns_http_api_call "dns/records.json" "domain-name=$zone&host=$host&type=TXT"
|
||||||
if ! _contains "$response" "\"id\":"; then
|
if ! _contains "$response" "\"id\":"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for i in $(echo "$response" | tr '{' "\n" | grep "$record"); do
|
for i in $(echo "$response" | tr '{' "\n" | grep -- "$record"); do
|
||||||
record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g')
|
record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g')
|
||||||
|
|
||||||
if [ ! -z "$record_id" ]; then
|
if [ -n "$record_id" ]; then
|
||||||
_debug zone "$zone"
|
_debug zone "$zone"
|
||||||
_debug host "$host"
|
_debug host "$host"
|
||||||
_debug record "$record"
|
_debug record "$record"
|
||||||
@ -91,7 +106,7 @@ dns_cloudns_rm() {
|
|||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
_dns_cloudns_init_check() {
|
_dns_cloudns_init_check() {
|
||||||
if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
|
if [ -n "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -134,10 +149,22 @@ _dns_cloudns_init_check() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_dns_cloudns_get_zone_info() {
|
||||||
|
zone=$1
|
||||||
|
_dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zone"
|
||||||
|
if ! _contains "$response" "\"status\":\"Failed\""; then
|
||||||
|
DOMAIN_TYPE=$(echo "$response" | _egrep_o '"type":"[^"]*"' | cut -d : -f 2 | tr -d '"')
|
||||||
|
if _contains "$DOMAIN_TYPE" "cloud"; then
|
||||||
|
DOMAIN_MASTER=$(echo "$response" | _egrep_o '"cloud-master":"[^"]*"' | cut -d : -f 2 | tr -d '"')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
_dns_cloudns_get_zone_name() {
|
_dns_cloudns_get_zone_name() {
|
||||||
i=2
|
i=2
|
||||||
while true; do
|
while true; do
|
||||||
zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100)
|
zoneForCheck=$(printf "%s" "$1" | cut -d . -f "$i"-100)
|
||||||
|
|
||||||
if [ -z "$zoneForCheck" ]; then
|
if [ -z "$zoneForCheck" ]; then
|
||||||
return 1
|
return 1
|
||||||
@ -164,7 +191,7 @@ _dns_cloudns_http_api_call() {
|
|||||||
_debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
|
_debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
|
||||||
_debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
|
_debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
|
||||||
|
|
||||||
if [ ! -z "$CLOUDNS_SUB_AUTH_ID" ]; then
|
if [ -n "$CLOUDNS_SUB_AUTH_ID" ]; then
|
||||||
auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID"
|
auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID"
|
||||||
else
|
else
|
||||||
auth_user="auth-id=$CLOUDNS_AUTH_ID"
|
auth_user="auth-id=$CLOUDNS_AUTH_ID"
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# DNS API for acme.sh for Core-Networks (https://beta.api.core-networks.de/doc/).
|
dns_cn_info='Core-Networks.de
|
||||||
# created by 5ll and francis
|
Site: beta.api.Core-Networks.de/doc/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cn
|
||||||
|
Options:
|
||||||
|
CN_User User
|
||||||
|
CN_Password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2142
|
||||||
|
Author: 5ll, francis
|
||||||
|
'
|
||||||
|
|
||||||
CN_API="https://beta.api.core-networks.de"
|
CN_API="https://beta.api.core-networks.de"
|
||||||
|
|
||||||
@ -124,7 +131,7 @@ _cn_get_root() {
|
|||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
|
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
_debug _H1 "${_H1}"
|
_debug _H1 "${_H1}"
|
||||||
|
|
||||||
@ -142,7 +149,7 @@ _cn_get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then
|
if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
|
@ -1,4 +1,15 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_conoha_info='ConoHa.jp
|
||||||
|
Domains: ConoHa.io
|
||||||
|
Site: ConoHa.jp
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_conoha
|
||||||
|
Options:
|
||||||
|
CONOHA_Username Username
|
||||||
|
CONOHA_Password Password
|
||||||
|
CONOHA_TenantId TenantId
|
||||||
|
CONOHA_IdentityServiceApi Identity Service API. E.g. "https://identity.xxxx.conoha.io/v2.0"
|
||||||
|
'
|
||||||
|
|
||||||
CONOHA_DNS_EP_PREFIX_REGEXP="https://dns-service\."
|
CONOHA_DNS_EP_PREFIX_REGEXP="https://dns-service\."
|
||||||
|
|
||||||
@ -115,9 +126,9 @@ dns_conoha_rm() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
record_id=$(printf "%s" "$response" | _egrep_o '{[^}]*}' \
|
record_id=$(printf "%s" "$response" | _egrep_o '{[^}]*}' |
|
||||||
| grep '"type":"TXT"' | grep "\"data\":\"$txtvalue\"" | _egrep_o "\"id\":\"[^\"]*\"" \
|
grep '"type":"TXT"' | grep "\"data\":\"$txtvalue\"" | _egrep_o "\"id\":\"[^\"]*\"" |
|
||||||
| _head_n 1 | cut -d : -f 2 | tr -d \")
|
_head_n 1 | cut -d : -f 2 | tr -d \")
|
||||||
if [ -z "$record_id" ]; then
|
if [ -z "$record_id" ]; then
|
||||||
_err "Can not get record id to remove."
|
_err "Can not get record id to remove."
|
||||||
return 1
|
return 1
|
||||||
@ -226,7 +237,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100).
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100).
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -240,7 +251,7 @@ _get_root() {
|
|||||||
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
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 \")
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
182
dnsapi/dns_constellix.sh
Normal file
182
dnsapi/dns_constellix.sh
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_constellix_info='Constellix.com
|
||||||
|
Site: Constellix.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_constellix
|
||||||
|
Options:
|
||||||
|
CONSTELLIX_Key API Key
|
||||||
|
CONSTELLIX_Secret API Secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2724
|
||||||
|
Author: Wout Decre <wout@canodus.be>
|
||||||
|
'
|
||||||
|
|
||||||
|
CONSTELLIX_Api="https://api.dns.constellix.com/v1"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_constellix_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
CONSTELLIX_Key="${CONSTELLIX_Key:-$(_readaccountconf_mutable CONSTELLIX_Key)}"
|
||||||
|
CONSTELLIX_Secret="${CONSTELLIX_Secret:-$(_readaccountconf_mutable CONSTELLIX_Secret)}"
|
||||||
|
|
||||||
|
if [ -z "$CONSTELLIX_Key" ] || [ -z "$CONSTELLIX_Secret" ]; then
|
||||||
|
_err "You did not specify the Contellix API key and secret yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable CONSTELLIX_Key "$CONSTELLIX_Key"
|
||||||
|
_saveaccountconf_mutable CONSTELLIX_Secret "$CONSTELLIX_Secret"
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The TXT record might already exist when working with wildcard certificates. In that case, update the record by adding the new value.
|
||||||
|
_debug "Search TXT record"
|
||||||
|
if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
|
||||||
|
_info "Adding TXT record"
|
||||||
|
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"add\":true,\"set\":{\"name\":\"${_sub_domain}\",\"ttl\":60,\"roundRobin\":[{\"value\":\"${txtvalue}\"}]}}]"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"success\":\"1 record(s) added, 0 record(s) updated, 0 record(s) deleted\"}" >/dev/null; then
|
||||||
|
_info "Added"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error adding TXT record"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
|
||||||
|
if _constellix_rest GET "domains/${_domain_id}/records/TXT/${_record_id}"; then
|
||||||
|
_new_rr_values=$(printf "%s\n" "$response" | _egrep_o '"roundRobin":\[[^]]*\]' | sed "s/\]$/,{\"value\":\"${txtvalue}\"}]/")
|
||||||
|
_debug _new_rr_values "$_new_rr_values"
|
||||||
|
_info "Updating TXT record"
|
||||||
|
if _constellix_rest PUT "domains/${_domain_id}/records/TXT/${_record_id}" "{\"name\":\"${_sub_domain}\",\"ttl\":60,${_new_rr_values}}"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"success\":\"Record.*updated successfully\"}" >/dev/null; then
|
||||||
|
_info "Updated"
|
||||||
|
return 0
|
||||||
|
elif printf -- "%s" "$response" | grep "{\"errors\":\[\"Contents are identical\"\]}" >/dev/null; then
|
||||||
|
_info "Already exists, no need to update"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error updating TXT record"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_constellix_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
CONSTELLIX_Key="${CONSTELLIX_Key:-$(_readaccountconf_mutable CONSTELLIX_Key)}"
|
||||||
|
CONSTELLIX_Secret="${CONSTELLIX_Secret:-$(_readaccountconf_mutable CONSTELLIX_Secret)}"
|
||||||
|
|
||||||
|
if [ -z "$CONSTELLIX_Key" ] || [ -z "$CONSTELLIX_Secret" ]; then
|
||||||
|
_err "You did not specify the Contellix API key and secret yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The TXT record might have been removed already when working with some wildcard certificates.
|
||||||
|
_debug "Search TXT record"
|
||||||
|
if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
|
||||||
|
_info "Removed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_info "Removing TXT record"
|
||||||
|
if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"delete\":true,\"filter\":{\"field\":\"name\",\"op\":\"eq\",\"value\":\"${_sub_domain}\"}}]"; then
|
||||||
|
if printf -- "%s" "$response" | grep "{\"success\":\"0 record(s) added, 0 record(s) updated, 1 record(s) deleted\"}" >/dev/null; then
|
||||||
|
_info "Removed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Error removing TXT record"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
_debug "Detecting root zone"
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _constellix_rest GET "domains/search?exact=$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$response" "\"name\":\"$h\""; then
|
||||||
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-"$p")
|
||||||
|
_domain="$h"
|
||||||
|
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_constellix_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
data="$3"
|
||||||
|
_debug "$ep"
|
||||||
|
|
||||||
|
rdate=$(date +"%s")"000"
|
||||||
|
hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)
|
||||||
|
|
||||||
|
export _H1="x-cnsdns-apiKey: $CONSTELLIX_Key"
|
||||||
|
export _H2="x-cnsdns-requestDate: $rdate"
|
||||||
|
export _H3="x-cnsdns-hmac: $hmac"
|
||||||
|
export _H4="Accept: application/json"
|
||||||
|
export _H5="Content-Type: application/json"
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$CONSTELLIX_Api/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$CONSTELLIX_Api/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
160
dnsapi/dns_cpanel.sh
Executable file
160
dnsapi/dns_cpanel.sh
Executable file
@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_cpanel_info='cPanel Server API
|
||||||
|
Manage DNS via cPanel Dashboard.
|
||||||
|
Site: cPanel.net
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_cpanel
|
||||||
|
Options:
|
||||||
|
cPanel_Username Username
|
||||||
|
cPanel_Apitoken API Token
|
||||||
|
cPanel_Hostname Server URL. E.g. "https://hostname:port"
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3732
|
||||||
|
Author: Bjarne Saltbaek
|
||||||
|
'
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
# Used to add txt record
|
||||||
|
dns_cpanel_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Adding TXT record to cPanel based system"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
_debug cPanel_Username "$cPanel_Username"
|
||||||
|
_debug cPanel_Apitoken "$cPanel_Apitoken"
|
||||||
|
_debug cPanel_Hostname "$cPanel_Hostname"
|
||||||
|
|
||||||
|
if ! _cpanel_login; then
|
||||||
|
_err "cPanel Login failed for user $cPanel_Username. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "No matching root domain for $fulldomain found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# adding entry
|
||||||
|
_info "Adding the entry"
|
||||||
|
stripped_fulldomain=$(echo "$fulldomain" | sed "s/.$_domain//")
|
||||||
|
_debug "Adding $stripped_fulldomain to $_domain zone"
|
||||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=add_zone_record&domain=$_domain&name=$stripped_fulldomain&type=TXT&txtdata=$txtvalue&ttl=1"
|
||||||
|
if _successful_update; then return 0; fi
|
||||||
|
_err "Couldn't create entry!"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: fulldomain txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_cpanel_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
_info "Using cPanel based system"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _cpanel_login; then
|
||||||
|
_err "cPanel Login failed for user $cPanel_Username. Check $HTTP_HEADER file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root; then
|
||||||
|
_err "No matching root domain for $fulldomain found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_findentry "$fulldomain" "$txtvalue"
|
||||||
|
if [ -z "$_id" ]; then
|
||||||
|
_info "Entry doesn't exist, nothing to delete"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_debug "Deleting record..."
|
||||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=remove_zone_record&domain=$_domain&line=$_id"
|
||||||
|
# removing entry
|
||||||
|
_debug "_result is: $_result"
|
||||||
|
|
||||||
|
if _successful_update; then return 0; fi
|
||||||
|
_err "Couldn't delete entry!"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_checkcredentials() {
|
||||||
|
cPanel_Username="${cPanel_Username:-$(_readaccountconf_mutable cPanel_Username)}"
|
||||||
|
cPanel_Apitoken="${cPanel_Apitoken:-$(_readaccountconf_mutable cPanel_Apitoken)}"
|
||||||
|
cPanel_Hostname="${cPanel_Hostname:-$(_readaccountconf_mutable cPanel_Hostname)}"
|
||||||
|
|
||||||
|
if [ -z "$cPanel_Username" ] || [ -z "$cPanel_Apitoken" ] || [ -z "$cPanel_Hostname" ]; then
|
||||||
|
cPanel_Username=""
|
||||||
|
cPanel_Apitoken=""
|
||||||
|
cPanel_Hostname=""
|
||||||
|
_err "You haven't specified cPanel username, apitoken and hostname yet."
|
||||||
|
_err "Please add credentials and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_saveaccountconf_mutable cPanel_Username "$cPanel_Username"
|
||||||
|
_saveaccountconf_mutable cPanel_Apitoken "$cPanel_Apitoken"
|
||||||
|
_saveaccountconf_mutable cPanel_Hostname "$cPanel_Hostname"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_cpanel_login() {
|
||||||
|
if ! _checkcredentials; then return 1; fi
|
||||||
|
|
||||||
|
if ! _myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=CustInfo&cpanel_jsonapi_func=displaycontactinfo"; then
|
||||||
|
_err "cPanel login failed for user $cPanel_Username."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_myget() {
|
||||||
|
#Adds auth header to request
|
||||||
|
export _H1="Authorization: cpanel $cPanel_Username:$cPanel_Apitoken"
|
||||||
|
_result=$(_get "$cPanel_Hostname/$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
_myget 'json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzones'
|
||||||
|
_domains=$(echo "$_result" | _egrep_o '"[a-z0-9\.\-]*":\["; cPanel first' | cut -d':' -f1 | sed 's/"//g' | sed 's/{//g')
|
||||||
|
_debug "_result is: $_result"
|
||||||
|
_debug "_domains is: $_domains"
|
||||||
|
if [ -z "$_domains" ]; then
|
||||||
|
_err "Primary domain list not found!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
for _domain in $_domains; do
|
||||||
|
_debug "Checking if $fulldomain ends with $_domain"
|
||||||
|
if (_endswith "$fulldomain" "$_domain"); then
|
||||||
|
_debug "Root domain: $_domain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_successful_update() {
|
||||||
|
if (echo "$_result" | _egrep_o 'data":\[[^]]*]' | grep -q '"newserial":null'); then return 1; fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_findentry() {
|
||||||
|
_debug "In _findentry"
|
||||||
|
#returns id of dns entry, if it exists
|
||||||
|
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&domain=$_domain"
|
||||||
|
_id=$(echo "$_result" | sed -e "s/},{/},\n{/g" | grep "$fulldomain" | grep "$txtvalue" | _egrep_o 'line":[0-9]+' | cut -d ':' -f 2)
|
||||||
|
_debug "_result is: $_result"
|
||||||
|
_debug "fulldomain. is $fulldomain."
|
||||||
|
_debug "txtvalue is $txtvalue"
|
||||||
|
_debug "_id is: $_id"
|
||||||
|
if [ -n "$_id" ]; then
|
||||||
|
_debug "Entry found with _id=$_id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
165
dnsapi/dns_curanet.sh
Normal file
165
dnsapi/dns_curanet.sh
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_curanet_info='Curanet.dk
|
||||||
|
Domains: scannet.dk wannafind.dk dandomain.dk
|
||||||
|
Site: Curanet.dk
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_curanet
|
||||||
|
Options:
|
||||||
|
CURANET_AUTHCLIENTID Auth ClientID. Requires scope dns
|
||||||
|
CURANET_AUTHSECRET Auth Secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3933
|
||||||
|
Author: Peter L. Hansen <peter@r12.dk>
|
||||||
|
'
|
||||||
|
|
||||||
|
CURANET_REST_URL="https://api.curanet.dk/dns/v1/Domains"
|
||||||
|
CURANET_AUTH_URL="https://apiauth.dk.team.blue/auth/realms/Curanet/protocol/openid-connect/token"
|
||||||
|
CURANET_ACCESS_TOKEN=""
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_curanet_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_curanet_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using curanet"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}"
|
||||||
|
CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}"
|
||||||
|
if [ -z "$CURANET_AUTHCLIENTID" ] || [ -z "$CURANET_AUTHSECRET" ]; then
|
||||||
|
CURANET_AUTHCLIENTID=""
|
||||||
|
CURANET_AUTHSECRET=""
|
||||||
|
_err "You don't specify curanet api client and secret."
|
||||||
|
_err "Please create your auth info and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_saveaccountconf_mutable CURANET_AUTHCLIENTID "$CURANET_AUTHCLIENTID"
|
||||||
|
_saveaccountconf_mutable CURANET_AUTHSECRET "$CURANET_AUTHSECRET"
|
||||||
|
|
||||||
|
if ! _get_token; then
|
||||||
|
_err "Unable to get token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json-patch+json"
|
||||||
|
export _H2="Accept: application/json"
|
||||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
|
||||||
|
data="{\"name\": \"$fulldomain\",\"type\": \"TXT\",\"ttl\": 60,\"priority\": 0,\"data\": \"$txtvalue\"}"
|
||||||
|
response="$(_post "$data" "$CURANET_REST_URL/${_domain}/Records" "" "")"
|
||||||
|
|
||||||
|
if _contains "$response" "$txtvalue"; then
|
||||||
|
_debug "TXT record added OK"
|
||||||
|
else
|
||||||
|
_err "Unable to add TXT record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_curanet_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using curanet"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}"
|
||||||
|
CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}"
|
||||||
|
|
||||||
|
if ! _get_token; then
|
||||||
|
_err "Unable to get token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "Invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Getting current record list to identify TXT to delete"
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Accept: application/json"
|
||||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
response="$(_get "$CURANET_REST_URL/${_domain}/Records" "" "")"
|
||||||
|
|
||||||
|
if ! _contains "$response" "$txtvalue"; then
|
||||||
|
_err "Unable to delete record (does not contain $txtvalue )"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
recordid=$(echo "$response" | _egrep_o "{\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue" | _egrep_o "id\":[0-9]+" | cut -c 5-)
|
||||||
|
|
||||||
|
if [ -z "$recordid" ]; then
|
||||||
|
_err "Unable to get recordid"
|
||||||
|
_debug "regex {\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue"
|
||||||
|
_debug "response $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "Deleting recordID $recordid"
|
||||||
|
response="$(_post "" "$CURANET_REST_URL/${_domain}/Records/$recordid" "" "DELETE")"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_get_token() {
|
||||||
|
response="$(_post "grant_type=client_credentials&client_id=$CURANET_AUTHCLIENTID&client_secret=$CURANET_AUTHSECRET&scope=dns" "$CURANET_AUTH_URL" "" "")"
|
||||||
|
if ! _contains "$response" "access_token"; then
|
||||||
|
_err "Unable get access token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
CURANET_ACCESS_TOKEN=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]+" | cut -c 17-)
|
||||||
|
|
||||||
|
if [ -z "$CURANET_ACCESS_TOKEN" ]; then
|
||||||
|
_err "Unable to get token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=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
|
||||||
|
|
||||||
|
export _H1="Content-Type: application/json"
|
||||||
|
export _H2="Accept: application/json"
|
||||||
|
export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
|
||||||
|
response="$(_get "$CURANET_REST_URL/$h/Records" "" "")"
|
||||||
|
|
||||||
|
if [ ! "$(echo "$response" | _egrep_o "Entity not found")" ]; then
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
185
dnsapi/dns_cx.sh
185
dnsapi/dns_cx.sh
@ -1,185 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
# CloudXNS Domain api
|
|
||||||
#
|
|
||||||
#CX_Key="1234"
|
|
||||||
#
|
|
||||||
#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() {
|
|
||||||
fulldomain=$1
|
|
||||||
txtvalue=$2
|
|
||||||
|
|
||||||
CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}"
|
|
||||||
CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}"
|
|
||||||
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then
|
|
||||||
CX_Key=""
|
|
||||||
CX_Secret=""
|
|
||||||
_err "You don't specify cloudxns.net api key or secret yet."
|
|
||||||
_err "Please create you key and try again."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
REST_API="$CX_Api"
|
|
||||||
|
|
||||||
#save the api key and email to the account conf file.
|
|
||||||
_saveaccountconf_mutable CX_Key "$CX_Key"
|
|
||||||
_saveaccountconf_mutable CX_Secret "$CX_Secret"
|
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
|
||||||
if ! _get_root "$fulldomain"; then
|
|
||||||
_err "invalid domain"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
add_record "$_domain" "$_sub_domain" "$txtvalue"
|
|
||||||
}
|
|
||||||
|
|
||||||
#fulldomain txtvalue
|
|
||||||
dns_cx_rm() {
|
|
||||||
fulldomain=$1
|
|
||||||
txtvalue=$2
|
|
||||||
CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}"
|
|
||||||
CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}"
|
|
||||||
REST_API="$CX_Api"
|
|
||||||
if _get_root "$fulldomain"; then
|
|
||||||
record_id=""
|
|
||||||
existing_records "$_domain" "$_sub_domain" "$txtvalue"
|
|
||||||
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.
|
|
||||||
# '0' means doesn't exist
|
|
||||||
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
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}')
|
|
||||||
_debug seg "$seg"
|
|
||||||
if [ -z "$seg" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#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" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#################### 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 ! _rest GET "domain"; 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" "$h."; then
|
|
||||||
seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}')
|
|
||||||
_debug seg "$seg"
|
|
||||||
_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 "%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"
|
|
||||||
_debug ep "$ep"
|
|
||||||
url="$REST_API/$ep"
|
|
||||||
_debug url "$url"
|
|
||||||
|
|
||||||
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 "%s" "$sec" | _digest md5 hex)
|
|
||||||
_debug hmac "$hmac"
|
|
||||||
|
|
||||||
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="$(_get "$url")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$?" != "0" ]; then
|
|
||||||
_err "error $ep"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_debug2 response "$response"
|
|
||||||
|
|
||||||
_contains "$response" '"code":1'
|
|
||||||
|
|
||||||
}
|
|
@ -1,40 +1,34 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
########
|
dns_cyon_info='cyon.ch
|
||||||
# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/Neilpang/acme.sh)
|
Site: cyon.ch
|
||||||
#
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cyon
|
||||||
# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
|
Options:
|
||||||
#
|
CY_Username Username
|
||||||
# Dependencies:
|
CY_Password API Token
|
||||||
# -------------
|
CY_OTP_Secret OTP token. Only required if using 2FA
|
||||||
# - oathtool (When using 2 Factor Authentication)
|
Issues: github.com/noplanman/cyon-api/issues
|
||||||
#
|
Author: Armando Lüscher <armando@noplanman.ch>
|
||||||
# Issues:
|
'
|
||||||
# -------
|
|
||||||
# Any issues / questions / suggestions can be posted here:
|
|
||||||
# https://github.com/noplanman/cyon-api/issues
|
|
||||||
#
|
|
||||||
# Author: Armando Lüscher <armando@noplanman.ch>
|
|
||||||
########
|
|
||||||
|
|
||||||
dns_cyon_add() {
|
dns_cyon_add() {
|
||||||
_cyon_load_credentials \
|
_cyon_load_credentials &&
|
||||||
&& _cyon_load_parameters "$@" \
|
_cyon_load_parameters "$@" &&
|
||||||
&& _cyon_print_header "add" \
|
_cyon_print_header "add" &&
|
||||||
&& _cyon_login \
|
_cyon_login &&
|
||||||
&& _cyon_change_domain_env \
|
_cyon_change_domain_env &&
|
||||||
&& _cyon_add_txt \
|
_cyon_add_txt &&
|
||||||
&& _cyon_logout
|
_cyon_logout
|
||||||
}
|
}
|
||||||
|
|
||||||
dns_cyon_rm() {
|
dns_cyon_rm() {
|
||||||
_cyon_load_credentials \
|
_cyon_load_credentials &&
|
||||||
&& _cyon_load_parameters "$@" \
|
_cyon_load_parameters "$@" &&
|
||||||
&& _cyon_print_header "delete" \
|
_cyon_print_header "delete" &&
|
||||||
&& _cyon_login \
|
_cyon_login &&
|
||||||
&& _cyon_change_domain_env \
|
_cyon_change_domain_env &&
|
||||||
&& _cyon_delete_txt \
|
_cyon_delete_txt &&
|
||||||
&& _cyon_logout
|
_cyon_logout
|
||||||
}
|
}
|
||||||
|
|
||||||
#########################
|
#########################
|
||||||
@ -44,7 +38,7 @@ dns_cyon_rm() {
|
|||||||
_cyon_load_credentials() {
|
_cyon_load_credentials() {
|
||||||
# Convert loaded password to/from base64 as needed.
|
# Convert loaded password to/from base64 as needed.
|
||||||
if [ "${CY_Password_B64}" ]; then
|
if [ "${CY_Password_B64}" ]; then
|
||||||
CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")"
|
CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64)"
|
||||||
elif [ "${CY_Password}" ]; then
|
elif [ "${CY_Password}" ]; then
|
||||||
CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)"
|
CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)"
|
||||||
fi
|
fi
|
||||||
@ -66,7 +60,7 @@ _cyon_load_credentials() {
|
|||||||
_debug "Save credentials to account.conf"
|
_debug "Save credentials to account.conf"
|
||||||
_saveaccountconf CY_Username "${CY_Username}"
|
_saveaccountconf CY_Username "${CY_Username}"
|
||||||
_saveaccountconf CY_Password_B64 "$CY_Password_B64"
|
_saveaccountconf CY_Password_B64 "$CY_Password_B64"
|
||||||
if [ ! -z "${CY_OTP_Secret}" ]; then
|
if [ -n "${CY_OTP_Secret}" ]; then
|
||||||
_saveaccountconf CY_OTP_Secret "$CY_OTP_Secret"
|
_saveaccountconf CY_OTP_Secret "$CY_OTP_Secret"
|
||||||
else
|
else
|
||||||
_clearaccountconf CY_OTP_Secret
|
_clearaccountconf CY_OTP_Secret
|
||||||
@ -164,7 +158,7 @@ _cyon_login() {
|
|||||||
# todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request.
|
# todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request.
|
||||||
|
|
||||||
# 2FA authentication with OTP?
|
# 2FA authentication with OTP?
|
||||||
if [ ! -z "${CY_OTP_Secret}" ]; then
|
if [ -n "${CY_OTP_Secret}" ]; then
|
||||||
_info " - Authorising with OTP code..."
|
_info " - Authorising with OTP code..."
|
||||||
|
|
||||||
if ! _exists oathtool; then
|
if ! _exists oathtool; then
|
||||||
|
@ -1,31 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
|
# shellcheck disable=SC2034
|
||||||
# vim: et ts=2 sw=2
|
dns_da_info='DirectAdmin Server API
|
||||||
#
|
Site: DirectAdmin.com/api.php
|
||||||
# DirectAdmin 1.41.0 API
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_da
|
||||||
# The DirectAdmin interface has it's own Let's encrypt functionality, but this
|
Options:
|
||||||
# script can be used to generate certificates for names which are not hosted on
|
DA_Api API Server URL. E.g. "https://remoteUser:remotePassword@da.domain.tld:8443"
|
||||||
# DirectAdmin
|
DA_Api_Insecure Insecure TLS. 0: check for cert validity, 1: always accept
|
||||||
#
|
Issues: github.com/TigerP/acme.sh/issues
|
||||||
# User must provide login data and URL to DirectAdmin incl. port.
|
'
|
||||||
# You can create login key, by using the Login Keys function
|
|
||||||
# ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to
|
|
||||||
# - CMD_API_DNS_CONTROL
|
|
||||||
# - CMD_API_SHOW_DOMAINS
|
|
||||||
#
|
|
||||||
# See also https://www.directadmin.com/api.php and
|
|
||||||
# https://www.directadmin.com/features.php?id=1298
|
|
||||||
#
|
|
||||||
# Report bugs to https://github.com/TigerP/acme.sh/issues
|
|
||||||
#
|
|
||||||
# Values to export:
|
|
||||||
# export DA_Api="https://remoteUser:remotePassword@da.example.com:8443"
|
|
||||||
# export DA_Api_Insecure=1
|
|
||||||
#
|
|
||||||
# Set DA_Api_Insecure to 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 #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
# Usage: dns_myapi_add _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
# Usage: dns_myapi_add _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
@ -78,7 +61,7 @@ _get_root() {
|
|||||||
# response will contain "list[]=example.com&list[]=example.org"
|
# response will contain "list[]=example.com&list[]=example.org"
|
||||||
_da_api CMD_API_SHOW_DOMAINS "" "${domain}"
|
_da_api CMD_API_SHOW_DOMAINS "" "${domain}"
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
# not valid
|
# not valid
|
||||||
@ -86,7 +69,7 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if _contains "$response" "$h" >/dev/null; then
|
if _contains "$response" "$h" >/dev/null; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#Created by RaidenII, to use DuckDNS's API to add/remove text records
|
dns_ddnss_info='DDNSS.de
|
||||||
#modified by helbgd @ 03/13/2018 to support ddnss.de
|
Site: DDNSS.de
|
||||||
#modified by mod242 @ 04/24/2018 to support different ddnss domains
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ddnss
|
||||||
#Please note: the Wildcard Feature must be turned on for the Host record
|
Options:
|
||||||
#and the checkbox for TXT needs to be enabled
|
DDNSS_Token API Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2230
|
||||||
# Pass credentials before "acme.sh --issue --dns dns_ddnss ..."
|
Author: RaidenII, helbgd, mod242
|
||||||
# --
|
'
|
||||||
# export DDNSS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
|
||||||
# --
|
|
||||||
#
|
|
||||||
|
|
||||||
DDNSS_DNS_API="https://ddnss.de/upd.php"
|
DDNSS_DNS_API="https://ddnss.de/upd.php"
|
||||||
|
|
||||||
@ -77,7 +74,7 @@ dns_ddnss_rm() {
|
|||||||
|
|
||||||
# Now remove the TXT record from DDNS DNS
|
# Now remove the TXT record from DDNS DNS
|
||||||
_info "Trying to remove TXT record"
|
_info "Trying to remove TXT record"
|
||||||
if _ddnss_rest GET "key=$DDNSS_Token&host=$_ddnss_domain&txtm=1&txt=."; then
|
if _ddnss_rest GET "key=$DDNSS_Token&host=$_ddnss_domain&txtm=2"; then
|
||||||
if [ "$response" = "Updated 1 hostname." ]; then
|
if [ "$response" = "Updated 1 hostname." ]; then
|
||||||
_info "TXT record has been successfully removed from your DDNSS domain."
|
_info "TXT record has been successfully removed from your DDNSS domain."
|
||||||
return 0
|
return 0
|
||||||
@ -119,7 +116,7 @@ _ddnss_rest() {
|
|||||||
|
|
||||||
# DDNSS uses GET to update domain info
|
# DDNSS uses GET to update domain info
|
||||||
if [ "$method" = "GET" ]; then
|
if [ "$method" = "GET" ]; then
|
||||||
response="$(_get "$url" | sed 's/<[a-zA-Z\/][^>]*>//g' | _tail_n 1)"
|
response="$(_get "$url" | sed 's/<[a-zA-Z\/][^>]*>//g' | tr -s "\n" | _tail_n 1)"
|
||||||
else
|
else
|
||||||
_err "Unsupported method"
|
_err "Unsupported method"
|
||||||
return 1
|
return 1
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#
|
# shellcheck disable=SC2034
|
||||||
# deSEC.io Domain API
|
dns_desec_info='deSEC.io
|
||||||
#
|
Site: desec.readthedocs.io/en/latest/
|
||||||
# Author: Zheng Qian
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_desec
|
||||||
#
|
Options:
|
||||||
# deSEC API doc
|
DDNSS_Token API Token
|
||||||
# https://desec.readthedocs.io/en/latest/
|
Issues: github.com/acmesh-official/acme.sh/issues/2180
|
||||||
|
Author: Zheng Qian
|
||||||
|
'
|
||||||
|
|
||||||
REST_API="https://desec.io/api/v1/domains"
|
REST_API="https://desec.io/api/v1/domains"
|
||||||
|
|
||||||
@ -20,21 +22,17 @@ dns_desec_add() {
|
|||||||
_debug txtvalue "$txtvalue"
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
|
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
|
||||||
DEDYN_NAME="${DEDYN_NAME:-$(_readaccountconf_mutable DEDYN_NAME)}"
|
|
||||||
|
|
||||||
if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then
|
if [ -z "$DEDYN_TOKEN" ]; then
|
||||||
DEDYN_TOKEN=""
|
DEDYN_TOKEN=""
|
||||||
DEDYN_NAME=""
|
_err "You did not specify DEDYN_TOKEN yet."
|
||||||
_err "You did not specify DEDYN_TOKEN and DEDYN_NAME yet."
|
|
||||||
_err "Please create your key and try again."
|
_err "Please create your key and try again."
|
||||||
_err "e.g."
|
_err "e.g."
|
||||||
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
|
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
|
||||||
_err "export DEDYN_NAME=foobar.dedyn.io"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
#save the api token and name to the account conf file.
|
#save the api token to the account conf file.
|
||||||
_saveaccountconf_mutable DEDYN_TOKEN "$DEDYN_TOKEN"
|
_saveaccountconf_mutable DEDYN_TOKEN "$DEDYN_TOKEN"
|
||||||
_saveaccountconf_mutable DEDYN_NAME "$DEDYN_NAME"
|
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
_debug "First detect the root zone"
|
||||||
if ! _get_root "$fulldomain" "$REST_API/"; then
|
if ! _get_root "$fulldomain" "$REST_API/"; then
|
||||||
@ -47,7 +45,7 @@ dns_desec_add() {
|
|||||||
# Get existing TXT record
|
# Get existing TXT record
|
||||||
_debug "Getting txt records"
|
_debug "Getting txt records"
|
||||||
txtvalues="\"\\\"$txtvalue\\\"\""
|
txtvalues="\"\\\"$txtvalue\\\"\""
|
||||||
_desec_rest GET "$REST_API/$DEDYN_NAME/rrsets/$_sub_domain/TXT/"
|
_desec_rest GET "$REST_API/$_domain/rrsets/$_sub_domain/TXT/"
|
||||||
|
|
||||||
if [ "$_code" = "200" ]; then
|
if [ "$_code" = "200" ]; then
|
||||||
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
||||||
@ -61,9 +59,9 @@ dns_desec_add() {
|
|||||||
fi
|
fi
|
||||||
_debug txtvalues "$txtvalues"
|
_debug txtvalues "$txtvalues"
|
||||||
_info "Adding record"
|
_info "Adding record"
|
||||||
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":60}]"
|
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":3600}]"
|
||||||
|
|
||||||
if _desec_rest PUT "$REST_API/$DEDYN_NAME/rrsets/" "$body"; then
|
if _desec_rest PUT "$REST_API/$_domain/rrsets/" "$body"; then
|
||||||
if _contains "$response" "$txtvalue"; then
|
if _contains "$response" "$txtvalue"; then
|
||||||
_info "Added, OK"
|
_info "Added, OK"
|
||||||
return 0
|
return 0
|
||||||
@ -87,16 +85,13 @@ dns_desec_rm() {
|
|||||||
_debug txtvalue "$txtvalue"
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
|
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
|
||||||
DEDYN_NAME="${DEDYN_NAME:-$(_readaccountconf_mutable DEDYN_NAME)}"
|
|
||||||
|
|
||||||
if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then
|
if [ -z "$DEDYN_TOKEN" ]; then
|
||||||
DEDYN_TOKEN=""
|
DEDYN_TOKEN=""
|
||||||
DEDYN_NAME=""
|
_err "You did not specify DEDYN_TOKEN yet."
|
||||||
_err "You did not specify DEDYN_TOKEN and DEDYN_NAME yet."
|
|
||||||
_err "Please create your key and try again."
|
_err "Please create your key and try again."
|
||||||
_err "e.g."
|
_err "e.g."
|
||||||
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
|
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
|
||||||
_err "export DEDYN_NAME=foobar.dedyn.io"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -112,7 +107,7 @@ dns_desec_rm() {
|
|||||||
# Get existing TXT record
|
# Get existing TXT record
|
||||||
_debug "Getting txt records"
|
_debug "Getting txt records"
|
||||||
txtvalues=""
|
txtvalues=""
|
||||||
_desec_rest GET "$REST_API/$DEDYN_NAME/rrsets/$_sub_domain/TXT/"
|
_desec_rest GET "$REST_API/$_domain/rrsets/$_sub_domain/TXT/"
|
||||||
|
|
||||||
if [ "$_code" = "200" ]; then
|
if [ "$_code" = "200" ]; then
|
||||||
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
||||||
@ -130,8 +125,8 @@ dns_desec_rm() {
|
|||||||
_debug txtvalues "$txtvalues"
|
_debug txtvalues "$txtvalues"
|
||||||
|
|
||||||
_info "Deleting record"
|
_info "Deleting record"
|
||||||
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":60}]"
|
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":3600}]"
|
||||||
_desec_rest PUT "$REST_API/$DEDYN_NAME/rrsets/" "$body"
|
_desec_rest PUT "$REST_API/$_domain/rrsets/" "$body"
|
||||||
if [ "$_code" = "200" ]; then
|
if [ "$_code" = "200" ]; then
|
||||||
_info "Deleted, OK"
|
_info "Deleted, OK"
|
||||||
return 0
|
return 0
|
||||||
@ -181,7 +176,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -193,7 +188,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
62
dnsapi/dns_df.sh
Normal file
62
dnsapi/dns_df.sh
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_df_info='DynDnsFree.de
|
||||||
|
Domains: dynup.de
|
||||||
|
Site: DynDnsFree.de
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_df
|
||||||
|
Options:
|
||||||
|
DF_user Username
|
||||||
|
DF_password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2897
|
||||||
|
Author: Thilo Gass <thilo.gass@gmail.com>
|
||||||
|
'
|
||||||
|
|
||||||
|
dyndnsfree_api="https://dynup.de/acme.php"
|
||||||
|
|
||||||
|
dns_df_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txt_value=$2
|
||||||
|
_info "Using DNS-01 dyndnsfree.de hook"
|
||||||
|
|
||||||
|
DF_user="${DF_user:-$(_readaccountconf_mutable DF_user)}"
|
||||||
|
DF_password="${DF_password:-$(_readaccountconf_mutable DF_password)}"
|
||||||
|
if [ -z "$DF_user" ] || [ -z "$DF_password" ]; then
|
||||||
|
DF_user=""
|
||||||
|
DF_password=""
|
||||||
|
_err "No auth details provided. Please set user credentials using the \$DF_user and \$DF_password environment variables."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
#save the api user and password to the account conf file.
|
||||||
|
_debug "Save user and password"
|
||||||
|
_saveaccountconf_mutable DF_user "$DF_user"
|
||||||
|
_saveaccountconf_mutable DF_password "$DF_password"
|
||||||
|
|
||||||
|
domain="$(printf "%s" "$fulldomain" | cut -d"." -f2-)"
|
||||||
|
|
||||||
|
get="$dyndnsfree_api?username=$DF_user&password=$DF_password&hostname=$domain&add_hostname=$fulldomain&txt=$txt_value"
|
||||||
|
|
||||||
|
if ! erg="$(_get "$get")"; then
|
||||||
|
_err "error Adding $fulldomain TXT: $txt_value"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _contains "$erg" "success"; then
|
||||||
|
_info "Success, TXT Added, OK"
|
||||||
|
else
|
||||||
|
_err "error Adding $fulldomain TXT: $txt_value erg: $erg"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug "ok Auto $fulldomain TXT: $txt_value erg: $erg"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_df_rm() {
|
||||||
|
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "TXT enrty in $fulldomain is deleted automatically"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
}
|
@ -1,16 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
## Will be called by acme.sh to add the txt record to your api system.
|
dns_dgon_info='DigitalOcean.com
|
||||||
## returns 0 means success, otherwise error.
|
Site: DigitalOcean.com/help/api/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dgon
|
||||||
## Author: thewer <github at thewer.com>
|
Options:
|
||||||
## GitHub: https://github.com/gitwer/acme.sh
|
DO_API_KEY API Key
|
||||||
|
Author: <github@thewer.com>
|
||||||
##
|
'
|
||||||
## Environment Variables Required:
|
|
||||||
##
|
|
||||||
## DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc"
|
|
||||||
##
|
|
||||||
|
|
||||||
##################### Public functions #####################
|
##################### Public functions #####################
|
||||||
|
|
||||||
@ -22,7 +18,7 @@ dns_dgon_add() {
|
|||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}"
|
DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}"
|
||||||
# Check if API Key Exist
|
# Check if API Key Exists
|
||||||
if [ -z "$DO_API_KEY" ]; then
|
if [ -z "$DO_API_KEY" ]; then
|
||||||
DO_API_KEY=""
|
DO_API_KEY=""
|
||||||
_err "You did not specify DigitalOcean API key."
|
_err "You did not specify DigitalOcean API key."
|
||||||
@ -77,7 +73,7 @@ dns_dgon_rm() {
|
|||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}"
|
DO_API_KEY="${DO_API_KEY:-$(_readaccountconf_mutable DO_API_KEY)}"
|
||||||
# Check if API Key Exist
|
# Check if API Key Exists
|
||||||
if [ -z "$DO_API_KEY" ]; then
|
if [ -z "$DO_API_KEY" ]; then
|
||||||
DO_API_KEY=""
|
DO_API_KEY=""
|
||||||
_err "You did not specify DigitalOcean API key."
|
_err "You did not specify DigitalOcean API key."
|
||||||
@ -122,12 +118,12 @@ dns_dgon_rm() {
|
|||||||
## check for what we are looking for: "type":"A","name":"$_sub_domain"
|
## check for what we are looking for: "type":"A","name":"$_sub_domain"
|
||||||
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")"
|
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")"
|
||||||
|
|
||||||
if [ ! -z "$record" ]; then
|
if [ -n "$record" ]; then
|
||||||
|
|
||||||
## we found records
|
## we found records
|
||||||
rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
|
rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
|
||||||
_debug rec_ids "$rec_ids"
|
_debug rec_ids "$rec_ids"
|
||||||
if [ ! -z "$rec_ids" ]; then
|
if [ -n "$rec_ids" ]; then
|
||||||
echo "$rec_ids" | while IFS= read -r rec_id; do
|
echo "$rec_ids" | while IFS= read -r rec_id; do
|
||||||
## delete the record
|
## delete the record
|
||||||
## delete URL for removing the one we dont want
|
## delete URL for removing the one we dont want
|
||||||
@ -192,6 +188,7 @@ _get_base_domain() {
|
|||||||
## get URL for the list of domains
|
## get URL for the list of domains
|
||||||
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
|
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
|
||||||
DOMURL="https://api.digitalocean.com/v2/domains"
|
DOMURL="https://api.digitalocean.com/v2/domains"
|
||||||
|
found=""
|
||||||
|
|
||||||
## while we dont have a matching domain we keep going
|
## while we dont have a matching domain we keep going
|
||||||
while [ -z "$found" ]; do
|
while [ -z "$found" ]; do
|
||||||
@ -205,10 +202,8 @@ _get_base_domain() {
|
|||||||
fi
|
fi
|
||||||
_debug2 domain_list "$domain_list"
|
_debug2 domain_list "$domain_list"
|
||||||
|
|
||||||
## for each shortening of our $fulldomain, check if it exists in the $domain_list
|
i=1
|
||||||
## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
|
while [ "$i" -gt 0 ]; do
|
||||||
i=2
|
|
||||||
while [ $i -gt 0 ]; do
|
|
||||||
## get next longest domain
|
## get next longest domain
|
||||||
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
|
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
|
||||||
## check we got something back from our cut (or are we at the end)
|
## check we got something back from our cut (or are we at the end)
|
||||||
@ -218,16 +213,16 @@ _get_base_domain() {
|
|||||||
## we got part of a domain back - grep it out
|
## we got part of a domain back - grep it out
|
||||||
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
|
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
|
||||||
## check if it exists
|
## check if it exists
|
||||||
if [ ! -z "$found" ]; then
|
if [ -n "$found" ]; then
|
||||||
## exists - exit loop returning the parts
|
## exists - exit loop returning the parts
|
||||||
sub_point=$(_math $i - 1)
|
sub_point=$(_math "$i" - 1)
|
||||||
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
|
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
## increment cut point $i
|
## increment cut point $i
|
||||||
i=$(_math $i + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ -z "$found" ]; then
|
if [ -z "$found" ]; then
|
||||||
|
188
dnsapi/dns_dnsexit.sh
Normal file
188
dnsapi/dns_dnsexit.sh
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dnsexit_info='DNSExit.com
|
||||||
|
Site: DNSExit.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnsexit
|
||||||
|
Options:
|
||||||
|
DNSEXIT_API_KEY API Key
|
||||||
|
DNSEXIT_AUTH_USER Username
|
||||||
|
DNSEXIT_AUTH_PASS Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/4719
|
||||||
|
Author: Samuel Jimenez
|
||||||
|
'
|
||||||
|
|
||||||
|
DNSEXIT_API_URL="https://api.dnsexit.com/dns/"
|
||||||
|
DNSEXIT_HOSTS_URL="https://update.dnsexit.com/ipupdate/hosts.jsp"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
#Usage: dns_dnsexit_add _acme-challenge.*.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_dnsexit_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using DNSExit.com"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
_debug 'Load account auth'
|
||||||
|
if ! get_account_info; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug 'First detect the root zone'
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"add\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":0,\"overwrite\":false}}"; then
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 _response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Remove the txt record after validation.
|
||||||
|
dns_dnsexit_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_info "Using DNSExit.com"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
_debug 'Load account auth'
|
||||||
|
if ! get_account_info; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug 'First detect the root zone'
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug _sub_domain "$_sub_domain"
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
|
||||||
|
if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"delete\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\"}}"; then
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 _response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
while true; do
|
||||||
|
_domain=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
|
_debug h "$_domain"
|
||||||
|
if [ -z "$_domain" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug login "$DNSEXIT_AUTH_USER"
|
||||||
|
_debug password "$DNSEXIT_AUTH_PASS"
|
||||||
|
_debug domain "$_domain"
|
||||||
|
|
||||||
|
_dnsexit_http "login=$DNSEXIT_AUTH_USER&password=$DNSEXIT_AUTH_PASS&domain=$_domain"
|
||||||
|
|
||||||
|
if _contains "$response" "0=$_domain"; then
|
||||||
|
_sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_debug "Go to next level of $_domain"
|
||||||
|
fi
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_dnsexit_rest() {
|
||||||
|
m=POST
|
||||||
|
ep=""
|
||||||
|
data="$1"
|
||||||
|
_debug _dnsexit_rest "$ep"
|
||||||
|
_debug data "$data"
|
||||||
|
|
||||||
|
api_key_trimmed=$(echo "$DNSEXIT_API_KEY" | tr -d '"')
|
||||||
|
|
||||||
|
export _H1="apikey: $api_key_trimmed"
|
||||||
|
export _H2='Content-Type: application/json'
|
||||||
|
|
||||||
|
if [ "$m" != "GET" ]; then
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$DNSEXIT_API_URL/$ep" "" "$m")"
|
||||||
|
else
|
||||||
|
response="$(_get "$DNSEXIT_API_URL/$ep")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_dnsexit_http() {
|
||||||
|
m=GET
|
||||||
|
param="$1"
|
||||||
|
_debug param "$param"
|
||||||
|
_debug get "$DNSEXIT_HOSTS_URL?$param"
|
||||||
|
|
||||||
|
response="$(_get "$DNSEXIT_HOSTS_URL?$param")"
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "Error $param"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get_account_info() {
|
||||||
|
|
||||||
|
DNSEXIT_API_KEY="${DNSEXIT_API_KEY:-$(_readaccountconf_mutable DNSEXIT_API_KEY)}"
|
||||||
|
if test -z "$DNSEXIT_API_KEY"; then
|
||||||
|
DNSEXIT_API_KEY=''
|
||||||
|
_err 'DNSEXIT_API_KEY was not exported'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable DNSEXIT_API_KEY "$DNSEXIT_API_KEY"
|
||||||
|
|
||||||
|
DNSEXIT_AUTH_USER="${DNSEXIT_AUTH_USER:-$(_readaccountconf_mutable DNSEXIT_AUTH_USER)}"
|
||||||
|
if test -z "$DNSEXIT_AUTH_USER"; then
|
||||||
|
DNSEXIT_AUTH_USER=""
|
||||||
|
_err 'DNSEXIT_AUTH_USER was not exported'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable DNSEXIT_AUTH_USER "$DNSEXIT_AUTH_USER"
|
||||||
|
|
||||||
|
DNSEXIT_AUTH_PASS="${DNSEXIT_AUTH_PASS:-$(_readaccountconf_mutable DNSEXIT_AUTH_PASS)}"
|
||||||
|
if test -z "$DNSEXIT_AUTH_PASS"; then
|
||||||
|
DNSEXIT_AUTH_PASS=""
|
||||||
|
_err 'DNSEXIT_AUTH_PASS was not exported'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable DNSEXIT_AUTH_PASS "$DNSEXIT_AUTH_PASS"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
86
dnsapi/dns_dnshome.sh
Executable file
86
dnsapi/dns_dnshome.sh
Executable file
@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dnshome_info='dnsHome.de
|
||||||
|
Site: dnsHome.de
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnshome
|
||||||
|
Options:
|
||||||
|
DNSHOME_Subdomain Subdomain
|
||||||
|
DNSHOME_SubdomainPassword Subdomain Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/3819
|
||||||
|
Author: dnsHome.de https://github.com/dnsHome-de
|
||||||
|
'
|
||||||
|
|
||||||
|
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
# Used to add txt record
|
||||||
|
dns_dnshome_add() {
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DNSHOME_Subdomain="${DNSHOME_Subdomain:-$(_readdomainconf DNSHOME_Subdomain)}"
|
||||||
|
DNSHOME_SubdomainPassword="${DNSHOME_SubdomainPassword:-$(_readdomainconf DNSHOME_SubdomainPassword)}"
|
||||||
|
|
||||||
|
if [ -z "$DNSHOME_Subdomain" ] || [ -z "$DNSHOME_SubdomainPassword" ]; then
|
||||||
|
DNSHOME_Subdomain=""
|
||||||
|
DNSHOME_SubdomainPassword=""
|
||||||
|
_err "Please specify/export your dnsHome.de Subdomain and Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_savedomainconf DNSHOME_Subdomain "$DNSHOME_Subdomain"
|
||||||
|
_savedomainconf DNSHOME_SubdomainPassword "$DNSHOME_SubdomainPassword"
|
||||||
|
|
||||||
|
DNSHOME_Api="https://$DNSHOME_Subdomain:$DNSHOME_SubdomainPassword@www.dnshome.de/dyndns.php"
|
||||||
|
|
||||||
|
_DNSHOME_rest POST "acme=add&txt=$txtvalue"
|
||||||
|
if ! echo "$response" | grep 'successfully' >/dev/null; then
|
||||||
|
_err "Error"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: txtvalue
|
||||||
|
# Used to remove the txt record after validation
|
||||||
|
dns_dnshome_rm() {
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
DNSHOME_Subdomain="${DNSHOME_Subdomain:-$(_readdomainconf DNSHOME_Subdomain)}"
|
||||||
|
DNSHOME_SubdomainPassword="${DNSHOME_SubdomainPassword:-$(_readdomainconf DNSHOME_SubdomainPassword)}"
|
||||||
|
|
||||||
|
DNSHOME_Api="https://$DNSHOME_Subdomain:$DNSHOME_SubdomainPassword@www.dnshome.de/dyndns.php"
|
||||||
|
|
||||||
|
if [ -z "$DNSHOME_Subdomain" ] || [ -z "$DNSHOME_SubdomainPassword" ]; then
|
||||||
|
DNSHOME_Subdomain=""
|
||||||
|
DNSHOME_SubdomainPassword=""
|
||||||
|
_err "Please specify/export your dnsHome.de Subdomain and Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_DNSHOME_rest POST "acme=rm&txt=$txtvalue"
|
||||||
|
if ! echo "$response" | grep 'successfully' >/dev/null; then
|
||||||
|
_err "Error"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
_DNSHOME_rest() {
|
||||||
|
method=$1
|
||||||
|
data="$2"
|
||||||
|
_debug "$data"
|
||||||
|
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(_post "$data" "$DNSHOME_Api" "" "$method")"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "error $data"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug2 response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# DNSimple domain api
|
dns_dnsimple_info='DNSimple.com
|
||||||
# https://github.com/pho3nixf1re/acme.sh/issues
|
Site: DNSimple.com
|
||||||
#
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dnsimple
|
||||||
# This is your oauth token which can be acquired on the account page. Please
|
Options:
|
||||||
# note that this must be an _account_ token and not a _user_ token.
|
DNSimple_OAUTH_TOKEN OAuth Token
|
||||||
# https://dnsimple.com/a/<your account id>/account/access_tokens
|
Issues: github.com/pho3nixf1re/acme.sh/issues
|
||||||
# DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
'
|
||||||
|
|
||||||
DNSimple_API="https://api.dnsimple.com/v2"
|
DNSimple_API="https://api.dnsimple.com/v2"
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
previous=1
|
previous=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
# not valid
|
# not valid
|
||||||
return 1
|
return 1
|
||||||
@ -105,7 +105,7 @@ _get_root() {
|
|||||||
if _contains "$response" 'not found'; then
|
if _contains "$response" 'not found'; then
|
||||||
_debug "$h not found"
|
_debug "$h not found"
|
||||||
else
|
else
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$previous)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$previous")
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
|
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
|
251
dnsapi/dns_dnsservices.sh
Executable file
251
dnsapi/dns_dnsservices.sh
Executable file
@ -0,0 +1,251 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dnsservices_info='DNS.Services
|
||||||
|
Site: DNS.Services
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dnsservices
|
||||||
|
Options:
|
||||||
|
DnsServices_Username Username
|
||||||
|
DnsServices_Password Password
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/4152
|
||||||
|
Author: Bjarke Bruun <bbruun@gmail.com>
|
||||||
|
'
|
||||||
|
|
||||||
|
DNSServices_API=https://dns.services/api
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: dns_dnsservices_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_dnsservices_add() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
_info "Using dns.services to create ACME DNS challenge"
|
||||||
|
_debug2 add_fulldomain "$fulldomain"
|
||||||
|
_debug2 add_txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
# Read username/password from environment or .acme.sh/accounts.conf
|
||||||
|
DnsServices_Username="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}"
|
||||||
|
DnsServices_Password="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}"
|
||||||
|
if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
|
||||||
|
DnsServices_Username=""
|
||||||
|
DnsServices_Password=""
|
||||||
|
_err "You didn't specify dns.services api username and password yet."
|
||||||
|
_err "Set environment variables DnsServices_Username and DnsServices_Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup GET/POST/DELETE headers
|
||||||
|
_setup_headers
|
||||||
|
|
||||||
|
#save the credentials to the account conf file.
|
||||||
|
_saveaccountconf_mutable DnsServices_Username "$DnsServices_Username"
|
||||||
|
_saveaccountconf_mutable DnsServices_Password "$DnsServices_Password"
|
||||||
|
|
||||||
|
if ! _contains "$DnsServices_Username" "@"; then
|
||||||
|
_err "It seems that the username variable DnsServices_Username has not been set/left blank"
|
||||||
|
_err "or is not a valid email. Please correct and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _get_root "${fulldomain}"; then
|
||||||
|
_err "Invalid domain ${fulldomain}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! createRecord "$fulldomain" "${txtvalue}"; then
|
||||||
|
_err "Error creating TXT record in domain $fulldomain in $rootZoneName"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 challenge-created "Created $fulldomain"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#Usage: fulldomain txtvalue
|
||||||
|
#Description: Remove the txt record after validation.
|
||||||
|
dns_dnsservices_rm() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
_info "Using dns.services to remove DNS record $fulldomain TXT $txtvalue"
|
||||||
|
_debug rm_fulldomain "$fulldomain"
|
||||||
|
_debug rm_txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
# Read username/password from environment or .acme.sh/accounts.conf
|
||||||
|
DnsServices_Username="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}"
|
||||||
|
DnsServices_Password="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}"
|
||||||
|
if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
|
||||||
|
DnsServices_Username=""
|
||||||
|
DnsServices_Password=""
|
||||||
|
_err "You didn't specify dns.services api username and password yet."
|
||||||
|
_err "Set environment variables DnsServices_Username and DnsServices_Password"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup GET/POST/DELETE headers
|
||||||
|
_setup_headers
|
||||||
|
|
||||||
|
if ! _get_root "${fulldomain}"; then
|
||||||
|
_err "Invalid domain ${fulldomain}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 rm_rootDomainInfo "found root domain $rootZoneName for $fulldomain"
|
||||||
|
|
||||||
|
if ! deleteRecord "${fulldomain}" "${txtvalue}"; then
|
||||||
|
_err "Error removing record: $fulldomain TXT ${txtvalue}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_setup_headers() {
|
||||||
|
# Set up API Headers for _get() and _post()
|
||||||
|
# The <function>_add or <function>_rm must have been called before to work
|
||||||
|
|
||||||
|
if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
|
||||||
|
_err "Could not setup BASIC authentication headers, they are missing"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DnsServiceCredentials="$(printf "%s" "$DnsServices_Username:$DnsServices_Password" | _base64)"
|
||||||
|
export _H1="Authorization: Basic $DnsServiceCredentials"
|
||||||
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
# Just return if headers are set
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_root() {
|
||||||
|
domain="$1"
|
||||||
|
_debug2 _get_root "Get the root domain of ${domain} for DNS API"
|
||||||
|
|
||||||
|
# Setup _get() and _post() headers
|
||||||
|
#_setup_headers
|
||||||
|
|
||||||
|
result=$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/dns")
|
||||||
|
result2="$(printf "%s\n" "$result" | tr '[' '\n' | grep '"name"')"
|
||||||
|
result3="$(printf "%s\n" "$result2" | tr '}' '\n' | grep '"name"' | sed "s,^\,,,g" | sed "s,$,},g")"
|
||||||
|
useResult=""
|
||||||
|
_debug2 _get_root "Got the following root domain(s) $result"
|
||||||
|
_debug2 _get_root "- JSON: $result"
|
||||||
|
|
||||||
|
if [ "$(printf "%s\n" "$result" | tr '}' '\n' | grep -c '"name"')" -gt "1" ]; then
|
||||||
|
checkMultiZones="true"
|
||||||
|
_debug2 _get_root "- multiple zones found"
|
||||||
|
else
|
||||||
|
checkMultiZones="false"
|
||||||
|
_debug2 _get_root "- single zone found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find/isolate the root zone to work with in createRecord() and deleteRecord()
|
||||||
|
rootZone=""
|
||||||
|
if [ "$checkMultiZones" = "true" ]; then
|
||||||
|
#rootZone=$(for x in $(printf "%s" "${result3}" | tr ',' '\n' | sed -n 's/.*"name":"\(.*\)",.*/\1/p'); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
|
||||||
|
rootZone=$(for x in $(printf "%s\n" "${result3}" | tr ',' '\n' | grep name | cut -d'"' -f4); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
|
||||||
|
if [ "$rootZone" != "" ]; then
|
||||||
|
_debug2 _rootZone "- root zone for $domain is $rootZone"
|
||||||
|
else
|
||||||
|
_err "Could not find root zone for $domain, is it correctly typed?"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
rootZone=$(echo "$result" | tr '}' '\n' | _egrep_o '"name":"[^"]*' | cut -d'"' -f4)
|
||||||
|
_debug2 _get_root "- only found 1 domain in API: $rootZone"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$rootZone" ]; then
|
||||||
|
_err "Could not find root domain for $domain - is it correctly typed?"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure we use the correct API zone data
|
||||||
|
useResult="$(printf "%s\n" "${result3}" tr ',' '\n' | grep "$rootZone")"
|
||||||
|
_debug2 _useResult "useResult=$useResult"
|
||||||
|
|
||||||
|
# Setup variables used by other functions to communicate with DNS.Services API
|
||||||
|
#zoneInfo=$(printf "%s\n" "$useResult" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"name":")([^"]*)"(.*)$,\2,g')
|
||||||
|
zoneInfo=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep '"name"' | cut -d'"' -f4)
|
||||||
|
rootZoneName="$rootZone"
|
||||||
|
subDomainName="$(printf "%s\n" "$domain" | sed "s,\.$rootZone,,g")"
|
||||||
|
subDomainNameClean="$(printf "%s\n" "$domain" | sed "s,_acme-challenge.,,g")"
|
||||||
|
rootZoneDomainID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep domain_id | cut -d'"' -f4)
|
||||||
|
rootZoneServiceID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep service_id | cut -d'"' -f4)
|
||||||
|
|
||||||
|
_debug2 _zoneInfo "Zone info from API : $zoneInfo"
|
||||||
|
_debug2 _get_root "Root zone name : $rootZoneName"
|
||||||
|
_debug2 _get_root "Root zone domain ID : $rootZoneDomainID"
|
||||||
|
_debug2 _get_root "Root zone service ID: $rootZoneServiceID"
|
||||||
|
_debug2 _get_root "Sub domain : $subDomainName"
|
||||||
|
|
||||||
|
_debug _get_root "Found valid root domain $rootZone for $subDomainNameClean"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
createRecord() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
# Get root domain information - needed for DNS.Services API communication
|
||||||
|
if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
|
||||||
|
_get_root "$fulldomain"
|
||||||
|
fi
|
||||||
|
if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
|
||||||
|
_err "Something happend - could not get the API zone information"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 createRecord "CNAME TXT value is: $txtvalue"
|
||||||
|
|
||||||
|
# Prepare data to send to API
|
||||||
|
data="{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"${txtvalue}\", \"ttl\":\"10\"}"
|
||||||
|
|
||||||
|
_debug2 createRecord "data to API: $data"
|
||||||
|
result=$(_post "$data" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records" "" "POST")
|
||||||
|
_debug2 createRecord "result from API: $result"
|
||||||
|
|
||||||
|
if [ "$(echo "$result" | _egrep_o "\"success\":true")" = "" ]; then
|
||||||
|
_err "Failed to create TXT record $fulldomain with content $txtvalue in zone $rootZoneName"
|
||||||
|
_err "$result"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Record \"$fulldomain TXT $txtvalue\" has been created"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteRecord() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
_log deleteRecord "Deleting $fulldomain TXT $txtvalue record"
|
||||||
|
|
||||||
|
if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
|
||||||
|
_get_root "$fulldomain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
result="$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID")"
|
||||||
|
#recordInfo="$(echo "$result" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}")"
|
||||||
|
#recordID="$(echo "$recordInfo" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"id":")([^"]*)"(.*)$,\2,g')"
|
||||||
|
recordID="$(printf "%s\n" "$result" | tr '}' '\n' | grep -- "$txtvalue" | tr ',' '\n' | grep '"id"' | cut -d'"' -f4)"
|
||||||
|
_debug2 _recordID "recordID used for deletion of record: $recordID"
|
||||||
|
|
||||||
|
if [ -z "$recordID" ]; then
|
||||||
|
_info "Record $fulldomain TXT $txtvalue not found or already deleted"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_debug2 deleteRecord "Found recordID=$recordID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 deleteRecord "DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID"
|
||||||
|
_log "curl DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID"
|
||||||
|
result="$(_H1="$_H1" _H2="$_H2" _post "" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID" "" "DELETE")"
|
||||||
|
_debug2 deleteRecord "API Delete result \"$result\""
|
||||||
|
_log "curl API Delete result \"$result\""
|
||||||
|
|
||||||
|
# Return OK regardless
|
||||||
|
return 0
|
||||||
|
}
|
148
dnsapi/dns_do.sh
148
dnsapi/dns_do.sh
@ -1,148 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
# DNS API for Domain-Offensive / Resellerinterface / Domainrobot
|
|
||||||
|
|
||||||
# Report bugs at https://github.com/seidler2547/acme.sh/issues
|
|
||||||
|
|
||||||
# set these environment variables to match your customer ID and password:
|
|
||||||
# DO_PID="KD-1234567"
|
|
||||||
# DO_PW="cdfkjl3n2"
|
|
||||||
|
|
||||||
DO_URL="https://soap.resellerinterface.de/"
|
|
||||||
|
|
||||||
######## Public functions #####################
|
|
||||||
|
|
||||||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
|
||||||
dns_do_add() {
|
|
||||||
fulldomain=$1
|
|
||||||
txtvalue=$2
|
|
||||||
if _dns_do_authenticate; then
|
|
||||||
_info "Adding TXT record to ${_domain} as ${fulldomain}"
|
|
||||||
_dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300
|
|
||||||
if _contains "${response}" '>success<'; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
_err "Could not create resource record, check logs"
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#fulldomain
|
|
||||||
dns_do_rm() {
|
|
||||||
fulldomain=$1
|
|
||||||
if _dns_do_authenticate; then
|
|
||||||
if _dns_do_list_rrs; then
|
|
||||||
_dns_do_had_error=0
|
|
||||||
for _rrid in ${_rr_list}; do
|
|
||||||
_info "Deleting resource record $_rrid for $_domain"
|
|
||||||
_dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}"
|
|
||||||
if ! _contains "${response}" '>success<'; then
|
|
||||||
_dns_do_had_error=1
|
|
||||||
_err "Could not delete resource record for ${_domain}, id ${_rrid}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
return $_dns_do_had_error
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
|
||||||
_dns_do_authenticate() {
|
|
||||||
_info "Authenticating as ${DO_PID}"
|
|
||||||
_dns_do_soap authPartner partner "${DO_PID}" password "${DO_PW}"
|
|
||||||
if _contains "${response}" '>success<'; then
|
|
||||||
_get_root "$fulldomain"
|
|
||||||
_debug "_domain $_domain"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
_err "Authentication failed, are DO_PID and DO_PW set correctly?"
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_dns_do_list_rrs() {
|
|
||||||
_dns_do_soap getRRList origin "${_domain}"
|
|
||||||
if ! _contains "${response}" 'SOAP-ENC:Array'; then
|
|
||||||
_err "getRRList origin ${_domain} failed"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_rr_list="$(echo "${response}" \
|
|
||||||
| tr -d "\n\r\t" \
|
|
||||||
| sed -e 's/<item xsi:type="ns2:Map">/\n/g' \
|
|
||||||
| grep ">$(_regexcape "$fulldomain")</value>" \
|
|
||||||
| sed -e 's/<\/item>/\n/g' \
|
|
||||||
| grep '>id</key><value' \
|
|
||||||
| _egrep_o '>[0-9]{1,16}<' \
|
|
||||||
| tr -d '><')"
|
|
||||||
[ "${_rr_list}" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
_dns_do_soap() {
|
|
||||||
func="$1"
|
|
||||||
shift
|
|
||||||
# put the parameters to xml
|
|
||||||
body="<tns:${func} xmlns:tns=\"${DO_URL}\">"
|
|
||||||
while [ "$1" ]; do
|
|
||||||
_k="$1"
|
|
||||||
shift
|
|
||||||
_v="$1"
|
|
||||||
shift
|
|
||||||
body="$body<$_k>$_v</$_k>"
|
|
||||||
done
|
|
||||||
body="$body</tns:${func}>"
|
|
||||||
_debug2 "SOAP request ${body}"
|
|
||||||
|
|
||||||
# build SOAP XML
|
|
||||||
_xml='<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
|
||||||
<env:Body>'"$body"'</env:Body>
|
|
||||||
</env:Envelope>'
|
|
||||||
|
|
||||||
# set SOAP headers
|
|
||||||
export _H1="SOAPAction: ${DO_URL}#${func}"
|
|
||||||
|
|
||||||
if ! response="$(_post "${_xml}" "${DO_URL}")"; then
|
|
||||||
_err "Error <$1>"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_debug2 "SOAP response $response"
|
|
||||||
|
|
||||||
# retrieve cookie header
|
|
||||||
_H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)"
|
|
||||||
export _H2
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
_get_root() {
|
|
||||||
domain=$1
|
|
||||||
i=1
|
|
||||||
|
|
||||||
_dns_do_soap getDomainList
|
|
||||||
_all_domains="$(echo "${response}" \
|
|
||||||
| tr -d "\n\r\t " \
|
|
||||||
| _egrep_o 'domain</key><value[^>]+>[^<]+' \
|
|
||||||
| sed -e 's/^domain<\/key><value[^>]*>//g')"
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
|
||||||
if [ -z "$h" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then
|
|
||||||
_domain="$h"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
i=$(_math $i + 1)
|
|
||||||
done
|
|
||||||
_debug "$domain not found"
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_regexcape() {
|
|
||||||
echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g'
|
|
||||||
}
|
|
@ -1,14 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_doapi_info='Domain-Offensive do.de
|
||||||
|
Official LetsEncrypt API for do.de / Domain-Offensive.
|
||||||
|
This API is also available to private customers/individuals.
|
||||||
|
Site: do.de
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_doapi
|
||||||
|
Options:
|
||||||
|
DO_LETOKEN LetsEncrypt Token
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2057
|
||||||
|
'
|
||||||
|
|
||||||
# Official Let's Encrypt API for do.de / Domain-Offensive
|
DO_API="https://my.do.de/api/letsencrypt"
|
||||||
#
|
|
||||||
# This is different from the dns_do adapter, because dns_do is only usable for enterprise customers
|
|
||||||
# This API is also available to private customers/individuals
|
|
||||||
#
|
|
||||||
# Provide the required LetsEncrypt token like this:
|
|
||||||
# DO_LETOKEN="FmD408PdqT1E269gUK57"
|
|
||||||
|
|
||||||
DO_API="https://www.do.de/api/letsencrypt"
|
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_domeneshop_info='DomeneShop.no
|
||||||
|
Site: DomeneShop.no
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_domeneshop
|
||||||
|
Options:
|
||||||
|
DOMENESHOP_Token Token
|
||||||
|
DOMENESHOP_Secret Secret
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2457
|
||||||
|
'
|
||||||
|
|
||||||
DOMENESHOP_Api_Endpoint="https://api.domeneshop.no/v0"
|
DOMENESHOP_Api_Endpoint="https://api.domeneshop.no/v0"
|
||||||
|
|
||||||
@ -84,7 +93,7 @@ _get_domainid() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug "h" "$h"
|
_debug "h" "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -93,7 +102,7 @@ _get_domainid() {
|
|||||||
|
|
||||||
if _contains "$response" "\"$h\"" >/dev/null; then
|
if _contains "$response" "\"$h\"" >/dev/null; then
|
||||||
# We have found the domain name.
|
# We have found the domain name.
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
_domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
|
_domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
|
||||||
return 0
|
return 0
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# Dnspod.cn Domain api
|
dns_dp_info='DNSPod.cn
|
||||||
#
|
Site: DNSPod.cn
|
||||||
#DP_Id="1234"
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dp
|
||||||
#
|
Options:
|
||||||
#DP_Key="sADDsdasdgdsf"
|
DP_Id Id
|
||||||
|
DP_Key Key
|
||||||
|
'
|
||||||
|
|
||||||
REST_API="https://dnsapi.cn"
|
REST_API="https://dnsapi.cn"
|
||||||
|
|
||||||
@ -53,7 +55,7 @@ dns_dp_rm() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
||||||
_err "Record.Lis error."
|
_err "Record.Lis error."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -70,12 +72,12 @@ dns_dp_rm() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _rest POST "Record.Remove" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
|
if ! _rest POST "Record.Remove" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&record_id=$record_id"; then
|
||||||
_err "Record.Remove error."
|
_err "Record.Remove error."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_contains "$response" "Action completed successful"
|
_contains "$response" "successful"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,11 +91,11 @@ add_record() {
|
|||||||
|
|
||||||
_info "Adding record"
|
_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
|
if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=%E9%BB%98%E8%AE%A4"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists"
|
_contains "$response" "successful" || _contains "$response" "Domain record already exists"
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
@ -107,21 +109,21 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&domain=$h"; then
|
if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&lang=en&domain=$h"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "Action completed successful"; then
|
if _contains "$response" "successful"; then
|
||||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
# Dnspod.com Domain api
|
dns_dpi_info='DNSPod.com
|
||||||
#
|
Site: DNSPod.com
|
||||||
#DPI_Id="1234"
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dpi
|
||||||
#
|
Options:
|
||||||
#DPI_Key="sADDsdasdgdsf"
|
DPI_Id Id
|
||||||
|
DPI_Key Key
|
||||||
|
'
|
||||||
|
|
||||||
REST_API="https://api.dnspod.com"
|
REST_API="https://api.dnspod.com"
|
||||||
|
|
||||||
@ -53,7 +55,7 @@ dns_dpi_rm() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _rest POST "Record.List" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
if ! _rest POST "Record.List" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
||||||
_err "Record.Lis error."
|
_err "Record.Lis error."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -63,19 +65,19 @@ dns_dpi_rm() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \")
|
record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2)
|
||||||
_debug record_id "$record_id"
|
_debug record_id "$record_id"
|
||||||
if [ -z "$record_id" ]; then
|
if [ -z "$record_id" ]; then
|
||||||
_err "Can not get record id."
|
_err "Can not get record id."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _rest POST "Record.Remove" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
|
if ! _rest POST "Record.Remove" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
|
||||||
_err "Record.Remove error."
|
_err "Record.Remove error."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_contains "$response" "Action completed successful"
|
_contains "$response" "Operation successful"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,11 +91,11 @@ add_record() {
|
|||||||
|
|
||||||
_info "Adding record"
|
_info "Adding record"
|
||||||
|
|
||||||
if ! _rest POST "Record.Create" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=default"; then
|
if ! _rest POST "Record.Create" "login_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=default"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists"
|
_contains "$response" "Operation successful" || _contains "$response" "Domain record already exists"
|
||||||
}
|
}
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
@ -107,21 +109,21 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _rest POST "Domain.Info" "user_token=$DPI_Id,$DPI_Key&format=json&domain=$h"; then
|
if ! _rest POST "Domain.Info" "login_token=$DPI_Id,$DPI_Key&format=json&domain=$h"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "Action completed successful"; then
|
if _contains "$response" "Operation successful"; then
|
||||||
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
|
||||||
_debug _domain_id "$_domain_id"
|
_debug _domain_id "$_domain_id"
|
||||||
if [ "$_domain_id" ]; then
|
if [ "$_domain_id" ]; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_debug _sub_domain "$_sub_domain"
|
_debug _sub_domain "$_sub_domain"
|
||||||
_domain="$h"
|
_domain="$h"
|
||||||
_debug _domain "$_domain"
|
_debug _domain "$_domain"
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dreamhost_info='DreamHost.com
|
||||||
|
Site: DreamHost.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dreamhost
|
||||||
|
Options:
|
||||||
|
DH_API_KEY API Key
|
||||||
|
Issues: github.com/RhinoLance/acme.sh
|
||||||
|
Author: RhinoLance
|
||||||
|
'
|
||||||
|
|
||||||
#Author: RhinoLance
|
|
||||||
#Report Bugs here: https://github.com/RhinoLance/acme.sh
|
|
||||||
#
|
|
||||||
|
|
||||||
#define the api endpoint
|
|
||||||
DH_API_ENDPOINT="https://api.dreamhost.com/"
|
DH_API_ENDPOINT="https://api.dreamhost.com/"
|
||||||
querystring=""
|
querystring=""
|
||||||
|
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#Created by RaidenII, to use DuckDNS's API to add/remove text records
|
dns_duckdns_info='DuckDNS.org
|
||||||
#06/27/2017
|
Site: www.DuckDNS.org
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_duckdns
|
||||||
# Pass credentials before "acme.sh --issue --dns dns_duckdns ..."
|
Options:
|
||||||
# --
|
DuckDNS_Token API Token
|
||||||
# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
Author: RaidenII
|
||||||
# --
|
'
|
||||||
#
|
|
||||||
# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh
|
|
||||||
|
|
||||||
DuckDNS_API="https://www.duckdns.org/update"
|
DuckDNS_API="https://www.duckdns.org/update"
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions ######################
|
||||||
|
|
||||||
#Usage: dns_duckdns_add _acme-challenge.domain.duckdns.org "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_duckdns_add _acme-challenge.domain.duckdns.org "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
dns_duckdns_add() {
|
dns_duckdns_add() {
|
||||||
@ -91,13 +89,12 @@ dns_duckdns_rm() {
|
|||||||
|
|
||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
#fulldomain=_acme-challenge.domain.duckdns.org
|
# fulldomain may be 'domain.duckdns.org' (if using --domain-alias) or '_acme-challenge.domain.duckdns.org'
|
||||||
#returns
|
# either way, return 'domain'. (duckdns does not allow further subdomains and restricts domains to [a-z0-9-].)
|
||||||
# _duckdns_domain=domain
|
|
||||||
_duckdns_get_domain() {
|
_duckdns_get_domain() {
|
||||||
|
|
||||||
# We'll extract the domain/username from full domain
|
# We'll extract the domain/username from full domain
|
||||||
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '[.][^.][^.]*[.]duckdns.org' | cut -d . -f 2)"
|
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '^(_acme-challenge\.)?([a-z0-9-]+\.)+duckdns\.org' | sed -n 's/^\([^.]\{1,\}\.\)*\([a-z0-9-]\{1,\}\)\.duckdns\.org$/\2/p;')"
|
||||||
|
|
||||||
if [ -z "$_duckdns_domain" ]; then
|
if [ -z "$_duckdns_domain" ]; then
|
||||||
_err "Error extracting the domain."
|
_err "Error extracting the domain."
|
||||||
@ -113,16 +110,21 @@ _duckdns_rest() {
|
|||||||
param="$2"
|
param="$2"
|
||||||
_debug param "$param"
|
_debug param "$param"
|
||||||
url="$DuckDNS_API?$param"
|
url="$DuckDNS_API?$param"
|
||||||
|
if [ -n "$DEBUG" ] && [ "$DEBUG" -gt 0 ]; then
|
||||||
|
url="$url&verbose=true"
|
||||||
|
fi
|
||||||
_debug url "$url"
|
_debug url "$url"
|
||||||
|
|
||||||
# DuckDNS uses GET to update domain info
|
# DuckDNS uses GET to update domain info
|
||||||
if [ "$method" = "GET" ]; then
|
if [ "$method" = "GET" ]; then
|
||||||
response="$(_get "$url")"
|
response="$(_get "$url")"
|
||||||
|
_debug2 response "$response"
|
||||||
|
if [ -n "$DEBUG" ] && [ "$DEBUG" -gt 0 ] && _contains "$response" "UPDATED" && _contains "$response" "OK"; then
|
||||||
|
response="OK"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
_err "Unsupported method"
|
_err "Unsupported method"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_debug2 response "$response"
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
#DD_API_User="xxxxx"
|
dns_durabledns_info='DurableDNS.com
|
||||||
#DD_API_Key="xxxxxx"
|
Site: DurableDNS.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_durabledns
|
||||||
|
Options:
|
||||||
|
DD_API_User API User
|
||||||
|
DD_API_Key API Key
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/2281
|
||||||
|
'
|
||||||
|
|
||||||
_DD_BASE="https://durabledns.com/services/dns"
|
_DD_BASE="https://durabledns.com/services/dns"
|
||||||
|
|
||||||
@ -104,7 +110,7 @@ _get_root() {
|
|||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -112,7 +118,7 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" ">$h.</origin>"; then
|
if _contains "$response" ">$h.</origin>"; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain=$h
|
_domain=$h
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
#
|
# shellcheck disable=SC2034
|
||||||
# Dyn.com Domain API
|
dns_dyn_info='Dyn.com
|
||||||
#
|
Domains: dynect.net
|
||||||
# Author: Gerd Naschenweng
|
Site: Dyn.com
|
||||||
# https://github.com/magicdude4eva
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dyn
|
||||||
#
|
Options:
|
||||||
|
DYN_Customer Customer
|
||||||
|
DYN_Username API Username
|
||||||
|
DYN_Password Secret
|
||||||
|
Author: Gerd Naschenweng <https://github.com/magicdude4eva>
|
||||||
|
'
|
||||||
|
|
||||||
# Dyn Managed DNS API
|
# Dyn Managed DNS API
|
||||||
# https://help.dyn.com/dns-api-knowledge-base/
|
# https://help.dyn.com/dns-api-knowledge-base/
|
||||||
#
|
#
|
||||||
@ -20,13 +26,6 @@
|
|||||||
# ZoneRemoveNode
|
# ZoneRemoveNode
|
||||||
# ZonePublish
|
# ZonePublish
|
||||||
# --
|
# --
|
||||||
#
|
|
||||||
# Pass credentials before "acme.sh --issue --dns dns_dyn ..."
|
|
||||||
# --
|
|
||||||
# export DYN_Customer="customer"
|
|
||||||
# export DYN_Username="apiuser"
|
|
||||||
# export DYN_Password="secret"
|
|
||||||
# --
|
|
||||||
|
|
||||||
DYN_API="https://api.dynect.net/REST"
|
DYN_API="https://api.dynect.net/REST"
|
||||||
|
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_dynu_info='Dynu.com
|
||||||
|
Site: Dynu.com
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dynu
|
||||||
|
Options:
|
||||||
|
Dynu_ClientId Client ID
|
||||||
|
Dynu_Secret Secret
|
||||||
|
Issues: github.com/shar0119/acme.sh
|
||||||
|
Author: Dynu Systems Inc
|
||||||
|
'
|
||||||
|
|
||||||
#Client ID
|
|
||||||
#Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d"
|
|
||||||
#
|
|
||||||
#Secret
|
|
||||||
#Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9"
|
|
||||||
#
|
|
||||||
#Token
|
#Token
|
||||||
Dynu_Token=""
|
Dynu_Token=""
|
||||||
#
|
#
|
||||||
#Endpoint
|
#Endpoint
|
||||||
Dynu_EndPoint="https://api.dynu.com/v2"
|
Dynu_EndPoint="https://api.dynu.com/v2"
|
||||||
#
|
|
||||||
#Author: Dynu Systems, Inc.
|
|
||||||
#Report Bugs here: https://github.com/shar0119/acme.sh
|
|
||||||
#
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
|
|
||||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
@ -125,7 +126,7 @@ _get_root() {
|
|||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
#not valid
|
||||||
@ -139,7 +140,7 @@ _get_root() {
|
|||||||
if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
|
if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
|
||||||
dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
|
dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
|
||||||
_domain_name=$h
|
_domain_name=$h
|
||||||
_node=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_node=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
p=$i
|
p=$i
|
||||||
@ -216,6 +217,10 @@ _dynu_authentication() {
|
|||||||
_err "Authentication failed."
|
_err "Authentication failed."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
if _contains "$response" "Authentication Exception"; then
|
||||||
|
_err "Authentication failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
if _contains "$response" "access_token"; then
|
if _contains "$response" "access_token"; then
|
||||||
Dynu_Token=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2)
|
Dynu_Token=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2)
|
||||||
fi
|
fi
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user