mirror of
				https://github.com/acmesh-official/acme.sh
				synced 2025-11-04 13:55:56 +08:00 
			
		
		
		
	Compare commits
	
		
			679 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b7caf7a016 | ||
| 
						 | 
					891198e4f3 | ||
| 
						 | 
					38c5910be4 | ||
| 
						 | 
					327e2fb0a4 | ||
| 
						 | 
					c20c219990 | ||
| 
						 | 
					4c30250782 | ||
| 
						 | 
					caf23f9a04 | ||
| 
						 | 
					beab808b76 | ||
| 
						 | 
					6c8920f63e | ||
| 
						 | 
					51be15f66d | ||
| 
						 | 
					110e25e784 | ||
| 
						 | 
					8414100d0b | ||
| 
						 | 
					a3f4cb154e | ||
| 
						 | 
					e6e22a1ca1 | ||
| 
						 | 
					b937665b90 | ||
| 
						 | 
					a7bc2293c0 | ||
| 
						 | 
					0d25f7612b | ||
| 
						 | 
					84e4181ed7 | ||
| 
						 | 
					f66a29d1c3 | ||
| 
						 | 
					dbd3881cea | ||
| 
						 | 
					7eb6bbe65f | ||
| 
						 | 
					a570fda1cb | ||
| 
						 | 
					3b06fa6523 | ||
| 
						 | 
					dcdbe2fbb8 | ||
| 
						 | 
					dc1f36da43 | ||
| 
						 | 
					05dbd395e6 | ||
| 
						 | 
					69e7360cc3 | ||
| 
						 | 
					7ef2533b98 | ||
| 
						 | 
					97f87c4229 | ||
| 
						 | 
					42a5cd961d | ||
| 
						 | 
					bf00d3157f | ||
| 
						 | 
					cf3ff4c136 | ||
| 
						 | 
					7fe06adcfd | ||
| 
						 | 
					ae3e5dbf2c | ||
| 
						 | 
					20304590b4 | ||
| 
						 | 
					2d8c0c0131 | ||
| 
						 | 
					70f4cad2ca | ||
| 
						 | 
					1f777a94a7 | ||
| 
						 | 
					7560c64f46 | ||
| 
						 | 
					bddde60522 | ||
| 
						 | 
					e9366f8c76 | ||
| 
						 | 
					4dba84d09e | ||
| 
						 | 
					0cce2d6098 | ||
| 
						 | 
					acbd8bce21 | ||
| 
						 | 
					dea8a08b64 | ||
| 
						 | 
					dde1bab1a8 | ||
| 
						 | 
					799e402077 | ||
| 
						 | 
					ce629e8e70 | ||
| 
						 | 
					20cfc4ac66 | ||
| 
						 | 
					132d5e8253 | ||
| 
						 | 
					cb8b341fb4 | ||
| 
						 | 
					982c54b605 | ||
| 
						 | 
					67f543332a | ||
| 
						 | 
					88ac4086c2 | ||
| 
						 | 
					15f96b7239 | ||
| 
						 | 
					0ea84ad799 | ||
| 
						 | 
					1522b713da | ||
| 
						 | 
					fe6b27bb59 | ||
| 
						 | 
					df14b15397 | ||
| 
						 | 
					7dd12044de | ||
| 
						 | 
					06e12a30e7 | ||
| 
						 | 
					d3fefd223d | ||
| 
						 | 
					c0639c6608 | ||
| 
						 | 
					05a2eb3df4 | ||
| 
						 | 
					d4befeb536 | ||
| 
						 | 
					f7f1168aad | ||
| 
						 | 
					d6cf15368a | ||
| 
						 | 
					a5fbf3fb80 | ||
| 
						 | 
					3cf8f78745 | ||
| 
						 | 
					59dab6eac7 | ||
| 
						 | 
					553d861b8a | ||
| 
						 | 
					7b5d94d062 | ||
| 
						 | 
					cb021efaee | ||
| 
						 | 
					c2344f3717 | ||
| 
						 | 
					f537c606f7 | ||
| 
						 | 
					c8f48a4a90 | ||
| 
						 | 
					c2ad1b4e46 | ||
| 
						 | 
					ba9d146d6c | ||
| 
						 | 
					a5b04a0328 | ||
| 
						 | 
					01249d0cb9 | ||
| 
						 | 
					71c273fbcb | ||
| 
						 | 
					aa9cbf7c55 | ||
| 
						 | 
					429b18ed48 | ||
| 
						 | 
					2690c05781 | ||
| 
						 | 
					e3b688c9d8 | ||
| 
						 | 
					41b6f18a5d | ||
| 
						 | 
					5a59c39036 | ||
| 
						 | 
					a02dd18ad7 | ||
| 
						 | 
					16bdc7d0a3 | ||
| 
						 | 
					40002e8040 | ||
| 
						 | 
					6748c55c04 | ||
| 
						 | 
					deb63b4adf | ||
| 
						 | 
					015a9b9271 | ||
| 
						 | 
					ab2305e259 | ||
| 
						 | 
					b99c998057 | ||
| 
						 | 
					6c0a7144f6 | ||
| 
						 | 
					bf50fce5bd | ||
| 
						 | 
					8718b156c4 | ||
| 
						 | 
					7bbdd1f839 | ||
| 
						 | 
					ffed1a4afa | ||
| 
						 | 
					1bfd3642e8 | ||
| 
						 | 
					577f4e0cc3 | ||
| 
						 | 
					e2f05f3fc9 | ||
| 
						 | 
					27f30631ed | ||
| 
						 | 
					1ccfa96c2e | ||
| 
						 | 
					a2c64e79ff | ||
| 
						 | 
					7b623f85cd | ||
| 
						 | 
					ed63eb6833 | ||
| 
						 | 
					b8d0d3c242 | ||
| 
						 | 
					fe1bfe9ae1 | ||
| 
						 | 
					7154c9ee5d | ||
| 
						 | 
					057c95bd1c | ||
| 
						 | 
					0cafc00c4f | ||
| 
						 | 
					75d2898efd | ||
| 
						 | 
					764a4c99fa | ||
| 
						 | 
					160513c671 | ||
| 
						 | 
					a2af26635f | ||
| 
						 | 
					63869deeb2 | ||
| 
						 | 
					91e387e8b9 | ||
| 
						 | 
					7d13146859 | ||
| 
						 | 
					699d2b7e7e | ||
| 
						 | 
					257de15c73 | ||
| 
						 | 
					5a0225d033 | ||
| 
						 | 
					bd2d0e6ad3 | ||
| 
						 | 
					0c0d1d4e52 | ||
| 
						 | 
					eab9603921 | ||
| 
						 | 
					c07db3aa14 | ||
| 
						 | 
					a19f7481b2 | ||
| 
						 | 
					2905edce35 | ||
| 
						 | 
					04a5d794ac | ||
| 
						 | 
					264b9819ff | ||
| 
						 | 
					4610204c83 | ||
| 
						 | 
					ecf1f17cf4 | ||
| 
						 | 
					9f942a6b65 | ||
| 
						 | 
					f4ed1b32b8 | ||
| 
						 | 
					ec0e871592 | ||
| 
						 | 
					7a756ebc4d | ||
| 
						 | 
					16dc21afff | ||
| 
						 | 
					3a1c6d84f0 | ||
| 
						 | 
					e684abdacd | ||
| 
						 | 
					e275cb1efd | ||
| 
						 | 
					60315e5b91 | ||
| 
						 | 
					43b1a4bf5a | ||
| 
						 | 
					cdb238e41c | ||
| 
						 | 
					3871e44d9c | ||
| 
						 | 
					5a51454d13 | ||
| 
						 | 
					d102943a32 | ||
| 
						 | 
					a0b27ddbd8 | ||
| 
						 | 
					b950b04e89 | ||
| 
						 | 
					c9a55f395b | ||
| 
						 | 
					424da01878 | ||
| 
						 | 
					3c933158c8 | ||
| 
						 | 
					7221d488e5 | ||
| 
						 | 
					1c16931e26 | ||
| 
						 | 
					0a4b70dbd2 | ||
| 
						 | 
					666c716bda | ||
| 
						 | 
					be477d7ae3 | ||
| 
						 | 
					6c8a623b88 | ||
| 
						 | 
					ff8de34415 | ||
| 
						 | 
					f8ca6d9833 | ||
| 
						 | 
					dbab519004 | ||
| 
						 | 
					e888c96591 | ||
| 
						 | 
					59519f0493 | ||
| 
						 | 
					e02f07d356 | ||
| 
						 | 
					2c90d220b8 | ||
| 
						 | 
					9feeba8d4b | ||
| 
						 | 
					45090fc897 | ||
| 
						 | 
					d761bdc1b1 | ||
| 
						 | 
					287a8c76b5 | ||
| 
						 | 
					a7dd86de71 | ||
| 
						 | 
					c541a2e5de | ||
| 
						 | 
					41dbf1ddac | ||
| 
						 | 
					70ed6b96d1 | ||
| 
						 | 
					ef26075a1c | ||
| 
						 | 
					91c87446be | ||
| 
						 | 
					dd207e1f02 | ||
| 
						 | 
					e947870da9 | ||
| 
						 | 
					f0b5f592dc | ||
| 
						 | 
					773a2a6cfe | ||
| 
						 | 
					c2a7e384ba | ||
| 
						 | 
					4a8b35ef5f | ||
| 
						 | 
					5cd0db32df | ||
| 
						 | 
					ef01de6149 | ||
| 
						 | 
					4e9749f655 | ||
| 
						 | 
					abb7a1fd47 | ||
| 
						 | 
					60bcee8c1d | ||
| 
						 | 
					8ded524236 | ||
| 
						 | 
					0a47f48191 | ||
| 
						 | 
					8601267b90 | ||
| 
						 | 
					6767e0c52d | ||
| 
						 | 
					5141d1775d | ||
| 
						 | 
					374af996d9 | ||
| 
						 | 
					dcc9624c15 | ||
| 
						 | 
					b3df1a2bf8 | ||
| 
						 | 
					3fc3c02a4f | ||
| 
						 | 
					874ddf9a32 | ||
| 
						 | 
					69aeb70cc3 | ||
| 
						 | 
					47e60cefe3 | ||
| 
						 | 
					4ea7f3cda5 | ||
| 
						 | 
					03288b521a | ||
| 
						 | 
					4b0a7a6e1f | ||
| 
						 | 
					383287adcd | ||
| 
						 | 
					5c00afc6fe | ||
| 
						 | 
					025e0e8093 | ||
| 
						 | 
					35fb1f8585 | ||
| 
						 | 
					9fb14eef0e | ||
| 
						 | 
					5d6d0c6176 | ||
| 
						 | 
					dd707242ef | ||
| 
						 | 
					4f32f1285a | ||
| 
						 | 
					a6ecdbae29 | ||
| 
						 | 
					4904d100ff | ||
| 
						 | 
					7a1f94bc20 | ||
| 
						 | 
					f9320fff8f | ||
| 
						 | 
					3dde83d8a0 | ||
| 
						 | 
					f3e77a989c | ||
| 
						 | 
					f3539b0354 | ||
| 
						 | 
					4e5d4b9695 | ||
| 
						 | 
					864315f6d1 | ||
| 
						 | 
					713b7338ea | ||
| 
						 | 
					53117b2f4c | ||
| 
						 | 
					90623142e1 | ||
| 
						 | 
					43503a20e5 | ||
| 
						 | 
					ec53b27dfe | ||
| 
						 | 
					238ecfc539 | ||
| 
						 | 
					b888792940 | ||
| 
						 | 
					e3d1ab52f8 | ||
| 
						 | 
					f9c2874c35 | ||
| 
						 | 
					2304f005e3 | ||
| 
						 | 
					b95f836256 | ||
| 
						 | 
					c94f9f21af | ||
| 
						 | 
					fc336e3733 | ||
| 
						 | 
					fc1df9f9a5 | ||
| 
						 | 
					5a604bfdee | ||
| 
						 | 
					281951a86b | ||
| 
						 | 
					35ec3adadc | ||
| 
						 | 
					b9256a1ba7 | ||
| 
						 | 
					734c9a1aa5 | ||
| 
						 | 
					6502a71083 | ||
| 
						 | 
					f2634b44cd | ||
| 
						 | 
					dcf9c467c3 | ||
| 
						 | 
					3dcacc1f8d | ||
| 
						 | 
					7cb81b0f35 | ||
| 
						 | 
					8155ba5224 | ||
| 
						 | 
					7169060425 | ||
| 
						 | 
					ddb9dd4e45 | ||
| 
						 | 
					2a05f24cb6 | ||
| 
						 | 
					039e4c662d | ||
| 
						 | 
					70351677a1 | ||
| 
						 | 
					33cadfb97d | ||
| 
						 | 
					5fbaeda217 | ||
| 
						 | 
					b44ba0d21a | ||
| 
						 | 
					b42532afe9 | ||
| 
						 | 
					da6a335b87 | ||
| 
						 | 
					0e8fef73bb | ||
| 
						 | 
					7122a960fa | ||
| 
						 | 
					d5b649a1a4 | ||
| 
						 | 
					233c724b2d | ||
| 
						 | 
					e1eb001872 | ||
| 
						 | 
					5899d7034f | ||
| 
						 | 
					23c3e9482f | ||
| 
						 | 
					dd980d9dca | ||
| 
						 | 
					d4ed50a915 | ||
| 
						 | 
					d986c7d126 | ||
| 
						 | 
					67a2a4f249 | ||
| 
						 | 
					a6e87e7e08 | ||
| 
						 | 
					33da8a7f62 | ||
| 
						 | 
					68c533f777 | ||
| 
						 | 
					671eecf203 | ||
| 
						 | 
					2454ac8ef1 | ||
| 
						 | 
					20f097faa4 | ||
| 
						 | 
					5dba8b493d | ||
| 
						 | 
					eb27013fba | ||
| 
						 | 
					4f8d1c5c9d | ||
| 
						 | 
					74168c3e05 | ||
| 
						 | 
					cece848801 | ||
| 
						 | 
					7ddbeaa078 | ||
| 
						 | 
					2d4aa7ff8b | ||
| 
						 | 
					79e044ac31 | ||
| 
						 | 
					15ae5a5135 | ||
| 
						 | 
					5684b7c329 | ||
| 
						 | 
					7e96120353 | ||
| 
						 | 
					cc36421fe5 | ||
| 
						 | 
					683aa727d5 | ||
| 
						 | 
					ea07b495ac | ||
| 
						 | 
					8d211c3524 | ||
| 
						 | 
					ab8df82563 | ||
| 
						 | 
					8ba9c4ab97 | ||
| 
						 | 
					2f70b8682e | ||
| 
						 | 
					86dd4ea480 | ||
| 
						 | 
					204e5f4418 | ||
| 
						 | 
					044a9bb6d3 | ||
| 
						 | 
					a31143328e | ||
| 
						 | 
					2bb29a105c | ||
| 
						 | 
					0013d98d04 | ||
| 
						 | 
					916743f44b | ||
| 
						 | 
					edebe65d95 | ||
| 
						 | 
					c43fcd0af6 | ||
| 
						 | 
					9a5c2b88dc | ||
| 
						 | 
					aaee0414c8 | ||
| 
						 | 
					d0c2fb9761 | ||
| 
						 | 
					4f076c6924 | ||
| 
						 | 
					51d4d1451a | ||
| 
						 | 
					7b9d76dc65 | ||
| 
						 | 
					f91aeea91c | ||
| 
						 | 
					4cabf84be9 | ||
| 
						 | 
					8a1f038a80 | ||
| 
						 | 
					c9cab9ab74 | ||
| 
						 | 
					bd78120bd5 | ||
| 
						 | 
					0de3bf0ac7 | ||
| 
						 | 
					2d144a8b43 | ||
| 
						 | 
					1a140a5515 | ||
| 
						 | 
					1ea8cfbfb0 | ||
| 
						 | 
					64fda95186 | ||
| 
						 | 
					7843c0c1b0 | ||
| 
						 | 
					a3784854a7 | ||
| 
						 | 
					8b7c000f47 | ||
| 
						 | 
					5b8d7a3f29 | ||
| 
						 | 
					328dbd57d4 | ||
| 
						 | 
					a8c448f4cb | ||
| 
						 | 
					de0419228f | ||
| 
						 | 
					d8a4e47a13 | ||
| 
						 | 
					bc920949cb | ||
| 
						 | 
					0717f8591c | ||
| 
						 | 
					affb65219e | ||
| 
						 | 
					3e628f2678 | ||
| 
						 | 
					ddabc38e3f | ||
| 
						 | 
					c0097497be | ||
| 
						 | 
					78198bfdaf | ||
| 
						 | 
					2fb9c923f4 | ||
| 
						 | 
					c485011ed1 | ||
| 
						 | 
					0b8ae68213 | ||
| 
						 | 
					0e73128f40 | ||
| 
						 | 
					927c003d22 | ||
| 
						 | 
					bd73823828 | ||
| 
						 | 
					11582bc7d3 | ||
| 
						 | 
					20ed4f96ba | ||
| 
						 | 
					19790e9011 | ||
| 
						 | 
					3a29e03458 | ||
| 
						 | 
					e2eb685d76 | ||
| 
						 | 
					86cb28fe34 | ||
| 
						 | 
					bcc9679339 | ||
| 
						 | 
					e5aeff50dc | ||
| 
						 | 
					5f44c195e9 | ||
| 
						 | 
					e4387e4aad | ||
| 
						 | 
					b1b336804d | ||
| 
						 | 
					9985c43817 | ||
| 
						 | 
					190cac394c | ||
| 
						 | 
					ae71a5abf6 | ||
| 
						 | 
					df199c5788 | ||
| 
						 | 
					c1ba4f1b55 | ||
| 
						 | 
					afc0097b12 | ||
| 
						 | 
					4e9f971c91 | ||
| 
						 | 
					b1cc28bbda | ||
| 
						 | 
					80d30bdd30 | ||
| 
						 | 
					f27566669b | ||
| 
						 | 
					29f12ddaf4 | ||
| 
						 | 
					ed15ff0515 | ||
| 
						 | 
					a8f71f79fe | ||
| 
						 | 
					68c2478e0e | ||
| 
						 | 
					be07fe9e9f | ||
| 
						 | 
					4d8b661d51 | ||
| 
						 | 
					ca0981645f | ||
| 
						 | 
					bc7e02b47a | ||
| 
						 | 
					55a55e9f74 | ||
| 
						 | 
					b41d40da40 | ||
| 
						 | 
					ba3e088b23 | ||
| 
						 | 
					1b59b0b739 | ||
| 
						 | 
					093cfcdf42 | ||
| 
						 | 
					41801a60ad | ||
| 
						 | 
					5ff0957861 | ||
| 
						 | 
					6913b8beb5 | ||
| 
						 | 
					c8d17bc363 | ||
| 
						 | 
					2cbf1259a8 | ||
| 
						 | 
					59dc513ac3 | ||
| 
						 | 
					87b110bb86 | ||
| 
						 | 
					1b3e1a7abe | ||
| 
						 | 
					0afabc60ae | ||
| 
						 | 
					2d4ea720eb | ||
| 
						 | 
					9c757bbe6e | ||
| 
						 | 
					40e0d72105 | ||
| 
						 | 
					eba788e8c9 | ||
| 
						 | 
					444b111a62 | ||
| 
						 | 
					a364ab4ea7 | ||
| 
						 | 
					be7840c827 | ||
| 
						 | 
					a46e51e8db | ||
| 
						 | 
					789ebb8990 | ||
| 
						 | 
					e3cd96bf19 | ||
| 
						 | 
					7746042adc | ||
| 
						 | 
					dfe80556d3 | ||
| 
						 | 
					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 | ||
| 
						 | 
					4951b58b21 | ||
| 
						 | 
					7be7586971 | ||
| 
						 | 
					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 | ||
| 
						 | 
					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 | ||
| 
						 | 
					a7f2d89e3f | ||
| 
						 | 
					190ec0c14c | ||
| 
						 | 
					62dad721fc | ||
| 
						 | 
					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 | ||
| 
						 | 
					00b6c6a437 | ||
| 
						 | 
					21ef3b0ecf | ||
| 
						 | 
					08d60fcbf2 | ||
| 
						 | 
					4cda54774a | ||
| 
						 | 
					613475ac26 | ||
| 
						 | 
					20d23fcb92 | ||
| 
						 | 
					6d84f59e6b | ||
| 
						 | 
					748cb28017 | 
							
								
								
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							@@ -3,7 +3,7 @@
 | 
			
		||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
 | 
			
		||||
patreon: # Replace with a single Patreon username
 | 
			
		||||
open_collective: acmesh
 | 
			
		||||
ko_fi: # Replace with a single Ko-fi username
 | 
			
		||||
ko_fi: neilpang
 | 
			
		||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
 | 
			
		||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
 | 
			
		||||
liberapay: # Replace with a single Liberapay username
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										719
									
								
								.github/workflows/DNS.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										719
									
								
								.github/workflows/DNS.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,254 +1,465 @@
 | 
			
		||||
name: DNS
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'dnsapi/*.sh'
 | 
			
		||||
      - '.github/workflows/DNS.yml'
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - 'dev'
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'dnsapi/*.sh'
 | 
			
		||||
      - '.github/workflows/DNS.yml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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: 1
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone 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
 | 
			
		||||
        echo "TEST_DNS_NO_WILDCARD" >> docker.env
 | 
			
		||||
        echo "TEST_DNS_SLEEP" >> docker.env
 | 
			
		||||
    - 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: 1
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run:  brew install socat
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone 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: 1
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Set git to use LF
 | 
			
		||||
      run: |
 | 
			
		||||
          git config --global core.autocrlf false
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - 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 http://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 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: macos-10.15
 | 
			
		||||
    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: 1
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/freebsd-vm@v0.1.4
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        prepare: pkg install -y socat curl
 | 
			
		||||
        usesh: true
 | 
			
		||||
        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: macos-10.15
 | 
			
		||||
    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: 1
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/solaris-vm@v0.0.5
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        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
 | 
			
		||||
name: DNS
 | 
			
		||||
on:
 | 
			
		||||
  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@v3
 | 
			
		||||
    - 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@v3
 | 
			
		||||
    - 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@v3
 | 
			
		||||
    - 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: macos-12
 | 
			
		||||
    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@v3
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/freebsd-vm@v0
 | 
			
		||||
      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: macos-12
 | 
			
		||||
    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@v3
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/openbsd-vm@v0
 | 
			
		||||
      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
 | 
			
		||||
        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: macos-12
 | 
			
		||||
    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@v3
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/netbsd-vm@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
 | 
			
		||||
        prepare: |
 | 
			
		||||
          pkg_add curl socat
 | 
			
		||||
        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: macos-12
 | 
			
		||||
    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@v3
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/dragonflybsd-vm@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_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
 | 
			
		||||
        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: macos-12
 | 
			
		||||
    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@v3
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/solaris-vm@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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) Pretend Pear X1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/dragonflybsd-vm@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
 | 
			
		||||
        copyback: "false"
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: |
 | 
			
		||||
          pkg install -y curl socat
 | 
			
		||||
        usesh: true
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								.github/workflows/FreeBSD.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								.github/workflows/FreeBSD.yml
									
									
									
									
										vendored
									
									
								
							@@ -14,6 +14,11 @@ on:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/FreeBSD.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  FreeBSD:
 | 
			
		||||
@@ -25,12 +30,18 @@ jobs:
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
         - TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
           CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
           CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
           CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
           TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: macos-10.15
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
@@ -38,9 +49,10 @@ jobs:
 | 
			
		||||
      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@v2
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0.0.3
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
@@ -48,14 +60,15 @@ jobs:
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/freebsd-vm@v0.1.5
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/freebsd-vm@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
 | 
			
		||||
        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
 | 
			
		||||
        prepare: pkg install -y socat curl wget
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								.github/workflows/Linux.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/Linux.yml
									
									
									
									
										vendored
									
									
								
							@@ -15,6 +15,12 @@ on:
 | 
			
		||||
      - '.github/workflows/Linux.yml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Linux:
 | 
			
		||||
@@ -25,12 +31,13 @@ jobs:
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
      TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd .. \
 | 
			
		||||
          && git clone https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								.github/workflows/MacOS.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/MacOS.yml
									
									
									
									
										vendored
									
									
								
							@@ -14,6 +14,11 @@ on:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/MacOS.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  MacOS:
 | 
			
		||||
@@ -25,11 +30,11 @@ jobs:
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
         - TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
           CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
           CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
           CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
           TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
         #- 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
 | 
			
		||||
@@ -39,13 +44,13 @@ jobs:
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run:  brew install socat
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd .. \
 | 
			
		||||
          && git clone https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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) Pretend Pear X1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/netbsd-vm@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: |
 | 
			
		||||
          pkg_add curl socat
 | 
			
		||||
        usesh: true
 | 
			
		||||
        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) Pretend Pear X1
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
      CA_ECDSA: ${{ matrix.CA_ECDSA }}
 | 
			
		||||
      CA: ${{ matrix.CA }}
 | 
			
		||||
      CA_EMAIL: ${{ matrix.CA_EMAIL }}
 | 
			
		||||
      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
        port: 8080
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/openbsd-vm@v0
 | 
			
		||||
      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
 | 
			
		||||
        usesh: true
 | 
			
		||||
        copyback: false
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								.github/workflows/PebbleStrict.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/PebbleStrict.yml
									
									
									
									
										vendored
									
									
								
							@@ -13,6 +13,13 @@ on:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/PebbleStrict.yml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  PebbleStrict:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
@@ -26,7 +33,7 @@ jobs:
 | 
			
		||||
      TEST_CA: "Pebble Intermediate CA"
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run: sudo apt-get install -y socat
 | 
			
		||||
    - name: Run Pebble
 | 
			
		||||
@@ -34,15 +41,15 @@ jobs:
 | 
			
		||||
    - 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 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh 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: 10.30.50.1
 | 
			
		||||
      ACME_DIRECTORY: https://localhost:14000/dir
 | 
			
		||||
      TestingDomain: 1.23.45.67
 | 
			
		||||
      TEST_ACME_Server: https://localhost:14000/dir
 | 
			
		||||
      HTTPS_INSECURE: 1
 | 
			
		||||
      Le_HTTPPort: 5002
 | 
			
		||||
      Le_TLSPort: 5001
 | 
			
		||||
@@ -51,12 +58,15 @@ jobs:
 | 
			
		||||
      TEST_IPCERT: 1
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run: sudo apt-get install -y socat
 | 
			
		||||
    - name: Run Pebble
 | 
			
		||||
      run: cd .. && curl https://raw.githubusercontent.com/letsencrypt/pebble/master/docker-compose.yml >docker-compose.yml && docker-compose up -d
 | 
			
		||||
      run: |
 | 
			
		||||
        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 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh 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
 | 
			
		||||
							
								
								
									
										37
									
								
								.github/workflows/Solaris.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								.github/workflows/Solaris.yml
									
									
									
									
										vendored
									
									
								
							@@ -15,6 +15,11 @@ on:
 | 
			
		||||
      - '.github/workflows/Solaris.yml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Solaris:
 | 
			
		||||
    strategy:
 | 
			
		||||
@@ -25,12 +30,18 @@ jobs:
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
         - TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
           CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
           CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
           CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
           TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: macos-10.15
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         #- TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
         #  CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
         #  CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
         #  TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
    runs-on: macos-12
 | 
			
		||||
    env:
 | 
			
		||||
      TEST_LOCAL: 1
 | 
			
		||||
      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
 | 
			
		||||
@@ -38,9 +49,10 @@ jobs:
 | 
			
		||||
      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@v2
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0.0.3
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - uses: vmactions/cf-tunnel@v0
 | 
			
		||||
      id: tunnel
 | 
			
		||||
      with:
 | 
			
		||||
        protocol: http
 | 
			
		||||
@@ -48,13 +60,14 @@ jobs:
 | 
			
		||||
    - name: Set envs
 | 
			
		||||
      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: cd .. && git clone https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/solaris-vm@v0.0.5
 | 
			
		||||
      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
    - uses: vmactions/solaris-vm@v0
 | 
			
		||||
      with:
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
 | 
			
		||||
        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
 | 
			
		||||
        copyback: "false"
 | 
			
		||||
        nat: |
 | 
			
		||||
          "8080": "80"
 | 
			
		||||
        prepare: pkgutil -y -i socat curl
 | 
			
		||||
        prepare: pkgutil -y -i socat curl wget
 | 
			
		||||
        run: |
 | 
			
		||||
          cd ../acmetest \
 | 
			
		||||
          && ./letest.sh
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								.github/workflows/Ubuntu.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/Ubuntu.yml
									
									
									
									
										vendored
									
									
								
							@@ -14,6 +14,11 @@ on:
 | 
			
		||||
      - '*.sh'
 | 
			
		||||
      - '.github/workflows/Ubuntu.yml'
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Ubuntu:
 | 
			
		||||
@@ -25,6 +30,12 @@ jobs:
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
         - TEST_ACME_Server: "LetsEncrypt.org_test"
 | 
			
		||||
           CA_ECDSA: ""
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
           ACME_USE_WGET: 1
 | 
			
		||||
         - TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
           CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
           CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
@@ -57,10 +68,11 @@ jobs:
 | 
			
		||||
      NO_REVOKE: ${{ matrix.NO_REVOKE }}
 | 
			
		||||
      TEST_IPCERT: ${{ matrix.TEST_IPCERT }}
 | 
			
		||||
      TestingDomain: ${{ matrix.TestingDomain }}
 | 
			
		||||
      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Install tools
 | 
			
		||||
      run: sudo apt-get install -y socat
 | 
			
		||||
      run: sudo apt-get install -y socat wget
 | 
			
		||||
    - name: Start StepCA
 | 
			
		||||
      if: ${{ matrix.TEST_ACME_Server=='https://localhost:9000/acme/acme/directory' }}
 | 
			
		||||
      run: |
 | 
			
		||||
@@ -68,15 +80,20 @@ jobs:
 | 
			
		||||
            -p 9000:9000 \
 | 
			
		||||
            -e "DOCKER_STEPCA_INIT_NAME=Smallstep" \
 | 
			
		||||
            -e "DOCKER_STEPCA_INIT_DNS_NAMES=localhost,$(hostname -f)" \
 | 
			
		||||
            -e "DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT=true" \
 | 
			
		||||
            -e "DOCKER_STEPCA_INIT_PASSWORD=test" \
 | 
			
		||||
            --name stepca \
 | 
			
		||||
            smallstep/step-ca \
 | 
			
		||||
            && sleep 5 && docker exec  stepca step ca provisioner add acme --type ACME \
 | 
			
		||||
            smallstep/step-ca:0.23.1
 | 
			
		||||
 | 
			
		||||
            sleep 5
 | 
			
		||||
            docker exec  stepca bash -c "echo test >test" \
 | 
			
		||||
            && docker exec stepca step ca provisioner add acme --type ACME --admin-subject  step --admin-password-file=/home/step/test \
 | 
			
		||||
            && docker exec  stepca kill -1 1 \
 | 
			
		||||
            && docker exec  stepca cat /home/step/certs/root_ca.crt | sudo bash -c "cat - >>/etc/ssl/certs/ca-certificates.crt"
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
          cd .. \
 | 
			
		||||
          && git clone https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && git clone --depth=1 https://github.com/acmesh-official/acmetest.git \
 | 
			
		||||
          && cp -r acme.sh acmetest/
 | 
			
		||||
    - name: Run acmetest
 | 
			
		||||
      run: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								.github/workflows/Windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								.github/workflows/Windows.yml
									
									
									
									
										vendored
									
									
								
							@@ -15,6 +15,11 @@ on:
 | 
			
		||||
      - '.github/workflows/Windows.yml'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concurrency: 
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Windows:
 | 
			
		||||
    strategy:
 | 
			
		||||
@@ -25,11 +30,11 @@ jobs:
 | 
			
		||||
           CA: ""
 | 
			
		||||
           CA_EMAIL: ""
 | 
			
		||||
           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
 | 
			
		||||
         - TEST_ACME_Server: "ZeroSSL.com"
 | 
			
		||||
           CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
 | 
			
		||||
           CA: "ZeroSSL RSA Domain Secure Site CA"
 | 
			
		||||
           CA_EMAIL: "githubtest@acme.sh"
 | 
			
		||||
           TEST_PREFERRED_CHAIN: ""
 | 
			
		||||
         #- 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 }}
 | 
			
		||||
@@ -44,7 +49,7 @@ jobs:
 | 
			
		||||
    - name: Set git to use LF
 | 
			
		||||
      run: |
 | 
			
		||||
          git config --global core.autocrlf false
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Install cygwin base packages with chocolatey
 | 
			
		||||
      run: |
 | 
			
		||||
          choco config get cacheLocation
 | 
			
		||||
@@ -52,7 +57,7 @@ jobs:
 | 
			
		||||
      shell: cmd
 | 
			
		||||
    - name: Install cygwin additional packages
 | 
			
		||||
      run: |
 | 
			
		||||
          C:\tools\cygwin\cygwinsetup.exe -qgnNdO -R C:/tools/cygwin -s http://mirrors.kernel.org/sourceware/cygwin/ -P socat,curl,cron,unzip,git,xxd
 | 
			
		||||
          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
 | 
			
		||||
@@ -64,7 +69,7 @@ jobs:
 | 
			
		||||
          echo "PATH=%PATH%"
 | 
			
		||||
    - name: Clone acmetest
 | 
			
		||||
      shell: cmd
 | 
			
		||||
      run: cd .. && git clone https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
 | 
			
		||||
      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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								.github/workflows/dockerhub.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/dockerhub.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,7 +11,11 @@ on:
 | 
			
		||||
      - "Dockerfile"
 | 
			
		||||
      - '.github/workflows/dockerhub.yml'
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
concurrency:
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  CheckToken:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
@@ -24,9 +28,9 @@ jobs:
 | 
			
		||||
        id: step_one
 | 
			
		||||
        run: |
 | 
			
		||||
          if [ "$DOCKER_PASSWORD" ] ; then
 | 
			
		||||
            echo "::set-output name=hasToken::true"
 | 
			
		||||
            echo "hasToken=true" >>$GITHUB_OUTPUT
 | 
			
		||||
          else
 | 
			
		||||
            echo "::set-output name=hasToken::false"
 | 
			
		||||
            echo "hasToken=false" >>$GITHUB_OUTPUT
 | 
			
		||||
          fi
 | 
			
		||||
      - name: Check the value
 | 
			
		||||
        run: echo ${{ steps.step_one.outputs.hasToken }}
 | 
			
		||||
@@ -37,11 +41,11 @@ jobs:
 | 
			
		||||
    if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: checkout code
 | 
			
		||||
        uses: actions/checkout@v2
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v1
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v1
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
      - name: login to docker hub
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								.github/workflows/issue.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/issue.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
name: "Update issues"
 | 
			
		||||
on:
 | 
			
		||||
  issues:
 | 
			
		||||
    types: [opened]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  comment:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/github-script@v6
 | 
			
		||||
        with:
 | 
			
		||||
          script: |
 | 
			
		||||
            github.rest.issues.createComment({
 | 
			
		||||
              issue_number: context.issue.number,
 | 
			
		||||
              owner: context.repo.owner,
 | 
			
		||||
              repo: context.repo.repo,
 | 
			
		||||
              body: "Please upgrade to the latest code and try again first. Maybe it's already fixed.              ```acme.sh --upgrade```              If it's still not working, please provide the log with `--debug 2`, otherwise, nobody can help you."
 | 
			
		||||
              
 | 
			
		||||
            })
 | 
			
		||||
							
								
								
									
										30
									
								
								.github/workflows/pr_dns.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/pr_dns.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
name: Check dns api
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  pull_request_target:
 | 
			
		||||
    types:
 | 
			
		||||
      - opened
 | 
			
		||||
    branches:
 | 
			
		||||
      - 'dev'
 | 
			
		||||
    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**
 | 
			
		||||
                Please make sure you're read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
 | 
			
		||||
                Then reply on this message, otherwise, your code will not be reviewed or merged.
 | 
			
		||||
                We look forward to reviewing your Pull request shortly ✨
 | 
			
		||||
                `
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								.github/workflows/pr_notify.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/pr_notify.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
name: Check dns 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're 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 ✨
 | 
			
		||||
                `
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								.github/workflows/shellcheck.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/shellcheck.yml
									
									
									
									
										vendored
									
									
								
							@@ -13,20 +13,25 @@ on:
 | 
			
		||||
      - '**.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@v2
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Install Shellcheck
 | 
			
		||||
      run: sudo apt-get install -y shellcheck
 | 
			
		||||
    - name: DoShellcheck
 | 
			
		||||
      run: shellcheck -V  && shellcheck -e SC2181 **/*.sh && echo "shellcheck OK"
 | 
			
		||||
      run: shellcheck -V  && shellcheck -e SC2181 -e SC2089 **/*.sh && echo "shellcheck OK"
 | 
			
		||||
 | 
			
		||||
  shfmt:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
FROM alpine:3.15
 | 
			
		||||
FROM alpine:3.17
 | 
			
		||||
 | 
			
		||||
RUN apk --no-cache add -f \
 | 
			
		||||
  openssl \
 | 
			
		||||
@@ -12,7 +12,8 @@ RUN apk --no-cache add -f \
 | 
			
		||||
  oath-toolkit-oathtool \
 | 
			
		||||
  tar \
 | 
			
		||||
  libidn \
 | 
			
		||||
  jq
 | 
			
		||||
  jq \
 | 
			
		||||
  cronie
 | 
			
		||||
 | 
			
		||||
ENV LE_CONFIG_HOME /acme.sh
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +26,7 @@ COPY ./ /install_acme.sh/
 | 
			
		||||
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RUN ln -s  /root/.acme.sh/acme.sh  /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab -
 | 
			
		||||
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null#> /proc/1/fd/1 2>/proc/1/fd/2#' | crontab -
 | 
			
		||||
 | 
			
		||||
RUN for verb in help \
 | 
			
		||||
  version \
 | 
			
		||||
@@ -64,11 +65,10 @@ RUN for verb in help \
 | 
			
		||||
 | 
			
		||||
RUN printf "%b" '#!'"/usr/bin/env sh\n \
 | 
			
		||||
if [ \"\$1\" = \"daemon\" ];  then \n \
 | 
			
		||||
 trap \"echo stop && killall crond && exit 0\" SIGTERM SIGINT \n \
 | 
			
		||||
 crond && while true; do sleep 1; done;\n \
 | 
			
		||||
 exec crond -n -s -m off \n \
 | 
			
		||||
else \n \
 | 
			
		||||
 exec -- \"\$@\"\n \
 | 
			
		||||
fi" >/entry.sh && chmod +x /entry.sh
 | 
			
		||||
fi\n" >/entry.sh && chmod +x /entry.sh
 | 
			
		||||
 | 
			
		||||
VOLUME /acme.sh
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								README.md
									
									
									
									
									
								
							@@ -1,10 +1,14 @@
 | 
			
		||||
# 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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
@@ -47,14 +51,12 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
 | 
			
		||||
- [ruby-china.org](https://ruby-china.org/topics/31983)
 | 
			
		||||
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
 | 
			
		||||
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
 | 
			
		||||
- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
 | 
			
		||||
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
 | 
			
		||||
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
 | 
			
		||||
- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html)
 | 
			
		||||
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
 | 
			
		||||
- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh)
 | 
			
		||||
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
 | 
			
		||||
- [CentOS Web Panel](http://centos-webpanel.com/)
 | 
			
		||||
- [CentOS Web Panel](https://control-webpanel.com)
 | 
			
		||||
- [lnmp.org](https://lnmp.org/)
 | 
			
		||||
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
 | 
			
		||||
 | 
			
		||||
@@ -68,21 +70,23 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
 | 
			
		||||
|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|NA|OpenBSD
 | 
			
		||||
|8|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
 | 
			
		||||
|9|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
 | 
			
		||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
 | 
			
		||||
|11|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
 | 
			
		||||
|12|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
 | 
			
		||||
|13|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
 | 
			
		||||
|14|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
 | 
			
		||||
|15|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
 | 
			
		||||
|16|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
 | 
			
		||||
|17|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
 | 
			
		||||
|18|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
 | 
			
		||||
|19|-----| Cloud Linux  https://github.com/acmesh-official/acme.sh/issues/111
 | 
			
		||||
|20|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
 | 
			
		||||
|21|[](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)
 | 
			
		||||
|7|[](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
 | 
			
		||||
|8|[](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
 | 
			
		||||
|9|[](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
 | 
			
		||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
 | 
			
		||||
|11|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
 | 
			
		||||
|12|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
 | 
			
		||||
|13|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
 | 
			
		||||
|14|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
 | 
			
		||||
|15|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
 | 
			
		||||
|16|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
 | 
			
		||||
|17|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
 | 
			
		||||
|18|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
 | 
			
		||||
|19|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
 | 
			
		||||
|10|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
 | 
			
		||||
|11|-----| Cloud Linux  https://github.com/acmesh-official/acme.sh/issues/111
 | 
			
		||||
|22|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
 | 
			
		||||
|23|[](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Check our [testing project](https://github.com/acmesh-official/acmetest):
 | 
			
		||||
@@ -95,6 +99,7 @@ https://github.com/acmesh-official/acmetest
 | 
			
		||||
- Letsencrypt.org CA
 | 
			
		||||
- [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA)
 | 
			
		||||
- [SSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/SSL.com-CA)
 | 
			
		||||
- [Google.com Public CA](https://github.com/acmesh-official/acme.sh/wiki/Google-Public-CA)
 | 
			
		||||
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
 | 
			
		||||
- Any other [RFC8555](https://tools.ietf.org/html/rfc8555)-compliant CA
 | 
			
		||||
 | 
			
		||||
@@ -354,10 +359,6 @@ Ok, it's done.
 | 
			
		||||
 | 
			
		||||
# 10. Issue ECC certificates
 | 
			
		||||
 | 
			
		||||
`Let's Encrypt` can now issue **ECDSA** certificates.
 | 
			
		||||
 | 
			
		||||
And we support them too!
 | 
			
		||||
 | 
			
		||||
Just set the `keylength` parameter with a prefix `ec-`.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
@@ -378,10 +379,12 @@ Please look at the `keylength` parameter above.
 | 
			
		||||
 | 
			
		||||
Valid values are:
 | 
			
		||||
 | 
			
		||||
1. **ec-256 (prime256v1, "ECDSA P-256")**
 | 
			
		||||
1. **ec-256 (prime256v1, "ECDSA P-256", which is the default key type)**
 | 
			
		||||
2. **ec-384 (secp384r1,  "ECDSA P-384")**
 | 
			
		||||
3. **ec-521 (secp521r1,  "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
 | 
			
		||||
 | 
			
		||||
4. **2048   (RSA2048)**
 | 
			
		||||
5. **3072   (RSA3072)**
 | 
			
		||||
6. **4096   (RSA4096)**
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 11. Issue Wildcard certificates
 | 
			
		||||
@@ -502,6 +505,12 @@ Support this project with your organization. Your logo will show up here with a
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a>
 | 
			
		||||
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### Sponsors
 | 
			
		||||
 | 
			
		||||
[](https://www.quantumca.com.cn/?__utm_source=acmesh-donation)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 19. License & Others
 | 
			
		||||
 | 
			
		||||
License is GPLv3
 | 
			
		||||
 
 | 
			
		||||
@@ -3,18 +3,29 @@
 | 
			
		||||
# 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_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"
 | 
			
		||||
@@ -22,6 +33,9 @@ cpanel_uapi_deploy() {
 | 
			
		||||
  _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"
 | 
			
		||||
@@ -32,31 +46,166 @@ cpanel_uapi_deploy() {
 | 
			
		||||
    _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")
 | 
			
		||||
 | 
			
		||||
  _debug _cert "$_cert"
 | 
			
		||||
  _debug _key "$_key"
 | 
			
		||||
  _debug2 _cert "$_cert"
 | 
			
		||||
  _debug2 _key "$_key"
 | 
			
		||||
 | 
			
		||||
  if [ "$(id -u)" = 0 ]; then
 | 
			
		||||
    if [ -z "$DEPLOY_CPANEL_USER" ]; then
 | 
			
		||||
    _getdeployconf DEPLOY_CPANEL_USER
 | 
			
		||||
    # fallback to _readdomainconf for old installs
 | 
			
		||||
    if [ -z "${DEPLOY_CPANEL_USER:=$(_readdomainconf DEPLOY_CPANEL_USER)}" ]; then
 | 
			
		||||
      _err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
 | 
			
		||||
    _response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
 | 
			
		||||
  else
 | 
			
		||||
    _response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
 | 
			
		||||
  fi
 | 
			
		||||
  error_response="status: 0"
 | 
			
		||||
  if test "${_response#*$error_response}" != "$_response"; then
 | 
			
		||||
    _err "Error in deploying certificate:"
 | 
			
		||||
    _err "$_response"
 | 
			
		||||
    return 1
 | 
			
		||||
    _debug DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
 | 
			
		||||
    _savedeployconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
 | 
			
		||||
 | 
			
		||||
    _uapi_user="$DEPLOY_CPANEL_USER"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug response "$_response"
 | 
			
		||||
  _info "Certificate successfully deployed"
 | 
			
		||||
  return 0
 | 
			
		||||
  # 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\"")"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -273,16 +273,27 @@ _check_curl_version() {
 | 
			
		||||
  _minor="$(_getfield "$_cversion" 2 '.')"
 | 
			
		||||
  _debug2 "_minor" "$_minor"
 | 
			
		||||
 | 
			
		||||
  if [ "$_major$_minor" -lt "740" ]; then
 | 
			
		||||
  if [ "$_major" -ge "8" ]; then
 | 
			
		||||
    #ok
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$_major" = "7" ]; then
 | 
			
		||||
    if [ "$_minor" -lt "40" ]; then
 | 
			
		||||
      _err "curl v$_cversion doesn't support unit socket"
 | 
			
		||||
      _err "Please upgrade to curl 7.40 or later."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if [ "$_minor" -lt "50" ]; then
 | 
			
		||||
      _debug "Use short host name"
 | 
			
		||||
      export _CURL_NO_HOST=1
 | 
			
		||||
    else
 | 
			
		||||
      export _CURL_NO_HOST=
 | 
			
		||||
    fi
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "curl v$_cversion doesn't support unit socket"
 | 
			
		||||
    _err "Please upgrade to curl 7.40 or later."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$_major$_minor" -lt "750" ]; then
 | 
			
		||||
    _debug "Use short host name"
 | 
			
		||||
    export _CURL_NO_HOST=1
 | 
			
		||||
  else
 | 
			
		||||
    export _CURL_NO_HOST=
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/).
 | 
			
		||||
# Here is the script to deploy the cert to G-Core CDN service (https://gcore.com/) using the G-Core Labs API (https://apidocs.gcore.com/cdn).
 | 
			
		||||
# Returns 0 when success.
 | 
			
		||||
#
 | 
			
		||||
# Written by temoffey <temofffey@gmail.com>
 | 
			
		||||
# Public domain, 2019
 | 
			
		||||
# Update by DreamOfIce <admin@dreamofice.cn> in 2023
 | 
			
		||||
 | 
			
		||||
#export DEPLOY_GCORE_CDN_USERNAME=myusername
 | 
			
		||||
#export DEPLOY_GCORE_CDN_PASSWORD=mypassword
 | 
			
		||||
@@ -56,7 +57,7 @@ gcore_cdn_deploy() {
 | 
			
		||||
  _request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}"
 | 
			
		||||
  _debug _request "$_request"
 | 
			
		||||
  export _H1="Content-Type:application/json"
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcdn.co/auth/jwt/login")
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcore.com/auth/jwt/login")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex=".*\"access\":\"\([-._0-9A-Za-z]*\)\".*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
@@ -69,8 +70,8 @@ gcore_cdn_deploy() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Find CDN resource with cname $_cdomain"
 | 
			
		||||
  export _H2="Authorization:Token $_token"
 | 
			
		||||
  _response=$(_get "https://api.gcdn.co/resources")
 | 
			
		||||
  export _H2="Authorization:Bearer $_token"
 | 
			
		||||
  _response=$(_get "https://api.gcore.com/cdn/resources")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex="\"primary_resource\":null},"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
@@ -102,7 +103,7 @@ gcore_cdn_deploy() {
 | 
			
		||||
  _date=$(date "+%d.%m.%Y %H:%M:%S")
 | 
			
		||||
  _request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}"
 | 
			
		||||
  _debug _request "$_request"
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcdn.co/sslData")
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcore.com/cdn/sslData")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex=".*\"id\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
@@ -117,7 +118,7 @@ gcore_cdn_deploy() {
 | 
			
		||||
  _info "Update CDN resource"
 | 
			
		||||
  _request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}"
 | 
			
		||||
  _debug _request "$_request"
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT")
 | 
			
		||||
  _response=$(_post "$_request" "https://api.gcore.com/cdn/resources/$_resourceId" '' "PUT")
 | 
			
		||||
  _debug _response "$_response"
 | 
			
		||||
  _regex=".*\"sslData\":\([0-9]*\).*$"
 | 
			
		||||
  _debug _regex "$_regex"
 | 
			
		||||
@@ -133,7 +134,7 @@ gcore_cdn_deploy() {
 | 
			
		||||
    _info "Not found old SSL certificate"
 | 
			
		||||
  else
 | 
			
		||||
    _info "Delete old SSL certificate"
 | 
			
		||||
    _response=$(_post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE")
 | 
			
		||||
    _response=$(_post '' "https://api.gcore.com/cdn/sslData/$_sslDataOld" '' "DELETE")
 | 
			
		||||
    _debug _response "$_response"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@ gitlab_deploy() {
 | 
			
		||||
 | 
			
		||||
  error_response="error"
 | 
			
		||||
 | 
			
		||||
  if test "${_response#*$error_response}" != "$_response"; then
 | 
			
		||||
  if test "${_response#*"$error_response"}" != "$_response"; then
 | 
			
		||||
    _err "Error in deploying certificate:"
 | 
			
		||||
    _err "$_response"
 | 
			
		||||
    return 1
 | 
			
		||||
 
 | 
			
		||||
@@ -20,18 +20,23 @@ mailcow_deploy() {
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  _mailcow_path="${DEPLOY_MAILCOW_PATH}"
 | 
			
		||||
  _getdeployconf DEPLOY_MAILCOW_PATH
 | 
			
		||||
  _getdeployconf DEPLOY_MAILCOW_RELOAD
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_mailcow_path" ]; then
 | 
			
		||||
  _debug DEPLOY_MAILCOW_PATH "$DEPLOY_MAILCOW_PATH"
 | 
			
		||||
  _debug DEPLOY_MAILCOW_RELOAD "$DEPLOY_MAILCOW_RELOAD"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_MAILCOW_PATH" ]; then
 | 
			
		||||
    _err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #Tests if _ssl_path is the mailcow root directory.
 | 
			
		||||
  if [ -f "${_mailcow_path}/generate_config.sh" ]; then
 | 
			
		||||
    _ssl_path="${_mailcow_path}/data/assets/ssl/"
 | 
			
		||||
  else
 | 
			
		||||
    _ssl_path="${_mailcow_path}"
 | 
			
		||||
  _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
 | 
			
		||||
@@ -39,31 +44,20 @@ mailcow_deploy() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # ECC or RSA
 | 
			
		||||
  if [ -z "${Le_Keylength}" ]; then
 | 
			
		||||
    Le_Keylength=""
 | 
			
		||||
  fi
 | 
			
		||||
  if _isEccKey "${Le_Keylength}"; then
 | 
			
		||||
    _info "ECC key type detected"
 | 
			
		||||
    _cert_name_prefix="ecdsa-"
 | 
			
		||||
  else
 | 
			
		||||
    _info "RSA key type detected"
 | 
			
		||||
    _cert_name_prefix=""
 | 
			
		||||
  fi
 | 
			
		||||
  _info "Copying key and cert"
 | 
			
		||||
  _real_key="$_ssl_path/${_cert_name_prefix}key.pem"
 | 
			
		||||
  _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_name_prefix}cert.pem"
 | 
			
		||||
  _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 -qaf name=postfix-mailcow); docker restart $(docker ps -qaf name=nginx-mailcow); docker restart $(docker ps -qaf name=dovecot-mailcow)"
 | 
			
		||||
  DEFAULT_MAILCOW_RELOAD="docker restart \$(docker ps --quiet --filter name=nginx-mailcow --filter name=dovecot-mailcow --filter name=postfix-mailcow)"
 | 
			
		||||
  _reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}"
 | 
			
		||||
 | 
			
		||||
  _info "Run reload: $_reload"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								deploy/proxmoxve.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								deploy/proxmoxve.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Deploy certificates to a proxmox virtual environment node using the API.
 | 
			
		||||
#
 | 
			
		||||
# Environment variables that can be set are:
 | 
			
		||||
# `DEPLOY_PROXMOXVE_SERVER`: The hostname of the proxmox ve node. Defaults to
 | 
			
		||||
#                            _cdomain.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_SERVER_PORT`: The port number the management interface is on.
 | 
			
		||||
#                                 Defaults to 8006.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_NODE_NAME`: The name of the node we'll be connecting to.
 | 
			
		||||
#                               Defaults to the host portion of the server
 | 
			
		||||
#                               domain name.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_USER`: The user we'll connect as. Defaults to root.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_USER_REALM`: The authentication realm the user authenticates
 | 
			
		||||
#                                with. Defaults to pam.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_API_TOKEN_NAME`: The name of the API token created for the
 | 
			
		||||
#                                    user account. Defaults to acme.
 | 
			
		||||
# `DEPLOY_PROXMOXVE_API_TOKEN_KEY`: The API token. Required.
 | 
			
		||||
 | 
			
		||||
proxmoxve_deploy() {
 | 
			
		||||
  _cdomain="$1"
 | 
			
		||||
  _ckey="$2"
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug2 _ckey "$_ckey"
 | 
			
		||||
  _debug _ccert "$_ccert"
 | 
			
		||||
  _debug _cca "$_cca"
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # "Sane" defaults.
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_SERVER
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_SERVER" ]; then
 | 
			
		||||
    _target_hostname="$_cdomain"
 | 
			
		||||
  else
 | 
			
		||||
    _target_hostname="$DEPLOY_PROXMOXVE_SERVER"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_SERVER "$DEPLOY_PROXMOXVE_SERVER"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_SERVER "$_target_hostname"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_SERVER_PORT
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_SERVER_PORT" ]; then
 | 
			
		||||
    _target_port="8006"
 | 
			
		||||
  else
 | 
			
		||||
    _target_port="$DEPLOY_PROXMOXVE_SERVER_PORT"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_SERVER_PORT "$DEPLOY_PROXMOXVE_SERVER_PORT"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_SERVER_PORT "$_target_port"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_NODE_NAME
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_NODE_NAME" ]; then
 | 
			
		||||
    _node_name=$(echo "$_target_hostname" | cut -d. -f1)
 | 
			
		||||
  else
 | 
			
		||||
    _node_name="$DEPLOY_PROXMOXVE_NODE_NAME"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_NODE_NAME "$DEPLOY_PROXMOXVE_NODE_NAME"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_NODE_NAME "$_node_name"
 | 
			
		||||
 | 
			
		||||
  # Complete URL.
 | 
			
		||||
  _target_url="https://${_target_hostname}:${_target_port}/api2/json/nodes/${_node_name}/certificates/custom"
 | 
			
		||||
  _debug TARGET_URL "$_target_url"
 | 
			
		||||
 | 
			
		||||
  # More "sane" defaults.
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_USER
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_USER" ]; then
 | 
			
		||||
    _proxmoxve_user="root"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxve_user="$DEPLOY_PROXMOXVE_USER"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_USER "$DEPLOY_PROXMOXVE_USER"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_USER "$_proxmoxve_user"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_USER_REALM
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_USER_REALM" ]; then
 | 
			
		||||
    _proxmoxve_user_realm="pam"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxve_user_realm="$DEPLOY_PROXMOXVE_USER_REALM"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_USER_REALM "$DEPLOY_PROXMOXVE_USER_REALM"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_USER_REALM "$_proxmoxve_user_realm"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_API_TOKEN_NAME
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_API_TOKEN_NAME" ]; then
 | 
			
		||||
    _proxmoxve_api_token_name="acme"
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxve_api_token_name="$DEPLOY_PROXMOXVE_API_TOKEN_NAME"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_NAME "$DEPLOY_PROXMOXVE_API_TOKEN_NAME"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_API_TOKEN_NAME "$_proxmoxve_api_token_name"
 | 
			
		||||
 | 
			
		||||
  # This is required.
 | 
			
		||||
  _getdeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY
 | 
			
		||||
  if [ -z "$DEPLOY_PROXMOXVE_API_TOKEN_KEY" ]; then
 | 
			
		||||
    _err "API key not provided."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
 | 
			
		||||
    _savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY _proxmoxve_api_token_key
 | 
			
		||||
 | 
			
		||||
  # PVE API Token header value. Used in "Authorization: PVEAPIToken".
 | 
			
		||||
  _proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
 | 
			
		||||
  _debug2 "Auth Header" _proxmoxve_header_api_token
 | 
			
		||||
 | 
			
		||||
  # Ugly. I hate putting heredocs inside functions because heredocs don't
 | 
			
		||||
  # account for whitespace correctly but it _does_ work and is several times
 | 
			
		||||
  # cleaner than anything else I had here.
 | 
			
		||||
  #
 | 
			
		||||
  # This dumps the json payload to a variable that should be passable to the
 | 
			
		||||
  # _psot function.
 | 
			
		||||
  _json_payload=$(
 | 
			
		||||
    cat <<HEREDOC
 | 
			
		||||
{
 | 
			
		||||
  "certificates": "$(tr '\n' ':' <"$_cfullchain" | sed 's/:/\\n/g')",
 | 
			
		||||
  "key": "$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')",
 | 
			
		||||
  "node":"$_node_name",
 | 
			
		||||
  "restart":"1",
 | 
			
		||||
  "force":"1"
 | 
			
		||||
}
 | 
			
		||||
HEREDOC
 | 
			
		||||
  )
 | 
			
		||||
  _debug2 Payload "$_json_payload"
 | 
			
		||||
 | 
			
		||||
  # Push certificates to server.
 | 
			
		||||
  export _HTTPS_INSECURE=1
 | 
			
		||||
  export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
 | 
			
		||||
  _post "$_json_payload" "$_target_url" "" POST "application/json"
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -53,7 +53,7 @@ qiniu_deploy() {
 | 
			
		||||
  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 "multiline")
 | 
			
		||||
  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:"
 | 
			
		||||
@@ -75,7 +75,7 @@ qiniu_deploy() {
 | 
			
		||||
    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 "multiline")
 | 
			
		||||
    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:"
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
# ```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
 | 
			
		||||
# ```
 | 
			
		||||
@@ -48,6 +49,16 @@
 | 
			
		||||
# 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 #####################
 | 
			
		||||
@@ -59,6 +70,7 @@ routeros_deploy() {
 | 
			
		||||
  _ccert="$3"
 | 
			
		||||
  _cca="$4"
 | 
			
		||||
  _cfullchain="$5"
 | 
			
		||||
  _err_code=0
 | 
			
		||||
 | 
			
		||||
  _debug _cdomain "$_cdomain"
 | 
			
		||||
  _debug _ckey "$_ckey"
 | 
			
		||||
@@ -80,6 +92,27 @@ routeros_deploy() {
 | 
			
		||||
    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
 | 
			
		||||
@@ -89,16 +122,26 @@ routeros_deploy() {
 | 
			
		||||
 | 
			
		||||
  _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"
 | 
			
		||||
 | 
			
		||||
  _info "Trying to push key '$_ckey' to router"
 | 
			
		||||
  scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key"
 | 
			
		||||
  _info "Trying to push cert '$_cfullchain' to router"
 | 
			
		||||
  scp "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer"
 | 
			
		||||
  DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=admin policy=ftp,read,write,password,sensitive \
 | 
			
		||||
source=\"## generated by routeros deploy script in acme.sh;\
 | 
			
		||||
\n/certificate remove [ find name=$_cdomain.cer_0 ];\
 | 
			
		||||
  # 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="/system script add name=\"LE Cert Deploy - $_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=\\\"\\\";\
 | 
			
		||||
@@ -110,12 +153,51 @@ source=\"## generated by routeros deploy script in acme.sh;\
 | 
			
		||||
\n$ROUTER_OS_ADDITIONAL_SERVICES;\
 | 
			
		||||
\n\"
 | 
			
		||||
"
 | 
			
		||||
  # shellcheck disable=SC2029
 | 
			
		||||
  ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "$DEPLOY_SCRIPT_CMD"
 | 
			
		||||
  # shellcheck disable=SC2029
 | 
			
		||||
  ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "/system script run \"LE Cert Deploy - $_cdomain\""
 | 
			
		||||
  # shellcheck disable=SC2029
 | 
			
		||||
  ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "/system script remove \"LE Cert Deploy - $_cdomain\""
 | 
			
		||||
 | 
			
		||||
  if ! _ssh_remote_cmd "$DEPLOY_SCRIPT_CMD"; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _ssh_remote_cmd "/system script run \"LE Cert Deploy - $_cdomain\""; then
 | 
			
		||||
    return $_err_code
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _ssh_remote_cmd "/system script remove \"LE Cert Deploy - $_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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										409
									
								
								deploy/ssh.sh
									
									
									
									
									
								
							
							
						
						
									
										409
									
								
								deploy/ssh.sh
									
									
									
									
									
								
							@@ -14,7 +14,7 @@
 | 
			
		||||
# The following examples are for QNAP NAS running QTS 4.2
 | 
			
		||||
# export DEPLOY_SSH_CMD=""  # defaults to "ssh -T"
 | 
			
		||||
# export DEPLOY_SSH_USER="admin"  # required
 | 
			
		||||
# export DEPLOY_SSH_SERVER="qnap"  # defaults to domain name
 | 
			
		||||
# export DEPLOY_SSH_SERVER="host1 host2:8022 192.168.0.1:9022"  # defaults to domain name, support multiple servers with optional port
 | 
			
		||||
# export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
 | 
			
		||||
# export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem"
 | 
			
		||||
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
 | 
			
		||||
@@ -23,6 +23,8 @@
 | 
			
		||||
# export DEPLOY_SSH_BACKUP=""  # yes or no, default to yes or previously saved value
 | 
			
		||||
# export DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy"  # path on remote system. Defaults to .acme_ssh_deploy
 | 
			
		||||
# export DEPLOY_SSH_MULTI_CALL=""  # yes or no, default to no or previously saved value
 | 
			
		||||
# export DEPLOY_SSH_USE_SCP="" yes or no, default to no
 | 
			
		||||
# export DEPLOY_SSH_SCP_CMD="" defaults to "scp -q"
 | 
			
		||||
#
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
@@ -42,72 +44,134 @@ ssh_deploy() {
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # USER is required to login by SSH to remote host.
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_user DEPLOY_SSH_USER
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_USER
 | 
			
		||||
  _debug2 DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_USER" ]; then
 | 
			
		||||
    if [ -z "$Le_Deploy_ssh_user" ]; then
 | 
			
		||||
      _err "DEPLOY_SSH_USER not defined."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    Le_Deploy_ssh_user="$DEPLOY_SSH_USER"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_user "$Le_Deploy_ssh_user"
 | 
			
		||||
    _err "DEPLOY_SSH_USER not defined."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
 | 
			
		||||
 | 
			
		||||
  # SERVER is optional. If not provided then use _cdomain
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_server DEPLOY_SSH_SERVER
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_SERVER
 | 
			
		||||
  _debug2 DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_SERVER" ]; then
 | 
			
		||||
    Le_Deploy_ssh_server="$DEPLOY_SSH_SERVER"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_server "$Le_Deploy_ssh_server"
 | 
			
		||||
  elif [ -z "$Le_Deploy_ssh_server" ]; then
 | 
			
		||||
    Le_Deploy_ssh_server="$_cdomain"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_SERVER" ]; then
 | 
			
		||||
    DEPLOY_SSH_SERVER="$_cdomain"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
 | 
			
		||||
 | 
			
		||||
  # CMD is optional. If not provided then use ssh
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_cmd DEPLOY_SSH_CMD
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_CMD
 | 
			
		||||
  _debug2 DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CMD" ]; then
 | 
			
		||||
    Le_Deploy_ssh_cmd="$DEPLOY_SSH_CMD"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_cmd "$Le_Deploy_ssh_cmd"
 | 
			
		||||
  elif [ -z "$Le_Deploy_ssh_cmd" ]; then
 | 
			
		||||
    Le_Deploy_ssh_cmd="ssh -T"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_CMD" ]; then
 | 
			
		||||
    DEPLOY_SSH_CMD="ssh -T"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
 | 
			
		||||
 | 
			
		||||
  # BACKUP is optional. If not provided then default to previously saved value or yes.
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_backup DEPLOY_SSH_BACKUP
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_BACKUP
 | 
			
		||||
  _debug2 DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
 | 
			
		||||
  if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then
 | 
			
		||||
    Le_Deploy_ssh_backup="no"
 | 
			
		||||
  elif [ -z "$Le_Deploy_ssh_backup" ] || [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
    Le_Deploy_ssh_backup="yes"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_BACKUP" ]; then
 | 
			
		||||
    DEPLOY_SSH_BACKUP="yes"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedomainconf Le_Deploy_ssh_backup "$Le_Deploy_ssh_backup"
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
 | 
			
		||||
 | 
			
		||||
  # BACKUP_PATH is optional. If not provided then default to previously saved value or .acme_ssh_deploy
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_backup_path DEPLOY_SSH_BACKUP_PATH
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_BACKUP_PATH
 | 
			
		||||
  _debug2 DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_BACKUP_PATH" ]; then
 | 
			
		||||
    Le_Deploy_ssh_backup_path="$DEPLOY_SSH_BACKUP_PATH"
 | 
			
		||||
  elif [ -z "$Le_Deploy_ssh_backup_path" ]; then
 | 
			
		||||
    Le_Deploy_ssh_backup_path=".acme_ssh_deploy"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_BACKUP_PATH" ]; then
 | 
			
		||||
    DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedomainconf Le_Deploy_ssh_backup_path "$Le_Deploy_ssh_backup_path"
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
 | 
			
		||||
 | 
			
		||||
  # MULTI_CALL is optional. If not provided then default to previously saved
 | 
			
		||||
  # value (which may be undefined... equivalent to "no").
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_multi_call DEPLOY_SSH_MULTI_CALL
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_MULTI_CALL
 | 
			
		||||
  _debug2 DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
 | 
			
		||||
  if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
    Le_Deploy_ssh_multi_call="yes"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_multi_call "$Le_Deploy_ssh_multi_call"
 | 
			
		||||
  elif [ "$DEPLOY_SSH_MULTI_CALL" = "no" ]; then
 | 
			
		||||
    Le_Deploy_ssh_multi_call=""
 | 
			
		||||
    _cleardomainconf Le_Deploy_ssh_multi_call
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_MULTI_CALL" ]; then
 | 
			
		||||
    DEPLOY_SSH_MULTI_CALL="no"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
 | 
			
		||||
 | 
			
		||||
  # KEYFILE is optional.
 | 
			
		||||
  # If provided then private key will be copied to provided filename.
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_keyfile DEPLOY_SSH_KEYFILE
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_KEYFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _deploy_ssh_servers=$Le_Deploy_ssh_server
 | 
			
		||||
  for Le_Deploy_ssh_server in $_deploy_ssh_servers; do
 | 
			
		||||
  # CERTFILE is optional.
 | 
			
		||||
  # If provided then certificate will be copied or appended to provided filename.
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_certfile DEPLOY_SSH_CERTFILE
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_CERTFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # CAFILE is optional.
 | 
			
		||||
  # If provided then CA intermediate certificate will be copied or appended to provided filename.
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_cafile DEPLOY_SSH_CAFILE
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_CAFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CAFILE" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # FULLCHAIN is optional.
 | 
			
		||||
  # If provided then fullchain certificate will be copied or appended to provided filename.
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_fullchain DEPLOY_SSH_FULLCHAIN
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_FULLCHAIN
 | 
			
		||||
  _debug2 DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # REMOTE_CMD is optional.
 | 
			
		||||
  # If provided then this command will be executed on remote host.
 | 
			
		||||
  _migratedeployconf Le_Deploy_ssh_remote_cmd DEPLOY_SSH_REMOTE_CMD
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_REMOTE_CMD
 | 
			
		||||
  _debug2 DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
 | 
			
		||||
    _savedeployconf DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # USE_SCP is optional. If not provided then default to previously saved
 | 
			
		||||
  # value (which may be undefined... equivalent to "no").
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_USE_SCP
 | 
			
		||||
  _debug2 DEPLOY_SSH_USE_SCP "$DEPLOY_SSH_USE_SCP"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_USE_SCP" ]; then
 | 
			
		||||
    DEPLOY_SSH_USE_SCP="no"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_USE_SCP "$DEPLOY_SSH_USE_SCP"
 | 
			
		||||
 | 
			
		||||
  # SCP_CMD is optional. If not provided then use scp
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_SCP_CMD
 | 
			
		||||
  _debug2 DEPLOY_SSH_SCP_CMD "$DEPLOY_SSH_SCP_CMD"
 | 
			
		||||
  if [ -z "$DEPLOY_SSH_SCP_CMD" ]; then
 | 
			
		||||
    DEPLOY_SSH_SCP_CMD="scp -q"
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf DEPLOY_SSH_SCP_CMD "$DEPLOY_SSH_SCP_CMD"
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
    DEPLOY_SSH_MULTI_CALL="yes"
 | 
			
		||||
    _info "Using scp as alternate method for copying files. Multicall Mode is implicit"
 | 
			
		||||
  elif [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
    _info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host"
 | 
			
		||||
  else
 | 
			
		||||
    _info "Required commands batched and sent in single call to remote host"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _deploy_ssh_servers="$DEPLOY_SSH_SERVER"
 | 
			
		||||
  for DEPLOY_SSH_SERVER in $_deploy_ssh_servers; do
 | 
			
		||||
    _ssh_deploy
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
@@ -117,16 +181,25 @@ _ssh_deploy() {
 | 
			
		||||
  _cmdstr=""
 | 
			
		||||
  _backupprefix=""
 | 
			
		||||
  _backupdir=""
 | 
			
		||||
  _local_cert_file=""
 | 
			
		||||
  _local_ca_file=""
 | 
			
		||||
  _local_full_file=""
 | 
			
		||||
 | 
			
		||||
  _info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server"
 | 
			
		||||
  if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
    _info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host"
 | 
			
		||||
  else
 | 
			
		||||
    _info "Required commands batched and sent in single call to remote host"
 | 
			
		||||
  fi
 | 
			
		||||
  case $DEPLOY_SSH_SERVER in
 | 
			
		||||
  *:*)
 | 
			
		||||
    _host=${DEPLOY_SSH_SERVER%:*}
 | 
			
		||||
    _port=${DEPLOY_SSH_SERVER##*:}
 | 
			
		||||
    ;;
 | 
			
		||||
  *)
 | 
			
		||||
    _host=$DEPLOY_SSH_SERVER
 | 
			
		||||
    _port=
 | 
			
		||||
    ;;
 | 
			
		||||
  esac
 | 
			
		||||
 | 
			
		||||
  if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    _backupprefix="$Le_Deploy_ssh_backup_path/$_cdomain-backup"
 | 
			
		||||
  _info "Deploy certificates to remote server $DEPLOY_SSH_USER@$_host:$_port"
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
    _backupprefix="$DEPLOY_SSH_BACKUP_PATH/$_cdomain-backup"
 | 
			
		||||
    _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
 | 
			
		||||
    # run cleanup on the backup directory, erase all older
 | 
			
		||||
    # than 180 days (15552000 seconds).
 | 
			
		||||
@@ -138,7 +211,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
 | 
			
		||||
    _cmdstr="mkdir -p $_backupdir; $_cmdstr"
 | 
			
		||||
    _info "Backup of old certificate files will be placed in remote directory $_backupdir"
 | 
			
		||||
    _info "Backup directories erased after 180 days."
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
@@ -146,129 +219,184 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # KEYFILE is optional.
 | 
			
		||||
  # If provided then private key will be copied to provided filename.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_KEYFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
    Le_Deploy_ssh_keyfile="$DEPLOY_SSH_KEYFILE"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_keyfile "$Le_Deploy_ssh_keyfile"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -n "$Le_Deploy_ssh_keyfile" ]; then
 | 
			
		||||
    if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir >/dev/null;"
 | 
			
		||||
      _cmdstr="$_cmdstr cp $DEPLOY_SSH_KEYFILE $_backupdir >/dev/null;"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
    # copy new certificate into file.
 | 
			
		||||
    _cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;"
 | 
			
		||||
    _info "will copy private key to remote file $Le_Deploy_ssh_keyfile"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
 | 
			
		||||
    # copy new key into file.
 | 
			
		||||
    if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
      # scp the file
 | 
			
		||||
      if ! _scp_remote_cmd "$_ckey" "$DEPLOY_SSH_KEYFILE"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    else
 | 
			
		||||
      # ssh echo to the file
 | 
			
		||||
      _cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE;"
 | 
			
		||||
      _info "will copy private key to remote file $DEPLOY_SSH_KEYFILE"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # CERTFILE is optional.
 | 
			
		||||
  # If provided then certificate will be copied or appended to provided filename.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_CERTFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
    Le_Deploy_ssh_certfile="$DEPLOY_SSH_CERTFILE"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -n "$Le_Deploy_ssh_certfile" ]; then
 | 
			
		||||
    _pipe=">"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
      # if filename is same as previous file then append.
 | 
			
		||||
      _pipe=">>"
 | 
			
		||||
    elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir >/dev/null;"
 | 
			
		||||
      _cmdstr="$_cmdstr cp $DEPLOY_SSH_CERTFILE $_backupdir >/dev/null;"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # copy new certificate into file.
 | 
			
		||||
    _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;"
 | 
			
		||||
    _info "will copy certificate to remote file $Le_Deploy_ssh_certfile"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
      # scp the file
 | 
			
		||||
      _local_cert_file=$(_mktemp)
 | 
			
		||||
      if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
        cat "$_ckey" >>"$_local_cert_file"
 | 
			
		||||
      fi
 | 
			
		||||
      cat "$_ccert" >>"$_local_cert_file"
 | 
			
		||||
      if ! _scp_remote_cmd "$_local_cert_file" "$DEPLOY_SSH_CERTFILE"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    else
 | 
			
		||||
      # ssh echo to the file
 | 
			
		||||
      _cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $DEPLOY_SSH_CERTFILE;"
 | 
			
		||||
      _info "will copy certificate to remote file $DEPLOY_SSH_CERTFILE"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # CAFILE is optional.
 | 
			
		||||
  # If provided then CA intermediate certificate will be copied or appended to provided filename.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_CAFILE
 | 
			
		||||
  _debug2 DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_CAFILE" ]; then
 | 
			
		||||
    Le_Deploy_ssh_cafile="$DEPLOY_SSH_CAFILE"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -n "$Le_Deploy_ssh_cafile" ]; then
 | 
			
		||||
    _pipe=">"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_keyfile" ] ||
 | 
			
		||||
      [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_certfile" ]; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ] ||
 | 
			
		||||
      [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
      # if filename is same as previous file then append.
 | 
			
		||||
      _pipe=">>"
 | 
			
		||||
    elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir >/dev/null;"
 | 
			
		||||
      _cmdstr="$_cmdstr cp $DEPLOY_SSH_CAFILE $_backupdir >/dev/null;"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # copy new certificate into file.
 | 
			
		||||
    _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;"
 | 
			
		||||
    _info "will copy CA file to remote file $Le_Deploy_ssh_cafile"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
      # scp the file
 | 
			
		||||
      _local_ca_file=$(_mktemp)
 | 
			
		||||
      if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
        cat "$_ckey" >>"$_local_ca_file"
 | 
			
		||||
      fi
 | 
			
		||||
      if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
        cat "$_ccert" >>"$_local_ca_file"
 | 
			
		||||
      fi
 | 
			
		||||
      cat "$_cca" >>"$_local_ca_file"
 | 
			
		||||
      if ! _scp_remote_cmd "$_local_ca_file" "$DEPLOY_SSH_CAFILE"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    else
 | 
			
		||||
      # ssh echo to the file
 | 
			
		||||
      _cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $DEPLOY_SSH_CAFILE;"
 | 
			
		||||
      _info "will copy CA file to remote file $DEPLOY_SSH_CAFILE"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # FULLCHAIN is optional.
 | 
			
		||||
  # If provided then fullchain certificate will be copied or appended to provided filename.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_FULLCHAIN
 | 
			
		||||
  _debug2 DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
 | 
			
		||||
    Le_Deploy_ssh_fullchain="$DEPLOY_SSH_FULLCHAIN"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -n "$Le_Deploy_ssh_fullchain" ]; then
 | 
			
		||||
    _pipe=">"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_keyfile" ] ||
 | 
			
		||||
      [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_certfile" ] ||
 | 
			
		||||
      [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_cafile" ]; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ] ||
 | 
			
		||||
      [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ] ||
 | 
			
		||||
      [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
 | 
			
		||||
      # if filename is same as previous file then append.
 | 
			
		||||
      _pipe=">>"
 | 
			
		||||
    elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then
 | 
			
		||||
    elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
 | 
			
		||||
      # backup file we are about to overwrite.
 | 
			
		||||
      _cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir >/dev/null;"
 | 
			
		||||
      _cmdstr="$_cmdstr cp $DEPLOY_SSH_FULLCHAIN $_backupdir >/dev/null;"
 | 
			
		||||
      if [ "$DEPLOY_SSH_FULLCHAIN" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # copy new certificate into file.
 | 
			
		||||
    _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;"
 | 
			
		||||
    _info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
    if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
 | 
			
		||||
      # scp the file
 | 
			
		||||
      _local_full_file=$(_mktemp)
 | 
			
		||||
      if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ]; then
 | 
			
		||||
        cat "$_ckey" >>"$_local_full_file"
 | 
			
		||||
      fi
 | 
			
		||||
      if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ]; then
 | 
			
		||||
        cat "$_ccert" >>"$_local_full_file"
 | 
			
		||||
      fi
 | 
			
		||||
      if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
 | 
			
		||||
        cat "$_cca" >>"$_local_full_file"
 | 
			
		||||
      fi
 | 
			
		||||
      cat "$_cfullchain" >>"$_local_full_file"
 | 
			
		||||
      if ! _scp_remote_cmd "$_local_full_file" "$DEPLOY_SSH_FULLCHAIN"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
      _cmdstr=""
 | 
			
		||||
    else
 | 
			
		||||
      # ssh echo to the file
 | 
			
		||||
      _cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $DEPLOY_SSH_FULLCHAIN;"
 | 
			
		||||
      _info "will copy fullchain to remote file $DEPLOY_SSH_FULLCHAIN"
 | 
			
		||||
      if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
        if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
          return $_err_code
 | 
			
		||||
        fi
 | 
			
		||||
        _cmdstr=""
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # REMOTE_CMD is optional.
 | 
			
		||||
  # If provided then this command will be executed on remote host.
 | 
			
		||||
  _getdeployconf DEPLOY_SSH_REMOTE_CMD
 | 
			
		||||
  _debug2 DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
 | 
			
		||||
    Le_Deploy_ssh_remote_cmd="$DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
    _savedomainconf Le_Deploy_ssh_remote_cmd "$Le_Deploy_ssh_remote_cmd"
 | 
			
		||||
  # cleanup local files if any
 | 
			
		||||
  if [ -f "$_local_cert_file" ]; then
 | 
			
		||||
    rm -f "$_local_cert_file"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then
 | 
			
		||||
    _cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd;"
 | 
			
		||||
    _info "Will execute remote command $Le_Deploy_ssh_remote_cmd"
 | 
			
		||||
    if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
 | 
			
		||||
  if [ -f "$_local_ca_file" ]; then
 | 
			
		||||
    rm -f "$_local_ca_file"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -f "$_local_full_file" ]; then
 | 
			
		||||
    rm -f "$_local_full_file"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
 | 
			
		||||
    _cmdstr="$_cmdstr $DEPLOY_SSH_REMOTE_CMD;"
 | 
			
		||||
    _info "Will execute remote command $DEPLOY_SSH_REMOTE_CMD"
 | 
			
		||||
    if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
 | 
			
		||||
      if ! _ssh_remote_cmd "$_cmdstr"; then
 | 
			
		||||
        return $_err_code
 | 
			
		||||
      fi
 | 
			
		||||
@@ -282,17 +410,25 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
 | 
			
		||||
      return $_err_code
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  # cleanup in case all is ok
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#cmd
 | 
			
		||||
_ssh_remote_cmd() {
 | 
			
		||||
  _cmd="$1"
 | 
			
		||||
 | 
			
		||||
  _ssh_cmd="$DEPLOY_SSH_CMD"
 | 
			
		||||
  if [ -n "$_port" ]; then
 | 
			
		||||
    _ssh_cmd="$_ssh_cmd -p $_port"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _secure_debug "Remote commands to execute: $_cmd"
 | 
			
		||||
  _info "Submitting sequence of commands to remote server by ssh"
 | 
			
		||||
  _info "Submitting sequence of commands to remote server by $_ssh_cmd"
 | 
			
		||||
 | 
			
		||||
  # quotations in bash cmd below intended.  Squash travis spellcheck error
 | 
			
		||||
  # shellcheck disable=SC2029
 | 
			
		||||
  $Le_Deploy_ssh_cmd "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmd'"
 | 
			
		||||
  $_ssh_cmd "$DEPLOY_SSH_USER@$_host" sh -c "'$_cmd'"
 | 
			
		||||
  _err_code="$?"
 | 
			
		||||
 | 
			
		||||
  if [ "$_err_code" != "0" ]; then
 | 
			
		||||
@@ -301,3 +437,26 @@ _ssh_remote_cmd() {
 | 
			
		||||
 | 
			
		||||
  return $_err_code
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# cmd scp
 | 
			
		||||
_scp_remote_cmd() {
 | 
			
		||||
  _src=$1
 | 
			
		||||
  _dest=$2
 | 
			
		||||
 | 
			
		||||
  _scp_cmd="$DEPLOY_SSH_SCP_CMD"
 | 
			
		||||
  if [ -n "$_port" ]; then
 | 
			
		||||
    _scp_cmd="$_scp_cmd -P $_port"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _secure_debug "Remote copy source $_src to destination $_dest"
 | 
			
		||||
  _info "Submitting secure copy by $_scp_cmd"
 | 
			
		||||
 | 
			
		||||
  $_scp_cmd "$_src" "$DEPLOY_SSH_USER"@"$_host":"$_dest"
 | 
			
		||||
  _err_code="$?"
 | 
			
		||||
 | 
			
		||||
  if [ "$_err_code" != "0" ]; then
 | 
			
		||||
    _err "Error code $_err_code returned from scp"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return $_err_code
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,12 @@ synology_dsm_deploy() {
 | 
			
		||||
 | 
			
		||||
  otp_code=""
 | 
			
		||||
  if [ -n "$SYNO_TOTP_SECRET" ]; then
 | 
			
		||||
    otp_code="$(oathtool --base32 --totp "${SYNO_TOTP_SECRET}" 2>/dev/null)"
 | 
			
		||||
    if _exists oathtool; then
 | 
			
		||||
      otp_code="$(oathtool --base32 --totp "${SYNO_TOTP_SECRET}" 2>/dev/null)"
 | 
			
		||||
    else
 | 
			
		||||
      _err "oathtool could not be found, install oathtool to use SYNO_TOTP_SECRET"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$SYNO_DID" ]; then
 | 
			
		||||
@@ -103,7 +108,7 @@ synology_dsm_deploy() {
 | 
			
		||||
    _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=$otp_code" "$_base_url/webapi/auth.cgi?enable_syno_token=yes")
 | 
			
		||||
  response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$otp_code&device_name=certrenewal&device_id=$SYNO_DID" "$_base_url/webapi/auth.cgi?enable_syno_token=yes")
 | 
			
		||||
  token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
 | 
			
		||||
  _debug3 response "$response"
 | 
			
		||||
  _debug token "$token"
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ truenas_deploy() {
 | 
			
		||||
  _getdeployconf DEPLOY_TRUENAS_APIKEY
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
 | 
			
		||||
    _err "TrueNAS Api Key is not found, please define DEPLOY_TRUENAS_APIKEY."
 | 
			
		||||
    _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"
 | 
			
		||||
@@ -62,15 +62,14 @@ truenas_deploy() {
 | 
			
		||||
 | 
			
		||||
  _info "Testing Connection TrueNAS"
 | 
			
		||||
  _response=$(_get "$_api_url/system/state")
 | 
			
		||||
  _info "TrueNAS System State: $_response."
 | 
			
		||||
  _info "TrueNAS system state: $_response."
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_response" ]; then
 | 
			
		||||
    _err "Unable to authenticate to $_api_url."
 | 
			
		||||
    _err 'Check your Connection and set DEPLOY_TRUENAS_HOSTNAME="192.168.178.x".'
 | 
			
		||||
    _err 'or'
 | 
			
		||||
    _err 'set DEPLOY_TRUENAS_HOSTNAME="<truenas_dnsname>".'
 | 
			
		||||
    _err 'Check your Connection and set DEPLOY_TRUENAS_SCHEME="https".'
 | 
			
		||||
    _err "Check your Api Key."
 | 
			
		||||
    _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
 | 
			
		||||
 | 
			
		||||
@@ -78,7 +77,7 @@ truenas_deploy() {
 | 
			
		||||
  _savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
 | 
			
		||||
  _savedeployconf DEPLOY_TRUENAS_SCHEME "$DEPLOY_TRUENAS_SCHEME"
 | 
			
		||||
 | 
			
		||||
  _info "Getting active certificate from TrueNAS"
 | 
			
		||||
  _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')
 | 
			
		||||
@@ -88,14 +87,14 @@ truenas_deploy() {
 | 
			
		||||
  _debug Active_UI_http_redirect "$_param_httpsredirect"
 | 
			
		||||
 | 
			
		||||
  if [ "$DEPLOY_TRUENAS_SCHEME" = "http" ] && [ "$_param_httpsredirect" = "true" ]; then
 | 
			
		||||
    _info "http Redirect active"
 | 
			
		||||
    _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 "Upload new certifikate to TrueNAS"
 | 
			
		||||
  _info "Uploading new certificate to TrueNAS"
 | 
			
		||||
  _certname="Letsencrypt_$(_utc_date | tr ' ' '_' | tr -d -- ':')"
 | 
			
		||||
  _debug3 _certname "$_certname"
 | 
			
		||||
 | 
			
		||||
@@ -104,30 +103,30 @@ truenas_deploy() {
 | 
			
		||||
 | 
			
		||||
  _debug3 _add_cert_result "$_add_cert_result"
 | 
			
		||||
 | 
			
		||||
  _info "Getting Certificate list to get new Cert ID"
 | 
			
		||||
  _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 "Activate Certificate ID: $_cert_id"
 | 
			
		||||
  _info "Current activate certificate ID: $_cert_id"
 | 
			
		||||
  _activateData="{\"ui_certificate\": \"${_cert_id}\"}"
 | 
			
		||||
  _activate_result="$(_post "$_activateData" "$_api_url/system/general" "" "PUT" "application/json")"
 | 
			
		||||
 | 
			
		||||
  _debug3 _activate_result "$_activate_result"
 | 
			
		||||
 | 
			
		||||
  _info "Check if WebDAV certificate is the same as the WEB UI"
 | 
			
		||||
  _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 "Update the WebDAV Certificate"
 | 
			
		||||
    _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 | sed -n 's/.*: \([0-9]\{1,\}\) }$/\1/p')
 | 
			
		||||
    _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 update successfully"
 | 
			
		||||
      _info "WebDAV certificate updated successfully"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Unable to set WebDAV certificate"
 | 
			
		||||
      _debug3 _activate_webdav_cert "$_activate_webdav_cert"
 | 
			
		||||
@@ -136,21 +135,21 @@ truenas_deploy() {
 | 
			
		||||
    fi
 | 
			
		||||
    _debug3 _webdav_new_cert_id "$_webdav_new_cert_id"
 | 
			
		||||
  else
 | 
			
		||||
    _info "WebDAV certificate not set or not the same as Web UI"
 | 
			
		||||
    _info "WebDAV certificate is not configured or is not the same as TrueNAS web UI"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Check if FTP certificate is the same as the WEB UI"
 | 
			
		||||
  _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 "Update the FTP Certificate"
 | 
			
		||||
    _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 | sed -n 's/.*: \([0-9]\{1,\}\) }$/\1/p')
 | 
			
		||||
    _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 update successfully"
 | 
			
		||||
      _info "FTP certificate updated successfully"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Unable to set FTP certificate"
 | 
			
		||||
      _debug3 _activate_ftp_cert "$_activate_ftp_cert"
 | 
			
		||||
@@ -159,22 +158,66 @@ truenas_deploy() {
 | 
			
		||||
    fi
 | 
			
		||||
    _debug3 _activate_ftp_cert "$_activate_ftp_cert"
 | 
			
		||||
  else
 | 
			
		||||
    _info "FTP certificate not set or not the same as Web UI"
 | 
			
		||||
    _info "FTP certificate is not configured or is not the same as TrueNAS web UI"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Delete old Certificate"
 | 
			
		||||
  _info "Checking if S3 certificate is the same as the TrueNAS web UI"
 | 
			
		||||
  _s3_list=$(_get "$_api_url/s3")
 | 
			
		||||
  _s3_cert_id=$(echo "$_s3_list" | grep '"certificate":' | tr -d -- '"certifa:_ ,')
 | 
			
		||||
 | 
			
		||||
  if [ "$_s3_cert_id" = "$_active_cert_id" ]; then
 | 
			
		||||
    _info "Updating the S3 certificate"
 | 
			
		||||
    _debug _s3_cert_id "$_s3_cert_id"
 | 
			
		||||
    _s3_data="{\"certificate\": \"${_cert_id}\"}"
 | 
			
		||||
    _activate_s3_cert="$(_post "$_s3_data" "$_api_url/s3" "" "PUT" "application/json")"
 | 
			
		||||
    _s3_new_cert_id=$(echo "$_activate_s3_cert" | _json_decode | grep '"certificate":' | sed -n 's/.*: \([0-9]\{1,\}\),\{0,1\}$/\1/p')
 | 
			
		||||
    if [ "$_s3_new_cert_id" -eq "$_cert_id" ]; then
 | 
			
		||||
      _info "S3 certificate updated successfully"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Unable to set S3 certificate"
 | 
			
		||||
      _debug3 _activate_s3_cert "$_activate_s3_cert"
 | 
			
		||||
      _debug3 _s3_new_cert_id "$_s3_new_cert_id"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _debug3 _activate_s3_cert "$_activate_s3_cert"
 | 
			
		||||
  else
 | 
			
		||||
    _info "S3 certificate is not configured or is not the same as TrueNAS web UI"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Checking if any chart release Apps is using the same certificate as TrueNAS web UI. Tool 'jq' is required"
 | 
			
		||||
  if _exists jq; then
 | 
			
		||||
    _info "Query all chart release"
 | 
			
		||||
    _release_list=$(_get "$_api_url/chart/release")
 | 
			
		||||
    _related_name_list=$(printf "%s" "$_release_list" | jq -r "[.[] | {name,certId: .config.ingress?.main.tls[]?.scaleCert} | select(.certId==$_active_cert_id) | .name ] | unique")
 | 
			
		||||
    _release_length=$(printf "%s" "$_related_name_list" | jq -r "length")
 | 
			
		||||
    _info "Found $_release_length related chart release in list: $_related_name_list"
 | 
			
		||||
    for i in $(seq 0 $((_release_length - 1))); do
 | 
			
		||||
      _release_name=$(echo "$_related_name_list" | jq -r ".[$i]")
 | 
			
		||||
      _info "Updating certificate from $_active_cert_id to $_cert_id for chart release: $_release_name"
 | 
			
		||||
      #Read the chart release configuration
 | 
			
		||||
      _chart_config=$(printf "%s" "$_release_list" | jq -r ".[] | select(.name==\"$_release_name\")")
 | 
			
		||||
      #Replace the old certificate id with the new one in path .config.ingress.main.tls[].scaleCert. Then update .config.ingress
 | 
			
		||||
      _updated_chart_config=$(printf "%s" "$_chart_config" | jq "(.config.ingress?.main.tls[]? | select(.scaleCert==$_active_cert_id) | .scaleCert  ) |= $_cert_id | .config.ingress ")
 | 
			
		||||
      _update_chart_result="$(_post "{\"values\" : { \"ingress\" : $_updated_chart_config } }" "$_api_url/chart/release/id/$_release_name" "" "PUT" "application/json")"
 | 
			
		||||
      _debug3 _update_chart_result "$_update_chart_result"
 | 
			
		||||
    done
 | 
			
		||||
  else
 | 
			
		||||
    _info "Tool 'jq' does not exists, skip chart release checking"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Deleting old certificate"
 | 
			
		||||
  _delete_result="$(_post "" "$_api_url/certificate/id/$_active_cert_id" "" "DELETE" "application/json")"
 | 
			
		||||
 | 
			
		||||
  _debug3 _delete_result "$_delete_result"
 | 
			
		||||
 | 
			
		||||
  _info "Reload WebUI from TrueNAS"
 | 
			
		||||
  _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 "Certupdate was not succesfull, please use --debug"
 | 
			
		||||
    _err "Certificate update was not succesful, please try again with --debug"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,16 @@
 | 
			
		||||
#
 | 
			
		||||
# VAULT_PREFIX - this contains the prefix path in vault
 | 
			
		||||
# VAULT_ADDR - vault requires this to find your vault server
 | 
			
		||||
# VAULT_SAVE_TOKEN - set to anything if you want to save the token
 | 
			
		||||
# VAULT_RENEW_TOKEN - set to anything if you want to renew the token to default TTL before deploying
 | 
			
		||||
# VAULT_KV_V2 - set to anything if you are using v2 of the kv engine
 | 
			
		||||
#
 | 
			
		||||
# additionally, you need to ensure that VAULT_TOKEN is avialable
 | 
			
		||||
# to access the vault server
 | 
			
		||||
 | 
			
		||||
#returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
######## Public functions #####################
 | 
			
		||||
 | 
			
		||||
#domain keyfile certfile cafile fullchain
 | 
			
		||||
vault_deploy() {
 | 
			
		||||
@@ -45,6 +48,26 @@ vault_deploy() {
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf VAULT_ADDR "$VAULT_ADDR"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_SAVE_TOKEN
 | 
			
		||||
  _savedeployconf VAULT_SAVE_TOKEN "$VAULT_SAVE_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_RENEW_TOKEN
 | 
			
		||||
  _savedeployconf VAULT_RENEW_TOKEN "$VAULT_RENEW_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_KV_V2
 | 
			
		||||
  _savedeployconf VAULT_KV_V2 "$VAULT_KV_V2"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_TOKEN
 | 
			
		||||
  if [ -z "$VAULT_TOKEN" ]; then
 | 
			
		||||
    _err "VAULT_TOKEN needs to be defined"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -n "$VAULT_SAVE_TOKEN" ]; then
 | 
			
		||||
    _savedeployconf VAULT_TOKEN "$VAULT_TOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _migratedeployconf FABIO VAULT_FABIO_MODE
 | 
			
		||||
 | 
			
		||||
  # JSON does not allow multiline strings.
 | 
			
		||||
  # So replacing new-lines with "\n" here
 | 
			
		||||
  _ckey=$(sed -z 's/\n/\\n/g' <"$2")
 | 
			
		||||
@@ -52,26 +75,56 @@ vault_deploy() {
 | 
			
		||||
  _cca=$(sed -z 's/\n/\\n/g' <"$4")
 | 
			
		||||
  _cfullchain=$(sed -z 's/\n/\\n/g' <"$5")
 | 
			
		||||
 | 
			
		||||
  URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain"
 | 
			
		||||
  export _H1="X-Vault-Token: $VAULT_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$FABIO" ]; then
 | 
			
		||||
  if [ -n "$VAULT_RENEW_TOKEN" ]; then
 | 
			
		||||
    URL="$VAULT_ADDR/v1/auth/token/renew-self"
 | 
			
		||||
    _info "Renew the Vault token to default TTL"
 | 
			
		||||
    if ! _post "" "$URL" >/dev/null; then
 | 
			
		||||
      _err "Failed to renew the Vault token"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  URL="$VAULT_ADDR/v1/$VAULT_PREFIX/$_cdomain"
 | 
			
		||||
 | 
			
		||||
  if [ -n "$VAULT_FABIO_MODE" ]; then
 | 
			
		||||
    _info "Writing certificate and key to $URL in Fabio mode"
 | 
			
		||||
    if [ -n "$VAULT_KV_V2" ]; then
 | 
			
		||||
      _post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL"
 | 
			
		||||
      _post "{ \"data\": {\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"} }" "$URL" >/dev/null || return 1
 | 
			
		||||
    else
 | 
			
		||||
      _post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL"
 | 
			
		||||
      _post "{\"cert\": \"$_cfullchain\", \"key\": \"$_ckey\"}" "$URL" >/dev/null || return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    if [ -n "$VAULT_KV_V2" ]; then
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem"
 | 
			
		||||
      _info "Writing certificate to $URL/cert.pem"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_ccert\"}}" "$URL/cert.pem" >/dev/null || return 1
 | 
			
		||||
      _info "Writing key to $URL/cert.key"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_ckey\"}}" "$URL/cert.key" >/dev/null || return 1
 | 
			
		||||
      _info "Writing CA certificate to $URL/ca.pem"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/ca.pem" >/dev/null || return 1
 | 
			
		||||
      _info "Writing full-chain certificate to $URL/fullchain.pem"
 | 
			
		||||
      _post "{\"data\": {\"value\": \"$_cfullchain\"}}" "$URL/fullchain.pem" >/dev/null || return 1
 | 
			
		||||
    else
 | 
			
		||||
      _post "{\"value\": \"$_ccert\"}" "$URL/cert.pem"
 | 
			
		||||
      _post "{\"value\": \"$_ckey\"}" "$URL/cert.key"
 | 
			
		||||
      _post "{\"value\": \"$_cca\"}" "$URL/chain.pem"
 | 
			
		||||
      _post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem"
 | 
			
		||||
      _info "Writing certificate to $URL/cert.pem"
 | 
			
		||||
      _post "{\"value\": \"$_ccert\"}" "$URL/cert.pem" >/dev/null || return 1
 | 
			
		||||
      _info "Writing key to $URL/cert.key"
 | 
			
		||||
      _post "{\"value\": \"$_ckey\"}" "$URL/cert.key" >/dev/null || return 1
 | 
			
		||||
      _info "Writing CA certificate to $URL/ca.pem"
 | 
			
		||||
      _post "{\"value\": \"$_cca\"}" "$URL/ca.pem" >/dev/null || return 1
 | 
			
		||||
      _info "Writing full-chain certificate to $URL/fullchain.pem"
 | 
			
		||||
      _post "{\"value\": \"$_cfullchain\"}" "$URL/fullchain.pem" >/dev/null || return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # To make it compatible with the wrong ca path `chain.pem` which was used in former versions
 | 
			
		||||
    if _contains "$(_get "$URL/chain.pem")" "-----BEGIN CERTIFICATE-----"; then
 | 
			
		||||
      _err "The CA certificate has moved from chain.pem to ca.pem, if you don't depend on chain.pem anymore, you can delete it to avoid this warning"
 | 
			
		||||
      _info "Updating CA certificate to $URL/chain.pem for backward compatibility"
 | 
			
		||||
      if [ -n "$VAULT_KV_V2" ]; then
 | 
			
		||||
        _post "{\"data\": {\"value\": \"$_cca\"}}" "$URL/chain.pem" >/dev/null || return 1
 | 
			
		||||
      else
 | 
			
		||||
        _post "{\"value\": \"$_cca\"}" "$URL/chain.pem" >/dev/null || return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@
 | 
			
		||||
#
 | 
			
		||||
# VAULT_PREFIX - this contains the prefix path in vault
 | 
			
		||||
# VAULT_ADDR - vault requires this to find your vault server
 | 
			
		||||
# VAULT_SAVE_TOKEN - set to anything if you want to save the token
 | 
			
		||||
# VAULT_RENEW_TOKEN - set to anything if you want to renew the token to default TTL before deploying
 | 
			
		||||
#
 | 
			
		||||
# additionally, you need to ensure that VAULT_TOKEN is avialable or
 | 
			
		||||
# `vault auth` has applied the appropriate authorization for the vault binary
 | 
			
		||||
@@ -33,15 +35,36 @@ vault_cli_deploy() {
 | 
			
		||||
  _debug _cfullchain "$_cfullchain"
 | 
			
		||||
 | 
			
		||||
  # validate required env vars
 | 
			
		||||
  _getdeployconf VAULT_PREFIX
 | 
			
		||||
  if [ -z "$VAULT_PREFIX" ]; then
 | 
			
		||||
    _err "VAULT_PREFIX needs to be defined (contains prefix path in vault)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf VAULT_PREFIX "$VAULT_PREFIX"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_ADDR
 | 
			
		||||
  if [ -z "$VAULT_ADDR" ]; then
 | 
			
		||||
    _err "VAULT_ADDR needs to be defined (contains vault connection address)"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _savedeployconf VAULT_ADDR "$VAULT_ADDR"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_SAVE_TOKEN
 | 
			
		||||
  _savedeployconf VAULT_SAVE_TOKEN "$VAULT_SAVE_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_RENEW_TOKEN
 | 
			
		||||
  _savedeployconf VAULT_RENEW_TOKEN "$VAULT_RENEW_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _getdeployconf VAULT_TOKEN
 | 
			
		||||
  if [ -z "$VAULT_TOKEN" ]; then
 | 
			
		||||
    _err "VAULT_TOKEN needs to be defined"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -n "$VAULT_SAVE_TOKEN" ]; then
 | 
			
		||||
    _savedeployconf VAULT_TOKEN "$VAULT_TOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _migratedeployconf FABIO VAULT_FABIO_MODE
 | 
			
		||||
 | 
			
		||||
  VAULT_CMD=$(command -v vault)
 | 
			
		||||
  if [ ! $? ]; then
 | 
			
		||||
@@ -49,13 +72,33 @@ vault_cli_deploy() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$FABIO" ]; then
 | 
			
		||||
  if [ -n "$VAULT_RENEW_TOKEN" ]; then
 | 
			
		||||
    _info "Renew the Vault token to default TTL"
 | 
			
		||||
    if ! $VAULT_CMD token renew; then
 | 
			
		||||
      _err "Failed to renew the Vault token"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "$VAULT_FABIO_MODE" ]; then
 | 
			
		||||
    _info "Writing certificate and key to ${VAULT_PREFIX}/${_cdomain} in Fabio mode"
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}" cert=@"$_cfullchain" key=@"$_ckey" || return 1
 | 
			
		||||
  else
 | 
			
		||||
    _info "Writing certificate to ${VAULT_PREFIX}/${_cdomain}/cert.pem"
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
 | 
			
		||||
    _info "Writing key to ${VAULT_PREFIX}/${_cdomain}/cert.key"
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
 | 
			
		||||
    _info "Writing CA certificate to ${VAULT_PREFIX}/${_cdomain}/ca.pem"
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/ca.pem" value=@"$_cca" || return 1
 | 
			
		||||
    _info "Writing full-chain certificate to ${VAULT_PREFIX}/${_cdomain}/fullchain.pem"
 | 
			
		||||
    $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
 | 
			
		||||
 | 
			
		||||
    # To make it compatible with the wrong ca path `chain.pem` which was used in former versions
 | 
			
		||||
    if $VAULT_CMD kv get "${VAULT_PREFIX}/${_cdomain}/chain.pem" >/dev/null; then
 | 
			
		||||
      _err "The CA certificate has moved from chain.pem to ca.pem, if you don't depend on chain.pem anymore, you can delete it to avoid this warning"
 | 
			
		||||
      _info "Updating CA certificate to ${VAULT_PREFIX}/${_cdomain}/chain.pem for backward compatibility"
 | 
			
		||||
      $VAULT_CMD kv put "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ dns_1984hosting_add() {
 | 
			
		||||
 | 
			
		||||
  _debug "Add TXT record $fulldomain with value '$txtvalue'"
 | 
			
		||||
  value="$(printf '%s' "$txtvalue" | _url_encode)"
 | 
			
		||||
  url="https://management.1984hosting.com/domains/entry/"
 | 
			
		||||
  url="https://1984.hosting/domains/entry/"
 | 
			
		||||
 | 
			
		||||
  postdata="entry=new"
 | 
			
		||||
  postdata="$postdata&type=TXT"
 | 
			
		||||
@@ -95,7 +95,7 @@ dns_1984hosting_rm() {
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
  _debug "Delete $fulldomain TXT record"
 | 
			
		||||
 | 
			
		||||
  url="https://management.1984hosting.com/domains"
 | 
			
		||||
  url="https://1984.hosting/domains"
 | 
			
		||||
  if ! _get_zone_id "$url" "$_domain"; then
 | 
			
		||||
    _err "invalid zone" "$_domain"
 | 
			
		||||
    return 1
 | 
			
		||||
@@ -138,7 +138,7 @@ _1984hosting_login() {
 | 
			
		||||
  _debug "Login to 1984Hosting as user $One984HOSTING_Username"
 | 
			
		||||
  username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
 | 
			
		||||
  password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
 | 
			
		||||
  url="https://management.1984hosting.com/accounts/checkuserauth/"
 | 
			
		||||
  url="https://1984.hosting/accounts/checkuserauth/"
 | 
			
		||||
 | 
			
		||||
  response="$(_post "username=$username&password=$password&otpkey=" $url)"
 | 
			
		||||
  response="$(echo "$response" | _normalizeJson)"
 | 
			
		||||
@@ -175,7 +175,7 @@ _check_cookies() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _authget "https://management.1984hosting.com/accounts/loginstatus/"
 | 
			
		||||
  _authget "https://1984.hosting/accounts/loginstatus/"
 | 
			
		||||
  if _contains "$response" '"ok": true'; then
 | 
			
		||||
    _debug "Cached cookies still valid"
 | 
			
		||||
    return 0
 | 
			
		||||
@@ -204,7 +204,7 @@ _get_root() {
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _authget "https://management.1984hosting.com/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
 | 
			
		||||
    _authget "https://1984.hosting/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
 | 
			
		||||
    if _contains "$_response" "serial" && ! _contains "$_response" "null"; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
      _domain="$h"
 | 
			
		||||
@@ -251,11 +251,11 @@ _htmlget() {
 | 
			
		||||
 | 
			
		||||
# add extra headers to request
 | 
			
		||||
_authpost() {
 | 
			
		||||
  url="https://management.1984hosting.com/domains"
 | 
			
		||||
  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://management.1984hosting.com/domains/$_zone_id"
 | 
			
		||||
  export _H2="Referer: https://1984.hosting/domains/$_zone_id"
 | 
			
		||||
  export _H3="X-CSRFToken: $csrf_header"
 | 
			
		||||
  _response=$(_post "$1" "$2")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
## Acmeproxy DNS provider to be used with acmeproxy (http://github.com/mdbraber/acmeproxy)
 | 
			
		||||
## Acmeproxy DNS provider to be used with acmeproxy (https://github.com/mdbraber/acmeproxy)
 | 
			
		||||
## API integration by Maarten den Braber
 | 
			
		||||
##
 | 
			
		||||
## Report any bugs via https://github.com/mdbraber/acme.sh
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Arvan_Token="Apikey xxxx"
 | 
			
		||||
# Arvan_Token="Apikey xxxx"
 | 
			
		||||
 | 
			
		||||
ARVAN_API_URL="https://napi.arvancloud.com/cdn/4.0/domains"
 | 
			
		||||
#Author: Vahid Fardi
 | 
			
		||||
#Report Bugs here: https://github.com/Neilpang/acme.sh
 | 
			
		||||
ARVAN_API_URL="https://napi.arvancloud.ir/cdn/4.0/domains"
 | 
			
		||||
# Author: Vahid Fardi
 | 
			
		||||
# Report Bugs here: https://github.com/Neilpang/acme.sh
 | 
			
		||||
#
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +18,7 @@ dns_arvan_add() {
 | 
			
		||||
 | 
			
		||||
  if [ -z "$Arvan_Token" ]; then
 | 
			
		||||
    _err "You didn't specify \"Arvan_Token\" token yet."
 | 
			
		||||
    _err "You can get yours from here https://npanel.arvancloud.com/profile/api-keys"
 | 
			
		||||
    _err "You can get yours from here https://npanel.arvancloud.ir/profile/api-keys"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  #save the api token to the account conf file.
 | 
			
		||||
@@ -40,7 +40,7 @@ dns_arvan_add() {
 | 
			
		||||
      _info "response id is $response"
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    elif _contains "$response" "Record Data is Duplicated"; then
 | 
			
		||||
    elif _contains "$response" "Record Data is duplicate"; then
 | 
			
		||||
      _info "Already exists, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
@@ -141,6 +141,7 @@ _arvan_rest() {
 | 
			
		||||
    response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
 | 
			
		||||
  elif [ "$mtd" = "POST" ]; then
 | 
			
		||||
    export _H2="Content-Type: application/json"
 | 
			
		||||
    export _H3="Accept: application/json"
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$ARVAN_API_URL/$ep" "" "$mtd")"
 | 
			
		||||
  else
 | 
			
		||||
 
 | 
			
		||||
@@ -152,34 +152,23 @@ dns_aws_rm() {
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=2
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  if aws_rest GET "2013-04-01/hostedzone"; then
 | 
			
		||||
    while true; do
 | 
			
		||||
      h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
      _debug2 "Checking domain: $h"
 | 
			
		||||
      if [ -z "$h" ]; then
 | 
			
		||||
        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"
 | 
			
		||||
          if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then
 | 
			
		||||
            _debug "Truncated request OK"
 | 
			
		||||
            i=2
 | 
			
		||||
            p=1
 | 
			
		||||
            continue
 | 
			
		||||
          else
 | 
			
		||||
            _err "Truncated request error."
 | 
			
		||||
          fi
 | 
			
		||||
        fi
 | 
			
		||||
        #not valid
 | 
			
		||||
        _err "Invalid domain"
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
  # 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)
 | 
			
		||||
    _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" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
 | 
			
		||||
        hostedzone="$(echo "$response" | tr -d '\n' | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
 | 
			
		||||
        _debug hostedzone "$hostedzone"
 | 
			
		||||
        if [ "$hostedzone" ]; then
 | 
			
		||||
          _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
 | 
			
		||||
@@ -192,10 +181,19 @@ _get_root() {
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
      fi
 | 
			
		||||
      p=$i
 | 
			
		||||
      i=$(_math "$i" + 1)
 | 
			
		||||
      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
 | 
			
		||||
  fi
 | 
			
		||||
    p=$i
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										248
									
								
								dnsapi/dns_bunny.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								dnsapi/dns_bunny.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,248 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
## Will be called by acme.sh to add the TXT record via the Bunny DNS API.
 | 
			
		||||
## returns 0 means success, otherwise error.
 | 
			
		||||
 | 
			
		||||
## Author: nosilver4u <nosilver4u at ewww.io>
 | 
			
		||||
## GitHub: https://github.com/nosilver4u/acme.sh
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Environment Variables Required:
 | 
			
		||||
##
 | 
			
		||||
## BUNNY_API_KEY="75310dc4-ca77-9ac3-9a19-f6355db573b49ce92ae1-2655-3ebd-61ac-3a3ae34834cc"
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
#####################  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
 | 
			
		||||
}
 | 
			
		||||
@@ -25,9 +25,16 @@ dns_cf_add() {
 | 
			
		||||
  CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
 | 
			
		||||
 | 
			
		||||
  if [ "$CF_Token" ]; then
 | 
			
		||||
    _saveaccountconf_mutable CF_Token "$CF_Token"
 | 
			
		||||
    _saveaccountconf_mutable CF_Account_ID "$CF_Account_ID"
 | 
			
		||||
    _saveaccountconf_mutable CF_Zone_ID "$CF_Zone_ID"
 | 
			
		||||
    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=""
 | 
			
		||||
@@ -45,6 +52,14 @@ dns_cf_add() {
 | 
			
		||||
    #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
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@ dns_cloudns_rm() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  for i in $(echo "$response" | tr '{' "\n" | grep "$record"); do
 | 
			
		||||
  for i in $(echo "$response" | tr '{' "\n" | grep -- "$record"); do
 | 
			
		||||
    record_id=$(echo "$i" | tr ',' "\n" | grep -E '^"id"' | sed -re 's/^\"id\"\:\"([0-9]+)\"$/\1/g')
 | 
			
		||||
 | 
			
		||||
    if [ -n "$record_id" ]; then
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
# cPanel_Hostname=hostname
 | 
			
		||||
#
 | 
			
		||||
# Usage: add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
 | 
			
		||||
# Used to add txt record
 | 
			
		||||
dns_cpanel_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
@@ -120,7 +121,7 @@ _myget() {
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  _myget 'json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzones'
 | 
			
		||||
  _domains=$(echo "$_result" | sed 's/.*\(zones.*\[\).*/\1/' | cut -d':' -f2 | sed 's/"//g' | sed 's/{//g')
 | 
			
		||||
  _domains=$(echo "$_result" | _egrep_o '"[a-z0-9\.\-]*":\["; cPanel first' | cut -d':' -f1 | sed 's/"//g' | sed 's/{//g')
 | 
			
		||||
  _debug "_result is: $_result"
 | 
			
		||||
  _debug "_domains is: $_domains"
 | 
			
		||||
  if [ -z "$_domains" ]; then
 | 
			
		||||
@@ -138,15 +139,15 @@ _get_root() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_successful_update() {
 | 
			
		||||
  if (echo "$_result" | grep -q 'newserial'); then return 0; fi
 | 
			
		||||
  return 1
 | 
			
		||||
  if (echo "$_result" | _egrep_o 'data":\[[^]]*]' | grep -q '"newserial":null'); then return 1; fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_findentry() {
 | 
			
		||||
  _debug "In _findentry"
 | 
			
		||||
  #returns id of dns entry, if it exists
 | 
			
		||||
  _myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&domain=$_domain"
 | 
			
		||||
  _id=$(echo "$_result" | sed "s/.*\(line.*$fulldomain.*$txtvalue\).*/\1/" | cut -d ':' -f 2 | cut -d ',' -f 1)
 | 
			
		||||
  _id=$(echo "$_result" | sed -e "s/},{/},\n{/g" | grep "$fulldomain" | grep "$txtvalue" | _egrep_o 'line":[0-9]+' | cut -d ':' -f 2)
 | 
			
		||||
  _debug "_result is: $_result"
 | 
			
		||||
  _debug "fulldomain. is $fulldomain."
 | 
			
		||||
  _debug "txtvalue is $txtvalue"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										159
									
								
								dnsapi/dns_curanet.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								dnsapi/dns_curanet.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,159 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Script to use with curanet.dk, scannet.dk, wannafind.dk, dandomain.dk DNS management.
 | 
			
		||||
#Requires api credentials with scope: dns
 | 
			
		||||
#Author: Peter L. Hansen <peter@r12.dk>
 | 
			
		||||
#Version 1.0
 | 
			
		||||
 | 
			
		||||
CURANET_REST_URL="https://api.curanet.dk/dns/v1/Domains"
 | 
			
		||||
CURANET_AUTH_URL="https://apiauth.dk.team.blue/auth/realms/Curanet/protocol/openid-connect/token"
 | 
			
		||||
CURANET_ACCESS_TOKEN=""
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_curanet_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_curanet_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using curanet"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}"
 | 
			
		||||
  CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}"
 | 
			
		||||
  if [ -z "$CURANET_AUTHCLIENTID" ] || [ -z "$CURANET_AUTHSECRET" ]; then
 | 
			
		||||
    CURANET_AUTHCLIENTID=""
 | 
			
		||||
    CURANET_AUTHSECRET=""
 | 
			
		||||
    _err "You don't specify curanet api client and secret."
 | 
			
		||||
    _err "Please create your auth info and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the credentials to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable CURANET_AUTHCLIENTID "$CURANET_AUTHCLIENTID"
 | 
			
		||||
  _saveaccountconf_mutable CURANET_AUTHSECRET "$CURANET_AUTHSECRET"
 | 
			
		||||
 | 
			
		||||
  if ! _get_token; then
 | 
			
		||||
    _err "Unable to get token"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json-patch+json"
 | 
			
		||||
  export _H2="Accept: application/json"
 | 
			
		||||
  export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
 | 
			
		||||
  data="{\"name\": \"$fulldomain\",\"type\": \"TXT\",\"ttl\": 60,\"priority\": 0,\"data\": \"$txtvalue\"}"
 | 
			
		||||
  response="$(_post "$data" "$CURANET_REST_URL/${_domain}/Records" "" "")"
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" "$txtvalue"; then
 | 
			
		||||
    _debug "TXT record added OK"
 | 
			
		||||
  else
 | 
			
		||||
    _err "Unable to add TXT record"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Remove the txt record after validation.
 | 
			
		||||
dns_curanet_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using curanet"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  CURANET_AUTHCLIENTID="${CURANET_AUTHCLIENTID:-$(_readaccountconf_mutable CURANET_AUTHCLIENTID)}"
 | 
			
		||||
  CURANET_AUTHSECRET="${CURANET_AUTHSECRET:-$(_readaccountconf_mutable CURANET_AUTHSECRET)}"
 | 
			
		||||
 | 
			
		||||
  if ! _get_token; then
 | 
			
		||||
    _err "Unable to get token"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Getting current record list to identify TXT to delete"
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  export _H2="Accept: application/json"
 | 
			
		||||
  export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
 | 
			
		||||
 | 
			
		||||
  response="$(_get "$CURANET_REST_URL/${_domain}/Records" "" "")"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "$txtvalue"; then
 | 
			
		||||
    _err "Unable to delete record (does not contain $txtvalue )"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  recordid=$(echo "$response" | _egrep_o "{\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue" | _egrep_o "id\":[0-9]+" | cut -c 5-)
 | 
			
		||||
 | 
			
		||||
  if [ -z "$recordid" ]; then
 | 
			
		||||
    _err "Unable to get recordid"
 | 
			
		||||
    _debug "regex {\"id\":[0-9]+,\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":60,\"priority\":0,\"data\":\"..$txtvalue"
 | 
			
		||||
    _debug "response $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Deleting recordID $recordid"
 | 
			
		||||
  response="$(_post "" "$CURANET_REST_URL/${_domain}/Records/$recordid" "" "DELETE")"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_get_token() {
 | 
			
		||||
  response="$(_post "grant_type=client_credentials&client_id=$CURANET_AUTHCLIENTID&client_secret=$CURANET_AUTHSECRET&scope=dns" "$CURANET_AUTH_URL" "" "")"
 | 
			
		||||
  if ! _contains "$response" "access_token"; then
 | 
			
		||||
    _err "Unable get access token"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  CURANET_ACCESS_TOKEN=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]+" | cut -c 17-)
 | 
			
		||||
 | 
			
		||||
  if [ -z "$CURANET_ACCESS_TOKEN" ]; then
 | 
			
		||||
    _err "Unable to get token"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    export _H1="Content-Type: application/json"
 | 
			
		||||
    export _H2="Accept: application/json"
 | 
			
		||||
    export _H3="Authorization: Bearer $CURANET_ACCESS_TOKEN"
 | 
			
		||||
    response="$(_get "$CURANET_REST_URL/$h/Records" "" "")"
 | 
			
		||||
 | 
			
		||||
    if [ ! "$(echo "$response" | _egrep_o "Entity not found")" ]; then
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										185
									
								
								dnsapi/dns_cx.sh
									
									
									
									
									
								
							
							
						
						
									
										185
									
								
								dnsapi/dns_cx.sh
									
									
									
									
									
								
							@@ -1,185 +0,0 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# CloudXNS Domain api
 | 
			
		||||
#
 | 
			
		||||
#CX_Key="1234"
 | 
			
		||||
#
 | 
			
		||||
#CX_Secret="sADDsdasdgdsf"
 | 
			
		||||
 | 
			
		||||
CX_Api="https://www.cloudxns.net/api2"
 | 
			
		||||
 | 
			
		||||
#REST_API
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_cx_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}"
 | 
			
		||||
  CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}"
 | 
			
		||||
  if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then
 | 
			
		||||
    CX_Key=""
 | 
			
		||||
    CX_Secret=""
 | 
			
		||||
    _err "You don't specify cloudxns.net  api key or secret yet."
 | 
			
		||||
    _err "Please create you key and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  REST_API="$CX_Api"
 | 
			
		||||
 | 
			
		||||
  #save the api key and email to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable CX_Key "$CX_Key"
 | 
			
		||||
  _saveaccountconf_mutable CX_Secret "$CX_Secret"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  add_record "$_domain" "$_sub_domain" "$txtvalue"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain txtvalue
 | 
			
		||||
dns_cx_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  CX_Key="${CX_Key:-$(_readaccountconf_mutable CX_Key)}"
 | 
			
		||||
  CX_Secret="${CX_Secret:-$(_readaccountconf_mutable CX_Secret)}"
 | 
			
		||||
  REST_API="$CX_Api"
 | 
			
		||||
  if _get_root "$fulldomain"; then
 | 
			
		||||
    record_id=""
 | 
			
		||||
    existing_records "$_domain" "$_sub_domain" "$txtvalue"
 | 
			
		||||
    if [ "$record_id" ]; then
 | 
			
		||||
      _rest DELETE "record/$record_id/$_domain_id" "{}"
 | 
			
		||||
      _info "Deleted record ${fulldomain}"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usage:  root  sub
 | 
			
		||||
#return if the sub record already exists.
 | 
			
		||||
#echos the existing records count.
 | 
			
		||||
# '0' means doesn't exist
 | 
			
		||||
existing_records() {
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  root=$1
 | 
			
		||||
  sub=$2
 | 
			
		||||
  if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}')
 | 
			
		||||
  _debug seg "$seg"
 | 
			
		||||
  if [ -z "$seg" ]; then
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then
 | 
			
		||||
    record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1)
 | 
			
		||||
    _debug record_id "$record_id"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#add the txt record.
 | 
			
		||||
#usage: root  sub  txtvalue
 | 
			
		||||
add_record() {
 | 
			
		||||
  root=$1
 | 
			
		||||
  sub=$2
 | 
			
		||||
  txtvalue=$3
 | 
			
		||||
  fulldomain="$sub.$root"
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
 | 
			
		||||
  if ! _rest POST "record" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  if ! _rest GET "domain"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "$h."; then
 | 
			
		||||
      seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}')
 | 
			
		||||
      _debug seg "$seg"
 | 
			
		||||
      _domain_id=$(printf "%s\n" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
 | 
			
		||||
      _debug _domain_id "$_domain_id"
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
        _debug _sub_domain "$_sub_domain"
 | 
			
		||||
        _domain="$h"
 | 
			
		||||
        _debug _domain "$_domain"
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    p="$i"
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: method  URI  data
 | 
			
		||||
_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  _debug ep "$ep"
 | 
			
		||||
  url="$REST_API/$ep"
 | 
			
		||||
  _debug url "$url"
 | 
			
		||||
 | 
			
		||||
  cdate=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
 | 
			
		||||
  _debug cdate "$cdate"
 | 
			
		||||
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug data "$data"
 | 
			
		||||
 | 
			
		||||
  sec="$CX_Key$url$data$cdate$CX_Secret"
 | 
			
		||||
  _debug sec "$sec"
 | 
			
		||||
  hmac=$(printf "%s" "$sec" | _digest md5 hex)
 | 
			
		||||
  _debug hmac "$hmac"
 | 
			
		||||
 | 
			
		||||
  export _H1="API-KEY: $CX_Key"
 | 
			
		||||
  export _H2="API-REQUEST-DATE: $cdate"
 | 
			
		||||
  export _H3="API-HMAC: $hmac"
 | 
			
		||||
  export _H4="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$data" ]; then
 | 
			
		||||
    response="$(_post "$data" "$url" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$url")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
 | 
			
		||||
  _contains "$response" '"code":1'
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -44,7 +44,7 @@ dns_cyon_rm() {
 | 
			
		||||
_cyon_load_credentials() {
 | 
			
		||||
  # Convert loaded password to/from base64 as needed.
 | 
			
		||||
  if [ "${CY_Password_B64}" ]; then
 | 
			
		||||
    CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")"
 | 
			
		||||
    CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64)"
 | 
			
		||||
  elif [ "${CY_Password}" ]; then
 | 
			
		||||
    CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)"
 | 
			
		||||
  fi
 | 
			
		||||
 
 | 
			
		||||
@@ -192,6 +192,7 @@ _get_base_domain() {
 | 
			
		||||
  ## 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.digitalocean.com/v2/domains"
 | 
			
		||||
  found=""
 | 
			
		||||
 | 
			
		||||
  ## while we dont have a matching domain we keep going
 | 
			
		||||
  while [ -z "$found" ]; do
 | 
			
		||||
@@ -205,9 +206,7 @@ _get_base_domain() {
 | 
			
		||||
    fi
 | 
			
		||||
    _debug2 domain_list "$domain_list"
 | 
			
		||||
 | 
			
		||||
    ## for each shortening of our $fulldomain, check if it exists in the $domain_list
 | 
			
		||||
    ## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
 | 
			
		||||
    i=2
 | 
			
		||||
    i=1
 | 
			
		||||
    while [ $i -gt 0 ]; do
 | 
			
		||||
      ## get next longest domain
 | 
			
		||||
      _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										248
									
								
								dnsapi/dns_dnsservices.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										248
									
								
								dnsapi/dns_dnsservices.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,248 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#This file name is "dns_dnsservices.sh"
 | 
			
		||||
#Script for Danish DNS registra and DNS hosting provider https://dns.services
 | 
			
		||||
 | 
			
		||||
#Author: Bjarke Bruun <bbruun@gmail.com>
 | 
			
		||||
#Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/4152
 | 
			
		||||
 | 
			
		||||
# Global variable to connect to the DNS.Services API
 | 
			
		||||
DNSServices_API=https://dns.services/api
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_dnsservices_add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_dnsservices_add() {
 | 
			
		||||
  fulldomain="$1"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
 | 
			
		||||
  _info "Using dns.services to create ACME DNS challenge"
 | 
			
		||||
  _debug2 add_fulldomain "$fulldomain"
 | 
			
		||||
  _debug2 add_txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  # Read username/password from environment or .acme.sh/accounts.conf
 | 
			
		||||
  DnsServices_Username="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}"
 | 
			
		||||
  DnsServices_Password="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}"
 | 
			
		||||
  if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
 | 
			
		||||
    DnsServices_Username=""
 | 
			
		||||
    DnsServices_Password=""
 | 
			
		||||
    _err "You didn't specify dns.services api username and password yet."
 | 
			
		||||
    _err "Set environment variables DnsServices_Username and DnsServices_Password"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Setup GET/POST/DELETE headers
 | 
			
		||||
  _setup_headers
 | 
			
		||||
 | 
			
		||||
  #save the credentials to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable DnsServices_Username "$DnsServices_Username"
 | 
			
		||||
  _saveaccountconf_mutable DnsServices_Password "$DnsServices_Password"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$DnsServices_Username" "@"; then
 | 
			
		||||
    _err "It seems that the username variable DnsServices_Username has not been set/left blank"
 | 
			
		||||
    _err "or is not a valid email. Please correct and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "${fulldomain}"; then
 | 
			
		||||
    _err "Invalid domain ${fulldomain}"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! createRecord "$fulldomain" "${txtvalue}"; then
 | 
			
		||||
    _err "Error creating TXT record in domain $fulldomain in $rootZoneName"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug2 challenge-created "Created $fulldomain"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Description: Remove the txt record after validation.
 | 
			
		||||
dns_dnsservices_rm() {
 | 
			
		||||
  fulldomain="$1"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
 | 
			
		||||
  _info "Using dns.services to remove DNS record $fulldomain TXT $txtvalue"
 | 
			
		||||
  _debug rm_fulldomain "$fulldomain"
 | 
			
		||||
  _debug rm_txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  # Read username/password from environment or .acme.sh/accounts.conf
 | 
			
		||||
  DnsServices_Username="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}"
 | 
			
		||||
  DnsServices_Password="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}"
 | 
			
		||||
  if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
 | 
			
		||||
    DnsServices_Username=""
 | 
			
		||||
    DnsServices_Password=""
 | 
			
		||||
    _err "You didn't specify dns.services api username and password yet."
 | 
			
		||||
    _err "Set environment variables DnsServices_Username and DnsServices_Password"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Setup GET/POST/DELETE headers
 | 
			
		||||
  _setup_headers
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "${fulldomain}"; then
 | 
			
		||||
    _err "Invalid domain ${fulldomain}"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug2 rm_rootDomainInfo "found root domain $rootZoneName for $fulldomain"
 | 
			
		||||
 | 
			
		||||
  if ! deleteRecord "${fulldomain}" "${txtvalue}"; then
 | 
			
		||||
    _err "Error removing record: $fulldomain TXT ${txtvalue}"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_setup_headers() {
 | 
			
		||||
  # Set up API Headers for _get() and _post()
 | 
			
		||||
  # The <function>_add or <function>_rm must have been called before to work
 | 
			
		||||
 | 
			
		||||
  if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then
 | 
			
		||||
    _err "Could not setup BASIC authentication headers, they are missing"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  DnsServiceCredentials="$(printf "%s" "$DnsServices_Username:$DnsServices_Password" | _base64)"
 | 
			
		||||
  export _H1="Authorization: Basic $DnsServiceCredentials"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  # Just return if headers are set
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  _debug2 _get_root "Get the root domain of ${domain} for DNS API"
 | 
			
		||||
 | 
			
		||||
  # Setup _get() and _post() headers
 | 
			
		||||
  #_setup_headers
 | 
			
		||||
 | 
			
		||||
  result=$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/dns")
 | 
			
		||||
  result2="$(printf "%s\n" "$result" | tr '[' '\n' | grep '"name"')"
 | 
			
		||||
  result3="$(printf "%s\n" "$result2" | tr '}' '\n' | grep '"name"' | sed "s,^\,,,g" | sed "s,$,},g")"
 | 
			
		||||
  useResult=""
 | 
			
		||||
  _debug2 _get_root "Got the following root domain(s) $result"
 | 
			
		||||
  _debug2 _get_root "- JSON: $result"
 | 
			
		||||
 | 
			
		||||
  if [ "$(printf "%s\n" "$result" | tr '}' '\n' | grep -c '"name"')" -gt "1" ]; then
 | 
			
		||||
    checkMultiZones="true"
 | 
			
		||||
    _debug2 _get_root "- multiple zones found"
 | 
			
		||||
  else
 | 
			
		||||
    checkMultiZones="false"
 | 
			
		||||
    _debug2 _get_root "- single zone found"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Find/isolate the root zone to work with in createRecord() and deleteRecord()
 | 
			
		||||
  rootZone=""
 | 
			
		||||
  if [ "$checkMultiZones" = "true" ]; then
 | 
			
		||||
    #rootZone=$(for x in $(printf "%s" "${result3}" | tr ',' '\n' | sed -n 's/.*"name":"\(.*\)",.*/\1/p'); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
 | 
			
		||||
    rootZone=$(for x in $(printf "%s\n" "${result3}" | tr ',' '\n' | grep name | cut -d'"' -f4); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
 | 
			
		||||
    if [ "$rootZone" != "" ]; then
 | 
			
		||||
      _debug2 _rootZone "- root zone for $domain is $rootZone"
 | 
			
		||||
    else
 | 
			
		||||
      _err "Could not find root zone for $domain, is it correctly typed?"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    rootZone=$(echo "$result" | tr '}' '\n' | _egrep_o '"name":"[^"]*' | cut -d'"' -f4)
 | 
			
		||||
    _debug2 _get_root "- only found 1 domain in API: $rootZone"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -z "$rootZone" ]; then
 | 
			
		||||
    _err "Could not find root domain for $domain - is it correctly typed?"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Make sure we use the correct API zone data
 | 
			
		||||
  useResult="$(printf "%s\n" "${result3}" tr ',' '\n' | grep "$rootZone")"
 | 
			
		||||
  _debug2 _useResult "useResult=$useResult"
 | 
			
		||||
 | 
			
		||||
  # Setup variables used by other functions to communicate with DNS.Services API
 | 
			
		||||
  #zoneInfo=$(printf "%s\n" "$useResult" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"name":")([^"]*)"(.*)$,\2,g')
 | 
			
		||||
  zoneInfo=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep '"name"' | cut -d'"' -f4)
 | 
			
		||||
  rootZoneName="$rootZone"
 | 
			
		||||
  subDomainName="$(printf "%s\n" "$domain" | sed "s,\.$rootZone,,g")"
 | 
			
		||||
  subDomainNameClean="$(printf "%s\n" "$domain" | sed "s,_acme-challenge.,,g")"
 | 
			
		||||
  rootZoneDomainID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep domain_id | cut -d'"' -f4)
 | 
			
		||||
  rootZoneServiceID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep service_id | cut -d'"' -f4)
 | 
			
		||||
 | 
			
		||||
  _debug2 _zoneInfo "Zone info from API  : $zoneInfo"
 | 
			
		||||
  _debug2 _get_root "Root zone name      : $rootZoneName"
 | 
			
		||||
  _debug2 _get_root "Root zone domain ID : $rootZoneDomainID"
 | 
			
		||||
  _debug2 _get_root "Root zone service ID: $rootZoneServiceID"
 | 
			
		||||
  _debug2 _get_root "Sub domain          : $subDomainName"
 | 
			
		||||
 | 
			
		||||
  _debug _get_root "Found valid root domain $rootZone for $subDomainNameClean"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
createRecord() {
 | 
			
		||||
  fulldomain="$1"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
 | 
			
		||||
  # Get root domain information - needed for DNS.Services API communication
 | 
			
		||||
  if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
 | 
			
		||||
    _get_root "$fulldomain"
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
 | 
			
		||||
    _err "Something happend - could not get the API zone information"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug2 createRecord "CNAME TXT value is: $txtvalue"
 | 
			
		||||
 | 
			
		||||
  # Prepare data to send to API
 | 
			
		||||
  data="{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"${txtvalue}\", \"ttl\":\"10\"}"
 | 
			
		||||
 | 
			
		||||
  _debug2 createRecord "data to API: $data"
 | 
			
		||||
  result=$(_post "$data" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records" "" "POST")
 | 
			
		||||
  _debug2 createRecord "result from API: $result"
 | 
			
		||||
 | 
			
		||||
  if [ "$(echo "$result" | _egrep_o "\"success\":true")" = "" ]; then
 | 
			
		||||
    _err "Failed to create TXT record $fulldomain with content $txtvalue in zone $rootZoneName"
 | 
			
		||||
    _err "$result"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Record \"$fulldomain TXT $txtvalue\" has been created"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
deleteRecord() {
 | 
			
		||||
  fulldomain="$1"
 | 
			
		||||
  txtvalue="$2"
 | 
			
		||||
 | 
			
		||||
  _log deleteRecord "Deleting $fulldomain TXT $txtvalue record"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
 | 
			
		||||
    _get_root "$fulldomain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  result="$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID")"
 | 
			
		||||
  #recordInfo="$(echo "$result" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}")"
 | 
			
		||||
  #recordID="$(echo "$recordInfo" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"id":")([^"]*)"(.*)$,\2,g')"
 | 
			
		||||
  recordID="$(printf "%s\n" "$result" | tr '}' '\n' | grep -- "$txtvalue" | tr ',' '\n' | grep '"id"' | cut -d'"' -f4)"
 | 
			
		||||
  _debug2 _recordID "recordID used for deletion of record: $recordID"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$recordID" ]; then
 | 
			
		||||
    _info "Record $fulldomain TXT $txtvalue not found or already deleted"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _debug2 deleteRecord "Found recordID=$recordID"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug2 deleteRecord "DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID"
 | 
			
		||||
  _log "curl DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID"
 | 
			
		||||
  result="$(_H1="$_H1" _H2="$_H2" _post "" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID" "" "DELETE")"
 | 
			
		||||
  _debug2 deleteRecord "API Delete result \"$result\""
 | 
			
		||||
  _log "curl API Delete result \"$result\""
 | 
			
		||||
 | 
			
		||||
  # Return OK regardless
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -94,8 +94,8 @@ _get_domain() {
 | 
			
		||||
  _your_hosts="$(echo "$_your_hosts" | awk '/\./ {print $1}')"
 | 
			
		||||
  for l in $_your_hosts; do
 | 
			
		||||
    #echo "host: $l"
 | 
			
		||||
    if test "${_full_domain#*$l}" != "$_full_domain"; then
 | 
			
		||||
      _record="${_full_domain%.$l}"
 | 
			
		||||
    if test "${_full_domain#*"$l"}" != "$_full_domain"; then
 | 
			
		||||
      _record=${_full_domain%."$l"}
 | 
			
		||||
      _host=$l
 | 
			
		||||
      _debug "The host is $_host and the record $_record"
 | 
			
		||||
      return 0
 | 
			
		||||
@@ -143,7 +143,7 @@ _dns_dynv6_add_http() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _get_zone_name "$_zone_id"
 | 
			
		||||
  record="${fulldomain%%.$_zone_name}"
 | 
			
		||||
  record=${fulldomain%%."$_zone_name"}
 | 
			
		||||
  _set_record TXT "$record" "$txtvalue"
 | 
			
		||||
  if _contains "$response" "$txtvalue"; then
 | 
			
		||||
    _info "Successfully added record"
 | 
			
		||||
@@ -161,7 +161,7 @@ _dns_dynv6_rm_http() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _get_zone_name "$_zone_id"
 | 
			
		||||
  record="${fulldomain%%.$_zone_name}"
 | 
			
		||||
  record=${fulldomain%%."$_zone_name"}
 | 
			
		||||
  _get_record_id "$_zone_id" "$record" "$txtvalue"
 | 
			
		||||
  _del_record "$_zone_id" "$_record_id"
 | 
			
		||||
  if [ -z "$response" ]; then
 | 
			
		||||
 
 | 
			
		||||
@@ -176,6 +176,7 @@ _EDGEDNS_credentials() {
 | 
			
		||||
  _debug "GettingEdge DNS credentials"
 | 
			
		||||
  _log "$(printf "ACME DNSAPI Edge DNS version %s" ${ACME_EDGEDNS_VERSION})"
 | 
			
		||||
  args_missing=0
 | 
			
		||||
  AKAMAI_ACCESS_TOKEN="${AKAMAI_ACCESS_TOKEN:-$(_readaccountconf_mutable AKAMAI_ACCESS_TOKEN)}"
 | 
			
		||||
  if [ -z "$AKAMAI_ACCESS_TOKEN" ]; then
 | 
			
		||||
    AKAMAI_ACCESS_TOKEN=""
 | 
			
		||||
    AKAMAI_CLIENT_TOKEN=""
 | 
			
		||||
@@ -184,6 +185,7 @@ _EDGEDNS_credentials() {
 | 
			
		||||
    _err "AKAMAI_ACCESS_TOKEN is missing"
 | 
			
		||||
    args_missing=1
 | 
			
		||||
  fi
 | 
			
		||||
  AKAMAI_CLIENT_TOKEN="${AKAMAI_CLIENT_TOKEN:-$(_readaccountconf_mutable AKAMAI_CLIENT_TOKEN)}"
 | 
			
		||||
  if [ -z "$AKAMAI_CLIENT_TOKEN" ]; then
 | 
			
		||||
    AKAMAI_ACCESS_TOKEN=""
 | 
			
		||||
    AKAMAI_CLIENT_TOKEN=""
 | 
			
		||||
@@ -192,6 +194,7 @@ _EDGEDNS_credentials() {
 | 
			
		||||
    _err "AKAMAI_CLIENT_TOKEN is missing"
 | 
			
		||||
    args_missing=1
 | 
			
		||||
  fi
 | 
			
		||||
  AKAMAI_HOST="${AKAMAI_HOST:-$(_readaccountconf_mutable AKAMAI_HOST)}"
 | 
			
		||||
  if [ -z "$AKAMAI_HOST" ]; then
 | 
			
		||||
    AKAMAI_ACCESS_TOKEN=""
 | 
			
		||||
    AKAMAI_CLIENT_TOKEN=""
 | 
			
		||||
@@ -200,6 +203,7 @@ _EDGEDNS_credentials() {
 | 
			
		||||
    _err "AKAMAI_HOST is missing"
 | 
			
		||||
    args_missing=1
 | 
			
		||||
  fi
 | 
			
		||||
  AKAMAI_CLIENT_SECRET="${AKAMAI_CLIENT_SECRET:-$(_readaccountconf_mutable AKAMAI_CLIENT_SECRET)}"
 | 
			
		||||
  if [ -z "$AKAMAI_CLIENT_SECRET" ]; then
 | 
			
		||||
    AKAMAI_ACCESS_TOKEN=""
 | 
			
		||||
    AKAMAI_CLIENT_TOKEN=""
 | 
			
		||||
@@ -414,7 +418,7 @@ _edgedns_make_data_to_sign() {
 | 
			
		||||
  _secure_debug2 "hdr" "$hdr"
 | 
			
		||||
  _edgedns_make_content_hash
 | 
			
		||||
  path="$(echo "$_request_url_path" | tr -d "\n\r" | sed 's/https\?:\/\///')"
 | 
			
		||||
  path="${path#*$AKAMAI_HOST}"
 | 
			
		||||
  path=${path#*"$AKAMAI_HOST"}
 | 
			
		||||
  _debug "hier path" "$path"
 | 
			
		||||
  # dont expose headers to sign so use MT string
 | 
			
		||||
  _mdata="$(printf "%s\thttps\t%s\t%s\t%s\t%s\t%s" "$_request_method" "$AKAMAI_HOST" "$path" "" "$_hash" "$hdr")"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										146
									
								
								dnsapi/dns_fornex.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								dnsapi/dns_fornex.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Author: Timur Umarov <inbox@tumarov.com>
 | 
			
		||||
 | 
			
		||||
FORNEX_API_URL="https://fornex.com/api/dns/v0.1"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_fornex_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_fornex_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  if ! _Fornex_API; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Unable to determine root domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _debug _domain "$_domain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _rest POST "$_domain/entry_set/add/" "host=$fulldomain&type=TXT&value=$txtvalue&apikey=$FORNEX_API_KEY"; then
 | 
			
		||||
    _debug _response "$response"
 | 
			
		||||
    if _contains "$response" '"ok": true' || _contains "$response" 'Такая запись уже существует.'; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: dns_fornex_rm   _acme-challenge.www.domain.com
 | 
			
		||||
dns_fornex_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  if ! _Fornex_API; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "Unable to determine root domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _debug _domain "$_domain"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _rest GET "$_domain/entry_set.json?apikey=$FORNEX_API_KEY"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "$txtvalue"; then
 | 
			
		||||
    _err "Txt record not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _record_id="$(echo "$response" | _egrep_o "{[^{]*\"value\"*:*\"$txtvalue\"[^}]*}" | sed -n -e 's#.*"id": \([0-9]*\).*#\1#p')"
 | 
			
		||||
  _debug "_record_id" "$_record_id"
 | 
			
		||||
  if [ -z "$_record_id" ]; then
 | 
			
		||||
    _err "can not find _record_id"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _rest POST "$_domain/entry_set/$_record_id/delete/" "apikey=$FORNEX_API_KEY"; then
 | 
			
		||||
    _err "Delete record error."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
 | 
			
		||||
  i=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _rest GET "domain_list.json?q=$h&apikey=$FORNEX_API_KEY"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"$h\"" >/dev/null; then
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _debug "$h not found"
 | 
			
		||||
    fi
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_Fornex_API() {
 | 
			
		||||
  FORNEX_API_KEY="${FORNEX_API_KEY:-$(_readaccountconf_mutable FORNEX_API_KEY)}"
 | 
			
		||||
  if [ -z "$FORNEX_API_KEY" ]; then
 | 
			
		||||
    FORNEX_API_KEY=""
 | 
			
		||||
 | 
			
		||||
    _err "You didn't specify the Fornex API key yet."
 | 
			
		||||
    _err "Please create your key and try again."
 | 
			
		||||
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable FORNEX_API_KEY "$FORNEX_API_KEY"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#method method action data
 | 
			
		||||
_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  export _H1="Accept: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$FORNEX_API_URL/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$FORNEX_API_URL/$ep" | _normalizeJson)"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _ret="$?"
 | 
			
		||||
  if [ "$_ret" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Gandi LiveDNS v5 API
 | 
			
		||||
# http://doc.livedns.gandi.net/
 | 
			
		||||
# https://doc.livedns.gandi.net/
 | 
			
		||||
# currently under beta
 | 
			
		||||
#
 | 
			
		||||
# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ dns_gcloud_rm() {
 | 
			
		||||
  _dns_gcloud_start_tr || return $?
 | 
			
		||||
  _dns_gcloud_get_rrdatas || return $?
 | 
			
		||||
  echo "$rrdatas" | _dns_gcloud_remove_rrs || return $?
 | 
			
		||||
  echo "$rrdatas" | grep -F -v "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
 | 
			
		||||
  echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
 | 
			
		||||
  _dns_gcloud_execute_tr || return $?
 | 
			
		||||
 | 
			
		||||
  _info "$fulldomain record added"
 | 
			
		||||
@@ -98,7 +98,7 @@ _dns_gcloud_remove_rrs() {
 | 
			
		||||
    --ttl="$ttl" \
 | 
			
		||||
    --type=TXT \
 | 
			
		||||
    --zone="$managedZone" \
 | 
			
		||||
    --transaction-file="$tr"; then
 | 
			
		||||
    --transaction-file="$tr" --; then
 | 
			
		||||
    _debug tr "$(cat "$tr")"
 | 
			
		||||
    rm -r "$trd"
 | 
			
		||||
    _err "_dns_gcloud_remove_rrs: failed to remove RRs"
 | 
			
		||||
@@ -113,7 +113,7 @@ _dns_gcloud_add_rrs() {
 | 
			
		||||
    --ttl="$ttl" \
 | 
			
		||||
    --type=TXT \
 | 
			
		||||
    --zone="$managedZone" \
 | 
			
		||||
    --transaction-file="$tr"; then
 | 
			
		||||
    --transaction-file="$tr" --; then
 | 
			
		||||
    _debug tr "$(cat "$tr")"
 | 
			
		||||
    rm -r "$trd"
 | 
			
		||||
    _err "_dns_gcloud_add_rrs: failed to add RRs"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										187
									
								
								dnsapi/dns_gcore.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										187
									
								
								dnsapi/dns_gcore.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,187 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#GCORE_Key='773$7b7adaf2a2b32bfb1b83787b4ff32a67eb178e3ada1af733e47b1411f2461f7f4fa7ed7138e2772a46124377bad7384b3bb8d87748f87b3f23db4b8bbe41b2bb'
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
GCORE_Api="https://api.gcorelabs.com/dns/v2"
 | 
			
		||||
GCORE_Doc="https://apidocs.gcore.com/dns"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_gcore_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  GCORE_Key="${GCORE_Key:-$(_readaccountconf_mutable GCORE_Key)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$GCORE_Key" ]; then
 | 
			
		||||
    GCORE_Key=""
 | 
			
		||||
    _err "You didn't specify a Gcore api key yet."
 | 
			
		||||
    _err "You can get yours from here $GCORE_Doc"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the api key to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable GCORE_Key "$GCORE_Key"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the zone name"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _zone_name "$_zone_name"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _gcore_rest GET "zones/$_zone_name/$fulldomain/TXT"
 | 
			
		||||
  payload=""
 | 
			
		||||
 | 
			
		||||
  if echo "$response" | grep "record is not found" >/dev/null; then
 | 
			
		||||
    _info "Record doesn't exists"
 | 
			
		||||
    payload="{\"resource_records\":[{\"content\":[\"$txtvalue\"],\"enabled\":true}],\"ttl\":120}"
 | 
			
		||||
  elif echo "$response" | grep "$txtvalue" >/dev/null; then
 | 
			
		||||
    _info "Already exists, OK"
 | 
			
		||||
    return 0
 | 
			
		||||
  elif echo "$response" | tr -d " " | grep \"name\":\""$fulldomain"\",\"type\":\"TXT\" >/dev/null; then
 | 
			
		||||
    _info "Record with mismatch txtvalue, try update it"
 | 
			
		||||
    payload=$(echo "$response" | tr -d " " | sed 's/"updated_at":[0-9]\+,//g' | sed 's/"meta":{}}]}/"meta":{}},{"content":['\""$txtvalue"\"'],"enabled":true}]}/')
 | 
			
		||||
  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 _gcore_rest PUT "zones/$_zone_name/$fulldomain/TXT" "$payload"; then
 | 
			
		||||
    if _contains "$response" "$txtvalue"; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    elif _contains "$response" "rrset is already exists"; 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_gcore_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  GCORE_Key="${GCORE_Key:-$(_readaccountconf_mutable GCORE_Key)}"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _zone_name "$_zone_name"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _gcore_rest GET "zones/$_zone_name/$fulldomain/TXT"
 | 
			
		||||
 | 
			
		||||
  if echo "$response" | grep "record is not found" >/dev/null; then
 | 
			
		||||
    _info "No such txt recrod"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! echo "$response" | tr -d " " | grep \"name\":\""$fulldomain"\",\"type\":\"TXT\" >/dev/null; then
 | 
			
		||||
    _err "Error: $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! echo "$response" | tr -d " " | grep \""$txtvalue"\" >/dev/null; then
 | 
			
		||||
    _info "No such txt recrod"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  count="$(echo "$response" | grep -o "content" | wc -l)"
 | 
			
		||||
 | 
			
		||||
  if [ "$count" = "1" ]; then
 | 
			
		||||
    if ! _gcore_rest DELETE "zones/$_zone_name/$fulldomain/TXT"; then
 | 
			
		||||
      _err "Delete record error. $response"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  payload="$(echo "$response" | tr -d " " | sed 's/"updated_at":[0-9]\+,//g' | sed 's/{"id":[0-9]\+,"content":\["'"$txtvalue"'"\],"enabled":true,"meta":{}}//' | sed 's/\[,/\[/' | sed 's/,,/,/' | sed 's/,\]/\]/')"
 | 
			
		||||
  if ! _gcore_rest PUT "zones/$_zone_name/$fulldomain/TXT" "$payload"; then
 | 
			
		||||
    _err "Delete record error. $response"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.sub.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.sub or _acme-challenge
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _zone_name=domain.com or sub.domain.com
 | 
			
		||||
_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 ! _gcore_rest GET "zones/$h"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"name\":\"$h\""; then
 | 
			
		||||
      _zone_name=$h
 | 
			
		||||
      if [ "$_zone_name" ]; 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_gcore_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  key_trimmed=$(echo "$GCORE_Key" | tr -d '"')
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  export _H2="Authorization: APIKey $key_trimmed"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$GCORE_Api/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$GCORE_Api/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Godaddy domain api
 | 
			
		||||
# Get API key and secret from https://developer.godaddy.com/
 | 
			
		||||
#
 | 
			
		||||
#GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
# GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
# GD_Secret="asdfsdfsfsdfsdfdfsdf"
 | 
			
		||||
#
 | 
			
		||||
#GD_Secret="asdfsdfsfsdfsdfdfsdf"
 | 
			
		||||
# Ex.: acme.sh --issue --staging --dns dns_gd -d "*.s.example.com" -d "s.example.com"
 | 
			
		||||
 | 
			
		||||
GD_Api="https://api.godaddy.com/v1"
 | 
			
		||||
 | 
			
		||||
@@ -20,8 +22,8 @@ dns_gd_add() {
 | 
			
		||||
  if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then
 | 
			
		||||
    GD_Key=""
 | 
			
		||||
    GD_Secret=""
 | 
			
		||||
    _err "You don't specify godaddy api key and secret yet."
 | 
			
		||||
    _err "Please create you key and try again."
 | 
			
		||||
    _err "You didn't specify godaddy api key and secret yet."
 | 
			
		||||
    _err "Please create your key and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
@@ -44,14 +46,15 @@ dns_gd_add() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" "$txtvalue"; then
 | 
			
		||||
    _info "The record is existing, skip"
 | 
			
		||||
    _info "This record already exists, skipping"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _add_data="{\"data\":\"$txtvalue\"}"
 | 
			
		||||
  for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do
 | 
			
		||||
    _debug2 t "$t"
 | 
			
		||||
    if [ "$t" ]; then
 | 
			
		||||
    # ignore empty (previously removed) records, to prevent useless _acme-challenge TXT entries
 | 
			
		||||
    if [ "$t" ] && [ "$t" != '""' ]; then
 | 
			
		||||
      _add_data="$_add_data,{\"data\":$t}"
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
@@ -59,13 +62,25 @@ dns_gd_add() {
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then
 | 
			
		||||
    _info "Added, sleeping 10 seconds"
 | 
			
		||||
    _sleep 10
 | 
			
		||||
    #todo: check if the record takes effect
 | 
			
		||||
    return 0
 | 
			
		||||
    _debug "Checking updated records of '${fulldomain}'"
 | 
			
		||||
 | 
			
		||||
    if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then
 | 
			
		||||
      _err "Validating TXT record for '${fulldomain}' with rest error [$?]." "$response"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _contains "$response" "$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
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
  _sleep 10
 | 
			
		||||
  _info "Added TXT record '${txtvalue}' for '${fulldomain}'."
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain
 | 
			
		||||
@@ -107,11 +122,20 @@ dns_gd_rm() {
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
  if [ -z "$_add_data" ]; then
 | 
			
		||||
    _add_data="{\"data\":\"\"}"
 | 
			
		||||
    # delete empty record
 | 
			
		||||
    _debug "Delete last record for '${fulldomain}'"
 | 
			
		||||
    if ! _gd_rest DELETE "domains/$_domain/records/TXT/$_sub_domain"; then
 | 
			
		||||
      _err "Cannot delete empty TXT record for '$fulldomain'"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    # remove specific TXT value, keeping other entries
 | 
			
		||||
    _debug2 _add_data "$_add_data"
 | 
			
		||||
    if ! _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then
 | 
			
		||||
      _err "Cannot update TXT record for '$fulldomain'"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 _add_data "$_add_data"
 | 
			
		||||
 | 
			
		||||
  _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
@@ -156,15 +180,15 @@ _gd_rest() {
 | 
			
		||||
  export _H1="Authorization: sso-key $GD_Key:$GD_Secret"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$data" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
  if [ "$data" ] || [ "$m" = "DELETE" ]; then
 | 
			
		||||
    _debug "data ($m): " "$data"
 | 
			
		||||
    response="$(_post "$data" "$GD_Api/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$GD_Api/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    _err "error on rest call ($m): $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,177 +0,0 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#Author: Herman Sletteng
 | 
			
		||||
#Report Bugs here: https://github.com/loial/acme.sh
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
# Note, gratisdns requires a login first, so the script needs to handle
 | 
			
		||||
# temporary cookies. Since acme.sh _get/_post currently don't directly support
 | 
			
		||||
# cookies, I've defined wrapper functions _myget/_mypost to set the headers
 | 
			
		||||
 | 
			
		||||
GDNSDK_API="https://admin.gratisdns.com"
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
#Usage: dns_gdnsdk_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_gdnsdk_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using gratisdns.dk"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
  if ! _gratisdns_login; then
 | 
			
		||||
    _err "Login failed!"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  #finding domain zone
 | 
			
		||||
  if ! _get_domain; then
 | 
			
		||||
    _err "No matching root domain for $fulldomain found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  # adding entry
 | 
			
		||||
  _info "Adding the entry"
 | 
			
		||||
  _mypost "action=dns_primary_record_added_txt&user_domain=$_domain&name=$fulldomain&txtdata=$txtvalue&ttl=1"
 | 
			
		||||
  if _successful_update; then return 0; fi
 | 
			
		||||
  _err "Couldn't create entry!"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Remove the txt record after validation.
 | 
			
		||||
dns_gdnsdk_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _info "Using gratisdns.dk"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
  if ! _gratisdns_login; then
 | 
			
		||||
    _err "Login failed!"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if ! _get_domain; then
 | 
			
		||||
    _err "No matching root domain for $fulldomain found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _findentry "$fulldomain" "$txtvalue"
 | 
			
		||||
  if [ -z "$_id" ]; then
 | 
			
		||||
    _info "Entry doesn't exist, nothing to delete"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "Deleting record..."
 | 
			
		||||
  _mypost "action=dns_primary_delete_txt&user_domain=$_domain&id=$_id"
 | 
			
		||||
  # removing entry
 | 
			
		||||
 | 
			
		||||
  if _successful_update; then return 0; fi
 | 
			
		||||
  _err "Couldn't delete entry!"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_checkcredentials() {
 | 
			
		||||
  GDNSDK_Username="${GDNSDK_Username:-$(_readaccountconf_mutable GDNSDK_Username)}"
 | 
			
		||||
  GDNSDK_Password="${GDNSDK_Password:-$(_readaccountconf_mutable GDNSDK_Password)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$GDNSDK_Username" ] || [ -z "$GDNSDK_Password" ]; then
 | 
			
		||||
    GDNSDK_Username=""
 | 
			
		||||
    GDNSDK_Password=""
 | 
			
		||||
    _err "You haven't specified gratisdns.dk username and password yet."
 | 
			
		||||
    _err "Please add credentials and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  #save the credentials to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable GDNSDK_Username "$GDNSDK_Username"
 | 
			
		||||
  _saveaccountconf_mutable GDNSDK_Password "$GDNSDK_Password"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_checkcookie() {
 | 
			
		||||
  GDNSDK_Cookie="${GDNSDK_Cookie:-$(_readaccountconf_mutable GDNSDK_Cookie)}"
 | 
			
		||||
  if [ -z "$GDNSDK_Cookie" ]; then
 | 
			
		||||
    _debug "No cached cookie found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _myget "action="
 | 
			
		||||
  if (echo "$_result" | grep -q "logmeout"); then
 | 
			
		||||
    _debug "Cached cookie still valid"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "Cached cookie no longer valid"
 | 
			
		||||
  GDNSDK_Cookie=""
 | 
			
		||||
  _saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_gratisdns_login() {
 | 
			
		||||
  if ! _checkcredentials; then return 1; fi
 | 
			
		||||
 | 
			
		||||
  if _checkcookie; then
 | 
			
		||||
    _debug "Already logged in"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "Logging into GratisDNS with user $GDNSDK_Username"
 | 
			
		||||
 | 
			
		||||
  if ! _mypost "login=$GDNSDK_Username&password=$GDNSDK_Password&action=logmein"; then
 | 
			
		||||
    _err "GratisDNS login failed for user $GDNSDK_Username bad RC from _post"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  GDNSDK_Cookie="$(grep -A 15 '302 Found' "$HTTP_HEADER" | _egrep_o 'Cookie: [^;]*' | _head_n 1 | cut -d ' ' -f2)"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$GDNSDK_Cookie" ]; then
 | 
			
		||||
    _err "GratisDNS login failed for user $GDNSDK_Username. Check $HTTP_HEADER file"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  export GDNSDK_Cookie
 | 
			
		||||
  _saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_myget() {
 | 
			
		||||
  #Adds cookie to request
 | 
			
		||||
  export _H1="Cookie: $GDNSDK_Cookie"
 | 
			
		||||
  _result=$(_get "$GDNSDK_API?$1")
 | 
			
		||||
}
 | 
			
		||||
_mypost() {
 | 
			
		||||
  #Adds cookie to request
 | 
			
		||||
  export _H1="Cookie: $GDNSDK_Cookie"
 | 
			
		||||
  _result=$(_post "$1" "$GDNSDK_API")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_get_domain() {
 | 
			
		||||
  _myget 'action=dns_primarydns'
 | 
			
		||||
  _domains=$(echo "$_result" | _egrep_o ' domain="[[:alnum:]._-]+' | sed 's/^.*"//')
 | 
			
		||||
  if [ -z "$_domains" ]; then
 | 
			
		||||
    _err "Primary domain list not found!"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  for _domain in $_domains; do
 | 
			
		||||
    if (_endswith "$fulldomain" "$_domain"); then
 | 
			
		||||
      _debug "Root domain: $_domain"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_successful_update() {
 | 
			
		||||
  if (echo "$_result" | grep -q 'table-success'); then return 0; fi
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_findentry() {
 | 
			
		||||
  #args    $1: fulldomain, $2: txtvalue
 | 
			
		||||
  #returns id of dns entry, if it exists
 | 
			
		||||
  _myget "action=dns_primary_changeDNSsetup&user_domain=$_domain"
 | 
			
		||||
  _debug3 "_result: $_result"
 | 
			
		||||
 | 
			
		||||
  _tmp_result=$(echo "$_result" | tr -d '\n\r' | _egrep_o "<td>$1</td>\s*<td>$2</td>[^?]*[^&]*&id=[^&]*")
 | 
			
		||||
  _debug _tmp_result "$_tmp_result"
 | 
			
		||||
  if [ -z "${_tmp_result:-}" ]; then
 | 
			
		||||
    _debug "The variable is _tmp_result is not supposed to be empty, there may be something wrong with the script"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _id=$(echo "$_tmp_result" | sed 's/^.*=//')
 | 
			
		||||
  if [ -n "$_id" ]; then
 | 
			
		||||
    _debug "Entry found with _id=$_id"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										232
									
								
								dnsapi/dns_geoscaling.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										232
									
								
								dnsapi/dns_geoscaling.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,232 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
# Geoscaling hook script for acme.sh
 | 
			
		||||
#
 | 
			
		||||
# Environment variables:
 | 
			
		||||
#
 | 
			
		||||
#  - $GEOSCALING_Username  (your Geoscaling username - this is usually NOT an amail address)
 | 
			
		||||
#  - $GEOSCALING_Password  (your Geoscaling password)
 | 
			
		||||
 | 
			
		||||
#-- dns_geoscaling_add() - Add TXT record --------------------------------------
 | 
			
		||||
# Usage: dns_geoscaling_add _acme-challenge.subdomain.domain.com "XyZ123..."
 | 
			
		||||
 | 
			
		||||
dns_geoscaling_add() {
 | 
			
		||||
  full_domain=$1
 | 
			
		||||
  txt_value=$2
 | 
			
		||||
  _info "Using DNS-01 Geoscaling DNS2 hook"
 | 
			
		||||
 | 
			
		||||
  GEOSCALING_Username="${GEOSCALING_Username:-$(_readaccountconf_mutable GEOSCALING_Username)}"
 | 
			
		||||
  GEOSCALING_Password="${GEOSCALING_Password:-$(_readaccountconf_mutable GEOSCALING_Password)}"
 | 
			
		||||
  if [ -z "$GEOSCALING_Username" ] || [ -z "$GEOSCALING_Password" ]; then
 | 
			
		||||
    GEOSCALING_Username=
 | 
			
		||||
    GEOSCALING_Password=
 | 
			
		||||
    _err "No auth details provided. Please set user credentials using the \$GEOSCALING_Username and \$GEOSCALING_Password environment variables."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable GEOSCALING_Username "${GEOSCALING_Username}"
 | 
			
		||||
  _saveaccountconf_mutable GEOSCALING_Password "${GEOSCALING_Password}"
 | 
			
		||||
 | 
			
		||||
  # Fills in the $zone_id and $zone_name
 | 
			
		||||
  find_zone "${full_domain}" || return 1
 | 
			
		||||
  _debug "Zone id '${zone_id}' will be used."
 | 
			
		||||
 | 
			
		||||
  # We're logged in here
 | 
			
		||||
 | 
			
		||||
  # we should add ${full_domain} minus the trailing ${zone_name}
 | 
			
		||||
 | 
			
		||||
  prefix=$(echo "${full_domain}" | sed "s|\\.${zone_name}\$||")
 | 
			
		||||
 | 
			
		||||
  body="id=${zone_id}&name=${prefix}&type=TXT&content=${txt_value}&ttl=300&prio=0"
 | 
			
		||||
 | 
			
		||||
  do_post "$body" "https://www.geoscaling.com/dns2/ajax/add_record.php"
 | 
			
		||||
  exit_code="$?"
 | 
			
		||||
  if [ "${exit_code}" -eq 0 ]; then
 | 
			
		||||
    _info "TXT record added successfully."
 | 
			
		||||
  else
 | 
			
		||||
    _err "Couldn't add the TXT record."
 | 
			
		||||
  fi
 | 
			
		||||
  do_logout
 | 
			
		||||
  return "${exit_code}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#-- dns_geoscaling_rm() - Remove TXT record ------------------------------------
 | 
			
		||||
# Usage: dns_geoscaling_rm _acme-challenge.subdomain.domain.com "XyZ123..."
 | 
			
		||||
 | 
			
		||||
dns_geoscaling_rm() {
 | 
			
		||||
  full_domain=$1
 | 
			
		||||
  txt_value=$2
 | 
			
		||||
  _info "Cleaning up after DNS-01 Geoscaling DNS2 hook"
 | 
			
		||||
 | 
			
		||||
  GEOSCALING_Username="${GEOSCALING_Username:-$(_readaccountconf_mutable GEOSCALING_Username)}"
 | 
			
		||||
  GEOSCALING_Password="${GEOSCALING_Password:-$(_readaccountconf_mutable GEOSCALING_Password)}"
 | 
			
		||||
  if [ -z "$GEOSCALING_Username" ] || [ -z "$GEOSCALING_Password" ]; then
 | 
			
		||||
    GEOSCALING_Username=
 | 
			
		||||
    GEOSCALING_Password=
 | 
			
		||||
    _err "No auth details provided. Please set user credentials using the \$GEOSCALING_Username and \$GEOSCALING_Password environment variables."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable GEOSCALING_Username "${GEOSCALING_Username}"
 | 
			
		||||
  _saveaccountconf_mutable GEOSCALING_Password "${GEOSCALING_Password}"
 | 
			
		||||
 | 
			
		||||
  # fills in the $zone_id
 | 
			
		||||
  find_zone "${full_domain}" || return 1
 | 
			
		||||
  _debug "Zone id '${zone_id}' will be used."
 | 
			
		||||
 | 
			
		||||
  # Here we're logged in
 | 
			
		||||
  # Find the record id to clean
 | 
			
		||||
 | 
			
		||||
  # get the domain
 | 
			
		||||
  response=$(do_get "https://www.geoscaling.com/dns2/index.php?module=domain&id=${zone_id}")
 | 
			
		||||
  _debug2 "response" "$response"
 | 
			
		||||
 | 
			
		||||
  table="$(echo "${response}" | tr -d '\n' | sed 's|.*<div class="box"><div class="boxtitle">Basic Records</div><div class="boxtext"><table|<table|; s|</table>.*|</table>|')"
 | 
			
		||||
  _debug2 table "${table}"
 | 
			
		||||
  names=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*</td>' | sed 's|</td>||; s|.*>||')
 | 
			
		||||
  ids=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*</td>' | sed 's|\.name">.*||; s|id="||')
 | 
			
		||||
  types=$(echo "${table}" | _egrep_o 'id="[0-9]+\.type">[^<]*</td>' | sed 's|</td>||; s|.*>||')
 | 
			
		||||
  values=$(echo "${table}" | _egrep_o 'id="[0-9]+\.content">[^<]*</td>' | sed 's|</td>||; s|.*>||')
 | 
			
		||||
 | 
			
		||||
  _debug2 names "${names}"
 | 
			
		||||
  _debug2 ids "${ids}"
 | 
			
		||||
  _debug2 types "${types}"
 | 
			
		||||
  _debug2 values "${values}"
 | 
			
		||||
 | 
			
		||||
  # look for line whose name is ${full_domain}, whose type is TXT, and whose value is ${txt_value}
 | 
			
		||||
  line_num="$(echo "${values}" | grep -F -n -- "${txt_value}" | _head_n 1 | cut -d ':' -f 1)"
 | 
			
		||||
  _debug2 line_num "${line_num}"
 | 
			
		||||
  found_id=
 | 
			
		||||
  if [ -n "$line_num" ]; then
 | 
			
		||||
    type=$(echo "${types}" | sed -n "${line_num}p")
 | 
			
		||||
    name=$(echo "${names}" | sed -n "${line_num}p")
 | 
			
		||||
    id=$(echo "${ids}" | sed -n "${line_num}p")
 | 
			
		||||
 | 
			
		||||
    _debug2 type "$type"
 | 
			
		||||
    _debug2 name "$name"
 | 
			
		||||
    _debug2 id "$id"
 | 
			
		||||
    _debug2 full_domain "$full_domain"
 | 
			
		||||
 | 
			
		||||
    if [ "${type}" = "TXT" ] && [ "${name}" = "${full_domain}" ]; then
 | 
			
		||||
      found_id=${id}
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "${found_id}" = "" ]; then
 | 
			
		||||
    _err "Can not find record id."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Remove the record
 | 
			
		||||
  body="id=${zone_id}&record_id=${found_id}"
 | 
			
		||||
  response=$(do_post "$body" "https://www.geoscaling.com/dns2/ajax/delete_record.php")
 | 
			
		||||
  exit_code="$?"
 | 
			
		||||
  if [ "$exit_code" -eq 0 ]; then
 | 
			
		||||
    _info "Record removed successfully."
 | 
			
		||||
  else
 | 
			
		||||
    _err "Could not clean (remove) up the record. Please go to Geoscaling administration interface and clean it by hand."
 | 
			
		||||
  fi
 | 
			
		||||
  do_logout
 | 
			
		||||
  return "${exit_code}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
########################## PRIVATE FUNCTIONS ###########################
 | 
			
		||||
 | 
			
		||||
do_get() {
 | 
			
		||||
  _url=$1
 | 
			
		||||
  export _H1="Cookie: $geoscaling_phpsessid_cookie"
 | 
			
		||||
  _get "${_url}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_post() {
 | 
			
		||||
  _body=$1
 | 
			
		||||
  _url=$2
 | 
			
		||||
  export _H1="Cookie: $geoscaling_phpsessid_cookie"
 | 
			
		||||
  _post "${_body}" "${_url}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_login() {
 | 
			
		||||
 | 
			
		||||
  _info "Logging in..."
 | 
			
		||||
 | 
			
		||||
  username_encoded="$(printf "%s" "${GEOSCALING_Username}" | _url_encode)"
 | 
			
		||||
  password_encoded="$(printf "%s" "${GEOSCALING_Password}" | _url_encode)"
 | 
			
		||||
  body="username=${username_encoded}&password=${password_encoded}"
 | 
			
		||||
 | 
			
		||||
  response=$(_post "$body" "https://www.geoscaling.com/dns2/index.php?module=auth")
 | 
			
		||||
  _debug2 response "${response}"
 | 
			
		||||
 | 
			
		||||
  #retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | _egrep_o '[0-9]+$')
 | 
			
		||||
  retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | cut -d ' ' -f 2)
 | 
			
		||||
 | 
			
		||||
  if [ "$retcode" != "302" ]; then
 | 
			
		||||
    _err "Geoscaling login failed for user ${GEOSCALING_Username}. Check ${HTTP_HEADER} file"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  geoscaling_phpsessid_cookie="$(grep -i '^set-cookie:' "${HTTP_HEADER}" | _egrep_o 'PHPSESSID=[^;]*;' | tr -d ';')"
 | 
			
		||||
  return 0
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_logout() {
 | 
			
		||||
  _info "Logging out."
 | 
			
		||||
  response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=auth")"
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
find_zone() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
 | 
			
		||||
  # do login
 | 
			
		||||
  do_login || return 1
 | 
			
		||||
 | 
			
		||||
  # get zones
 | 
			
		||||
  response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=domains")"
 | 
			
		||||
 | 
			
		||||
  table="$(echo "${response}" | tr -d '\n' | sed 's|.*<div class="box"><div class="boxtitle">Your domains</div><div class="boxtext"><table|<table|; s|</table>.*|</table>|')"
 | 
			
		||||
  _debug2 table "${table}"
 | 
			
		||||
  zone_names="$(echo "${table}" | _egrep_o '<b>[^<]*</b>' | sed 's|<b>||;s|</b>||')"
 | 
			
		||||
  _debug2 _matches "${zone_names}"
 | 
			
		||||
  # Zone names and zone IDs are in same order
 | 
			
		||||
  zone_ids=$(echo "${table}" | _egrep_o '<a href=.index\.php\?module=domain&id=[0-9]+. onclick="javascript:show_loader\(\);">' | sed 's|.*id=||;s|. .*||')
 | 
			
		||||
 | 
			
		||||
  _debug2 "These are the zones on this Geoscaling account:"
 | 
			
		||||
  _debug2 "zone_names" "${zone_names}"
 | 
			
		||||
  _debug2 "And these are their respective IDs:"
 | 
			
		||||
  _debug2 "zone_ids" "${zone_ids}"
 | 
			
		||||
  if [ -z "${zone_names}" ] || [ -z "${zone_ids}" ]; then
 | 
			
		||||
    _err "Can not get zone names or IDs."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  # Walk through all possible zone names
 | 
			
		||||
  strip_counter=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    attempted_zone=$(echo "${domain}" | cut -d . -f ${strip_counter}-)
 | 
			
		||||
 | 
			
		||||
    # All possible zone names have been tried
 | 
			
		||||
    if [ -z "${attempted_zone}" ]; then
 | 
			
		||||
      _err "No zone for domain '${domain}' found."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _debug "Looking for zone '${attempted_zone}'"
 | 
			
		||||
 | 
			
		||||
    line_num="$(echo "${zone_names}" | grep -n "^${attempted_zone}\$" | _head_n 1 | cut -d : -f 1)"
 | 
			
		||||
    _debug2 line_num "${line_num}"
 | 
			
		||||
    if [ "$line_num" ]; then
 | 
			
		||||
      zone_id=$(echo "${zone_ids}" | sed -n "${line_num}p")
 | 
			
		||||
      zone_name=$(echo "${zone_names}" | sed -n "${line_num}p")
 | 
			
		||||
      if [ -z "${zone_id}" ]; then
 | 
			
		||||
        _err "Can not find zone id."
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
      _debug "Found relevant zone '${attempted_zone}' with id '${zone_id}' - will be used for domain '${domain}'."
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    _debug "Zone '${attempted_zone}' doesn't exist, let's try a less specific zone."
 | 
			
		||||
    strip_counter=$(_math "${strip_counter}" + 1)
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
# vim: et:ts=2:sw=2:
 | 
			
		||||
							
								
								
									
										173
									
								
								dnsapi/dns_googledomains.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										173
									
								
								dnsapi/dns_googledomains.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,173 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Author: Alex Leigh <leigh at alexleigh dot me>
 | 
			
		||||
# Created: 2023-03-02
 | 
			
		||||
 | 
			
		||||
#GOOGLEDOMAINS_ACCESS_TOKEN="xxxx"
 | 
			
		||||
#GOOGLEDOMAINS_ZONE="xxxx"
 | 
			
		||||
GOOGLEDOMAINS_API="https://acmedns.googleapis.com/v1/acmeChallengeSets"
 | 
			
		||||
 | 
			
		||||
######## Public functions ########
 | 
			
		||||
 | 
			
		||||
#Usage: dns_googledomains_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_googledomains_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _info "Invoking Google Domains ACME DNS API."
 | 
			
		||||
 | 
			
		||||
  if ! _dns_googledomains_setup; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  zone="$(_dns_googledomains_get_zone "$fulldomain")"
 | 
			
		||||
  if [ -z "$zone" ]; then
 | 
			
		||||
    _err "Could not find a Google Domains-managed zone containing the requested domain."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug zone "$zone"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  _info "Adding TXT record for $fulldomain."
 | 
			
		||||
  if _dns_googledomains_api "$zone" ":rotateChallenges" "{\"accessToken\":\"$GOOGLEDOMAINS_ACCESS_TOKEN\",\"recordsToAdd\":[{\"fqdn\":\"$fulldomain\",\"digest\":\"$txtvalue\"}],\"keepExpiredRecords\":true}"; then
 | 
			
		||||
    if _contains "$response" "$txtvalue"; then
 | 
			
		||||
      _info "TXT record added."
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Error adding TXT record."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _err "Error adding TXT record."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: dns_googledomains_rm   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_googledomains_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  _info "Invoking Google Domains ACME DNS API."
 | 
			
		||||
 | 
			
		||||
  if ! _dns_googledomains_setup; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  zone="$(_dns_googledomains_get_zone "$fulldomain")"
 | 
			
		||||
  if [ -z "$zone" ]; then
 | 
			
		||||
    _err "Could not find a Google Domains-managed domain based on request."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug zone "$zone"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  _info "Removing TXT record for $fulldomain."
 | 
			
		||||
  if _dns_googledomains_api "$zone" ":rotateChallenges" "{\"accessToken\":\"$GOOGLEDOMAINS_ACCESS_TOKEN\",\"recordsToRemove\":[{\"fqdn\":\"$fulldomain\",\"digest\":\"$txtvalue\"}],\"keepExpiredRecords\":true}"; then
 | 
			
		||||
    if _contains "$response" "$txtvalue"; then
 | 
			
		||||
      _err "Error removing TXT record."
 | 
			
		||||
      return 1
 | 
			
		||||
    else
 | 
			
		||||
      _info "TXT record removed."
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _err "Error removing TXT record."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
######## Private functions ########
 | 
			
		||||
 | 
			
		||||
_dns_googledomains_setup() {
 | 
			
		||||
  if [ -n "$GOOGLEDOMAINS_SETUP_COMPLETED" ]; then
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  GOOGLEDOMAINS_ACCESS_TOKEN="${GOOGLEDOMAINS_ACCESS_TOKEN:-$(_readaccountconf_mutable GOOGLEDOMAINS_ACCESS_TOKEN)}"
 | 
			
		||||
  GOOGLEDOMAINS_ZONE="${GOOGLEDOMAINS_ZONE:-$(_readaccountconf_mutable GOOGLEDOMAINS_ZONE)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$GOOGLEDOMAINS_ACCESS_TOKEN" ]; then
 | 
			
		||||
    GOOGLEDOMAINS_ACCESS_TOKEN=""
 | 
			
		||||
    _err "Google Domains access token was not specified."
 | 
			
		||||
    _err "Please visit Google Domains Security settings to provision an ACME DNS API access token."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$GOOGLEDOMAINS_ZONE" ]; then
 | 
			
		||||
    _savedomainconf GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
 | 
			
		||||
    _savedomainconf GOOGLEDOMAINS_ZONE "$GOOGLEDOMAINS_ZONE"
 | 
			
		||||
  else
 | 
			
		||||
    _saveaccountconf_mutable GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
 | 
			
		||||
    _clearaccountconf_mutable GOOGLEDOMAINS_ZONE
 | 
			
		||||
    _clearaccountconf GOOGLEDOMAINS_ZONE
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug GOOGLEDOMAINS_ACCESS_TOKEN "$GOOGLEDOMAINS_ACCESS_TOKEN"
 | 
			
		||||
  _debug GOOGLEDOMAINS_ZONE "$GOOGLEDOMAINS_ZONE"
 | 
			
		||||
 | 
			
		||||
  GOOGLEDOMAINS_SETUP_COMPLETED=1
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_dns_googledomains_get_zone() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
 | 
			
		||||
  # Use zone directly if provided
 | 
			
		||||
  if [ "$GOOGLEDOMAINS_ZONE" ]; then
 | 
			
		||||
    if ! _dns_googledomains_api "$GOOGLEDOMAINS_ZONE"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    echo "$GOOGLEDOMAINS_ZONE"
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  i=2
 | 
			
		||||
  while true; do
 | 
			
		||||
    curr=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
    _debug curr "$curr"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$curr" ]; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _dns_googledomains_api "$curr"; then
 | 
			
		||||
      echo "$curr"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_dns_googledomains_api() {
 | 
			
		||||
  zone=$1
 | 
			
		||||
  apimethod=$2
 | 
			
		||||
  data="$3"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$data" ]; then
 | 
			
		||||
    response="$(_get "$GOOGLEDOMAINS_API/$zone$apimethod")"
 | 
			
		||||
  else
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    export _H1="Content-Type: application/json"
 | 
			
		||||
    response="$(_post "$data" "$GOOGLEDOMAINS_API/$zone$apimethod")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug response "$response"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$response" "\"error\": {"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
# HUAWEICLOUD_Username
 | 
			
		||||
# HUAWEICLOUD_Password
 | 
			
		||||
# HUAWEICLOUD_ProjectID
 | 
			
		||||
# HUAWEICLOUD_DomainName
 | 
			
		||||
 | 
			
		||||
iam_api="https://iam.myhuaweicloud.com"
 | 
			
		||||
dns_api="https://dns.ap-southeast-1.myhuaweicloud.com" # Should work
 | 
			
		||||
@@ -14,6 +14,8 @@ dns_api="https://dns.ap-southeast-1.myhuaweicloud.com" # Should work
 | 
			
		||||
#
 | 
			
		||||
# Ref: https://support.huaweicloud.com/intl/zh-cn/api-dns/zh-cn_topic_0132421999.html
 | 
			
		||||
#
 | 
			
		||||
# About "DomainName" parameters see: https://support.huaweicloud.com/api-iam/iam_01_0006.html
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
dns_huaweicloud_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
@@ -21,16 +23,16 @@ dns_huaweicloud_add() {
 | 
			
		||||
 | 
			
		||||
  HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
 | 
			
		||||
  HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}"
 | 
			
		||||
  HUAWEICLOUD_ProjectID="${HUAWEICLOUD_ProjectID:-$(_readaccountconf_mutable HUAWEICLOUD_ProjectID)}"
 | 
			
		||||
  HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_DomainName)}"
 | 
			
		||||
 | 
			
		||||
  # Check information
 | 
			
		||||
  if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_ProjectID}" ]; then
 | 
			
		||||
  if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_DomainName}" ]; then
 | 
			
		||||
    _err "Not enough information provided to dns_huaweicloud!"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  unset token # Clear token
 | 
			
		||||
  token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_ProjectID}")"
 | 
			
		||||
  token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_DomainName}")"
 | 
			
		||||
  if [ -z "${token}" ]; then # Check token
 | 
			
		||||
    _err "dns_api(dns_huaweicloud): Error getting token."
 | 
			
		||||
    return 1
 | 
			
		||||
@@ -56,7 +58,7 @@ dns_huaweicloud_add() {
 | 
			
		||||
  # Do saving work if all succeeded
 | 
			
		||||
  _saveaccountconf_mutable HUAWEICLOUD_Username "${HUAWEICLOUD_Username}"
 | 
			
		||||
  _saveaccountconf_mutable HUAWEICLOUD_Password "${HUAWEICLOUD_Password}"
 | 
			
		||||
  _saveaccountconf_mutable HUAWEICLOUD_ProjectID "${HUAWEICLOUD_ProjectID}"
 | 
			
		||||
  _saveaccountconf_mutable HUAWEICLOUD_DomainName "${HUAWEICLOUD_DomainName}"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -72,16 +74,16 @@ dns_huaweicloud_rm() {
 | 
			
		||||
 | 
			
		||||
  HUAWEICLOUD_Username="${HUAWEICLOUD_Username:-$(_readaccountconf_mutable HUAWEICLOUD_Username)}"
 | 
			
		||||
  HUAWEICLOUD_Password="${HUAWEICLOUD_Password:-$(_readaccountconf_mutable HUAWEICLOUD_Password)}"
 | 
			
		||||
  HUAWEICLOUD_ProjectID="${HUAWEICLOUD_ProjectID:-$(_readaccountconf_mutable HUAWEICLOUD_ProjectID)}"
 | 
			
		||||
  HUAWEICLOUD_DomainName="${HUAWEICLOUD_DomainName:-$(_readaccountconf_mutable HUAWEICLOUD_DomainName)}"
 | 
			
		||||
 | 
			
		||||
  # Check information
 | 
			
		||||
  if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_ProjectID}" ]; then
 | 
			
		||||
  if [ -z "${HUAWEICLOUD_Username}" ] || [ -z "${HUAWEICLOUD_Password}" ] || [ -z "${HUAWEICLOUD_DomainName}" ]; then
 | 
			
		||||
    _err "Not enough information provided to dns_huaweicloud!"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  unset token # Clear token
 | 
			
		||||
  token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_ProjectID}")"
 | 
			
		||||
  token="$(_get_token "${HUAWEICLOUD_Username}" "${HUAWEICLOUD_Password}" "${HUAWEICLOUD_DomainName}")"
 | 
			
		||||
  if [ -z "${token}" ]; then # Check token
 | 
			
		||||
    _err "dns_api(dns_huaweicloud): Error getting token."
 | 
			
		||||
    return 1
 | 
			
		||||
@@ -96,19 +98,59 @@ dns_huaweicloud_rm() {
 | 
			
		||||
  fi
 | 
			
		||||
  _debug "Zone ID is:" "${zoneid}"
 | 
			
		||||
 | 
			
		||||
  # Remove all records
 | 
			
		||||
  # Therotically HuaweiCloud does not allow more than one record set
 | 
			
		||||
  # But remove them recurringly to increase robusty
 | 
			
		||||
  while [ "${record_id}" != "0" ]; do
 | 
			
		||||
    _debug "Removing Record"
 | 
			
		||||
    _rm_record "${token}" "${zoneid}" "${record_id}"
 | 
			
		||||
    record_id="$(_get_recordset_id "${token}" "${fulldomain}" "${zoneid}")"
 | 
			
		||||
  done
 | 
			
		||||
  record_id="$(_get_recordset_id "${token}" "${fulldomain}" "${zoneid}")"
 | 
			
		||||
  _recursive_rm_record "${token}" "${fulldomain}" "${zoneid}" "${record_id}"
 | 
			
		||||
  ret="$?"
 | 
			
		||||
  if [ "${ret}" != "0" ]; then
 | 
			
		||||
    _err "dns_api(dns_huaweicloud): Error removing record."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
###################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
# _recursive_rm_record
 | 
			
		||||
# remove all records from the record set
 | 
			
		||||
#
 | 
			
		||||
# _token=$1
 | 
			
		||||
# _domain=$2
 | 
			
		||||
# _zoneid=$3
 | 
			
		||||
# _record_id=$4
 | 
			
		||||
#
 | 
			
		||||
# Returns 0 on success
 | 
			
		||||
_recursive_rm_record() {
 | 
			
		||||
  _token=$1
 | 
			
		||||
  _domain=$2
 | 
			
		||||
  _zoneid=$3
 | 
			
		||||
  _record_id=$4
 | 
			
		||||
 | 
			
		||||
  # Most likely to have problems will huaweicloud side if more than 50 attempts but still cannot fully remove the record set
 | 
			
		||||
  # Maybe can be removed manually in the dashboard
 | 
			
		||||
  _retry_cnt=50
 | 
			
		||||
 | 
			
		||||
  # Remove all records
 | 
			
		||||
  # Therotically HuaweiCloud does not allow more than one record set
 | 
			
		||||
  # But remove them recurringly to increase robusty
 | 
			
		||||
 | 
			
		||||
  while [ "${_record_id}" != "0" ] && [ "${_retry_cnt}" != "0" ]; do
 | 
			
		||||
    _debug "Removing Record"
 | 
			
		||||
    _retry_cnt=$((_retry_cnt - 1))
 | 
			
		||||
    _rm_record "${_token}" "${_zoneid}" "${_record_id}"
 | 
			
		||||
    _record_id="$(_get_recordset_id "${_token}" "${_domain}" "${_zoneid}")"
 | 
			
		||||
    _debug2 "Checking record exists: record_id=${_record_id}"
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  # Check if retry count is reached
 | 
			
		||||
  if [ "${_retry_cnt}" = "0" ]; then
 | 
			
		||||
    _debug "Failed to remove record after 50 attempts, please try removing it manually in the dashboard"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# _get_zoneid
 | 
			
		||||
#
 | 
			
		||||
# _token=$1
 | 
			
		||||
@@ -122,7 +164,7 @@ _get_zoneid() {
 | 
			
		||||
 | 
			
		||||
  i=1
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "${_domain_string}" | cut -d . -f $i-100)
 | 
			
		||||
    h=$(printf "%s" "${_domain_string}" | cut -d . -f "$i"-100)
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
@@ -133,11 +175,11 @@ _get_zoneid() {
 | 
			
		||||
    if _contains "${response}" '"id"'; then
 | 
			
		||||
      zoneidlist=$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")
 | 
			
		||||
      zonenamelist=$(echo "${response}" | _egrep_o "\"name\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")
 | 
			
		||||
      _debug2 "Return Zone ID(s):" "${zoneidlist}"
 | 
			
		||||
      _debug2 "Return Zone Name(s):" "${zonenamelist}"
 | 
			
		||||
      _debug2 "Returned Zone ID(s):" "${zoneidlist}"
 | 
			
		||||
      _debug2 "Returned Zone Name(s):" "${zonenamelist}"
 | 
			
		||||
      zoneidnum=0
 | 
			
		||||
      zoneidcount=$(echo "${zoneidlist}" | grep -c '^')
 | 
			
		||||
      _debug "Retund Zone ID(s) Count:" "${zoneidcount}"
 | 
			
		||||
      _debug "Returned Zone ID(s) Count:" "${zoneidcount}"
 | 
			
		||||
      while [ "${zoneidnum}" -lt "${zoneidcount}" ]; do
 | 
			
		||||
        zoneidnum=$(_math "$zoneidnum" + 1)
 | 
			
		||||
        _zoneid=$(echo "${zoneidlist}" | sed -n "${zoneidnum}p")
 | 
			
		||||
@@ -204,8 +246,7 @@ _add_record() {
 | 
			
		||||
      \"type\": \"TXT\",
 | 
			
		||||
      \"ttl\": 1,
 | 
			
		||||
      \"records\": [
 | 
			
		||||
        ${_exist_record},
 | 
			
		||||
        \"\\\"${_txtvalue}\\\"\"
 | 
			
		||||
        ${_exist_record},\"\\\"${_txtvalue}\\\"\"
 | 
			
		||||
      ]
 | 
			
		||||
    }"
 | 
			
		||||
  fi
 | 
			
		||||
@@ -213,19 +254,16 @@ _add_record() {
 | 
			
		||||
  _record_id="$(_get_recordset_id "${_token}" "${_domain}" "${zoneid}")"
 | 
			
		||||
  _debug "Record Set ID is:" "${_record_id}"
 | 
			
		||||
 | 
			
		||||
  # Remove all records
 | 
			
		||||
  while [ "${_record_id}" != "0" ]; do
 | 
			
		||||
    _debug "Removing Record"
 | 
			
		||||
    _rm_record "${_token}" "${zoneid}" "${_record_id}"
 | 
			
		||||
    _record_id="$(_get_recordset_id "${_token}" "${_domain}" "${zoneid}")"
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  # Add brand new records with all old and new records
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
  export _H1="X-Auth-Token: ${_token}"
 | 
			
		||||
 | 
			
		||||
  _debug2 "${_post_body}"
 | 
			
		||||
  _post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets" >/dev/null
 | 
			
		||||
  if [ -z "${_exist_record}" ]; then
 | 
			
		||||
    _post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets" >/dev/null
 | 
			
		||||
  else
 | 
			
		||||
    _post "${_post_body}" "${dns_api}/v2/zones/${zoneid}/recordsets/${_record_id}" false "PUT" >/dev/null
 | 
			
		||||
  fi
 | 
			
		||||
  _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
 | 
			
		||||
  if [ "$_code" != "202" ]; then
 | 
			
		||||
    _err "dns_huaweicloud: http code ${_code}"
 | 
			
		||||
@@ -253,7 +291,7 @@ _rm_record() {
 | 
			
		||||
_get_token() {
 | 
			
		||||
  _username=$1
 | 
			
		||||
  _password=$2
 | 
			
		||||
  _project=$3
 | 
			
		||||
  _domain_name=$3
 | 
			
		||||
 | 
			
		||||
  _debug "Getting Token"
 | 
			
		||||
  body="{
 | 
			
		||||
@@ -267,14 +305,14 @@ _get_token() {
 | 
			
		||||
            \"name\": \"${_username}\",
 | 
			
		||||
            \"password\": \"${_password}\",
 | 
			
		||||
            \"domain\": {
 | 
			
		||||
              \"name\": \"${_username}\"
 | 
			
		||||
              \"name\": \"${_domain_name}\"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      \"scope\": {
 | 
			
		||||
        \"project\": {
 | 
			
		||||
          \"id\": \"${_project}\"
 | 
			
		||||
          \"name\": \"ap-southeast-1\"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ dns_infomaniak_add() {
 | 
			
		||||
  domain_id=${zone_and_id#* }
 | 
			
		||||
 | 
			
		||||
  # extract first part of domain
 | 
			
		||||
  key=${fulldomain%.$zone}
 | 
			
		||||
  key=${fulldomain%."$zone"}
 | 
			
		||||
 | 
			
		||||
  _debug "zone:$zone id:$domain_id key:$key"
 | 
			
		||||
 | 
			
		||||
@@ -149,7 +149,7 @@ dns_infomaniak_rm() {
 | 
			
		||||
  domain_id=${zone_and_id#* }
 | 
			
		||||
 | 
			
		||||
  # extract first part of domain
 | 
			
		||||
  key=${fulldomain%.$zone}
 | 
			
		||||
  key=${fulldomain%."$zone"}
 | 
			
		||||
 | 
			
		||||
  _debug "zone:$zone id:$domain_id key:$key"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Supports IONOS DNS API Beta v1.0.0
 | 
			
		||||
# Supports IONOS DNS API v1.0.1
 | 
			
		||||
#
 | 
			
		||||
# Usage:
 | 
			
		||||
#   Export IONOS_PREFIX and IONOS_SECRET before calling acme.sh:
 | 
			
		||||
@@ -26,7 +26,7 @@ dns_ionos_add() {
 | 
			
		||||
 | 
			
		||||
  _body="[{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}]"
 | 
			
		||||
 | 
			
		||||
  if _ionos_rest POST "$IONOS_ROUTE_ZONES/$_zone_id/records" "$_body" && [ -z "$response" ]; then
 | 
			
		||||
  if _ionos_rest POST "$IONOS_ROUTE_ZONES/$_zone_id/records" "$_body" && [ "$_code" = "201" ]; then
 | 
			
		||||
    _info "TXT record has been created successfully."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
@@ -47,7 +47,7 @@ dns_ionos_rm() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _ionos_rest DELETE "$IONOS_ROUTE_ZONES/$_zone_id/records/$_record_id" && [ -z "$response" ]; then
 | 
			
		||||
  if _ionos_rest DELETE "$IONOS_ROUTE_ZONES/$_zone_id/records/$_record_id" && [ "$_code" = "200" ]; then
 | 
			
		||||
    _info "TXT record has been deleted successfully."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
@@ -85,7 +85,7 @@ _get_root() {
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  if _ionos_rest GET "$IONOS_ROUTE_ZONES"; then
 | 
			
		||||
    response="$(echo "$response" | tr -d "\n")"
 | 
			
		||||
    _response="$(echo "$_response" | tr -d "\n")"
 | 
			
		||||
 | 
			
		||||
    while true; do
 | 
			
		||||
      h=$(printf "%s" "$domain" | cut -d . -f $i-100)
 | 
			
		||||
@@ -93,7 +93,7 @@ _get_root() {
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      _zone="$(echo "$response" | _egrep_o "\"name\":\"$h\".*\}")"
 | 
			
		||||
      _zone="$(echo "$_response" | _egrep_o "\"name\":\"$h\".*\}")"
 | 
			
		||||
      if [ "$_zone" ]; then
 | 
			
		||||
        _zone_id=$(printf "%s\n" "$_zone" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
 | 
			
		||||
        if [ "$_zone_id" ]; then
 | 
			
		||||
@@ -120,9 +120,9 @@ _ionos_get_record() {
 | 
			
		||||
  txtrecord=$3
 | 
			
		||||
 | 
			
		||||
  if _ionos_rest GET "$IONOS_ROUTE_ZONES/$zone_id?recordName=$fulldomain&recordType=TXT"; then
 | 
			
		||||
    response="$(echo "$response" | tr -d "\n")"
 | 
			
		||||
    _response="$(echo "$_response" | tr -d "\n")"
 | 
			
		||||
 | 
			
		||||
    _record="$(echo "$response" | _egrep_o "\"name\":\"$fulldomain\"[^\}]*\"type\":\"TXT\"[^\}]*\"content\":\"\\\\\"$txtrecord\\\\\"\".*\}")"
 | 
			
		||||
    _record="$(echo "$_response" | _egrep_o "\"name\":\"$fulldomain\"[^\}]*\"type\":\"TXT\"[^\}]*\"content\":\"\\\\\"$txtrecord\\\\\"\".*\}")"
 | 
			
		||||
    if [ "$_record" ]; then
 | 
			
		||||
      _record_id=$(printf "%s\n" "$_record" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
 | 
			
		||||
 | 
			
		||||
@@ -142,22 +142,30 @@ _ionos_rest() {
 | 
			
		||||
 | 
			
		||||
  export _H1="X-API-Key: $IONOS_API_KEY"
 | 
			
		||||
 | 
			
		||||
  # clear headers
 | 
			
		||||
  : >"$HTTP_HEADER"
 | 
			
		||||
 | 
			
		||||
  if [ "$method" != "GET" ]; then
 | 
			
		||||
    export _H2="Accept: application/json"
 | 
			
		||||
    export _H3="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
    response="$(_post "$data" "$IONOS_API$route" "" "$method" "application/json")"
 | 
			
		||||
    _response="$(_post "$data" "$IONOS_API$route" "" "$method" "application/json")"
 | 
			
		||||
  else
 | 
			
		||||
    export _H2="Accept: */*"
 | 
			
		||||
    export _H3=
 | 
			
		||||
    response="$(_get "$IONOS_API$route")"
 | 
			
		||||
 | 
			
		||||
    _response="$(_get "$IONOS_API$route")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "Error $route: $response"
 | 
			
		||||
    _err "Error $route: $_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 "response" "$response"
 | 
			
		||||
 | 
			
		||||
  _debug2 "_response" "$_response"
 | 
			
		||||
  _debug2 "_code" "$_code"
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										157
									
								
								dnsapi/dns_ipv64.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										157
									
								
								dnsapi/dns_ipv64.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,157 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Created by Roman Lumetsberger, to use ipv64.net's API to add/remove text records
 | 
			
		||||
#2022/11/29
 | 
			
		||||
 | 
			
		||||
# Pass credentials before "acme.sh --issue --dns dns_ipv64 ..."
 | 
			
		||||
# --
 | 
			
		||||
# export IPv64_Token="aaaaaaaaaaaaaaaaaaaaaaaaaa"
 | 
			
		||||
# --
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
IPv64_API="https://ipv64.net/api"
 | 
			
		||||
 | 
			
		||||
########  Public functions ######################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_ipv64_add _acme-challenge.domain.ipv64.net "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_ipv64_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  IPv64_Token="${IPv64_Token:-$(_readaccountconf_mutable IPv64_Token)}"
 | 
			
		||||
  if [ -z "$IPv64_Token" ]; then
 | 
			
		||||
    _err "You must export variable: IPv64_Token"
 | 
			
		||||
    _err "The API Key for your IPv64 account is necessary."
 | 
			
		||||
    _err "You can look it up in your IPv64 account."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Now save the credentials.
 | 
			
		||||
  _saveaccountconf_mutable IPv64_Token "$IPv64_Token"
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain" "$fulldomain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  # convert to lower case
 | 
			
		||||
  _domain="$(echo "$_domain" | _lower_case)"
 | 
			
		||||
  _sub_domain="$(echo "$_sub_domain" | _lower_case)"
 | 
			
		||||
  # Now add the TXT record
 | 
			
		||||
  _info "Trying to add TXT record"
 | 
			
		||||
  if _ipv64_rest "POST" "add_record=$_domain&praefix=$_sub_domain&type=TXT&content=$txtvalue"; then
 | 
			
		||||
    _info "TXT record has been successfully added."
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Errors happened during adding the TXT record, response=$_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage: fulldomain txtvalue
 | 
			
		||||
#Usage: dns_ipv64_rm _acme-challenge.domain.ipv64.net "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
#Remove the txt record after validation.
 | 
			
		||||
dns_ipv64_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  IPv64_Token="${IPv64_Token:-$(_readaccountconf_mutable IPv64_Token)}"
 | 
			
		||||
  if [ -z "$IPv64_Token" ]; then
 | 
			
		||||
    _err "You must export variable: IPv64_Token"
 | 
			
		||||
    _err "The API Key for your IPv64 account is necessary."
 | 
			
		||||
    _err "You can look it up in your IPv64 account."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain" "$fulldomain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  # convert to lower case
 | 
			
		||||
  _domain="$(echo "$_domain" | _lower_case)"
 | 
			
		||||
  _sub_domain="$(echo "$_sub_domain" | _lower_case)"
 | 
			
		||||
  # Now delete the TXT record
 | 
			
		||||
  _info "Trying to delete TXT record"
 | 
			
		||||
  if _ipv64_rest "DELETE" "del_record=$_domain&praefix=$_sub_domain&type=TXT&content=$txtvalue"; then
 | 
			
		||||
    _info "TXT record has been successfully deleted."
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _err "Errors happened during deleting the TXT record, response=$_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  _ipv64_get "get_domains"
 | 
			
		||||
  domain_data=$_response
 | 
			
		||||
 | 
			
		||||
  while true; do
 | 
			
		||||
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    #if _contains "$domain_data" "\""$h"\"\:"; then
 | 
			
		||||
    if _contains "$domain_data" "\"""$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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#send get request to api
 | 
			
		||||
# $1 has to set the api-function
 | 
			
		||||
_ipv64_get() {
 | 
			
		||||
  url="$IPv64_API?$1"
 | 
			
		||||
  export _H1="Authorization: Bearer $IPv64_Token"
 | 
			
		||||
 | 
			
		||||
  _response=$(_get "$url")
 | 
			
		||||
  _response="$(echo "$_response" | _normalizeJson)"
 | 
			
		||||
 | 
			
		||||
  if _contains "$_response" "429 Too Many Requests"; then
 | 
			
		||||
    _info "API throttled, sleeping to reset the limit"
 | 
			
		||||
    _sleep 10
 | 
			
		||||
    _response=$(_get "$url")
 | 
			
		||||
    _response="$(echo "$_response" | _normalizeJson)"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_ipv64_rest() {
 | 
			
		||||
  url="$IPv64_API"
 | 
			
		||||
  export _H1="Authorization: Bearer $IPv64_Token"
 | 
			
		||||
  export _H2="Content-Type: application/x-www-form-urlencoded"
 | 
			
		||||
  _response=$(_post "$2" "$url" "" "$1")
 | 
			
		||||
 | 
			
		||||
  if _contains "$_response" "429 Too Many Requests"; then
 | 
			
		||||
    _info "API throttled, sleeping to reset the limit"
 | 
			
		||||
    _sleep 10
 | 
			
		||||
    _response=$(_post "$2" "$url" "" "$1")
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$_response" "\"info\":\"success\""; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$_response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -32,7 +32,11 @@ dns_ispconfig_rm() {
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
 | 
			
		||||
_ISPC_credentials() {
 | 
			
		||||
  if [ -z "${ISPC_User}" ] || [ -z "$ISPC_Password" ] || [ -z "${ISPC_Api}" ] || [ -n "${ISPC_Api_Insecure}" ]; then
 | 
			
		||||
  ISPC_User="${ISPC_User:-$(_readaccountconf_mutable ISPC_User)}"
 | 
			
		||||
  ISPC_Password="${ISPC_Password:-$(_readaccountconf_mutable ISPC_Password)}"
 | 
			
		||||
  ISPC_Api="${ISPC_Api:-$(_readaccountconf_mutable ISPC_Api)}"
 | 
			
		||||
  ISPC_Api_Insecure="${ISPC_Api_Insecure:-$(_readaccountconf_mutable ISPC_Api_Insecure)}"
 | 
			
		||||
  if [ -z "${ISPC_User}" ] || [ -z "${ISPC_Password}" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then
 | 
			
		||||
    ISPC_User=""
 | 
			
		||||
    ISPC_Password=""
 | 
			
		||||
    ISPC_Api=""
 | 
			
		||||
@@ -40,10 +44,10 @@ _ISPC_credentials() {
 | 
			
		||||
    _err "You haven't specified the ISPConfig Login data, URL and whether you want check the ISPC SSL cert. Please try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _saveaccountconf ISPC_User "${ISPC_User}"
 | 
			
		||||
    _saveaccountconf ISPC_Password "${ISPC_Password}"
 | 
			
		||||
    _saveaccountconf ISPC_Api "${ISPC_Api}"
 | 
			
		||||
    _saveaccountconf ISPC_Api_Insecure "${ISPC_Api_Insecure}"
 | 
			
		||||
    _saveaccountconf_mutable ISPC_User "${ISPC_User}"
 | 
			
		||||
    _saveaccountconf_mutable ISPC_Password "${ISPC_Password}"
 | 
			
		||||
    _saveaccountconf_mutable ISPC_Api "${ISPC_Api}"
 | 
			
		||||
    _saveaccountconf_mutable ISPC_Api_Insecure "${ISPC_Api_Insecure}"
 | 
			
		||||
    # Set whether curl should use secure or insecure mode
 | 
			
		||||
    export HTTPS_INSECURE="${ISPC_Api_Insecure}"
 | 
			
		||||
  fi
 | 
			
		||||
 
 | 
			
		||||
@@ -5,51 +5,81 @@
 | 
			
		||||
# Environment variables:
 | 
			
		||||
#
 | 
			
		||||
#  - $KAS_Login (Kasserver API login name)
 | 
			
		||||
#  - $KAS_Authtype (Kasserver API auth type. Default: sha1)
 | 
			
		||||
#  - $KAS_Authtype (Kasserver API auth type. Default: plain)
 | 
			
		||||
#  - $KAS_Authdata (Kasserver API auth data.)
 | 
			
		||||
#
 | 
			
		||||
# Author: Martin Kammerlander, Phlegx Systems OG <martin.kammerlander@phlegx.com>
 | 
			
		||||
# Updated by: Marc-Oliver Lange <git@die-lang.es>
 | 
			
		||||
# Credits: Inspired by dns_he.sh. Thanks a lot man!
 | 
			
		||||
# Git repo: https://github.com/phlegx/acme.sh
 | 
			
		||||
# TODO: Better Error handling
 | 
			
		||||
# Last update: squared GmbH <github@squaredgmbh.de>
 | 
			
		||||
# Credits:
 | 
			
		||||
# - dns_he.sh. Thanks a lot man!
 | 
			
		||||
# - Martin Kammerlander, Phlegx Systems OG <martin.kammerlander@phlegx.com>
 | 
			
		||||
# - Marc-Oliver Lange <git@die-lang.es>
 | 
			
		||||
# - https://github.com/o1oo11oo/kasapi.sh
 | 
			
		||||
########################################################################
 | 
			
		||||
KAS_Api="https://kasapi.kasserver.com/dokumentation/formular.php"
 | 
			
		||||
KAS_Api_GET="$(_get "https://kasapi.kasserver.com/soap/wsdl/KasApi.wsdl")"
 | 
			
		||||
KAS_Api="$(echo "$KAS_Api_GET" | tr -d ' ' | grep -i "<soap:addresslocation=" | sed "s/='/\n/g" | grep -i "http" | sed "s/'\/>//g")"
 | 
			
		||||
_info "[KAS] -> API URL $KAS_Api"
 | 
			
		||||
 | 
			
		||||
KAS_Auth_GET="$(_get "https://kasapi.kasserver.com/soap/wsdl/KasAuth.wsdl")"
 | 
			
		||||
KAS_Auth="$(echo "$KAS_Auth_GET" | tr -d ' ' | grep -i "<soap:addresslocation=" | sed "s/='/\n/g" | grep -i "http" | sed "s/'\/>//g")"
 | 
			
		||||
_info "[KAS] -> AUTH URL $KAS_Auth"
 | 
			
		||||
 | 
			
		||||
KAS_default_ratelimit=5 # TODO - Every response delivers a ratelimit (seconds) where KASAPI is blocking a request.
 | 
			
		||||
 | 
			
		||||
########  Public functions  #####################
 | 
			
		||||
dns_kas_add() {
 | 
			
		||||
  _fulldomain=$1
 | 
			
		||||
  _txtvalue=$2
 | 
			
		||||
  _info "Using DNS-01 All-inkl/Kasserver hook"
 | 
			
		||||
  _info "Adding $_fulldomain DNS TXT entry on All-inkl/Kasserver"
 | 
			
		||||
  _info "Check and Save Props"
 | 
			
		||||
 | 
			
		||||
  _info "[KAS] -> Using DNS-01 All-inkl/Kasserver hook"
 | 
			
		||||
  _info "[KAS] -> Check and Save Props"
 | 
			
		||||
  _check_and_save
 | 
			
		||||
  _info "Checking Zone and Record_Name"
 | 
			
		||||
 | 
			
		||||
  _info "[KAS] -> Adding $_fulldomain DNS TXT entry on all-inkl.com/Kasserver"
 | 
			
		||||
  _info "[KAS] -> Retriving Credential Token"
 | 
			
		||||
  _get_credential_token
 | 
			
		||||
 | 
			
		||||
  _info "[KAS] -> Checking Zone and Record_Name"
 | 
			
		||||
  _get_zone_and_record_name "$_fulldomain"
 | 
			
		||||
  _info "Getting Record ID"
 | 
			
		||||
 | 
			
		||||
  _info "[KAS] -> Checking for existing Record entries"
 | 
			
		||||
  _get_record_id
 | 
			
		||||
 | 
			
		||||
  _info "Creating TXT DNS record"
 | 
			
		||||
  params="?kas_login=$KAS_Login"
 | 
			
		||||
  params="$params&kas_auth_type=$KAS_Authtype"
 | 
			
		||||
  params="$params&kas_auth_data=$KAS_Authdata"
 | 
			
		||||
  params="$params&var1=record_name"
 | 
			
		||||
  params="$params&wert1=$_record_name"
 | 
			
		||||
  params="$params&var2=record_type"
 | 
			
		||||
  params="$params&wert2=TXT"
 | 
			
		||||
  params="$params&var3=record_data"
 | 
			
		||||
  params="$params&wert3=$_txtvalue"
 | 
			
		||||
  params="$params&var4=record_aux"
 | 
			
		||||
  params="$params&wert4=0"
 | 
			
		||||
  params="$params&kas_action=add_dns_settings"
 | 
			
		||||
  params="$params&var5=zone_host"
 | 
			
		||||
  params="$params&wert5=$_zone"
 | 
			
		||||
  _debug2 "Wait for 10 seconds by default before calling KAS API."
 | 
			
		||||
  _sleep 10
 | 
			
		||||
  response="$(_get "$KAS_Api$params")"
 | 
			
		||||
  _debug2 "response" "$response"
 | 
			
		||||
  # If there is a record_id, delete the entry
 | 
			
		||||
  if [ -n "$_record_id" ]; then
 | 
			
		||||
    _info "[KAS] -> Existing records found. Now deleting old entries"
 | 
			
		||||
    for i in $_record_id; do
 | 
			
		||||
      _delete_RecordByID "$i"
 | 
			
		||||
    done
 | 
			
		||||
  else
 | 
			
		||||
    _info "[KAS] -> No record found."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "TRUE"; then
 | 
			
		||||
    _err "An unkown error occurred, please check manually."
 | 
			
		||||
  _info "[KAS] -> Creating TXT DNS record"
 | 
			
		||||
  action="add_dns_settings"
 | 
			
		||||
  kasReqParam="\"record_name\":\"$_record_name\""
 | 
			
		||||
  kasReqParam="$kasReqParam,\"record_type\":\"TXT\""
 | 
			
		||||
  kasReqParam="$kasReqParam,\"record_data\":\"$_txtvalue\""
 | 
			
		||||
  kasReqParam="$kasReqParam,\"record_aux\":\"0\""
 | 
			
		||||
  kasReqParam="$kasReqParam,\"zone_host\":\"$_zone\""
 | 
			
		||||
  response="$(_callAPI "$action" "$kasReqParam")"
 | 
			
		||||
  _debug2 "[KAS] -> Response" "$response"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$response" ]; then
 | 
			
		||||
    _info "[KAS] -> Response was empty, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif _contains "$response" "<SOAP-ENV:Fault>"; then
 | 
			
		||||
    faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
 | 
			
		||||
    case "${faultstring}" in
 | 
			
		||||
    "record_already_exists")
 | 
			
		||||
      _info "[KAS] -> The record already exists, which must not be a problem. Please check manually."
 | 
			
		||||
      ;;
 | 
			
		||||
    *)
 | 
			
		||||
      _err "[KAS] -> An error =>$faultstring<= occurred, please check manually."
 | 
			
		||||
      return 1
 | 
			
		||||
      ;;
 | 
			
		||||
    esac
 | 
			
		||||
  elif ! _contains "$response" "<item><key xsi:type=\"xsd:string\">ReturnString</key><value xsi:type=\"xsd:string\">TRUE</value></item>"; then
 | 
			
		||||
    _err "[KAS] -> An unknown error occurred, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
@@ -58,45 +88,62 @@ dns_kas_add() {
 | 
			
		||||
dns_kas_rm() {
 | 
			
		||||
  _fulldomain=$1
 | 
			
		||||
  _txtvalue=$2
 | 
			
		||||
  _info "Using DNS-01 All-inkl/Kasserver hook"
 | 
			
		||||
  _info "Cleaning up after All-inkl/Kasserver hook"
 | 
			
		||||
  _info "Removing $_fulldomain DNS TXT entry on All-inkl/Kasserver"
 | 
			
		||||
 | 
			
		||||
  _info "Check and Save Props"
 | 
			
		||||
  _info "[KAS] -> Using DNS-01 All-inkl/Kasserver hook"
 | 
			
		||||
  _info "[KAS] -> Check and Save Props"
 | 
			
		||||
  _check_and_save
 | 
			
		||||
  _info "Checking Zone and Record_Name"
 | 
			
		||||
 | 
			
		||||
  _info "[KAS] -> Cleaning up after All-inkl/Kasserver hook"
 | 
			
		||||
  _info "[KAS] -> Removing $_fulldomain DNS TXT entry on All-inkl/Kasserver"
 | 
			
		||||
  _info "[KAS] -> Retriving Credential Token"
 | 
			
		||||
  _get_credential_token
 | 
			
		||||
 | 
			
		||||
  _info "[KAS] -> Checking Zone and Record_Name"
 | 
			
		||||
  _get_zone_and_record_name "$_fulldomain"
 | 
			
		||||
  _info "Getting Record ID"
 | 
			
		||||
 | 
			
		||||
  _info "[KAS] -> Getting Record ID"
 | 
			
		||||
  _get_record_id
 | 
			
		||||
 | 
			
		||||
  _info "[KAS] -> Removing entries with ID: $_record_id"
 | 
			
		||||
  # If there is a record_id, delete the entry
 | 
			
		||||
  if [ -n "$_record_id" ]; then
 | 
			
		||||
    params="?kas_login=$KAS_Login"
 | 
			
		||||
    params="$params&kas_auth_type=$KAS_Authtype"
 | 
			
		||||
    params="$params&kas_auth_data=$KAS_Authdata"
 | 
			
		||||
    params="$params&kas_action=delete_dns_settings"
 | 
			
		||||
 | 
			
		||||
    for i in $_record_id; do
 | 
			
		||||
      params2="$params&var1=record_id"
 | 
			
		||||
      params2="$params2&wert1=$i"
 | 
			
		||||
      _debug2 "Wait for 10 seconds by default before calling KAS API."
 | 
			
		||||
      _sleep 10
 | 
			
		||||
      response="$(_get "$KAS_Api$params2")"
 | 
			
		||||
      _debug2 "response" "$response"
 | 
			
		||||
      if ! _contains "$response" "TRUE"; then
 | 
			
		||||
        _err "Either the txt record is not found or another error occurred, please check manually."
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
      _delete_RecordByID "$i"
 | 
			
		||||
    done
 | 
			
		||||
  else # Cannot delete or unkown error
 | 
			
		||||
    _err "No record_id found that can be deleted. Please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
    _info "[KAS] -> No record_id found that can be deleted. Please check manually."
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
########################## PRIVATE FUNCTIONS ###########################
 | 
			
		||||
# Delete Record ID
 | 
			
		||||
_delete_RecordByID() {
 | 
			
		||||
  recId=$1
 | 
			
		||||
  action="delete_dns_settings"
 | 
			
		||||
  kasReqParam="\"record_id\":\"$recId\""
 | 
			
		||||
  response="$(_callAPI "$action" "$kasReqParam")"
 | 
			
		||||
  _debug2 "[KAS] -> Response" "$response"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$response" ]; then
 | 
			
		||||
    _info "[KAS] -> Response was empty, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif _contains "$response" "<SOAP-ENV:Fault>"; then
 | 
			
		||||
    faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
 | 
			
		||||
    case "${faultstring}" in
 | 
			
		||||
    "record_id_not_found")
 | 
			
		||||
      _info "[KAS] -> The record was not found, which perhaps is not a problem. Please check manually."
 | 
			
		||||
      ;;
 | 
			
		||||
    *)
 | 
			
		||||
      _err "[KAS] -> An error =>$faultstring<= occurred, please check manually."
 | 
			
		||||
      return 1
 | 
			
		||||
      ;;
 | 
			
		||||
    esac
 | 
			
		||||
  elif ! _contains "$response" "<item><key xsi:type=\"xsd:string\">ReturnString</key><value xsi:type=\"xsd:string\">TRUE</value></item>"; then
 | 
			
		||||
    _err "[KAS] -> An unknown error occurred, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
# Checks for the ENV variables and saves them
 | 
			
		||||
_check_and_save() {
 | 
			
		||||
  KAS_Login="${KAS_Login:-$(_readaccountconf_mutable KAS_Login)}"
 | 
			
		||||
@@ -107,7 +154,7 @@ _check_and_save() {
 | 
			
		||||
    KAS_Login=
 | 
			
		||||
    KAS_Authtype=
 | 
			
		||||
    KAS_Authdata=
 | 
			
		||||
    _err "No auth details provided. Please set user credentials using the \$KAS_Login, \$KAS_Authtype, and \$KAS_Authdata environment variables."
 | 
			
		||||
    _err "[KAS] -> No auth details provided. Please set user credentials using the \$KAS_Login, \$KAS_Authtype, and \$KAS_Authdata environment variables."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable KAS_Login "$KAS_Login"
 | 
			
		||||
@@ -119,50 +166,116 @@ _check_and_save() {
 | 
			
		||||
# Gets back the base domain/zone and record name.
 | 
			
		||||
# See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
 | 
			
		||||
_get_zone_and_record_name() {
 | 
			
		||||
  params="?kas_login=$KAS_Login"
 | 
			
		||||
  params="?kas_login=$KAS_Login"
 | 
			
		||||
  params="$params&kas_auth_type=$KAS_Authtype"
 | 
			
		||||
  params="$params&kas_auth_data=$KAS_Authdata"
 | 
			
		||||
  params="$params&kas_action=get_domains"
 | 
			
		||||
  action="get_domains"
 | 
			
		||||
  response="$(_callAPI "$action")"
 | 
			
		||||
  _debug2 "[KAS] -> Response" "$response"
 | 
			
		||||
 | 
			
		||||
  _debug2 "Wait for 10 seconds by default before calling KAS API."
 | 
			
		||||
  _sleep 10
 | 
			
		||||
  response="$(_get "$KAS_Api$params")"
 | 
			
		||||
  _debug2 "response" "$response"
 | 
			
		||||
  _zonen="$(echo "$response" | tr -d "\n\r" | tr -d " " | tr '[]' '<>' | sed "s/=>Array/\n=> Array/g" | tr ' ' '\n' | grep "domain_name" | tr '<' '\n' | grep "domain_name" | sed "s/domain_name>=>//g")"
 | 
			
		||||
  _domain="$1"
 | 
			
		||||
  _temp_domain="$(echo "$1" | sed 's/\.$//')"
 | 
			
		||||
  _rootzone="$_domain"
 | 
			
		||||
  for i in $_zonen; do
 | 
			
		||||
    l1=${#_rootzone}
 | 
			
		||||
  if [ -z "$response" ]; then
 | 
			
		||||
    _info "[KAS] -> Response was empty, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif _contains "$response" "<SOAP-ENV:Fault>"; then
 | 
			
		||||
    faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
 | 
			
		||||
    _err "[KAS] -> Either no domains were found or another error =>$faultstring<= occurred, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  zonen="$(echo "$response" | sed 's/<item>/\n/g' | sed -r 's/(.*<key xsi:type="xsd:string">domain_name<\/key><value xsi:type="xsd:string">)(.*)(<\/value.*)/\2/' | sed '/^</d')"
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  temp_domain="$(echo "$1" | sed 's/\.$//')"
 | 
			
		||||
  rootzone="$domain"
 | 
			
		||||
  for i in $zonen; do
 | 
			
		||||
    l1=${#rootzone}
 | 
			
		||||
    l2=${#i}
 | 
			
		||||
    if _endswith "$_domain" "$i" && [ "$l1" -ge "$l2" ]; then
 | 
			
		||||
      _rootzone="$i"
 | 
			
		||||
    if _endswith "$domain" "$i" && [ "$l1" -ge "$l2" ]; then
 | 
			
		||||
      rootzone="$i"
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
  _zone="${_rootzone}."
 | 
			
		||||
  _temp_record_name="$(echo "$_temp_domain" | sed "s/$_rootzone//g")"
 | 
			
		||||
  _record_name="$(echo "$_temp_record_name" | sed 's/\.$//')"
 | 
			
		||||
  _debug2 "Zone:" "$_zone"
 | 
			
		||||
  _debug2 "Domain:" "$_domain"
 | 
			
		||||
  _debug2 "Record_Name:" "$_record_name"
 | 
			
		||||
  _zone="${rootzone}."
 | 
			
		||||
  temp_record_name="$(echo "$temp_domain" | sed "s/$rootzone//g")"
 | 
			
		||||
  _record_name="$(echo "$temp_record_name" | sed 's/\.$//')"
 | 
			
		||||
  _debug "[KAS] -> Zone:" "$_zone"
 | 
			
		||||
  _debug "[KAS] -> Domain:" "$domain"
 | 
			
		||||
  _debug "[KAS] -> Record_Name:" "$_record_name"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Retrieve the DNS record ID
 | 
			
		||||
_get_record_id() {
 | 
			
		||||
  params="?kas_login=$KAS_Login"
 | 
			
		||||
  params="$params&kas_auth_type=$KAS_Authtype"
 | 
			
		||||
  params="$params&kas_auth_data=$KAS_Authdata"
 | 
			
		||||
  params="$params&kas_action=get_dns_settings"
 | 
			
		||||
  params="$params&var1=zone_host"
 | 
			
		||||
  params="$params&wert1=$_zone"
 | 
			
		||||
  action="get_dns_settings"
 | 
			
		||||
  kasReqParam="\"zone_host\":\"$_zone\""
 | 
			
		||||
  response="$(_callAPI "$action" "$kasReqParam")"
 | 
			
		||||
  _debug2 "[KAS] -> Response" "$response"
 | 
			
		||||
 | 
			
		||||
  _debug2 "Wait for 10 seconds by default before calling KAS API."
 | 
			
		||||
  _sleep 10
 | 
			
		||||
  response="$(_get "$KAS_Api$params")"
 | 
			
		||||
  _debug2 "response" "$response"
 | 
			
		||||
  _record_id="$(echo "$response" | tr -d "\n\r" | tr -d " " | tr '[]' '<>' | sed "s/=>Array/\n=> Array/g" | tr ' ' '\n' | grep "=>$_record_name<" | grep '>TXT<' | tr '<' '\n' | grep record_id | sed "s/record_id>=>//g")"
 | 
			
		||||
  _debug2 _record_id "$_record_id"
 | 
			
		||||
  if [ -z "$response" ]; then
 | 
			
		||||
    _info "[KAS] -> Response was empty, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif _contains "$response" "<SOAP-ENV:Fault>"; then
 | 
			
		||||
    faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
 | 
			
		||||
    _err "[KAS] -> Either no domains were found or another error =>$faultstring<= occurred, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _record_id="$(echo "$response" | tr -d '\n\r' | sed "s/<item xsi:type=\"ns2:Map\">/\n/g" | grep -i "$_record_name" | grep -i ">TXT<" | sed "s/<item><key xsi:type=\"xsd:string\">record_id<\/key><value xsi:type=\"xsd:string\">/=>/g" | grep -i "$_txtvalue" | sed "s/<\/value><\/item>/\n/g" | grep "=>" | sed "s/=>//g")"
 | 
			
		||||
  _debug "[KAS] -> Record Id: " "$_record_id"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Retrieve credential token
 | 
			
		||||
_get_credential_token() {
 | 
			
		||||
  baseParamAuth="\"kas_login\":\"$KAS_Login\""
 | 
			
		||||
  baseParamAuth="$baseParamAuth,\"kas_auth_type\":\"$KAS_Authtype\""
 | 
			
		||||
  baseParamAuth="$baseParamAuth,\"kas_auth_data\":\"$KAS_Authdata\""
 | 
			
		||||
  baseParamAuth="$baseParamAuth,\"session_lifetime\":600"
 | 
			
		||||
  baseParamAuth="$baseParamAuth,\"session_update_lifetime\":\"Y\""
 | 
			
		||||
 | 
			
		||||
  data='<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:xmethodsKasApiAuthentication" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:KasAuth><Params xsi:type="xsd:string">{'
 | 
			
		||||
  data="$data$baseParamAuth}</Params></ns1:KasAuth></SOAP-ENV:Body></SOAP-ENV:Envelope>"
 | 
			
		||||
 | 
			
		||||
  _debug "[KAS] -> Be friendly and wait $KAS_default_ratelimit seconds by default before calling KAS API."
 | 
			
		||||
  _sleep $KAS_default_ratelimit
 | 
			
		||||
 | 
			
		||||
  contentType="text/xml"
 | 
			
		||||
  export _H1="SOAPAction: urn:xmethodsKasApiAuthentication#KasAuth"
 | 
			
		||||
  response="$(_post "$data" "$KAS_Auth" "" "POST" "$contentType")"
 | 
			
		||||
  _debug2 "[KAS] -> Response" "$response"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$response" ]; then
 | 
			
		||||
    _info "[KAS] -> Response was empty, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  elif _contains "$response" "<SOAP-ENV:Fault>"; then
 | 
			
		||||
    faultstring="$(echo "$response" | tr -d '\n\r' | sed "s/<faultstring>/\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
 | 
			
		||||
    _err "[KAS] -> Could not retrieve login token or antoher error =>$faultstring<= occurred, please check manually."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _credential_token="$(echo "$response" | tr '\n' ' ' | sed 's/.*return xsi:type="xsd:string">\(.*\)<\/return>/\1/' | sed 's/<\/ns1:KasAuthResponse\(.*\)Envelope>.*//')"
 | 
			
		||||
  _debug "[KAS] -> Credential Token: " "$_credential_token"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_callAPI() {
 | 
			
		||||
  kasaction=$1
 | 
			
		||||
  kasReqParams=$2
 | 
			
		||||
 | 
			
		||||
  baseParamAuth="\"kas_login\":\"$KAS_Login\""
 | 
			
		||||
  baseParamAuth="$baseParamAuth,\"kas_auth_type\":\"session\""
 | 
			
		||||
  baseParamAuth="$baseParamAuth,\"kas_auth_data\":\"$_credential_token\""
 | 
			
		||||
 | 
			
		||||
  data='<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:xmethodsKasApi" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:KasApi><Params xsi:type="xsd:string">{'
 | 
			
		||||
  data="$data$baseParamAuth,\"kas_action\":\"$kasaction\""
 | 
			
		||||
  if [ -n "$kasReqParams" ]; then
 | 
			
		||||
    data="$data,\"KasRequestParams\":{$kasReqParams}"
 | 
			
		||||
  fi
 | 
			
		||||
  data="$data}</Params></ns1:KasApi></SOAP-ENV:Body></SOAP-ENV:Envelope>"
 | 
			
		||||
 | 
			
		||||
  _debug2 "[KAS] -> Request" "$data"
 | 
			
		||||
 | 
			
		||||
  _debug "[KAS] -> Be friendly and wait $KAS_default_ratelimit seconds by default before calling KAS API."
 | 
			
		||||
  _sleep $KAS_default_ratelimit
 | 
			
		||||
 | 
			
		||||
  contentType="text/xml"
 | 
			
		||||
  export _H1="SOAPAction: urn:xmethodsKasApi#KasApi"
 | 
			
		||||
  response="$(_post "$data" "$KAS_Api" "" "POST" "$contentType")"
 | 
			
		||||
  _debug2 "[KAS] -> Response" "$response"
 | 
			
		||||
  echo "$response"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
############################################################
 | 
			
		||||
# KingHost API support                                     #
 | 
			
		||||
# http://api.kinghost.net/doc/                             #
 | 
			
		||||
# https://api.kinghost.net/doc/                             #
 | 
			
		||||
#                                                          #
 | 
			
		||||
# Author: Felipe Keller Braz <felipebraz@kinghost.com.br>  #
 | 
			
		||||
# Report Bugs here: https://github.com/kinghost/acme.sh    #
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										147
									
								
								dnsapi/dns_la.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								dnsapi/dns_la.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#LA_Id="test123"
 | 
			
		||||
#LA_Key="d1j2fdo4dee3948"
 | 
			
		||||
 | 
			
		||||
LA_Api="https://api.dns.la/api"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: dns_la_add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_la_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  LA_Id="${LA_Id:-$(_readaccountconf_mutable LA_Id)}"
 | 
			
		||||
  LA_Key="${LA_Key:-$(_readaccountconf_mutable LA_Key)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$LA_Id" ] || [ -z "$LA_Key" ]; then
 | 
			
		||||
    LA_Id=""
 | 
			
		||||
    LA_Key=""
 | 
			
		||||
    _err "You didn't specify a dnsla api id and key yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the api key and email to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable LA_Id "$LA_Id"
 | 
			
		||||
  _saveaccountconf_mutable LA_Key "$LA_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"
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _la_rest "record.ashx?cmd=create&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&host=$_sub_domain&recordtype=TXT&recorddata=$txtvalue&recordline="; then
 | 
			
		||||
    if _contains "$response" '"resultid":'; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    elif _contains "$response" '"code":532'; 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_la_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  LA_Id="${LA_Id:-$(_readaccountconf_mutable LA_Id)}"
 | 
			
		||||
  LA_Key="${LA_Key:-$(_readaccountconf_mutable LA_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"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  if ! _la_rest "record.ashx?cmd=listn&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&domain=$_domain&host=$_sub_domain&recordtype=TXT&recorddata=$txtvalue"; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" '"recordid":'; then
 | 
			
		||||
    _info "Don't need to remove."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  record_id=$(printf "%s" "$response" | grep '"recordid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
 | 
			
		||||
  _debug "record_id" "$record_id"
 | 
			
		||||
  if [ -z "$record_id" ]; then
 | 
			
		||||
    _err "Can not get record id to remove."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if ! _la_rest "record.ashx?cmd=remove&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&domain=$_domain&recordid=$record_id"; then
 | 
			
		||||
    _err "Delete record error."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _contains "$response" '"code":300'
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  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)
 | 
			
		||||
    if [ -z "$h" ]; then
 | 
			
		||||
      #not valid
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _la_rest "domain.ashx?cmd=get&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domain=$h"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" '"domainid":'; then
 | 
			
		||||
      _domain_id=$(printf "%s" "$response" | grep '"domainid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
 | 
			
		||||
      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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Usage:  URI
 | 
			
		||||
_la_rest() {
 | 
			
		||||
  url="$LA_Api/$1"
 | 
			
		||||
  _debug "$url"
 | 
			
		||||
 | 
			
		||||
  if ! response="$(_get "$url" | tr -d ' ' | tr "}" ",")"; then
 | 
			
		||||
    _err "Error: $url"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -3,10 +3,10 @@
 | 
			
		||||
#Author: Rolph Haspers <r.haspers@global.leaseweb.com>
 | 
			
		||||
#Utilize leaseweb.com API to finish dns-01 verifications.
 | 
			
		||||
#Requires a Leaseweb API Key (export LSW_Key="Your Key")
 | 
			
		||||
#See http://developer.leaseweb.com for more information.
 | 
			
		||||
#See https://developer.leaseweb.com for more information.
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
LSW_API="https://api.leaseweb.com/hosting/v2/domains/"
 | 
			
		||||
LSW_API="https://api.leaseweb.com/hosting/v2/domains"
 | 
			
		||||
 | 
			
		||||
#Usage: dns_leaseweb_add   _acme-challenge.www.domain.com
 | 
			
		||||
dns_leaseweb_add() {
 | 
			
		||||
 
 | 
			
		||||
@@ -32,8 +32,12 @@ dns_loopia_add() {
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
 | 
			
		||||
  _loopia_add_sub_domain "$_domain" "$_sub_domain"
 | 
			
		||||
  _loopia_add_record "$_domain" "$_sub_domain" "$txtvalue"
 | 
			
		||||
  if ! _loopia_add_sub_domain "$_domain" "$_sub_domain"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  if ! _loopia_add_record "$_domain" "$_sub_domain" "$txtvalue"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -70,12 +74,13 @@ dns_loopia_rm() {
 | 
			
		||||
        <value><string>%s</string></value>
 | 
			
		||||
      </param>
 | 
			
		||||
    </params>
 | 
			
		||||
  </methodCall>' "$LOOPIA_User" "$LOOPIA_Password" "$_domain" "$_sub_domain")
 | 
			
		||||
  </methodCall>' "$LOOPIA_User" "$Encoded_Password" "$_domain" "$_sub_domain")
 | 
			
		||||
 | 
			
		||||
  response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "OK"; then
 | 
			
		||||
    _err "Error could not get txt records"
 | 
			
		||||
    err_response=$(echo "$response" | sed 's/.*<string>\(.*\)<\/string>.*/\1/')
 | 
			
		||||
    _err "Error could not get txt records: $err_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
@@ -101,6 +106,12 @@ _loopia_load_config() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _contains "$LOOPIA_Password" "'" || _contains "$LOOPIA_Password" '"'; then
 | 
			
		||||
    _err "Password contains quoute or double quoute and this is not supported by dns_loopia.sh"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  Encoded_Password=$(_xml_encode "$LOOPIA_Password")
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -133,11 +144,12 @@ _loopia_get_records() {
 | 
			
		||||
        <value><string>%s</string></value>
 | 
			
		||||
      </param>
 | 
			
		||||
    </params>
 | 
			
		||||
  </methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain")
 | 
			
		||||
  </methodCall>' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain")
 | 
			
		||||
 | 
			
		||||
  response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
 | 
			
		||||
  if ! _contains "$response" "<array>"; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    err_response=$(echo "$response" | sed 's/.*<string>\(.*\)<\/string>.*/\1/')
 | 
			
		||||
    _err "Error: $err_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
@@ -162,7 +174,7 @@ _get_root() {
 | 
			
		||||
    <value><string>%s</string></value>
 | 
			
		||||
   </param>
 | 
			
		||||
  </params>
 | 
			
		||||
  </methodCall>' $LOOPIA_User $LOOPIA_Password)
 | 
			
		||||
  </methodCall>' "$LOOPIA_User" "$Encoded_Password")
 | 
			
		||||
 | 
			
		||||
  response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
 | 
			
		||||
  while true; do
 | 
			
		||||
@@ -206,32 +218,35 @@ _loopia_add_record() {
 | 
			
		||||
        <value><string>%s</string></value>
 | 
			
		||||
      </param>
 | 
			
		||||
      <param>
 | 
			
		||||
        <struct>
 | 
			
		||||
          <member>
 | 
			
		||||
            <name>type</name>
 | 
			
		||||
            <value><string>TXT</string></value>
 | 
			
		||||
          </member>
 | 
			
		||||
          <member>
 | 
			
		||||
            <name>priority</name>
 | 
			
		||||
            <value><int>0</int></value>
 | 
			
		||||
          </member>
 | 
			
		||||
          <member>
 | 
			
		||||
            <name>ttl</name>
 | 
			
		||||
            <value><int>300</int></value>
 | 
			
		||||
          </member>
 | 
			
		||||
          <member>
 | 
			
		||||
            <name>rdata</name>
 | 
			
		||||
            <value><string>%s</string></value>
 | 
			
		||||
          </member>
 | 
			
		||||
        </struct>
 | 
			
		||||
        <value>
 | 
			
		||||
          <struct>
 | 
			
		||||
            <member>
 | 
			
		||||
              <name>type</name>
 | 
			
		||||
              <value><string>TXT</string></value>
 | 
			
		||||
            </member>
 | 
			
		||||
            <member>
 | 
			
		||||
              <name>priority</name>
 | 
			
		||||
              <value><int>0</int></value>
 | 
			
		||||
            </member>
 | 
			
		||||
            <member>
 | 
			
		||||
              <name>ttl</name>
 | 
			
		||||
              <value><int>300</int></value>
 | 
			
		||||
            </member>
 | 
			
		||||
            <member>
 | 
			
		||||
              <name>rdata</name>
 | 
			
		||||
              <value><string>%s</string></value>
 | 
			
		||||
            </member>
 | 
			
		||||
          </struct>
 | 
			
		||||
        </value>
 | 
			
		||||
      </param>
 | 
			
		||||
    </params>
 | 
			
		||||
  </methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain" "$txtval")
 | 
			
		||||
  </methodCall>' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain" "$txtval")
 | 
			
		||||
 | 
			
		||||
  response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "OK"; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    err_response=$(echo "$response" | sed 's/.*<string>\(.*\)<\/string>.*/\1/')
 | 
			
		||||
    _err "Error: $err_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
@@ -255,7 +270,7 @@ _sub_domain_exists() {
 | 
			
		||||
        <value><string>%s</string></value>
 | 
			
		||||
      </param>
 | 
			
		||||
    </params>
 | 
			
		||||
  </methodCall>' $LOOPIA_User $LOOPIA_Password "$domain")
 | 
			
		||||
  </methodCall>' "$LOOPIA_User" "$Encoded_Password" "$domain")
 | 
			
		||||
 | 
			
		||||
  response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
 | 
			
		||||
 | 
			
		||||
@@ -290,13 +305,22 @@ _loopia_add_sub_domain() {
 | 
			
		||||
        <value><string>%s</string></value>
 | 
			
		||||
      </param>
 | 
			
		||||
    </params>
 | 
			
		||||
  </methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain")
 | 
			
		||||
  </methodCall>' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain")
 | 
			
		||||
 | 
			
		||||
  response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
 | 
			
		||||
 | 
			
		||||
  if ! _contains "$response" "OK"; then
 | 
			
		||||
    _err "Error"
 | 
			
		||||
    err_response=$(echo "$response" | sed 's/.*<string>\(.*\)<\/string>.*/\1/')
 | 
			
		||||
    _err "Error: $err_response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_xml_encode() {
 | 
			
		||||
  encoded_string=$1
 | 
			
		||||
  encoded_string=$(echo "$encoded_string" | sed 's/&/\&/')
 | 
			
		||||
  encoded_string=$(echo "$encoded_string" | sed 's/</\</')
 | 
			
		||||
  encoded_string=$(echo "$encoded_string" | sed 's/>/\>/')
 | 
			
		||||
  printf "%s" "$encoded_string"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -163,6 +163,7 @@ _retrieve_miab_env() {
 | 
			
		||||
  _saveaccountconf_mutable MIAB_Username "$MIAB_Username"
 | 
			
		||||
  _saveaccountconf_mutable MIAB_Password "$MIAB_Password"
 | 
			
		||||
  _saveaccountconf_mutable MIAB_Server "$MIAB_Server"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Useage: _miab_rest  "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"  "custom/_acme-challenge.www.domain.com/txt  "POST"
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,7 @@ dns_mydevil_rm() {
 | 
			
		||||
  validRecords="^${num}${w}${fulldomain}${w}TXT${w}${any}${txtvalue}$"
 | 
			
		||||
  for id in $(devil dns list "$domain" | tail -n+2 | grep "${validRecords}" | cut -w -s -f 1); do
 | 
			
		||||
    _info "Removing record $id from domain $domain"
 | 
			
		||||
    devil dns del "$domain" "$id" || _err "Could not remove DNS record."
 | 
			
		||||
    echo "y" | devil dns del "$domain" "$id" || _err "Could not remove DNS record."
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +87,9 @@ mydevil_get_domain() {
 | 
			
		||||
  domain=""
 | 
			
		||||
 | 
			
		||||
  for domain in $(devil dns list | cut -w -s -f 1 | tail -n+2); do
 | 
			
		||||
    _debug "Checking domain: $domain"
 | 
			
		||||
    if _endswith "$fulldomain" "$domain"; then
 | 
			
		||||
      _debug "Fulldomain '$fulldomain' matches '$domain'"
 | 
			
		||||
      printf -- "%s" "$domain"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
 
 | 
			
		||||
@@ -150,7 +150,7 @@ _get_root() {
 | 
			
		||||
_mydnsjp_retrieve_domain() {
 | 
			
		||||
  _debug "Login to MyDNS.JP"
 | 
			
		||||
 | 
			
		||||
  response="$(_post "masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/?MENU=100")"
 | 
			
		||||
  response="$(_post "MENU=100&masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/members/")"
 | 
			
		||||
  cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)"
 | 
			
		||||
 | 
			
		||||
  # If cookies is not empty then logon successful
 | 
			
		||||
@@ -159,22 +159,8 @@ _mydnsjp_retrieve_domain() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Retrieve DOMAIN INFO page"
 | 
			
		||||
 | 
			
		||||
  export _H1="Cookie:${cookie}"
 | 
			
		||||
 | 
			
		||||
  response="$(_get "$MYDNSJP_API/?MENU=300")"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "Fail to retrieve DOMAIN INFO."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _root_domain=$(echo "$response" | grep "DNSINFO\[domainname\]" | sed 's/^.*value="\([^"]*\)".*/\1/')
 | 
			
		||||
 | 
			
		||||
  # Logout
 | 
			
		||||
  response="$(_get "$MYDNSJP_API/?MENU=090")"
 | 
			
		||||
 | 
			
		||||
  _debug _root_domain "$_root_domain"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_root_domain" ]; then
 | 
			
		||||
 
 | 
			
		||||
@@ -82,7 +82,7 @@ _get_root() {
 | 
			
		||||
    _debug "Failed domain lookup via domains.getList api call. Trying domain lookup via domains.dns.getHosts api."
 | 
			
		||||
    # The above "getList" api will only return hosts *owned* by the calling user. However, if the calling
 | 
			
		||||
    # user is not the owner, but still has administrative rights, we must query the getHosts api directly.
 | 
			
		||||
    # See this comment and the official namecheap response: http://disq.us/p/1q6v9x9
 | 
			
		||||
    # See this comment and the official namecheap response: https://disq.us/p/1q6v9x9
 | 
			
		||||
    if ! _get_root_by_getHosts "$fulldomain"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
@@ -259,7 +259,7 @@ _set_namecheap_TXT() {
 | 
			
		||||
  _debug hosts "$hosts"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$hosts" ]; then
 | 
			
		||||
    _error "Hosts not found"
 | 
			
		||||
    _err "Hosts not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
@@ -313,7 +313,7 @@ _del_namecheap_TXT() {
 | 
			
		||||
  _debug hosts "$hosts"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$hosts" ]; then
 | 
			
		||||
    _error "Hosts not found"
 | 
			
		||||
    _err "Hosts not found"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ _get_root() {
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "<domain>$host"; then
 | 
			
		||||
    if _contains "$response" ">$host</domain>"; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
      _domain="$host"
 | 
			
		||||
      return 0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								dnsapi/dns_nanelo.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								dnsapi/dns_nanelo.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Official DNS API for Nanelo.com
 | 
			
		||||
 | 
			
		||||
# Provide the required API Key like this:
 | 
			
		||||
# NANELO_TOKEN="FmD408PdqT1E269gUK57"
 | 
			
		||||
 | 
			
		||||
NANELO_API="https://api.nanelo.com/v1/"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
# Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_nanelo_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  NANELO_TOKEN="${NANELO_TOKEN:-$(_readaccountconf_mutable NANELO_TOKEN)}"
 | 
			
		||||
  if [ -z "$NANELO_TOKEN" ]; then
 | 
			
		||||
    NANELO_TOKEN=""
 | 
			
		||||
    _err "You didn't configure a Nanelo API Key yet."
 | 
			
		||||
    _err "Please set NANELO_TOKEN and try again."
 | 
			
		||||
    _err "Login to Nanelo.com and go to Settings > API Keys to get a Key"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _info "Adding TXT record to ${fulldomain}"
 | 
			
		||||
  response="$(_get "$NANELO_API$NANELO_TOKEN/dns/addrecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
 | 
			
		||||
  if _contains "${response}" 'success'; then
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Could not create resource record, please check the logs"
 | 
			
		||||
  _err "${response}"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dns_nanelo_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  NANELO_TOKEN="${NANELO_TOKEN:-$(_readaccountconf_mutable NANELO_TOKEN)}"
 | 
			
		||||
  if [ -z "$NANELO_TOKEN" ]; then
 | 
			
		||||
    NANELO_TOKEN=""
 | 
			
		||||
    _err "You didn't configure a Nanelo API Key yet."
 | 
			
		||||
    _err "Please set NANELO_TOKEN and try again."
 | 
			
		||||
    _err "Login to Nanelo.com and go to Settings > API Keys to get a Key"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _info "Deleting resource record $fulldomain"
 | 
			
		||||
  response="$(_get "$NANELO_API$NANELO_TOKEN/dns/deleterecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
 | 
			
		||||
  if _contains "${response}" 'success'; then
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Could not delete resource record, please check the logs"
 | 
			
		||||
  _err "${response}"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#NederHost_Key="sdfgikogfdfghjklkjhgfcdcfghjk"
 | 
			
		||||
#NederHost_Key="sdfgikogfdfghjklkjhgfcdcfghj"
 | 
			
		||||
 | 
			
		||||
NederHost_Api="https://api.nederhost.nl/dns/v1"
 | 
			
		||||
 | 
			
		||||
@@ -112,12 +112,8 @@ _nederhost_rest() {
 | 
			
		||||
  export _H1="Authorization: Bearer $NederHost_Key"
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$NederHost_Api/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$NederHost_Api/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
  _debug data "$data"
 | 
			
		||||
  response="$(_post "$data" "$NederHost_Api/$ep" "" "$m")"
 | 
			
		||||
 | 
			
		||||
  _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
 | 
			
		||||
  _debug "http response code $_code"
 | 
			
		||||
 
 | 
			
		||||
@@ -18,15 +18,15 @@ dns_netlify_add() {
 | 
			
		||||
    NETLIFY_ACCESS_TOKEN=""
 | 
			
		||||
    _err "Please specify your Netlify Access Token and try again."
 | 
			
		||||
    return 1
 | 
			
		||||
  else
 | 
			
		||||
    _saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Using Netlify"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain" "$accesstoken"; then
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
@@ -62,9 +62,9 @@ dns_netlify_rm() {
 | 
			
		||||
  _debug txtdomain "$txtdomain"
 | 
			
		||||
  _debug txt "$txt"
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
 | 
			
		||||
  NETLIFY_ACCESS_TOKEN="${NETLIFY_ACCESS_TOKEN:-$(_readaccountconf_mutable NETLIFY_ACCESS_TOKEN)}"
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$txtdomain" "$accesstoken"; then
 | 
			
		||||
  if ! _get_root "$txtdomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
@@ -114,7 +114,7 @@ _get_root() {
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
 | 
			
		||||
      _domain_id=$(echo "$response" | _egrep_o "\"[^\"]*\",\"name\":\"$h" | cut -d , -f 1 | tr -d \")
 | 
			
		||||
      _domain_id=$(echo "$response" | _egrep_o "\"[^\"]*\",\"name\":\"$h\"" | cut -d , -f 1 | 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
 | 
			
		||||
 
 | 
			
		||||
@@ -159,7 +159,7 @@ _oci_config() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$(printf "%s\n" "$OCI_CLI_KEY" | wc -l)" -eq 1 ]; then
 | 
			
		||||
    OCI_CLI_KEY=$(printf "%s" "$OCI_CLI_KEY" | _dbase64 multiline)
 | 
			
		||||
    OCI_CLI_KEY=$(printf "%s" "$OCI_CLI_KEY" | _dbase64)
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
@@ -265,6 +265,7 @@ _signed_request() {
 | 
			
		||||
    _response="$(_get "https://${_sig_host}${_sig_target}")"
 | 
			
		||||
  elif [ "$_curl_method" = "PATCH" ]; then
 | 
			
		||||
    export _H1="$_date_header"
 | 
			
		||||
    # shellcheck disable=SC2090
 | 
			
		||||
    export _H2="$_sig_body_sha256"
 | 
			
		||||
    export _H3="$_sig_body_type"
 | 
			
		||||
    export _H4="$_sig_body_length"
 | 
			
		||||
 
 | 
			
		||||
@@ -57,16 +57,16 @@ _dns_openstack_create_recordset() {
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_recordset_id" ]; then
 | 
			
		||||
    _info "Creating a new recordset"
 | 
			
		||||
    if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record "$txtvalue" "$_zone_id" "$fulldomain."); then
 | 
			
		||||
    if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record="$txtvalue" "$_zone_id" "$fulldomain."); then
 | 
			
		||||
      _err "No recordset ID found after create"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _info "Updating existing recordset"
 | 
			
		||||
    # Build new list of --record <rec> args for update
 | 
			
		||||
    _record_args="--record $txtvalue"
 | 
			
		||||
    # Build new list of --record=<rec> args for update
 | 
			
		||||
    _record_args="--record=$txtvalue"
 | 
			
		||||
    for _rec in $_records; do
 | 
			
		||||
      _record_args="$_record_args --record $_rec"
 | 
			
		||||
      _record_args="$_record_args --record=$_rec"
 | 
			
		||||
    done
 | 
			
		||||
    # shellcheck disable=SC2086
 | 
			
		||||
    if ! _recordset_id=$(openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain."); then
 | 
			
		||||
@@ -107,13 +107,13 @@ _dns_openstack_delete_recordset() {
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _info "Found existing records, updating recordset"
 | 
			
		||||
    # Build new list of --record <rec> args for update
 | 
			
		||||
    # Build new list of --record=<rec> args for update
 | 
			
		||||
    _record_args=""
 | 
			
		||||
    for _rec in $_records; do
 | 
			
		||||
      if [ "$_rec" = "$txtvalue" ]; then
 | 
			
		||||
        continue
 | 
			
		||||
      fi
 | 
			
		||||
      _record_args="$_record_args --record $_rec"
 | 
			
		||||
      _record_args="$_record_args --record=$_rec"
 | 
			
		||||
    done
 | 
			
		||||
    # shellcheck disable=SC2086
 | 
			
		||||
    if ! openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain." >/dev/null; then
 | 
			
		||||
 
 | 
			
		||||
@@ -137,7 +137,7 @@ _get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=2
 | 
			
		||||
  p=1
 | 
			
		||||
  if _opns_rest "GET" "/domain/get"; then
 | 
			
		||||
  if _opns_rest "GET" "/domain/searchPrimaryDomain"; then
 | 
			
		||||
    _domain_response="$response"
 | 
			
		||||
  else
 | 
			
		||||
    return 1
 | 
			
		||||
@@ -150,8 +150,7 @@ _get_root() {
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _debug h "$h"
 | 
			
		||||
    id=$(echo "$_domain_response" | _egrep_o "\"[^\"]*\":{\"enabled\":\"1\",\"type\":{\"master\":{\"value\":\"master\",\"selected\":1},\"slave\":{\"value\":\"slave\",\"selected\":0}},\"masterip\":{\"\":{[^}]*}}(,\"allownotifyslave\":{\"\":{[^}]*}},|,)\"domainname\":\"${h}\"" | cut -d ':' -f 1 | cut -d '"' -f 2)
 | 
			
		||||
 | 
			
		||||
    id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"primary\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
 | 
			
		||||
    if [ -n "$id" ]; then
 | 
			
		||||
      _debug id "$id"
 | 
			
		||||
      _host=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,9 @@
 | 
			
		||||
#'ovh-eu'
 | 
			
		||||
OVH_EU='https://eu.api.ovh.com/1.0'
 | 
			
		||||
 | 
			
		||||
#'ovh-us'
 | 
			
		||||
OVH_US='https://api.us.ovhcloud.com/1.0'
 | 
			
		||||
 | 
			
		||||
#'ovh-ca':
 | 
			
		||||
OVH_CA='https://ca.api.ovh.com/1.0'
 | 
			
		||||
 | 
			
		||||
@@ -29,9 +32,6 @@ SYS_EU='https://eu.api.soyoustart.com/1.0'
 | 
			
		||||
#'soyoustart-ca'
 | 
			
		||||
SYS_CA='https://ca.api.soyoustart.com/1.0'
 | 
			
		||||
 | 
			
		||||
#'runabove-ca'
 | 
			
		||||
RAV_CA='https://api.runabove.com/1.0'
 | 
			
		||||
 | 
			
		||||
wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-OVH-domain-api"
 | 
			
		||||
 | 
			
		||||
ovh_success="https://github.com/acmesh-official/acme.sh/wiki/OVH-Success"
 | 
			
		||||
@@ -45,6 +45,10 @@ _ovh_get_api() {
 | 
			
		||||
    printf "%s" $OVH_EU
 | 
			
		||||
    return
 | 
			
		||||
    ;;
 | 
			
		||||
  ovh-us | ovhus)
 | 
			
		||||
    printf "%s" $OVH_US
 | 
			
		||||
    return
 | 
			
		||||
    ;;
 | 
			
		||||
  ovh-ca | ovhca)
 | 
			
		||||
    printf "%s" $OVH_CA
 | 
			
		||||
    return
 | 
			
		||||
@@ -65,14 +69,15 @@ _ovh_get_api() {
 | 
			
		||||
    printf "%s" $SYS_CA
 | 
			
		||||
    return
 | 
			
		||||
    ;;
 | 
			
		||||
  runabove-ca | runaboveca)
 | 
			
		||||
    printf "%s" $RAV_CA
 | 
			
		||||
  # raw API url starts with https://
 | 
			
		||||
  https*)
 | 
			
		||||
    printf "%s" "$1"
 | 
			
		||||
    return
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
  *)
 | 
			
		||||
 | 
			
		||||
    _err "Unknown parameter : $1"
 | 
			
		||||
    _err "Unknown endpoint : $1"
 | 
			
		||||
    return 1
 | 
			
		||||
    ;;
 | 
			
		||||
  esac
 | 
			
		||||
@@ -92,7 +97,7 @@ _initAuth() {
 | 
			
		||||
 | 
			
		||||
  if [ "$OVH_AK" != "$(_readaccountconf OVH_AK)" ]; then
 | 
			
		||||
    _info "It seems that your ovh key is changed, let's clear consumer key first."
 | 
			
		||||
    _clearaccountconf OVH_CK
 | 
			
		||||
    _clearaccountconf_mutable OVH_CK
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable OVH_AK "$OVH_AK"
 | 
			
		||||
  _saveaccountconf_mutable OVH_AS "$OVH_AS"
 | 
			
		||||
@@ -118,13 +123,14 @@ _initAuth() {
 | 
			
		||||
    #return and wait for retry.
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable OVH_CK "$OVH_CK"
 | 
			
		||||
 | 
			
		||||
  _info "Checking authentication"
 | 
			
		||||
 | 
			
		||||
  if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL" || _contains "$response" "NOT_CREDENTIAL"; then
 | 
			
		||||
    _err "The consumer key is invalid: $OVH_CK"
 | 
			
		||||
    _err "Please retry to create a new one."
 | 
			
		||||
    _clearaccountconf OVH_CK
 | 
			
		||||
    _clearaccountconf_mutable OVH_CK
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _info "Consumer key is ok."
 | 
			
		||||
@@ -198,6 +204,8 @@ dns_ovh_rm() {
 | 
			
		||||
      if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
      _ovh_rest POST "domain/zone/$_domain/refresh"
 | 
			
		||||
      _debug "Refresh:$response"
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
@@ -233,8 +241,7 @@ _ovh_authentication() {
 | 
			
		||||
  _secure_debug consumerKey "$consumerKey"
 | 
			
		||||
 | 
			
		||||
  OVH_CK="$consumerKey"
 | 
			
		||||
  _saveaccountconf OVH_CK "$OVH_CK"
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable OVH_CK "$OVH_CK"
 | 
			
		||||
  _info "Please open this link to do authentication: $(__green "$validationUrl")"
 | 
			
		||||
 | 
			
		||||
  _info "Here is a guide for you: $(__green "$wiki")"
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ pleskxml_init_checks_done=0
 | 
			
		||||
NEWLINE='\
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
pleskxml_tplt_get_domains="<packet><customer><get-domain-list><filter/></get-domain-list></customer></packet>"
 | 
			
		||||
pleskxml_tplt_get_domains="<packet><webspace><get><filter/><dataset><gen_info/></dataset></get></webspace></packet>"
 | 
			
		||||
# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh
 | 
			
		||||
# Also used to test credentials and URI.
 | 
			
		||||
# No params.
 | 
			
		||||
@@ -145,22 +145,25 @@ dns_pleskxml_rm() {
 | 
			
		||||
  )"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$reclist" ]; then
 | 
			
		||||
    _err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting."
 | 
			
		||||
    _err "No TXT records found for root domain $fulldomain (Plesk domain ID ${root_domain_id}). Exiting."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "Got list of DNS TXT records for root domain '$root_domain_name':"
 | 
			
		||||
  _debug "Got list of DNS TXT records for root Plesk domain ID ${root_domain_id} of root domain $fulldomain:"
 | 
			
		||||
  _debug "$reclist"
 | 
			
		||||
 | 
			
		||||
  # Extracting the id of the TXT record for the full domain (NOT case-sensitive) and corresponding value
 | 
			
		||||
  recid="$(
 | 
			
		||||
    _value "$reclist" |
 | 
			
		||||
      grep "<host>${fulldomain}.</host>" |
 | 
			
		||||
      grep -i "<host>${fulldomain}.</host>" |
 | 
			
		||||
      grep "<value>${txtvalue}</value>" |
 | 
			
		||||
      sed 's/^.*<id>\([0-9]\{1,\}\)<\/id>.*$/\1/'
 | 
			
		||||
  )"
 | 
			
		||||
 | 
			
		||||
  _debug "Got id from line: $recid"
 | 
			
		||||
 | 
			
		||||
  if ! _value "$recid" | grep '^[0-9]\{1,\}$' >/dev/null; then
 | 
			
		||||
    _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
 | 
			
		||||
    _err "DNS records for root domain '${fulldomain}.' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
 | 
			
		||||
    _err "Cannot delete TXT record. Exiting."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
@@ -251,9 +254,12 @@ _call_api() {
 | 
			
		||||
 | 
			
		||||
  # Detect any <status> that isn't "ok". None of the used calls should fail if the API is working correctly.
 | 
			
		||||
  # Also detect if there simply aren't any status lines (null result?) and report that, as well.
 | 
			
		||||
  # Remove <data></data> structure from result string, since it might contain <status> values that are related to the status of the domain and not to the API request
 | 
			
		||||
 | 
			
		||||
  statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *<status>[^<]*</status> *$')"
 | 
			
		||||
  statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *<status>ok</status> *$')"
 | 
			
		||||
  statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | sed '/<data>/,/<\/data>/d' | grep -c '^ *<status>[^<]*</status> *$')"
 | 
			
		||||
  statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | sed '/<data>/,/<\/data>/d' | grep -c '^ *<status>ok</status> *$')"
 | 
			
		||||
  _debug "statuslines_count_total=$statuslines_count_total."
 | 
			
		||||
  _debug "statuslines_count_okay=$statuslines_count_okay."
 | 
			
		||||
 | 
			
		||||
  if [ -z "$statuslines_count_total" ]; then
 | 
			
		||||
 | 
			
		||||
@@ -375,7 +381,7 @@ _pleskxml_get_root_domain() {
 | 
			
		||||
  # Output will be one line per known domain, containing 2 <name> tages and a single <id> tag
 | 
			
		||||
  # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
 | 
			
		||||
 | 
			
		||||
  output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' '<type>domain</type>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
 | 
			
		||||
  output="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
 | 
			
		||||
 | 
			
		||||
  _debug 'Domains managed by Plesk server are (ignore the hacked output):'
 | 
			
		||||
  _debug "$output"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										115
									
								
								dnsapi/dns_rage4.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										115
									
								
								dnsapi/dns_rage4.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#RAGE4_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
#
 | 
			
		||||
#RAGE4_USERNAME="xxxx@sss.com"
 | 
			
		||||
 | 
			
		||||
RAGE4_Api="https://rage4.com/rapi/"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_rage4_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  unquotedtxtvalue=$(echo "$txtvalue" | tr -d \")
 | 
			
		||||
 | 
			
		||||
  RAGE4_USERNAME="${RAGE4_USERNAME:-$(_readaccountconf_mutable RAGE4_USERNAME)}"
 | 
			
		||||
  RAGE4_TOKEN="${RAGE4_TOKEN:-$(_readaccountconf_mutable RAGE4_TOKEN)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$RAGE4_USERNAME" ] || [ -z "$RAGE4_TOKEN" ]; then
 | 
			
		||||
    RAGE4_USERNAME=""
 | 
			
		||||
    RAGE4_TOKEN=""
 | 
			
		||||
    _err "You didn't specify a Rage4 api token and username yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  #save the api key and email to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable RAGE4_USERNAME "$RAGE4_USERNAME"
 | 
			
		||||
  _saveaccountconf_mutable RAGE4_TOKEN "$RAGE4_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
 | 
			
		||||
  _rage4_rest "createrecord/?id=$_domain_id&name=$fulldomain&content=$unquotedtxtvalue&type=TXT&active=true&ttl=1"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain txtvalue
 | 
			
		||||
dns_rage4_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  RAGE4_USERNAME="${RAGE4_USERNAME:-$(_readaccountconf_mutable RAGE4_USERNAME)}"
 | 
			
		||||
  RAGE4_TOKEN="${RAGE4_TOKEN:-$(_readaccountconf_mutable RAGE4_TOKEN)}"
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  _rage4_rest "getrecords/?id=${_domain_id}"
 | 
			
		||||
 | 
			
		||||
  _record_id=$(echo "$response" | sed -rn 's/.*"id":([[:digit:]]+)[^\}]*'"$txtvalue"'.*/\1/p')
 | 
			
		||||
  _rage4_rest "deleterecord/?id=${_record_id}"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
 | 
			
		||||
  if ! _rage4_rest "getdomains"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _get_root_domain "$domain"
 | 
			
		||||
 | 
			
		||||
  for line in $(echo "$response" | tr '}' '\n'); do
 | 
			
		||||
    __domain=$(echo "$line" | sed -rn 's/.*"name":"([^"]*)",.*/\1/p')
 | 
			
		||||
    __domain_id=$(echo "$line" | sed -rn 's/.*"id":([^,]*),.*/\1/p')
 | 
			
		||||
    if [ "$domain" != "${domain%"$__domain"*}" ]; then
 | 
			
		||||
      _domain_id="$__domain_id"
 | 
			
		||||
      break
 | 
			
		||||
    fi
 | 
			
		||||
  done
 | 
			
		||||
 | 
			
		||||
  if [ -z "$_domain_id" ]; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_rage4_rest() {
 | 
			
		||||
  ep="$1"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  username_trimmed=$(echo "$RAGE4_USERNAME" | tr -d '"')
 | 
			
		||||
  token_trimmed=$(echo "$RAGE4_TOKEN" | tr -d '"')
 | 
			
		||||
  auth=$(printf '%s:%s' "$username_trimmed" "$token_trimmed" | _base64)
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  export _H2="Authorization: Basic $auth"
 | 
			
		||||
 | 
			
		||||
  response="$(_get "$RAGE4_Api$ep")"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -92,10 +92,10 @@ _get_root() {
 | 
			
		||||
  domains_list=$(echo "${response}" | grep dname | sed -r "s/.*dname=\"([^\"]+)\".*/\\1/g")
 | 
			
		||||
 | 
			
		||||
  for ITEM in ${domains_list}; do
 | 
			
		||||
    IDN_ITEM="$(_idn "${ITEM}")"
 | 
			
		||||
    IDN_ITEM=${ITEM}
 | 
			
		||||
    case "${domain}" in
 | 
			
		||||
    *${IDN_ITEM}*)
 | 
			
		||||
      _domain=${IDN_ITEM}
 | 
			
		||||
      _domain="$(_idn "${ITEM}")"
 | 
			
		||||
      _debug _domain "${_domain}"
 | 
			
		||||
      return 0
 | 
			
		||||
      ;;
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ dns_selectel_rm() {
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _record_seg="$(echo "$response" | _egrep_o "\"content\" *: *\"$txtvalue\"[^}]*}")"
 | 
			
		||||
  _record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")"
 | 
			
		||||
  _debug2 "_record_seg" "$_record_seg"
 | 
			
		||||
  if [ -z "$_record_seg" ]; then
 | 
			
		||||
    _err "can not find _record_seg"
 | 
			
		||||
@@ -120,7 +120,7 @@ _get_root() {
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"name\": \"$h\","; then
 | 
			
		||||
    if _contains "$response" "\"name\" *: *\"$h\","; then
 | 
			
		||||
      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
      _domain=$h
 | 
			
		||||
      _debug "Getting domain id for $h"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								dnsapi/dns_selfhost.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								dnsapi/dns_selfhost.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
#
 | 
			
		||||
#       Author: Marvin Edeler
 | 
			
		||||
#       Report Bugs here: https://github.com/Marvo2011/acme.sh/issues/1
 | 
			
		||||
#	Last Edit: 17.02.2022
 | 
			
		||||
 | 
			
		||||
dns_selfhost_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txt=$2
 | 
			
		||||
  _info "Calling acme-dns on selfhost"
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txt"
 | 
			
		||||
 | 
			
		||||
  SELFHOSTDNS_UPDATE_URL="https://selfhost.de/cgi-bin/api.pl"
 | 
			
		||||
 | 
			
		||||
  # Get values, but don't save until we successfully validated
 | 
			
		||||
  SELFHOSTDNS_USERNAME="${SELFHOSTDNS_USERNAME:-$(_readaccountconf_mutable SELFHOSTDNS_USERNAME)}"
 | 
			
		||||
  SELFHOSTDNS_PASSWORD="${SELFHOSTDNS_PASSWORD:-$(_readaccountconf_mutable SELFHOSTDNS_PASSWORD)}"
 | 
			
		||||
  # These values are domain dependent, so read them from there
 | 
			
		||||
  SELFHOSTDNS_MAP="${SELFHOSTDNS_MAP:-$(_readdomainconf SELFHOSTDNS_MAP)}"
 | 
			
		||||
  # Selfhost api can't dynamically add TXT record,
 | 
			
		||||
  # so we have to store the last used RID of the domain to support a second RID for wildcard domains
 | 
			
		||||
  # (format: 'fulldomainA:lastRid fulldomainB:lastRid ...')
 | 
			
		||||
  SELFHOSTDNS_MAP_LAST_USED_INTERNAL=$(_readdomainconf SELFHOSTDNS_MAP_LAST_USED_INTERNAL)
 | 
			
		||||
 | 
			
		||||
  if [ -z "${SELFHOSTDNS_USERNAME:-}" ] || [ -z "${SELFHOSTDNS_PASSWORD:-}" ]; then
 | 
			
		||||
    _err "SELFHOSTDNS_USERNAME and SELFHOSTDNS_PASSWORD must be set"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # get the domain entry from SELFHOSTDNS_MAP
 | 
			
		||||
  # only match full domains (at the beginning of the string or with a leading whitespace),
 | 
			
		||||
  # e.g. don't match mytest.example.com or sub.test.example.com for test.example.com
 | 
			
		||||
  # if the domain is defined multiple times only the last occurance will be matched
 | 
			
		||||
  mapEntry=$(echo "$SELFHOSTDNS_MAP" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain)(:[[:digit:]]+)([:]?[[:digit:]]*)(.*)/\2\3\4/p")
 | 
			
		||||
  _debug2 mapEntry "$mapEntry"
 | 
			
		||||
  if test -z "$mapEntry"; then
 | 
			
		||||
    _err "SELFHOSTDNS_MAP must contain the fulldomain incl. prefix and at least one RID"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # get the RIDs from the map entry
 | 
			
		||||
  rid1=$(echo "$mapEntry" | cut -d: -f2)
 | 
			
		||||
  rid2=$(echo "$mapEntry" | cut -d: -f3)
 | 
			
		||||
 | 
			
		||||
  # read last used rid domain
 | 
			
		||||
  lastUsedRidForDomainEntry=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain:[[:digit:]]+)(.*)/\2/p")
 | 
			
		||||
  _debug2 lastUsedRidForDomainEntry "$lastUsedRidForDomainEntry"
 | 
			
		||||
  lastUsedRidForDomain=$(echo "$lastUsedRidForDomainEntry" | cut -d: -f2)
 | 
			
		||||
 | 
			
		||||
  rid="$rid1"
 | 
			
		||||
  if [ "$lastUsedRidForDomain" = "$rid" ] && ! test -z "$rid2"; then
 | 
			
		||||
    rid="$rid2"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Trying to add $txt on selfhost for rid: $rid"
 | 
			
		||||
 | 
			
		||||
  data="?username=$SELFHOSTDNS_USERNAME&password=$SELFHOSTDNS_PASSWORD&rid=$rid&content=$txt"
 | 
			
		||||
  response="$(_get "$SELFHOSTDNS_UPDATE_URL$data")"
 | 
			
		||||
 | 
			
		||||
  if ! echo "$response" | grep "200 OK" >/dev/null; then
 | 
			
		||||
    _err "Invalid response of acme-dns for selfhost"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # write last used rid domain
 | 
			
		||||
  newLastUsedRidForDomainEntry="$fulldomain:$rid"
 | 
			
		||||
  if ! test -z "$lastUsedRidForDomainEntry"; then
 | 
			
		||||
    # replace last used rid entry for domain
 | 
			
		||||
    SELFHOSTDNS_MAP_LAST_USED_INTERNAL=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/$lastUsedRidForDomainEntry/$newLastUsedRidForDomainEntry/p")
 | 
			
		||||
  else
 | 
			
		||||
    # add last used rid entry for domain
 | 
			
		||||
    if test -z "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL"; then
 | 
			
		||||
      SELFHOSTDNS_MAP_LAST_USED_INTERNAL="$newLastUsedRidForDomainEntry"
 | 
			
		||||
    else
 | 
			
		||||
      SELFHOSTDNS_MAP_LAST_USED_INTERNAL="$SELFHOSTDNS_MAP_LAST_USED_INTERNAL $newLastUsedRidForDomainEntry"
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  # Now that we know the values are good, save them
 | 
			
		||||
  _saveaccountconf_mutable SELFHOSTDNS_USERNAME "$SELFHOSTDNS_USERNAME"
 | 
			
		||||
  _saveaccountconf_mutable SELFHOSTDNS_PASSWORD "$SELFHOSTDNS_PASSWORD"
 | 
			
		||||
  # These values are domain dependent, so store them there
 | 
			
		||||
  _savedomainconf SELFHOSTDNS_MAP "$SELFHOSTDNS_MAP"
 | 
			
		||||
  _savedomainconf SELFHOSTDNS_MAP_LAST_USED_INTERNAL "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dns_selfhost_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txt=$2
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txt"
 | 
			
		||||
  _info "Creating and removing of records is not supported by selfhost API, will not delete anything."
 | 
			
		||||
}
 | 
			
		||||
@@ -53,7 +53,7 @@ dns_servercow_add() {
 | 
			
		||||
  if printf -- "%s" "$response" | grep "{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\"" >/dev/null; then
 | 
			
		||||
    _info "A txt record with the same name already exists."
 | 
			
		||||
    # trim the string on the left
 | 
			
		||||
    txtvalue_old=${response#*{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\",\"content\":\"}
 | 
			
		||||
    txtvalue_old=${response#*{\"name\":\""$_sub_domain"\",\"ttl\":20,\"type\":\"TXT\",\"content\":\"}
 | 
			
		||||
    # trim the string on the right
 | 
			
		||||
    txtvalue_old=${txtvalue_old%%\"*}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,8 @@
 | 
			
		||||
#SIMPLY_AccountName="accountname"
 | 
			
		||||
#SIMPLY_ApiKey="apikey"
 | 
			
		||||
#
 | 
			
		||||
#SIMPLY_Api="https://api.simply.com/1/[ACCOUNTNAME]/[APIKEY]"
 | 
			
		||||
SIMPLY_Api_Default="https://api.simply.com/1"
 | 
			
		||||
#SIMPLY_Api="https://api.simply.com/2/"
 | 
			
		||||
SIMPLY_Api_Default="https://api.simply.com/2"
 | 
			
		||||
 | 
			
		||||
#This is used for determining success of REST call
 | 
			
		||||
SIMPLY_SUCCESS_CODE='"status":200'
 | 
			
		||||
@@ -237,12 +237,18 @@ _simply_rest() {
 | 
			
		||||
  _debug2 ep "$ep"
 | 
			
		||||
  _debug2 m "$m"
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  basicauth=$(printf "%s:%s" "$SIMPLY_AccountName" "$SIMPLY_ApiKey" | _base64)
 | 
			
		||||
 | 
			
		||||
  if [ "$basicauth" ]; then
 | 
			
		||||
    export _H1="Authorization: Basic $basicauth"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  export _H2="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    response="$(_post "$data" "$SIMPLY_Api/$SIMPLY_AccountName/$SIMPLY_ApiKey/$ep" "" "$m")"
 | 
			
		||||
    response="$(_post "$data" "$SIMPLY_Api/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$SIMPLY_Api/$SIMPLY_AccountName/$SIMPLY_ApiKey/$ep")"
 | 
			
		||||
    response="$(_get "$SIMPLY_Api/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
TRANSIP_Api_Url="https://api.transip.nl/v6"
 | 
			
		||||
TRANSIP_Token_Read_Only="false"
 | 
			
		||||
TRANSIP_Token_Global_Key="false"
 | 
			
		||||
TRANSIP_Token_Expiration="30 minutes"
 | 
			
		||||
# You can't reuse a label token, so we leave this empty normally
 | 
			
		||||
TRANSIP_Token_Label=""
 | 
			
		||||
@@ -96,7 +95,11 @@ _transip_get_token() {
 | 
			
		||||
  nonce=$(echo "TRANSIP$(_time)" | _digest sha1 hex | cut -c 1-32)
 | 
			
		||||
  _debug nonce "$nonce"
 | 
			
		||||
 | 
			
		||||
  data="{\"login\":\"${TRANSIP_Username}\",\"nonce\":\"${nonce}\",\"read_only\":\"${TRANSIP_Token_Read_Only}\",\"expiration_time\":\"${TRANSIP_Token_Expiration}\",\"label\":\"${TRANSIP_Token_Label}\",\"global_key\":\"${TRANSIP_Token_Global_Key}\"}"
 | 
			
		||||
  # make IP whitelisting configurable
 | 
			
		||||
  TRANSIP_Token_Global_Key="${TRANSIP_Token_Global_Key:-$(_readaccountconf_mutable TRANSIP_Token_Global_Key)}"
 | 
			
		||||
  _saveaccountconf_mutable TRANSIP_Token_Global_Key "$TRANSIP_Token_Global_Key"
 | 
			
		||||
 | 
			
		||||
  data="{\"login\":\"${TRANSIP_Username}\",\"nonce\":\"${nonce}\",\"read_only\":\"${TRANSIP_Token_Read_Only}\",\"expiration_time\":\"${TRANSIP_Token_Expiration}\",\"label\":\"${TRANSIP_Token_Label}\",\"global_key\":\"${TRANSIP_Token_Global_Key:-false}\"}"
 | 
			
		||||
  _debug data "$data"
 | 
			
		||||
 | 
			
		||||
  #_signature=$(printf "%s" "$data" | openssl dgst -sha512 -sign "$TRANSIP_Key_File" | _base64)
 | 
			
		||||
@@ -139,6 +142,18 @@ _transip_setup() {
 | 
			
		||||
  _saveaccountconf_mutable TRANSIP_Username "$TRANSIP_Username"
 | 
			
		||||
  _saveaccountconf_mutable TRANSIP_Key_File "$TRANSIP_Key_File"
 | 
			
		||||
 | 
			
		||||
  # download key file if it's an URL
 | 
			
		||||
  if _startswith "$TRANSIP_Key_File" "http"; then
 | 
			
		||||
    _debug "download transip key file"
 | 
			
		||||
    TRANSIP_Key_URL=$TRANSIP_Key_File
 | 
			
		||||
    TRANSIP_Key_File="$(_mktemp)"
 | 
			
		||||
    chmod 600 "$TRANSIP_Key_File"
 | 
			
		||||
    if ! _get "$TRANSIP_Key_URL" >"$TRANSIP_Key_File"; then
 | 
			
		||||
      _err "Error getting key file from : $TRANSIP_Key_URL"
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -f "$TRANSIP_Key_File" ]; then
 | 
			
		||||
    if ! grep "BEGIN PRIVATE KEY" "$TRANSIP_Key_File" >/dev/null 2>&1; then
 | 
			
		||||
      _err "Key file doesn't seem to be a valid key: ${TRANSIP_Key_File}"
 | 
			
		||||
@@ -156,6 +171,12 @@ _transip_setup() {
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ -n "${TRANSIP_Key_URL}" ]; then
 | 
			
		||||
    _debug "delete transip key file"
 | 
			
		||||
    rm "${TRANSIP_Key_File}"
 | 
			
		||||
    TRANSIP_Key_File=$TRANSIP_Key_URL
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _get_root "$fulldomain" || return 1
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										160
									
								
								dnsapi/dns_udr.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								dnsapi/dns_udr.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# united-domains Reselling (https://www.ud-reselling.com/) DNS API
 | 
			
		||||
# Author: Andreas Scherer (https://github.com/andischerer)
 | 
			
		||||
# Created: 2021-02-01
 | 
			
		||||
#
 | 
			
		||||
# Set the environment variables as below:
 | 
			
		||||
#
 | 
			
		||||
#    export UDR_USER="your_username_goes_here"
 | 
			
		||||
#    export UDR_PASS="some_password_goes_here"
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
UDR_API="https://api.domainreselling.de/api/call.cgi"
 | 
			
		||||
UDR_TTL="30"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt"
 | 
			
		||||
dns_udr_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}"
 | 
			
		||||
  UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}"
 | 
			
		||||
  if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then
 | 
			
		||||
    UDR_USER=""
 | 
			
		||||
    UDR_PASS=""
 | 
			
		||||
    _err "You didn't specify an UD-Reselling username and password yet"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  # save the username and password to the account conf file.
 | 
			
		||||
  _saveaccountconf_mutable UDR_USER "$UDR_USER"
 | 
			
		||||
  _saveaccountconf_mutable UDR_PASS "$UDR_PASS"
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _dnszone "${_dnszone}"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}"
 | 
			
		||||
  _debug resource_record "${rr}"
 | 
			
		||||
  if _contains "$response" "$rr" >/dev/null; then
 | 
			
		||||
    _err "Error, it would appear that this record already exists. Please review existing TXT records for this domain."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&addrr0=${rr}"; then
 | 
			
		||||
    _err "Adding the record did not succeed, please verify/check."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Added, OK"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dns_udr_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}"
 | 
			
		||||
  UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}"
 | 
			
		||||
  if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then
 | 
			
		||||
    UDR_USER=""
 | 
			
		||||
    UDR_PASS=""
 | 
			
		||||
    _err "You didn't specify an UD-Reselling username and password yet"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _dnszone "${_dnszone}"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}"
 | 
			
		||||
  _debug resource_record "${rr}"
 | 
			
		||||
  if _contains "$response" "$rr" >/dev/null; then
 | 
			
		||||
    if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&delrr0=${rr}"; then
 | 
			
		||||
      _err "Deleting the record did not succeed, please verify/check."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    _info "Removed, OK"
 | 
			
		||||
    return 0
 | 
			
		||||
  else
 | 
			
		||||
    _info "Text record is not present, will not delete anything."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
 | 
			
		||||
  if ! _udr_rest "QueryDNSZoneList" ""; 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
 | 
			
		||||
      _dnszone=$(echo "$response" | _egrep_o "${h}")
 | 
			
		||||
      if [ "$_dnszone" ]; then
 | 
			
		||||
        return 0
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    i=$(_math "$i" + 1)
 | 
			
		||||
  done
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_udr_rest() {
 | 
			
		||||
  if [ -n "$2" ]; then
 | 
			
		||||
    data="command=$1&$2"
 | 
			
		||||
  else
 | 
			
		||||
    data="command=$1"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug data "${data}"
 | 
			
		||||
  response="$(_post "${data}" "${UDR_API}?s_login=${UDR_USER}&s_pw=${UDR_PASS}" "" "POST")"
 | 
			
		||||
 | 
			
		||||
  _code=$(echo "$response" | _egrep_o "code = ([0-9]+)" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
 | 
			
		||||
  _description=$(echo "$response" | _egrep_o "description = .*" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
 | 
			
		||||
 | 
			
		||||
  _debug response_code "$_code"
 | 
			
		||||
  _debug response_description "$_description"
 | 
			
		||||
 | 
			
		||||
  if [ ! "$_code" = "200" ]; then
 | 
			
		||||
    _err "DNS-API-Error: $_description"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,8 @@
 | 
			
		||||
#
 | 
			
		||||
# ULTRA_PWD="some_password_goes_here"
 | 
			
		||||
 | 
			
		||||
ULTRA_API="https://restapi.ultradns.com/v2/"
 | 
			
		||||
ULTRA_API="https://api.ultradns.com/v3/"
 | 
			
		||||
ULTRA_AUTH_API="https://api.ultradns.com/v2/"
 | 
			
		||||
 | 
			
		||||
#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt"
 | 
			
		||||
dns_ultra_add() {
 | 
			
		||||
@@ -121,7 +122,7 @@ _get_root() {
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if _contains "${response}" "${h}." >/dev/null; then
 | 
			
		||||
      _domain_id=$(echo "$response" | _egrep_o "${h}")
 | 
			
		||||
      _domain_id=$(echo "$response" | _egrep_o "${h}" | head -1)
 | 
			
		||||
      if [ "$_domain_id" ]; then
 | 
			
		||||
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
 | 
			
		||||
        _domain="${h}"
 | 
			
		||||
@@ -142,23 +143,25 @@ _ultra_rest() {
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
  _debug TOKEN "${AUTH_TOKEN}"
 | 
			
		||||
  if [ -z "$AUTH_TOKEN" ]; then
 | 
			
		||||
    _ultra_login
 | 
			
		||||
  fi
 | 
			
		||||
  _debug TOKEN "$AUTH_TOKEN"
 | 
			
		||||
 | 
			
		||||
  _ultra_login
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  export _H2="Authorization: Bearer ${AUTH_TOKEN}"
 | 
			
		||||
  export _H2="Authorization: Bearer $AUTH_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "${data}"
 | 
			
		||||
    response="$(_post "${data}" "${ULTRA_API}"/"${ep}" "" "${m}")"
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$ULTRA_API$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$ULTRA_API/$ep")"
 | 
			
		||||
    response="$(_get "$ULTRA_API$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_ultra_login() {
 | 
			
		||||
  export _H1=""
 | 
			
		||||
  export _H2=""
 | 
			
		||||
  AUTH_TOKEN=$(_post "grant_type=password&username=${ULTRA_USR}&password=${ULTRA_PWD}" "${ULTRA_API}authorization/token" | cut -d, -f3 | cut -d\" -f4)
 | 
			
		||||
  AUTH_TOKEN=$(_post "grant_type=password&username=${ULTRA_USR}&password=${ULTRA_PWD}" "${ULTRA_AUTH_API}authorization/token" | cut -d, -f3 | cut -d\" -f4)
 | 
			
		||||
  export AUTH_TOKEN
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										142
									
								
								dnsapi/dns_vercel.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								dnsapi/dns_vercel.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
# Vercel DNS API
 | 
			
		||||
#
 | 
			
		||||
# This is your API token which can be acquired on the account page.
 | 
			
		||||
# https://vercel.com/account/tokens
 | 
			
		||||
#
 | 
			
		||||
# VERCEL_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
 | 
			
		||||
 | 
			
		||||
VERCEL_API="https://api.vercel.com"
 | 
			
		||||
 | 
			
		||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_vercel_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
  _debug fulldomain "$fulldomain"
 | 
			
		||||
  _debug txtvalue "$txtvalue"
 | 
			
		||||
 | 
			
		||||
  VERCEL_TOKEN="${VERCEL_TOKEN:-$(_readaccountconf_mutable VERCEL_TOKEN)}"
 | 
			
		||||
 | 
			
		||||
  if [ -z "$VERCEL_TOKEN" ]; then
 | 
			
		||||
    VERCEL_TOKEN=""
 | 
			
		||||
    _err "You have not set the Vercel API token yet."
 | 
			
		||||
    _err "Please visit https://vercel.com/account/tokens to generate it."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _saveaccountconf_mutable VERCEL_TOKEN "$VERCEL_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _vercel_rest POST "v2/domains/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}"; then
 | 
			
		||||
    if printf -- "%s" "$response" | grep "\"uid\":\"" >/dev/null; then
 | 
			
		||||
      _info "Added"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Unexpected response while adding text record."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dns_vercel_rm() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _vercel_rest GET "v2/domains/$_domain/records"
 | 
			
		||||
 | 
			
		||||
  count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
 | 
			
		||||
 | 
			
		||||
  if [ "$count" = "0" ]; then
 | 
			
		||||
    _info "Don't need to remove."
 | 
			
		||||
  else
 | 
			
		||||
    _record_id=$(printf "%s" "$response" | _egrep_o "\"id\":[^,]*,\"slug\":\"[^,]*\",\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\",\"value\":\"$txtvalue\"" | cut -d: -f2 | cut -d, -f1 | tr -d '"')
 | 
			
		||||
 | 
			
		||||
    if [ "$_record_id" ]; then
 | 
			
		||||
      echo "$_record_id" | while read -r item; do
 | 
			
		||||
        if _vercel_rest DELETE "v2/domains/$_domain/records/$item"; then
 | 
			
		||||
          _info "removed record" "$item"
 | 
			
		||||
          return 0
 | 
			
		||||
        else
 | 
			
		||||
          _err "failed to remove record" "$item"
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
      done
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain="$1"
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
    if ! _vercel_rest GET "v4/domains/$h"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if _contains "$response" "\"name\":\"$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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_vercel_rest() {
 | 
			
		||||
  m="$1"
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
 | 
			
		||||
  path="$VERCEL_API/$ep"
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  export _H2="Authorization: Bearer $VERCEL_TOKEN"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _secure_debug2 data "$data"
 | 
			
		||||
    response="$(_post "$data" "$path" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$path")"
 | 
			
		||||
  fi
 | 
			
		||||
  _ret="$?"
 | 
			
		||||
  _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
 | 
			
		||||
  _debug "http response code $_code"
 | 
			
		||||
  _secure_debug2 response "$response"
 | 
			
		||||
  if [ "$_ret" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  response="$(printf "%s" "$response" | _normalizeJson)"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -3,10 +3,10 @@
 | 
			
		||||
#
 | 
			
		||||
#VULTR_API_KEY=000011112222333344445555666677778888
 | 
			
		||||
 | 
			
		||||
VULTR_Api="https://api.vultr.com/v1"
 | 
			
		||||
VULTR_Api="https://api.vultr.com/v2"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_vultr_add() {
 | 
			
		||||
  fulldomain=$1
 | 
			
		||||
@@ -31,14 +31,14 @@ dns_vultr_add() {
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug 'Getting txt records'
 | 
			
		||||
  _vultr_rest GET "dns/records?domain=$_domain"
 | 
			
		||||
  _vultr_rest GET "domains/$_domain/records"
 | 
			
		||||
 | 
			
		||||
  if printf "%s\n" "$response" | grep -- "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then
 | 
			
		||||
    _err 'Error'
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _vultr_rest POST 'dns/create_record' "domain=$_domain&name=$_sub_domain&data=\"$txtvalue\"&type=TXT"; then
 | 
			
		||||
  if ! _vultr_rest POST "domains/$_domain/records" "{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\",\"type\":\"TXT\"}"; then
 | 
			
		||||
    _err "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
@@ -71,14 +71,14 @@ dns_vultr_rm() {
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug 'Getting txt records'
 | 
			
		||||
  _vultr_rest GET "dns/records?domain=$_domain"
 | 
			
		||||
  _vultr_rest GET "domains/$_domain/records"
 | 
			
		||||
 | 
			
		||||
  if printf "%s\n" "$response" | grep -- "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then
 | 
			
		||||
    _err 'Error'
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep -- "$txtvalue" | tr ',' '\n' | grep -i 'RECORDID' | cut -d : -f 2)"
 | 
			
		||||
  _record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep -- "$txtvalue" | tr ',' '\n' | grep -i 'id' | cut -d : -f 2 | tr -d '"')"
 | 
			
		||||
  _debug _record_id "$_record_id"
 | 
			
		||||
  if [ "$_record_id" ]; then
 | 
			
		||||
    _info "Successfully retrieved the record id for ACME challenge."
 | 
			
		||||
@@ -87,7 +87,7 @@ dns_vultr_rm() {
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if ! _vultr_rest POST 'dns/delete_record' "domain=$_domain&RECORDID=$_record_id"; then
 | 
			
		||||
  if ! _vultr_rest DELETE "domains/$_domain/records/$_record_id"; then
 | 
			
		||||
    _err "$response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
@@ -112,11 +112,11 @@ _get_root() {
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! _vultr_rest GET "dns/list"; then
 | 
			
		||||
    if ! _vultr_rest GET "domains"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if printf "%s\n" "$response" | grep '^\[.*\]' >/dev/null; then
 | 
			
		||||
    if printf "%s\n" "$response" | grep -E '^\{.*\}' >/dev/null; then
 | 
			
		||||
      if _contains "$response" "\"domain\":\"$_domain\""; then
 | 
			
		||||
        _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
 | 
			
		||||
        return 0
 | 
			
		||||
@@ -139,10 +139,10 @@ _vultr_rest() {
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  api_key_trimmed=$(echo $VULTR_API_KEY | tr -d '"')
 | 
			
		||||
  api_key_trimmed=$(echo "$VULTR_API_KEY" | tr -d '"')
 | 
			
		||||
 | 
			
		||||
  export _H1="Api-Key: $api_key_trimmed"
 | 
			
		||||
  export _H2='Content-Type: application/x-www-form-urlencoded'
 | 
			
		||||
  export _H1="Authorization: Bearer $api_key_trimmed"
 | 
			
		||||
  export _H2='Content-Type: application/json'
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ RECORD=''
 | 
			
		||||
 | 
			
		||||
# Usage: dns_world4you_add <fqdn> <value>
 | 
			
		||||
dns_world4you_add() {
 | 
			
		||||
  fqdn="$1"
 | 
			
		||||
  fqdn=$(echo "$1" | _lower_case)
 | 
			
		||||
  value="$2"
 | 
			
		||||
  _info "Using world4you to add record"
 | 
			
		||||
  _debug fulldomain "$fqdn"
 | 
			
		||||
@@ -24,7 +24,7 @@ dns_world4you_add() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  export _H1="Cookie: W4YSESSID=$sessid"
 | 
			
		||||
  form=$(_get "$WORLD4YOU_API/dashboard/paketuebersicht")
 | 
			
		||||
  form=$(_get "$WORLD4YOU_API/")
 | 
			
		||||
  _get_paketnr "$fqdn" "$form"
 | 
			
		||||
  paketnr="$PAKETNR"
 | 
			
		||||
  if [ -z "$paketnr" ]; then
 | 
			
		||||
@@ -49,33 +49,32 @@ dns_world4you_add() {
 | 
			
		||||
  ret=$(_post "$body" "$WORLD4YOU_API/$paketnr/dns" '' POST 'application/x-www-form-urlencoded')
 | 
			
		||||
  _resethttp
 | 
			
		||||
 | 
			
		||||
  if _contains "$(_head_n 3 <"$HTTP_HEADER")" '302'; then
 | 
			
		||||
  if _contains "$(_head_n 1 <"$HTTP_HEADER")" '302'; then
 | 
			
		||||
    res=$(_get "$WORLD4YOU_API/$paketnr/dns")
 | 
			
		||||
    if _contains "$res" "successfully"; then
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      msg=$(echo "$res" | tr '\n' '\t' | sed 's/.*<h3 class="mb-5">[^\t]*\t *\([^\t]*\)\t.*/\1/')
 | 
			
		||||
      if _contains "$msg" '^<\!DOCTYPE html>'; then
 | 
			
		||||
        msg='Unknown error'
 | 
			
		||||
      fi
 | 
			
		||||
      _err "Unable to add record: $msg"
 | 
			
		||||
      if _contains "$msg" '^<\!DOCTYPE html>'; then
 | 
			
		||||
      msg=$(echo "$res" | grep -A 15 'data-type="danger"' | grep "<h3[^>]*>[^<]" | sed 's/<[^>]*>//g' | sed 's/^\s*//g')
 | 
			
		||||
      if [ "$msg" = '' ]; then
 | 
			
		||||
        _err "Unable to add record: Unknown error"
 | 
			
		||||
        echo "$ret" >'error-01.html'
 | 
			
		||||
        echo "$res" >'error-02.html'
 | 
			
		||||
        _err "View error-01.html and error-02.html for debugging"
 | 
			
		||||
      else
 | 
			
		||||
        _err "Unable to add record: my.world4you.com: $msg"
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _err "$(_head_n 3 <"$HTTP_HEADER")"
 | 
			
		||||
    _err "View $HTTP_HEADER for debugging"
 | 
			
		||||
    msg=$(echo "$ret" | grep '"form-error-message"' | sed 's/^.*<div class="form-error-message">\([^<]*\)<\/div>.*$/\1/')
 | 
			
		||||
    _err "Unable to add record: my.world4you.com: $msg"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: dns_world4you_rm <fqdn> <value>
 | 
			
		||||
dns_world4you_rm() {
 | 
			
		||||
  fqdn="$1"
 | 
			
		||||
  fqdn=$(echo "$1" | _lower_case)
 | 
			
		||||
  value="$2"
 | 
			
		||||
  _info "Using world4you to remove record"
 | 
			
		||||
  _debug fulldomain "$fqdn"
 | 
			
		||||
@@ -87,7 +86,7 @@ dns_world4you_rm() {
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  export _H1="Cookie: W4YSESSID=$sessid"
 | 
			
		||||
  form=$(_get "$WORLD4YOU_API/dashboard/paketuebersicht")
 | 
			
		||||
  form=$(_get "$WORLD4YOU_API/")
 | 
			
		||||
  _get_paketnr "$fqdn" "$form"
 | 
			
		||||
  paketnr="$PAKETNR"
 | 
			
		||||
  if [ -z "$paketnr" ]; then
 | 
			
		||||
@@ -114,26 +113,25 @@ dns_world4you_rm() {
 | 
			
		||||
  ret=$(_post "$body" "$WORLD4YOU_API/$paketnr/dns/record/delete" '' POST 'application/x-www-form-urlencoded')
 | 
			
		||||
  _resethttp
 | 
			
		||||
 | 
			
		||||
  if _contains "$(_head_n 3 <"$HTTP_HEADER")" '302'; then
 | 
			
		||||
  if _contains "$(_head_n 1 <"$HTTP_HEADER")" '302'; then
 | 
			
		||||
    res=$(_get "$WORLD4YOU_API/$paketnr/dns")
 | 
			
		||||
    if _contains "$res" "successfully"; then
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      msg=$(echo "$res" | tr '\n' '\t' | sed 's/.*<h3 class="mb-5">[^\t]*\t *\([^\t]*\)\t.*/\1/')
 | 
			
		||||
      if _contains "$msg" '^<\!DOCTYPE html>'; then
 | 
			
		||||
        msg='Unknown error'
 | 
			
		||||
      fi
 | 
			
		||||
      _err "Unable to remove record: $msg"
 | 
			
		||||
      if _contains "$msg" '^<\!DOCTYPE html>'; then
 | 
			
		||||
      msg=$(echo "$res" | grep -A 15 'data-type="danger"' | grep "<h3[^>]*>[^<]" | sed 's/<[^>]*>//g' | sed 's/^\s*//g')
 | 
			
		||||
      if [ "$msg" = '' ]; then
 | 
			
		||||
        _err "Unable to remove record: Unknown error"
 | 
			
		||||
        echo "$ret" >'error-01.html'
 | 
			
		||||
        echo "$res" >'error-02.html'
 | 
			
		||||
        _err "View error-01.html and error-02.html for debugging"
 | 
			
		||||
      else
 | 
			
		||||
        _err "Unable to remove record: my.world4you.com: $msg"
 | 
			
		||||
      fi
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _err "$(_head_n 3 <"$HTTP_HEADER")"
 | 
			
		||||
    _err "View $HTTP_HEADER for debugging"
 | 
			
		||||
    msg=$(echo "$ret" | grep "form-error-message" | sed 's/^.*<div class="form-error-message">\([^<]*\)<\/div>.*$/\1/')
 | 
			
		||||
    _err "Unable to remove record: my.world4you.com: $msg"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
@@ -157,34 +155,47 @@ _login() {
 | 
			
		||||
  _saveaccountconf_mutable WORLD4YOU_USERNAME "$WORLD4YOU_USERNAME"
 | 
			
		||||
  _saveaccountconf_mutable WORLD4YOU_PASSWORD "$WORLD4YOU_PASSWORD"
 | 
			
		||||
 | 
			
		||||
  _resethttp
 | 
			
		||||
  export ACME_HTTP_NO_REDIRECTS=1
 | 
			
		||||
  page=$(_get "$WORLD4YOU_API/login")
 | 
			
		||||
  _resethttp
 | 
			
		||||
 | 
			
		||||
  if _contains "$(_head_n 1 <"$HTTP_HEADER")" '302'; then
 | 
			
		||||
    _info "Already logged in"
 | 
			
		||||
    _parse_sessid
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Logging in..."
 | 
			
		||||
 | 
			
		||||
  username="$WORLD4YOU_USERNAME"
 | 
			
		||||
  password="$WORLD4YOU_PASSWORD"
 | 
			
		||||
  csrf_token=$(_get "$WORLD4YOU_API/login" | grep '_csrf_token' | sed 's/^.*<input[^>]*value=\"\([^"]*\)\".*$/\1/')
 | 
			
		||||
  sessid=$(grep 'W4YSESSID' <"$HTTP_HEADER" | sed 's/^.*W4YSESSID=\([^;]*\);.*$/\1/')
 | 
			
		||||
  csrf_token=$(echo "$page" | grep '_csrf_token' | sed 's/^.*<input[^>]*value=\"\([^"]*\)\".*$/\1/')
 | 
			
		||||
  _parse_sessid
 | 
			
		||||
 | 
			
		||||
  export _H1="Cookie: W4YSESSID=$sessid"
 | 
			
		||||
  export _H2="X-Requested-With: XMLHttpRequest"
 | 
			
		||||
  body="_username=$username&_password=$password&_csrf_token=$csrf_token"
 | 
			
		||||
  ret=$(_post "$body" "$WORLD4YOU_API/login" '' POST 'application/x-www-form-urlencoded')
 | 
			
		||||
  unset _H2
 | 
			
		||||
 | 
			
		||||
  _debug ret "$ret"
 | 
			
		||||
  if _contains "$ret" "\"success\":true"; then
 | 
			
		||||
    _info "Successfully logged in"
 | 
			
		||||
    sessid=$(grep 'W4YSESSID' <"$HTTP_HEADER" | sed 's/^.*W4YSESSID=\([^;]*\);.*$/\1/')
 | 
			
		||||
    _parse_sessid
 | 
			
		||||
  else
 | 
			
		||||
    _err "Unable to log in: $(echo "$ret" | sed 's/^.*"message":"\([^\"]*\)".*$/\1/')"
 | 
			
		||||
    msg=$(echo "$ret" | sed 's/^.*"message":"\([^\"]*\)".*$/\1/')
 | 
			
		||||
    _err "Unable to log in: my.world4you.com: $msg"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage _get_paketnr <fqdn> <form>
 | 
			
		||||
# Usage: _get_paketnr <fqdn> <form>
 | 
			
		||||
_get_paketnr() {
 | 
			
		||||
  fqdn="$1"
 | 
			
		||||
  form="$2"
 | 
			
		||||
 | 
			
		||||
  domains=$(echo "$form" | grep '^ *[A-Za-z0-9_\.-]*\.[A-Za-z0-9_-]*$' | sed 's/^ *\(.*\)$/\1/')
 | 
			
		||||
  domains=$(echo "$form" | grep '<ul class="nav header-paket-list">' | sed 's/<li/\n<li/g' | sed 's/<[^>]*>/ /g' | sed 's/^.*>\([^>]*\)$/\1/')
 | 
			
		||||
  domain=''
 | 
			
		||||
  for domain in $domains; do
 | 
			
		||||
    if _contains "$fqdn" "$domain\$"; then
 | 
			
		||||
@@ -199,6 +210,11 @@ _get_paketnr() {
 | 
			
		||||
  TLD="$domain"
 | 
			
		||||
  _debug domain "$domain"
 | 
			
		||||
  RECORD=$(echo "$fqdn" | cut -c"1-$((${#fqdn} - ${#TLD} - 1))")
 | 
			
		||||
  PAKETNR=$(echo "$form" | grep "data-textfilter=\".* $domain " | _head_n 1 | sed 's/^.* \([0-9]*\) .*$/\1/')
 | 
			
		||||
  PAKETNR=$(echo "$domains" | grep "$domain" | sed 's/^[^,]*, *\([0-9]*\).*$/\1/')
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Usage: _parse_sessid
 | 
			
		||||
_parse_sessid() {
 | 
			
		||||
  sessid=$(grep 'W4YSESSID' <"$HTTP_HEADER" | _tail_n 1 | sed 's/^.*W4YSESSID=\([^;]*\);.*$/\1/')
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										264
									
								
								dnsapi/dns_yc.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								dnsapi/dns_yc.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#YC_Zone_ID="" # DNS Zone ID
 | 
			
		||||
#YC_Folder_ID="" # YC Folder ID
 | 
			
		||||
#YC_SA_ID="" # Service Account ID
 | 
			
		||||
#YC_SA_Key_ID="" # Service Account IAM Key ID
 | 
			
		||||
#YC_SA_Key_File_Path="/path/to/private.key" # Path to private.key use instead of YC_SA_Key_File_PEM_b64
 | 
			
		||||
#YC_SA_Key_File_PEM_b64="" # Base64 content of private.key use instead of YC_SA_Key_File_Path
 | 
			
		||||
YC_Api="https://dns.api.cloud.yandex.net/dns/v1"
 | 
			
		||||
 | 
			
		||||
########  Public functions #####################
 | 
			
		||||
 | 
			
		||||
#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
 | 
			
		||||
dns_yc_add() {
 | 
			
		||||
  fulldomain="$(echo "$1". | _lower_case)" # Add dot at end of domain name
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  YC_SA_Key_File_PEM_b64="${YC_SA_Key_File_PEM_b64:-$(_readaccountconf_mutable YC_SA_Key_File_PEM_b64)}"
 | 
			
		||||
  YC_SA_Key_File_Path="${YC_SA_Key_File_Path:-$(_readaccountconf_mutable YC_SA_Key_File_Path)}"
 | 
			
		||||
 | 
			
		||||
  if [ "$YC_SA_Key_File_PEM_b64" ]; then
 | 
			
		||||
    echo "$YC_SA_Key_File_PEM_b64" | _dbase64 >private.key
 | 
			
		||||
    YC_SA_Key_File="private.key"
 | 
			
		||||
    _savedomainconf YC_SA_Key_File_PEM_b64 "$YC_SA_Key_File_PEM_b64"
 | 
			
		||||
  else
 | 
			
		||||
    YC_SA_Key_File="$YC_SA_Key_File_Path"
 | 
			
		||||
    _savedomainconf YC_SA_Key_File_Path "$YC_SA_Key_File_Path"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  YC_Zone_ID="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
 | 
			
		||||
  YC_Folder_ID="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
 | 
			
		||||
  YC_SA_ID="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
 | 
			
		||||
  YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
 | 
			
		||||
 | 
			
		||||
  if [ "$YC_SA_ID" ] && [ "$YC_SA_Key_ID" ] && [ "$YC_SA_Key_File" ]; then
 | 
			
		||||
    if [ -f "$YC_SA_Key_File" ]; then
 | 
			
		||||
      if _isRSA "$YC_SA_Key_File" >/dev/null 2>&1; then
 | 
			
		||||
        if [ "$YC_Zone_ID" ]; then
 | 
			
		||||
          _savedomainconf YC_Zone_ID "$YC_Zone_ID"
 | 
			
		||||
          _savedomainconf YC_SA_ID "$YC_SA_ID"
 | 
			
		||||
          _savedomainconf YC_SA_Key_ID "$YC_SA_Key_ID"
 | 
			
		||||
        elif [ "$YC_Folder_ID" ]; then
 | 
			
		||||
          _savedomainconf YC_Folder_ID "$YC_Folder_ID"
 | 
			
		||||
          _saveaccountconf_mutable YC_SA_ID "$YC_SA_ID"
 | 
			
		||||
          _saveaccountconf_mutable YC_SA_Key_ID "$YC_SA_Key_ID"
 | 
			
		||||
          _clearaccountconf_mutable YC_Zone_ID
 | 
			
		||||
          _clearaccountconf YC_Zone_ID
 | 
			
		||||
        else
 | 
			
		||||
          _err "You didn't specify a Yandex Cloud Zone ID or Folder ID yet."
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
      else
 | 
			
		||||
        _err "YC_SA_Key_File not a RSA file(_isRSA function return false)."
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      _err "YC_SA_Key_File not found in path $YC_SA_Key_File."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  else
 | 
			
		||||
    _clearaccountconf YC_Zone_ID
 | 
			
		||||
    _clearaccountconf YC_Folder_ID
 | 
			
		||||
    _clearaccountconf YC_SA_ID
 | 
			
		||||
    _clearaccountconf YC_SA_Key_ID
 | 
			
		||||
    _clearaccountconf YC_SA_Key_File_PEM_b64
 | 
			
		||||
    _clearaccountconf YC_SA_Key_File_Path
 | 
			
		||||
    _err "You didn't specify a YC_SA_ID or YC_SA_Key_ID or YC_SA_Key_File."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _debug "First detect the root zone"
 | 
			
		||||
  if ! _get_root "$fulldomain"; then
 | 
			
		||||
    _err "invalid domain"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug _domain_id "$_domain_id"
 | 
			
		||||
  _debug _sub_domain "$_sub_domain"
 | 
			
		||||
  _debug _domain "$_domain"
 | 
			
		||||
 | 
			
		||||
  _debug "Getting txt records"
 | 
			
		||||
  if ! _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
 | 
			
		||||
    _err "Error: $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _info "Adding record"
 | 
			
		||||
  if _yc_rest POST "zones/$_domain_id:upsertRecordSets" "{\"merges\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
 | 
			
		||||
    if _contains "$response" "\"done\": true"; then
 | 
			
		||||
      _info "Added, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Add txt record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Add txt record error."
 | 
			
		||||
  return 1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#fulldomain txtvalue
 | 
			
		||||
dns_yc_rm() {
 | 
			
		||||
  fulldomain="$(echo "$1". | _lower_case)" # Add dot at end of domain name
 | 
			
		||||
  txtvalue=$2
 | 
			
		||||
 | 
			
		||||
  YC_Zone_ID="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
 | 
			
		||||
  YC_Folder_ID="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
 | 
			
		||||
  YC_SA_ID="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
 | 
			
		||||
  YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
 | 
			
		||||
 | 
			
		||||
  _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"
 | 
			
		||||
  if _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
 | 
			
		||||
    exists_txtvalue=$(echo "$response" | _normalizeJson | _egrep_o "\"data\".*\][^,]*" | _egrep_o "[^:]*$")
 | 
			
		||||
    _debug exists_txtvalue "$exists_txtvalue"
 | 
			
		||||
  else
 | 
			
		||||
    _err "Error: $response"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if _yc_rest POST "zones/$_domain_id:updateRecordSets" "{\"deletions\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":$exists_txtvalue}]}"; then
 | 
			
		||||
    if _contains "$response" "\"done\": true"; then
 | 
			
		||||
      _info "Delete, OK"
 | 
			
		||||
      return 0
 | 
			
		||||
    else
 | 
			
		||||
      _err "Delete record error."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "Delete record error."
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
####################  Private functions below ##################################
 | 
			
		||||
#_acme-challenge.www.domain.com
 | 
			
		||||
#returns
 | 
			
		||||
# _sub_domain=_acme-challenge.www
 | 
			
		||||
# _domain=domain.com
 | 
			
		||||
# _domain_id=sdjkglgdfewsdfg
 | 
			
		||||
_get_root() {
 | 
			
		||||
  domain=$1
 | 
			
		||||
  i=1
 | 
			
		||||
  p=1
 | 
			
		||||
 | 
			
		||||
  # Use Zone ID directly if provided
 | 
			
		||||
  if [ "$YC_Zone_ID" ]; then
 | 
			
		||||
    if ! _yc_rest GET "zones/$YC_Zone_ID"; then
 | 
			
		||||
      return 1
 | 
			
		||||
    else
 | 
			
		||||
      if echo "$response" | tr -d " " | _egrep_o "\"id\":\"$YC_Zone_ID\"" >/dev/null; then
 | 
			
		||||
        _domain=$(echo "$response" | _egrep_o "\"zone\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
 | 
			
		||||
        if [ "$_domain" ]; then
 | 
			
		||||
          _cutlength=$((${#domain} - ${#_domain}))
 | 
			
		||||
          _sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
 | 
			
		||||
          _domain_id=$YC_Zone_ID
 | 
			
		||||
          return 0
 | 
			
		||||
        else
 | 
			
		||||
          return 1
 | 
			
		||||
        fi
 | 
			
		||||
      else
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    fi
 | 
			
		||||
  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 [ "$YC_Folder_ID" ]; then
 | 
			
		||||
      if ! _yc_rest GET "zones?folderId=$YC_Folder_ID"; then
 | 
			
		||||
        return 1
 | 
			
		||||
      fi
 | 
			
		||||
    else
 | 
			
		||||
      echo "You didn't specify a Yandex Cloud Folder ID."
 | 
			
		||||
      return 1
 | 
			
		||||
    fi
 | 
			
		||||
    if _contains "$response" "\"zone\": \"$h\""; then
 | 
			
		||||
      _domain_id=$(echo "$response" | _normalizeJson | _egrep_o "[^{]*\"zone\":\"$h\"[^}]*" | _egrep_o "\"id\"[^,]*" | _egrep_o "[^:]*$" | 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_yc_rest() {
 | 
			
		||||
  m=$1
 | 
			
		||||
  ep="$2"
 | 
			
		||||
  data="$3"
 | 
			
		||||
  _debug "$ep"
 | 
			
		||||
 | 
			
		||||
  if [ ! "$YC_Token" ]; then
 | 
			
		||||
    _debug "Login"
 | 
			
		||||
    _yc_login
 | 
			
		||||
  else
 | 
			
		||||
    _debug "Token already exists. Skip Login."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  token_trimmed=$(echo "$YC_Token" | tr -d '"')
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  export _H2="Authorization: Bearer $token_trimmed"
 | 
			
		||||
 | 
			
		||||
  if [ "$m" != "GET" ]; then
 | 
			
		||||
    _debug data "$data"
 | 
			
		||||
    response="$(_post "$data" "$YC_Api/$ep" "" "$m")"
 | 
			
		||||
  else
 | 
			
		||||
    response="$(_get "$YC_Api/$ep")"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  if [ "$?" != "0" ]; then
 | 
			
		||||
    _err "error $ep"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _debug2 response "$response"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_yc_login() {
 | 
			
		||||
  header=$(echo "{\"typ\":\"JWT\",\"alg\":\"PS256\",\"kid\":\"$YC_SA_Key_ID\"}" | _normalizeJson | _base64 | _url_replace)
 | 
			
		||||
  _debug header "$header"
 | 
			
		||||
 | 
			
		||||
  _current_timestamp=$(_time)
 | 
			
		||||
  _expire_timestamp=$(_math "$_current_timestamp" + 1200) # 20 minutes
 | 
			
		||||
  payload=$(echo "{\"iss\":\"$YC_SA_ID\",\"aud\":\"https://iam.api.cloud.yandex.net/iam/v1/tokens\",\"iat\":$_current_timestamp,\"exp\":$_expire_timestamp}" | _normalizeJson | _base64 | _url_replace)
 | 
			
		||||
  _debug payload "$payload"
 | 
			
		||||
 | 
			
		||||
  #signature=$(printf "%s.%s" "$header" "$payload" | ${ACME_OPENSSL_BIN:-openssl} dgst -sign "$YC_SA_Key_File -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _base64 | _url_replace )
 | 
			
		||||
  _signature=$(printf "%s.%s" "$header" "$payload" | _sign "$YC_SA_Key_File" "sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _url_replace)
 | 
			
		||||
  _debug2 _signature "$_signature"
 | 
			
		||||
 | 
			
		||||
  rm -rf "$YC_SA_Key_File"
 | 
			
		||||
 | 
			
		||||
  _jwt=$(printf "{\"jwt\": \"%s.%s.%s\"}" "$header" "$payload" "$_signature")
 | 
			
		||||
  _debug2 _jwt "$_jwt"
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
  _iam_response="$(_post "$_jwt" "https://iam.api.cloud.yandex.net/iam/v1/tokens" "" "POST")"
 | 
			
		||||
  _debug3 _iam_response "$(echo "$_iam_response" | _normalizeJson)"
 | 
			
		||||
 | 
			
		||||
  YC_Token="$(echo "$_iam_response" | _normalizeJson | _egrep_o "\"iamToken\"[^,]*" | _egrep_o "[^:]*$" | tr -d '"')"
 | 
			
		||||
  _debug3 YC_Token
 | 
			
		||||
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								notify/callmebotWhatsApp.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								notify/callmebotWhatsApp.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Support CallMeBot Whatsapp webhooks
 | 
			
		||||
 | 
			
		||||
#CALLMEBOT_YOUR_PHONE_NO=""
 | 
			
		||||
#CALLMEBOT_API_KEY=""
 | 
			
		||||
 | 
			
		||||
callmebotWhatsApp_send() {
 | 
			
		||||
  _subject="$1"
 | 
			
		||||
  _content="$2"
 | 
			
		||||
  _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
 | 
			
		||||
  _debug "_statusCode" "$_statusCode"
 | 
			
		||||
 | 
			
		||||
  CALLMEBOT_YOUR_PHONE_NO="${CALLMEBOT_YOUR_PHONE_NO:-$(_readaccountconf_mutable CALLMEBOT_YOUR_PHONE_NO)}"
 | 
			
		||||
  if [ -z "$CALLMEBOT_YOUR_PHONE_NO" ]; then
 | 
			
		||||
    CALLMEBOT_YOUR_PHONE_NO=""
 | 
			
		||||
    _err "You didn't specify a Slack webhook url CALLMEBOT_YOUR_PHONE_NO yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable CALLMEBOT_YOUR_PHONE_NO "$CALLMEBOT_YOUR_PHONE_NO"
 | 
			
		||||
 | 
			
		||||
  CALLMEBOT_API_KEY="${CALLMEBOT_API_KEY:-$(_readaccountconf_mutable CALLMEBOT_API_KEY)}"
 | 
			
		||||
  if [ "$CALLMEBOT_API_KEY" ]; then
 | 
			
		||||
    _saveaccountconf_mutable CALLMEBOT_API_KEY "$CALLMEBOT_API_KEY"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _waUrl="https://api.callmebot.com/whatsapp.php"
 | 
			
		||||
 | 
			
		||||
  _Phone_No="$(printf "%s" "$CALLMEBOT_YOUR_PHONE_NO" | _url_encode)"
 | 
			
		||||
  _apikey="$(printf "%s" "$CALLMEBOT_API_KEY" | _url_encode)"
 | 
			
		||||
  _message="$(printf "*%s*\\n%s" "$_subject" "$_content" | _url_encode)"
 | 
			
		||||
 | 
			
		||||
  _finalUrl="$_waUrl?phone=$_Phone_No&apikey=$_apikey&text=$_message"
 | 
			
		||||
  response="$(_get "$_finalUrl")"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" = "0" ] && _contains ".<p><b>Message queued.</b> You will receive it in a few seconds."; then
 | 
			
		||||
    _info "wa send success."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
  _err "wa send error."
 | 
			
		||||
  _debug "URL" "$_finalUrl"
 | 
			
		||||
  _debug "Response" "$response"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								notify/discord.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								notify/discord.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Support Discord webhooks
 | 
			
		||||
 | 
			
		||||
# Required:
 | 
			
		||||
#DISCORD_WEBHOOK_URL=""
 | 
			
		||||
# Optional:
 | 
			
		||||
#DISCORD_USERNAME=""
 | 
			
		||||
#DISCORD_AVATAR_URL=""
 | 
			
		||||
 | 
			
		||||
discord_send() {
 | 
			
		||||
  _subject="$1"
 | 
			
		||||
  _content="$2"
 | 
			
		||||
  _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
 | 
			
		||||
  _debug "_statusCode" "$_statusCode"
 | 
			
		||||
 | 
			
		||||
  DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-$(_readaccountconf_mutable DISCORD_WEBHOOK_URL)}"
 | 
			
		||||
  if [ -z "$DISCORD_WEBHOOK_URL" ]; then
 | 
			
		||||
    DISCORD_WEBHOOK_URL=""
 | 
			
		||||
    _err "You didn't specify a Discord webhook url DISCORD_WEBHOOK_URL yet."
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable DISCORD_WEBHOOK_URL "$DISCORD_WEBHOOK_URL"
 | 
			
		||||
 | 
			
		||||
  DISCORD_USERNAME="${DISCORD_USERNAME:-$(_readaccountconf_mutable DISCORD_USERNAME)}"
 | 
			
		||||
  if [ "$DISCORD_USERNAME" ]; then
 | 
			
		||||
    _saveaccountconf_mutable DISCORD_USERNAME "$DISCORD_USERNAME"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  DISCORD_AVATAR_URL="${DISCORD_AVATAR_URL:-$(_readaccountconf_mutable DISCORD_AVATAR_URL)}"
 | 
			
		||||
  if [ "$DISCORD_AVATAR_URL" ]; then
 | 
			
		||||
    _saveaccountconf_mutable DISCORD_AVATAR_URL "$DISCORD_AVATAR_URL"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  export _H1="Content-Type: application/json"
 | 
			
		||||
 | 
			
		||||
  _content="$(printf "**%s**\n%s" "$_subject" "$_content" | _json_encode)"
 | 
			
		||||
  _data="{\"content\": \"$_content\" "
 | 
			
		||||
  if [ "$DISCORD_USERNAME" ]; then
 | 
			
		||||
    _data="$_data, \"username\": \"$DISCORD_USERNAME\" "
 | 
			
		||||
  fi
 | 
			
		||||
  if [ "$DISCORD_AVATAR_URL" ]; then
 | 
			
		||||
    _data="$_data, \"avatar_url\": \"$DISCORD_AVATAR_URL\" "
 | 
			
		||||
  fi
 | 
			
		||||
  _data="$_data}"
 | 
			
		||||
 | 
			
		||||
  if _post "$_data" "$DISCORD_WEBHOOK_URL?wait=true"; then
 | 
			
		||||
    # shellcheck disable=SC2154
 | 
			
		||||
    if [ "$response" ]; then
 | 
			
		||||
      _info "discord send success."
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "discord send error."
 | 
			
		||||
  _err "$response"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								notify/slack_app.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										45
									
								
								notify/slack_app.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Support Slack APP notifications
 | 
			
		||||
 | 
			
		||||
#SLACK_APP_CHANNEL=""
 | 
			
		||||
#SLACK_APP_TOKEN=""
 | 
			
		||||
 | 
			
		||||
slack_app_send() {
 | 
			
		||||
  _subject="$1"
 | 
			
		||||
  _content="$2"
 | 
			
		||||
  _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
 | 
			
		||||
  _debug "_statusCode" "$_statusCode"
 | 
			
		||||
 | 
			
		||||
  SLACK_APP_CHANNEL="${SLACK_APP_CHANNEL:-$(_readaccountconf_mutable SLACK_APP_CHANNEL)}"
 | 
			
		||||
  if [ -n "$SLACK_APP_CHANNEL" ]; then
 | 
			
		||||
    _saveaccountconf_mutable SLACK_APP_CHANNEL "$SLACK_APP_CHANNEL"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  SLACK_APP_TOKEN="${SLACK_APP_TOKEN:-$(_readaccountconf_mutable SLACK_APP_TOKEN)}"
 | 
			
		||||
  if [ -n "$SLACK_APP_TOKEN" ]; then
 | 
			
		||||
    _saveaccountconf_mutable SLACK_APP_TOKEN "$SLACK_APP_TOKEN"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _content="$(printf "*%s*\n%s" "$_subject" "$_content" | _json_encode)"
 | 
			
		||||
  _data="{\"text\": \"$_content\", "
 | 
			
		||||
  if [ -n "$SLACK_APP_CHANNEL" ]; then
 | 
			
		||||
    _data="$_data\"channel\": \"$SLACK_APP_CHANNEL\", "
 | 
			
		||||
  fi
 | 
			
		||||
  _data="$_data\"mrkdwn\": \"true\"}"
 | 
			
		||||
 | 
			
		||||
  export _H1="Authorization: Bearer $SLACK_APP_TOKEN"
 | 
			
		||||
 | 
			
		||||
  SLACK_APP_API_URL="https://slack.com/api/chat.postMessage"
 | 
			
		||||
  if _post "$_data" "$SLACK_APP_API_URL" "" "POST" "application/json; charset=utf-8"; then
 | 
			
		||||
    # shellcheck disable=SC2154
 | 
			
		||||
    SLACK_APP_RESULT_OK=$(echo "$response" | _egrep_o 'ok" *: *true')
 | 
			
		||||
    if [ "$?" = "0" ] && [ "$SLACK_APP_RESULT_OK" ]; then
 | 
			
		||||
      _info "slack send success."
 | 
			
		||||
      return 0
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
  _err "slack send error."
 | 
			
		||||
  _err "$response"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
@@ -169,7 +169,7 @@ _clean_email_header() {
 | 
			
		||||
# email
 | 
			
		||||
_email_has_display_name() {
 | 
			
		||||
  _email="$1"
 | 
			
		||||
  expr "$_email" : '^.*[<>"]' >/dev/null
 | 
			
		||||
  echo "$_email" | grep -q -E '^.*[<>"]'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
@@ -249,7 +249,7 @@ _mime_encoded_word() {
 | 
			
		||||
  _text="$1"
 | 
			
		||||
  # (regex character ranges like [a-z] can be locale-dependent; enumerate ASCII chars to avoid that)
 | 
			
		||||
  _ascii='] $`"'"[!#%&'()*+,./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ~^_abcdefghijklmnopqrstuvwxyz{|}~-"
 | 
			
		||||
  if expr "$_text" : "^.*[^$_ascii]" >/dev/null; then
 | 
			
		||||
  if echo "$_text" | grep -q -E "^.*[^$_ascii]"; then
 | 
			
		||||
    # At least one non-ASCII char; convert entire thing to encoded word
 | 
			
		||||
    printf "%s" "=?UTF-8?B?$(printf "%s" "$_text" | _base64)?="
 | 
			
		||||
  else
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										49
									
								
								notify/weixin_work.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								notify/weixin_work.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
#!/usr/bin/env sh
 | 
			
		||||
 | 
			
		||||
#Support weixin work webhooks api
 | 
			
		||||
 | 
			
		||||
#WEIXIN_WORK_WEBHOOK="xxxx"
 | 
			
		||||
 | 
			
		||||
#optional
 | 
			
		||||
#WEIXIN_WORK_KEYWORD="yyyy"
 | 
			
		||||
 | 
			
		||||
#`WEIXIN_WORK_SIGNING_KEY`="SEC08ffdbd403cbc3fc8a65xxxxxxxxxxxxxxxxxxxx"
 | 
			
		||||
 | 
			
		||||
# subject  content statusCode
 | 
			
		||||
weixin_work_send() {
 | 
			
		||||
  _subject="$1"
 | 
			
		||||
  _content="$2"
 | 
			
		||||
  _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
 | 
			
		||||
  _debug "_subject" "$_subject"
 | 
			
		||||
  _debug "_content" "$_content"
 | 
			
		||||
  _debug "_statusCode" "$_statusCode"
 | 
			
		||||
 | 
			
		||||
  WEIXIN_WORK_WEBHOOK="${WEIXIN_WORK_WEBHOOK:-$(_readaccountconf_mutable WEIXIN_WORK_WEBHOOK)}"
 | 
			
		||||
  if [ -z "$WEIXIN_WORK_WEBHOOK" ]; then
 | 
			
		||||
    WEIXIN_WORK_WEBHOOK=""
 | 
			
		||||
    _err "You didn't specify a weixin_work webhooks WEIXIN_WORK_WEBHOOK yet."
 | 
			
		||||
    _err "You can get yours from https://work.weixin.qq.com/api/doc/90000/90136/91770"
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
  _saveaccountconf_mutable WEIXIN_WORK_WEBHOOK "$WEIXIN_WORK_WEBHOOK"
 | 
			
		||||
 | 
			
		||||
  WEIXIN_WORK_KEYWORD="${WEIXIN_WORK_KEYWORD:-$(_readaccountconf_mutable WEIXIN_WORK_KEYWORD)}"
 | 
			
		||||
  if [ "$WEIXIN_WORK_KEYWORD" ]; then
 | 
			
		||||
    _saveaccountconf_mutable WEIXIN_WORK_KEYWORD "$WEIXIN_WORK_KEYWORD"
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _content=$(echo "$_content" | _json_encode)
 | 
			
		||||
  _subject=$(echo "$_subject" | _json_encode)
 | 
			
		||||
  _data="{\"msgtype\": \"text\", \"text\": {\"content\": \"[$WEIXIN_WORK_KEYWORD]\n$_subject\n$_content\"}}"
 | 
			
		||||
 | 
			
		||||
  response="$(_post "$_data" "$WEIXIN_WORK_WEBHOOK" "" "POST" "application/json")"
 | 
			
		||||
 | 
			
		||||
  if [ "$?" = "0" ] && _contains "$response" "errmsg\":\"ok"; then
 | 
			
		||||
    _info "weixin_work webhooks event fired success."
 | 
			
		||||
    return 0
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  _err "weixin_work webhooks event fired error."
 | 
			
		||||
  _err "$response"
 | 
			
		||||
  return 1
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user