mirror of
				https://github.com/acmesh-official/acme.sh
				synced 2025-11-04 13:55:56 +08:00 
			
		
		
		
	Compare commits
	
		
			5578 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					82ff65c3d4 | ||
| 
						 | 
					0c7943868d | ||
| 
						 | 
					3cdce86339 | ||
| 
						 | 
					08246f7005 | ||
| 
						 | 
					c377ec69d3 | ||
| 
						 | 
					a2c2b7ffee | ||
| 
						 | 
					25c564bae1 | ||
| 
						 | 
					15197455f2 | ||
| 
						 | 
					f7cc72be35 | ||
| 
						 | 
					17da49bb78 | ||
| 
						 | 
					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 | ||
| 
						 | 
					16dc21afff | ||
| 
						 | 
					3a1c6d84f0 | ||
| 
						 | 
					e684abdacd | ||
| 
						 | 
					e275cb1efd | ||
| 
						 | 
					60315e5b91 | ||
| 
						 | 
					43b1a4bf5a | ||
| 
						 | 
					cdb238e41c | ||
| 
						 | 
					3871e44d9c | ||
| 
						 | 
					5a51454d13 | ||
| 
						 | 
					778ee02803 | ||
| 
						 | 
					d102943a32 | ||
| 
						 | 
					a0b27ddbd8 | ||
| 
						 | 
					b950b04e89 | ||
| 
						 | 
					c9a55f395b | ||
| 
						 | 
					424da01878 | ||
| 
						 | 
					3c933158c8 | ||
| 
						 | 
					7221d488e5 | ||
| 
						 | 
					1c16931e26 | ||
| 
						 | 
					0a4b70dbd2 | ||
| 
						 | 
					666c716bda | ||
| 
						 | 
					be477d7ae3 | ||
| 
						 | 
					6c8a623b88 | ||
| 
						 | 
					ff8de34415 | ||
| 
						 | 
					f8ca6d9833 | ||
| 
						 | 
					dbab519004 | ||
| 
						 | 
					e888c96591 | ||
| 
						 | 
					59519f0493 | ||
| 
						 | 
					e02f07d356 | ||
| 
						 | 
					2c90d220b8 | ||
| 
						 | 
					9feeba8d4b | ||
| 
						 | 
					45090fc897 | ||
| 
						 | 
					d761bdc1b1 | ||
| 
						 | 
					287a8c76b5 | ||
| 
						 | 
					a7dd86de71 | ||
| 
						 | 
					c541a2e5de | ||
| 
						 | 
					41dbf1ddac | ||
| 
						 | 
					70ed6b96d1 | ||
| 
						 | 
					ef26075a1c | ||
| 
						 | 
					91c87446be | ||
| 
						 | 
					dd207e1f02 | ||
| 
						 | 
					e947870da9 | ||
| 
						 | 
					f0b5f592dc | ||
| 
						 | 
					773a2a6cfe | ||
| 
						 | 
					c2a7e384ba | ||
| 
						 | 
					4a8b35ef5f | ||
| 
						 | 
					5cd0db32df | ||
| 
						 | 
					ef01de6149 | ||
| 
						 | 
					4e9749f655 | ||
| 
						 | 
					abb7a1fd47 | ||
| 
						 | 
					60bcee8c1d | ||
| 
						 | 
					8ded524236 | ||
| 
						 | 
					0a47f48191 | ||
| 
						 | 
					8601267b90 | ||
| 
						 | 
					6767e0c52d | ||
| 
						 | 
					5141d1775d | ||
| 
						 | 
					374af996d9 | ||
| 
						 | 
					dcc9624c15 | ||
| 
						 | 
					b3df1a2bf8 | ||
| 
						 | 
					3fc3c02a4f | ||
| 
						 | 
					874ddf9a32 | ||
| 
						 | 
					69aeb70cc3 | ||
| 
						 | 
					47e60cefe3 | ||
| 
						 | 
					4ea7f3cda5 | ||
| 
						 | 
					03288b521a | ||
| 
						 | 
					4b0a7a6e1f | ||
| 
						 | 
					383287adcd | ||
| 
						 | 
					5c00afc6fe | ||
| 
						 | 
					025e0e8093 | ||
| 
						 | 
					35fb1f8585 | ||
| 
						 | 
					9fb14eef0e | ||
| 
						 | 
					5d6d0c6176 | ||
| 
						 | 
					dd707242ef | ||
| 
						 | 
					4f32f1285a | ||
| 
						 | 
					a6ecdbae29 | ||
| 
						 | 
					4904d100ff | ||
| 
						 | 
					7a1f94bc20 | ||
| 
						 | 
					f9320fff8f | ||
| 
						 | 
					3dde83d8a0 | ||
| 
						 | 
					f3e77a989c | ||
| 
						 | 
					f3539b0354 | ||
| 
						 | 
					4e5d4b9695 | ||
| 
						 | 
					864315f6d1 | ||
| 
						 | 
					713b7338ea | ||
| 
						 | 
					53117b2f4c | ||
| 
						 | 
					90623142e1 | ||
| 
						 | 
					43503a20e5 | ||
| 
						 | 
					ec53b27dfe | ||
| 
						 | 
					238ecfc539 | ||
| 
						 | 
					b888792940 | ||
| 
						 | 
					e3d1ab52f8 | ||
| 
						 | 
					f9c2874c35 | ||
| 
						 | 
					2304f005e3 | ||
| 
						 | 
					b95f836256 | ||
| 
						 | 
					c94f9f21af | ||
| 
						 | 
					fc336e3733 | ||
| 
						 | 
					fc1df9f9a5 | ||
| 
						 | 
					5a604bfdee | ||
| 
						 | 
					281951a86b | ||
| 
						 | 
					35ec3adadc | ||
| 
						 | 
					b9256a1ba7 | ||
| 
						 | 
					734c9a1aa5 | ||
| 
						 | 
					6502a71083 | ||
| 
						 | 
					f2634b44cd | ||
| 
						 | 
					dcf9c467c3 | ||
| 
						 | 
					3dcacc1f8d | ||
| 
						 | 
					7cb81b0f35 | ||
| 
						 | 
					8155ba5224 | ||
| 
						 | 
					7169060425 | ||
| 
						 | 
					ddb9dd4e45 | ||
| 
						 | 
					2a05f24cb6 | ||
| 
						 | 
					039e4c662d | ||
| 
						 | 
					70351677a1 | ||
| 
						 | 
					33cadfb97d | ||
| 
						 | 
					5fbaeda217 | ||
| 
						 | 
					b44ba0d21a | ||
| 
						 | 
					b42532afe9 | ||
| 
						 | 
					da6a335b87 | ||
| 
						 | 
					0e8fef73bb | ||
| 
						 | 
					7122a960fa | ||
| 
						 | 
					d5b649a1a4 | ||
| 
						 | 
					233c724b2d | ||
| 
						 | 
					e1eb001872 | ||
| 
						 | 
					5899d7034f | ||
| 
						 | 
					23c3e9482f | ||
| 
						 | 
					dd980d9dca | ||
| 
						 | 
					d4ed50a915 | ||
| 
						 | 
					d986c7d126 | ||
| 
						 | 
					67a2a4f249 | ||
| 
						 | 
					a6e87e7e08 | ||
| 
						 | 
					33da8a7f62 | ||
| 
						 | 
					68c533f777 | ||
| 
						 | 
					671eecf203 | ||
| 
						 | 
					2454ac8ef1 | ||
| 
						 | 
					20f097faa4 | ||
| 
						 | 
					5dba8b493d | ||
| 
						 | 
					eb27013fba | ||
| 
						 | 
					4f8d1c5c9d | ||
| 
						 | 
					74168c3e05 | ||
| 
						 | 
					cece848801 | ||
| 
						 | 
					7ddbeaa078 | ||
| 
						 | 
					2d4aa7ff8b | ||
| 
						 | 
					79e044ac31 | ||
| 
						 | 
					15ae5a5135 | ||
| 
						 | 
					5684b7c329 | ||
| 
						 | 
					7e96120353 | ||
| 
						 | 
					cc36421fe5 | ||
| 
						 | 
					683aa727d5 | ||
| 
						 | 
					ea07b495ac | ||
| 
						 | 
					8d211c3524 | ||
| 
						 | 
					ab8df82563 | ||
| 
						 | 
					8ba9c4ab97 | ||
| 
						 | 
					2f70b8682e | ||
| 
						 | 
					86dd4ea480 | ||
| 
						 | 
					204e5f4418 | ||
| 
						 | 
					044a9bb6d3 | ||
| 
						 | 
					a31143328e | ||
| 
						 | 
					2bb29a105c | ||
| 
						 | 
					0013d98d04 | ||
| 
						 | 
					916743f44b | ||
| 
						 | 
					edebe65d95 | ||
| 
						 | 
					c43fcd0af6 | ||
| 
						 | 
					9a5c2b88dc | ||
| 
						 | 
					aaee0414c8 | ||
| 
						 | 
					d0c2fb9761 | ||
| 
						 | 
					4f076c6924 | ||
| 
						 | 
					51d4d1451a | ||
| 
						 | 
					7b9d76dc65 | ||
| 
						 | 
					f91aeea91c | ||
| 
						 | 
					4cabf84be9 | ||
| 
						 | 
					8a1f038a80 | ||
| 
						 | 
					c9cab9ab74 | ||
| 
						 | 
					bd78120bd5 | ||
| 
						 | 
					0de3bf0ac7 | ||
| 
						 | 
					2d144a8b43 | ||
| 
						 | 
					1a140a5515 | ||
| 
						 | 
					1ea8cfbfb0 | ||
| 
						 | 
					64fda95186 | ||
| 
						 | 
					7843c0c1b0 | ||
| 
						 | 
					a3784854a7 | ||
| 
						 | 
					8b7c000f47 | ||
| 
						 | 
					5b8d7a3f29 | ||
| 
						 | 
					328dbd57d4 | ||
| 
						 | 
					a8c448f4cb | ||
| 
						 | 
					de0419228f | ||
| 
						 | 
					d8a4e47a13 | ||
| 
						 | 
					bc920949cb | ||
| 
						 | 
					0717f8591c | ||
| 
						 | 
					affb65219e | ||
| 
						 | 
					3e628f2678 | ||
| 
						 | 
					ddabc38e3f | ||
| 
						 | 
					c0097497be | ||
| 
						 | 
					78198bfdaf | ||
| 
						 | 
					2fb9c923f4 | ||
| 
						 | 
					c485011ed1 | ||
| 
						 | 
					0b8ae68213 | ||
| 
						 | 
					0e73128f40 | ||
| 
						 | 
					927c003d22 | ||
| 
						 | 
					bd73823828 | ||
| 
						 | 
					11582bc7d3 | ||
| 
						 | 
					20ed4f96ba | ||
| 
						 | 
					19790e9011 | ||
| 
						 | 
					3a29e03458 | ||
| 
						 | 
					e2eb685d76 | ||
| 
						 | 
					86cb28fe34 | ||
| 
						 | 
					bcc9679339 | ||
| 
						 | 
					e5aeff50dc | ||
| 
						 | 
					5f44c195e9 | ||
| 
						 | 
					e4387e4aad | ||
| 
						 | 
					b1b336804d | ||
| 
						 | 
					9985c43817 | ||
| 
						 | 
					190cac394c | ||
| 
						 | 
					ae71a5abf6 | ||
| 
						 | 
					df199c5788 | ||
| 
						 | 
					c1ba4f1b55 | ||
| 
						 | 
					afc0097b12 | ||
| 
						 | 
					4e9f971c91 | ||
| 
						 | 
					b1cc28bbda | ||
| 
						 | 
					80d30bdd30 | ||
| 
						 | 
					f27566669b | ||
| 
						 | 
					29f12ddaf4 | ||
| 
						 | 
					ed15ff0515 | ||
| 
						 | 
					a8f71f79fe | ||
| 
						 | 
					68c2478e0e | ||
| 
						 | 
					be07fe9e9f | ||
| 
						 | 
					4d8b661d51 | ||
| 
						 | 
					ca0981645f | ||
| 
						 | 
					bc7e02b47a | ||
| 
						 | 
					55a55e9f74 | ||
| 
						 | 
					b41d40da40 | ||
| 
						 | 
					ba3e088b23 | ||
| 
						 | 
					1b59b0b739 | ||
| 
						 | 
					093cfcdf42 | ||
| 
						 | 
					41801a60ad | ||
| 
						 | 
					5ff0957861 | ||
| 
						 | 
					6913b8beb5 | ||
| 
						 | 
					c8d17bc363 | ||
| 
						 | 
					2cbf1259a8 | ||
| 
						 | 
					59dc513ac3 | ||
| 
						 | 
					87b110bb86 | ||
| 
						 | 
					1b3e1a7abe | ||
| 
						 | 
					0afabc60ae | ||
| 
						 | 
					2d4ea720eb | ||
| 
						 | 
					9c757bbe6e | ||
| 
						 | 
					40e0d72105 | ||
| 
						 | 
					eba788e8c9 | ||
| 
						 | 
					444b111a62 | ||
| 
						 | 
					a364ab4ea7 | ||
| 
						 | 
					be7840c827 | ||
| 
						 | 
					a46e51e8db | ||
| 
						 | 
					789ebb8990 | ||
| 
						 | 
					e3cd96bf19 | ||
| 
						 | 
					7746042adc | ||
| 
						 | 
					dfe80556d3 | ||
| 
						 | 
					b3529dc748 | ||
| 
						 | 
					543c4423a2 | ||
| 
						 | 
					3bd4d32b8d | ||
| 
						 | 
					56a686d3e0 | ||
| 
						 | 
					2f97c789dd | ||
| 
						 | 
					04ca808e80 | ||
| 
						 | 
					9b79743c5d | ||
| 
						 | 
					a386826808 | ||
| 
						 | 
					668894fc4d | ||
| 
						 | 
					dc882e6279 | ||
| 
						 | 
					d6eebf82be | ||
| 
						 | 
					799f509ba9 | ||
| 
						 | 
					688a234127 | ||
| 
						 | 
					095697900b | ||
| 
						 | 
					6ccf617d62 | ||
| 
						 | 
					b3b4811b2c | ||
| 
						 | 
					966e4246e5 | ||
| 
						 | 
					9377c4f3ad | ||
| 
						 | 
					b876128635 | ||
| 
						 | 
					c0da801580 | ||
| 
						 | 
					149310e1ec | ||
| 
						 | 
					4e625c18dc | ||
| 
						 | 
					a5d5113be3 | ||
| 
						 | 
					7900c493af | ||
| 
						 | 
					76fe5d8831 | ||
| 
						 | 
					37031721dd | ||
| 
						 | 
					3cc283cbee | ||
| 
						 | 
					35cf98fff2 | ||
| 
						 | 
					ca41ea2d5c | ||
| 
						 | 
					daffc4e6a4 | ||
| 
						 | 
					5f3cb9019b | ||
| 
						 | 
					13c7182948 | ||
| 
						 | 
					4951b58b21 | ||
| 
						 | 
					7be7586971 | ||
| 
						 | 
					edc76795d4 | ||
| 
						 | 
					6d64098288 | ||
| 
						 | 
					4351110082 | ||
| 
						 | 
					6652138d3e | ||
| 
						 | 
					c8d0d475e4 | ||
| 
						 | 
					39b25029fc | ||
| 
						 | 
					b5cabd6d8e | ||
| 
						 | 
					b169a5c707 | ||
| 
						 | 
					4f816c06b0 | ||
| 
						 | 
					b5f49d9563 | ||
| 
						 | 
					274fd5ab8b | ||
| 
						 | 
					e48d7de763 | ||
| 
						 | 
					f426940bd2 | ||
| 
						 | 
					6c11dd12d7 | ||
| 
						 | 
					c2b14d3075 | ||
| 
						 | 
					8a144ebfee | ||
| 
						 | 
					5440fcdf54 | ||
| 
						 | 
					66b2d496af | ||
| 
						 | 
					444a0282d7 | ||
| 
						 | 
					f897ab4eb8 | ||
| 
						 | 
					993c187e37 | ||
| 
						 | 
					3ce67b282f | ||
| 
						 | 
					606e59a5d0 | ||
| 
						 | 
					0f26b1eafb | ||
| 
						 | 
					d2a9d731ed | ||
| 
						 | 
					80e6b1fc01 | ||
| 
						 | 
					58a89edad7 | ||
| 
						 | 
					bfe47eb152 | ||
| 
						 | 
					32adc38e94 | ||
| 
						 | 
					1584971908 | ||
| 
						 | 
					025bebb3e2 | ||
| 
						 | 
					4047adcc35 | ||
| 
						 | 
					d4cf03c9fd | ||
| 
						 | 
					5ba2068fc2 | ||
| 
						 | 
					3ce7d410c8 | ||
| 
						 | 
					74f28021e7 | ||
| 
						 | 
					f90cbb636a | ||
| 
						 | 
					c8929ca0cb | ||
| 
						 | 
					9fb5bb620d | ||
| 
						 | 
					ed58f32052 | ||
| 
						 | 
					6d5743c506 | ||
| 
						 | 
					a551619de6 | ||
| 
						 | 
					873b113cb3 | ||
| 
						 | 
					2280e66d73 | ||
| 
						 | 
					2133897bbe | ||
| 
						 | 
					bee5cb55a1 | ||
| 
						 | 
					0a0838b616 | ||
| 
						 | 
					b376dfa1e6 | ||
| 
						 | 
					7ac0577b34 | ||
| 
						 | 
					f16e060e87 | ||
| 
						 | 
					9aaae24583 | ||
| 
						 | 
					915ced7b92 | ||
| 
						 | 
					5a36b9075f | ||
| 
						 | 
					b5a7f46ecc | ||
| 
						 | 
					4381657c5e | ||
| 
						 | 
					d440b2f2b2 | ||
| 
						 | 
					5b42aea9e7 | ||
| 
						 | 
					e1d7a6b9ac | ||
| 
						 | 
					38778f8adc | ||
| 
						 | 
					8b7a86bd17 | ||
| 
						 | 
					619bae745b | ||
| 
						 | 
					2cf72bad30 | ||
| 
						 | 
					e6959f093c | ||
| 
						 | 
					8d783e8e1f | ||
| 
						 | 
					f03098551e | ||
| 
						 | 
					6887805402 | ||
| 
						 | 
					7f9074adbf | ||
| 
						 | 
					64847afc3f | ||
| 
						 | 
					c4df8090e2 | ||
| 
						 | 
					fe3523f47a | ||
| 
						 | 
					199d846acb | ||
| 
						 | 
					b07e479840 | ||
| 
						 | 
					9bf37fde02 | ||
| 
						 | 
					1054325b2d | ||
| 
						 | 
					ef8cb11707 | ||
| 
						 | 
					c23c40df8a | ||
| 
						 | 
					77d606df34 | ||
| 
						 | 
					e717c9dba2 | ||
| 
						 | 
					3d312e2140 | ||
| 
						 | 
					18de21f723 | ||
| 
						 | 
					6aebaf6f47 | ||
| 
						 | 
					641f6977a9 | ||
| 
						 | 
					a092a2fa43 | ||
| 
						 | 
					84c2b0c3d7 | ||
| 
						 | 
					24ce7c1991 | ||
| 
						 | 
					8be3465f94 | ||
| 
						 | 
					227eac10f1 | ||
| 
						 | 
					1cbd5485e7 | ||
| 
						 | 
					610c3cf681 | ||
| 
						 | 
					96d45cc341 | ||
| 
						 | 
					8ba7d02fdb | ||
| 
						 | 
					ef8a199a5a | ||
| 
						 | 
					db83643c1e | ||
| 
						 | 
					9b6f775276 | ||
| 
						 | 
					69040dd668 | ||
| 
						 | 
					14b5914233 | ||
| 
						 | 
					a0eabd2298 | ||
| 
						 | 
					9b27298d54 | ||
| 
						 | 
					3075b4515a | ||
| 
						 | 
					39bc417706 | ||
| 
						 | 
					e11e32cd52 | ||
| 
						 | 
					019a7bd66b | ||
| 
						 | 
					4d89ce5d50 | ||
| 
						 | 
					c31027b284 | ||
| 
						 | 
					f17ec7a4f5 | ||
| 
						 | 
					deec6aab1a | ||
| 
						 | 
					3e8d9a1987 | ||
| 
						 | 
					5e465a298f | ||
| 
						 | 
					515c9e7811 | ||
| 
						 | 
					03c8309703 | ||
| 
						 | 
					2c28d6b10c | ||
| 
						 | 
					df79443ed8 | ||
| 
						 | 
					2b891f7f1d | ||
| 
						 | 
					e4ed0b1884 | ||
| 
						 | 
					c8c1c09189 | ||
| 
						 | 
					08ae8cc3cb | ||
| 
						 | 
					201673ca8a | ||
| 
						 | 
					29e23ac9ce | ||
| 
						 | 
					00483e8cdd | ||
| 
						 | 
					83da01a2e1 | ||
| 
						 | 
					7cd6ff054b | ||
| 
						 | 
					6be2bb2289 | ||
| 
						 | 
					439defca42 | ||
| 
						 | 
					8a85bb2989 | ||
| 
						 | 
					5e7519183d | ||
| 
						 | 
					40e7eca1ee | ||
| 
						 | 
					481f02de88 | ||
| 
						 | 
					6a90856f0e | ||
| 
						 | 
					dcbbee8adb | ||
| 
						 | 
					225adcc836 | ||
| 
						 | 
					0f607413d0 | ||
| 
						 | 
					922553032b | ||
| 
						 | 
					b49999721c | ||
| 
						 | 
					de4c4eedd8 | ||
| 
						 | 
					bcc984fc09 | ||
| 
						 | 
					d53262fab6 | ||
| 
						 | 
					532e44bcea | ||
| 
						 | 
					3fb67629c1 | ||
| 
						 | 
					6145465823 | ||
| 
						 | 
					fb5091a388 | ||
| 
						 | 
					d6c68f1a84 | ||
| 
						 | 
					0d05f9ba80 | ||
| 
						 | 
					a300df0020 | ||
| 
						 | 
					a50158cbeb | ||
| 
						 | 
					7278fd25e5 | ||
| 
						 | 
					6fb8c0ec4c | ||
| 
						 | 
					07cedc55e2 | ||
| 
						 | 
					ae3cc81f03 | ||
| 
						 | 
					97a45e3b02 | ||
| 
						 | 
					451b290b79 | ||
| 
						 | 
					499ea07934 | ||
| 
						 | 
					afa06267a2 | ||
| 
						 | 
					d4a6d9c076 | ||
| 
						 | 
					c3f6112443 | ||
| 
						 | 
					3411b736dd | ||
| 
						 | 
					c603b9c40b | ||
| 
						 | 
					1e2c5d038f | ||
| 
						 | 
					9d6d96adf3 | ||
| 
						 | 
					8d574ecb34 | ||
| 
						 | 
					9ebb2ac2e4 | ||
| 
						 | 
					7b935eec5d | ||
| 
						 | 
					b209f66654 | ||
| 
						 | 
					b98b4951b4 | ||
| 
						 | 
					227d62a5dc | ||
| 
						 | 
					7fae5553a8 | ||
| 
						 | 
					6ead019873 | ||
| 
						 | 
					6d2ab4d270 | ||
| 
						 | 
					a6b58bc88d | ||
| 
						 | 
					ea3c37d754 | ||
| 
						 | 
					e58b00d9a2 | ||
| 
						 | 
					b75e90f8c9 | ||
| 
						 | 
					e82f3439c3 | ||
| 
						 | 
					0bc8e3bee5 | ||
| 
						 | 
					13f80acb2d | ||
| 
						 | 
					8fe813acff | ||
| 
						 | 
					0ed4fc6a12 | ||
| 
						 | 
					b37bf06de8 | ||
| 
						 | 
					27bbf0ccaf | ||
| 
						 | 
					ed56d52af3 | ||
| 
						 | 
					fb457968ec | ||
| 
						 | 
					0bea2e2b94 | ||
| 
						 | 
					72d02f442e | ||
| 
						 | 
					bcf63b5d27 | ||
| 
						 | 
					e88442cb46 | ||
| 
						 | 
					930609e875 | ||
| 
						 | 
					8752d08ce9 | ||
| 
						 | 
					c46ceb06b4 | ||
| 
						 | 
					92e4ecce3b | ||
| 
						 | 
					8a2f673903 | ||
| 
						 | 
					ac0dd90c37 | ||
| 
						 | 
					9470850258 | ||
| 
						 | 
					2982e9943e | ||
| 
						 | 
					80e13bc24a | ||
| 
						 | 
					9a677534a7 | ||
| 
						 | 
					af08d67fad | ||
| 
						 | 
					a2901d61ea | ||
| 
						 | 
					aaae83efec | ||
| 
						 | 
					7369298638 | ||
| 
						 | 
					a761bd20fa | ||
| 
						 | 
					01ace11293 | ||
| 
						 | 
					205e95a246 | ||
| 
						 | 
					2c2a43e1ec | ||
| 
						 | 
					0c9a6da623 | ||
| 
						 | 
					888d91d14a | ||
| 
						 | 
					2c0cc87b4c | ||
| 
						 | 
					ee0fadf247 | ||
| 
						 | 
					9fb89d7fd2 | ||
| 
						 | 
					af5c36e4ad | ||
| 
						 | 
					a5f943e227 | ||
| 
						 | 
					f8532ba812 | ||
| 
						 | 
					fac4e151cc | ||
| 
						 | 
					f3a0a25380 | ||
| 
						 | 
					5a237795ea | ||
| 
						 | 
					3a99a77104 | ||
| 
						 | 
					5ce8050e46 | ||
| 
						 | 
					5ae3a020bd | ||
| 
						 | 
					af193291fa | ||
| 
						 | 
					b39df5cef0 | ||
| 
						 | 
					dc61c9e277 | ||
| 
						 | 
					10a15e1188 | ||
| 
						 | 
					a2bb6a4f1f | ||
| 
						 | 
					38a19fa574 | ||
| 
						 | 
					9ec4b59afb | ||
| 
						 | 
					20877146df | ||
| 
						 | 
					2a2d556551 | ||
| 
						 | 
					0f762d98a4 | ||
| 
						 | 
					36752cb6a8 | ||
| 
						 | 
					a7f2d89e3f | ||
| 
						 | 
					90b65c6618 | ||
| 
						 | 
					190ec0c14c | ||
| 
						 | 
					7250a300df | ||
| 
						 | 
					34bb00450d | ||
| 
						 | 
					63dadd8983 | ||
| 
						 | 
					67c990e8cf | ||
| 
						 | 
					0292e20c86 | ||
| 
						 | 
					9088c8741a | ||
| 
						 | 
					faedea2120 | ||
| 
						 | 
					e1a0f5706d | ||
| 
						 | 
					a78a4e6716 | ||
| 
						 | 
					6bbf927f57 | ||
| 
						 | 
					4c32bc8e22 | ||
| 
						 | 
					df671a77f6 | ||
| 
						 | 
					dca9def42c | ||
| 
						 | 
					edee7ea284 | ||
| 
						 | 
					658d09ed84 | ||
| 
						 | 
					c41de8f270 | ||
| 
						 | 
					9d2ee2127d | ||
| 
						 | 
					e49ece8793 | ||
| 
						 | 
					188274277a | ||
| 
						 | 
					c0cb3945f1 | ||
| 
						 | 
					e07795e8f0 | ||
| 
						 | 
					bda454fe9c | ||
| 
						 | 
					856e77053e | ||
| 
						 | 
					62dad721fc | ||
| 
						 | 
					2ce145f359 | ||
| 
						 | 
					7e7291ace9 | ||
| 
						 | 
					61c853a3c1 | ||
| 
						 | 
					4346139d65 | ||
| 
						 | 
					1476f83ba7 | ||
| 
						 | 
					c959d64099 | ||
| 
						 | 
					e67d26caeb | ||
| 
						 | 
					86c3fa0df0 | ||
| 
						 | 
					75ae57e194 | ||
| 
						 | 
					d42feae0af | ||
| 
						 | 
					45971b8083 | ||
| 
						 | 
					8e9bbd1bb3 | ||
| 
						 | 
					ec10a3eab4 | ||
| 
						 | 
					49deb4af24 | ||
| 
						 | 
					10f171b6e4 | ||
| 
						 | 
					735db1a12b | ||
| 
						 | 
					b2f4cc2dc5 | ||
| 
						 | 
					d43b587d17 | ||
| 
						 | 
					6b07a955f2 | ||
| 
						 | 
					e23c02575d | ||
| 
						 | 
					b1bf634136 | ||
| 
						 | 
					459faf4dfb | ||
| 
						 | 
					8cdceb83b2 | ||
| 
						 | 
					6aa1ec0802 | ||
| 
						 | 
					0727f7054b | ||
| 
						 | 
					37cc611e3f | ||
| 
						 | 
					c39e6c4423 | ||
| 
						 | 
					1566656af3 | ||
| 
						 | 
					737eba57bd | ||
| 
						 | 
					d32cedd7dc | ||
| 
						 | 
					2b6aa26703 | ||
| 
						 | 
					95f1336060 | ||
| 
						 | 
					f46ee93597 | ||
| 
						 | 
					56d799f449 | ||
| 
						 | 
					6251652c93 | ||
| 
						 | 
					6a2c9a0dc1 | ||
| 
						 | 
					4dd709b543 | ||
| 
						 | 
					aa9f5b8c4a | ||
| 
						 | 
					f485f3fdb5 | ||
| 
						 | 
					c6a0ec64cb | ||
| 
						 | 
					e0b179e5f3 | ||
| 
						 | 
					dac7a3d272 | ||
| 
						 | 
					beed123fb0 | ||
| 
						 | 
					267e582827 | ||
| 
						 | 
					69c02cae76 | ||
| 
						 | 
					4f386663e7 | ||
| 
						 | 
					eaae0547f2 | ||
| 
						 | 
					cc5cfc7525 | ||
| 
						 | 
					795dee85ef | ||
| 
						 | 
					dbd5bef038 | ||
| 
						 | 
					dd2a420578 | ||
| 
						 | 
					eeee30ca03 | ||
| 
						 | 
					82e8792737 | ||
| 
						 | 
					6b18b3df34 | ||
| 
						 | 
					fcebed19b9 | ||
| 
						 | 
					af5f6176b5 | ||
| 
						 | 
					424cc46db0 | ||
| 
						 | 
					ce47ccecc4 | ||
| 
						 | 
					d940f17390 | ||
| 
						 | 
					6351b5d0dc | ||
| 
						 | 
					9c4ac24a66 | ||
| 
						 | 
					bf66df2a29 | ||
| 
						 | 
					962ce380cd | ||
| 
						 | 
					9769afb944 | ||
| 
						 | 
					052b45a510 | ||
| 
						 | 
					84a96e862e | ||
| 
						 | 
					3105235a7a | ||
| 
						 | 
					71a32477e4 | ||
| 
						 | 
					342bce2168 | ||
| 
						 | 
					86d8cbc4d2 | ||
| 
						 | 
					6b63bd6a44 | ||
| 
						 | 
					225707c877 | ||
| 
						 | 
					66da6f18e3 | ||
| 
						 | 
					bdf8bf391c | ||
| 
						 | 
					f66d9e1a22 | ||
| 
						 | 
					ccd3d96942 | ||
| 
						 | 
					b203f2abaa | ||
| 
						 | 
					5490a2f3ba | ||
| 
						 | 
					ba44235471 | ||
| 
						 | 
					be556f9e36 | ||
| 
						 | 
					5e5ba11601 | ||
| 
						 | 
					e384df30fa | ||
| 
						 | 
					4635dacf7f | ||
| 
						 | 
					18e4d270d9 | ||
| 
						 | 
					3bcb91f6ae | ||
| 
						 | 
					5e574a355d | ||
| 
						 | 
					640c7c5fa3 | ||
| 
						 | 
					eb6395a62c | ||
| 
						 | 
					2b2845aa07 | ||
| 
						 | 
					54d8c66f3e | ||
| 
						 | 
					95bbf1b190 | ||
| 
						 | 
					ee2dab51f3 | ||
| 
						 | 
					f63409eed9 | ||
| 
						 | 
					ad8940ad73 | ||
| 
						 | 
					b8bfb5a56c | ||
| 
						 | 
					3f58823430 | ||
| 
						 | 
					e488220bfc | ||
| 
						 | 
					e6e0771496 | ||
| 
						 | 
					fe77d43fa0 | ||
| 
						 | 
					737a7a2db2 | ||
| 
						 | 
					6ae8d10132 | ||
| 
						 | 
					7d249b6d3b | ||
| 
						 | 
					a532b82771 | ||
| 
						 | 
					e8756482aa | ||
| 
						 | 
					35d6da785b | ||
| 
						 | 
					dbdcbd4b9e | ||
| 
						 | 
					a2b6f49c5c | ||
| 
						 | 
					a31ed4a723 | ||
| 
						 | 
					b2fc84c98e | ||
| 
						 | 
					927369b06d | ||
| 
						 | 
					401fd37e35 | ||
| 
						 | 
					759cdf10c5 | ||
| 
						 | 
					6e7ce1eec1 | ||
| 
						 | 
					2a65955e88 | ||
| 
						 | 
					00b6c6a437 | ||
| 
						 | 
					21ef3b0ecf | ||
| 
						 | 
					5b0d6a1375 | ||
| 
						 | 
					0510da0853 | ||
| 
						 | 
					365d22d076 | ||
| 
						 | 
					1760169ef9 | ||
| 
						 | 
					32ea224933 | ||
| 
						 | 
					38a067e203 | ||
| 
						 | 
					ab6f1b6df7 | ||
| 
						 | 
					b36802edff | ||
| 
						 | 
					1d2af0f291 | ||
| 
						 | 
					40e8c5e2b0 | ||
| 
						 | 
					e11d0d37ee | ||
| 
						 | 
					ea4266538a | ||
| 
						 | 
					aa7bf9169f | ||
| 
						 | 
					a95e83ab6e | ||
| 
						 | 
					86daaf4bf2 | ||
| 
						 | 
					d5b4f02932 | ||
| 
						 | 
					15deec6c53 | ||
| 
						 | 
					17b1875151 | ||
| 
						 | 
					bfda8f0b8a | ||
| 
						 | 
					7bb0ff986b | ||
| 
						 | 
					f3cfef4021 | ||
| 
						 | 
					3184c3c21b | ||
| 
						 | 
					9264737985 | ||
| 
						 | 
					c9b353a689 | ||
| 
						 | 
					fda6502f33 | ||
| 
						 | 
					0fdac82b93 | ||
| 
						 | 
					6b3d6d5211 | ||
| 
						 | 
					cb89ee39f5 | ||
| 
						 | 
					be827be742 | ||
| 
						 | 
					8fcecd59a0 | ||
| 
						 | 
					83b49b23e4 | ||
| 
						 | 
					f5ee618986 | ||
| 
						 | 
					8339b88180 | ||
| 
						 | 
					f2958818c8 | ||
| 
						 | 
					608547c62c | ||
| 
						 | 
					20f604948f | ||
| 
						 | 
					f72a4f966d | ||
| 
						 | 
					bd00db4292 | ||
| 
						 | 
					68debc474a | ||
| 
						 | 
					6a7f993a9a | ||
| 
						 | 
					ae25931b37 | ||
| 
						 | 
					84fe6654cc | ||
| 
						 | 
					d2d023cca7 | ||
| 
						 | 
					64908e0080 | ||
| 
						 | 
					d4e1899747 | ||
| 
						 | 
					7f9b8d68ac | ||
| 
						 | 
					5b1e849bde | ||
| 
						 | 
					fba6de76b1 | ||
| 
						 | 
					8419b42e83 | ||
| 
						 | 
					5f38c15b1f | ||
| 
						 | 
					c3b72baa8e | ||
| 
						 | 
					16d0416f22 | ||
| 
						 | 
					b9aa4f4478 | ||
| 
						 | 
					8d3ad3a8c1 | ||
| 
						 | 
					5a689ce897 | ||
| 
						 | 
					35e22703af | ||
| 
						 | 
					41a2d0e06c | ||
| 
						 | 
					4d95e35c06 | ||
| 
						 | 
					4e553f34ba | ||
| 
						 | 
					b910726c43 | ||
| 
						 | 
					64e3cab6ab | ||
| 
						 | 
					f3196396a2 | ||
| 
						 | 
					148336929d | ||
| 
						 | 
					2f3ec3a77f | ||
| 
						 | 
					8d7a487013 | ||
| 
						 | 
					622464ff5e | ||
| 
						 | 
					b45a44e405 | ||
| 
						 | 
					a196958bd6 | ||
| 
						 | 
					f101418658 | ||
| 
						 | 
					aa05a1e81d | ||
| 
						 | 
					384bc62f25 | ||
| 
						 | 
					46e62f1a9a | ||
| 
						 | 
					c5c2014081 | ||
| 
						 | 
					c668c603cc | ||
| 
						 | 
					8f3b7c179e | ||
| 
						 | 
					ea18c47011 | ||
| 
						 | 
					ced7110a78 | ||
| 
						 | 
					92f13eb8bf | ||
| 
						 | 
					1312ef7e50 | ||
| 
						 | 
					e992979113 | ||
| 
						 | 
					d317b49940 | ||
| 
						 | 
					6be53468c5 | ||
| 
						 | 
					046094bdcb | ||
| 
						 | 
					1064c270d9 | ||
| 
						 | 
					2447fccf1e | ||
| 
						 | 
					c43c711f72 | ||
| 
						 | 
					f354e6de69 | ||
| 
						 | 
					08d60fcbf2 | ||
| 
						 | 
					4cda54774a | ||
| 
						 | 
					613475ac26 | ||
| 
						 | 
					12615c46f8 | ||
| 
						 | 
					20d23fcb92 | ||
| 
						 | 
					b335840f97 | ||
| 
						 | 
					6d84f59e6b | ||
| 
						 | 
					c5efec678e | ||
| 
						 | 
					83cb89e4f7 | ||
| 
						 | 
					6bdf689d0f | ||
| 
						 | 
					e164362069 | ||
| 
						 | 
					72e3f33f28 | ||
| 
						 | 
					ccfd907914 | ||
| 
						 | 
					5a44e63cad | ||
| 
						 | 
					d96cca3822 | ||
| 
						 | 
					06580bf0e4 | ||
| 
						 | 
					b21bd64764 | ||
| 
						 | 
					6b97dc6734 | ||
| 
						 | 
					4e3f328a02 | ||
| 
						 | 
					8380ca2fdd | ||
| 
						 | 
					ec678bc6d2 | ||
| 
						 | 
					2b5e2d4760 | ||
| 
						 | 
					89abad7980 | ||
| 
						 | 
					d84da5bdbf | ||
| 
						 | 
					5cc1d9521c | ||
| 
						 | 
					a199fc6113 | ||
| 
						 | 
					655e34b166 | ||
| 
						 | 
					5ea3a02d6a | ||
| 
						 | 
					15c68c9594 | ||
| 
						 | 
					08438608d1 | ||
| 
						 | 
					2da94e0fdf | ||
| 
						 | 
					63165764dc | ||
| 
						 | 
					103810ce20 | ||
| 
						 | 
					f59356b96b | ||
| 
						 | 
					4a8511f680 | ||
| 
						 | 
					cd6698c688 | ||
| 
						 | 
					c7ca9d7e36 | ||
| 
						 | 
					d70b759cb9 | ||
| 
						 | 
					ae3dda0f8f | ||
| 
						 | 
					e229ba5945 | ||
| 
						 | 
					dcc50093bb | ||
| 
						 | 
					0831690f79 | ||
| 
						 | 
					98ef51514f | ||
| 
						 | 
					ac9993394c | ||
| 
						 | 
					849c3fd9c9 | ||
| 
						 | 
					da58fcbfce | ||
| 
						 | 
					fa3cd9736f | ||
| 
						 | 
					a0c5d17539 | ||
| 
						 | 
					224cd04673 | ||
| 
						 | 
					52243d0870 | ||
| 
						 | 
					d519873fa4 | ||
| 
						 | 
					a76dcd4ba1 | ||
| 
						 | 
					2d07185300 | ||
| 
						 | 
					518e1df257 | ||
| 
						 | 
					e0def66959 | ||
| 
						 | 
					772d970074 | ||
| 
						 | 
					ba7d85145a | ||
| 
						 | 
					fb73dceab0 | ||
| 
						 | 
					13fd83e0ba | ||
| 
						 | 
					719ba75fcc | ||
| 
						 | 
					13ab98440c | ||
| 
						 | 
					1752004301 | ||
| 
						 | 
					536a5f7cff | ||
| 
						 | 
					a69aece23a | ||
| 
						 | 
					77d3815baa | ||
| 
						 | 
					e225e17386 | ||
| 
						 | 
					3290208749 | ||
| 
						 | 
					3106187aac | ||
| 
						 | 
					eae490b5b1 | ||
| 
						 | 
					bcce77508a | ||
| 
						 | 
					e9bdf02cfc | ||
| 
						 | 
					77f659c9b9 | ||
| 
						 | 
					c66e157a14 | ||
| 
						 | 
					2c927277e2 | ||
| 
						 | 
					29fe1c86da | ||
| 
						 | 
					1ae9c48370 | ||
| 
						 | 
					078a8b40e9 | ||
| 
						 | 
					9daeae1695 | ||
| 
						 | 
					014e016058 | ||
| 
						 | 
					f41f93af3a | ||
| 
						 | 
					51539521b1 | ||
| 
						 | 
					522dec34a5 | ||
| 
						 | 
					ea6d76cce6 | ||
| 
						 | 
					7c7d61f61e | ||
| 
						 | 
					184dde92a2 | ||
| 
						 | 
					7e43c794fd | ||
| 
						 | 
					c7285967d6 | ||
| 
						 | 
					41f4baadb9 | ||
| 
						 | 
					20082ec9fb | ||
| 
						 | 
					8dae8c52c0 | ||
| 
						 | 
					068076c0d5 | ||
| 
						 | 
					c0ae44a41b | ||
| 
						 | 
					593e8e1f63 | ||
| 
						 | 
					707cf35f0a | ||
| 
						 | 
					30f11d0e16 | ||
| 
						 | 
					53d6ab6c23 | ||
| 
						 | 
					280e44304a | ||
| 
						 | 
					79fac4466e | ||
| 
						 | 
					0e9f09e582 | ||
| 
						 | 
					25d0fdf8ff | ||
| 
						 | 
					1d089d4541 | ||
| 
						 | 
					1c78663378 | ||
| 
						 | 
					7666022840 | ||
| 
						 | 
					946c8b498a | ||
| 
						 | 
					406ca66c8d | ||
| 
						 | 
					ed971df93a | ||
| 
						 | 
					74c054b2a5 | ||
| 
						 | 
					017a10189c | ||
| 
						 | 
					f1c361855e | ||
| 
						 | 
					3d72df4666 | ||
| 
						 | 
					6f732a9957 | ||
| 
						 | 
					447bf77dfe | ||
| 
						 | 
					6621ef6a0b | ||
| 
						 | 
					e327c8758e | ||
| 
						 | 
					8a08de5691 | ||
| 
						 | 
					3d7375be8b | ||
| 
						 | 
					da754e9a71 | ||
| 
						 | 
					cc9ec806b2 | ||
| 
						 | 
					72cf037c0d | ||
| 
						 | 
					8ae08b29e4 | ||
| 
						 | 
					4967fa020f | ||
| 
						 | 
					54f2640ef2 | ||
| 
						 | 
					56246592c7 | ||
| 
						 | 
					1ff5d71e12 | ||
| 
						 | 
					67c42c5911 | ||
| 
						 | 
					d0b514890a | ||
| 
						 | 
					c0285fbc15 | ||
| 
						 | 
					a438c841e1 | ||
| 
						 | 
					19d7c2b336 | ||
| 
						 | 
					afb6c70909 | ||
| 
						 | 
					d9af496b13 | ||
| 
						 | 
					a55cf40b1b | ||
| 
						 | 
					b19008d1b8 | ||
| 
						 | 
					6f88c81616 | ||
| 
						 | 
					43cb230f19 | ||
| 
						 | 
					fd6a59202d | ||
| 
						 | 
					e353f66eaa | ||
| 
						 | 
					9293bcfb1c | ||
| 
						 | 
					d154118600 | ||
| 
						 | 
					c2273d2c8e | ||
| 
						 | 
					495ba01d8e | ||
| 
						 | 
					f627a02886 | ||
| 
						 | 
					5f9daa6640 | ||
| 
						 | 
					9edda556de | ||
| 
						 | 
					3891a52aeb | ||
| 
						 | 
					1e5e3353f3 | ||
| 
						 | 
					7aa4b8247c | ||
| 
						 | 
					37339ddafc | ||
| 
						 | 
					dcb51683c5 | ||
| 
						 | 
					74a4a788b1 | ||
| 
						 | 
					7909273a21 | ||
| 
						 | 
					130e8dbd40 | ||
| 
						 | 
					40e2ebed95 | ||
| 
						 | 
					bf9b33acec | ||
| 
						 | 
					7710a33b6c | ||
| 
						 | 
					af3ea2d4fd | ||
| 
						 | 
					52a3255936 | ||
| 
						 | 
					5ab9ca1c0d | ||
| 
						 | 
					461f602992 | ||
| 
						 | 
					46180435cc | ||
| 
						 | 
					7f2699c6da | ||
| 
						 | 
					aede5c486b | ||
| 
						 | 
					fb2407386f | ||
| 
						 | 
					aa59c46c4c | ||
| 
						 | 
					07afc4953a | ||
| 
						 | 
					c5557fc488 | ||
| 
						 | 
					8c14150536 | ||
| 
						 | 
					c127903127 | ||
| 
						 | 
					0881cf1379 | ||
| 
						 | 
					d0a16b0ec0 | ||
| 
						 | 
					290beb90a7 | ||
| 
						 | 
					0a4ef17135 | ||
| 
						 | 
					e0c32ce700 | ||
| 
						 | 
					e65144a105 | ||
| 
						 | 
					ae40445dba | ||
| 
						 | 
					25a8240d12 | ||
| 
						 | 
					1a4a180e8c | ||
| 
						 | 
					aea10a3b93 | ||
| 
						 | 
					f855862ade | ||
| 
						 | 
					91d37c7875 | ||
| 
						 | 
					5707b93110 | ||
| 
						 | 
					81b2d0732f | ||
| 
						 | 
					ec0538d251 | ||
| 
						 | 
					eb0c629fad | ||
| 
						 | 
					509d3f6d30 | ||
| 
						 | 
					c576af7c6f | ||
| 
						 | 
					f3682f0e8e | ||
| 
						 | 
					1c58913eeb | ||
| 
						 | 
					cfbc294832 | ||
| 
						 | 
					64ad8b1dac | ||
| 
						 | 
					fb079f9e50 | ||
| 
						 | 
					39b09f8f87 | ||
| 
						 | 
					53d26e5c5c | ||
| 
						 | 
					e21f3e6c73 | ||
| 
						 | 
					edd46eb3d1 | ||
| 
						 | 
					e71238571a | ||
| 
						 | 
					2867ec509e | ||
| 
						 | 
					d853a9ebbe | ||
| 
						 | 
					e0d2fa98f3 | ||
| 
						 | 
					cc90f83463 | ||
| 
						 | 
					9ea1238e1b | ||
| 
						 | 
					1530abbd1a | ||
| 
						 | 
					3bfcd18a03 | ||
| 
						 | 
					6b7db22981 | ||
| 
						 | 
					8adb8a6986 | ||
| 
						 | 
					37e3e2f9c2 | ||
| 
						 | 
					3c7be32ef5 | ||
| 
						 | 
					051775b9b4 | ||
| 
						 | 
					e0d5b91388 | ||
| 
						 | 
					4dd2027428 | ||
| 
						 | 
					42ab98b830 | ||
| 
						 | 
					2b2bce6457 | ||
| 
						 | 
					69ee816541 | ||
| 
						 | 
					2cbf3f7e15 | ||
| 
						 | 
					923eece3f5 | ||
| 
						 | 
					d4fb313ff0 | ||
| 
						 | 
					7dce465c06 | ||
| 
						 | 
					5a30f5c00e | ||
| 
						 | 
					fd406af962 | ||
| 
						 | 
					9e5ae30372 | ||
| 
						 | 
					0fe3538331 | ||
| 
						 | 
					b0f5ad75ae | ||
| 
						 | 
					a290f63a15 | ||
| 
						 | 
					d078ce794e | ||
| 
						 | 
					06f51a5c34 | ||
| 
						 | 
					db96778064 | ||
| 
						 | 
					d8918ea156 | ||
| 
						 | 
					3503474bb8 | ||
| 
						 | 
					eb1606b086 | ||
| 
						 | 
					1330a092fa | ||
| 
						 | 
					d3c74cfb45 | ||
| 
						 | 
					d044545520 | ||
| 
						 | 
					d1cdc1c6a0 | ||
| 
						 | 
					dc8d91ea39 | ||
| 
						 | 
					ae5a6d330d | ||
| 
						 | 
					fe0bee21b0 | ||
| 
						 | 
					c090c19bfe | ||
| 
						 | 
					8fbec785e8 | ||
| 
						 | 
					06fb3d9476 | ||
| 
						 | 
					fe3e8a7bb6 | ||
| 
						 | 
					ce2ff25edd | ||
| 
						 | 
					65a1b892e3 | ||
| 
						 | 
					e272fde95e | ||
| 
						 | 
					d48bff0e20 | ||
| 
						 | 
					6e61c34f0f | ||
| 
						 | 
					2d9506eb54 | ||
| 
						 | 
					b1988c7b67 | ||
| 
						 | 
					fb5d72c29b | ||
| 
						 | 
					ac148ce0e9 | ||
| 
						 | 
					016dca654e | ||
| 
						 | 
					6502bdecbe | ||
| 
						 | 
					91a739af6e | ||
| 
						 | 
					e232565971 | ||
| 
						 | 
					472488ebe8 | ||
| 
						 | 
					8de3698b23 | ||
| 
						 | 
					a694b46914 | ||
| 
						 | 
					c384ed960c | ||
| 
						 | 
					2386d2e299 | ||
| 
						 | 
					cc7e1a72c1 | ||
| 
						 | 
					5cc0fa7c98 | ||
| 
						 | 
					4ce848ab51 | ||
| 
						 | 
					2e34e11b02 | ||
| 
						 | 
					8eda5f36fb | ||
| 
						 | 
					3dbe5d872b | ||
| 
						 | 
					96a95ba9fe | ||
| 
						 | 
					f594ed659e | ||
| 
						 | 
					5c4bfbbd95 | ||
| 
						 | 
					8733635638 | ||
| 
						 | 
					928aa74e89 | ||
| 
						 | 
					8fdfe673e8 | ||
| 
						 | 
					494a6e6090 | ||
| 
						 | 
					89bb7e6b0e | ||
| 
						 | 
					52cfb9a041 | ||
| 
						 | 
					3817ddef41 | ||
| 
						 | 
					0f494c9dd6 | ||
| 
						 | 
					3ff97ecf45 | ||
| 
						 | 
					a10c0b516b | ||
| 
						 | 
					5eb1469dbf | ||
| 
						 | 
					9a90fe3794 | ||
| 
						 | 
					1917c4b04a | ||
| 
						 | 
					2b01d4a203 | ||
| 
						 | 
					c5100219d1 | ||
| 
						 | 
					a730a08161 | ||
| 
						 | 
					4bb8e3a121 | ||
| 
						 | 
					eacc00f786 | ||
| 
						 | 
					17f5e557ed | ||
| 
						 | 
					c33e5bc40f | ||
| 
						 | 
					06d1a98ad2 | ||
| 
						 | 
					afe6f4030e | ||
| 
						 | 
					6e49c4ffe0 | ||
| 
						 | 
					28d9f00610 | ||
| 
						 | 
					8f688e5e13 | ||
| 
						 | 
					5a182eddbf | ||
| 
						 | 
					4b615cb3a9 | ||
| 
						 | 
					b36247a091 | ||
| 
						 | 
					6e77756d6a | ||
| 
						 | 
					585c0c3818 | ||
| 
						 | 
					6ff75f9a9f | ||
| 
						 | 
					906ef43c00 | ||
| 
						 | 
					4528957235 | ||
| 
						 | 
					9d448a42a7 | ||
| 
						 | 
					979e10f9d5 | ||
| 
						 | 
					31f65b89bb | ||
| 
						 | 
					86639dbc02 | ||
| 
						 | 
					bf8c33703c | ||
| 
						 | 
					ffe7ef4764 | ||
| 
						 | 
					30dae70e2b | ||
| 
						 | 
					557a747d55 | ||
| 
						 | 
					fe273b3829 | ||
| 
						 | 
					2439bb30e8 | ||
| 
						 | 
					e48b6bd22d | ||
| 
						 | 
					1de9ffacb0 | ||
| 
						 | 
					93fd6170a3 | ||
| 
						 | 
					b7c3e6099c | ||
| 
						 | 
					d8163e9835 | ||
| 
						 | 
					12b1916599 | ||
| 
						 | 
					9d8cdb5976 | ||
| 
						 | 
					29a7c1938a | ||
| 
						 | 
					8636d3139e | ||
| 
						 | 
					987571ce91 | ||
| 
						 | 
					6f4c5fcc87 | ||
| 
						 | 
					a7ca010d4e | ||
| 
						 | 
					a836842a7e | ||
| 
						 | 
					f8c11a324a | ||
| 
						 | 
					052c9be111 | ||
| 
						 | 
					854e520528 | ||
| 
						 | 
					05737b85eb | ||
| 
						 | 
					c8a2308739 | ||
| 
						 | 
					ed46a078f9 | ||
| 
						 | 
					4f7c2bf8c3 | ||
| 
						 | 
					0e341726d2 | ||
| 
						 | 
					a4f9746d3a | ||
| 
						 | 
					556c546b2e | ||
| 
						 | 
					aa479948f9 | ||
| 
						 | 
					fa3cee9d58 | ||
| 
						 | 
					0021fb8a33 | ||
| 
						 | 
					c8c727e6c6 | ||
| 
						 | 
					b8494ab3cc | ||
| 
						 | 
					2eda03f5de | ||
| 
						 | 
					3a38358946 | ||
| 
						 | 
					e7fc697e57 | ||
| 
						 | 
					6c9845b9f3 | ||
| 
						 | 
					9e146a8a5a | ||
| 
						 | 
					433d9bfb02 | ||
| 
						 | 
					94917e315e | ||
| 
						 | 
					ced6852735 | ||
| 
						 | 
					8dc55f417d | ||
| 
						 | 
					3d338bba3c | ||
| 
						 | 
					631398f700 | ||
| 
						 | 
					7984d8cdfb | ||
| 
						 | 
					783a6110ef | ||
| 
						 | 
					5d4d53c3a1 | ||
| 
						 | 
					3014955ece | ||
| 
						 | 
					0481f20c6b | ||
| 
						 | 
					76309601eb | ||
| 
						 | 
					84dd864886 | ||
| 
						 | 
					7924e01b15 | ||
| 
						 | 
					dadc70630b | ||
| 
						 | 
					effc37a702 | ||
| 
						 | 
					deac3fc918 | ||
| 
						 | 
					e6dea4c92c | ||
| 
						 | 
					075e992fa0 | ||
| 
						 | 
					565ca81b30 | ||
| 
						 | 
					58c4eaaf86 | ||
| 
						 | 
					77e8008752 | ||
| 
						 | 
					2ba10fcbc7 | ||
| 
						 | 
					4956a58026 | ||
| 
						 | 
					92332fc385 | ||
| 
						 | 
					9366f4b40e | ||
| 
						 | 
					f49e8ec5ad | ||
| 
						 | 
					cd33647087 | ||
| 
						 | 
					71ebcac7f2 | ||
| 
						 | 
					f06aee21eb | ||
| 
						 | 
					5fbbc17376 | ||
| 
						 | 
					a9d8830106 | ||
| 
						 | 
					d21e6235ad | ||
| 
						 | 
					289f79bbb0 | ||
| 
						 | 
					768e9f4c09 | ||
| 
						 | 
					62c776d90c | ||
| 
						 | 
					464022bea2 | ||
| 
						 | 
					61549b4a74 | ||
| 
						 | 
					18df3dc07a | ||
| 
						 | 
					3725724c54 | ||
| 
						 | 
					500a005aac | ||
| 
						 | 
					584cc6de2e | ||
| 
						 | 
					2e5a6e21cf | ||
| 
						 | 
					b79f63db78 | ||
| 
						 | 
					7ed7a57d92 | ||
| 
						 | 
					1eaf7c89b7 | ||
| 
						 | 
					1fff8dd306 | ||
| 
						 | 
					c3a3d02bea | ||
| 
						 | 
					a9261970dd | ||
| 
						 | 
					b7a3fe05a4 | ||
| 
						 | 
					ab6b9006b7 | ||
| 
						 | 
					d9a8b057c3 | ||
| 
						 | 
					2ec6215b1c | ||
| 
						 | 
					c59a8c9644 | ||
| 
						 | 
					10de4b6b7b | ||
| 
						 | 
					0be214e79e | ||
| 
						 | 
					d6083c68fd | ||
| 
						 | 
					22f7ac22d5 | ||
| 
						 | 
					a00046f9b2 | ||
| 
						 | 
					8a24275ba9 | ||
| 
						 | 
					ca841252bd | ||
| 
						 | 
					54195b16ad | ||
| 
						 | 
					cb90167c76 | ||
| 
						 | 
					ac4ae85a4a | ||
| 
						 | 
					48b2a271cc | ||
| 
						 | 
					596807055e | ||
| 
						 | 
					cee20c4eb9 | ||
| 
						 | 
					15fb47cb3d | ||
| 
						 | 
					2635dfef96 | ||
| 
						 | 
					7d7789ae96 | ||
| 
						 | 
					cc69285420 | ||
| 
						 | 
					99d3a283ef | ||
| 
						 | 
					9b532584d6 | ||
| 
						 | 
					7576eb38d9 | ||
| 
						 | 
					8440d013f8 | ||
| 
						 | 
					174c87a192 | ||
| 
						 | 
					32b62d6d4f | ||
| 
						 | 
					a0c2d312e9 | ||
| 
						 | 
					5127a9ae3c | ||
| 
						 | 
					b5653a1c06 | ||
| 
						 | 
					671bd1022e | ||
| 
						 | 
					94bba4ac9c | ||
| 
						 | 
					fe1136aa95 | ||
| 
						 | 
					8950ffcc5e | ||
| 
						 | 
					2bc627970e | ||
| 
						 | 
					44615c6fa2 | ||
| 
						 | 
					00f55ea0bc | ||
| 
						 | 
					be43cebf7d | ||
| 
						 | 
					f38317d01f | ||
| 
						 | 
					da839aae66 | ||
| 
						 | 
					768e00ff1a | ||
| 
						 | 
					40631f465e | ||
| 
						 | 
					f665c73bb1 | ||
| 
						 | 
					be067466fe | ||
| 
						 | 
					3c309df6dd | ||
| 
						 | 
					b7e6d98647 | ||
| 
						 | 
					48942de75e | ||
| 
						 | 
					fbcbc10174 | ||
| 
						 | 
					342b48105f | ||
| 
						 | 
					5f3e7f02cc | ||
| 
						 | 
					bfccf29ccf | ||
| 
						 | 
					1e3bb1f02b | ||
| 
						 | 
					0dcf6771e7 | ||
| 
						 | 
					062503c523 | ||
| 
						 | 
					c3d7f2f170 | ||
| 
						 | 
					faf6c16717 | ||
| 
						 | 
					3d79d78134 | ||
| 
						 | 
					35cab4ee73 | ||
| 
						 | 
					a0edb8f2ad | ||
| 
						 | 
					5cfe5e312b | ||
| 
						 | 
					13f6ec04d5 | ||
| 
						 | 
					2edc4a79b9 | ||
| 
						 | 
					4661185719 | ||
| 
						 | 
					895da5cbf0 | ||
| 
						 | 
					ef9147512b | ||
| 
						 | 
					abe05456f7 | ||
| 
						 | 
					f9dfd3b348 | ||
| 
						 | 
					9449501537 | ||
| 
						 | 
					f3b5d5ab7b | ||
| 
						 | 
					8ee5726e0c | ||
| 
						 | 
					effa7fd57d | ||
| 
						 | 
					6c039d2ad0 | ||
| 
						 | 
					2dd8527566 | ||
| 
						 | 
					fe811ce32e | ||
| 
						 | 
					9fee0805c4 | ||
| 
						 | 
					1987c32761 | ||
| 
						 | 
					0ed2659698 | ||
| 
						 | 
					9878856dfe | ||
| 
						 | 
					ed01fd4edf | ||
| 
						 | 
					9474933070 | ||
| 
						 | 
					f3987b453c | ||
| 
						 | 
					dcb4cb3a1e | ||
| 
						 | 
					198b840059 | ||
| 
						 | 
					42583cf3bb | ||
| 
						 | 
					2e15371d61 | ||
| 
						 | 
					339ff8ca77 | ||
| 
						 | 
					268eaddad8 | ||
| 
						 | 
					bb3cc1130b | ||
| 
						 | 
					64ae3280e1 | ||
| 
						 | 
					95235d69c2 | ||
| 
						 | 
					d639c7be39 | ||
| 
						 | 
					d7cafe25ff | ||
| 
						 | 
					996f53373e | ||
| 
						 | 
					6f55370ad4 | ||
| 
						 | 
					fd511966a7 | ||
| 
						 | 
					c4ddddd434 | ||
| 
						 | 
					83a4db3b31 | ||
| 
						 | 
					e35ef75949 | ||
| 
						 | 
					d9dec6fe6b | ||
| 
						 | 
					69bdbaed41 | ||
| 
						 | 
					30f359e642 | ||
| 
						 | 
					29d0a1714e | ||
| 
						 | 
					fcb97f802f | ||
| 
						 | 
					9ad05e640d | ||
| 
						 | 
					449f00f960 | ||
| 
						 | 
					8e64329d05 | ||
| 
						 | 
					4284777556 | ||
| 
						 | 
					81c496d96c | ||
| 
						 | 
					65c06da275 | ||
| 
						 | 
					5d0657c49a | ||
| 
						 | 
					f6f6550bfb | ||
| 
						 | 
					e01fb50359 | ||
| 
						 | 
					28ce1c1249 | ||
| 
						 | 
					7db592d27a | ||
| 
						 | 
					b8e5c0d898 | ||
| 
						 | 
					067c1771d0 | ||
| 
						 | 
					349429b76e | ||
| 
						 | 
					cc8f2afce9 | ||
| 
						 | 
					2e97b20f94 | ||
| 
						 | 
					1a163243ec | ||
| 
						 | 
					75660e6f21 | ||
| 
						 | 
					199ca77c2a | ||
| 
						 | 
					1e2d2abbdf | ||
| 
						 | 
					11b980f574 | ||
| 
						 | 
					3274f9f155 | ||
| 
						 | 
					f90f8824bb | ||
| 
						 | 
					c7116d40ca | ||
| 
						 | 
					6ef66399f8 | ||
| 
						 | 
					2b8561f27d | ||
| 
						 | 
					b20d8f195b | ||
| 
						 | 
					6cf0eb9e1d | ||
| 
						 | 
					c349e9aabe | ||
| 
						 | 
					6ee38ceaba | ||
| 
						 | 
					c60613fbcb | ||
| 
						 | 
					bcc1b7b48a | ||
| 
						 | 
					b19cb0805c | ||
| 
						 | 
					7dfc5a78ba | ||
| 
						 | 
					a077132d82 | ||
| 
						 | 
					8ed6be6307 | ||
| 
						 | 
					c490dd1563 | ||
| 
						 | 
					d866b3df1f | ||
| 
						 | 
					97f3fb4496 | ||
| 
						 | 
					7530266330 | ||
| 
						 | 
					6a53f356d2 | ||
| 
						 | 
					075dc1e4e9 | ||
| 
						 | 
					97b87d4ce4 | ||
| 
						 | 
					e203e98375 | ||
| 
						 | 
					9fcd104065 | ||
| 
						 | 
					178e0ba87c | ||
| 
						 | 
					cc40110d7e | ||
| 
						 | 
					d58fb2bbc0 | ||
| 
						 | 
					812333e9ae | ||
| 
						 | 
					92bbdce435 | ||
| 
						 | 
					bc62d49fc9 | ||
| 
						 | 
					fe54d5b8ae | ||
| 
						 | 
					7cc30c268b | ||
| 
						 | 
					df60a2248a | ||
| 
						 | 
					aa85d0ffeb | ||
| 
						 | 
					1db963361c | ||
| 
						 | 
					8a55b20284 | ||
| 
						 | 
					beec349bc5 | ||
| 
						 | 
					b025ed6057 | ||
| 
						 | 
					27a54bcbaa | ||
| 
						 | 
					6b20993d2a | ||
| 
						 | 
					9ab16bdbb3 | ||
| 
						 | 
					23088bc897 | ||
| 
						 | 
					054a62de60 | ||
| 
						 | 
					5aff548794 | ||
| 
						 | 
					ff8fe7e018 | ||
| 
						 | 
					c6617ebc9f | ||
| 
						 | 
					15fa0c264f | ||
| 
						 | 
					25468f55ff | ||
| 
						 | 
					2340c55d76 | ||
| 
						 | 
					13c1f4ab19 | ||
| 
						 | 
					a160b798ca | ||
| 
						 | 
					71f00a9efd | ||
| 
						 | 
					967096f01c | ||
| 
						 | 
					7616e94fd3 | ||
| 
						 | 
					27ec69fb97 | ||
| 
						 | 
					182d150eaa | ||
| 
						 | 
					098ef976f7 | ||
| 
						 | 
					ea724e343b | ||
| 
						 | 
					85736d697c | ||
| 
						 | 
					576a146ed2 | ||
| 
						 | 
					69c5291e52 | ||
| 
						 | 
					4875ef045a | ||
| 
						 | 
					369cfc2413 | ||
| 
						 | 
					491842ea34 | ||
| 
						 | 
					9801876a2f | ||
| 
						 | 
					9c28a04c65 | ||
| 
						 | 
					596a1764ef | ||
| 
						 | 
					8e09e1b248 | ||
| 
						 | 
					d5674c85d7 | ||
| 
						 | 
					ed6649b1d3 | ||
| 
						 | 
					02baa778c5 | ||
| 
						 | 
					5fd0e5add2 | ||
| 
						 | 
					23eccb2f20 | ||
| 
						 | 
					3c523fb824 | ||
| 
						 | 
					5dbfc2786d | ||
| 
						 | 
					c61495df52 | ||
| 
						 | 
					6ad5ea1696 | ||
| 
						 | 
					39fa27a2dc | ||
| 
						 | 
					348bae53fe | ||
| 
						 | 
					6bc00fc5e5 | ||
| 
						 | 
					54c0f015f9 | ||
| 
						 | 
					ea3e6dae93 | ||
| 
						 | 
					80e52c73b0 | ||
| 
						 | 
					92dbe6cdf8 | ||
| 
						 | 
					0781e8cf12 | ||
| 
						 | 
					5c893f0f39 | ||
| 
						 | 
					e05dc99006 | ||
| 
						 | 
					f864416e39 | ||
| 
						 | 
					472dbd641c | ||
| 
						 | 
					05141b4f52 | ||
| 
						 | 
					b71a088da7 | ||
| 
						 | 
					d76fb566a2 | ||
| 
						 | 
					24a40af103 | ||
| 
						 | 
					4a60292f82 | ||
| 
						 | 
					784b914e07 | ||
| 
						 | 
					7eea866869 | ||
| 
						 | 
					4ab5456a98 | ||
| 
						 | 
					8881a9f40e | ||
| 
						 | 
					4db7f6f59c | ||
| 
						 | 
					edbf8509e1 | ||
| 
						 | 
					a017fbadd3 | ||
| 
						 | 
					201f4b7e4a | ||
| 
						 | 
					c9ff536e24 | ||
| 
						 | 
					238efb02c6 | ||
| 
						 | 
					f7e12b629f | ||
| 
						 | 
					7cbca7fc1e | ||
| 
						 | 
					be7b87cda3 | ||
| 
						 | 
					07979a13fb | ||
| 
						 | 
					9073c4554f | ||
| 
						 | 
					8694e0ad19 | ||
| 
						 | 
					60fe987a5f | ||
| 
						 | 
					86256162de | ||
| 
						 | 
					db24ca3dc1 | ||
| 
						 | 
					5e3aa2db1d | ||
| 
						 | 
					b74a501fac | ||
| 
						 | 
					490a7d4a78 | ||
| 
						 | 
					b147195189 | ||
| 
						 | 
					b561666d80 | ||
| 
						 | 
					a0b5305e3e | ||
| 
						 | 
					e7a6c17260 | ||
| 
						 | 
					c8ee9e6447 | ||
| 
						 | 
					f2d350002e | ||
| 
						 | 
					fabd26f85b | ||
| 
						 | 
					17dcf7d2e5 | ||
| 
						 | 
					6f62995c96 | ||
| 
						 | 
					f405f4bbc4 | ||
| 
						 | 
					98124de362 | ||
| 
						 | 
					1e4ea90021 | ||
| 
						 | 
					053f4a9a2e | ||
| 
						 | 
					2c7d2230b3 | ||
| 
						 | 
					040e0d8387 | ||
| 
						 | 
					b5c382f929 | ||
| 
						 | 
					0c9c1ae673 | ||
| 
						 | 
					da0e0bdaec | ||
| 
						 | 
					1f5b6a6a35 | ||
| 
						 | 
					d25b2890be | ||
| 
						 | 
					d73438a397 | ||
| 
						 | 
					e91538a554 | ||
| 
						 | 
					8a3b514372 | ||
| 
						 | 
					7eca955b79 | ||
| 
						 | 
					4242354c03 | ||
| 
						 | 
					8728389c88 | ||
| 
						 | 
					6651801b3f | ||
| 
						 | 
					9190ce3701 | ||
| 
						 | 
					90e2064d72 | ||
| 
						 | 
					943d419f98 | ||
| 
						 | 
					551316bcb6 | ||
| 
						 | 
					9dd5089940 | ||
| 
						 | 
					06e7ebbdeb | ||
| 
						 | 
					a83b16e12a | ||
| 
						 | 
					91a8b97cf4 | ||
| 
						 | 
					41754c92c3 | ||
| 
						 | 
					e544995b83 | ||
| 
						 | 
					45cf5c4c0f | ||
| 
						 | 
					900eedfc2e | ||
| 
						 | 
					faaa7bfa3a | ||
| 
						 | 
					70366a98bd | ||
| 
						 | 
					e88180b4d5 | ||
| 
						 | 
					b639683ac1 | ||
| 
						 | 
					918c8f9295 | ||
| 
						 | 
					c2214cd4b5 | ||
| 
						 | 
					7d7e5bac12 | ||
| 
						 | 
					185b558561 | ||
| 
						 | 
					4632035581 | ||
| 
						 | 
					d7f81dff23 | ||
| 
						 | 
					c849738c6f | ||
| 
						 | 
					3cd7a2e6d6 | ||
| 
						 | 
					0d4904f05d | ||
| 
						 | 
					0b539a5977 | ||
| 
						 | 
					395fdc9d61 | ||
| 
						 | 
					3b3d7eff3c | ||
| 
						 | 
					763c05313b | ||
| 
						 | 
					9f80df3fcb | ||
| 
						 | 
					f170ee9e59 | ||
| 
						 | 
					339218508d | ||
| 
						 | 
					f0c710b245 | ||
| 
						 | 
					e66337a1db | ||
| 
						 | 
					e087bccd33 | ||
| 
						 | 
					8017774bf3 | ||
| 
						 | 
					5f4d08ada5 | ||
| 
						 | 
					1ad450d753 | ||
| 
						 | 
					f1692b3436 | ||
| 
						 | 
					2a9c56d9e3 | ||
| 
						 | 
					39a5688464 | ||
| 
						 | 
					e4e6173eff | ||
| 
						 | 
					cf7334eb7d | ||
| 
						 | 
					fdb96e91f1 | ||
| 
						 | 
					844c5027ea | ||
| 
						 | 
					8d0e485120 | ||
| 
						 | 
					281ee1a853 | ||
| 
						 | 
					a0bf29e600 | ||
| 
						 | 
					d66c430e46 | ||
| 
						 | 
					f511a52705 | ||
| 
						 | 
					a674e410e0 | ||
| 
						 | 
					fc63445c80 | ||
| 
						 | 
					0c15655574 | ||
| 
						 | 
					328b6d1cc6 | ||
| 
						 | 
					b67d663a38 | ||
| 
						 | 
					dd6c5c9eea | ||
| 
						 | 
					2e87e64bd1 | ||
| 
						 | 
					08c210d833 | ||
| 
						 | 
					a9403013df | ||
| 
						 | 
					05477c1a03 | ||
| 
						 | 
					fcb6198a82 | ||
| 
						 | 
					410d0bc125 | ||
| 
						 | 
					abc62b9348 | ||
| 
						 | 
					6fbf33c8f4 | ||
| 
						 | 
					0ceb750dc7 | ||
| 
						 | 
					a48c22d14f | ||
| 
						 | 
					1521199e44 | ||
| 
						 | 
					2910be82a4 | ||
| 
						 | 
					07fdb087dc | ||
| 
						 | 
					58150f5dcd | ||
| 
						 | 
					e7a6ff39f9 | ||
| 
						 | 
					b086afb272 | ||
| 
						 | 
					7decf76883 | ||
| 
						 | 
					d81369d63a | ||
| 
						 | 
					c0fbe8237b | ||
| 
						 | 
					58923b2846 | ||
| 
						 | 
					d9f9477a52 | ||
| 
						 | 
					966c744992 | ||
| 
						 | 
					ddc91ce7c3 | ||
| 
						 | 
					7e5107d90f | ||
| 
						 | 
					c1668c9bdb | ||
| 
						 | 
					7ddc2ccf1a | ||
| 
						 | 
					3cd85fb395 | ||
| 
						 | 
					b805ea9bf6 | ||
| 
						 | 
					edd76f595a | ||
| 
						 | 
					c131b63852 | ||
| 
						 | 
					a70f377388 | ||
| 
						 | 
					50f76446a9 | ||
| 
						 | 
					15ce3ec41a | ||
| 
						 | 
					b7b01999d9 | ||
| 
						 | 
					956114fc42 | ||
| 
						 | 
					bb3a986859 | ||
| 
						 | 
					50fefc3bb0 | ||
| 
						 | 
					c5eea2e7c5 | ||
| 
						 | 
					19555a98ed | ||
| 
						 | 
					9021f006f0 | ||
| 
						 | 
					2d5f14388e | ||
| 
						 | 
					ab47bf6451 | ||
| 
						 | 
					d8bd45c2bd | ||
| 
						 | 
					de692d3dcc | ||
| 
						 | 
					4adb525513 | ||
| 
						 | 
					72235a5f72 | ||
| 
						 | 
					b6508cccec | ||
| 
						 | 
					d9e7cf659e | ||
| 
						 | 
					cf500cd817 | ||
| 
						 | 
					f1338aca84 | ||
| 
						 | 
					284de4ac5d | ||
| 
						 | 
					19c4345162 | ||
| 
						 | 
					d5d38b3331 | ||
| 
						 | 
					bd04638d27 | ||
| 
						 | 
					2f0d0e7c7a | ||
| 
						 | 
					0b531e9fbc | ||
| 
						 | 
					e3ebd582ec | ||
| 
						 | 
					95ef046d0a | ||
| 
						 | 
					21fd46d66b | ||
| 
						 | 
					b3a801df11 | ||
| 
						 | 
					a6d22e3b22 | ||
| 
						 | 
					0415c050a5 | ||
| 
						 | 
					014396cf11 | ||
| 
						 | 
					70488f9c56 | ||
| 
						 | 
					0052ab7148 | ||
| 
						 | 
					f725040dd5 | ||
| 
						 | 
					f131863642 | ||
| 
						 | 
					8791047005 | ||
| 
						 | 
					836a293f16 | ||
| 
						 | 
					1177cc3f29 | ||
| 
						 | 
					269847d19d | ||
| 
						 | 
					df22f68088 | ||
| 
						 | 
					d83d8552b8 | ||
| 
						 | 
					365aa69afd | ||
| 
						 | 
					578c338d40 | ||
| 
						 | 
					389518e1b8 | ||
| 
						 | 
					d42ff227f1 | ||
| 
						 | 
					737e9e48ca | ||
| 
						 | 
					f96d91cb6c | ||
| 
						 | 
					85503655ab | ||
| 
						 | 
					8d811760a9 | ||
| 
						 | 
					4e0de22375 | ||
| 
						 | 
					7cfbf100eb | ||
| 
						 | 
					edbe026b49 | ||
| 
						 | 
					1e967eceef | ||
| 
						 | 
					7d20db93d3 | ||
| 
						 | 
					fb05a42d2e | ||
| 
						 | 
					9490ae1440 | ||
| 
						 | 
					e932be0fb3 | ||
| 
						 | 
					70b49980cb | ||
| 
						 | 
					5e6ee5fd48 | ||
| 
						 | 
					a2d872a9f0 | ||
| 
						 | 
					e8bcde31b7 | ||
| 
						 | 
					40cda9220a | ||
| 
						 | 
					a494683bc8 | ||
| 
						 | 
					2ba6a85eca | ||
| 
						 | 
					0e58158a59 | ||
| 
						 | 
					af740592c9 | ||
| 
						 | 
					c9452c9f31 | ||
| 
						 | 
					494a1603e4 | ||
| 
						 | 
					5207e111e1 | ||
| 
						 | 
					06995fb080 | ||
| 
						 | 
					1f5cafc2d1 | ||
| 
						 | 
					f190de39a6 | ||
| 
						 | 
					272ab746a6 | ||
| 
						 | 
					fce0bf6e59 | ||
| 
						 | 
					5957786b2c | ||
| 
						 | 
					4f3f4e23e4 | ||
| 
						 | 
					5f5096e1d4 | ||
| 
						 | 
					4b35aef728 | ||
| 
						 | 
					6a0ed51f5e | ||
| 
						 | 
					67360e93b8 | ||
| 
						 | 
					f18f4c69f2 | ||
| 
						 | 
					41435578d2 | ||
| 
						 | 
					7f33ae3bee | ||
| 
						 | 
					645135bf56 | ||
| 
						 | 
					eb9005ad74 | ||
| 
						 | 
					14089f8c6a | ||
| 
						 | 
					49094120d9 | ||
| 
						 | 
					fe4111a9f5 | ||
| 
						 | 
					61613bee98 | ||
| 
						 | 
					3ce967d8e5 | ||
| 
						 | 
					aad9afad59 | ||
| 
						 | 
					8cbbd022fc | ||
| 
						 | 
					9b23cd6d19 | ||
| 
						 | 
					d94f241d3c | ||
| 
						 | 
					bb7c11adf1 | ||
| 
						 | 
					5c295254bf | ||
| 
						 | 
					236e8cc95c | ||
| 
						 | 
					421973e0d9 | ||
| 
						 | 
					e2a5af1cf7 | ||
| 
						 | 
					65aa7b1084 | ||
| 
						 | 
					f8c8330258 | ||
| 
						 | 
					f5411ac9ab | ||
| 
						 | 
					f31debc09c | ||
| 
						 | 
					6654d7a919 | ||
| 
						 | 
					44743b5f6f | ||
| 
						 | 
					f80276584f | ||
| 
						 | 
					cda41debfc | ||
| 
						 | 
					2c50d01c26 | ||
| 
						 | 
					01ebb6576d | ||
| 
						 | 
					aaca0b6f76 | ||
| 
						 | 
					84e1f3649f | ||
| 
						 | 
					0ab2cfaf8b | ||
| 
						 | 
					0545d6f083 | ||
| 
						 | 
					d804228956 | ||
| 
						 | 
					f7d70df2ba | ||
| 
						 | 
					4daef52991 | ||
| 
						 | 
					a329547682 | ||
| 
						 | 
					f02af8d481 | ||
| 
						 | 
					dd6c067832 | ||
| 
						 | 
					dbc435506c | ||
| 
						 | 
					f00e289014 | ||
| 
						 | 
					1dffaba266 | ||
| 
						 | 
					958a2f4274 | ||
| 
						 | 
					21718a69d3 | ||
| 
						 | 
					c650ae0e19 | ||
| 
						 | 
					9ed435d04a | ||
| 
						 | 
					fed6a0c24e | ||
| 
						 | 
					44b9a8e7ed | ||
| 
						 | 
					c16757b03a | ||
| 
						 | 
					5d0dde5c15 | ||
| 
						 | 
					f60356e8c7 | ||
| 
						 | 
					cdf8f78962 | ||
| 
						 | 
					4539d236df | ||
| 
						 | 
					8718ac0c4b | ||
| 
						 | 
					94787d537a | ||
| 
						 | 
					bb8cff967e | ||
| 
						 | 
					eca57beec1 | ||
| 
						 | 
					58b4eb04f9 | ||
| 
						 | 
					a9d46297c4 | ||
| 
						 | 
					e9edecf34a | ||
| 
						 | 
					71a5f0e84e | ||
| 
						 | 
					fb22ee94d9 | ||
| 
						 | 
					f03904ebce | ||
| 
						 | 
					6cc9f49d97 | ||
| 
						 | 
					f5f0680ec7 | ||
| 
						 | 
					60e04b9065 | ||
| 
						 | 
					025da92450 | ||
| 
						 | 
					5de4aa091b | ||
| 
						 | 
					1fe8235a85 | ||
| 
						 | 
					0ab14399ae | ||
| 
						 | 
					15dded712c | ||
| 
						 | 
					8837f7e6e8 | ||
| 
						 | 
					e8defd821a | ||
| 
						 | 
					5d6effeff5 | ||
| 
						 | 
					427c278012 | ||
| 
						 | 
					a78a09f594 | ||
| 
						 | 
					59fd48cfe2 | ||
| 
						 | 
					cc78ab4855 | ||
| 
						 | 
					063562261e | ||
| 
						 | 
					70619dd0b7 | ||
| 
						 | 
					63031fb278 | ||
| 
						 | 
					7f924a56b3 | ||
| 
						 | 
					114f2a1465 | ||
| 
						 | 
					5d2777634a | ||
| 
						 | 
					2d5b4a0003 | ||
| 
						 | 
					adfa1704e2 | ||
| 
						 | 
					ab3fd6be8f | ||
| 
						 | 
					47702d075e | ||
| 
						 | 
					341f000b9c | ||
| 
						 | 
					0deea53931 | ||
| 
						 | 
					8b3d792bec | ||
| 
						 | 
					b82c48b66f | ||
| 
						 | 
					fa91516dce | ||
| 
						 | 
					4954b44d8e | ||
| 
						 | 
					d132e51ac7 | ||
| 
						 | 
					243b6ae985 | ||
| 
						 | 
					8780ba3626 | ||
| 
						 | 
					694194be2f | ||
| 
						 | 
					c7f61f8b80 | ||
| 
						 | 
					3a7c7fe4e8 | ||
| 
						 | 
					668967a719 | ||
| 
						 | 
					d15c14ab93 | ||
| 
						 | 
					52b81608a1 | ||
| 
						 | 
					048f754d83 | ||
| 
						 | 
					748cb28017 | ||
| 
						 | 
					d5ef3a3f8c | ||
| 
						 | 
					e768e285ce | ||
| 
						 | 
					a102d775b2 | ||
| 
						 | 
					65e82b03ad | ||
| 
						 | 
					80a636bd14 | ||
| 
						 | 
					a4c57ee363 | ||
| 
						 | 
					94bf54e7e0 | ||
| 
						 | 
					b18ce5ade0 | ||
| 
						 | 
					99793bb2c4 | ||
| 
						 | 
					093936e594 | ||
| 
						 | 
					036a37e351 | ||
| 
						 | 
					d904df57ca | ||
| 
						 | 
					d507979ec1 | ||
| 
						 | 
					9bbcfead67 | ||
| 
						 | 
					81036894c0 | ||
| 
						 | 
					9044adecb5 | ||
| 
						 | 
					9190fdd42c | ||
| 
						 | 
					eab35605e4 | ||
| 
						 | 
					28b65a7e7b | ||
| 
						 | 
					5d1d2308b4 | ||
| 
						 | 
					cf5952f508 | ||
| 
						 | 
					3b0d7bc4ad | ||
| 
						 | 
					22f8ab110e | ||
| 
						 | 
					8f2d085d28 | ||
| 
						 | 
					c3d7f5b28b | ||
| 
						 | 
					45e6000619 | ||
| 
						 | 
					7cd00a6760 | ||
| 
						 | 
					cecc53fed3 | ||
| 
						 | 
					58c2c70146 | ||
| 
						 | 
					ad9f488df6 | ||
| 
						 | 
					b19799bc72 | ||
| 
						 | 
					1209b9b86e | ||
| 
						 | 
					da957a3caf | ||
| 
						 | 
					1bfd0f0149 | ||
| 
						 | 
					3ff48b8559 | ||
| 
						 | 
					6ba1eda96f | ||
| 
						 | 
					dddfe07867 | ||
| 
						 | 
					054f67eeb8 | ||
| 
						 | 
					65c59c8c30 | ||
| 
						 | 
					24925a1739 | ||
| 
						 | 
					c49b40ee95 | ||
| 
						 | 
					c06db30a65 | ||
| 
						 | 
					0ed6fef49b | ||
| 
						 | 
					9bad11ec79 | ||
| 
						 | 
					74cdcde449 | ||
| 
						 | 
					1613461504 | ||
| 
						 | 
					0043f3558c | ||
| 
						 | 
					a9c4b8dd1a | ||
| 
						 | 
					5d00edc896 | ||
| 
						 | 
					3bad815982 | ||
| 
						 | 
					08cc7587ab | ||
| 
						 | 
					458c0db3a8 | ||
| 
						 | 
					a995333081 | ||
| 
						 | 
					8eb608a839 | ||
| 
						 | 
					1041c9f9fc | ||
| 
						 | 
					1564742b76 | ||
| 
						 | 
					b887fd153d | ||
| 
						 | 
					d083674fb1 | ||
| 
						 | 
					ed7a945261 | ||
| 
						 | 
					ef5ffa939f | ||
| 
						 | 
					d842ccb287 | ||
| 
						 | 
					233893f122 | ||
| 
						 | 
					2febdfc363 | ||
| 
						 | 
					2c971a2598 | ||
| 
						 | 
					b4c3c20e5e | ||
| 
						 | 
					b6fbb012ad | ||
| 
						 | 
					7f4db5a731 | ||
| 
						 | 
					4dfdfa0b7d | ||
| 
						 | 
					dbf659c575 | ||
| 
						 | 
					cd8e04471c | ||
| 
						 | 
					93de1e4903 | ||
| 
						 | 
					2df43c9e2b | ||
| 
						 | 
					5ace44493a | ||
| 
						 | 
					a57ba3d81c | ||
| 
						 | 
					6298112531 | ||
| 
						 | 
					25afca55f6 | ||
| 
						 | 
					52a16c917f | ||
| 
						 | 
					eef9a60037 | ||
| 
						 | 
					e158b5ccf6 | ||
| 
						 | 
					f03d7efb5e | ||
| 
						 | 
					da7b1fb014 | ||
| 
						 | 
					62378d063e | ||
| 
						 | 
					5fac282ee0 | ||
| 
						 | 
					6eff873a07 | ||
| 
						 | 
					1fb306c9d3 | ||
| 
						 | 
					c064b3896a | ||
| 
						 | 
					8400d1e60e | ||
| 
						 | 
					5530e74382 | ||
| 
						 | 
					47a38a8977 | ||
| 
						 | 
					47883a94a6 | ||
| 
						 | 
					ca19bbd366 | ||
| 
						 | 
					7595808d26 | ||
| 
						 | 
					37d22a144a | ||
| 
						 | 
					dc697a6862 | ||
| 
						 | 
					5398bac533 | ||
| 
						 | 
					9984a168cb | ||
| 
						 | 
					286f3713b0 | ||
| 
						 | 
					ff9be30f86 | ||
| 
						 | 
					34cebe8c0c | ||
| 
						 | 
					200cd5972a | ||
| 
						 | 
					808d1af578 | ||
| 
						 | 
					d0995665a3 | ||
| 
						 | 
					6132af8ecb | ||
| 
						 | 
					2e9c4914a8 | ||
| 
						 | 
					bf0d513e5b | ||
| 
						 | 
					2be435ff32 | ||
| 
						 | 
					20ba820253 | ||
| 
						 | 
					44fd332965 | ||
| 
						 | 
					0453d656d6 | ||
| 
						 | 
					cb7e38577d | ||
| 
						 | 
					4fa59ea04e | ||
| 
						 | 
					bc2ed602e7 | ||
| 
						 | 
					a1c4d159dd | ||
| 
						 | 
					598f29b78e | ||
| 
						 | 
					f61f2d6e5e | ||
| 
						 | 
					f38df4df11 | ||
| 
						 | 
					1e34ccbe2e | ||
| 
						 | 
					2a8746f6b0 | ||
| 
						 | 
					554e083f3d | ||
| 
						 | 
					5d881a8b0f | ||
| 
						 | 
					3d81641139 | ||
| 
						 | 
					c25b4ba099 | ||
| 
						 | 
					fd64c20807 | ||
| 
						 | 
					80f1034dd6 | ||
| 
						 | 
					15b841da06 | ||
| 
						 | 
					902c08e9c9 | ||
| 
						 | 
					ea652c023e | ||
| 
						 | 
					3c79bb77db | ||
| 
						 | 
					d8dbb85946 | ||
| 
						 | 
					20702d26ec | ||
| 
						 | 
					7d7e9501fa | ||
| 
						 | 
					efef76d9cf | ||
| 
						 | 
					e1e1ee31f0 | ||
| 
						 | 
					142ca58d38 | ||
| 
						 | 
					3b01bf7bda | ||
| 
						 | 
					30416f54d1 | ||
| 
						 | 
					f21ef0d2e9 | ||
| 
						 | 
					39ced21a6f | ||
| 
						 | 
					0f24417cb3 | ||
| 
						 | 
					f84a87f2a2 | ||
| 
						 | 
					dada57e5c4 | ||
| 
						 | 
					d437d6fde9 | ||
| 
						 | 
					69b11575e3 | ||
| 
						 | 
					72e1a1b2e9 | ||
| 
						 | 
					8ba573d196 | ||
| 
						 | 
					4593231049 | ||
| 
						 | 
					de9eac760b | ||
| 
						 | 
					12ad8d52ae | ||
| 
						 | 
					22f9a3b467 | ||
| 
						 | 
					b64f0ba83f | ||
| 
						 | 
					f73a494407 | ||
| 
						 | 
					46ee74ed16 | ||
| 
						 | 
					806b746fc0 | ||
| 
						 | 
					cc820e97c6 | ||
| 
						 | 
					283b04df73 | ||
| 
						 | 
					6420d1239f | ||
| 
						 | 
					04771e5a4a | ||
| 
						 | 
					3d9608faa0 | ||
| 
						 | 
					b7b4ae4262 | ||
| 
						 | 
					b73b078705 | ||
| 
						 | 
					887fa8649b | ||
| 
						 | 
					f6172d7273 | ||
| 
						 | 
					c70681712d | ||
| 
						 | 
					80ca6de531 | ||
| 
						 | 
					c6c395cd0f | ||
| 
						 | 
					4831064623 | ||
| 
						 | 
					d4660a23c0 | ||
| 
						 | 
					e184a1b9e6 | ||
| 
						 | 
					f8662c9bc2 | ||
| 
						 | 
					463df9e4ba | ||
| 
						 | 
					c768581829 | ||
| 
						 | 
					754f7a7891 | ||
| 
						 | 
					21450a08c2 | ||
| 
						 | 
					c355b25bb1 | ||
| 
						 | 
					1fe3d80838 | ||
| 
						 | 
					930e16b64a | ||
| 
						 | 
					2077a70d03 | ||
| 
						 | 
					cbdb8bd9b9 | ||
| 
						 | 
					5dcb417676 | ||
| 
						 | 
					71bc993e3d | ||
| 
						 | 
					c2812896f8 | ||
| 
						 | 
					d43227ede4 | ||
| 
						 | 
					8554ae38ed | ||
| 
						 | 
					da656caf1e | ||
| 
						 | 
					51fc853228 | ||
| 
						 | 
					7a30cb9de7 | ||
| 
						 | 
					1b475cf9f3 | ||
| 
						 | 
					719b690451 | ||
| 
						 | 
					3cdc523dec | ||
| 
						 | 
					eb49127b9e | ||
| 
						 | 
					d07172a528 | ||
| 
						 | 
					79637097ba | ||
| 
						 | 
					1259341095 | ||
| 
						 | 
					5d3bc95ac5 | ||
| 
						 | 
					de25232a73 | ||
| 
						 | 
					95769de464 | ||
| 
						 | 
					52a168b961 | ||
| 
						 | 
					b3b00b6700 | ||
| 
						 | 
					8e8cda132c | ||
| 
						 | 
					6459ccb185 | ||
| 
						 | 
					548f83c3ad | ||
| 
						 | 
					555e0de9e4 | ||
| 
						 | 
					cc4bce283f | ||
| 
						 | 
					8189a34d14 | ||
| 
						 | 
					5d88ad554f | ||
| 
						 | 
					2cc50a2b65 | ||
| 
						 | 
					33670a5bd0 | ||
| 
						 | 
					64f8a222cb | ||
| 
						 | 
					d9a9695fe0 | ||
| 
						 | 
					8e6c4e1aca | ||
| 
						 | 
					490fbfc13e | ||
| 
						 | 
					4b45973361 | ||
| 
						 | 
					4c27e08e3d | ||
| 
						 | 
					94eb80597b | ||
| 
						 | 
					d610eb15d8 | ||
| 
						 | 
					4f53cd1704 | ||
| 
						 | 
					d795fac37a | ||
| 
						 | 
					ce6b71a58b | ||
| 
						 | 
					09f74a9af8 | ||
| 
						 | 
					552a49a680 | ||
| 
						 | 
					97741398fb | ||
| 
						 | 
					f8b225e70e | ||
| 
						 | 
					57b4eda014 | ||
| 
						 | 
					fc3a181779 | ||
| 
						 | 
					9541ea6a9f | ||
| 
						 | 
					f716f6060e | ||
| 
						 | 
					dc0cca8c83 | ||
| 
						 | 
					4f303de00c | ||
| 
						 | 
					05aa26e619 | ||
| 
						 | 
					1c4b831922 | ||
| 
						 | 
					6613ae57b0 | ||
| 
						 | 
					b6552aff75 | ||
| 
						 | 
					3c98fae4f2 | ||
| 
						 | 
					2028e4c8ad | ||
| 
						 | 
					5c7feba77b | ||
| 
						 | 
					23f2677052 | ||
| 
						 | 
					6b67511748 | ||
| 
						 | 
					36e0feea43 | ||
| 
						 | 
					69392f67e8 | ||
| 
						 | 
					e7d130cc11 | ||
| 
						 | 
					6e3ba3ca45 | ||
| 
						 | 
					0f54cf83f4 | ||
| 
						 | 
					fb209cdfde | ||
| 
						 | 
					431c53efcf | ||
| 
						 | 
					79ad0ff56b | ||
| 
						 | 
					d4dad58b0d | ||
| 
						 | 
					baff032e3b | ||
| 
						 | 
					26309f51e3 | ||
| 
						 | 
					f8f53a6bd9 | ||
| 
						 | 
					ac3667c765 | ||
| 
						 | 
					96180e7555 | ||
| 
						 | 
					024619676b | ||
| 
						 | 
					5aa0f547cf | ||
| 
						 | 
					b1ce6ffcc7 | ||
| 
						 | 
					f01936ca4f | ||
| 
						 | 
					70fdb1042f | ||
| 
						 | 
					58cfc0d0cf | ||
| 
						 | 
					4eff3b6a24 | ||
| 
						 | 
					e5f69f0815 | ||
| 
						 | 
					ef7b51beb7 | ||
| 
						 | 
					8494ac8f3d | ||
| 
						 | 
					8dea519235 | ||
| 
						 | 
					0712e98904 | ||
| 
						 | 
					c7ccddbcb9 | ||
| 
						 | 
					efd3e8067b | ||
| 
						 | 
					c6f7b7f35f | ||
| 
						 | 
					f3dd1603db | ||
| 
						 | 
					be7688a4df | ||
| 
						 | 
					8e2f11389d | ||
| 
						 | 
					346454c21b | ||
| 
						 | 
					c822870cf8 | ||
| 
						 | 
					9666cf680e | ||
| 
						 | 
					a88622c1be | ||
| 
						 | 
					c3fbc36ce7 | ||
| 
						 | 
					f174d7dd39 | ||
| 
						 | 
					38a8721a91 | ||
| 
						 | 
					8aedf26a87 | ||
| 
						 | 
					b2ff9240ac | ||
| 
						 | 
					7a3c61b744 | ||
| 
						 | 
					8dd1df71cc | ||
| 
						 | 
					b59b0f0386 | ||
| 
						 | 
					f59f484c01 | ||
| 
						 | 
					a44ea0ddf0 | ||
| 
						 | 
					2214507db0 | ||
| 
						 | 
					a138425417 | ||
| 
						 | 
					99c47dd50a | ||
| 
						 | 
					594b83e7a6 | ||
| 
						 | 
					76d0ef0851 | ||
| 
						 | 
					549ebbb462 | ||
| 
						 | 
					341656ddb9 | ||
| 
						 | 
					5a3c3b4876 | ||
| 
						 | 
					375b8dceb7 | ||
| 
						 | 
					f37546e173 | ||
| 
						 | 
					239d53426a | ||
| 
						 | 
					3ccac629bc | ||
| 
						 | 
					e8e6feeb0f | ||
| 
						 | 
					c22705a593 | ||
| 
						 | 
					ec7889dfa8 | ||
| 
						 | 
					563d59526f | ||
| 
						 | 
					0ffd5de6fc | ||
| 
						 | 
					5014f83b86 | ||
| 
						 | 
					953a9b1768 | ||
| 
						 | 
					c641b61b26 | ||
| 
						 | 
					d87e507865 | ||
| 
						 | 
					ec1f9841b2 | ||
| 
						 | 
					84b0f29d87 | ||
| 
						 | 
					b23e05dbc5 | ||
| 
						 | 
					37978b4fe5 | ||
| 
						 | 
					ef15e55947 | ||
| 
						 | 
					c282dd086f | ||
| 
						 | 
					aac9f089d9 | ||
| 
						 | 
					adce8f52e8 | ||
| 
						 | 
					20b64c8900 | ||
| 
						 | 
					66d781a226 | ||
| 
						 | 
					867ec010ab | ||
| 
						 | 
					ce0c6da9fa | ||
| 
						 | 
					e22c5ea800 | ||
| 
						 | 
					16bfb1727f | ||
| 
						 | 
					f1f14040b8 | ||
| 
						 | 
					9cb328966c | ||
| 
						 | 
					0c76890572 | ||
| 
						 | 
					18fc42e63b | ||
| 
						 | 
					fc8d9df516 | ||
| 
						 | 
					afdf8a78c0 | ||
| 
						 | 
					0b3ae1f972 | ||
| 
						 | 
					6a5ee72722 | ||
| 
						 | 
					c6ec8bc0d9 | ||
| 
						 | 
					51cfd996eb | ||
| 
						 | 
					6d0e4bed4b | ||
| 
						 | 
					05247dc4a4 | ||
| 
						 | 
					43011f3bfa | ||
| 
						 | 
					38854bd876 | ||
| 
						 | 
					a9726bd52f | ||
| 
						 | 
					4216c9e8f7 | ||
| 
						 | 
					a8d670fc0d | ||
| 
						 | 
					cbacc779fc | ||
| 
						 | 
					896778cead | ||
| 
						 | 
					04b0c62bf9 | ||
| 
						 | 
					63a779baa8 | ||
| 
						 | 
					2d1a776db7 | ||
| 
						 | 
					3d2df3ba93 | ||
| 
						 | 
					eb6238781d | ||
| 
						 | 
					6140a3c26b | ||
| 
						 | 
					6eaf2d67b7 | ||
| 
						 | 
					35b34c43ed | ||
| 
						 | 
					f67a9d2de1 | ||
| 
						 | 
					05acf28e0d | ||
| 
						 | 
					5698bec621 | ||
| 
						 | 
					3ec495225f | ||
| 
						 | 
					fee9baca89 | ||
| 
						 | 
					bec26ce754 | ||
| 
						 | 
					343d7df57c | ||
| 
						 | 
					dca6a4bbd5 | ||
| 
						 | 
					a32b95544b | ||
| 
						 | 
					2422e0b481 | ||
| 
						 | 
					3441bd0e7c | ||
| 
						 | 
					05ced9fbc4 | ||
| 
						 | 
					b7c3df455e | ||
| 
						 | 
					d7affad059 | ||
| 
						 | 
					34c3da9117 | ||
| 
						 | 
					a22d3b2390 | ||
| 
						 | 
					7c09bdc6e0 | ||
| 
						 | 
					bc291141b1 | ||
| 
						 | 
					c1b089d1c3 | ||
| 
						 | 
					1271f97b66 | ||
| 
						 | 
					582c77805c | ||
| 
						 | 
					9eb5f65b8f | ||
| 
						 | 
					00f01a9889 | ||
| 
						 | 
					671edc33e1 | ||
| 
						 | 
					1253357a39 | ||
| 
						 | 
					6df31eb7f5 | ||
| 
						 | 
					9299a83b17 | ||
| 
						 | 
					a6614abd24 | ||
| 
						 | 
					4c9d99040c | ||
| 
						 | 
					6ef8adc863 | ||
| 
						 | 
					a00300f88a | ||
| 
						 | 
					274393ac64 | ||
| 
						 | 
					1339b9422d | ||
| 
						 | 
					ed9e196bf6 | ||
| 
						 | 
					bba5376a36 | ||
| 
						 | 
					df3575217a | ||
| 
						 | 
					c504506455 | ||
| 
						 | 
					2a28772312 | ||
| 
						 | 
					d04c6dd3ac | ||
| 
						 | 
					e48daffad9 | ||
| 
						 | 
					58642286c9 | ||
| 
						 | 
					6d62ae226a | ||
| 
						 | 
					14f6f9ec94 | ||
| 
						 | 
					e10f447b5b | ||
| 
						 | 
					1d1f61613c | ||
| 
						 | 
					b85c1a8861 | ||
| 
						 | 
					430956d304 | ||
| 
						 | 
					c0449a3ed2 | ||
| 
						 | 
					18ad01533b | ||
| 
						 | 
					288049dc95 | ||
| 
						 | 
					573c8f3b13 | ||
| 
						 | 
					e85d7a7be5 | ||
| 
						 | 
					7015215f26 | ||
| 
						 | 
					ffa5472b31 | ||
| 
						 | 
					e00f0b4cf1 | ||
| 
						 | 
					dc5c220e8f | ||
| 
						 | 
					933d49b0b0 | ||
| 
						 | 
					9af85f5a7e | ||
| 
						 | 
					7ec52145e8 | ||
| 
						 | 
					aa6112482d | ||
| 
						 | 
					d035cdcff9 | ||
| 
						 | 
					7ad3ddef2a | ||
| 
						 | 
					38e08bb91f | ||
| 
						 | 
					252a21e2ae | ||
| 
						 | 
					ba7db3edda | ||
| 
						 | 
					f64b061a28 | ||
| 
						 | 
					f323ced4ca | ||
| 
						 | 
					c06ec7c6ba | ||
| 
						 | 
					835f9aad91 | ||
| 
						 | 
					a4ec9f8b44 | ||
| 
						 | 
					47c33d0344 | ||
| 
						 | 
					f500c7abcb | ||
| 
						 | 
					95dd7b5323 | ||
| 
						 | 
					65c950e1a4 | ||
| 
						 | 
					e484f32b1a | ||
| 
						 | 
					bc396e7a90 | ||
| 
						 | 
					ce182f43db | ||
| 
						 | 
					9382d52d55 | ||
| 
						 | 
					dd156d0689 | ||
| 
						 | 
					ac9f6e3a41 | ||
| 
						 | 
					1e7534b9d7 | ||
| 
						 | 
					72d800ed10 | ||
| 
						 | 
					54143ae6d4 | ||
| 
						 | 
					8ef5daa807 | ||
| 
						 | 
					fa47ea4196 | ||
| 
						 | 
					683592fa86 | ||
| 
						 | 
					477a04760c | ||
| 
						 | 
					b4a62bfa30 | ||
| 
						 | 
					10eec7d48c | ||
| 
						 | 
					b8cc10ab5d | ||
| 
						 | 
					be0df07dfb | ||
| 
						 | 
					5244097e2d | ||
| 
						 | 
					1ba4ab2bd1 | ||
| 
						 | 
					2e855f8983 | ||
| 
						 | 
					51b4a9e350 | ||
| 
						 | 
					237d28cf83 | ||
| 
						 | 
					5723fd112f | ||
| 
						 | 
					73b89c554e | ||
| 
						 | 
					950d024a11 | ||
| 
						 | 
					815a3be48b | ||
| 
						 | 
					bfa6e52470 | ||
| 
						 | 
					ec654d2355 | ||
| 
						 | 
					dfb4883c93 | ||
| 
						 | 
					4bf1f579f5 | ||
| 
						 | 
					b9994e52eb | ||
| 
						 | 
					80d63dbb7c | ||
| 
						 | 
					6b817d4563 | ||
| 
						 | 
					1081d98bf9 | ||
| 
						 | 
					91d82da497 | ||
| 
						 | 
					0ca46774ac | ||
| 
						 | 
					f0d6d46766 | ||
| 
						 | 
					4a81205e04 | ||
| 
						 | 
					0ac37981cb | ||
| 
						 | 
					400c31d031 | ||
| 
						 | 
					54b38086e5 | ||
| 
						 | 
					e0deca33d0 | ||
| 
						 | 
					0b2b8b960b | ||
| 
						 | 
					9b173dcd71 | ||
| 
						 | 
					a3361806ab | ||
| 
						 | 
					5bdfdfefbe | ||
| 
						 | 
					ee38cccad8 | ||
| 
						 | 
					f82ff90f06 | ||
| 
						 | 
					9826b8ae69 | ||
| 
						 | 
					c7849a43e1 | ||
| 
						 | 
					874bd093cb | ||
| 
						 | 
					2cbc04e04d | ||
| 
						 | 
					143eac092c | ||
| 
						 | 
					d74dfb1f5c | ||
| 
						 | 
					d42cf6daeb | ||
| 
						 | 
					75191e7187 | ||
| 
						 | 
					b9b2cd278b | ||
| 
						 | 
					55dea4ee9d | ||
| 
						 | 
					41c951811e | ||
| 
						 | 
					72e7eb6777 | ||
| 
						 | 
					9a733a57e7 | ||
| 
						 | 
					45e8bb03e4 | ||
| 
						 | 
					54e189616c | ||
| 
						 | 
					c25947d544 | ||
| 
						 | 
					80af3d6ada | ||
| 
						 | 
					93d29a9733 | ||
| 
						 | 
					ccc2142b45 | ||
| 
						 | 
					28c153a0a2 | ||
| 
						 | 
					b8e6287774 | ||
| 
						 | 
					8d393ff137 | ||
| 
						 | 
					5c09788ec4 | ||
| 
						 | 
					5e970cdca4 | ||
| 
						 | 
					28cadc5e06 | ||
| 
						 | 
					3cdfa4051d | ||
| 
						 | 
					28a9df669d | ||
| 
						 | 
					5f94474330 | ||
| 
						 | 
					42497028c4 | ||
| 
						 | 
					57b16e3ac2 | ||
| 
						 | 
					bd9af86de1 | ||
| 
						 | 
					2ce9fb9760 | ||
| 
						 | 
					ae66c6f0b4 | ||
| 
						 | 
					fe5f34231b | ||
| 
						 | 
					d694ee8651 | ||
| 
						 | 
					89c0b67b8c | ||
| 
						 | 
					0e9fbf3c5a | ||
| 
						 | 
					ec40807c54 | ||
| 
						 | 
					971a85a6f8 | ||
| 
						 | 
					8e36695bc4 | ||
| 
						 | 
					2e2e056198 | ||
| 
						 | 
					e0d4115ed7 | ||
| 
						 | 
					6ff3f5d1ff | ||
| 
						 | 
					a2738e8599 | ||
| 
						 | 
					468edfa6cc | ||
| 
						 | 
					c83f2f98bd | ||
| 
						 | 
					06f860c8ea | ||
| 
						 | 
					24e574ee09 | ||
| 
						 | 
					803b67af9f | ||
| 
						 | 
					f803c6c0bf | ||
| 
						 | 
					254feecf21 | ||
| 
						 | 
					c6b6855131 | ||
| 
						 | 
					9bc45563ee | ||
| 
						 | 
					882ac74a0c | ||
| 
						 | 
					d883a870e1 | ||
| 
						 | 
					f2c6e3f65b | ||
| 
						 | 
					1ff3dcf138 | ||
| 
						 | 
					9b564431b0 | ||
| 
						 | 
					65058db89f | ||
| 
						 | 
					487d2a9221 | ||
| 
						 | 
					6151debeab | ||
| 
						 | 
					e05ef230a7 | ||
| 
						 | 
					685755fe64 | ||
| 
						 | 
					465ece5d25 | ||
| 
						 | 
					10d1361a2c | ||
| 
						 | 
					34617270a5 | ||
| 
						 | 
					29aa370aa6 | ||
| 
						 | 
					1ef7fd3659 | ||
| 
						 | 
					6650072fe6 | ||
| 
						 | 
					951bd3a517 | ||
| 
						 | 
					2e3ddd3a61 | ||
| 
						 | 
					dd628514c6 | ||
| 
						 | 
					c74686a197 | ||
| 
						 | 
					c42dbbfec8 | ||
| 
						 | 
					ea3678f8c7 | ||
| 
						 | 
					51099bf148 | ||
| 
						 | 
					924e0261f9 | ||
| 
						 | 
					dc5eda7ebb | ||
| 
						 | 
					aec6636205 | ||
| 
						 | 
					a18c3ff07d | ||
| 
						 | 
					64928b28bc | ||
| 
						 | 
					0bbaa51945 | ||
| 
						 | 
					561803c0a7 | ||
| 
						 | 
					1a5279bd6e | ||
| 
						 | 
					534ddf01db | ||
| 
						 | 
					937d5b5472 | ||
| 
						 | 
					03a407d4df | ||
| 
						 | 
					3508e1e6d5 | ||
| 
						 | 
					145b1f4fb3 | ||
| 
						 | 
					09bce5e6d6 | ||
| 
						 | 
					51447961cb | ||
| 
						 | 
					89e73594eb | ||
| 
						 | 
					0cddc8a154 | ||
| 
						 | 
					8152309435 | ||
| 
						 | 
					a3089a719f | ||
| 
						 | 
					2cb0b00e3a | ||
| 
						 | 
					2aa219e150 | ||
| 
						 | 
					93740c997c | ||
| 
						 | 
					264ec5bab7 | ||
| 
						 | 
					e340593ad1 | ||
| 
						 | 
					05b6afcd17 | ||
| 
						 | 
					10994d65be | ||
| 
						 | 
					a18ce275ab | ||
| 
						 | 
					66c39a953a | ||
| 
						 | 
					09fb9dcd92 | ||
| 
						 | 
					eb5c2e9823 | ||
| 
						 | 
					50d5c4b9ca | ||
| 
						 | 
					54708c6131 | ||
| 
						 | 
					0e9ba9a004 | ||
| 
						 | 
					08f10d3cea | ||
| 
						 | 
					c2dd7e0f6e | ||
| 
						 | 
					57fc5d28a9 | ||
| 
						 | 
					49bdcad4b6 | ||
| 
						 | 
					ef7d259bb7 | ||
| 
						 | 
					688fe131c9 | ||
| 
						 | 
					68b42a00e0 | ||
| 
						 | 
					7368a790a3 | ||
| 
						 | 
					527e1b8a16 | ||
| 
						 | 
					f6d6658de7 | ||
| 
						 | 
					6a929d6a1a | ||
| 
						 | 
					ae380cb21e | ||
| 
						 | 
					cf4c603362 | ||
| 
						 | 
					d83c9da830 | ||
| 
						 | 
					9a7c9e8d98 | ||
| 
						 | 
					ace947e6b3 | ||
| 
						 | 
					5f9378569b | ||
| 
						 | 
					a180b95cca | ||
| 
						 | 
					73bbe25d26 | ||
| 
						 | 
					fc5e3a0aec | ||
| 
						 | 
					7625d66259 | ||
| 
						 | 
					30f2c2bd77 | ||
| 
						 | 
					e3052c8c57 | ||
| 
						 | 
					7b6ebc5c98 | ||
| 
						 | 
					0093dc3d32 | ||
| 
						 | 
					ccefd3be02 | ||
| 
						 | 
					d509ef7581 | ||
| 
						 | 
					5e165819a1 | ||
| 
						 | 
					d180f01b45 | ||
| 
						 | 
					91c09dd0a0 | ||
| 
						 | 
					f6ca92337b | ||
| 
						 | 
					ade9d662db | ||
| 
						 | 
					baca196da6 | ||
| 
						 | 
					10801bfb25 | ||
| 
						 | 
					a89a62071b | ||
| 
						 | 
					657051e4b6 | ||
| 
						 | 
					f6f6d89e06 | ||
| 
						 | 
					1a6af5d896 | ||
| 
						 | 
					a4b83895a3 | ||
| 
						 | 
					4f03548608 | ||
| 
						 | 
					773e1d4e05 | ||
| 
						 | 
					d9ef8c1779 | ||
| 
						 | 
					f9e3a2132f | ||
| 
						 | 
					9ab318cafc | ||
| 
						 | 
					1a126b700f | ||
| 
						 | 
					92dd5e1610 | ||
| 
						 | 
					11ecbd27be | ||
| 
						 | 
					585ef998d0 | ||
| 
						 | 
					c297aff99b | ||
| 
						 | 
					68142c9835 | ||
| 
						 | 
					b8f4fa359c | ||
| 
						 | 
					2b765fdedb | ||
| 
						 | 
					0accdb9e34 | ||
| 
						 | 
					5d468f7ca5 | ||
| 
						 | 
					83768f0531 | ||
| 
						 | 
					acae0ac2a6 | ||
| 
						 | 
					0f86651089 | ||
| 
						 | 
					a77f2fa424 | ||
| 
						 | 
					6198e43fe6 | ||
| 
						 | 
					621d4745b4 | ||
| 
						 | 
					dac75a1dda | ||
| 
						 | 
					f1c0f3d45f | ||
| 
						 | 
					08b6a2c36d | ||
| 
						 | 
					130b67821c | ||
| 
						 | 
					b902769fa8 | ||
| 
						 | 
					7503a58d1f | ||
| 
						 | 
					096ce1a207 | ||
| 
						 | 
					d1ef039e39 | ||
| 
						 | 
					040ca5320d | ||
| 
						 | 
					625c85291d | ||
| 
						 | 
					b28835a604 | ||
| 
						 | 
					6340704173 | ||
| 
						 | 
					522b7c51f7 | ||
| 
						 | 
					388ff75260 | ||
| 
						 | 
					37ef0a0cb6 | ||
| 
						 | 
					d7be2c5b8a | ||
| 
						 | 
					b50e701cae | ||
| 
						 | 
					b7a0443091 | ||
| 
						 | 
					a8f0fd1fff | ||
| 
						 | 
					a89d50d34e | ||
| 
						 | 
					6489cfbce6 | ||
| 
						 | 
					d10f40f109 | ||
| 
						 | 
					175b56b43c | ||
| 
						 | 
					9b68a3ef4a | ||
| 
						 | 
					345d6c5687 | ||
| 
						 | 
					5b1b5cc8f2 | ||
| 
						 | 
					1b062ab929 | ||
| 
						 | 
					a7420ca3d4 | ||
| 
						 | 
					4dcd1f3e65 | ||
| 
						 | 
					e46b392a8d | ||
| 
						 | 
					47ff768b70 | ||
| 
						 | 
					ba4bd3ed55 | ||
| 
						 | 
					68428a5d5e | ||
| 
						 | 
					28694e8afb | ||
| 
						 | 
					c420a0ae2b | ||
| 
						 | 
					a85e50f465 | ||
| 
						 | 
					4962cc3da8 | ||
| 
						 | 
					bb703281a2 | ||
| 
						 | 
					52f5564122 | ||
| 
						 | 
					1dc420ce51 | ||
| 
						 | 
					20af1ceb7d | ||
| 
						 | 
					ec982ccacb | ||
| 
						 | 
					a97e74b2d4 | ||
| 
						 | 
					fecc5b09f8 | ||
| 
						 | 
					d1030eb0b2 | ||
| 
						 | 
					fb749dc526 | ||
| 
						 | 
					e6df1828d9 | ||
| 
						 | 
					9ff53fea98 | ||
| 
						 | 
					4f1888d2ea | ||
| 
						 | 
					53dcd0dee9 | ||
| 
						 | 
					e291ada371 | ||
| 
						 | 
					9c9fed749a | ||
| 
						 | 
					61556a54e2 | ||
| 
						 | 
					79e2f8a2e5 | ||
| 
						 | 
					d1f39e6217 | ||
| 
						 | 
					4aa488f48b | ||
| 
						 | 
					2d72b25c43 | ||
| 
						 | 
					f23b0aacd7 | ||
| 
						 | 
					98d27c4a6a | ||
| 
						 | 
					d01ab227b8 | ||
| 
						 | 
					0cfeee4ded | ||
| 
						 | 
					c97e43dcd6 | ||
| 
						 | 
					eda321954d | ||
| 
						 | 
					64e5392788 | ||
| 
						 | 
					987f95221c | ||
| 
						 | 
					6e917d156c | ||
| 
						 | 
					aeed287122 | ||
| 
						 | 
					36e697b344 | ||
| 
						 | 
					c2d0d4d28c | ||
| 
						 | 
					ea6a3c0963 | ||
| 
						 | 
					8902a5c5cd | ||
| 
						 | 
					3021c5cfad | ||
| 
						 | 
					16db9a7337 | ||
| 
						 | 
					aec9c3c9a4 | ||
| 
						 | 
					0daa225e26 | ||
| 
						 | 
					85be2b85fd | ||
| 
						 | 
					a7d6146169 | ||
| 
						 | 
					978ec91107 | ||
| 
						 | 
					297859c5bc | ||
| 
						 | 
					bea52aa743 | ||
| 
						 | 
					54f1be69c7 | ||
| 
						 | 
					a4cc9ef2cc | ||
| 
						 | 
					4f47594b6d | ||
| 
						 | 
					189a7766d4 | ||
| 
						 | 
					df9174577a | ||
| 
						 | 
					bd1bb7a71b | ||
| 
						 | 
					4b6e7e6c37 | ||
| 
						 | 
					8896642e25 | ||
| 
						 | 
					0ecb5a3fec | ||
| 
						 | 
					d289b0b450 | ||
| 
						 | 
					b8489464b3 | ||
| 
						 | 
					ebaa3f39e4 | ||
| 
						 | 
					e19753dcde | ||
| 
						 | 
					13255a3762 | ||
| 
						 | 
					15ce2a3d67 | ||
| 
						 | 
					3bb97b81de | ||
| 
						 | 
					9247780073 | ||
| 
						 | 
					37161d3017 | ||
| 
						 | 
					4532037e4f | ||
| 
						 | 
					0fe08e1b33 | ||
| 
						 | 
					236acbd6e8 | ||
| 
						 | 
					16b0704acc | ||
| 
						 | 
					61bcd67a5d | ||
| 
						 | 
					0629c2a086 | ||
| 
						 | 
					fbdc5a0eb5 | ||
| 
						 | 
					68a8d81b6a | ||
| 
						 | 
					a368301dbf | ||
| 
						 | 
					89989adcad | ||
| 
						 | 
					95cdb4b2bc | ||
| 
						 | 
					228c835466 | ||
| 
						 | 
					d604166194 | ||
| 
						 | 
					d643a2ff13 | ||
| 
						 | 
					b581a171f0 | ||
| 
						 | 
					307336cfc4 | ||
| 
						 | 
					fc30171725 | ||
| 
						 | 
					34be7e99f0 | ||
| 
						 | 
					7679df062c | ||
| 
						 | 
					71cfd874ae | ||
| 
						 | 
					08be0c374a | ||
| 
						 | 
					7decce9718 | ||
| 
						 | 
					22bab90a90 | ||
| 
						 | 
					02882fb327 | ||
| 
						 | 
					c74d597c84 | ||
| 
						 | 
					653c77e852 | ||
| 
						 | 
					2b36f4f57f | ||
| 
						 | 
					82b0ebb787 | ||
| 
						 | 
					3f35006c26 | ||
| 
						 | 
					2ffd8637e1 | ||
| 
						 | 
					44c1572b8f | ||
| 
						 | 
					d0d749074e | ||
| 
						 | 
					dbc44c08df | ||
| 
						 | 
					46fbd7f1e1 | ||
| 
						 | 
					5048c6c22a | ||
| 
						 | 
					709d82e764 | ||
| 
						 | 
					9d64b35ed8 | ||
| 
						 | 
					0f00862e5e | ||
| 
						 | 
					532e79c7d0 | ||
| 
						 | 
					4ebad10557 | ||
| 
						 | 
					0b04a7f17f | ||
| 
						 | 
					77f96b386e | ||
| 
						 | 
					ea86ddc693 | ||
| 
						 | 
					e3e43d0ba0 | ||
| 
						 | 
					b10929fe23 | ||
| 
						 | 
					f512cb8e35 | ||
| 
						 | 
					4f240f538d | ||
| 
						 | 
					db6db6a4e9 | ||
| 
						 | 
					f2add8de94 | ||
| 
						 | 
					88c6621cfe | ||
| 
						 | 
					c152b6f0ad | ||
| 
						 | 
					53c0188248 | ||
| 
						 | 
					725addafda | ||
| 
						 | 
					19628c4732 | ||
| 
						 | 
					04eaf7f175 | ||
| 
						 | 
					f5850d0c08 | ||
| 
						 | 
					855eb8355a | ||
| 
						 | 
					fdbb7fd30f | ||
| 
						 | 
					30d0ac0784 | ||
| 
						 | 
					3d5c75420a | ||
| 
						 | 
					1d5967d143 | ||
| 
						 | 
					110a41d18d | ||
| 
						 | 
					a3d8b9935a | ||
| 
						 | 
					08357e3cb0 | ||
| 
						 | 
					162a445a50 | ||
| 
						 | 
					c7257bcf46 | ||
| 
						 | 
					dfca8c09e0 | ||
| 
						 | 
					7690f73e81 | ||
| 
						 | 
					86fbb5952e | ||
| 
						 | 
					78c92642e4 | ||
| 
						 | 
					b3f6129718 | ||
| 
						 | 
					2a52603b7e | ||
| 
						 | 
					e6f9f258ec | ||
| 
						 | 
					22e7b4c911 | ||
| 
						 | 
					af5f7a7779 | ||
| 
						 | 
					693d692a47 | ||
| 
						 | 
					81f0189d23 | ||
| 
						 | 
					e7f7e96d58 | ||
| 
						 | 
					1dab2ac7d3 | ||
| 
						 | 
					ec54074392 | ||
| 
						 | 
					23b4c9c667 | ||
| 
						 | 
					472ed721a3 | ||
| 
						 | 
					ed3f2646f0 | ||
| 
						 | 
					0499d2b5c4 | ||
| 
						 | 
					81ba629b56 | ||
| 
						 | 
					0bb746ba39 | ||
| 
						 | 
					16a0f40ac2 | ||
| 
						 | 
					f84103918a | ||
| 
						 | 
					b5ca9bbab2 | ||
| 
						 | 
					ff38d2bba6 | ||
| 
						 | 
					8f2a8a0051 | ||
| 
						 | 
					97147b594b | ||
| 
						 | 
					9ff6d6e7b5 | ||
| 
						 | 
					a0ec5b18e7 | ||
| 
						 | 
					f2acdd27fd | ||
| 
						 | 
					4ade446b55 | ||
| 
						 | 
					ec6569fbea | ||
| 
						 | 
					1ad6742dbc | ||
| 
						 | 
					63ea3e8d27 | ||
| 
						 | 
					9ace7db216 | ||
| 
						 | 
					841513501a | ||
| 
						 | 
					5c94147603 | ||
| 
						 | 
					02f6d4cb66 | ||
| 
						 | 
					ec5fad433c | ||
| 
						 | 
					b4fa97fd54 | ||
| 
						 | 
					d30b441ede | ||
| 
						 | 
					dda29f7e2f | ||
| 
						 | 
					952e281993 | ||
| 
						 | 
					1fa026b9c7 | ||
| 
						 | 
					e8c91e6e12 | ||
| 
						 | 
					41425f7f74 | ||
| 
						 | 
					2cf01c23a2 | ||
| 
						 | 
					412f85b665 | ||
| 
						 | 
					ebc90f6ab8 | ||
| 
						 | 
					84d80e93bc | ||
| 
						 | 
					b7e92dbced | ||
| 
						 | 
					44dcb0d0a9 | ||
| 
						 | 
					b3e3e080a9 | ||
| 
						 | 
					1167cdcaec | ||
| 
						 | 
					4eda39a31d | ||
| 
						 | 
					759b75ca48 | ||
| 
						 | 
					55e862a4a4 | ||
| 
						 | 
					227547f826 | ||
| 
						 | 
					7c41dd5e31 | ||
| 
						 | 
					a964646803 | ||
| 
						 | 
					9dac02ba5d | ||
| 
						 | 
					43877d2647 | ||
| 
						 | 
					cc6159b39b | ||
| 
						 | 
					43ff787b04 | ||
| 
						 | 
					3633598462 | ||
| 
						 | 
					94922f2df6 | ||
| 
						 | 
					75fe022f96 | ||
| 
						 | 
					bc839569fb | ||
| 
						 | 
					f62a4a0c0c | ||
| 
						 | 
					8dacd51abb | ||
| 
						 | 
					06302871bc | ||
| 
						 | 
					c1ec2afeca | ||
| 
						 | 
					e2f1338f94 | ||
| 
						 | 
					5f9b57d300 | ||
| 
						 | 
					ddf77f10e9 | ||
| 
						 | 
					127532c226 | ||
| 
						 | 
					572adbaad2 | ||
| 
						 | 
					f4c7822bc9 | ||
| 
						 | 
					89561b8d45 | ||
| 
						 | 
					b0775f7a58 | ||
| 
						 | 
					2dc50e6633 | ||
| 
						 | 
					ad2a3d603e | ||
| 
						 | 
					545f23551f | ||
| 
						 | 
					b15c1ffedc | ||
| 
						 | 
					56d70e4ea7 | ||
| 
						 | 
					0b934232fd | ||
| 
						 | 
					a4a53e1355 | ||
| 
						 | 
					c445e70cff | ||
| 
						 | 
					e8eec2cb41 | ||
| 
						 | 
					dd6fa4af00 | ||
| 
						 | 
					afdb9a63ff | ||
| 
						 | 
					10ba2cd312 | ||
| 
						 | 
					4c1fa9c242 | ||
| 
						 | 
					3c6b707353 | ||
| 
						 | 
					96efc8c7f0 | ||
| 
						 | 
					0cd6afde6f | ||
| 
						 | 
					82b11da4ca | ||
| 
						 | 
					4ec39ab707 | ||
| 
						 | 
					d2a60f3ca4 | ||
| 
						 | 
					3bc6628227 | ||
| 
						 | 
					27579e0701 | ||
| 
						 | 
					f91bcfeb4b | ||
| 
						 | 
					e19809d5b5 | ||
| 
						 | 
					dd068467de | ||
| 
						 | 
					3099c799b2 | ||
| 
						 | 
					cd3ef8fa5a | ||
| 
						 | 
					72ce37704b | ||
| 
						 | 
					4420d073bb | ||
| 
						 | 
					9cc9f519fc | ||
| 
						 | 
					0483d841e3 | ||
| 
						 | 
					5546120312 | ||
| 
						 | 
					ad613e2437 | ||
| 
						 | 
					c544759d36 | ||
| 
						 | 
					20503d3c58 | ||
| 
						 | 
					3fb17c5de8 | ||
| 
						 | 
					a5e4bf16d3 | ||
| 
						 | 
					68d9aad3a2 | ||
| 
						 | 
					0aba1b4ad3 | ||
| 
						 | 
					e12c7c8d27 | ||
| 
						 | 
					83a040722e | ||
| 
						 | 
					b7b504d43a | ||
| 
						 | 
					b18804f57f | ||
| 
						 | 
					550a5fb4c0 | ||
| 
						 | 
					40f0238bb7 | ||
| 
						 | 
					089823785e | ||
| 
						 | 
					ecf7dded07 | ||
| 
						 | 
					909aba27d1 | ||
| 
						 | 
					cd4f29135b | ||
| 
						 | 
					68c5c366f4 | ||
| 
						 | 
					29a5311ae0 | ||
| 
						 | 
					62d774a548 | ||
| 
						 | 
					c9baca7910 | ||
| 
						 | 
					86366ae157 | ||
| 
						 | 
					c4094c68ee | ||
| 
						 | 
					ec67a1b2c1 | ||
| 
						 | 
					7ba9a5972d | ||
| 
						 | 
					b32071ad04 | ||
| 
						 | 
					b38c4e1a28 | ||
| 
						 | 
					a13b2b4018 | ||
| 
						 | 
					65a2f789dc | ||
| 
						 | 
					8bd12ed040 | ||
| 
						 | 
					6914662dd8 | ||
| 
						 | 
					bcb11d9b7e | ||
| 
						 | 
					920cab6f12 | ||
| 
						 | 
					9756adb933 | ||
| 
						 | 
					2671af13cd | ||
| 
						 | 
					b7b94e38ac | ||
| 
						 | 
					37792e9b38 | ||
| 
						 | 
					f90bf756fb | ||
| 
						 | 
					0b363a5c98 | ||
| 
						 | 
					ebb1a8af1b | ||
| 
						 | 
					f4ba7fcaf4 | ||
| 
						 | 
					13964ac726 | ||
| 
						 | 
					c8c1140f15 | ||
| 
						 | 
					9a473640fb | ||
| 
						 | 
					405173a0b4 | ||
| 
						 | 
					8e43b86f06 | ||
| 
						 | 
					eea9aaf940 | ||
| 
						 | 
					67d3e8d049 | ||
| 
						 | 
					f99ca918db | ||
| 
						 | 
					79a0a66f1f | ||
| 
						 | 
					08681f4a8b | ||
| 
						 | 
					a58ef94a9c | ||
| 
						 | 
					2b9ebd6662 | ||
| 
						 | 
					6fdd2f40ed | ||
| 
						 | 
					238990a285 | ||
| 
						 | 
					1a77490969 | ||
| 
						 | 
					5431d05168 | ||
| 
						 | 
					eb97cdc33a | ||
| 
						 | 
					b54d6589c3 | ||
| 
						 | 
					463768fcf7 | ||
| 
						 | 
					a43545c6ea | ||
| 
						 | 
					9f067d7f56 | ||
| 
						 | 
					8907e2d850 | ||
| 
						 | 
					c84466b131 | ||
| 
						 | 
					f62b956e74 | ||
| 
						 | 
					9841063df9 | ||
| 
						 | 
					803c8177d3 | ||
| 
						 | 
					7ff525468f | ||
| 
						 | 
					7c1c36f043 | ||
| 
						 | 
					565e3f75c7 | ||
| 
						 | 
					842f030355 | ||
| 
						 | 
					f4ad42bb84 | ||
| 
						 | 
					fb08b53f0b | ||
| 
						 | 
					598becf619 | ||
| 
						 | 
					9a27b38976 | ||
| 
						 | 
					7917aa2a7c | ||
| 
						 | 
					d0c97a589b | ||
| 
						 | 
					8ec1ea7b7a | ||
| 
						 | 
					71cb6d2bce | ||
| 
						 | 
					ee258f1425 | ||
| 
						 | 
					50278674f0 | ||
| 
						 | 
					be5085f205 | ||
| 
						 | 
					ca9476f72a | ||
| 
						 | 
					d55c64c838 | ||
| 
						 | 
					563de2cc90 | ||
| 
						 | 
					6afe3ccc3b | ||
| 
						 | 
					14ad5955b5 | ||
| 
						 | 
					a6f2110141 | ||
| 
						 | 
					393d8b9ded | ||
| 
						 | 
					137dc1eac0 | ||
| 
						 | 
					5fee82ce39 | ||
| 
						 | 
					552710ac2a | ||
| 
						 | 
					0a9a11636a | ||
| 
						 | 
					e550631275 | ||
| 
						 | 
					87d2f7f27a | ||
| 
						 | 
					a40cd2b46f | ||
| 
						 | 
					4b581f3720 | ||
| 
						 | 
					12956679e7 | ||
| 
						 | 
					7903fcb48c | ||
| 
						 | 
					9672c6b885 | ||
| 
						 | 
					55369d30a6 | ||
| 
						 | 
					a207199879 | ||
| 
						 | 
					a894b7cc9b | ||
| 
						 | 
					46b3a9158c | ||
| 
						 | 
					26421684dc | ||
| 
						 | 
					fd536d373e | ||
| 
						 | 
					4f59a821d3 | ||
| 
						 | 
					b29e21efa8 | ||
| 
						 | 
					a63dc75b43 | ||
| 
						 | 
					9f6f721a13 | ||
| 
						 | 
					fdb9d93b12 | ||
| 
						 | 
					475e6e28eb | ||
| 
						 | 
					4c1f70af4b | ||
| 
						 | 
					454c90820d | ||
| 
						 | 
					0a4e61c1dd | ||
| 
						 | 
					31d9ba7e02 | ||
| 
						 | 
					6a81b0f807 | ||
| 
						 | 
					3322630732 | ||
| 
						 | 
					d8885984ab | ||
| 
						 | 
					4a18c45e4f | ||
| 
						 | 
					5b7cac1002 | ||
| 
						 | 
					5aef9266cb | ||
| 
						 | 
					80b40c02b4 | ||
| 
						 | 
					56d6079c4a | ||
| 
						 | 
					3975792bf1 | ||
| 
						 | 
					697e694de6 | ||
| 
						 | 
					828d8eaadb | ||
| 
						 | 
					30ee00ff50 | ||
| 
						 | 
					7e130c2618 | ||
| 
						 | 
					b859dd660c | ||
| 
						 | 
					8868783476 | ||
| 
						 | 
					dc0dd6588c | ||
| 
						 | 
					f7e7e885a9 | ||
| 
						 | 
					d813be1f48 | ||
| 
						 | 
					0d03309c2f | ||
| 
						 | 
					1dab353fdb | ||
| 
						 | 
					8eb4efaddb | ||
| 
						 | 
					12c900ea7d | ||
| 
						 | 
					c31db83b26 | ||
| 
						 | 
					98ce8f4c2f | ||
| 
						 | 
					1f25b4a8a9 | ||
| 
						 | 
					1756bbff84 | ||
| 
						 | 
					21b2ffa42e | ||
| 
						 | 
					b6efdac1db | ||
| 
						 | 
					fc9d321ebe | ||
| 
						 | 
					68a290c347 | ||
| 
						 | 
					9133de50e9 | ||
| 
						 | 
					73d04b976e | ||
| 
						 | 
					a35d271669 | ||
| 
						 | 
					72a7f932c6 | ||
| 
						 | 
					2e74df2583 | ||
| 
						 | 
					4fffb3c816 | ||
| 
						 | 
					840b3a34cb | ||
| 
						 | 
					dc267663a7 | ||
| 
						 | 
					8d6443b25d | ||
| 
						 | 
					e3c7fc8077 | ||
| 
						 | 
					bbf2a15f27 | ||
| 
						 | 
					5a326b82bd | ||
| 
						 | 
					f1b0dd7836 | ||
| 
						 | 
					c205777542 | ||
| 
						 | 
					329a1e6f16 | ||
| 
						 | 
					9c39121e99 | ||
| 
						 | 
					e5e57e684e | ||
| 
						 | 
					659a60aeec | ||
| 
						 | 
					b0268adad9 | ||
| 
						 | 
					71e4bbfc99 | ||
| 
						 | 
					c6bd004d63 | ||
| 
						 | 
					8b6986ba18 | ||
| 
						 | 
					b23718f3ad | ||
| 
						 | 
					8113548920 | ||
| 
						 | 
					b401dbbf65 | ||
| 
						 | 
					75dd0a770f | ||
| 
						 | 
					6d8292cdd8 | ||
| 
						 | 
					0575eb671a | ||
| 
						 | 
					d06eea53ef | ||
| 
						 | 
					9e96a93172 | ||
| 
						 | 
					441f8f3ce8 | ||
| 
						 | 
					1d4dec5510 | ||
| 
						 | 
					167758003c | ||
| 
						 | 
					0a3ac1f5c3 | ||
| 
						 | 
					9173140ddf | ||
| 
						 | 
					7aeb113c62 | ||
| 
						 | 
					22cd408efb | ||
| 
						 | 
					4fbd21da57 | ||
| 
						 | 
					68f66ca101 | ||
| 
						 | 
					ed95509a4f | ||
| 
						 | 
					4162975f9f | ||
| 
						 | 
					ac0cdcf70b | ||
| 
						 | 
					63134fafec | ||
| 
						 | 
					5494e88e08 | ||
| 
						 | 
					86276ad17b | ||
| 
						 | 
					c883ec40d7 | ||
| 
						 | 
					d19bc328f3 | ||
| 
						 | 
					f286f904dc | ||
| 
						 | 
					150029a5e2 | ||
| 
						 | 
					709a3fb06f | ||
| 
						 | 
					d3c9d0b331 | ||
| 
						 | 
					cc2d59468d | ||
| 
						 | 
					91391fba5d | ||
| 
						 | 
					4369402855 | ||
| 
						 | 
					0bc556618f | ||
| 
						 | 
					cb11580981 | ||
| 
						 | 
					0366e8758c | ||
| 
						 | 
					8d230dd798 | ||
| 
						 | 
					411b342a27 | ||
| 
						 | 
					b9b7032386 | ||
| 
						 | 
					c38ef9023b | ||
| 
						 | 
					9cecd525e2 | ||
| 
						 | 
					4f5995abc0 | ||
| 
						 | 
					2945b230e4 | ||
| 
						 | 
					261cc448f7 | ||
| 
						 | 
					616b0b6baa | ||
| 
						 | 
					d99968ee6d | ||
| 
						 | 
					4a65ff6ae2 | ||
| 
						 | 
					94f91ae687 | ||
| 
						 | 
					28e4bcf67f | ||
| 
						 | 
					884461f1a6 | ||
| 
						 | 
					26c669e42d | ||
| 
						 | 
					f60dde4138 | ||
| 
						 | 
					9c545059ae | ||
| 
						 | 
					05dea7b22a | ||
| 
						 | 
					5b3f915d90 | ||
| 
						 | 
					d987d61ea9 | ||
| 
						 | 
					dedb56d295 | ||
| 
						 | 
					8697972d5d | ||
| 
						 | 
					f90a2ae195 | ||
| 
						 | 
					c34aadfbf7 | ||
| 
						 | 
					084de9d8e0 | ||
| 
						 | 
					69b780ee32 | ||
| 
						 | 
					c7b904501c | ||
| 
						 | 
					206be3c161 | ||
| 
						 | 
					48e8022095 | ||
| 
						 | 
					4715a1a5e0 | ||
| 
						 | 
					ed2ba6bc3a | ||
| 
						 | 
					ca1d62bec0 | ||
| 
						 | 
					6a4aad1aa8 | ||
| 
						 | 
					3f0462b68b | ||
| 
						 | 
					f3a622d1a7 | ||
| 
						 | 
					3cd5b9ca2e | ||
| 
						 | 
					e9782c3219 | ||
| 
						 | 
					8d348954a7 | ||
| 
						 | 
					7d19d784df | ||
| 
						 | 
					733b4e0a34 | ||
| 
						 | 
					08d29a8342 | ||
| 
						 | 
					675e2d25d6 | ||
| 
						 | 
					ba20af48d3 | ||
| 
						 | 
					707e053949 | ||
| 
						 | 
					c47e67e52c | ||
| 
						 | 
					3a95bfb699 | ||
| 
						 | 
					6567bb4c12 | ||
| 
						 | 
					39ba697e19 | ||
| 
						 | 
					c9818ea2c4 | ||
| 
						 | 
					afe5cb588d | ||
| 
						 | 
					e9e999542d | ||
| 
						 | 
					d9db90752e | ||
| 
						 | 
					f7c3f52817 | ||
| 
						 | 
					681e3785ef | ||
| 
						 | 
					5f593994c7 | ||
| 
						 | 
					ec73aeba16 | ||
| 
						 | 
					7573e560b6 | ||
| 
						 | 
					c8bc155cfe | ||
| 
						 | 
					1eae73105a | ||
| 
						 | 
					360dc140ea | ||
| 
						 | 
					f85348ba94 | ||
| 
						 | 
					2f15ad4be0 | ||
| 
						 | 
					f254bb39a5 | ||
| 
						 | 
					c58465d630 | ||
| 
						 | 
					03a1386902 | ||
| 
						 | 
					70b56eb527 | ||
| 
						 | 
					4e05062def | ||
| 
						 | 
					266333468b | ||
| 
						 | 
					e32b3aac22 | ||
| 
						 | 
					676402d918 | ||
| 
						 | 
					edb4d066a9 | ||
| 
						 | 
					03f4518da9 | ||
| 
						 | 
					8259e82787 | ||
| 
						 | 
					838d3ddc17 | ||
| 
						 | 
					66686de4e4 | ||
| 
						 | 
					ce8dca7afe | ||
| 
						 | 
					9f5ef4c1cb | ||
| 
						 | 
					f0a87da375 | ||
| 
						 | 
					263e30d25d | ||
| 
						 | 
					15ffc30d88 | ||
| 
						 | 
					8a5c4979ad | ||
| 
						 | 
					e36fbd6af5 | ||
| 
						 | 
					3216806fae | ||
| 
						 | 
					f8526f027c | ||
| 
						 | 
					ed3066aae7 | ||
| 
						 | 
					98a7e72f0a | ||
| 
						 | 
					a2259865b3 | ||
| 
						 | 
					63f3283591 | ||
| 
						 | 
					d670ea4f59 | ||
| 
						 | 
					d0d10bc6e7 | ||
| 
						 | 
					4fea06c9fa | ||
| 
						 | 
					09fed60dec | ||
| 
						 | 
					75b9c39b0e | ||
| 
						 | 
					5957a1068f | ||
| 
						 | 
					df5229c7c8 | ||
| 
						 | 
					ed817c81de | ||
| 
						 | 
					dd72f7638d | ||
| 
						 | 
					a77e4aa6fa | ||
| 
						 | 
					792f3775ce | ||
| 
						 | 
					4c7700ec3b | ||
| 
						 | 
					eee296c4c2 | ||
| 
						 | 
					499f745732 | ||
| 
						 | 
					446388e0ba | ||
| 
						 | 
					e1628bcdd8 | ||
| 
						 | 
					6d5874fc45 | ||
| 
						 | 
					3d563dea87 | ||
| 
						 | 
					09304c33c1 | ||
| 
						 | 
					521d8c4b1f | ||
| 
						 | 
					b7d573a4b8 | ||
| 
						 | 
					4a62385dcc | ||
| 
						 | 
					98e15f658e | ||
| 
						 | 
					0a3fa35c5d | ||
| 
						 | 
					02e095bec2 | ||
| 
						 | 
					696d9c6bd3 | ||
| 
						 | 
					50dee5d464 | ||
| 
						 | 
					87a8dda955 | ||
| 
						 | 
					d7c73f590c | ||
| 
						 | 
					d1b197e339 | ||
| 
						 | 
					8f5ee989ba | ||
| 
						 | 
					ce9c227425 | ||
| 
						 | 
					e32c2b84ee | ||
| 
						 | 
					ce9f77afed | ||
| 
						 | 
					2bc38b2063 | ||
| 
						 | 
					b4f4c28871 | ||
| 
						 | 
					b14ef537e1 | ||
| 
						 | 
					84ac386481 | ||
| 
						 | 
					f0365d32aa | ||
| 
						 | 
					795764f22f | ||
| 
						 | 
					986f61ac92 | ||
| 
						 | 
					09576f2f4f | ||
| 
						 | 
					bba474dc6b | ||
| 
						 | 
					fde971fe81 | ||
| 
						 | 
					882e1db1d6 | ||
| 
						 | 
					6b0333e919 | ||
| 
						 | 
					914808b867 | ||
| 
						 | 
					8d38cf4d1f | ||
| 
						 | 
					2d1d512d0f | ||
| 
						 | 
					37bc099d39 | ||
| 
						 | 
					9e3c931b34 | ||
| 
						 | 
					f8fb0e67b4 | ||
| 
						 | 
					86ef6e6987 | ||
| 
						 | 
					e8fd373e6c | ||
| 
						 | 
					7efa546665 | ||
| 
						 | 
					4d2a0697ed | ||
| 
						 | 
					c6023782a4 | ||
| 
						 | 
					30283282d2 | ||
| 
						 | 
					7588fc0989 | ||
| 
						 | 
					d698c1093a | ||
| 
						 | 
					7b327d47c0 | ||
| 
						 | 
					e629985cf4 | ||
| 
						 | 
					aa875f1147 | ||
| 
						 | 
					fe843bc466 | ||
| 
						 | 
					5b355c6ca7 | ||
| 
						 | 
					a3f7ff90e3 | ||
| 
						 | 
					1f3f8a5073 | ||
| 
						 | 
					9c88971bc1 | ||
| 
						 | 
					aad309ee4f | ||
| 
						 | 
					e80ca4ddbc | ||
| 
						 | 
					28ccad28c2 | ||
| 
						 | 
					ba9e7fbf64 | ||
| 
						 | 
					28c85cf8e7 | ||
| 
						 | 
					526b5a8d25 | ||
| 
						 | 
					6b15cf3f72 | ||
| 
						 | 
					fbd8ab47ea | ||
| 
						 | 
					893917a25d | ||
| 
						 | 
					af5ff2bb93 | ||
| 
						 | 
					8995d3434f | ||
| 
						 | 
					5f9b0675e2 | ||
| 
						 | 
					46ac97a3ff | ||
| 
						 | 
					db3264ab8c | ||
| 
						 | 
					a0923622ae | ||
| 
						 | 
					aa9975ad0d | ||
| 
						 | 
					6787c81abe | ||
| 
						 | 
					72205176e1 | ||
| 
						 | 
					480742cc15 | ||
| 
						 | 
					48bdfa2377 | ||
| 
						 | 
					2ff6f4d3cf | ||
| 
						 | 
					ae32938531 | ||
| 
						 | 
					d3da603292 | ||
| 
						 | 
					912bcf9487 | ||
| 
						 | 
					413f071861 | ||
| 
						 | 
					668c43abf3 | ||
| 
						 | 
					43e9553ebc | ||
| 
						 | 
					e8b54a5087 | ||
| 
						 | 
					39852662a6 | ||
| 
						 | 
					6a66ba8a21 | ||
| 
						 | 
					36a7a84080 | ||
| 
						 | 
					7e0b334b38 | ||
| 
						 | 
					5d8d217a13 | ||
| 
						 | 
					f2aa5c0235 | ||
| 
						 | 
					323febe8c7 | ||
| 
						 | 
					32d8f349c9 | ||
| 
						 | 
					3910495cce | ||
| 
						 | 
					fe69afdefb | ||
| 
						 | 
					9082862b9d | ||
| 
						 | 
					cb4a2cf029 | ||
| 
						 | 
					26b5180bf7 | ||
| 
						 | 
					11bfb1e5fd | ||
| 
						 | 
					e431df06ab | ||
| 
						 | 
					cbf0ceacd5 | ||
| 
						 | 
					8c634d8323 | ||
| 
						 | 
					4ae108009c | ||
| 
						 | 
					a5c1c30368 | ||
| 
						 | 
					8cd3086be0 | ||
| 
						 | 
					dd37ae26a5 | ||
| 
						 | 
					fdaebc7365 | ||
| 
						 | 
					a8b62261f6 | ||
| 
						 | 
					47eb913c22 | ||
| 
						 | 
					7a46293f7a | ||
| 
						 | 
					6b26d2b62d | ||
| 
						 | 
					cac3b3ea35 | ||
| 
						 | 
					dff4d03bd4 | ||
| 
						 | 
					28d83d42e2 | ||
| 
						 | 
					38f1b4d205 | ||
| 
						 | 
					931d19eece | ||
| 
						 | 
					88bbe55b85 | ||
| 
						 | 
					dd17124ec6 | ||
| 
						 | 
					674b50889e | ||
| 
						 | 
					263c38caec | ||
| 
						 | 
					3881f22192 | ||
| 
						 | 
					664446631f | ||
| 
						 | 
					c5f1cca3a0 | ||
| 
						 | 
					a7407097e1 | ||
| 
						 | 
					14bb60c61f | ||
| 
						 | 
					749c0e51e6 | ||
| 
						 | 
					0f120c41f1 | ||
| 
						 | 
					65a7d56957 | ||
| 
						 | 
					8ab8a6eefb | ||
| 
						 | 
					52351d7dc8 | ||
| 
						 | 
					224e0c298a | ||
| 
						 | 
					a5a0e564dd | ||
| 
						 | 
					b8c94fc7cf | ||
| 
						 | 
					62dd3a5380 | ||
| 
						 | 
					e3ddb677e1 | ||
| 
						 | 
					32d7bd5ab1 | ||
| 
						 | 
					861df49670 | ||
| 
						 | 
					716f727753 | ||
| 
						 | 
					7e381f8e5d | ||
| 
						 | 
					183063a244 | ||
| 
						 | 
					ef871775b7 | ||
| 
						 | 
					45e386b26d | ||
| 
						 | 
					e1db5db8ac | ||
| 
						 | 
					c3a289cebc | ||
| 
						 | 
					20e51f0b4d | ||
| 
						 | 
					464dc93751 | ||
| 
						 | 
					7445a3be59 | ||
| 
						 | 
					2bbc25c1eb | ||
| 
						 | 
					fd56fe6eb2 | ||
| 
						 | 
					92dfa8becc | ||
| 
						 | 
					ae8f9561ad | ||
| 
						 | 
					3052ba433a | ||
| 
						 | 
					508012342d | ||
| 
						 | 
					9fa207e613 | ||
| 
						 | 
					2d7b9817cb | ||
| 
						 | 
					e8d808d708 | ||
| 
						 | 
					d71595fc75 | ||
| 
						 | 
					399d6592b8 | ||
| 
						 | 
					628a6ffa07 | ||
| 
						 | 
					3e1a94cbcd | ||
| 
						 | 
					a6c2d4b0e2 | ||
| 
						 | 
					19277aec87 | ||
| 
						 | 
					14c2755436 | ||
| 
						 | 
					50a9145386 | ||
| 
						 | 
					9046509b95 | ||
| 
						 | 
					61eaa44cf8 | ||
| 
						 | 
					2d1e9abb60 | ||
| 
						 | 
					d064260bf1 | ||
| 
						 | 
					a19ad3fd1d | ||
| 
						 | 
					3bc59a0327 | ||
| 
						 | 
					5a883889a2 | ||
| 
						 | 
					fbaa7a4d67 | ||
| 
						 | 
					6baa6d488b | ||
| 
						 | 
					e26f9b8095 | ||
| 
						 | 
					f92fae7625 | ||
| 
						 | 
					3d0e269241 | ||
| 
						 | 
					c17c47f18a | ||
| 
						 | 
					98b8bfb3fa | ||
| 
						 | 
					83b1a98db1 | ||
| 
						 | 
					f49f55f4a5 | ||
| 
						 | 
					e58d19b420 | ||
| 
						 | 
					58f753136a | ||
| 
						 | 
					22d827adf1 | ||
| 
						 | 
					d3de50e0f9 | ||
| 
						 | 
					432037d20d | ||
| 
						 | 
					86ef0a2609 | ||
| 
						 | 
					bae50da799 | ||
| 
						 | 
					2c45f27356 | ||
| 
						 | 
					759f4f2c62 | ||
| 
						 | 
					28355335f8 | ||
| 
						 | 
					9f6832d636 | ||
| 
						 | 
					5309afc347 | ||
| 
						 | 
					693627a858 | ||
| 
						 | 
					48eaa0e5bf | ||
| 
						 | 
					9ad7ac632a | ||
| 
						 | 
					41e3ecad46 | ||
| 
						 | 
					d6f8d63742 | ||
| 
						 | 
					28b0929554 | ||
| 
						 | 
					55787ff7b9 | ||
| 
						 | 
					b00919c692 | ||
| 
						 | 
					d43392628b | ||
| 
						 | 
					aa831fee5b | ||
| 
						 | 
					5c568d6999 | ||
| 
						 | 
					d84665cb64 | ||
| 
						 | 
					4bb5d27c59 | ||
| 
						 | 
					11a9f1d1f0 | ||
| 
						 | 
					123e8f21b5 | ||
| 
						 | 
					6d6b2efdb5 | ||
| 
						 | 
					94b925f5ef | ||
| 
						 | 
					c86755f1ab | ||
| 
						 | 
					b8418ced44 | ||
| 
						 | 
					fac0beaa0a | ||
| 
						 | 
					c1f5229906 | ||
| 
						 | 
					d5865989cf | ||
| 
						 | 
					90e587a974 | ||
| 
						 | 
					4a6b31fbe2 | ||
| 
						 | 
					a63766a005 | ||
| 
						 | 
					47359c1a3b | ||
| 
						 | 
					abd0dad2bf | ||
| 
						 | 
					54a52f7048 | ||
| 
						 | 
					f213215c81 | ||
| 
						 | 
					96cde120b4 | ||
| 
						 | 
					0096ef4ddb | ||
| 
						 | 
					ce6c7d4b59 | ||
| 
						 | 
					b51ed9bbb7 | ||
| 
						 | 
					5c6af92a0d | ||
| 
						 | 
					d8eb08e214 | ||
| 
						 | 
					c6f5c7f1a3 | ||
| 
						 | 
					a6b6e31cda | ||
| 
						 | 
					28145a9deb | ||
| 
						 | 
					fa991c8501 | ||
| 
						 | 
					5f345d2089 | ||
| 
						 | 
					520c454f22 | ||
| 
						 | 
					cc81668c8f | ||
| 
						 | 
					6426e044d6 | ||
| 
						 | 
					7b92371a03 | ||
| 
						 | 
					6105d4a4e6 | ||
| 
						 | 
					84649e9d20 | ||
| 
						 | 
					1f7df33e28 | ||
| 
						 | 
					3c394f08b0 | ||
| 
						 | 
					64f07d9bf3 | ||
| 
						 | 
					849a6c12be | ||
| 
						 | 
					9144ce746e | ||
| 
						 | 
					64821ad4f5 | ||
| 
						 | 
					2655e726c9 | ||
| 
						 | 
					de2970d7ef | ||
| 
						 | 
					012dd6986b | ||
| 
						 | 
					2ef9904d00 | ||
| 
						 | 
					2c83224f07 | ||
| 
						 | 
					0c63090a23 | ||
| 
						 | 
					0159277dbf | ||
| 
						 | 
					6ca5f3d8f6 | ||
| 
						 | 
					875625b147 | ||
| 
						 | 
					da0bd5a9dc | ||
| 
						 | 
					ce02ad641b | ||
| 
						 | 
					78915896d5 | ||
| 
						 | 
					4a5d2e16d0 | ||
| 
						 | 
					3e3161c747 | ||
| 
						 | 
					694af4aeb1 | ||
| 
						 | 
					7f59d7ea48 | ||
| 
						 | 
					52b945164c | ||
| 
						 | 
					584fb2904b | ||
| 
						 | 
					71013b372d | ||
| 
						 | 
					338b3ba590 | ||
| 
						 | 
					6b798b01a8 | ||
| 
						 | 
					7df20e5049 | ||
| 
						 | 
					7128d79935 | ||
| 
						 | 
					e27dfbb0bb | ||
| 
						 | 
					a51f109930 | ||
| 
						 | 
					3e101521dd | ||
| 
						 | 
					726c7a4d32 | ||
| 
						 | 
					767f05cfa7 | ||
| 
						 | 
					47b49f1be9 | ||
| 
						 | 
					a4964b9073 | ||
| 
						 | 
					40e2ec3ae9 | ||
| 
						 | 
					120cde169b | ||
| 
						 | 
					1c35f46b45 | ||
| 
						 | 
					eea713eed2 | ||
| 
						 | 
					8c88757451 | ||
| 
						 | 
					dd171ca44a | ||
| 
						 | 
					cfd086a140 | ||
| 
						 | 
					72fe7396d6 | ||
| 
						 | 
					03140865f0 | ||
| 
						 | 
					a4fc802d1b | ||
| 
						 | 
					b6d760b903 | ||
| 
						 | 
					e4b24d20ac | ||
| 
						 | 
					91607bb2a1 | ||
| 
						 | 
					fce8223663 | ||
| 
						 | 
					d1067c60bf | ||
| 
						 | 
					441c26dd32 | ||
| 
						 | 
					00781dd4e1 | ||
| 
						 | 
					c7b8debb6e | ||
| 
						 | 
					d51c383866 | ||
| 
						 | 
					f7d4698ef0 | ||
| 
						 | 
					3fdbbafcb5 | ||
| 
						 | 
					c82cd90ed6 | ||
| 
						 | 
					b6fc8398cf | ||
| 
						 | 
					e90f3b84c1 | ||
| 
						 | 
					9134b6ea98 | ||
| 
						 | 
					4a380ad7fc | ||
| 
						 | 
					c05eb0b1b2 | ||
| 
						 | 
					66feebfc0e | ||
| 
						 | 
					04a609b51f | ||
| 
						 | 
					258cf20c92 | ||
| 
						 | 
					41c8d88217 | ||
| 
						 | 
					37f39c0870 | ||
| 
						 | 
					6ba4f8b54c | ||
| 
						 | 
					c1f8ffa386 | ||
| 
						 | 
					97893d293b | ||
| 
						 | 
					78d1cfb464 | ||
| 
						 | 
					b5fdfe27d5 | ||
| 
						 | 
					cd9fb3b635 | ||
| 
						 | 
					9e9f839d96 | ||
| 
						 | 
					60814ecfe1 | ||
| 
						 | 
					0170c20e9a | ||
| 
						 | 
					01cc2e13d8 | ||
| 
						 | 
					f823f170e6 | ||
| 
						 | 
					be186bd39b | ||
| 
						 | 
					ea25492c28 | ||
| 
						 | 
					79a2bed640 | ||
| 
						 | 
					cd8fc35968 | ||
| 
						 | 
					d2cde379ad | ||
| 
						 | 
					506c41cb15 | ||
| 
						 | 
					72f54ca6c1 | ||
| 
						 | 
					f8d22c486e | ||
| 
						 | 
					c1151b0d45 | ||
| 
						 | 
					0e65fdd6f7 | ||
| 
						 | 
					3164b5ab13 | ||
| 
						 | 
					e6cda79ee8 | ||
| 
						 | 
					45e21d5000 | ||
| 
						 | 
					7e212c4d40 | ||
| 
						 | 
					775aae7082 | ||
| 
						 | 
					a01da2fd92 | ||
| 
						 | 
					cd2fe698bb | ||
| 
						 | 
					4f209e8992 | ||
| 
						 | 
					eb207322d3 | ||
| 
						 | 
					06a2e5fc82 | ||
| 
						 | 
					a8ae23d0a2 | ||
| 
						 | 
					2befb5e784 | ||
| 
						 | 
					6d5e7826ae | ||
| 
						 | 
					c99d4948b7 | ||
| 
						 | 
					ee6f78805f | ||
| 
						 | 
					9a419bd63f | ||
| 
						 | 
					4a139934f6 | ||
| 
						 | 
					2823306810 | ||
| 
						 | 
					4f3b3a273f | ||
| 
						 | 
					6ae3911972 | ||
| 
						 | 
					136aebc009 | ||
| 
						 | 
					6541492a55 | ||
| 
						 | 
					59e9750602 | ||
| 
						 | 
					8ea800205c | ||
| 
						 | 
					59bb9268a1 | ||
| 
						 | 
					a582e7c2fb | ||
| 
						 | 
					6e2669ed1d | ||
| 
						 | 
					7b8a82ce90 | ||
| 
						 | 
					a8bad622ff | ||
| 
						 | 
					9c4f7aa688 | ||
| 
						 | 
					e75b56073b | ||
| 
						 | 
					a95ccc7e4c | ||
| 
						 | 
					8101aceab5 | ||
| 
						 | 
					1c9b19833c | ||
| 
						 | 
					488745f378 | ||
| 
						 | 
					b140e2553b | ||
| 
						 | 
					ae29929714 | ||
| 
						 | 
					9f80909f6a | ||
| 
						 | 
					8201458332 | ||
| 
						 | 
					7a88d80a10 | ||
| 
						 | 
					3ced411769 | ||
| 
						 | 
					f7c346de09 | ||
| 
						 | 
					731ed6952f | ||
| 
						 | 
					454ad6f8bd | ||
| 
						 | 
					8a3b6bf0e6 | ||
| 
						 | 
					fb6e0658cf | ||
| 
						 | 
					f9b8d7a9d8 | ||
| 
						 | 
					f763e1edd7 | ||
| 
						 | 
					dbc3ad1304 | ||
| 
						 | 
					4249e13eb4 | ||
| 
						 | 
					ca7ebd9333 | ||
| 
						 | 
					3f1e6c128f | ||
| 
						 | 
					1f635b90e7 | ||
| 
						 | 
					db3043553c | ||
| 
						 | 
					f87890cb4b | ||
| 
						 | 
					5911594906 | ||
| 
						 | 
					9a1f769828 | ||
| 
						 | 
					b91c0a0616 | ||
| 
						 | 
					4a9f607d31 | ||
| 
						 | 
					a00169451f | ||
| 
						 | 
					ecba959dd9 | ||
| 
						 | 
					a8202d4b37 | ||
| 
						 | 
					657334fb67 | ||
| 
						 | 
					78712245f7 | ||
| 
						 | 
					70702e41e9 | ||
| 
						 | 
					0ca3141088 | ||
| 
						 | 
					ac0970abba | ||
| 
						 | 
					9eeebb147f | ||
| 
						 | 
					dcf8457f4d | ||
| 
						 | 
					534a5ad688 | ||
| 
						 | 
					529cbc0379 | ||
| 
						 | 
					b6aff65997 | ||
| 
						 | 
					b615cce92d | ||
| 
						 | 
					aea631d9d2 | ||
| 
						 | 
					bf942a4cb3 | ||
| 
						 | 
					ceafe389af | ||
| 
						 | 
					f62457a24e | ||
| 
						 | 
					bab4f691c5 | ||
| 
						 | 
					c7becddb78 | ||
| 
						 | 
					cc3660e259 | ||
| 
						 | 
					6e93ff8bca | ||
| 
						 | 
					212d0f24d8 | ||
| 
						 | 
					114003406d | ||
| 
						 | 
					4c99c0127b | ||
| 
						 | 
					c809b33161 | ||
| 
						 | 
					3f1c7da15e | ||
| 
						 | 
					a46695581e | ||
| 
						 | 
					7902d10a3a | ||
| 
						 | 
					8aff2bd74c | ||
| 
						 | 
					352dd907ac | ||
| 
						 | 
					43f195160e | ||
| 
						 | 
					872bfe4757 | ||
| 
						 | 
					70bd493a25 | ||
| 
						 | 
					bd065838fa | ||
| 
						 | 
					5f6e3da766 | ||
| 
						 | 
					ee56b9cd4e | ||
| 
						 | 
					1a27172f20 | ||
| 
						 | 
					4ef1159666 | ||
| 
						 | 
					c924e7c537 | ||
| 
						 | 
					814bd7cb0d | ||
| 
						 | 
					5f2d8c0155 | ||
| 
						 | 
					372f691fd6 | ||
| 
						 | 
					5f05a452fc | ||
| 
						 | 
					afe3283c53 | ||
| 
						 | 
					641a2895a6 | ||
| 
						 | 
					c73c33f94c | ||
| 
						 | 
					6c7da215e7 | ||
| 
						 | 
					754a4a7c8b | ||
| 
						 | 
					0427e8bbb4 | ||
| 
						 | 
					c47f6ed30a | ||
| 
						 | 
					3bdc317fc8 | ||
| 
						 | 
					20cce349e4 | ||
| 
						 | 
					5261162fdf | ||
| 
						 | 
					acf117584b | ||
| 
						 | 
					7b4bbed553 | ||
| 
						 | 
					270ce87582 | ||
| 
						 | 
					2fc0225bc9 | ||
| 
						 | 
					3536cd336d | ||
| 
						 | 
					86dd290c1d | ||
| 
						 | 
					95949b6519 | ||
| 
						 | 
					6499a7298d | ||
| 
						 | 
					042e09d29f | ||
| 
						 | 
					36309e6dbc | ||
| 
						 | 
					e1ac201de1 | ||
| 
						 | 
					f0c4e44d2f | ||
| 
						 | 
					1dc3036822 | ||
| 
						 | 
					a6b399286e | ||
| 
						 | 
					856811bd2e | ||
| 
						 | 
					53273a15bf | ||
| 
						 | 
					3eeb090578 | ||
| 
						 | 
					a9726fde19 | ||
| 
						 | 
					f81d4033fa | ||
| 
						 | 
					5e864ea3b5 | ||
| 
						 | 
					8148bfeacf | ||
| 
						 | 
					1e30718df6 | ||
| 
						 | 
					72e1eb88d9 | ||
| 
						 | 
					8ee5ede834 | ||
| 
						 | 
					bd8b1a2501 | ||
| 
						 | 
					a098167bdb | ||
| 
						 | 
					7790208126 | ||
| 
						 | 
					e52304edb4 | ||
| 
						 | 
					afdd02a80d | ||
| 
						 | 
					c73fdd4022 | ||
| 
						 | 
					4356eefbb1 | ||
| 
						 | 
					6104680caa | ||
| 
						 | 
					4373fdf48c | ||
| 
						 | 
					f9cdfd3e5b | ||
| 
						 | 
					b6c2fc5a69 | ||
| 
						 | 
					cc6610edc2 | ||
| 
						 | 
					7b8ddfdd96 | ||
| 
						 | 
					443a5ca0c2 | ||
| 
						 | 
					3794b5cb58 | ||
| 
						 | 
					6cb5377d73 | ||
| 
						 | 
					a3a92ff1df | ||
| 
						 | 
					2068efdb38 | ||
| 
						 | 
					258ca1b434 | ||
| 
						 | 
					103fa959cb | ||
| 
						 | 
					b6d48b7a14 | ||
| 
						 | 
					412e4e6cf9 | ||
| 
						 | 
					e6f81173a3 | ||
| 
						 | 
					d50281453d | ||
| 
						 | 
					4bb488258d | ||
| 
						 | 
					f6da19ba83 | ||
| 
						 | 
					88bb7b780d | ||
| 
						 | 
					3805e5d37e | ||
| 
						 | 
					a2d6daaef4 | ||
| 
						 | 
					48e9006cd1 | ||
| 
						 | 
					a25a4b5d11 | ||
| 
						 | 
					309bec474f | ||
| 
						 | 
					d36440a06d | ||
| 
						 | 
					2a2f772412 | ||
| 
						 | 
					6a524bff9d | ||
| 
						 | 
					5def1169db | ||
| 
						 | 
					a07395fb56 | ||
| 
						 | 
					e25a375f43 | ||
| 
						 | 
					96801e3478 | ||
| 
						 | 
					90100aa169 | ||
| 
						 | 
					415f375ce6 | ||
| 
						 | 
					94e9844179 | ||
| 
						 | 
					50d238b60f | ||
| 
						 | 
					5c3b41bd93 | ||
| 
						 | 
					3e0971c159 | ||
| 
						 | 
					57740fbd18 | ||
| 
						 | 
					16bc8f7040 | ||
| 
						 | 
					c145f24621 | ||
| 
						 | 
					cd3a4573f2 | ||
| 
						 | 
					8eab77f3c6 | ||
| 
						 | 
					4af339424a | ||
| 
						 | 
					4dd69a8b1a | ||
| 
						 | 
					baa1160594 | ||
| 
						 | 
					f438ff4bab | ||
| 
						 | 
					a25b2af66c | ||
| 
						 | 
					31b67ab92e | ||
| 
						 | 
					5303b36913 | ||
| 
						 | 
					a5c56c547d | ||
| 
						 | 
					ccf9a9976c | ||
| 
						 | 
					ab1efd923b | ||
| 
						 | 
					d6780f9e49 | ||
| 
						 | 
					8534e3b2f7 | ||
| 
						 | 
					8e6cf669ad | ||
| 
						 | 
					577380e98e | ||
| 
						 | 
					235b5b0c15 | ||
| 
						 | 
					aefed1d1b9 | ||
| 
						 | 
					d39649f30d | ||
| 
						 | 
					1546b7e5a9 | ||
| 
						 | 
					ff74778dea | ||
| 
						 | 
					29b21b828b | ||
| 
						 | 
					a6a0495392 | ||
| 
						 | 
					3281043e27 | ||
| 
						 | 
					f7299403f7 | ||
| 
						 | 
					a0d251a336 | ||
| 
						 | 
					8ca45d3d03 | ||
| 
						 | 
					d988542afc | ||
| 
						 | 
					4285d81ca9 | ||
| 
						 | 
					4286b2917e | ||
| 
						 | 
					d09b5cb80e | ||
| 
						 | 
					90fd18bf42 | ||
| 
						 | 
					cae6c8e5f5 | ||
| 
						 | 
					473340c53b | ||
| 
						 | 
					ae302ee600 | ||
| 
						 | 
					27934ac4ca | ||
| 
						 | 
					283ef9adb7 | ||
| 
						 | 
					377fe5ecde | ||
| 
						 | 
					c848d3ee22 | ||
| 
						 | 
					eb6be88fac | ||
| 
						 | 
					fceb728501 | ||
| 
						 | 
					a0df46258d | ||
| 
						 | 
					57d1db58db | ||
| 
						 | 
					d61b687853 | ||
| 
						 | 
					42ab7a5d72 | ||
| 
						 | 
					18cb11dcbf | ||
| 
						 | 
					e9d5407792 | ||
| 
						 | 
					a09b2c0074 | ||
| 
						 | 
					a460ac021f | ||
| 
						 | 
					bdee66fe29 | ||
| 
						 | 
					84a251c8c7 | ||
| 
						 | 
					cea763bb11 | ||
| 
						 | 
					accbda9d2f | ||
| 
						 | 
					7d64e141e4 | ||
| 
						 | 
					6a9b4db448 | ||
| 
						 | 
					e6a95ecd08 | ||
| 
						 | 
					10cb7585a7 | ||
| 
						 | 
					a577c7215f | ||
| 
						 | 
					e5244cf3c0 | ||
| 
						 | 
					c4d0aec536 | ||
| 
						 | 
					796647158e | ||
| 
						 | 
					266e9d0619 | ||
| 
						 | 
					9ec54ef89b | ||
| 
						 | 
					256cb90f3c | ||
| 
						 | 
					8f3a3b293d | ||
| 
						 | 
					975a7359a2 | ||
| 
						 | 
					ac7361a55e | ||
| 
						 | 
					bd41f50ba5 | ||
| 
						 | 
					a2038ab07e | ||
| 
						 | 
					6445a7674b | ||
| 
						 | 
					3bf2e89a1a | ||
| 
						 | 
					e6a13bf386 | ||
| 
						 | 
					42b2adc03e | ||
| 
						 | 
					f2b9af01e8 | ||
| 
						 | 
					9bd2d92755 | ||
| 
						 | 
					babe884b7c | ||
| 
						 | 
					200287254b | ||
| 
						 | 
					0b797f5964 | ||
| 
						 | 
					13a8c309f5 | ||
| 
						 | 
					528d2f29d3 | ||
| 
						 | 
					0aba5dc8de | ||
| 
						 | 
					72fcf5ab85 | ||
| 
						 | 
					dd9df068b0 | ||
| 
						 | 
					81772fb703 | ||
| 
						 | 
					6304566603 | ||
| 
						 | 
					5f8b60a0e5 | ||
| 
						 | 
					4964e075df | ||
| 
						 | 
					e2accaf70e | ||
| 
						 | 
					7b32bbfc26 | ||
| 
						 | 
					6963f3880d | ||
| 
						 | 
					6c3430b6e5 | ||
| 
						 | 
					c53f36a777 | ||
| 
						 | 
					bb4d378733 | ||
| 
						 | 
					333090a967 | ||
| 
						 | 
					c8f5646d53 | ||
| 
						 | 
					3002f6dfd5 | ||
| 
						 | 
					3f8a50e2ae | ||
| 
						 | 
					1a504118e5 | ||
| 
						 | 
					2e602ef6b0 | ||
| 
						 | 
					d55c3faaeb | ||
| 
						 | 
					6b185d20c0 | ||
| 
						 | 
					1bbc33a0cf | ||
| 
						 | 
					a71eba07a1 | ||
| 
						 | 
					422dd1fa4f | ||
| 
						 | 
					c4b2e5829e | ||
| 
						 | 
					1be222f6ed | ||
| 
						 | 
					5463f459e6 | ||
| 
						 | 
					14d7bfdab2 | ||
| 
						 | 
					ea722da3de | ||
| 
						 | 
					9aed1e2d17 | ||
| 
						 | 
					63c6ed3fd0 | ||
| 
						 | 
					744bfc1982 | ||
| 
						 | 
					d04434e3ec | ||
| 
						 | 
					dc3b7b5775 | ||
| 
						 | 
					17fbfd14db | ||
| 
						 | 
					d0f5aece5f | ||
| 
						 | 
					eeda3062e1 | ||
| 
						 | 
					168d712dec | ||
| 
						 | 
					e64ad5176e | ||
| 
						 | 
					0e1469f359 | ||
| 
						 | 
					7d2b6cfeaf | ||
| 
						 | 
					8e7d029946 | ||
| 
						 | 
					9dd62ae0f8 | ||
| 
						 | 
					e7dff4756f | ||
| 
						 | 
					fa98d72f3a | ||
| 
						 | 
					ccf0492890 | ||
| 
						 | 
					eb0ef6bd3d | ||
| 
						 | 
					19aa2ccf4d | ||
| 
						 | 
					841b762796 | ||
| 
						 | 
					63fb90806c | ||
| 
						 | 
					88ada80686 | ||
| 
						 | 
					7d5ddf5e6a | ||
| 
						 | 
					8f01919f62 | ||
| 
						 | 
					98394f99b5 | ||
| 
						 | 
					cae50e16a7 | ||
| 
						 | 
					4a2ac7bd2e | ||
| 
						 | 
					a3bdaa85f2 | ||
| 
						 | 
					40ef86f475 | ||
| 
						 | 
					4cee14f3c5 | ||
| 
						 | 
					48d9a8c180 | ||
| 
						 | 
					1d384e3192 | ||
| 
						 | 
					87f3dc4558 | ||
| 
						 | 
					8afec596aa | ||
| 
						 | 
					26e3263aec | ||
| 
						 | 
					08b4e1a744 | ||
| 
						 | 
					d68f0999a4 | ||
| 
						 | 
					2c9ed4c565 | ||
| 
						 | 
					be4f87c760 | ||
| 
						 | 
					b963dadc14 | ||
| 
						 | 
					26e7fd8b80 | ||
| 
						 | 
					b9a972bccd | ||
| 
						 | 
					bb7b9280d3 | ||
| 
						 | 
					395fbbfd14 | ||
| 
						 | 
					896dfe3def | ||
| 
						 | 
					6c4f33910c | ||
| 
						 | 
					0a301cdd21 | ||
| 
						 | 
					ce59fc6c10 | ||
| 
						 | 
					cbb74c984f | ||
| 
						 | 
					287623df58 | ||
| 
						 | 
					f8c1d97a25 | ||
| 
						 | 
					2a188905da | ||
| 
						 | 
					c2b1e38d7f | ||
| 
						 | 
					fcc0aef7f4 | ||
| 
						 | 
					eaa3de2dce | ||
| 
						 | 
					f3dc5dd12f | ||
| 
						 | 
					d2f0178fab | ||
| 
						 | 
					326c386b2e | ||
| 
						 | 
					6e68c4e2d6 | ||
| 
						 | 
					a79e96802f | ||
| 
						 | 
					65b22b493c | ||
| 
						 | 
					b73f5a4e94 | ||
| 
						 | 
					3b74ac841e | ||
| 
						 | 
					253bf776b5 | ||
| 
						 | 
					eef4acd07d | ||
| 
						 | 
					b6f00ea241 | ||
| 
						 | 
					0bd4a4f98f | ||
| 
						 | 
					0b52645bb6 | ||
| 
						 | 
					8e845d9f21 | ||
| 
						 | 
					d29aa43ba4 | ||
| 
						 | 
					450efea191 | ||
| 
						 | 
					241cfc4342 | ||
| 
						 | 
					7c67e3d7e2 | ||
| 
						 | 
					674790a511 | ||
| 
						 | 
					4e3c82e329 | ||
| 
						 | 
					df711b0ea2 | ||
| 
						 | 
					1019fd9a9d | ||
| 
						 | 
					8a420dd853 | ||
| 
						 | 
					f8bcfeb2ab | ||
| 
						 | 
					34e5beda6a | ||
| 
						 | 
					6185244754 | ||
| 
						 | 
					60d9509e39 | ||
| 
						 | 
					ded4469efe | ||
| 
						 | 
					1f95d8eedf | ||
| 
						 | 
					aa66dfff57 | ||
| 
						 | 
					25263ce40f | ||
| 
						 | 
					e85deb54e1 | ||
| 
						 | 
					4750fd159e | ||
| 
						 | 
					7eea9533e8 | ||
| 
						 | 
					ec675b9ad2 | ||
| 
						 | 
					486e77f474 | ||
| 
						 | 
					048059ba1f | ||
| 
						 | 
					ed3dda7da9 | ||
| 
						 | 
					fa93d68b08 | ||
| 
						 | 
					6093a4f9f8 | ||
| 
						 | 
					4e20d89d9c | ||
| 
						 | 
					b420ec6cb9 | ||
| 
						 | 
					375f6101e9 | ||
| 
						 | 
					2844d73dc7 | ||
| 
						 | 
					6c1176f853 | ||
| 
						 | 
					df037db0bb | ||
| 
						 | 
					949cc7d21b | ||
| 
						 | 
					9244529007 | ||
| 
						 | 
					319d49ddbe | ||
| 
						 | 
					96fcfdb6c6 | ||
| 
						 | 
					d61ef6b49a | ||
| 
						 | 
					804a6c8d47 | ||
| 
						 | 
					c487cd6af2 | ||
| 
						 | 
					6a2592a9d9 | ||
| 
						 | 
					0f48b15695 | ||
| 
						 | 
					4320b8a5a5 | ||
| 
						 | 
					a20707cd73 | ||
| 
						 | 
					5da1d3b73b | ||
| 
						 | 
					be15e63d41 | ||
| 
						 | 
					9bc5f686eb | ||
| 
						 | 
					5bed21dace | ||
| 
						 | 
					a97e651582 | ||
| 
						 | 
					dff641a665 | ||
| 
						 | 
					47a25cc3e8 | ||
| 
						 | 
					5e3a5f627a | ||
| 
						 | 
					9201e0a5b9 | ||
| 
						 | 
					4c80ed3208 | ||
| 
						 | 
					f34579e921 | ||
| 
						 | 
					cc1d3b20b6 | ||
| 
						 | 
					8c56356459 | ||
| 
						 | 
					21f728f0ea | ||
| 
						 | 
					8051b6e8b6 | ||
| 
						 | 
					4c38fec3b5 | ||
| 
						 | 
					c4cdcf44c5 | ||
| 
						 | 
					347dab0c14 | ||
| 
						 | 
					a3d3ea2b4b | ||
| 
						 | 
					5332387125 | ||
| 
						 | 
					5b21cbe0de | ||
| 
						 | 
					2f4111a2e2 | ||
| 
						 | 
					326ac485b3 | ||
| 
						 | 
					f9b419d1e4 | ||
| 
						 | 
					f4e81953ce | ||
| 
						 | 
					2b09253961 | ||
| 
						 | 
					1994c6828e | ||
| 
						 | 
					f5c381d5b4 | ||
| 
						 | 
					7e2af8364f | ||
| 
						 | 
					0a2ab2aed2 | ||
| 
						 | 
					2310a9bbc0 | ||
| 
						 | 
					5b3e3d9cf4 | ||
| 
						 | 
					c97c79ab2f | ||
| 
						 | 
					1231b71245 | ||
| 
						 | 
					824ffa24f4 | ||
| 
						 | 
					148f869bec | ||
| 
						 | 
					c140fe9bae | ||
| 
						 | 
					4b02ee5b46 | ||
| 
						 | 
					de3bac53bf | ||
| 
						 | 
					3f1a76d9e4 | ||
| 
						 | 
					0138e167e9 | ||
| 
						 | 
					bcd2ee6204 | ||
| 
						 | 
					4c1d521711 | ||
| 
						 | 
					90c70fa5bf | ||
| 
						 | 
					8e15c48092 | ||
| 
						 | 
					9cf65e31cd | ||
| 
						 | 
					a6e5876d96 | ||
| 
						 | 
					937e723036 | ||
| 
						 | 
					3a439063a6 | ||
| 
						 | 
					12d876a005 | ||
| 
						 | 
					4e2426a2b4 | ||
| 
						 | 
					020f9cd2a6 | ||
| 
						 | 
					d7eebe9df0 | ||
| 
						 | 
					cebc5bf9fc | ||
| 
						 | 
					dbe68684a0 | ||
| 
						 | 
					27a05ff271 | ||
| 
						 | 
					1489ddc49a | ||
| 
						 | 
					9be2c1beb9 | ||
| 
						 | 
					9964e6eba3 | ||
| 
						 | 
					8bcc19d91e | ||
| 
						 | 
					3c07f57aad | ||
| 
						 | 
					3262a916e0 | ||
| 
						 | 
					7883cc5891 | ||
| 
						 | 
					ded7a5438c | ||
| 
						 | 
					cd98951001 | ||
| 
						 | 
					8470c60e06 | ||
| 
						 | 
					394b1002b3 | ||
| 
						 | 
					9a61d6293d | ||
| 
						 | 
					192ad27f8f | ||
| 
						 | 
					eb0fc67461 | ||
| 
						 | 
					adbe5e9048 | ||
| 
						 | 
					fcdf41ba29 | ||
| 
						 | 
					7b40cbe8c1 | ||
| 
						 | 
					0933929cfe | ||
| 
						 | 
					7f618e7ecc | ||
| 
						 | 
					482cb73702 | ||
| 
						 | 
					fd1598017a | ||
| 
						 | 
					96f79475f1 | ||
| 
						 | 
					3576754c21 | ||
| 
						 | 
					6c3a0bc72c | ||
| 
						 | 
					49d75a0cd4 | ||
| 
						 | 
					756dbac39f | ||
| 
						 | 
					7df062b7d7 | ||
| 
						 | 
					e485e8f60d | ||
| 
						 | 
					fde8ea081a | ||
| 
						 | 
					f7217c5f26 | ||
| 
						 | 
					fd77e463a1 | ||
| 
						 | 
					6d7f6750e9 | ||
| 
						 | 
					695482ded7 | ||
| 
						 | 
					fc9649dbc4 | ||
| 
						 | 
					afb67d375f | ||
| 
						 | 
					66e38ae69e | ||
| 
						 | 
					1f4e64f81d | ||
| 
						 | 
					ad541f713d | ||
| 
						 | 
					f3b434397b | ||
| 
						 | 
					1d5d49312c | ||
| 
						 | 
					232c7361a9 | ||
| 
						 | 
					721543653b | ||
| 
						 | 
					d8ba26e664 | ||
| 
						 | 
					7ecabeac97 | ||
| 
						 | 
					6cf7be4b7e | ||
| 
						 | 
					5f2e56674c | ||
| 
						 | 
					58d4c74b0b | ||
| 
						 | 
					e137792efd | ||
| 
						 | 
					b7b934913e | ||
| 
						 | 
					cd8fcbf9c6 | ||
| 
						 | 
					d0300d4443 | ||
| 
						 | 
					40acd9a4c3 | ||
| 
						 | 
					e66b4d5390 | ||
| 
						 | 
					f94433e504 | ||
| 
						 | 
					7f32488b78 | ||
| 
						 | 
					8f73e24175 | ||
| 
						 | 
					f3c984281c | ||
| 
						 | 
					00777a10ae | ||
| 
						 | 
					0d6ce9f977 | ||
| 
						 | 
					997c517ba2 | ||
| 
						 | 
					849bed4bef | ||
| 
						 | 
					61a48a5b9f | ||
| 
						 | 
					df14085ec8 | ||
| 
						 | 
					00b34eb2a4 | ||
| 
						 | 
					84a6730b1a | ||
| 
						 | 
					357b514bc9 | ||
| 
						 | 
					d39b108274 | ||
| 
						 | 
					39f3239682 | ||
| 
						 | 
					506f36b26d | ||
| 
						 | 
					4cedbf80df | ||
| 
						 | 
					b54ce31078 | ||
| 
						 | 
					9b2aa974ba | ||
| 
						 | 
					291c97dc81 | ||
| 
						 | 
					bcbecff6f6 | ||
| 
						 | 
					3d22708f67 | ||
| 
						 | 
					8afd31902f | ||
| 
						 | 
					89f66ebf6d | ||
| 
						 | 
					d1a2208196 | ||
| 
						 | 
					42d1fe5422 | ||
| 
						 | 
					1424e8a2de | ||
| 
						 | 
					7dc548b4b8 | ||
| 
						 | 
					2c37d94611 | ||
| 
						 | 
					b1e4a7c615 | ||
| 
						 | 
					b0561058c6 | ||
| 
						 | 
					d039295070 | ||
| 
						 | 
					4ddafb8e84 | ||
| 
						 | 
					a536231ded | ||
| 
						 | 
					13fe54c938 | ||
| 
						 | 
					d54ffdd187 | ||
| 
						 | 
					5c539af7d7 | ||
| 
						 | 
					47d9a9cf20 | ||
| 
						 | 
					7f25205aeb | ||
| 
						 | 
					688973fa44 | ||
| 
						 | 
					400661d432 | ||
| 
						 | 
					7ebecf3851 | ||
| 
						 | 
					0a5eaec0f2 | ||
| 
						 | 
					4bdab73dd5 | ||
| 
						 | 
					f19f21007c | ||
| 
						 | 
					e4e60ed654 | ||
| 
						 | 
					f21dd9117d | ||
| 
						 | 
					2aa75f034f | ||
| 
						 | 
					5c78e0a462 | ||
| 
						 | 
					52cdedcba0 | ||
| 
						 | 
					c87cd0de73 | ||
| 
						 | 
					12530655df | ||
| 
						 | 
					7479706b29 | ||
| 
						 | 
					070a141601 | ||
| 
						 | 
					7044236824 | ||
| 
						 | 
					3e9478b58d | ||
| 
						 | 
					5ffca2d138 | ||
| 
						 | 
					a8d4a98621 | ||
| 
						 | 
					905f7f4ecc | ||
| 
						 | 
					819d2bc560 | ||
| 
						 | 
					c0b2027588 | ||
| 
						 | 
					136d1b04b5 | ||
| 
						 | 
					f063dd195e | ||
| 
						 | 
					be972fc0b5 | ||
| 
						 | 
					ff90a5d321 | ||
| 
						 | 
					ac11ba3d60 | ||
| 
						 | 
					3d8598654c | ||
| 
						 | 
					55a5da2102 | ||
| 
						 | 
					f881d6c44f | ||
| 
						 | 
					5df2ca3ef3 | ||
| 
						 | 
					a15f87ae39 | ||
| 
						 | 
					0dd6377fe6 | ||
| 
						 | 
					c7257e0a3c | ||
| 
						 | 
					3b7fbcd0c3 | ||
| 
						 | 
					5fe91af6c3 | ||
| 
						 | 
					4dd646a424 | ||
| 
						 | 
					dcbd90ce04 | ||
| 
						 | 
					04683338a2 | ||
| 
						 | 
					b778f9c40e | ||
| 
						 | 
					e0a96be378 | ||
| 
						 | 
					ac690fceaf | ||
| 
						 | 
					1e5e03cc46 | ||
| 
						 | 
					d3c4cd8270 | ||
| 
						 | 
					17361df66b | ||
| 
						 | 
					7d0452c7e3 | ||
| 
						 | 
					cee0ab87fc | ||
| 
						 | 
					dd0b0cae93 | ||
| 
						 | 
					c4bf5eef73 | ||
| 
						 | 
					2587639914 | ||
| 
						 | 
					bfa1ae59cc | ||
| 
						 | 
					9e8575c315 | ||
| 
						 | 
					e36340ce64 | ||
| 
						 | 
					63ec05a66c | ||
| 
						 | 
					6f1c72f5b4 | ||
| 
						 | 
					27924ffd5b | ||
| 
						 | 
					527029574c | ||
| 
						 | 
					cf3aeafcdb | ||
| 
						 | 
					f08a79d372 | ||
| 
						 | 
					eb2a26cfaa | ||
| 
						 | 
					5378d9ca26 | ||
| 
						 | 
					bce11af09a | ||
| 
						 | 
					158abf5c6c | ||
| 
						 | 
					b30c1daf72 | ||
| 
						 | 
					0649206796 | ||
| 
						 | 
					f589a1d245 | ||
| 
						 | 
					8a604bd2a1 | ||
| 
						 | 
					68eb627d62 | ||
| 
						 | 
					9c87a5890d | ||
| 
						 | 
					b8a8e2280d | ||
| 
						 | 
					e538a13e28 | ||
| 
						 | 
					6fb2a1ed39 | ||
| 
						 | 
					6b00787f45 | ||
| 
						 | 
					e735d8d4e5 | ||
| 
						 | 
					8cd4fd0b50 | ||
| 
						 | 
					8f4b0559ce | ||
| 
						 | 
					5288c54aad | ||
| 
						 | 
					51f8bec81b | ||
| 
						 | 
					49d3e5d324 | ||
| 
						 | 
					1fff0e5592 | ||
| 
						 | 
					8d53ec5353 | ||
| 
						 | 
					a1e1bfc71b | ||
| 
						 | 
					29992f54a3 | ||
| 
						 | 
					4ab6786163 | ||
| 
						 | 
					eaad34a69a | ||
| 
						 | 
					839f18d052 | ||
| 
						 | 
					58ef6d8385 | ||
| 
						 | 
					a0037c9333 | ||
| 
						 | 
					177b57e1c0 | ||
| 
						 | 
					810c129ca9 | ||
| 
						 | 
					f731a4c704 | ||
| 
						 | 
					39a1f1ef64 | ||
| 
						 | 
					d24a87caf1 | ||
| 
						 | 
					9683ffe13a | ||
| 
						 | 
					fab2d9dc6a | ||
| 
						 | 
					59f7a2f6ef | ||
| 
						 | 
					9b12407028 | ||
| 
						 | 
					96c4bb7fd0 | ||
| 
						 | 
					81532f375e | ||
| 
						 | 
					79eb8e2b35 | ||
| 
						 | 
					7c2e875494 | ||
| 
						 | 
					c715b4637d | ||
| 
						 | 
					c719a61ea7 | ||
| 
						 | 
					58e4d337e4 | ||
| 
						 | 
					509c802045 | ||
| 
						 | 
					4fd63f4e30 | ||
| 
						 | 
					f5c28c72fd | ||
| 
						 | 
					342128a457 | ||
| 
						 | 
					b1931828e1 | ||
| 
						 | 
					4410226db1 | ||
| 
						 | 
					945bd4b1b0 | ||
| 
						 | 
					77f1ea40cd | ||
| 
						 | 
					98c8c7ce0d | ||
| 
						 | 
					795bf9e101 | ||
| 
						 | 
					851fedf751 | ||
| 
						 | 
					5be6ab8c89 | ||
| 
						 | 
					3670412c7c | ||
| 
						 | 
					e6cd596dc9 | ||
| 
						 | 
					abf4278d09 | ||
| 
						 | 
					2b2b65fe18 | ||
| 
						 | 
					3d6a125bdc | ||
| 
						 | 
					9efd40a366 | ||
| 
						 | 
					383fa8401d | ||
| 
						 | 
					bf8ffade29 | ||
| 
						 | 
					1633d14547 | ||
| 
						 | 
					e55605dbe9 | ||
| 
						 | 
					cdec38ba12 | ||
| 
						 | 
					1cb6e9e7d0 | ||
| 
						 | 
					d1d2f6f451 | ||
| 
						 | 
					743f821f1e | ||
| 
						 | 
					b95a99e0c2 | ||
| 
						 | 
					3ebbeb103c | ||
| 
						 | 
					0d4035e996 | ||
| 
						 | 
					88ed5e506a | ||
| 
						 | 
					76a3371b40 | ||
| 
						 | 
					7b2fa1edb4 | ||
| 
						 | 
					1965035166 | ||
| 
						 | 
					76d4ff056a | ||
| 
						 | 
					a239a9efd5 | ||
| 
						 | 
					4747e7c5b9 | ||
| 
						 | 
					35ca729cb9 | ||
| 
						 | 
					a0fa7421d1 | ||
| 
						 | 
					ddf293bbcd | ||
| 
						 | 
					45d6e00ff1 | ||
| 
						 | 
					6dfc8fe0ea | ||
| 
						 | 
					96f106d6aa | ||
| 
						 | 
					044da37c95 | ||
| 
						 | 
					e0b00ee11a | ||
| 
						 | 
					086444c73a | ||
| 
						 | 
					1efb2085e9 | ||
| 
						 | 
					e1c42eb6cc | ||
| 
						 | 
					a2da26cbdd | ||
| 
						 | 
					8796adfd63 | ||
| 
						 | 
					95a6c28d98 | ||
| 
						 | 
					85e1f4ea13 | ||
| 
						 | 
					d9c9114b3b | ||
| 
						 | 
					02140ce763 | ||
| 
						 | 
					af1cc3b331 | ||
| 
						 | 
					e852044b64 | ||
| 
						 | 
					f845b371ce | ||
| 
						 | 
					3a1bd3114b | ||
| 
						 | 
					a6d2e3a1e6 | ||
| 
						 | 
					93bce1b24c | ||
| 
						 | 
					7b87f29c9c | ||
| 
						 | 
					cb6f622957 | ||
| 
						 | 
					43d3b51bde | ||
| 
						 | 
					57e015155a | ||
| 
						 | 
					2436d7e0ba | ||
| 
						 | 
					e6e85b0c55 | ||
| 
						 | 
					e43fd39594 | ||
| 
						 | 
					52765466c1 | ||
| 
						 | 
					fa9afb0860 | ||
| 
						 | 
					e02bede4f5 | ||
| 
						 | 
					113089be5d | ||
| 
						 | 
					712b895170 | ||
| 
						 | 
					dca163f54d | ||
| 
						 | 
					fc6cf4d963 | ||
| 
						 | 
					4126c7e188 | ||
| 
						 | 
					db50462920 | ||
| 
						 | 
					9310b44cef | ||
| 
						 | 
					6480250221 | ||
| 
						 | 
					710ce7c2e9 | ||
| 
						 | 
					b88e64f0d0 | ||
| 
						 | 
					c70432996a | ||
| 
						 | 
					c6dcf2a0e2 | ||
| 
						 | 
					2aec627503 | ||
| 
						 | 
					b7924ce58b | ||
| 
						 | 
					0bc745f68f | ||
| 
						 | 
					8acdf823a2 | ||
| 
						 | 
					52f8b787c9 | ||
| 
						 | 
					ad153ae041 | ||
| 
						 | 
					2527f8f599 | ||
| 
						 | 
					72af092cc1 | ||
| 
						 | 
					2751060b91 | ||
| 
						 | 
					b3449db2f8 | ||
| 
						 | 
					7db28745c8 | ||
| 
						 | 
					6921211461 | ||
| 
						 | 
					9f90618a70 | ||
| 
						 | 
					302c41edc9 | ||
| 
						 | 
					5d943a35f8 | ||
| 
						 | 
					03f8d6e946 | ||
| 
						 | 
					68a35155e4 | ||
| 
						 | 
					9d725af602 | ||
| 
						 | 
					d04ccb7a3f | ||
| 
						 | 
					76c1ed6628 | ||
| 
						 | 
					6f4abe95cb | ||
| 
						 | 
					e35e313240 | ||
| 
						 | 
					3a77a6eded | ||
| 
						 | 
					0a7a1b9bfb | ||
| 
						 | 
					3e1418d662 | ||
| 
						 | 
					884f70fb39 | ||
| 
						 | 
					9499a1142b | ||
| 
						 | 
					6e8dcdce78 | ||
| 
						 | 
					ce9fae82bd | ||
| 
						 | 
					afa3fc8bf9 | ||
| 
						 | 
					09eccf6fc0 | ||
| 
						 | 
					edfefb6763 | ||
| 
						 | 
					98b3dcbf37 | ||
| 
						 | 
					2698ef6c5f | ||
| 
						 | 
					46b2ee3bae | ||
| 
						 | 
					e7ee3a7dd5 | ||
| 
						 | 
					0085e6f83b | ||
| 
						 | 
					c90fa3bcfc | ||
| 
						 | 
					0ec9b9823f | ||
| 
						 | 
					50d7b8452d | ||
| 
						 | 
					d88f8e862b | ||
| 
						 | 
					fd72cced13 | ||
| 
						 | 
					18a90734d9 | ||
| 
						 | 
					a4b2cebef6 | ||
| 
						 | 
					68d708e56d | ||
| 
						 | 
					e925ab0999 | ||
| 
						 | 
					a6feb0a887 | ||
| 
						 | 
					cd9c3a79e5 | ||
| 
						 | 
					f3685bdef9 | ||
| 
						 | 
					e2edf20833 | ||
| 
						 | 
					31e39f1f6d | ||
| 
						 | 
					d47c5eaf60 | ||
| 
						 | 
					b8e515a738 | ||
| 
						 | 
					0984585d58 | ||
| 
						 | 
					f3e0eb705b | ||
| 
						 | 
					04e0f87c03 | ||
| 
						 | 
					8ccfcd272f | ||
| 
						 | 
					ba69062a36 | ||
| 
						 | 
					cea24b5f70 | ||
| 
						 | 
					f3a71f62a1 | ||
| 
						 | 
					4e4a6d8397 | ||
| 
						 | 
					d8beaf727f | ||
| 
						 | 
					c961c865ee | ||
| 
						 | 
					34f25fa590 | ||
| 
						 | 
					7af1155c11 | ||
| 
						 | 
					c9d7daab70 | ||
| 
						 | 
					e3feac3fd8 | ||
| 
						 | 
					1a5989350f | ||
| 
						 | 
					3365df7778 | ||
| 
						 | 
					3be5a68e12 | ||
| 
						 | 
					f158caa2eb | ||
| 
						 | 
					e02c94eb00 | ||
| 
						 | 
					981077516b | ||
| 
						 | 
					7c488b5913 | ||
| 
						 | 
					0e44f587a5 | ||
| 
						 | 
					e593d86b80 | ||
| 
						 | 
					339a8ad610 | ||
| 
						 | 
					d1190febec | ||
| 
						 | 
					5d2c5b01a8 | ||
| 
						 | 
					0f954c20ff | ||
| 
						 | 
					d6edff3182 | ||
| 
						 | 
					dba26c3240 | ||
| 
						 | 
					0ca5b7996c | ||
| 
						 | 
					9507b121ac | ||
| 
						 | 
					3812b275e9 | ||
| 
						 | 
					6c1561f415 | ||
| 
						 | 
					9ab6353d73 | ||
| 
						 | 
					ff60dc4d24 | ||
| 
						 | 
					62e7d904b4 | ||
| 
						 | 
					5d3de4b670 | ||
| 
						 | 
					7d75ad4c56 | ||
| 
						 | 
					63e1d3610c | ||
| 
						 | 
					b4325026b1 | ||
| 
						 | 
					b072ef2e82 | ||
| 
						 | 
					562a4c056e | ||
| 
						 | 
					bb8248556d | ||
| 
						 | 
					b22b085b50 | ||
| 
						 | 
					e82ea94bb6 | ||
| 
						 | 
					a50252382a | ||
| 
						 | 
					9bdb799b41 | ||
| 
						 | 
					989651c23b | ||
| 
						 | 
					259cefab72 | ||
| 
						 | 
					09ed421948 | ||
| 
						 | 
					87f5ec5be5 | ||
| 
						 | 
					f78b656f5f | ||
| 
						 | 
					e6b940e247 | ||
| 
						 | 
					50a9680f17 | ||
| 
						 | 
					f2a6dc4dfd | ||
| 
						 | 
					67e3dd36b3 | ||
| 
						 | 
					6c4cc357b5 | ||
| 
						 | 
					94965a418f | ||
| 
						 | 
					40e6ba1100 | ||
| 
						 | 
					0aed065a75 | ||
| 
						 | 
					70b63a5ed4 | ||
| 
						 | 
					1476a9ecf1 | ||
| 
						 | 
					600a235140 | ||
| 
						 | 
					ffa9f104db | ||
| 
						 | 
					b807ec0956 | ||
| 
						 | 
					5f8daeeb6d | ||
| 
						 | 
					aec8307417 | ||
| 
						 | 
					e96a1b01b9 | ||
| 
						 | 
					d78ba322bf | ||
| 
						 | 
					ab5c1b0a3a | ||
| 
						 | 
					53fa16d39f | ||
| 
						 | 
					05cf405cb5 | ||
| 
						 | 
					5d833336d3 | ||
| 
						 | 
					13be9d8d79 | ||
| 
						 | 
					bb6326f4d4 | ||
| 
						 | 
					c070407ab2 | ||
| 
						 | 
					6b27bdadcd | ||
| 
						 | 
					1c22c2f76a | ||
| 
						 | 
					aa262d4124 | ||
| 
						 | 
					646c0bfcb9 | ||
| 
						 | 
					da68775472 | ||
| 
						 | 
					bcf96608d1 | ||
| 
						 | 
					dd17ac5045 | ||
| 
						 | 
					59182dbc97 | ||
| 
						 | 
					11df7187b3 | ||
| 
						 | 
					0899803294 | ||
| 
						 | 
					4742e0951c | ||
| 
						 | 
					c3b1eb0837 | ||
| 
						 | 
					542d7977db | ||
| 
						 | 
					11927a768e | ||
| 
						 | 
					ecd5bcec0c | ||
| 
						 | 
					911a2084d4 | ||
| 
						 | 
					839bf0e2c9 | ||
| 
						 | 
					25555b8c3e | ||
| 
						 | 
					cd7ac94f35 | ||
| 
						 | 
					68aea3af9e | ||
| 
						 | 
					cdb3216cc9 | ||
| 
						 | 
					78f0201dfa | ||
| 
						 | 
					2cd47b0240 | ||
| 
						 | 
					be83a6a37a | ||
| 
						 | 
					8ca99e85b0 | ||
| 
						 | 
					f5b546b3c8 | ||
| 
						 | 
					50c9d17830 | ||
| 
						 | 
					d87af4788e | ||
| 
						 | 
					0f21537f14 | ||
| 
						 | 
					0533bde9f2 | ||
| 
						 | 
					32b3717c32 | ||
| 
						 | 
					d950f801af | ||
| 
						 | 
					a8ab028b84 | ||
| 
						 | 
					2aff36e74b | ||
| 
						 | 
					db7e4bf940 | ||
| 
						 | 
					ee20015d44 | ||
| 
						 | 
					80941f8413 | ||
| 
						 | 
					cf909db159 | ||
| 
						 | 
					27dbe77fad | ||
| 
						 | 
					38f2334360 | ||
| 
						 | 
					6489a48e1f | ||
| 
						 | 
					ca24d1762e | ||
| 
						 | 
					2f4b84c074 | ||
| 
						 | 
					6ae810a1fa | ||
| 
						 | 
					3e3f695536 | ||
| 
						 | 
					3ca93f4a4c | ||
| 
						 | 
					f2d9930773 | ||
| 
						 | 
					2fbf399156 | ||
| 
						 | 
					5fe91d6577 | ||
| 
						 | 
					753d0e7df7 | ||
| 
						 | 
					e2cc350fbc | ||
| 
						 | 
					07feb87dee | ||
| 
						 | 
					1699e94f0f | ||
| 
						 | 
					8a1e335bf5 | ||
| 
						 | 
					5413bf8753 | ||
| 
						 | 
					c4c5ecd03d | ||
| 
						 | 
					3b67cf4378 | ||
| 
						 | 
					f3c937f9ed | ||
| 
						 | 
					671a699472 | ||
| 
						 | 
					cc4fa1c6bd | ||
| 
						 | 
					d11d476126 | ||
| 
						 | 
					1ce06c7cdd | ||
| 
						 | 
					800f02ba38 | ||
| 
						 | 
					79964b0e85 | ||
| 
						 | 
					0924d11bbf | ||
| 
						 | 
					3cf85634eb | ||
| 
						 | 
					ec01175d37 | ||
| 
						 | 
					5bb518ff0f | ||
| 
						 | 
					5415381cf4 | ||
| 
						 | 
					129ae06354 | ||
| 
						 | 
					058e5d5f4b | ||
| 
						 | 
					2ec493f033 | ||
| 
						 | 
					a33e8a8509 | ||
| 
						 | 
					7b4be7be40 | ||
| 
						 | 
					b90917a529 | ||
| 
						 | 
					180f05f6f0 | ||
| 
						 | 
					b2686e5b6d | ||
| 
						 | 
					831b95f13a | ||
| 
						 | 
					331b599a81 | ||
| 
						 | 
					3ef817ebc2 | ||
| 
						 | 
					d0f7c309ab | ||
| 
						 | 
					e5079b9dad | ||
| 
						 | 
					9e773c9143 | ||
| 
						 | 
					fe600441c9 | ||
| 
						 | 
					291b56db93 | ||
| 
						 | 
					ec9975c3d9 | ||
| 
						 | 
					9e57161941 | ||
| 
						 | 
					20444bf253 | ||
| 
						 | 
					e428c4402e | ||
| 
						 | 
					15af89d51c | ||
| 
						 | 
					ed95865696 | ||
| 
						 | 
					8c71bd57e7 | ||
| 
						 | 
					d204be4b2c | ||
| 
						 | 
					4743171b4f | ||
| 
						 | 
					1fb800f7a7 | ||
| 
						 | 
					3a3b0dd5c1 | ||
| 
						 | 
					2959281d42 | ||
| 
						 | 
					e2c939fb97 | ||
| 
						 | 
					51d0e4325e | ||
| 
						 | 
					08b6cf0231 | ||
| 
						 | 
					059147b74a | ||
| 
						 | 
					c96ceeb1bd | ||
| 
						 | 
					0a3b6c4813 | ||
| 
						 | 
					8636a15695 | ||
| 
						 | 
					96db9362c5 | ||
| 
						 | 
					f81f93e9c3 | ||
| 
						 | 
					a3a04b5f76 | ||
| 
						 | 
					329174b6d9 | ||
| 
						 | 
					b171aff418 | ||
| 
						 | 
					1f6ffa3e88 | ||
| 
						 | 
					24d2a8b9d5 | ||
| 
						 | 
					4d8b99a307 | ||
| 
						 | 
					f9085fedd7 | ||
| 
						 | 
					c4236e58d1 | ||
| 
						 | 
					eb91f9575a | ||
| 
						 | 
					947d14ffeb | ||
| 
						 | 
					b3f915b5b2 | ||
| 
						 | 
					03d3f3afc3 | ||
| 
						 | 
					2728170c01 | ||
| 
						 | 
					c107f3632a | ||
| 
						 | 
					7d1c5fca0b | ||
| 
						 | 
					e71887e1a8 | ||
| 
						 | 
					39d1eeda23 | ||
| 
						 | 
					b482f3ce94 | ||
| 
						 | 
					122cc48c29 | ||
| 
						 | 
					c7c57cfa0e | ||
| 
						 | 
					1f9ca20dfd | ||
| 
						 | 
					5efbaa4849 | ||
| 
						 | 
					c6f55bc4ab | ||
| 
						 | 
					b28a3db3d6 | ||
| 
						 | 
					b9ece28f68 | ||
| 
						 | 
					79b4907cb9 | ||
| 
						 | 
					dfbc244b00 | ||
| 
						 | 
					b23d5e26e2 | ||
| 
						 | 
					f1f3074306 | ||
| 
						 | 
					f162ad193f | ||
| 
						 | 
					5254f300f9 | ||
| 
						 | 
					c0d0100ca8 | ||
| 
						 | 
					21f201e371 | ||
| 
						 | 
					bbc378ed8f | ||
| 
						 | 
					e2d494321c | ||
| 
						 | 
					c2df8043db | ||
| 
						 | 
					df62150b5a | ||
| 
						 | 
					eea52a5fa6 | ||
| 
						 | 
					2d9015b840 | ||
| 
						 | 
					187ef29914 | ||
| 
						 | 
					c12be766e9 | ||
| 
						 | 
					2168458f11 | ||
| 
						 | 
					8cf0725593 | ||
| 
						 | 
					8379f015d7 | ||
| 
						 | 
					f08c01800f | ||
| 
						 | 
					a62706678e | ||
| 
						 | 
					e4468562d2 | ||
| 
						 | 
					7316bf7bc5 | ||
| 
						 | 
					3747b7d930 | ||
| 
						 | 
					5be3f22d06 | ||
| 
						 | 
					c572ce946b | ||
| 
						 | 
					d69f0289ca | ||
| 
						 | 
					048e5210f7 | ||
| 
						 | 
					219e9115c0 | ||
| 
						 | 
					b43416af97 | ||
| 
						 | 
					6d84da588b | ||
| 
						 | 
					569d6c557c | ||
| 
						 | 
					58bb94d7c7 | ||
| 
						 | 
					4f8f775e69 | ||
| 
						 | 
					5eed02f7e9 | ||
| 
						 | 
					983f1f28ca | ||
| 
						 | 
					192ede5e64 | ||
| 
						 | 
					803a7aa878 | ||
| 
						 | 
					93f3098aec | ||
| 
						 | 
					dbd94d047b | ||
| 
						 | 
					be39ab32d1 | ||
| 
						 | 
					8b1fb3cb0c | ||
| 
						 | 
					9e04222ee6 | ||
| 
						 | 
					72349507c4 | ||
| 
						 | 
					79db8daddd | ||
| 
						 | 
					b967f83f20 | ||
| 
						 | 
					74a7592b4b | ||
| 
						 | 
					486f23538f | ||
| 
						 | 
					df86ff2191 | ||
| 
						 | 
					41266f05e9 | ||
| 
						 | 
					d2aa331838 | ||
| 
						 | 
					670cb9d223 | ||
| 
						 | 
					a205762bf0 | ||
| 
						 | 
					2ffab66d97 | ||
| 
						 | 
					10a6aec998 | ||
| 
						 | 
					7cbe31baad | ||
| 
						 | 
					8a09dc1b9b | ||
| 
						 | 
					fa6e174651 | ||
| 
						 | 
					8dee328eae | ||
| 
						 | 
					340155e6a6 | ||
| 
						 | 
					7fe19a030a | ||
| 
						 | 
					11e0ccdc91 | ||
| 
						 | 
					e55b2f4f8d | ||
| 
						 | 
					d78c1f695e | ||
| 
						 | 
					cfbb3e86b3 | ||
| 
						 | 
					fa6234e417 | ||
| 
						 | 
					9eeae9ad7e | ||
| 
						 | 
					28688488ff | ||
| 
						 | 
					41d804719f | ||
| 
						 | 
					bc18168662 | ||
| 
						 | 
					1fadae82c7 | ||
| 
						 | 
					791c62ca64 | ||
| 
						 | 
					4441a6ff59 | ||
| 
						 | 
					63a7002477 | ||
| 
						 | 
					a746139c53 | ||
| 
						 | 
					697a060132 | ||
| 
						 | 
					7834c25253 | ||
| 
						 | 
					2e7cbfcff5 | ||
| 
						 | 
					5b771039fc | ||
| 
						 | 
					2f1bc5864f | ||
| 
						 | 
					16d79ebaac | ||
| 
						 | 
					e009ec8b93 | ||
| 
						 | 
					738ece513f | ||
| 
						 | 
					5a7b7b51c5 | ||
| 
						 | 
					5bba9bddfd | ||
| 
						 | 
					a3c0c7546d | ||
| 
						 | 
					05d7ae4efa | ||
| 
						 | 
					44483dba21 | ||
| 
						 | 
					3498a5856a | ||
| 
						 | 
					cd79b0343a | ||
| 
						 | 
					abba00d595 | ||
| 
						 | 
					88a8d7d901 | ||
| 
						 | 
					d5c00071d3 | ||
| 
						 | 
					c7a6cf5463 | ||
| 
						 | 
					2ce2a15fc6 | ||
| 
						 | 
					ce4be4e91e | ||
| 
						 | 
					82dc2244c0 | ||
| 
						 | 
					ab45b7783f | ||
| 
						 | 
					4a56b2406b | ||
| 
						 | 
					e0cd2cdb9c | ||
| 
						 | 
					7575094cf3 | ||
| 
						 | 
					40a3ae04b6 | ||
| 
						 | 
					ac26f84170 | ||
| 
						 | 
					fa574fe833 | ||
| 
						 | 
					9a6e18ce80 | ||
| 
						 | 
					8f9034fc8b | ||
| 
						 | 
					3e5b102445 | ||
| 
						 | 
					d8b1a8d439 | ||
| 
						 | 
					54ae008dd7 | ||
| 
						 | 
					9c1747581f | ||
| 
						 | 
					ae4f7b6d7a | ||
| 
						 | 
					c947322a69 | ||
| 
						 | 
					14db45215f | ||
| 
						 | 
					d20831e41a | ||
| 
						 | 
					1bb902984b | ||
| 
						 | 
					09f1c58872 | ||
| 
						 | 
					7f944c2c8b | ||
| 
						 | 
					1c02b85802 | ||
| 
						 | 
					a2801649b4 | ||
| 
						 | 
					15777732d3 | ||
| 
						 | 
					b875037150 | ||
| 
						 | 
					a6d583cb5e | ||
| 
						 | 
					9882f3429a | ||
| 
						 | 
					29d47c4de2 | ||
| 
						 | 
					a9b15f1c36 | ||
| 
						 | 
					ce38ecb966 | ||
| 
						 | 
					13ffa17048 | ||
| 
						 | 
					a387682dfb | ||
| 
						 | 
					f6ed197cd3 | ||
| 
						 | 
					243593cdaa | ||
| 
						 | 
					1e2d559859 | ||
| 
						 | 
					20ea859183 | ||
| 
						 | 
					86b24ea059 | ||
| 
						 | 
					07af42476d | ||
| 
						 | 
					87edf71e93 | ||
| 
						 | 
					e3ea2ed420 | ||
| 
						 | 
					8f9a1881a4 | ||
| 
						 | 
					a8c6111197 | ||
| 
						 | 
					3de8570022 | ||
| 
						 | 
					a0311b0134 | ||
| 
						 | 
					158a628c0e | ||
| 
						 | 
					422e5026d6 | ||
| 
						 | 
					797cbb9b20 | ||
| 
						 | 
					be68fbd4f5 | ||
| 
						 | 
					e440223b40 | ||
| 
						 | 
					5766250288 | ||
| 
						 | 
					870274ad9d | ||
| 
						 | 
					9d6abcd9be | ||
| 
						 | 
					432771dfe3 | ||
| 
						 | 
					69925ce823 | ||
| 
						 | 
					e9f9f515bd | ||
| 
						 | 
					efd96153d8 | ||
| 
						 | 
					c7b8f223ee | ||
| 
						 | 
					a0636d5a87 | ||
| 
						 | 
					c7b16249b8 | ||
| 
						 | 
					031e885e4d | ||
| 
						 | 
					796e2cc156 | ||
| 
						 | 
					7af784adce | ||
| 
						 | 
					a988a91e2e | ||
| 
						 | 
					a8b564fa64 | ||
| 
						 | 
					b97e140389 | ||
| 
						 | 
					e51bef6d12 | ||
| 
						 | 
					4bd31f4967 | ||
| 
						 | 
					7ff7a7c527 | ||
| 
						 | 
					c4a375b3a5 | ||
| 
						 | 
					e3698edd19 | ||
| 
						 | 
					e591d5cfe4 | ||
| 
						 | 
					a2e62f8e1d | ||
| 
						 | 
					764963e986 | ||
| 
						 | 
					4da493f3f3 | ||
| 
						 | 
					dec90f7e5e | ||
| 
						 | 
					2ea5b283a8 | ||
| 
						 | 
					eb23549cd6 | ||
| 
						 | 
					cd90062850 | ||
| 
						 | 
					3ec72fcee9 | ||
| 
						 | 
					b9091e14b3 | ||
| 
						 | 
					5daefc0194 | ||
| 
						 | 
					44edb2bd2f | ||
| 
						 | 
					201aa24448 | ||
| 
						 | 
					e799ef2977 | ||
| 
						 | 
					0c538f7527 | ||
| 
						 | 
					ca7202eb0a | ||
| 
						 | 
					d5ec5f80ff | ||
| 
						 | 
					0c944a03fe | ||
| 
						 | 
					79a267ab08 | ||
| 
						 | 
					f9a6988ece | ||
| 
						 | 
					95e06de5de | ||
| 
						 | 
					f530a5074b | ||
| 
						 | 
					439580b91b | ||
| 
						 | 
					4c2a384159 | ||
| 
						 | 
					22b83d7630 | ||
| 
						 | 
					cfd0b040e4 | ||
| 
						 | 
					b001840dee | ||
| 
						 | 
					0b5bff01e1 | ||
| 
						 | 
					4fe7b6cd65 | ||
| 
						 | 
					49e1f7d8bf | ||
| 
						 | 
					803fb243bf | ||
| 
						 | 
					20a6ab3d1a | ||
| 
						 | 
					21f16b50f3 | ||
| 
						 | 
					f58e83ee87 | ||
| 
						 | 
					a9b4a2a1ac | ||
| 
						 | 
					5be1449db5 | ||
| 
						 | 
					37d7f89c6d | ||
| 
						 | 
					57e58ce76c | ||
| 
						 | 
					1d9f76e2c8 | ||
| 
						 | 
					56e0269e5e | ||
| 
						 | 
					662df85e54 | ||
| 
						 | 
					2be4a5e486 | ||
| 
						 | 
					a43d697225 | ||
| 
						 | 
					f06c1e6c78 | ||
| 
						 | 
					b5d1918401 | ||
| 
						 | 
					d018be5d36 | ||
| 
						 | 
					67184d7b20 | ||
| 
						 | 
					dfdd48b990 | ||
| 
						 | 
					cae9cee295 | ||
| 
						 | 
					067d586c1c | ||
| 
						 | 
					d22b7938da | ||
| 
						 | 
					29b7510957 | ||
| 
						 | 
					9d548d81ac | ||
| 
						 | 
					3c33cdfa3d | ||
| 
						 | 
					288485b209 | ||
| 
						 | 
					1cbf416b10 | ||
| 
						 | 
					3b46d3dd74 | ||
| 
						 | 
					c243829234 | ||
| 
						 | 
					fd80faa389 | ||
| 
						 | 
					e7d4352292 | ||
| 
						 | 
					610e0f21d6 | ||
| 
						 | 
					7e512bad96 | ||
| 
						 | 
					98c4ce0c45 | ||
| 
						 | 
					93fc48a2db | ||
| 
						 | 
					d7eea12a6f | ||
| 
						 | 
					2a1e06f8a9 | ||
| 
						 | 
					02d54a783a | ||
| 
						 | 
					5961d44339 | ||
| 
						 | 
					fb3be8509d | ||
| 
						 | 
					97be53741d | ||
| 
						 | 
					0d2c26735e | ||
| 
						 | 
					2d0a8ddb58 | ||
| 
						 | 
					72518d4827 | ||
| 
						 | 
					c6cd744186 | ||
| 
						 | 
					ae2db62f1c | ||
| 
						 | 
					8c76b8bc36 | ||
| 
						 | 
					18256c4923 | ||
| 
						 | 
					f345cc66cf | ||
| 
						 | 
					5dbf664a6b | ||
| 
						 | 
					d7c6679d70 | ||
| 
						 | 
					cae203be71 | ||
| 
						 | 
					8a29fbc850 | ||
| 
						 | 
					00bcbd367f | ||
| 
						 | 
					1befee5aca | ||
| 
						 | 
					5982f4bcf0 | ||
| 
						 | 
					2399476a21 | ||
| 
						 | 
					9e45ac939b | ||
| 
						 | 
					34f231f9f3 | ||
| 
						 | 
					f940b2a58e | ||
| 
						 | 
					0383c33558 | ||
| 
						 | 
					3afa4b210d | ||
| 
						 | 
					998783eb9d | ||
| 
						 | 
					54d61bdc4a | ||
| 
						 | 
					2d279c4c5c | ||
| 
						 | 
					0fb206fe15 | ||
| 
						 | 
					5d6fd8099f | ||
| 
						 | 
					800e3f4599 | ||
| 
						 | 
					35c61f52fe | ||
| 
						 | 
					3db446633c | ||
| 
						 | 
					e3b4a0213c | ||
| 
						 | 
					9910ff5fa1 | ||
| 
						 | 
					049be10406 | ||
| 
						 | 
					9aa3be7f9f | ||
| 
						 | 
					aba5c634ae | ||
| 
						 | 
					9774b01b0e | ||
| 
						 | 
					ecf0a710e1 | ||
| 
						 | 
					c9febbdd87 | ||
| 
						 | 
					caa2e45a8c | ||
| 
						 | 
					d9130c9852 | ||
| 
						 | 
					661f05837c | ||
| 
						 | 
					a61fe418b2 | ||
| 
						 | 
					b15cfc2c5a | ||
| 
						 | 
					c575c9af3d | ||
| 
						 | 
					81f27e9077 | ||
| 
						 | 
					f78babfaa0 | ||
| 
						 | 
					66990cf872 | ||
| 
						 | 
					095fe2ed1b | ||
| 
						 | 
					483ebc8141 | ||
| 
						 | 
					b9311282eb | ||
| 
						 | 
					d6f0c2b52b | ||
| 
						 | 
					bb25febd70 | ||
| 
						 | 
					19ab2a29ce | ||
| 
						 | 
					24b4fe9867 | ||
| 
						 | 
					aa7b82de04 | ||
| 
						 | 
					3d826bed3a | ||
| 
						 | 
					d2ae7e78ef | ||
| 
						 | 
					656bd330f8 | ||
| 
						 | 
					c2c8f32010 | ||
| 
						 | 
					9f43c270e6 | ||
| 
						 | 
					50827188ff | ||
| 
						 | 
					6ae0f7f5c6 | ||
| 
						 | 
					c583d6bb49 | ||
| 
						 | 
					fdcb6b721c | ||
| 
						 | 
					ef858ef062 | ||
| 
						 | 
					fe04faf675 | ||
| 
						 | 
					ad752b317d | ||
| 
						 | 
					59649e9b1e | ||
| 
						 | 
					6bf281f905 | ||
| 
						 | 
					4dfc8b262c | ||
| 
						 | 
					f08ffe9feb | ||
| 
						 | 
					65de3110a9 | ||
| 
						 | 
					f6dcd98995 | ||
| 
						 | 
					950172dc01 | ||
| 
						 | 
					5c48e139d4 | ||
| 
						 | 
					150e9c8a48 | ||
| 
						 | 
					b19ba13aff | ||
| 
						 | 
					30bfc2cea7 | ||
| 
						 | 
					30c2d84c6d | ||
| 
						 | 
					fbd2038fa7 | ||
| 
						 | 
					e4b8d9b9d6 | ||
| 
						 | 
					6b50003697 | ||
| 
						 | 
					a73c5b3355 | ||
| 
						 | 
					eb59817e81 | ||
| 
						 | 
					d404e92d16 | ||
| 
						 | 
					86c017ec9d | ||
| 
						 | 
					0407c4e0f7 | ||
| 
						 | 
					0ba95a3dd4 | ||
| 
						 | 
					0463b5d6cd | ||
| 
						 | 
					3f4513b3a9 | ||
| 
						 | 
					fb2029e717 | ||
| 
						 | 
					48f02fb61b | ||
| 
						 | 
					0c00e870c6 | ||
| 
						 | 
					7da50703fb | ||
| 
						 | 
					cfdaff5a46 | ||
| 
						 | 
					18e46962c2 | ||
| 
						 | 
					319e0ae3cf | ||
| 
						 | 
					e69a7c38d9 | ||
| 
						 | 
					ecd685755b | ||
| 
						 | 
					276b51d927 | ||
| 
						 | 
					251d1c5c9f | ||
| 
						 | 
					028e17475f | ||
| 
						 | 
					1ab630435d | ||
| 
						 | 
					89002ed298 | ||
| 
						 | 
					d0871bdae3 | ||
| 
						 | 
					5ea6e9c9c0 | 
							
								
								
									
										12
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
# These are supported funding model platforms
 | 
			
		||||
 | 
			
		||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
 | 
			
		||||
patreon: # Replace with a single Patreon username
 | 
			
		||||
open_collective: acmesh
 | 
			
		||||
ko_fi: neilpang
 | 
			
		||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
 | 
			
		||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
 | 
			
		||||
liberapay: # Replace with a single Liberapay username
 | 
			
		||||
issuehunt: # Replace with a single IssueHunt username
 | 
			
		||||
otechie: # Replace with a single Otechie username
 | 
			
		||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
 | 
			
		||||
							
								
								
									
										27
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
<!--
 | 
			
		||||
我很忙, 每天可能只有 几秒钟 时间看你的 issue, 如果不按照我的要求写 issue, 你可能不会得到任何回复, 石沉大海.
 | 
			
		||||
 | 
			
		||||
请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试信息. 我做不了什么.
 | 
			
		||||
如何调试 https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
 | 
			
		||||
 | 
			
		||||
If it is a bug report:
 | 
			
		||||
- make sure you are able to repro it on the latest released version.
 | 
			
		||||
You can install the latest version by: `acme.sh --upgrade`
 | 
			
		||||
 | 
			
		||||
- Search the existing issues.
 | 
			
		||||
- Refer to the [WIKI](https://wiki.acme.sh).
 | 
			
		||||
- Debug info [Debug](https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh).
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
Steps to reproduce
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
Debug log
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
acme.sh  --issue .....   --debug 2
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<!--
 | 
			
		||||
1. Do NOT send pull request to `master` branch.
 | 
			
		||||
Please send to `dev` branch instead.
 | 
			
		||||
Any PR to `master` branch will NOT be merged.
 | 
			
		||||
 | 
			
		||||
2. For dns api support, read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
 | 
			
		||||
You will NOT get any review without passing this guide.  You also need to fix the CI errors.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
							
								
								
									
										515
									
								
								.github/workflows/DNS.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										515
									
								
								.github/workflows/DNS.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,515 @@
 | 
			
		||||
name: DNS
 | 
			
		||||
on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
  push:
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'dnsapi/*.sh'
 | 
			
		||||
      - '.github/workflows/DNS.yml'
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - 'dev'
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'dnsapi/*.sh'
 | 
			
		||||
      - '.github/workflows/DNS.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  CheckToken:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    outputs:
 | 
			
		||||
      hasToken: ${{ steps.step_one.outputs.hasToken }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Set the value
 | 
			
		||||
        id: step_one
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{secrets.TokenName1}}" ] ; then
 | 
			
		||||
            echo "::set-output name=hasToken::true"
 | 
			
		||||
          else
 | 
			
		||||
            echo "::set-output name=hasToken::false"
 | 
			
		||||
          fi
 | 
			
		||||
      - name: Check the value
 | 
			
		||||
        run: echo ${{ steps.step_one.outputs.hasToken }}
 | 
			
		||||
 | 
			
		||||
  Fail:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: CheckToken
 | 
			
		||||
    if: "contains(needs.CheckToken.outputs.hasToken, 'false')"
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: "Read this:   https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Test"
 | 
			
		||||
      run: |
 | 
			
		||||
        echo "Read this:   https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Test"
 | 
			
		||||
        if [ "${{github.repository_owner}}" != "acmesh-official" ]; then
 | 
			
		||||
          false
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
  Docker:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: CheckToken
 | 
			
		||||
    if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Set env file
 | 
			
		||||
      run: |
 | 
			
		||||
        cd ../acmetest
 | 
			
		||||
        if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
          echo "${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}" >> docker.env
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
          echo "${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}" >> docker.env
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
          echo "${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}" >> docker.env
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
          echo "${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}" >> docker.env
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
          echo "${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}" >> docker.env
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: cd ../acmetest && ./rundocker.sh  testall
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  MacOS:
 | 
			
		||||
    runs-on: macos-latest
 | 
			
		||||
    needs: Docker
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run:  brew install socat
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
        if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
        fi
 | 
			
		||||
        cd ../acmetest
 | 
			
		||||
        ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  Windows:
 | 
			
		||||
    runs-on: windows-latest
 | 
			
		||||
    needs: MacOS
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Set git to use LF
 | 
			
		||||
      run: |
 | 
			
		||||
          git config --global core.autocrlf false
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install cygwin base packages with chocolatey
 | 
			
		||||
      run: |
 | 
			
		||||
          choco config get cacheLocation
 | 
			
		||||
          choco install --no-progress cygwin
 | 
			
		||||
      shell: cmd
 | 
			
		||||
    - name: Install cygwin additional packages
 | 
			
		||||
      run: |
 | 
			
		||||
          C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s https://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git
 | 
			
		||||
      shell: cmd
 | 
			
		||||
    - name: Set ENV
 | 
			
		||||
      shell: cmd
 | 
			
		||||
      run: |
 | 
			
		||||
          echo PATH=C:\tools\cygwin\bin;C:\tools\cygwin\usr\bin >> %GITHUB_ENV%
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      shell: bash
 | 
			
		||||
      run: |
 | 
			
		||||
        if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
        fi
 | 
			
		||||
        if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
          export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
        fi
 | 
			
		||||
        cd ../acmetest
 | 
			
		||||
        ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  FreeBSD:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: Windows
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/freebsd-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        prepare: pkg install -y socat curl
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  OpenBSD:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: FreeBSD
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/openbsd-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        prepare: pkg_add socat curl libiconv
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  NetBSD:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: OpenBSD
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/netbsd-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        prepare: |
 | 
			
		||||
          /usr/sbin/pkg_add curl socat
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  DragonFlyBSD:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: NetBSD
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/dragonflybsd-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        prepare: |
 | 
			
		||||
          pkg install -y curl socat libnghttp2
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  Solaris:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: DragonFlyBSD
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Solaris doesn't accept the expired ISRG X1 root
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/solaris-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        copyback: false
 | 
			
		||||
        prepare: pkgutil -y -i socat
 | 
			
		||||
        run: |
 | 
			
		||||
          pkg set-mediator -v -I default@1.1 openssl
 | 
			
		||||
          export PATH=/usr/gnu/bin:$PATH
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  Omnios:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: Solaris
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_DNS : ${{ secrets.TEST_DNS }}
 | 
			
		||||
      TestingDomain: ${{ secrets.TestingDomain }}
 | 
			
		||||
      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
 | 
			
		||||
      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
 | 
			
		||||
      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
 | 
			
		||||
      CASE: le_test_dnsapi
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      DEBUG: ${{ secrets.DEBUG }}
 | 
			
		||||
      http_proxy: ${{ secrets.http_proxy }}
 | 
			
		||||
      https_proxy: ${{ secrets.https_proxy }}
 | 
			
		||||
      HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Omnios doesn't accept the expired ISRG X1 root
 | 
			
		||||
      TokenName1: ${{ secrets.TokenName1}}
 | 
			
		||||
      TokenName2: ${{ secrets.TokenName2}}
 | 
			
		||||
      TokenName3: ${{ secrets.TokenName3}}
 | 
			
		||||
      TokenName4: ${{ secrets.TokenName4}}
 | 
			
		||||
      TokenName5: ${{ secrets.TokenName5}}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/omnios-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        copyback: false
 | 
			
		||||
        prepare: pkg install socat
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "${{ secrets.TokenName1}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName2}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName3}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName4}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
 | 
			
		||||
          fi
 | 
			
		||||
          if [ "${{ secrets.TokenName5}}" ] ; then
 | 
			
		||||
            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
 | 
			
		||||
          fi
 | 
			
		||||
          cd ../acmetest
 | 
			
		||||
          ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										71
									
								
								.github/workflows/DragonFlyBSD.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								.github/workflows/DragonFlyBSD.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
name: DragonFlyBSD
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/DragonFlyBSD.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/DragonFlyBSD.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  DragonFlyBSD:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/dragonflybsd-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: |
 | 
			
		||||
          pkg install -y curl socat libnghttp2
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										76
									
								
								.github/workflows/FreeBSD.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								.github/workflows/FreeBSD.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
name: FreeBSD
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/FreeBSD.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/FreeBSD.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  FreeBSD:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/freebsd-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: pkg install -y socat curl wget
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										48
									
								
								.github/workflows/Linux.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								.github/workflows/Linux.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
name: Linux
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Linux.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Linux.yml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Linux:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: ["ubuntu:latest", "debian:latest", "almalinux:latest", "fedora:latest", "opensuse/leap:latest", "alpine:latest", "oraclelinux:8", "kalilinux/kali", "archlinux:latest", "gentoo/stage3"]
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
      TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd .. \
 | 
			
		||||
          && git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./rundocker.sh  testplat ${{ matrix.os }}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										60
									
								
								.github/workflows/MacOS.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								.github/workflows/MacOS.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
name: MacOS
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/MacOS.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/MacOS.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  MacOS:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: macos-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run:  brew install socat
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd .. \
 | 
			
		||||
          && git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && sudo --preserve-env ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										71
									
								
								.github/workflows/NetBSD.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								.github/workflows/NetBSD.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
name: NetBSD
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/NetBSD.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/NetBSD.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  NetBSD:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/netbsd-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: |
 | 
			
		||||
          /usr/sbin/pkg_add curl socat
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										75
									
								
								.github/workflows/Omnios.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								.github/workflows/Omnios.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
name: Omnios
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Omnios.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Omnios.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Omnios:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/omnios-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: pkg install socat wget
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										76
									
								
								.github/workflows/OpenBSD.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								.github/workflows/OpenBSD.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
name: OpenBSD
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/OpenBSD.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/OpenBSD.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  OpenBSD:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/openbsd-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: pkg_add socat curl wget libnghttp2
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										72
									
								
								.github/workflows/PebbleStrict.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								.github/workflows/PebbleStrict.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
name: PebbleStrict
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/PebbleStrict.yml'
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/PebbleStrict.yml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  PebbleStrict:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TestingDomain: example.com
 | 
			
		||||
      TestingAltDomains: www.example.com
 | 
			
		||||
      TEST_ACME_Server: https://localhost:14000/dir
 | 
			
		||||
      HTTPS_INSECURE: 1
 | 
			
		||||
      Le_HTTPPort: 5002
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_CA: "Pebble Intermediate CA"
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run: sudo apt-get install -y socat
 | 
			
		||||
    - name: Run Pebble
 | 
			
		||||
      run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker compose up -d
 | 
			
		||||
    - name: Set up Pebble
 | 
			
		||||
      run: curl --request POST --data '{"ip":"10.30.50.1"}' http://localhost:8055/set-default-ipv4
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: cd ../acmetest && ./letest.sh
 | 
			
		||||
 | 
			
		||||
  PebbleStrict_IPCert:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TestingDomain: 1.23.45.67
 | 
			
		||||
      TEST_ACME_Server: https://localhost:14000/dir
 | 
			
		||||
      HTTPS_INSECURE: 1
 | 
			
		||||
      Le_HTTPPort: 5002
 | 
			
		||||
      Le_TLSPort: 5001
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_CA: "Pebble Intermediate CA"
 | 
			
		||||
      TEST_IPCERT: 1
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run: sudo apt-get install -y socat
 | 
			
		||||
    - name: Run Pebble
 | 
			
		||||
      run: |
 | 
			
		||||
        docker run --rm -itd --name=pebble \
 | 
			
		||||
        -e PEBBLE_VA_ALWAYS_VALID=1 \
 | 
			
		||||
        -p 14000:14000 -p 15000:15000   letsencrypt/pebble:latest pebble -config /test/config/pebble-config.json -strict
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: cd ../acmetest && ./letest.sh
 | 
			
		||||
							
								
								
									
										75
									
								
								.github/workflows/Solaris.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								.github/workflows/Solaris.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
name: Solaris
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Solaris.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Solaris.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Solaris:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/solaris-vm@v1
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: pkgutil -y -i socat curl wget
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										103
									
								
								.github/workflows/Ubuntu.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								.github/workflows/Ubuntu.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
name: Ubuntu
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Ubuntu.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Ubuntu.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Ubuntu:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         - TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
           CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
           CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
           CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
           TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
         - TEST_ACME_Server: "https://localhost:9000/acme/acme/directory"
 | 
			
		||||
           CA_ECDSA: "Smallstep Intermediate CA"
 | 
			
		||||
           CA: "Smallstep Intermediate CA"
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
           NO_REVOKE: 1
 | 
			
		||||
         - TEST_ACME_Server: "https://localhost:9000/acme/acme/directory"
 | 
			
		||||
           CA_ECDSA: "Smallstep Intermediate CA"
 | 
			
		||||
           CA: "Smallstep Intermediate CA"
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
           NO_REVOKE: 1
 | 
			
		||||
           TEST_IPCERT: 1
 | 
			
		||||
           TestingDomain: "172.17.0.1"
 | 
			
		||||
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      NO_ECC_384: ${{ matrix.NO_ECC_384 }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      NO_REVOKE: ${{ matrix.NO_REVOKE }}
 | 
			
		||||
      TEST_IPCERT: ${{ matrix.TEST_IPCERT }}
 | 
			
		||||
      TestingDomain: ${{ matrix.TestingDomain }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run: sudo apt-get install -y socat wget
 | 
			
		||||
    - name: Start StepCA
 | 
			
		||||
      if: ${{ matrix.TEST_ACME_Server=='https://localhost:9000/acme/acme/directory' }}
 | 
			
		||||
      run: |
 | 
			
		||||
           docker run --rm -d \
 | 
			
		||||
            -p 9000:9000 \
 | 
			
		||||
            -e "DOCKER_STEPCA_INIT_NAME=Smallstep" \
 | 
			
		||||
            -e "DOCKER_STEPCA_INIT_DNS_NAMES=localhost,$(hostname -f)" \
 | 
			
		||||
            -e "DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT=true" \
 | 
			
		||||
            -e "DOCKER_STEPCA_INIT_PASSWORD=test" \
 | 
			
		||||
            --name stepca \
 | 
			
		||||
            smallstep/step-ca:0.23.1
 | 
			
		||||
 | 
			
		||||
            sleep 5
 | 
			
		||||
            docker exec  stepca bash -c "echo test >test" \
 | 
			
		||||
            && docker exec stepca step ca provisioner add acme --type ACME --admin-subject  step --admin-password-file=/home/step/test \
 | 
			
		||||
            && docker exec  stepca kill -1 1 \
 | 
			
		||||
            && docker exec  stepca cat /home/step/certs/root_ca.crt | sudo bash -c "cat - >>/etc/ssl/certs/ca-certificates.crt"
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd .. \
 | 
			
		||||
          && git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && sudo --preserve-env ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										78
									
								
								.github/workflows/Windows.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								.github/workflows/Windows.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
name: Windows
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Windows.yml'
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Windows.yml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Windows:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING)
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: windows-latest
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      #The 80 port is used by Windows server, we have to use a custom port, tunnel will also use this port.
 | 
			
		||||
      Le_HTTPPort: 8888
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Set git to use LF
 | 
			
		||||
      run: |
 | 
			
		||||
          git config --global core.autocrlf false
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install cygwin base packages with chocolatey
 | 
			
		||||
      run: |
 | 
			
		||||
          choco config get cacheLocation
 | 
			
		||||
          choco install --no-progress cygwin
 | 
			
		||||
      shell: cmd
 | 
			
		||||
    - name: Install cygwin additional packages
 | 
			
		||||
      run: |
 | 
			
		||||
          C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s https://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git,xxd
 | 
			
		||||
      shell: cmd
 | 
			
		||||
    - name: Set ENV
 | 
			
		||||
      shell: cmd
 | 
			
		||||
      run: |
 | 
			
		||||
          echo PATH=C:\tools\cygwin\bin;C:\tools\cygwin\usr\bin;%PATH% >> %GITHUB_ENV%
 | 
			
		||||
    - name: Check ENV
 | 
			
		||||
      shell: cmd
 | 
			
		||||
      run: |
 | 
			
		||||
          echo "PATH=%PATH%"
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      shell: cmd
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      shell: cmd
 | 
			
		||||
      run: cd ../acmetest && bash.exe -c ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										86
									
								
								.github/workflows/dockerhub.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								.github/workflows/dockerhub.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
 | 
			
		||||
name: Build DockerHub
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    tags:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '**.sh'
 | 
			
		||||
      - "Dockerfile"
 | 
			
		||||
      - '.github/workflows/dockerhub.yml'
 | 
			
		||||
 | 
			
		||||
concurrency:
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  DOCKER_IMAGE: neilpang/acme.sh
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  CheckToken:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    outputs:
 | 
			
		||||
      hasToken: ${{ steps.step_one.outputs.hasToken }}
 | 
			
		||||
    env: 
 | 
			
		||||
      DOCKER_PASSWORD : ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Set the value
 | 
			
		||||
        id: step_one
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "$DOCKER_PASSWORD" ] ; then
 | 
			
		||||
            echo "hasToken=true" >>$GITHUB_OUTPUT
 | 
			
		||||
          else
 | 
			
		||||
            echo "hasToken=false" >>$GITHUB_OUTPUT
 | 
			
		||||
          fi
 | 
			
		||||
      - name: Check the value
 | 
			
		||||
        run: echo ${{ steps.step_one.outputs.hasToken }}
 | 
			
		||||
        
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: CheckToken
 | 
			
		||||
    if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: checkout code
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        with:
 | 
			
		||||
          persist-credentials: false
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
      - name: Extract Docker metadata
 | 
			
		||||
        id: meta
 | 
			
		||||
        uses: docker/metadata-action@v5.5.1
 | 
			
		||||
        with:
 | 
			
		||||
          images: ${DOCKER_IMAGE}
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
      - name: login to docker hub
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
 | 
			
		||||
      - name: build and push the image
 | 
			
		||||
        run: |
 | 
			
		||||
          if [[ $GITHUB_REF == refs/tags/* ]]; then
 | 
			
		||||
            DOCKER_IMAGE_TAG=${GITHUB_REF#refs/tags/}
 | 
			
		||||
          fi
 | 
			
		||||
 | 
			
		||||
          if [[ $GITHUB_REF == refs/heads/* ]]; then
 | 
			
		||||
            DOCKER_IMAGE_TAG=${GITHUB_REF#refs/heads/}
 | 
			
		||||
 | 
			
		||||
            if [[ $DOCKER_IMAGE_TAG == master ]]; then
 | 
			
		||||
              DOCKER_IMAGE_TAG=latest
 | 
			
		||||
              AUTO_UPGRADE=1
 | 
			
		||||
            fi
 | 
			
		||||
          fi
 | 
			
		||||
 | 
			
		||||
          DOCKER_LABELS=()
 | 
			
		||||
          while read -r label; do
 | 
			
		||||
            DOCKER_LABELS+=(--label "${label}")
 | 
			
		||||
          done <<<"${DOCKER_METADATA_OUTPUT_LABELS}"
 | 
			
		||||
 | 
			
		||||
          docker buildx build \
 | 
			
		||||
            --tag ${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \
 | 
			
		||||
            "${DOCKER_LABELS[@]}" \
 | 
			
		||||
            --output "type=image,push=true" \
 | 
			
		||||
            --build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \
 | 
			
		||||
            --platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x .
 | 
			
		||||
							
								
								
									
										19
									
								
								.github/workflows/issue.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/issue.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
name: "Update issues"
 | 
			
		||||
on:
 | 
			
		||||
  issues:
 | 
			
		||||
    types: [opened]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  comment:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/github-script@v6
 | 
			
		||||
        with:
 | 
			
		||||
          script: |
 | 
			
		||||
            github.rest.issues.createComment({
 | 
			
		||||
              issue_number: context.issue.number,
 | 
			
		||||
              owner: context.repo.owner,
 | 
			
		||||
              repo: context.repo.repo,
 | 
			
		||||
              body: "Please upgrade to the latest code and try again first. Maybe it's already fixed.              ```acme.sh --upgrade```              If it's still not working, please provide the log with `--debug 2`, otherwise, nobody can help you."
 | 
			
		||||
              
 | 
			
		||||
            })
 | 
			
		||||
							
								
								
									
										33
									
								
								.github/workflows/pr_dns.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/pr_dns.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
name: Check dns api
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  pull_request_target:
 | 
			
		||||
    types:
 | 
			
		||||
      - opened
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'dnsapi/*.sh'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  welcome:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/github-script@v6
 | 
			
		||||
        with:
 | 
			
		||||
          script: |
 | 
			
		||||
            await github.rest.issues.createComment({
 | 
			
		||||
              issue_number: context.issue.number,
 | 
			
		||||
              owner: context.repo.owner,
 | 
			
		||||
              repo: context.repo.repo,
 | 
			
		||||
              body: `**Welcome**
 | 
			
		||||
                    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, 都必须确保通过这个测试.
 | 
			
		||||
                `
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								.github/workflows/pr_notify.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/pr_notify.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
name: Check notify api
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  pull_request_target:
 | 
			
		||||
    types:
 | 
			
		||||
      - opened
 | 
			
		||||
    branches:
 | 
			
		||||
      - 'dev'
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'notify/*.sh'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  welcome:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/github-script@v6
 | 
			
		||||
        with:
 | 
			
		||||
          script: |
 | 
			
		||||
            await github.rest.issues.createComment({
 | 
			
		||||
              issue_number: context.issue.number,
 | 
			
		||||
              owner: context.repo.owner,
 | 
			
		||||
              repo: context.repo.repo,
 | 
			
		||||
              body: `**Welcome**
 | 
			
		||||
                Please make sure you've read our [Code-of-conduct](../wiki/Code-of-conduct) and  add the usage here: [notify](../wiki/notify).
 | 
			
		||||
                Then reply on this message, otherwise, your code will not be reviewed or merged.
 | 
			
		||||
                We look forward to reviewing your Pull request shortly ✨
 | 
			
		||||
                `
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										38
									
								
								.github/workflows/shellcheck.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/shellcheck.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
name: Shellcheck
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '*'
 | 
			
		||||
    paths:
 | 
			
		||||
      - '**.sh'
 | 
			
		||||
      - '.github/workflows/shellcheck.yml'
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
    paths:
 | 
			
		||||
      - '**.sh'
 | 
			
		||||
      - '.github/workflows/shellcheck.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  ShellCheck:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install Shellcheck
 | 
			
		||||
      run: sudo apt-get install -y shellcheck
 | 
			
		||||
    - name: DoShellcheck
 | 
			
		||||
      run: shellcheck -V  && shellcheck -e SC2181 -e SC2089 **/*.sh && echo "shellcheck OK"
 | 
			
		||||
 | 
			
		||||
  shfmt:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - name: Install shfmt
 | 
			
		||||
      run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt
 | 
			
		||||
    - name: shfmt
 | 
			
		||||
      run: ~/shfmt -l -w -i 2 . ; git diff --exit-code && echo "shfmt OK"
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										80
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
FROM alpine:3.21
 | 
			
		||||
 | 
			
		||||
RUN apk --no-cache add -f \
 | 
			
		||||
  openssl \
 | 
			
		||||
  openssh-client \
 | 
			
		||||
  coreutils \
 | 
			
		||||
  bind-tools \
 | 
			
		||||
  curl \
 | 
			
		||||
  sed \
 | 
			
		||||
  socat \
 | 
			
		||||
  tzdata \
 | 
			
		||||
  oath-toolkit-oathtool \
 | 
			
		||||
  tar \
 | 
			
		||||
  libidn \
 | 
			
		||||
  jq \
 | 
			
		||||
  cronie
 | 
			
		||||
 | 
			
		||||
ENV LE_CONFIG_HOME=/acme.sh
 | 
			
		||||
 | 
			
		||||
ARG AUTO_UPGRADE=1
 | 
			
		||||
 | 
			
		||||
ENV AUTO_UPGRADE=$AUTO_UPGRADE
 | 
			
		||||
 | 
			
		||||
#Install
 | 
			
		||||
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#> /proc/1/fd/1 2>/proc/1/fd/2#' | crontab -
 | 
			
		||||
 | 
			
		||||
RUN for verb in help \
 | 
			
		||||
  version \
 | 
			
		||||
  install \
 | 
			
		||||
  uninstall \
 | 
			
		||||
  upgrade \
 | 
			
		||||
  issue \
 | 
			
		||||
  signcsr \
 | 
			
		||||
  deploy \
 | 
			
		||||
  install-cert \
 | 
			
		||||
  renew \
 | 
			
		||||
  renew-all \
 | 
			
		||||
  revoke \
 | 
			
		||||
  remove \
 | 
			
		||||
  list \
 | 
			
		||||
  info \
 | 
			
		||||
  showcsr \
 | 
			
		||||
  install-cronjob \
 | 
			
		||||
  uninstall-cronjob \
 | 
			
		||||
  cron \
 | 
			
		||||
  toPkcs \
 | 
			
		||||
  toPkcs8 \
 | 
			
		||||
  update-account \
 | 
			
		||||
  register-account \
 | 
			
		||||
  create-account-key \
 | 
			
		||||
  create-domain-key \
 | 
			
		||||
  createCSR \
 | 
			
		||||
  deactivate \
 | 
			
		||||
  deactivate-account \
 | 
			
		||||
  set-notify \
 | 
			
		||||
  set-default-ca \
 | 
			
		||||
  set-default-chain \
 | 
			
		||||
  ; do \
 | 
			
		||||
    printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
 | 
			
		||||
  ; done
 | 
			
		||||
 | 
			
		||||
RUN printf "%b" '#!'"/usr/bin/env sh\n \
 | 
			
		||||
if [ \"\$1\" = \"daemon\" ];  then \n \
 | 
			
		||||
 exec crond -n -s -m off \n \
 | 
			
		||||
else \n \
 | 
			
		||||
 exec -- \"\$@\"\n \
 | 
			
		||||
fi\n" >/entry.sh && chmod +x /entry.sh
 | 
			
		||||
 | 
			
		||||
VOLUME /acme.sh
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/entry.sh"]
 | 
			
		||||
CMD ["--help"]
 | 
			
		||||
							
								
								
									
										674
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										674
									
								
								LICENSE.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,674 @@
 | 
			
		||||
                    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
                       Version 3, 29 June 2007
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 of this license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
                            Preamble
 | 
			
		||||
 | 
			
		||||
  The GNU General Public License is a free, copyleft license for
 | 
			
		||||
software and other kinds of works.
 | 
			
		||||
 | 
			
		||||
  The licenses for most software and other practical works are designed
 | 
			
		||||
to take away your freedom to share and change the works.  By contrast,
 | 
			
		||||
the GNU General Public License is intended to guarantee your freedom to
 | 
			
		||||
share and change all versions of a program--to make sure it remains free
 | 
			
		||||
software for all its users.  We, the Free Software Foundation, use the
 | 
			
		||||
GNU General Public License for most of our software; it applies also to
 | 
			
		||||
any other work released this way by its authors.  You can apply it to
 | 
			
		||||
your programs, too.
 | 
			
		||||
 | 
			
		||||
  When we speak of free software, we are referring to freedom, not
 | 
			
		||||
price.  Our General Public Licenses are designed to make sure that you
 | 
			
		||||
have the freedom to distribute copies of free software (and charge for
 | 
			
		||||
them if you wish), that you receive source code or can get it if you
 | 
			
		||||
want it, that you can change the software or use pieces of it in new
 | 
			
		||||
free programs, and that you know you can do these things.
 | 
			
		||||
 | 
			
		||||
  To protect your rights, we need to prevent others from denying you
 | 
			
		||||
these rights or asking you to surrender the rights.  Therefore, you have
 | 
			
		||||
certain responsibilities if you distribute copies of the software, or if
 | 
			
		||||
you modify it: responsibilities to respect the freedom of others.
 | 
			
		||||
 | 
			
		||||
  For example, if you distribute copies of such a program, whether
 | 
			
		||||
gratis or for a fee, you must pass on to the recipients the same
 | 
			
		||||
freedoms that you received.  You must make sure that they, too, receive
 | 
			
		||||
or can get the source code.  And you must show them these terms so they
 | 
			
		||||
know their rights.
 | 
			
		||||
 | 
			
		||||
  Developers that use the GNU GPL protect your rights with two steps:
 | 
			
		||||
(1) assert copyright on the software, and (2) offer you this License
 | 
			
		||||
giving you legal permission to copy, distribute and/or modify it.
 | 
			
		||||
 | 
			
		||||
  For the developers' and authors' protection, the GPL clearly explains
 | 
			
		||||
that there is no warranty for this free software.  For both users' and
 | 
			
		||||
authors' sake, the GPL requires that modified versions be marked as
 | 
			
		||||
changed, so that their problems will not be attributed erroneously to
 | 
			
		||||
authors of previous versions.
 | 
			
		||||
 | 
			
		||||
  Some devices are designed to deny users access to install or run
 | 
			
		||||
modified versions of the software inside them, although the manufacturer
 | 
			
		||||
can do so.  This is fundamentally incompatible with the aim of
 | 
			
		||||
protecting users' freedom to change the software.  The systematic
 | 
			
		||||
pattern of such abuse occurs in the area of products for individuals to
 | 
			
		||||
use, which is precisely where it is most unacceptable.  Therefore, we
 | 
			
		||||
have designed this version of the GPL to prohibit the practice for those
 | 
			
		||||
products.  If such problems arise substantially in other domains, we
 | 
			
		||||
stand ready to extend this provision to those domains in future versions
 | 
			
		||||
of the GPL, as needed to protect the freedom of users.
 | 
			
		||||
 | 
			
		||||
  Finally, every program is threatened constantly by software patents.
 | 
			
		||||
States should not allow patents to restrict development and use of
 | 
			
		||||
software on general-purpose computers, but in those that do, we wish to
 | 
			
		||||
avoid the special danger that patents applied to a free program could
 | 
			
		||||
make it effectively proprietary.  To prevent this, the GPL assures that
 | 
			
		||||
patents cannot be used to render the program non-free.
 | 
			
		||||
 | 
			
		||||
  The precise terms and conditions for copying, distribution and
 | 
			
		||||
modification follow.
 | 
			
		||||
 | 
			
		||||
                       TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
  0. Definitions.
 | 
			
		||||
 | 
			
		||||
  "This License" refers to version 3 of the GNU General Public License.
 | 
			
		||||
 | 
			
		||||
  "Copyright" also means copyright-like laws that apply to other kinds of
 | 
			
		||||
works, such as semiconductor masks.
 | 
			
		||||
 | 
			
		||||
  "The Program" refers to any copyrightable work licensed under this
 | 
			
		||||
License.  Each licensee is addressed as "you".  "Licensees" and
 | 
			
		||||
"recipients" may be individuals or organizations.
 | 
			
		||||
 | 
			
		||||
  To "modify" a work means to copy from or adapt all or part of the work
 | 
			
		||||
in a fashion requiring copyright permission, other than the making of an
 | 
			
		||||
exact copy.  The resulting work is called a "modified version" of the
 | 
			
		||||
earlier work or a work "based on" the earlier work.
 | 
			
		||||
 | 
			
		||||
  A "covered work" means either the unmodified Program or a work based
 | 
			
		||||
on the Program.
 | 
			
		||||
 | 
			
		||||
  To "propagate" a work means to do anything with it that, without
 | 
			
		||||
permission, would make you directly or secondarily liable for
 | 
			
		||||
infringement under applicable copyright law, except executing it on a
 | 
			
		||||
computer or modifying a private copy.  Propagation includes copying,
 | 
			
		||||
distribution (with or without modification), making available to the
 | 
			
		||||
public, and in some countries other activities as well.
 | 
			
		||||
 | 
			
		||||
  To "convey" a work means any kind of propagation that enables other
 | 
			
		||||
parties to make or receive copies.  Mere interaction with a user through
 | 
			
		||||
a computer network, with no transfer of a copy, is not conveying.
 | 
			
		||||
 | 
			
		||||
  An interactive user interface displays "Appropriate Legal Notices"
 | 
			
		||||
to the extent that it includes a convenient and prominently visible
 | 
			
		||||
feature that (1) displays an appropriate copyright notice, and (2)
 | 
			
		||||
tells the user that there is no warranty for the work (except to the
 | 
			
		||||
extent that warranties are provided), that licensees may convey the
 | 
			
		||||
work under this License, and how to view a copy of this License.  If
 | 
			
		||||
the interface presents a list of user commands or options, such as a
 | 
			
		||||
menu, a prominent item in the list meets this criterion.
 | 
			
		||||
 | 
			
		||||
  1. Source Code.
 | 
			
		||||
 | 
			
		||||
  The "source code" for a work means the preferred form of the work
 | 
			
		||||
for making modifications to it.  "Object code" means any non-source
 | 
			
		||||
form of a work.
 | 
			
		||||
 | 
			
		||||
  A "Standard Interface" means an interface that either is an official
 | 
			
		||||
standard defined by a recognized standards body, or, in the case of
 | 
			
		||||
interfaces specified for a particular programming language, one that
 | 
			
		||||
is widely used among developers working in that language.
 | 
			
		||||
 | 
			
		||||
  The "System Libraries" of an executable work include anything, other
 | 
			
		||||
than the work as a whole, that (a) is included in the normal form of
 | 
			
		||||
packaging a Major Component, but which is not part of that Major
 | 
			
		||||
Component, and (b) serves only to enable use of the work with that
 | 
			
		||||
Major Component, or to implement a Standard Interface for which an
 | 
			
		||||
implementation is available to the public in source code form.  A
 | 
			
		||||
"Major Component", in this context, means a major essential component
 | 
			
		||||
(kernel, window system, and so on) of the specific operating system
 | 
			
		||||
(if any) on which the executable work runs, or a compiler used to
 | 
			
		||||
produce the work, or an object code interpreter used to run it.
 | 
			
		||||
 | 
			
		||||
  The "Corresponding Source" for a work in object code form means all
 | 
			
		||||
the source code needed to generate, install, and (for an executable
 | 
			
		||||
work) run the object code and to modify the work, including scripts to
 | 
			
		||||
control those activities.  However, it does not include the work's
 | 
			
		||||
System Libraries, or general-purpose tools or generally available free
 | 
			
		||||
programs which are used unmodified in performing those activities but
 | 
			
		||||
which are not part of the work.  For example, Corresponding Source
 | 
			
		||||
includes interface definition files associated with source files for
 | 
			
		||||
the work, and the source code for shared libraries and dynamically
 | 
			
		||||
linked subprograms that the work is specifically designed to require,
 | 
			
		||||
such as by intimate data communication or control flow between those
 | 
			
		||||
subprograms and other parts of the work.
 | 
			
		||||
 | 
			
		||||
  The Corresponding Source need not include anything that users
 | 
			
		||||
can regenerate automatically from other parts of the Corresponding
 | 
			
		||||
Source.
 | 
			
		||||
 | 
			
		||||
  The Corresponding Source for a work in source code form is that
 | 
			
		||||
same work.
 | 
			
		||||
 | 
			
		||||
  2. Basic Permissions.
 | 
			
		||||
 | 
			
		||||
  All rights granted under this License are granted for the term of
 | 
			
		||||
copyright on the Program, and are irrevocable provided the stated
 | 
			
		||||
conditions are met.  This License explicitly affirms your unlimited
 | 
			
		||||
permission to run the unmodified Program.  The output from running a
 | 
			
		||||
covered work is covered by this License only if the output, given its
 | 
			
		||||
content, constitutes a covered work.  This License acknowledges your
 | 
			
		||||
rights of fair use or other equivalent, as provided by copyright law.
 | 
			
		||||
 | 
			
		||||
  You may make, run and propagate covered works that you do not
 | 
			
		||||
convey, without conditions so long as your license otherwise remains
 | 
			
		||||
in force.  You may convey covered works to others for the sole purpose
 | 
			
		||||
of having them make modifications exclusively for you, or provide you
 | 
			
		||||
with facilities for running those works, provided that you comply with
 | 
			
		||||
the terms of this License in conveying all material for which you do
 | 
			
		||||
not control copyright.  Those thus making or running the covered works
 | 
			
		||||
for you must do so exclusively on your behalf, under your direction
 | 
			
		||||
and control, on terms that prohibit them from making any copies of
 | 
			
		||||
your copyrighted material outside their relationship with you.
 | 
			
		||||
 | 
			
		||||
  Conveying under any other circumstances is permitted solely under
 | 
			
		||||
the conditions stated below.  Sublicensing is not allowed; section 10
 | 
			
		||||
makes it unnecessary.
 | 
			
		||||
 | 
			
		||||
  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
 | 
			
		||||
 | 
			
		||||
  No covered work shall be deemed part of an effective technological
 | 
			
		||||
measure under any applicable law fulfilling obligations under article
 | 
			
		||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
 | 
			
		||||
similar laws prohibiting or restricting circumvention of such
 | 
			
		||||
measures.
 | 
			
		||||
 | 
			
		||||
  When you convey a covered work, you waive any legal power to forbid
 | 
			
		||||
circumvention of technological measures to the extent such circumvention
 | 
			
		||||
is effected by exercising rights under this License with respect to
 | 
			
		||||
the covered work, and you disclaim any intention to limit operation or
 | 
			
		||||
modification of the work as a means of enforcing, against the work's
 | 
			
		||||
users, your or third parties' legal rights to forbid circumvention of
 | 
			
		||||
technological measures.
 | 
			
		||||
 | 
			
		||||
  4. Conveying Verbatim Copies.
 | 
			
		||||
 | 
			
		||||
  You may convey verbatim copies of the Program's source code as you
 | 
			
		||||
receive it, in any medium, provided that you conspicuously and
 | 
			
		||||
appropriately publish on each copy an appropriate copyright notice;
 | 
			
		||||
keep intact all notices stating that this License and any
 | 
			
		||||
non-permissive terms added in accord with section 7 apply to the code;
 | 
			
		||||
keep intact all notices of the absence of any warranty; and give all
 | 
			
		||||
recipients a copy of this License along with the Program.
 | 
			
		||||
 | 
			
		||||
  You may charge any price or no price for each copy that you convey,
 | 
			
		||||
and you may offer support or warranty protection for a fee.
 | 
			
		||||
 | 
			
		||||
  5. Conveying Modified Source Versions.
 | 
			
		||||
 | 
			
		||||
  You may convey a work based on the Program, or the modifications to
 | 
			
		||||
produce it from the Program, in the form of source code under the
 | 
			
		||||
terms of section 4, provided that you also meet all of these conditions:
 | 
			
		||||
 | 
			
		||||
    a) The work must carry prominent notices stating that you modified
 | 
			
		||||
    it, and giving a relevant date.
 | 
			
		||||
 | 
			
		||||
    b) The work must carry prominent notices stating that it is
 | 
			
		||||
    released under this License and any conditions added under section
 | 
			
		||||
    7.  This requirement modifies the requirement in section 4 to
 | 
			
		||||
    "keep intact all notices".
 | 
			
		||||
 | 
			
		||||
    c) You must license the entire work, as a whole, under this
 | 
			
		||||
    License to anyone who comes into possession of a copy.  This
 | 
			
		||||
    License will therefore apply, along with any applicable section 7
 | 
			
		||||
    additional terms, to the whole of the work, and all its parts,
 | 
			
		||||
    regardless of how they are packaged.  This License gives no
 | 
			
		||||
    permission to license the work in any other way, but it does not
 | 
			
		||||
    invalidate such permission if you have separately received it.
 | 
			
		||||
 | 
			
		||||
    d) If the work has interactive user interfaces, each must display
 | 
			
		||||
    Appropriate Legal Notices; however, if the Program has interactive
 | 
			
		||||
    interfaces that do not display Appropriate Legal Notices, your
 | 
			
		||||
    work need not make them do so.
 | 
			
		||||
 | 
			
		||||
  A compilation of a covered work with other separate and independent
 | 
			
		||||
works, which are not by their nature extensions of the covered work,
 | 
			
		||||
and which are not combined with it such as to form a larger program,
 | 
			
		||||
in or on a volume of a storage or distribution medium, is called an
 | 
			
		||||
"aggregate" if the compilation and its resulting copyright are not
 | 
			
		||||
used to limit the access or legal rights of the compilation's users
 | 
			
		||||
beyond what the individual works permit.  Inclusion of a covered work
 | 
			
		||||
in an aggregate does not cause this License to apply to the other
 | 
			
		||||
parts of the aggregate.
 | 
			
		||||
 | 
			
		||||
  6. Conveying Non-Source Forms.
 | 
			
		||||
 | 
			
		||||
  You may convey a covered work in object code form under the terms
 | 
			
		||||
of sections 4 and 5, provided that you also convey the
 | 
			
		||||
machine-readable Corresponding Source under the terms of this License,
 | 
			
		||||
in one of these ways:
 | 
			
		||||
 | 
			
		||||
    a) Convey the object code in, or embodied in, a physical product
 | 
			
		||||
    (including a physical distribution medium), accompanied by the
 | 
			
		||||
    Corresponding Source fixed on a durable physical medium
 | 
			
		||||
    customarily used for software interchange.
 | 
			
		||||
 | 
			
		||||
    b) Convey the object code in, or embodied in, a physical product
 | 
			
		||||
    (including a physical distribution medium), accompanied by a
 | 
			
		||||
    written offer, valid for at least three years and valid for as
 | 
			
		||||
    long as you offer spare parts or customer support for that product
 | 
			
		||||
    model, to give anyone who possesses the object code either (1) a
 | 
			
		||||
    copy of the Corresponding Source for all the software in the
 | 
			
		||||
    product that is covered by this License, on a durable physical
 | 
			
		||||
    medium customarily used for software interchange, for a price no
 | 
			
		||||
    more than your reasonable cost of physically performing this
 | 
			
		||||
    conveying of source, or (2) access to copy the
 | 
			
		||||
    Corresponding Source from a network server at no charge.
 | 
			
		||||
 | 
			
		||||
    c) Convey individual copies of the object code with a copy of the
 | 
			
		||||
    written offer to provide the Corresponding Source.  This
 | 
			
		||||
    alternative is allowed only occasionally and noncommercially, and
 | 
			
		||||
    only if you received the object code with such an offer, in accord
 | 
			
		||||
    with subsection 6b.
 | 
			
		||||
 | 
			
		||||
    d) Convey the object code by offering access from a designated
 | 
			
		||||
    place (gratis or for a charge), and offer equivalent access to the
 | 
			
		||||
    Corresponding Source in the same way through the same place at no
 | 
			
		||||
    further charge.  You need not require recipients to copy the
 | 
			
		||||
    Corresponding Source along with the object code.  If the place to
 | 
			
		||||
    copy the object code is a network server, the Corresponding Source
 | 
			
		||||
    may be on a different server (operated by you or a third party)
 | 
			
		||||
    that supports equivalent copying facilities, provided you maintain
 | 
			
		||||
    clear directions next to the object code saying where to find the
 | 
			
		||||
    Corresponding Source.  Regardless of what server hosts the
 | 
			
		||||
    Corresponding Source, you remain obligated to ensure that it is
 | 
			
		||||
    available for as long as needed to satisfy these requirements.
 | 
			
		||||
 | 
			
		||||
    e) Convey the object code using peer-to-peer transmission, provided
 | 
			
		||||
    you inform other peers where the object code and Corresponding
 | 
			
		||||
    Source of the work are being offered to the general public at no
 | 
			
		||||
    charge under subsection 6d.
 | 
			
		||||
 | 
			
		||||
  A separable portion of the object code, whose source code is excluded
 | 
			
		||||
from the Corresponding Source as a System Library, need not be
 | 
			
		||||
included in conveying the object code work.
 | 
			
		||||
 | 
			
		||||
  A "User Product" is either (1) a "consumer product", which means any
 | 
			
		||||
tangible personal property which is normally used for personal, family,
 | 
			
		||||
or household purposes, or (2) anything designed or sold for incorporation
 | 
			
		||||
into a dwelling.  In determining whether a product is a consumer product,
 | 
			
		||||
doubtful cases shall be resolved in favor of coverage.  For a particular
 | 
			
		||||
product received by a particular user, "normally used" refers to a
 | 
			
		||||
typical or common use of that class of product, regardless of the status
 | 
			
		||||
of the particular user or of the way in which the particular user
 | 
			
		||||
actually uses, or expects or is expected to use, the product.  A product
 | 
			
		||||
is a consumer product regardless of whether the product has substantial
 | 
			
		||||
commercial, industrial or non-consumer uses, unless such uses represent
 | 
			
		||||
the only significant mode of use of the product.
 | 
			
		||||
 | 
			
		||||
  "Installation Information" for a User Product means any methods,
 | 
			
		||||
procedures, authorization keys, or other information required to install
 | 
			
		||||
and execute modified versions of a covered work in that User Product from
 | 
			
		||||
a modified version of its Corresponding Source.  The information must
 | 
			
		||||
suffice to ensure that the continued functioning of the modified object
 | 
			
		||||
code is in no case prevented or interfered with solely because
 | 
			
		||||
modification has been made.
 | 
			
		||||
 | 
			
		||||
  If you convey an object code work under this section in, or with, or
 | 
			
		||||
specifically for use in, a User Product, and the conveying occurs as
 | 
			
		||||
part of a transaction in which the right of possession and use of the
 | 
			
		||||
User Product is transferred to the recipient in perpetuity or for a
 | 
			
		||||
fixed term (regardless of how the transaction is characterized), the
 | 
			
		||||
Corresponding Source conveyed under this section must be accompanied
 | 
			
		||||
by the Installation Information.  But this requirement does not apply
 | 
			
		||||
if neither you nor any third party retains the ability to install
 | 
			
		||||
modified object code on the User Product (for example, the work has
 | 
			
		||||
been installed in ROM).
 | 
			
		||||
 | 
			
		||||
  The requirement to provide Installation Information does not include a
 | 
			
		||||
requirement to continue to provide support service, warranty, or updates
 | 
			
		||||
for a work that has been modified or installed by the recipient, or for
 | 
			
		||||
the User Product in which it has been modified or installed.  Access to a
 | 
			
		||||
network may be denied when the modification itself materially and
 | 
			
		||||
adversely affects the operation of the network or violates the rules and
 | 
			
		||||
protocols for communication across the network.
 | 
			
		||||
 | 
			
		||||
  Corresponding Source conveyed, and Installation Information provided,
 | 
			
		||||
in accord with this section must be in a format that is publicly
 | 
			
		||||
documented (and with an implementation available to the public in
 | 
			
		||||
source code form), and must require no special password or key for
 | 
			
		||||
unpacking, reading or copying.
 | 
			
		||||
 | 
			
		||||
  7. Additional Terms.
 | 
			
		||||
 | 
			
		||||
  "Additional permissions" are terms that supplement the terms of this
 | 
			
		||||
License by making exceptions from one or more of its conditions.
 | 
			
		||||
Additional permissions that are applicable to the entire Program shall
 | 
			
		||||
be treated as though they were included in this License, to the extent
 | 
			
		||||
that they are valid under applicable law.  If additional permissions
 | 
			
		||||
apply only to part of the Program, that part may be used separately
 | 
			
		||||
under those permissions, but the entire Program remains governed by
 | 
			
		||||
this License without regard to the additional permissions.
 | 
			
		||||
 | 
			
		||||
  When you convey a copy of a covered work, you may at your option
 | 
			
		||||
remove any additional permissions from that copy, or from any part of
 | 
			
		||||
it.  (Additional permissions may be written to require their own
 | 
			
		||||
removal in certain cases when you modify the work.)  You may place
 | 
			
		||||
additional permissions on material, added by you to a covered work,
 | 
			
		||||
for which you have or can give appropriate copyright permission.
 | 
			
		||||
 | 
			
		||||
  Notwithstanding any other provision of this License, for material you
 | 
			
		||||
add to a covered work, you may (if authorized by the copyright holders of
 | 
			
		||||
that material) supplement the terms of this License with terms:
 | 
			
		||||
 | 
			
		||||
    a) Disclaiming warranty or limiting liability differently from the
 | 
			
		||||
    terms of sections 15 and 16 of this License; or
 | 
			
		||||
 | 
			
		||||
    b) Requiring preservation of specified reasonable legal notices or
 | 
			
		||||
    author attributions in that material or in the Appropriate Legal
 | 
			
		||||
    Notices displayed by works containing it; or
 | 
			
		||||
 | 
			
		||||
    c) Prohibiting misrepresentation of the origin of that material, or
 | 
			
		||||
    requiring that modified versions of such material be marked in
 | 
			
		||||
    reasonable ways as different from the original version; or
 | 
			
		||||
 | 
			
		||||
    d) Limiting the use for publicity purposes of names of licensors or
 | 
			
		||||
    authors of the material; or
 | 
			
		||||
 | 
			
		||||
    e) Declining to grant rights under trademark law for use of some
 | 
			
		||||
    trade names, trademarks, or service marks; or
 | 
			
		||||
 | 
			
		||||
    f) Requiring indemnification of licensors and authors of that
 | 
			
		||||
    material by anyone who conveys the material (or modified versions of
 | 
			
		||||
    it) with contractual assumptions of liability to the recipient, for
 | 
			
		||||
    any liability that these contractual assumptions directly impose on
 | 
			
		||||
    those licensors and authors.
 | 
			
		||||
 | 
			
		||||
  All other non-permissive additional terms are considered "further
 | 
			
		||||
restrictions" within the meaning of section 10.  If the Program as you
 | 
			
		||||
received it, or any part of it, contains a notice stating that it is
 | 
			
		||||
governed by this License along with a term that is a further
 | 
			
		||||
restriction, you may remove that term.  If a license document contains
 | 
			
		||||
a further restriction but permits relicensing or conveying under this
 | 
			
		||||
License, you may add to a covered work material governed by the terms
 | 
			
		||||
of that license document, provided that the further restriction does
 | 
			
		||||
not survive such relicensing or conveying.
 | 
			
		||||
 | 
			
		||||
  If you add terms to a covered work in accord with this section, you
 | 
			
		||||
must place, in the relevant source files, a statement of the
 | 
			
		||||
additional terms that apply to those files, or a notice indicating
 | 
			
		||||
where to find the applicable terms.
 | 
			
		||||
 | 
			
		||||
  Additional terms, permissive or non-permissive, may be stated in the
 | 
			
		||||
form of a separately written license, or stated as exceptions;
 | 
			
		||||
the above requirements apply either way.
 | 
			
		||||
 | 
			
		||||
  8. Termination.
 | 
			
		||||
 | 
			
		||||
  You may not propagate or modify a covered work except as expressly
 | 
			
		||||
provided under this License.  Any attempt otherwise to propagate or
 | 
			
		||||
modify it is void, and will automatically terminate your rights under
 | 
			
		||||
this License (including any patent licenses granted under the third
 | 
			
		||||
paragraph of section 11).
 | 
			
		||||
 | 
			
		||||
  However, if you cease all violation of this License, then your
 | 
			
		||||
license from a particular copyright holder is reinstated (a)
 | 
			
		||||
provisionally, unless and until the copyright holder explicitly and
 | 
			
		||||
finally terminates your license, and (b) permanently, if the copyright
 | 
			
		||||
holder fails to notify you of the violation by some reasonable means
 | 
			
		||||
prior to 60 days after the cessation.
 | 
			
		||||
 | 
			
		||||
  Moreover, your license from a particular copyright holder is
 | 
			
		||||
reinstated permanently if the copyright holder notifies you of the
 | 
			
		||||
violation by some reasonable means, this is the first time you have
 | 
			
		||||
received notice of violation of this License (for any work) from that
 | 
			
		||||
copyright holder, and you cure the violation prior to 30 days after
 | 
			
		||||
your receipt of the notice.
 | 
			
		||||
 | 
			
		||||
  Termination of your rights under this section does not terminate the
 | 
			
		||||
licenses of parties who have received copies or rights from you under
 | 
			
		||||
this License.  If your rights have been terminated and not permanently
 | 
			
		||||
reinstated, you do not qualify to receive new licenses for the same
 | 
			
		||||
material under section 10.
 | 
			
		||||
 | 
			
		||||
  9. Acceptance Not Required for Having Copies.
 | 
			
		||||
 | 
			
		||||
  You are not required to accept this License in order to receive or
 | 
			
		||||
run a copy of the Program.  Ancillary propagation of a covered work
 | 
			
		||||
occurring solely as a consequence of using peer-to-peer transmission
 | 
			
		||||
to receive a copy likewise does not require acceptance.  However,
 | 
			
		||||
nothing other than this License grants you permission to propagate or
 | 
			
		||||
modify any covered work.  These actions infringe copyright if you do
 | 
			
		||||
not accept this License.  Therefore, by modifying or propagating a
 | 
			
		||||
covered work, you indicate your acceptance of this License to do so.
 | 
			
		||||
 | 
			
		||||
  10. Automatic Licensing of Downstream Recipients.
 | 
			
		||||
 | 
			
		||||
  Each time you convey a covered work, the recipient automatically
 | 
			
		||||
receives a license from the original licensors, to run, modify and
 | 
			
		||||
propagate that work, subject to this License.  You are not responsible
 | 
			
		||||
for enforcing compliance by third parties with this License.
 | 
			
		||||
 | 
			
		||||
  An "entity transaction" is a transaction transferring control of an
 | 
			
		||||
organization, or substantially all assets of one, or subdividing an
 | 
			
		||||
organization, or merging organizations.  If propagation of a covered
 | 
			
		||||
work results from an entity transaction, each party to that
 | 
			
		||||
transaction who receives a copy of the work also receives whatever
 | 
			
		||||
licenses to the work the party's predecessor in interest had or could
 | 
			
		||||
give under the previous paragraph, plus a right to possession of the
 | 
			
		||||
Corresponding Source of the work from the predecessor in interest, if
 | 
			
		||||
the predecessor has it or can get it with reasonable efforts.
 | 
			
		||||
 | 
			
		||||
  You may not impose any further restrictions on the exercise of the
 | 
			
		||||
rights granted or affirmed under this License.  For example, you may
 | 
			
		||||
not impose a license fee, royalty, or other charge for exercise of
 | 
			
		||||
rights granted under this License, and you may not initiate litigation
 | 
			
		||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
 | 
			
		||||
any patent claim is infringed by making, using, selling, offering for
 | 
			
		||||
sale, or importing the Program or any portion of it.
 | 
			
		||||
 | 
			
		||||
  11. Patents.
 | 
			
		||||
 | 
			
		||||
  A "contributor" is a copyright holder who authorizes use under this
 | 
			
		||||
License of the Program or a work on which the Program is based.  The
 | 
			
		||||
work thus licensed is called the contributor's "contributor version".
 | 
			
		||||
 | 
			
		||||
  A contributor's "essential patent claims" are all patent claims
 | 
			
		||||
owned or controlled by the contributor, whether already acquired or
 | 
			
		||||
hereafter acquired, that would be infringed by some manner, permitted
 | 
			
		||||
by this License, of making, using, or selling its contributor version,
 | 
			
		||||
but do not include claims that would be infringed only as a
 | 
			
		||||
consequence of further modification of the contributor version.  For
 | 
			
		||||
purposes of this definition, "control" includes the right to grant
 | 
			
		||||
patent sublicenses in a manner consistent with the requirements of
 | 
			
		||||
this License.
 | 
			
		||||
 | 
			
		||||
  Each contributor grants you a non-exclusive, worldwide, royalty-free
 | 
			
		||||
patent license under the contributor's essential patent claims, to
 | 
			
		||||
make, use, sell, offer for sale, import and otherwise run, modify and
 | 
			
		||||
propagate the contents of its contributor version.
 | 
			
		||||
 | 
			
		||||
  In the following three paragraphs, a "patent license" is any express
 | 
			
		||||
agreement or commitment, however denominated, not to enforce a patent
 | 
			
		||||
(such as an express permission to practice a patent or covenant not to
 | 
			
		||||
sue for patent infringement).  To "grant" such a patent license to a
 | 
			
		||||
party means to make such an agreement or commitment not to enforce a
 | 
			
		||||
patent against the party.
 | 
			
		||||
 | 
			
		||||
  If you convey a covered work, knowingly relying on a patent license,
 | 
			
		||||
and the Corresponding Source of the work is not available for anyone
 | 
			
		||||
to copy, free of charge and under the terms of this License, through a
 | 
			
		||||
publicly available network server or other readily accessible means,
 | 
			
		||||
then you must either (1) cause the Corresponding Source to be so
 | 
			
		||||
available, or (2) arrange to deprive yourself of the benefit of the
 | 
			
		||||
patent license for this particular work, or (3) arrange, in a manner
 | 
			
		||||
consistent with the requirements of this License, to extend the patent
 | 
			
		||||
license to downstream recipients.  "Knowingly relying" means you have
 | 
			
		||||
actual knowledge that, but for the patent license, your conveying the
 | 
			
		||||
covered work in a country, or your recipient's use of the covered work
 | 
			
		||||
in a country, would infringe one or more identifiable patents in that
 | 
			
		||||
country that you have reason to believe are valid.
 | 
			
		||||
 | 
			
		||||
  If, pursuant to or in connection with a single transaction or
 | 
			
		||||
arrangement, you convey, or propagate by procuring conveyance of, a
 | 
			
		||||
covered work, and grant a patent license to some of the parties
 | 
			
		||||
receiving the covered work authorizing them to use, propagate, modify
 | 
			
		||||
or convey a specific copy of the covered work, then the patent license
 | 
			
		||||
you grant is automatically extended to all recipients of the covered
 | 
			
		||||
work and works based on it.
 | 
			
		||||
 | 
			
		||||
  A patent license is "discriminatory" if it does not include within
 | 
			
		||||
the scope of its coverage, prohibits the exercise of, or is
 | 
			
		||||
conditioned on the non-exercise of one or more of the rights that are
 | 
			
		||||
specifically granted under this License.  You may not convey a covered
 | 
			
		||||
work if you are a party to an arrangement with a third party that is
 | 
			
		||||
in the business of distributing software, under which you make payment
 | 
			
		||||
to the third party based on the extent of your activity of conveying
 | 
			
		||||
the work, and under which the third party grants, to any of the
 | 
			
		||||
parties who would receive the covered work from you, a discriminatory
 | 
			
		||||
patent license (a) in connection with copies of the covered work
 | 
			
		||||
conveyed by you (or copies made from those copies), or (b) primarily
 | 
			
		||||
for and in connection with specific products or compilations that
 | 
			
		||||
contain the covered work, unless you entered into that arrangement,
 | 
			
		||||
or that patent license was granted, prior to 28 March 2007.
 | 
			
		||||
 | 
			
		||||
  Nothing in this License shall be construed as excluding or limiting
 | 
			
		||||
any implied license or other defenses to infringement that may
 | 
			
		||||
otherwise be available to you under applicable patent law.
 | 
			
		||||
 | 
			
		||||
  12. No Surrender of Others' Freedom.
 | 
			
		||||
 | 
			
		||||
  If conditions are imposed on you (whether by court order, agreement or
 | 
			
		||||
otherwise) that contradict the conditions of this License, they do not
 | 
			
		||||
excuse you from the conditions of this License.  If you cannot convey a
 | 
			
		||||
covered work so as to satisfy simultaneously your obligations under this
 | 
			
		||||
License and any other pertinent obligations, then as a consequence you may
 | 
			
		||||
not convey it at all.  For example, if you agree to terms that obligate you
 | 
			
		||||
to collect a royalty for further conveying from those to whom you convey
 | 
			
		||||
the Program, the only way you could satisfy both those terms and this
 | 
			
		||||
License would be to refrain entirely from conveying the Program.
 | 
			
		||||
 | 
			
		||||
  13. Use with the GNU Affero General Public License.
 | 
			
		||||
 | 
			
		||||
  Notwithstanding any other provision of this License, you have
 | 
			
		||||
permission to link or combine any covered work with a work licensed
 | 
			
		||||
under version 3 of the GNU Affero General Public License into a single
 | 
			
		||||
combined work, and to convey the resulting work.  The terms of this
 | 
			
		||||
License will continue to apply to the part which is the covered work,
 | 
			
		||||
but the special requirements of the GNU Affero General Public License,
 | 
			
		||||
section 13, concerning interaction through a network will apply to the
 | 
			
		||||
combination as such.
 | 
			
		||||
 | 
			
		||||
  14. Revised Versions of this License.
 | 
			
		||||
 | 
			
		||||
  The Free Software Foundation may publish revised and/or new versions of
 | 
			
		||||
the GNU General Public License from time to time.  Such new versions will
 | 
			
		||||
be similar in spirit to the present version, but may differ in detail to
 | 
			
		||||
address new problems or concerns.
 | 
			
		||||
 | 
			
		||||
  Each version is given a distinguishing version number.  If the
 | 
			
		||||
Program specifies that a certain numbered version of the GNU General
 | 
			
		||||
Public License "or any later version" applies to it, you have the
 | 
			
		||||
option of following the terms and conditions either of that numbered
 | 
			
		||||
version or of any later version published by the Free Software
 | 
			
		||||
Foundation.  If the Program does not specify a version number of the
 | 
			
		||||
GNU General Public License, you may choose any version ever published
 | 
			
		||||
by the Free Software Foundation.
 | 
			
		||||
 | 
			
		||||
  If the Program specifies that a proxy can decide which future
 | 
			
		||||
versions of the GNU General Public License can be used, that proxy's
 | 
			
		||||
public statement of acceptance of a version permanently authorizes you
 | 
			
		||||
to choose that version for the Program.
 | 
			
		||||
 | 
			
		||||
  Later license versions may give you additional or different
 | 
			
		||||
permissions.  However, no additional obligations are imposed on any
 | 
			
		||||
author or copyright holder as a result of your choosing to follow a
 | 
			
		||||
later version.
 | 
			
		||||
 | 
			
		||||
  15. Disclaimer of Warranty.
 | 
			
		||||
 | 
			
		||||
  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
 | 
			
		||||
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
 | 
			
		||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
 | 
			
		||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
 | 
			
		||||
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
 | 
			
		||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
 | 
			
		||||
 | 
			
		||||
  16. Limitation of Liability.
 | 
			
		||||
 | 
			
		||||
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 | 
			
		||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
 | 
			
		||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
 | 
			
		||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
 | 
			
		||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
 | 
			
		||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
 | 
			
		||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
 | 
			
		||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
 | 
			
		||||
SUCH DAMAGES.
 | 
			
		||||
 | 
			
		||||
  17. Interpretation of Sections 15 and 16.
 | 
			
		||||
 | 
			
		||||
  If the disclaimer of warranty and limitation of liability provided
 | 
			
		||||
above cannot be given local legal effect according to their terms,
 | 
			
		||||
reviewing courts shall apply local law that most closely approximates
 | 
			
		||||
an absolute waiver of all civil liability in connection with the
 | 
			
		||||
Program, unless a warranty or assumption of liability accompanies a
 | 
			
		||||
copy of the Program in return for a fee.
 | 
			
		||||
 | 
			
		||||
                     END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
            How to Apply These Terms to Your New Programs
 | 
			
		||||
 | 
			
		||||
  If you develop a new program, and you want it to be of the greatest
 | 
			
		||||
possible use to the public, the best way to achieve this is to make it
 | 
			
		||||
free software which everyone can redistribute and change under these terms.
 | 
			
		||||
 | 
			
		||||
  To do so, attach the following notices to the program.  It is safest
 | 
			
		||||
to attach them to the start of each source file to most effectively
 | 
			
		||||
state the exclusion of warranty; and each file should have at least
 | 
			
		||||
the "copyright" line and a pointer to where the full notice is found.
 | 
			
		||||
 | 
			
		||||
    <one line to give the program's name and a brief idea of what it does.>
 | 
			
		||||
    Copyright (C) <year>  <name of author>
 | 
			
		||||
 | 
			
		||||
    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
    it under the terms of the GNU General Public License as published by
 | 
			
		||||
    the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
    (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
    This program is distributed in the hope that it will be useful,
 | 
			
		||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
    GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Also add information on how to contact you by electronic and paper mail.
 | 
			
		||||
 | 
			
		||||
  If the program does terminal interaction, make it output a short
 | 
			
		||||
notice like this when it starts in an interactive mode:
 | 
			
		||||
 | 
			
		||||
    <program>  Copyright (C) <year>  <name of author>
 | 
			
		||||
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 | 
			
		||||
    This is free software, and you are welcome to redistribute it
 | 
			
		||||
    under certain conditions; type `show c' for details.
 | 
			
		||||
 | 
			
		||||
The hypothetical commands `show w' and `show c' should show the appropriate
 | 
			
		||||
parts of the General Public License.  Of course, your program's commands
 | 
			
		||||
might be different; for a GUI interface, you would use an "about box".
 | 
			
		||||
 | 
			
		||||
  You should also get your employer (if you work as a programmer) or school,
 | 
			
		||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
 | 
			
		||||
For more information on this, and how to apply and follow the GNU GPL, see
 | 
			
		||||
<https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
  The GNU General Public License does not permit incorporating your program
 | 
			
		||||
into proprietary programs.  If your program is a subroutine library, you
 | 
			
		||||
may consider it more useful to permit linking proprietary applications with
 | 
			
		||||
the library.  If this is what you want to do, use the GNU Lesser General
 | 
			
		||||
Public License instead of this License.  But first, please read
 | 
			
		||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
 | 
			
		||||
							
								
								
									
										505
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										505
									
								
								README.md
									
									
									
									
									
								
							@@ -1,92 +1,158 @@
 | 
			
		||||
# An ACME Shell script: acme.sh
 | 
			
		||||
# An ACME Shell script: acme.sh 
 | 
			
		||||
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)
 | 
			
		||||
[](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)
 | 
			
		||||
[](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)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<a href="https://opencollective.com/acmesh" alt="Financial Contributors on Open Collective"><img src="https://opencollective.com/acmesh/all/badge.svg?label=financial+contributors" /></a> 
 | 
			
		||||
[](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
			
		||||
[](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
 | 
			
		||||
[](https://hub.docker.com/r/neilpang/acme.sh "Click to view the image on Docker Hub")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- An ACME protocol client written purely in Shell (Unix shell) language.
 | 
			
		||||
- Fully ACME protocol implementation.
 | 
			
		||||
- Simple, powerful and very easy to use. You only need 3 minutes to learn.
 | 
			
		||||
- Bash, dash and sh compatible. 
 | 
			
		||||
- Simplest shell script for Let's Encrypt free certificate client.
 | 
			
		||||
- Purely written in Shell with no dependencies on python or Let's Encrypt official client.
 | 
			
		||||
- Just one script, to issue, renew and install your certificates automatically.
 | 
			
		||||
- Full ACME protocol implementation.
 | 
			
		||||
- Support ECDSA certs
 | 
			
		||||
- Support SAN and wildcard certs
 | 
			
		||||
- Simple, powerful and very easy to use. You only need 3 minutes to learn it.
 | 
			
		||||
- Bash, dash and sh compatible.
 | 
			
		||||
- Purely written in Shell with no dependencies on python.
 | 
			
		||||
- Just one script to issue, renew and install your certificates automatically.
 | 
			
		||||
- DOES NOT require `root/sudoer` access.
 | 
			
		||||
- Docker ready
 | 
			
		||||
- IPv6 ready
 | 
			
		||||
- Cron job notifications for renewal or error etc.
 | 
			
		||||
 | 
			
		||||
It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
 | 
			
		||||
It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates.
 | 
			
		||||
 | 
			
		||||
Wiki: https://github.com/acmesh-official/acme.sh/wiki
 | 
			
		||||
 | 
			
		||||
For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/acmesh-official/acme.sh/wiki/Run-acme.sh-in-docker)
 | 
			
		||||
 | 
			
		||||
Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Wiki: https://github.com/Neilpang/acme.sh/wiki
 | 
			
		||||
# [中文说明](https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
 | 
			
		||||
 | 
			
		||||
# Who:
 | 
			
		||||
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
 | 
			
		||||
- [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)
 | 
			
		||||
- [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)
 | 
			
		||||
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
 | 
			
		||||
- [CentOS Web Panel](https://control-webpanel.com)
 | 
			
		||||
- [lnmp.org](https://lnmp.org/)
 | 
			
		||||
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
 | 
			
		||||
 | 
			
		||||
# Tested OS
 | 
			
		||||
 | 
			
		||||
#Tested OS
 | 
			
		||||
| NO | Status| Platform|
 | 
			
		||||
|----|-------|---------|
 | 
			
		||||
|1|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu
 | 
			
		||||
|2|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian
 | 
			
		||||
|3|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS
 | 
			
		||||
|4|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included)
 | 
			
		||||
|5|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD
 | 
			
		||||
|6|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense
 | 
			
		||||
|7|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE
 | 
			
		||||
|8|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl)
 | 
			
		||||
|9|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux
 | 
			
		||||
|10|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora
 | 
			
		||||
|11|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux
 | 
			
		||||
|12|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux
 | 
			
		||||
|13|[](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh
 | 
			
		||||
|14|-----| Cloud Linux  https://github.com/Neilpang/le/issues/111
 | 
			
		||||
|15|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD
 | 
			
		||||
|16|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia
 | 
			
		||||
|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT)
 | 
			
		||||
|18|[](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris
 | 
			
		||||
|1|[](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)|Mac OSX
 | 
			
		||||
|2|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)|Windows (cygwin with curl, openssl and crontab included)
 | 
			
		||||
|3|[](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)|FreeBSD
 | 
			
		||||
|4|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)|Solaris
 | 
			
		||||
|5|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)| Ubuntu
 | 
			
		||||
|6|NA|pfsense
 | 
			
		||||
|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/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)
 | 
			
		||||
 | 
			
		||||
For all build statuses, check our [daily build project](https://github.com/Neilpang/acmetest): 
 | 
			
		||||
 | 
			
		||||
https://github.com/Neilpang/acmetest
 | 
			
		||||
Check our [testing project](https://github.com/acmesh-official/acmetest):
 | 
			
		||||
 | 
			
		||||
# Supported Mode
 | 
			
		||||
https://github.com/acmesh-official/acmetest
 | 
			
		||||
 | 
			
		||||
1. Webroot mode
 | 
			
		||||
2. Standalone mode
 | 
			
		||||
3. Apache mode
 | 
			
		||||
4. Dns mode
 | 
			
		||||
# Supported CA
 | 
			
		||||
 | 
			
		||||
- [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA)(default)
 | 
			
		||||
- Letsencrypt.org 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
 | 
			
		||||
 | 
			
		||||
# Supported modes
 | 
			
		||||
 | 
			
		||||
- Webroot mode
 | 
			
		||||
- Standalone mode
 | 
			
		||||
- Standalone tls-alpn mode
 | 
			
		||||
- Apache mode
 | 
			
		||||
- Nginx mode
 | 
			
		||||
- DNS mode
 | 
			
		||||
- [DNS alias mode](https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode)
 | 
			
		||||
- [Stateless mode](https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 1. How to install
 | 
			
		||||
 | 
			
		||||
### 1. Install online:
 | 
			
		||||
### 1. Install online
 | 
			
		||||
 | 
			
		||||
Check this project: https://github.com/Neilpang/get.acme.sh
 | 
			
		||||
Check this project: https://github.com/acmesh-official/get.acme.sh
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
curl https://get.acme.sh | sh
 | 
			
		||||
 | 
			
		||||
curl https://get.acme.sh | sh -s email=my@example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Or:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
wget -O -  https://get.acme.sh | sh
 | 
			
		||||
 | 
			
		||||
wget -O -  https://get.acme.sh | sh -s email=my@example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 2. Or, Install from git:
 | 
			
		||||
### 2. Or, Install from git
 | 
			
		||||
 | 
			
		||||
Clone this project: 
 | 
			
		||||
Clone this project and launch installation:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
git clone https://github.com/Neilpang/acme.sh.git
 | 
			
		||||
git clone https://github.com/acmesh-official/acme.sh.git
 | 
			
		||||
cd ./acme.sh
 | 
			
		||||
./acme.sh --install
 | 
			
		||||
./acme.sh --install -m my@example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You `don't have to be root` then, although `it is recommended`.
 | 
			
		||||
 | 
			
		||||
Advanced Installation:  https://github.com/Neilpang/acme.sh/wiki/How-to-install
 | 
			
		||||
Advanced Installation: https://github.com/acmesh-official/acme.sh/wiki/How-to-install
 | 
			
		||||
 | 
			
		||||
The installer will perform 3 actions:
 | 
			
		||||
 | 
			
		||||
1. Create and copy `acme.sh` to your home dir (`$HOME`):  `~/.acme.sh/`.
 | 
			
		||||
All certs will be placed in this folder.
 | 
			
		||||
2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`. 
 | 
			
		||||
3. Create everyday cron job to check and renew the cert if needed.
 | 
			
		||||
1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`.
 | 
			
		||||
All certs will be placed in this folder too.
 | 
			
		||||
2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`.
 | 
			
		||||
3. Create daily cron job to check and renew the certs if needed.
 | 
			
		||||
 | 
			
		||||
Cron entry example:
 | 
			
		||||
 | 
			
		||||
@@ -94,241 +160,366 @@ Cron entry example:
 | 
			
		||||
0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
After the installation, you must close current terminal and reopen again to make the alias take effect.
 | 
			
		||||
After the installation, you must close the current terminal and reopen it to make the alias take effect.
 | 
			
		||||
 | 
			
		||||
Ok, you are ready to issue certs now.
 | 
			
		||||
 | 
			
		||||
Ok, you are ready to issue cert now.
 | 
			
		||||
Show help message:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
root@v1:~# acme.sh -h
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
# 2. Just issue a cert:
 | 
			
		||||
 | 
			
		||||
# 2. Just issue a cert
 | 
			
		||||
 | 
			
		||||
**Example 1:** Single domain.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue -d aa.com -w /home/wwwroot/aa.com
 | 
			
		||||
acme.sh --issue -d example.com -w /home/wwwroot/example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
or:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue -d example.com -w /home/username/public_html
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
or:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue -d example.com -w /var/www/html
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Example 2:** Multiple domains in the same cert.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue -d aa.com -d www.aa.com -d cp.aa.com -w /home/wwwroot/aa.com 
 | 
			
		||||
acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The parameter `/home/wwwroot/aa.com` is the web root folder. You **MUST** have `write access` to this folder.
 | 
			
		||||
The parameter `/home/wwwroot/example.com` or `/home/username/public_html` or `/var/www/html` is the web root folder where you host your website files. You **MUST** have `write access` to this folder.
 | 
			
		||||
 | 
			
		||||
Second argument **"aa.com"** is the main domain you want to issue cert for.
 | 
			
		||||
You must have at least a domain there.
 | 
			
		||||
Second argument **"example.com"** is the main domain you want to issue the cert for.
 | 
			
		||||
You must have at least one domain there.
 | 
			
		||||
 | 
			
		||||
You must point and bind all the domains to the same webroot dir: `/home/wwwroot/aa.com`.
 | 
			
		||||
You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`.
 | 
			
		||||
 | 
			
		||||
Generate/issued certs will be placed in `~/.acme.sh/aa.com/`
 | 
			
		||||
The certs will be placed in `~/.acme.sh/example.com/`
 | 
			
		||||
 | 
			
		||||
The issued cert will be renewed every 80 days automatically.
 | 
			
		||||
The certs will be renewed automatically every **60** days.
 | 
			
		||||
 | 
			
		||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 3. Install issued cert to apache/nginx etc.
 | 
			
		||||
# 3. Install the cert to Apache/Nginx etc.
 | 
			
		||||
 | 
			
		||||
After you issue a cert, you probably want to install the cert with your nginx/apache or other servers you may be using.
 | 
			
		||||
After the cert is generated, you probably want to install/copy the cert to your Apache/Nginx or other servers.
 | 
			
		||||
You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future.
 | 
			
		||||
 | 
			
		||||
**Apache** example:
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --installcert -d aa.com \
 | 
			
		||||
--certpath /path/to/certfile/in/apache/nginx  \
 | 
			
		||||
--keypath  /path/to/keyfile/in/apache/nginx  \
 | 
			
		||||
--capath   /path/to/ca/certfile/apache/nginx   \
 | 
			
		||||
--fullchainpath path/to/fullchain/certfile/apache/nginx \
 | 
			
		||||
--reloadcmd  "service apache2|nginx reload"
 | 
			
		||||
acme.sh --install-cert -d example.com \
 | 
			
		||||
--cert-file      /path/to/certfile/in/apache/cert.pem  \
 | 
			
		||||
--key-file       /path/to/keyfile/in/apache/key.pem  \
 | 
			
		||||
--fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
 | 
			
		||||
--reloadcmd     "service apache2 force-reload"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Nginx** example:
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --install-cert -d example.com \
 | 
			
		||||
--key-file       /path/to/keyfile/in/nginx/key.pem  \
 | 
			
		||||
--fullchain-file /path/to/fullchain/nginx/cert.pem \
 | 
			
		||||
--reloadcmd     "service nginx force-reload"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Only the domain is required, all the other parameters are optional.
 | 
			
		||||
 | 
			
		||||
Install the issued cert/key to the production apache or nginx path.
 | 
			
		||||
The ownership and permission info of existing files are preserved. You can pre-create the files to define the ownership and permission.
 | 
			
		||||
 | 
			
		||||
The cert will be `renewed every 80 days by default` (which is configurable). Once the cert is renewed, the apache/nginx will be automatically reloaded by the command: `service apache2 reload` or `service nginx reload`.
 | 
			
		||||
Install/copy the cert/key to the production Apache or Nginx path.
 | 
			
		||||
 | 
			
		||||
The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
**Please take care:  The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewed cert in 60 days.**
 | 
			
		||||
 | 
			
		||||
# 4. Use Standalone server to issue cert
 | 
			
		||||
 | 
			
		||||
**(requires you be root/sudoer, or you have permission to listen tcp 80 port)**
 | 
			
		||||
**(requires you to be root/sudoer or have permission to listen on port 80 (TCP))**
 | 
			
		||||
 | 
			
		||||
The tcp `80` port **MUST** be free to listen, otherwise you will be prompted to free the `80` port and try again.
 | 
			
		||||
Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue --standalone -d aa.com -d www.aa.com -d cp.aa.com
 | 
			
		||||
acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
 | 
			
		||||
# 5. Use Standalone tls server to issue cert
 | 
			
		||||
# 5. Use Standalone ssl server to issue cert
 | 
			
		||||
 | 
			
		||||
**(requires you be root/sudoer, or you have permission to listen tcp 443 port)**
 | 
			
		||||
**(requires you to be root/sudoer or have permission to listen on port 443 (TCP))**
 | 
			
		||||
 | 
			
		||||
acme.sh supports `tls-sni-01` validation.
 | 
			
		||||
 | 
			
		||||
The tcp `443` port **MUST** be free to listen, otherwise you will be prompted to free the `443` port and try again.
 | 
			
		||||
Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue --tls -d aa.com -d www.aa.com -d cp.aa.com
 | 
			
		||||
acme.sh --issue --alpn -d example.com -d www.example.com -d cp.example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 6. Use Apache mode
 | 
			
		||||
 | 
			
		||||
**(requires you be root/sudoer, since it is required to interact with apache server)**
 | 
			
		||||
**(requires you to be root/sudoer, since it is required to interact with Apache server)**
 | 
			
		||||
 | 
			
		||||
If you are running a web server, apache or nginx, it is recommended to use the `Webroot mode`.
 | 
			
		||||
If you are running a web server, it is recommended to use the `Webroot mode`.
 | 
			
		||||
 | 
			
		||||
Particularly, if you are running an apache server, you should use apache mode instead. This mode doesn't write any files to your web root folder.
 | 
			
		||||
Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder.
 | 
			
		||||
 | 
			
		||||
Just set string "apache" as the second argument, it will force use of apache plugin automatically.
 | 
			
		||||
Just set string "apache" as the second argument and it will force use of apache plugin automatically.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
acme.sh --issue --apache -d aa.com -d www.aa.com -d user.aa.com
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
**This apache mode is only to issue the cert, it will not change your apache config files.
 | 
			
		||||
You will need to configure your website config files to use the cert by yourself.
 | 
			
		||||
We don't want to mess with your apache server, don't worry.**
 | 
			
		||||
 | 
			
		||||
# 7. Use DNS mode:
 | 
			
		||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
 | 
			
		||||
Support the `dns-01` challenge.
 | 
			
		||||
# 7. Use Nginx mode
 | 
			
		||||
 | 
			
		||||
**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
 | 
			
		||||
 | 
			
		||||
If you are running a web server, it is recommended to use the `Webroot mode`.
 | 
			
		||||
 | 
			
		||||
Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
 | 
			
		||||
 | 
			
		||||
Just set string "nginx" as the second argument.
 | 
			
		||||
 | 
			
		||||
It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version.
 | 
			
		||||
 | 
			
		||||
So, the config is not changed.
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**This nginx mode is only to issue the cert, it will not change your nginx config files.
 | 
			
		||||
You will need to configure your website config files to use the cert by yourself.
 | 
			
		||||
We don't want to mess with your nginx server, don't worry.**
 | 
			
		||||
 | 
			
		||||
More examples: https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
 | 
			
		||||
 | 
			
		||||
# 8. Automatic DNS API integration
 | 
			
		||||
 | 
			
		||||
If your DNS provider supports API access, we can use that API to automatically issue the certs.
 | 
			
		||||
 | 
			
		||||
You don't have to do anything manually!
 | 
			
		||||
 | 
			
		||||
### Currently acme.sh supports most of the dns providers:
 | 
			
		||||
 | 
			
		||||
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
 | 
			
		||||
 | 
			
		||||
# 9. Use DNS manual mode:
 | 
			
		||||
 | 
			
		||||
See: https://github.com/acmesh-official/acme.sh/wiki/dns-manual-mode first.
 | 
			
		||||
 | 
			
		||||
If your dns provider doesn't support any api access, you can add the txt record by hand.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue --dns -d aa.com -d www.aa.com -d user.aa.com
 | 
			
		||||
acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You should get the output like below:
 | 
			
		||||
You should get an output like below:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```sh
 | 
			
		||||
Add the following txt record:
 | 
			
		||||
Domain:_acme-challenge.aa.com
 | 
			
		||||
Domain:_acme-challenge.example.com
 | 
			
		||||
Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c
 | 
			
		||||
 | 
			
		||||
Add the following txt record:
 | 
			
		||||
Domain:_acme-challenge.www.aa.com
 | 
			
		||||
Domain:_acme-challenge.www.example.com
 | 
			
		||||
Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
 | 
			
		||||
 | 
			
		||||
Please add those txt records to the domains. Waiting for the dns to take effect.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then just rerun with `renew` argument:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --renew -d aa.com
 | 
			
		||||
acme.sh --renew -d example.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Ok, it's finished.
 | 
			
		||||
Ok, it's done.
 | 
			
		||||
 | 
			
		||||
# 8. Automatic DNS API integration
 | 
			
		||||
**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.**
 | 
			
		||||
 | 
			
		||||
If your DNS provider supports API access, we can use API to automatically issue the certs.
 | 
			
		||||
**Please use dns api mode instead.**
 | 
			
		||||
 | 
			
		||||
You don't have do anything manually!
 | 
			
		||||
# 10. Issue ECC certificates
 | 
			
		||||
 | 
			
		||||
### Currently acme.sh supports:
 | 
			
		||||
 | 
			
		||||
1. Cloudflare.com API
 | 
			
		||||
2. Dnspod.cn API
 | 
			
		||||
3. Cloudxns.com API
 | 
			
		||||
4. Godaddy.com API
 | 
			
		||||
5. OVH, kimsufi, soyoustart and runabove API
 | 
			
		||||
6. AWS Route 53, see: https://github.com/Neilpang/acme.sh/issues/65
 | 
			
		||||
7. lexicon dns api: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
 | 
			
		||||
   (DigitalOcean, DNSimple, DnsMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
 | 
			
		||||
 | 
			
		||||
##### More APIs are coming soon...
 | 
			
		||||
 | 
			
		||||
If your DNS provider is not on the supported list above, you can write your own script API easily. If you do please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute to the project.
 | 
			
		||||
 | 
			
		||||
For more details: [How to use dns api](dnsapi)
 | 
			
		||||
 | 
			
		||||
# 9. Issue ECC certificate:
 | 
			
		||||
 | 
			
		||||
`Let's Encrypt` now can issue **ECDSA** certificates.
 | 
			
		||||
 | 
			
		||||
And we also support it.
 | 
			
		||||
 | 
			
		||||
Just set the `length` parameter with a prefix `ec-`.
 | 
			
		||||
Just set the `keylength` parameter with a prefix `ec-`.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
 | 
			
		||||
### Single domain ECC cerfiticate:
 | 
			
		||||
### Single domain ECC certificate
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue -w /home/wwwroot/aa.com -d aa.com --keylength  ec-256
 | 
			
		||||
acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
SAN multi domain ECC certificate:
 | 
			
		||||
### SAN multi domain ECC certificate
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
acme.sh --issue -w /home/wwwroot/aa.com -d aa.com -d www.aa.com --keylength  ec-256
 | 
			
		||||
acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Please look at the last parameter above.
 | 
			
		||||
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)**
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 10. How to renew the cert
 | 
			
		||||
# 11. Issue Wildcard certificates
 | 
			
		||||
 | 
			
		||||
No, you don't need to renew the certs manually.  All the certs will be renewed automatically every 80 days.
 | 
			
		||||
 | 
			
		||||
However, you can also force to renew any cert:
 | 
			
		||||
It's simple, just give a wildcard domain as the `-d` parameter.
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh  --issue -d example.com  -d '*.example.com'  --dns dns_cf
 | 
			
		||||
```
 | 
			
		||||
acme.sh --renew  -d  aa.com --force
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 12. How to renew the certs
 | 
			
		||||
 | 
			
		||||
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
 | 
			
		||||
 | 
			
		||||
However, you can also force to renew a cert:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh --renew -d example.com --force
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
or, for ECC cert:
 | 
			
		||||
```
 | 
			
		||||
acme.sh --renew  -d  aa.com  --force --ecc
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh --renew -d example.com --force --ecc
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# 11. How to upgrade `acme.sh`
 | 
			
		||||
acme.sh is in developing, it's strongly recommended to use the latest code.
 | 
			
		||||
 | 
			
		||||
# 13. How to stop cert renewal
 | 
			
		||||
 | 
			
		||||
To stop renewal of a cert, you can execute the following to remove the cert from the renewal list:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh --remove -d example.com [--ecc]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The cert/key file is not removed from the disk.
 | 
			
		||||
 | 
			
		||||
You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 14. How to upgrade `acme.sh`
 | 
			
		||||
 | 
			
		||||
acme.sh is in constant development, so it's strongly recommended to use the latest code.
 | 
			
		||||
 | 
			
		||||
You can update acme.sh to the latest code:
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh --upgrade
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# 12. Issue a cert from existing CSR
 | 
			
		||||
You can also enable auto upgrade:
 | 
			
		||||
 | 
			
		||||
https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh --upgrade --auto-upgrade
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then **acme.sh** will be kept up to date automatically.
 | 
			
		||||
 | 
			
		||||
Disable auto upgrade:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
acme.sh --upgrade --auto-upgrade 0
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Under the Hood
 | 
			
		||||
# 15. Issue a cert from an existing CSR
 | 
			
		||||
 | 
			
		||||
https://github.com/acmesh-official/acme.sh/wiki/Issue-a-cert-from-existing-CSR
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 16. Send notifications in cronjob
 | 
			
		||||
 | 
			
		||||
https://github.com/acmesh-official/acme.sh/wiki/notify
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 17. Under the Hood
 | 
			
		||||
 | 
			
		||||
Speak ACME language using shell, directly to "Let's Encrypt".
 | 
			
		||||
 | 
			
		||||
TODO:
 | 
			
		||||
 | 
			
		||||
# Acknowledgment
 | 
			
		||||
 | 
			
		||||
# 18. Acknowledgments
 | 
			
		||||
 | 
			
		||||
1. Acme-tiny: https://github.com/diafygi/acme-tiny
 | 
			
		||||
2. ACME protocol: https://github.com/ietf-wg-acme/acme
 | 
			
		||||
3. Certbot: https://github.com/certbot/certbot
 | 
			
		||||
 | 
			
		||||
# License & Other
 | 
			
		||||
 | 
			
		||||
## Contributors
 | 
			
		||||
 | 
			
		||||
### Code Contributors
 | 
			
		||||
 | 
			
		||||
This project exists thanks to all the people who contribute.
 | 
			
		||||
<a href="https://github.com/acmesh-official/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a>
 | 
			
		||||
 | 
			
		||||
### Financial Contributors
 | 
			
		||||
 | 
			
		||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/acmesh/contribute)]
 | 
			
		||||
 | 
			
		||||
#### Individuals
 | 
			
		||||
 | 
			
		||||
<a href="https://opencollective.com/acmesh"><img src="https://opencollective.com/acmesh/individuals.svg?width=890"></a>
 | 
			
		||||
 | 
			
		||||
#### Organizations
 | 
			
		||||
 | 
			
		||||
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/acmesh/contribute)]
 | 
			
		||||
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/0/website"><img src="https://opencollective.com/acmesh/organization/0/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/1/website"><img src="https://opencollective.com/acmesh/organization/1/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/2/website"><img src="https://opencollective.com/acmesh/organization/2/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/3/website"><img src="https://opencollective.com/acmesh/organization/3/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/4/website"><img src="https://opencollective.com/acmesh/organization/4/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/5/website"><img src="https://opencollective.com/acmesh/organization/5/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/6/website"><img src="https://opencollective.com/acmesh/organization/6/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/7/website"><img src="https://opencollective.com/acmesh/organization/7/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 19. License & Others
 | 
			
		||||
 | 
			
		||||
License is GPLv3
 | 
			
		||||
 | 
			
		||||
Please Star and Fork me.
 | 
			
		||||
 | 
			
		||||
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcomed.
 | 
			
		||||
[Issues](https://github.com/acmesh-official/acme.sh/issues) and [pull requests](https://github.com/acmesh-official/acme.sh/pulls) are welcome.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Donate
 | 
			
		||||
1. PayPal:  donate@acme.sh
 | 
			
		||||
# 20. Donate
 | 
			
		||||
Your donation makes **acme.sh** better:
 | 
			
		||||
 | 
			
		||||
[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list)
 | 
			
		||||
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
 | 
			
		||||
 | 
			
		||||
[Donate List](https://github.com/acmesh-official/acme.sh/wiki/Donate-list)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								deploy/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								deploy/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# Using deploy api
 | 
			
		||||
 | 
			
		||||
deploy hook usage:
 | 
			
		||||
 | 
			
		||||
https://github.com/acmesh-official/acme.sh/wiki/deployhooks
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										88
									
								
								deploy/ali_cdn.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								deploy/ali_cdn.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034,SC2154
 | 
			
		||||
 | 
			
		||||
# Script to create certificate to Alibaba Cloud CDN
 | 
			
		||||
#
 | 
			
		||||
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
 | 
			
		||||
#
 | 
			
		||||
# This deployment required following variables
 | 
			
		||||
# export Ali_Key="ALIACCESSKEY"
 | 
			
		||||
# export Ali_Secret="ALISECRETKEY"
 | 
			
		||||
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
 | 
			
		||||
#
 | 
			
		||||
# To specify the CDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
 | 
			
		||||
# export DEPLOY_ALI_CDN_DOMAIN="cdn.example.com"
 | 
			
		||||
# If you have multiple CDN domains using the same certificate, just
 | 
			
		||||
# export DEPLOY_ALI_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
 | 
			
		||||
#
 | 
			
		||||
# For DCDN, see ali_dcdn deploy hook
 | 
			
		||||
 | 
			
		||||
Ali_CDN_API="https://cdn.aliyuncs.com/"
 | 
			
		||||
 | 
			
		||||
ali_cdn_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # Load dnsapi/dns_ali.sh to reduce the duplicated codes
 | 
			
		||||
  # https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
 | 
			
		||||
  dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
 | 
			
		||||
  # shellcheck source=/dev/null
 | 
			
		||||
  if ! . "$dnsapi_ali"; then
 | 
			
		||||
    _err "Error loading file $dnsapi_ali. Please check your API file and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _prepare_ali_credentials || return 1
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_ALI_CDN_DOMAIN
 | 
			
		||||
  if [ "$DEPLOY_ALI_CDN_DOMAIN" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_ALI_CDN_DOMAIN "$DEPLOY_ALI_CDN_DOMAIN"
 | 
			
		||||
  else
 | 
			
		||||
    DEPLOY_ALI_CDN_DOMAIN="$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # read cert and key files and urlencode both
 | 
			
		||||
  _cert=$(_url_encode upper-hex <"$_cfullchain")
 | 
			
		||||
  _key=$(_url_encode upper-hex <"$_ckey")
 | 
			
		||||
 | 
			
		||||
  _debug2 _cert "$_cert"
 | 
			
		||||
  _debug2 _key "$_key"
 | 
			
		||||
 | 
			
		||||
  ## update domain ssl config
 | 
			
		||||
  for domain in $DEPLOY_ALI_CDN_DOMAIN; do
 | 
			
		||||
    _set_cdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
 | 
			
		||||
    if _ali_rest "Set CDN domain SSL certificate for $domain" "" POST; then
 | 
			
		||||
      _info "Domain $domain certificate has been deployed successfully"
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# domain pub pri
 | 
			
		||||
_set_cdn_domain_ssl_certificate_query() {
 | 
			
		||||
  endpoint=$Ali_CDN_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=SetCdnDomainSSLCertificate'
 | 
			
		||||
  query=$query'&CertType=upload'
 | 
			
		||||
  query=$query'&DomainName='$1
 | 
			
		||||
  query=$query'&Format=json'
 | 
			
		||||
  query=$query'&SSLPri='$3
 | 
			
		||||
  query=$query'&SSLProtocol=on'
 | 
			
		||||
  query=$query'&SSLPub='$2
 | 
			
		||||
  query=$query'&SignatureMethod=HMAC-SHA1'
 | 
			
		||||
  query=$query"&SignatureNonce=$(_ali_nonce)"
 | 
			
		||||
  query=$query'&SignatureVersion=1.0'
 | 
			
		||||
  query=$query'&Timestamp='$(_timestamp)
 | 
			
		||||
  query=$query'&Version=2018-05-10'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										88
									
								
								deploy/ali_dcdn.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								deploy/ali_dcdn.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034,SC2154
 | 
			
		||||
 | 
			
		||||
# Script to create certificate to Alibaba Cloud DCDN
 | 
			
		||||
#
 | 
			
		||||
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun
 | 
			
		||||
#
 | 
			
		||||
# This deployment required following variables
 | 
			
		||||
# export Ali_Key="ALIACCESSKEY"
 | 
			
		||||
# export Ali_Secret="ALISECRETKEY"
 | 
			
		||||
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi
 | 
			
		||||
#
 | 
			
		||||
# To specify the DCDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates
 | 
			
		||||
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn.example.com"
 | 
			
		||||
# If you have multiple CDN domains using the same certificate, just
 | 
			
		||||
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn1.example.com dcdn2.example.com"
 | 
			
		||||
#
 | 
			
		||||
# For regular CDN, see ali_cdn deploy hook
 | 
			
		||||
 | 
			
		||||
Ali_DCDN_API="https://dcdn.aliyuncs.com/"
 | 
			
		||||
 | 
			
		||||
ali_dcdn_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # Load dnsapi/dns_ali.sh to reduce the duplicated codes
 | 
			
		||||
  # https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276
 | 
			
		||||
  dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)"
 | 
			
		||||
  # shellcheck source=/dev/null
 | 
			
		||||
  if ! . "$dnsapi_ali"; then
 | 
			
		||||
    _err "Error loading file $dnsapi_ali. Please check your API file and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _prepare_ali_credentials || return 1
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_ALI_DCDN_DOMAIN
 | 
			
		||||
  if [ "$DEPLOY_ALI_DCDN_DOMAIN" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_ALI_DCDN_DOMAIN "$DEPLOY_ALI_DCDN_DOMAIN"
 | 
			
		||||
  else
 | 
			
		||||
    DEPLOY_ALI_DCDN_DOMAIN="$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # read cert and key files and urlencode both
 | 
			
		||||
  _cert=$(_url_encode upper-hex <"$_cfullchain")
 | 
			
		||||
  _key=$(_url_encode upper-hex <"$_ckey")
 | 
			
		||||
 | 
			
		||||
  _debug2 _cert "$_cert"
 | 
			
		||||
  _debug2 _key "$_key"
 | 
			
		||||
 | 
			
		||||
  ## update domain ssl config
 | 
			
		||||
  for domain in $DEPLOY_ALI_DCDN_DOMAIN; do
 | 
			
		||||
    _set_dcdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key"
 | 
			
		||||
    if _ali_rest "Set DCDN domain SSL certificate for $domain" "" POST; then
 | 
			
		||||
      _info "Domain $domain certificate has been deployed successfully"
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# domain pub pri
 | 
			
		||||
_set_dcdn_domain_ssl_certificate_query() {
 | 
			
		||||
  endpoint=$Ali_DCDN_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=SetDcdnDomainSSLCertificate'
 | 
			
		||||
  query=$query'&CertType=upload'
 | 
			
		||||
  query=$query'&DomainName='$1
 | 
			
		||||
  query=$query'&Format=json'
 | 
			
		||||
  query=$query'&SSLPri='$3
 | 
			
		||||
  query=$query'&SSLProtocol=on'
 | 
			
		||||
  query=$query'&SSLPub='$2
 | 
			
		||||
  query=$query'&SignatureMethod=HMAC-SHA1'
 | 
			
		||||
  query=$query"&SignatureNonce=$(_ali_nonce)"
 | 
			
		||||
  query=$query'&SignatureVersion=1.0'
 | 
			
		||||
  query=$query'&Timestamp='$(_timestamp)
 | 
			
		||||
  query=$query'&Version=2018-01-15'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								deploy/apache.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/apache.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to apache server.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
apache_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _err "Deploy cert to apache server, Not implemented yet"
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										92
									
								
								deploy/cleverreach.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								deploy/cleverreach.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# Here is the script to deploy the cert to your CleverReach Account using the CleverReach REST API.
 | 
			
		||||
# Your OAuth needs the right scope, please contact CleverReach support for that.
 | 
			
		||||
#
 | 
			
		||||
# Written by Jan-Philipp Benecke <github@bnck.me>
 | 
			
		||||
# Public domain, 2020
 | 
			
		||||
#
 | 
			
		||||
# Following environment variables must be set:
 | 
			
		||||
#
 | 
			
		||||
#export DEPLOY_CLEVERREACH_CLIENT_ID=myid
 | 
			
		||||
#export DEPLOY_CLEVERREACH_CLIENT_SECRET=mysecret
 | 
			
		||||
 | 
			
		||||
cleverreach_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _rest_endpoint="https://rest.cleverreach.com"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_CLEVERREACH_CLIENT_ID
 | 
			
		||||
  _getdeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET
 | 
			
		||||
  _getdeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID
 | 
			
		||||
 | 
			
		||||
  if [ -z "${DEPLOY_CLEVERREACH_CLIENT_ID}" ]; then
 | 
			
		||||
    _err "CleverReach Client ID is not found, please define DEPLOY_CLEVERREACH_CLIENT_ID."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "${DEPLOY_CLEVERREACH_CLIENT_SECRET}" ]; then
 | 
			
		||||
    _err "CleverReach client secret is not found, please define DEPLOY_CLEVERREACH_CLIENT_SECRET."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedeployconf DEPLOY_CLEVERREACH_CLIENT_ID "${DEPLOY_CLEVERREACH_CLIENT_ID}"
 | 
			
		||||
  _savedeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET "${DEPLOY_CLEVERREACH_CLIENT_SECRET}"
 | 
			
		||||
  _savedeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
 | 
			
		||||
 | 
			
		||||
  _info "Obtaining a CleverReach access token"
 | 
			
		||||
 | 
			
		||||
  _data="{\"grant_type\": \"client_credentials\", \"client_id\": \"${DEPLOY_CLEVERREACH_CLIENT_ID}\", \"client_secret\": \"${DEPLOY_CLEVERREACH_CLIENT_SECRET}\"}"
 | 
			
		||||
  _auth_result="$(_post "$_data" "$_rest_endpoint/oauth/token.php" "" "POST" "application/json")"
 | 
			
		||||
 | 
			
		||||
  _debug _data "$_data"
 | 
			
		||||
  _debug _auth_result "$_auth_result"
 | 
			
		||||
 | 
			
		||||
  _regex=".*\"access_token\":\"\([-._0-9A-Za-z]*\)\".*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _access_token=$(echo "$_auth_result" | _json_decode | sed -n "s/$_regex/\1/p")
 | 
			
		||||
 | 
			
		||||
  _debug _subclient "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
 | 
			
		||||
 | 
			
		||||
  if [ -n "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
 | 
			
		||||
    _info "Obtaining token for sub-client ${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
 | 
			
		||||
    export _H1="Authorization: Bearer ${_access_token}"
 | 
			
		||||
    _subclient_token_result="$(_get "$_rest_endpoint/v3/clients/$DEPLOY_CLEVERREACH_SUBCLIENT_ID/token")"
 | 
			
		||||
    _access_token=$(echo "$_subclient_token_result" | sed -n "s/\"//p")
 | 
			
		||||
 | 
			
		||||
    _debug _subclient_token_result "$_access_token"
 | 
			
		||||
 | 
			
		||||
    _info "Destroying parent token at CleverReach, as it not needed anymore"
 | 
			
		||||
    _destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
 | 
			
		||||
    _debug _destroy_result "$_destroy_result"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Uploading certificate and key to CleverReach"
 | 
			
		||||
 | 
			
		||||
  _certData="{\"cert\":\"$(_json_encode <"$_cfullchain")\", \"key\":\"$(_json_encode <"$_ckey")\"}"
 | 
			
		||||
  export _H1="Authorization: Bearer ${_access_token}"
 | 
			
		||||
  _add_cert_result="$(_post "$_certData" "$_rest_endpoint/v3/ssl" "" "POST" "application/json")"
 | 
			
		||||
 | 
			
		||||
  if [ -z "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
 | 
			
		||||
    _info "Destroying token at CleverReach, as it not needed anymore"
 | 
			
		||||
    _destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
 | 
			
		||||
    _debug _destroy_result "$_destroy_result"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! echo "$_add_cert_result" | grep '"error":' >/dev/null; then
 | 
			
		||||
    _info "Uploaded certificate successfully"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _debug _add_cert_result "$_add_cert_result"
 | 
			
		||||
    _err "Unable to update certificate"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										98
									
								
								deploy/consul.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								deploy/consul.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is a script to deploy cert to hashicorp consul using curl
 | 
			
		||||
# (https://www.consul.io/)
 | 
			
		||||
#
 | 
			
		||||
# it requires following environment variables:
 | 
			
		||||
#
 | 
			
		||||
# CONSUL_PREFIX - this contains the prefix path in consul
 | 
			
		||||
# CONSUL_HTTP_ADDR - consul requires this to find your consul server
 | 
			
		||||
#
 | 
			
		||||
# additionally, you need to ensure that CONSUL_HTTP_TOKEN is available
 | 
			
		||||
# to access the consul server
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
consul_deploy() {
 | 
			
		||||
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # validate required env vars
 | 
			
		||||
  _getdeployconf CONSUL_PREFIX
 | 
			
		||||
  if [ -z "$CONSUL_PREFIX" ]; then
 | 
			
		||||
    _err "CONSUL_PREFIX needs to be defined (contains prefix path in vault)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf CONSUL_PREFIX "$CONSUL_PREFIX"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf CONSUL_HTTP_ADDR
 | 
			
		||||
  if [ -z "$CONSUL_HTTP_ADDR" ]; then
 | 
			
		||||
    _err "CONSUL_HTTP_ADDR needs to be defined (contains consul connection address)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf CONSUL_HTTP_ADDR "$CONSUL_HTTP_ADDR"
 | 
			
		||||
 | 
			
		||||
  CONSUL_CMD=$(command -v consul)
 | 
			
		||||
 | 
			
		||||
  # force CLI, but the binary does not exist => error
 | 
			
		||||
  if [ -n "$USE_CLI" ] && [ -z "$CONSUL_CMD" ]; then
 | 
			
		||||
    _err "Cannot find the consul binary!"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # use the CLI first
 | 
			
		||||
  if [ -n "$USE_CLI" ] || [ -n "$CONSUL_CMD" ]; then
 | 
			
		||||
    _info "Found consul binary, deploying with CLI"
 | 
			
		||||
    consul_deploy_cli "$CONSUL_CMD" "$CONSUL_PREFIX"
 | 
			
		||||
  else
 | 
			
		||||
    _info "Did not find consul binary, deploying with API"
 | 
			
		||||
    consul_deploy_api "$CONSUL_HTTP_ADDR" "$CONSUL_PREFIX" "$CONSUL_HTTP_TOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
consul_deploy_api() {
 | 
			
		||||
  CONSUL_HTTP_ADDR="$1"
 | 
			
		||||
  CONSUL_PREFIX="$2"
 | 
			
		||||
  CONSUL_HTTP_TOKEN="$3"
 | 
			
		||||
 | 
			
		||||
  URL="$CONSUL_HTTP_ADDR/v1/kv/$CONSUL_PREFIX"
 | 
			
		||||
  export _H1="X-Consul-Token: $CONSUL_HTTP_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$FABIO" ]; then
 | 
			
		||||
    _post "$(cat "$_cfullchain")" "$URL/${_cdomain}-cert.pem" '' "PUT" || return 1
 | 
			
		||||
    _post "$(cat "$_ckey")" "$URL/${_cdomain}-key.pem" '' "PUT" || return 1
 | 
			
		||||
  else
 | 
			
		||||
    _post "$(cat "$_ccert")" "$URL/${_cdomain}/cert.pem" '' "PUT" || return 1
 | 
			
		||||
    _post "$(cat "$_ckey")" "$URL/${_cdomain}/cert.key" '' "PUT" || return 1
 | 
			
		||||
    _post "$(cat "$_cca")" "$URL/${_cdomain}/chain.pem" '' "PUT" || return 1
 | 
			
		||||
    _post "$(cat "$_cfullchain")" "$URL/${_cdomain}/fullchain.pem" '' "PUT" || return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
consul_deploy_cli() {
 | 
			
		||||
  CONSUL_CMD="$1"
 | 
			
		||||
  CONSUL_PREFIX="$2"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$FABIO" ]; then
 | 
			
		||||
    $CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-cert.pem" @"$_cfullchain" || return 1
 | 
			
		||||
    $CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-key.pem" @"$_ckey" || return 1
 | 
			
		||||
  else
 | 
			
		||||
    $CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
 | 
			
		||||
    $CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
 | 
			
		||||
    $CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
 | 
			
		||||
    $CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										211
									
								
								deploy/cpanel_uapi.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								deploy/cpanel_uapi.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# Here is the script to deploy the cert to your cpanel using the cpanel API.
 | 
			
		||||
# Uses command line uapi.  --user option is needed only if run as root.
 | 
			
		||||
# Returns 0 when success.
 | 
			
		||||
#
 | 
			
		||||
# Configure DEPLOY_CPANEL_AUTO_<...> options to enable or restrict automatic
 | 
			
		||||
# detection of deployment targets through UAPI (if not set, defaults below are used.)
 | 
			
		||||
# - ENABLED : 'true' for multi-site / wildcard capability; otherwise single-site mode.
 | 
			
		||||
# - NOMATCH : 'true' to allow deployment to sites that do not match the certificate.
 | 
			
		||||
# - INCLUDE : Comma-separated list - sites must match this field.
 | 
			
		||||
# - EXCLUDE : Comma-separated list - sites must NOT match this field.
 | 
			
		||||
# INCLUDE/EXCLUDE both support non-lexical, glob-style matches using '*'
 | 
			
		||||
#
 | 
			
		||||
# Please note that I am no longer using Github. If you want to report an issue
 | 
			
		||||
# or contact me, visit https://forum.webseodesigners.com/web-design-seo-and-hosting-f16/
 | 
			
		||||
#
 | 
			
		||||
# Written by Santeri Kannisto <santeri.kannisto@webseodesigners.com>
 | 
			
		||||
# Public domain, 2017-2018
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_CPANEL_USER=myusername
 | 
			
		||||
# export DEPLOY_CPANEL_AUTO_ENABLED='true'
 | 
			
		||||
# export DEPLOY_CPANEL_AUTO_NOMATCH='false'
 | 
			
		||||
# export DEPLOY_CPANEL_AUTO_INCLUDE='*'
 | 
			
		||||
# export DEPLOY_CPANEL_AUTO_EXCLUDE=''
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
cpanel_uapi_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  # re-declare vars inherited from acme.sh but not passed to make ShellCheck happy
 | 
			
		||||
  : "${Le_Alt:=""}"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  if ! _exists uapi; then
 | 
			
		||||
    _err "The command uapi is not found."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # declare useful constants
 | 
			
		||||
  uapi_error_response='status: 0'
 | 
			
		||||
 | 
			
		||||
  # read cert and key files and urlencode both
 | 
			
		||||
  _cert=$(_url_encode <"$_ccert")
 | 
			
		||||
  _key=$(_url_encode <"$_ckey")
 | 
			
		||||
 | 
			
		||||
  _debug2 _cert "$_cert"
 | 
			
		||||
  _debug2 _key "$_key"
 | 
			
		||||
 | 
			
		||||
  if [ "$(id -u)" = 0 ]; then
 | 
			
		||||
    _getdeployconf DEPLOY_CPANEL_USER
 | 
			
		||||
    # fallback to _readdomainconf for old installs
 | 
			
		||||
    if [ -z "${DEPLOY_CPANEL_USER:=$(_readdomainconf DEPLOY_CPANEL_USER)}" ]; then
 | 
			
		||||
      _err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _debug DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
 | 
			
		||||
    _savedeployconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
 | 
			
		||||
 | 
			
		||||
    _uapi_user="$DEPLOY_CPANEL_USER"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Load all AUTO envars and set defaults - see above for usage
 | 
			
		||||
  __cpanel_initautoparam ENABLED 'true'
 | 
			
		||||
  __cpanel_initautoparam NOMATCH 'false'
 | 
			
		||||
  __cpanel_initautoparam INCLUDE '*'
 | 
			
		||||
  __cpanel_initautoparam EXCLUDE ''
 | 
			
		||||
 | 
			
		||||
  # Auto mode
 | 
			
		||||
  if [ "$DEPLOY_CPANEL_AUTO_ENABLED" = "true" ]; then
 | 
			
		||||
    # call API for site config
 | 
			
		||||
    _response=$(uapi DomainInfo list_domains)
 | 
			
		||||
    # exit if error in response
 | 
			
		||||
    if [ -z "$_response" ] || [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
 | 
			
		||||
      _err "Error in deploying certificate - cannot retrieve sitelist:"
 | 
			
		||||
      _err "\n$_response"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # parse response to create site list
 | 
			
		||||
    sitelist=$(__cpanel_parse_response "$_response")
 | 
			
		||||
    _debug "UAPI sites found: $sitelist"
 | 
			
		||||
 | 
			
		||||
    # filter sitelist using configured domains
 | 
			
		||||
    # skip if NOMATCH is "true"
 | 
			
		||||
    if [ "$DEPLOY_CPANEL_AUTO_NOMATCH" = "true" ]; then
 | 
			
		||||
      _debug "DEPLOY_CPANEL_AUTO_NOMATCH is true"
 | 
			
		||||
      _info "UAPI nomatch mode is enabled - Will not validate sites are valid for the certificate"
 | 
			
		||||
    else
 | 
			
		||||
      _debug "DEPLOY_CPANEL_AUTO_NOMATCH is false"
 | 
			
		||||
      d="$(echo "${Le_Alt}," | sed -e "s/^$_cdomain,//" -e "s/,$_cdomain,/,/")"
 | 
			
		||||
      d="$(echo "$_cdomain,$d" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\[\^\.\]\*/g')"
 | 
			
		||||
      sitelist="$(echo "$sitelist" | grep -ix "$d")"
 | 
			
		||||
      _debug2 "Matched UAPI sites: $sitelist"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # filter sites that do not match $DEPLOY_CPANEL_AUTO_INCLUDE
 | 
			
		||||
    _info "Applying sitelist filter DEPLOY_CPANEL_AUTO_INCLUDE: $DEPLOY_CPANEL_AUTO_INCLUDE"
 | 
			
		||||
    sitelist="$(echo "$sitelist" | grep -ix "$(echo "$DEPLOY_CPANEL_AUTO_INCLUDE" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\.\*/g')")"
 | 
			
		||||
    _debug2 "Remaining sites: $sitelist"
 | 
			
		||||
 | 
			
		||||
    # filter sites that match $DEPLOY_CPANEL_AUTO_EXCLUDE
 | 
			
		||||
    _info "Applying sitelist filter DEPLOY_CPANEL_AUTO_EXCLUDE: $DEPLOY_CPANEL_AUTO_EXCLUDE"
 | 
			
		||||
    sitelist="$(echo "$sitelist" | grep -vix "$(echo "$DEPLOY_CPANEL_AUTO_EXCLUDE" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\.\*/g')")"
 | 
			
		||||
    _debug2 "Remaining sites: $sitelist"
 | 
			
		||||
 | 
			
		||||
    # counter for success / failure check
 | 
			
		||||
    successes=0
 | 
			
		||||
    if [ -n "$sitelist" ]; then
 | 
			
		||||
      sitetotal="$(echo "$sitelist" | wc -l)"
 | 
			
		||||
      _debug "$sitetotal sites to deploy"
 | 
			
		||||
    else
 | 
			
		||||
      sitetotal=0
 | 
			
		||||
      _debug "No sites to deploy"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # for each site: call uapi to publish cert and log result. Only return failure if all fail
 | 
			
		||||
    for site in $sitelist; do
 | 
			
		||||
      # call uapi to publish cert, check response for errors and log them.
 | 
			
		||||
      if [ -n "$_uapi_user" ]; then
 | 
			
		||||
        _response=$(uapi --user="$_uapi_user" SSL install_ssl domain="$site" cert="$_cert" key="$_key")
 | 
			
		||||
      else
 | 
			
		||||
        _response=$(uapi SSL install_ssl domain="$site" cert="$_cert" key="$_key")
 | 
			
		||||
      fi
 | 
			
		||||
      if [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
 | 
			
		||||
        _err "Error in deploying certificate to $site:"
 | 
			
		||||
        _err "$_response"
 | 
			
		||||
      else
 | 
			
		||||
        successes=$((successes + 1))
 | 
			
		||||
        _debug "$_response"
 | 
			
		||||
        _info "Succcessfully deployed to $site"
 | 
			
		||||
      fi
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    # Raise error if all updates fail
 | 
			
		||||
    if [ "$sitetotal" -gt 0 ] && [ "$successes" -eq 0 ]; then
 | 
			
		||||
      _err "Could not deploy to any of $sitetotal sites via UAPI"
 | 
			
		||||
      _debug "successes: $successes, sitetotal: $sitetotal"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _info "Successfully deployed certificate to $successes of $sitetotal sites via UAPI"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    # "classic" mode - will only try to deploy to the primary domain; will not check UAPI first
 | 
			
		||||
    if [ -n "$_uapi_user" ]; then
 | 
			
		||||
      _response=$(uapi --user="$_uapi_user" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
 | 
			
		||||
    else
 | 
			
		||||
      _response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
 | 
			
		||||
      _err "Error in deploying certificate:"
 | 
			
		||||
      _err "$_response"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _debug response "$_response"
 | 
			
		||||
    _info "Certificate successfully deployed"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
########  Private functions #####################
 | 
			
		||||
 | 
			
		||||
# Internal utility to process YML from UAPI - looks at main_domain, sub_domains, addon domains and parked domains
 | 
			
		||||
#[response]
 | 
			
		||||
__cpanel_parse_response() {
 | 
			
		||||
  if [ $# -gt 0 ]; then resp="$*"; else resp="$(cat)"; fi
 | 
			
		||||
 | 
			
		||||
  echo "$resp" |
 | 
			
		||||
    sed -En \
 | 
			
		||||
      -e 's/\r$//' \
 | 
			
		||||
      -e 's/^( *)([_.[:alnum:]]+) *: *(.*)/\1,\2,\3/p' \
 | 
			
		||||
      -e 's/^( *)- (.*)/\1,-,\2/p' |
 | 
			
		||||
    awk -F, '{
 | 
			
		||||
      level = length($1)/2;
 | 
			
		||||
      section[level] = $2;
 | 
			
		||||
      for (i in section) {if (i > level) {delete section[i]}}
 | 
			
		||||
      if (length($3) > 0) {
 | 
			
		||||
        prefix="";
 | 
			
		||||
        for (i=0; i < level; i++)
 | 
			
		||||
          { prefix = (prefix)(section[i])("/") }
 | 
			
		||||
        printf("%s%s=%s\n", prefix, $2, $3);
 | 
			
		||||
      }
 | 
			
		||||
    }' |
 | 
			
		||||
    sed -En -e 's/^result\/data\/(main_domain|sub_domains\/-|addon_domains\/-|parked_domains\/-)=(.*)$/\2/p'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Load parameter by prefix+name - fallback to default if not set, and save to config
 | 
			
		||||
#pname pdefault
 | 
			
		||||
__cpanel_initautoparam() {
 | 
			
		||||
  pname="$1"
 | 
			
		||||
  pdefault="$2"
 | 
			
		||||
  pkey="DEPLOY_CPANEL_AUTO_$pname"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf "$pkey"
 | 
			
		||||
  [ -n "$(eval echo "\"\$$pkey\"")" ] || eval "$pkey=\"$pdefault\""
 | 
			
		||||
  _debug2 "$pkey" "$(eval echo "\"\$$pkey\"")"
 | 
			
		||||
  _savedeployconf "$pkey" "$(eval echo "\"\$$pkey\"")"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										312
									
								
								deploy/docker.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										312
									
								
								deploy/docker.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,312 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#DEPLOY_DOCKER_CONTAINER_LABEL="xxxxxxx"
 | 
			
		||||
 | 
			
		||||
#DEPLOY_DOCKER_CONTAINER_KEY_FILE="/path/to/key.pem"
 | 
			
		||||
#DEPLOY_DOCKER_CONTAINER_CERT_FILE="/path/to/cert.pem"
 | 
			
		||||
#DEPLOY_DOCKER_CONTAINER_CA_FILE="/path/to/ca.pem"
 | 
			
		||||
#DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/path/to/fullchain.pem"
 | 
			
		||||
#DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
 | 
			
		||||
 | 
			
		||||
_DEPLOY_DOCKER_WIKI="https://github.com/acmesh-official/acme.sh/wiki/deploy-to-docker-containers"
 | 
			
		||||
 | 
			
		||||
_DOCKER_HOST_DEFAULT="/var/run/docker.sock"
 | 
			
		||||
 | 
			
		||||
docker_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _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"
 | 
			
		||||
  if [ -z "$DEPLOY_DOCKER_CONTAINER_LABEL" ]; then
 | 
			
		||||
    _err "The DEPLOY_DOCKER_CONTAINER_LABEL variable is not defined, we use this label to find the container."
 | 
			
		||||
    _err "See: $_DEPLOY_DOCKER_WIKI"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedeployconf DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL"
 | 
			
		||||
 | 
			
		||||
  if [ "$DOCKER_HOST" ]; then
 | 
			
		||||
    _saveaccountconf DOCKER_HOST "$DOCKER_HOST"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _exists docker && docker version | grep -i docker >/dev/null; then
 | 
			
		||||
    _info "Using docker command"
 | 
			
		||||
    export _USE_DOCKER_COMMAND=1
 | 
			
		||||
  else
 | 
			
		||||
    export _USE_DOCKER_COMMAND=
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  export _USE_UNIX_SOCKET=
 | 
			
		||||
  if [ -z "$_USE_DOCKER_COMMAND" ]; then
 | 
			
		||||
    export _USE_REST=
 | 
			
		||||
    if [ "$DOCKER_HOST" ]; then
 | 
			
		||||
      _debug "Try use docker host: $DOCKER_HOST"
 | 
			
		||||
      export _USE_REST=1
 | 
			
		||||
    else
 | 
			
		||||
      export _DOCKER_SOCK="$_DOCKER_HOST_DEFAULT"
 | 
			
		||||
      _debug "Try use $_DOCKER_SOCK"
 | 
			
		||||
      if [ ! -e "$_DOCKER_SOCK" ] || [ ! -w "$_DOCKER_SOCK" ]; then
 | 
			
		||||
        _err "$_DOCKER_SOCK is not available"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
      export _USE_UNIX_SOCKET=1
 | 
			
		||||
      if ! _exists "curl"; then
 | 
			
		||||
        _err "Please install curl first."
 | 
			
		||||
        _err "We need curl to work."
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
      if ! _check_curl_version; then
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_DOCKER_CONTAINER_KEY_FILE
 | 
			
		||||
  _debug2 DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE"
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_DOCKER_CONTAINER_CERT_FILE
 | 
			
		||||
  _debug2 DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE"
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_DOCKER_CONTAINER_CA_FILE
 | 
			
		||||
  _debug2 DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE"
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE
 | 
			
		||||
  _debug2 DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then
 | 
			
		||||
    _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
 | 
			
		||||
    _savedeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" "base64"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _cid="$(_get_id "$DEPLOY_DOCKER_CONTAINER_LABEL")"
 | 
			
		||||
  _info "Container id: $_cid"
 | 
			
		||||
  if [ -z "$_cid" ]; then
 | 
			
		||||
    _err "can not find container id"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then
 | 
			
		||||
    if ! _docker_cp "$_cid" "$_ckey" "$DEPLOY_DOCKER_CONTAINER_KEY_FILE"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then
 | 
			
		||||
    if ! _docker_cp "$_cid" "$_ccert" "$DEPLOY_DOCKER_CONTAINER_CERT_FILE"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then
 | 
			
		||||
    if ! _docker_cp "$_cid" "$_cca" "$DEPLOY_DOCKER_CONTAINER_CA_FILE"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then
 | 
			
		||||
    if ! _docker_cp "$_cid" "$_cfullchain" "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    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
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#label
 | 
			
		||||
_get_id() {
 | 
			
		||||
  _label="$1"
 | 
			
		||||
  if [ "$_USE_DOCKER_COMMAND" ]; then
 | 
			
		||||
    docker ps -f label="$_label" --format "{{.ID}}"
 | 
			
		||||
  elif [ "$_USE_REST" ]; then
 | 
			
		||||
    _err "Not implemented yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif [ "$_USE_UNIX_SOCKET" ]; then
 | 
			
		||||
    _req="{\"label\":[\"$_label\"]}"
 | 
			
		||||
    _debug2 _req "$_req"
 | 
			
		||||
    _req="$(printf "%s" "$_req" | _url_encode)"
 | 
			
		||||
    _debug2 _req "$_req"
 | 
			
		||||
    listjson="$(_curl_unix_sock "${_DOCKER_SOCK:-$_DOCKER_HOST_DEFAULT}" GET "/containers/json?filters=$_req")"
 | 
			
		||||
    _debug2 "listjson" "$listjson"
 | 
			
		||||
    echo "$listjson" | tr '{,' '\n' | grep -i '"id":' | _head_n 1 | cut -d '"' -f 4
 | 
			
		||||
  else
 | 
			
		||||
    _err "Not implemented yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#id  cmd
 | 
			
		||||
_docker_exec() {
 | 
			
		||||
  _eargs="$*"
 | 
			
		||||
  _debug2 "_docker_exec $_eargs"
 | 
			
		||||
  _dcid="$1"
 | 
			
		||||
  shift
 | 
			
		||||
  if [ "$_USE_DOCKER_COMMAND" ]; then
 | 
			
		||||
    docker exec -i "$_dcid" sh -c "$*"
 | 
			
		||||
  elif [ "$_USE_REST" ]; then
 | 
			
		||||
    _err "Not implemented yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif [ "$_USE_UNIX_SOCKET" ]; then
 | 
			
		||||
    _cmd="$*"
 | 
			
		||||
    #_cmd="$(printf "%s" "$_cmd" | sed 's/ /","/g')"
 | 
			
		||||
    _debug2 _cmd "$_cmd"
 | 
			
		||||
    #create exec instance:
 | 
			
		||||
    cjson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/containers/$_dcid/exec" "{\"Cmd\": [\"sh\", \"-c\", \"$_cmd\"]}")"
 | 
			
		||||
    _debug2 cjson "$cjson"
 | 
			
		||||
    execid="$(echo "$cjson" | cut -d '"' -f 4)"
 | 
			
		||||
    _debug execid "$execid"
 | 
			
		||||
    ejson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/exec/$execid/start" "{\"Detach\": false,\"Tty\": false}")"
 | 
			
		||||
    _debug2 ejson "$ejson"
 | 
			
		||||
    if [ "$ejson" ]; then
 | 
			
		||||
      _err "$ejson"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _err "Not implemented yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#id from  to
 | 
			
		||||
_docker_cp() {
 | 
			
		||||
  _dcid="$1"
 | 
			
		||||
  _from="$2"
 | 
			
		||||
  _to="$3"
 | 
			
		||||
  _info "Copying file from $_from to $_to"
 | 
			
		||||
  _dir="$(dirname "$_to")"
 | 
			
		||||
  _debug2 _dir "$_dir"
 | 
			
		||||
  if ! _docker_exec "$_dcid" mkdir -p "$_dir"; then
 | 
			
		||||
    _err "Can not create dir: $_dir"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$_USE_DOCKER_COMMAND" ]; then
 | 
			
		||||
    if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
 | 
			
		||||
      _docker_exec "$_dcid" tee "$_to" <"$_from"
 | 
			
		||||
    else
 | 
			
		||||
      _docker_exec "$_dcid" tee "$_to" <"$_from" >/dev/null
 | 
			
		||||
    fi
 | 
			
		||||
    if [ "$?" = "0" ]; then
 | 
			
		||||
      _info "Success"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _info "Error"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  elif [ "$_USE_REST" ]; then
 | 
			
		||||
    _err "Not implemented yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif [ "$_USE_UNIX_SOCKET" ]; then
 | 
			
		||||
    _frompath="$_from"
 | 
			
		||||
    if _startswith "$_frompath" '/'; then
 | 
			
		||||
      _frompath="$(echo "$_from" | cut -b 2-)" #remove the first '/' char
 | 
			
		||||
    fi
 | 
			
		||||
    _debug2 "_frompath" "$_frompath"
 | 
			
		||||
    _toname="$(basename "$_to")"
 | 
			
		||||
    _debug2 "_toname" "$_toname"
 | 
			
		||||
    _debug2 "_from" "$_from"
 | 
			
		||||
    if ! tar --transform="s,$(printf "%s" "$_frompath" | tr '*' .),$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then
 | 
			
		||||
      _err "copy error"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Not implemented yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#sock method  endpoint data content-type
 | 
			
		||||
_curl_unix_sock() {
 | 
			
		||||
  _socket="$1"
 | 
			
		||||
  _method="$2"
 | 
			
		||||
  _endpoint="$3"
 | 
			
		||||
  _data="$4"
 | 
			
		||||
  _ctype="$5"
 | 
			
		||||
  if [ -z "$_ctype" ]; then
 | 
			
		||||
    _ctype="Content-Type: application/json"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _data "$_data"
 | 
			
		||||
  _debug2 "url" "http://localhost$_endpoint"
 | 
			
		||||
  if [ "$_CURL_NO_HOST" ]; then
 | 
			
		||||
    _cux_url="http:$_endpoint"
 | 
			
		||||
  else
 | 
			
		||||
    _cux_url="http://localhost$_endpoint"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
 | 
			
		||||
    curl -vvv --silent --unix-socket "$_socket" -X "$_method" --data-binary "$_data" --header "$_ctype" "$_cux_url"
 | 
			
		||||
  else
 | 
			
		||||
    curl --silent --unix-socket "$_socket" -X "$_method" --data-binary "$_data" --header "$_ctype" "$_cux_url"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_check_curl_version() {
 | 
			
		||||
  _cversion="$(curl -V | grep '^curl ' | cut -d ' ' -f 2)"
 | 
			
		||||
  _debug2 "_cversion" "$_cversion"
 | 
			
		||||
 | 
			
		||||
  _major="$(_getfield "$_cversion" 1 '.')"
 | 
			
		||||
  _debug2 "_major" "$_major"
 | 
			
		||||
 | 
			
		||||
  _minor="$(_getfield "$_cversion" 2 '.')"
 | 
			
		||||
  _debug2 "_minor" "$_minor"
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								deploy/dovecot.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/dovecot.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to dovecot server.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
dovecot_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _err "Not implemented yet"
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										113
									
								
								deploy/exim4.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								deploy/exim4.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to exim4 server.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
#DEPLOY_EXIM4_CONF="/etc/exim/exim.conf"
 | 
			
		||||
#DEPLOY_EXIM4_RELOAD="service exim4 restart"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
exim4_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"
 | 
			
		||||
 | 
			
		||||
  _ssl_path="/etc/acme.sh/exim4"
 | 
			
		||||
  if ! mkdir -p "$_ssl_path"; then
 | 
			
		||||
    _err "Can not create folder:$_ssl_path"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Copying key and cert"
 | 
			
		||||
  _real_key="$_ssl_path/exim4.key"
 | 
			
		||||
  if ! cat "$_ckey" >"$_real_key"; then
 | 
			
		||||
    _err "Error: write key file to: $_real_key"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _real_fullchain="$_ssl_path/exim4.pem"
 | 
			
		||||
  if ! cat "$_cfullchain" >"$_real_fullchain"; then
 | 
			
		||||
    _err "Error: write key file to: $_real_fullchain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  DEFAULT_EXIM4_RELOAD="service exim4 restart"
 | 
			
		||||
  _reload="${DEPLOY_EXIM4_RELOAD:-$DEFAULT_EXIM4_RELOAD}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$IS_RENEW" ]; then
 | 
			
		||||
    DEFAULT_EXIM4_CONF="/etc/exim/exim.conf"
 | 
			
		||||
    if [ ! -f "$DEFAULT_EXIM4_CONF" ]; then
 | 
			
		||||
      DEFAULT_EXIM4_CONF="/etc/exim4/exim4.conf.template"
 | 
			
		||||
    fi
 | 
			
		||||
    _exim4_conf="${DEPLOY_EXIM4_CONF:-$DEFAULT_EXIM4_CONF}"
 | 
			
		||||
    _debug _exim4_conf "$_exim4_conf"
 | 
			
		||||
    if [ ! -f "$_exim4_conf" ]; then
 | 
			
		||||
      if [ -z "$DEPLOY_EXIM4_CONF" ]; then
 | 
			
		||||
        _err "exim4 conf is not found, please define DEPLOY_EXIM4_CONF"
 | 
			
		||||
        return 1
 | 
			
		||||
      else
 | 
			
		||||
        _err "It seems that the specified exim4 conf is not valid, please check."
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
    if [ ! -w "$_exim4_conf" ]; then
 | 
			
		||||
      _err "The file $_exim4_conf is not writable, please change the permission."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _backup_conf="$DOMAIN_BACKUP_PATH/exim4.conf.bak"
 | 
			
		||||
    _info "Backup $_exim4_conf to $_backup_conf"
 | 
			
		||||
    cp "$_exim4_conf" "$_backup_conf"
 | 
			
		||||
 | 
			
		||||
    _info "Modify exim4 conf: $_exim4_conf"
 | 
			
		||||
    if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" &&
 | 
			
		||||
      _setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then
 | 
			
		||||
      _info "Set config success!"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Config exim4 server error, please report bug to us."
 | 
			
		||||
      _info "Restoring exim4 conf"
 | 
			
		||||
      if cat "$_backup_conf" >"$_exim4_conf"; then
 | 
			
		||||
        _info "Restore conf success"
 | 
			
		||||
        eval "$_reload"
 | 
			
		||||
      else
 | 
			
		||||
        _err "Oops, error restore exim4 conf, please report bug to us."
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Run reload: $_reload"
 | 
			
		||||
  if eval "$_reload"; then
 | 
			
		||||
    _info "Reload success!"
 | 
			
		||||
    if [ "$DEPLOY_EXIM4_CONF" ]; then
 | 
			
		||||
      _savedomainconf DEPLOY_EXIM4_CONF "$DEPLOY_EXIM4_CONF"
 | 
			
		||||
    else
 | 
			
		||||
      _cleardomainconf DEPLOY_EXIM4_CONF
 | 
			
		||||
    fi
 | 
			
		||||
    if [ "$DEPLOY_EXIM4_RELOAD" ]; then
 | 
			
		||||
      _savedomainconf DEPLOY_EXIM4_RELOAD "$DEPLOY_EXIM4_RELOAD"
 | 
			
		||||
    else
 | 
			
		||||
      _cleardomainconf DEPLOY_EXIM4_RELOAD
 | 
			
		||||
    fi
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Reload error, restoring"
 | 
			
		||||
    if cat "$_backup_conf" >"$_exim4_conf"; then
 | 
			
		||||
      _info "Restore conf success"
 | 
			
		||||
      eval "$_reload"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Oops, error restore exim4 conf, please report bug to us."
 | 
			
		||||
    fi
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								deploy/fritzbox.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								deploy/fritzbox.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to an AVM FRITZ!Box router.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
#DEPLOY_FRITZBOX_USERNAME="username"
 | 
			
		||||
#DEPLOY_FRITZBOX_PASSWORD="password"
 | 
			
		||||
#DEPLOY_FRITZBOX_URL="https://fritz.box"
 | 
			
		||||
 | 
			
		||||
# Kudos to wikrie at Github for his FRITZ!Box update script:
 | 
			
		||||
# https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
fritzbox_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  if ! _exists iconv; then
 | 
			
		||||
    if ! _exists uconv; then
 | 
			
		||||
      if ! _exists perl; then
 | 
			
		||||
        _err "iconv or uconv or perl not found"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Clear traces of incorrectly stored values
 | 
			
		||||
  _clearaccountconf DEPLOY_FRITZBOX_USERNAME
 | 
			
		||||
  _clearaccountconf DEPLOY_FRITZBOX_PASSWORD
 | 
			
		||||
  _clearaccountconf DEPLOY_FRITZBOX_URL
 | 
			
		||||
 | 
			
		||||
  # Read config from saved values or env
 | 
			
		||||
  _getdeployconf DEPLOY_FRITZBOX_USERNAME
 | 
			
		||||
  _getdeployconf DEPLOY_FRITZBOX_PASSWORD
 | 
			
		||||
  _getdeployconf DEPLOY_FRITZBOX_URL
 | 
			
		||||
 | 
			
		||||
  _debug DEPLOY_FRITZBOX_URL "$DEPLOY_FRITZBOX_URL"
 | 
			
		||||
  _debug DEPLOY_FRITZBOX_USERNAME "$DEPLOY_FRITZBOX_USERNAME"
 | 
			
		||||
  _secure_debug DEPLOY_FRITZBOX_PASSWORD "$DEPLOY_FRITZBOX_PASSWORD"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_FRITZBOX_USERNAME" ]; then
 | 
			
		||||
    _err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$DEPLOY_FRITZBOX_PASSWORD" ]; then
 | 
			
		||||
    _err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$DEPLOY_FRITZBOX_URL" ]; then
 | 
			
		||||
    _err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Save current values
 | 
			
		||||
  _savedeployconf DEPLOY_FRITZBOX_USERNAME "$DEPLOY_FRITZBOX_USERNAME"
 | 
			
		||||
  _savedeployconf DEPLOY_FRITZBOX_PASSWORD "$DEPLOY_FRITZBOX_PASSWORD"
 | 
			
		||||
  _savedeployconf DEPLOY_FRITZBOX_URL "$DEPLOY_FRITZBOX_URL"
 | 
			
		||||
 | 
			
		||||
  # Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate
 | 
			
		||||
  export HTTPS_INSECURE=1
 | 
			
		||||
 | 
			
		||||
  _info "Log in to the FRITZ!Box"
 | 
			
		||||
  _fritzbox_challenge="$(_get "${DEPLOY_FRITZBOX_URL}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
 | 
			
		||||
  if _exists iconv; then
 | 
			
		||||
    _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | iconv -f ASCII -t UTF16LE | _digest md5 hex)"
 | 
			
		||||
  elif _exists uconv; then
 | 
			
		||||
    _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | uconv -f ASCII -t UTF16LE | _digest md5 hex)"
 | 
			
		||||
  else
 | 
			
		||||
    _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | _digest md5 hex)"
 | 
			
		||||
  fi
 | 
			
		||||
  _fritzbox_sid="$(_get "${DEPLOY_FRITZBOX_URL}/login_sid.lua?sid=0000000000000000&username=${DEPLOY_FRITZBOX_USERNAME}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
 | 
			
		||||
 | 
			
		||||
  if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
 | 
			
		||||
    _err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Generate form POST request"
 | 
			
		||||
  _post_request="$(_mktemp)"
 | 
			
		||||
  _post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
 | 
			
		||||
  # _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it!
 | 
			
		||||
  _CERTPASSWORD_=
 | 
			
		||||
  {
 | 
			
		||||
    printf -- "--"
 | 
			
		||||
    printf -- "%s\r\n" "${_post_boundary}"
 | 
			
		||||
    printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}"
 | 
			
		||||
    printf -- "--"
 | 
			
		||||
    printf -- "%s\r\n" "${_post_boundary}"
 | 
			
		||||
    printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}"
 | 
			
		||||
    printf -- "--"
 | 
			
		||||
    printf -- "%s\r\n" "${_post_boundary}"
 | 
			
		||||
    printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n"
 | 
			
		||||
    printf "Content-Type: application/octet-stream\r\n\r\n"
 | 
			
		||||
    cat "${_ckey}" "${_cfullchain}"
 | 
			
		||||
    printf "\r\n"
 | 
			
		||||
    printf -- "--"
 | 
			
		||||
    printf -- "%s--" "${_post_boundary}"
 | 
			
		||||
  } >>"${_post_request}"
 | 
			
		||||
 | 
			
		||||
  _info "Upload certificate to the FRITZ!Box"
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
 | 
			
		||||
  _post "$(cat "${_post_request}")" "${DEPLOY_FRITZBOX_URL}/cgi-bin/firmwarecfg" | grep SSL
 | 
			
		||||
 | 
			
		||||
  retval=$?
 | 
			
		||||
  if [ $retval = 0 ]; then
 | 
			
		||||
    _info "Upload successful"
 | 
			
		||||
  else
 | 
			
		||||
    _err "Upload failed"
 | 
			
		||||
  fi
 | 
			
		||||
  rm "${_post_request}"
 | 
			
		||||
 | 
			
		||||
  return $retval
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										143
									
								
								deploy/gcore_cdn.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								deploy/gcore_cdn.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
 | 
			
		||||
gcore_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"
 | 
			
		||||
 | 
			
		||||
  _fullchain=$(tr '\r\n' '*#' <"$_cfullchain" | sed 's/*#/#/g;s/##/#/g;s/#/\\n/g')
 | 
			
		||||
  _key=$(tr '\r\n' '*#' <"$_ckey" | sed 's/*#/#/g;s/#/\\n/g')
 | 
			
		||||
 | 
			
		||||
  _debug _fullchain "$_fullchain"
 | 
			
		||||
  _debug _key "$_key"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_GCORE_CDN_USERNAME" ]; then
 | 
			
		||||
    if [ -z "$Le_Deploy_gcore_cdn_username" ]; then
 | 
			
		||||
      _err "Please define the target username: export DEPLOY_GCORE_CDN_USERNAME=username"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    Le_Deploy_gcore_cdn_username="$DEPLOY_GCORE_CDN_USERNAME"
 | 
			
		||||
    _savedomainconf Le_Deploy_gcore_cdn_username "$Le_Deploy_gcore_cdn_username"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_GCORE_CDN_PASSWORD" ]; then
 | 
			
		||||
    if [ -z "$Le_Deploy_gcore_cdn_password" ]; then
 | 
			
		||||
      _err "Please define the target password: export DEPLOY_GCORE_CDN_PASSWORD=password"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    Le_Deploy_gcore_cdn_password="$DEPLOY_GCORE_CDN_PASSWORD"
 | 
			
		||||
    _savedomainconf Le_Deploy_gcore_cdn_password "$Le_Deploy_gcore_cdn_password"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Get authorization token"
 | 
			
		||||
  _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.gcore.com/auth/jwt/login")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex=".*\"access\":\"\([-._0-9A-Za-z]*\)\".*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _token=$(echo "$_response" | sed -n "s/$_regex/\1/p")
 | 
			
		||||
  _debug _token "$_token"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_token" ]; then
 | 
			
		||||
    _err "Error G-Core Labs API authorization"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Find CDN resource with cname $_cdomain"
 | 
			
		||||
  export _H2="Authorization:Bearer $_token"
 | 
			
		||||
  _response=$(_get "https://api.gcore.com/cdn/resources")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex="\"primary_resource\":null},"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _response=$(echo "$_response" | sed "s/$_regex/$_regex\n/g")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex="^.*\"cname\":\"$_cdomain\".*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _resource=$(echo "$_response" | _egrep_o "$_regex")
 | 
			
		||||
  _debug _resource "$_resource"
 | 
			
		||||
  _regex=".*\"id\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
 | 
			
		||||
  _debug _resourceId "$_resourceId"
 | 
			
		||||
  _regex=".*\"sslData\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
 | 
			
		||||
  _debug _sslDataOld "$_sslDataOld"
 | 
			
		||||
  _regex=".*\"originGroup\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
 | 
			
		||||
  _debug _originGroup "$_originGroup"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_resourceId" ] || [ -z "$_originGroup" ]; then
 | 
			
		||||
    _err "Not found CDN resource with cname $_cdomain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Add new SSL certificate"
 | 
			
		||||
  _date=$(date "+%d.%m.%Y %H:%M:%S")
 | 
			
		||||
  _request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}"
 | 
			
		||||
  _debug _request "$_request"
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcore.com/cdn/sslData")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex=".*\"id\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p")
 | 
			
		||||
  _debug _sslDataAdd "$_sslDataAdd"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_sslDataAdd" ]; then
 | 
			
		||||
    _err "Error new SSL certificate add"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Update CDN resource"
 | 
			
		||||
  _request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}"
 | 
			
		||||
  _debug _request "$_request"
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcore.com/cdn/resources/$_resourceId" '' "PUT")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex=".*\"sslData\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
  _sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p")
 | 
			
		||||
  _debug _sslDataNew "$_sslDataNew"
 | 
			
		||||
 | 
			
		||||
  if [ "$_sslDataNew" != "$_sslDataAdd" ]; then
 | 
			
		||||
    _err "Error CDN resource update"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_sslDataOld" ] || [ "$_sslDataOld" = "null" ]; then
 | 
			
		||||
    _info "Not found old SSL certificate"
 | 
			
		||||
  else
 | 
			
		||||
    _info "Delete old SSL certificate"
 | 
			
		||||
    _response=$(_post '' "https://api.gcore.com/cdn/sslData/$_sslDataOld" '' "DELETE")
 | 
			
		||||
    _debug _response "$_response"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Certificate successfully deployed"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										80
									
								
								deploy/gitlab.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								deploy/gitlab.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to deploy certificate to a Gitlab hosted page
 | 
			
		||||
 | 
			
		||||
# The following variables exported from environment will be used.
 | 
			
		||||
# If not set then values previously saved in domain.conf file are used.
 | 
			
		||||
 | 
			
		||||
# All the variables are required
 | 
			
		||||
 | 
			
		||||
# export GITLAB_TOKEN="xxxxxxx"
 | 
			
		||||
# export GITLAB_PROJECT_ID=012345
 | 
			
		||||
# export GITLAB_DOMAIN="mydomain.com"
 | 
			
		||||
 | 
			
		||||
gitlab_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 "$GITLAB_TOKEN" ]; then
 | 
			
		||||
    if [ -z "$Le_Deploy_gitlab_token" ]; then
 | 
			
		||||
      _err "GITLAB_TOKEN not defined."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    Le_Deploy_gitlab_token="$GITLAB_TOKEN"
 | 
			
		||||
    _savedomainconf Le_Deploy_gitlab_token "$Le_Deploy_gitlab_token"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$GITLAB_PROJECT_ID" ]; then
 | 
			
		||||
    if [ -z "$Le_Deploy_gitlab_project_id" ]; then
 | 
			
		||||
      _err "GITLAB_PROJECT_ID not defined."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    Le_Deploy_gitlab_project_id="$GITLAB_PROJECT_ID"
 | 
			
		||||
    _savedomainconf Le_Deploy_gitlab_project_id "$Le_Deploy_gitlab_project_id"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$GITLAB_DOMAIN" ]; then
 | 
			
		||||
    if [ -z "$Le_Deploy_gitlab_domain" ]; then
 | 
			
		||||
      _err "GITLAB_DOMAIN not defined."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    Le_Deploy_gitlab_domain="$GITLAB_DOMAIN"
 | 
			
		||||
    _savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  string_fullchain=$(_url_encode <"$_cfullchain")
 | 
			
		||||
  string_key=$(_url_encode <"$_ckey")
 | 
			
		||||
 | 
			
		||||
  body="certificate=$string_fullchain&key=$string_key"
 | 
			
		||||
 | 
			
		||||
  export _H1="PRIVATE-TOKEN: $Le_Deploy_gitlab_token"
 | 
			
		||||
 | 
			
		||||
  gitlab_url="https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain"
 | 
			
		||||
 | 
			
		||||
  _response=$(_post "$body" "$gitlab_url" 0 PUT | _dbase64 "multiline")
 | 
			
		||||
 | 
			
		||||
  error_response="error"
 | 
			
		||||
 | 
			
		||||
  if test "${_response#*"$error_response"}" != "$_response"; then
 | 
			
		||||
    _err "Error in deploying certificate:"
 | 
			
		||||
    _err "$_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug response "$_response"
 | 
			
		||||
  _info "Certificate successfully deployed"
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										403
									
								
								deploy/haproxy.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										403
									
								
								deploy/haproxy.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,403 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script for acme.sh to deploy certificates to haproxy
 | 
			
		||||
#
 | 
			
		||||
# The following variables can be exported:
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_HAPROXY_PEM_NAME="${domain}.pem"
 | 
			
		||||
#
 | 
			
		||||
# Defines the name of the PEM file.
 | 
			
		||||
# Defaults to "<domain>.pem"
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_HAPROXY_PEM_PATH="/etc/haproxy"
 | 
			
		||||
#
 | 
			
		||||
# Defines location of PEM file for HAProxy.
 | 
			
		||||
# Defaults to /etc/haproxy
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy"
 | 
			
		||||
#
 | 
			
		||||
# OPTIONAL: Reload command used post deploy
 | 
			
		||||
# This defaults to be a no-op (ie "true").
 | 
			
		||||
# It is strongly recommended to set this something that makes sense
 | 
			
		||||
# for your distro.
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_HAPROXY_ISSUER="no"
 | 
			
		||||
#
 | 
			
		||||
# OPTIONAL: Places CA file as "${DEPLOY_HAPROXY_PEM}.issuer"
 | 
			
		||||
# Note: Required for OCSP stapling to work
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_HAPROXY_BUNDLE="no"
 | 
			
		||||
#
 | 
			
		||||
# OPTIONAL: Deploy this certificate as part of a multi-cert bundle
 | 
			
		||||
# This adds a suffix to the certificate based on the certificate type
 | 
			
		||||
# eg RSA certificates will have .rsa as a suffix to the file name
 | 
			
		||||
# HAProxy will load all certificates and provide one or the other
 | 
			
		||||
# depending on client capabilities
 | 
			
		||||
# 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 #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
haproxy_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  _cmdpfx=""
 | 
			
		||||
 | 
			
		||||
  # Some defaults
 | 
			
		||||
  DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy"
 | 
			
		||||
  DEPLOY_HAPROXY_PEM_NAME_DEFAULT="${_cdomain}.pem"
 | 
			
		||||
  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}"
 | 
			
		||||
  _debug _ccert "${_ccert}"
 | 
			
		||||
  _debug _cca "${_cca}"
 | 
			
		||||
  _debug _cfullchain "${_cfullchain}"
 | 
			
		||||
 | 
			
		||||
  # PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_HAPROXY_PEM_PATH
 | 
			
		||||
  _debug2 DEPLOY_HAPROXY_PEM_PATH "${DEPLOY_HAPROXY_PEM_PATH}"
 | 
			
		||||
  if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then
 | 
			
		||||
    Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}"
 | 
			
		||||
    _savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_haproxy_pem_path}" ]; then
 | 
			
		||||
    Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Ensure PEM_PATH exists
 | 
			
		||||
  if [ -d "${Le_Deploy_haproxy_pem_path}" ]; then
 | 
			
		||||
    _debug "PEM_PATH ${Le_Deploy_haproxy_pem_path} exists"
 | 
			
		||||
  else
 | 
			
		||||
    _err "PEM_PATH ${Le_Deploy_haproxy_pem_path} does not exist"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_HAPROXY_PEM_NAME
 | 
			
		||||
  _debug2 DEPLOY_HAPROXY_PEM_NAME "${DEPLOY_HAPROXY_PEM_NAME}"
 | 
			
		||||
  if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then
 | 
			
		||||
    Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}"
 | 
			
		||||
    _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}"
 | 
			
		||||
  _getdeployconf DEPLOY_HAPROXY_BUNDLE
 | 
			
		||||
  _debug2 DEPLOY_HAPROXY_BUNDLE "${DEPLOY_HAPROXY_BUNDLE}"
 | 
			
		||||
  if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then
 | 
			
		||||
    Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}"
 | 
			
		||||
    _savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_haproxy_bundle}" ]; then
 | 
			
		||||
    Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_HAPROXY_ISSUER
 | 
			
		||||
  _debug2 DEPLOY_HAPROXY_ISSUER "${DEPLOY_HAPROXY_ISSUER}"
 | 
			
		||||
  if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then
 | 
			
		||||
    Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}"
 | 
			
		||||
    _savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_haproxy_issuer}" ]; then
 | 
			
		||||
    Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_HAPROXY_RELOAD
 | 
			
		||||
  _debug2 DEPLOY_HAPROXY_RELOAD "${DEPLOY_HAPROXY_RELOAD}"
 | 
			
		||||
  if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then
 | 
			
		||||
    Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}"
 | 
			
		||||
    _savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_haproxy_reload}" ]; then
 | 
			
		||||
    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"
 | 
			
		||||
    # Initialise $Le_Keylength if its not already set
 | 
			
		||||
    if [ -z "${Le_Keylength}" ]; then
 | 
			
		||||
      Le_Keylength=""
 | 
			
		||||
    fi
 | 
			
		||||
    if _isEccKey "${Le_Keylength}"; then
 | 
			
		||||
      _info "ECC key type detected"
 | 
			
		||||
      _suffix=".ecdsa"
 | 
			
		||||
    else
 | 
			
		||||
      _info "RSA key type detected"
 | 
			
		||||
      _suffix=".rsa"
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _suffix=""
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _suffix "${_suffix}"
 | 
			
		||||
 | 
			
		||||
  # Set variables for later
 | 
			
		||||
  _pem="${Le_Deploy_haproxy_pem_path}/${Le_Deploy_haproxy_pem_name}${_suffix}"
 | 
			
		||||
  _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 "${_ccert}" "${_cca}" "${_ckey}" | grep . >"${_temppem}"
 | 
			
		||||
  _ret="$?"
 | 
			
		||||
 | 
			
		||||
  # Check that we could create the temporary file
 | 
			
		||||
  if [ "${_ret}" != "0" ]; then
 | 
			
		||||
    _err "Error code ${_ret} returned during PEM file creation"
 | 
			
		||||
    [ -f "${_temppem}" ] && rm -f "${_temppem}"
 | 
			
		||||
    return ${_ret}
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Move PEM file into place
 | 
			
		||||
  _info "Moving new certificate into place"
 | 
			
		||||
  _debug _pem "${_pem}"
 | 
			
		||||
  cat "${_temppem}" >"${_pem}"
 | 
			
		||||
  _ret=$?
 | 
			
		||||
 | 
			
		||||
  # Clean up temp file
 | 
			
		||||
  [ -f "${_temppem}" ] && rm -f "${_temppem}"
 | 
			
		||||
 | 
			
		||||
  # Deal with any failure of moving PEM file into place
 | 
			
		||||
  if [ "${_ret}" != "0" ]; then
 | 
			
		||||
    _err "Error code ${_ret} returned while moving new certificate into place"
 | 
			
		||||
    return ${_ret}
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Update .issuer file if requested
 | 
			
		||||
  if [ "${Le_Deploy_haproxy_issuer}" = "yes" ]; then
 | 
			
		||||
    _info "Updating .issuer file"
 | 
			
		||||
    _debug _issuer "${_issuer}"
 | 
			
		||||
    cat "${_cca}" >"${_issuer}"
 | 
			
		||||
    _ret="$?"
 | 
			
		||||
 | 
			
		||||
    if [ "${_ret}" != "0" ]; then
 | 
			
		||||
      _err "Error code ${_ret} returned while copying issuer/CA certificate into place"
 | 
			
		||||
      return ${_ret}
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    [ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option
 | 
			
		||||
  if [ -z "${Le_OCSP_Staple}" ]; then
 | 
			
		||||
    Le_OCSP_Staple="0"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "${Le_OCSP_Staple}" = "1" ]; then
 | 
			
		||||
    _info "Updating OCSP stapling info"
 | 
			
		||||
    _debug _ocsp "${_ocsp}"
 | 
			
		||||
    _info "Extracting OCSP URL"
 | 
			
		||||
    _ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}")
 | 
			
		||||
    _debug _ocsp_url "${_ocsp_url}"
 | 
			
		||||
 | 
			
		||||
    # Only process OCSP if URL was present
 | 
			
		||||
    if [ "${_ocsp_url}" != "" ]; then
 | 
			
		||||
      # Extract the hostname from the OCSP URL
 | 
			
		||||
      _info "Extracting OCSP URL"
 | 
			
		||||
      _ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3)
 | 
			
		||||
      _debug _ocsp_host "${_ocsp_host}"
 | 
			
		||||
 | 
			
		||||
      # Only process the certificate if we have a .issuer file
 | 
			
		||||
      if [ -r "${_issuer}" ]; then
 | 
			
		||||
        # Check if issuer cert is also a root CA cert
 | 
			
		||||
        _subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
 | 
			
		||||
        _debug _subjectdn "${_subjectdn}"
 | 
			
		||||
        _issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
 | 
			
		||||
        _debug _issuerdn "${_issuerdn}"
 | 
			
		||||
        _info "Requesting OCSP response"
 | 
			
		||||
        # If the issuer is a CA cert then our command line has "-CAfile" added
 | 
			
		||||
        if [ "${_subjectdn}" = "${_issuerdn}" ]; then
 | 
			
		||||
          _cafile_argument="-CAfile \"${_issuer}\""
 | 
			
		||||
        else
 | 
			
		||||
          _cafile_argument=""
 | 
			
		||||
        fi
 | 
			
		||||
        _debug _cafile_argument "${_cafile_argument}"
 | 
			
		||||
        # if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
 | 
			
		||||
        _openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2)
 | 
			
		||||
        _debug _openssl_version "${_openssl_version}"
 | 
			
		||||
        _openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
 | 
			
		||||
        _openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
 | 
			
		||||
        if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
 | 
			
		||||
          _header_sep="="
 | 
			
		||||
        else
 | 
			
		||||
          _header_sep=" "
 | 
			
		||||
        fi
 | 
			
		||||
        # Request the OCSP response from the issuer and store it
 | 
			
		||||
        _openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \
 | 
			
		||||
          -issuer \"${_issuer}\" \
 | 
			
		||||
          -cert \"${_pem}\" \
 | 
			
		||||
          -url \"${_ocsp_url}\" \
 | 
			
		||||
          -header Host${_header_sep}\"${_ocsp_host}\" \
 | 
			
		||||
          -respout \"${_ocsp}\" \
 | 
			
		||||
          -verify_other \"${_issuer}\" \
 | 
			
		||||
          ${_cafile_argument} \
 | 
			
		||||
          | grep -q \"${_pem}: good\""
 | 
			
		||||
        _debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"
 | 
			
		||||
        eval "${_openssl_ocsp_cmd}"
 | 
			
		||||
        _ret=$?
 | 
			
		||||
      else
 | 
			
		||||
        # Non fatal: No issuer file was present so no OCSP stapling file created
 | 
			
		||||
        _err "OCSP stapling in use but no .issuer file was present"
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      # Non fatal: No OCSP url was found int the certificate
 | 
			
		||||
      _err "OCSP update requested but no OCSP URL was found in certificate"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # Non fatal: Check return code of openssl command
 | 
			
		||||
    if [ "${_ret}" != "0" ]; then
 | 
			
		||||
      _err "Updating OCSP stapling failed with return code ${_ret}"
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    # An OCSP file was already present but certificate did not have OCSP extension
 | 
			
		||||
    if [ -f "${_ocsp}" ]; then
 | 
			
		||||
      _err "OCSP was not requested but .ocsp file exists."
 | 
			
		||||
      # Could remove the file at this step, although HAProxy just ignores it in this case
 | 
			
		||||
      # rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "${Le_Deploy_haproxy_hot_update}" = "yes" ]; then
 | 
			
		||||
    # set the socket name for messages
 | 
			
		||||
    if [ -n "${_cmdpfx}" ]; then
 | 
			
		||||
      _socketname="master CLI"
 | 
			
		||||
    else
 | 
			
		||||
      _socketname="stats socket"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # Update certificate over HAProxy stats socket or master CLI.
 | 
			
		||||
    if _exists socat; then
 | 
			
		||||
      # look for the certificate on the stats socket, to chose between updating or creating one
 | 
			
		||||
      _socat_cert_cmd="echo '${_cmdpfx}show ssl cert' | socat '${_statssock}' - | grep -q '^${_pem}$'"
 | 
			
		||||
      _debug _socat_cert_cmd "${_socat_cert_cmd}"
 | 
			
		||||
      eval "${_socat_cert_cmd}"
 | 
			
		||||
      _ret=$?
 | 
			
		||||
      if [ "${_ret}" != "0" ]; then
 | 
			
		||||
        _newcert="1"
 | 
			
		||||
        _info "Creating new certificate '${_pem}' over HAProxy ${_socketname}."
 | 
			
		||||
        # certificate wasn't found, it's a new one. We should check if the crt-list exists and creates/inserts the certificate.
 | 
			
		||||
        _socat_crtlist_show_cmd="echo '${_cmdpfx}show ssl crt-list' | socat '${_statssock}' - | grep -q '^${Le_Deploy_haproxy_pem_path}$'"
 | 
			
		||||
        _debug _socat_crtlist_show_cmd "${_socat_crtlist_show_cmd}"
 | 
			
		||||
        eval "${_socat_crtlist_show_cmd}"
 | 
			
		||||
        _ret=$?
 | 
			
		||||
        if [ "${_ret}" != "0" ]; then
 | 
			
		||||
          _err "Couldn't find '${Le_Deploy_haproxy_pem_path}' in haproxy 'show ssl crt-list'"
 | 
			
		||||
          return "${_ret}"
 | 
			
		||||
        fi
 | 
			
		||||
        # create a new certificate
 | 
			
		||||
        _socat_new_cmd="echo '${_cmdpfx}new ssl cert ${_pem}' | socat '${_statssock}' - | grep -q 'New empty'"
 | 
			
		||||
        _debug _socat_new_cmd "${_socat_new_cmd}"
 | 
			
		||||
        eval "${_socat_new_cmd}"
 | 
			
		||||
        _ret=$?
 | 
			
		||||
        if [ "${_ret}" != "0" ]; then
 | 
			
		||||
          _err "Couldn't create '${_pem}' in haproxy"
 | 
			
		||||
          return "${_ret}"
 | 
			
		||||
        fi
 | 
			
		||||
      else
 | 
			
		||||
        _info "Update existing certificate '${_pem}' over HAProxy ${_socketname}."
 | 
			
		||||
      fi
 | 
			
		||||
      _socat_cert_set_cmd="echo -e '${_cmdpfx}set ssl cert ${_pem} <<\n$(cat "${_pem}")\n' | socat '${_statssock}' - | grep -q 'Transaction created'"
 | 
			
		||||
      _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
 | 
			
		||||
    # 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								deploy/keychain.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								deploy/keychain.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
keychain_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"
 | 
			
		||||
 | 
			
		||||
  /usr/bin/security import "$_ckey" -k "/Library/Keychains/System.keychain"
 | 
			
		||||
  /usr/bin/security import "$_ccert" -k "/Library/Keychains/System.keychain"
 | 
			
		||||
  /usr/bin/security import "$_cca" -k "/Library/Keychains/System.keychain"
 | 
			
		||||
  /usr/bin/security import "$_cfullchain" -k "/Library/Keychains/System.keychain"
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								deploy/keyhelp_api.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								deploy/keyhelp_api.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
keyhelp_api_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
 | 
			
		||||
  # Read config from saved values or env
 | 
			
		||||
  _getdeployconf DEPLOY_KEYHELP_HOST
 | 
			
		||||
  _getdeployconf DEPLOY_KEYHELP_API_KEY
 | 
			
		||||
 | 
			
		||||
  _debug DEPLOY_KEYHELP_HOST "$DEPLOY_KEYHELP_HOST"
 | 
			
		||||
  _secure_debug DEPLOY_KEYHELP_API_KEY "$DEPLOY_KEYHELP_API_KEY"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_KEYHELP_HOST" ]; then
 | 
			
		||||
    _err "KeyHelp host not found, please define DEPLOY_KEYHELP_HOST."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$DEPLOY_KEYHELP_API_KEY" ]; then
 | 
			
		||||
    _err "KeyHelp api key not found, please define DEPLOY_KEYHELP_API_KEY."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Save current values
 | 
			
		||||
  _savedeployconf DEPLOY_KEYHELP_HOST "$DEPLOY_KEYHELP_HOST"
 | 
			
		||||
  _savedeployconf DEPLOY_KEYHELP_API_KEY "$DEPLOY_KEYHELP_API_KEY"
 | 
			
		||||
 | 
			
		||||
  _request_key="$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')"
 | 
			
		||||
  _request_cert="$(tr '\n' ':' <"$_ccert" | sed 's/:/\\n/g')"
 | 
			
		||||
  _request_ca="$(tr '\n' ':' <"$_cca" | sed 's/:/\\n/g')"
 | 
			
		||||
 | 
			
		||||
  _request_body="{
 | 
			
		||||
    \"name\": \"$_cdomain\",
 | 
			
		||||
    \"components\": {
 | 
			
		||||
      \"private_key\": \"$_request_key\",
 | 
			
		||||
      \"certificate\": \"$_request_cert\",
 | 
			
		||||
      \"ca_certificate\": \"$_request_ca\"
 | 
			
		||||
    }
 | 
			
		||||
  }"
 | 
			
		||||
 | 
			
		||||
  _hosts="$(echo "$DEPLOY_KEYHELP_HOST" | tr "," " ")"
 | 
			
		||||
  _keys="$(echo "$DEPLOY_KEYHELP_API_KEY" | tr "," " ")"
 | 
			
		||||
  _i=1
 | 
			
		||||
 | 
			
		||||
  for _host in $_hosts; do
 | 
			
		||||
    _key="$(_getfield "$_keys" "$_i" " ")"
 | 
			
		||||
    _i="$(_math "$_i" + 1)"
 | 
			
		||||
 | 
			
		||||
    export _H1="X-API-Key: $_key"
 | 
			
		||||
 | 
			
		||||
    _put_url="$_host/api/v2/certificates/name/$_cdomain"
 | 
			
		||||
    if _post "$_request_body" "$_put_url" "" "PUT" "application/json" >/dev/null; then
 | 
			
		||||
      _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Cannot make PUT request to $_put_url"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if [ "$_code" = "404" ]; then
 | 
			
		||||
      _info "$_cdomain not found, creating new entry at $_host"
 | 
			
		||||
 | 
			
		||||
      _post_url="$_host/api/v2/certificates"
 | 
			
		||||
      if _post "$_request_body" "$_post_url" "" "POST" "application/json" >/dev/null; then
 | 
			
		||||
        _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
 | 
			
		||||
      else
 | 
			
		||||
        _err "Cannot make POST request to $_post_url"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _startswith "$_code" "2"; then
 | 
			
		||||
      _info "$_cdomain set at $_host"
 | 
			
		||||
    else
 | 
			
		||||
      _err "HTTP status code is $_code"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								deploy/kong.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										77
									
								
								deploy/kong.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# If certificate already exists it will update only cert and key, not touching other parameters
 | 
			
		||||
# If certificate doesn't exist it will only upload cert and key, and not set other parameters
 | 
			
		||||
# Note that we deploy full chain
 | 
			
		||||
# Written by Geoffroi Genot <ggenot@voxbone.com>
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
kong_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  _info "Deploying certificate on Kong instance"
 | 
			
		||||
  if [ -z "$KONG_URL" ]; then
 | 
			
		||||
    _debug "KONG_URL Not set, using default http://localhost:8001"
 | 
			
		||||
    KONG_URL="http://localhost:8001"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  #Get ssl_uuid linked to the domain
 | 
			
		||||
  ssl_uuid=$(_get "$KONG_URL/certificates/$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
 | 
			
		||||
  if [ -z "$ssl_uuid" ]; then
 | 
			
		||||
    _debug "Unable to get Kong ssl_uuid for domain $_cdomain"
 | 
			
		||||
    _debug "Make sure that KONG_URL is correctly configured"
 | 
			
		||||
    _debug "Make sure that a Kong certificate match the sni"
 | 
			
		||||
    _debug "Kong url: $KONG_URL"
 | 
			
		||||
    _info "No existing certificate, creating..."
 | 
			
		||||
    #return 1
 | 
			
		||||
  fi
 | 
			
		||||
  #Save kong url if it's succesful (First run case)
 | 
			
		||||
  _saveaccountconf KONG_URL "$KONG_URL"
 | 
			
		||||
  #Generate DEIM
 | 
			
		||||
  delim="-----MultipartDelimiter$(date "+%s%N")"
 | 
			
		||||
  nl="\015\012"
 | 
			
		||||
  #Set Header
 | 
			
		||||
  _H1="Content-Type: multipart/form-data; boundary=$delim"
 | 
			
		||||
  #Generate data for request (Multipart/form-data with mixed content)
 | 
			
		||||
  if [ -z "$ssl_uuid" ]; then
 | 
			
		||||
    #set sni to domain
 | 
			
		||||
    content="--$delim${nl}Content-Disposition: form-data; name=\"snis[]\"${nl}${nl}$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
  #add key
 | 
			
		||||
  content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
 | 
			
		||||
  #Add cert
 | 
			
		||||
  content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
 | 
			
		||||
  #Close multipart
 | 
			
		||||
  content="$content${nl}--$delim--${nl}"
 | 
			
		||||
  #Convert CRLF
 | 
			
		||||
  content=$(printf %b "$content")
 | 
			
		||||
  #DEBUG
 | 
			
		||||
  _debug header "$_H1"
 | 
			
		||||
  _debug content "$content"
 | 
			
		||||
  #Check if sslcreated (if not => POST else => PATCH)
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ssl_uuid" ]; then
 | 
			
		||||
    #Post certificate to Kong
 | 
			
		||||
    response=$(_post "$content" "$KONG_URL/certificates" "" "POST")
 | 
			
		||||
  else
 | 
			
		||||
    #patch
 | 
			
		||||
    response=$(_post "$content" "$KONG_URL/certificates/$ssl_uuid" "" "PATCH")
 | 
			
		||||
  fi
 | 
			
		||||
  if ! [ "$(echo "$response" | _egrep_o "created_at")" = "created_at" ]; then
 | 
			
		||||
    _err "An error occurred with cert upload. Check response:"
 | 
			
		||||
    _err "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug response "$response"
 | 
			
		||||
  _info "Certificate successfully deployed"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										280
									
								
								deploy/lighttpd.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								deploy/lighttpd.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,280 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script for acme.sh to deploy certificates to lighttpd
 | 
			
		||||
#
 | 
			
		||||
# The following variables can be exported:
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_LIGHTTPD_PEM_NAME="${domain}.pem"
 | 
			
		||||
#
 | 
			
		||||
# Defines the name of the PEM file.
 | 
			
		||||
# Defaults to "<domain>.pem"
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_LIGHTTPD_PEM_PATH="/etc/lighttpd"
 | 
			
		||||
#
 | 
			
		||||
# Defines location of PEM file for Lighttpd.
 | 
			
		||||
# Defaults to /etc/lighttpd
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_LIGHTTPD_RELOAD="systemctl reload lighttpd"
 | 
			
		||||
#
 | 
			
		||||
# OPTIONAL: Reload command used post deploy
 | 
			
		||||
# This defaults to be a no-op (ie "true").
 | 
			
		||||
# It is strongly recommended to set this something that makes sense
 | 
			
		||||
# for your distro.
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_LIGHTTPD_ISSUER="yes"
 | 
			
		||||
#
 | 
			
		||||
# OPTIONAL: Places CA file as "${DEPLOY_LIGHTTPD_PEM}.issuer"
 | 
			
		||||
# Note: Required for OCSP stapling to work
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_LIGHTTPD_BUNDLE="no"
 | 
			
		||||
#
 | 
			
		||||
# OPTIONAL: Deploy this certificate as part of a multi-cert bundle
 | 
			
		||||
# This adds a suffix to the certificate based on the certificate type
 | 
			
		||||
# eg RSA certificates will have .rsa as a suffix to the file name
 | 
			
		||||
# Lighttpd will load all certificates and provide one or the other
 | 
			
		||||
# depending on client capabilities
 | 
			
		||||
# Note: This functionality requires Lighttpd was compiled against
 | 
			
		||||
# a version of OpenSSL that supports this.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
lighttpd_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  # Some defaults
 | 
			
		||||
  DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT="/etc/lighttpd"
 | 
			
		||||
  DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT="${_cdomain}.pem"
 | 
			
		||||
  DEPLOY_LIGHTTPD_BUNDLE_DEFAULT="no"
 | 
			
		||||
  DEPLOY_LIGHTTPD_ISSUER_DEFAULT="yes"
 | 
			
		||||
  DEPLOY_LIGHTTPD_RELOAD_DEFAULT="true"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "${_cdomain}"
 | 
			
		||||
  _debug _ckey "${_ckey}"
 | 
			
		||||
  _debug _ccert "${_ccert}"
 | 
			
		||||
  _debug _cca "${_cca}"
 | 
			
		||||
  _debug _cfullchain "${_cfullchain}"
 | 
			
		||||
 | 
			
		||||
  # PEM_PATH is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_LIGHTTPD_PEM_PATH
 | 
			
		||||
  _debug2 DEPLOY_LIGHTTPD_PEM_PATH "${DEPLOY_LIGHTTPD_PEM_PATH}"
 | 
			
		||||
  if [ -n "${DEPLOY_LIGHTTPD_PEM_PATH}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH}"
 | 
			
		||||
    _savedomainconf Le_Deploy_lighttpd_pem_path "${Le_Deploy_lighttpd_pem_path}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_lighttpd_pem_path}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Ensure PEM_PATH exists
 | 
			
		||||
  if [ -d "${Le_Deploy_lighttpd_pem_path}" ]; then
 | 
			
		||||
    _debug "PEM_PATH ${Le_Deploy_lighttpd_pem_path} exists"
 | 
			
		||||
  else
 | 
			
		||||
    _err "PEM_PATH ${Le_Deploy_lighttpd_pem_path} does not exist"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # PEM_NAME is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_LIGHTTPD_PEM_NAME
 | 
			
		||||
  _debug2 DEPLOY_LIGHTTPD_PEM_NAME "${DEPLOY_LIGHTTPD_PEM_NAME}"
 | 
			
		||||
  if [ -n "${DEPLOY_LIGHTTPD_PEM_NAME}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME}"
 | 
			
		||||
    _savedomainconf Le_Deploy_lighttpd_pem_name "${Le_Deploy_lighttpd_pem_name}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_lighttpd_pem_name}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # BUNDLE is optional. If not provided then assume "${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_LIGHTTPD_BUNDLE
 | 
			
		||||
  _debug2 DEPLOY_LIGHTTPD_BUNDLE "${DEPLOY_LIGHTTPD_BUNDLE}"
 | 
			
		||||
  if [ -n "${DEPLOY_LIGHTTPD_BUNDLE}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE}"
 | 
			
		||||
    _savedomainconf Le_Deploy_lighttpd_bundle "${Le_Deploy_lighttpd_bundle}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_lighttpd_bundle}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # ISSUER is optional. If not provided then assume "${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_LIGHTTPD_ISSUER
 | 
			
		||||
  _debug2 DEPLOY_LIGHTTPD_ISSUER "${DEPLOY_LIGHTTPD_ISSUER}"
 | 
			
		||||
  if [ -n "${DEPLOY_LIGHTTPD_ISSUER}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER}"
 | 
			
		||||
    _savedomainconf Le_Deploy_lighttpd_issuer "${Le_Deploy_lighttpd_issuer}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_lighttpd_issuer}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # RELOAD is optional. If not provided then assume "${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}"
 | 
			
		||||
  _getdeployconf DEPLOY_LIGHTTPD_RELOAD
 | 
			
		||||
  _debug2 DEPLOY_LIGHTTPD_RELOAD "${DEPLOY_LIGHTTPD_RELOAD}"
 | 
			
		||||
  if [ -n "${DEPLOY_LIGHTTPD_RELOAD}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD}"
 | 
			
		||||
    _savedomainconf Le_Deploy_lighttpd_reload "${Le_Deploy_lighttpd_reload}"
 | 
			
		||||
  elif [ -z "${Le_Deploy_lighttpd_reload}" ]; then
 | 
			
		||||
    Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Set the suffix depending if we are creating a bundle or not
 | 
			
		||||
  if [ "${Le_Deploy_lighttpd_bundle}" = "yes" ]; then
 | 
			
		||||
    _info "Bundle creation requested"
 | 
			
		||||
    # Initialise $Le_Keylength if its not already set
 | 
			
		||||
    if [ -z "${Le_Keylength}" ]; then
 | 
			
		||||
      Le_Keylength=""
 | 
			
		||||
    fi
 | 
			
		||||
    if _isEccKey "${Le_Keylength}"; then
 | 
			
		||||
      _info "ECC key type detected"
 | 
			
		||||
      _suffix=".ecdsa"
 | 
			
		||||
    else
 | 
			
		||||
      _info "RSA key type detected"
 | 
			
		||||
      _suffix=".rsa"
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _suffix=""
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _suffix "${_suffix}"
 | 
			
		||||
 | 
			
		||||
  # Set variables for later
 | 
			
		||||
  _pem="${Le_Deploy_lighttpd_pem_path}/${Le_Deploy_lighttpd_pem_name}${_suffix}"
 | 
			
		||||
  _issuer="${_pem}.issuer"
 | 
			
		||||
  _ocsp="${_pem}.ocsp"
 | 
			
		||||
  _reload="${Le_Deploy_lighttpd_reload}"
 | 
			
		||||
 | 
			
		||||
  _info "Deploying PEM file"
 | 
			
		||||
  # Create a temporary PEM file
 | 
			
		||||
  _temppem="$(_mktemp)"
 | 
			
		||||
  _debug _temppem "${_temppem}"
 | 
			
		||||
  cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
 | 
			
		||||
  _ret="$?"
 | 
			
		||||
 | 
			
		||||
  # Check that we could create the temporary file
 | 
			
		||||
  if [ "${_ret}" != "0" ]; then
 | 
			
		||||
    _err "Error code ${_ret} returned during PEM file creation"
 | 
			
		||||
    [ -f "${_temppem}" ] && rm -f "${_temppem}"
 | 
			
		||||
    return ${_ret}
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Move PEM file into place
 | 
			
		||||
  _info "Moving new certificate into place"
 | 
			
		||||
  _debug _pem "${_pem}"
 | 
			
		||||
  cat "${_temppem}" >"${_pem}"
 | 
			
		||||
  _ret=$?
 | 
			
		||||
 | 
			
		||||
  # Clean up temp file
 | 
			
		||||
  [ -f "${_temppem}" ] && rm -f "${_temppem}"
 | 
			
		||||
 | 
			
		||||
  # Deal with any failure of moving PEM file into place
 | 
			
		||||
  if [ "${_ret}" != "0" ]; then
 | 
			
		||||
    _err "Error code ${_ret} returned while moving new certificate into place"
 | 
			
		||||
    return ${_ret}
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Update .issuer file if requested
 | 
			
		||||
  if [ "${Le_Deploy_lighttpd_issuer}" = "yes" ]; then
 | 
			
		||||
    _info "Updating .issuer file"
 | 
			
		||||
    _debug _issuer "${_issuer}"
 | 
			
		||||
    cat "${_cca}" >"${_issuer}"
 | 
			
		||||
    _ret="$?"
 | 
			
		||||
 | 
			
		||||
    if [ "${_ret}" != "0" ]; then
 | 
			
		||||
      _err "Error code ${_ret} returned while copying issuer/CA certificate into place"
 | 
			
		||||
      return ${_ret}
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    [ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option
 | 
			
		||||
  if [ -z "${Le_OCSP_Staple}" ]; then
 | 
			
		||||
    Le_OCSP_Staple="0"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "${Le_OCSP_Staple}" = "1" ]; then
 | 
			
		||||
    _info "Updating OCSP stapling info"
 | 
			
		||||
    _debug _ocsp "${_ocsp}"
 | 
			
		||||
    _info "Extracting OCSP URL"
 | 
			
		||||
    _ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}")
 | 
			
		||||
    _debug _ocsp_url "${_ocsp_url}"
 | 
			
		||||
 | 
			
		||||
    # Only process OCSP if URL was present
 | 
			
		||||
    if [ "${_ocsp_url}" != "" ]; then
 | 
			
		||||
      # Extract the hostname from the OCSP URL
 | 
			
		||||
      _info "Extracting OCSP URL"
 | 
			
		||||
      _ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3)
 | 
			
		||||
      _debug _ocsp_host "${_ocsp_host}"
 | 
			
		||||
 | 
			
		||||
      # Only process the certificate if we have a .issuer file
 | 
			
		||||
      if [ -r "${_issuer}" ]; then
 | 
			
		||||
        # Check if issuer cert is also a root CA cert
 | 
			
		||||
        _subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
 | 
			
		||||
        _debug _subjectdn "${_subjectdn}"
 | 
			
		||||
        _issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
 | 
			
		||||
        _debug _issuerdn "${_issuerdn}"
 | 
			
		||||
        _info "Requesting OCSP response"
 | 
			
		||||
        # If the issuer is a CA cert then our command line has "-CAfile" added
 | 
			
		||||
        if [ "${_subjectdn}" = "${_issuerdn}" ]; then
 | 
			
		||||
          _cafile_argument="-CAfile \"${_issuer}\""
 | 
			
		||||
        else
 | 
			
		||||
          _cafile_argument=""
 | 
			
		||||
        fi
 | 
			
		||||
        _debug _cafile_argument "${_cafile_argument}"
 | 
			
		||||
        # if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
 | 
			
		||||
        _openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2)
 | 
			
		||||
        _debug _openssl_version "${_openssl_version}"
 | 
			
		||||
        _openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
 | 
			
		||||
        _openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
 | 
			
		||||
        if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
 | 
			
		||||
          _header_sep="="
 | 
			
		||||
        else
 | 
			
		||||
          _header_sep=" "
 | 
			
		||||
        fi
 | 
			
		||||
        # Request the OCSP response from the issuer and store it
 | 
			
		||||
        _openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \
 | 
			
		||||
          -issuer \"${_issuer}\" \
 | 
			
		||||
          -cert \"${_pem}\" \
 | 
			
		||||
          -url \"${_ocsp_url}\" \
 | 
			
		||||
          -header Host${_header_sep}\"${_ocsp_host}\" \
 | 
			
		||||
          -respout \"${_ocsp}\" \
 | 
			
		||||
          -verify_other \"${_issuer}\" \
 | 
			
		||||
          ${_cafile_argument} \
 | 
			
		||||
          | grep -q \"${_pem}: good\""
 | 
			
		||||
        _debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"
 | 
			
		||||
        eval "${_openssl_ocsp_cmd}"
 | 
			
		||||
        _ret=$?
 | 
			
		||||
      else
 | 
			
		||||
        # Non fatal: No issuer file was present so no OCSP stapling file created
 | 
			
		||||
        _err "OCSP stapling in use but no .issuer file was present"
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      # Non fatal: No OCSP url was found int the certificate
 | 
			
		||||
      _err "OCSP update requested but no OCSP URL was found in certificate"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # Non fatal: Check return code of openssl command
 | 
			
		||||
    if [ "${_ret}" != "0" ]; then
 | 
			
		||||
      _err "Updating OCSP stapling failed with return code ${_ret}"
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    # An OCSP file was already present but certificate did not have OCSP extension
 | 
			
		||||
    if [ -f "${_ocsp}" ]; then
 | 
			
		||||
      _err "OCSP was not requested but .ocsp file exists."
 | 
			
		||||
      # Could remove the file at this step, although Lighttpd just ignores it in this case
 | 
			
		||||
      # rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Reload Lighttpd
 | 
			
		||||
  _debug _reload "${_reload}"
 | 
			
		||||
  eval "${_reload}"
 | 
			
		||||
  _ret=$?
 | 
			
		||||
  if [ "${_ret}" != "0" ]; then
 | 
			
		||||
    _err "Error code ${_ret} during reload"
 | 
			
		||||
    return ${_ret}
 | 
			
		||||
  else
 | 
			
		||||
    _info "Reload successful"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								deploy/mailcow.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								deploy/mailcow.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to mailcow.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
mailcow_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_MAILCOW_PATH
 | 
			
		||||
  _getdeployconf DEPLOY_MAILCOW_RELOAD
 | 
			
		||||
 | 
			
		||||
  _debug DEPLOY_MAILCOW_PATH "$DEPLOY_MAILCOW_PATH"
 | 
			
		||||
  _debug DEPLOY_MAILCOW_RELOAD "$DEPLOY_MAILCOW_RELOAD"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_MAILCOW_PATH" ]; then
 | 
			
		||||
    _err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedeployconf DEPLOY_MAILCOW_PATH "$DEPLOY_MAILCOW_PATH"
 | 
			
		||||
  [ -n "$DEPLOY_MAILCOW_RELOAD" ] && _savedeployconf DEPLOY_MAILCOW_RELOAD "$DEPLOY_MAILCOW_RELOAD"
 | 
			
		||||
 | 
			
		||||
  _ssl_path="$DEPLOY_MAILCOW_PATH"
 | 
			
		||||
  if [ -f "$DEPLOY_MAILCOW_PATH/generate_config.sh" ]; then
 | 
			
		||||
    _ssl_path="$DEPLOY_MAILCOW_PATH/data/assets/ssl/"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ ! -d "$_ssl_path" ]; then
 | 
			
		||||
    _err "Cannot find mailcow ssl path: $_ssl_path"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Copying key and cert"
 | 
			
		||||
  _real_key="$_ssl_path/key.pem"
 | 
			
		||||
  if ! cat "$_ckey" >"$_real_key"; then
 | 
			
		||||
    _err "Error: write key file to: $_real_key"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _real_fullchain="$_ssl_path/cert.pem"
 | 
			
		||||
  if ! cat "$_cfullchain" >"$_real_fullchain"; then
 | 
			
		||||
    _err "Error: write cert file to: $_real_fullchain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  DEFAULT_MAILCOW_RELOAD="docker restart \$(docker ps --quiet --filter name=nginx-mailcow --filter name=dovecot-mailcow --filter name=postfix-mailcow)"
 | 
			
		||||
  _reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}"
 | 
			
		||||
 | 
			
		||||
  _info "Run reload: $_reload"
 | 
			
		||||
  if eval "$_reload"; then
 | 
			
		||||
    _info "Reload success!"
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								deploy/myapi.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								deploy/myapi.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a sample custom api script.
 | 
			
		||||
#This file name is "myapi.sh"
 | 
			
		||||
#So, here must be a method   myapi_deploy()
 | 
			
		||||
#Which will be called by acme.sh to deploy the cert
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
myapi_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _err "Not implemented yet"
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								deploy/mydevil.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										59
									
								
								deploy/mydevil.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# MyDevil.net API (2019-02-03)
 | 
			
		||||
#
 | 
			
		||||
# MyDevil.net already supports automatic Let's Encrypt certificates,
 | 
			
		||||
# except for wildcard domains.
 | 
			
		||||
#
 | 
			
		||||
# This script depends on `devil` command that MyDevil.net provides,
 | 
			
		||||
# which means that it works only on server side.
 | 
			
		||||
#
 | 
			
		||||
# Author: Marcin Konicki <https://ahwayakchih.neoni.net>
 | 
			
		||||
#
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
# Usage: mydevil_deploy domain keyfile certfile cafile fullchain
 | 
			
		||||
mydevil_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  ip=""
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  if ! _exists "devil"; then
 | 
			
		||||
    _err "Could not find 'devil' command."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  ip=$(mydevil_get_ip "$_cdomain")
 | 
			
		||||
  if [ -z "$ip" ]; then
 | 
			
		||||
    _err "Could not find IP for domain $_cdomain."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Delete old certificate first
 | 
			
		||||
  _info "Removing old certificate for $_cdomain at $ip"
 | 
			
		||||
  devil ssl www del "$ip" "$_cdomain"
 | 
			
		||||
 | 
			
		||||
  # Add new certificate
 | 
			
		||||
  _info "Adding new certificate for $_cdomain at $ip"
 | 
			
		||||
  devil ssl www add "$ip" "$_cfullchain" "$_ckey" "$_cdomain" || return 1
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
# Usage: ip=$(mydevil_get_ip domain.com)
 | 
			
		||||
#        echo $ip
 | 
			
		||||
mydevil_get_ip() {
 | 
			
		||||
  devil dns list "$1" | cut -w -s -f 3,7 | grep "^A$(printf '\t')" | cut -w -s -f 2 || return 1
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								deploy/mysqld.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/mysqld.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to mysqld server.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
mysqld_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _err "deploy cert to mysqld server, Not implemented yet"
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								deploy/nginx.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/nginx.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to nginx server.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
nginx_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _err "deploy cert to nginx server, Not implemented yet"
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										156
									
								
								deploy/openmediavault.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								deploy/openmediavault.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# This deploy hook is tested on OpenMediaVault 5.x. It supports both local and remote deployment.
 | 
			
		||||
# The way it works is that if a cert with the matching domain name is not found, it will firstly create a dummy cert to get its uuid, and then replace it with your cert.
 | 
			
		||||
#
 | 
			
		||||
# DEPLOY_OMV_WEBUI_ADMIN - This is OMV web gui admin account. Default value is admin. It's required as the user parameter (-u) for the omv-rpc command.
 | 
			
		||||
# DEPLOY_OMV_HOST and DEPLOY_OMV_SSH_USER are optional. They are used for remote deployment through ssh (support public key authentication only). Per design, OMV web gui admin doesn't have ssh permission, so another account is needed for ssh.
 | 
			
		||||
#
 | 
			
		||||
# returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
openmediavault_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_OMV_WEBUI_ADMIN
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_OMV_WEBUI_ADMIN" ]; then
 | 
			
		||||
    DEPLOY_OMV_WEBUI_ADMIN="admin"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedeployconf DEPLOY_OMV_WEBUI_ADMIN "$DEPLOY_OMV_WEBUI_ADMIN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_OMV_HOST
 | 
			
		||||
  _getdeployconf DEPLOY_OMV_SSH_USER
 | 
			
		||||
 | 
			
		||||
  if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then
 | 
			
		||||
    _info "[OMV deploy-hook] Deploy certificate remotely through ssh."
 | 
			
		||||
    _savedeployconf DEPLOY_OMV_HOST "$DEPLOY_OMV_HOST"
 | 
			
		||||
    _savedeployconf DEPLOY_OMV_SSH_USER "$DEPLOY_OMV_SSH_USER"
 | 
			
		||||
  else
 | 
			
		||||
    _info "[OMV deploy-hook] Deploy certificate locally."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then
 | 
			
		||||
 | 
			
		||||
    _command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{\"start\": 0, \"limit\": -1}' | jq -r '.data[] | select(.name==\"/CN='$_cdomain'\") | .uuid'"
 | 
			
		||||
    # shellcheck disable=SC2029
 | 
			
		||||
    _uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$_uuid" ]; then
 | 
			
		||||
      _info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!"
 | 
			
		||||
      _command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{\"cn\": \"test.example.com\", \"size\": 4096, \"days\": 3650, \"c\": \"\", \"st\": \"\", \"l\": \"\", \"o\": \"\", \"ou\": \"\", \"email\": \"\"}' | jq -r '.uuid'"
 | 
			
		||||
      # shellcheck disable=SC2029
 | 
			
		||||
      _uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
 | 
			
		||||
      _debug _command "$_command"
 | 
			
		||||
 | 
			
		||||
      if [ -z "$_uuid" ]; then
 | 
			
		||||
        _err "[OMV deploy-hook] An error occured while creating the certificate"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid"
 | 
			
		||||
    _fullchain=$(jq <"$_cfullchain" -aRs .)
 | 
			
		||||
    _key=$(jq <"$_ckey" -aRs .)
 | 
			
		||||
 | 
			
		||||
    _debug _fullchain "$_fullchain"
 | 
			
		||||
    _debug _key "$_key"
 | 
			
		||||
 | 
			
		||||
    _info "[OMV deploy-hook] Updating key and certificate in openmediavault"
 | 
			
		||||
    _command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'"
 | 
			
		||||
    # shellcheck disable=SC2029
 | 
			
		||||
    _result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
 | 
			
		||||
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
    _debug _result "$_result"
 | 
			
		||||
 | 
			
		||||
    _command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')"
 | 
			
		||||
    # shellcheck disable=SC2029
 | 
			
		||||
    _result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
 | 
			
		||||
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
    _debug _result "$_result"
 | 
			
		||||
 | 
			
		||||
    _info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)"
 | 
			
		||||
    _command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'"
 | 
			
		||||
    # shellcheck disable=SC2029
 | 
			
		||||
    _result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
 | 
			
		||||
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
    _debug _result "$_result"
 | 
			
		||||
 | 
			
		||||
    _info "[OMV deploy-hook] Asking nginx to reload"
 | 
			
		||||
    _command="nginx -s reload"
 | 
			
		||||
    # shellcheck disable=SC2029
 | 
			
		||||
    _result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
 | 
			
		||||
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
    _debug _result "$_result"
 | 
			
		||||
 | 
			
		||||
  else
 | 
			
		||||
 | 
			
		||||
    # shellcheck disable=SC2086
 | 
			
		||||
    _uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{"start": 0, "limit": -1}' | jq -r '.data[] | select(.name=="/CN='$_cdomain'") | .uuid')
 | 
			
		||||
    if [ -z "$_uuid" ]; then
 | 
			
		||||
      _info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!"
 | 
			
		||||
      # shellcheck disable=SC2086
 | 
			
		||||
      _uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{"cn": "test.example.com", "size": 4096, "days": 3650, "c": "", "st": "", "l": "", "o": "", "ou": "", "email": ""}' | jq -r '.uuid')
 | 
			
		||||
 | 
			
		||||
      if [ -z "$_uuid" ]; then
 | 
			
		||||
        _err "[OMB deploy-hook] An error occured while creating the certificate"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid"
 | 
			
		||||
    _fullchain=$(jq <"$_cfullchain" -aRs .)
 | 
			
		||||
    _key=$(jq <"$_ckey" -aRs .)
 | 
			
		||||
 | 
			
		||||
    _debug _fullchain "$_fullchain"
 | 
			
		||||
    _debug _key "$_key"
 | 
			
		||||
 | 
			
		||||
    _info "[OMV deploy-hook] Updating key and certificate in openmediavault"
 | 
			
		||||
    _command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'"
 | 
			
		||||
    _result=$(eval "$_command")
 | 
			
		||||
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
    _debug _result "$_result"
 | 
			
		||||
 | 
			
		||||
    _command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')"
 | 
			
		||||
    _result=$(eval "$_command")
 | 
			
		||||
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
    _debug _result "$_result"
 | 
			
		||||
 | 
			
		||||
    _info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)"
 | 
			
		||||
    _command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'"
 | 
			
		||||
    _result=$(eval "$_command")
 | 
			
		||||
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
    _debug _result "$_result"
 | 
			
		||||
 | 
			
		||||
    _info "[OMV deploy-hook] Asking nginx to reload"
 | 
			
		||||
    _command="nginx -s reload"
 | 
			
		||||
    _result=$(eval "$_command")
 | 
			
		||||
 | 
			
		||||
    _debug _command "$_command"
 | 
			
		||||
    _debug _result "$_result"
 | 
			
		||||
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								deploy/opensshd.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/opensshd.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to opensshd server.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
opensshd_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _err "deploy cert to opensshd server, Not implemented yet"
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										262
									
								
								deploy/openstack.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								deploy/openstack.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,262 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# OpenStack Barbican deploy hook
 | 
			
		||||
#
 | 
			
		||||
# This requires you to have OpenStackClient and python-barbicanclient
 | 
			
		||||
# installed.
 | 
			
		||||
#
 | 
			
		||||
# You will require Keystone V3 credentials loaded into your environment, which
 | 
			
		||||
# could be either password or v3applicationcredential type.
 | 
			
		||||
#
 | 
			
		||||
# Author: Andy Botting <andy@andybotting.com>
 | 
			
		||||
 | 
			
		||||
openstack_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  if ! _exists openstack; then
 | 
			
		||||
    _err "OpenStack client not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _openstack_credentials || return $?
 | 
			
		||||
 | 
			
		||||
  _info "Generate import pkcs12"
 | 
			
		||||
  _import_pkcs12="$(_mktemp)"
 | 
			
		||||
  if ! _openstack_to_pkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca"; then
 | 
			
		||||
    _err "Error creating pkcs12 certificate"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _import_pkcs12 "$_import_pkcs12"
 | 
			
		||||
  _base64_pkcs12=$(_base64 "multiline" <"$_import_pkcs12")
 | 
			
		||||
 | 
			
		||||
  secretHrefs=$(_openstack_get_secrets)
 | 
			
		||||
  _debug secretHrefs "$secretHrefs"
 | 
			
		||||
  _openstack_store_secret || return $?
 | 
			
		||||
 | 
			
		||||
  if [ -n "$secretHrefs" ]; then
 | 
			
		||||
    _info "Cleaning up existing secret"
 | 
			
		||||
    _openstack_delete_secrets || return $?
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Certificate successfully deployed"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_openstack_store_secret() {
 | 
			
		||||
  if ! openstack secret store --name "$_cdomain." -t 'application/octet-stream' -e base64 --payload "$_base64_pkcs12"; then
 | 
			
		||||
    _err "Failed to create OpenStack secret"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_openstack_delete_secrets() {
 | 
			
		||||
  echo "$secretHrefs" | while read -r secretHref; do
 | 
			
		||||
    _info "Deleting old secret $secretHref"
 | 
			
		||||
    if ! openstack secret delete "$secretHref"; then
 | 
			
		||||
      _err "Failed to delete OpenStack secret"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
  return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_openstack_get_secrets() {
 | 
			
		||||
  if ! secretHrefs=$(openstack secret list -f value --name "$_cdomain." | cut -d' ' -f1); then
 | 
			
		||||
    _err "Failed to list secrets"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  echo "$secretHrefs"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_openstack_to_pkcs() {
 | 
			
		||||
  # The existing _toPkcs command can't allow an empty password, due to sh
 | 
			
		||||
  # -z test, so copied here and forcing the empty password.
 | 
			
		||||
  _cpfx="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
 | 
			
		||||
  ${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_openstack_credentials() {
 | 
			
		||||
  _debug "Check OpenStack credentials"
 | 
			
		||||
 | 
			
		||||
  # If we have OS_AUTH_URL already set in the environment, then assume we want
 | 
			
		||||
  # to use those, otherwise use stored credentials
 | 
			
		||||
  if [ -n "$OS_AUTH_URL" ]; then
 | 
			
		||||
    _debug "OS_AUTH_URL env var found, using environment"
 | 
			
		||||
  else
 | 
			
		||||
    _debug "OS_AUTH_URL not found, loading stored credentials"
 | 
			
		||||
    OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}"
 | 
			
		||||
    OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}"
 | 
			
		||||
    OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}"
 | 
			
		||||
    OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}"
 | 
			
		||||
    OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}"
 | 
			
		||||
    OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}"
 | 
			
		||||
    OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}"
 | 
			
		||||
    OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}"
 | 
			
		||||
    OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}"
 | 
			
		||||
    OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}"
 | 
			
		||||
    OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}"
 | 
			
		||||
    OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}"
 | 
			
		||||
    OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Check each var and either save or clear it depending on whether its set.
 | 
			
		||||
  # The helps us clear out old vars in the case where a user may want
 | 
			
		||||
  # to switch between password and app creds
 | 
			
		||||
  _debug "OS_AUTH_URL" "$OS_AUTH_URL"
 | 
			
		||||
  if [ -n "$OS_AUTH_URL" ]; then
 | 
			
		||||
    export OS_AUTH_URL
 | 
			
		||||
    _saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_AUTH_URL
 | 
			
		||||
    _clearaccountconf SAVED_OS_AUTH_URL
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION"
 | 
			
		||||
  if [ -n "$OS_IDENTITY_API_VERSION" ]; then
 | 
			
		||||
    export OS_IDENTITY_API_VERSION
 | 
			
		||||
    _saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_IDENTITY_API_VERSION
 | 
			
		||||
    _clearaccountconf SAVED_OS_IDENTITY_API_VERSION
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE"
 | 
			
		||||
  if [ -n "$OS_AUTH_TYPE" ]; then
 | 
			
		||||
    export OS_AUTH_TYPE
 | 
			
		||||
    _saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_AUTH_TYPE
 | 
			
		||||
    _clearaccountconf SAVED_OS_AUTH_TYPE
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID"
 | 
			
		||||
  if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then
 | 
			
		||||
    export OS_APPLICATION_CREDENTIAL_ID
 | 
			
		||||
    _saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_APPLICATION_CREDENTIAL_ID
 | 
			
		||||
    _clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET"
 | 
			
		||||
  if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
 | 
			
		||||
    export OS_APPLICATION_CREDENTIAL_SECRET
 | 
			
		||||
    _saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_APPLICATION_CREDENTIAL_SECRET
 | 
			
		||||
    _clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_USERNAME" "$OS_USERNAME"
 | 
			
		||||
  if [ -n "$OS_USERNAME" ]; then
 | 
			
		||||
    export OS_USERNAME
 | 
			
		||||
    _saveaccountconf_mutable OS_USERNAME "$OS_USERNAME"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_USERNAME
 | 
			
		||||
    _clearaccountconf SAVED_OS_USERNAME
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _secure_debug "OS_PASSWORD" "$OS_PASSWORD"
 | 
			
		||||
  if [ -n "$OS_PASSWORD" ]; then
 | 
			
		||||
    export OS_PASSWORD
 | 
			
		||||
    _saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_PASSWORD
 | 
			
		||||
    _clearaccountconf SAVED_OS_PASSWORD
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME"
 | 
			
		||||
  if [ -n "$OS_PROJECT_NAME" ]; then
 | 
			
		||||
    export OS_PROJECT_NAME
 | 
			
		||||
    _saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_PROJECT_NAME
 | 
			
		||||
    _clearaccountconf SAVED_OS_PROJECT_NAME
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_PROJECT_ID" "$OS_PROJECT_ID"
 | 
			
		||||
  if [ -n "$OS_PROJECT_ID" ]; then
 | 
			
		||||
    export OS_PROJECT_ID
 | 
			
		||||
    _saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_PROJECT_ID
 | 
			
		||||
    _clearaccountconf SAVED_OS_PROJECT_ID
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME"
 | 
			
		||||
  if [ -n "$OS_USER_DOMAIN_NAME" ]; then
 | 
			
		||||
    export OS_USER_DOMAIN_NAME
 | 
			
		||||
    _saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_USER_DOMAIN_NAME
 | 
			
		||||
    _clearaccountconf SAVED_OS_USER_DOMAIN_NAME
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID"
 | 
			
		||||
  if [ -n "$OS_USER_DOMAIN_ID" ]; then
 | 
			
		||||
    export OS_USER_DOMAIN_ID
 | 
			
		||||
    _saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_USER_DOMAIN_ID
 | 
			
		||||
    _clearaccountconf SAVED_OS_USER_DOMAIN_ID
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME"
 | 
			
		||||
  if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then
 | 
			
		||||
    export OS_PROJECT_DOMAIN_NAME
 | 
			
		||||
    _saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_PROJECT_DOMAIN_NAME
 | 
			
		||||
    _clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID"
 | 
			
		||||
  if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then
 | 
			
		||||
    export OS_PROJECT_DOMAIN_ID
 | 
			
		||||
    _saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID"
 | 
			
		||||
  else
 | 
			
		||||
    unset OS_PROJECT_DOMAIN_ID
 | 
			
		||||
    _clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then
 | 
			
		||||
    # Application Credential auth
 | 
			
		||||
    if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
 | 
			
		||||
      _err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID"
 | 
			
		||||
      _err "and OS_APPLICATION_CREDENTIAL_SECRET must be set."
 | 
			
		||||
      _err "Please check your credentials and try again."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    # Password auth
 | 
			
		||||
    if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then
 | 
			
		||||
      _err "OpenStack username or password not found."
 | 
			
		||||
      _err "Please check your credentials and try again."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then
 | 
			
		||||
      _err "When using password authentication, OS_PROJECT_NAME or"
 | 
			
		||||
      _err "OS_PROJECT_ID must be set."
 | 
			
		||||
      _err "Please check your credentials and try again."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										301
									
								
								deploy/panos.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								deploy/panos.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,301 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to deploy certificates to Palo Alto Networks PANOS via API
 | 
			
		||||
# Note PANOS API KEY and IP address needs to be set prior to running.
 | 
			
		||||
# The following variables exported from environment will be used.
 | 
			
		||||
# If not set then values previously saved in domain.conf file are used.
 | 
			
		||||
#
 | 
			
		||||
# Firewall admin with superuser and IP address is required.
 | 
			
		||||
#
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
_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
 | 
			
		||||
      panos_key=$(echo "$1" | sed 's/^.*\(<key>\)\(.*\)<\/key>.*/\2/g')
 | 
			
		||||
      _panos_key=$panos_key
 | 
			
		||||
    else
 | 
			
		||||
      message="PAN-OS Key could not be set."
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    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 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
 | 
			
		||||
    _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
 | 
			
		||||
    _debug "**** Deploying $type ****"
 | 
			
		||||
    #Generate DELIM
 | 
			
		||||
    delim="-----MultipartDelimiter$(date "+%s%N")"
 | 
			
		||||
    nl="\015\012"
 | 
			
		||||
    #Set Header
 | 
			
		||||
    export _H1="Content-Type: multipart/form-data; boundary=$delim"
 | 
			
		||||
    if [ "$type" = 'cert' ]; then
 | 
			
		||||
      panos_url="${panos_url}?type=import"
 | 
			
		||||
      content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\ncertificate"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_cdomain"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
 | 
			
		||||
      if [ "$_panos_template" ]; then
 | 
			
		||||
        content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
 | 
			
		||||
      fi
 | 
			
		||||
      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"
 | 
			
		||||
      content="--$delim${nl}Content-Disposition: form-data; name=\"category\"\r\n\r\nprivate-key"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"certificate-name\"\r\n\r\n$_cdomain"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n123456"
 | 
			
		||||
      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cdomain.key")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
 | 
			
		||||
      if [ "$_panos_template" ]; then
 | 
			
		||||
        content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
 | 
			
		||||
      fi
 | 
			
		||||
      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}"
 | 
			
		||||
    #Convert CRLF
 | 
			
		||||
    content=$(printf %b "$content")
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Commit changes
 | 
			
		||||
  if [ "$type" = 'commit' ]; then
 | 
			
		||||
    _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 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"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# This is the main function that will call the other functions to deploy everything.
 | 
			
		||||
panos_deploy() {
 | 
			
		||||
  _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  # VALID FILE CHECK
 | 
			
		||||
  if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
 | 
			
		||||
    _err "Unable to find a valid key and/or cert.  If this is an ECDSA/ECC cert, use the --ecc flag when deploying."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # PANOS_HOST
 | 
			
		||||
  if [ "$PANOS_HOST" ]; then
 | 
			
		||||
    _debug "Detected ENV variable PANOS_HOST. Saving to file."
 | 
			
		||||
    _savedeployconf PANOS_HOST "$PANOS_HOST" 1
 | 
			
		||||
  else
 | 
			
		||||
    _debug "Attempting to load variable PANOS_HOST from file."
 | 
			
		||||
    _getdeployconf PANOS_HOST
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # PANOS USER
 | 
			
		||||
  if [ "$PANOS_USER" ]; then
 | 
			
		||||
    _debug "Detected ENV variable PANOS_USER. Saving to file."
 | 
			
		||||
    _savedeployconf PANOS_USER "$PANOS_USER" 1
 | 
			
		||||
  else
 | 
			
		||||
    _debug "Attempting to load variable PANOS_USER from file."
 | 
			
		||||
    _getdeployconf PANOS_USER
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # PANOS_PASS
 | 
			
		||||
  if [ "$PANOS_PASS" ]; then
 | 
			
		||||
    _debug "Detected ENV variable PANOS_PASS. Saving to file."
 | 
			
		||||
    _savedeployconf PANOS_PASS "$PANOS_PASS" 1
 | 
			
		||||
  else
 | 
			
		||||
    _debug "Attempting to load variable PANOS_PASS from file."
 | 
			
		||||
    _getdeployconf PANOS_PASS
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # PANOS_KEY
 | 
			
		||||
  _getdeployconf PANOS_KEY
 | 
			
		||||
  if [ "$PANOS_KEY" ]; then
 | 
			
		||||
    _debug "Detected saved key."
 | 
			
		||||
    _panos_key=$PANOS_KEY
 | 
			
		||||
  else
 | 
			
		||||
    _debug "No key detected"
 | 
			
		||||
    unset _panos_key
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # PANOS_TEMPLATE
 | 
			
		||||
  if [ "$PANOS_TEMPLATE" ]; then
 | 
			
		||||
    _debug "Detected ENV variable PANOS_TEMPLATE. Saving to file."
 | 
			
		||||
    _savedeployconf PANOS_TEMPLATE "$PANOS_TEMPLATE" 1
 | 
			
		||||
  else
 | 
			
		||||
    _debug "Attempting to load variable PANOS_TEMPLATE from file."
 | 
			
		||||
    _getdeployconf PANOS_TEMPLATE
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # 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
 | 
			
		||||
    # Generate a new API key if no valid API key is found
 | 
			
		||||
    if [ -z "$_panos_key" ]; then
 | 
			
		||||
      _debug "**** Generating new PANOS API KEY ****"
 | 
			
		||||
      deployer keygen
 | 
			
		||||
      _savedeployconf PANOS_KEY "$_panos_key" 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # Confirm that a valid key was generated
 | 
			
		||||
    if [ -z "$_panos_key" ]; then
 | 
			
		||||
      _err "Unable to generate an API key.  The user and pass may be invalid or not authorized to generate a new key.  Please check the PANOS_USER and PANOS_PASS credentials and try again"
 | 
			
		||||
      return 1
 | 
			
		||||
    else
 | 
			
		||||
      deployer cert
 | 
			
		||||
      deployer key
 | 
			
		||||
      deployer commit
 | 
			
		||||
      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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								deploy/peplink.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								deploy/peplink.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to deploy cert to Peplink Routers
 | 
			
		||||
#
 | 
			
		||||
# The following environment variables must be set:
 | 
			
		||||
#
 | 
			
		||||
# PEPLINK_Hostname - Peplink hostname
 | 
			
		||||
# PEPLINK_Username - Peplink username to login
 | 
			
		||||
# PEPLINK_Password - Peplink password to login
 | 
			
		||||
#
 | 
			
		||||
# The following environmental variables may be set if you don't like their
 | 
			
		||||
# default values:
 | 
			
		||||
#
 | 
			
		||||
# PEPLINK_Certtype - Certificate type to target for replacement
 | 
			
		||||
#                    defaults to "webadmin", can be one of:
 | 
			
		||||
#                      * "chub" (ContentHub)
 | 
			
		||||
#                      * "openvpn" (OpenVPN CA)
 | 
			
		||||
#                      * "portal" (Captive Portal SSL)
 | 
			
		||||
#                      * "webadmin" (Web Admin SSL)
 | 
			
		||||
#                      * "webproxy" (Proxy Root CA)
 | 
			
		||||
#                      * "wwan_ca" (Wi-Fi WAN CA)
 | 
			
		||||
#                      * "wwan_client" (Wi-Fi WAN Client)
 | 
			
		||||
# PEPLINK_Scheme   - defaults to "https"
 | 
			
		||||
# PEPLINK_Port     - defaults to "443"
 | 
			
		||||
#
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
_peplink_get_cookie_data() {
 | 
			
		||||
  grep -i "\W$1=" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
peplink_deploy() {
 | 
			
		||||
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
 | 
			
		||||
  # Get Hostname, Username and Password, but don't save until we successfully authenticate
 | 
			
		||||
  _getdeployconf PEPLINK_Hostname
 | 
			
		||||
  _getdeployconf PEPLINK_Username
 | 
			
		||||
  _getdeployconf PEPLINK_Password
 | 
			
		||||
  if [ -z "${PEPLINK_Hostname:-}" ] || [ -z "${PEPLINK_Username:-}" ] || [ -z "${PEPLINK_Password:-}" ]; then
 | 
			
		||||
    _err "PEPLINK_Hostname & PEPLINK_Username & PEPLINK_Password must be set"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 PEPLINK_Hostname "$PEPLINK_Hostname"
 | 
			
		||||
  _debug2 PEPLINK_Username "$PEPLINK_Username"
 | 
			
		||||
  _secure_debug2 PEPLINK_Password "$PEPLINK_Password"
 | 
			
		||||
 | 
			
		||||
  # Optional certificate type, scheme, and port for Peplink
 | 
			
		||||
  _getdeployconf PEPLINK_Certtype
 | 
			
		||||
  _getdeployconf PEPLINK_Scheme
 | 
			
		||||
  _getdeployconf PEPLINK_Port
 | 
			
		||||
 | 
			
		||||
  # Don't save the certificate type until we verify it exists and is supported
 | 
			
		||||
  _savedeployconf PEPLINK_Scheme "$PEPLINK_Scheme"
 | 
			
		||||
  _savedeployconf PEPLINK_Port "$PEPLINK_Port"
 | 
			
		||||
 | 
			
		||||
  # Default vaules for certificate type, scheme, and port
 | 
			
		||||
  [ -n "${PEPLINK_Certtype}" ] || PEPLINK_Certtype="webadmin"
 | 
			
		||||
  [ -n "${PEPLINK_Scheme}" ] || PEPLINK_Scheme="https"
 | 
			
		||||
  [ -n "${PEPLINK_Port}" ] || PEPLINK_Port="443"
 | 
			
		||||
 | 
			
		||||
  _debug2 PEPLINK_Certtype "$PEPLINK_Certtype"
 | 
			
		||||
  _debug2 PEPLINK_Scheme "$PEPLINK_Scheme"
 | 
			
		||||
  _debug2 PEPLINK_Port "$PEPLINK_Port"
 | 
			
		||||
 | 
			
		||||
  _base_url="$PEPLINK_Scheme://$PEPLINK_Hostname:$PEPLINK_Port"
 | 
			
		||||
  _debug _base_url "$_base_url"
 | 
			
		||||
 | 
			
		||||
  # Login, get the auth token from the cookie
 | 
			
		||||
  _info "Logging into $PEPLINK_Hostname:$PEPLINK_Port"
 | 
			
		||||
  encoded_username="$(printf "%s" "$PEPLINK_Username" | _url_encode)"
 | 
			
		||||
  encoded_password="$(printf "%s" "$PEPLINK_Password" | _url_encode)"
 | 
			
		||||
  response=$(_post "func=login&username=$encoded_username&password=$encoded_password" "$_base_url/cgi-bin/MANGA/api.cgi")
 | 
			
		||||
  auth_token=$(_peplink_get_cookie_data "bauth" <"$HTTP_HEADER")
 | 
			
		||||
  _debug3 response "$response"
 | 
			
		||||
  _debug auth_token "$auth_token"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$auth_token" ]; then
 | 
			
		||||
    _err "Unable to authenticate to $PEPLINK_Hostname:$PEPLINK_Port using $PEPLINK_Scheme."
 | 
			
		||||
    _err "Check your username and password."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _H1="Cookie: $auth_token"
 | 
			
		||||
  export _H1
 | 
			
		||||
  _debug2 H1 "${_H1}"
 | 
			
		||||
 | 
			
		||||
  # Now that we know the hostnameusername and password are good, save them
 | 
			
		||||
  _savedeployconf PEPLINK_Hostname "$PEPLINK_Hostname"
 | 
			
		||||
  _savedeployconf PEPLINK_Username "$PEPLINK_Username"
 | 
			
		||||
  _savedeployconf PEPLINK_Password "$PEPLINK_Password"
 | 
			
		||||
 | 
			
		||||
  _info "Generate form POST request"
 | 
			
		||||
 | 
			
		||||
  encoded_key="$(_url_encode <"$_ckey")"
 | 
			
		||||
  encoded_fullchain="$(_url_encode <"$_cfullchain")"
 | 
			
		||||
  body="cert_type=$PEPLINK_Certtype&cert_uid=§ion=CERT_modify&key_pem=$encoded_key&key_pem_passphrase=&key_pem_passphrase_confirm=&cert_pem=$encoded_fullchain"
 | 
			
		||||
  _debug3 body "$body"
 | 
			
		||||
 | 
			
		||||
  _info "Upload $PEPLINK_Certtype certificate to the Peplink"
 | 
			
		||||
 | 
			
		||||
  response=$(_post "$body" "$_base_url/cgi-bin/MANGA/admin.cgi")
 | 
			
		||||
  _debug3 response "$response"
 | 
			
		||||
 | 
			
		||||
  if echo "$response" | grep 'Success' >/dev/null; then
 | 
			
		||||
    # We've verified this certificate type is valid, so save it
 | 
			
		||||
    _savedeployconf PEPLINK_Certtype "$PEPLINK_Certtype"
 | 
			
		||||
    _info "Certificate was updated"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Unable to update certificate, error code $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										142
									
								
								deploy/proxmoxve.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								deploy/proxmoxve.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Deploy certificates to a proxmox virtual environment node using the API.
 | 
			
		||||
#
 | 
			
		||||
# Environment variables that can be set are:
 | 
			
		||||
# `DEPLOY_PROXMOXVE_SERVER`: The hostname of the proxmox ve node. Defaults to
 | 
			
		||||
#                            _cdomain.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_SERVER_PORT`: The port number the management interface is on.
 | 
			
		||||
#                                 Defaults to 8006.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_NODE_NAME`: The name of the node we'll be connecting to.
 | 
			
		||||
#                               Defaults to the host portion of the server
 | 
			
		||||
#                               domain name.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_USER`: The user we'll connect as. Defaults to root.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_USER_REALM`: The authentication realm the user authenticates
 | 
			
		||||
#                                with. Defaults to pam.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_API_TOKEN_NAME`: The name of the API token created for the
 | 
			
		||||
#                                    user account. Defaults to acme.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_API_TOKEN_KEY`: The API token. Required.
 | 
			
		||||
 | 
			
		||||
proxmoxve_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug2 _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # "Sane" defaults.
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_SERVER
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_SERVER" ]; then
 | 
			
		||||
    _target_hostname="$_cdomain"
 | 
			
		||||
  else
 | 
			
		||||
    _target_hostname="$DEPLOY_PROXMOXVE_SERVER"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_SERVER "$DEPLOY_PROXMOXVE_SERVER"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_SERVER "$_target_hostname"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_SERVER_PORT
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_SERVER_PORT" ]; then
 | 
			
		||||
    _target_port="8006"
 | 
			
		||||
  else
 | 
			
		||||
    _target_port="$DEPLOY_PROXMOXVE_SERVER_PORT"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_SERVER_PORT "$DEPLOY_PROXMOXVE_SERVER_PORT"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_SERVER_PORT "$_target_port"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_NODE_NAME
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_NODE_NAME" ]; then
 | 
			
		||||
    _node_name=$(echo "$_target_hostname" | cut -d. -f1)
 | 
			
		||||
  else
 | 
			
		||||
    _node_name="$DEPLOY_PROXMOXVE_NODE_NAME"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_NODE_NAME "$DEPLOY_PROXMOXVE_NODE_NAME"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_NODE_NAME "$_node_name"
 | 
			
		||||
 | 
			
		||||
  # Complete URL.
 | 
			
		||||
  _target_url="https://${_target_hostname}:${_target_port}/api2/json/nodes/${_node_name}/certificates/custom"
 | 
			
		||||
  _debug TARGET_URL "$_target_url"
 | 
			
		||||
 | 
			
		||||
  # More "sane" defaults.
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_USER
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_USER" ]; then
 | 
			
		||||
    _proxmoxve_user="root"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxve_user="$DEPLOY_PROXMOXVE_USER"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_USER "$DEPLOY_PROXMOXVE_USER"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_USER "$_proxmoxve_user"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_USER_REALM
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_USER_REALM" ]; then
 | 
			
		||||
    _proxmoxve_user_realm="pam"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxve_user_realm="$DEPLOY_PROXMOXVE_USER_REALM"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_USER_REALM "$DEPLOY_PROXMOXVE_USER_REALM"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_USER_REALM "$_proxmoxve_user_realm"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_API_TOKEN_NAME
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_API_TOKEN_NAME" ]; then
 | 
			
		||||
    _proxmoxve_api_token_name="acme"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxve_api_token_name="$DEPLOY_PROXMOXVE_API_TOKEN_NAME"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_NAME "$DEPLOY_PROXMOXVE_API_TOKEN_NAME"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_API_TOKEN_NAME "$_proxmoxve_api_token_name"
 | 
			
		||||
 | 
			
		||||
  # This is required.
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_API_TOKEN_KEY" ]; then
 | 
			
		||||
    _err "API key not provided."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY "$_proxmoxve_api_token_key"
 | 
			
		||||
 | 
			
		||||
  # PVE API Token header value. Used in "Authorization: PVEAPIToken".
 | 
			
		||||
  _proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
 | 
			
		||||
  _debug2 "Auth Header" "$_proxmoxve_header_api_token"
 | 
			
		||||
 | 
			
		||||
  # Ugly. I hate putting heredocs inside functions because heredocs don't
 | 
			
		||||
  # account for whitespace correctly but it _does_ work and is several times
 | 
			
		||||
  # cleaner than anything else I had here.
 | 
			
		||||
  #
 | 
			
		||||
  # This dumps the json payload to a variable that should be passable to the
 | 
			
		||||
  # _psot function.
 | 
			
		||||
  _json_payload=$(
 | 
			
		||||
    cat <<HEREDOC
 | 
			
		||||
{
 | 
			
		||||
  "certificates": "$(tr '\n' ':' <"$_cfullchain" | sed 's/:/\\n/g')",
 | 
			
		||||
  "key": "$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')",
 | 
			
		||||
  "node":"$_node_name",
 | 
			
		||||
  "restart":"1",
 | 
			
		||||
  "force":"1"
 | 
			
		||||
}
 | 
			
		||||
HEREDOC
 | 
			
		||||
  )
 | 
			
		||||
  _debug2 Payload "$_json_payload"
 | 
			
		||||
 | 
			
		||||
  _info "Push certificates to server"
 | 
			
		||||
  export HTTPS_INSECURE=1
 | 
			
		||||
  export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								deploy/pureftpd.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								deploy/pureftpd.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to pureftpd server.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
pureftpd_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _err "deploy cert to pureftpd server, Not implemented yet"
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										96
									
								
								deploy/qiniu.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								deploy/qiniu.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to create certificate to qiniu.com
 | 
			
		||||
#
 | 
			
		||||
# This deployment required following variables
 | 
			
		||||
# export QINIU_AK="QINIUACCESSKEY"
 | 
			
		||||
# export QINIU_SK="QINIUSECRETKEY"
 | 
			
		||||
# export QINIU_CDN_DOMAIN="cdn.example.com"
 | 
			
		||||
# If you have more than one domain, just
 | 
			
		||||
# export QINIU_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
 | 
			
		||||
 | 
			
		||||
QINIU_API_BASE="https://api.qiniu.com"
 | 
			
		||||
 | 
			
		||||
qiniu_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 "$QINIU_AK" ]; then
 | 
			
		||||
    _err "QINIU_AK is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf QINIU_AK "$QINIU_AK"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$QINIU_SK" ]; then
 | 
			
		||||
    _err "QINIU_SK is not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _savedomainconf QINIU_SK "$QINIU_SK"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$QINIU_CDN_DOMAIN" ]; then
 | 
			
		||||
    _savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN"
 | 
			
		||||
  else
 | 
			
		||||
    QINIU_CDN_DOMAIN="$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  ## upload certificate
 | 
			
		||||
  string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
 | 
			
		||||
  string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
 | 
			
		||||
 | 
			
		||||
  sslcert_path="/sslcert"
 | 
			
		||||
  sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$QINIU_CDN_DOMAIN\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}"
 | 
			
		||||
  sslcert_access_token="$(_make_access_token "$sslcert_path")"
 | 
			
		||||
  _debug sslcert_access_token "$sslcert_access_token"
 | 
			
		||||
  export _H1="Authorization: QBox $sslcert_access_token"
 | 
			
		||||
  sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64)
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$sslcert_response" "certID"; then
 | 
			
		||||
    _err "Error in creating certificate:"
 | 
			
		||||
    _err "$sslcert_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug sslcert_response "$sslcert_response"
 | 
			
		||||
  _info "Certificate successfully uploaded, updating domain $_cdomain"
 | 
			
		||||
 | 
			
		||||
  ## extract certId
 | 
			
		||||
  _certId="$(printf "%s" "$sslcert_response" | _normalizeJson | _egrep_o "certID\": *\"[^\"]*\"" | cut -d : -f 2)"
 | 
			
		||||
  _debug certId "$_certId"
 | 
			
		||||
 | 
			
		||||
  ## update domain ssl config
 | 
			
		||||
  update_body="{\"certid\":$_certId,\"forceHttps\":false}"
 | 
			
		||||
  for domain in $QINIU_CDN_DOMAIN; do
 | 
			
		||||
    update_path="/domain/$domain/httpsconf"
 | 
			
		||||
    update_access_token="$(_make_access_token "$update_path")"
 | 
			
		||||
    _debug update_access_token "$update_access_token"
 | 
			
		||||
    export _H1="Authorization: QBox $update_access_token"
 | 
			
		||||
    update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64)
 | 
			
		||||
 | 
			
		||||
    if _contains "$update_response" "error"; then
 | 
			
		||||
      _err "Error in updating domain $domain httpsconf:"
 | 
			
		||||
      _err "$update_response"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _debug update_response "$update_response"
 | 
			
		||||
    _info "Domain $domain certificate has been deployed successfully"
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_make_access_token() {
 | 
			
		||||
  _token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64 | tr -- '+/' '-_')"
 | 
			
		||||
  echo "$QINIU_AK:$_token"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										204
									
								
								deploy/routeros.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								deploy/routeros.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,204 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is a script to deploy cert to routeros router.
 | 
			
		||||
# Deploy the cert to remote routeros
 | 
			
		||||
#
 | 
			
		||||
# ```sh
 | 
			
		||||
# acme.sh --deploy -d ftp.example.com --deploy-hook routeros
 | 
			
		||||
# ```
 | 
			
		||||
#
 | 
			
		||||
# Before you can deploy the certificate to router os, you need
 | 
			
		||||
# to add the id_rsa.pub key to the routeros and assign a user
 | 
			
		||||
# to that key.
 | 
			
		||||
#
 | 
			
		||||
# The user need to have access to ssh, ftp, read and write.
 | 
			
		||||
#
 | 
			
		||||
# There are no need to enable ftp service for the script to work,
 | 
			
		||||
# as they are transmitted over SCP, however ftp is needed to store
 | 
			
		||||
# the files on the router.
 | 
			
		||||
#
 | 
			
		||||
# Then you need to set the environment variables for the
 | 
			
		||||
# deploy script to work.
 | 
			
		||||
#
 | 
			
		||||
# ```sh
 | 
			
		||||
# export ROUTER_OS_USERNAME=certuser
 | 
			
		||||
# export ROUTER_OS_HOST=router.example.com
 | 
			
		||||
# export ROUTER_OS_PORT=22
 | 
			
		||||
#
 | 
			
		||||
# acme.sh --deploy -d ftp.example.com --deploy-hook routeros
 | 
			
		||||
# ```
 | 
			
		||||
#
 | 
			
		||||
# The deploy script will remove previously deployed certificates,
 | 
			
		||||
# and it does this with an assumption on how RouterOS names imported
 | 
			
		||||
# certificates, adding a "cer_0" suffix at the end. This is true for
 | 
			
		||||
# versions 6.32 -> 6.41.3, but it is not guaranteed that it will be
 | 
			
		||||
# true for future versions when upgrading.
 | 
			
		||||
#
 | 
			
		||||
# If the router have other certificates with the same name as the one
 | 
			
		||||
# beeing deployed, then this script will remove those certificates.
 | 
			
		||||
#
 | 
			
		||||
# At the end of the script, the services that use those certificates
 | 
			
		||||
# could be updated. Currently only the www-ssl service is beeing
 | 
			
		||||
# updated, but more services could be added.
 | 
			
		||||
#
 | 
			
		||||
# For instance:
 | 
			
		||||
# ```sh
 | 
			
		||||
# export ROUTER_OS_ADDITIONAL_SERVICES="/ip service set api-ssl certificate=$_cdomain.cer_0"
 | 
			
		||||
# ```
 | 
			
		||||
#
 | 
			
		||||
# One optional thing to do as well is to create a script that updates
 | 
			
		||||
# all the required services and run that script in a single command.
 | 
			
		||||
#
 | 
			
		||||
# To adopt parameters to `scp` and/or `ssh` set the optional
 | 
			
		||||
# `ROUTER_OS_SSH_CMD` and `ROUTER_OS_SCP_CMD` variables accordingly,
 | 
			
		||||
# see ssh(1) and scp(1) for parameters to those commands.
 | 
			
		||||
#
 | 
			
		||||
# Example:
 | 
			
		||||
# ```ssh
 | 
			
		||||
# export ROUTER_OS_SSH_CMD="ssh -i /acme.sh/.ssh/router.example.com -o UserKnownHostsFile=/acme.sh/.ssh/known_hosts"
 | 
			
		||||
# export ROUTER_OS_SCP_CMD="scp -i /acme.sh/.ssh/router.example.com -o UserKnownHostsFile=/acme.sh/.ssh/known_hosts"
 | 
			
		||||
# ````
 | 
			
		||||
#
 | 
			
		||||
# returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
routeros_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 ROUTER_OS_HOST
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ROUTER_OS_HOST" ]; then
 | 
			
		||||
    _debug "Using _cdomain as ROUTER_OS_HOST, please set if not correct."
 | 
			
		||||
    ROUTER_OS_HOST="$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf ROUTER_OS_USERNAME
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ROUTER_OS_USERNAME" ]; then
 | 
			
		||||
    _err "Need to set the env variable ROUTER_OS_USERNAME"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf ROUTER_OS_PORT
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ROUTER_OS_PORT" ]; then
 | 
			
		||||
    _debug "Using default port 22 as ROUTER_OS_PORT, please set if not correct."
 | 
			
		||||
    ROUTER_OS_PORT=22
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf ROUTER_OS_SSH_CMD
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ROUTER_OS_SSH_CMD" ]; then
 | 
			
		||||
    _debug "Use default ssh setup."
 | 
			
		||||
    ROUTER_OS_SSH_CMD="ssh -p $ROUTER_OS_PORT"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf ROUTER_OS_SCP_CMD
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ROUTER_OS_SCP_CMD" ]; then
 | 
			
		||||
    _debug "USe default scp setup."
 | 
			
		||||
    ROUTER_OS_SCP_CMD="scp -P $ROUTER_OS_PORT"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _getdeployconf ROUTER_OS_ADDITIONAL_SERVICES
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ROUTER_OS_ADDITIONAL_SERVICES" ]; then
 | 
			
		||||
    _debug "Not enabling additional services"
 | 
			
		||||
    ROUTER_OS_ADDITIONAL_SERVICES=""
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedeployconf ROUTER_OS_HOST "$ROUTER_OS_HOST"
 | 
			
		||||
  _savedeployconf ROUTER_OS_USERNAME "$ROUTER_OS_USERNAME"
 | 
			
		||||
  _savedeployconf ROUTER_OS_PORT "$ROUTER_OS_PORT"
 | 
			
		||||
  _savedeployconf ROUTER_OS_SSH_CMD "$ROUTER_OS_SSH_CMD"
 | 
			
		||||
  _savedeployconf ROUTER_OS_SCP_CMD "$ROUTER_OS_SCP_CMD"
 | 
			
		||||
  _savedeployconf ROUTER_OS_ADDITIONAL_SERVICES "$ROUTER_OS_ADDITIONAL_SERVICES"
 | 
			
		||||
 | 
			
		||||
  # push key to routeros
 | 
			
		||||
  if ! _scp_certificate "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key"; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # push certificate chain to routeros
 | 
			
		||||
  if ! _scp_certificate "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer"; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  DEPLOY_SCRIPT_CMD=":do {/system script remove \"LECertDeploy-$_cdomain\" } on-error={ }; \
 | 
			
		||||
/system script add name=\"LECertDeploy-$_cdomain\" owner=$ROUTER_OS_USERNAME \
 | 
			
		||||
comment=\"generated by routeros deploy script in acme.sh\" \
 | 
			
		||||
source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
 | 
			
		||||
\n/certificate remove [ find name=$_cdomain.cer_1 ];\
 | 
			
		||||
\n/certificate remove [ find name=$_cdomain.cer_2 ];\
 | 
			
		||||
\ndelay 1;\
 | 
			
		||||
\n/certificate import file-name=\\\"$_cdomain.cer\\\" passphrase=\\\"\\\";\
 | 
			
		||||
\n/certificate import file-name=\\\"$_cdomain.key\\\" passphrase=\\\"\\\";\
 | 
			
		||||
\ndelay 1;\
 | 
			
		||||
\n:do {/file remove $_cdomain.cer; } on-error={ }\
 | 
			
		||||
\n:do {/file remove $_cdomain.key; } on-error={ }\
 | 
			
		||||
\ndelay 2;\
 | 
			
		||||
\n/ip service set www-ssl certificate=$_cdomain.cer_0;\
 | 
			
		||||
\n$ROUTER_OS_ADDITIONAL_SERVICES;\
 | 
			
		||||
\n\"
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
  if ! _ssh_remote_cmd "$DEPLOY_SCRIPT_CMD"; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _ssh_remote_cmd "/system script run \"LECertDeploy-$_cdomain\""; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _ssh_remote_cmd "/system script remove \"LECertDeploy-$_cdomain\""; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# inspired by deploy/ssh.sh
 | 
			
		||||
_ssh_remote_cmd() {
 | 
			
		||||
  _cmd="$1"
 | 
			
		||||
  _secure_debug "Remote commands to execute: $_cmd"
 | 
			
		||||
  _info "Submitting sequence of commands to routeros"
 | 
			
		||||
  # quotations in bash cmd below intended.  Squash travis spellcheck error
 | 
			
		||||
  # shellcheck disable=SC2029
 | 
			
		||||
  $ROUTER_OS_SSH_CMD "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "$_cmd"
 | 
			
		||||
  _err_code="$?"
 | 
			
		||||
 | 
			
		||||
  if [ "$_err_code" != "0" ]; then
 | 
			
		||||
    _err "Error code $_err_code returned from routeros"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return $_err_code
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_scp_certificate() {
 | 
			
		||||
  _src="$1"
 | 
			
		||||
  _dst="$2"
 | 
			
		||||
  _secure_debug "scp '$_src' to '$_dst'"
 | 
			
		||||
  _info "Push key '$_src' to routeros"
 | 
			
		||||
 | 
			
		||||
  $ROUTER_OS_SCP_CMD "$_src" "$_dst"
 | 
			
		||||
  _err_code="$?"
 | 
			
		||||
 | 
			
		||||
  if [ "$_err_code" != "0" ]; then
 | 
			
		||||
    _err "Error code $_err_code returned from scp"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return $_err_code
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										462
									
								
								deploy/ssh.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										462
									
								
								deploy/ssh.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,462 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Script to deploy certificates to remote server by SSH
 | 
			
		||||
# Note that SSH must be able to login to remote host without a password...
 | 
			
		||||
# SSH Keys must have been exchanged with the remote host.  Validate and
 | 
			
		||||
# test that you can login to USER@SERVER from the host running acme.sh before
 | 
			
		||||
# using this script.
 | 
			
		||||
#
 | 
			
		||||
# The following variables exported from environment will be used.
 | 
			
		||||
# If not set then values previously saved in domain.conf file are used.
 | 
			
		||||
#
 | 
			
		||||
# Only a username is required.  All others are optional.
 | 
			
		||||
#
 | 
			
		||||
# 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="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"
 | 
			
		||||
# export DEPLOY_SSH_FULLCHAIN=""
 | 
			
		||||
# export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart"
 | 
			
		||||
# 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 #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
ssh_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  _deploy_ssh_servers=""
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _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
 | 
			
		||||
    _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 [ -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 [ -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 [ -z "$DEPLOY_SSH_BACKUP" ]; then
 | 
			
		||||
    DEPLOY_SSH_BACKUP="yes"
 | 
			
		||||
  fi
 | 
			
		||||
  _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 [ -z "$DEPLOY_SSH_BACKUP_PATH" ]; then
 | 
			
		||||
    DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
 | 
			
		||||
 | 
			
		||||
  # MULTI_CALL is optional. If not provided then default to previously saved
 | 
			
		||||
  # value (which may be undefined... equivalent to "no").
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_multi_call DEPLOY_SSH_MULTI_CALL
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_MULTI_CALL
 | 
			
		||||
  _debug2 DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_MULTI_CALL" ]; then
 | 
			
		||||
    DEPLOY_SSH_MULTI_CALL="no"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
 | 
			
		||||
 | 
			
		||||
  # KEYFILE is optional.
 | 
			
		||||
  # 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
 | 
			
		||||
 | 
			
		||||
  # 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_ssh_deploy() {
 | 
			
		||||
  _err_code=0
 | 
			
		||||
  _cmdstr=""
 | 
			
		||||
  _backupprefix=""
 | 
			
		||||
  _backupdir=""
 | 
			
		||||
  _local_cert_file=""
 | 
			
		||||
  _local_ca_file=""
 | 
			
		||||
  _local_full_file=""
 | 
			
		||||
 | 
			
		||||
  case $DEPLOY_SSH_SERVER in
 | 
			
		||||
  *:*)
 | 
			
		||||
    _host=${DEPLOY_SSH_SERVER%:*}
 | 
			
		||||
    _port=${DEPLOY_SSH_SERVER##*:}
 | 
			
		||||
    ;;
 | 
			
		||||
  *)
 | 
			
		||||
    _host=$DEPLOY_SSH_SERVER
 | 
			
		||||
    _port=
 | 
			
		||||
    ;;
 | 
			
		||||
  esac
 | 
			
		||||
 | 
			
		||||
  _info "Deploy certificates to remote server $DEPLOY_SSH_USER@$_host:$_port"
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
    _backupprefix="$DEPLOY_SSH_BACKUP_PATH/$_cdomain-backup"
 | 
			
		||||
    _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
 | 
			
		||||
    # run cleanup on the backup directory, erase all older
 | 
			
		||||
    # than 180 days (15552000 seconds).
 | 
			
		||||
    _cmdstr="{ now=\"\$(date -u +%s)\"; for fn in $_backupprefix*; \
 | 
			
		||||
do if [ -d \"\$fn\" ] && [ \"\$(expr \$now - \$(date -ur \$fn +%s) )\" -ge \"15552000\" ]; \
 | 
			
		||||
then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; done; }; $_cmdstr"
 | 
			
		||||
    # Alternate version of above... _cmdstr="find $_backupprefix* -type d -mtime +180 2>/dev/null | xargs rm -rf; $_cmdstr"
 | 
			
		||||
    # Create our backup directory for overwritten cert files.
 | 
			
		||||
    _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 [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $DEPLOY_SSH_KEYFILE $_backupdir >/dev/null;"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # copy new key into file.
 | 
			
		||||
    if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
      # scp the file
 | 
			
		||||
      if ! _scp_remote_cmd "$_ckey" "$DEPLOY_SSH_KEYFILE"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      # ssh echo to the file
 | 
			
		||||
      _cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE;"
 | 
			
		||||
      _info "will copy private key to remote file $DEPLOY_SSH_KEYFILE"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
    _pipe=">"
 | 
			
		||||
    if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
      # if filename is same as previous file then append.
 | 
			
		||||
      _pipe=">>"
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $DEPLOY_SSH_CERTFILE $_backupdir >/dev/null;"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # copy new certificate into file.
 | 
			
		||||
    if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
      # scp the file
 | 
			
		||||
      _local_cert_file=$(_mktemp)
 | 
			
		||||
      if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
        cat "$_ckey" >>"$_local_cert_file"
 | 
			
		||||
      fi
 | 
			
		||||
      cat "$_ccert" >>"$_local_cert_file"
 | 
			
		||||
      if ! _scp_remote_cmd "$_local_cert_file" "$DEPLOY_SSH_CERTFILE"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      # ssh echo to the file
 | 
			
		||||
      _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $DEPLOY_SSH_CERTFILE;"
 | 
			
		||||
      _info "will copy certificate to remote file $DEPLOY_SSH_CERTFILE"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CAFILE" ]; then
 | 
			
		||||
    _pipe=">"
 | 
			
		||||
    if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ] ||
 | 
			
		||||
      [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
      # if filename is same as previous file then append.
 | 
			
		||||
      _pipe=">>"
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $DEPLOY_SSH_CAFILE $_backupdir >/dev/null;"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # copy new certificate into file.
 | 
			
		||||
    if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
      # scp the file
 | 
			
		||||
      _local_ca_file=$(_mktemp)
 | 
			
		||||
      if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
        cat "$_ckey" >>"$_local_ca_file"
 | 
			
		||||
      fi
 | 
			
		||||
      if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
        cat "$_ccert" >>"$_local_ca_file"
 | 
			
		||||
      fi
 | 
			
		||||
      cat "$_cca" >>"$_local_ca_file"
 | 
			
		||||
      if ! _scp_remote_cmd "$_local_ca_file" "$DEPLOY_SSH_CAFILE"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      # ssh echo to the file
 | 
			
		||||
      _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $DEPLOY_SSH_CAFILE;"
 | 
			
		||||
      _info "will copy CA file to remote file $DEPLOY_SSH_CAFILE"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
 | 
			
		||||
    _pipe=">"
 | 
			
		||||
    if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ] ||
 | 
			
		||||
      [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ] ||
 | 
			
		||||
      [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
 | 
			
		||||
      # if filename is same as previous file then append.
 | 
			
		||||
      _pipe=">>"
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $DEPLOY_SSH_FULLCHAIN $_backupdir >/dev/null;"
 | 
			
		||||
      if [ "$DEPLOY_SSH_FULLCHAIN" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # copy new certificate into file.
 | 
			
		||||
    if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
      # scp the file
 | 
			
		||||
      _local_full_file=$(_mktemp)
 | 
			
		||||
      if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
        cat "$_ckey" >>"$_local_full_file"
 | 
			
		||||
      fi
 | 
			
		||||
      if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
        cat "$_ccert" >>"$_local_full_file"
 | 
			
		||||
      fi
 | 
			
		||||
      if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
 | 
			
		||||
        cat "$_cca" >>"$_local_full_file"
 | 
			
		||||
      fi
 | 
			
		||||
      cat "$_cfullchain" >>"$_local_full_file"
 | 
			
		||||
      if ! _scp_remote_cmd "$_local_full_file" "$DEPLOY_SSH_FULLCHAIN"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      # ssh echo to the file
 | 
			
		||||
      _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $DEPLOY_SSH_FULLCHAIN;"
 | 
			
		||||
      _info "will copy fullchain to remote file $DEPLOY_SSH_FULLCHAIN"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # cleanup local files if any
 | 
			
		||||
  if [ -f "$_local_cert_file" ]; then
 | 
			
		||||
    rm -f "$_local_cert_file"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -f "$_local_ca_file" ]; then
 | 
			
		||||
    rm -f "$_local_ca_file"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -f "$_local_full_file" ]; then
 | 
			
		||||
    rm -f "$_local_full_file"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
 | 
			
		||||
    _cmdstr="$_cmdstr $DEPLOY_SSH_REMOTE_CMD;"
 | 
			
		||||
    _info "Will execute remote command $DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
    if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # if commands not all sent in multiple calls then all commands sent in a single SSH call now...
 | 
			
		||||
  if [ -n "$_cmdstr" ]; then
 | 
			
		||||
    if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
      return $_err_code
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  # cleanup in case all is ok
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#cmd
 | 
			
		||||
_ssh_remote_cmd() {
 | 
			
		||||
  _cmd="$1"
 | 
			
		||||
 | 
			
		||||
  _ssh_cmd="$DEPLOY_SSH_CMD"
 | 
			
		||||
  if [ -n "$_port" ]; then
 | 
			
		||||
    _ssh_cmd="$_ssh_cmd -p $_port"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _secure_debug "Remote commands to execute: $_cmd"
 | 
			
		||||
  _info "Submitting sequence of commands to remote server by $_ssh_cmd"
 | 
			
		||||
 | 
			
		||||
  # quotations in bash cmd below intended.  Squash travis spellcheck error
 | 
			
		||||
  # shellcheck disable=SC2029
 | 
			
		||||
  $_ssh_cmd "$DEPLOY_SSH_USER@$_host" sh -c "'$_cmd'"
 | 
			
		||||
  _err_code="$?"
 | 
			
		||||
 | 
			
		||||
  if [ "$_err_code" != "0" ]; then
 | 
			
		||||
    _err "Error code $_err_code returned from ssh"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										98
									
								
								deploy/strongswan.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								deploy/strongswan.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a sample custom api script.
 | 
			
		||||
#This file name is "myapi.sh"
 | 
			
		||||
#So, here must be a method   myapi_deploy()
 | 
			
		||||
#Which will be called by acme.sh to deploy the cert
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
strongswan_deploy() {
 | 
			
		||||
  _cdomain="${1}"
 | 
			
		||||
  _ckey="${2}"
 | 
			
		||||
  _ccert="${3}"
 | 
			
		||||
  _cca="${4}"
 | 
			
		||||
  _cfullchain="${5}"
 | 
			
		||||
  _info "Using strongswan"
 | 
			
		||||
  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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										445
									
								
								deploy/synology_dsm.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										445
									
								
								deploy/synology_dsm.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,445 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# ACME.sh 3rd party deploy plugin for Synology DSM
 | 
			
		||||
################################################################################
 | 
			
		||||
# Authors: Brian Hartvigsen (creator), https://github.com/tresni
 | 
			
		||||
#          Martin Arndt (contributor), https://troublezone.net/
 | 
			
		||||
# Updated: 2023-07-03
 | 
			
		||||
# Issues:  https://github.com/acmesh-official/acme.sh/issues/2727
 | 
			
		||||
################################################################################
 | 
			
		||||
# Usage (shown values are the examples):
 | 
			
		||||
# 1. Set required environment variables:
 | 
			
		||||
# - use automatically created temp admin user to authenticate
 | 
			
		||||
#   export SYNO_USE_TEMP_ADMIN=1
 | 
			
		||||
# - or provide your own admin user credential to authenticate
 | 
			
		||||
#   1. export SYNO_USERNAME="adminUser"
 | 
			
		||||
#   2. export SYNO_PASSWORD="adminPassword"
 | 
			
		||||
# 2. Set optional environment variables
 | 
			
		||||
# - common optional variables
 | 
			
		||||
#   - export SYNO_SCHEME="http"         - defaults to "http"
 | 
			
		||||
#   - export SYNO_HOSTNAME="localhost"  - defaults to "localhost"
 | 
			
		||||
#   - export SYNO_PORT="5000"           - defaults to "5000"
 | 
			
		||||
#   - export SYNO_CREATE=1 - to allow creating the cert if it doesn't exist
 | 
			
		||||
#   - export SYNO_CERTIFICATE="" - to replace a specific cert by its
 | 
			
		||||
#                                    description
 | 
			
		||||
# - temp admin optional variables
 | 
			
		||||
#   - export SYNO_LOCAL_HOSTNAME=1   - if set to 1, force to treat hostname is
 | 
			
		||||
#                                      targeting current local machine (since
 | 
			
		||||
#                                      this method only locally supported)
 | 
			
		||||
# - exsiting admin 2FA-OTP optional variables
 | 
			
		||||
#   - export SYNO_OTP_CODE="XXXXXX" - if set, script won't require to
 | 
			
		||||
#                                     interactive input the OTP code
 | 
			
		||||
#   - export SYNO_DEVICE_NAME="CertRenewal" - if set, script won't require to
 | 
			
		||||
#                                             interactive input the device name
 | 
			
		||||
#   - export SYNO_DEVICE_ID=""      - (deprecated, auth with OTP code instead)
 | 
			
		||||
#                                     required for omitting 2FA-OTP
 | 
			
		||||
# 3. Run command:
 | 
			
		||||
# acme.sh --deploy --deploy-hook synology_dsm -d example.com
 | 
			
		||||
################################################################################
 | 
			
		||||
# Dependencies:
 | 
			
		||||
# - curl
 | 
			
		||||
# - synouser & synogroup & synosetkeyvalue (Required for SYNO_USE_TEMP_ADMIN=1)
 | 
			
		||||
################################################################################
 | 
			
		||||
# Return value:
 | 
			
		||||
# 0 means success, otherwise error.
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
########## Public functions ####################################################
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
synology_dsm_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
 | 
			
		||||
  # Get username and password, but don't save until we authenticated successfully
 | 
			
		||||
  _migratedeployconf SYNO_Username SYNO_USERNAME
 | 
			
		||||
  _migratedeployconf SYNO_Password SYNO_PASSWORD
 | 
			
		||||
  _migratedeployconf SYNO_Device_ID SYNO_DEVICE_ID
 | 
			
		||||
  _migratedeployconf SYNO_Device_Name SYNO_DEVICE_NAME
 | 
			
		||||
  _getdeployconf SYNO_USERNAME
 | 
			
		||||
  _getdeployconf SYNO_PASSWORD
 | 
			
		||||
  _getdeployconf SYNO_DEVICE_ID
 | 
			
		||||
  _getdeployconf SYNO_DEVICE_NAME
 | 
			
		||||
 | 
			
		||||
  # Prepare to use temp admin if SYNO_USE_TEMP_ADMIN is set
 | 
			
		||||
  _getdeployconf SYNO_USE_TEMP_ADMIN
 | 
			
		||||
  _check2cleardeployconfexp SYNO_USE_TEMP_ADMIN
 | 
			
		||||
  _debug2 SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
 | 
			
		||||
    if ! _exists synouser || ! _exists synogroup || ! _exists synosetkeyvalue; then
 | 
			
		||||
      _err "Missing required tools to creat temp admin user, please set SYNO_USERNAME and SYNO_PASSWORD instead."
 | 
			
		||||
      _err "Notice: temp admin user authorization method only supports local deployment on DSM."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if synouser --help 2>&1 | grep -q 'Permission denied'; then
 | 
			
		||||
      _err "For creating temp admin user, the deploy script must be run as root."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    [ -n "$SYNO_USERNAME" ] || _savedeployconf SYNO_USERNAME ""
 | 
			
		||||
    [ -n "$SYNO_PASSWORD" ] || _savedeployconf SYNO_PASSWORD ""
 | 
			
		||||
 | 
			
		||||
    _debug "Setting temp admin user credential..."
 | 
			
		||||
    SYNO_USERNAME=sc-acmesh-tmp
 | 
			
		||||
    SYNO_PASSWORD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
 | 
			
		||||
    # Set 2FA-OTP settings to empty consider they won't be needed.
 | 
			
		||||
    SYNO_DEVICE_ID=
 | 
			
		||||
    SYNO_DEVICE_NAME=
 | 
			
		||||
    SYNO_OTP_CODE=
 | 
			
		||||
  else
 | 
			
		||||
    _debug2 SYNO_USERNAME "$SYNO_USERNAME"
 | 
			
		||||
    _secure_debug2 SYNO_PASSWORD "$SYNO_PASSWORD"
 | 
			
		||||
    _debug2 SYNO_DEVICE_NAME "$SYNO_DEVICE_NAME"
 | 
			
		||||
    _secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$SYNO_USERNAME" ] || [ -z "$SYNO_PASSWORD" ]; then
 | 
			
		||||
    _err "You must set either SYNO_USE_TEMP_ADMIN, or set both SYNO_USERNAME and SYNO_PASSWORD."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Optional scheme, hostname and port for Synology DSM
 | 
			
		||||
  _migratedeployconf SYNO_Scheme SYNO_SCHEME
 | 
			
		||||
  _migratedeployconf SYNO_Hostname SYNO_HOSTNAME
 | 
			
		||||
  _migratedeployconf SYNO_Port SYNO_PORT
 | 
			
		||||
  _getdeployconf SYNO_SCHEME
 | 
			
		||||
  _getdeployconf SYNO_HOSTNAME
 | 
			
		||||
  _getdeployconf SYNO_PORT
 | 
			
		||||
 | 
			
		||||
  # Default values for scheme, hostname and port
 | 
			
		||||
  # Defaulting to localhost and http, because it's localhost…
 | 
			
		||||
  [ -n "$SYNO_SCHEME" ] || SYNO_SCHEME=http
 | 
			
		||||
  [ -n "$SYNO_HOSTNAME" ] || SYNO_HOSTNAME=localhost
 | 
			
		||||
  [ -n "$SYNO_PORT" ] || SYNO_PORT=5000
 | 
			
		||||
  _savedeployconf SYNO_SCHEME "$SYNO_SCHEME"
 | 
			
		||||
  _savedeployconf SYNO_HOSTNAME "$SYNO_HOSTNAME"
 | 
			
		||||
  _savedeployconf SYNO_PORT "$SYNO_PORT"
 | 
			
		||||
  _debug2 SYNO_SCHEME "$SYNO_SCHEME"
 | 
			
		||||
  _debug2 SYNO_HOSTNAME "$SYNO_HOSTNAME"
 | 
			
		||||
  _debug2 SYNO_PORT "$SYNO_PORT"
 | 
			
		||||
 | 
			
		||||
  # Get the certificate description, but don't save it until we verify it's real
 | 
			
		||||
  _migratedeployconf SYNO_Certificate SYNO_CERTIFICATE "base64"
 | 
			
		||||
  _getdeployconf SYNO_CERTIFICATE
 | 
			
		||||
  _check2cleardeployconfexp SYNO_CERTIFICATE
 | 
			
		||||
  _debug SYNO_CERTIFICATE "${SYNO_CERTIFICATE:-}"
 | 
			
		||||
 | 
			
		||||
  # shellcheck disable=SC1003 # We are not trying to escape a single quote
 | 
			
		||||
  if printf "%s" "$SYNO_CERTIFICATE" | grep '\\'; then
 | 
			
		||||
    _err "Do not use a backslash (\) in your certificate description"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Getting API version..."
 | 
			
		||||
  _base_url="$SYNO_SCHEME://$SYNO_HOSTNAME:$SYNO_PORT"
 | 
			
		||||
  _debug _base_url "$_base_url"
 | 
			
		||||
  response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
 | 
			
		||||
  api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([^"]*\)".*/\1/p')
 | 
			
		||||
  api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
 | 
			
		||||
  _debug3 response "$response"
 | 
			
		||||
  _debug3 api_path "$api_path"
 | 
			
		||||
  _debug3 api_version "$api_version"
 | 
			
		||||
 | 
			
		||||
  # Login, get the session ID and SynoToken from JSON
 | 
			
		||||
  _info "Logging into $SYNO_HOSTNAME:$SYNO_PORT..."
 | 
			
		||||
  encoded_username="$(printf "%s" "$SYNO_USERNAME" | _url_encode)"
 | 
			
		||||
  encoded_password="$(printf "%s" "$SYNO_PASSWORD" | _url_encode)"
 | 
			
		||||
 | 
			
		||||
  # ## START ## - DEPRECATED, for backward compatibility
 | 
			
		||||
  _getdeployconf SYNO_TOTP_SECRET
 | 
			
		||||
 | 
			
		||||
  if [ -n "$SYNO_TOTP_SECRET" ]; then
 | 
			
		||||
    _info "WARNING: Usage of SYNO_TOTP_SECRET is deprecated!"
 | 
			
		||||
    _info "         See synology_dsm.sh script or ACME.sh Wiki page for details:"
 | 
			
		||||
    _info "         https://github.com/acmesh-official/acme.sh/wiki/Synology-NAS-Guide"
 | 
			
		||||
    if ! _exists oathtool; then
 | 
			
		||||
      _err "oathtool could not be found, install oathtool to use SYNO_TOTP_SECRET"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    DEPRECATED_otp_code="$(oathtool --base32 --totp "$SYNO_TOTP_SECRET" 2>/dev/null)"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$SYNO_DEVICE_ID" ]; then
 | 
			
		||||
      _getdeployconf SYNO_DID
 | 
			
		||||
      [ -n "$SYNO_DID" ] || SYNO_DEVICE_ID="$SYNO_DID"
 | 
			
		||||
    fi
 | 
			
		||||
    if [ -n "$SYNO_DEVICE_ID" ]; then
 | 
			
		||||
      _H1="Cookie: did=$SYNO_DEVICE_ID"
 | 
			
		||||
      export _H1
 | 
			
		||||
      _debug3 H1 "${_H1}"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$DEPRECATED_otp_code&device_name=certrenewal&device_id=$SYNO_DEVICE_ID" "$_base_url/webapi/$api_path?enable_syno_token=yes")
 | 
			
		||||
    _debug3 response "$response"
 | 
			
		||||
  # ## END ## - DEPRECATED, for backward compatibility
 | 
			
		||||
  # If SYNO_DEVICE_ID or SYNO_OTP_CODE is set, we treat current account enabled 2FA-OTP.
 | 
			
		||||
  # Notice that if SYNO_USE_TEMP_ADMIN=1, both variables will be unset
 | 
			
		||||
  else
 | 
			
		||||
    if [ -n "$SYNO_DEVICE_ID" ] || [ -n "$SYNO_OTP_CODE" ]; then
 | 
			
		||||
      response='{"error":{"code":403}}'
 | 
			
		||||
    # Assume the current account disabled 2FA-OTP, try to log in right away.
 | 
			
		||||
    else
 | 
			
		||||
      if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
 | 
			
		||||
        _getdeployconf SYNO_LOCAL_HOSTNAME
 | 
			
		||||
        _debug SYNO_LOCAL_HOSTNAME "${SYNO_LOCAL_HOSTNAME:-}"
 | 
			
		||||
        if [ "$SYNO_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
 | 
			
		||||
 | 
			
		||||
  error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
 | 
			
		||||
  _debug2 error_code "$error_code"
 | 
			
		||||
  # Account has 2FA-OTP enabled, since error 403 reported.
 | 
			
		||||
  # https://global.download.synology.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu/DSM_Login_Web_API_Guide_enu.pdf
 | 
			
		||||
  if [ "$error_code" == "403" ]; then
 | 
			
		||||
    if [ -z "$SYNO_DEVICE_NAME" ]; then
 | 
			
		||||
      printf "Enter device name or leave empty for default (CertRenewal): "
 | 
			
		||||
      read -r SYNO_DEVICE_NAME
 | 
			
		||||
      [ -n "$SYNO_DEVICE_NAME" ] || SYNO_DEVICE_NAME="CertRenewal"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if [ -n "$SYNO_DEVICE_ID" ]; then
 | 
			
		||||
      # Omit OTP code with SYNO_DEVICE_ID.
 | 
			
		||||
      response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_DEVICE_NAME&device_id=$SYNO_DEVICE_ID")
 | 
			
		||||
      _secure_debug3 response "$response"
 | 
			
		||||
    else
 | 
			
		||||
      # Require the OTP code if still unset.
 | 
			
		||||
      if [ -z "$SYNO_OTP_CODE" ]; then
 | 
			
		||||
        printf "Enter OTP code for user '%s': " "$SYNO_USERNAME"
 | 
			
		||||
        read -r SYNO_OTP_CODE
 | 
			
		||||
      fi
 | 
			
		||||
      _secure_debug SYNO_OTP_CODE "${SYNO_OTP_CODE:-}"
 | 
			
		||||
 | 
			
		||||
      if [ -z "$SYNO_OTP_CODE" ]; then
 | 
			
		||||
        response='{"error":{"code":404}}'
 | 
			
		||||
      else
 | 
			
		||||
        response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_DEVICE_NAME&otp_code=$SYNO_OTP_CODE")
 | 
			
		||||
        _secure_debug3 response "$response"
 | 
			
		||||
 | 
			
		||||
        id_property='device_id'
 | 
			
		||||
        [ "${api_version}" -gt '6' ] || id_property='did'
 | 
			
		||||
        SYNO_DEVICE_ID=$(echo "$response" | grep "$id_property" | sed -n 's/.*"'$id_property'" *: *"\([^"]*\).*/\1/p')
 | 
			
		||||
        _secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
    error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
 | 
			
		||||
    _debug2 error_code "$error_code"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$error_code" ]; then
 | 
			
		||||
    if [ "$error_code" == "403" ] && [ -n "$SYNO_DEVICE_ID" ]; then
 | 
			
		||||
      _cleardeployconf SYNO_DEVICE_ID
 | 
			
		||||
      _err "Failed to authenticate with SYNO_DEVICE_ID (may expired or invalid), please try again in a new terminal window."
 | 
			
		||||
    elif [ "$error_code" == "404" ]; then
 | 
			
		||||
      _err "Failed to authenticate with provided 2FA-OTP code, please try again in a new terminal window."
 | 
			
		||||
    elif [ "$error_code" == "406" ]; then
 | 
			
		||||
      if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
 | 
			
		||||
        _err "Failed with unexcepted error, please report this by providing full log with '--debug 3'."
 | 
			
		||||
      else
 | 
			
		||||
        _err "Enforce auth with 2FA-OTP enabled, please configure the user to enable 2FA-OTP to continue."
 | 
			
		||||
      fi
 | 
			
		||||
    elif [ "$error_code" == "400" ]; then
 | 
			
		||||
      _err "Failed to authenticate, no such account or incorrect password."
 | 
			
		||||
    elif [ "$error_code" == "401" ]; then
 | 
			
		||||
      _err "Failed to authenticate with a non-existent account."
 | 
			
		||||
    elif [ "$error_code" == "408" ] || [ "$error_code" == "409" ] || [ "$error_code" == "410" ]; then
 | 
			
		||||
      _err "Failed to authenticate, the account password has expired or must be changed."
 | 
			
		||||
    else
 | 
			
		||||
      _err "Failed to authenticate with error: $error_code."
 | 
			
		||||
    fi
 | 
			
		||||
    _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  sid=$(echo "$response" | grep "sid" | sed -n 's/.*"sid" *: *"\([^"]*\).*/\1/p')
 | 
			
		||||
  token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
 | 
			
		||||
  _debug "Session ID" "$sid"
 | 
			
		||||
  _debug SynoToken "$token"
 | 
			
		||||
  if [ -z "$sid" ] || [ -z "$token" ]; then
 | 
			
		||||
    # Still can't get necessary info even got no errors, may Synology have API updated?
 | 
			
		||||
    _err "Unable to authenticate to $_base_url, you may report this by providing full log with '--debug 3'."
 | 
			
		||||
    _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _H1="X-SYNO-TOKEN: $token"
 | 
			
		||||
  export _H1
 | 
			
		||||
  _debug2 H1 "${_H1}"
 | 
			
		||||
 | 
			
		||||
  # Now that we know the username and password are good, save them if not in temp admin mode.
 | 
			
		||||
  if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
 | 
			
		||||
    _cleardeployconf SYNO_USERNAME
 | 
			
		||||
    _cleardeployconf SYNO_PASSWORD
 | 
			
		||||
    _cleardeployconf SYNO_DEVICE_ID
 | 
			
		||||
    _cleardeployconf SYNO_DEVICE_NAME
 | 
			
		||||
    _savedeployconf SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
 | 
			
		||||
    _savedeployconf SYNO_LOCAL_HOSTNAME "$SYNO_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..."
 | 
			
		||||
  response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1&_sid=$sid" "$_base_url/webapi/entry.cgi")
 | 
			
		||||
  _debug3 response "$response"
 | 
			
		||||
  escaped_certificate="$(printf "%s" "$SYNO_CERTIFICATE" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')"
 | 
			
		||||
  _debug escaped_certificate "$escaped_certificate"
 | 
			
		||||
  id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p")
 | 
			
		||||
  _debug2 id "$id"
 | 
			
		||||
 | 
			
		||||
  error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
 | 
			
		||||
  _debug2 error_code "$error_code"
 | 
			
		||||
  if [ -n "$error_code" ]; then
 | 
			
		||||
    if [ "$error_code" -eq 105 ]; then
 | 
			
		||||
      _err "Current user is not administrator and does not have sufficient permission for deploying."
 | 
			
		||||
    else
 | 
			
		||||
      _err "Failed to fetch certificate info: $error_code, please try again or contact Synology to learn more."
 | 
			
		||||
    fi
 | 
			
		||||
    _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _migratedeployconf SYNO_Create SYNO_CREATE
 | 
			
		||||
  _getdeployconf SYNO_CREATE
 | 
			
		||||
  _debug2 SYNO_CREATE "$SYNO_CREATE"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$id" ] && [ -z "$SYNO_CREATE" ]; then
 | 
			
		||||
    _err "Unable to find certificate: $SYNO_CERTIFICATE and $SYNO_CREATE is not set."
 | 
			
		||||
    _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # We've verified this certificate description is a thing, so save it
 | 
			
		||||
  _savedeployconf SYNO_CERTIFICATE "$SYNO_CERTIFICATE" "base64"
 | 
			
		||||
 | 
			
		||||
  _info "Generating form POST request..."
 | 
			
		||||
  nl="\0015\0012"
 | 
			
		||||
  delim="--------------------------$(_utc_date | tr -d -- '-: ')"
 | 
			
		||||
  content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\0012"
 | 
			
		||||
  content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\0012"
 | 
			
		||||
  content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012"
 | 
			
		||||
  content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id"
 | 
			
		||||
  content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_CERTIFICATE}"
 | 
			
		||||
  if echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then
 | 
			
		||||
    _debug2 default "This is the default certificate"
 | 
			
		||||
    content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}true"
 | 
			
		||||
  else
 | 
			
		||||
    _debug2 default "This is NOT the default certificate"
 | 
			
		||||
  fi
 | 
			
		||||
  content="$content${nl}--$delim--${nl}"
 | 
			
		||||
  content="$(printf "%b_" "$content")"
 | 
			
		||||
  content="${content%_}" # protect trailing \n
 | 
			
		||||
 | 
			
		||||
  _info "Upload certificate to the Synology DSM."
 | 
			
		||||
  response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token&_sid=$sid" "" "POST" "multipart/form-data; boundary=${delim}")
 | 
			
		||||
  _debug3 response "$response"
 | 
			
		||||
 | 
			
		||||
  if ! echo "$response" | grep '"error":' >/dev/null; then
 | 
			
		||||
    if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
 | 
			
		||||
      _info "Restart HTTP services succeeded."
 | 
			
		||||
    else
 | 
			
		||||
      _info "Restart HTTP services failed."
 | 
			
		||||
    fi
 | 
			
		||||
    _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
 | 
			
		||||
    _logout
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
 | 
			
		||||
    _err "Unable to update certificate, got error response: $response."
 | 
			
		||||
    _logout
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
_logout() {
 | 
			
		||||
  # Logout CERT user only to not occupy a permanent session, e.g. in DSM's "Connected Users" widget (based on previous variables)
 | 
			
		||||
  response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=logout&_sid=$sid")
 | 
			
		||||
  _debug3 response "$response"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_temp_admin_create() {
 | 
			
		||||
  _username="$1"
 | 
			
		||||
  _password="$2"
 | 
			
		||||
  synouser --del "$_username" >/dev/null 2>/dev/null
 | 
			
		||||
  synouser --add "$_username" "$_password" "" 0 "" 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										272
									
								
								deploy/truenas.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								deploy/truenas.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,272 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is a scipt to deploy the cert to your TrueNAS using the REST API.
 | 
			
		||||
# https://www.truenas.com/docs/hub/additional-topics/api/rest_api.html
 | 
			
		||||
#
 | 
			
		||||
# Written by Frank Plass github@f-plass.de
 | 
			
		||||
# https://github.com/danb35/deploy-freenas/blob/master/deploy_freenas.py
 | 
			
		||||
# Thanks to danb35 for your template!
 | 
			
		||||
#
 | 
			
		||||
# Following environment variables must be set:
 | 
			
		||||
#
 | 
			
		||||
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI>"
 | 
			
		||||
#
 | 
			
		||||
# The following environmental variables may be set if you don't like their
 | 
			
		||||
# default values:
 | 
			
		||||
#
 | 
			
		||||
# DEPLOY_TRUENAS_HOSTNAME - defaults to localhost
 | 
			
		||||
# DEPLOY_TRUENAS_SCHEME - defaults to http, set alternatively to https
 | 
			
		||||
#
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
truenas_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_TRUENAS_APIKEY
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
 | 
			
		||||
    _err "TrueNAS API key not found, please set the DEPLOY_TRUENAS_APIKEY environment variable."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _secure_debug2 DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
 | 
			
		||||
 | 
			
		||||
  # Optional hostname, scheme for TrueNAS
 | 
			
		||||
  _getdeployconf DEPLOY_TRUENAS_HOSTNAME
 | 
			
		||||
  _getdeployconf DEPLOY_TRUENAS_SCHEME
 | 
			
		||||
 | 
			
		||||
  # default values for hostname and scheme
 | 
			
		||||
  [ -n "${DEPLOY_TRUENAS_HOSTNAME}" ] || DEPLOY_TRUENAS_HOSTNAME="localhost"
 | 
			
		||||
  [ -n "${DEPLOY_TRUENAS_SCHEME}" ] || DEPLOY_TRUENAS_SCHEME="http"
 | 
			
		||||
 | 
			
		||||
  _debug2 DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
 | 
			
		||||
  _debug2 DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
 | 
			
		||||
 | 
			
		||||
  _api_url="$DEPLOY_TRUENAS_SCHEME://$DEPLOY_TRUENAS_HOSTNAME/api/v2.0"
 | 
			
		||||
  _debug _api_url "$_api_url"
 | 
			
		||||
 | 
			
		||||
  _H1="Authorization: Bearer $DEPLOY_TRUENAS_APIKEY"
 | 
			
		||||
  _secure_debug3 _H1 "$_H1"
 | 
			
		||||
 | 
			
		||||
  _info "Testing Connection TrueNAS"
 | 
			
		||||
  _response=$(_get "$_api_url/system/state")
 | 
			
		||||
  _info "TrueNAS system state: $_response."
 | 
			
		||||
 | 
			
		||||
  _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.'
 | 
			
		||||
    _err 'DEPLOY_TRUENAS_HOSTNAME="192.168.x.y" or DEPLOY_TRUENAS_HOSTNAME="truenas.example.com".'
 | 
			
		||||
    _err 'DEPLOY_TRUENAS_SCHEME="https" or DEPLOY_TRUENAS_SCHEME="http".'
 | 
			
		||||
    _err "Verify your TrueNAS API key is valid and set correctly, e.g. DEPLOY_TRUENAS_APIKEY=xxxx...."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
 | 
			
		||||
  _savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
 | 
			
		||||
  _savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
 | 
			
		||||
 | 
			
		||||
  _info "Getting current active certificate from TrueNAS"
 | 
			
		||||
  _response=$(_get "$_api_url/system/general")
 | 
			
		||||
  _active_cert_id=$(echo "$_response" | grep -B2 '"name":' | grep 'id' | tr -d -- '"id: ,')
 | 
			
		||||
  _active_cert_name=$(echo "$_response" | grep '"name":' | sed -n 's/.*: "\(.\{1,\}\)",$/\1/p')
 | 
			
		||||
  _param_httpsredirect=$(echo "$_response" | grep '"ui_httpsredirect":' | sed -n 's/.*": \(.\{1,\}\),$/\1/p')
 | 
			
		||||
  _debug Active_UI_Certificate_ID "$_active_cert_id"
 | 
			
		||||
  _debug Active_UI_Certificate_Name "$_active_cert_name"
 | 
			
		||||
  _debug Active_UI_http_redirect "$_param_httpsredirect"
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_TRUENAS_SCHEME" = "http" ] && [ "$_param_httpsredirect" = "true" ]; then
 | 
			
		||||
    _info "HTTP->HTTPS redirection is enabled"
 | 
			
		||||
    _info "Setting DEPLOY_TRUENAS_SCHEME to 'https'"
 | 
			
		||||
    DEPLOY_TRUENAS_SCHEME="https"
 | 
			
		||||
    _api_url="$DEPLOY_TRUENAS_SCHEME://$DEPLOY_TRUENAS_HOSTNAME/api/v2.0"
 | 
			
		||||
    _savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Uploading new certificate to TrueNAS"
 | 
			
		||||
  _certname="Letsencrypt_$(_utc_date | tr ' ' '_' | tr -d -- ':')"
 | 
			
		||||
  _debug3 _certname "$_certname"
 | 
			
		||||
 | 
			
		||||
  _certData="{\"create_type\": \"CERTIFICATE_CREATE_IMPORTED\", \"name\": \"${_certname}\", \"certificate\": \"$(_json_encode <"$_cfullchain")\", \"privatekey\": \"$(_json_encode <"$_ckey")\"}"
 | 
			
		||||
  _add_cert_result="$(_post "$_certData" "$_api_url/certificate" "" "POST" "application/json")"
 | 
			
		||||
 | 
			
		||||
  _debug3 _add_cert_result "$_add_cert_result"
 | 
			
		||||
 | 
			
		||||
  _info "Fetching list of installed certificates"
 | 
			
		||||
  _cert_list=$(_get "$_api_url/system/general/ui_certificate_choices")
 | 
			
		||||
  _cert_id=$(echo "$_cert_list" | grep "$_certname" | sed -n 's/.*"\([0-9]\{1,\}\)".*$/\1/p')
 | 
			
		||||
 | 
			
		||||
  _debug3 _cert_id "$_cert_id"
 | 
			
		||||
 | 
			
		||||
  _info "Current activate certificate ID: $_cert_id"
 | 
			
		||||
  _activateData="{\"ui_certificate\": \"${_cert_id}\"}"
 | 
			
		||||
  _activate_result="$(_post "$_activateData" "$_api_url/system/general" "" "PUT" "application/json")"
 | 
			
		||||
 | 
			
		||||
  _debug3 _activate_result "$_activate_result"
 | 
			
		||||
 | 
			
		||||
  _truenas_version_23_10="23.10"
 | 
			
		||||
  _truenas_version_24_10="24.10"
 | 
			
		||||
 | 
			
		||||
  _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"
 | 
			
		||||
    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
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Checking if FTP certificate is the same as the TrueNAS web UI"
 | 
			
		||||
  _ftp_list=$(_get "$_api_url/ftp")
 | 
			
		||||
  _ftp_cert_id=$(echo "$_ftp_list" | grep '"ssltls_certificate":' | tr -d -- '"certislfa:_ ,')
 | 
			
		||||
 | 
			
		||||
  if [ "$_ftp_cert_id" = "$_active_cert_id" ]; then
 | 
			
		||||
    _info "Updating the FTP certificate"
 | 
			
		||||
    _debug _ftp_cert_id "$_ftp_cert_id"
 | 
			
		||||
    _ftp_data="{\"ssltls_certificate\": \"${_cert_id}\"}"
 | 
			
		||||
    _activate_ftp_cert="$(_post "$_ftp_data" "$_api_url/ftp" "" "PUT" "application/json")"
 | 
			
		||||
    _ftp_new_cert_id=$(echo "$_activate_ftp_cert" | _json_decode | grep '"ssltls_certificate":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
 | 
			
		||||
    if [ "$_ftp_new_cert_id" -eq "$_cert_id" ]; then
 | 
			
		||||
      _info "FTP certificate updated successfully"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Unable to set FTP certificate"
 | 
			
		||||
      _debug3 _activate_ftp_cert "$_activate_ftp_cert"
 | 
			
		||||
      _debug3 _ftp_new_cert_id "$_ftp_new_cert_id"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _debug3 _activate_ftp_cert "$_activate_ftp_cert"
 | 
			
		||||
  else
 | 
			
		||||
    _info "FTP certificate is not configured or is not the same as TrueNAS web UI"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Deleting old certificate"
 | 
			
		||||
  _delete_result="$(_post "" "$_api_url/certificate/id/$_active_cert_id" "" "DELETE" "application/json")"
 | 
			
		||||
 | 
			
		||||
  _debug3 _delete_result "$_delete_result"
 | 
			
		||||
 | 
			
		||||
  _info "Reloading TrueNAS web UI"
 | 
			
		||||
  _restart_UI=$(_get "$_api_url/system/general/ui_restart")
 | 
			
		||||
  _debug2 _restart_UI "$_restart_UI"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$_add_cert_result" ] && [ -n "$_activate_result" ]; then
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Certificate update was not succesful, please try again with --debug"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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!"
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										305
									
								
								deploy/unifi.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								deploy/unifi.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,305 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is a script to deploy cert on a Unifi Controller or Cloud Key device.
 | 
			
		||||
# It supports:
 | 
			
		||||
#   - self-hosted Unifi Controller
 | 
			
		||||
#   - Unifi Cloud Key (Gen1/2/2+)
 | 
			
		||||
#   - Unifi Cloud Key running UnifiOS (v2.0.0+, Gen2/2+ only)
 | 
			
		||||
#   - Unifi Dream Machine
 | 
			
		||||
#       This has not been tested on other "all-in-one" devices such as
 | 
			
		||||
#       UDM Pro or Unifi Express.
 | 
			
		||||
#
 | 
			
		||||
#       OS Version v2.0.0+
 | 
			
		||||
#       Network Application version 7.0.0+
 | 
			
		||||
#       OS version ~3.1 removed java and keytool from the UnifiOS.
 | 
			
		||||
#       Using PKCS12 format keystore appears to work fine.
 | 
			
		||||
#
 | 
			
		||||
# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3359
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
# The deploy-hook automatically detects standard Unifi installations
 | 
			
		||||
# for each of the supported environments. Most users should not need
 | 
			
		||||
# to set any of these variables, but if you are running a self-hosted
 | 
			
		||||
# Controller with custom locations, set these as necessary before running
 | 
			
		||||
# the deploy hook. (Defaults shown below.)
 | 
			
		||||
#
 | 
			
		||||
# Settings for Unifi Controller:
 | 
			
		||||
# Location of Java keystore or unifi.keystore.jks file:
 | 
			
		||||
#DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore"
 | 
			
		||||
# Keystore password (built into Unifi Controller, not a user-set password):
 | 
			
		||||
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
 | 
			
		||||
# Command to restart Unifi Controller:
 | 
			
		||||
# 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:
 | 
			
		||||
#DEPLOY_UNIFI_CLOUDKEY_CERTDIR="/etc/ssl/private"
 | 
			
		||||
# Command to restart maintenance pages and Controller
 | 
			
		||||
# (same setting as above, default is updated when running on Cloud Key Gen1):
 | 
			
		||||
#DEPLOY_UNIFI_RELOAD="service nginx restart && service unifi restart"
 | 
			
		||||
#
 | 
			
		||||
# Settings for UnifiOS (Cloud Key Gen2):
 | 
			
		||||
# Directory where unifi-core.crt and unifi-core.key live:
 | 
			
		||||
#DEPLOY_UNIFI_CORE_CONFIG="/data/unifi-core/config/"
 | 
			
		||||
# Command to restart unifi-core:
 | 
			
		||||
# DEPLOY_UNIFI_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.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
unifi_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_UNIFI_KEYSTORE
 | 
			
		||||
  _getdeployconf DEPLOY_UNIFI_KEYPASS
 | 
			
		||||
  _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=""
 | 
			
		||||
 | 
			
		||||
  # Default reload commands accumulated as we auto-detect environments:
 | 
			
		||||
  _reload_cmd=""
 | 
			
		||||
 | 
			
		||||
  # Unifi Controller environment (self hosted or any Cloud Key) --
 | 
			
		||||
  # auto-detect by file /usr/lib/unifi/data/keystore
 | 
			
		||||
  _unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-/usr/lib/unifi/data/keystore}"
 | 
			
		||||
  if [ -f "$_unifi_keystore" ]; then
 | 
			
		||||
    _debug _unifi_keystore "$_unifi_keystore"
 | 
			
		||||
    if ! _exists keytool; then
 | 
			
		||||
      _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."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-aircontrolenterprise}"
 | 
			
		||||
 | 
			
		||||
    _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
 | 
			
		||||
      _err "Error generating pkcs12. Please re-run with --debug and report a bug."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # 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
 | 
			
		||||
      _debug "Copying new keystore to $_unifi_keystore"
 | 
			
		||||
      cp -f "$_import_pkcs12" "$_unifi_keystore"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # 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!"
 | 
			
		||||
  elif [ "$DEPLOY_UNIFI_KEYSTORE" ]; then
 | 
			
		||||
    _err "The specified DEPLOY_UNIFI_KEYSTORE='$DEPLOY_UNIFI_KEYSTORE' is not valid, please check."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Cloud Key environment (non-UnifiOS -- nginx serves admin pages) --
 | 
			
		||||
  # auto-detect by file /etc/ssl/private/cloudkey.key:
 | 
			
		||||
  _cloudkey_certdir="${DEPLOY_UNIFI_CLOUDKEY_CERTDIR:-/etc/ssl/private}"
 | 
			
		||||
  if [ -f "${_cloudkey_certdir}/cloudkey.key" ]; then
 | 
			
		||||
    _info "Installing certificate for Cloud Key Gen1 (nginx admin pages)"
 | 
			
		||||
    _debug _cloudkey_certdir "$_cloudkey_certdir"
 | 
			
		||||
    if [ ! -w "$_cloudkey_certdir" ]; then
 | 
			
		||||
      _err "The directory $_cloudkey_certdir is not writable; please check permissions."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    # Cloud Key expects to load the keystore from /etc/ssl/private/unifi.keystore.jks.
 | 
			
		||||
    # 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)
 | 
			
		||||
 | 
			
		||||
    if systemctl -q is-active nginx; then
 | 
			
		||||
      _reload_cmd="${_reload_cmd:+$_reload_cmd && }service nginx restart"
 | 
			
		||||
    fi
 | 
			
		||||
    _info "Install Cloud Key Gen1 certificate success!"
 | 
			
		||||
    _services_updated="${_services_updated} nginx"
 | 
			
		||||
  elif [ "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR" ]; then
 | 
			
		||||
    _err "The specified DEPLOY_UNIFI_CLOUDKEY_CERTDIR='$DEPLOY_UNIFI_CLOUDKEY_CERTDIR' is not valid, please check."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # UnifiOS environment -- auto-detect by /data/unifi-core/config/unifi-core.key:
 | 
			
		||||
  _unifi_core_config="${DEPLOY_UNIFI_CORE_CONFIG:-/data/unifi-core/config}"
 | 
			
		||||
  if [ -f "${_unifi_core_config}/unifi-core.key" ]; then
 | 
			
		||||
    _info "Installing certificate for UnifiOS"
 | 
			
		||||
    _debug _unifi_core_config "$_unifi_core_config"
 | 
			
		||||
    if [ ! -w "$_unifi_core_config" ]; then
 | 
			
		||||
      _err "The directory $_unifi_core_config is not writable; please check permissions."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # Save the existing certs in case something goes wrong.
 | 
			
		||||
    cp -f "${_unifi_core_config}"/unifi-core.crt "${_unifi_core_config}"/unifi-core_original.crt
 | 
			
		||||
    cp -f "${_unifi_core_config}"/unifi-core.key "${_unifi_core_config}"/unifi-core_original.key
 | 
			
		||||
    _info "Previous certificate and key saved to ${_unifi_core_config}/unifi-core_original.crt.key."
 | 
			
		||||
 | 
			
		||||
    cat "$_cfullchain" >"${_unifi_core_config}/unifi-core.crt"
 | 
			
		||||
    cat "$_ckey" >"${_unifi_core_config}/unifi-core.key"
 | 
			
		||||
 | 
			
		||||
    _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
 | 
			
		||||
    _err "The specified DEPLOY_UNIFI_CORE_CONFIG='$DEPLOY_UNIFI_CORE_CONFIG' is not valid, please check."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_services_updated" ]; then
 | 
			
		||||
    # None of the Unifi environments were auto-detected, so no deployment has occurred
 | 
			
		||||
    # (and none of DEPLOY_UNIFI_{KEYSTORE,CLOUDKEY_CERTDIR,CORE_CONFIG} were set).
 | 
			
		||||
    _err "Unable to detect Unifi environment in standard location."
 | 
			
		||||
    _err "(This deploy hook must be run on the Unifi device, not a remote machine.)"
 | 
			
		||||
    _err "For non-standard Unifi installations, set DEPLOY_UNIFI_KEYSTORE,"
 | 
			
		||||
    _err "DEPLOY_UNIFI_CLOUDKEY_CERTDIR, and/or DEPLOY_UNIFI_CORE_CONFIG as appropriate."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _reload_cmd="${DEPLOY_UNIFI_RELOAD:-$_reload_cmd}"
 | 
			
		||||
  if [ -z "$_reload_cmd" ]; then
 | 
			
		||||
    _err "Certificates were installed for services:${_services_updated},"
 | 
			
		||||
    _err "but none appear to be active. Please set DEPLOY_UNIFI_RELOAD"
 | 
			
		||||
    _err "to a command that will restart the necessary services."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _info "Reload services (this may take some time): $_reload_cmd"
 | 
			
		||||
  if eval "$_reload_cmd"; then
 | 
			
		||||
    _info "Reload success!"
 | 
			
		||||
  else
 | 
			
		||||
    _err "Reload error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Successful, so save all (non-default) config:
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_CLOUDKEY_CERTDIR "$DEPLOY_UNIFI_CLOUDKEY_CERTDIR"
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_CORE_CONFIG "$DEPLOY_UNIFI_CORE_CONFIG"
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_OS_RELOAD "$DEPLOY_UNIFI_OS_RELOAD"
 | 
			
		||||
  _savedeployconf DEPLOY_UNIFI_SYSTEM_PROPERTIES "$DEPLOY_UNIFI_SYSTEM_PROPERTIES"
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										201
									
								
								deploy/vault.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								deploy/vault.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,201 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is a script to deploy cert to hashicorp vault using curl
 | 
			
		||||
# (https://www.vaultproject.io/)
 | 
			
		||||
#
 | 
			
		||||
# it requires following environment variables:
 | 
			
		||||
#
 | 
			
		||||
# VAULT_PREFIX - this contains the prefix path in vault
 | 
			
		||||
# VAULT_ADDR - vault requires this to find your vault server
 | 
			
		||||
# VAULT_SAVE_TOKEN - set to anything if you want to save the token
 | 
			
		||||
# VAULT_RENEW_TOKEN - set to anything if you want to renew the token to default TTL before deploying
 | 
			
		||||
# VAULT_KV_V2 - set to anything if you are using v2 of the kv engine
 | 
			
		||||
#
 | 
			
		||||
# additionally, you need to ensure that VAULT_TOKEN is avialable
 | 
			
		||||
# to access the vault server
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
######## Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
vault_deploy() {
 | 
			
		||||
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # validate required env vars
 | 
			
		||||
  _getdeployconf VAULT_PREFIX
 | 
			
		||||
  if [ -z "$VAULT_PREFIX" ]; then
 | 
			
		||||
    _err "VAULT_PREFIX needs to be defined (contains prefix path in vault)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf VAULT_PREFIX "$VAULT_PREFIX"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_ADDR
 | 
			
		||||
  if [ -z "$VAULT_ADDR" ]; then
 | 
			
		||||
    _err "VAULT_ADDR needs to be defined (contains vault connection address)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf VAULT_ADDR "$VAULT_ADDR"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_SAVE_TOKEN
 | 
			
		||||
  _savedeployconf VAULT_SAVE_TOKEN "$VAULT_SAVE_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_RENEW_TOKEN
 | 
			
		||||
  _savedeployconf VAULT_RENEW_TOKEN "$VAULT_RENEW_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_KV_V2
 | 
			
		||||
  _savedeployconf VAULT_KV_V2 "$VAULT_KV_V2"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_TOKEN
 | 
			
		||||
  if [ -z "$VAULT_TOKEN" ]; then
 | 
			
		||||
    _err "VAULT_TOKEN needs to be defined"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -n "$VAULT_SAVE_TOKEN" ]; then
 | 
			
		||||
    _savedeployconf VAULT_TOKEN "$VAULT_TOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _migratedeployconf FABIO VAULT_FABIO_MODE
 | 
			
		||||
 | 
			
		||||
  # JSON does not allow multiline strings.
 | 
			
		||||
  # So replacing new-lines with "\n" here
 | 
			
		||||
  _ckey=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$2")
 | 
			
		||||
  _ccert=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$3")
 | 
			
		||||
  _cca=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$4")
 | 
			
		||||
  _cfullchain=$(sed -e ':a' -e N -e '$ ! ba' -e 's/\n/\\n/g' <"$5")
 | 
			
		||||
 | 
			
		||||
  export _H1="X-Vault-Token: $VAULT_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$VAULT_RENEW_TOKEN" ]; then
 | 
			
		||||
    URL="$VAULT_ADDR/v1/auth/token/renew-self"
 | 
			
		||||
    _info "Renew the Vault token to default TTL"
 | 
			
		||||
    _response=$(_post "" "$URL")
 | 
			
		||||
    if [ "$?" != "0" ]; then
 | 
			
		||||
      _err "Failed to renew the Vault token"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										104
									
								
								deploy/vault_cli.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								deploy/vault_cli.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is a script to deploy cert to hashicorp vault
 | 
			
		||||
# (https://www.vaultproject.io/)
 | 
			
		||||
#
 | 
			
		||||
# it requires the vault binary to be available in PATH, and the following
 | 
			
		||||
# environment variables:
 | 
			
		||||
#
 | 
			
		||||
# VAULT_PREFIX - this contains the prefix path in vault
 | 
			
		||||
# VAULT_ADDR - vault requires this to find your vault server
 | 
			
		||||
# VAULT_SAVE_TOKEN - set to anything if you want to save the token
 | 
			
		||||
# VAULT_RENEW_TOKEN - set to anything if you want to renew the token to default TTL before deploying
 | 
			
		||||
#
 | 
			
		||||
# additionally, you need to ensure that VAULT_TOKEN is avialable or
 | 
			
		||||
# `vault auth` has applied the appropriate authorization for the vault binary
 | 
			
		||||
# to access the vault server
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
vault_cli_deploy() {
 | 
			
		||||
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # validate required env vars
 | 
			
		||||
  _getdeployconf VAULT_PREFIX
 | 
			
		||||
  if [ -z "$VAULT_PREFIX" ]; then
 | 
			
		||||
    _err "VAULT_PREFIX needs to be defined (contains prefix path in vault)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf VAULT_PREFIX "$VAULT_PREFIX"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_ADDR
 | 
			
		||||
  if [ -z "$VAULT_ADDR" ]; then
 | 
			
		||||
    _err "VAULT_ADDR needs to be defined (contains vault connection address)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf VAULT_ADDR "$VAULT_ADDR"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_SAVE_TOKEN
 | 
			
		||||
  _savedeployconf VAULT_SAVE_TOKEN "$VAULT_SAVE_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_RENEW_TOKEN
 | 
			
		||||
  _savedeployconf VAULT_RENEW_TOKEN "$VAULT_RENEW_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_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
 | 
			
		||||
    _err "cannot find vault binary!"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
    _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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								deploy/vsftpd.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								deploy/vsftpd.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Here is a script to deploy cert to vsftpd server.
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
#DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf"
 | 
			
		||||
#DEPLOY_VSFTPD_RELOAD="service vsftpd restart"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
vsftpd_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"
 | 
			
		||||
 | 
			
		||||
  _ssl_path="/etc/acme.sh/vsftpd"
 | 
			
		||||
  if ! mkdir -p "$_ssl_path"; then
 | 
			
		||||
    _err "Can not create folder:$_ssl_path"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Copying key and cert"
 | 
			
		||||
  _real_key="$_ssl_path/vsftpd.key"
 | 
			
		||||
  if ! cat "$_ckey" >"$_real_key"; then
 | 
			
		||||
    _err "Error: write key file to: $_real_key"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _real_fullchain="$_ssl_path/vsftpd.chain.pem"
 | 
			
		||||
  if ! cat "$_cfullchain" >"$_real_fullchain"; then
 | 
			
		||||
    _err "Error: write key file to: $_real_fullchain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  DEFAULT_VSFTPD_RELOAD="service vsftpd restart"
 | 
			
		||||
  _reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$IS_RENEW" ]; then
 | 
			
		||||
    DEFAULT_VSFTPD_CONF="/etc/vsftpd.conf"
 | 
			
		||||
    _vsftpd_conf="${DEPLOY_VSFTPD_CONF:-$DEFAULT_VSFTPD_CONF}"
 | 
			
		||||
    if [ ! -f "$_vsftpd_conf" ]; then
 | 
			
		||||
      if [ -z "$DEPLOY_VSFTPD_CONF" ]; then
 | 
			
		||||
        _err "vsftpd conf is not found, please define DEPLOY_VSFTPD_CONF"
 | 
			
		||||
        return 1
 | 
			
		||||
      else
 | 
			
		||||
        _err "It seems that the specified vsftpd conf is not valid, please check."
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
    if [ ! -w "$_vsftpd_conf" ]; then
 | 
			
		||||
      _err "The file $_vsftpd_conf is not writable, please change the permission."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak"
 | 
			
		||||
    _info "Backup $_vsftpd_conf to $_backup_conf"
 | 
			
		||||
    cp "$_vsftpd_conf" "$_backup_conf"
 | 
			
		||||
 | 
			
		||||
    _info "Modify vsftpd conf: $_vsftpd_conf"
 | 
			
		||||
    if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" &&
 | 
			
		||||
      _setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" &&
 | 
			
		||||
      _setopt "$_vsftpd_conf" "ssl_enable" "=" "YES"; then
 | 
			
		||||
      _info "Set config success!"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Config vsftpd server error, please report bug to us."
 | 
			
		||||
      _info "Restoring vsftpd conf"
 | 
			
		||||
      if cat "$_backup_conf" >"$_vsftpd_conf"; then
 | 
			
		||||
        _info "Restore conf success"
 | 
			
		||||
        eval "$_reload"
 | 
			
		||||
      else
 | 
			
		||||
        _err "Oops, error restore vsftpd conf, please report bug to us."
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Run reload: $_reload"
 | 
			
		||||
  if eval "$_reload"; then
 | 
			
		||||
    _info "Reload success!"
 | 
			
		||||
    if [ "$DEPLOY_VSFTPD_CONF" ]; then
 | 
			
		||||
      _savedomainconf DEPLOY_VSFTPD_CONF "$DEPLOY_VSFTPD_CONF"
 | 
			
		||||
    else
 | 
			
		||||
      _cleardomainconf DEPLOY_VSFTPD_CONF
 | 
			
		||||
    fi
 | 
			
		||||
    if [ "$DEPLOY_VSFTPD_RELOAD" ]; then
 | 
			
		||||
      _savedomainconf DEPLOY_VSFTPD_RELOAD "$DEPLOY_VSFTPD_RELOAD"
 | 
			
		||||
    else
 | 
			
		||||
      _cleardomainconf DEPLOY_VSFTPD_RELOAD
 | 
			
		||||
    fi
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Reload error, restoring"
 | 
			
		||||
    if cat "$_backup_conf" >"$_vsftpd_conf"; then
 | 
			
		||||
      _info "Restore conf success"
 | 
			
		||||
      eval "$_reload"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Oops, error restore vsftpd conf, please report bug to us."
 | 
			
		||||
    fi
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								dnsapi/README.md
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								dnsapi/README.md
									
									
									
									
									
								
							@@ -1,118 +1,6 @@
 | 
			
		||||
# How to use dns api
 | 
			
		||||
 | 
			
		||||
## Use CloudFlare domain api to automatically issue cert
 | 
			
		||||
 | 
			
		||||
For now, we support clourflare integeration.
 | 
			
		||||
 | 
			
		||||
First you need to login to your clourflare account to get your api key.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
 | 
			
		||||
export CF_Email="xxxx@sss.com"
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Ok, let's issue cert now:
 | 
			
		||||
```
 | 
			
		||||
acme.sh   --issue   --dns dns_cf   -d aa.com  -d www.aa.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `CF_Key` and `CF_Email`  will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key.
 | 
			
		||||
# How to use DNS API
 | 
			
		||||
DNS api usage:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Use Dnspod.cn domain api to automatically issue cert
 | 
			
		||||
 | 
			
		||||
For now, we support dnspod.cn integeration.
 | 
			
		||||
 | 
			
		||||
First you need to login to your dnspod.cn account to get your api key and key id.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
export DP_Id="1234"
 | 
			
		||||
 | 
			
		||||
export DP_Key="sADDsdasdgdsf"
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Ok, let's issue cert now:
 | 
			
		||||
```
 | 
			
		||||
acme.sh   --issue   --dns dns_dp   -d aa.com  -d www.aa.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `DP_Id` and `DP_Key`  will be saved in `~/.acme.sh/account.conf`, when next time you use dnspod.cn api, it will reuse this key.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Use Cloudxns.com domain api to automatically issue cert
 | 
			
		||||
 | 
			
		||||
For now, we support Cloudxns.com integeration.
 | 
			
		||||
 | 
			
		||||
First you need to login to your Cloudxns.com account to get your api key and key secret.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
export CX_Key="1234"
 | 
			
		||||
 | 
			
		||||
export CX_Secret="sADDsdasdgdsf"
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Ok, let's issue cert now:
 | 
			
		||||
```
 | 
			
		||||
acme.sh   --issue   --dns dns_cx   -d aa.com  -d www.aa.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `CX_Key` and `CX_Secret`  will be saved in `~/.acme.sh/account.conf`, when next time you use Cloudxns.com api, it will reuse this key.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Use Godaddy.com domain api to automatically issue cert
 | 
			
		||||
 | 
			
		||||
We support Godaddy integeration.
 | 
			
		||||
 | 
			
		||||
First you need to login to your Godaddy account to get your api key and api secret.
 | 
			
		||||
 | 
			
		||||
https://developer.godaddy.com/keys/
 | 
			
		||||
 | 
			
		||||
Please Create a Production key, instead of a Test key.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
export GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
 | 
			
		||||
export GD_Secret="asdfsdafdsfdsfdsfdsfdsafd"
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Ok, let's issue cert now:
 | 
			
		||||
```
 | 
			
		||||
acme.sh   --issue   --dns dns_gd   -d aa.com  -d www.aa.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `GD_Key` and `GD_Secret`  will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key.
 | 
			
		||||
 | 
			
		||||
## Use OVH/kimsufi/soyoustart/runabove API
 | 
			
		||||
 | 
			
		||||
https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api
 | 
			
		||||
 | 
			
		||||
# Use custom api
 | 
			
		||||
 | 
			
		||||
If your api is not supported yet,  you can write your own dns api.
 | 
			
		||||
 | 
			
		||||
Let's assume you want to name it 'myapi',
 | 
			
		||||
 | 
			
		||||
1. Create a bash script named  `~/.acme.sh/dns_myapi.sh`,
 | 
			
		||||
2. In the script, you must have a function named `dns_myapi_add()`. Which will be called by acme.sh to add dns records.
 | 
			
		||||
3. Then you can use your api to issue cert like:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
acme.sh  --issue  --dns  dns_myapi  -d aa.com  -d www.aa.com
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Use lexicon dns api
 | 
			
		||||
 | 
			
		||||
https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
 | 
			
		||||
 | 
			
		||||
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										268
									
								
								dnsapi/dns_1984hosting.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										268
									
								
								dnsapi/dns_1984hosting.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,268 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_1984hosting_info='1984.hosting
 | 
			
		||||
Domains: 1984.is
 | 
			
		||||
Site: 1984.hosting
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_1984hosting
 | 
			
		||||
Options:
 | 
			
		||||
 One984HOSTING_Username Username
 | 
			
		||||
 One984HOSTING_Password Password
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2851
 | 
			
		||||
Author: Adrian Fedoreanu
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
######## Public functions #####################
 | 
			
		||||
 | 
			
		||||
# Usage: dns_1984hosting_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# Add a text record.
 | 
			
		||||
dns_1984hosting_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _info "Add TXT record using 1984Hosting."
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  if ! _1984hosting_login; then
 | 
			
		||||
    _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone."
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Invalid domain '$fulldomain'."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Add TXT record $fulldomain with value '$txtvalue'."
 | 
			
		||||
  value="$(printf '%s' "$txtvalue" | _url_encode)"
 | 
			
		||||
  url="https://1984.hosting/domains/entry/"
 | 
			
		||||
 | 
			
		||||
  postdata="entry=new"
 | 
			
		||||
  postdata="$postdata&type=TXT"
 | 
			
		||||
  postdata="$postdata&ttl=900"
 | 
			
		||||
  postdata="$postdata&zone=$_domain"
 | 
			
		||||
  postdata="$postdata&host=$_sub_domain"
 | 
			
		||||
  postdata="$postdata&rdata=%22$value%22"
 | 
			
		||||
  _debug2 postdata "$postdata"
 | 
			
		||||
 | 
			
		||||
  _authpost "$postdata" "$url"
 | 
			
		||||
  if _contains "$_response" '"haserrors": true'; then
 | 
			
		||||
    _err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif _contains "$_response" "html>"; then
 | 
			
		||||
    _err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif _contains "$_response" '"auth": false'; then
 | 
			
		||||
    _err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Added acme challenge TXT record for $fulldomain at 1984Hosting."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: fulldomain txtvalue
 | 
			
		||||
# Remove the txt record after validation.
 | 
			
		||||
dns_1984hosting_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _info "Delete TXT record using 1984Hosting."
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  if ! _1984hosting_login; then
 | 
			
		||||
    _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone."
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Invalid domain '$fulldomain'."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug "Delete $fulldomain TXT record."
 | 
			
		||||
 | 
			
		||||
  url="https://1984.hosting/domains"
 | 
			
		||||
  if ! _get_zone_id "$url" "$_domain"; then
 | 
			
		||||
    _err "Invalid zone '$_domain'."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _htmlget "$url/$_zone_id" "$txtvalue"
 | 
			
		||||
  entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
 | 
			
		||||
  _debug2 entry_id "$entry_id"
 | 
			
		||||
  if [ -z "$entry_id" ]; then
 | 
			
		||||
    _err "Error getting TXT entry_id for $1."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _authpost "entry=$entry_id" "$url/delentry/"
 | 
			
		||||
  if ! _contains "$_response" '"ok": true'; then
 | 
			
		||||
    _err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
_1984hosting_login() {
 | 
			
		||||
  if ! _check_credentials; then return 1; fi
 | 
			
		||||
 | 
			
		||||
  if _check_cookies; then
 | 
			
		||||
    _debug "Already logged in."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Login to 1984Hosting as user $One984HOSTING_Username."
 | 
			
		||||
  username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
 | 
			
		||||
  password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
 | 
			
		||||
  url="https://1984.hosting/api/auth/"
 | 
			
		||||
 | 
			
		||||
  _get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
 | 
			
		||||
  csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
 | 
			
		||||
  sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o '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 '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
 | 
			
		||||
  fi
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_check_credentials() {
 | 
			
		||||
  One984HOSTING_Username="${One984HOSTING_Username:-$(_readaccountconf_mutable One984HOSTING_Username)}"
 | 
			
		||||
  One984HOSTING_Password="${One984HOSTING_Password:-$(_readaccountconf_mutable One984HOSTING_Password)}"
 | 
			
		||||
  if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
 | 
			
		||||
    One984HOSTING_Username=""
 | 
			
		||||
    One984HOSTING_Password=""
 | 
			
		||||
    _clearaccountconf_mutable One984HOSTING_Username
 | 
			
		||||
    _clearaccountconf_mutable One984HOSTING_Password
 | 
			
		||||
    _err "You haven't specified 1984Hosting username or password yet."
 | 
			
		||||
    _err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_check_cookies() {
 | 
			
		||||
  One984HOSTING_SESSIONID_COOKIE="${One984HOSTING_SESSIONID_COOKIE:-$(_readaccountconf_mutable One984HOSTING_SESSIONID_COOKIE)}"
 | 
			
		||||
  One984HOSTING_CSRFTOKEN_COOKIE="${One984HOSTING_CSRFTOKEN_COOKIE:-$(_readaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE)}"
 | 
			
		||||
  if [ -z "$One984HOSTING_SESSIONID_COOKIE" ] || [ -z "$One984HOSTING_CSRFTOKEN_COOKIE" ]; then
 | 
			
		||||
    _debug "No cached cookie(s) found."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _authget "https://1984.hosting/api/auth/"
 | 
			
		||||
  if _contains "$_response" '"ok": true'; then
 | 
			
		||||
    _debug "Cached cookies still valid."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Cached cookies no longer valid. Clearing cookies."
 | 
			
		||||
  One984HOSTING_SESSIONID_COOKIE=""
 | 
			
		||||
  One984HOSTING_CSRFTOKEN_COOKIE=""
 | 
			
		||||
  _clearaccountconf_mutable One984HOSTING_SESSIONID_COOKIE
 | 
			
		||||
  _clearaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# _acme-challenge.www.domain.com
 | 
			
		||||
# Returns
 | 
			
		||||
#  _sub_domain=_acme-challenge.www
 | 
			
		||||
#  _domain=domain.com
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
 | 
			
		||||
    # not valid
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _authget "https://1984.hosting/domains/zonestatus/$h/?cached=no"
 | 
			
		||||
    if _contains "$_response" '"ok": true'; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      _domain="$h"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: _get_zone_id url domain.com
 | 
			
		||||
# Returns zone id for domain.com
 | 
			
		||||
_get_zone_id() {
 | 
			
		||||
  url=$1
 | 
			
		||||
  domain=$2
 | 
			
		||||
  _htmlget "$url" "$domain"
 | 
			
		||||
  _zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
 | 
			
		||||
  _debug2 _zone_id "$_zone_id"
 | 
			
		||||
  if [ -z "$_zone_id" ]; then
 | 
			
		||||
    _err "Error getting _zone_id for $2."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Add extra headers to request
 | 
			
		||||
_authget() {
 | 
			
		||||
  export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
 | 
			
		||||
  _response=$(_get "$1" | _normalizeJson)
 | 
			
		||||
  _debug2 _response "$_response"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Truncate huge HTML response
 | 
			
		||||
_htmlget() {
 | 
			
		||||
  export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
 | 
			
		||||
  _response=$(_get "$1" | grep "$2")
 | 
			
		||||
  if _contains "$_response" "@$2"; then
 | 
			
		||||
    _response=$(echo "$_response" | grep -v "[@]" | _head_n 1)
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 _response "$_response"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Add extra headers to request
 | 
			
		||||
_authpost() {
 | 
			
		||||
  url="https://1984.hosting/domains"
 | 
			
		||||
  _get_zone_id "$url" "$_domain"
 | 
			
		||||
  csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
 | 
			
		||||
  export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
 | 
			
		||||
  export _H2="Referer: https://1984.hosting/domains/$_zone_id"
 | 
			
		||||
  export _H3="X-CSRFToken: $csrf_header"
 | 
			
		||||
  _response="$(_post "$1" "$2" | _normalizeJson)"
 | 
			
		||||
  _debug2 _response "$_response"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										94
									
								
								dnsapi/dns_acmedns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										94
									
								
								dnsapi/dns_acmedns.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# 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"
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
dns_acmedns_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using acme-dns"
 | 
			
		||||
  _debug "fulldomain $fulldomain"
 | 
			
		||||
  _debug "txtvalue $txtvalue"
 | 
			
		||||
 | 
			
		||||
  #for compatiblity from account conf
 | 
			
		||||
  ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readaccountconf_mutable ACMEDNS_USERNAME)}"
 | 
			
		||||
  _clearaccountconf_mutable ACMEDNS_USERNAME
 | 
			
		||||
  ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readaccountconf_mutable ACMEDNS_PASSWORD)}"
 | 
			
		||||
  _clearaccountconf_mutable ACMEDNS_PASSWORD
 | 
			
		||||
  ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readaccountconf_mutable ACMEDNS_SUBDOMAIN)}"
 | 
			
		||||
  _clearaccountconf_mutable ACMEDNS_SUBDOMAIN
 | 
			
		||||
 | 
			
		||||
  ACMEDNS_BASE_URL="${ACMEDNS_BASE_URL:-$(_readdomainconf ACMEDNS_BASE_URL)}"
 | 
			
		||||
  ACMEDNS_USERNAME="${ACMEDNS_USERNAME:-$(_readdomainconf ACMEDNS_USERNAME)}"
 | 
			
		||||
  ACMEDNS_PASSWORD="${ACMEDNS_PASSWORD:-$(_readdomainconf ACMEDNS_PASSWORD)}"
 | 
			
		||||
  ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN:-$(_readdomainconf ACMEDNS_SUBDOMAIN)}"
 | 
			
		||||
 | 
			
		||||
  if [ "$ACMEDNS_BASE_URL" = "" ]; then
 | 
			
		||||
    ACMEDNS_BASE_URL="https://auth.acme-dns.io"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  ACMEDNS_UPDATE_URL="$ACMEDNS_BASE_URL/update"
 | 
			
		||||
  ACMEDNS_REGISTER_URL="$ACMEDNS_BASE_URL/register"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ACMEDNS_USERNAME" ] || [ -z "$ACMEDNS_PASSWORD" ]; then
 | 
			
		||||
    response="$(_post "" "$ACMEDNS_REGISTER_URL" "" "POST")"
 | 
			
		||||
    _debug response "$response"
 | 
			
		||||
    ACMEDNS_USERNAME=$(echo "$response" | sed -n 's/^{.*\"username\":[ ]*\"\([^\"]*\)\".*}/\1/p')
 | 
			
		||||
    _debug "received username: $ACMEDNS_USERNAME"
 | 
			
		||||
    ACMEDNS_PASSWORD=$(echo "$response" | sed -n 's/^{.*\"password\":[ ]*\"\([^\"]*\)\".*}/\1/p')
 | 
			
		||||
    _debug "received password: $ACMEDNS_PASSWORD"
 | 
			
		||||
    ACMEDNS_SUBDOMAIN=$(echo "$response" | sed -n 's/^{.*\"subdomain\":[ ]*\"\([^\"]*\)\".*}/\1/p')
 | 
			
		||||
    _debug "received subdomain: $ACMEDNS_SUBDOMAIN"
 | 
			
		||||
    ACMEDNS_FULLDOMAIN=$(echo "$response" | sed -n 's/^{.*\"fulldomain\":[ ]*\"\([^\"]*\)\".*}/\1/p')
 | 
			
		||||
    _info "##########################################################"
 | 
			
		||||
    _info "# Create $fulldomain CNAME $ACMEDNS_FULLDOMAIN DNS entry #"
 | 
			
		||||
    _info "##########################################################"
 | 
			
		||||
    _info "Press enter to continue... "
 | 
			
		||||
    read -r _
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _savedomainconf ACMEDNS_BASE_URL "$ACMEDNS_BASE_URL"
 | 
			
		||||
  _savedomainconf ACMEDNS_USERNAME "$ACMEDNS_USERNAME"
 | 
			
		||||
  _savedomainconf ACMEDNS_PASSWORD "$ACMEDNS_PASSWORD"
 | 
			
		||||
  _savedomainconf ACMEDNS_SUBDOMAIN "$ACMEDNS_SUBDOMAIN"
 | 
			
		||||
 | 
			
		||||
  export _H1="X-Api-User: $ACMEDNS_USERNAME"
 | 
			
		||||
  export _H2="X-Api-Key: $ACMEDNS_PASSWORD"
 | 
			
		||||
  data="{\"subdomain\":\"$ACMEDNS_SUBDOMAIN\", \"txt\": \"$txtvalue\"}"
 | 
			
		||||
 | 
			
		||||
  _debug data "$data"
 | 
			
		||||
  response="$(_post "$data" "$ACMEDNS_UPDATE_URL" "" "POST")"
 | 
			
		||||
  _debug response "$response"
 | 
			
		||||
 | 
			
		||||
  if ! echo "$response" | grep "\"$txtvalue\"" >/dev/null; then
 | 
			
		||||
    _err "invalid response of acme-dns"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Remove the txt record after validation.
 | 
			
		||||
dns_acmedns_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using acme-dns"
 | 
			
		||||
  _debug "fulldomain $fulldomain"
 | 
			
		||||
  _debug "txtvalue $txtvalue"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
							
								
								
									
										91
									
								
								dnsapi/dns_acmeproxy.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										91
									
								
								dnsapi/dns_acmeproxy.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
#!/usr/bin/env 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}"
 | 
			
		||||
  txtvalue="${2}"
 | 
			
		||||
  action="present"
 | 
			
		||||
 | 
			
		||||
  _debug "Calling: _acmeproxy_request() '${fulldomain}' '${txtvalue}' '${action}'"
 | 
			
		||||
  _acmeproxy_request "$fulldomain" "$txtvalue" "$action"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dns_acmeproxy_rm() {
 | 
			
		||||
  fulldomain="${1}"
 | 
			
		||||
  txtvalue="${2}"
 | 
			
		||||
  action="cleanup"
 | 
			
		||||
 | 
			
		||||
  _debug "Calling: _acmeproxy_request() '${fulldomain}' '${txtvalue}' '${action}'"
 | 
			
		||||
  _acmeproxy_request "$fulldomain" "$txtvalue" "$action"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_acmeproxy_request() {
 | 
			
		||||
 | 
			
		||||
  ## Nothing to see here, just some housekeeping
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  action=$3
 | 
			
		||||
 | 
			
		||||
  _info "Using acmeproxy"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  ACMEPROXY_ENDPOINT="${ACMEPROXY_ENDPOINT:-$(_readaccountconf_mutable ACMEPROXY_ENDPOINT)}"
 | 
			
		||||
  ACMEPROXY_USERNAME="${ACMEPROXY_USERNAME:-$(_readaccountconf_mutable ACMEPROXY_USERNAME)}"
 | 
			
		||||
  ACMEPROXY_PASSWORD="${ACMEPROXY_PASSWORD:-$(_readaccountconf_mutable ACMEPROXY_PASSWORD)}"
 | 
			
		||||
 | 
			
		||||
  ## Check for the endpoint
 | 
			
		||||
  if [ -z "$ACMEPROXY_ENDPOINT" ]; then
 | 
			
		||||
    ACMEPROXY_ENDPOINT=""
 | 
			
		||||
    _err "You didn't specify the endpoint"
 | 
			
		||||
    _err "Please set them via 'export ACMEPROXY_ENDPOINT=https://ip:port' and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  ## Save the credentials to the account file
 | 
			
		||||
  _saveaccountconf_mutable ACMEPROXY_ENDPOINT "$ACMEPROXY_ENDPOINT"
 | 
			
		||||
  _saveaccountconf_mutable ACMEPROXY_USERNAME "$ACMEPROXY_USERNAME"
 | 
			
		||||
  _saveaccountconf_mutable ACMEPROXY_PASSWORD "$ACMEPROXY_PASSWORD"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$ACMEPROXY_USERNAME" ] || [ -z "$ACMEPROXY_PASSWORD" ]; then
 | 
			
		||||
    _info "ACMEPROXY_USERNAME and/or ACMEPROXY_PASSWORD not set - using without client authentication! Make sure you're using server authentication (e.g. IP-based)"
 | 
			
		||||
    export _H1="Accept: application/json"
 | 
			
		||||
    export _H2="Content-Type: application/json"
 | 
			
		||||
  else
 | 
			
		||||
    ## Base64 encode the credentials
 | 
			
		||||
    credentials=$(printf "%b" "$ACMEPROXY_USERNAME:$ACMEPROXY_PASSWORD" | _base64)
 | 
			
		||||
 | 
			
		||||
    ## Construct the HTTP Authorization header
 | 
			
		||||
    export _H1="Authorization: Basic $credentials"
 | 
			
		||||
    export _H2="Accept: application/json"
 | 
			
		||||
    export _H3="Content-Type: application/json"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  ## Add the challenge record to the acmeproxy grid member
 | 
			
		||||
  response="$(_post "{\"fqdn\": \"$fulldomain.\", \"value\": \"$txtvalue\"}" "$ACMEPROXY_ENDPOINT/$action" "" "POST")"
 | 
			
		||||
 | 
			
		||||
  ## Let's see if we get something intelligible back from the unit
 | 
			
		||||
  if echo "$response" | grep "\"$txtvalue\"" >/dev/null; then
 | 
			
		||||
    _info "Successfully updated the txt record"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Error encountered during record addition"
 | 
			
		||||
    _err "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
							
								
								
									
										206
									
								
								dnsapi/dns_active24.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										206
									
								
								dnsapi/dns_active24.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,206 @@
 | 
			
		||||
#!/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_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
 | 
			
		||||
dns_active24_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _active24_init
 | 
			
		||||
 | 
			
		||||
  _info "Adding txt record"
 | 
			
		||||
  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
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: fulldomain txtvalue
 | 
			
		||||
# Used to remove the txt record after validation
 | 
			
		||||
dns_active24_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _active24_init
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  # 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" "error"; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # 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 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
 | 
			
		||||
        _info "Removed txt record."
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  _err "No txt records found."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  if ! _active24_rest GET "/v1/user/self/service"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug "h" "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"$h\"" >/dev/null; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_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)}"
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										148
									
								
								dnsapi/dns_ad.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										148
									
								
								dnsapi/dns_ad.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,148 @@
 | 
			
		||||
#!/usr/bin/env 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"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_ad_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AD_API_KEY" ]; then
 | 
			
		||||
    AD_API_KEY=""
 | 
			
		||||
    _err "You didn't specify the AD api key yet."
 | 
			
		||||
    _err "Please create you key and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf AD_API_KEY "$AD_API_KEY"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _ad_tmpl_json="{\"domain\":$_domain_id,\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}"
 | 
			
		||||
 | 
			
		||||
  if _ad_rest POST "record/" "$_ad_tmpl_json" && [ -z "$response" ]; then
 | 
			
		||||
    _info "txt record updated success."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain txtvalue
 | 
			
		||||
dns_ad_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$response" ]; then
 | 
			
		||||
    record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
 | 
			
		||||
    _debug record_id "$record_id"
 | 
			
		||||
    if [ -z "$record_id" ]; then
 | 
			
		||||
      _err "Can not get record id to remove."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if _ad_rest DELETE "record/$record_id/" && [ -z "$response" ]; then
 | 
			
		||||
      _info "txt record deleted success."
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    _debug response "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=12345
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  if _ad_rest GET "domain/"; then
 | 
			
		||||
    response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
 | 
			
		||||
    while true; do
 | 
			
		||||
      h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
      _debug h "$h"
 | 
			
		||||
      if [ -z "$h" ]; then
 | 
			
		||||
        #not valid
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")"
 | 
			
		||||
      if [ "$hostedzone" ]; then
 | 
			
		||||
        _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
 | 
			
		||||
        if [ "$_domain_id" ]; then
 | 
			
		||||
          _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
          _domain=$h
 | 
			
		||||
          return 0
 | 
			
		||||
        fi
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
      p=$i
 | 
			
		||||
      i=$(_math "$i" + 1)
 | 
			
		||||
    done
 | 
			
		||||
  fi
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#method uri qstr data
 | 
			
		||||
_ad_rest() {
 | 
			
		||||
  mtd="$1"
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
 | 
			
		||||
  _debug mtd "$mtd"
 | 
			
		||||
  _debug ep "$ep"
 | 
			
		||||
 | 
			
		||||
  export _H1="Accept: application/json"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$mtd" != "GET" ]; then
 | 
			
		||||
    # both POST and DELETE.
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$AD_API_URL/$ep" "" "$mtd")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$AD_API_URL/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										220
									
								
								dnsapi/dns_ali.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										220
									
								
								dnsapi/dns_ali.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,220 @@
 | 
			
		||||
#!/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
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
# 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_DNS_API="https://alidns.aliyuncs.com/"
 | 
			
		||||
 | 
			
		||||
#Usage: dns_ali_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_ali_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _prepare_ali_credentials || return 1
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Add record"
 | 
			
		||||
  _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _ali_rest "Add record"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dns_ali_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
 | 
			
		||||
  Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _clean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  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=1
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _describe_records_query "$h"
 | 
			
		||||
    if ! _ali_rest "Get root" "ignore"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "PageNumber"; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      _debug _sub_domain "$_sub_domain"
 | 
			
		||||
      _domain="$h"
 | 
			
		||||
      _debug _domain "$_domain"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    p="$i"
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_check_exist_query() {
 | 
			
		||||
  _qdomain="$1"
 | 
			
		||||
  _qsubdomain="$2"
 | 
			
		||||
  endpoint=$Ali_DNS_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=DescribeDomainRecords'
 | 
			
		||||
  query=$query'&DomainName='$_qdomain
 | 
			
		||||
  query=$query'&Format=json'
 | 
			
		||||
  query=$query'&RRKeyWord='$_qsubdomain
 | 
			
		||||
  query=$query'&SignatureMethod=HMAC-SHA1'
 | 
			
		||||
  query=$query"&SignatureNonce=$(_ali_nonce)"
 | 
			
		||||
  query=$query'&SignatureVersion=1.0'
 | 
			
		||||
  query=$query'&Timestamp='$(_timestamp)
 | 
			
		||||
  query=$query'&TypeKeyWord=TXT'
 | 
			
		||||
  query=$query'&Version=2015-01-09'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_add_record_query() {
 | 
			
		||||
  endpoint=$Ali_DNS_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=AddDomainRecord'
 | 
			
		||||
  query=$query'&DomainName='$1
 | 
			
		||||
  query=$query'&Format=json'
 | 
			
		||||
  query=$query'&RR='$2
 | 
			
		||||
  query=$query'&SignatureMethod=HMAC-SHA1'
 | 
			
		||||
  query=$query"&SignatureNonce=$(_ali_nonce)"
 | 
			
		||||
  query=$query'&SignatureVersion=1.0'
 | 
			
		||||
  query=$query'&Timestamp='$(_timestamp)
 | 
			
		||||
  query=$query'&Type=TXT'
 | 
			
		||||
  query=$query'&Value='$3
 | 
			
		||||
  query=$query'&Version=2015-01-09'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_delete_record_query() {
 | 
			
		||||
  endpoint=$Ali_DNS_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=DeleteDomainRecord'
 | 
			
		||||
  query=$query'&Format=json'
 | 
			
		||||
  query=$query'&RecordId='$1
 | 
			
		||||
  query=$query'&SignatureMethod=HMAC-SHA1'
 | 
			
		||||
  query=$query"&SignatureNonce=$(_ali_nonce)"
 | 
			
		||||
  query=$query'&SignatureVersion=1.0'
 | 
			
		||||
  query=$query'&Timestamp='$(_timestamp)
 | 
			
		||||
  query=$query'&Version=2015-01-09'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_describe_records_query() {
 | 
			
		||||
  endpoint=$Ali_DNS_API
 | 
			
		||||
  query=''
 | 
			
		||||
  query=$query'AccessKeyId='$Ali_Key
 | 
			
		||||
  query=$query'&Action=DescribeDomainRecords'
 | 
			
		||||
  query=$query'&DomainName='$1
 | 
			
		||||
  query=$query'&Format=json'
 | 
			
		||||
  query=$query'&SignatureMethod=HMAC-SHA1'
 | 
			
		||||
  query=$query"&SignatureNonce=$(_ali_nonce)"
 | 
			
		||||
  query=$query'&SignatureVersion=1.0'
 | 
			
		||||
  query=$query'&Timestamp='$(_timestamp)
 | 
			
		||||
  query=$query'&Version=2015-01-09'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_clean() {
 | 
			
		||||
  _check_exist_query "$_domain" "$_sub_domain"
 | 
			
		||||
  # do not correct grammar here
 | 
			
		||||
  if ! _ali_rest "Check exist records" "ignore"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep -- "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)"
 | 
			
		||||
  _debug2 record_id "$record_id"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$record_id" ]; then
 | 
			
		||||
    _debug "record not found, skip"
 | 
			
		||||
  else
 | 
			
		||||
    _delete_record_query "$record_id"
 | 
			
		||||
    _ali_rest "Delete record $record_id" "ignore"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										185
									
								
								dnsapi/dns_alviy.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								dnsapi/dns_alviy.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,185 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_alviy_info='Alviy.com
 | 
			
		||||
Site: Alviy.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_alviy
 | 
			
		||||
Options:
 | 
			
		||||
 Alviy_token API token. Get it from the https://cloud.alviy.com/token
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/5115
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
Alviy_Api="https://cloud.alviy.com/api/v1"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_alviy_add  _acme-challenge.www.domain.com   "content"
 | 
			
		||||
dns_alviy_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
 | 
			
		||||
  if [ -z "$Alviy_token" ]; then
 | 
			
		||||
    Alviy_token=""
 | 
			
		||||
    _err "Please specify Alviy token."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the api key and email to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable Alviy_token "$Alviy_token"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting existing records"
 | 
			
		||||
  if _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
 | 
			
		||||
    _info "This record already exists, skipping"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _add_data="{\"content\":\"$txtvalue\",\"type\":\"TXT\"}"
 | 
			
		||||
  _debug2 _add_data "$_add_data"
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _alviy_rest POST "zone/$_domain/domain/$fulldomain/" "$_add_data"; then
 | 
			
		||||
    _debug "Checking updated records of '${fulldomain}'"
 | 
			
		||||
 | 
			
		||||
    if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
 | 
			
		||||
      _err "TXT record '${txtvalue}' for '${fulldomain}', value wasn't set!"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
  else
 | 
			
		||||
    _err "Add txt record error, value '${txtvalue}' for '${fulldomain}' was not set."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _sleep 10
 | 
			
		||||
  _info "Added TXT record '${txtvalue}' for '${fulldomain}'."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain
 | 
			
		||||
dns_alviy_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then
 | 
			
		||||
    _info "The record does not exist, skip"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _add_data=""
 | 
			
		||||
  uuid=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | tr "," "\n" | grep uuid | cut -d \" -f4)
 | 
			
		||||
  # delete record
 | 
			
		||||
  _debug "Delete TXT record for '${fulldomain}'"
 | 
			
		||||
  if ! _alviy_rest DELETE "zone/$_domain/record/$uuid" "{\"confirm\":1}"; then
 | 
			
		||||
    _err "Cannot delete empty TXT record for '$fulldomain'"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _info "The record '$fulldomain'='$txtvalue' deleted"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=3
 | 
			
		||||
  a="init"
 | 
			
		||||
  while [ -n "$a" ]; do
 | 
			
		||||
    a=$(printf "%s" "$domain" | cut -d . -f $i-)
 | 
			
		||||
    i=$((i + 1))
 | 
			
		||||
  done
 | 
			
		||||
  n=$((i - 3))
 | 
			
		||||
  h=$(printf "%s" "$domain" | cut -d . -f $n-)
 | 
			
		||||
  if [ -z "$h" ]; then
 | 
			
		||||
    #not valid
 | 
			
		||||
    _alviy_rest GET "zone/$domain/"
 | 
			
		||||
    _debug "can't get host from $domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _alviy_rest GET "zone/$h/"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" '"code":"NOT_FOUND"'; then
 | 
			
		||||
    _debug "$h not found"
 | 
			
		||||
  else
 | 
			
		||||
    s=$((n - 1))
 | 
			
		||||
    _sub_domain=$(printf "%s" "$domain" | cut -d . -f -$s)
 | 
			
		||||
    _domain="$h"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_alviy_txt_exists() {
 | 
			
		||||
  zone=$1
 | 
			
		||||
  domain=$2
 | 
			
		||||
  content_data=$3
 | 
			
		||||
  _debug "Getting existing records"
 | 
			
		||||
 | 
			
		||||
  if ! _alviy_rest GET "zone/$zone/domain/$domain/TXT/"; then
 | 
			
		||||
    _info "The record does not exist"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "$3"; then
 | 
			
		||||
    _info "The record has other value"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  # GOOD code return - TRUE function
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_alviy_rest() {
 | 
			
		||||
  method=$1
 | 
			
		||||
  path="$2"
 | 
			
		||||
  content_data="$3"
 | 
			
		||||
  _debug "$path"
 | 
			
		||||
 | 
			
		||||
  export _H1="Authorization: Bearer $Alviy_token"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$content_data" ] || [ "$method" = "DELETE" ]; then
 | 
			
		||||
    _debug "data ($method): " "$content_data"
 | 
			
		||||
    response="$(_post "$content_data" "$Alviy_Api/$path" "" "$method")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$Alviy_Api/$path")"
 | 
			
		||||
  fi
 | 
			
		||||
  _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
 | 
			
		||||
  if [ "$_code" = "401" ]; then
 | 
			
		||||
    _err "It seems that your api key or secret is not correct."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$_code" != "200" ]; then
 | 
			
		||||
    _err "API call error ($method): $path Response code $_code"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error on rest call ($method): $path. Response:"
 | 
			
		||||
    _err "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								dnsapi/dns_anx.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								dnsapi/dns_anx.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_anx_info='Anexia.com CloudDNS
 | 
			
		||||
Site: Anexia.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_anx
 | 
			
		||||
Options:
 | 
			
		||||
 ANX_Token API Token
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/3238
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
ANX_API='https://engine.anexia-it.com/api/clouddns/v1'
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
dns_anx_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _info "Using ANX CDNS API"
 | 
			
		||||
 | 
			
		||||
  ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  if [ "$ANX_Token" ]; then
 | 
			
		||||
    _saveaccountconf_mutable ANX_Token "$ANX_Token"
 | 
			
		||||
  else
 | 
			
		||||
    _err "You didn't specify a ANEXIA Engine API token."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Always add records, wildcard need two records with the same name
 | 
			
		||||
  _anx_rest POST "zone.json/${_domain}/records" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"rdata\":\"$txtvalue\"}"
 | 
			
		||||
  if _contains "$response" "$txtvalue"; then
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dns_anx_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _info "Using ANX CDNS API"
 | 
			
		||||
 | 
			
		||||
  ANX_Token="${ANX_Token:-$(_readaccountconf_mutable ANX_Token)}"
 | 
			
		||||
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _get_record_id
 | 
			
		||||
 | 
			
		||||
  if _is_uuid "$_record_id"; then
 | 
			
		||||
    if ! _anx_rest DELETE "zone.json/${_domain}/records/$_record_id"; then
 | 
			
		||||
      _err "Delete record"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _info "No record found."
 | 
			
		||||
  fi
 | 
			
		||||
  echo "$response" | tr -d " " | grep \"status\":\"OK\" >/dev/null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_is_uuid() {
 | 
			
		||||
  pattern='^\{?[A-Z0-9a-z]{8}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{4}-[A-Z0-9a-z]{12}\}?$'
 | 
			
		||||
  if echo "$1" | _egrep_o "$pattern" >/dev/null; then
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_record_id() {
 | 
			
		||||
  _debug subdomain "$_sub_domain"
 | 
			
		||||
  _debug domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  if _anx_rest GET "zone.json/${_domain}/records?name=$_sub_domain&type=TXT"; then
 | 
			
		||||
    _debug response "$response"
 | 
			
		||||
    if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then
 | 
			
		||||
      _record_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"identifier\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
    else
 | 
			
		||||
      _record_id=''
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _err "Search existing record"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_anx_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  export _H2="Authorization: Token $ANX_Token"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "${ANX_API}/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "${ANX_API}/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # shellcheck disable=SC2181
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _anx_rest GET "zone.json/${h}"
 | 
			
		||||
    if _contains "$response" "\"name\":\"$h\""; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										177
									
								
								dnsapi/dns_artfiles.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								dnsapi/dns_artfiles.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_artfiles_info='ArtFiles.de
 | 
			
		||||
Site: ArtFiles.de
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_artfiles
 | 
			
		||||
Options:
 | 
			
		||||
 AF_API_USERNAME API Username
 | 
			
		||||
 AF_API_PASSWORD API Password
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/4718
 | 
			
		||||
Author: Martin Arndt <https://troublezone.net/>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
########## API configuration ###################################################
 | 
			
		||||
 | 
			
		||||
AF_API_SUCCESS='status":"OK'
 | 
			
		||||
AF_URL_DCP='https://dcp.c.artfiles.de/api/'
 | 
			
		||||
AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
 | 
			
		||||
AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html'
 | 
			
		||||
 | 
			
		||||
########## Public functions ####################################################
 | 
			
		||||
 | 
			
		||||
# Adds a new TXT record for given ACME challenge value & domain.
 | 
			
		||||
# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
 | 
			
		||||
dns_artfiles_add() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  txtValue="$2"
 | 
			
		||||
  _info 'Using ArtFiles.de DNS addition API…'
 | 
			
		||||
  _debug 'Domain' "$domain"
 | 
			
		||||
  _debug 'txtValue' "$txtValue"
 | 
			
		||||
 | 
			
		||||
  _set_credentials
 | 
			
		||||
  _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
 | 
			
		||||
  _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
 | 
			
		||||
 | 
			
		||||
  _set_headers
 | 
			
		||||
  _get_zone "$domain"
 | 
			
		||||
  _dns 'GET'
 | 
			
		||||
  if ! _contains "$response" 'TXT'; then
 | 
			
		||||
    _err 'Retrieving TXT records failed.'
 | 
			
		||||
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _clean_records
 | 
			
		||||
  _dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")"
 | 
			
		||||
  if ! _contains "$response" "$AF_API_SUCCESS"; then
 | 
			
		||||
    _err 'Adding ACME challenge value failed.'
 | 
			
		||||
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Removes the existing TXT record for given ACME challenge value & domain.
 | 
			
		||||
# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
 | 
			
		||||
dns_artfiles_rm() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  txtValue="$2"
 | 
			
		||||
  _info 'Using ArtFiles.de DNS removal API…'
 | 
			
		||||
  _debug 'Domain' "$domain"
 | 
			
		||||
  _debug 'txtValue' "$txtValue"
 | 
			
		||||
 | 
			
		||||
  _set_credentials
 | 
			
		||||
  _set_headers
 | 
			
		||||
  _get_zone "$domain"
 | 
			
		||||
  if ! _dns 'GET'; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "$txtValue"; then
 | 
			
		||||
    _err 'Retrieved TXT records are missing given ACME challenge value.'
 | 
			
		||||
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _clean_records
 | 
			
		||||
  response="$(printf -- '%s' "$response" | sed '/_acme-challenge "'"$txtValue"'"/d')"
 | 
			
		||||
  _dns 'SET' "$response"
 | 
			
		||||
  if ! _contains "$response" "$AF_API_SUCCESS"; then
 | 
			
		||||
    _err 'Removing ACME challenge value failed.'
 | 
			
		||||
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
########## Private functions ###################################################
 | 
			
		||||
 | 
			
		||||
# Cleans awful TXT records response of ArtFiles's API & pretty prints it.
 | 
			
		||||
# Usage: _clean_records
 | 
			
		||||
_clean_records() {
 | 
			
		||||
  _info 'Cleaning TXT records…'
 | 
			
		||||
  # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
 | 
			
		||||
  # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
 | 
			
		||||
  # from '\"' & turn '\n' into real LF characters.
 | 
			
		||||
  # Yup, awful API to use - but that's all we got to get this working, so… ;)
 | 
			
		||||
  _debug2 'Raw  ' "$response"
 | 
			
		||||
  response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
 | 
			
		||||
  _debug2 'Clean' "$response"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Executes an HTTP GET or POST request for getting or setting DNS records,
 | 
			
		||||
# containing given payload upon POST.
 | 
			
		||||
# Usage: _dns [GET | SET] [payload]
 | 
			
		||||
_dns() {
 | 
			
		||||
  _info 'Executing HTTP request…'
 | 
			
		||||
  action="$1"
 | 
			
		||||
  payload="$(printf -- '%s' "$2" | _url_encode)"
 | 
			
		||||
  url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
 | 
			
		||||
 | 
			
		||||
  if [ "$action" = 'SET' ]; then
 | 
			
		||||
    _debug2 'Payload' "$payload"
 | 
			
		||||
    response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$url" '' 10)"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "$AF_API_SUCCESS"; then
 | 
			
		||||
    _err "DNS API error: $response"
 | 
			
		||||
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug 'Response' "$response"
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Gets the root domain zone for given domain.
 | 
			
		||||
# Usage: _get_zone _acme-challenge.www.example.com
 | 
			
		||||
_get_zone() {
 | 
			
		||||
  fqdn="$1"
 | 
			
		||||
  domains="$(_get "$AF_URL_DOMAINS" '' 10)"
 | 
			
		||||
  _info 'Getting domain zone…'
 | 
			
		||||
  _debug2 'FQDN' "$fqdn"
 | 
			
		||||
  _debug2 'Domains' "$domains"
 | 
			
		||||
 | 
			
		||||
  while _contains "$fqdn" "."; do
 | 
			
		||||
    if _contains "$domains" "$fqdn"; then
 | 
			
		||||
      domain="$fqdn"
 | 
			
		||||
      _info "Found root domain zone: $domain"
 | 
			
		||||
      break
 | 
			
		||||
    else
 | 
			
		||||
      fqdn="${fqdn#*.}"
 | 
			
		||||
      _debug2 'FQDN' "$fqdn"
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  if [ "$domain" = "$fqdn" ]; then
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _err 'Couldn'\''t find root domain zone.'
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Sets the credentials for accessing ArtFiles's API
 | 
			
		||||
# Usage: _set_credentials
 | 
			
		||||
_set_credentials() {
 | 
			
		||||
  _info 'Setting credentials…'
 | 
			
		||||
  AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
 | 
			
		||||
  AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
 | 
			
		||||
  if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
 | 
			
		||||
    _err 'Missing ArtFiles.de username and/or password.'
 | 
			
		||||
    _err 'Please ensure both are set via export command & try again.'
 | 
			
		||||
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Adds the HTTP Authorization & Content-Type headers to a follow-up request.
 | 
			
		||||
# Usage: _set_headers
 | 
			
		||||
_set_headers() {
 | 
			
		||||
  _info 'Setting headers…'
 | 
			
		||||
  encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
 | 
			
		||||
  export _H1="Authorization: Basic $encoded"
 | 
			
		||||
  export _H2='Content-Type: application/json'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										156
									
								
								dnsapi/dns_arvan.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								dnsapi/dns_arvan.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_arvan_info='ArvanCloud.ir
 | 
			
		||||
Site: ArvanCloud.ir
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_arvan
 | 
			
		||||
Options:
 | 
			
		||||
 Arvan_Token API Token
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2796
 | 
			
		||||
Author: Vahid Fardi
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
ARVAN_API_URL="https://napi.arvancloud.ir/cdn/4.0/domains"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_arvan_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_arvan_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using Arvan"
 | 
			
		||||
 | 
			
		||||
  Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$Arvan_Token" ]; then
 | 
			
		||||
    _err "You didn't specify \"Arvan_Token\" token yet."
 | 
			
		||||
    _err "You can get yours from here https://npanel.arvancloud.ir/profile/api-keys"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  #save the api token to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable Arvan_Token "$Arvan_Token"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _arvan_rest POST "$_domain/dns-records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":{\"text\":\"$txtvalue\"},\"ttl\":120}"; then
 | 
			
		||||
    if _contains "$response" "$txtvalue"; then
 | 
			
		||||
      _info "response id is $response"
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    elif _contains "$response" "Record Data is duplicate"; then
 | 
			
		||||
      _info "Already exists, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Add txt record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Remove the txt record after validation.
 | 
			
		||||
dns_arvan_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using Arvan"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  Arvan_Token="${Arvan_Token:-$(_readaccountconf_mutable Arvan_Token)}"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _arvan_rest GET "${_domain}/dns-records"
 | 
			
		||||
  if ! printf "%s" "$response" | grep \"current_page\":1 >/dev/null; then
 | 
			
		||||
    _err "Error on Arvan Api"
 | 
			
		||||
    _err "Please create a github issue with debbug log"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _record_id=$(echo "$response" | _egrep_o ".\"id\":\"[^\"]*\",\"type\":\"txt\",\"name\":\"_acme-challenge\",\"value\":{\"text\":\"$txtvalue\"}" | cut -d : -f 2 | cut -d , -f 1 | tr -d \")
 | 
			
		||||
  if ! _arvan_rest "DELETE" "${_domain}/dns-records/${_record_id}"; then
 | 
			
		||||
    _err "Error on Arvan Api"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "$response"
 | 
			
		||||
  _contains "$response" 'dns record deleted'
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _arvan_rest GET "$h"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if _contains "$response" "\"domain\":\"$h\""; then
 | 
			
		||||
      _domain_id=$(echo "$response" | cut -d : -f 3 | cut -d , -f 1 | tr -d \")
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_arvan_rest() {
 | 
			
		||||
  mtd="$1"
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
 | 
			
		||||
  token_trimmed=$(echo "$Arvan_Token" | tr -d '"')
 | 
			
		||||
  export _H1="Authorization: $token_trimmed"
 | 
			
		||||
 | 
			
		||||
  if [ "$mtd" = "DELETE" ]; then
 | 
			
		||||
    #DELETE Request shouldn't have Content-Type
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
 | 
			
		||||
  elif [ "$mtd" = "POST" ]; then
 | 
			
		||||
    export _H2="Content-Type: application/json"
 | 
			
		||||
    export _H3="Accept: application/json"
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$ARVAN_API_URL/$ep$data")"
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										177
									
								
								dnsapi/dns_aurora.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								dnsapi/dns_aurora.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_aurora_info='versio.nl AuroraDNS
 | 
			
		||||
Domains: pcextreme.nl
 | 
			
		||||
Site: versio.nl
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_aurora
 | 
			
		||||
Options:
 | 
			
		||||
 AURORA_Key API Key
 | 
			
		||||
 AURORA_Secret API Secret
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/3459
 | 
			
		||||
Author: Jasper Zonneveld
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
AURORA_Api="https://api.auroradns.eu"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_aurora_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}"
 | 
			
		||||
  AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AURORA_Key" ] || [ -z "$AURORA_Secret" ]; then
 | 
			
		||||
    AURORA_Key=""
 | 
			
		||||
    AURORA_Secret=""
 | 
			
		||||
    _err "You didn't specify an Aurora api key and secret yet."
 | 
			
		||||
    _err "You can get yours from here https://cp.pcextreme.nl/auroradns/users."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the api key and secret to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable AURORA_Key "$AURORA_Key"
 | 
			
		||||
  _saveaccountconf_mutable AURORA_Secret "$AURORA_Secret"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _aurora_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then
 | 
			
		||||
    if _contains "$response" "$txtvalue"; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    elif _contains "$response" "RecordExistsError"; then
 | 
			
		||||
      _info "Already exists, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Add txt record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain txtvalue
 | 
			
		||||
dns_aurora_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  AURORA_Key="${AURORA_Key:-$(_readaccountconf_mutable AURORA_Key)}"
 | 
			
		||||
  AURORA_Secret="${AURORA_Secret:-$(_readaccountconf_mutable AURORA_Secret)}"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting records"
 | 
			
		||||
  _aurora_rest GET "zones/${_domain_id}/records"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "$txtvalue"; then
 | 
			
		||||
    _info "Don't need to remove."
 | 
			
		||||
  else
 | 
			
		||||
    records=$(echo "$response" | _normalizeJson | tr -d "[]" | sed "s/},{/}|{/g" | tr "|" "\n")
 | 
			
		||||
    if [ "$(echo "$records" | wc -l)" -le 2 ]; then
 | 
			
		||||
      _err "Can not parse records."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    record_id=$(echo "$records" | grep "\"type\": *\"TXT\"" | grep "\"name\": *\"$_sub_domain\"" | grep "\"content\": *\"$txtvalue\"" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
 | 
			
		||||
    _debug "record_id" "$record_id"
 | 
			
		||||
    if [ -z "$record_id" ]; then
 | 
			
		||||
      _err "Can not get record id to remove."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if ! _aurora_rest DELETE "zones/$_domain_id/records/$record_id"; then
 | 
			
		||||
      _err "Delete record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _aurora_rest GET "zones/$h"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"name\": \"$h\""; then
 | 
			
		||||
      _domain_id=$(echo "$response" | _normalizeJson | tr -d "{}" | tr "," "\n" | grep "\"id\": *\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
 | 
			
		||||
      _debug _domain_id "$_domain_id"
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_aurora_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  key_trimmed=$(echo "$AURORA_Key" | tr -d '"')
 | 
			
		||||
  secret_trimmed=$(echo "$AURORA_Secret" | tr -d '"')
 | 
			
		||||
 | 
			
		||||
  timestamp=$(date -u +"%Y%m%dT%H%M%SZ")
 | 
			
		||||
  signature=$(printf "%s/%s%s" "$m" "$ep" "$timestamp" | _hmac sha256 "$(printf "%s" "$secret_trimmed" | _hex_dump | tr -d " ")" | _base64)
 | 
			
		||||
  authorization=$(printf "AuroraDNSv1 %s" "$(printf "%s:%s" "$key_trimmed" "$signature" | _base64)")
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json; charset=UTF-8"
 | 
			
		||||
  export _H2="X-AuroraDNS-Date: $timestamp"
 | 
			
		||||
  export _H3="Authorization: $authorization"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$AURORA_Api/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$AURORA_Api/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										263
									
								
								dnsapi/dns_autodns.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								dnsapi/dns_autodns.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,263 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# 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"
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   txtdomain
 | 
			
		||||
#   txt
 | 
			
		||||
dns_autodns_add() {
 | 
			
		||||
  fulldomain="$1"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
 | 
			
		||||
  AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}"
 | 
			
		||||
  AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}"
 | 
			
		||||
  AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then
 | 
			
		||||
    _err "You don't specify autodns user, password and context."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable AUTODNS_USER "$AUTODNS_USER"
 | 
			
		||||
  _saveaccountconf_mutable AUTODNS_PASSWORD "$AUTODNS_PASSWORD"
 | 
			
		||||
  _saveaccountconf_mutable AUTODNS_CONTEXT "$AUTODNS_CONTEXT"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
 | 
			
		||||
  if ! _get_autodns_zone "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _zone "$_zone"
 | 
			
		||||
  _debug _system_ns "$_system_ns"
 | 
			
		||||
 | 
			
		||||
  _info "Adding TXT record"
 | 
			
		||||
 | 
			
		||||
  autodns_response="$(_autodns_zone_update "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" -eq "0" ]; then
 | 
			
		||||
    _info "Added, OK"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   txtdomain
 | 
			
		||||
#   txt
 | 
			
		||||
dns_autodns_rm() {
 | 
			
		||||
  fulldomain="$1"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
 | 
			
		||||
  AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}"
 | 
			
		||||
  AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}"
 | 
			
		||||
  AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then
 | 
			
		||||
    _err "You don't specify autodns user, password and context."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
 | 
			
		||||
  if ! _get_autodns_zone "$fulldomain"; then
 | 
			
		||||
    _err "zone not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _zone "$_zone"
 | 
			
		||||
  _debug _system_ns "$_system_ns"
 | 
			
		||||
 | 
			
		||||
  _info "Delete TXT record"
 | 
			
		||||
 | 
			
		||||
  autodns_response="$(_autodns_zone_cleanup "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" -eq "0" ]; then
 | 
			
		||||
    _info "Deleted, OK"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   fulldomain
 | 
			
		||||
# Returns:
 | 
			
		||||
#   _sub_domain=_acme-challenge.www
 | 
			
		||||
#   _zone=domain.com
 | 
			
		||||
#   _system_ns
 | 
			
		||||
_get_autodns_zone() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      # not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    autodns_response="$(_autodns_zone_inquire "$h")"
 | 
			
		||||
 | 
			
		||||
    if [ "$?" -ne "0" ]; then
 | 
			
		||||
      _err "invalid domain"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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")
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_build_request_auth_xml() {
 | 
			
		||||
  printf "<auth>
 | 
			
		||||
    <user>%s</user>
 | 
			
		||||
    <password>%s</password>
 | 
			
		||||
    <context>%s</context>
 | 
			
		||||
  </auth>" "$AUTODNS_USER" "$AUTODNS_PASSWORD" "$AUTODNS_CONTEXT"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   zone
 | 
			
		||||
_build_zone_inquire_xml() {
 | 
			
		||||
  printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
 | 
			
		||||
  <request>
 | 
			
		||||
    %s
 | 
			
		||||
    <task>
 | 
			
		||||
      <code>0205</code>
 | 
			
		||||
      <view>
 | 
			
		||||
        <children>1</children>
 | 
			
		||||
        <limit>1</limit>
 | 
			
		||||
      </view>
 | 
			
		||||
      <where>
 | 
			
		||||
        <key>name</key>
 | 
			
		||||
        <operator>eq</operator>
 | 
			
		||||
        <value>%s</value>
 | 
			
		||||
      </where>
 | 
			
		||||
    </task>
 | 
			
		||||
  </request>" "$(_build_request_auth_xml)" "$1"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   zone
 | 
			
		||||
#   subdomain
 | 
			
		||||
#   txtvalue
 | 
			
		||||
#   system_ns
 | 
			
		||||
_build_zone_update_xml() {
 | 
			
		||||
  printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
 | 
			
		||||
  <request>
 | 
			
		||||
    %s
 | 
			
		||||
    <task>
 | 
			
		||||
      <code>0202001</code>
 | 
			
		||||
      <default>
 | 
			
		||||
        <rr_add>
 | 
			
		||||
          <name>%s</name>
 | 
			
		||||
          <ttl>600</ttl>
 | 
			
		||||
          <type>TXT</type>
 | 
			
		||||
          <value>%s</value>
 | 
			
		||||
        </rr_add>
 | 
			
		||||
      </default>
 | 
			
		||||
      <zone>
 | 
			
		||||
        <name>%s</name>
 | 
			
		||||
        <system_ns>%s</system_ns>
 | 
			
		||||
      </zone>
 | 
			
		||||
    </task>
 | 
			
		||||
  </request>" "$(_build_request_auth_xml)" "$2" "$3" "$1" "$4"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   zone
 | 
			
		||||
_autodns_zone_inquire() {
 | 
			
		||||
  request_data="$(_build_zone_inquire_xml "$1")"
 | 
			
		||||
  autodns_response="$(_autodns_api_call "$request_data")"
 | 
			
		||||
  ret="$?"
 | 
			
		||||
 | 
			
		||||
  printf "%s" "$autodns_response"
 | 
			
		||||
  return "$ret"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   zone
 | 
			
		||||
#   subdomain
 | 
			
		||||
#   txtvalue
 | 
			
		||||
#   system_ns
 | 
			
		||||
_autodns_zone_update() {
 | 
			
		||||
  request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")"
 | 
			
		||||
  autodns_response="$(_autodns_api_call "$request_data")"
 | 
			
		||||
  ret="$?"
 | 
			
		||||
 | 
			
		||||
  printf "%s" "$autodns_response"
 | 
			
		||||
  return "$ret"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   zone
 | 
			
		||||
#   subdomain
 | 
			
		||||
#   txtvalue
 | 
			
		||||
#   system_ns
 | 
			
		||||
_autodns_zone_cleanup() {
 | 
			
		||||
  request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")"
 | 
			
		||||
  # replace 'rr_add>' with 'rr_rem>' in request_data
 | 
			
		||||
  request_data="$(printf -- "%s" "$request_data" | sed 's/rr_add>/rr_rem>/g')"
 | 
			
		||||
  autodns_response="$(_autodns_api_call "$request_data")"
 | 
			
		||||
  ret="$?"
 | 
			
		||||
 | 
			
		||||
  printf "%s" "$autodns_response"
 | 
			
		||||
  return "$ret"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Arguments:
 | 
			
		||||
#   request_data
 | 
			
		||||
_autodns_api_call() {
 | 
			
		||||
  request_data="$1"
 | 
			
		||||
 | 
			
		||||
  _debug request_data "$request_data"
 | 
			
		||||
 | 
			
		||||
  autodns_response="$(_post "$request_data" "$AUTODNS_API")"
 | 
			
		||||
  ret="$?"
 | 
			
		||||
 | 
			
		||||
  _debug autodns_response "$autodns_response"
 | 
			
		||||
 | 
			
		||||
  if [ "$ret" -ne "0" ]; then
 | 
			
		||||
    _err "error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$autodns_response" "<type>success</type>" >/dev/null; then
 | 
			
		||||
    _info "success"
 | 
			
		||||
    printf "%s" "$autodns_response"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										380
									
								
								dnsapi/dns_aws.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										380
									
								
								dnsapi/dns_aws.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,380 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_aws_info='Amazon AWS Route53 domain API
 | 
			
		||||
Site: docs.aws.amazon.com/route53/
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_aws
 | 
			
		||||
Options:
 | 
			
		||||
 AWS_ACCESS_KEY_ID API Key ID
 | 
			
		||||
 AWS_SECRET_ACCESS_KEY API Secret
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
# All `_sleep` commands are included to avoid Route53 throttling, see
 | 
			
		||||
# https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
 | 
			
		||||
 | 
			
		||||
AWS_HOST="route53.amazonaws.com"
 | 
			
		||||
AWS_URL="https://$AWS_HOST"
 | 
			
		||||
 | 
			
		||||
AWS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Amazon-Route53-API"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_aws_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
 | 
			
		||||
  AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
 | 
			
		||||
  AWS_DNS_SLOWRATE="${AWS_DNS_SLOWRATE:-$(_readaccountconf_mutable AWS_DNS_SLOWRATE)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
 | 
			
		||||
    _use_container_role || _use_instance_role
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
 | 
			
		||||
    AWS_ACCESS_KEY_ID=""
 | 
			
		||||
    AWS_SECRET_ACCESS_KEY=""
 | 
			
		||||
    _err "You haven't specified the aws route53 api key id and and api key secret yet."
 | 
			
		||||
    _err "Please create your key and try again. see $(__green $AWS_WIKI)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save for future use, unless using a role which will be fetched as needed
 | 
			
		||||
  if [ -z "$_using_role" ]; then
 | 
			
		||||
    _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
 | 
			
		||||
    _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
 | 
			
		||||
    _saveaccountconf_mutable AWS_DNS_SLOWRATE "$AWS_DNS_SLOWRATE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    _sleep 1
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _info "Getting existing records for $fulldomain"
 | 
			
		||||
  if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
 | 
			
		||||
    _sleep 1
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" "<Name>$fulldomain.</Name>"; then
 | 
			
		||||
    _resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
 | 
			
		||||
    _debug "_resource_record" "$_resource_record"
 | 
			
		||||
  else
 | 
			
		||||
    _debug "single new add"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then
 | 
			
		||||
    _info "The TXT record already exists. Skipping."
 | 
			
		||||
    _sleep 1
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Adding records"
 | 
			
		||||
 | 
			
		||||
  _aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords>$_resource_record<ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
 | 
			
		||||
 | 
			
		||||
  if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
 | 
			
		||||
    _info "TXT record updated successfully."
 | 
			
		||||
    if [ -n "$AWS_DNS_SLOWRATE" ]; then
 | 
			
		||||
      _info "Slow rate activated: sleeping for $AWS_DNS_SLOWRATE seconds"
 | 
			
		||||
      _sleep "$AWS_DNS_SLOWRATE"
 | 
			
		||||
    else
 | 
			
		||||
      _sleep 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _sleep 1
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain txtvalue
 | 
			
		||||
dns_aws_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
 | 
			
		||||
  AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
 | 
			
		||||
  AWS_DNS_SLOWRATE="${AWS_DNS_SLOWRATE:-$(_readaccountconf_mutable AWS_DNS_SLOWRATE)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
 | 
			
		||||
    _use_container_role || _use_instance_role
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    _sleep 1
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _info "Getting existing records for $fulldomain"
 | 
			
		||||
  if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
 | 
			
		||||
    _sleep 1
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" "<Name>$fulldomain.</Name>"; then
 | 
			
		||||
    _resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
 | 
			
		||||
    _debug "_resource_record" "$_resource_record"
 | 
			
		||||
  else
 | 
			
		||||
    _debug "no records exist, skip"
 | 
			
		||||
    _sleep 1
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords>$_resource_record</ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
 | 
			
		||||
 | 
			
		||||
  if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
 | 
			
		||||
    _info "TXT record deleted successfully."
 | 
			
		||||
    if [ -n "$AWS_DNS_SLOWRATE" ]; then
 | 
			
		||||
      _info "Slow rate activated: sleeping for $AWS_DNS_SLOWRATE seconds"
 | 
			
		||||
      _sleep "$AWS_DNS_SLOWRATE"
 | 
			
		||||
    else
 | 
			
		||||
      _sleep 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _sleep 1
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  # 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 | sed 's/\./\\./g')
 | 
			
		||||
    _debug "Checking domain: $h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      _error "invalid domain"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # iterate over paginated result for list_hosted_zones
 | 
			
		||||
    aws_rest GET "2013-04-01/hostedzone"
 | 
			
		||||
    while true; do
 | 
			
		||||
      if _contains "$response" "<Name>$h.</Name>"; then
 | 
			
		||||
        hostedzone="$(echo "$response" | tr -d '\n' | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
 | 
			
		||||
        _debug hostedzone "$hostedzone"
 | 
			
		||||
        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")
 | 
			
		||||
            _domain=$h
 | 
			
		||||
            return 0
 | 
			
		||||
          fi
 | 
			
		||||
          _err "Can't find domain with id: $h"
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
      if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
 | 
			
		||||
        _debug "IsTruncated"
 | 
			
		||||
        _nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)"
 | 
			
		||||
        _debug "NextMarker" "$_nextMarker"
 | 
			
		||||
      else
 | 
			
		||||
        break
 | 
			
		||||
      fi
 | 
			
		||||
      _debug "Checking domain: $h - Next Page "
 | 
			
		||||
      aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"
 | 
			
		||||
    done
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_use_container_role() {
 | 
			
		||||
  # automatically set if running inside ECS
 | 
			
		||||
  if [ -z "$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" ]; then
 | 
			
		||||
    _debug "No ECS environment variable detected"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _use_metadata "169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_use_instance_role() {
 | 
			
		||||
  _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
 | 
			
		||||
 | 
			
		||||
  _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 '\"')"
 | 
			
		||||
        _value="${_line#*:}"
 | 
			
		||||
        _debug3 "_key" "$_key"
 | 
			
		||||
        _secure_debug3 "_value" "$_value"
 | 
			
		||||
        case "$_key" in
 | 
			
		||||
        AccessKeyId) echo "AWS_ACCESS_KEY_ID=$_value" ;;
 | 
			
		||||
        SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;;
 | 
			
		||||
        Token) echo "AWS_SESSION_TOKEN=$_value" ;;
 | 
			
		||||
        esac
 | 
			
		||||
      done |
 | 
			
		||||
      paste -sd' ' -
 | 
			
		||||
  )"
 | 
			
		||||
  _secure_debug "_aws_creds" "$_aws_creds"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_aws_creds" ]; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  eval "$_aws_creds"
 | 
			
		||||
  _using_role=true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#method uri qstr data
 | 
			
		||||
aws_rest() {
 | 
			
		||||
  mtd="$1"
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  qsr="$3"
 | 
			
		||||
  data="$4"
 | 
			
		||||
 | 
			
		||||
  _debug mtd "$mtd"
 | 
			
		||||
  _debug ep "$ep"
 | 
			
		||||
  _debug qsr "$qsr"
 | 
			
		||||
  _debug data "$data"
 | 
			
		||||
 | 
			
		||||
  CanonicalURI="/$ep"
 | 
			
		||||
  _debug2 CanonicalURI "$CanonicalURI"
 | 
			
		||||
 | 
			
		||||
  CanonicalQueryString="$qsr"
 | 
			
		||||
  _debug2 CanonicalQueryString "$CanonicalQueryString"
 | 
			
		||||
 | 
			
		||||
  RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
 | 
			
		||||
  _debug2 RequestDate "$RequestDate"
 | 
			
		||||
 | 
			
		||||
  #RequestDate="20161120T141056Z" ##############
 | 
			
		||||
 | 
			
		||||
  export _H1="x-amz-date: $RequestDate"
 | 
			
		||||
 | 
			
		||||
  aws_host="$AWS_HOST"
 | 
			
		||||
  CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n"
 | 
			
		||||
  SignedHeaders="host;x-amz-date"
 | 
			
		||||
  if [ -n "$AWS_SESSION_TOKEN" ]; then
 | 
			
		||||
    export _H3="x-amz-security-token: $AWS_SESSION_TOKEN"
 | 
			
		||||
    CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n"
 | 
			
		||||
    SignedHeaders="${SignedHeaders};x-amz-security-token"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 CanonicalHeaders "$CanonicalHeaders"
 | 
			
		||||
  _debug2 SignedHeaders "$SignedHeaders"
 | 
			
		||||
 | 
			
		||||
  RequestPayload="$data"
 | 
			
		||||
  _debug2 RequestPayload "$RequestPayload"
 | 
			
		||||
 | 
			
		||||
  Hash="sha256"
 | 
			
		||||
 | 
			
		||||
  CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)"
 | 
			
		||||
  _debug2 CanonicalRequest "$CanonicalRequest"
 | 
			
		||||
 | 
			
		||||
  HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)"
 | 
			
		||||
  _debug2 HashedCanonicalRequest "$HashedCanonicalRequest"
 | 
			
		||||
 | 
			
		||||
  Algorithm="AWS4-HMAC-SHA256"
 | 
			
		||||
  _debug2 Algorithm "$Algorithm"
 | 
			
		||||
 | 
			
		||||
  RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
 | 
			
		||||
  _debug2 RequestDateOnly "$RequestDateOnly"
 | 
			
		||||
 | 
			
		||||
  Region="us-east-1"
 | 
			
		||||
  Service="route53"
 | 
			
		||||
 | 
			
		||||
  CredentialScope="$RequestDateOnly/$Region/$Service/aws4_request"
 | 
			
		||||
  _debug2 CredentialScope "$CredentialScope"
 | 
			
		||||
 | 
			
		||||
  StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"
 | 
			
		||||
 | 
			
		||||
  _debug2 StringToSign "$StringToSign"
 | 
			
		||||
 | 
			
		||||
  kSecret="AWS4$AWS_SECRET_ACCESS_KEY"
 | 
			
		||||
 | 
			
		||||
  #kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
 | 
			
		||||
 | 
			
		||||
  _secure_debug2 kSecret "$kSecret"
 | 
			
		||||
 | 
			
		||||
  kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
 | 
			
		||||
  _secure_debug2 kSecretH "$kSecretH"
 | 
			
		||||
 | 
			
		||||
  kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
 | 
			
		||||
  _debug2 kDateH "$kDateH"
 | 
			
		||||
 | 
			
		||||
  kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)"
 | 
			
		||||
  _debug2 kRegionH "$kRegionH"
 | 
			
		||||
 | 
			
		||||
  kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)"
 | 
			
		||||
  _debug2 kServiceH "$kServiceH"
 | 
			
		||||
 | 
			
		||||
  kSigningH="$(printf "%s" "aws4_request" | _hmac "$Hash" "$kServiceH" hex)"
 | 
			
		||||
  _debug2 kSigningH "$kSigningH"
 | 
			
		||||
 | 
			
		||||
  signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)"
 | 
			
		||||
  _debug2 signature "$signature"
 | 
			
		||||
 | 
			
		||||
  Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
 | 
			
		||||
  _debug2 Authorization "$Authorization"
 | 
			
		||||
 | 
			
		||||
  _H2="Authorization: $Authorization"
 | 
			
		||||
  _debug _H2 "$_H2"
 | 
			
		||||
 | 
			
		||||
  url="$AWS_URL/$ep"
 | 
			
		||||
  if [ "$qsr" ]; then
 | 
			
		||||
    url="$AWS_URL/$ep?$qsr"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$mtd" = "GET" ]; then
 | 
			
		||||
    response="$(_get "$url")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_post "$data" "$url")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _ret="$?"
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  if [ "$_ret" = "0" ]; then
 | 
			
		||||
    if _contains "$response" "<ErrorResponse"; then
 | 
			
		||||
      _err "Response error:$response"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return "$_ret"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										208
									
								
								dnsapi/dns_azion.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								dnsapi/dns_azion.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,208 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_azion_info='Azion.om
 | 
			
		||||
Site: Azion.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_azion
 | 
			
		||||
Options:
 | 
			
		||||
 AZION_Email Email
 | 
			
		||||
 AZION_Password Password
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/3555
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
AZION_Api="https://api.azionapi.net"
 | 
			
		||||
 | 
			
		||||
########  Public functions ########
 | 
			
		||||
 | 
			
		||||
# Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
dns_azion_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _debug "Detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Domain not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
 | 
			
		||||
  _info "Add or update record"
 | 
			
		||||
  _get_record "$_domain_id" "$_sub_domain"
 | 
			
		||||
  if [ "$record_id" ]; then
 | 
			
		||||
    _payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [$answers_list, \"$txtvalue\"], \"ttl\": 20}"
 | 
			
		||||
    if _azion_rest PUT "intelligent_dns/$_domain_id/records/$record_id" "$_payload"; then
 | 
			
		||||
      if _contains "$response" "$txtvalue"; then
 | 
			
		||||
        _info "Record updated."
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _payload="{\"record_type\": \"TXT\", \"entry\": \"$_sub_domain\", \"answers_list\": [\"$txtvalue\"], \"ttl\": 20}"
 | 
			
		||||
    if _azion_rest POST "intelligent_dns/$_domain_id/records" "$_payload"; then
 | 
			
		||||
      if _contains "$response" "$txtvalue"; then
 | 
			
		||||
        _info "Record added."
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Failed to add or update record."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: fulldomain txtvalue
 | 
			
		||||
# Used to remove the txt record after validation
 | 
			
		||||
dns_azion_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _debug "Detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Domain not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
 | 
			
		||||
  _info "Removing record"
 | 
			
		||||
  _get_record "$_domain_id" "$_sub_domain"
 | 
			
		||||
  if [ "$record_id" ]; then
 | 
			
		||||
    if _azion_rest DELETE "intelligent_dns/$_domain_id/records/$record_id"; then
 | 
			
		||||
      _info "Record removed."
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Failed to remove record."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _info "Record not found or already removed."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
# Usage: _acme-challenge.www.domain.com
 | 
			
		||||
# returns
 | 
			
		||||
#  _sub_domain=_acme-challenge.www
 | 
			
		||||
#  _domain=domain.com
 | 
			
		||||
#  _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  if ! _azion_rest GET "intelligent_dns"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      # not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"domain\":\"$h\""; then
 | 
			
		||||
      _domain_id=$(echo "$response" | tr '{' "\n" | grep "\"domain\":\"$h\"" | _egrep_o "\"id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      _debug _domain_id "$_domain_id"
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_record() {
 | 
			
		||||
  _domain_id=$1
 | 
			
		||||
  _record=$2
 | 
			
		||||
 | 
			
		||||
  if ! _azion_rest GET "intelligent_dns/$_domain_id/records"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" "\"entry\":\"$_record\""; then
 | 
			
		||||
    _json_record=$(echo "$response" | tr '{' "\n" | grep "\"entry\":\"$_record\"")
 | 
			
		||||
    if [ "$_json_record" ]; then
 | 
			
		||||
      record_id=$(echo "$_json_record" | _egrep_o "\"record_id\":[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      answers_list=$(echo "$_json_record" | _egrep_o "\"answers_list\":\[.*\]" | _head_n 1 | cut -d : -f 2 | tr -d \[\])
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_token() {
 | 
			
		||||
  AZION_Email="${AZION_Email:-$(_readaccountconf_mutable AZION_Email)}"
 | 
			
		||||
  AZION_Password="${AZION_Password:-$(_readaccountconf_mutable AZION_Password)}"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$AZION_Email" "@"; then
 | 
			
		||||
    _err "It seems that the AZION_Email is not a valid email address. Revalidate your environments."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AZION_Email" ] || [ -z "$AZION_Password" ]; then
 | 
			
		||||
    _err "You didn't specified a AZION_Email/AZION_Password to generate Azion token."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable AZION_Email "$AZION_Email"
 | 
			
		||||
  _saveaccountconf_mutable AZION_Password "$AZION_Password"
 | 
			
		||||
 | 
			
		||||
  _basic_auth=$(printf "%s:%s" "$AZION_Email" "$AZION_Password" | _base64)
 | 
			
		||||
  _debug _basic_auth "$_basic_auth"
 | 
			
		||||
 | 
			
		||||
  export _H1="Accept: application/json; version=3"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
  export _H3="Authorization: Basic $_basic_auth"
 | 
			
		||||
 | 
			
		||||
  response="$(_post "" "$AZION_Api/tokens" "" "POST")"
 | 
			
		||||
  if _contains "$response" "\"token\":\"" >/dev/null; then
 | 
			
		||||
    _azion_token=$(echo "$response" | _egrep_o "\"token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
 | 
			
		||||
    export AZION_Token="$_azion_token"
 | 
			
		||||
  else
 | 
			
		||||
    _err "Failed to generate Azion token"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_azion_rest() {
 | 
			
		||||
  _method=$1
 | 
			
		||||
  _uri="$2"
 | 
			
		||||
  _data="$3"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$AZION_Token" ]; then
 | 
			
		||||
    _get_token
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 token "$AZION_Token"
 | 
			
		||||
 | 
			
		||||
  export _H1="Accept: application/json; version=3"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
  export _H3="Authorization: token $AZION_Token"
 | 
			
		||||
 | 
			
		||||
  if [ "$_method" != "GET" ]; then
 | 
			
		||||
    _debug _data "$_data"
 | 
			
		||||
    response="$(_post "$_data" "$AZION_Api/$_uri" "" "$_method")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$AZION_Api/$_uri")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $_method $_uri $_data"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										428
									
								
								dnsapi/dns_azure.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										428
									
								
								dnsapi/dns_azure.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,428 @@
 | 
			
		||||
#!/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
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
# Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
#
 | 
			
		||||
# 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() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
 | 
			
		||||
  if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
 | 
			
		||||
    AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
    AZUREDNS_TENANTID=""
 | 
			
		||||
    AZUREDNS_APPID=""
 | 
			
		||||
    AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
    AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
    _err "You didn't specify the Azure Subscription ID"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  #save subscription id to account conf file.
 | 
			
		||||
  _saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID"
 | 
			
		||||
 | 
			
		||||
  AZUREDNS_MANAGEDIDENTITY="${AZUREDNS_MANAGEDIDENTITY:-$(_readaccountconf_mutable AZUREDNS_MANAGEDIDENTITY)}"
 | 
			
		||||
  if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
 | 
			
		||||
    _info "Using Azure managed identity"
 | 
			
		||||
    #save managed identity as preferred authentication method, clear service principal credentials from conf file.
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_MANAGEDIDENTITY "$AZUREDNS_MANAGEDIDENTITY"
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_TENANTID ""
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_APPID ""
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_CLIENTSECRET ""
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_BEARERTOKEN ""
 | 
			
		||||
  else
 | 
			
		||||
    _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_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_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.
 | 
			
		||||
    _saveaccountconf_mutable AZUREDNS_MANAGEDIDENTITY "false"
 | 
			
		||||
    _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
 | 
			
		||||
 | 
			
		||||
  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"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01"
 | 
			
		||||
  _debug "$acmeRecordURI"
 | 
			
		||||
  # Get existing TXT record
 | 
			
		||||
  _azure_rest GET "$acmeRecordURI" "" "$accesstoken"
 | 
			
		||||
  values="{\"value\":[\"$txtvalue\"]}"
 | 
			
		||||
  timestamp="$(_time)"
 | 
			
		||||
  if [ "$_code" = "200" ]; then
 | 
			
		||||
    vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"")"
 | 
			
		||||
    _debug "existing TXT found"
 | 
			
		||||
    _debug "$vlist"
 | 
			
		||||
    existingts="$(echo "$response" | _egrep_o "\"acmetscheck\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")"
 | 
			
		||||
    if [ -z "$existingts" ]; then
 | 
			
		||||
      # the record was not created by acme.sh. Copy the exisiting entires
 | 
			
		||||
      existingts=$timestamp
 | 
			
		||||
    fi
 | 
			
		||||
    _diff="$(_math "$timestamp - $existingts")"
 | 
			
		||||
    _debug "existing txt age: $_diff"
 | 
			
		||||
    # only use recently added records and discard if older than 2 hours because they are probably orphaned
 | 
			
		||||
    if [ "$_diff" -lt 7200 ]; then
 | 
			
		||||
      _debug "existing txt value: $vlist"
 | 
			
		||||
      for v in $vlist; do
 | 
			
		||||
        values="$values ,{\"value\":[\"$v\"]}"
 | 
			
		||||
      done
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  # Add the txtvalue TXT Record
 | 
			
		||||
  body="{\"properties\":{\"metadata\":{\"acmetscheck\":\"$timestamp\"},\"TTL\":10, \"TXTRecords\":[$values]}}"
 | 
			
		||||
  _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken"
 | 
			
		||||
  if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
 | 
			
		||||
    _info "validation value added"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "error adding validation value ($_code)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: fulldomain txtvalue
 | 
			
		||||
# Used to remove the txt record after validation
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
 | 
			
		||||
  if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
 | 
			
		||||
    AZUREDNS_SUBSCRIPTIONID=""
 | 
			
		||||
    AZUREDNS_TENANTID=""
 | 
			
		||||
    AZUREDNS_APPID=""
 | 
			
		||||
    AZUREDNS_CLIENTSECRET=""
 | 
			
		||||
    AZUREDNS_BEARERTOKEN=""
 | 
			
		||||
    _err "You didn't specify the Azure Subscription ID "
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  AZUREDNS_MANAGEDIDENTITY="${AZUREDNS_MANAGEDIDENTITY:-$(_readaccountconf_mutable AZUREDNS_MANAGEDIDENTITY)}"
 | 
			
		||||
  if [ "$AZUREDNS_MANAGEDIDENTITY" = true ]; then
 | 
			
		||||
    _info "Using Azure managed identity"
 | 
			
		||||
  else
 | 
			
		||||
    _info "You didn't ask to use Azure managed identity, checking service principal credentials 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_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_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
 | 
			
		||||
 | 
			
		||||
  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"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01"
 | 
			
		||||
  _debug "$acmeRecordURI"
 | 
			
		||||
  # Get existing TXT record
 | 
			
		||||
  _azure_rest GET "$acmeRecordURI" "" "$accesstoken"
 | 
			
		||||
  timestamp="$(_time)"
 | 
			
		||||
  if [ "$_code" = "200" ]; then
 | 
			
		||||
    vlist="$(echo "$response" | _egrep_o "\"value\"\\s*:\\s*\\[\\s*\"[^\"]*\"\\s*]" | cut -d : -f 2 | tr -d "[]\"" | grep -v -- "$txtvalue")"
 | 
			
		||||
    values=""
 | 
			
		||||
    comma=""
 | 
			
		||||
    for v in $vlist; do
 | 
			
		||||
      values="$values$comma{\"value\":[\"$v\"]}"
 | 
			
		||||
      comma=","
 | 
			
		||||
    done
 | 
			
		||||
    if [ -z "$values" ]; then
 | 
			
		||||
      # No values left remove record
 | 
			
		||||
      _debug "removing validation record completely $acmeRecordURI"
 | 
			
		||||
      _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken"
 | 
			
		||||
      if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then
 | 
			
		||||
        _info "validation record removed"
 | 
			
		||||
      else
 | 
			
		||||
        _err "error removing validation record ($_code)"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      # Remove only txtvalue from the TXT Record
 | 
			
		||||
      body="{\"properties\":{\"metadata\":{\"acmetscheck\":\"$timestamp\"},\"TTL\":10, \"TXTRecords\":[$values]}}"
 | 
			
		||||
      _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken"
 | 
			
		||||
      if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
 | 
			
		||||
        _info "validation value removed"
 | 
			
		||||
        return 0
 | 
			
		||||
      else
 | 
			
		||||
        _err "error removing validation value ($_code)"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
###################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_azure_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  accesstoken="$4"
 | 
			
		||||
 | 
			
		||||
  MAX_REQUEST_RETRY_TIMES=5
 | 
			
		||||
  _request_retry_times=0
 | 
			
		||||
  while [ "${_request_retry_times}" -lt "$MAX_REQUEST_RETRY_TIMES" ]; do
 | 
			
		||||
    _debug3 _request_retry_times "$_request_retry_times"
 | 
			
		||||
    export _H1="authorization: Bearer $accesstoken"
 | 
			
		||||
    export _H2="accept: application/json"
 | 
			
		||||
    export _H3="Content-Type: application/json"
 | 
			
		||||
    # clear headers from previous request to avoid getting wrong http code on timeouts
 | 
			
		||||
    : >"$HTTP_HEADER"
 | 
			
		||||
    _debug "$ep"
 | 
			
		||||
    if [ "$m" != "GET" ]; then
 | 
			
		||||
      _secure_debug2 "data $data"
 | 
			
		||||
      response="$(_post "$data" "$ep" "" "$m")"
 | 
			
		||||
    else
 | 
			
		||||
      response="$(_get "$ep")"
 | 
			
		||||
    fi
 | 
			
		||||
    _ret="$?"
 | 
			
		||||
    _secure_debug2 "response $response"
 | 
			
		||||
    _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
 | 
			
		||||
    _debug "http response code $_code"
 | 
			
		||||
    if [ "$_code" = "401" ]; then
 | 
			
		||||
      # we have an invalid access token set to expired
 | 
			
		||||
      _saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "0"
 | 
			
		||||
      _err "Access denied. Invalid access token. Make sure your Azure settings are correct. See: $wiki"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    # 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"
 | 
			
		||||
      _sleep "$_request_retry_times"
 | 
			
		||||
      continue
 | 
			
		||||
    fi
 | 
			
		||||
    break
 | 
			
		||||
  done
 | 
			
		||||
  if [ "$_request_retry_times" = "$MAX_REQUEST_RETRY_TIMES" ]; then
 | 
			
		||||
    _err "Error Azure REST called was retried $MAX_REQUEST_RETRY_TIMES times."
 | 
			
		||||
    _err "Calling $ep failed."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  response="$(echo "$response" | _normalizeJson)"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
## 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_ACCESSTOKEN:-$(_readaccountconf_mutable AZUREDNS_ACCESSTOKEN)}"
 | 
			
		||||
  expires_on="${AZUREDNS_TOKENVALIDTO:-$(_readaccountconf_mutable AZUREDNS_TOKENVALIDTO)}"
 | 
			
		||||
 | 
			
		||||
  # can we reuse the bearer token?
 | 
			
		||||
  if [ -n "$accesstoken" ] && [ -n "$expires_on" ]; then
 | 
			
		||||
    if [ "$(_time)" -lt "$expires_on" ]; then
 | 
			
		||||
      # brearer token is still valid - reuse it
 | 
			
		||||
      _debug "reusing bearer token"
 | 
			
		||||
      printf "%s" "$accesstoken"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _debug "bearer token expired"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "getting new bearer token"
 | 
			
		||||
 | 
			
		||||
  if [ "$managedIdentity" = true ]; then
 | 
			
		||||
    # https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
 | 
			
		||||
    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 \")
 | 
			
		||||
  else
 | 
			
		||||
    export _H1="accept: application/json"
 | 
			
		||||
    export _H2="Content-Type: application/x-www-form-urlencoded"
 | 
			
		||||
    body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials"
 | 
			
		||||
    _secure_debug2 "data $body"
 | 
			
		||||
    response="$(_post "$body" "https://login.microsoftonline.com/$tenantID/oauth2/token" "" "POST")"
 | 
			
		||||
    _ret="$?"
 | 
			
		||||
    _secure_debug2 "response $response"
 | 
			
		||||
    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 \")
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$accesstoken" ]; then
 | 
			
		||||
    _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_ACCESSTOKEN "$accesstoken"
 | 
			
		||||
  _saveaccountconf_mutable AZUREDNS_TOKENVALIDTO "$expires_on"
 | 
			
		||||
  printf "%s" "$accesstoken"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  subscriptionId=$2
 | 
			
		||||
  accesstoken=$3
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  ## 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)
 | 
			
		||||
    _debug2 "Checking domain: $h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      _err "Invalid domain"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
 | 
			
		||||
      _domain_id=$(echo "$response" | _egrep_o "\\{\"id\":\"[^\"]*\\/$h\"" | head -n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        if [ "$i" = 1 ]; then
 | 
			
		||||
          #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")
 | 
			
		||||
        fi
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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 ##################################
 | 
			
		||||
							
								
								
									
										245
									
								
								dnsapi/dns_bunny.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								dnsapi/dns_bunny.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,245 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_bunny_info='Bunny.net
 | 
			
		||||
Site: Bunny.net/dns/
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_bunny
 | 
			
		||||
Options:
 | 
			
		||||
 BUNNY_API_KEY API Key
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/4296
 | 
			
		||||
Author: <nosilver4u@ewww.io>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
#####################  Public functions  #####################
 | 
			
		||||
 | 
			
		||||
## Create the text record for validation.
 | 
			
		||||
## Usage: fulldomain txtvalue
 | 
			
		||||
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_bunny_add() {
 | 
			
		||||
  fulldomain="$(echo "$1" | _lower_case)"
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  BUNNY_API_KEY="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
 | 
			
		||||
  # Check if API Key is set
 | 
			
		||||
  if [ -z "$BUNNY_API_KEY" ]; then
 | 
			
		||||
    BUNNY_API_KEY=""
 | 
			
		||||
    _err "You did not specify Bunny.net API key."
 | 
			
		||||
    _err "Please export BUNNY_API_KEY and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Using Bunny.net dns validation - add record"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  ## save the env vars (key and domain split location) for later automated use
 | 
			
		||||
  _saveaccountconf_mutable BUNNY_API_KEY "$BUNNY_API_KEY"
 | 
			
		||||
 | 
			
		||||
  ## split the domain for Bunny API
 | 
			
		||||
  if ! _get_base_domain "$fulldomain"; then
 | 
			
		||||
    _err "domain not found in your account for addition"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
 | 
			
		||||
  ## Set the header with our post type and auth key
 | 
			
		||||
  export _H1="Accept: application/json"
 | 
			
		||||
  export _H2="AccessKey: $BUNNY_API_KEY"
 | 
			
		||||
  export _H3="Content-Type: application/json"
 | 
			
		||||
  PURL="https://api.bunny.net/dnszone/$_domain_id/records"
 | 
			
		||||
  PBODY='{"Id":'$_domain_id',"Type":3,"Name":"'$_sub_domain'","Value":"'$txtvalue'","ttl":120}'
 | 
			
		||||
 | 
			
		||||
  _debug PURL "$PURL"
 | 
			
		||||
  _debug PBODY "$PBODY"
 | 
			
		||||
 | 
			
		||||
  ## the create request - POST
 | 
			
		||||
  ## args: BODY, URL, [need64, httpmethod]
 | 
			
		||||
  response="$(_post "$PBODY" "$PURL" "" "PUT")"
 | 
			
		||||
 | 
			
		||||
  ## check response
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error in response: $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
 | 
			
		||||
  ## finished correctly
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
## Remove the txt record after validation.
 | 
			
		||||
## Usage: fulldomain txtvalue
 | 
			
		||||
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_bunny_rm() {
 | 
			
		||||
  fulldomain="$(echo "$1" | _lower_case)"
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  BUNNY_API_KEY="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
 | 
			
		||||
  # Check if API Key Exists
 | 
			
		||||
  if [ -z "$BUNNY_API_KEY" ]; then
 | 
			
		||||
    BUNNY_API_KEY=""
 | 
			
		||||
    _err "You did not specify Bunny.net API key."
 | 
			
		||||
    _err "Please export BUNNY_API_KEY and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Using Bunny.net dns validation - remove record"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  ## split the domain for Bunny API
 | 
			
		||||
  if ! _get_base_domain "$fulldomain"; then
 | 
			
		||||
    _err "Domain not found in your account for TXT record removal"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
 | 
			
		||||
  ## Set the header with our post type and key auth key
 | 
			
		||||
  export _H1="Accept: application/json"
 | 
			
		||||
  export _H2="AccessKey: $BUNNY_API_KEY"
 | 
			
		||||
  ## get URL for the list of DNS records
 | 
			
		||||
  GURL="https://api.bunny.net/dnszone/$_domain_id"
 | 
			
		||||
 | 
			
		||||
  ## 1) Get the domain/zone records
 | 
			
		||||
  ## the fetch request - GET
 | 
			
		||||
  ## args: URL, [onlyheader, timeout]
 | 
			
		||||
  domain_list="$(_get "$GURL")"
 | 
			
		||||
 | 
			
		||||
  ## check response
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error in domain_list response: $domain_list"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 domain_list "$domain_list"
 | 
			
		||||
 | 
			
		||||
  ## 2) search through records
 | 
			
		||||
  ## check for what we are looking for: "Type":3,"Value":"$txtvalue","Name":"$_sub_domain"
 | 
			
		||||
  record="$(echo "$domain_list" | _egrep_o "\"Id\"\s*\:\s*\"*[0-9]+\"*,\s*\"Type\"[^}]*\"Value\"\s*\:\s*\"$txtvalue\"[^}]*\"Name\"\s*\:\s*\"$_sub_domain\"")"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$record" ]; then
 | 
			
		||||
 | 
			
		||||
    ## We found records
 | 
			
		||||
    rec_ids="$(echo "$record" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
 | 
			
		||||
    _debug rec_ids "$rec_ids"
 | 
			
		||||
    if [ -n "$rec_ids" ]; then
 | 
			
		||||
      echo "$rec_ids" | while IFS= read -r rec_id; do
 | 
			
		||||
        ## delete the record
 | 
			
		||||
        ## delete URL for removing the one we dont want
 | 
			
		||||
        DURL="https://api.bunny.net/dnszone/$_domain_id/records/$rec_id"
 | 
			
		||||
 | 
			
		||||
        ## the removal request - DELETE
 | 
			
		||||
        ## args: BODY, URL, [need64, httpmethod]
 | 
			
		||||
        response="$(_post "" "$DURL" "" "DELETE")"
 | 
			
		||||
 | 
			
		||||
        ## check response (sort of)
 | 
			
		||||
        if [ "$?" != "0" ]; then
 | 
			
		||||
          _err "error in remove response: $response"
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
        _debug2 response "$response"
 | 
			
		||||
 | 
			
		||||
      done
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  ## finished correctly
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#####################  Private functions below  #####################
 | 
			
		||||
 | 
			
		||||
## Split the domain provided into the "base domain" and the "start prefix".
 | 
			
		||||
## This function searches for the longest subdomain in your account
 | 
			
		||||
## for the full domain given and splits it into the base domain (zone)
 | 
			
		||||
## and the prefix/record to be added/removed
 | 
			
		||||
## USAGE: fulldomain
 | 
			
		||||
## EG: "_acme-challenge.two.three.four.domain.com"
 | 
			
		||||
## returns
 | 
			
		||||
## _sub_domain="_acme-challenge.two"
 | 
			
		||||
## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists
 | 
			
		||||
## _domain_id=234
 | 
			
		||||
## if only "domain.com" exists it will return
 | 
			
		||||
## _sub_domain="_acme-challenge.two.three.four"
 | 
			
		||||
## _domain="domain.com"
 | 
			
		||||
## _domain_id=234
 | 
			
		||||
_get_base_domain() {
 | 
			
		||||
  # args
 | 
			
		||||
  fulldomain="$(echo "$1" | _lower_case)"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
 | 
			
		||||
  # domain max legal length = 253
 | 
			
		||||
  MAX_DOM=255
 | 
			
		||||
  page=1
 | 
			
		||||
 | 
			
		||||
  ## get a list of domains for the account to check thru
 | 
			
		||||
  ## Set the headers
 | 
			
		||||
  export _H1="Accept: application/json"
 | 
			
		||||
  export _H2="AccessKey: $BUNNY_API_KEY"
 | 
			
		||||
  _debug BUNNY_API_KEY "$BUNNY_API_KEY"
 | 
			
		||||
  ## get URL for the list of domains
 | 
			
		||||
  ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
 | 
			
		||||
  DOMURL="https://api.bunny.net/dnszone"
 | 
			
		||||
 | 
			
		||||
  ## while we dont have a matching domain we keep going
 | 
			
		||||
  while [ -z "$found" ]; do
 | 
			
		||||
    ## get the domain list (current page)
 | 
			
		||||
    domain_list="$(_get "$DOMURL")"
 | 
			
		||||
 | 
			
		||||
    ## check response
 | 
			
		||||
    if [ "$?" != "0" ]; then
 | 
			
		||||
      _err "error in domain_list response: $domain_list"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _debug2 domain_list "$domain_list"
 | 
			
		||||
 | 
			
		||||
    i=1
 | 
			
		||||
    while [ "$i" -gt 0 ]; do
 | 
			
		||||
      ## get next longest domain
 | 
			
		||||
      _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
 | 
			
		||||
      ## check we got something back from our cut (or are we at the end)
 | 
			
		||||
      if [ -z "$_domain" ]; then
 | 
			
		||||
        break
 | 
			
		||||
      fi
 | 
			
		||||
      ## we got part of a domain back - grep it out
 | 
			
		||||
      found="$(echo "$domain_list" | _egrep_o "\"Id\"\s*:\s*\"*[0-9]+\"*,\s*\"Domain\"\s*\:\s*\"$_domain\"")"
 | 
			
		||||
      ## check if it exists
 | 
			
		||||
      if [ -n "$found" ]; then
 | 
			
		||||
        ## exists - exit loop returning the parts
 | 
			
		||||
        sub_point=$(_math "$i" - 1)
 | 
			
		||||
        _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
 | 
			
		||||
        _domain_id="$(echo "$found" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
 | 
			
		||||
        _debug _domain_id "$_domain_id"
 | 
			
		||||
        _debug _domain "$_domain"
 | 
			
		||||
        _debug _sub_domain "$_sub_domain"
 | 
			
		||||
        found=""
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      ## increment cut point $i
 | 
			
		||||
      i=$(_math "$i" + 1)
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    if [ -z "$found" ]; then
 | 
			
		||||
      page=$(_math "$page" + 1)
 | 
			
		||||
      nextpage="https://api.bunny.net/dnszone?page=$page"
 | 
			
		||||
      ## Find the next page if we don't have a match.
 | 
			
		||||
      hasnextpage="$(echo "$domain_list" | _egrep_o "\"HasMoreItems\"\s*:\s*true")"
 | 
			
		||||
      if [ -z "$hasnextpage" ]; then
 | 
			
		||||
        _err "No record and no nextpage in Bunny.net domain search."
 | 
			
		||||
        found=""
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
      _debug2 nextpage "$nextpage"
 | 
			
		||||
      DOMURL="$nextpage"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  ## We went through the entire domain zone list and didn't find one that matched.
 | 
			
		||||
  ## If we ever get here, something is broken in the code...
 | 
			
		||||
  _err "Domain not found in Bunny.net account, but we should never get here!"
 | 
			
		||||
  found=""
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										267
									
								
								dnsapi/dns_cf.sh
									
									
									
									
									
								
							
							
						
						
									
										267
									
								
								dnsapi/dns_cf.sh
									
									
									
									
									
								
							@@ -1,87 +1,159 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
#
 | 
			
		||||
#CF_Email="xxxx@sss.com"
 | 
			
		||||
 | 
			
		||||
# 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"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_cf_add(){
 | 
			
		||||
dns_cf_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  
 | 
			
		||||
  if [ -z "$CF_Key" ] || [ -z "$CF_Email" ] ; then
 | 
			
		||||
    _err "You don't specify cloudflare api key and email yet."
 | 
			
		||||
    _err "Please create you key and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
 | 
			
		||||
  CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
 | 
			
		||||
  CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
 | 
			
		||||
  CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
 | 
			
		||||
  CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
 | 
			
		||||
  CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
 | 
			
		||||
 | 
			
		||||
  if [ "$CF_Token" ]; then
 | 
			
		||||
    if [ "$CF_Zone_ID" ]; then
 | 
			
		||||
      _savedomainconf CF_Token "$CF_Token"
 | 
			
		||||
      _savedomainconf CF_Account_ID "$CF_Account_ID"
 | 
			
		||||
      _savedomainconf CF_Zone_ID "$CF_Zone_ID"
 | 
			
		||||
    else
 | 
			
		||||
      _saveaccountconf_mutable CF_Token "$CF_Token"
 | 
			
		||||
      _saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
 | 
			
		||||
      _clearaccountconf_mutable CF_Zone_ID
 | 
			
		||||
      _clearaccountconf CF_Zone_ID
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
 | 
			
		||||
      CF_Key=""
 | 
			
		||||
      CF_Email=""
 | 
			
		||||
      _err "You didn't specify a Cloudflare api key and email yet."
 | 
			
		||||
      _err "You can get yours from here https://dash.cloudflare.com/profile."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _contains "$CF_Email" "@"; then
 | 
			
		||||
      _err "It seems that the CF_Email=$CF_Email is not a valid email address."
 | 
			
		||||
      _err "Please check and retry."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    #save the api key and email to the account conf file.
 | 
			
		||||
    _saveaccountconf_mutable CF_Key "$CF_Key"
 | 
			
		||||
    _saveaccountconf_mutable CF_Email "$CF_Email"
 | 
			
		||||
 | 
			
		||||
    _clearaccountconf_mutable CF_Token
 | 
			
		||||
    _clearaccountconf_mutable CF_Account_ID
 | 
			
		||||
    _clearaccountconf_mutable CF_Zone_ID
 | 
			
		||||
    _clearaccountconf CF_Token
 | 
			
		||||
    _clearaccountconf CF_Account_ID
 | 
			
		||||
    _clearaccountconf CF_Zone_ID
 | 
			
		||||
 | 
			
		||||
  fi
 | 
			
		||||
  
 | 
			
		||||
  #save the api key and email to the account conf file.
 | 
			
		||||
  _saveaccountconf CF_Key "$CF_Key"
 | 
			
		||||
  _saveaccountconf CF_Email "$CF_Email"
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root $fulldomain ; then
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
 | 
			
		||||
  
 | 
			
		||||
  if ! printf "$response" | grep \"success\":true > /dev/null ; then
 | 
			
		||||
 | 
			
		||||
  if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  
 | 
			
		||||
  count=$(printf "%s\n" "$response" | _egrep_o \"count\":[^,]* | cut -d : -f 2)
 | 
			
		||||
  _debug count "$count"
 | 
			
		||||
  if [ "$count" = "0" ] ; then
 | 
			
		||||
    _info "Adding record"
 | 
			
		||||
    if _cf_rest POST "zones/$_domain_id/dns_records"  "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
 | 
			
		||||
      if printf $response | grep $fulldomain > /dev/null ; then
 | 
			
		||||
        _info "Added, sleeping 10 seconds"
 | 
			
		||||
        sleep 10
 | 
			
		||||
        #todo: check if the record takes effect
 | 
			
		||||
        return 0
 | 
			
		||||
      else
 | 
			
		||||
        _err "Add txt record error."
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
  # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
 | 
			
		||||
  # we can not use updating anymore.
 | 
			
		||||
  #  count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
 | 
			
		||||
  #  _debug count "$count"
 | 
			
		||||
  #  if [ "$count" = "0" ]; then
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
 | 
			
		||||
    if _contains "$response" "$txtvalue"; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    elif _contains "$response" "The record already exists"; then
 | 
			
		||||
      _info "Already exists, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Add txt record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _err "Add txt record error."
 | 
			
		||||
  else
 | 
			
		||||
    _info "Updating record"
 | 
			
		||||
    record_id=$(printf "%s\n" "$response" | _egrep_o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \"| head -1)
 | 
			
		||||
    _debug "record_id" $record_id
 | 
			
		||||
    
 | 
			
		||||
    _cf_rest PUT "zones/$_domain_id/dns_records/$record_id"  "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}"
 | 
			
		||||
    if [ "$?" = "0" ]; then
 | 
			
		||||
      _info "Updated, sleeping 10 seconds"
 | 
			
		||||
      sleep 10
 | 
			
		||||
      #todo: check if the record takes effect
 | 
			
		||||
      return 0;
 | 
			
		||||
    fi
 | 
			
		||||
    _err "Update error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain txtvalue
 | 
			
		||||
dns_cf_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}"
 | 
			
		||||
  CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}"
 | 
			
		||||
  CF_Zone_ID="${CF_Zone_ID:-$(_readaccountconf_mutable CF_Zone_ID)}"
 | 
			
		||||
  CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
 | 
			
		||||
  CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"
 | 
			
		||||
 | 
			
		||||
####################  Private functions bellow ##################################
 | 
			
		||||
  if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
 | 
			
		||||
    _err "Error: $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ")
 | 
			
		||||
  _debug count "$count"
 | 
			
		||||
  if [ "$count" = "0" ]; then
 | 
			
		||||
    _info "Don't need to remove."
 | 
			
		||||
  else
 | 
			
		||||
    record_id=$(echo "$response" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
 | 
			
		||||
    _debug "record_id" "$record_id"
 | 
			
		||||
    if [ -z "$record_id" ]; then
 | 
			
		||||
      _err "Can not get record id to remove."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if ! _cf_rest DELETE "zones/$_domain_id/dns_records/$record_id"; then
 | 
			
		||||
      _err "Delete record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    echo "$response" | tr -d " " | grep \"success\":true >/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
@@ -89,30 +161,59 @@ dns_cf_add(){
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=2
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
  while [ '1' ] ; do
 | 
			
		||||
    h=$(printf $domain | cut -d . -f $i-100)
 | 
			
		||||
    if [ -z "$h" ] ; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1;
 | 
			
		||||
 | 
			
		||||
  # Use Zone ID directly if provided
 | 
			
		||||
  if [ "$CF_Zone_ID" ]; then
 | 
			
		||||
    if ! _cf_rest GET "zones/$CF_Zone_ID"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    else
 | 
			
		||||
      if echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then
 | 
			
		||||
        _domain=$(echo "$response" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
 | 
			
		||||
        if [ "$_domain" ]; then
 | 
			
		||||
          _cutlength=$((${#domain} - ${#_domain} - 1))
 | 
			
		||||
          _sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
 | 
			
		||||
          _domain_id=$CF_Zone_ID
 | 
			
		||||
          return 0
 | 
			
		||||
        else
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
      else
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    if ! _cf_rest GET "zones?name=$h" ; then
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    if printf $response | grep \"name\":\"$h\" >/dev/null ; then
 | 
			
		||||
      _domain_id=$(printf "%s\n" "$response" | _egrep_o \"id\":\"[^\"]*\" | head -1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      if [ "$_domain_id" ] ; then
 | 
			
		||||
        _sub_domain=$(printf $domain | cut -d . -f 1-$p)
 | 
			
		||||
 | 
			
		||||
    if [ "$CF_Account_ID" ]; then
 | 
			
		||||
      if ! _cf_rest GET "zones?name=$h&account.id=$CF_Account_ID"; then
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      if ! _cf_rest GET "zones?name=$h"; then
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(expr $i + 1)
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
@@ -121,25 +222,31 @@ _cf_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug $ep
 | 
			
		||||
  
 | 
			
		||||
  _H1="X-Auth-Email: $CF_Email"
 | 
			
		||||
  _H2="X-Auth-Key: $CF_Key"
 | 
			
		||||
  _H3="Content-Type: application/json"
 | 
			
		||||
  
 | 
			
		||||
  if [ "$data" ] ; then
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  email_trimmed=$(echo "$CF_Email" | tr -d '"')
 | 
			
		||||
  key_trimmed=$(echo "$CF_Key" | tr -d '"')
 | 
			
		||||
  token_trimmed=$(echo "$CF_Token" | tr -d '"')
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  if [ "$token_trimmed" ]; then
 | 
			
		||||
    export _H2="Authorization: Bearer $token_trimmed"
 | 
			
		||||
  else
 | 
			
		||||
    export _H2="X-Auth-Email: $email_trimmed"
 | 
			
		||||
    export _H3="X-Auth-Key: $key_trimmed"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$CF_Api/$ep" "" $m)"
 | 
			
		||||
    response="$(_post "$data" "$CF_Api/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$CF_Api/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
  
 | 
			
		||||
  if [ "$?" != "0" ] ; then
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										202
									
								
								dnsapi/dns_clouddns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										202
									
								
								dnsapi/dns_clouddns.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_clouddns_info='vshosting.cz CloudDNS
 | 
			
		||||
Site: github.com/vshosting/clouddns
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_clouddns
 | 
			
		||||
Options:
 | 
			
		||||
 CLOUDDNS_EMAIL Email
 | 
			
		||||
 CLOUDDNS_PASSWORD Password
 | 
			
		||||
 CLOUDDNS_CLIENT_ID Client ID
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2699
 | 
			
		||||
Author: Radek Sprta <sprta@vshosting.cz>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
CLOUDDNS_API='https://admin.vshosting.cloud/clouddns'
 | 
			
		||||
CLOUDDNS_LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login'
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_clouddns_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _debug "fulldomain" "$fulldomain"
 | 
			
		||||
 | 
			
		||||
  CLOUDDNS_CLIENT_ID="${CLOUDDNS_CLIENT_ID:-$(_readaccountconf_mutable CLOUDDNS_CLIENT_ID)}"
 | 
			
		||||
  CLOUDDNS_EMAIL="${CLOUDDNS_EMAIL:-$(_readaccountconf_mutable CLOUDDNS_EMAIL)}"
 | 
			
		||||
  CLOUDDNS_PASSWORD="${CLOUDDNS_PASSWORD:-$(_readaccountconf_mutable CLOUDDNS_PASSWORD)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$CLOUDDNS_PASSWORD" ] || [ -z "$CLOUDDNS_EMAIL" ] || [ -z "$CLOUDDNS_CLIENT_ID" ]; then
 | 
			
		||||
    CLOUDDNS_CLIENT_ID=""
 | 
			
		||||
    CLOUDDNS_EMAIL=""
 | 
			
		||||
    CLOUDDNS_PASSWORD=""
 | 
			
		||||
    _err "You didn't specify a CloudDNS password, email and client ID yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if ! _contains "$CLOUDDNS_EMAIL" "@"; then
 | 
			
		||||
    _err "It seems that the CLOUDDNS_EMAIL=$CLOUDDNS_EMAIL is not a valid email address."
 | 
			
		||||
    _err "Please check and retry."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  # Save CloudDNS client id, email and password to config file
 | 
			
		||||
  _saveaccountconf_mutable CLOUDDNS_CLIENT_ID "$CLOUDDNS_CLIENT_ID"
 | 
			
		||||
  _saveaccountconf_mutable CLOUDDNS_EMAIL "$CLOUDDNS_EMAIL"
 | 
			
		||||
  _saveaccountconf_mutable CLOUDDNS_PASSWORD "$CLOUDDNS_PASSWORD"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  # Add TXT record
 | 
			
		||||
  data="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"value\":\"$txtvalue\",\"domainId\":\"$_domain_id\"}"
 | 
			
		||||
  if _clouddns_api POST "record-txt" "$data"; then
 | 
			
		||||
    if _contains "$response" "$txtvalue"; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
    elif _contains "$response" '"code":4136'; then
 | 
			
		||||
      _info "Already exists, OK"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Add TXT record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Publishing record changes"
 | 
			
		||||
  _clouddns_api PUT "domain/$_domain_id/publish" "{\"soaTtl\":300}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: rm _acme-challenge.www.domain.com
 | 
			
		||||
dns_clouddns_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  _debug "fulldomain" "$fulldomain"
 | 
			
		||||
 | 
			
		||||
  CLOUDDNS_CLIENT_ID="${CLOUDDNS_CLIENT_ID:-$(_readaccountconf_mutable CLOUDDNS_CLIENT_ID)}"
 | 
			
		||||
  CLOUDDNS_EMAIL="${CLOUDDNS_EMAIL:-$(_readaccountconf_mutable CLOUDDNS_EMAIL)}"
 | 
			
		||||
  CLOUDDNS_PASSWORD="${CLOUDDNS_PASSWORD:-$(_readaccountconf_mutable CLOUDDNS_PASSWORD)}"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  # Get record ID
 | 
			
		||||
  _clouddns_api GET "domain/$_domain_id"
 | 
			
		||||
  if _contains "$response" "lastDomainRecordList"; then
 | 
			
		||||
    re="\"lastDomainRecordList\".*\"id\":\"([^\"}]*)\"[^}]*\"name\":\"$fulldomain.\","
 | 
			
		||||
    _last_domains=$(echo "$response" | _egrep_o "$re")
 | 
			
		||||
    re2="\"id\":\"([^\"}]*)\"[^}]*\"name\":\"$fulldomain.\","
 | 
			
		||||
    _record_id=$(echo "$_last_domains" | _egrep_o "$re2" | _head_n 1 | cut -d : -f 2 | cut -d , -f 1 | tr -d "\"")
 | 
			
		||||
    _debug _record_id "$_record_id"
 | 
			
		||||
  else
 | 
			
		||||
    _err "Could not retrieve record ID"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Removing record"
 | 
			
		||||
  if _clouddns_api DELETE "record/$_record_id"; then
 | 
			
		||||
    if _contains "$response" "\"error\":"; then
 | 
			
		||||
      _err "Could not remove record"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Publishing record changes"
 | 
			
		||||
  _clouddns_api PUT "domain/$_domain_id/publish" "{\"soaTtl\":300}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
# Usage: _get_root _acme-challenge.www.domain.com
 | 
			
		||||
# Returns:
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
 | 
			
		||||
  # Get domain root
 | 
			
		||||
  data="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$CLOUDDNS_CLIENT_ID\"}]}"
 | 
			
		||||
  _clouddns_api "POST" "domain/search" "$data"
 | 
			
		||||
  domain_slice="$domain"
 | 
			
		||||
  while [ -z "$domain_root" ]; do
 | 
			
		||||
    if _contains "$response" "\"domainName\":\"$domain_slice\.\""; then
 | 
			
		||||
      domain_root="$domain_slice"
 | 
			
		||||
      _debug domain_root "$domain_root"
 | 
			
		||||
    fi
 | 
			
		||||
    domain_slice="$(echo "$domain_slice" | cut -d . -f 2-)"
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  # Get domain id
 | 
			
		||||
  data="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$CLOUDDNS_CLIENT_ID\"}, \
 | 
			
		||||
      {\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}"
 | 
			
		||||
  _clouddns_api "POST" "domain/search" "$data"
 | 
			
		||||
  if _contains "$response" "\"id\":\""; then
 | 
			
		||||
    re='domainType\":\"[^\"]*\",\"id\":\"([^\"]*)\",' # Match domain id
 | 
			
		||||
    _domain_id=$(echo "$response" | _egrep_o "$re" | _head_n 1 | cut -d : -f 3 | tr -d "\",")
 | 
			
		||||
    if [ "$_domain_id" ]; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | sed "s/.$domain_root//")
 | 
			
		||||
      _domain="$domain_root"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
    _err 'Domain name not found on your CloudDNS account'
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: _clouddns_api GET domain/search '{"data": "value"}'
 | 
			
		||||
# Returns:
 | 
			
		||||
#  response='{"message": "api response"}'
 | 
			
		||||
_clouddns_api() {
 | 
			
		||||
  method=$1
 | 
			
		||||
  endpoint="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug endpoint "$endpoint"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$CLOUDDNS_TOKEN" ]; then
 | 
			
		||||
    _clouddns_login
 | 
			
		||||
  fi
 | 
			
		||||
  _debug CLOUDDNS_TOKEN "$CLOUDDNS_TOKEN"
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  export _H2="Authorization: Bearer $CLOUDDNS_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if [ "$method" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$CLOUDDNS_API/$endpoint" "" "$method" | tr -d '\t\r\n ')"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$CLOUDDNS_API/$endpoint" | tr -d '\t\r\n ')"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # shellcheck disable=SC2181
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "Error $endpoint"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Returns:
 | 
			
		||||
#  CLOUDDNS_TOKEN=dslfje2rj23l
 | 
			
		||||
_clouddns_login() {
 | 
			
		||||
  login_data="{\"email\": \"$CLOUDDNS_EMAIL\", \"password\": \"$CLOUDDNS_PASSWORD\"}"
 | 
			
		||||
  response="$(_post "$login_data" "$CLOUDDNS_LOGIN_API" "" "POST" "Content-Type: application/json")"
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" "\"accessToken\":\""; then
 | 
			
		||||
    CLOUDDNS_TOKEN=$(echo "$response" | _egrep_o "\"accessToken\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
 | 
			
		||||
    export CLOUDDNS_TOKEN
 | 
			
		||||
  else
 | 
			
		||||
    echo 'Could not get CloudDNS access token; check your credentials'
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										212
									
								
								dnsapi/dns_cloudns.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										212
									
								
								dnsapi/dns_cloudns.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,212 @@
 | 
			
		||||
#!/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>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
CLOUDNS_API="https://api.cloudns.net"
 | 
			
		||||
DOMAIN_TYPE=
 | 
			
		||||
DOMAIN_MASTER=
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_cloudns_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_cloudns_add() {
 | 
			
		||||
  _info "Using cloudns"
 | 
			
		||||
 | 
			
		||||
  if ! _dns_cloudns_init_check; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  zone="$(_dns_cloudns_get_zone_name "$1")"
 | 
			
		||||
  if [ -z "$zone" ]; then
 | 
			
		||||
    _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  host="$(echo "$1" | sed "s/\.$zone\$//")"
 | 
			
		||||
  record=$2
 | 
			
		||||
 | 
			
		||||
  _debug zone "$zone"
 | 
			
		||||
  _debug host "$host"
 | 
			
		||||
  _debug record "$record"
 | 
			
		||||
 | 
			
		||||
  _info "Adding the TXT record for $1"
 | 
			
		||||
  _dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60"
 | 
			
		||||
  if ! _contains "$response" "\"status\":\"Success\""; then
 | 
			
		||||
    _err "Record cannot be added."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _info "Added."
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: dns_cloudns_rm   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_cloudns_rm() {
 | 
			
		||||
  _info "Using cloudns"
 | 
			
		||||
 | 
			
		||||
  if ! _dns_cloudns_init_check; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$zone" ]; then
 | 
			
		||||
    zone="$(_dns_cloudns_get_zone_name "$1")"
 | 
			
		||||
    if [ -z "$zone" ]; then
 | 
			
		||||
      _err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  host="$(echo "$1" | sed "s/\.$zone\$//")"
 | 
			
		||||
  record=$2
 | 
			
		||||
 | 
			
		||||
  _dns_cloudns_get_zone_info "$zone"
 | 
			
		||||
 | 
			
		||||
  _debug "Type" "$DOMAIN_TYPE"
 | 
			
		||||
  _debug "Cloud Master" "$DOMAIN_MASTER"
 | 
			
		||||
  if _contains "$DOMAIN_TYPE" "cloud"; then
 | 
			
		||||
    zone=$DOMAIN_MASTER
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "ZONE" "$zone"
 | 
			
		||||
 | 
			
		||||
  _dns_cloudns_http_api_call "dns/records.json" "domain-name=$zone&host=$host&type=TXT"
 | 
			
		||||
  if ! _contains "$response" "\"id\":"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
      _debug zone "$zone"
 | 
			
		||||
      _debug host "$host"
 | 
			
		||||
      _debug record "$record"
 | 
			
		||||
      _debug record_id "$record_id"
 | 
			
		||||
 | 
			
		||||
      _info "Deleting the TXT record for $1"
 | 
			
		||||
      _dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id"
 | 
			
		||||
 | 
			
		||||
      if ! _contains "$response" "\"status\":\"Success\""; then
 | 
			
		||||
        _err "The TXT record for $1 cannot be deleted."
 | 
			
		||||
      else
 | 
			
		||||
        _info "Deleted."
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
_dns_cloudns_init_check() {
 | 
			
		||||
  if [ -n "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}"
 | 
			
		||||
  CLOUDNS_SUB_AUTH_ID="${CLOUDNS_SUB_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_SUB_AUTH_ID)}"
 | 
			
		||||
  CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}"
 | 
			
		||||
  if [ -z "$CLOUDNS_AUTH_ID$CLOUDNS_SUB_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then
 | 
			
		||||
    CLOUDNS_AUTH_ID=""
 | 
			
		||||
    CLOUDNS_SUB_AUTH_ID=""
 | 
			
		||||
    CLOUDNS_AUTH_PASSWORD=""
 | 
			
		||||
    _err "You don't specify cloudns api id and password yet."
 | 
			
		||||
    _err "Please create you id and password and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$CLOUDNS_AUTH_ID" ] && [ -z "$CLOUDNS_SUB_AUTH_ID" ]; then
 | 
			
		||||
    _err "CLOUDNS_AUTH_ID or CLOUDNS_SUB_AUTH_ID is not configured"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then
 | 
			
		||||
    _err "CLOUDNS_AUTH_PASSWORD is not configured"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _dns_cloudns_http_api_call "dns/login.json" ""
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "\"status\":\"Success\""; then
 | 
			
		||||
    _err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # save the api id and password to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
 | 
			
		||||
  _saveaccountconf_mutable CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
 | 
			
		||||
  _saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
 | 
			
		||||
 | 
			
		||||
  CLOUDNS_INIT_CHECK_COMPLETED=1
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_dns_cloudns_get_zone_info() {
 | 
			
		||||
  zone=$1
 | 
			
		||||
  _dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zone"
 | 
			
		||||
  if ! _contains "$response" "\"status\":\"Failed\""; then
 | 
			
		||||
    DOMAIN_TYPE=$(echo "$response" | _egrep_o '"type":"[^"]*"' | cut -d : -f 2 | tr -d '"')
 | 
			
		||||
    if _contains "$DOMAIN_TYPE" "cloud"; then
 | 
			
		||||
      DOMAIN_MASTER=$(echo "$response" | _egrep_o '"cloud-master":"[^"]*"' | cut -d : -f 2 | tr -d '"')
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_dns_cloudns_get_zone_name() {
 | 
			
		||||
  i=2
 | 
			
		||||
  while true; do
 | 
			
		||||
    zoneForCheck=$(printf "%s" "$1" | cut -d . -f "$i"-100)
 | 
			
		||||
 | 
			
		||||
    if [ -z "$zoneForCheck" ]; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _debug zoneForCheck "$zoneForCheck"
 | 
			
		||||
 | 
			
		||||
    _dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck"
 | 
			
		||||
 | 
			
		||||
    if ! _contains "$response" "\"status\":\"Failed\""; then
 | 
			
		||||
      echo "$zoneForCheck"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_dns_cloudns_http_api_call() {
 | 
			
		||||
  method=$1
 | 
			
		||||
 | 
			
		||||
  _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
 | 
			
		||||
  _debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
 | 
			
		||||
  _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$CLOUDNS_SUB_AUTH_ID" ]; then
 | 
			
		||||
    auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID"
 | 
			
		||||
  else
 | 
			
		||||
    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=$encoded_password"
 | 
			
		||||
  else
 | 
			
		||||
    data="$auth_user&auth-password=$encoded_password&$2"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  response="$(_get "$CLOUDNS_API/$method?$data")"
 | 
			
		||||
 | 
			
		||||
  _debug response "$response"
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										164
									
								
								dnsapi/dns_cn.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								dnsapi/dns_cn.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,164 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# 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"
 | 
			
		||||
 | 
			
		||||
########  Public functions  #####################
 | 
			
		||||
 | 
			
		||||
dns_cn_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  if ! _cn_login; then
 | 
			
		||||
    _err "login failed"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _cn_get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "_sub_domain $_sub_domain"
 | 
			
		||||
  _debug "_domain $_domain"
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  curData="{\"name\":\"$_sub_domain\",\"ttl\":120,\"type\":\"TXT\",\"data\":\"$txtvalue\"}"
 | 
			
		||||
  curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/")"
 | 
			
		||||
 | 
			
		||||
  _debug "curData $curData"
 | 
			
		||||
  _debug "curResult $curResult"
 | 
			
		||||
 | 
			
		||||
  if _contains "$curResult" ""; then
 | 
			
		||||
    _info "Added, OK"
 | 
			
		||||
 | 
			
		||||
    if ! _cn_commit; then
 | 
			
		||||
      _err "commiting changes failed"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
  else
 | 
			
		||||
    _err "Add txt record error."
 | 
			
		||||
    _debug "curData is $curData"
 | 
			
		||||
    _debug "curResult is $curResult"
 | 
			
		||||
    _err "error adding text record, response was $curResult"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dns_cn_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  if ! _cn_login; then
 | 
			
		||||
    _err "login failed"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _cn_get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Deleting record"
 | 
			
		||||
  curData="{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\"}"
 | 
			
		||||
  curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/delete")"
 | 
			
		||||
  _debug curData is "$curData"
 | 
			
		||||
 | 
			
		||||
  _info "commiting changes"
 | 
			
		||||
  if ! _cn_commit; then
 | 
			
		||||
    _err "commiting changes failed"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Deletet txt record"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
###################  Private functions below  ##################################
 | 
			
		||||
_cn_login() {
 | 
			
		||||
  CN_User="${CN_User:-$(_readaccountconf_mutable CN_User)}"
 | 
			
		||||
  CN_Password="${CN_Password:-$(_readaccountconf_mutable CN_Password)}"
 | 
			
		||||
  if [ -z "$CN_User" ] || [ -z "$CN_Password" ]; then
 | 
			
		||||
    CN_User=""
 | 
			
		||||
    CN_Password=""
 | 
			
		||||
    _err "You must export variables: CN_User and CN_Password"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the config variables to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable CN_User "$CN_User"
 | 
			
		||||
  _saveaccountconf_mutable CN_Password "$CN_Password"
 | 
			
		||||
 | 
			
		||||
  _info "Getting an AUTH-Token"
 | 
			
		||||
  curData="{\"login\":\"${CN_User}\",\"password\":\"${CN_Password}\"}"
 | 
			
		||||
  curResult="$(_post "${curData}" "${CN_API}/auth/token")"
 | 
			
		||||
  _debug "Calling _CN_login: '${curData}' '${CN_API}/auth/token'"
 | 
			
		||||
 | 
			
		||||
  if _contains "${curResult}" '"token":"'; then
 | 
			
		||||
    authToken=$(echo "${curResult}" | cut -d ":" -f2 | cut -d "," -f1 | sed 's/^.\(.*\).$/\1/')
 | 
			
		||||
    export _H1="Authorization: Bearer $authToken"
 | 
			
		||||
    _info "Successfully acquired AUTH-Token"
 | 
			
		||||
    _debug "AUTH-Token: '${authToken}'"
 | 
			
		||||
    _debug "_H1 '${_H1}'"
 | 
			
		||||
  else
 | 
			
		||||
    _err "Couldn't acquire an AUTH-Token"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Commit changes
 | 
			
		||||
_cn_commit() {
 | 
			
		||||
  _info "Commiting changes"
 | 
			
		||||
  _post "" "${CN_API}/dnszones/$h/records/commit"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_cn_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    _debug _H1 "${_H1}"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _cn_zonelist="$(_get ${CN_API}/dnszones/)"
 | 
			
		||||
    _debug _cn_zonelist "${_cn_zonelist}"
 | 
			
		||||
 | 
			
		||||
    if [ "$?" != "0" ]; then
 | 
			
		||||
      _err "something went wrong while getting the zone list"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _debug "Zonelist does not contain domain - iterating "
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
 | 
			
		||||
  done
 | 
			
		||||
  _err "Zonelist does not contain domain - exiting"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										264
									
								
								dnsapi/dns_conoha.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										264
									
								
								dnsapi/dns_conoha.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
#!/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\."
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_conoha_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_conoha_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using conoha"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  _debug "Check uesrname and password"
 | 
			
		||||
  CONOHA_Username="${CONOHA_Username:-$(_readaccountconf_mutable CONOHA_Username)}"
 | 
			
		||||
  CONOHA_Password="${CONOHA_Password:-$(_readaccountconf_mutable CONOHA_Password)}"
 | 
			
		||||
  CONOHA_TenantId="${CONOHA_TenantId:-$(_readaccountconf_mutable CONOHA_TenantId)}"
 | 
			
		||||
  CONOHA_IdentityServiceApi="${CONOHA_IdentityServiceApi:-$(_readaccountconf_mutable CONOHA_IdentityServiceApi)}"
 | 
			
		||||
  if [ -z "$CONOHA_Username" ] || [ -z "$CONOHA_Password" ] || [ -z "$CONOHA_TenantId" ] || [ -z "$CONOHA_IdentityServiceApi" ]; then
 | 
			
		||||
    CONOHA_Username=""
 | 
			
		||||
    CONOHA_Password=""
 | 
			
		||||
    CONOHA_TenantId=""
 | 
			
		||||
    CONOHA_IdentityServiceApi=""
 | 
			
		||||
    _err "You didn't specify a conoha api username and password yet."
 | 
			
		||||
    _err "Please create the user and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable CONOHA_Username "$CONOHA_Username"
 | 
			
		||||
  _saveaccountconf_mutable CONOHA_Password "$CONOHA_Password"
 | 
			
		||||
  _saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId"
 | 
			
		||||
  _saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi"
 | 
			
		||||
 | 
			
		||||
  if token="$(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId")"; then
 | 
			
		||||
    accesstoken="$(printf "%s" "$token" | sed -n 1p)"
 | 
			
		||||
    CONOHA_Api="$(printf "%s" "$token" | sed -n 2p)"
 | 
			
		||||
  else
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  body="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"data\":\"$txtvalue\",\"ttl\":60}"
 | 
			
		||||
  if _conoha_rest POST "$CONOHA_Api/v1/domains/$_domain_id/records" "$body" "$accesstoken"; then
 | 
			
		||||
    if _contains "$response" '"data":"'"$txtvalue"'"'; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Add txt record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Remove the txt record after validation.
 | 
			
		||||
dns_conoha_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using conoha"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  _debug "Check uesrname and password"
 | 
			
		||||
  CONOHA_Username="${CONOHA_Username:-$(_readaccountconf_mutable CONOHA_Username)}"
 | 
			
		||||
  CONOHA_Password="${CONOHA_Password:-$(_readaccountconf_mutable CONOHA_Password)}"
 | 
			
		||||
  CONOHA_TenantId="${CONOHA_TenantId:-$(_readaccountconf_mutable CONOHA_TenantId)}"
 | 
			
		||||
  CONOHA_IdentityServiceApi="${CONOHA_IdentityServiceApi:-$(_readaccountconf_mutable CONOHA_IdentityServiceApi)}"
 | 
			
		||||
  if [ -z "$CONOHA_Username" ] || [ -z "$CONOHA_Password" ] || [ -z "$CONOHA_TenantId" ] || [ -z "$CONOHA_IdentityServiceApi" ]; then
 | 
			
		||||
    CONOHA_Username=""
 | 
			
		||||
    CONOHA_Password=""
 | 
			
		||||
    CONOHA_TenantId=""
 | 
			
		||||
    CONOHA_IdentityServiceApi=""
 | 
			
		||||
    _err "You didn't specify a conoha api username and password yet."
 | 
			
		||||
    _err "Please create the user and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable CONOHA_Username "$CONOHA_Username"
 | 
			
		||||
  _saveaccountconf_mutable CONOHA_Password "$CONOHA_Password"
 | 
			
		||||
  _saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId"
 | 
			
		||||
  _saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi"
 | 
			
		||||
 | 
			
		||||
  if token="$(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId")"; then
 | 
			
		||||
    accesstoken="$(printf "%s" "$token" | sed -n 1p)"
 | 
			
		||||
    CONOHA_Api="$(printf "%s" "$token" | sed -n 2p)"
 | 
			
		||||
  else
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  if ! _conoha_rest GET "$CONOHA_Api/v1/domains/$_domain_id/records" "" "$accesstoken"; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  record_id=$(printf "%s" "$response" | _egrep_o '{[^}]*}' |
 | 
			
		||||
    grep '"type":"TXT"' | grep "\"data\":\"$txtvalue\"" | _egrep_o "\"id\":\"[^\"]*\"" |
 | 
			
		||||
    _head_n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
  if [ -z "$record_id" ]; then
 | 
			
		||||
    _err "Can not get record id to remove."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug record_id "$record_id"
 | 
			
		||||
 | 
			
		||||
  _info "Removing the txt record"
 | 
			
		||||
  if ! _conoha_rest DELETE "$CONOHA_Api/v1/domains/$_domain_id/records/$record_id" "" "$accesstoken"; then
 | 
			
		||||
    _err "Delete record error."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_conoha_rest() {
 | 
			
		||||
  m="$1"
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  accesstoken="$4"
 | 
			
		||||
 | 
			
		||||
  export _H1="Accept: application/json"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
  if [ -n "$accesstoken" ]; then
 | 
			
		||||
    export _H3="X-Auth-Token: $accesstoken"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _secure_debug2 data "$data"
 | 
			
		||||
    response="$(_post "$data" "$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
  _ret="$?"
 | 
			
		||||
  _secure_debug2 response "$response"
 | 
			
		||||
  if [ "$_ret" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  response="$(printf "%s" "$response" | _normalizeJson)"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_conoha_get_accesstoken() {
 | 
			
		||||
  ep="$1"
 | 
			
		||||
  username="$2"
 | 
			
		||||
  password="$3"
 | 
			
		||||
  tenantId="$4"
 | 
			
		||||
 | 
			
		||||
  accesstoken="$(_readaccountconf_mutable conoha_accesstoken)"
 | 
			
		||||
  expires="$(_readaccountconf_mutable conoha_tokenvalidto)"
 | 
			
		||||
  CONOHA_Api="$(_readaccountconf_mutable conoha_dns_ep)"
 | 
			
		||||
 | 
			
		||||
  # can we reuse the access token?
 | 
			
		||||
  if [ -n "$accesstoken" ] && [ -n "$expires" ] && [ -n "$CONOHA_Api" ]; then
 | 
			
		||||
    utc_date="$(_utc_date | sed "s/ /T/")"
 | 
			
		||||
    if expr "$utc_date" "<" "$expires" >/dev/null; then
 | 
			
		||||
      # access token is still valid - reuse it
 | 
			
		||||
      _debug "reusing access token"
 | 
			
		||||
      printf "%s\n%s\n" "$accesstoken" "$CONOHA_Api"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _debug "access token expired"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "getting new access token"
 | 
			
		||||
 | 
			
		||||
  body="$(printf '{"auth":{"passwordCredentials":{"username":"%s","password":"%s"},"tenantId":"%s"}}' "$username" "$password" "$tenantId")"
 | 
			
		||||
  if ! _conoha_rest POST "$ep" "$body" ""; then
 | 
			
		||||
    _err error "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  accesstoken=$(printf "%s" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
  expires=$(printf "%s" "$response" | _egrep_o "\"expires\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2-4 | tr -d \" | tr -d Z) #expect UTC
 | 
			
		||||
  if [ -z "$accesstoken" ] || [ -z "$expires" ]; then
 | 
			
		||||
    _err "no acccess token received. Check your Conoha settings see $WIKI"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable conoha_accesstoken "$accesstoken"
 | 
			
		||||
  _saveaccountconf_mutable conoha_tokenvalidto "$expires"
 | 
			
		||||
 | 
			
		||||
  CONOHA_Api=$(printf "%s" "$response" | _egrep_o 'publicURL":"'"$CONOHA_DNS_EP_PREFIX_REGEXP"'[^"]*"' | _head_n 1 | cut -d : -f 2-3 | tr -d \")
 | 
			
		||||
  if [ -z "$CONOHA_Api" ]; then
 | 
			
		||||
    _err "failed to get conoha dns endpoint url"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable conoha_dns_ep "$CONOHA_Api"
 | 
			
		||||
 | 
			
		||||
  printf "%s\n%s\n" "$accesstoken" "$CONOHA_Api"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  accesstoken="$3"
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100).
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _conoha_rest GET "$ep/v1/domains?name=$h" "" "$accesstoken"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
 | 
			
		||||
      _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
 | 
			
		||||
        _domain=$h
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										185
									
								
								dnsapi/dns_constellix.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								dnsapi/dns_constellix.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,185 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
# shellcheck disable=SC2034
 | 
			
		||||
dns_constellix_info='Constellix.com
 | 
			
		||||
Site: Constellix.com
 | 
			
		||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_constellix
 | 
			
		||||
Options:
 | 
			
		||||
 CONSTELLIX_Key API Key
 | 
			
		||||
 CONSTELLIX_Secret API Secret
 | 
			
		||||
Issues: github.com/acmesh-official/acme.sh/issues/2724
 | 
			
		||||
Author: Wout Decre <wout@canodus.be>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
CONSTELLIX_Api="https://api.dns.constellix.com/v1"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
# Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
dns_constellix_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  CONSTELLIX_Key="${CONSTELLIX_Key:-$(_readaccountconf_mutable CONSTELLIX_Key)}"
 | 
			
		||||
  CONSTELLIX_Secret="${CONSTELLIX_Secret:-$(_readaccountconf_mutable CONSTELLIX_Secret)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$CONSTELLIX_Key" ] || [ -z "$CONSTELLIX_Secret" ]; then
 | 
			
		||||
    _err "You did not specify the Contellix API key and secret yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable CONSTELLIX_Key "$CONSTELLIX_Key"
 | 
			
		||||
  _saveaccountconf_mutable CONSTELLIX_Secret "$CONSTELLIX_Secret"
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # The TXT record might already exist when working with wildcard certificates. In that case, update the record by adding the new value.
 | 
			
		||||
  _debug "Search TXT record"
 | 
			
		||||
  if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
 | 
			
		||||
    if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
 | 
			
		||||
      _info "Adding TXT record"
 | 
			
		||||
      if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"add\":true,\"set\":{\"name\":\"${_sub_domain}\",\"ttl\":60,\"roundRobin\":[{\"value\":\"${txtvalue}\"}]}}]"; then
 | 
			
		||||
        if printf -- "%s" "$response" | grep "{\"success\":\"1 record(s) added, 0 record(s) updated, 0 record(s) deleted\"}" >/dev/null; then
 | 
			
		||||
          _info "Added"
 | 
			
		||||
          return 0
 | 
			
		||||
        else
 | 
			
		||||
          _err "Error adding TXT record"
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      _record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
 | 
			
		||||
      if _constellix_rest GET "domains/${_domain_id}/records/TXT/${_record_id}"; then
 | 
			
		||||
        _new_rr_values=$(printf "%s\n" "$response" | _egrep_o '"roundRobin":\[[^]]*\]' | sed "s/\]$/,{\"value\":\"${txtvalue}\"}]/")
 | 
			
		||||
        _debug _new_rr_values "$_new_rr_values"
 | 
			
		||||
        _info "Updating TXT record"
 | 
			
		||||
        if _constellix_rest PUT "domains/${_domain_id}/records/TXT/${_record_id}" "{\"name\":\"${_sub_domain}\",\"ttl\":60,${_new_rr_values}}"; then
 | 
			
		||||
          if printf -- "%s" "$response" | grep "{\"success\":\"Record.*updated successfully\"}" >/dev/null; then
 | 
			
		||||
            _info "Updated"
 | 
			
		||||
            return 0
 | 
			
		||||
          elif printf -- "%s" "$response" | grep "{\"errors\":\[\"Contents are identical\"\]}" >/dev/null; then
 | 
			
		||||
            _info "Already exists, no need to update"
 | 
			
		||||
            return 0
 | 
			
		||||
          else
 | 
			
		||||
            _err "Error updating TXT record"
 | 
			
		||||
          fi
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: fulldomain txtvalue
 | 
			
		||||
# Used to remove the txt record after validation
 | 
			
		||||
dns_constellix_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  CONSTELLIX_Key="${CONSTELLIX_Key:-$(_readaccountconf_mutable CONSTELLIX_Key)}"
 | 
			
		||||
  CONSTELLIX_Secret="${CONSTELLIX_Secret:-$(_readaccountconf_mutable CONSTELLIX_Secret)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$CONSTELLIX_Key" ] || [ -z "$CONSTELLIX_Secret" ]; then
 | 
			
		||||
    _err "You did not specify the Contellix API key and secret yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # The TXT record might have been removed already when working with some wildcard certificates.
 | 
			
		||||
  _debug "Search TXT record"
 | 
			
		||||
  if _constellix_rest GET "domains/${_domain_id}/records/TXT/search?exact=${_sub_domain}"; then
 | 
			
		||||
    if printf -- "%s" "$response" | grep "{\"errors\":\[\"Requested record was not found\"\]}" >/dev/null; then
 | 
			
		||||
      _info "Removed"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _info "Removing TXT record"
 | 
			
		||||
      if _constellix_rest POST "domains/${_domain_id}/records" "[{\"type\":\"txt\",\"delete\":true,\"filter\":{\"field\":\"name\",\"op\":\"eq\",\"value\":\"${_sub_domain}\"}}]"; then
 | 
			
		||||
        if printf -- "%s" "$response" | grep "{\"success\":\"0 record(s) added, 0 record(s) updated, 1 record(s) deleted\"}" >/dev/null; then
 | 
			
		||||
          _info "Removed"
 | 
			
		||||
          return 0
 | 
			
		||||
        else
 | 
			
		||||
          _err "Error removing TXT record"
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$(echo "$1" | _lower_case)
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  _debug "Detecting root zone"
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _constellix_rest GET "domains/search?exact=$h"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"name\":\"$h\""; then
 | 
			
		||||
      _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]*" | cut -d ':' -f 2)
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-"$p")
 | 
			
		||||
        _domain="$h"
 | 
			
		||||
 | 
			
		||||
        _debug _domain_id "$_domain_id"
 | 
			
		||||
        _debug _sub_domain "$_sub_domain"
 | 
			
		||||
        _debug _domain "$_domain"
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_constellix_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  # Prevent rate limit
 | 
			
		||||
  _sleep 2
 | 
			
		||||
 | 
			
		||||
  rdate=$(date +"%s")"000"
 | 
			
		||||
  hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)
 | 
			
		||||
 | 
			
		||||
  export _H1="x-cnsdns-apiKey: $CONSTELLIX_Key"
 | 
			
		||||
  export _H2="x-cnsdns-requestDate: $rdate"
 | 
			
		||||
  export _H3="x-cnsdns-hmac: $hmac"
 | 
			
		||||
  export _H4="Accept: application/json"
 | 
			
		||||
  export _H5="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$CONSTELLIX_Api/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$CONSTELLIX_Api/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "Error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user