mirror of
				https://github.com/acmesh-official/acme.sh
				synced 2025-11-04 05:46:05 +08:00 
			
		
		
		
	Compare commits
	
		
			1064 Commits
		
	
	
		
			v3.0.5
			...
			08246f7005
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					08246f7005 | ||
| 
						 | 
					c377ec69d3 | ||
| 
						 | 
					a2c2b7ffee | ||
| 
						 | 
					25c564bae1 | ||
| 
						 | 
					15197455f2 | ||
| 
						 | 
					a5754e9ec4 | ||
| 
						 | 
					2584f09083 | ||
| 
						 | 
					d439933b52 | ||
| 
						 | 
					094d03bf7a | ||
| 
						 | 
					0f5093c0b7 | ||
| 
						 | 
					80748b9fe0 | ||
| 
						 | 
					b244c76dd5 | ||
| 
						 | 
					a91ab54449 | ||
| 
						 | 
					f2dbf56db1 | ||
| 
						 | 
					e5214ea2e5 | ||
| 
						 | 
					11995b958a | ||
| 
						 | 
					493ec4be52 | ||
| 
						 | 
					604e6873ba | ||
| 
						 | 
					5954f0dde5 | ||
| 
						 | 
					b8e91036dd | ||
| 
						 | 
					f22b490a10 | ||
| 
						 | 
					49ed112ef5 | ||
| 
						 | 
					471e0c05f9 | ||
| 
						 | 
					c3ec827fdd | ||
| 
						 | 
					ca7bdd9101 | ||
| 
						 | 
					070cd0f4df | ||
| 
						 | 
					1b00ced7ad | ||
| 
						 | 
					44c7473ef9 | ||
| 
						 | 
					20c80f553f | ||
| 
						 | 
					b4a5149ba2 | ||
| 
						 | 
					df350e6660 | ||
| 
						 | 
					d76f4b27b0 | ||
| 
						 | 
					a1ea2a5aa6 | ||
| 
						 | 
					8608e9cd3a | ||
| 
						 | 
					5f8f7ee576 | ||
| 
						 | 
					03dd318265 | ||
| 
						 | 
					fc3bfda3bd | ||
| 
						 | 
					c4d228ad8d | ||
| 
						 | 
					30faf500eb | ||
| 
						 | 
					26d4bac85f | ||
| 
						 | 
					e0e3cdc316 | ||
| 
						 | 
					1deb52f86d | ||
| 
						 | 
					39cb87dc4b | ||
| 
						 | 
					04e2549239 | ||
| 
						 | 
					e06cdbf0ac | ||
| 
						 | 
					d366b7e4fc | ||
| 
						 | 
					5aae3333bc | ||
| 
						 | 
					6d40ac8644 | ||
| 
						 | 
					d7c428fc8d | ||
| 
						 | 
					28f8f56fa3 | ||
| 
						 | 
					b1f6b5314c | ||
| 
						 | 
					fdb1e8c2e4 | ||
| 
						 | 
					5b02e86334 | ||
| 
						 | 
					10627e4974 | ||
| 
						 | 
					1b89e7c2ec | ||
| 
						 | 
					bcf0afb25e | ||
| 
						 | 
					ab22c8ca1c | ||
| 
						 | 
					49513cb2ee | ||
| 
						 | 
					1b5e66f9c2 | ||
| 
						 | 
					1f486fc9a5 | ||
| 
						 | 
					4cdbdf2125 | ||
| 
						 | 
					ec5ecabdec | ||
| 
						 | 
					e391301348 | ||
| 
						 | 
					49866b6cf4 | ||
| 
						 | 
					09d326de3a | ||
| 
						 | 
					8cac773e34 | ||
| 
						 | 
					365ec1fd5e | ||
| 
						 | 
					40e58ed12d | ||
| 
						 | 
					0c98dc54fe | ||
| 
						 | 
					3b0f624302 | ||
| 
						 | 
					56242423b2 | ||
| 
						 | 
					8113711b7a | ||
| 
						 | 
					85ec6343ff | ||
| 
						 | 
					daf183e2cc | ||
| 
						 | 
					c6819cbd6b | ||
| 
						 | 
					01ed3c3326 | ||
| 
						 | 
					76b68f7ccb | ||
| 
						 | 
					2bea808251 | ||
| 
						 | 
					52e051bb02 | ||
| 
						 | 
					74fdf649d3 | ||
| 
						 | 
					2402f463e1 | ||
| 
						 | 
					424d33faa0 | ||
| 
						 | 
					b64c57fa36 | ||
| 
						 | 
					6966b3810d | ||
| 
						 | 
					02cb4764c2 | ||
| 
						 | 
					89071f7226 | ||
| 
						 | 
					d6eca62e56 | ||
| 
						 | 
					b025e7f0f2 | ||
| 
						 | 
					0f7ecad4e8 | ||
| 
						 | 
					4a16aaacb6 | ||
| 
						 | 
					21c758c3d3 | ||
| 
						 | 
					c338f1a3fa | ||
| 
						 | 
					242085d676 | ||
| 
						 | 
					289d895423 | ||
| 
						 | 
					f2b248243c | ||
| 
						 | 
					fcd358eb71 | ||
| 
						 | 
					06d3739a8d | ||
| 
						 | 
					bff1064dbd | ||
| 
						 | 
					19678db933 | ||
| 
						 | 
					3d8b682380 | ||
| 
						 | 
					7b76502ee6 | ||
| 
						 | 
					28687ad7c7 | ||
| 
						 | 
					d420ff0e56 | ||
| 
						 | 
					ed1adfd878 | ||
| 
						 | 
					58a7b9ffa7 | ||
| 
						 | 
					af5d046698 | ||
| 
						 | 
					9e75b707ce | ||
| 
						 | 
					8241b078ce | ||
| 
						 | 
					c8f1e41197 | ||
| 
						 | 
					cddf098f47 | ||
| 
						 | 
					500cfbc19c | ||
| 
						 | 
					133ae8555a | ||
| 
						 | 
					f132010acb | ||
| 
						 | 
					426305dc33 | ||
| 
						 | 
					55282851c4 | ||
| 
						 | 
					9e7d1b9ce7 | ||
| 
						 | 
					e0da5f1703 | ||
| 
						 | 
					4f5a70b804 | ||
| 
						 | 
					42bbd1b44a | ||
| 
						 | 
					fdeaf861e3 | ||
| 
						 | 
					8e032a27c4 | ||
| 
						 | 
					5bfd43b718 | ||
| 
						 | 
					ce7f4cb9a8 | ||
| 
						 | 
					99a4cf9e07 | ||
| 
						 | 
					b82f6801cd | ||
| 
						 | 
					184cb0b9a8 | ||
| 
						 | 
					bf2e99efa6 | ||
| 
						 | 
					7543d5220c | ||
| 
						 | 
					ca73e1f024 | ||
| 
						 | 
					c2ccc1f980 | ||
| 
						 | 
					f34d4bd6da | ||
| 
						 | 
					8a4b436314 | ||
| 
						 | 
					42aaf7c2a0 | ||
| 
						 | 
					eb00852a71 | ||
| 
						 | 
					b0535d8b40 | ||
| 
						 | 
					8b4d93cc14 | ||
| 
						 | 
					e2d0923122 | ||
| 
						 | 
					2928d84339 | ||
| 
						 | 
					b5e3883891 | ||
| 
						 | 
					d01aefd1eb | ||
| 
						 | 
					e1d447847f | ||
| 
						 | 
					24a1b93842 | ||
| 
						 | 
					bed1c311e3 | ||
| 
						 | 
					1ce8d3ae9b | ||
| 
						 | 
					676d76696b | ||
| 
						 | 
					1d18432f8d | ||
| 
						 | 
					277f13ff9d | ||
| 
						 | 
					2d365efe56 | ||
| 
						 | 
					e81635f23d | ||
| 
						 | 
					827315e059 | ||
| 
						 | 
					e55a54f3d4 | ||
| 
						 | 
					5e8b40faf6 | ||
| 
						 | 
					45b9982172 | ||
| 
						 | 
					9b267bb572 | ||
| 
						 | 
					20c7bfaff2 | ||
| 
						 | 
					dd29f970a2 | ||
| 
						 | 
					b6a9a1a7ee | ||
| 
						 | 
					1d87d9b370 | ||
| 
						 | 
					40b6db6a27 | ||
| 
						 | 
					9ba662a53e | ||
| 
						 | 
					dc228c3dbb | ||
| 
						 | 
					dd5dd422d1 | ||
| 
						 | 
					73fdf4b957 | ||
| 
						 | 
					a1de13657e | ||
| 
						 | 
					93c19356d0 | ||
| 
						 | 
					8a27c902ff | ||
| 
						 | 
					be35b55444 | ||
| 
						 | 
					a16ccf40c2 | ||
| 
						 | 
					5c7b1cd5f2 | ||
| 
						 | 
					fc5e593e2d | ||
| 
						 | 
					f1dc79f0b5 | ||
| 
						 | 
					34ea97dcea | ||
| 
						 | 
					f2931163aa | ||
| 
						 | 
					7e320d2748 | ||
| 
						 | 
					b38d15adb3 | ||
| 
						 | 
					29342e036f | ||
| 
						 | 
					45660902ed | ||
| 
						 | 
					72318cb687 | ||
| 
						 | 
					f277223a65 | ||
| 
						 | 
					262fa74f5e | ||
| 
						 | 
					ebdd221c72 | ||
| 
						 | 
					2b8d162c9e | ||
| 
						 | 
					dc8cf0e02e | ||
| 
						 | 
					a054f6a97b | ||
| 
						 | 
					63d11c3eea | ||
| 
						 | 
					dd2199c472 | ||
| 
						 | 
					661c3bbdb0 | ||
| 
						 | 
					5236e81068 | ||
| 
						 | 
					c459b5eb9d | ||
| 
						 | 
					384f0fb89a | ||
| 
						 | 
					1d628bcf9e | ||
| 
						 | 
					b8644f49ec | ||
| 
						 | 
					091c1f7ef1 | ||
| 
						 | 
					c838c3fa73 | ||
| 
						 | 
					8bb9106b42 | ||
| 
						 | 
					a7e3b0d184 | ||
| 
						 | 
					449c542d5a | ||
| 
						 | 
					7f19f8cf0e | ||
| 
						 | 
					b2eb704394 | ||
| 
						 | 
					899da0c6ea | ||
| 
						 | 
					def41a7878 | ||
| 
						 | 
					6dbe49089e | ||
| 
						 | 
					5883e5159f | ||
| 
						 | 
					26a5d48f63 | ||
| 
						 | 
					aa6feb4b62 | ||
| 
						 | 
					2fef0ebea8 | ||
| 
						 | 
					66dd51a09a | ||
| 
						 | 
					077868604d | ||
| 
						 | 
					6c555cb666 | ||
| 
						 | 
					0ae80272fb | ||
| 
						 | 
					7bef7cf9e2 | ||
| 
						 | 
					6fa24cad18 | ||
| 
						 | 
					6313020ba9 | ||
| 
						 | 
					b2eead386d | ||
| 
						 | 
					b826123d98 | ||
| 
						 | 
					cf537070d8 | ||
| 
						 | 
					7a00a1dced | ||
| 
						 | 
					80c37ebab2 | ||
| 
						 | 
					5fb4136bcd | ||
| 
						 | 
					6a249ca1dd | ||
| 
						 | 
					5610d4782f | ||
| 
						 | 
					fef4574c3c | ||
| 
						 | 
					65239c7c25 | ||
| 
						 | 
					1b123054b3 | ||
| 
						 | 
					b29d17f522 | ||
| 
						 | 
					04aefbf28d | ||
| 
						 | 
					f8fcf02448 | ||
| 
						 | 
					0e4fd5269b | ||
| 
						 | 
					6328496bfb | ||
| 
						 | 
					524b40b75f | ||
| 
						 | 
					0241552c0d | ||
| 
						 | 
					fd3f6687ce | ||
| 
						 | 
					93d63749e6 | ||
| 
						 | 
					41bf5450d9 | ||
| 
						 | 
					d37553e7b8 | ||
| 
						 | 
					101d7138c2 | ||
| 
						 | 
					11de3aed51 | ||
| 
						 | 
					4bb41cf886 | ||
| 
						 | 
					1ae7dd9b11 | ||
| 
						 | 
					a3250fac6d | ||
| 
						 | 
					fabe6317ef | ||
| 
						 | 
					23a4af9a52 | ||
| 
						 | 
					9526dbadad | ||
| 
						 | 
					6ad469c637 | ||
| 
						 | 
					b30cdf0873 | ||
| 
						 | 
					234bc93ddb | ||
| 
						 | 
					42862852b8 | ||
| 
						 | 
					7a6101c417 | ||
| 
						 | 
					588123ed11 | ||
| 
						 | 
					ac9852f9df | ||
| 
						 | 
					d093476da5 | ||
| 
						 | 
					9ad794f2cc | ||
| 
						 | 
					b0f566a80d | ||
| 
						 | 
					d3f3a70350 | ||
| 
						 | 
					3f262fe872 | ||
| 
						 | 
					150c708726 | ||
| 
						 | 
					7512dbffbb | ||
| 
						 | 
					3cfa882fe1 | ||
| 
						 | 
					35f3b7088d | ||
| 
						 | 
					07220a324d | ||
| 
						 | 
					c7cecd5b4f | ||
| 
						 | 
					ab1a2045d9 | ||
| 
						 | 
					5e9a067e87 | ||
| 
						 | 
					4cc460be64 | ||
| 
						 | 
					112bfccfd5 | ||
| 
						 | 
					b29cb08956 | ||
| 
						 | 
					e42ad9be24 | ||
| 
						 | 
					02da1700e0 | ||
| 
						 | 
					a9f97e1fe2 | ||
| 
						 | 
					d75077c6f9 | ||
| 
						 | 
					b81939f02d | ||
| 
						 | 
					0e1d90dd0c | ||
| 
						 | 
					03e9c612b9 | ||
| 
						 | 
					f981c782bb | ||
| 
						 | 
					3fbdb7a007 | ||
| 
						 | 
					2997a15ba8 | ||
| 
						 | 
					b4c02eca16 | ||
| 
						 | 
					223dc87cef | ||
| 
						 | 
					3ccdae0634 | ||
| 
						 | 
					11af6f49c6 | ||
| 
						 | 
					22dfedf6ae | ||
| 
						 | 
					157d3f9dc9 | ||
| 
						 | 
					f3b2ab5e60 | ||
| 
						 | 
					fca53a0c2c | ||
| 
						 | 
					6d302889fa | ||
| 
						 | 
					a94ac63e74 | ||
| 
						 | 
					19e73f8889 | ||
| 
						 | 
					aacc26179e | ||
| 
						 | 
					36f060dcf6 | ||
| 
						 | 
					cb12c8dfd5 | ||
| 
						 | 
					9cd1d1a9dc | ||
| 
						 | 
					ebaf4c9c01 | ||
| 
						 | 
					44240339d9 | ||
| 
						 | 
					9110375173 | ||
| 
						 | 
					c3557bbe3f | ||
| 
						 | 
					cd924099e4 | ||
| 
						 | 
					f2a311bb81 | ||
| 
						 | 
					d7855e8fe5 | ||
| 
						 | 
					1e52efa867 | ||
| 
						 | 
					5d6f1bd2d7 | ||
| 
						 | 
					48ccaa8b91 | ||
| 
						 | 
					54ac0048c4 | ||
| 
						 | 
					276e089419 | ||
| 
						 | 
					3b8608761b | ||
| 
						 | 
					413a91646c | ||
| 
						 | 
					4299c6a802 | ||
| 
						 | 
					4232923641 | ||
| 
						 | 
					75b4bb306b | ||
| 
						 | 
					4e0686f73c | ||
| 
						 | 
					709f1e76d3 | ||
| 
						 | 
					0c2d7b9c06 | ||
| 
						 | 
					412e14a41c | ||
| 
						 | 
					532b425dd9 | ||
| 
						 | 
					2bb5fbdee5 | ||
| 
						 | 
					38c41b72d6 | ||
| 
						 | 
					30e89c3cdc | ||
| 
						 | 
					b665014707 | ||
| 
						 | 
					e98e7a232f | ||
| 
						 | 
					5f7ad72d45 | ||
| 
						 | 
					9c25365b12 | ||
| 
						 | 
					0cc74b7cfe | ||
| 
						 | 
					8a972ea213 | ||
| 
						 | 
					2229bcc98b | ||
| 
						 | 
					1bfd4672e1 | ||
| 
						 | 
					15e9e8575a | ||
| 
						 | 
					d7bafa6dfa | ||
| 
						 | 
					35632f2109 | ||
| 
						 | 
					08807b498e | ||
| 
						 | 
					e8a453c567 | ||
| 
						 | 
					717802611a | ||
| 
						 | 
					b6a77e0231 | ||
| 
						 | 
					21b966c8e6 | ||
| 
						 | 
					64a1e88982 | ||
| 
						 | 
					34c8b882c6 | ||
| 
						 | 
					fa420d70cc | ||
| 
						 | 
					2c67934191 | ||
| 
						 | 
					821adcf178 | ||
| 
						 | 
					8bf9482bc0 | ||
| 
						 | 
					fe8ad3548b | ||
| 
						 | 
					4f17bc0d86 | ||
| 
						 | 
					1a43c81840 | ||
| 
						 | 
					d3cf3f7a5c | ||
| 
						 | 
					4f96a2a667 | ||
| 
						 | 
					7b63ebfcaa | ||
| 
						 | 
					724f3aa301 | ||
| 
						 | 
					1ff326c89c | ||
| 
						 | 
					2663f500cf | ||
| 
						 | 
					43ed998ed6 | ||
| 
						 | 
					a3032ab945 | ||
| 
						 | 
					af9425ed8f | ||
| 
						 | 
					d058ac6174 | ||
| 
						 | 
					a4e7806d21 | ||
| 
						 | 
					03906cc055 | ||
| 
						 | 
					10833dcf39 | ||
| 
						 | 
					a00323412b | ||
| 
						 | 
					838a20ea95 | ||
| 
						 | 
					e8ed8cbfcd | ||
| 
						 | 
					5c78a5e4fe | ||
| 
						 | 
					30ed4af38d | ||
| 
						 | 
					6a9304dd1c | ||
| 
						 | 
					1782eeb785 | ||
| 
						 | 
					1aabb7d6de | ||
| 
						 | 
					d4b8f9700b | ||
| 
						 | 
					2ebecf1aa0 | ||
| 
						 | 
					7031df4948 | ||
| 
						 | 
					e0381dd757 | ||
| 
						 | 
					9b2eae24d2 | ||
| 
						 | 
					7362e8de4d | ||
| 
						 | 
					87beb0a5f2 | ||
| 
						 | 
					fc69cea4f7 | ||
| 
						 | 
					ad4780a1ac | ||
| 
						 | 
					72f487d2e3 | ||
| 
						 | 
					0c10bd7dd7 | ||
| 
						 | 
					b0418cb394 | ||
| 
						 | 
					a30c81dadc | ||
| 
						 | 
					40df6e87db | ||
| 
						 | 
					0dff1dce8f | ||
| 
						 | 
					ffd3b3e6b5 | ||
| 
						 | 
					1305b0d8dd | ||
| 
						 | 
					5fc41a3ea2 | ||
| 
						 | 
					fc87fc7cab | ||
| 
						 | 
					8887a0a6d3 | ||
| 
						 | 
					ba7764aeaf | ||
| 
						 | 
					28afe6f29f | ||
| 
						 | 
					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 | ||
| 
						 | 
					1029dd3504 | ||
| 
						 | 
					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 | ||
| 
						 | 
					8860915fb9 | ||
| 
						 | 
					f0d486d1ff | ||
| 
						 | 
					0fa20da990 | ||
| 
						 | 
					b27767e8f4 | ||
| 
						 | 
					0b2edd28df | ||
| 
						 | 
					dd634382d7 | ||
| 
						 | 
					c7fb155733 | ||
| 
						 | 
					1eaa2cc619 | ||
| 
						 | 
					92a47aaac5 | ||
| 
						 | 
					fcffe8beb9 | ||
| 
						 | 
					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 | ||
| 
						 | 
					833632eee3 | ||
| 
						 | 
					abc76299c0 | ||
| 
						 | 
					c96fcf319a | ||
| 
						 | 
					5214a7c3ec | ||
| 
						 | 
					1700f064b3 | ||
| 
						 | 
					de902166a8 | ||
| 
						 | 
					2f08bd1965 | ||
| 
						 | 
					13c68cd799 | ||
| 
						 | 
					c7d78f4594 | ||
| 
						 | 
					2f1ca949f0 | ||
| 
						 | 
					a6488ff9ac | ||
| 
						 | 
					cb5eae888d | ||
| 
						 | 
					b6f7710621 | ||
| 
						 | 
					3e36f05a8c | ||
| 
						 | 
					bcb7e5f2c8 | ||
| 
						 | 
					3cefcd8204 | ||
| 
						 | 
					4cd1871816 | ||
| 
						 | 
					7aaa9583fa | ||
| 
						 | 
					dd582c0306 | ||
| 
						 | 
					5cded5b53e | ||
| 
						 | 
					0a64567822 | ||
| 
						 | 
					7ca861805d | ||
| 
						 | 
					6b6faa8129 | ||
| 
						 | 
					f84577bcda | ||
| 
						 | 
					37f9fd3498 | ||
| 
						 | 
					11aef82993 | ||
| 
						 | 
					9861e2d724 | ||
| 
						 | 
					7ae0d0caa3 | ||
| 
						 | 
					2f5ea120cb | ||
| 
						 | 
					93d2c9a3f0 | ||
| 
						 | 
					60569fdd83 | ||
| 
						 | 
					0f61e9c15e | ||
| 
						 | 
					d81fc155cb | ||
| 
						 | 
					60f7750d77 | ||
| 
						 | 
					9bdfd8f4fe | ||
| 
						 | 
					945b7de76f | ||
| 
						 | 
					3c35eadbc4 | ||
| 
						 | 
					ab86e056a2 | ||
| 
						 | 
					a2bc79ddd5 | ||
| 
						 | 
					51151293d7 | ||
| 
						 | 
					5789e80d74 | ||
| 
						 | 
					2beb2f5659 | ||
| 
						 | 
					65868ab8a7 | ||
| 
						 | 
					89586530a5 | ||
| 
						 | 
					0d93145834 | ||
| 
						 | 
					617f4acfd6 | ||
| 
						 | 
					fe4113d623 | ||
| 
						 | 
					9a0e15cced | ||
| 
						 | 
					e0c63d58b2 | ||
| 
						 | 
					f7f8ea9b97 | ||
| 
						 | 
					58cad98cd8 | ||
| 
						 | 
					2f8fb360aa | ||
| 
						 | 
					05ec3922f1 | ||
| 
						 | 
					1c9423ef31 | ||
| 
						 | 
					74ffbb2172 | ||
| 
						 | 
					cc9c85cc1a | ||
| 
						 | 
					3f40380c69 | ||
| 
						 | 
					1116b73a08 | ||
| 
						 | 
					a8f252e45d | ||
| 
						 | 
					b821836dc4 | ||
| 
						 | 
					a42fef0905 | ||
| 
						 | 
					f7e09af5c9 | ||
| 
						 | 
					304cb56337 | ||
| 
						 | 
					6b7b5caf54 | ||
| 
						 | 
					fd461fe015 | ||
| 
						 | 
					2797d2c535 | ||
| 
						 | 
					f440656572 | ||
| 
						 | 
					b1d019146a | ||
| 
						 | 
					1dbc58d4e0 | ||
| 
						 | 
					47ccb28482 | ||
| 
						 | 
					744dea00ca | ||
| 
						 | 
					373c2b379c | ||
| 
						 | 
					95b51cf9e6 | ||
| 
						 | 
					a375e924b4 | ||
| 
						 | 
					957bbab440 | ||
| 
						 | 
					bd48c99383 | ||
| 
						 | 
					d989617825 | ||
| 
						 | 
					177d9b7cb0 | ||
| 
						 | 
					7a1305c1bb | ||
| 
						 | 
					52d1d421a3 | ||
| 
						 | 
					c3cc13595d | ||
| 
						 | 
					adc8031e34 | ||
| 
						 | 
					0974c74a89 | ||
| 
						 | 
					c64aae6f39 | ||
| 
						 | 
					b7b1714637 | ||
| 
						 | 
					d8525493a1 | ||
| 
						 | 
					30d0945855 | ||
| 
						 | 
					ffde1f8343 | ||
| 
						 | 
					96c35b41ed | ||
| 
						 | 
					dc2979926f | ||
| 
						 | 
					ff357dd3fb | ||
| 
						 | 
					f35e15204d | ||
| 
						 | 
					3ae4ba3300 | ||
| 
						 | 
					8bb29f53d1 | ||
| 
						 | 
					2e3c1ef4ac | ||
| 
						 | 
					ada7e12b5a | ||
| 
						 | 
					b8949ba3dd | ||
| 
						 | 
					73fe47ba79 | ||
| 
						 | 
					577920de86 | ||
| 
						 | 
					0d8a314bcf | ||
| 
						 | 
					e7cfde1904 | ||
| 
						 | 
					517baa3235 | ||
| 
						 | 
					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 | ||
| 
						 | 
					bfba44fbad | ||
| 
						 | 
					c466f063c8 | ||
| 
						 | 
					ebaa39b03f | ||
| 
						 | 
					fa3d7ad14b | ||
| 
						 | 
					c51104f956 | ||
| 
						 | 
					84795ff4d9 | ||
| 
						 | 
					cc5c722e29 | ||
| 
						 | 
					4fcddd1893 | ||
| 
						 | 
					295af01687 | ||
| 
						 | 
					c8604255e4 | ||
| 
						 | 
					492826a7f2 | ||
| 
						 | 
					e7284df1df | ||
| 
						 | 
					3b46060caa | ||
| 
						 | 
					696182cfa4 | ||
| 
						 | 
					d1a1d1da8f | ||
| 
						 | 
					c508984f56 | ||
| 
						 | 
					54eba51b35 | ||
| 
						 | 
					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 | ||
| 
						 | 
					b0ca4435fd | ||
| 
						 | 
					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 | ||
| 
						 | 
					ed72b090af | ||
| 
						 | 
					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 | ||
| 
						 | 
					778ee02803 | ||
| 
						 | 
					affb65219e | ||
| 
						 | 
					78198bfdaf | ||
| 
						 | 
					190cac394c | ||
| 
						 | 
					be07fe9e9f | ||
| 
						 | 
					ca0981645f | ||
| 
						 | 
					bc7e02b47a | ||
| 
						 | 
					55a55e9f74 | ||
| 
						 | 
					b41d40da40 | ||
| 
						 | 
					ba3e088b23 | ||
| 
						 | 
					40e0d72105 | ||
| 
						 | 
					dfe80556d3 | ||
| 
						 | 
					b3529dc748 | ||
| 
						 | 
					095697900b | ||
| 
						 | 
					13c7182948 | ||
| 
						 | 
					edc76795d4 | ||
| 
						 | 
					d2a9d731ed | ||
| 
						 | 
					3ce7d410c8 | ||
| 
						 | 
					74f28021e7 | ||
| 
						 | 
					f90cbb636a | ||
| 
						 | 
					c8929ca0cb | ||
| 
						 | 
					9fb5bb620d | ||
| 
						 | 
					ed58f32052 | ||
| 
						 | 
					6d2ab4d270 | ||
| 
						 | 
					a6b58bc88d | ||
| 
						 | 
					ea3c37d754 | ||
| 
						 | 
					b37bf06de8 | ||
| 
						 | 
					27bbf0ccaf | ||
| 
						 | 
					ed56d52af3 | ||
| 
						 | 
					fb457968ec | ||
| 
						 | 
					0bea2e2b94 | ||
| 
						 | 
					72d02f442e | ||
| 
						 | 
					bcf63b5d27 | ||
| 
						 | 
					71a32477e4 | ||
| 
						 | 
					08d60fcbf2 | ||
| 
						 | 
					4cda54774a | ||
| 
						 | 
					613475ac26 | ||
| 
						 | 
					20d23fcb92 | ||
| 
						 | 
					92dbe6cdf8 | ||
| 
						 | 
					0781e8cf12 | ||
| 
						 | 
					b71a088da7 | ||
| 
						 | 
					d76fb566a2 | ||
| 
						 | 
					24a40af103 | ||
| 
						 | 
					4a60292f82 | ||
| 
						 | 
					7eea866869 | ||
| 
						 | 
					4ab5456a98 | ||
| 
						 | 
					8881a9f40e | 
							
								
								
									
										162
									
								
								.github/workflows/DNS.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										162
									
								
								.github/workflows/DNS.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,6 @@
 | 
			
		||||
name: DNS
 | 
			
		||||
on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
  push:
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'dnsapi/*.sh'
 | 
			
		||||
@@ -65,7 +66,7 @@ jobs:
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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
 | 
			
		||||
@@ -113,7 +114,7 @@ jobs:
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run:  brew install socat
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
@@ -121,19 +122,19 @@ jobs:
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
        if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
 | 
			
		||||
          export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
 | 
			
		||||
          export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
 | 
			
		||||
          export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
 | 
			
		||||
          export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
 | 
			
		||||
          export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
        fi
 | 
			
		||||
        cd ../acmetest
 | 
			
		||||
        ./letest.sh
 | 
			
		||||
@@ -164,7 +165,7 @@ jobs:
 | 
			
		||||
    - name: Set git to use LF
 | 
			
		||||
      run: |
 | 
			
		||||
          git config --global core.autocrlf false
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install cygwin base packages with chocolatey
 | 
			
		||||
      run: |
 | 
			
		||||
          choco config get cacheLocation
 | 
			
		||||
@@ -184,19 +185,19 @@ jobs:
 | 
			
		||||
      shell: bash
 | 
			
		||||
      run: |
 | 
			
		||||
        if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
 | 
			
		||||
          export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
 | 
			
		||||
          export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
 | 
			
		||||
          export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
 | 
			
		||||
          export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
 | 
			
		||||
          export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
        fi
 | 
			
		||||
        cd ../acmetest
 | 
			
		||||
        ./letest.sh
 | 
			
		||||
@@ -204,7 +205,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  FreeBSD:
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: Windows
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
@@ -223,10 +224,10 @@ jobs:
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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@v0
 | 
			
		||||
    - 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
 | 
			
		||||
@@ -234,19 +235,19 @@ jobs:
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
@@ -255,7 +256,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  OpenBSD:
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: FreeBSD
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
@@ -274,30 +275,30 @@ jobs:
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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@v0
 | 
			
		||||
    - 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
 | 
			
		||||
        prepare: pkg_add socat curl libiconv
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
@@ -306,7 +307,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  NetBSD:
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: OpenBSD
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
@@ -325,31 +326,31 @@ jobs:
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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@v0
 | 
			
		||||
    - 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: |
 | 
			
		||||
          pkg_add curl socat
 | 
			
		||||
          /usr/sbin/pkg_add curl socat
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
@@ -358,7 +359,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  DragonFlyBSD:
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: NetBSD
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
@@ -377,31 +378,31 @@ jobs:
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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@v0
 | 
			
		||||
    - 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
 | 
			
		||||
          pkg install -y curl socat libnghttp2
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
@@ -413,7 +414,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  Solaris:
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: DragonFlyBSD
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
@@ -433,10 +434,10 @@ jobs:
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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@v0
 | 
			
		||||
    - 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
 | 
			
		||||
@@ -445,19 +446,68 @@ jobs:
 | 
			
		||||
          pkg set-mediator -v -I default@1.1 openssl
 | 
			
		||||
          export PATH=/usr/gnu/bin:$PATH
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
 | 
			
		||||
            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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										142
									
								
								.github/workflows/DragonFlyBSD.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										142
									
								
								.github/workflows/DragonFlyBSD.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,71 +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) Pretend Pear X1
 | 
			
		||||
         #- 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-12
 | 
			
		||||
    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@v3
 | 
			
		||||
    - 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@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
 | 
			
		||||
        copyback: "false"
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: |
 | 
			
		||||
          pkg install -y curl socat
 | 
			
		||||
        usesh: true
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/workflows/FreeBSD.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/FreeBSD.yml
									
									
									
									
										vendored
									
									
								
							@@ -29,19 +29,19 @@ jobs:
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           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: macos-12
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
@@ -51,7 +51,7 @@ jobs:
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
@@ -61,7 +61,7 @@ jobs:
 | 
			
		||||
      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@v0
 | 
			
		||||
    - 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: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/Linux.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/Linux.yml
									
									
									
									
										vendored
									
									
								
							@@ -26,14 +26,14 @@ jobs:
 | 
			
		||||
  Linux:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "centos:7", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "mageia", "gentoo/stage3"]
 | 
			
		||||
        os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "gentoo/stage3"]
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
      TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
      TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd .. \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/MacOS.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/MacOS.yml
									
									
									
									
										vendored
									
									
								
							@@ -29,7 +29,7 @@ jobs:
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
@@ -44,7 +44,7 @@ jobs:
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run:  brew install socat
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										143
									
								
								.github/workflows/NetBSD.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										143
									
								
								.github/workflows/NetBSD.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,72 +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) Pretend Pear X1
 | 
			
		||||
         #- 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-12
 | 
			
		||||
    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@v3
 | 
			
		||||
    - 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@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: |
 | 
			
		||||
          export PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r|cut -f '1 2' -d.)/All/"
 | 
			
		||||
          pkg_add curl socat
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								.github/workflows/OpenBSD.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/OpenBSD.yml
									
									
									
									
										vendored
									
									
								
							@@ -29,19 +29,19 @@ jobs:
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           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: macos-12
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
@@ -51,7 +51,7 @@ jobs:
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
@@ -61,12 +61,12 @@ jobs:
 | 
			
		||||
      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@v0
 | 
			
		||||
    - 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
 | 
			
		||||
        prepare: pkg_add socat curl wget libnghttp2
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/PebbleStrict.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/PebbleStrict.yml
									
									
									
									
										vendored
									
									
								
							@@ -33,11 +33,11 @@ jobs:
 | 
			
		||||
      TEST_CA: "Pebble Intermediate CA"
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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
 | 
			
		||||
      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
 | 
			
		||||
@@ -58,7 +58,7 @@ jobs:
 | 
			
		||||
      TEST_IPCERT: 1
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run: sudo apt-get install -y socat
 | 
			
		||||
    - name: Run Pebble
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										149
									
								
								.github/workflows/Solaris.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										149
									
								
								.github/workflows/Solaris.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,74 +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) Pretend Pear X1
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           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: macos-12
 | 
			
		||||
    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@v3
 | 
			
		||||
    - 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@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
 | 
			
		||||
        copyback: "false"
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: pkgutil -y -i socat curl wget
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								.github/workflows/Ubuntu.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/workflows/Ubuntu.yml
									
									
									
									
										vendored
									
									
								
							@@ -29,12 +29,12 @@ jobs:
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         - TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
           CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
@@ -70,7 +70,7 @@ jobs:
 | 
			
		||||
      TestingDomain: ${{ matrix.TestingDomain }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run: sudo apt-get install -y socat wget
 | 
			
		||||
    - name: Start StepCA
 | 
			
		||||
@@ -80,9 +80,14 @@ jobs:
 | 
			
		||||
            -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 \
 | 
			
		||||
            && sleep 5 && docker exec  stepca step ca provisioner add acme --type ACME \
 | 
			
		||||
            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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/Windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/Windows.yml
									
									
									
									
										vendored
									
									
								
							@@ -29,7 +29,7 @@ jobs:
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
@@ -49,7 +49,7 @@ jobs:
 | 
			
		||||
    - name: Set git to use LF
 | 
			
		||||
      run: |
 | 
			
		||||
          git config --global core.autocrlf false
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install cygwin base packages with chocolatey
 | 
			
		||||
      run: |
 | 
			
		||||
          choco config get cacheLocation
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								.github/workflows/dockerhub.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/dockerhub.yml
									
									
									
									
										vendored
									
									
								
							@@ -15,6 +15,8 @@ concurrency:
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  DOCKER_IMAGE: neilpang/acme.sh
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  CheckToken:
 | 
			
		||||
@@ -28,9 +30,9 @@ jobs:
 | 
			
		||||
        id: step_one
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "$DOCKER_PASSWORD" ] ; then
 | 
			
		||||
            echo "::set-output name=hasToken::true"
 | 
			
		||||
            echo "hasToken=true" >>$GITHUB_OUTPUT
 | 
			
		||||
          else
 | 
			
		||||
            echo "::set-output name=hasToken::false"
 | 
			
		||||
            echo "hasToken=false" >>$GITHUB_OUTPUT
 | 
			
		||||
          fi
 | 
			
		||||
      - name: Check the value
 | 
			
		||||
        run: echo ${{ steps.step_one.outputs.hasToken }}
 | 
			
		||||
@@ -41,18 +43,23 @@ jobs:
 | 
			
		||||
    if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: checkout code
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        with:
 | 
			
		||||
          persist-credentials: false
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v1
 | 
			
		||||
        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@v1
 | 
			
		||||
        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: |
 | 
			
		||||
          DOCKER_IMAGE=neilpang/acme.sh
 | 
			
		||||
 | 
			
		||||
          if [[ $GITHUB_REF == refs/tags/* ]]; then
 | 
			
		||||
            DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/}
 | 
			
		||||
          fi
 | 
			
		||||
@@ -66,8 +73,14 @@ jobs:
 | 
			
		||||
            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 .
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								.github/workflows/pr_dns.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/pr_dns.yml
									
									
									
									
										vendored
									
									
								
							@@ -4,8 +4,6 @@ on:
 | 
			
		||||
  pull_request_target:
 | 
			
		||||
    types:
 | 
			
		||||
      - opened
 | 
			
		||||
    branches:
 | 
			
		||||
      - 'dev'
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'dnsapi/*.sh'
 | 
			
		||||
 | 
			
		||||
@@ -22,9 +20,14 @@ jobs:
 | 
			
		||||
              owner: context.repo.owner,
 | 
			
		||||
              repo: context.repo.repo,
 | 
			
		||||
              body: `**Welcome**
 | 
			
		||||
                Please make sure you're 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 ✨
 | 
			
		||||
                    READ ME !!!!!
 | 
			
		||||
                    Read me !!!!!!
 | 
			
		||||
                    First thing: don't send PR to the master branch, please send to the dev branch instead.
 | 
			
		||||
                    Please read the [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide).
 | 
			
		||||
                    You MUST pass the [DNS-API-Test](../wiki/DNS-API-Test).
 | 
			
		||||
                    Then reply on this message, otherwise, your code will not be reviewed or merged.
 | 
			
		||||
                    Please also make sure to add/update the usage here: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2
 | 
			
		||||
                    注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试.
 | 
			
		||||
                `
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/pr_notify.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/pr_notify.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
name: Check dns api
 | 
			
		||||
name: Check notify api
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  pull_request_target:
 | 
			
		||||
@@ -22,7 +22,7 @@ jobs:
 | 
			
		||||
              owner: context.repo.owner,
 | 
			
		||||
              repo: context.repo.repo,
 | 
			
		||||
              body: `**Welcome**
 | 
			
		||||
                Please make sure you're read our [Code-of-conduct](../wiki/Code-of-conduct) and  add the usage here: [notify](../wiki/notify).
 | 
			
		||||
                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 ✨
 | 
			
		||||
                `
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/shellcheck.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/shellcheck.yml
									
									
									
									
										vendored
									
									
								
							@@ -22,7 +22,7 @@ jobs:
 | 
			
		||||
  ShellCheck:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install Shellcheck
 | 
			
		||||
      run: sudo apt-get install -y shellcheck
 | 
			
		||||
    - name: DoShellcheck
 | 
			
		||||
@@ -31,7 +31,7 @@ jobs:
 | 
			
		||||
  shfmt:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								.github/workflows/wiki-monitor.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								.github/workflows/wiki-monitor.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
name: Notify via Issue on Wiki Edit
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  gollum:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  notify:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout wiki repository
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        with:
 | 
			
		||||
          repository: ${{ github.repository }}.wiki
 | 
			
		||||
          path: wiki
 | 
			
		||||
          fetch-depth: 0
 | 
			
		||||
 | 
			
		||||
      - name: Generate wiki change message
 | 
			
		||||
        run: |
 | 
			
		||||
            actor="${{ github.actor }}"
 | 
			
		||||
            sender_url=$(jq -r '.sender.html_url' "$GITHUB_EVENT_PATH")
 | 
			
		||||
            page_name=$(jq -r '.pages[0].page_name' "$GITHUB_EVENT_PATH")
 | 
			
		||||
            page_sha=$(jq -r '.pages[0].sha' "$GITHUB_EVENT_PATH")
 | 
			
		||||
            page_url=$(jq -r '.pages[0].html_url' "$GITHUB_EVENT_PATH")
 | 
			
		||||
            page_action=$(jq -r '.pages[0].action' "$GITHUB_EVENT_PATH")
 | 
			
		||||
            now="$(date '+%Y-%m-%d %H:%M:%S')"
 | 
			
		||||
 | 
			
		||||
            cd wiki
 | 
			
		||||
            prev_sha=$(git rev-list $page_sha^ -- "$page_name.md" | head -n 1)
 | 
			
		||||
            if [ -n "$prev_sha" ]; then
 | 
			
		||||
                git diff $prev_sha $page_sha -- "$page_name.md" > ../wiki.diff || echo "(No diff found)" > ../wiki.diff
 | 
			
		||||
            else
 | 
			
		||||
                echo "(no diff)" > ../wiki.diff
 | 
			
		||||
            fi
 | 
			
		||||
            cd ..
 | 
			
		||||
            {
 | 
			
		||||
            echo "Wiki edited"
 | 
			
		||||
            echo -n "User: "
 | 
			
		||||
            echo "[$actor]($sender_url)"
 | 
			
		||||
            echo "Time: $now"
 | 
			
		||||
            echo "Page: [$page_name]($page_url) (Action: $page_action)"
 | 
			
		||||
            echo ""
 | 
			
		||||
            echo "----"
 | 
			
		||||
            echo "###  diff:"
 | 
			
		||||
            echo '```diff'
 | 
			
		||||
            cat wiki.diff
 | 
			
		||||
            echo '```'
 | 
			
		||||
            } > wiki-change-msg.txt
 | 
			
		||||
 | 
			
		||||
      - name: Create issue to notify Neilpang
 | 
			
		||||
        uses: peter-evans/create-issue-from-file@v5
 | 
			
		||||
        with:
 | 
			
		||||
          title: "Wiki edited"
 | 
			
		||||
          content-filepath: ./wiki-change-msg.txt
 | 
			
		||||
          assignees: Neilpang
 | 
			
		||||
        env:
 | 
			
		||||
          TZ: Asia/Shanghai
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
FROM alpine:3.16.3
 | 
			
		||||
FROM alpine:3.21
 | 
			
		||||
 | 
			
		||||
RUN apk --no-cache add -f \
 | 
			
		||||
  openssl \
 | 
			
		||||
@@ -12,20 +12,25 @@ RUN apk --no-cache add -f \
 | 
			
		||||
  oath-toolkit-oathtool \
 | 
			
		||||
  tar \
 | 
			
		||||
  libidn \
 | 
			
		||||
  jq
 | 
			
		||||
  jq \
 | 
			
		||||
  cronie
 | 
			
		||||
 | 
			
		||||
ENV LE_CONFIG_HOME /acme.sh
 | 
			
		||||
ENV LE_CONFIG_HOME=/acme.sh
 | 
			
		||||
 | 
			
		||||
ARG AUTO_UPGRADE=1
 | 
			
		||||
 | 
			
		||||
ENV AUTO_UPGRADE $AUTO_UPGRADE
 | 
			
		||||
ENV AUTO_UPGRADE=$AUTO_UPGRADE
 | 
			
		||||
 | 
			
		||||
#Install
 | 
			
		||||
COPY ./ /install_acme.sh/
 | 
			
		||||
COPY ./acme.sh /install_acme.sh/acme.sh
 | 
			
		||||
COPY ./deploy /install_acme.sh/deploy
 | 
			
		||||
COPY ./dnsapi /install_acme.sh/dnsapi
 | 
			
		||||
COPY ./notify /install_acme.sh/notify
 | 
			
		||||
 | 
			
		||||
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 \
 | 
			
		||||
  version \
 | 
			
		||||
@@ -64,12 +69,10 @@ RUN for verb in help \
 | 
			
		||||
 | 
			
		||||
RUN printf "%b" '#!'"/usr/bin/env sh\n \
 | 
			
		||||
if [ \"\$1\" = \"daemon\" ];  then \n \
 | 
			
		||||
 trap \"echo stop && killall crond && exit 0\" SIGTERM SIGINT \n \
 | 
			
		||||
 crond && sleep infinity &\n \
 | 
			
		||||
 wait \n \
 | 
			
		||||
 exec crond -n -s -m off \n \
 | 
			
		||||
else \n \
 | 
			
		||||
 exec -- \"\$@\"\n \
 | 
			
		||||
fi" >/entry.sh && chmod +x /entry.sh
 | 
			
		||||
fi\n" >/entry.sh && chmod +x /entry.sh
 | 
			
		||||
 | 
			
		||||
VOLUME /acme.sh
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								README.md
									
									
									
									
									
								
							@@ -8,7 +8,7 @@
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)
 | 
			
		||||
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
@@ -51,14 +51,12 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
 | 
			
		||||
- [ruby-china.org](https://ruby-china.org/topics/31983)
 | 
			
		||||
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
 | 
			
		||||
- [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)
 | 
			
		||||
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
 | 
			
		||||
- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html)
 | 
			
		||||
- [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)
 | 
			
		||||
- [CentOS Web Panel](http://centos-webpanel.com/)
 | 
			
		||||
- [CentOS Web Panel](https://control-webpanel.com)
 | 
			
		||||
- [lnmp.org](https://lnmp.org/)
 | 
			
		||||
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
 | 
			
		||||
 | 
			
		||||
@@ -75,20 +73,21 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
 | 
			
		||||
|7|[](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
 | 
			
		||||
|8|[](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
 | 
			
		||||
|9|[](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
 | 
			
		||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
 | 
			
		||||
|11|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
 | 
			
		||||
|12|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
 | 
			
		||||
|13|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
 | 
			
		||||
|14|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
 | 
			
		||||
|15|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
 | 
			
		||||
|16|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
 | 
			
		||||
|17|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
 | 
			
		||||
|18|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
 | 
			
		||||
|19|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
 | 
			
		||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
 | 
			
		||||
|11|-----| Cloud Linux  https://github.com/acmesh-official/acme.sh/issues/111
 | 
			
		||||
|22|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
 | 
			
		||||
|23|[](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)
 | 
			
		||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)|Omnios
 | 
			
		||||
|11|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
 | 
			
		||||
|12|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
 | 
			
		||||
|13|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
 | 
			
		||||
|14|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
 | 
			
		||||
|15|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
 | 
			
		||||
|16|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
 | 
			
		||||
|17|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
 | 
			
		||||
|18|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
 | 
			
		||||
|19|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
 | 
			
		||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
 | 
			
		||||
|11|[](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://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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Check our [testing project](https://github.com/acmesh-official/acmetest):
 | 
			
		||||
@@ -99,9 +98,9 @@ https://github.com/acmesh-official/acmetest
 | 
			
		||||
 | 
			
		||||
- [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA)(default)
 | 
			
		||||
- 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)
 | 
			
		||||
- [Actalis.com CA](https://github.com/acmesh-official/acme.sh/wiki/Actalis.com-CA)
 | 
			
		||||
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
 | 
			
		||||
- Any other [RFC8555](https://tools.ietf.org/html/rfc8555)-compliant CA
 | 
			
		||||
 | 
			
		||||
@@ -361,10 +360,6 @@ Ok, it's done.
 | 
			
		||||
 | 
			
		||||
# 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-`.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
@@ -385,10 +380,12 @@ Please look at the `keylength` parameter above.
 | 
			
		||||
 | 
			
		||||
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")**
 | 
			
		||||
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
 | 
			
		||||
@@ -510,10 +507,6 @@ Support this project with your organization. Your logo will show up here with a
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### Sponsors
 | 
			
		||||
 | 
			
		||||
[](https://www.quantumca.com.cn/?__utm_source=acmesh-donation)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 19. License & Others
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								deploy/cachefly.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								deploy/cachefly.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to deploy certificate to CacheFly
 | 
			
		||||
# https://api.cachefly.com/api/2.5/docs#tag/Certificates/paths/~1certificates/post
 | 
			
		||||
 | 
			
		||||
# This deployment required following variables
 | 
			
		||||
# export CACHEFLY_TOKEN="Your CacheFly API Token"
 | 
			
		||||
 | 
			
		||||
# returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
CACHEFLY_API_BASE="https://api.cachefly.com/api/2.5"
 | 
			
		||||
 | 
			
		||||
cachefly_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 [ -z "$CACHEFLY_TOKEN" ]; then
 | 
			
		||||
    _err "CACHEFLY_TOKEN is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf CACHEFLY_TOKEN "$CACHEFLY_TOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Deploying certificate to CacheFly..."
 | 
			
		||||
 | 
			
		||||
  ## upload certificate
 | 
			
		||||
  string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
 | 
			
		||||
  string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
 | 
			
		||||
 | 
			
		||||
  _request_body="{\"certificate\":\"$string_fullchain\",\"certificateKey\":\"$string_key\"}"
 | 
			
		||||
  _debug _request_body "$_request_body"
 | 
			
		||||
  _debug CACHEFLY_TOKEN "$CACHEFLY_TOKEN"
 | 
			
		||||
  export _H1="Authorization: Bearer $CACHEFLY_TOKEN"
 | 
			
		||||
  _response=$(_post "$_request_body" "$CACHEFLY_API_BASE/certificates" "" "POST" "application/json")
 | 
			
		||||
 | 
			
		||||
  if _contains "$_response" "message"; then
 | 
			
		||||
    _err "Error in deploying $_cdomain certificate to CacheFly."
 | 
			
		||||
    _err "$_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug response "$_response"
 | 
			
		||||
  _info "Domain $_cdomain certificate successfully deployed to CacheFly."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								deploy/directadmin.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								deploy/directadmin.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to deploy certificate to DirectAdmin
 | 
			
		||||
# https://docs.directadmin.com/directadmin/customizing-workflow/api-all-about.html#creating-a-login-key
 | 
			
		||||
# https://docs.directadmin.com/changelog/version-1.24.4.html#cmd-api-catch-all-pop-passwords-frontpage-protected-dirs-ssl-certs
 | 
			
		||||
 | 
			
		||||
# This deployment required following variables
 | 
			
		||||
# export DirectAdmin_SCHEME="https" # Optional, https or http, defaults to https
 | 
			
		||||
# export DirectAdmin_ENDPOINT="example.com:2222"
 | 
			
		||||
# export DirectAdmin_USERNAME="Your DirectAdmin Username"
 | 
			
		||||
# export DirectAdmin_KEY="Your DirectAdmin Login Key or Password"
 | 
			
		||||
# export DirectAdmin_MAIN_DOMAIN="Your DirectAdmin Main Domain, NOT Subdomain"
 | 
			
		||||
 | 
			
		||||
# returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
directadmin_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 [ -z "$DirectAdmin_ENDPOINT" ]; then
 | 
			
		||||
    _err "DirectAdmin_ENDPOINT is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf DirectAdmin_ENDPOINT "$DirectAdmin_ENDPOINT"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$DirectAdmin_USERNAME" ]; then
 | 
			
		||||
    _err "DirectAdmin_USERNAME is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf DirectAdmin_USERNAME "$DirectAdmin_USERNAME"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$DirectAdmin_KEY" ]; then
 | 
			
		||||
    _err "DirectAdmin_KEY is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf DirectAdmin_KEY "$DirectAdmin_KEY"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$DirectAdmin_MAIN_DOMAIN" ]; then
 | 
			
		||||
    _err "DirectAdmin_MAIN_DOMAIN is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf DirectAdmin_MAIN_DOMAIN "$DirectAdmin_MAIN_DOMAIN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Optional SCHEME
 | 
			
		||||
  _getdeployconf DirectAdmin_SCHEME
 | 
			
		||||
  # set default values for DirectAdmin_SCHEME
 | 
			
		||||
  [ -n "${DirectAdmin_SCHEME}" ] || DirectAdmin_SCHEME="https"
 | 
			
		||||
 | 
			
		||||
  _info "Deploying certificate to DirectAdmin..."
 | 
			
		||||
 | 
			
		||||
  # upload certificate
 | 
			
		||||
  string_cfullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
 | 
			
		||||
  string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
 | 
			
		||||
 | 
			
		||||
  _request_body="{\"domain\":\"$DirectAdmin_MAIN_DOMAIN\",\"action\":\"save\",\"type\":\"paste\",\"certificate\":\"$string_key\n$string_cfullchain\n\"}"
 | 
			
		||||
  _debug _request_body "$_request_body"
 | 
			
		||||
  _debug DirectAdmin_ENDPOINT "$DirectAdmin_ENDPOINT"
 | 
			
		||||
  _debug DirectAdmin_USERNAME "$DirectAdmin_USERNAME"
 | 
			
		||||
  _debug DirectAdmin_KEY "$DirectAdmin_KEY"
 | 
			
		||||
  _debug DirectAdmin_MAIN_DOMAIN "$DirectAdmin_MAIN_DOMAIN"
 | 
			
		||||
  _response=$(_post "$_request_body" "$DirectAdmin_SCHEME://$DirectAdmin_USERNAME:$DirectAdmin_KEY@$DirectAdmin_ENDPOINT/CMD_API_SSL" "" "POST" "application/json")
 | 
			
		||||
 | 
			
		||||
  if _contains "$_response" "error=1"; then
 | 
			
		||||
    _err "Error in deploying $_cdomain certificate to DirectAdmin Domain $DirectAdmin_MAIN_DOMAIN."
 | 
			
		||||
    _err "$_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "$_response"
 | 
			
		||||
  _info "Domain $_cdomain certificate successfully deployed to DirectAdmin Domain $DirectAdmin_MAIN_DOMAIN."
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -18,6 +18,7 @@ docker_deploy() {
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  _cpfx="$6"
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _getdeployconf DEPLOY_DOCKER_CONTAINER_LABEL
 | 
			
		||||
  _debug2 DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL"
 | 
			
		||||
@@ -88,6 +89,12 @@ docker_deploy() {
 | 
			
		||||
    _savedeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_DOCKER_CONTAINER_PFX_FILE
 | 
			
		||||
  _debug2 DEPLOY_DOCKER_CONTAINER_PFX_FILE "$DEPLOY_DOCKER_CONTAINER_PFX_FILE"
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_PFX_FILE" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_DOCKER_CONTAINER_PFX_FILE "$DEPLOY_DOCKER_CONTAINER_PFX_FILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD
 | 
			
		||||
  _debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
 | 
			
		||||
@@ -125,6 +132,12 @@ docker_deploy() {
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_PFX_FILE" ]; then
 | 
			
		||||
    if ! _docker_cp "$_cid" "$_cpfx" "$DEPLOY_DOCKER_CONTAINER_PFX_FILE"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then
 | 
			
		||||
    _info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"
 | 
			
		||||
    if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then
 | 
			
		||||
@@ -273,16 +286,27 @@ _check_curl_version() {
 | 
			
		||||
  _minor="$(_getfield "$_cversion" 2 '.')"
 | 
			
		||||
  _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 "Please upgrade to curl 7.40 or later."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if [ "$_minor" -lt "50" ]; then
 | 
			
		||||
      _debug "Use short host name"
 | 
			
		||||
      export _CURL_NO_HOST=1
 | 
			
		||||
    else
 | 
			
		||||
      export _CURL_NO_HOST=
 | 
			
		||||
    fi
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "curl v$_cversion doesn't support unit socket"
 | 
			
		||||
    _err "Please upgrade to curl 7.40 or later."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$_major$_minor" -lt "750" ]; then
 | 
			
		||||
    _debug "Use short host name"
 | 
			
		||||
    export _CURL_NO_HOST=1
 | 
			
		||||
  else
 | 
			
		||||
    export _CURL_NO_HOST=
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								deploy/edgio.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								deploy/edgio.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is a script to deploy cert to edgio using its API
 | 
			
		||||
# https://docs.edg.io/guides/v7/develop/rest_api/authentication
 | 
			
		||||
# https://docs.edg.io/rest_api/#tag/tls-certs/operation/postConfigV01TlsCerts
 | 
			
		||||
 | 
			
		||||
# This deployment required following variables
 | 
			
		||||
# export EDGIO_CLIENT_ID="Your Edgio Client ID"
 | 
			
		||||
# export EDGIO_CLIENT_SECRET="Your Edgio Client Secret"
 | 
			
		||||
# export EDGIO_ENVIRONMENT_ID="Your Edgio Environment ID"
 | 
			
		||||
 | 
			
		||||
# If have more than one Environment ID
 | 
			
		||||
# export EDGIO_ENVIRONMENT_ID="ENVIRONMENT_ID_1 ENVIRONMENT_ID_2"
 | 
			
		||||
 | 
			
		||||
# returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
edgio_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 [ -z "$EDGIO_CLIENT_ID" ]; then
 | 
			
		||||
    _err "EDGIO_CLIENT_ID is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf EDGIO_CLIENT_ID "$EDGIO_CLIENT_ID"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$EDGIO_CLIENT_SECRET" ]; then
 | 
			
		||||
    _err "EDGIO_CLIENT_SECRET is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf EDGIO_CLIENT_SECRET "$EDGIO_CLIENT_SECRET"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$EDGIO_ENVIRONMENT_ID" ]; then
 | 
			
		||||
    _err "EDGIO_ENVIRONMENT_ID is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf EDGIO_ENVIRONMENT_ID "$EDGIO_ENVIRONMENT_ID"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Getting access token"
 | 
			
		||||
  _data="client_id=$EDGIO_CLIENT_ID&client_secret=$EDGIO_CLIENT_SECRET&grant_type=client_credentials&scope=app.config"
 | 
			
		||||
  _debug Get_access_token_data "$_data"
 | 
			
		||||
  _response=$(_post "$_data" "https://id.edgio.app/connect/token" "" "POST" "application/x-www-form-urlencoded")
 | 
			
		||||
  _debug Get_access_token_response "$_response"
 | 
			
		||||
  _access_token=$(echo "$_response" | _json_decode | _egrep_o '"access_token":"[^"]*' | cut -d : -f 2 | tr -d '"')
 | 
			
		||||
  _debug _access_token "$_access_token"
 | 
			
		||||
  if [ -z "$_access_token" ]; then
 | 
			
		||||
    _err "Error in getting access token"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Uploading certificate"
 | 
			
		||||
  string_ccert=$(sed 's/$/\\n/' "$_ccert" | tr -d '\n')
 | 
			
		||||
  string_cca=$(sed 's/$/\\n/' "$_cca" | tr -d '\n')
 | 
			
		||||
  string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
 | 
			
		||||
 | 
			
		||||
  for ENVIRONMENT_ID in $EDGIO_ENVIRONMENT_ID; do
 | 
			
		||||
    _data="{\"environment_id\":\"$ENVIRONMENT_ID\",\"primary_cert\":\"$string_ccert\",\"intermediate_cert\":\"$string_cca\",\"private_key\":\"$string_key\"}"
 | 
			
		||||
    _debug Upload_certificate_data "$_data"
 | 
			
		||||
    _H1="Authorization: Bearer $_access_token"
 | 
			
		||||
    _response=$(_post "$_data" "https://edgioapis.com/config/v0.1/tls-certs" "" "POST" "application/json")
 | 
			
		||||
    if _contains "$_response" "message"; then
 | 
			
		||||
      _err "Error in deploying $_cdomain certificate to Edgio ENVIRONMENT_ID $ENVIRONMENT_ID."
 | 
			
		||||
      _err "$_response"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _debug Upload_certificate_response "$_response"
 | 
			
		||||
    _info "Domain $_cdomain certificate successfully deployed to Edgio ENVIRONMENT_ID $ENVIRONMENT_ID."
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -109,6 +109,5 @@ exim4_deploy() {
 | 
			
		||||
    fi
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
#!/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.
 | 
			
		||||
#
 | 
			
		||||
# Written by temoffey <temofffey@gmail.com>
 | 
			
		||||
# Public domain, 2019
 | 
			
		||||
# Update by DreamOfIce <admin@dreamofice.cn> in 2023
 | 
			
		||||
 | 
			
		||||
#export DEPLOY_GCORE_CDN_USERNAME=myusername
 | 
			
		||||
#export DEPLOY_GCORE_CDN_PASSWORD=mypassword
 | 
			
		||||
@@ -56,7 +57,7 @@ gcore_cdn_deploy() {
 | 
			
		||||
  _request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}"
 | 
			
		||||
  _debug _request "$_request"
 | 
			
		||||
  export _H1="Content-Type:application/json"
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcdn.co/auth/jwt/login")
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcore.com/auth/jwt/login")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex=".*\"access\":\"\([-._0-9A-Za-z]*\)\".*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
@@ -69,8 +70,8 @@ gcore_cdn_deploy() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Find CDN resource with cname $_cdomain"
 | 
			
		||||
  export _H2="Authorization:Token $_token"
 | 
			
		||||
  _response=$(_get "https://api.gcdn.co/resources")
 | 
			
		||||
  export _H2="Authorization:Bearer $_token"
 | 
			
		||||
  _response=$(_get "https://api.gcore.com/cdn/resources")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex="\"primary_resource\":null},"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
@@ -102,7 +103,7 @@ gcore_cdn_deploy() {
 | 
			
		||||
  _date=$(date "+%d.%m.%Y %H:%M:%S")
 | 
			
		||||
  _request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}"
 | 
			
		||||
  _debug _request "$_request"
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcdn.co/sslData")
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcore.com/cdn/sslData")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex=".*\"id\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
@@ -117,7 +118,7 @@ gcore_cdn_deploy() {
 | 
			
		||||
  _info "Update CDN resource"
 | 
			
		||||
  _request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}"
 | 
			
		||||
  _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"
 | 
			
		||||
  _regex=".*\"sslData\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
@@ -133,7 +134,7 @@ gcore_cdn_deploy() {
 | 
			
		||||
    _info "Not found old SSL certificate"
 | 
			
		||||
  else
 | 
			
		||||
    _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"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,19 @@
 | 
			
		||||
# Note: This functionality requires HAProxy was compiled against
 | 
			
		||||
# 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 #####################
 | 
			
		||||
 | 
			
		||||
@@ -46,6 +59,7 @@ haproxy_deploy() {
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  _cmdpfx=""
 | 
			
		||||
 | 
			
		||||
  # Some defaults
 | 
			
		||||
  DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy"
 | 
			
		||||
@@ -53,6 +67,8 @@ haproxy_deploy() {
 | 
			
		||||
  DEPLOY_HAPROXY_BUNDLE_DEFAULT="no"
 | 
			
		||||
  DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
 | 
			
		||||
  DEPLOY_HAPROXY_RELOAD_DEFAULT="true"
 | 
			
		||||
  DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT="no"
 | 
			
		||||
  DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT="UNIX:/run/haproxy/admin.sock"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "${_cdomain}"
 | 
			
		||||
  _debug _ckey "${_ckey}"
 | 
			
		||||
@@ -86,6 +102,11 @@ haproxy_deploy() {
 | 
			
		||||
    _savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
 | 
			
		||||
@@ -118,6 +139,36 @@ haproxy_deploy() {
 | 
			
		||||
    Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
 | 
			
		||||
  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
 | 
			
		||||
  if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
 | 
			
		||||
    _info "Bundle creation requested"
 | 
			
		||||
@@ -142,12 +193,13 @@ haproxy_deploy() {
 | 
			
		||||
  _issuer="${_pem}.issuer"
 | 
			
		||||
  _ocsp="${_pem}.ocsp"
 | 
			
		||||
  _reload="${Le_Deploy_haproxy_reload}"
 | 
			
		||||
  _statssock="${Le_Deploy_haproxy_stats_socket}"
 | 
			
		||||
 | 
			
		||||
  _info "Deploying PEM file"
 | 
			
		||||
  # Create a temporary PEM file
 | 
			
		||||
  _temppem="$(_mktemp)"
 | 
			
		||||
  _debug _temppem "${_temppem}"
 | 
			
		||||
  cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
 | 
			
		||||
  cat "${_ccert}" "${_cca}" "${_ckey}" | grep . >"${_temppem}"
 | 
			
		||||
  _ret="$?"
 | 
			
		||||
 | 
			
		||||
  # Check that we could create the temporary file
 | 
			
		||||
@@ -265,15 +317,86 @@ haproxy_deploy() {
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Reload HAProxy
 | 
			
		||||
  _debug _reload "${_reload}"
 | 
			
		||||
  eval "${_reload}"
 | 
			
		||||
  _ret=$?
 | 
			
		||||
  if [ "${_ret}" != "0" ]; then
 | 
			
		||||
    _err "Error code ${_ret} during reload"
 | 
			
		||||
    return ${_ret}
 | 
			
		||||
  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'"
 | 
			
		||||
      _secure_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
 | 
			
		||||
    _info "Reload successful"
 | 
			
		||||
    # Reload HAProxy
 | 
			
		||||
    _debug _reload "${_reload}"
 | 
			
		||||
    eval "${_reload}"
 | 
			
		||||
    _ret=$?
 | 
			
		||||
    if [ "${_ret}" != "0" ]; then
 | 
			
		||||
      _err "Error code ${_ret} during reload"
 | 
			
		||||
      return ${_ret}
 | 
			
		||||
    else
 | 
			
		||||
      _info "Reload successful"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								deploy/kemplm.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										98
									
								
								deploy/kemplm.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to a Kemp Loadmaster.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
#DEPLOY_KEMP_TOKEN="token"
 | 
			
		||||
#DEPLOY_KEMP_URL="https://kemplm.example.com"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
kemplm_deploy() {
 | 
			
		||||
  _domain="$1"
 | 
			
		||||
  _key_file="$2"
 | 
			
		||||
  _cert_file="$3"
 | 
			
		||||
  _ca_file="$4"
 | 
			
		||||
  _fullchain_file="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug _key_file "$_key_file"
 | 
			
		||||
  _debug _cert_file "$_cert_file"
 | 
			
		||||
  _debug _ca_file "$_ca_file"
 | 
			
		||||
  _debug _fullchain_file "$_fullchain_file"
 | 
			
		||||
 | 
			
		||||
  if ! _exists jq; then
 | 
			
		||||
    _err "jq not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Rename wildcard certs, kemp accepts only alphanumeric names so we delete '*.' from filename
 | 
			
		||||
  _kemp_domain=$(echo "${_domain}" | sed 's/\*\.//')
 | 
			
		||||
  _debug _kemp_domain "$_kemp_domain"
 | 
			
		||||
 | 
			
		||||
  # Read config from saved values or env
 | 
			
		||||
  _getdeployconf DEPLOY_KEMP_TOKEN
 | 
			
		||||
  _getdeployconf DEPLOY_KEMP_URL
 | 
			
		||||
 | 
			
		||||
  _debug DEPLOY_KEMP_URL "$DEPLOY_KEMP_URL"
 | 
			
		||||
  _secure_debug DEPLOY_KEMP_TOKEN "$DEPLOY_KEMP_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_KEMP_TOKEN" ]; then
 | 
			
		||||
    _err "Kemp Loadmaster token is not found, please define DEPLOY_KEMP_TOKEN."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$DEPLOY_KEMP_URL" ]; then
 | 
			
		||||
    _err "Kemp Loadmaster URL is not found, please define DEPLOY_KEMP_URL."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Save current values
 | 
			
		||||
  _savedeployconf DEPLOY_KEMP_TOKEN "$DEPLOY_KEMP_TOKEN"
 | 
			
		||||
  _savedeployconf DEPLOY_KEMP_URL "$DEPLOY_KEMP_URL"
 | 
			
		||||
 | 
			
		||||
  # Check if certificate is already installed
 | 
			
		||||
  _info "Check if certificate is already present"
 | 
			
		||||
  _list_request="{\"cmd\": \"listcert\", \"apikey\": \"${DEPLOY_KEMP_TOKEN}\"}"
 | 
			
		||||
  _debug3 _list_request "${_list_request}"
 | 
			
		||||
  _kemp_cert_count=$(HTTPS_INSECURE=1 _post "${_list_request}" "${DEPLOY_KEMP_URL}/accessv2" | jq -r '.cert[] | .name' | grep -c "${_kemp_domain}")
 | 
			
		||||
  _debug2 _kemp_cert_count "${_kemp_cert_count}"
 | 
			
		||||
 | 
			
		||||
  _kemp_replace_cert=1
 | 
			
		||||
  if [ "${_kemp_cert_count}" -eq 0 ]; then
 | 
			
		||||
    _kemp_replace_cert=0
 | 
			
		||||
    _info "Certificate does not exist on Kemp Loadmaster"
 | 
			
		||||
  else
 | 
			
		||||
    _info "Certificate already exists on Kemp Loadmaster"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _kemp_replace_cert "${_kemp_replace_cert}"
 | 
			
		||||
 | 
			
		||||
  # Upload new certificate to Kemp Loadmaster
 | 
			
		||||
  _kemp_upload_cert=$(_mktemp)
 | 
			
		||||
  cat "${_fullchain_file}" "${_key_file}" | base64 | tr -d '\n' >"${_kemp_upload_cert}"
 | 
			
		||||
 | 
			
		||||
  _info "Uploading certificate to Kemp Loadmaster"
 | 
			
		||||
  _add_data=$(cat "${_kemp_upload_cert}")
 | 
			
		||||
  _add_request="{\"cmd\": \"addcert\", \"apikey\": \"${DEPLOY_KEMP_TOKEN}\", \"replace\": ${_kemp_replace_cert}, \"cert\": \"${_kemp_domain}\", \"data\": \"${_add_data}\"}"
 | 
			
		||||
  _debug3 _add_request "${_add_request}"
 | 
			
		||||
  _kemp_post_result=$(HTTPS_INSECURE=1 _post "${_add_request}" "${DEPLOY_KEMP_URL}/accessv2")
 | 
			
		||||
  _retval=$?
 | 
			
		||||
  _debug2 _kemp_post_result "${_kemp_post_result}"
 | 
			
		||||
  if [ "${_retval}" -eq 0 ]; then
 | 
			
		||||
    _kemp_post_status=$(echo "${_kemp_post_result}" | jq -r '.status')
 | 
			
		||||
    _kemp_post_message=$(echo "${_kemp_post_result}" | jq -r '.message')
 | 
			
		||||
    if [ "${_kemp_post_status}" = "ok" ]; then
 | 
			
		||||
      _info "Upload successful"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Upload failed: ${_kemp_post_message}"
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _err "Upload failed"
 | 
			
		||||
    _retval=1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  rm "${_kemp_upload_cert}"
 | 
			
		||||
 | 
			
		||||
  return $_retval
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								deploy/keyhelp.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								deploy/keyhelp.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to deploy certificate to KeyHelp
 | 
			
		||||
# This deployment required following variables
 | 
			
		||||
# export DEPLOY_KEYHELP_BASEURL="https://keyhelp.example.com"
 | 
			
		||||
# export DEPLOY_KEYHELP_USERNAME="Your KeyHelp Username"
 | 
			
		||||
# export DEPLOY_KEYHELP_PASSWORD="Your KeyHelp Password"
 | 
			
		||||
# export DEPLOY_KEYHELP_DOMAIN_ID="Depoly certificate to this Domain ID"
 | 
			
		||||
 | 
			
		||||
# Open the 'Edit domain' page, and you will see id=xxx at the end of the URL. This is the Domain ID.
 | 
			
		||||
# https://DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit&id=xxx
 | 
			
		||||
 | 
			
		||||
# If have more than one domain name
 | 
			
		||||
# export DEPLOY_KEYHELP_DOMAIN_ID="111 222 333"
 | 
			
		||||
 | 
			
		||||
keyhelp_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 [ -z "$DEPLOY_KEYHELP_BASEURL" ]; then
 | 
			
		||||
    _err "DEPLOY_KEYHELP_BASEURL is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf DEPLOY_KEYHELP_BASEURL "$DEPLOY_KEYHELP_BASEURL"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_KEYHELP_USERNAME" ]; then
 | 
			
		||||
    _err "DEPLOY_KEYHELP_USERNAME is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf DEPLOY_KEYHELP_USERNAME "$DEPLOY_KEYHELP_USERNAME"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_KEYHELP_PASSWORD" ]; then
 | 
			
		||||
    _err "DEPLOY_KEYHELP_PASSWORD is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf DEPLOY_KEYHELP_PASSWORD "$DEPLOY_KEYHELP_PASSWORD"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_KEYHELP_DOMAIN_ID" ]; then
 | 
			
		||||
    _err "DEPLOY_KEYHELP_DOMAIN_ID is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf DEPLOY_KEYHELP_DOMAIN_ID "$DEPLOY_KEYHELP_DOMAIN_ID"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Optional DEPLOY_KEYHELP_ENFORCE_HTTPS
 | 
			
		||||
  _getdeployconf DEPLOY_KEYHELP_ENFORCE_HTTPS
 | 
			
		||||
  # set default values for DEPLOY_KEYHELP_ENFORCE_HTTPS
 | 
			
		||||
  [ -n "${DEPLOY_KEYHELP_ENFORCE_HTTPS}" ] || DEPLOY_KEYHELP_ENFORCE_HTTPS="1"
 | 
			
		||||
 | 
			
		||||
  _info "Logging in to keyhelp panel"
 | 
			
		||||
  username_encoded="$(printf "%s" "${DEPLOY_KEYHELP_USERNAME}" | _url_encode)"
 | 
			
		||||
  password_encoded="$(printf "%s" "${DEPLOY_KEYHELP_PASSWORD}" | _url_encode)"
 | 
			
		||||
  _H1="Content-Type: application/x-www-form-urlencoded"
 | 
			
		||||
  _response=$(_get "$DEPLOY_KEYHELP_BASEURL/index.php?submit=1&username=$username_encoded&password=$password_encoded" "TRUE")
 | 
			
		||||
  _cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)"
 | 
			
		||||
 | 
			
		||||
  # If cookies is not empty then logon successful
 | 
			
		||||
  if [ -z "$_cookie" ]; then
 | 
			
		||||
    _err "Fail to get cookie."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "cookie" "$_cookie"
 | 
			
		||||
 | 
			
		||||
  _info "Uploading certificate"
 | 
			
		||||
  _date=$(date +"%Y%m%d")
 | 
			
		||||
  encoded_key="$(_url_encode <"$_ckey")"
 | 
			
		||||
  encoded_ccert="$(_url_encode <"$_ccert")"
 | 
			
		||||
  encoded_cca="$(_url_encode <"$_cca")"
 | 
			
		||||
  certificate_name="$_cdomain-$_date"
 | 
			
		||||
 | 
			
		||||
  _request_body="submit=1&certificate_name=$certificate_name&add_type=upload&text_private_key=$encoded_key&text_certificate=$encoded_ccert&text_ca_certificate=$encoded_cca"
 | 
			
		||||
  _H1="Cookie: $_cookie"
 | 
			
		||||
  _response=$(_post "$_request_body" "$DEPLOY_KEYHELP_BASEURL/index.php?page=ssl_certificates&action=add" "" "POST")
 | 
			
		||||
  _message=$(echo "$_response" | grep -A 2 'message-body' | sed -n '/<div class="message-body ">/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
 | 
			
		||||
  _info "_message" "$_message"
 | 
			
		||||
  if [ -z "$_message" ]; then
 | 
			
		||||
    _err "Fail to upload certificate."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  for DOMAIN_ID in $DEPLOY_KEYHELP_DOMAIN_ID; do
 | 
			
		||||
    _info "Apply certificate to domain id $DOMAIN_ID"
 | 
			
		||||
    _response=$(_get "$DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit&id=$DOMAIN_ID")
 | 
			
		||||
    cert_value=$(echo "$_response" | grep "$certificate_name" | sed -n 's/.*value="\([^"]*\).*/\1/p')
 | 
			
		||||
    target_type=$(echo "$_response" | grep 'target_type' | grep 'checked' | sed -n 's/.*value="\([^"]*\).*/\1/p')
 | 
			
		||||
    if [ "$target_type" = "directory" ]; then
 | 
			
		||||
      path=$(echo "$_response" | awk '/name="path"/{getline; print}' | sed -n 's/.*value="\([^"]*\).*/\1/p')
 | 
			
		||||
    fi
 | 
			
		||||
    echo "$_response" | grep "is_prefer_https" | grep "checked" >/dev/null
 | 
			
		||||
    if [ $? -eq 0 ]; then
 | 
			
		||||
      is_prefer_https=1
 | 
			
		||||
    else
 | 
			
		||||
      is_prefer_https=0
 | 
			
		||||
    fi
 | 
			
		||||
    echo "$_response" | grep "hsts_enabled" | grep "checked" >/dev/null
 | 
			
		||||
    if [ $? -eq 0 ]; then
 | 
			
		||||
      hsts_enabled=1
 | 
			
		||||
    else
 | 
			
		||||
      hsts_enabled=0
 | 
			
		||||
    fi
 | 
			
		||||
    _debug "cert_value" "$cert_value"
 | 
			
		||||
    if [ -z "$cert_value" ]; then
 | 
			
		||||
      _err "Fail to get certificate id."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _request_body="submit=1&id=$DOMAIN_ID&target_type=$target_type&path=$path&is_prefer_https=$is_prefer_https&hsts_enabled=$hsts_enabled&certificate_type=custom&certificate_id=$cert_value&enforce_https=$DEPLOY_KEYHELP_ENFORCE_HTTPS"
 | 
			
		||||
    _response=$(_post "$_request_body" "$DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit" "" "POST")
 | 
			
		||||
    _message=$(echo "$_response" | grep -A 2 'message-body' | sed -n '/<div class="message-body ">/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
 | 
			
		||||
    _info "_message" "$_message"
 | 
			
		||||
    if [ -z "$_message" ]; then
 | 
			
		||||
      _err "Fail to apply certificate."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  _info "Domain $_cdomain certificate successfully deployed to KeyHelp Domain ID $DEPLOY_KEYHELP_DOMAIN_ID."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								deploy/netlify.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								deploy/netlify.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to deploy certificate to Netlify
 | 
			
		||||
# https://docs.netlify.com/api/get-started/#authentication
 | 
			
		||||
# https://open-api.netlify.com/#tag/sniCertificate
 | 
			
		||||
 | 
			
		||||
# This deployment required following variables
 | 
			
		||||
# export Netlify_ACCESS_TOKEN="Your Netlify Access Token"
 | 
			
		||||
# export Netlify_SITE_ID="Your Netlify Site ID"
 | 
			
		||||
 | 
			
		||||
# If have more than one SITE ID
 | 
			
		||||
# export Netlify_SITE_ID="SITE_ID_1 SITE_ID_2"
 | 
			
		||||
 | 
			
		||||
# returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
netlify_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 [ -z "$Netlify_ACCESS_TOKEN" ]; then
 | 
			
		||||
    _err "Netlify_ACCESS_TOKEN is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf Netlify_ACCESS_TOKEN "$Netlify_ACCESS_TOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$Netlify_SITE_ID" ]; then
 | 
			
		||||
    _err "Netlify_SITE_ID is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf Netlify_SITE_ID "$Netlify_SITE_ID"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Deploying certificate to Netlify..."
 | 
			
		||||
 | 
			
		||||
  ## upload certificate
 | 
			
		||||
  string_ccert=$(sed 's/$/\\n/' "$_ccert" | tr -d '\n')
 | 
			
		||||
  string_cca=$(sed 's/$/\\n/' "$_cca" | tr -d '\n')
 | 
			
		||||
  string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
 | 
			
		||||
 | 
			
		||||
  for SITE_ID in $Netlify_SITE_ID; do
 | 
			
		||||
    _request_body="{\"certificate\":\"$string_ccert\",\"key\":\"$string_key\",\"ca_certificates\":\"$string_cca\"}"
 | 
			
		||||
    _debug _request_body "$_request_body"
 | 
			
		||||
    _debug Netlify_ACCESS_TOKEN "$Netlify_ACCESS_TOKEN"
 | 
			
		||||
    export _H1="Authorization: Bearer $Netlify_ACCESS_TOKEN"
 | 
			
		||||
    _response=$(_post "$_request_body" "https://api.netlify.com/api/v1/sites/$SITE_ID/ssl" "" "POST" "application/json")
 | 
			
		||||
 | 
			
		||||
    if _contains "$_response" "\"error\""; then
 | 
			
		||||
      _err "Error in deploying $_cdomain certificate to Netlify SITE_ID $SITE_ID."
 | 
			
		||||
      _err "$_response"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _debug response "$_response"
 | 
			
		||||
    _info "Domain $_cdomain certificate successfully deployed to Netlify SITE_ID $SITE_ID."
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										254
									
								
								deploy/panos.sh
									
									
									
									
									
								
							
							
						
						
									
										254
									
								
								deploy/panos.sh
									
									
									
									
									
								
							@@ -7,13 +7,26 @@
 | 
			
		||||
#
 | 
			
		||||
# Firewall admin with superuser and IP address is required.
 | 
			
		||||
#
 | 
			
		||||
# export PANOS_USER=""  # required
 | 
			
		||||
# export PANOS_PASS=""  # required
 | 
			
		||||
# export PANOS_HOST=""  # required
 | 
			
		||||
# REQUIRED:
 | 
			
		||||
#     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
 | 
			
		||||
#    export PANOS_TEMPLATE_STACK="" # set a Template Stack if certificate should also be pushed automatically
 | 
			
		||||
#    export PANOS_VSYS="Shared"  # name of the vsys to import the certificate
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
_COMMIT_WAIT_INTERVAL=30   # query commit status every 30 seconds
 | 
			
		||||
_COMMIT_WAIT_ITERATIONS=20 # query commit status 20 times (20*30 = 600 seconds = 10 minutes)
 | 
			
		||||
 | 
			
		||||
# This function is to parse the XML response from the firewall
 | 
			
		||||
parse_response() {
 | 
			
		||||
  type=$2
 | 
			
		||||
  _debug "API Response: $1"
 | 
			
		||||
  if [ "$type" = 'keygen' ]; then
 | 
			
		||||
    status=$(echo "$1" | sed 's/^.*\(['\'']\)\([a-z]*\)'\''.*/\2/g')
 | 
			
		||||
    if [ "$status" = "success" ]; then
 | 
			
		||||
@@ -23,25 +36,52 @@ parse_response() {
 | 
			
		||||
      message="PAN-OS Key could not be set."
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
 | 
			
		||||
    message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
 | 
			
		||||
    if [ "$type" = 'commit' ]; then
 | 
			
		||||
      job_id=$(echo "$1" | sed 's/^.*\(<job>\)\(.*\)<\/job>.*/\2/g')
 | 
			
		||||
      _commit_job_id=$job_id
 | 
			
		||||
    elif [ "$type" = 'job_status' ]; then
 | 
			
		||||
      job_status=$(echo "$1" | tr -d '\n' | sed 's/^.*<result>\([^<]*\)<\/result>.*/\1/g')
 | 
			
		||||
      _commit_job_status=$job_status
 | 
			
		||||
    fi
 | 
			
		||||
    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 keygen, cert, key, commit
 | 
			
		||||
  _debug "**** Deploying $type *****"
 | 
			
		||||
  type=$1 # Types are keytest, keygen, cert, key, commit, job_status, push
 | 
			
		||||
  panos_url="https://$_panos_host/api/"
 | 
			
		||||
  export _H1="Content-Type: application/x-www-form-urlencoded"
 | 
			
		||||
 | 
			
		||||
  #Test API Key by performing a lookup
 | 
			
		||||
  if [ "$type" = 'keytest' ]; then
 | 
			
		||||
    _debug "**** Testing saved API Key ****"
 | 
			
		||||
    # 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
 | 
			
		||||
    _H1="Content-Type: application/x-www-form-urlencoded"
 | 
			
		||||
    _debug "**** Generating new API Key ****"
 | 
			
		||||
    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
 | 
			
		||||
    #Generate DEIM
 | 
			
		||||
    _debug "**** Deploying $type ****"
 | 
			
		||||
    #Generate DELIM
 | 
			
		||||
    delim="-----MultipartDelimiter$(date "+%s%N")"
 | 
			
		||||
    nl="\015\012"
 | 
			
		||||
    #Set Header
 | 
			
		||||
@@ -53,6 +93,12 @@ deployer() {
 | 
			
		||||
      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
 | 
			
		||||
      if [ "$_panos_vsys" ]; then
 | 
			
		||||
        content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl-vsys\"\r\n\r\n$_panos_vsys"
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
    if [ "$type" = 'key' ]; then
 | 
			
		||||
      panos_url="${panos_url}?type=import"
 | 
			
		||||
@@ -61,7 +107,13 @@ deployer() {
 | 
			
		||||
      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 "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
 | 
			
		||||
      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
 | 
			
		||||
      if [ "$_panos_vsys" ]; then
 | 
			
		||||
        content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl-vsys\"\r\n\r\n$_panos_vsys"
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
    #Close multipart
 | 
			
		||||
    content="$content${nl}--$delim--${nl}${nl}"
 | 
			
		||||
@@ -69,20 +121,44 @@ deployer() {
 | 
			
		||||
    content=$(printf %b "$content")
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Commit changes
 | 
			
		||||
  if [ "$type" = 'commit' ]; then
 | 
			
		||||
    export _H1="Content-Type: application/x-www-form-urlencoded"
 | 
			
		||||
    cmd=$(printf "%s" "<commit><partial><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
 | 
			
		||||
    content="type=commit&key=$_panos_key&cmd=$cmd"
 | 
			
		||||
    _debug "**** Committing changes ****"
 | 
			
		||||
    #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
 | 
			
		||||
 | 
			
		||||
  # Query job status
 | 
			
		||||
  if [ "$type" = 'job_status' ]; then
 | 
			
		||||
    echo "**** Querying job $_commit_job_id status ****"
 | 
			
		||||
    cmd=$(printf "%s" "<show><jobs><id>$_commit_job_id</id></jobs></show>" | _url_encode)
 | 
			
		||||
    content="type=op&key=$_panos_key&cmd=$cmd"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Push changes
 | 
			
		||||
  if [ "$type" = 'push' ]; then
 | 
			
		||||
    echo "**** Pushing changes ****"
 | 
			
		||||
    cmd=$(printf "%s" "<commit-all><template-stack><name>$_panos_template_stack</name><admin><member>$_panos_user</member></admin></template-stack></commit-all>" | _url_encode)
 | 
			
		||||
    content="type=commit&action=all&key=$_panos_key&cmd=$cmd"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  response=$(_post "$content" "$panos_url" "" "POST")
 | 
			
		||||
  parse_response "$response" "$type"
 | 
			
		||||
  # Saving response to variables
 | 
			
		||||
  response_status=$status
 | 
			
		||||
  #DEBUG
 | 
			
		||||
  _debug response_status "$response_status"
 | 
			
		||||
  if [ "$response_status" = "success" ]; then
 | 
			
		||||
    _debug "Successfully deployed $type"
 | 
			
		||||
    return 0
 | 
			
		||||
  elif [ "$_commit_job_status" ]; then
 | 
			
		||||
    _debug "Commit Job Status = $_commit_job_status"
 | 
			
		||||
  else
 | 
			
		||||
    _err "Deploy of type $type failed. Try deploying with --debug to troubleshoot."
 | 
			
		||||
    _debug "$message"
 | 
			
		||||
@@ -92,48 +168,134 @@ deployer() {
 | 
			
		||||
 | 
			
		||||
# This is the main function that will call the other functions to deploy everything.
 | 
			
		||||
panos_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  # PANOS ENV VAR check
 | 
			
		||||
  if [ -z "$PANOS_USER" ] || [ -z "$PANOS_PASS" ] || [ -z "$PANOS_HOST" ]; then
 | 
			
		||||
    _debug "No ENV variables found lets check for saved variables"
 | 
			
		||||
    _getdeployconf PANOS_USER
 | 
			
		||||
    _getdeployconf PANOS_PASS
 | 
			
		||||
    _getdeployconf PANOS_HOST
 | 
			
		||||
    _panos_user=$PANOS_USER
 | 
			
		||||
    _panos_pass=$PANOS_PASS
 | 
			
		||||
    _panos_host=$PANOS_HOST
 | 
			
		||||
    if [ -z "$_panos_user" ] && [ -z "$_panos_pass" ] && [ -z "$_panos_host" ]; then
 | 
			
		||||
      _err "No host, user and pass found.. If this is the first time deploying please set PANOS_HOST, PANOS_USER and PANOS_PASS in environment variables. Delete them after you have succesfully deployed certs."
 | 
			
		||||
      return 1
 | 
			
		||||
    else
 | 
			
		||||
      _debug "Using saved env variables."
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _debug "Detected ENV variables to be saved to the deploy conf."
 | 
			
		||||
    # Encrypt and save user
 | 
			
		||||
    _savedeployconf PANOS_USER "$PANOS_USER" 1
 | 
			
		||||
    _savedeployconf PANOS_PASS "$PANOS_PASS" 1
 | 
			
		||||
    _savedeployconf PANOS_HOST "$PANOS_HOST" 1
 | 
			
		||||
    _panos_user="$PANOS_USER"
 | 
			
		||||
    _panos_pass="$PANOS_PASS"
 | 
			
		||||
    _panos_host="$PANOS_HOST"
 | 
			
		||||
 | 
			
		||||
  # 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
 | 
			
		||||
  _debug "Let's use username and pass to generate token."
 | 
			
		||||
  if [ -z "$_panos_user" ] || [ -z "$_panos_pass" ] || [ -z "$_panos_host" ]; then
 | 
			
		||||
    _err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
 | 
			
		||||
 | 
			
		||||
  # 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
 | 
			
		||||
 | 
			
		||||
  # PANOS_TEMPLATE_STACK
 | 
			
		||||
  if [ "$PANOS_TEMPLATE_STACK" ]; then
 | 
			
		||||
    _debug "Detected ENV variable PANOS_TEMPLATE_STACK. Saving to file."
 | 
			
		||||
    _savedeployconf PANOS_TEMPLATE_STACK "$PANOS_TEMPLATE_STACK" 1
 | 
			
		||||
  else
 | 
			
		||||
    _debug "Attempting to load variable PANOS_TEMPLATE_STACK from file."
 | 
			
		||||
    _getdeployconf PANOS_TEMPLATE_STACK
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # PANOS_TEMPLATE_STACK
 | 
			
		||||
  if [ "$PANOS_VSYS" ]; then
 | 
			
		||||
    _debug "Detected ENV variable PANOS_VSYS. Saving to file."
 | 
			
		||||
    _savedeployconf PANOS_VSYS "$PANOS_VSYS" 1
 | 
			
		||||
  else
 | 
			
		||||
    _debug "Attempting to load variable PANOS_VSYS from file."
 | 
			
		||||
    _getdeployconf PANOS_VSYS
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #Store variables
 | 
			
		||||
  _panos_host=$PANOS_HOST
 | 
			
		||||
  _panos_user=$PANOS_USER
 | 
			
		||||
  _panos_pass=$PANOS_PASS
 | 
			
		||||
  _panos_template=$PANOS_TEMPLATE
 | 
			
		||||
  _panos_template_stack=$PANOS_TEMPLATE_STACK
 | 
			
		||||
  _panos_vsys=$PANOS_VSYS
 | 
			
		||||
 | 
			
		||||
  #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
 | 
			
		||||
    _debug "Getting PANOS KEY"
 | 
			
		||||
    deployer keygen
 | 
			
		||||
    # Generate a new API key if no valid API key is found
 | 
			
		||||
    if [ -z "$_panos_key" ]; then
 | 
			
		||||
      _err "Missing apikey."
 | 
			
		||||
      _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
 | 
			
		||||
      if [ "$_panos_template_stack" ]; then
 | 
			
		||||
        # try to get job status for 20 times in 30 sec interval
 | 
			
		||||
        i=0
 | 
			
		||||
        while [ "$i" -lt $_COMMIT_WAIT_ITERATIONS ]; do
 | 
			
		||||
          deployer job_status
 | 
			
		||||
          if [ "$_commit_job_status" = "OK" ]; then
 | 
			
		||||
            echo "Commit finished!"
 | 
			
		||||
            break
 | 
			
		||||
          fi
 | 
			
		||||
          sleep $_COMMIT_WAIT_INTERVAL
 | 
			
		||||
          i=$((i + 1))
 | 
			
		||||
        done
 | 
			
		||||
        deployer push
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										130
									
								
								deploy/proxmoxbs.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								deploy/proxmoxbs.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Deploy certificates to a proxmox backup server using the API.
 | 
			
		||||
#
 | 
			
		||||
# Environment variables that can be set are:
 | 
			
		||||
# `DEPLOY_PROXMOXBS_SERVER`: The hostname of the proxmox backup server. Defaults to
 | 
			
		||||
#                            _cdomain.
 | 
			
		||||
# `DEPLOY_PROXMOXBS_SERVER_PORT`: The port number the management interface is on.
 | 
			
		||||
#                                 Defaults to 8007.
 | 
			
		||||
# `DEPLOY_PROXMOXBS_USER`: The user we'll connect as. Defaults to root.
 | 
			
		||||
# `DEPLOY_PROXMOXBS_USER_REALM`: The authentication realm the user authenticates
 | 
			
		||||
#                                with. Defaults to pam.
 | 
			
		||||
# `DEPLOY_PROXMOXBS_API_TOKEN_NAME`: The name of the API token created for the
 | 
			
		||||
#                                    user account. Defaults to acme.
 | 
			
		||||
# `DEPLOY_PROXMOXBS_API_TOKEN_KEY`: The API token. Required.
 | 
			
		||||
 | 
			
		||||
proxmoxbs_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_PROXMOXBS_SERVER
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXBS_SERVER" ]; then
 | 
			
		||||
    _target_hostname="$_cdomain"
 | 
			
		||||
  else
 | 
			
		||||
    _target_hostname="$DEPLOY_PROXMOXBS_SERVER"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXBS_SERVER "$DEPLOY_PROXMOXBS_SERVER"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXBS_SERVER "$_target_hostname"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXBS_SERVER_PORT
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXBS_SERVER_PORT" ]; then
 | 
			
		||||
    _target_port="8007"
 | 
			
		||||
  else
 | 
			
		||||
    _target_port="$DEPLOY_PROXMOXBS_SERVER_PORT"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXBS_SERVER_PORT "$DEPLOY_PROXMOXBS_SERVER_PORT"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXBS_SERVER_PORT "$_target_port"
 | 
			
		||||
 | 
			
		||||
  # Complete URL.
 | 
			
		||||
  _target_url="https://${_target_hostname}:${_target_port}/api2/json/nodes/localhost/certificates/custom"
 | 
			
		||||
  _debug TARGET_URL "$_target_url"
 | 
			
		||||
 | 
			
		||||
  # More "sane" defaults.
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXBS_USER
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXBS_USER" ]; then
 | 
			
		||||
    _proxmoxbs_user="root"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxbs_user="$DEPLOY_PROXMOXBS_USER"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXBS_USER "$DEPLOY_PROXMOXBS_USER"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXBS_USER "$_proxmoxbs_user"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXBS_USER_REALM
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXBS_USER_REALM" ]; then
 | 
			
		||||
    _proxmoxbs_user_realm="pam"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxbs_user_realm="$DEPLOY_PROXMOXBS_USER_REALM"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXBS_USER_REALM "$DEPLOY_PROXMOXBS_USER_REALM"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXBS_USER_REALM "$_proxmoxbs_user_realm"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXBS_API_TOKEN_NAME
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXBS_API_TOKEN_NAME" ]; then
 | 
			
		||||
    _proxmoxbs_api_token_name="acme"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxbs_api_token_name="$DEPLOY_PROXMOXBS_API_TOKEN_NAME"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXBS_API_TOKEN_NAME "$DEPLOY_PROXMOXBS_API_TOKEN_NAME"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXBS_API_TOKEN_NAME "$_proxmoxbs_api_token_name"
 | 
			
		||||
 | 
			
		||||
  # This is required.
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXBS_API_TOKEN_KEY
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXBS_API_TOKEN_KEY" ]; then
 | 
			
		||||
    _err "API key not provided."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxbs_api_token_key="$DEPLOY_PROXMOXBS_API_TOKEN_KEY"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXBS_API_TOKEN_KEY "$DEPLOY_PROXMOXBS_API_TOKEN_KEY"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXBS_API_TOKEN_KEY "$_proxmoxbs_api_token_key"
 | 
			
		||||
 | 
			
		||||
  # PBS API Token header value. Used in "Authorization: PBSAPIToken".
 | 
			
		||||
  _proxmoxbs_header_api_token="${_proxmoxbs_user}@${_proxmoxbs_user_realm}!${_proxmoxbs_api_token_name}:${_proxmoxbs_api_token_key}"
 | 
			
		||||
  _debug2 "Auth Header" "$_proxmoxbs_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":"localhost",
 | 
			
		||||
  "restart":true,
 | 
			
		||||
  "force":true
 | 
			
		||||
}
 | 
			
		||||
HEREDOC
 | 
			
		||||
  )
 | 
			
		||||
  _debug2 Payload "$_json_payload"
 | 
			
		||||
 | 
			
		||||
  _info "Push certificates to server"
 | 
			
		||||
  export HTTPS_INSECURE=1
 | 
			
		||||
  export _H1="Authorization: PBSAPIToken=${_proxmoxbs_header_api_token}"
 | 
			
		||||
  response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
 | 
			
		||||
  _retval=$?
 | 
			
		||||
  if [ "${_retval}" -eq 0 ]; then
 | 
			
		||||
    _debug3 response "$response"
 | 
			
		||||
    _info "Certificate successfully deployed"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Certificate deployment failed"
 | 
			
		||||
    _debug "Response" "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -99,11 +99,11 @@ proxmoxve_deploy() {
 | 
			
		||||
    _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
 | 
			
		||||
  _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
 | 
			
		||||
  _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
 | 
			
		||||
@@ -124,9 +124,19 @@ HEREDOC
 | 
			
		||||
  )
 | 
			
		||||
  _debug2 Payload "$_json_payload"
 | 
			
		||||
 | 
			
		||||
  # Push certificates to server.
 | 
			
		||||
  export _HTTPS_INSECURE=1
 | 
			
		||||
  _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"
 | 
			
		||||
  response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
 | 
			
		||||
  _retval=$?
 | 
			
		||||
  if [ "${_retval}" -eq 0 ]; then
 | 
			
		||||
    _debug3 response "$response"
 | 
			
		||||
    _info "Certificate successfully deployed"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Certificate deployment failed"
 | 
			
		||||
    _debug "Response" "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -137,17 +137,18 @@ routeros_deploy() {
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=$ROUTER_OS_USERNAME \
 | 
			
		||||
  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=\\\"\\\";\
 | 
			
		||||
\n/certificate import file-name=\\\"$_cdomain.cer\\\" passphrase=\\\"\\\";\
 | 
			
		||||
\n/certificate import file-name=\\\"$_cdomain.key\\\" passphrase=\\\"\\\";\
 | 
			
		||||
\ndelay 1;\
 | 
			
		||||
\n/file remove $_cdomain.cer;\
 | 
			
		||||
\n/file remove $_cdomain.key;\
 | 
			
		||||
\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;\
 | 
			
		||||
@@ -158,11 +159,11 @@ source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _ssh_remote_cmd "/system script run \"LE Cert Deploy - $_cdomain\""; then
 | 
			
		||||
  if ! _ssh_remote_cmd "/system script run \"LECertDeploy-$_cdomain\""; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _ssh_remote_cmd "/system script remove \"LE Cert Deploy - $_cdomain\""; then
 | 
			
		||||
  if ! _ssh_remote_cmd "/system script remove \"LECertDeploy-$_cdomain\""; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										200
									
								
								deploy/ruckus.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										200
									
								
								deploy/ruckus.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,200 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is a script to deploy cert to Ruckus ZoneDirector / Unleashed.
 | 
			
		||||
#
 | 
			
		||||
# Public domain, 2024, Tony Rielly <https://github.com/ms264556>
 | 
			
		||||
#
 | 
			
		||||
# ```sh
 | 
			
		||||
# acme.sh --deploy -d ruckus.example.com --deploy-hook ruckus
 | 
			
		||||
# ```
 | 
			
		||||
#
 | 
			
		||||
# Then you need to set the environment variables for the
 | 
			
		||||
# deploy script to work.
 | 
			
		||||
#
 | 
			
		||||
# ```sh
 | 
			
		||||
# export RUCKUS_HOST=myruckus.example.com
 | 
			
		||||
# export RUCKUS_USER=myruckususername
 | 
			
		||||
# export RUCKUS_PASS=myruckuspassword
 | 
			
		||||
#
 | 
			
		||||
# acme.sh --deploy -d myruckus.example.com --deploy-hook ruckus
 | 
			
		||||
# ```
 | 
			
		||||
#
 | 
			
		||||
# returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
ruckus_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  _err_code=0
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf RUCKUS_HOST
 | 
			
		||||
  _getdeployconf RUCKUS_USER
 | 
			
		||||
  _getdeployconf RUCKUS_PASS
 | 
			
		||||
 | 
			
		||||
  if [ -z "$RUCKUS_HOST" ]; then
 | 
			
		||||
    _debug "Using _cdomain as RUCKUS_HOST, please set if not correct."
 | 
			
		||||
    RUCKUS_HOST="$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$RUCKUS_USER" ]; then
 | 
			
		||||
    _err "Need to set the env variable RUCKUS_USER"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$RUCKUS_PASS" ]; then
 | 
			
		||||
    _err "Need to set the env variable RUCKUS_PASS"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedeployconf RUCKUS_HOST "$RUCKUS_HOST"
 | 
			
		||||
  _savedeployconf RUCKUS_USER "$RUCKUS_USER"
 | 
			
		||||
  _savedeployconf RUCKUS_PASS "$RUCKUS_PASS"
 | 
			
		||||
 | 
			
		||||
  _debug RUCKUS_HOST "$RUCKUS_HOST"
 | 
			
		||||
  _debug RUCKUS_USER "$RUCKUS_USER"
 | 
			
		||||
  _secure_debug RUCKUS_PASS "$RUCKUS_PASS"
 | 
			
		||||
 | 
			
		||||
  export ACME_HTTP_NO_REDIRECTS=1
 | 
			
		||||
 | 
			
		||||
  _info "Discovering the login URL"
 | 
			
		||||
  _get "https://$RUCKUS_HOST" >/dev/null
 | 
			
		||||
  _login_url="$(_response_header 'Location')"
 | 
			
		||||
  if [ -n "$_login_url" ]; then
 | 
			
		||||
    _login_path=$(echo "$_login_url" | sed 's|https\?://[^/]\+||')
 | 
			
		||||
    if [ -z "$_login_path" ]; then
 | 
			
		||||
      # redirect was to a different host
 | 
			
		||||
      _err "Connection failed: redirected to a different host. Configure Unleashed with a Preferred Master or Management Interface."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "${_login_url}" ]; then
 | 
			
		||||
    _err "Connection failed: couldn't find login page."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _base_url=$(dirname "$_login_url")
 | 
			
		||||
  _login_page=$(basename "$_login_url")
 | 
			
		||||
 | 
			
		||||
  if [ "$_login_page" = "index.html" ]; then
 | 
			
		||||
    _err "Connection temporarily unavailable: Unleashed Rebuilding."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$_login_page" = "wizard.jsp" ]; then
 | 
			
		||||
    _err "Connection failed: Setup Wizard not complete."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Login"
 | 
			
		||||
  _username_encoded="$(printf "%s" "$RUCKUS_USER" | _url_encode)"
 | 
			
		||||
  _password_encoded="$(printf "%s" "$RUCKUS_PASS" | _url_encode)"
 | 
			
		||||
  _login_query="$(printf "%s" "username=${_username_encoded}&password=${_password_encoded}&ok=Log+In")"
 | 
			
		||||
  _post "$_login_query" "$_login_url" >/dev/null
 | 
			
		||||
 | 
			
		||||
  _login_code="$(_response_code)"
 | 
			
		||||
  if [ "$_login_code" = "200" ]; then
 | 
			
		||||
    _err "Login failed: incorrect credentials."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Collect Session Cookie"
 | 
			
		||||
  _H1="Cookie: $(_response_cookie)"
 | 
			
		||||
  export _H1
 | 
			
		||||
  _info "Collect CSRF Token"
 | 
			
		||||
  _H2="X-CSRF-Token: $(_response_header 'HTTP_X_CSRF_TOKEN')"
 | 
			
		||||
  export _H2
 | 
			
		||||
 | 
			
		||||
  if _isRSA "$_ckey" >/dev/null 2>&1; then
 | 
			
		||||
    _debug "Using RSA certificate."
 | 
			
		||||
  else
 | 
			
		||||
    _info "Verifying ECC certificate support."
 | 
			
		||||
 | 
			
		||||
    _ul_version="$(_get_unleashed_version)"
 | 
			
		||||
    if [ -z "$_ul_version" ]; then
 | 
			
		||||
      _err "Your controller doesn't support ECC certificates. Please deploy an RSA certificate."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _ul_version_major="$(echo "$_ul_version" | cut -d . -f 1)"
 | 
			
		||||
    _ul_version_minor="$(echo "$_ul_version" | cut -d . -f 2)"
 | 
			
		||||
    if [ "$_ul_version_major" -lt "200" ]; then
 | 
			
		||||
      _err "ZoneDirector doesn't support ECC certificates. Please deploy an RSA certificate."
 | 
			
		||||
      return 1
 | 
			
		||||
    elif [ "$_ul_version_minor" -lt "13" ]; then
 | 
			
		||||
      _err "Unleashed $_ul_version_major.$_ul_version_minor doesn't support ECC certificates. Please deploy an RSA certificate or upgrade to Unleashed 200.13+."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _debug "ECC certificates OK for Unleashed $_ul_version_major.$_ul_version_minor."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Uploading certificate"
 | 
			
		||||
  _post_upload "uploadcert" "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _info "Uploading private key"
 | 
			
		||||
  _post_upload "uploadprivatekey" "$_ckey"
 | 
			
		||||
 | 
			
		||||
  _info "Replacing certificate"
 | 
			
		||||
  _replace_cert_ajax='<ajax-request action="docmd" comp="system" updater="rid.0.5" xcmd="replace-cert" checkAbility="6" timeout="-1"><xcmd cmd="replace-cert" cn="'$RUCKUS_HOST'"/></ajax-request>'
 | 
			
		||||
  _post "$_replace_cert_ajax" "$_base_url/_cmdstat.jsp" >/dev/null
 | 
			
		||||
 | 
			
		||||
  _info "Rebooting"
 | 
			
		||||
  _cert_reboot_ajax='<ajax-request action="docmd" comp="worker" updater="rid.0.5" xcmd="cert-reboot" checkAbility="6"><xcmd cmd="cert-reboot" action="undefined"/></ajax-request>'
 | 
			
		||||
  _post "$_cert_reboot_ajax" "$_base_url/_cmdstat.jsp" >/dev/null
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_response_code() {
 | 
			
		||||
  _egrep_o <"$HTTP_HEADER" "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n" | _egrep_o "^[0-9]*"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_response_header() {
 | 
			
		||||
  grep <"$HTTP_HEADER" -i "^$1:" | cut -d ':' -f 2- | tr -d "\r\n\t "
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_response_cookie() {
 | 
			
		||||
  _response_header 'Set-Cookie' | sed 's/;.*//'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_unleashed_version() {
 | 
			
		||||
  _post '<ajax-request action="getstat" comp="system"><sysinfo/></ajax-request>' "$_base_url/_cmdstat.jsp" | _egrep_o "version-num=\"[^\"]*\"" | cut -d '"' -f 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_post_upload() {
 | 
			
		||||
  _post_action="$1"
 | 
			
		||||
  _post_file="$2"
 | 
			
		||||
 | 
			
		||||
  _post_boundary="----FormBoundary$(date "+%s%N")"
 | 
			
		||||
 | 
			
		||||
  _post_data="$({
 | 
			
		||||
    printf -- "--%s\r\n" "$_post_boundary"
 | 
			
		||||
    printf -- "Content-Disposition: form-data; name=\"u\"; filename=\"%s\"\r\n" "$_post_action"
 | 
			
		||||
    printf -- "Content-Type: application/octet-stream\r\n\r\n"
 | 
			
		||||
    printf -- "%s\r\n" "$(cat "$_post_file")"
 | 
			
		||||
 | 
			
		||||
    printf -- "--%s\r\n" "$_post_boundary"
 | 
			
		||||
    printf -- "Content-Disposition: form-data; name=\"action\"\r\n\r\n"
 | 
			
		||||
    printf -- "%s\r\n" "$_post_action"
 | 
			
		||||
 | 
			
		||||
    printf -- "--%s\r\n" "$_post_boundary"
 | 
			
		||||
    printf -- "Content-Disposition: form-data; name=\"callback\"\r\n\r\n"
 | 
			
		||||
    printf -- "%s\r\n" "uploader_$_post_action"
 | 
			
		||||
 | 
			
		||||
    printf -- "--%s--\r\n\r\n" "$_post_boundary"
 | 
			
		||||
  })"
 | 
			
		||||
 | 
			
		||||
  _post "$_post_data" "$_base_url/_upload.jsp?request_type=xhr" "" "" "multipart/form-data; boundary=$_post_boundary" >/dev/null
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										409
									
								
								deploy/ssh.sh
									
									
									
									
									
								
							
							
						
						
									
										409
									
								
								deploy/ssh.sh
									
									
									
									
									
								
							@@ -14,7 +14,7 @@
 | 
			
		||||
# The following examples are for QNAP NAS running QTS 4.2
 | 
			
		||||
# export DEPLOY_SSH_CMD=""  # defaults to "ssh -T"
 | 
			
		||||
# 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_CERTFILE="/etc/stunnel/stunnel.pem"
 | 
			
		||||
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
 | 
			
		||||
@@ -23,6 +23,8 @@
 | 
			
		||||
# 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 #####################
 | 
			
		||||
 | 
			
		||||
@@ -42,72 +44,134 @@ ssh_deploy() {
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # 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 "$Le_Deploy_ssh_user" ]; then
 | 
			
		||||
      _err "DEPLOY_SSH_USER not defined."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    Le_Deploy_ssh_user="$DEPLOY_SSH_USER"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_user "$Le_Deploy_ssh_user"
 | 
			
		||||
    _err "DEPLOY_SSH_USER not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
 | 
			
		||||
 | 
			
		||||
  # SERVER is optional. If not provided then use _cdomain
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_server DEPLOY_SSH_SERVER
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_SERVER
 | 
			
		||||
  _debug2 DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_SERVER" ]; then
 | 
			
		||||
    Le_Deploy_ssh_server="$DEPLOY_SSH_SERVER"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_server "$Le_Deploy_ssh_server"
 | 
			
		||||
  elif [ -z "$Le_Deploy_ssh_server" ]; then
 | 
			
		||||
    Le_Deploy_ssh_server="$_cdomain"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_SERVER" ]; then
 | 
			
		||||
    DEPLOY_SSH_SERVER="$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
 | 
			
		||||
 | 
			
		||||
  # CMD is optional. If not provided then use ssh
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_cmd DEPLOY_SSH_CMD
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_CMD
 | 
			
		||||
  _debug2 DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CMD" ]; then
 | 
			
		||||
    Le_Deploy_ssh_cmd="$DEPLOY_SSH_CMD"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_cmd "$Le_Deploy_ssh_cmd"
 | 
			
		||||
  elif [ -z "$Le_Deploy_ssh_cmd" ]; then
 | 
			
		||||
    Le_Deploy_ssh_cmd="ssh -T"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_CMD" ]; then
 | 
			
		||||
    DEPLOY_SSH_CMD="ssh -T"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
 | 
			
		||||
 | 
			
		||||
  # BACKUP is optional. If not provided then default to previously saved value or yes.
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_backup DEPLOY_SSH_BACKUP
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_BACKUP
 | 
			
		||||
  _debug2 DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
 | 
			
		||||
  if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then
 | 
			
		||||
    Le_Deploy_ssh_backup="no"
 | 
			
		||||
  elif [ -z "$Le_Deploy_ssh_backup" ] || [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
    Le_Deploy_ssh_backup="yes"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_BACKUP" ]; then
 | 
			
		||||
    DEPLOY_SSH_BACKUP="yes"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedomainconf Le_Deploy_ssh_backup "$Le_Deploy_ssh_backup"
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
 | 
			
		||||
 | 
			
		||||
  # 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 [ -n "$DEPLOY_SSH_BACKUP_PATH" ]; then
 | 
			
		||||
    Le_Deploy_ssh_backup_path="$DEPLOY_SSH_BACKUP_PATH"
 | 
			
		||||
  elif [ -z "$Le_Deploy_ssh_backup_path" ]; then
 | 
			
		||||
    Le_Deploy_ssh_backup_path=".acme_ssh_deploy"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_BACKUP_PATH" ]; then
 | 
			
		||||
    DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedomainconf Le_Deploy_ssh_backup_path "$Le_Deploy_ssh_backup_path"
 | 
			
		||||
  _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 [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
    Le_Deploy_ssh_multi_call="yes"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_multi_call "$Le_Deploy_ssh_multi_call"
 | 
			
		||||
  elif [ "$DEPLOY_SSH_MULTI_CALL" = "no" ]; then
 | 
			
		||||
    Le_Deploy_ssh_multi_call=""
 | 
			
		||||
    _cleardomainconf Le_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.
 | 
			
		||||
  # 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
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _deploy_ssh_servers=$Le_Deploy_ssh_server
 | 
			
		||||
  for Le_Deploy_ssh_server in $_deploy_ssh_servers; do
 | 
			
		||||
  # CERTFILE is optional.
 | 
			
		||||
  # 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
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # CAFILE is optional.
 | 
			
		||||
  # 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
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # FULLCHAIN is optional.
 | 
			
		||||
  # 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
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # REMOTE_CMD is optional.
 | 
			
		||||
  # 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
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # USE_SCP is optional. If not provided then default to previously saved
 | 
			
		||||
  # value (which may be undefined... equivalent to "no").
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_USE_SCP
 | 
			
		||||
  _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
 | 
			
		||||
}
 | 
			
		||||
@@ -117,16 +181,25 @@ _ssh_deploy() {
 | 
			
		||||
  _cmdstr=""
 | 
			
		||||
  _backupprefix=""
 | 
			
		||||
  _backupdir=""
 | 
			
		||||
  _local_cert_file=""
 | 
			
		||||
  _local_ca_file=""
 | 
			
		||||
  _local_full_file=""
 | 
			
		||||
 | 
			
		||||
  _info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server"
 | 
			
		||||
  if [ "$Le_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
 | 
			
		||||
  case $DEPLOY_SSH_SERVER in
 | 
			
		||||
  *:*)
 | 
			
		||||
    _host=${DEPLOY_SSH_SERVER%:*}
 | 
			
		||||
    _port=${DEPLOY_SSH_SERVER##*:}
 | 
			
		||||
    ;;
 | 
			
		||||
  *)
 | 
			
		||||
    _host=$DEPLOY_SSH_SERVER
 | 
			
		||||
    _port=
 | 
			
		||||
    ;;
 | 
			
		||||
  esac
 | 
			
		||||
 | 
			
		||||
  if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    _backupprefix="$Le_Deploy_ssh_backup_path/$_cdomain-backup"
 | 
			
		||||
  _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
 | 
			
		||||
    # than 180 days (15552000 seconds).
 | 
			
		||||
@@ -138,7 +211,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
 | 
			
		||||
    _cmdstr="mkdir -p $_backupdir; $_cmdstr"
 | 
			
		||||
    _info "Backup of old certificate files will be placed in remote directory $_backupdir"
 | 
			
		||||
    _info "Backup directories erased after 180 days."
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
@@ -146,129 +219,184 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # KEYFILE is optional.
 | 
			
		||||
  # If provided then private key will be copied to provided filename.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_KEYFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
    Le_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
 | 
			
		||||
    if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir >/dev/null;"
 | 
			
		||||
      _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 certificate into file.
 | 
			
		||||
    _cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;"
 | 
			
		||||
    _info "will copy private key to remote file $Le_Deploy_ssh_keyfile"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
 | 
			
		||||
    # 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
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  # CERTFILE is optional.
 | 
			
		||||
  # If provided then certificate will be copied or appended to provided filename.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_CERTFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
    Le_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 [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
      # if filename is same as previous file then append.
 | 
			
		||||
      _pipe=">>"
 | 
			
		||||
    elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir >/dev/null;"
 | 
			
		||||
      _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.
 | 
			
		||||
    _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;"
 | 
			
		||||
    _info "will copy certificate to remote file $Le_Deploy_ssh_certfile"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
    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
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  # CAFILE is optional.
 | 
			
		||||
  # If provided then CA intermediate certificate will be copied or appended to provided filename.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_CAFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CAFILE" ]; then
 | 
			
		||||
    Le_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 [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ] ||
 | 
			
		||||
      [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
      # if filename is same as previous file then append.
 | 
			
		||||
      _pipe=">>"
 | 
			
		||||
    elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir >/dev/null;"
 | 
			
		||||
      _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.
 | 
			
		||||
    _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;"
 | 
			
		||||
    _info "will copy CA file to remote file $Le_Deploy_ssh_cafile"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
    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
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  # FULLCHAIN is optional.
 | 
			
		||||
  # If provided then fullchain certificate will be copied or appended to provided filename.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_FULLCHAIN
 | 
			
		||||
  _debug2 DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
 | 
			
		||||
    Le_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 [ "$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 [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir >/dev/null;"
 | 
			
		||||
      _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.
 | 
			
		||||
    _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;"
 | 
			
		||||
    _info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
    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
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  # REMOTE_CMD is optional.
 | 
			
		||||
  # If provided then this command will be executed on remote host.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_REMOTE_CMD
 | 
			
		||||
  _debug2 DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
 | 
			
		||||
    Le_Deploy_ssh_remote_cmd="$DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_remote_cmd "$Le_Deploy_ssh_remote_cmd"
 | 
			
		||||
  # cleanup local files if any
 | 
			
		||||
  if [ -f "$_local_cert_file" ]; then
 | 
			
		||||
    rm -f "$_local_cert_file"
 | 
			
		||||
  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"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
  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
 | 
			
		||||
@@ -282,17 +410,25 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
 | 
			
		||||
      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"
 | 
			
		||||
  _info "Submitting sequence of commands to remote server by $_ssh_cmd"
 | 
			
		||||
 | 
			
		||||
  # quotations in bash cmd below intended.  Squash travis spellcheck error
 | 
			
		||||
  # shellcheck disable=SC2029
 | 
			
		||||
  $Le_Deploy_ssh_cmd "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmd'"
 | 
			
		||||
  $_ssh_cmd "$DEPLOY_SSH_USER@$_host" sh -c "'$_cmd'"
 | 
			
		||||
  _err_code="$?"
 | 
			
		||||
 | 
			
		||||
  if [ "$_err_code" != "0" ]; then
 | 
			
		||||
@@ -301,3 +437,26 @@ _ssh_remote_cmd() {
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,46 +10,89 @@
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
strongswan_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _cdomain="${1}"
 | 
			
		||||
  _ckey="${2}"
 | 
			
		||||
  _ccert="${3}"
 | 
			
		||||
  _cca="${4}"
 | 
			
		||||
  _cfullchain="${5}"
 | 
			
		||||
  _info "Using strongswan"
 | 
			
		||||
 | 
			
		||||
  if [ -x /usr/sbin/ipsec ]; then
 | 
			
		||||
    _ipsec=/usr/sbin/ipsec
 | 
			
		||||
  elif [ -x /usr/sbin/strongswan ]; then
 | 
			
		||||
    _ipsec=/usr/sbin/strongswan
 | 
			
		||||
  elif [ -x /usr/local/sbin/ipsec ]; then
 | 
			
		||||
    _ipsec=/usr/local/sbin/ipsec
 | 
			
		||||
  else
 | 
			
		||||
  if _exists ipsec; then
 | 
			
		||||
    _ipsec=ipsec
 | 
			
		||||
  elif _exists strongswan; then
 | 
			
		||||
    _ipsec=strongswan
 | 
			
		||||
  fi
 | 
			
		||||
  if _exists swanctl; then
 | 
			
		||||
    _swanctl=swanctl
 | 
			
		||||
  fi
 | 
			
		||||
  # For legacy stroke mode
 | 
			
		||||
  if [ -n "${_ipsec}" ]; then
 | 
			
		||||
    _info "${_ipsec} command detected"
 | 
			
		||||
    _confdir=$(${_ipsec} --confdir)
 | 
			
		||||
    if [ -z "${_confdir}" ]; then
 | 
			
		||||
      _err "no strongswan --confdir is detected"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _info _confdir "${_confdir}"
 | 
			
		||||
    __deploy_cert "$@" "stroke" "${_confdir}"
 | 
			
		||||
    ${_ipsec} reload
 | 
			
		||||
  fi
 | 
			
		||||
  # For modern vici mode
 | 
			
		||||
  if [ -n "${_swanctl}" ]; then
 | 
			
		||||
    _info "${_swanctl} command detected"
 | 
			
		||||
    for _dir in /usr/local/etc/swanctl /etc/swanctl /etc/strongswan/swanctl; do
 | 
			
		||||
      if [ -d ${_dir} ]; then
 | 
			
		||||
        _confdir=${_dir}
 | 
			
		||||
        _info _confdir "${_confdir}"
 | 
			
		||||
        break
 | 
			
		||||
      fi
 | 
			
		||||
    done
 | 
			
		||||
    if [ -z "${_confdir}" ]; then
 | 
			
		||||
      _err "no swanctl config dir is found"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    __deploy_cert "$@" "vici" "${_confdir}"
 | 
			
		||||
    ${_swanctl} --load-creds
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "${_swanctl}" ] && [ -z "${_ipsec}" ]; then
 | 
			
		||||
    _err "no strongswan or ipsec command is detected"
 | 
			
		||||
    _err "no swanctl is detected"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info _ipsec "$_ipsec"
 | 
			
		||||
 | 
			
		||||
  _confdir=$($_ipsec --confdir)
 | 
			
		||||
  if [ $? -ne 0 ] || [ -z "$_confdir" ]; then
 | 
			
		||||
    _err "no strongswan --confdir is detected"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info _confdir "$_confdir"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  cat "$_ckey" >"${_confdir}/ipsec.d/private/$(basename "$_ckey")"
 | 
			
		||||
  cat "$_ccert" >"${_confdir}/ipsec.d/certs/$(basename "$_ccert")"
 | 
			
		||||
  cat "$_cca" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cca")"
 | 
			
		||||
  cat "$_cfullchain" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cfullchain")"
 | 
			
		||||
 | 
			
		||||
  $_ipsec reload
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
__deploy_cert() {
 | 
			
		||||
  _cdomain="${1}"
 | 
			
		||||
  _ckey="${2}"
 | 
			
		||||
  _ccert="${3}"
 | 
			
		||||
  _cca="${4}"
 | 
			
		||||
  _cfullchain="${5}"
 | 
			
		||||
  _swan_mode="${6}"
 | 
			
		||||
  _confdir="${7}"
 | 
			
		||||
  _debug _cdomain "${_cdomain}"
 | 
			
		||||
  _debug _ckey "${_ckey}"
 | 
			
		||||
  _debug _ccert "${_ccert}"
 | 
			
		||||
  _debug _cca "${_cca}"
 | 
			
		||||
  _debug _cfullchain "${_cfullchain}"
 | 
			
		||||
  _debug _swan_mode "${_swan_mode}"
 | 
			
		||||
  _debug _confdir "${_confdir}"
 | 
			
		||||
  if [ "${_swan_mode}" = "vici" ]; then
 | 
			
		||||
    _dir_private="private"
 | 
			
		||||
    _dir_cert="x509"
 | 
			
		||||
    _dir_ca="x509ca"
 | 
			
		||||
  elif [ "${_swan_mode}" = "stroke" ]; then
 | 
			
		||||
    _dir_private="ipsec.d/private"
 | 
			
		||||
    _dir_cert="ipsec.d/certs"
 | 
			
		||||
    _dir_ca="ipsec.d/cacerts"
 | 
			
		||||
  else
 | 
			
		||||
    _err "unknown StrongSwan mode ${_swan_mode}"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  cat "${_ckey}" >"${_confdir}/${_dir_private}/$(basename "${_ckey}")"
 | 
			
		||||
  cat "${_ccert}" >"${_confdir}/${_dir_cert}/$(basename "${_ccert}")"
 | 
			
		||||
  cat "${_cca}" >"${_confdir}/${_dir_ca}/$(basename "${_cca}")"
 | 
			
		||||
  if [ "${_swan_mode}" = "stroke" ]; then
 | 
			
		||||
    cat "${_cfullchain}" >"${_confdir}/${_dir_ca}/$(basename "${_cfullchain}")"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,34 +1,53 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# Here is a script to deploy cert to Synology DSM
 | 
			
		||||
#
 | 
			
		||||
# It requires following environment variables:
 | 
			
		||||
#
 | 
			
		||||
# SYNO_Username - Synology Username to login (must be an administrator)
 | 
			
		||||
# SYNO_Password - Synology Password to login
 | 
			
		||||
# SYNO_Certificate - Certificate description to target for replacement
 | 
			
		||||
#
 | 
			
		||||
# The following environmental variables may be set if you don't like their
 | 
			
		||||
# default values:
 | 
			
		||||
#
 | 
			
		||||
# SYNO_Scheme - defaults to http
 | 
			
		||||
# SYNO_Hostname - defaults to localhost
 | 
			
		||||
# SYNO_Port - defaults to 5000
 | 
			
		||||
# SYNO_DID - device ID to skip OTP - defaults to empty
 | 
			
		||||
# SYNO_TOTP_SECRET - TOTP secret to generate OTP - defaults to empty
 | 
			
		||||
#
 | 
			
		||||
################################################################################
 | 
			
		||||
# 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:
 | 
			
		||||
# -------------
 | 
			
		||||
# - jq and curl
 | 
			
		||||
# - oathtool (When using 2 Factor Authentication and SYNO_TOTP_SECRET is set)
 | 
			
		||||
#
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
# - 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"
 | 
			
		||||
@@ -36,148 +55,391 @@ synology_dsm_deploy() {
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
 | 
			
		||||
  # Get Username and Password, but don't save until we successfully authenticate
 | 
			
		||||
  _getdeployconf SYNO_Username
 | 
			
		||||
  _getdeployconf SYNO_Password
 | 
			
		||||
  _getdeployconf SYNO_Create
 | 
			
		||||
  _getdeployconf SYNO_DID
 | 
			
		||||
  _getdeployconf SYNO_TOTP_SECRET
 | 
			
		||||
  if [ -z "${SYNO_Username:-}" ] || [ -z "${SYNO_Password:-}" ]; then
 | 
			
		||||
    _err "SYNO_Username & SYNO_Password must be set"
 | 
			
		||||
  # 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
 | 
			
		||||
  _debug2 SYNO_Username "$SYNO_Username"
 | 
			
		||||
  _secure_debug2 SYNO_Password "$SYNO_Password"
 | 
			
		||||
 | 
			
		||||
  # Optional scheme, hostname, and port for Synology DSM
 | 
			
		||||
  _getdeployconf SYNO_Scheme
 | 
			
		||||
  _getdeployconf SYNO_Hostname
 | 
			
		||||
  _getdeployconf SYNO_Port
 | 
			
		||||
  # 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 vaules 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"
 | 
			
		||||
  # 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"
 | 
			
		||||
 | 
			
		||||
  _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 verfiy it's real
 | 
			
		||||
  _getdeployconf SYNO_Certificate
 | 
			
		||||
  _debug SYNO_Certificate "${SYNO_Certificate:-}"
 | 
			
		||||
  # 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
 | 
			
		||||
  if printf "%s" "$SYNO_CERTIFICATE" | grep '\\'; then
 | 
			
		||||
    _err "Do not use a backslash (\) in your certificate description"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port"
 | 
			
		||||
  _debug "Getting API version..."
 | 
			
		||||
  _base_url="$SYNO_SCHEME://$SYNO_HOSTNAME:$SYNO_PORT"
 | 
			
		||||
  _debug _base_url "$_base_url"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting API version"
 | 
			
		||||
  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 token from JSON and session id from cookie
 | 
			
		||||
  _info "Logging into $SYNO_Hostname:$SYNO_Port"
 | 
			
		||||
  encoded_username="$(printf "%s" "$SYNO_Username" | _url_encode)"
 | 
			
		||||
  encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)"
 | 
			
		||||
  # 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
 | 
			
		||||
 | 
			
		||||
  otp_code=""
 | 
			
		||||
  if [ -n "$SYNO_TOTP_SECRET" ]; then
 | 
			
		||||
    if _exists oathtool; then
 | 
			
		||||
      otp_code="$(oathtool --base32 --totp "${SYNO_TOTP_SECRET}" 2>/dev/null)"
 | 
			
		||||
    else
 | 
			
		||||
    _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_HOSTNAME" != "localhost" ] && [ "$SYNO_HOSTNAME" != "127.0.0.1" ]; then
 | 
			
		||||
          if [ "$SYNO_LOCAL_HOSTNAME" != "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
 | 
			
		||||
 | 
			
		||||
  if [ -n "$SYNO_DID" ]; then
 | 
			
		||||
    _H1="Cookie: did=$SYNO_DID"
 | 
			
		||||
    export _H1
 | 
			
		||||
    _debug3 H1 "${_H1}"
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$otp_code&device_name=certrenewal&device_id=$SYNO_DID" "$_base_url/webapi/auth.cgi?enable_syno_token=yes")
 | 
			
		||||
  token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
 | 
			
		||||
  _debug3 response "$response"
 | 
			
		||||
  _debug token "$token"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$token" ]; then
 | 
			
		||||
    _err "Unable to authenticate to $SYNO_Hostname:$SYNO_Port using $SYNO_Scheme."
 | 
			
		||||
    _err "Check your username and password."
 | 
			
		||||
    _err "If two-factor authentication is enabled for the user, set SYNO_TOTP_SECRET."
 | 
			
		||||
  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
 | 
			
		||||
  _savedeployconf SYNO_Username "$SYNO_Username"
 | 
			
		||||
  _savedeployconf SYNO_Password "$SYNO_Password"
 | 
			
		||||
  _savedeployconf SYNO_DID "$SYNO_DID"
 | 
			
		||||
  _savedeployconf SYNO_TOTP_SECRET "$SYNO_TOTP_SECRET"
 | 
			
		||||
  # 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_LOCAL_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"
 | 
			
		||||
  _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')"
 | 
			
		||||
  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"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$id" ] && [ -z "${SYNO_Create:-}" ]; then
 | 
			
		||||
    _err "Unable to find certificate: $SYNO_Certificate and \$SYNO_Create is not set"
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  # we've verified this certificate description is a thing, so save it
 | 
			
		||||
  _savedeployconf SYNO_Certificate "$SYNO_Certificate" "base64"
 | 
			
		||||
  _migratedeployconf SYNO_Create SYNO_CREATE
 | 
			
		||||
  _getdeployconf SYNO_CREATE
 | 
			
		||||
  _debug2 SYNO_CREATE "$SYNO_CREATE"
 | 
			
		||||
 | 
			
		||||
  _info "Generate form POST request"
 | 
			
		||||
  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}"
 | 
			
		||||
  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"
 | 
			
		||||
    _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"
 | 
			
		||||
    _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"
 | 
			
		||||
  _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 "http services were restarted"
 | 
			
		||||
      _info "Restart HTTP services succeeded."
 | 
			
		||||
    else
 | 
			
		||||
      _info "http services were NOT restarted"
 | 
			
		||||
      _info "Restart HTTP services failed."
 | 
			
		||||
    fi
 | 
			
		||||
    _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
 | 
			
		||||
    _logout
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Unable to update certificate, error code $response"
 | 
			
		||||
    _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 "" 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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# Following environment variables must be set:
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI"
 | 
			
		||||
# 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:
 | 
			
		||||
@@ -64,6 +64,20 @@ truenas_deploy() {
 | 
			
		||||
  _response=$(_get "$_api_url/system/state")
 | 
			
		||||
  _info "TrueNAS system state: $_response."
 | 
			
		||||
 | 
			
		||||
  _info "Getting TrueNAS version"
 | 
			
		||||
  _response=$(_get "$_api_url/system/version")
 | 
			
		||||
 | 
			
		||||
  if echo "$_response" | grep -q "SCALE"; then
 | 
			
		||||
    _truenas_os=$(echo "$_response" | cut -d '-' -f 2)
 | 
			
		||||
    _truenas_version=$(echo "$_response" | cut -d '-' -f 3 | tr -d '"' | cut -d '.' -f 1,2)
 | 
			
		||||
  else
 | 
			
		||||
    _truenas_os="unknown"
 | 
			
		||||
    _truenas_version="unknown"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Detected TrueNAS system os: $_truenas_os"
 | 
			
		||||
  _info "Detected TrueNAS system version: $_truenas_version"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_response" ]; then
 | 
			
		||||
    _err "Unable to authenticate to $_api_url."
 | 
			
		||||
    _err 'Check your connection settings are correct, e.g.'
 | 
			
		||||
@@ -115,27 +129,106 @@ truenas_deploy() {
 | 
			
		||||
 | 
			
		||||
  _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: ,')
 | 
			
		||||
  _truenas_version_23_10="23.10"
 | 
			
		||||
  _truenas_version_24_10="24.10"
 | 
			
		||||
 | 
			
		||||
  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"
 | 
			
		||||
  _check_version=$(printf "%s\n%s" "$_truenas_version_23_10" "$_truenas_version" | sort -V | head -n 1)
 | 
			
		||||
  if [ "$_truenas_os" != "SCALE" ] || [ "$_check_version" != "$_truenas_version_23_10" ]; then
 | 
			
		||||
    _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"
 | 
			
		||||
      return 1
 | 
			
		||||
    else
 | 
			
		||||
      _info "WebDAV 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
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$_truenas_os" = "SCALE" ]; then
 | 
			
		||||
    _check_version=$(printf "%s\n%s" "$_truenas_version_24_10" "$_truenas_version" | sort -V | head -n 1)
 | 
			
		||||
    if [ "$_check_version" != "$_truenas_version_24_10" ]; then
 | 
			
		||||
      _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
 | 
			
		||||
    else
 | 
			
		||||
      _info "Checking if any app is using the same certificate as TrueNAS web UI. Tool 'jq' is required"
 | 
			
		||||
      if _exists jq; then
 | 
			
		||||
        _info "Query all apps"
 | 
			
		||||
        _app_list=$(_get "$_api_url/app")
 | 
			
		||||
        _app_id_list=$(printf "%s" "$_app_list" | jq -r '.[].name')
 | 
			
		||||
        _app_length=$(echo "$_app_id_list" | wc -l)
 | 
			
		||||
        _info "Found $_app_length apps"
 | 
			
		||||
        _info "Checking for each app if an update is needed"
 | 
			
		||||
        for i in $(seq 1 "$_app_length"); do
 | 
			
		||||
          _app_id=$(echo "$_app_id_list" | sed -n "${i}p")
 | 
			
		||||
          _app_config="$(_post "\"$_app_id\"" "$_api_url/app/config" "" "POST" "application/json")"
 | 
			
		||||
          # Check if the app use the same certificate TrueNAS web UI
 | 
			
		||||
          _app_active_cert_config=$(echo "$_app_config" | tr -d '\000-\037' | _json_decode | jq -r ".ix_certificates[\"$_active_cert_id\"]")
 | 
			
		||||
          if [ "$_app_active_cert_config" != "null" ]; then
 | 
			
		||||
            _info "Updating certificate from $_active_cert_id to $_cert_id for app: $_app_id"
 | 
			
		||||
            #Replace the old certificate id with the new one in path
 | 
			
		||||
            _update_app_result="$(_post "{\"values\" : { \"network\": { \"certificate_id\": $_cert_id } } }" "$_api_url/app/id/$_app_id" "" "PUT" "application/json")"
 | 
			
		||||
            _debug3 _update_app_result "$_update_app_result"
 | 
			
		||||
          fi
 | 
			
		||||
        done
 | 
			
		||||
      else
 | 
			
		||||
        _info "Tool 'jq' does not exists, skip app checking"
 | 
			
		||||
      fi
 | 
			
		||||
    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"
 | 
			
		||||
@@ -161,29 +254,6 @@ truenas_deploy() {
 | 
			
		||||
    _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 "Deleting old certificate"
 | 
			
		||||
  _delete_result="$(_post "" "$_api_url/certificate/id/$_active_cert_id" "" "DELETE" "application/json")"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										343
									
								
								deploy/truenas_ws.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								deploy/truenas_ws.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,343 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# TrueNAS deploy script for SCALE/CORE using websocket
 | 
			
		||||
# It is recommend to use a wildcard certificate
 | 
			
		||||
#
 | 
			
		||||
# Websocket Documentation: https://www.truenas.com/docs/api/scale_websocket_api.html
 | 
			
		||||
#
 | 
			
		||||
# Tested with TrueNAS Scale - Electric Eel 24.10
 | 
			
		||||
# Changes certificate in the following services:
 | 
			
		||||
#  - Web UI
 | 
			
		||||
#  - FTP
 | 
			
		||||
#  - iX Apps
 | 
			
		||||
#
 | 
			
		||||
# The following environment variables must be set:
 | 
			
		||||
# ------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# # API KEY
 | 
			
		||||
# # Use the folowing URL to create a new API token: <TRUENAS_HOSTNAME OR IP>/ui/apikeys
 | 
			
		||||
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI"
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
### Private functions
 | 
			
		||||
 | 
			
		||||
# Call websocket method
 | 
			
		||||
# Usage:
 | 
			
		||||
#   _ws_response=$(_ws_call "math.dummycalc" "'{"x": 4, "y": 5}'")
 | 
			
		||||
#   _info "$_ws_response"
 | 
			
		||||
#
 | 
			
		||||
# Output:
 | 
			
		||||
#   {"z": 9}
 | 
			
		||||
#
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   $@ - midclt arguments for call
 | 
			
		||||
#
 | 
			
		||||
# Returns:
 | 
			
		||||
#   JSON/JOBID
 | 
			
		||||
_ws_call() {
 | 
			
		||||
  _debug "_ws_call arg1" "$1"
 | 
			
		||||
  _debug "_ws_call arg2" "$2"
 | 
			
		||||
  _debug "_ws_call arg3" "$3"
 | 
			
		||||
  if [ $# -eq 3 ]; then
 | 
			
		||||
    _ws_response=$(midclt --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2" "$3")
 | 
			
		||||
  fi
 | 
			
		||||
  if [ $# -eq 2 ]; then
 | 
			
		||||
    _ws_response=$(midclt --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1" "$2")
 | 
			
		||||
  fi
 | 
			
		||||
  if [ $# -eq 1 ]; then
 | 
			
		||||
    _ws_response=$(midclt --uri "$_ws_uri" -K "$DEPLOY_TRUENAS_APIKEY" call "$1")
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "_ws_response" "$_ws_response"
 | 
			
		||||
  printf "%s" "$_ws_response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Upload certificate with webclient api
 | 
			
		||||
_ws_upload_cert() {
 | 
			
		||||
 | 
			
		||||
  /usr/bin/env python - <<EOF
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from truenas_api_client import Client
 | 
			
		||||
with Client(uri="$_ws_uri") as c:
 | 
			
		||||
 | 
			
		||||
  ### Login with API key
 | 
			
		||||
  print("I:Trying to upload new certificate...")
 | 
			
		||||
  ret = c.call("auth.login_with_api_key", "${DEPLOY_TRUENAS_APIKEY}")
 | 
			
		||||
  if ret:
 | 
			
		||||
    ### upload certificate
 | 
			
		||||
    with open('$1', 'r') as file:
 | 
			
		||||
      fullchain = file.read()
 | 
			
		||||
    with open('$2', 'r') as file:
 | 
			
		||||
      privatekey = file.read()
 | 
			
		||||
    ret = c.call("certificate.create", {"name": "$3", "create_type": "CERTIFICATE_CREATE_IMPORTED", "certificate": fullchain, "privatekey": privatekey, "passphrase": ""}, job=True)
 | 
			
		||||
    print("R:" + str(ret["id"]))
 | 
			
		||||
    sys.exit(0)
 | 
			
		||||
  else:
 | 
			
		||||
    print("R:0")
 | 
			
		||||
    print("E:_ws_upload_cert error!")
 | 
			
		||||
    sys.exit(7)
 | 
			
		||||
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
  return $?
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check argument is a number
 | 
			
		||||
# Usage:
 | 
			
		||||
#
 | 
			
		||||
# Output:
 | 
			
		||||
#   n/a
 | 
			
		||||
#
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   $1 - Anything
 | 
			
		||||
#
 | 
			
		||||
# Returns:
 | 
			
		||||
#   0: true
 | 
			
		||||
#   1: false
 | 
			
		||||
_ws_check_jobid() {
 | 
			
		||||
  case "$1" in
 | 
			
		||||
  [0-9]*)
 | 
			
		||||
    return 0
 | 
			
		||||
    ;;
 | 
			
		||||
  esac
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Wait for job to finish and return result as JSON
 | 
			
		||||
# Usage:
 | 
			
		||||
#   _ws_result=$(_ws_get_job_result "$_ws_jobid")
 | 
			
		||||
#   _new_certid=$(printf "%s" "$_ws_result" | jq -r '."id"')
 | 
			
		||||
#
 | 
			
		||||
# Output:
 | 
			
		||||
#   JSON result of the job
 | 
			
		||||
#
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   $1 - JobID
 | 
			
		||||
#
 | 
			
		||||
# Returns:
 | 
			
		||||
#   n/a
 | 
			
		||||
_ws_get_job_result() {
 | 
			
		||||
  while true; do
 | 
			
		||||
    _sleep 2
 | 
			
		||||
    _ws_response=$(_ws_call "core.get_jobs" "[[\"id\", \"=\", $1]]")
 | 
			
		||||
    if [ "$(printf "%s" "$_ws_response" | jq -r '.[]."state"')" != "RUNNING" ]; then
 | 
			
		||||
      _ws_result="$(printf "%s" "$_ws_response" | jq '.[]."result"')"
 | 
			
		||||
      _debug "_ws_result" "$_ws_result"
 | 
			
		||||
      printf "%s" "$_ws_result"
 | 
			
		||||
      _ws_error="$(printf "%s" "$_ws_response" | jq '.[]."error"')"
 | 
			
		||||
      if [ "$_ws_error" != "null" ]; then
 | 
			
		||||
        _err "Job $1 failed:"
 | 
			
		||||
        _err "$_ws_error"
 | 
			
		||||
        return 7
 | 
			
		||||
      fi
 | 
			
		||||
      break
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
########################
 | 
			
		||||
### Public functions ###
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
# truenas_ws_deploy
 | 
			
		||||
#
 | 
			
		||||
# Deploy new certificate to TrueNAS services
 | 
			
		||||
#
 | 
			
		||||
# Arguments
 | 
			
		||||
#  1: Domain
 | 
			
		||||
#  2: Key-File
 | 
			
		||||
#  3: Certificate-File
 | 
			
		||||
#  4: CA-File
 | 
			
		||||
#  5: FullChain-File
 | 
			
		||||
# Returns:
 | 
			
		||||
#  0: Success
 | 
			
		||||
#  1: Missing API Key
 | 
			
		||||
#  2: TrueNAS not ready
 | 
			
		||||
#  3: Not a JobID
 | 
			
		||||
#  4: FTP cert error
 | 
			
		||||
#  5: WebUI cert error
 | 
			
		||||
#  6: Job error
 | 
			
		||||
#  7: WS call error
 | 
			
		||||
#
 | 
			
		||||
truenas_ws_deploy() {
 | 
			
		||||
  _domain="$1"
 | 
			
		||||
  _file_key="$2"
 | 
			
		||||
  _file_cert="$3"
 | 
			
		||||
  _file_ca="$4"
 | 
			
		||||
  _file_fullchain="$5"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug _file_key "$_file_key"
 | 
			
		||||
  _debug _file_cert "$_file_cert"
 | 
			
		||||
  _debug _file_ca "$_file_ca"
 | 
			
		||||
  _debug _file_fullchain "$_file_fullchain"
 | 
			
		||||
 | 
			
		||||
  ########## Environment check
 | 
			
		||||
 | 
			
		||||
  _info "Checking environment variables..."
 | 
			
		||||
  _getdeployconf DEPLOY_TRUENAS_APIKEY
 | 
			
		||||
  _getdeployconf DEPLOY_TRUENAS_HOSTNAME
 | 
			
		||||
  _getdeployconf DEPLOY_TRUENAS_PROTOCOL
 | 
			
		||||
  # Check API Key
 | 
			
		||||
  if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
 | 
			
		||||
    _err "TrueNAS API key not found, please set the DEPLOY_TRUENAS_APIKEY environment variable."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  # Check Hostname, default to localhost if not set
 | 
			
		||||
  if [ -z "$DEPLOY_TRUENAS_HOSTNAME" ]; then
 | 
			
		||||
    _info "TrueNAS hostname not set. Using 'localhost'."
 | 
			
		||||
    DEPLOY_TRUENAS_HOSTNAME="localhost"
 | 
			
		||||
  fi
 | 
			
		||||
  # Check protocol, default to ws if not set
 | 
			
		||||
  if [ -z "$DEPLOY_TRUENAS_PROTOCOL" ]; then
 | 
			
		||||
    _info "TrueNAS protocol not set. Using 'ws'."
 | 
			
		||||
    DEPLOY_TRUENAS_PROTOCOL="ws"
 | 
			
		||||
  fi
 | 
			
		||||
  _ws_uri="$DEPLOY_TRUENAS_PROTOCOL://$DEPLOY_TRUENAS_HOSTNAME/websocket"
 | 
			
		||||
  _debug2 DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
 | 
			
		||||
  _debug2 DEPLOY_TRUENAS_PROTOCOL "$DEPLOY_TRUENAS_PROTOCOL"
 | 
			
		||||
  _debug _ws_uri "$_ws_uri"
 | 
			
		||||
  _secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
 | 
			
		||||
  _info "Environment variables: OK"
 | 
			
		||||
 | 
			
		||||
  ########## Health check
 | 
			
		||||
 | 
			
		||||
  _info "Checking TrueNAS health..."
 | 
			
		||||
  _ws_response=$(_ws_call "system.ready" | tr '[:lower:]' '[:upper:]')
 | 
			
		||||
  _ws_ret=$?
 | 
			
		||||
  if [ $_ws_ret -gt 0 ]; then
 | 
			
		||||
    _err "Error calling system.ready:"
 | 
			
		||||
    _err "$_ws_response"
 | 
			
		||||
    return $_ws_ret
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$_ws_response" != "TRUE" ]; then
 | 
			
		||||
    _err "TrueNAS is not ready."
 | 
			
		||||
    _err "Please check environment variables DEPLOY_TRUENAS_APIKEY, DEPLOY_TRUENAS_HOSTNAME and DEPLOY_TRUENAS_PROTOCOL."
 | 
			
		||||
    _err "Verify API key."
 | 
			
		||||
    return 2
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
 | 
			
		||||
  _savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
 | 
			
		||||
  _savedeployconf DEPLOY_TRUENAS_PROTOCOL "$DEPLOY_TRUENAS_PROTOCOL"
 | 
			
		||||
  _info "TrueNAS health: OK"
 | 
			
		||||
 | 
			
		||||
  ########## System info
 | 
			
		||||
 | 
			
		||||
  _info "Gather system info..."
 | 
			
		||||
  _ws_response=$(_ws_call "system.info")
 | 
			
		||||
  _truenas_version=$(printf "%s" "$_ws_response" | jq -r '."version"')
 | 
			
		||||
  _info "TrueNAS version: $_truenas_version"
 | 
			
		||||
 | 
			
		||||
  ########## Gather current certificate
 | 
			
		||||
 | 
			
		||||
  _info "Gather current WebUI certificate..."
 | 
			
		||||
  _ws_response="$(_ws_call "system.general.config")"
 | 
			
		||||
  _ui_certificate_id=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."id"')
 | 
			
		||||
  _ui_certificate_name=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."name"')
 | 
			
		||||
  _info "Current WebUI certificate ID: $_ui_certificate_id"
 | 
			
		||||
  _info "Current WebUI certificate name: $_ui_certificate_name"
 | 
			
		||||
 | 
			
		||||
  ########## Upload new certificate
 | 
			
		||||
 | 
			
		||||
  _info "Upload new certificate..."
 | 
			
		||||
  _certname="acme_$(_utc_date | tr -d '\-\:' | tr ' ' '_')"
 | 
			
		||||
  _info "New WebUI certificate name: $_certname"
 | 
			
		||||
  _debug _certname "$_certname"
 | 
			
		||||
  _ws_out=$(_ws_upload_cert "$_file_fullchain" "$_file_key" "$_certname")
 | 
			
		||||
 | 
			
		||||
  echo "$_ws_out" | while IFS= read -r LINE; do
 | 
			
		||||
    case "$LINE" in
 | 
			
		||||
    I:*)
 | 
			
		||||
      _info "${LINE#I:}"
 | 
			
		||||
      ;;
 | 
			
		||||
    D:*)
 | 
			
		||||
      _debug "${LINE#D:}"
 | 
			
		||||
      ;;
 | 
			
		||||
    E*)
 | 
			
		||||
      _err "${LINE#E:}"
 | 
			
		||||
      ;;
 | 
			
		||||
    *) ;;
 | 
			
		||||
 | 
			
		||||
    esac
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  _new_certid=$(echo "$_ws_out" | grep 'R:' | cut -d ':' -f 2)
 | 
			
		||||
 | 
			
		||||
  _info "New certificate ID: $_new_certid"
 | 
			
		||||
 | 
			
		||||
  ########## FTP
 | 
			
		||||
 | 
			
		||||
  _info "Replace FTP certificate..."
 | 
			
		||||
  _ws_response=$(_ws_call "ftp.update" "{\"ssltls_certificate\": $_new_certid}")
 | 
			
		||||
  _ftp_certid=$(printf "%s" "$_ws_response" | jq -r '."ssltls_certificate"')
 | 
			
		||||
  if [ "$_ftp_certid" != "$_new_certid" ]; then
 | 
			
		||||
    _err "Cannot set FTP certificate."
 | 
			
		||||
    _debug "_ws_response" "$_ws_response"
 | 
			
		||||
    return 4
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  ########## ix Apps (SCALE only)
 | 
			
		||||
 | 
			
		||||
  _info "Replace app certificates..."
 | 
			
		||||
  _ws_response=$(_ws_call "app.query")
 | 
			
		||||
  for _app_name in $(printf "%s" "$_ws_response" | jq -r '.[]."name"'); do
 | 
			
		||||
    _info "Checking app $_app_name..."
 | 
			
		||||
    _ws_response=$(_ws_call "app.config" "$_app_name")
 | 
			
		||||
    if [ "$(printf "%s" "$_ws_response" | jq -r '."network" | has("certificate_id")')" = "true" ]; then
 | 
			
		||||
      _info "App has certificate option, setup new certificate..."
 | 
			
		||||
      _info "App will be redeployed after updating the certificate."
 | 
			
		||||
      _ws_jobid=$(_ws_call "app.update" "$_app_name" "{\"values\": {\"network\": {\"certificate_id\": $_new_certid}}}")
 | 
			
		||||
      _debug "_ws_jobid" "$_ws_jobid"
 | 
			
		||||
      if ! _ws_check_jobid "$_ws_jobid"; then
 | 
			
		||||
        _err "No JobID returned from websocket method."
 | 
			
		||||
        return 3
 | 
			
		||||
      fi
 | 
			
		||||
      _ws_result=$(_ws_get_job_result "$_ws_jobid")
 | 
			
		||||
      _ws_ret=$?
 | 
			
		||||
      if [ $_ws_ret -gt 0 ]; then
 | 
			
		||||
        return $_ws_ret
 | 
			
		||||
      fi
 | 
			
		||||
      _debug "_ws_result" "$_ws_result"
 | 
			
		||||
      _info "App certificate replaced."
 | 
			
		||||
    else
 | 
			
		||||
      _info "App has no certificate option, skipping..."
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  ########## WebUI
 | 
			
		||||
 | 
			
		||||
  _info "Replace WebUI certificate..."
 | 
			
		||||
  _ws_response=$(_ws_call "system.general.update" "{\"ui_certificate\": $_new_certid}")
 | 
			
		||||
  _changed_certid=$(printf "%s" "$_ws_response" | jq -r '."ui_certificate"."id"')
 | 
			
		||||
  if [ "$_changed_certid" != "$_new_certid" ]; then
 | 
			
		||||
    _err "WebUI certificate change error.."
 | 
			
		||||
    return 5
 | 
			
		||||
  else
 | 
			
		||||
    _info "WebUI certificate replaced."
 | 
			
		||||
  fi
 | 
			
		||||
  _info "Restarting WebUI..."
 | 
			
		||||
  _ws_response=$(_ws_call "system.general.ui_restart")
 | 
			
		||||
  _info "Waiting for UI restart..."
 | 
			
		||||
  _sleep 15
 | 
			
		||||
 | 
			
		||||
  ########## Certificates
 | 
			
		||||
 | 
			
		||||
  _info "Deleting old certificate..."
 | 
			
		||||
  _ws_jobid=$(_ws_call "certificate.delete" "$_ui_certificate_id")
 | 
			
		||||
  if ! _ws_check_jobid "$_ws_jobid"; then
 | 
			
		||||
    _err "No JobID returned from websocket method."
 | 
			
		||||
    return 3
 | 
			
		||||
  fi
 | 
			
		||||
  _ws_result=$(_ws_get_job_result "$_ws_jobid")
 | 
			
		||||
  _ws_ret=$?
 | 
			
		||||
  if [ $_ws_ret -gt 0 ]; then
 | 
			
		||||
    return $_ws_ret
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Have a nice day...bye!"
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										145
									
								
								deploy/unifi.sh
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								deploy/unifi.sh
									
									
									
									
									
								
							@@ -5,6 +5,15 @@
 | 
			
		||||
#   - 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.
 | 
			
		||||
@@ -21,7 +30,9 @@
 | 
			
		||||
# Keystore password (built into Unifi Controller, not a user-set password):
 | 
			
		||||
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
 | 
			
		||||
# Command to restart Unifi Controller:
 | 
			
		||||
#DEPLOY_UNIFI_RELOAD="service unifi restart"
 | 
			
		||||
# DEPLOY_UNIFI_RELOAD="systemctl restart unifi"
 | 
			
		||||
# System Properties file location for controller
 | 
			
		||||
#DEPLOY_UNIFI_SYSTEM_PROPERTIES="/usr/lib/unifi/data/system.properties"
 | 
			
		||||
#
 | 
			
		||||
# Settings for Unifi Cloud Key Gen1 (nginx admin pages):
 | 
			
		||||
# Directory where cloudkey.crt and cloudkey.key live:
 | 
			
		||||
@@ -34,7 +45,7 @@
 | 
			
		||||
# 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"
 | 
			
		||||
# DEPLOY_UNIFI_OS_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.
 | 
			
		||||
@@ -60,12 +71,16 @@ unifi_deploy() {
 | 
			
		||||
  _getdeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR
 | 
			
		||||
  _getdeployconf DEPLOY_UNIFI_CORE_CONFIG
 | 
			
		||||
  _getdeployconf DEPLOY_UNIFI_RELOAD
 | 
			
		||||
  _getdeployconf DEPLOY_UNIFI_SYSTEM_PROPERTIES
 | 
			
		||||
  _getdeployconf DEPLOY_UNIFI_OS_RELOAD
 | 
			
		||||
 | 
			
		||||
  _debug2 DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
 | 
			
		||||
  _debug2 DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
 | 
			
		||||
  _debug2 DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
 | 
			
		||||
  _debug2 DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
 | 
			
		||||
  _debug2 DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
 | 
			
		||||
  _debug2 DEPLOY_UNIFI_OS_RELOAD "$DEPLOY_UNIFI_OS_RELOAD"
 | 
			
		||||
  _debug2 DEPLOY_UNIFI_SYSTEM_PROPERTIES "$DEPLOY_UNIFI_SYSTEM_PROPERTIES"
 | 
			
		||||
 | 
			
		||||
  # Space-separated list of environments detected and installed:
 | 
			
		||||
  _services_updated=""
 | 
			
		||||
@@ -74,14 +89,16 @@ unifi_deploy() {
 | 
			
		||||
  _reload_cmd=""
 | 
			
		||||
 | 
			
		||||
  # Unifi Controller environment (self hosted or any Cloud Key) --
 | 
			
		||||
  # auto-detect by file /usr/lib/unifi/data/keystore:
 | 
			
		||||
  # auto-detect by file /usr/lib/unifi/data/keystore
 | 
			
		||||
  _unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}"
 | 
			
		||||
  if [ -f "$_unifi_keystore" ]; then
 | 
			
		||||
    _info "Installing certificate for Unifi Controller (Java keystore)"
 | 
			
		||||
    _debug _unifi_keystore "$_unifi_keystore"
 | 
			
		||||
    if ! _exists keytool; then
 | 
			
		||||
      _err "keytool not found"
 | 
			
		||||
      return 1
 | 
			
		||||
      _do_keytool=0
 | 
			
		||||
      _info "Installing certificate for Unifi Controller (PKCS12 keystore)."
 | 
			
		||||
    else
 | 
			
		||||
      _do_keytool=1
 | 
			
		||||
      _info "Installing certificate for Unifi Controller (Java keystore)"
 | 
			
		||||
    fi
 | 
			
		||||
    if [ ! -w "$_unifi_keystore" ]; then
 | 
			
		||||
      _err "The file $_unifi_keystore is not writable, please change the permission."
 | 
			
		||||
@@ -92,6 +109,7 @@ unifi_deploy() {
 | 
			
		||||
 | 
			
		||||
    _debug "Generate import pkcs12"
 | 
			
		||||
    _import_pkcs12="$(_mktemp)"
 | 
			
		||||
    _debug "_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
 | 
			
		||||
@@ -99,22 +117,77 @@ unifi_deploy() {
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _debug "Import into keystore: $_unifi_keystore"
 | 
			
		||||
    if keytool -importkeystore \
 | 
			
		||||
      -deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
 | 
			
		||||
      -srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
 | 
			
		||||
      -alias unifi -noprompt; then
 | 
			
		||||
      _debug "Import keystore success!"
 | 
			
		||||
      rm "$_import_pkcs12"
 | 
			
		||||
    # 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 \
 | 
			
		||||
        -deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
 | 
			
		||||
        -srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
 | 
			
		||||
        -alias unifi -noprompt; then
 | 
			
		||||
        _debug "Import keystore success!"
 | 
			
		||||
      else
 | 
			
		||||
        _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"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      _err "Error importing into Unifi Java keystore."
 | 
			
		||||
      _err "Please re-run with --debug and report a bug."
 | 
			
		||||
      rm "$_import_pkcs12"
 | 
			
		||||
      return 1
 | 
			
		||||
      _debug "Copying new keystore to $_unifi_keystore"
 | 
			
		||||
      cp -f "$_import_pkcs12" "$_unifi_keystore"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if systemctl -q is-active unifi; then
 | 
			
		||||
      _reload_cmd="${_reload_cmd:+$_reload_cmd && }service unifi restart"
 | 
			
		||||
    # correct file ownership according to the directory, the keystore is placed in
 | 
			
		||||
    _unifi_keystore_dir=$(dirname "${_unifi_keystore}")
 | 
			
		||||
    _unifi_keystore_dir_owner=$(find "${_unifi_keystore_dir}" -maxdepth 0 -printf '%u\n')
 | 
			
		||||
    _unifi_keystore_owner=$(find "${_unifi_keystore}" -maxdepth 0 -printf '%u\n')
 | 
			
		||||
    if ! [ "${_unifi_keystore_owner}" = "${_unifi_keystore_dir_owner}" ]; then
 | 
			
		||||
      _debug "Changing keystore owner to ${_unifi_keystore_dir_owner}"
 | 
			
		||||
      chown "$_unifi_keystore_dir_owner" "${_unifi_keystore}" >/dev/null 2>&1 # fail quietly if we're not running as root
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # Update unifi service for certificate cipher compatibility
 | 
			
		||||
    _unifi_system_properties="${DEPLOY_UNIFI_SYSTEM_PROPERTIES:-/usr/lib/unifi/data/system.properties}"
 | 
			
		||||
    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
 | 
			
		||||
      if [ -f "$(dirname "${DEPLOY_UNIFI_KEYSTORE}")/system.properties" ]; then
 | 
			
		||||
        _unifi_system_properties="$(dirname "${DEPLOY_UNIFI_KEYSTORE}")/system.properties"
 | 
			
		||||
      else
 | 
			
		||||
        _unifi_system_properties="/usr/lib/unifi/data/system.properties"
 | 
			
		||||
      fi
 | 
			
		||||
      if [ -f "${_unifi_system_properties}" ]; then
 | 
			
		||||
        cp -f "${_unifi_system_properties}" "${_unifi_system_properties}"_original
 | 
			
		||||
        _info "Updating system configuration for cipher compatibility."
 | 
			
		||||
        _info "Saved original system config to ${_unifi_system_properties}_original"
 | 
			
		||||
        sed -i '/unifi\.https\.ciphers/d' "${_unifi_system_properties}"
 | 
			
		||||
        echo "unifi.https.ciphers=ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-GCM-SHA256" >>"${_unifi_system_properties}"
 | 
			
		||||
        sed -i '/unifi\.https\.sslEnabledProtocols/d' "${_unifi_system_properties}"
 | 
			
		||||
        echo "unifi.https.sslEnabledProtocols=TLSv1.3,TLSv1.2" >>"${_unifi_system_properties}"
 | 
			
		||||
        _info "System configuration updated."
 | 
			
		||||
      fi
 | 
			
		||||
    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 OS (e.g. unifi-core service), otherwise stop for later restart.
 | 
			
		||||
    _unifi_reload="${DEPLOY_UNIFI_RELOAD:-systemctl restart unifi}"
 | 
			
		||||
    if [ ! -f "${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}/unifi-core.key" ]; then
 | 
			
		||||
      _reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_reload"
 | 
			
		||||
    else
 | 
			
		||||
      _info "Stopping Unifi Controller for later restart."
 | 
			
		||||
      _unifi_stop=$(echo "${_unifi_reload}" | sed -e 's/restart/stop/')
 | 
			
		||||
      $_unifi_stop
 | 
			
		||||
      _reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_reload"
 | 
			
		||||
      _info "Unifi Controller stopped."
 | 
			
		||||
    fi
 | 
			
		||||
    _services_updated="${_services_updated} unifi"
 | 
			
		||||
    _info "Install Unifi Controller certificate success!"
 | 
			
		||||
@@ -134,13 +207,24 @@ unifi_deploy() {
 | 
			
		||||
      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
 | 
			
		||||
    # It appears that unifi won't start if this is a symlink, so we'll copy it instead.
 | 
			
		||||
 | 
			
		||||
    # 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
 | 
			
		||||
 | 
			
		||||
    _info "Updating ${_cloudkey_certdir}/unifi.keystore.jks"
 | 
			
		||||
    if [ -e "${_cloudkey_certdir}/unifi.keystore.jks" ]; then
 | 
			
		||||
      if [ -L "${_cloudkey_certdir}/unifi.keystore.jks" ]; then
 | 
			
		||||
        rm -f "${_cloudkey_certdir}/unifi.keystore.jks"
 | 
			
		||||
      else
 | 
			
		||||
        mv "${_cloudkey_certdir}/unifi.keystore.jks" "${_cloudkey_certdir}/unifi.keystore.jks_original"
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    cp "${_unifi_keystore}" "${_cloudkey_certdir}/unifi.keystore.jks"
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
@@ -165,12 +249,17 @@ unifi_deploy() {
 | 
			
		||||
      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
 | 
			
		||||
    _unifi_os_reload="${DEPLOY_UNIFI_OS_RELOAD:-systemctl restart unifi-core}"
 | 
			
		||||
    _reload_cmd="${_reload_cmd:+$_reload_cmd && }$_unifi_os_reload"
 | 
			
		||||
 | 
			
		||||
    _info "Install UnifiOS certificate success!"
 | 
			
		||||
    _services_updated="${_services_updated} unifi-core"
 | 
			
		||||
  elif [ "$DEPLOY_UNIFI_CORE_CONFIG" ]; then
 | 
			
		||||
@@ -209,6 +298,8 @@ unifi_deploy() {
 | 
			
		||||
  _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"
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_OS_RELOAD "$DEPLOY_UNIFI_OS_RELOAD"
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_SYSTEM_PROPERTIES "$DEPLOY_UNIFI_SYSTEM_PROPERTIES"
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										167
									
								
								deploy/vault.sh
									
									
									
									
									
								
							
							
						
						
									
										167
									
								
								deploy/vault.sh
									
									
									
									
									
								
							@@ -7,13 +7,16 @@
 | 
			
		||||
#
 | 
			
		||||
# 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 #####################
 | 
			
		||||
######## Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
vault_deploy() {
 | 
			
		||||
@@ -45,34 +48,154 @@ vault_deploy() {
 | 
			
		||||
  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 -z 's/\n/\\n/g' <"$2")
 | 
			
		||||
  _ccert=$(sed -z 's/\n/\\n/g' <"$3")
 | 
			
		||||
  _cca=$(sed -z 's/\n/\\n/g' <"$4")
 | 
			
		||||
  _cfullchain=$(sed -z 's/\n/\\n/g' <"$5")
 | 
			
		||||
  _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")
 | 
			
		||||
 | 
			
		||||
  URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain"
 | 
			
		||||
  export _H1="X-Vault-Token: $VAULT_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$FABIO" ]; then
 | 
			
		||||
    if [ -n "$VAULT_KV_V2" ]; then
 | 
			
		||||
      _post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL"
 | 
			
		||||
    else
 | 
			
		||||
      _post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL"
 | 
			
		||||
  if [ -n "$VAULT_RENEW_TOKEN" ]; then
 | 
			
		||||
    URL="$VAULT_ADDR/v1/auth/token/renew-self"
 | 
			
		||||
    _info "Renew the Vault token to default TTL"
 | 
			
		||||
    _response=$(_post "" "$URL")
 | 
			
		||||
    if [ "$?" != "0" ]; then
 | 
			
		||||
      _err "Failed to renew the Vault token"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    if [ -n "$VAULT_KV_V2" ]; then
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem"
 | 
			
		||||
    else
 | 
			
		||||
      _post "{\"value\": \"$_ccert\"}" "$URL/cert.pem"
 | 
			
		||||
      _post "{\"value\": \"$_ckey\"}" "$URL/cert.key"
 | 
			
		||||
      _post "{\"value\": \"$_cca\"}" "$URL/chain.pem"
 | 
			
		||||
      _post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem"
 | 
			
		||||
    if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
      _err "Failed to renew the Vault token: $_response"
 | 
			
		||||
      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
 | 
			
		||||
      _response=$(_post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      _response=$(_post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    if [ -n "$VAULT_KV_V2" ]; then
 | 
			
		||||
      _info "Writing certificate to $URL/cert.pem"
 | 
			
		||||
      _response=$(_post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error writing cert.pem: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      _info "Writing key to $URL/cert.key"
 | 
			
		||||
      _response=$(_post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error writing cert.key: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      _info "Writing CA certificate to $URL/ca.pem"
 | 
			
		||||
      _response=$(_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/ca.pem")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error writing ca.pem: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      _info "Writing full-chain certificate to $URL/fullchain.pem"
 | 
			
		||||
      _response=$(_post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error writing fullchain.pem: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      _info "Writing certificate to $URL/cert.pem"
 | 
			
		||||
      _response=$(_post "{\"value\": \"$_ccert\"}" "$URL/cert.pem")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error writing cert.pem: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      _info "Writing key to $URL/cert.key"
 | 
			
		||||
      _response=$(_post "{\"value\": \"$_ckey\"}" "$URL/cert.key")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error writing cert.key: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      _info "Writing CA certificate to $URL/ca.pem"
 | 
			
		||||
      _response=$(_post "{\"value\": \"$_cca\"}" "$URL/ca.pem")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error writing ca.pem: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      _info "Writing full-chain certificate to $URL/fullchain.pem"
 | 
			
		||||
      _response=$(_post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem")
 | 
			
		||||
      if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
      if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
        _err "Vault error writing fullchain.pem: $_response"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    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
 | 
			
		||||
        _response=$(_post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem")
 | 
			
		||||
        if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
        if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
          _err "Vault error writing chain.pem: $_response"
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
      else
 | 
			
		||||
        _response=$(_post "{\"value\": \"$_cca\"}" "$URL/chain.pem")
 | 
			
		||||
        if [ "$?" != "0" ]; then return 1; fi
 | 
			
		||||
        if echo "$_response" | grep -q '"errors":\['; then
 | 
			
		||||
          _err "Vault error writing chain.pem: $_response"
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
#
 | 
			
		||||
# additionally, you need to ensure that VAULT_TOKEN is avialable or
 | 
			
		||||
# `vault auth` has applied the appropriate authorization for the vault binary
 | 
			
		||||
@@ -33,15 +35,36 @@ vault_cli_deploy() {
 | 
			
		||||
  _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_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
 | 
			
		||||
@@ -49,13 +72,33 @@ vault_cli_deploy() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$FABIO" ]; then
 | 
			
		||||
  if [ -n "$VAULT_RENEW_TOKEN" ]; then
 | 
			
		||||
    _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
 | 
			
		||||
    _info "Writing certificate to ${VAULT_PREFIX}/${_cdomain}/cert.pem"
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
 | 
			
		||||
    _info "Writing key to ${VAULT_PREFIX}/${_cdomain}/cert.key"
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || 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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -106,5 +106,5 @@ vsftpd_deploy() {
 | 
			
		||||
    fi
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										500
									
								
								deploy/zyxel_gs1900.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										500
									
								
								deploy/zyxel_gs1900.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,500 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Deploy certificates to Zyxel GS1900 series switches
 | 
			
		||||
#
 | 
			
		||||
# This script uses the https web administration interface in order
 | 
			
		||||
# to upload updated certificates to Zyxel GS1900 series switches.
 | 
			
		||||
# Only a few models have been tested but untested switches from the
 | 
			
		||||
# same model line may work as well. If you test and confirm a switch
 | 
			
		||||
# as working please submit a pull request updating this compatibility
 | 
			
		||||
# list!
 | 
			
		||||
#
 | 
			
		||||
# Known Issues:
 | 
			
		||||
#   1. This is a consumer grade switch and is a bit underpowered
 | 
			
		||||
#      the longer the RSA key size the slower your switch web UI
 | 
			
		||||
#      will be. RSA 2048 will work, RSA 4096 will work but you may
 | 
			
		||||
#      experience performance problems.
 | 
			
		||||
#   2. You must use RSA certificates. The switch will reject EC-256
 | 
			
		||||
#      and EC-384 certificates in firmware 2.80
 | 
			
		||||
#      See: https://community.zyxel.com/en/discussion/21506/bug-cannot-import-ssl-cert-on-gs1900-8-and-gs1900-24e-firmware-v2-80/
 | 
			
		||||
#
 | 
			
		||||
# Current GS1900 Switch Compatibility:
 | 
			
		||||
#   GS1900-8    - Working as of firmware V2.80
 | 
			
		||||
#   GS1900-8HP  - Untested
 | 
			
		||||
#   GS1900-10HP - Untested
 | 
			
		||||
#   GS1900-16   - Untested
 | 
			
		||||
#   GS1900-24   - Untested
 | 
			
		||||
#   GS1900-24E  - Working as of firmware V2.80
 | 
			
		||||
#   GS1900-24EP - Untested
 | 
			
		||||
#   GS1900-24HP - Untested
 | 
			
		||||
#   GS1900-48   - Untested
 | 
			
		||||
#   GS1900-48HP - Untested
 | 
			
		||||
#
 | 
			
		||||
# Prerequisite Setup Steps:
 | 
			
		||||
#   1. Install at least firmware V2.80 on your switch
 | 
			
		||||
#   2. Enable HTTPS web management on your switch
 | 
			
		||||
#
 | 
			
		||||
# Usage:
 | 
			
		||||
#   1. Ensure the switch has firmware V2.80 or later.
 | 
			
		||||
#   2. Ensure the switch has HTTPS management enabled.
 | 
			
		||||
#   3. Set the appropriate environment variables for your environment.
 | 
			
		||||
#
 | 
			
		||||
#      DEPLOY_ZYXEL_SWITCH          - The switch hostname. (Default: _cdomain)
 | 
			
		||||
#      DEPLOY_ZYXEL_SWITCH_USER     - The webadmin user. (Default: admin)
 | 
			
		||||
#      DEPLOY_ZYXEL_SWITCH_PASSWORD - The webadmin password for the switch.
 | 
			
		||||
#      DEPLOY_ZYXEL_SWITCH_REBOOT   - If "1" reboot after update. (Default: "0")
 | 
			
		||||
#
 | 
			
		||||
#   4. Run the deployment plugin:
 | 
			
		||||
#      acme.sh --deploy --deploy-hook zyxel_gs1900 -d example.com
 | 
			
		||||
#
 | 
			
		||||
# returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
zyxel_gs1900_deploy() {
 | 
			
		||||
  _zyxel_gs1900_minimum_firmware_version="v2.80"
 | 
			
		||||
 | 
			
		||||
  _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"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_ZYXEL_SWITCH
 | 
			
		||||
  _getdeployconf DEPLOY_ZYXEL_SWITCH_USER
 | 
			
		||||
  _getdeployconf DEPLOY_ZYXEL_SWITCH_PASSWORD
 | 
			
		||||
  _getdeployconf DEPLOY_ZYXEL_SWITCH_REBOOT
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_ZYXEL_SWITCH" ]; then
 | 
			
		||||
    DEPLOY_ZYXEL_SWITCH="$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_ZYXEL_SWITCH_USER" ]; then
 | 
			
		||||
    DEPLOY_ZYXEL_SWITCH_USER="admin"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_ZYXEL_SWITCH_PASSWORD" ]; then
 | 
			
		||||
    DEPLOY_ZYXEL_SWITCH_PASSWORD="1234"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_ZYXEL_SWITCH_REBOOT" ]; then
 | 
			
		||||
    DEPLOY_ZYXEL_SWITCH_REBOOT="0"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedeployconf DEPLOY_ZYXEL_SWITCH "$DEPLOY_ZYXEL_SWITCH"
 | 
			
		||||
  _savedeployconf DEPLOY_ZYXEL_SWITCH_USER "$DEPLOY_ZYXEL_SWITCH_USER"
 | 
			
		||||
  _savedeployconf DEPLOY_ZYXEL_SWITCH_PASSWORD "$DEPLOY_ZYXEL_SWITCH_PASSWORD"
 | 
			
		||||
  _savedeployconf DEPLOY_ZYXEL_SWITCH_REBOOT "$DEPLOY_ZYXEL_SWITCH_REBOOT"
 | 
			
		||||
 | 
			
		||||
  _debug DEPLOY_ZYXEL_SWITCH "$DEPLOY_ZYXEL_SWITCH"
 | 
			
		||||
  _debug DEPLOY_ZYXEL_SWITCH_USER "$DEPLOY_ZYXEL_SWITCH_USER"
 | 
			
		||||
  _secure_debug DEPLOY_ZYXEL_SWITCH_PASSWORD "$DEPLOY_ZYXEL_SWITCH_PASSWORD"
 | 
			
		||||
  _debug DEPLOY_ZYXEL_SWITCH_REBOOT "$DEPLOY_ZYXEL_SWITCH_REBOOT"
 | 
			
		||||
 | 
			
		||||
  _zyxel_switch_base_uri="https://${DEPLOY_ZYXEL_SWITCH}"
 | 
			
		||||
 | 
			
		||||
  _info "Beginning to deploy to a Zyxel GS1900 series switch at ${_zyxel_switch_base_uri}."
 | 
			
		||||
  _zyxel_gs1900_deployment_precheck || return $?
 | 
			
		||||
 | 
			
		||||
  _zyxel_gs1900_should_update
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _info "The switch already has our certificate installed. No update required."
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _info "The switch does not yet have our certificate installed."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Logging into the switch web interface."
 | 
			
		||||
  _zyxel_gs1900_login || return $?
 | 
			
		||||
 | 
			
		||||
  _info "Validating the switch is compatible with this deployment process."
 | 
			
		||||
  _zyxel_gs1900_validate_device_compatibility || return $?
 | 
			
		||||
 | 
			
		||||
  _info "Uploading the certificate."
 | 
			
		||||
  _zyxel_gs1900_upload_certificate || return $?
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_ZYXEL_SWITCH_REBOOT" = "1" ]; then
 | 
			
		||||
    _info "Rebooting the switch."
 | 
			
		||||
    _zyxel_gs1900_trigger_reboot || return $?
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_zyxel_gs1900_deployment_precheck() {
 | 
			
		||||
  # Initialize the keylength if it isn't already
 | 
			
		||||
  if [ -z "$Le_Keylength" ]; then
 | 
			
		||||
    Le_Keylength=""
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _isEccKey "$Le_Keylength"; then
 | 
			
		||||
    _info "Warning: Zyxel GS1900 switches are not currently known to work with ECC keys!"
 | 
			
		||||
    _info "You can continue, but your switch may reject your key."
 | 
			
		||||
  elif [ -n "$Le_Keylength" ] && [ "$Le_Keylength" -gt "2048" ]; then
 | 
			
		||||
    _info "Warning: Your RSA key length is greater than 2048!"
 | 
			
		||||
    _info "You can continue, but you may experience performance issues in the web administration interface."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Check the server for some common failure modes prior to authentication and certificate upload in order to avoid
 | 
			
		||||
  # sending a certificate when we may not want to.
 | 
			
		||||
  test_login_response=$(_post "username=test&password=test&login=true;" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" 2>&1)
 | 
			
		||||
  test_login_page_exitcode="$?"
 | 
			
		||||
  _debug3 "Test Login Response: ${test_login_response}"
 | 
			
		||||
  if [ "$test_login_page_exitcode" -ne "0" ]; then
 | 
			
		||||
    if { [ "${ACME_USE_WGET:-0}" = "0" ] && [ "$test_login_page_exitcode" = "60" ]; } || { [ "${ACME_USE_WGET:-0}" = "1" ] && [ "$test_login_page_exitcode" = "5" ]; }; then
 | 
			
		||||
      _err "The SSL certificate at $_zyxel_switch_base_uri could not be validated."
 | 
			
		||||
      _err "Please double check your hostname, port, and that you are actually connecting to your switch."
 | 
			
		||||
      _err "If the problem persists then please ensure that the certificate is not self-signed, has not"
 | 
			
		||||
      _err "expired, and matches the switch hostname. If you expect validation to fail then you can disable"
 | 
			
		||||
      _err "certificate validation by running with --insecure."
 | 
			
		||||
      return 1
 | 
			
		||||
    elif [ "${ACME_USE_WGET:-0}" = "0" ] && [ "$test_login_page_exitcode" = "56" ]; then
 | 
			
		||||
      _debug3 "Intentionally ignore curl exit code 56 in our precheck"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Failed to submit the initial login attempt to $_zyxel_switch_base_uri."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_zyxel_gs1900_login() {
 | 
			
		||||
  # Login to the switch and set the appropriate auth cookie in _H1
 | 
			
		||||
  username_encoded=$(printf "%s" "$DEPLOY_ZYXEL_SWITCH_USER" | _url_encode)
 | 
			
		||||
  password_encoded=$(_zyxel_gs1900_password_obfuscate "$DEPLOY_ZYXEL_SWITCH_PASSWORD" | _url_encode)
 | 
			
		||||
 | 
			
		||||
  login_response=$(_post "username=${username_encoded}&password=${password_encoded}&login=true;" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" | tr -d '\n')
 | 
			
		||||
  auth_response=$(_post "authId=${login_response}&login_chk=true" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" | tr -d '\n')
 | 
			
		||||
  if [ "$auth_response" != "OK" ]; then
 | 
			
		||||
    _err "Login failed due to invalid credentials."
 | 
			
		||||
    _err "Please double check the configured username and password and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  sessionid=$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'HTTPS_XSSID=[^;]*;' | tr -d ';')
 | 
			
		||||
  _secure_debug2 "sessionid" "$sessionid"
 | 
			
		||||
 | 
			
		||||
  export _H1="Cookie: $sessionid"
 | 
			
		||||
  _secure_debug2 "_H1" "$_H1"
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_zyxel_gs1900_validate_device_compatibility() {
 | 
			
		||||
  # Check the switches model and firmware version and throw errors
 | 
			
		||||
  # if this script isn't compatible.
 | 
			
		||||
  device_info_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=12" | tr -d '\n')
 | 
			
		||||
 | 
			
		||||
  model_name=$(_zyxel_gs1900_get_model "$device_info_html")
 | 
			
		||||
  _debug2 "model_name" "$model_name"
 | 
			
		||||
  if [ -z "$model_name" ]; then
 | 
			
		||||
    _err "Could not find the switch model name."
 | 
			
		||||
    _err "Please re-run with --debug and report a bug."
 | 
			
		||||
    return $?
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! expr "$model_name" : "GS1900-" >/dev/null; then
 | 
			
		||||
    _err "Switch is an unsupported model: $model_name"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  firmware_version=$(_zyxel_gs1900_get_firmware_version "$device_info_html")
 | 
			
		||||
  _debug2 "firmware_version" "$firmware_version"
 | 
			
		||||
  if [ -z "$firmware_version" ]; then
 | 
			
		||||
    _err "Could not find the switch firmware version."
 | 
			
		||||
    _err "Please re-run with --debug and report a bug."
 | 
			
		||||
    return $?
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug2 "_zyxel_gs1900_minimum_firmware_version" "$_zyxel_gs1900_minimum_firmware_version"
 | 
			
		||||
  minimum_major_version=$(_zyxel_gs1900_parse_major_version "$_zyxel_gs1900_minimum_firmware_version")
 | 
			
		||||
  _debug2 "minimum_major_version" "$minimum_major_version"
 | 
			
		||||
  minimum_minor_version=$(_zyxel_gs1900_parse_minor_version "$_zyxel_gs1900_minimum_firmware_version")
 | 
			
		||||
  _debug2 "minimum_minor_version" "$minimum_minor_version"
 | 
			
		||||
 | 
			
		||||
  _debug2 "firmware_version" "$firmware_version"
 | 
			
		||||
  firmware_major_version=$(_zyxel_gs1900_parse_major_version "$firmware_version")
 | 
			
		||||
  _debug2 "firmware_major_version" "$firmware_major_version"
 | 
			
		||||
  firmware_minor_version=$(_zyxel_gs1900_parse_minor_version "$firmware_version")
 | 
			
		||||
  _debug2 "firmware_minor_version" "$firmware_minor_version"
 | 
			
		||||
 | 
			
		||||
  _ret=0
 | 
			
		||||
  if [ "$firmware_major_version" -lt "$minimum_major_version" ]; then
 | 
			
		||||
    _ret=1
 | 
			
		||||
  elif [ "$firmware_major_version" -eq "$minimum_major_version" ] && [ "$firmware_minor_version" -lt "$minimum_minor_version" ]; then
 | 
			
		||||
    _ret=1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$_ret" != "0" ]; then
 | 
			
		||||
    _err "Unsupported firmware version $firmware_version. Please upgrade to at least version $_zyxel_gs1900_minimum_firmware_version."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return $?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_zyxel_gs1900_should_update() {
 | 
			
		||||
  # Get the remote certificate serial number
 | 
			
		||||
  _remote_cert=$(${ACME_OPENSSL_BIN:-openssl} s_client -showcerts -connect "${DEPLOY_ZYXEL_SWITCH}:443" 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p')
 | 
			
		||||
  _debug3 "_remote_cert" "$_remote_cert"
 | 
			
		||||
 | 
			
		||||
  _remote_cert_serial=$(printf "%s" "${_remote_cert}" | ${ACME_OPENSSL_BIN:-openssl} x509 -noout -serial)
 | 
			
		||||
  _debug2 "_remote_cert_serial" "$_remote_cert_serial"
 | 
			
		||||
 | 
			
		||||
  # Get our certificate serial number
 | 
			
		||||
  _our_cert_serial=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -serial <"${_ccert}")
 | 
			
		||||
  _debug2 "_our_cert_serial" "$_our_cert_serial"
 | 
			
		||||
 | 
			
		||||
  [ "${_remote_cert_serial}" != "${_our_cert_serial}" ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_zyxel_gs1900_upload_certificate() {
 | 
			
		||||
  # Generate a PKCS12 certificate with a temporary password since the web interface
 | 
			
		||||
  # requires a password be present. Then upload that certificate.
 | 
			
		||||
  temp_cert_password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 64)
 | 
			
		||||
  _secure_debug2 "temp_cert_password" "$temp_cert_password"
 | 
			
		||||
 | 
			
		||||
  temp_pkcs12="$(_mktemp)"
 | 
			
		||||
  _debug2 "temp_pkcs12" "$temp_pkcs12"
 | 
			
		||||
  _toPkcs "$temp_pkcs12" "$_ckey" "$_ccert" "$_cca" "$temp_cert_password"
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "Failed to generate a pkcs12 certificate."
 | 
			
		||||
    _err "Please re-run with --debug and report a bug."
 | 
			
		||||
 | 
			
		||||
    # ensure the temporary certificate file is cleaned up
 | 
			
		||||
    [ -f "${temp_pkcs12}" ] && rm -f "${temp_pkcs12}"
 | 
			
		||||
 | 
			
		||||
    return $?
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Load the upload page
 | 
			
		||||
  upload_page_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=5914" | tr -d '\n')
 | 
			
		||||
 | 
			
		||||
  # Get the first instance of XSSID from the upload page
 | 
			
		||||
  form_xss_value=$(printf "%s" "$upload_page_html" | _egrep_o 'name="XSSID"\s*value="[^"]+"' | sed 's/^.*="\([^"]\{1,\}\)"$/\1/g' | head -n 1)
 | 
			
		||||
  _secure_debug2 "form_xss_value" "$form_xss_value"
 | 
			
		||||
 | 
			
		||||
  _info "Generating the certificate upload request"
 | 
			
		||||
  upload_post_request="$(_mktemp)"
 | 
			
		||||
  upload_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    printf -- "--%s\r\n" "${upload_post_boundary}"
 | 
			
		||||
    printf "Content-Disposition: form-data; name=\"XSSID\"\r\n\r\n%s\r\n" "${form_xss_value}"
 | 
			
		||||
    printf -- "--%s\r\n" "${upload_post_boundary}"
 | 
			
		||||
    printf "Content-Disposition: form-data; name=\"http_file\"; filename=\"temp_pkcs12.pfx\"\r\n"
 | 
			
		||||
    printf "Content-Type: application/pkcs12\r\n\r\n"
 | 
			
		||||
    cat "${temp_pkcs12}"
 | 
			
		||||
    printf "\r\n"
 | 
			
		||||
    printf -- "--%s\r\n" "${upload_post_boundary}"
 | 
			
		||||
    printf "Content-Disposition: form-data; name=\"pwd\"\r\n\r\n%s\r\n" "${temp_cert_password}"
 | 
			
		||||
    printf -- "--%s\r\n" "${upload_post_boundary}"
 | 
			
		||||
    printf "Content-Disposition: form-data; name=\"cmd\"\r\n\r\n%s\r\n" "31"
 | 
			
		||||
    printf -- "--%s\r\n" "${upload_post_boundary}"
 | 
			
		||||
    printf "Content-Disposition: form-data; name=\"sysSubmit\"\r\n\r\n%s\r\n" "Import"
 | 
			
		||||
    printf -- "--%s--\r\n" "${upload_post_boundary}"
 | 
			
		||||
  } >"${upload_post_request}"
 | 
			
		||||
 | 
			
		||||
  _info "Upload certificate to the switch"
 | 
			
		||||
 | 
			
		||||
  # Unfortunately we cannot rely upon the switch response across switch models
 | 
			
		||||
  # to return a consistent body return - so we cannot inspect the result of this
 | 
			
		||||
  # upload to determine success.
 | 
			
		||||
  upload_response=$(_zyxel_upload_pkcs12 "${upload_post_request}" "${upload_post_boundary}" 2>&1)
 | 
			
		||||
  _debug3 "Upload response: ${upload_response}"
 | 
			
		||||
  rm "${upload_post_request}"
 | 
			
		||||
 | 
			
		||||
  # Pause for a few seconds to give the switch a chance to process the certificate
 | 
			
		||||
  # For some reason I've found this to be necessary on my GS1900-24E
 | 
			
		||||
  _debug2 "Waiting 4 seconds for the switch to process the newly uploaded certificate."
 | 
			
		||||
  sleep "4"
 | 
			
		||||
 | 
			
		||||
  # Check to see whether or not our update was successful
 | 
			
		||||
  _ret=0
 | 
			
		||||
  _zyxel_gs1900_should_update
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _info "The certificate was updated successfully"
 | 
			
		||||
  else
 | 
			
		||||
    _ret=1
 | 
			
		||||
    _err "The certificate upload does not appear to have worked."
 | 
			
		||||
    _err "The remote certificate does not match the certificate we tried to upload."
 | 
			
		||||
    _err "Please re-run with --debug 2 and review for unexpected errors. If none can be found please submit a bug."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # ensure the temporary files are cleaned up
 | 
			
		||||
  [ -f "${temp_pkcs12}" ] && rm -f "${temp_pkcs12}"
 | 
			
		||||
 | 
			
		||||
  return $_ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# make the certificate upload request using either
 | 
			
		||||
# --data binary with @ for file access in CURL
 | 
			
		||||
# or using --post-file for wget to ensure we upload
 | 
			
		||||
# the pkcs12 without getting tripped up on null bytes
 | 
			
		||||
#
 | 
			
		||||
# Usage _zyxel_upload_pkcs12 [body file name] [post boundary marker]
 | 
			
		||||
_zyxel_upload_pkcs12() {
 | 
			
		||||
  bodyfilename="$1"
 | 
			
		||||
  multipartformmarker="$2"
 | 
			
		||||
  _post_url="${_zyxel_switch_base_uri}/cgi-bin/httpuploadcert.cgi"
 | 
			
		||||
  httpmethod="POST"
 | 
			
		||||
  _postContentType="multipart/form-data; boundary=${multipartformmarker}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$httpmethod" ]; then
 | 
			
		||||
    httpmethod="POST"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug $httpmethod
 | 
			
		||||
  _debug "_post_url" "$_post_url"
 | 
			
		||||
  _debug2 "bodyfilename" "$bodyfilename"
 | 
			
		||||
  _debug2 "_postContentType" "$_postContentType"
 | 
			
		||||
 | 
			
		||||
  _inithttp
 | 
			
		||||
 | 
			
		||||
  if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
 | 
			
		||||
    _CURL="$_ACME_CURL"
 | 
			
		||||
    if [ "$HTTPS_INSECURE" ]; then
 | 
			
		||||
      _CURL="$_CURL --insecure  "
 | 
			
		||||
    fi
 | 
			
		||||
    if [ "$httpmethod" = "HEAD" ]; then
 | 
			
		||||
      _CURL="$_CURL -I  "
 | 
			
		||||
    fi
 | 
			
		||||
    _debug "_CURL" "$_CURL"
 | 
			
		||||
 | 
			
		||||
    response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data-binary "@${bodyfilename}" "$_post_url")"
 | 
			
		||||
 | 
			
		||||
    _ret="$?"
 | 
			
		||||
    if [ "$_ret" != "0" ]; then
 | 
			
		||||
      _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret"
 | 
			
		||||
      if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
 | 
			
		||||
        _err "Here is the curl dump log:"
 | 
			
		||||
        _err "$(cat "$_CURL_DUMP")"
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  elif [ "$_ACME_WGET" ]; then
 | 
			
		||||
    _WGET="$_ACME_WGET"
 | 
			
		||||
    if [ "$HTTPS_INSECURE" ]; then
 | 
			
		||||
      _WGET="$_WGET --no-check-certificate "
 | 
			
		||||
    fi
 | 
			
		||||
    _debug "_WGET" "$_WGET"
 | 
			
		||||
 | 
			
		||||
    response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-file="${bodyfilename}" "$_post_url" 2>"$HTTP_HEADER")"
 | 
			
		||||
 | 
			
		||||
    _ret="$?"
 | 
			
		||||
    if [ "$_ret" = "8" ]; then
 | 
			
		||||
      _ret=0
 | 
			
		||||
      _debug "wget returned 8 as the server returned a 'Bad Request' response. Let's process the response later."
 | 
			
		||||
    fi
 | 
			
		||||
    if [ "$_ret" != "0" ]; then
 | 
			
		||||
      _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
 | 
			
		||||
    fi
 | 
			
		||||
    if _contains "$_WGET" " -d "; then
 | 
			
		||||
      # Demultiplex wget debug output
 | 
			
		||||
      cat "$HTTP_HEADER" >&2
 | 
			
		||||
      _sed_i '/^[^ ][^ ]/d; /^ *$/d' "$HTTP_HEADER"
 | 
			
		||||
    fi
 | 
			
		||||
    # remove leading whitespaces from header to match curl format
 | 
			
		||||
    _sed_i 's/^  //g' "$HTTP_HEADER"
 | 
			
		||||
  else
 | 
			
		||||
    _ret="$?"
 | 
			
		||||
    _err "Neither curl nor wget have been found, cannot make $httpmethod request."
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "_ret" "$_ret"
 | 
			
		||||
  printf "%s" "$response"
 | 
			
		||||
  return $_ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_zyxel_gs1900_trigger_reboot() {
 | 
			
		||||
  # Trigger a reboot via the management reboot page in the web ui
 | 
			
		||||
  reboot_page_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=5888" | tr -d '\n')
 | 
			
		||||
  reboot_xss_value=$(printf "%s" "$reboot_page_html" | _egrep_o 'name="XSSID"\s*value="[^"]+"' | sed 's/^.*="\([^"]\{1,\}\)"$/\1/g')
 | 
			
		||||
  _secure_debug2 "reboot_xss_value" "$reboot_xss_value"
 | 
			
		||||
 | 
			
		||||
  reboot_response_html=$(_post "XSSID=${reboot_xss_value}&cmd=5889&sysSubmit=Reboot" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi" '' "POST" "application/x-www-form-urlencoded")
 | 
			
		||||
  reboot_message=$(printf "%s" "$reboot_response_html" | tr -d '\t\r\n\v\f' | _egrep_o "Rebooting now...")
 | 
			
		||||
 | 
			
		||||
  if [ -z "$reboot_message" ]; then
 | 
			
		||||
    _err "Failed to trigger switch reboot!"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# password
 | 
			
		||||
_zyxel_gs1900_password_obfuscate() {
 | 
			
		||||
  # Return the password obfuscated via the same method used by the
 | 
			
		||||
  # switch's web UI login process
 | 
			
		||||
  echo "$1" | awk '{
 | 
			
		||||
    encoded = "";
 | 
			
		||||
    password = $1;
 | 
			
		||||
    allowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 | 
			
		||||
    len = length($1);
 | 
			
		||||
    pwi = length($1);
 | 
			
		||||
 | 
			
		||||
    for (i=1; i <= (321 - pwi); i++)
 | 
			
		||||
    {
 | 
			
		||||
      if (0 == i % 5 && pwi > 0)
 | 
			
		||||
      {
 | 
			
		||||
        encoded = (encoded)(substr(password, pwi--, 1));
 | 
			
		||||
      }
 | 
			
		||||
      else if (i == 123)
 | 
			
		||||
      {
 | 
			
		||||
        if (len < 10)
 | 
			
		||||
        {
 | 
			
		||||
          encoded = (encoded)(0);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
          encoded = (encoded)(int(len / 10));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      else if (i == 289)
 | 
			
		||||
      {
 | 
			
		||||
        encoded = (encoded)(len % 10)
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        encoded = (encoded)(substr(allowed, int(rand() * length(allowed)), 1))
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    printf("%s", encoded);
 | 
			
		||||
  }'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# html label
 | 
			
		||||
_zyxel_html_table_lookup() {
 | 
			
		||||
  # Look up a value in the html representing the status page of the switch
 | 
			
		||||
  # when provided with the html of the page and the label (i.e. "Model Name:")
 | 
			
		||||
  html="$1"
 | 
			
		||||
  label=$(printf "%s" "$2" | tr -d ' ')
 | 
			
		||||
  lookup_result=$(printf "%s" "$html" | tr -d "\t\r\n\v\f" | sed 's/<tr>/\n<tr>/g' | sed 's/<td[^>]*>/<td>/g' | tr -d ' ' | grep -i "$label" | sed "s/<tr><td>$label<\/td><td>\([^<]\{1,\}\)<\/td><\/tr>/\1/i")
 | 
			
		||||
  printf "%s" "$lookup_result"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# html
 | 
			
		||||
_zyxel_gs1900_get_model() {
 | 
			
		||||
  html="$1"
 | 
			
		||||
  model_name=$(_zyxel_html_table_lookup "$html" "Model Name:")
 | 
			
		||||
  printf "%s" "$model_name"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# html
 | 
			
		||||
_zyxel_gs1900_get_firmware_version() {
 | 
			
		||||
  html="$1"
 | 
			
		||||
  firmware_version=$(_zyxel_html_table_lookup "$html" "Firmware Version:" | _egrep_o "V[^.]+.[^(]+")
 | 
			
		||||
  printf "%s" "$firmware_version"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# version_number
 | 
			
		||||
_zyxel_gs1900_parse_major_version() {
 | 
			
		||||
  printf "%s" "$1" | sed 's/^V\([0-9]\{1,\}\).\{1,\}$/\1/gi'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# version_number
 | 
			
		||||
_zyxel_gs1900_parse_minor_version() {
 | 
			
		||||
  printf "%s" "$1" | sed 's/^.\{1,\}\.\([0-9]\{1,\}\)$/\1/gi'
 | 
			
		||||
}
 | 
			
		||||
@@ -1,46 +1,42 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#This file name is "dns_1984hosting.sh"
 | 
			
		||||
#So, here must be a method dns_1984hosting_add()
 | 
			
		||||
#Which will be called by acme.sh to add the txt record to your api system.
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
# 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
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
#Author: Adrian Fedoreanu
 | 
			
		||||
#Report Bugs here: https://github.com/acmesh-official/acme.sh
 | 
			
		||||
# or here... https://github.com/acmesh-official/acme.sh/issues/2851
 | 
			
		||||
#
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
######## Public functions #####################
 | 
			
		||||
 | 
			
		||||
# Export 1984HOSTING username and password in following variables
 | 
			
		||||
#
 | 
			
		||||
#  One984HOSTING_Username=username
 | 
			
		||||
#  One984HOSTING_Password=password
 | 
			
		||||
#
 | 
			
		||||
# sessionid cookie is saved in ~/.acme.sh/account.conf
 | 
			
		||||
# username/password need to be set only when changed.
 | 
			
		||||
 | 
			
		||||
#Usage: dns_1984hosting_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# 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"
 | 
			
		||||
  _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"
 | 
			
		||||
    _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  _debug "First detect the root zone."
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain" "$fulldomain"
 | 
			
		||||
    _err "Invalid domain '$fulldomain'."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Add TXT record $fulldomain with value '$txtvalue'"
 | 
			
		||||
  _debug "Add TXT record $fulldomain with value '$txtvalue'."
 | 
			
		||||
  value="$(printf '%s' "$txtvalue" | _url_encode)"
 | 
			
		||||
  url="https://1984.hosting/domains/entry/"
 | 
			
		||||
 | 
			
		||||
@@ -53,102 +49,108 @@ dns_1984hosting_add() {
 | 
			
		||||
  _debug2 postdata "$postdata"
 | 
			
		||||
 | 
			
		||||
  _authpost "$postdata" "$url"
 | 
			
		||||
  response="$(echo "$_response" | _normalizeJson)"
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" '"haserrors": true'; then
 | 
			
		||||
    _err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post"
 | 
			
		||||
  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"
 | 
			
		||||
  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"
 | 
			
		||||
  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"
 | 
			
		||||
  _info "Added acme challenge TXT record for $fulldomain at 1984Hosting."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Remove the txt record after validation.
 | 
			
		||||
# Usage: fulldomain txtvalue
 | 
			
		||||
# Remove the txt record after validation.
 | 
			
		||||
dns_1984hosting_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _info "Delete TXT record using 1984Hosting"
 | 
			
		||||
  _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"
 | 
			
		||||
    _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  _debug "First detect the root zone."
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain" "$fulldomain"
 | 
			
		||||
    _err "Invalid domain '$fulldomain'."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug "Delete $fulldomain TXT record"
 | 
			
		||||
  _debug "Delete $fulldomain TXT record."
 | 
			
		||||
 | 
			
		||||
  url="https://1984.hosting/domains"
 | 
			
		||||
  if ! _get_zone_id "$url" "$_domain"; then
 | 
			
		||||
    _err "invalid zone" "$_domain"
 | 
			
		||||
    _err "Invalid zone '$_domain'."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _htmlget "$url/$_zone_id" "$txtvalue"
 | 
			
		||||
  _debug2 _response "$_response"
 | 
			
		||||
  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"
 | 
			
		||||
    _err "Error getting TXT entry_id for $1."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _authpost "entry=$entry_id" "$url/delentry/"
 | 
			
		||||
  response="$(echo "$_response" | _normalizeJson)"
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" '"ok": true'; then
 | 
			
		||||
    _err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post"
 | 
			
		||||
  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"
 | 
			
		||||
  _info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
# usage: _1984hosting_login username password
 | 
			
		||||
# returns 0 success
 | 
			
		||||
_1984hosting_login() {
 | 
			
		||||
  if ! _check_credentials; then return 1; fi
 | 
			
		||||
 | 
			
		||||
  if _check_cookies; then
 | 
			
		||||
    _debug "Already logged in"
 | 
			
		||||
    _debug "Already logged in."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Login to 1984Hosting as user $One984HOSTING_Username"
 | 
			
		||||
  _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/accounts/checkuserauth/"
 | 
			
		||||
  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 'cookie1984nammnamm=[^;]*;' | 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_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | 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
 | 
			
		||||
@@ -157,9 +159,13 @@ _1984hosting_login() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_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
 | 
			
		||||
@@ -171,42 +177,43 @@ _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"
 | 
			
		||||
    _debug "No cached cookie(s) found."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _authget "https://1984.hosting/accounts/loginstatus/"
 | 
			
		||||
  if _contains "$response" '"ok": true'; then
 | 
			
		||||
    _debug "Cached cookies still valid"
 | 
			
		||||
  _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"
 | 
			
		||||
 | 
			
		||||
  _debug "Cached cookies no longer valid. Clearing cookies."
 | 
			
		||||
  One984HOSTING_SESSIONID_COOKIE=""
 | 
			
		||||
  One984HOSTING_CSRFTOKEN_COOKIE=""
 | 
			
		||||
  _saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
 | 
			
		||||
  _saveaccountconf_mutable One984HOSTING_CSRFTOKEN_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
 | 
			
		||||
# _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)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
 | 
			
		||||
    # not valid
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _authget "https://1984.hosting/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
 | 
			
		||||
    if _contains "$_response" "serial" && ! _contains "$_response" "null"; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
    _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
 | 
			
		||||
@@ -216,46 +223,46 @@ _get_root() {
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usage: _get_zone_id url domain.com
 | 
			
		||||
#returns zone id for domain.com
 | 
			
		||||
# Usage: _get_zone_id url domain.com
 | 
			
		||||
# Returns zone id for domain.com
 | 
			
		||||
_get_zone_id() {
 | 
			
		||||
  url=$1
 | 
			
		||||
  domain=$2
 | 
			
		||||
  _htmlget "$url" "$domain"
 | 
			
		||||
  _debug2 _response "$_response"
 | 
			
		||||
  _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"
 | 
			
		||||
    _err "Error getting _zone_id for $2."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# add extra headers to request
 | 
			
		||||
# Add extra headers to request
 | 
			
		||||
_authget() {
 | 
			
		||||
  export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
 | 
			
		||||
  export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
 | 
			
		||||
  _response=$(_get "$1" | _normalizeJson)
 | 
			
		||||
  _debug2 _response "$_response"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# truncate huge HTML response
 | 
			
		||||
# echo: Argument list too long
 | 
			
		||||
# Truncate huge HTML response
 | 
			
		||||
_htmlget() {
 | 
			
		||||
  export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
 | 
			
		||||
  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
 | 
			
		||||
# 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 _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")
 | 
			
		||||
  _response="$(_post "$1" "$2" | _normalizeJson)"
 | 
			
		||||
  _debug2 _response "$_response"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,18 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#
 | 
			
		||||
#Author: Wolfgang Ebner
 | 
			
		||||
#Author: Sven Neubuaer
 | 
			
		||||
#Report Bugs here: https://github.com/dampfklon/acme.sh
 | 
			
		||||
#
 | 
			
		||||
# Usage:
 | 
			
		||||
# export ACMEDNS_BASE_URL="https://auth.acme-dns.io"
 | 
			
		||||
#
 | 
			
		||||
# You can optionally define an already existing account:
 | 
			
		||||
#
 | 
			
		||||
# export ACMEDNS_USERNAME="<username>"
 | 
			
		||||
# export ACMEDNS_PASSWORD="<password>"
 | 
			
		||||
# export ACMEDNS_SUBDOMAIN="<subdomain>"
 | 
			
		||||
#
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_acmedns_info='acme-dns Server API
 | 
			
		||||
 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 #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_acmedns_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 | 
			
		||||
## Acmeproxy DNS provider to be used with acmeproxy (https://github.com/mdbraber/acmeproxy)
 | 
			
		||||
## API integration by Maarten den Braber
 | 
			
		||||
##
 | 
			
		||||
## Report any bugs via https://github.com/mdbraber/acme.sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_acmeproxy_info='AcmeProxy Server API
 | 
			
		||||
 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.
 | 
			
		||||
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() {
 | 
			
		||||
  fulldomain="${1}"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,17 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_active24_info='Active24.cz
 | 
			
		||||
Site: Active24.cz
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_active24
 | 
			
		||||
Options:
 | 
			
		||||
 Active24_ApiKey API Key. Called "Identifier" in the Active24 Admin
 | 
			
		||||
 Active24_ApiSecret API Secret. Called "Secret key" in the Active24 Admin
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2059
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
#ACTIVE24_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
 | 
			
		||||
ACTIVE24_Api="https://api.active24.com"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
Active24_Api="https://rest.active24.cz"
 | 
			
		||||
# export Active24_ApiKey=ak48l3h7-ak5d-qn4t-p8gc-b6fs8c3l
 | 
			
		||||
# export Active24_ApiSecret=ajvkeo3y82ndsu2smvxy3o36496dcascksldncsq
 | 
			
		||||
 | 
			
		||||
# Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
@@ -15,8 +22,8 @@ dns_active24_add() {
 | 
			
		||||
  _active24_init
 | 
			
		||||
 | 
			
		||||
  _info "Adding txt record"
 | 
			
		||||
  if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then
 | 
			
		||||
    if _contains "$response" "errors"; then
 | 
			
		||||
  if _active24_rest POST "/v2/service/$_service_id/dns/record" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then
 | 
			
		||||
    if _contains "$response" "error"; then
 | 
			
		||||
      _err "Add txt record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    else
 | 
			
		||||
@@ -24,6 +31,7 @@ dns_active24_add() {
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
@@ -37,19 +45,25 @@ dns_active24_rm() {
 | 
			
		||||
  _active24_init
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _active24_rest GET "dns/$_domain/records/v1"
 | 
			
		||||
  # The API needs to send data in body in order the filter to work
 | 
			
		||||
  # TODO: web can also add content $txtvalue to filter and then get the id from response
 | 
			
		||||
  _active24_rest GET "/v2/service/$_service_id/dns/record" "{\"page\":1,\"descending\":true,\"sortBy\":\"name\",\"rowsPerPage\":100,\"totalRecords\":0,\"filters\":{\"type\":[\"TXT\"],\"name\":\"${_sub_domain}\"}}"
 | 
			
		||||
  #_active24_rest GET "/v2/service/$_service_id/dns/record?rowsPerPage=100"
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" "errors"; then
 | 
			
		||||
  if _contains "$response" "error"; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  hash_ids=$(echo "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o "hashId\":\"[^\"]+" | cut -c10-)
 | 
			
		||||
  # Note: it might never be more than one record actually, NEEDS more INVESTIGATION
 | 
			
		||||
  record_ids=$(printf "%s" "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o '"id" *: *[^,]+' | cut -d ':' -f 2)
 | 
			
		||||
  _debug2 record_ids "$record_ids"
 | 
			
		||||
 | 
			
		||||
  for hash_id in $hash_ids; do
 | 
			
		||||
    _debug "Removing hash_id" "$hash_id"
 | 
			
		||||
    if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then
 | 
			
		||||
      if _contains "$response" "errors"; then
 | 
			
		||||
  for redord_id in $record_ids; do
 | 
			
		||||
    _debug "Removing record_id" "$redord_id"
 | 
			
		||||
    _debug "txtvalue" "$txtvalue"
 | 
			
		||||
    if _active24_rest DELETE "/v2/service/$_service_id/dns/record/$redord_id" ""; then
 | 
			
		||||
      if _contains "$response" "error"; then
 | 
			
		||||
        _err "Unable to remove txt record."
 | 
			
		||||
        return 1
 | 
			
		||||
      else
 | 
			
		||||
@@ -63,23 +77,17 @@ dns_active24_rm() {
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  if ! _active24_rest GET "dns/domains/v1"; then
 | 
			
		||||
  if ! _active24_rest GET "/v1/user/self/service"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug "h" "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -87,7 +95,7 @@ _get_root() {
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
@@ -97,21 +105,98 @@ _get_root() {
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_active24_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
_active24_init() {
 | 
			
		||||
  Active24_ApiKey="${Active24_ApiKey:-$(_readaccountconf_mutable Active24_ApiKey)}"
 | 
			
		||||
  Active24_ApiSecret="${Active24_ApiSecret:-$(_readaccountconf_mutable Active24_ApiSecret)}"
 | 
			
		||||
  #Active24_ServiceId="${Active24_ServiceId:-$(_readaccountconf_mutable Active24_ServiceId)}"
 | 
			
		||||
 | 
			
		||||
  export _H1="Authorization: Bearer $ACTIVE24_Token"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug "data" "$data"
 | 
			
		||||
    response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$ACTIVE24_Api/$ep")"
 | 
			
		||||
  if [ -z "$Active24_ApiKey" ] || [ -z "$Active24_ApiSecret" ]; then
 | 
			
		||||
    Active24_ApiKey=""
 | 
			
		||||
    Active24_ApiSecret=""
 | 
			
		||||
    _err "You don't specify Active24 api key and ApiSecret yet."
 | 
			
		||||
    _err "Please create your key and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the credentials to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable Active24_ApiKey "$Active24_ApiKey"
 | 
			
		||||
  _saveaccountconf_mutable Active24_ApiSecret "$Active24_ApiSecret"
 | 
			
		||||
 | 
			
		||||
  _debug "A24 API CHECK"
 | 
			
		||||
  if ! _active24_rest GET "/v2/check"; then
 | 
			
		||||
    _err "A24 API check failed with: $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! echo "$response" | tr -d " " | grep \"verified\":true >/dev/null; then
 | 
			
		||||
    _err "A24 API check failed with: $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _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"
 | 
			
		||||
  _active24_get_service_id "$_domain"
 | 
			
		||||
  _debug _service_id "$_service_id"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_active24_get_service_id() {
 | 
			
		||||
  _d=$1
 | 
			
		||||
  if ! _active24_rest GET "/v1/user/self/zone/${_d}"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    response=$(echo "$response" | _json_decode)
 | 
			
		||||
    _service_id=$(echo "$response" | _egrep_o '"id" *: *[^,]+' | cut -d ':' -f 2)
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_active24_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep_qs=$2 # with query string
 | 
			
		||||
  # ep=$2
 | 
			
		||||
  ep=$(printf "%s" "$ep_qs" | cut -d '?' -f1) # no query string
 | 
			
		||||
  data="$3"
 | 
			
		||||
 | 
			
		||||
  _debug "A24 $ep"
 | 
			
		||||
  _debug "A24 $Active24_ApiKey"
 | 
			
		||||
  _debug "A24 $Active24_ApiSecret"
 | 
			
		||||
 | 
			
		||||
  timestamp=$(_time)
 | 
			
		||||
  datez=$(date -u +"%Y%m%dT%H%M%SZ")
 | 
			
		||||
  canonicalRequest="${m} ${ep} ${timestamp}"
 | 
			
		||||
  signature=$(printf "%s" "$canonicalRequest" | _hmac sha1 "$(printf "%s" "$Active24_ApiSecret" | _hex_dump | tr -d " ")" hex)
 | 
			
		||||
  authorization64="$(printf "%s:%s" "$Active24_ApiKey" "$signature" | _base64)"
 | 
			
		||||
 | 
			
		||||
  export _H1="Date: ${datez}"
 | 
			
		||||
  export _H2="Accept: application/json"
 | 
			
		||||
  export _H3="Content-Type: application/json"
 | 
			
		||||
  export _H4="Authorization: Basic ${authorization64}"
 | 
			
		||||
 | 
			
		||||
  _debug2 H1 "$_H1"
 | 
			
		||||
  _debug2 H2 "$_H2"
 | 
			
		||||
  _debug2 H3 "$_H3"
 | 
			
		||||
  _debug2 H4 "$_H4"
 | 
			
		||||
 | 
			
		||||
  # _sleep 1
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug2 "${m} $Active24_Api${ep_qs}"
 | 
			
		||||
    _debug "data" "$data"
 | 
			
		||||
    response="$(_post "$data" "$Active24_Api${ep_qs}" "" "$m" "application/json")"
 | 
			
		||||
  else
 | 
			
		||||
    if [ -z "$data" ]; then
 | 
			
		||||
      _debug2 "GET $Active24_Api${ep_qs}"
 | 
			
		||||
      response="$(_get "$Active24_Api${ep_qs}")"
 | 
			
		||||
    else
 | 
			
		||||
      _debug2 "GET $Active24_Api${ep_qs} with data: ${data}"
 | 
			
		||||
      response="$(_post "$data" "$Active24_Api${ep_qs}" "" "$m" "application/json")"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
@@ -119,23 +204,3 @@ _active24_rest() {
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_active24_init() {
 | 
			
		||||
  ACTIVE24_Token="${ACTIVE24_Token:-$(_readaccountconf_mutable ACTIVE24_Token)}"
 | 
			
		||||
  if [ -z "$ACTIVE24_Token" ]; then
 | 
			
		||||
    ACTIVE24_Token=""
 | 
			
		||||
    _err "You didn't specify a Active24 api token yet."
 | 
			
		||||
    _err "Please create the token and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable ACTIVE24_Token "$ACTIVE24_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"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,13 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
 | 
			
		||||
#This is the Alwaysdata api wrapper for acme.sh
 | 
			
		||||
#
 | 
			
		||||
#Author: Paul Koppen
 | 
			
		||||
#Report Bugs here: https://github.com/wpk-/acme.sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_ad_info='AlwaysData.com
 | 
			
		||||
Site: AlwaysData.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ad
 | 
			
		||||
Options:
 | 
			
		||||
 AD_API_KEY API Key
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/pull/503
 | 
			
		||||
Author: Paul Koppen
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
 | 
			
		||||
 | 
			
		||||
@@ -94,7 +95,7 @@ _get_root() {
 | 
			
		||||
  if _ad_rest GET "domain/"; then
 | 
			
		||||
    response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
 | 
			
		||||
    while true; do
 | 
			
		||||
      h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
      h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
      _debug h "$h"
 | 
			
		||||
      if [ -z "$h" ]; then
 | 
			
		||||
        #not valid
 | 
			
		||||
@@ -105,7 +106,7 @@ _get_root() {
 | 
			
		||||
      if [ "$hostedzone" ]; then
 | 
			
		||||
        _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
 | 
			
		||||
        if [ "$_domain_id" ]; then
 | 
			
		||||
          _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
          _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
          _domain=$h
 | 
			
		||||
          return 0
 | 
			
		||||
        fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,27 @@
 | 
			
		||||
#!/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_Secret="0p5EYueFNq501xnCPzKNbx6K51qPH2"
 | 
			
		||||
Ali_DNS_API="https://alidns.aliyuncs.com/"
 | 
			
		||||
 | 
			
		||||
#Usage: dns_ali_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_ali_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  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"
 | 
			
		||||
  _prepare_ali_credentials || return 1
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
@@ -46,14 +46,74 @@ dns_ali_rm() {
 | 
			
		||||
  _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() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=2
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
  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
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
@@ -65,7 +125,7 @@ _get_root() {
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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"
 | 
			
		||||
      _domain="$h"
 | 
			
		||||
      _debug _domain "$_domain"
 | 
			
		||||
@@ -77,52 +137,10 @@ _get_root() {
 | 
			
		||||
  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() {
 | 
			
		||||
  _qdomain="$1"
 | 
			
		||||
  _qsubdomain="$2"
 | 
			
		||||
  endpoint=$Ali_DNS_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=DescribeDomainRecords'
 | 
			
		||||
@@ -138,6 +156,7 @@ _check_exist_query() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_add_record_query() {
 | 
			
		||||
  endpoint=$Ali_DNS_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=AddDomainRecord'
 | 
			
		||||
@@ -154,6 +173,7 @@ _add_record_query() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_delete_record_query() {
 | 
			
		||||
  endpoint=$Ali_DNS_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=DeleteDomainRecord'
 | 
			
		||||
@@ -167,6 +187,7 @@ _delete_record_query() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_describe_records_query() {
 | 
			
		||||
  endpoint=$Ali_DNS_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=DescribeDomainRecords'
 | 
			
		||||
@@ -197,7 +218,3 @@ _clean() {
 | 
			
		||||
  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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,12 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Anexia CloudDNS acme.sh hook
 | 
			
		||||
# Author: MA
 | 
			
		||||
 | 
			
		||||
#ANX_Token="xxxx"
 | 
			
		||||
# 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'
 | 
			
		||||
 | 
			
		||||
@@ -127,18 +130,17 @@ _get_root() {
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  _anx_rest GET "zone.json"
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    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)
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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'
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +1,16 @@
 | 
			
		||||
#!/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_Token="Apikey xxxx"
 | 
			
		||||
ARVAN_API_URL="https://napi.arvancloud.ir/cdn/4.0/domains"
 | 
			
		||||
 | 
			
		||||
ARVAN_API_URL="https://napi.arvancloud.com/cdn/4.0/domains"
 | 
			
		||||
#Author: Vahid Fardi
 | 
			
		||||
#Report Bugs here: https://github.com/Neilpang/acme.sh
 | 
			
		||||
#
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_arvan_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
@@ -18,7 +23,7 @@ dns_arvan_add() {
 | 
			
		||||
 | 
			
		||||
  if [ -z "$Arvan_Token" ]; then
 | 
			
		||||
    _err "You didn't specify \"Arvan_Token\" token yet."
 | 
			
		||||
    _err "You can get yours from here https://npanel.arvancloud.com/profile/api-keys"
 | 
			
		||||
    _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.
 | 
			
		||||
@@ -40,7 +45,7 @@ dns_arvan_add() {
 | 
			
		||||
      _info "response id is $response"
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    elif _contains "$response" "Record Data is Duplicated"; then
 | 
			
		||||
    elif _contains "$response" "Record Data is duplicate"; then
 | 
			
		||||
      _info "Already exists, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
@@ -102,7 +107,7 @@ _get_root() {
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -115,7 +120,7 @@ _get_root() {
 | 
			
		||||
    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)
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
@@ -141,6 +146,7 @@ _arvan_rest() {
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,15 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#AURORA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
#
 | 
			
		||||
#AURORA_Secret="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
# 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"
 | 
			
		||||
 | 
			
		||||
@@ -111,7 +117,7 @@ _get_root() {
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -126,7 +132,7 @@ _get_root() {
 | 
			
		||||
      _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)
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,15 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
# This is the InternetX autoDNS xml api wrapper for acme.sh
 | 
			
		||||
# Author: auerswald@gmail.com
 | 
			
		||||
# Created: 2018-01-14
 | 
			
		||||
#
 | 
			
		||||
#     export AUTODNS_USER="username"
 | 
			
		||||
#     export AUTODNS_PASSWORD="password"
 | 
			
		||||
#     export AUTODNS_CONTEXT="context"
 | 
			
		||||
#
 | 
			
		||||
# Usage:
 | 
			
		||||
#     acme.sh --issue --dns dns_autodns -d example.com
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_autodns_info='InternetX autoDNS
 | 
			
		||||
 InternetX autoDNS XML API
 | 
			
		||||
Site: InternetX.com/autodns/
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_autodns
 | 
			
		||||
Options:
 | 
			
		||||
 AUTODNS_USER Username
 | 
			
		||||
 AUTODNS_PASSWORD Password
 | 
			
		||||
 AUTODNS_CONTEXT Context
 | 
			
		||||
Author: <auerswald@gmail.com>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
AUTODNS_API="https://gateway.autodns.com"
 | 
			
		||||
 | 
			
		||||
@@ -111,7 +110,7 @@ _get_autodns_zone() {
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
@@ -129,7 +128,7 @@ _get_autodns_zone() {
 | 
			
		||||
    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)"
 | 
			
		||||
      _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
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
#!/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
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
#
 | 
			
		||||
#AWS_SECRET_ACCESS_KEY="xxxxxxx"
 | 
			
		||||
 | 
			
		||||
#This is the Amazon Route53 api wrapper for acme.sh
 | 
			
		||||
#All `_sleep` commands are included to avoid Route53 throttling, see
 | 
			
		||||
#https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
 | 
			
		||||
# All `_sleep` commands are included to avoid Route53 throttling, see
 | 
			
		||||
# https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
 | 
			
		||||
 | 
			
		||||
AWS_HOST="route53.amazonaws.com"
 | 
			
		||||
AWS_URL="https://$AWS_HOST"
 | 
			
		||||
@@ -145,7 +147,6 @@ dns_aws_rm() {
 | 
			
		||||
  fi
 | 
			
		||||
  _sleep 1
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
@@ -157,7 +158,7 @@ _get_root() {
 | 
			
		||||
 | 
			
		||||
  # iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100 | sed 's/\./\\./g')
 | 
			
		||||
    _debug "Checking domain: $h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      _error "invalid domain"
 | 
			
		||||
@@ -173,7 +174,7 @@ _get_root() {
 | 
			
		||||
        if [ "$hostedzone" ]; then
 | 
			
		||||
          _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
 | 
			
		||||
          if [ "$_domain_id" ]; then
 | 
			
		||||
            _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
            _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
            _domain=$h
 | 
			
		||||
            return 0
 | 
			
		||||
          fi
 | 
			
		||||
@@ -207,24 +208,40 @@ _use_container_role() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_use_instance_role() {
 | 
			
		||||
  _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
 | 
			
		||||
  _instance_role_name_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
 | 
			
		||||
 | 
			
		||||
  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"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _aws_role=$(_get "$_url" "" 1)
 | 
			
		||||
  _debug "_aws_role" "$_aws_role"
 | 
			
		||||
  _use_metadata "$_url$_aws_role"
 | 
			
		||||
 | 
			
		||||
  _instance_role_name=$(_get "$_instance_role_name_url" "" 1)
 | 
			
		||||
  _debug "_instance_role_name" "$_instance_role_name"
 | 
			
		||||
  _use_metadata "$_instance_role_name_url$_instance_role_name" "$_token"
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_use_metadata() {
 | 
			
		||||
  export _H1="X-aws-ec2-metadata-token: $2"
 | 
			
		||||
  _aws_creds="$(
 | 
			
		||||
    _get "$1" "" 1 |
 | 
			
		||||
      _normalizeJson |
 | 
			
		||||
      tr '{,}' '\n' |
 | 
			
		||||
      while read -r _line; do
 | 
			
		||||
        _key="$(echo "${_line%%:*}" | tr -d '"')"
 | 
			
		||||
        _key="$(echo "${_line%%:*}" | tr -d '\"')"
 | 
			
		||||
        _value="${_line#*:}"
 | 
			
		||||
        _debug3 "_key" "$_key"
 | 
			
		||||
        _secure_debug3 "_value" "$_value"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,13 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#AZION_Email=""
 | 
			
		||||
#AZION_Password=""
 | 
			
		||||
#
 | 
			
		||||
# 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"
 | 
			
		||||
 | 
			
		||||
@@ -96,7 +100,7 @@ _get_root() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      # not valid
 | 
			
		||||
@@ -107,7 +111,7 @@ _get_root() {
 | 
			
		||||
      _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)
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,25 @@
 | 
			
		||||
#!/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"
 | 
			
		||||
 AZUREDNS_BEARERTOKEN Bearer Token. Used instead of service principal credentials or managed identity. Optional.
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS"
 | 
			
		||||
wiki=https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Azure-DNS
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
# Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# 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() {
 | 
			
		||||
@@ -20,6 +32,7 @@ dns_azure_add() {
 | 
			
		||||
    AZUREDNS_TENANTID=""
 | 
			
		||||
    AZUREDNS_APPID=""
 | 
			
		||||
    AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
    AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
    _err "You didn't specify the Azure Subscription ID"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
@@ -34,37 +47,45 @@ dns_azure_add() {
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_TENANTID ""
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_APPID ""
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_CLIENTSECRET ""
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_BEARERTOKEN ""
 | 
			
		||||
  else
 | 
			
		||||
    _info "You didn't ask to use Azure managed identity, checking service principal credentials"
 | 
			
		||||
    _info "You didn't ask to use Azure managed identity, checking service principal credentials or provided bearer token"
 | 
			
		||||
    AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
 | 
			
		||||
    AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
 | 
			
		||||
    AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
 | 
			
		||||
    AZUREDNS_BEARERTOKEN="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
 | 
			
		||||
    if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
 | 
			
		||||
      if [ -z "$AZUREDNS_TENANTID" ]; then
 | 
			
		||||
        AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
        AZUREDNS_TENANTID=""
 | 
			
		||||
        AZUREDNS_APPID=""
 | 
			
		||||
        AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
        AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
        _err "You didn't specify the Azure Tenant ID "
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
    if [ -z "$AZUREDNS_TENANTID" ]; then
 | 
			
		||||
      AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
      AZUREDNS_TENANTID=""
 | 
			
		||||
      AZUREDNS_APPID=""
 | 
			
		||||
      AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
      _err "You didn't specify the Azure Tenant ID "
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
      if [ -z "$AZUREDNS_APPID" ]; then
 | 
			
		||||
        AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
        AZUREDNS_TENANTID=""
 | 
			
		||||
        AZUREDNS_APPID=""
 | 
			
		||||
        AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
        AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
        _err "You didn't specify the Azure App ID"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
    if [ -z "$AZUREDNS_APPID" ]; then
 | 
			
		||||
      AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
      AZUREDNS_TENANTID=""
 | 
			
		||||
      AZUREDNS_APPID=""
 | 
			
		||||
      AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
      _err "You didn't specify the Azure App ID"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
 | 
			
		||||
      AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
      AZUREDNS_TENANTID=""
 | 
			
		||||
      AZUREDNS_APPID=""
 | 
			
		||||
      AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
      _err "You didn't specify the Azure Client Secret"
 | 
			
		||||
      return 1
 | 
			
		||||
      if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
 | 
			
		||||
        AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
        AZUREDNS_TENANTID=""
 | 
			
		||||
        AZUREDNS_APPID=""
 | 
			
		||||
        AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
        AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
        _err "You didn't specify the Azure Client Secret"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      _info "Using provided bearer token"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    #save account details to account conf file, don't opt in for azure manages identity check.
 | 
			
		||||
@@ -72,9 +93,14 @@ dns_azure_add() {
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$AZUREDNS_BEARERTOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
 | 
			
		||||
  if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
 | 
			
		||||
    accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
 | 
			
		||||
  else
 | 
			
		||||
    accesstoken=$(echo "$AZUREDNS_BEARERTOKEN" | sed "s/Bearer //g")
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
@@ -124,7 +150,7 @@ dns_azure_add() {
 | 
			
		||||
# Usage: fulldomain txtvalue
 | 
			
		||||
# 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() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
@@ -136,6 +162,7 @@ dns_azure_rm() {
 | 
			
		||||
    AZUREDNS_TENANTID=""
 | 
			
		||||
    AZUREDNS_APPID=""
 | 
			
		||||
    AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
    AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
    _err "You didn't specify the Azure Subscription ID "
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
@@ -144,40 +171,51 @@ dns_azure_rm() {
 | 
			
		||||
  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"
 | 
			
		||||
    _info "You didn't ask to use Azure managed identity, checking service principal credentials or provided bearer token"
 | 
			
		||||
    AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
 | 
			
		||||
    AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
 | 
			
		||||
    AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
 | 
			
		||||
    AZUREDNS_BEARERTOKEN="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
 | 
			
		||||
    if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
 | 
			
		||||
      if [ -z "$AZUREDNS_TENANTID" ]; then
 | 
			
		||||
        AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
        AZUREDNS_TENANTID=""
 | 
			
		||||
        AZUREDNS_APPID=""
 | 
			
		||||
        AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
        AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
        _err "You didn't specify the Azure Tenant ID "
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
    if [ -z "$AZUREDNS_TENANTID" ]; then
 | 
			
		||||
      AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
      AZUREDNS_TENANTID=""
 | 
			
		||||
      AZUREDNS_APPID=""
 | 
			
		||||
      AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
      _err "You didn't specify the Azure Tenant ID "
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
      if [ -z "$AZUREDNS_APPID" ]; then
 | 
			
		||||
        AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
        AZUREDNS_TENANTID=""
 | 
			
		||||
        AZUREDNS_APPID=""
 | 
			
		||||
        AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
        AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
        _err "You didn't specify the Azure App ID"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
    if [ -z "$AZUREDNS_APPID" ]; then
 | 
			
		||||
      AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
      AZUREDNS_TENANTID=""
 | 
			
		||||
      AZUREDNS_APPID=""
 | 
			
		||||
      AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
      _err "You didn't specify the Azure App ID"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
 | 
			
		||||
      AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
      AZUREDNS_TENANTID=""
 | 
			
		||||
      AZUREDNS_APPID=""
 | 
			
		||||
      AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
      _err "You didn't specify the Azure Client Secret"
 | 
			
		||||
      return 1
 | 
			
		||||
      if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
 | 
			
		||||
        AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
        AZUREDNS_TENANTID=""
 | 
			
		||||
        AZUREDNS_APPID=""
 | 
			
		||||
        AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
        AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
        _err "You didn't specify the Azure Client Secret"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      _info "Using provided bearer token"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
 | 
			
		||||
  if [ -z "$AZUREDNS_BEARERTOKEN" ]; then
 | 
			
		||||
    accesstoken=$(_azure_getaccess_token "$AZUREDNS_MANAGEDIDENTITY" "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
 | 
			
		||||
  else
 | 
			
		||||
    accesstoken=$(echo "$AZUREDNS_BEARERTOKEN" | sed "s/Bearer //g")
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
@@ -256,10 +294,10 @@ _azure_rest() {
 | 
			
		||||
    if [ "$_code" = "401" ]; then
 | 
			
		||||
      # we have an invalid access token set to expired
 | 
			
		||||
      _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
 | 
			
		||||
    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
 | 
			
		||||
      _request_retry_times="$(_math "$_request_retry_times" + 1)"
 | 
			
		||||
      _info "REST call error $_code retrying $ep in $_request_retry_times s"
 | 
			
		||||
@@ -277,14 +315,14 @@ _azure_rest() {
 | 
			
		||||
  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() {
 | 
			
		||||
  managedIdentity=$1
 | 
			
		||||
  tenantID=$2
 | 
			
		||||
  clientID=$3
 | 
			
		||||
  clientSecret=$4
 | 
			
		||||
 | 
			
		||||
  accesstoken="${AZUREDNS_BEARERTOKEN:-$(_readaccountconf_mutable AZUREDNS_BEARERTOKEN)}"
 | 
			
		||||
  accesstoken="${AZUREDNS_ACCESSTOKEN:-$(_readaccountconf_mutable AZUREDNS_ACCESSTOKEN)}"
 | 
			
		||||
  expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
 | 
			
		||||
 | 
			
		||||
  # can we reuse the bearer token?
 | 
			
		||||
@@ -301,9 +339,18 @@ _azure_getaccess_token() {
 | 
			
		||||
  _debug "getting new bearer token"
 | 
			
		||||
 | 
			
		||||
  if [ "$managedIdentity" = true ]; then
 | 
			
		||||
    # https://docs.microsoft.com/en-us/azure/active-directory/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/)"
 | 
			
		||||
    # https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
 | 
			
		||||
    if [ -n "$IDENTITY_ENDPOINT" ]; then
 | 
			
		||||
      # Some Azure environments may set IDENTITY_ENDPOINT (formerly MSI_ENDPOINT) to have an alternative metadata endpoint
 | 
			
		||||
      url="$IDENTITY_ENDPOINT?api-version=2019-08-01&resource=https://management.azure.com/"
 | 
			
		||||
      headers="X-IDENTITY-HEADER: $IDENTITY_HEADER"
 | 
			
		||||
    else
 | 
			
		||||
      url="http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
 | 
			
		||||
      headers="Metadata: true"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    export _H1="$headers"
 | 
			
		||||
    response="$(_get "$url")"
 | 
			
		||||
    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 \")
 | 
			
		||||
@@ -321,14 +368,14 @@ _azure_getaccess_token() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$_ret" != "0" ]; then
 | 
			
		||||
    _err "error $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable AZUREDNS_BEARERTOKEN "$accesstoken"
 | 
			
		||||
  _saveaccountconf_mutable AZUREDNS_ACCESSTOKEN "$accesstoken"
 | 
			
		||||
  _saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "$expires_on"
 | 
			
		||||
  printf "%s" "$accesstoken"
 | 
			
		||||
  return 0
 | 
			
		||||
@@ -341,15 +388,18 @@ _get_root() {
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  ## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list
 | 
			
		||||
  ## returns up to 100 zones in one response therefore handling more results is not not implemented
 | 
			
		||||
  ## (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
 | 
			
		||||
  ## 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. Handling more results is not implemented
 | 
			
		||||
  ## (ZoneListResult with continuation token for the next page of results)
 | 
			
		||||
  ##
 | 
			
		||||
  ## 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"
 | 
			
		||||
  # Find matching domain name in Json response
 | 
			
		||||
  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"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -364,7 +414,7 @@ _get_root() {
 | 
			
		||||
          #create the record at the domain apex (@) if only the domain name was provided as --domain-alias
 | 
			
		||||
          _sub_domain="@"
 | 
			
		||||
        else
 | 
			
		||||
          _sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
          _sub_domain=$(echo "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        fi
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										281
									
								
								dnsapi/dns_beget.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										281
									
								
								dnsapi/dns_beget.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,281 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_beget_info='Beget.com
 | 
			
		||||
Site: Beget.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_beget
 | 
			
		||||
Options:
 | 
			
		||||
 BEGET_User API user
 | 
			
		||||
 BEGET_Password API password
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/6200
 | 
			
		||||
Author: ARNik <arnik@arnik.ru>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
Beget_Api="https://api.beget.com/api"
 | 
			
		||||
 | 
			
		||||
####################  Public functions ####################
 | 
			
		||||
 | 
			
		||||
# Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
dns_beget_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _debug "dns_beget_add() $fulldomain $txtvalue"
 | 
			
		||||
  fulldomain=$(echo "$fulldomain" | _lower_case)
 | 
			
		||||
 | 
			
		||||
  Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}"
 | 
			
		||||
  Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$Beget_Username" ] || [ -z "$Beget_Password" ]; then
 | 
			
		||||
    Beget_Username=""
 | 
			
		||||
    Beget_Password=""
 | 
			
		||||
    _err "You must export variables: Beget_Username, and Beget_Password"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the credentials to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable Beget_Username "$Beget_Username"
 | 
			
		||||
  _saveaccountconf_mutable Beget_Password "$Beget_Password"
 | 
			
		||||
 | 
			
		||||
  _info "Prepare subdomain."
 | 
			
		||||
  if ! _prepare_subdomain "$fulldomain"; then
 | 
			
		||||
    _err "Can't prepare subdomain."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Get domain records"
 | 
			
		||||
  data="{\"fqdn\":\"$fulldomain\"}"
 | 
			
		||||
  res=$(_api_call "$Beget_Api/dns/getData" "$data")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _err "Can't get domain records."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Add new TXT record"
 | 
			
		||||
  data="{\"fqdn\":\"$fulldomain\",\"records\":{"
 | 
			
		||||
  data=${data}$(_parce_records "$res" "A")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "AAAA")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "CAA")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "MX")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "SRV")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "TXT")
 | 
			
		||||
  data=$(echo "$data" | sed 's/,$//')
 | 
			
		||||
  data=${data}'}}'
 | 
			
		||||
 | 
			
		||||
  str=$(_txt_to_dns_json "$txtvalue")
 | 
			
		||||
  data=$(_add_record "$data" "TXT" "$str")
 | 
			
		||||
 | 
			
		||||
  res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _err "Can't change domain records."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: fulldomain txtvalue
 | 
			
		||||
# Used to remove the txt record after validation
 | 
			
		||||
dns_beget_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _debug "dns_beget_rm() $fulldomain $txtvalue"
 | 
			
		||||
  fulldomain=$(echo "$fulldomain" | _lower_case)
 | 
			
		||||
 | 
			
		||||
  Beget_Username="${Beget_Username:-$(_readaccountconf_mutable Beget_Username)}"
 | 
			
		||||
  Beget_Password="${Beget_Password:-$(_readaccountconf_mutable Beget_Password)}"
 | 
			
		||||
 | 
			
		||||
  _info "Get current domain records"
 | 
			
		||||
  data="{\"fqdn\":\"$fulldomain\"}"
 | 
			
		||||
  res=$(_api_call "$Beget_Api/dns/getData" "$data")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _err "Can't get domain records."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Remove TXT record"
 | 
			
		||||
  data="{\"fqdn\":\"$fulldomain\",\"records\":{"
 | 
			
		||||
  data=${data}$(_parce_records "$res" "A")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "AAAA")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "CAA")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "MX")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "SRV")
 | 
			
		||||
  data=${data}$(_parce_records "$res" "TXT")
 | 
			
		||||
  data=$(echo "$data" | sed 's/,$//')
 | 
			
		||||
  data=${data}'}}'
 | 
			
		||||
 | 
			
		||||
  str=$(_txt_to_dns_json "$txtvalue")
 | 
			
		||||
  data=$(_rm_record "$data" "$str")
 | 
			
		||||
 | 
			
		||||
  res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _err "Can't change domain records."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ####################
 | 
			
		||||
 | 
			
		||||
# Create subdomain if needed
 | 
			
		||||
# Usage: _prepare_subdomain [fulldomain]
 | 
			
		||||
_prepare_subdomain() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
 | 
			
		||||
  _info "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"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_sub_domain" ]; then
 | 
			
		||||
    _debug "$fulldomain is a root domain."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Get subdomain list"
 | 
			
		||||
  res=$(_api_call "$Beget_Api/domain/getSubdomainList")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _err "Can't get subdomain list."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$res" "\"fqdn\":\"$fulldomain\""; then
 | 
			
		||||
    _debug "Subdomain $fulldomain already exist."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Subdomain $fulldomain does not exist. Let's create one."
 | 
			
		||||
  data="{\"subdomain\":\"$_sub_domain\",\"domain_id\":$_domain_id}"
 | 
			
		||||
  res=$(_api_call "$Beget_Api/domain/addSubdomainVirtual" "$data")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _err "Can't create subdomain."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Cleanup subdomen records"
 | 
			
		||||
  data="{\"fqdn\":\"$fulldomain\",\"records\":{}}"
 | 
			
		||||
  res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _debug "Can't cleanup $fulldomain records."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  data="{\"fqdn\":\"www.$fulldomain\",\"records\":{}}"
 | 
			
		||||
  res=$(_api_call "$Beget_Api/dns/changeRecords" "$data")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _debug "Can't cleanup www.$fulldomain records."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: _get_root _acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=32436365
 | 
			
		||||
_get_root() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  _debug "Get domain list"
 | 
			
		||||
  res=$(_api_call "$Beget_Api/domain/getList")
 | 
			
		||||
  if ! _is_api_reply_ok "$res"; then
 | 
			
		||||
    _err "Can't get domain list."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$res" "$h"; then
 | 
			
		||||
      _domain_id=$(echo "$res" | _egrep_o "\"id\":[0-9]*,\"fqdn\":\"$h\"" | cut -d , -f1 | cut -d : -f2)
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        if [ "$h" != "$fulldomain" ]; then
 | 
			
		||||
          _sub_domain=$(echo "$fulldomain" | cut -d . -f 1-"$p")
 | 
			
		||||
        else
 | 
			
		||||
          _sub_domain=""
 | 
			
		||||
        fi
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p="$i"
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Parce DNS records from json string
 | 
			
		||||
# Usage: _parce_records [j_str] [record_name]
 | 
			
		||||
_parce_records() {
 | 
			
		||||
  j_str=$1
 | 
			
		||||
  record_name=$2
 | 
			
		||||
  res="\"$record_name\":["
 | 
			
		||||
  res=${res}$(echo "$j_str" | _egrep_o "\"$record_name\":\[.*" | cut -d '[' -f2 | cut -d ']' -f1)
 | 
			
		||||
  res=${res}"],"
 | 
			
		||||
  echo "$res"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: _add_record [data] [record_name] [record_data]
 | 
			
		||||
_add_record() {
 | 
			
		||||
  data=$1
 | 
			
		||||
  record_name=$2
 | 
			
		||||
  record_data=$3
 | 
			
		||||
  echo "$data" | sed "s/\"$record_name\":\[/\"$record_name\":\[$record_data,/" | sed "s/,\]/\]/"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: _rm_record [data] [record_data]
 | 
			
		||||
_rm_record() {
 | 
			
		||||
  data=$1
 | 
			
		||||
  record_data=$2
 | 
			
		||||
  echo "$data" | sed "s/$record_data//g" | sed "s/,\+/,/g" |
 | 
			
		||||
    sed "s/{,/{/g" | sed "s/,}/}/g" |
 | 
			
		||||
    sed "s/\[,/\[/g" | sed "s/,\]/\]/g"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_txt_to_dns_json() {
 | 
			
		||||
  echo "{\"ttl\":600,\"txtdata\":\"$1\"}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: _api_call [api_url] [input_data]
 | 
			
		||||
_api_call() {
 | 
			
		||||
  api_url="$1"
 | 
			
		||||
  input_data="$2"
 | 
			
		||||
 | 
			
		||||
  _debug "_api_call $api_url"
 | 
			
		||||
  _debug "Request: $input_data"
 | 
			
		||||
 | 
			
		||||
  # res=$(curl -s -L -D ./http.header \
 | 
			
		||||
  # "$api_url" \
 | 
			
		||||
  # --data-urlencode login=$Beget_Username \
 | 
			
		||||
  # --data-urlencode passwd=$Beget_Password \
 | 
			
		||||
  # --data-urlencode input_format=json \
 | 
			
		||||
  # --data-urlencode output_format=json \
 | 
			
		||||
  # --data-urlencode "input_data=$input_data")
 | 
			
		||||
 | 
			
		||||
  url="$api_url?login=$Beget_Username&passwd=$Beget_Password&input_format=json&output_format=json"
 | 
			
		||||
  if [ -n "$input_data" ]; then
 | 
			
		||||
    url=${url}"&input_data="
 | 
			
		||||
    url=${url}$(echo "$input_data" | _url_encode)
 | 
			
		||||
  fi
 | 
			
		||||
  res=$(_get "$url")
 | 
			
		||||
 | 
			
		||||
  _debug "Reply: $res"
 | 
			
		||||
  echo "$res"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: _is_api_reply_ok [api_reply]
 | 
			
		||||
_is_api_reply_ok() {
 | 
			
		||||
  _contains "$1" '^{"status":"success","answer":{"status":"success","result":.*}}$'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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 ##################################
 | 
			
		||||
@@ -1,16 +1,13 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
## Will be called by acme.sh to add the TXT record via the Bunny DNS API.
 | 
			
		||||
## returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
## Author: nosilver4u <nosilver4u at ewww.io>
 | 
			
		||||
## GitHub: https://github.com/nosilver4u/acme.sh
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Environment Variables Required:
 | 
			
		||||
##
 | 
			
		||||
## BUNNY_API_KEY="75310dc4-ca77-9ac3-9a19-f6355db573b49ce92ae1-2655-3ebd-61ac-3a3ae34834cc"
 | 
			
		||||
##
 | 
			
		||||
# 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  #####################
 | 
			
		||||
 | 
			
		||||
@@ -199,7 +196,7 @@ _get_base_domain() {
 | 
			
		||||
    _debug2 domain_list "$domain_list"
 | 
			
		||||
 | 
			
		||||
    i=1
 | 
			
		||||
    while [ $i -gt 0 ]; do
 | 
			
		||||
    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)
 | 
			
		||||
@@ -211,7 +208,7 @@ _get_base_domain() {
 | 
			
		||||
      ## check if it exists
 | 
			
		||||
      if [ -n "$found" ]; then
 | 
			
		||||
        ## 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")
 | 
			
		||||
        _domain_id="$(echo "$found" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
 | 
			
		||||
        _debug _domain_id "$_domain_id"
 | 
			
		||||
@@ -221,11 +218,11 @@ _get_base_domain() {
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      ## increment cut point $i
 | 
			
		||||
      i=$(_math $i + 1)
 | 
			
		||||
      i=$(_math "$i" + 1)
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    if [ -z "$found" ]; then
 | 
			
		||||
      page=$(_math $page + 1)
 | 
			
		||||
      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")"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,16 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
#
 | 
			
		||||
#CF_Email="xxxx@sss.com"
 | 
			
		||||
 | 
			
		||||
#CF_Token="xxxx"
 | 
			
		||||
#CF_Account_ID="xxxx"
 | 
			
		||||
#CF_Zone_ID="xxxx"
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_cf_info='CloudFlare
 | 
			
		||||
Site: CloudFlare.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cf
 | 
			
		||||
Options:
 | 
			
		||||
 CF_Key API Key
 | 
			
		||||
 CF_Email Your account email
 | 
			
		||||
OptionsAlt:
 | 
			
		||||
 CF_Token API Token
 | 
			
		||||
 CF_Account_ID Account ID
 | 
			
		||||
 CF_Zone_ID Zone ID. Optional.
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
CF_Api="https://api.cloudflare.com/client/v4"
 | 
			
		||||
 | 
			
		||||
@@ -183,7 +186,7 @@ _get_root() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -203,7 +206,7 @@ _get_root() {
 | 
			
		||||
    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 \" | tr -d " ")
 | 
			
		||||
      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
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,15 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Author: Radek Sprta <sprta@vshosting.cz>
 | 
			
		||||
 | 
			
		||||
#CLOUDDNS_EMAIL=XXXXX
 | 
			
		||||
#CLOUDDNS_PASSWORD="YYYYYYYYY"
 | 
			
		||||
#CLOUDDNS_CLIENT_ID=XXXXX
 | 
			
		||||
# 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'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,15 @@
 | 
			
		||||
#!/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/
 | 
			
		||||
# Editor: I Komang Suryadana
 | 
			
		||||
 | 
			
		||||
#CLOUDNS_AUTH_ID=XXXXX
 | 
			
		||||
#CLOUDNS_SUB_AUTH_ID=XXXXX
 | 
			
		||||
#CLOUDNS_AUTH_PASSWORD="YYYYYYYYY"
 | 
			
		||||
CLOUDNS_API="https://api.cloudns.net"
 | 
			
		||||
DOMAIN_TYPE=
 | 
			
		||||
DOMAIN_MASTER=
 | 
			
		||||
@@ -78,7 +81,7 @@ dns_cloudns_rm() {
 | 
			
		||||
    return 1
 | 
			
		||||
  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')
 | 
			
		||||
 | 
			
		||||
    if [ -n "$record_id" ]; then
 | 
			
		||||
@@ -161,7 +164,7 @@ _dns_cloudns_get_zone_info() {
 | 
			
		||||
_dns_cloudns_get_zone_name() {
 | 
			
		||||
  i=2
 | 
			
		||||
  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
 | 
			
		||||
      return 1
 | 
			
		||||
@@ -194,10 +197,11 @@ _dns_cloudns_http_api_call() {
 | 
			
		||||
    auth_user="auth-id=$CLOUDNS_AUTH_ID"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  encoded_password=$(echo "$CLOUDNS_AUTH_PASSWORD" | tr -d "\n\r" | _url_encode)
 | 
			
		||||
  if [ -z "$2" ]; then
 | 
			
		||||
    data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD"
 | 
			
		||||
    data="$auth_user&auth-password=$encoded_password"
 | 
			
		||||
  else
 | 
			
		||||
    data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD&$2"
 | 
			
		||||
    data="$auth_user&auth-password=$encoded_password&$2"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  response="$(_get "$CLOUDNS_API/$method?$data")"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,14 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# DNS API for acme.sh for Core-Networks (https://beta.api.core-networks.de/doc/).
 | 
			
		||||
# created by 5ll and francis
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_cn_info='Core-Networks.de
 | 
			
		||||
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"
 | 
			
		||||
 | 
			
		||||
@@ -124,7 +131,7 @@ _cn_get_root() {
 | 
			
		||||
  p=1
 | 
			
		||||
  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 _H1 "${_H1}"
 | 
			
		||||
 | 
			
		||||
@@ -142,7 +149,7 @@ _cn_get_root() {
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,15 @@
 | 
			
		||||
#!/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\."
 | 
			
		||||
 | 
			
		||||
@@ -226,7 +237,7 @@ _get_root() {
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100).
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100).
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -240,7 +251,7 @@ _get_root() {
 | 
			
		||||
    if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
 | 
			
		||||
      _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,16 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Author: Wout Decre <wout@canodus.be>
 | 
			
		||||
# 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"
 | 
			
		||||
#CONSTELLIX_Key="XXX"
 | 
			
		||||
#CONSTELLIX_Secret="XXX"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
@@ -111,12 +117,12 @@ dns_constellix_rm() {
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  domain=$(echo "$1" | _lower_case)
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  _debug "Detecting root zone"
 | 
			
		||||
  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
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
@@ -128,7 +134,7 @@ _get_root() {
 | 
			
		||||
    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)
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-"$p")
 | 
			
		||||
        _domain="$h"
 | 
			
		||||
 | 
			
		||||
        _debug _domain_id "$_domain_id"
 | 
			
		||||
@@ -150,6 +156,9 @@ _constellix_rest() {
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  # Prevent rate limit
 | 
			
		||||
  _sleep 2
 | 
			
		||||
 | 
			
		||||
  rdate=$(date +"%s")"000"
 | 
			
		||||
  hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,18 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#
 | 
			
		||||
#Author: Bjarne Saltbaek
 | 
			
		||||
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/3732
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
# 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 #####################
 | 
			
		||||
#
 | 
			
		||||
# Export CPANEL username,api token and hostname in the following variables
 | 
			
		||||
#
 | 
			
		||||
# cPanel_Username=username
 | 
			
		||||
# cPanel_Apitoken=apitoken
 | 
			
		||||
# cPanel_Hostname=hostname
 | 
			
		||||
#
 | 
			
		||||
# Usage: add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
dns_cpanel_add() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,15 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Script to use with curanet.dk, scannet.dk, wannafind.dk, dandomain.dk DNS management.
 | 
			
		||||
#Requires api credentials with scope: dns
 | 
			
		||||
#Author: Peter L. Hansen <peter@r12.dk>
 | 
			
		||||
#Version 1.0
 | 
			
		||||
# 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"
 | 
			
		||||
@@ -136,7 +142,7 @@ _get_root() {
 | 
			
		||||
  i=1
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,15 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
########
 | 
			
		||||
# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/acmesh-official/acme.sh)
 | 
			
		||||
#
 | 
			
		||||
# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
 | 
			
		||||
#
 | 
			
		||||
# Dependencies:
 | 
			
		||||
# -------------
 | 
			
		||||
# - oathtool (When using 2 Factor Authentication)
 | 
			
		||||
#
 | 
			
		||||
# Issues:
 | 
			
		||||
# -------
 | 
			
		||||
# Any issues / questions / suggestions can be posted here:
 | 
			
		||||
# https://github.com/noplanman/cyon-api/issues
 | 
			
		||||
#
 | 
			
		||||
# Author: Armando Lüscher <armando@noplanman.ch>
 | 
			
		||||
########
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_cyon_info='cyon.ch
 | 
			
		||||
Site: cyon.ch
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cyon
 | 
			
		||||
Options:
 | 
			
		||||
 CY_Username Username
 | 
			
		||||
 CY_Password API Token
 | 
			
		||||
 CY_OTP_Secret OTP token. Only required if using 2FA
 | 
			
		||||
Issues: github.com/noplanman/cyon-api/issues
 | 
			
		||||
Author: Armando Lüscher <armando@noplanman.ch>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
dns_cyon_add() {
 | 
			
		||||
  _cyon_load_credentials &&
 | 
			
		||||
@@ -221,10 +215,8 @@ _cyon_change_domain_env() {
 | 
			
		||||
 | 
			
		||||
  if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi
 | 
			
		||||
 | 
			
		||||
  domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)"
 | 
			
		||||
 | 
			
		||||
  # Bail if domain environment change fails.
 | 
			
		||||
  if [ "${domain_env_success}" != "true" ]; then
 | 
			
		||||
  if [ "$(printf "%s" "${domain_env_response}" | _cyon_get_environment_change_status)" != "true" ]; then
 | 
			
		||||
    _err "    $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)"
 | 
			
		||||
    _err ""
 | 
			
		||||
    return 1
 | 
			
		||||
@@ -238,7 +230,7 @@ _cyon_add_txt() {
 | 
			
		||||
  _info "  - Adding DNS TXT entry..."
 | 
			
		||||
 | 
			
		||||
  add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async"
 | 
			
		||||
  add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}"
 | 
			
		||||
  add_txt_data="name=${fulldomain_idn}.&ttl=900&type=TXT&dnscontent=${txtvalue}"
 | 
			
		||||
 | 
			
		||||
  add_txt_response="$(_post "$add_txt_data" "$add_txt_url")"
 | 
			
		||||
  _debug add_txt_response "${add_txt_response}"
 | 
			
		||||
@@ -247,9 +239,10 @@ _cyon_add_txt() {
 | 
			
		||||
 | 
			
		||||
  add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)"
 | 
			
		||||
  add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)"
 | 
			
		||||
  add_txt_validation="$(printf "%s" "${add_txt_response}" | _cyon_get_validation_status)"
 | 
			
		||||
 | 
			
		||||
  # Bail if adding TXT entry fails.
 | 
			
		||||
  if [ "${add_txt_status}" != "true" ]; then
 | 
			
		||||
  if [ "${add_txt_status}" != "true" ] || [ "${add_txt_validation}" != "true" ]; then
 | 
			
		||||
    _err "    ${add_txt_message}"
 | 
			
		||||
    _err ""
 | 
			
		||||
    return 1
 | 
			
		||||
@@ -311,13 +304,21 @@ _cyon_get_response_message() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_cyon_get_response_status() {
 | 
			
		||||
  _egrep_o '"status":\w*' | cut -d : -f 2
 | 
			
		||||
  _egrep_o '"status":[a-zA-z0-9]*' | cut -d : -f 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_cyon_get_validation_status() {
 | 
			
		||||
  _egrep_o '"valid":[a-zA-z0-9]*' | cut -d : -f 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_cyon_get_response_success() {
 | 
			
		||||
  _egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_cyon_get_environment_change_status() {
 | 
			
		||||
  _egrep_o '"authenticated":[a-zA-z0-9]*' | cut -d : -f 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_cyon_check_if_2fa_missed() {
 | 
			
		||||
  # Did we miss the 2FA?
 | 
			
		||||
  if test "${1#*multi_factor_form}" != "${1}"; then
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +1,14 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
 | 
			
		||||
# vim: et ts=2 sw=2
 | 
			
		||||
#
 | 
			
		||||
# DirectAdmin 1.41.0 API
 | 
			
		||||
# The DirectAdmin interface has it's own Let's encrypt functionality, but this
 | 
			
		||||
# script can be used to generate certificates for names which are not hosted on
 | 
			
		||||
# DirectAdmin
 | 
			
		||||
#
 | 
			
		||||
# 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)
 | 
			
		||||
#
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_da_info='DirectAdmin Server API
 | 
			
		||||
Site: DirectAdmin.com/api.php
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_da
 | 
			
		||||
Options:
 | 
			
		||||
 DA_Api API Server URL. E.g. "https://remoteUser:remotePassword@da.domain.tld:8443"
 | 
			
		||||
 DA_Api_Insecure Insecure TLS. 0: check for cert validity, 1: always accept
 | 
			
		||||
Issues: github.com/TigerP/acme.sh/issues
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
# 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"
 | 
			
		||||
  _da_api CMD_API_SHOW_DOMAINS "" "${domain}"
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      # not valid
 | 
			
		||||
@@ -86,7 +69,7 @@ _get_root() {
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    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
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,13 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Created by RaidenII, to use DuckDNS's API to add/remove text records
 | 
			
		||||
#modified by helbgd @ 03/13/2018 to support ddnss.de
 | 
			
		||||
#modified by mod242 @ 04/24/2018 to support different ddnss domains
 | 
			
		||||
#Please note: the Wildcard Feature must be turned on for the Host record
 | 
			
		||||
#and the checkbox for TXT needs to be enabled
 | 
			
		||||
 | 
			
		||||
# Pass credentials before "acme.sh --issue --dns dns_ddnss ..."
 | 
			
		||||
# --
 | 
			
		||||
# export DDNSS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
 | 
			
		||||
# --
 | 
			
		||||
#
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_ddnss_info='DDNSS.de
 | 
			
		||||
Site: DDNSS.de
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ddnss
 | 
			
		||||
Options:
 | 
			
		||||
 DDNSS_Token API Token
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2230
 | 
			
		||||
Author: @helbgd, @mod242
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
DDNSS_DNS_API="https://ddnss.de/upd.php"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#
 | 
			
		||||
# deSEC.io Domain API
 | 
			
		||||
#
 | 
			
		||||
# Author: Zheng Qian
 | 
			
		||||
#
 | 
			
		||||
# deSEC API doc
 | 
			
		||||
# https://desec.readthedocs.io/en/latest/
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_desec_info='deSEC.io
 | 
			
		||||
Site: desec.readthedocs.io/en/latest/
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_desec
 | 
			
		||||
Options:
 | 
			
		||||
 DDNSS_Token API Token
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2180
 | 
			
		||||
Author: Zheng Qian
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
REST_API="https://desec.io/api/v1/domains"
 | 
			
		||||
 | 
			
		||||
@@ -174,7 +176,7 @@ _get_root() {
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -186,7 +188,7 @@ _get_root() {
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,15 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
# https://dyndnsfree.de hook script for acme.sh
 | 
			
		||||
#
 | 
			
		||||
# Environment variables:
 | 
			
		||||
#
 | 
			
		||||
#  - $DF_user      (your dyndnsfree.de username)
 | 
			
		||||
#  - $DF_password  (your dyndnsfree.de password)
 | 
			
		||||
#
 | 
			
		||||
# Author: Thilo Gass <thilo.gass@gmail.com>
 | 
			
		||||
# Git repo: https://github.com/ThiloGa/acme.sh
 | 
			
		||||
 | 
			
		||||
#-- dns_df_add() - Add TXT record --------------------------------------
 | 
			
		||||
# Usage: dns_df_add _acme-challenge.subdomain.domain.com "XyZ123..."
 | 
			
		||||
# 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"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,12 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
## Will be called by acme.sh to add the txt record to your api system.
 | 
			
		||||
## returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
## Author: thewer <github at thewer.com>
 | 
			
		||||
## GitHub: https://github.com/gitwer/acme.sh
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Environment Variables Required:
 | 
			
		||||
##
 | 
			
		||||
## DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc"
 | 
			
		||||
##
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_dgon_info='DigitalOcean.com
 | 
			
		||||
Site: DigitalOcean.com/help/api/
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dgon
 | 
			
		||||
Options:
 | 
			
		||||
 DO_API_KEY API Key
 | 
			
		||||
Author: <github@thewer.com>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
#####################  Public functions  #####################
 | 
			
		||||
 | 
			
		||||
@@ -207,7 +203,7 @@ _get_base_domain() {
 | 
			
		||||
    _debug2 domain_list "$domain_list"
 | 
			
		||||
 | 
			
		||||
    i=1
 | 
			
		||||
    while [ $i -gt 0 ]; do
 | 
			
		||||
    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)
 | 
			
		||||
@@ -219,14 +215,14 @@ _get_base_domain() {
 | 
			
		||||
      ## check if it exists
 | 
			
		||||
      if [ -n "$found" ]; then
 | 
			
		||||
        ## 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")
 | 
			
		||||
        _debug _domain "$_domain"
 | 
			
		||||
        _debug _sub_domain "$_sub_domain"
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      ## increment cut point $i
 | 
			
		||||
      i=$(_math $i + 1)
 | 
			
		||||
      i=$(_math "$i" + 1)
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +1,14 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# dnsHome.de API for acme.sh
 | 
			
		||||
#
 | 
			
		||||
# This Script adds the necessary TXT record to a Subdomain
 | 
			
		||||
#
 | 
			
		||||
# Author dnsHome.de (https://github.com/dnsHome-de)
 | 
			
		||||
#
 | 
			
		||||
# Report Bugs to https://github.com/acmesh-official/acme.sh/issues/3819
 | 
			
		||||
#
 | 
			
		||||
# export DNSHOME_Subdomain=""
 | 
			
		||||
# export DNSHOME_SubdomainPassword=""
 | 
			
		||||
# 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
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
# Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# DNSimple domain api
 | 
			
		||||
# https://github.com/pho3nixf1re/acme.sh/issues
 | 
			
		||||
#
 | 
			
		||||
# This is your oauth token which can be acquired on the account page. Please
 | 
			
		||||
# note that this must be an _account_ token and not a _user_ token.
 | 
			
		||||
# https://dnsimple.com/a/<your account id>/account/access_tokens
 | 
			
		||||
# DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_dnsimple_info='DNSimple.com
 | 
			
		||||
Site: DNSimple.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dnsimple
 | 
			
		||||
Options:
 | 
			
		||||
 DNSimple_OAUTH_TOKEN OAuth Token
 | 
			
		||||
Issues: github.com/pho3nixf1re/acme.sh/issues
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
DNSimple_API="https://api.dnsimple.com/v2"
 | 
			
		||||
 | 
			
		||||
@@ -92,7 +92,7 @@ _get_root() {
 | 
			
		||||
  i=2
 | 
			
		||||
  previous=1
 | 
			
		||||
  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
 | 
			
		||||
      # not valid
 | 
			
		||||
      return 1
 | 
			
		||||
@@ -105,7 +105,7 @@ _get_root() {
 | 
			
		||||
    if _contains "$response" 'not found'; then
 | 
			
		||||
      _debug "$h not found"
 | 
			
		||||
    else
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$previous)
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$previous")
 | 
			
		||||
      _domain="$h"
 | 
			
		||||
 | 
			
		||||
      _debug _domain "$_domain"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,15 @@
 | 
			
		||||
#!/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>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
#This file name is "dns_dnsservices.sh"
 | 
			
		||||
#Script for Danish DNS registra and DNS hosting provider https://dns.services
 | 
			
		||||
 | 
			
		||||
#Author: Bjarke Bruun <bbruun@gmail.com>
 | 
			
		||||
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/4152
 | 
			
		||||
 | 
			
		||||
# Global variable to connect to the DNS.Services API
 | 
			
		||||
DNSServices_API=https://dns.services/api
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
# 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
 | 
			
		||||
#
 | 
			
		||||
# 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"
 | 
			
		||||
DO_API="https://my.do.de/api/letsencrypt"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,13 @@
 | 
			
		||||
#!/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"
 | 
			
		||||
 | 
			
		||||
@@ -84,7 +93,7 @@ _get_domainid() {
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug "h" "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -93,7 +102,7 @@ _get_domainid() {
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"$h\"" >/dev/null; then
 | 
			
		||||
      # 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
 | 
			
		||||
      _domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2)
 | 
			
		||||
      return 0
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Dnspod.cn Domain api
 | 
			
		||||
#
 | 
			
		||||
#DP_Id="1234"
 | 
			
		||||
#
 | 
			
		||||
#DP_Key="sADDsdasdgdsf"
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_dp_info='DNSPod.cn
 | 
			
		||||
Site: DNSPod.cn
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dp
 | 
			
		||||
Options:
 | 
			
		||||
 DP_Id Id
 | 
			
		||||
 DP_Key Key
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
REST_API="https://dnsapi.cn"
 | 
			
		||||
 | 
			
		||||
@@ -107,7 +109,7 @@ _get_root() {
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  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
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
@@ -121,7 +123,7 @@ _get_root() {
 | 
			
		||||
      _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      _debug _domain_id "$_domain_id"
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _debug _sub_domain "$_sub_domain"
 | 
			
		||||
        _domain="$h"
 | 
			
		||||
        _debug _domain "$_domain"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Dnspod.com Domain api
 | 
			
		||||
#
 | 
			
		||||
#DPI_Id="1234"
 | 
			
		||||
#
 | 
			
		||||
#DPI_Key="sADDsdasdgdsf"
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_dpi_info='DNSPod.com
 | 
			
		||||
Site: DNSPod.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_dpi
 | 
			
		||||
Options:
 | 
			
		||||
 DPI_Id Id
 | 
			
		||||
 DPI_Key Key
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
REST_API="https://api.dnspod.com"
 | 
			
		||||
 | 
			
		||||
@@ -107,7 +109,7 @@ _get_root() {
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  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
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
@@ -121,7 +123,7 @@ _get_root() {
 | 
			
		||||
      _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      _debug _domain_id "$_domain_id"
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _debug _sub_domain "$_sub_domain"
 | 
			
		||||
        _domain="$h"
 | 
			
		||||
        _debug _domain "$_domain"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,14 @@
 | 
			
		||||
#!/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/"
 | 
			
		||||
querystring=""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,12 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Created by RaidenII, to use DuckDNS's API to add/remove text records
 | 
			
		||||
#06/27/2017
 | 
			
		||||
 | 
			
		||||
# Pass credentials before "acme.sh --issue --dns dns_duckdns ..."
 | 
			
		||||
# --
 | 
			
		||||
# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
 | 
			
		||||
# --
 | 
			
		||||
#
 | 
			
		||||
# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_duckdns_info='DuckDNS.org
 | 
			
		||||
Site: www.DuckDNS.org
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_duckdns
 | 
			
		||||
Options:
 | 
			
		||||
 DuckDNS_Token API Token
 | 
			
		||||
Author: @RaidenII
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
DuckDNS_API="https://www.duckdns.org/update"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,13 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#DD_API_User="xxxxx"
 | 
			
		||||
#DD_API_Key="xxxxxx"
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_durabledns_info='DurableDNS.com
 | 
			
		||||
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"
 | 
			
		||||
 | 
			
		||||
@@ -104,7 +110,7 @@ _get_root() {
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -112,7 +118,7 @@ _get_root() {
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,16 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#
 | 
			
		||||
# Dyn.com Domain API
 | 
			
		||||
#
 | 
			
		||||
# Author: Gerd Naschenweng
 | 
			
		||||
# https://github.com/magicdude4eva
 | 
			
		||||
#
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_dyn_info='Dyn.com
 | 
			
		||||
Domains: dynect.net
 | 
			
		||||
Site: Dyn.com
 | 
			
		||||
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 <@magicdude4eva>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
# Dyn Managed DNS API
 | 
			
		||||
# https://help.dyn.com/dns-api-knowledge-base/
 | 
			
		||||
#
 | 
			
		||||
@@ -20,13 +26,6 @@
 | 
			
		||||
# ZoneRemoveNode
 | 
			
		||||
# 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"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,21 @@
 | 
			
		||||
#!/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
 | 
			
		||||
Dynu_Token=""
 | 
			
		||||
#
 | 
			
		||||
#Endpoint
 | 
			
		||||
Dynu_EndPoint="https://api.dynu.com/v2"
 | 
			
		||||
#
 | 
			
		||||
#Author: Dynu Systems, Inc.
 | 
			
		||||
#Report Bugs here: https://github.com/shar0119/acme.sh
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
@@ -125,7 +126,7 @@ _get_root() {
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -139,7 +140,7 @@ _get_root() {
 | 
			
		||||
    if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
 | 
			
		||||
      dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
 | 
			
		||||
      _domain_name=$h
 | 
			
		||||
      _node=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
      _node=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,23 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#Author StefanAbl
 | 
			
		||||
#Usage specify a private keyfile to use with dynv6 'export KEY="path/to/keyfile"'
 | 
			
		||||
#or use the HTTP REST API by by specifying a token 'export DYNV6_TOKEN="value"
 | 
			
		||||
#if no keyfile is specified, you will be asked if you want to create one in /home/$USER/.ssh/dynv6 and /home/$USER/.ssh/dynv6.pub
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_dynv6_info='DynV6.com
 | 
			
		||||
Site: DynV6.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_dynv6
 | 
			
		||||
Options:
 | 
			
		||||
 DYNV6_TOKEN REST API token. Get from https://DynV6.com/keys
 | 
			
		||||
OptionsAlt:
 | 
			
		||||
 KEY Path to SSH private key file. E.g. "/root/.ssh/dynv6"
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2702
 | 
			
		||||
Author: @StefanAbl
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
dynv6_api="https://dynv6.com/api/v2"
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
# Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
 | 
			
		||||
#Usage: dns_dynv6_add  _acme-challenge.www.domain.com  "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_dynv6_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  fulldomain="$(echo "$1" | _lower_case)"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
  _info "Using dynv6 api"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
@@ -36,15 +43,14 @@ dns_dynv6_add() {
 | 
			
		||||
      _err "Something went wrong! it does not seem like the record was added successfully"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Remove the txt record after validation.
 | 
			
		||||
dns_dynv6_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  fulldomain="$(echo "$1" | _lower_case)"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
  _info "Using dynv6 API"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
@@ -199,7 +205,7 @@ _get_zone_id() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep id | tr -d '"')"
 | 
			
		||||
  zone_id="$(echo "$response" | tr '}' '\n' | grep "$selected" | tr ',' '\n' | grep '"id":' | tr -d '"')"
 | 
			
		||||
  _zone_id="${zone_id#id:}"
 | 
			
		||||
  _debug "zone id: $_zone_id"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,17 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_easydns_info='easyDNS.net
 | 
			
		||||
Site: easyDNS.net
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_easydns
 | 
			
		||||
Options:
 | 
			
		||||
 EASYDNS_Token API Token
 | 
			
		||||
 EASYDNS_Key API Key
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2647
 | 
			
		||||
Author: @Neilpang, wurzelpanzer <wurzelpanzer@maximolider.net>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
#######################################################
 | 
			
		||||
#
 | 
			
		||||
# easyDNS REST API for acme.sh by Neilpang based on dns_cf.sh
 | 
			
		||||
#
 | 
			
		||||
# API Documentation: https://sandbox.rest.easydns.net:3001/
 | 
			
		||||
#
 | 
			
		||||
# Author: wurzelpanzer [wurzelpanzer@maximolider.net]
 | 
			
		||||
# Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2647
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
####################  Public functions #################
 | 
			
		||||
 | 
			
		||||
#EASYDNS_Key="xxxxxxxxxxxxxxxxxxxxxxxx"
 | 
			
		||||
@@ -118,7 +121,7 @@ _get_root() {
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -130,7 +133,7 @@ _get_root() {
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"status\":200"; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										163
									
								
								dnsapi/dns_edgecenter.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								dnsapi/dns_edgecenter.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_edgecenter_info='EdgeCenter.ru
 | 
			
		||||
Site: EdgeCenter.ru
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgecenter
 | 
			
		||||
Options:
 | 
			
		||||
 EDGECENTER_API_KEY API Key
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/6313
 | 
			
		||||
Author: Konstantin Ruchev <konstantin.ruchev@edgecenter.ru>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
EDGECENTER_API="https://api.edgecenter.ru"
 | 
			
		||||
DOMAIN_TYPE=
 | 
			
		||||
DOMAIN_MASTER=
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_edgecenter_add   _acme-challenge.www.domain.com   "TXT_RECORD_VALUE"
 | 
			
		||||
dns_edgecenter_add() {
 | 
			
		||||
  fulldomain="$1"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
 | 
			
		||||
  _info "Using EdgeCenter DNS API"
 | 
			
		||||
 | 
			
		||||
  if ! _dns_edgecenter_init_check; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Detecting root zone for $fulldomain"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  subdomain="${fulldomain%."$_zone"}"
 | 
			
		||||
  subdomain=${subdomain%.}
 | 
			
		||||
 | 
			
		||||
  _debug "Zone: $_zone"
 | 
			
		||||
  _debug "Subdomain: $subdomain"
 | 
			
		||||
  _debug "TXT value: $txtvalue"
 | 
			
		||||
 | 
			
		||||
  payload='{"resource_records": [ { "content": ["'"$txtvalue"'"] } ], "ttl": 60 }'
 | 
			
		||||
  _dns_edgecenter_http_api_call "post" "dns/v2/zones/$_zone/$subdomain.$_zone/txt" "$payload"
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" '"error":"rrset is already exists"'; then
 | 
			
		||||
    _debug "RRSet exists, merging values"
 | 
			
		||||
    _dns_edgecenter_http_api_call "get" "dns/v2/zones/$_zone/$subdomain.$_zone/txt"
 | 
			
		||||
    current="$response"
 | 
			
		||||
    newlist=""
 | 
			
		||||
    for v in $(echo "$current" | sed -n 's/.*"content":\["\([^"]*\)"\].*/\1/p'); do
 | 
			
		||||
      newlist="$newlist {\"content\":[\"$v\"]},"
 | 
			
		||||
    done
 | 
			
		||||
    newlist="$newlist{\"content\":[\"$txtvalue\"]}"
 | 
			
		||||
    putdata="{\"resource_records\":[${newlist}]}
 | 
			
		||||
"
 | 
			
		||||
    _dns_edgecenter_http_api_call "put" "dns/v2/zones/$_zone/$subdomain.$_zone/txt" "$putdata"
 | 
			
		||||
    _info "Updated existing RRSet with new TXT value."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" '"exception":'; then
 | 
			
		||||
    _err "Record cannot be added."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "TXT record added successfully."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: dns_edgecenter_rm   _acme-challenge.www.domain.com   "TXT_RECORD_VALUE"
 | 
			
		||||
dns_edgecenter_rm() {
 | 
			
		||||
  fulldomain="$1"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
 | 
			
		||||
  _info "Removing TXT record for $fulldomain"
 | 
			
		||||
 | 
			
		||||
  if ! _dns_edgecenter_init_check; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  subdomain="${fulldomain%."$_zone"}"
 | 
			
		||||
  subdomain=${subdomain%.}
 | 
			
		||||
 | 
			
		||||
  _dns_edgecenter_http_api_call "delete" "dns/v2/zones/$_zone/$subdomain.$_zone/txt"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$response" ]; then
 | 
			
		||||
    _info "TXT record deleted successfully."
 | 
			
		||||
  else
 | 
			
		||||
    _info "TXT record may not have been deleted: $response"
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_dns_edgecenter_init_check() {
 | 
			
		||||
  EDGECENTER_API_KEY="${EDGECENTER_API_KEY:-$(_readaccountconf_mutable EDGECENTER_API_KEY)}"
 | 
			
		||||
  if [ -z "$EDGECENTER_API_KEY" ]; then
 | 
			
		||||
    _err "EDGECENTER_API_KEY was not exported."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable EDGECENTER_API_KEY "$EDGECENTER_API_KEY"
 | 
			
		||||
  export _H1="Authorization: APIKey $EDGECENTER_API_KEY"
 | 
			
		||||
 | 
			
		||||
  _dns_edgecenter_http_api_call "get" "dns/v2/clients/me/features"
 | 
			
		||||
  if ! _contains "$response" '"id":'; then
 | 
			
		||||
    _err "Invalid API key."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  i=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-)
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _dns_edgecenter_http_api_call "get" "dns/v2/zones/$h"
 | 
			
		||||
    if ! _contains "$response" 'zone is not found'; then
 | 
			
		||||
      _zone="$h"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    i=$((i + 1))
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_dns_edgecenter_http_api_call() {
 | 
			
		||||
  mtd="$1"
 | 
			
		||||
  endpoint="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
 | 
			
		||||
  export _H1="Authorization: APIKey $EDGECENTER_API_KEY"
 | 
			
		||||
 | 
			
		||||
  case "$mtd" in
 | 
			
		||||
  get)
 | 
			
		||||
    response="$(_get "$EDGECENTER_API/$endpoint")"
 | 
			
		||||
    ;;
 | 
			
		||||
  post)
 | 
			
		||||
    response="$(_post "$data" "$EDGECENTER_API/$endpoint")"
 | 
			
		||||
    ;;
 | 
			
		||||
  delete)
 | 
			
		||||
    response="$(_post "" "$EDGECENTER_API/$endpoint" "" "DELETE")"
 | 
			
		||||
    ;;
 | 
			
		||||
  put)
 | 
			
		||||
    response="$(_post "$data" "$EDGECENTER_API/$endpoint" "" "PUT")"
 | 
			
		||||
    ;;
 | 
			
		||||
  *)
 | 
			
		||||
    _err "Unknown HTTP method $mtd"
 | 
			
		||||
    return 1
 | 
			
		||||
    ;;
 | 
			
		||||
  esac
 | 
			
		||||
 | 
			
		||||
  _debug "HTTP $mtd response: $response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,15 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_edgedns_info='Akamai.com Edge DNS
 | 
			
		||||
Site: techdocs.Akamai.com/edge-dns/reference/edge-dns-api
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgedns
 | 
			
		||||
Options: Specify individual credentials
 | 
			
		||||
 AKAMAI_HOST Host
 | 
			
		||||
 AKAMAI_ACCESS_TOKEN Access token
 | 
			
		||||
 AKAMAI_CLIENT_TOKEN Client token
 | 
			
		||||
 AKAMAI_CLIENT_SECRET Client secret
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/3157
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
# Akamai Edge DNS v2  API
 | 
			
		||||
# User must provide Open Edgegrid API credentials to the EdgeDNS installation. The remote user in EdgeDNS must have CRUD access to
 | 
			
		||||
@@ -6,18 +17,10 @@
 | 
			
		||||
 | 
			
		||||
# Report bugs to https://control.akamai.com/apps/support-ui/#/contact-support
 | 
			
		||||
 | 
			
		||||
# Values to export:
 | 
			
		||||
# --EITHER--
 | 
			
		||||
# *** TBD. NOT IMPLEMENTED YET ***
 | 
			
		||||
# specify Edgegrid credentials file and section
 | 
			
		||||
# AKAMAI_EDGERC=<full file path>
 | 
			
		||||
# AKAMAI_EDGERC_SECTION="default"
 | 
			
		||||
## --OR--
 | 
			
		||||
# specify indiviual credentials
 | 
			
		||||
# export AKAMAI_HOST = <host>
 | 
			
		||||
# export AKAMAI_ACCESS_TOKEN = <access token>
 | 
			
		||||
# export AKAMAI_CLIENT_TOKEN = <client token>
 | 
			
		||||
# export AKAMAI_CLIENT_SECRET = <client secret>
 | 
			
		||||
# Specify Edgegrid credentials file and section.
 | 
			
		||||
# AKAMAI_EDGERC Edge RC. Full file path
 | 
			
		||||
# AKAMAI_EDGERC_SECTION Edge RC Section. E.g. "default"
 | 
			
		||||
 | 
			
		||||
ACME_EDGEDNS_VERSION="0.1.0"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,14 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#This is the euserv.eu api wrapper for acme.sh
 | 
			
		||||
#
 | 
			
		||||
#Author: Michael Brueckner
 | 
			
		||||
#Report Bugs: https://www.github.com/initit/acme.sh  or  mbr@initit.de
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#EUSERV_Username="username"
 | 
			
		||||
#
 | 
			
		||||
#EUSERV_Password="password"
 | 
			
		||||
#
 | 
			
		||||
# Dependencies:
 | 
			
		||||
# -------------
 | 
			
		||||
# - none -
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_euserv_info='EUserv.com
 | 
			
		||||
Domains: EUserv.eu
 | 
			
		||||
Site: EUserv.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_euserv
 | 
			
		||||
Options:
 | 
			
		||||
 EUSERV_Username Username
 | 
			
		||||
 EUSERV_Password Password
 | 
			
		||||
Author: Michael Brueckner
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
EUSERV_Api="https://api.euserv.net"
 | 
			
		||||
 | 
			
		||||
@@ -155,7 +151,7 @@ _get_root() {
 | 
			
		||||
  response="$_euserv_domain_orders"
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(echo "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(echo "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -163,7 +159,7 @@ _get_root() {
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "$h"; then
 | 
			
		||||
      _sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
      _sub_domain=$(echo "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      _domain="$h"
 | 
			
		||||
      if ! _euserv_get_domain_id "$_domain"; then
 | 
			
		||||
        _err "invalid domain"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,12 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_exoscale_info='Exoscale.com
 | 
			
		||||
Site: Exoscale.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_exoscale
 | 
			
		||||
Options:
 | 
			
		||||
 EXOSCALE_API_KEY API Key
 | 
			
		||||
 EXOSCALE_SECRET_KEY API Secret key
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
EXOSCALE_API=https://api.exoscale.com/dns/v1
 | 
			
		||||
 | 
			
		||||
@@ -111,7 +119,7 @@ _get_root() {
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
@@ -122,7 +130,7 @@ _get_root() {
 | 
			
		||||
      _domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      _domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      if [ "$_domain_token" ] && [ "$_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
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,15 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_fornex_info='Fornex.com
 | 
			
		||||
Site: Fornex.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_fornex
 | 
			
		||||
Options:
 | 
			
		||||
 FORNEX_API_KEY API Key
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/3998
 | 
			
		||||
Author: Timur Umarov <inbox@tumarov.com>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
#Author: Timur Umarov <inbox@tumarov.com>
 | 
			
		||||
 | 
			
		||||
FORNEX_API_URL="https://fornex.com/api/dns/v0.1"
 | 
			
		||||
FORNEX_API_URL="https://fornex.com/api"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
@@ -23,12 +30,10 @@ dns_fornex_add() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _rest POST "$_domain/entry_set/add/" "host=$fulldomain&type=TXT&value=$txtvalue&apikey=$FORNEX_API_KEY"; then
 | 
			
		||||
  if _rest POST "dns/domain/$_domain/entry_set/" "{\"host\" : \"${fulldomain}\" , \"type\" : \"TXT\" , \"value\" : \"${txtvalue}\" , \"ttl\" : null}"; then
 | 
			
		||||
    _debug _response "$response"
 | 
			
		||||
    if _contains "$response" '"ok": true' || _contains "$response" 'Такая запись уже существует.'; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    _info "Added, OK"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
@@ -51,21 +56,21 @@ dns_fornex_rm() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _rest GET "$_domain/entry_set.json?apikey=$FORNEX_API_KEY"
 | 
			
		||||
  _rest GET "dns/domain/$_domain/entry_set?type=TXT&q=$fulldomain"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "$txtvalue"; then
 | 
			
		||||
    _err "Txt record not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _record_id="$(echo "$response" | _egrep_o "{[^{]*\"value\"*:*\"$txtvalue\"[^}]*}" | sed -n -e 's#.*"id": \([0-9]*\).*#\1#p')"
 | 
			
		||||
  _record_id="$(echo "$response" | _egrep_o "\{[^\{]*\"value\"*:*\"$txtvalue\"[^\}]*\}" | sed -n -e 's#.*"id":\([0-9]*\).*#\1#p')"
 | 
			
		||||
  _debug "_record_id" "$_record_id"
 | 
			
		||||
  if [ -z "$_record_id" ]; then
 | 
			
		||||
    _err "can not find _record_id"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _rest POST "$_domain/entry_set/$_record_id/delete/" "apikey=$FORNEX_API_KEY"; then
 | 
			
		||||
  if ! _rest DELETE "dns/domain/$_domain/entry_set/$_record_id/"; then
 | 
			
		||||
    _err "Delete record error."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
@@ -83,18 +88,18 @@ _get_root() {
 | 
			
		||||
 | 
			
		||||
  i=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _rest GET "domain_list.json?q=$h&apikey=$FORNEX_API_KEY"; then
 | 
			
		||||
    if ! _rest GET "dns/domain/?q=$h"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"$h\"" >/dev/null; then
 | 
			
		||||
    if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
@@ -127,7 +132,9 @@ _rest() {
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  export _H1="Accept: application/json"
 | 
			
		||||
  export _H1="Authorization: Api-Key $FORNEX_API_KEY"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
  export _H3="Accept: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user