mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-10-31 07:43:33 +00:00 
			
		
		
		
	Moved certrbot plugin list to backend
frontend doesn't include when building in react version adds swagger for existing dns-providers endpoint
This commit is contained in:
		
							
								
								
									
										21
									
								
								backend/certbot/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								backend/certbot/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| # Certbot dns-plugins | ||||
|  | ||||
| This file contains info about available Certbot DNS plugins. | ||||
| This only works for plugins which use the standard argument structure, so: | ||||
| --authenticator <plugin-name> --<plugin-name>-credentials <FILE> --<plugin-name>-propagation-seconds <number> | ||||
|  | ||||
| File Structure: | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "cloudflare": { | ||||
|     "display_name": "Name displayed to the user", | ||||
|     "package_name": "Package name in PyPi repo", | ||||
|     "version_requirement": "Optional package version requirements (e.g. ==1.3 or >=1.2,<2.0, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)", | ||||
|     "dependencies": "Additional dependencies, space separated (as you would pass it to pip install)", | ||||
|     "credentials": "Template of the credentials file", | ||||
|     "full_plugin_name": "The full plugin name as used in the commandline with certbot, e.g. 'dns-njalla'" | ||||
|   }, | ||||
|   ... | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										602
									
								
								backend/certbot/dns-plugins.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										602
									
								
								backend/certbot/dns-plugins.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,602 @@ | ||||
| { | ||||
| 	"acmedns": { | ||||
| 		"name": "ACME-DNS", | ||||
| 		"package_name": "certbot-dns-acmedns", | ||||
| 		"version": "~=0.1.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_acmedns_api_url = http://acmedns-server/\ndns_acmedns_registration_file = /data/acme-registration.json", | ||||
| 		"full_plugin_name": "dns-acmedns" | ||||
| 	}, | ||||
| 	"active24": { | ||||
| 		"name": "Active24", | ||||
| 		"package_name": "certbot-dns-active24", | ||||
| 		"version": "~=2.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_active24_api_key = <identifier>\ndns_active24_secret = <secret>", | ||||
| 		"full_plugin_name": "dns-active24" | ||||
| 	}, | ||||
| 	"aliyun": { | ||||
| 		"name": "Aliyun", | ||||
| 		"package_name": "certbot-dns-aliyun", | ||||
| 		"version": "~=2.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_aliyun_access_key = 12345678\ndns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef", | ||||
| 		"full_plugin_name": "dns-aliyun" | ||||
| 	}, | ||||
| 	"azure": { | ||||
| 		"name": "Azure", | ||||
| 		"package_name": "certbot-dns-azure", | ||||
| 		"version": "~=1.2.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "# This plugin supported API authentication using either Service Principals or utilizing a Managed Identity assigned to the virtual machine.\n# Regardless which authentication method used, the identity will need the “DNS Zone Contributor” role assigned to it.\n# As multiple Azure DNS Zones in multiple resource groups can exist, the config file needs a mapping of zone to resource group ID. Multiple zones -> ID mappings can be listed by using the key dns_azure_zoneX where X is a unique number. At least 1 zone mapping is required.\n\n# Using a service principal (option 1)\ndns_azure_sp_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5\ndns_azure_sp_client_secret = E-xqXU83Y-jzTI6xe9fs2YC~mck3ZzUih9\ndns_azure_tenant_id = ed1090f3-ab18-4b12-816c-599af8a88cf7\n\n# Using used assigned MSI (option 2)\n# dns_azure_msi_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5\n\n# Using system assigned MSI (option 3)\n# dns_azure_msi_system_assigned = true\n\n# Zones (at least one always required)\ndns_azure_zone1 = example.com:/subscriptions/c135abce-d87d-48df-936c-15596c6968a5/resourceGroups/dns1\ndns_azure_zone2 = example.org:/subscriptions/99800903-fb14-4992-9aff-12eaf2744622/resourceGroups/dns2", | ||||
| 		"full_plugin_name": "dns-azure" | ||||
| 	}, | ||||
| 	"baidu": { | ||||
| 		"name": "baidu", | ||||
| 		"package_name": "certbot-dns-baidu", | ||||
| 		"version": "~=0.1.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_baidu_access_key = 12345678\ndns_baidu_secret_key = 1234567890abcdef1234567890abcdef", | ||||
| 		"full_plugin_name": "dns-baidu" | ||||
| 	}, | ||||
| 	"beget": { | ||||
| 		"name":"Beget", | ||||
| 		"package_name": "certbot-beget-plugin", | ||||
| 		"version": "~=1.0.0.dev9", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "# Beget API credentials used by Certbot\nbeget_plugin_username = username\nbeget_plugin_password = password", | ||||
| 		"full_plugin_name": "beget-plugin" | ||||
| 	}, | ||||
| 	"bunny": { | ||||
| 		"name": "bunny.net", | ||||
| 		"package_name": "certbot-dns-bunny", | ||||
| 		"version": "~=0.0.9", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "# Bunny API token used by Certbot (see https://dash.bunny.net/account/settings)\ndns_bunny_api_key = xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx", | ||||
| 		"full_plugin_name": "dns-bunny" | ||||
| 	}, | ||||
| 	"cdmon": { | ||||
| 		"name": "cdmon", | ||||
| 		"package_name": "certbot-dns-cdmon", | ||||
| 		"version": "~=0.4.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_cdmon_api_key=your-cdmon-api-token\ndns_cdmon_domain=your_domain_is_optional", | ||||
| 		"full_plugin_name": "dns-cdmon" | ||||
| 	}, | ||||
| 	"cloudflare": { | ||||
| 		"name": "Cloudflare", | ||||
| 		"package_name": "certbot-dns-cloudflare", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567", | ||||
| 		"full_plugin_name": "dns-cloudflare" | ||||
| 	}, | ||||
| 	"cloudns": { | ||||
| 		"name": "ClouDNS", | ||||
| 		"package_name": "certbot-dns-cloudns", | ||||
| 		"version": "~=0.6.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "# Target user ID (see https://www.cloudns.net/api-settings/)\n\tdns_cloudns_auth_id=1234\n\t# Alternatively, one of the following two options can be set:\n\t# dns_cloudns_sub_auth_id=1234\n\t# dns_cloudns_sub_auth_user=foobar\n\n\t# API password\n\tdns_cloudns_auth_password=password1", | ||||
| 		"full_plugin_name": "dns-cloudns" | ||||
| 	}, | ||||
| 	"cloudxns": { | ||||
| 		"name": "CloudXNS", | ||||
| 		"package_name": "certbot-dns-cloudxns", | ||||
| 		"version": "~=1.32.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef\ndns_cloudxns_secret_key = 1122334455667788", | ||||
| 		"full_plugin_name": "dns-cloudxns" | ||||
| 	}, | ||||
| 	"constellix": { | ||||
| 		"name": "Constellix", | ||||
| 		"package_name": "certbot-dns-constellix", | ||||
| 		"version": "~=0.2.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_constellix_apikey = 5fb4e76f-ac91-43e5-f982458bc595\ndns_constellix_secretkey = 47d99fd0-32e7-4e07-85b46d08e70b\ndns_constellix_endpoint = https://api.dns.constellix.com/v1", | ||||
| 		"full_plugin_name": "dns-constellix" | ||||
| 	}, | ||||
| 	"corenetworks": { | ||||
| 		"name": "Core Networks", | ||||
| 		"package_name": "certbot-dns-corenetworks", | ||||
| 		"version": "~=0.1.4", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_corenetworks_username = asaHB12r\ndns_corenetworks_password = secure_password", | ||||
| 		"full_plugin_name": "dns-corenetworks" | ||||
| 	}, | ||||
| 	"cpanel": { | ||||
| 		"name": "cPanel", | ||||
| 		"package_name": "certbot-dns-cpanel", | ||||
| 		"version": "~=0.4.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "cpanel_url = https://cpanel.example.com:2083\ncpanel_username = your_username\ncpanel_password = your_password\ncpanel_token = your_api_token", | ||||
| 		"full_plugin_name": "cpanel" | ||||
| 	}, | ||||
| 	"ddnss": { | ||||
| 		"name": "DDNSS", | ||||
| 		"package_name": "certbot-dns-ddnss", | ||||
| 		"version": "~=1.1.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_ddnss_token = YOUR_DDNSS_API_TOKEN", | ||||
| 		"full_plugin_name": "dns-ddnss" | ||||
| 	}, | ||||
| 	"desec": { | ||||
| 		"name": "deSEC", | ||||
| 		"package_name": "certbot-dns-desec", | ||||
| 		"version": "~=1.2.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_desec_token = YOUR_DESEC_API_TOKEN\ndns_desec_endpoint = https://desec.io/api/v1/", | ||||
| 		"full_plugin_name": "dns-desec" | ||||
| 	}, | ||||
| 	"duckdns": { | ||||
| 		"name": "DuckDNS", | ||||
| 		"package_name": "certbot-dns-duckdns", | ||||
| 		"version": "~=1.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_duckdns_token=your-duckdns-token", | ||||
| 		"full_plugin_name": "dns-duckdns" | ||||
| 	}, | ||||
| 	"digitalocean": { | ||||
| 		"name": "DigitalOcean", | ||||
| 		"package_name": "certbot-dns-digitalocean", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff", | ||||
| 		"full_plugin_name": "dns-digitalocean" | ||||
| 	}, | ||||
| 	"directadmin": { | ||||
| 		"name": "DirectAdmin", | ||||
| 		"package_name": "certbot-dns-directadmin", | ||||
| 		"version": "~=0.0.23", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "directadmin_url = https://my.directadminserver.com:2222\ndirectadmin_username = username\ndirectadmin_password = aSuperStrongPassword", | ||||
| 		"full_plugin_name": "directadmin" | ||||
| 	}, | ||||
| 	"dnsimple": { | ||||
| 		"name": "DNSimple", | ||||
| 		"package_name": "certbot-dns-dnsimple", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw", | ||||
| 		"full_plugin_name": "dns-dnsimple" | ||||
| 	}, | ||||
| 	"dnsmadeeasy": { | ||||
| 		"name": "DNS Made Easy", | ||||
| 		"package_name": "certbot-dns-dnsmadeeasy", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a\ndns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55", | ||||
| 		"full_plugin_name": "dns-dnsmadeeasy" | ||||
| 	}, | ||||
| 	"dnsmulti": { | ||||
| 		"name": "DnsMulti", | ||||
| 		"package_name": "certbot-dns-multi", | ||||
| 		"version": "~=4.9", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "# See https://go-acme.github.io/lego/dns/#dns-providers for list of providers and their settings\n# Example provider configuration for DreamHost\n# dns_multi_provider = dreamhost\n# DREAMHOST_API_KEY = ABCDEFG1234", | ||||
| 		"full_plugin_name": "dns-multi" | ||||
| 	}, | ||||
| 	"dnspod": { | ||||
| 		"name": "DNSPod", | ||||
| 		"package_name": "certbot-dns-dnspod", | ||||
| 		"version": "~=0.1.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_dnspod_email = \"email@example.com\"\ndns_dnspod_api_token = \"id,key\"", | ||||
| 		"full_plugin_name": "dns-dnspod" | ||||
| 	}, | ||||
| 	"domainoffensive": { | ||||
| 		"name": "DomainOffensive (do.de)", | ||||
| 		"package_name": "certbot-dns-domainoffensive", | ||||
| 		"version": "~=2.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_domainoffensive_api_token = YOUR_DO_DE_AUTH_TOKEN", | ||||
| 		"full_plugin_name": "dns-domainoffensive" | ||||
| 	}, | ||||
| 	"domeneshop": { | ||||
| 		"name": "Domeneshop", | ||||
| 		"package_name": "certbot-dns-domeneshop", | ||||
| 		"version": "~=0.2.8", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_domeneshop_client_token=YOUR_DOMENESHOP_CLIENT_TOKEN\ndns_domeneshop_client_secret=YOUR_DOMENESHOP_CLIENT_SECRET", | ||||
| 		"full_plugin_name": "dns-domeneshop" | ||||
| 	}, | ||||
| 	"dynu": { | ||||
| 		"name": "Dynu", | ||||
| 		"package_name": "certbot-dns-dynu", | ||||
| 		"version": "~=0.0.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_dynu_auth_token = YOUR_DYNU_AUTH_TOKEN", | ||||
| 		"full_plugin_name": "dns-dynu" | ||||
| 	}, | ||||
| 	"easydns": { | ||||
| 		"name": "easyDNS", | ||||
| 		"package_name": "certbot-dns-easydns", | ||||
| 		"version": "~=0.1.2", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_easydns_usertoken = YOUR_EASYDNS_USERTOKEN\ndns_easydns_userkey = YOUR_EASYDNS_USERKEY\ndns_easydns_endpoint = https://rest.easydns.net", | ||||
| 		"full_plugin_name": "dns-easydns" | ||||
| 	}, | ||||
| 	"eurodns": { | ||||
| 		"name": "EuroDNS", | ||||
| 		"package_name": "certbot-dns-eurodns", | ||||
| 		"version": "~=0.0.4", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_eurodns_applicationId = myuser\ndns_eurodns_apiKey = mysecretpassword\ndns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy", | ||||
| 		"full_plugin_name": "dns-eurodns" | ||||
| 	}, | ||||
| 	"firstdomains": { | ||||
|                 "name": "First Domains", | ||||
|                 "package_name": "certbot-dns-firstdomains", | ||||
|                 "version": ">=1.0", | ||||
|                 "dependencies": "", | ||||
|                 "credentials": "dns_firstdomains_username = myremoteuser\ndns_firstdomains_password = verysecureremoteuserpassword", | ||||
|                 "full_plugin_name": "dns-firstdomains" | ||||
|         }, | ||||
| 	"freedns": { | ||||
| 		"name": "FreeDNS", | ||||
| 		"package_name": "certbot-dns-freedns", | ||||
| 		"version": "~=0.1.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_freedns_username = myremoteuser\ndns_freedns_password = verysecureremoteuserpassword", | ||||
| 		"full_plugin_name": "dns-freedns" | ||||
| 	}, | ||||
| 	"gandi": { | ||||
| 		"name": "Gandi Live DNS", | ||||
| 		"package_name": "certbot-dns-gandi", | ||||
| 		"version": "~=1.6.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "# Gandi personal access token\ndns_gandi_token=PERSONAL_ACCESS_TOKEN", | ||||
| 		"full_plugin_name": "dns-gandi" | ||||
| 	}, | ||||
| 	"gcore": { | ||||
| 		"name": "Gcore DNS", | ||||
| 		"package_name": "certbot-dns-gcore", | ||||
| 		"version": "~=0.1.8", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_gcore_apitoken = 0123456789abcdef0123456789abcdef01234567", | ||||
| 		"full_plugin_name": "dns-gcore" | ||||
| 	}, | ||||
| 	"godaddy": { | ||||
| 		"name": "GoDaddy", | ||||
| 		"package_name": "certbot-dns-godaddy", | ||||
| 		"version": "==2.8.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_godaddy_secret = 0123456789abcdef0123456789abcdef01234567\ndns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123", | ||||
| 		"full_plugin_name": "dns-godaddy" | ||||
| 	}, | ||||
| 	"google": { | ||||
| 		"name": "Google", | ||||
| 		"package_name": "certbot-dns-google", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "{\n\"type\": \"service_account\",\n...\n}", | ||||
| 		"full_plugin_name": "dns-google" | ||||
| 	}, | ||||
| 	"googledomains": { | ||||
| 		"name": "GoogleDomainsDNS", | ||||
| 		"package_name": "certbot-dns-google-domains", | ||||
| 		"version": "~=0.1.5", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_google_domains_access_token = 0123456789abcdef0123456789abcdef01234567\ndns_google_domains_zone = \"example.com\"", | ||||
| 		"full_plugin_name": "dns-google-domains" | ||||
| 	}, | ||||
| 	"he": { | ||||
| 		"name": "Hurricane Electric", | ||||
| 		"package_name": "certbot-dns-he", | ||||
| 		"version": "~=1.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_he_user = Me\ndns_he_pass = my HE password", | ||||
| 		"full_plugin_name": "dns-he" | ||||
| 	}, | ||||
| 	"hetzner": { | ||||
| 		"name": "Hetzner", | ||||
| 		"package_name": "certbot-dns-hetzner", | ||||
| 		"version": "~=1.0.4", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef", | ||||
| 		"full_plugin_name": "dns-hetzner" | ||||
| 	}, | ||||
| 	"hostingnl": { | ||||
| 		"name": "Hosting.nl", | ||||
| 		"package_name": "certbot-dns-hostingnl", | ||||
| 		"version": "~=0.1.5", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_hostingnl_api_key = 0123456789abcdef0123456789abcdef", | ||||
| 		"full_plugin_name": "dns-hostingnl" | ||||
| 	}, | ||||
| 	"hover": { | ||||
| 		"name": "Hover", | ||||
| 		"package_name": "certbot-dns-hover", | ||||
| 		"version": "~=1.2.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_hover_hoverurl = https://www.hover.com\ndns_hover_username = hover-admin-username\ndns_hover_password = hover-admin-password\ndns_hover_totpsecret = 2fa-totp-secret", | ||||
| 		"full_plugin_name": "dns-hover" | ||||
| 	}, | ||||
| 	"infomaniak": { | ||||
| 		"name": "Infomaniak", | ||||
| 		"package_name": "certbot-dns-infomaniak", | ||||
| 		"version": "~=0.2.2", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_infomaniak_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", | ||||
| 		"full_plugin_name": "dns-infomaniak" | ||||
| 	}, | ||||
| 	"inwx": { | ||||
| 		"name": "INWX", | ||||
| 		"package_name": "certbot-dns-inwx", | ||||
| 		"version": "~=2.1.2", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_inwx_url = https://api.domrobot.com/xmlrpc/\ndns_inwx_username = your_username\ndns_inwx_password = your_password\ndns_inwx_shared_secret = your_shared_secret optional", | ||||
| 		"full_plugin_name": "dns-inwx" | ||||
| 	}, | ||||
| 	"ionos": { | ||||
| 		"name": "IONOS", | ||||
| 		"package_name": "certbot-dns-ionos", | ||||
| 		"version": "==2022.11.24", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_ionos_prefix = myapikeyprefix\ndns_ionos_secret = verysecureapikeysecret\ndns_ionos_endpoint = https://api.hosting.ionos.com", | ||||
| 		"full_plugin_name": "dns-ionos" | ||||
| 	}, | ||||
| 	"ispconfig": { | ||||
| 		"name": "ISPConfig", | ||||
| 		"package_name": "certbot-dns-ispconfig", | ||||
| 		"version": "~=0.2.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_ispconfig_username = myremoteuser\ndns_ispconfig_password = verysecureremoteuserpassword\ndns_ispconfig_endpoint = https://localhost:8080", | ||||
| 		"full_plugin_name": "dns-ispconfig" | ||||
| 	}, | ||||
| 	"isset": { | ||||
| 		"name": "Isset", | ||||
| 		"package_name": "certbot-dns-isset", | ||||
| 		"version": "~=0.0.3", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_isset_endpoint=\"https://customer.isset.net/api\"\ndns_isset_token=\"<token>\"", | ||||
| 		"full_plugin_name": "dns-isset" | ||||
| 	}, | ||||
| 	"joker": { | ||||
| 		"name": "Joker", | ||||
| 		"package_name": "certbot-dns-joker", | ||||
| 		"version": "~=1.1.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>", | ||||
| 		"full_plugin_name": "dns-joker" | ||||
| 	}, | ||||
| 	"leaseweb": { | ||||
| 		"name": "LeaseWeb", | ||||
| 		"package_name": "certbot-dns-leaseweb", | ||||
| 		"version": "~=1.0.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_leaseweb_api_token = 01234556789", | ||||
| 		"full_plugin_name": "dns-leaseweb" | ||||
| 	}, | ||||
| 	"linode": { | ||||
| 		"name": "Linode", | ||||
| 		"package_name": "certbot-dns-linode", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64\ndns_linode_version = [<blank>|3|4]", | ||||
| 		"full_plugin_name": "dns-linode" | ||||
| 	}, | ||||
| 	"loopia": { | ||||
| 		"name": "Loopia", | ||||
| 		"package_name": "certbot-dns-loopia", | ||||
| 		"version": "~=1.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_loopia_user = user@loopiaapi\ndns_loopia_password = abcdef0123456789abcdef01234567abcdef0123", | ||||
| 		"full_plugin_name": "dns-loopia" | ||||
| 	}, | ||||
| 	"luadns": { | ||||
| 		"name": "LuaDNS", | ||||
| 		"package_name": "certbot-dns-luadns", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "dns_luadns_email = user@example.com\ndns_luadns_token = 0123456789abcdef0123456789abcdef", | ||||
| 		"full_plugin_name": "dns-luadns" | ||||
| 	}, | ||||
| 	"mijnhost": { | ||||
| 		"name": "mijn.host", | ||||
| 		"package_name": "certbot-dns-mijn-host", | ||||
| 		"version": "~=0.0.4", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_mijn_host_api_key=0123456789abcdef0123456789abcdef", | ||||
| 		"full_plugin_name": "dns-mijn-host" | ||||
| 	}, | ||||
| 	"namecheap": { | ||||
| 		"name": "Namecheap", | ||||
| 		"package_name": "certbot-dns-namecheap", | ||||
| 		"version": "~=1.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_namecheap_username  = 123456\ndns_namecheap_api_key      = 0123456789abcdef0123456789abcdef01234567", | ||||
| 		"full_plugin_name": "dns-namecheap" | ||||
| 	}, | ||||
| 	"netcup": { | ||||
| 		"name": "netcup", | ||||
| 		"package_name": "certbot-dns-netcup", | ||||
| 		"version": "~=1.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_netcup_customer_id  = 123456\ndns_netcup_api_key      = 0123456789abcdef0123456789abcdef01234567\ndns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123", | ||||
| 		"full_plugin_name": "dns-netcup" | ||||
| 	}, | ||||
| 	"nicru": { | ||||
| 		"name": "nic.ru", | ||||
| 		"package_name": "certbot-dns-nicru", | ||||
| 		"version": "~=1.0.3", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_nicru_client_id = application-id\ndns_nicru_client_secret = application-token\ndns_nicru_username = 0001110/NIC-D\ndns_nicru_password = password\ndns_nicru_scope = .+:.+/zones/example.com(/.+)?\ndns_nicru_service = DNS_SERVICE_NAME\ndns_nicru_zone = example.com", | ||||
| 		"full_plugin_name": "dns-nicru" | ||||
| 	}, | ||||
| 	"njalla": { | ||||
| 		"name": "Njalla", | ||||
| 		"package_name": "certbot-dns-njalla", | ||||
| 		"version": "~=1.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_njalla_token = 0123456789abcdef0123456789abcdef01234567", | ||||
| 		"full_plugin_name": "dns-njalla" | ||||
| 	}, | ||||
| 	"nsone": { | ||||
| 		"name": "NS1", | ||||
| 		"package_name": "certbot-dns-nsone", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw", | ||||
| 		"full_plugin_name": "dns-nsone" | ||||
| 	}, | ||||
| 	"oci": { | ||||
| 		"name": "Oracle Cloud Infrastructure DNS", | ||||
| 		"package_name": "certbot-dns-oci", | ||||
| 		"version": "~=0.3.6", | ||||
| 		"dependencies": "oci", | ||||
| 		"credentials": "[DEFAULT]\nuser = ocid1.user.oc1...\nfingerprint = xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx\ntenancy = ocid1.tenancy.oc1...\nregion = us-ashburn-1\nkey_file = ~/.oci/oci_api_key.pem", | ||||
| 		"full_plugin_name": "dns-oci" | ||||
| 	}, | ||||
| 	"ovh": { | ||||
| 		"name": "OVH", | ||||
| 		"package_name": "certbot-dns-ovh", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "dns_ovh_endpoint = ovh-eu\ndns_ovh_application_key = MDAwMDAwMDAwMDAw\ndns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw\ndns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw", | ||||
| 		"full_plugin_name": "dns-ovh" | ||||
| 	}, | ||||
| 	"plesk": { | ||||
| 		"name": "Plesk", | ||||
| 		"package_name": "certbot-dns-plesk", | ||||
| 		"version": "~=0.3.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_plesk_username = your-username\ndns_plesk_password = secret\ndns_plesk_api_url = https://plesk-api-host:8443", | ||||
| 		"full_plugin_name": "dns-plesk" | ||||
| 	}, | ||||
| 	"porkbun": { | ||||
| 		"name": "Porkbun", | ||||
| 		"package_name": "certbot-dns-porkbun", | ||||
| 		"version": "~=0.9", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_porkbun_key=your-porkbun-api-key\ndns_porkbun_secret=your-porkbun-api-secret", | ||||
| 		"full_plugin_name": "dns-porkbun" | ||||
| 	}, | ||||
| 	"powerdns": { | ||||
| 		"name": "PowerDNS", | ||||
| 		"package_name": "certbot-dns-powerdns", | ||||
| 		"version": "~=0.2.1", | ||||
| 		"dependencies": "PyYAML==5.3.1", | ||||
| 		"credentials": "dns_powerdns_api_url = https://api.mypowerdns.example.org\ndns_powerdns_api_key = AbCbASsd!@34", | ||||
| 		"full_plugin_name": "dns-powerdns" | ||||
| 	}, | ||||
| 	"regru": { | ||||
| 		"name": "reg.ru", | ||||
| 		"package_name": "certbot-regru", | ||||
| 		"version": "~=1.0.2", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_username=username\ndns_password=password", | ||||
| 		"full_plugin_name": "dns" | ||||
| 	}, | ||||
| 	"rfc2136": { | ||||
| 		"name": "RFC 2136", | ||||
| 		"package_name": "certbot-dns-rfc2136", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "# Target DNS server\ndns_rfc2136_server = 192.0.2.1\n# Target DNS port\ndns_rfc2136_port = 53\n# TSIG key name\ndns_rfc2136_name = keyname.\n# TSIG key secret\ndns_rfc2136_secret = 4q4wM/2I180UXoMyN4INVhJNi8V9BCV+jMw2mXgZw/CSuxUT8C7NKKFs AmKd7ak51vWKgSl12ib86oQRPkpDjg==\n# TSIG key algorithm\ndns_rfc2136_algorithm = HMAC-SHA512", | ||||
| 		"full_plugin_name": "dns-rfc2136" | ||||
| 	}, | ||||
| 	"rockenstein": { | ||||
| 		"name": "rockenstein AG", | ||||
| 		"package_name": "certbot-dns-rockenstein", | ||||
| 		"version": "~=1.0.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_rockenstein_token=<token>", | ||||
| 		"full_plugin_name": "dns-rockenstein" | ||||
| 	}, | ||||
| 	"route53": { | ||||
| 		"name": "Route 53 (Amazon)", | ||||
| 		"package_name": "certbot-dns-route53", | ||||
| 		"version": "=={{certbot-version}}", | ||||
| 		"dependencies": "acme=={{certbot-version}}", | ||||
| 		"credentials": "[default]\naws_access_key_id=AKIAIOSFODNN7EXAMPLE\naws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", | ||||
| 		"full_plugin_name": "dns-route53" | ||||
| 	}, | ||||
| 	"spaceship": { | ||||
| 		"name": "Spaceship", | ||||
| 		"package_name": "certbot-dns-spaceship", | ||||
| 		"version": "~=1.0.4", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "[spaceship]\napi_key=your_api_key\napi_secret=your_api_secret", | ||||
| 		"full_plugin_name": "dns-spaceship" | ||||
| 	}, | ||||
| 	"strato": { | ||||
| 		"name": "Strato", | ||||
| 		"package_name": "certbot-dns-strato", | ||||
| 		"version": "~=0.2.2", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_strato_username = user\ndns_strato_password = pass\n# uncomment if youre using two factor authentication:\n# dns_strato_totp_devicename = 2fa_device\n# dns_strato_totp_secret = 2fa_secret\n#\n# uncomment if domain name contains special characters\n# insert domain display name as seen on your account page here\n# dns_strato_domain_display_name = my-punicode-url.de\n#\n# if youre not using strato.de or another special endpoint you can customise it below\n# you will probably only need to adjust the host, but you can also change the complete endpoint url\n# dns_strato_custom_api_scheme = https\n# dns_strato_custom_api_host = www.strato.de\n# dns_strato_custom_api_port = 443\n# dns_strato_custom_api_path = \"/apps/CustomerService\"", | ||||
| 		"full_plugin_name": "dns-strato" | ||||
| 	}, | ||||
| 	        "selectelv2": { | ||||
|                 "name": "Selectel api v2", | ||||
|                 "package_name": "certbot-dns-selectel-api-v2", | ||||
|                 "version": "~=0.3.0", | ||||
|                 "dependencies": "", | ||||
|                 "credentials": "dns_selectel_api_v2_account_id = your_account_id\ndns_selectel_api_v2_project_name = your_project\ndns_selectel_api_v2_username = your_username\ndns_selectel_api_v2_password = your_password", | ||||
|                 "full_plugin_name": "dns-selectel-api-v2" | ||||
|         }, | ||||
| 	"timeweb": { | ||||
| 		"name": "Timeweb Cloud", | ||||
| 		"package_name": "certbot-dns-timeweb", | ||||
| 		"version": "~=1.0.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_timeweb_api_key = XXXXXXXXXXXXXXXXXXX", | ||||
| 		"full_plugin_name": "dns-timeweb" | ||||
| 	}, | ||||
| 	"transip": { | ||||
| 		"name": "TransIP", | ||||
| 		"package_name": "certbot-dns-transip", | ||||
| 		"version": "~=0.5.2", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_transip_username = my_username\ndns_transip_key_file = /etc/letsencrypt/transip-rsa.key", | ||||
| 		"full_plugin_name": "dns-transip" | ||||
| 	}, | ||||
| 	"tencentcloud": { | ||||
| 		"name": "Tencent Cloud", | ||||
| 		"package_name": "certbot-dns-tencentcloud", | ||||
| 		"version": "~=2.0.2", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_tencentcloud_secret_id  = TENCENT_CLOUD_SECRET_ID\ndns_tencentcloud_secret_key = TENCENT_CLOUD_SECRET_KEY", | ||||
| 		"full_plugin_name": "dns-tencentcloud" | ||||
| 	}, | ||||
| 	"vultr": { | ||||
| 		"name": "Vultr", | ||||
| 		"package_name": "certbot-dns-vultr", | ||||
| 		"version": "~=1.1.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_vultr_key = YOUR_VULTR_API_KEY", | ||||
| 		"full_plugin_name": "dns-vultr" | ||||
| 	}, | ||||
| 	"websupport": { | ||||
| 		"name": "Websupport.sk", | ||||
| 		"package_name": "certbot-dns-websupport", | ||||
| 		"version": "~=2.0.1", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>", | ||||
| 		"full_plugin_name": "dns-websupport" | ||||
| 	}, | ||||
| 	"wedos": { | ||||
| 		"name": "Wedos", | ||||
| 		"package_name": "certbot-dns-wedos", | ||||
| 		"version": "~=2.2", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "dns_wedos_user = <wedos_registration>\ndns_wedos_auth = <wapi_password>", | ||||
| 		"full_plugin_name": "dns-wedos" | ||||
| 	}, | ||||
| 	"edgedns": { | ||||
| 		"name": "Akamai Edge DNS", | ||||
| 		"package_name": "certbot-plugin-edgedns", | ||||
| 		"version": "~=0.1.0", | ||||
| 		"dependencies": "", | ||||
| 		"credentials": "edgedns_client_secret = as3d1asd5d1a32sdfsdfs2d1asd5=\nedgedns_host = sdflskjdf-dfsdfsdf-sdfsdfsdf.luna.akamaiapis.net\nedgedns_access_token = kjdsi3-34rfsdfsdf-234234fsdfsdf\nedgedns_client_token = dkfjdf-342fsdfsd-23fsdfsdfsdf", | ||||
| 		"full_plugin_name": "edgedns" | ||||
| 	}, | ||||
| 	"zoneedit": { | ||||
| 		"name": "ZoneEdit", | ||||
| 		"package_name": "certbot-dns-zoneedit", | ||||
| 		"version": "~=0.3.2", | ||||
| 		"dependencies": "--no-deps dnspython", | ||||
| 		"credentials": "dns_zoneedit_user = <login-user-id>\ndns_zoneedit_token = <dyn-authentication-token>", | ||||
| 		"full_plugin_name": "dns-zoneedit" | ||||
|  	} | ||||
| } | ||||
| @@ -1,11 +1,11 @@ | ||||
| import fs from "node:fs"; | ||||
| import https from "node:https"; | ||||
| import path from "path"; | ||||
| import archiver from "archiver"; | ||||
| import _ from "lodash"; | ||||
| import moment from "moment"; | ||||
| import path from "path"; | ||||
| import tempWrite from "temp-write"; | ||||
| import dnsPlugins from "../global/certbot-dns-plugins.json" with { type: "json" }; | ||||
| import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" }; | ||||
| import { installPlugin } from "../lib/certbot.js"; | ||||
| import { useLetsencryptServer, useLetsencryptStaging } from "../lib/config.js"; | ||||
| import error from "../lib/error.js"; | ||||
| @@ -26,7 +26,11 @@ const omissions = () => { | ||||
| }; | ||||
|  | ||||
| const internalCertificate = { | ||||
| 	allowedSslFiles: ["certificate", "certificate_key", "intermediate_certificate"], | ||||
| 	allowedSslFiles: [ | ||||
| 		"certificate", | ||||
| 		"certificate_key", | ||||
| 		"intermediate_certificate", | ||||
| 	], | ||||
| 	intervalTimeout: 1000 * 60 * 60, // 1 hour | ||||
| 	interval: null, | ||||
| 	intervalProcessing: false, | ||||
| @@ -53,7 +57,10 @@ const internalCertificate = { | ||||
| 			); | ||||
|  | ||||
| 			const expirationThreshold = moment() | ||||
| 				.add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1]) | ||||
| 				.add( | ||||
| 					internalCertificate.renewBeforeExpirationBy[0], | ||||
| 					internalCertificate.renewBeforeExpirationBy[1], | ||||
| 				) | ||||
| 				.format("YYYY-MM-DD HH:mm:ss"); | ||||
|  | ||||
| 			// Fetch all the letsencrypt certs from the db that will expire within the configured threshold | ||||
| @@ -119,91 +126,115 @@ const internalCertificate = { | ||||
| 			data.nice_name = data.domain_names.join(", "); | ||||
| 		} | ||||
|  | ||||
| 		const certificate = await certificateModel.query().insertAndFetch(data).then(utils.omitRow(omissions())); | ||||
| 		// this command really should clean up and delete the cert if it can't fully succeed | ||||
| 		const certificate = await certificateModel | ||||
| 			.query() | ||||
| 			.insertAndFetch(data) | ||||
| 			.then(utils.omitRow(omissions())); | ||||
|  | ||||
| 		if (certificate.provider === "letsencrypt") { | ||||
| 			// Request a new Cert from LE. Let the fun begin. | ||||
| 		try { | ||||
| 			if (certificate.provider === "letsencrypt") { | ||||
| 				// Request a new Cert from LE. Let the fun begin. | ||||
|  | ||||
| 			// 1. Find out any hosts that are using any of the hostnames in this cert | ||||
| 			// 2. Disable them in nginx temporarily | ||||
| 			// 3. Generate the LE config | ||||
| 			// 4. Request cert | ||||
| 			// 5. Remove LE config | ||||
| 			// 6. Re-instate previously disabled hosts | ||||
|  | ||||
| 			// 1. Find out any hosts that are using any of the hostnames in this cert | ||||
| 			const inUseResult = await internalHost.getHostsWithDomains(certificate.domain_names); | ||||
|  | ||||
| 			// 2. Disable them in nginx temporarily | ||||
| 			await internalCertificate.disableInUseHosts(inUseResult); | ||||
|  | ||||
| 			const user = await userModel.query().where("is_deleted", 0).andWhere("id", data.owner_user_id).first(); | ||||
| 			if (!user || !user.email) { | ||||
| 				throw new error.ValidationError( | ||||
| 					"A valid email address must be set on your user account to use Let's Encrypt", | ||||
| 				); | ||||
| 			} | ||||
|  | ||||
| 			// With DNS challenge no config is needed, so skip 3 and 5. | ||||
| 			if (certificate.meta?.dns_challenge) { | ||||
| 				try { | ||||
| 					await internalNginx.reload(); | ||||
| 					// 4. Request cert | ||||
| 					await internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate, user.email); | ||||
| 					await internalNginx.reload(); | ||||
| 					// 6. Re-instate previously disabled hosts | ||||
| 					await internalCertificate.enableInUseHosts(inUseResult); | ||||
| 				} catch (err) { | ||||
| 					// In the event of failure, revert things and throw err back | ||||
| 					await internalCertificate.enableInUseHosts(inUseResult); | ||||
| 					await internalNginx.reload(); | ||||
| 					throw err; | ||||
| 				} | ||||
| 			} else { | ||||
| 				// 1. Find out any hosts that are using any of the hostnames in this cert | ||||
| 				// 2. Disable them in nginx temporarily | ||||
| 				// 3. Generate the LE config | ||||
| 				// 4. Request cert | ||||
| 				// 5. Remove LE config | ||||
| 				// 6. Re-instate previously disabled hosts | ||||
|  | ||||
| 				// 1. Find out any hosts that are using any of the hostnames in this cert | ||||
| 				const inUseResult = await internalHost.getHostsWithDomains( | ||||
| 					certificate.domain_names, | ||||
| 				); | ||||
|  | ||||
| 				// 2. Disable them in nginx temporarily | ||||
| 				await internalCertificate.disableInUseHosts(inUseResult); | ||||
|  | ||||
| 				const user = await userModel | ||||
| 					.query() | ||||
| 					.where("is_deleted", 0) | ||||
| 					.andWhere("id", data.owner_user_id) | ||||
| 					.first(); | ||||
| 				if (!user || !user.email) { | ||||
| 					throw new error.ValidationError( | ||||
| 						"A valid email address must be set on your user account to use Let's Encrypt", | ||||
| 					); | ||||
| 				} | ||||
|  | ||||
| 				// With DNS challenge no config is needed, so skip 3 and 5. | ||||
| 				if (certificate.meta?.dns_challenge) { | ||||
| 					try { | ||||
| 						await internalNginx.reload(); | ||||
| 						// 4. Request cert | ||||
| 						await internalCertificate.requestLetsEncryptSslWithDnsChallenge( | ||||
| 							certificate, | ||||
| 							user.email, | ||||
| 						); | ||||
| 						await internalNginx.reload(); | ||||
| 						// 6. Re-instate previously disabled hosts | ||||
| 						await internalCertificate.enableInUseHosts(inUseResult); | ||||
| 					} catch (err) { | ||||
| 						// In the event of failure, revert things and throw err back | ||||
| 						await internalCertificate.enableInUseHosts(inUseResult); | ||||
| 						await internalNginx.reload(); | ||||
| 						throw err; | ||||
| 					} | ||||
| 				} else { | ||||
| 					// 3. Generate the LE config | ||||
| 					try { | ||||
| 						await internalNginx.generateLetsEncryptRequestConfig(certificate); | ||||
| 						await internalNginx.reload(); | ||||
| 						setTimeout(() => {}, 5000); | ||||
| 						// 4. Request cert | ||||
| 						await internalCertificate.requestLetsEncryptSsl( | ||||
| 							certificate, | ||||
| 							user.email, | ||||
| 						); | ||||
| 						// 5. Remove LE config | ||||
| 						await internalNginx.deleteLetsEncryptRequestConfig(certificate); | ||||
| 						await internalNginx.reload(); | ||||
| 						// 6. Re-instate previously disabled hosts | ||||
| 						await internalCertificate.enableInUseHosts(inUseResult); | ||||
| 					} catch (err) { | ||||
| 						// In the event of failure, revert things and throw err back | ||||
| 						await internalNginx.deleteLetsEncryptRequestConfig(certificate); | ||||
| 						await internalCertificate.enableInUseHosts(inUseResult); | ||||
| 						await internalNginx.reload(); | ||||
| 						throw err; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				// At this point, the letsencrypt cert should exist on disk. | ||||
| 				// Lets get the expiry date from the file and update the row silently | ||||
| 				try { | ||||
| 					await internalNginx.generateLetsEncryptRequestConfig(certificate); | ||||
| 					await internalNginx.reload(); | ||||
| 					setTimeout(() => {}, 5000); | ||||
| 					// 4. Request cert | ||||
| 					await internalCertificate.requestLetsEncryptSsl(certificate, user.email); | ||||
| 					// 5. Remove LE config | ||||
| 					await internalNginx.deleteLetsEncryptRequestConfig(certificate); | ||||
| 					await internalNginx.reload(); | ||||
| 					// 6. Re-instate previously disabled hosts | ||||
| 					await internalCertificate.enableInUseHosts(inUseResult); | ||||
| 					const certInfo = await internalCertificate.getCertificateInfoFromFile( | ||||
| 						`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`, | ||||
| 					); | ||||
| 					const savedRow = await certificateModel | ||||
| 						.query() | ||||
| 						.patchAndFetchById(certificate.id, { | ||||
| 							expires_on: moment(certInfo.dates.to, "X").format( | ||||
| 								"YYYY-MM-DD HH:mm:ss", | ||||
| 							), | ||||
| 						}) | ||||
| 						.then(utils.omitRow(omissions())); | ||||
|  | ||||
| 					// Add cert data for audit log | ||||
| 					savedRow.meta = _.assign({}, savedRow.meta, { | ||||
| 						letsencrypt_certificate: certInfo, | ||||
| 					}); | ||||
| 					return savedRow; | ||||
| 				} catch (err) { | ||||
| 					// In the event of failure, revert things and throw err back | ||||
| 					await internalNginx.deleteLetsEncryptRequestConfig(certificate); | ||||
| 					await internalCertificate.enableInUseHosts(inUseResult); | ||||
| 					await internalNginx.reload(); | ||||
| 					// Delete the certificate from the database if it was not created successfully | ||||
| 					await certificateModel.query().deleteById(certificate.id); | ||||
| 					throw err; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// At this point, the letsencrypt cert should exist on disk. | ||||
| 			// Lets get the expiry date from the file and update the row silently | ||||
| 			try { | ||||
| 				const certInfo = await internalCertificate.getCertificateInfoFromFile( | ||||
| 					`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`, | ||||
| 				); | ||||
| 				const savedRow = await certificateModel | ||||
| 					.query() | ||||
| 					.patchAndFetchById(certificate.id, { | ||||
| 						expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"), | ||||
| 					}) | ||||
| 					.then(utils.omitRow(omissions())); | ||||
|  | ||||
| 				// Add cert data for audit log | ||||
| 				savedRow.meta = _.assign({}, savedRow.meta, { | ||||
| 					letsencrypt_certificate: certInfo, | ||||
| 				}); | ||||
| 				return savedRow; | ||||
| 			} catch (err) { | ||||
| 				// Delete the certificate from the database if it was not created successfully | ||||
| 				await certificateModel.query().deleteById(certificate.id); | ||||
| 				throw err; | ||||
| 			} | ||||
| 		} catch (err) { | ||||
| 			// Delete the certificate here. This is a hard delete, since it never existed properly | ||||
| 			await certificateModel.query().deleteById(certificate.id); | ||||
| 			throw err; | ||||
| 		} | ||||
|  | ||||
| 		data.meta = _.assign({}, data.meta || {}, certificate.meta); | ||||
| @@ -313,7 +344,9 @@ const internalCertificate = { | ||||
| 		if (certificate.provider === "letsencrypt") { | ||||
| 			const zipDirectory = internalCertificate.getLiveCertPath(data.id); | ||||
| 			if (!fs.existsSync(zipDirectory)) { | ||||
| 				throw new error.ItemNotFoundError(`Certificate ${certificate.nice_name} does not exists`); | ||||
| 				throw new error.ItemNotFoundError( | ||||
| 					`Certificate ${certificate.nice_name} does not exists`, | ||||
| 				); | ||||
| 			} | ||||
|  | ||||
| 			const certFiles = fs | ||||
| @@ -330,7 +363,9 @@ const internalCertificate = { | ||||
| 				fileName: opName, | ||||
| 			}; | ||||
| 		} | ||||
| 		throw new error.ValidationError("Only Let'sEncrypt certificates can be downloaded"); | ||||
| 		throw new error.ValidationError( | ||||
| 			"Only Let'sEncrypt certificates can be downloaded", | ||||
| 		); | ||||
| 	}, | ||||
|  | ||||
| 	/** | ||||
| @@ -435,7 +470,10 @@ const internalCertificate = { | ||||
| 	 * @returns {Promise} | ||||
| 	 */ | ||||
| 	getCount: async (userId, visibility) => { | ||||
| 		const query = certificateModel.query().count("id as count").where("is_deleted", 0); | ||||
| 		const query = certificateModel | ||||
| 			.query() | ||||
| 			.count("id as count") | ||||
| 			.where("is_deleted", 0); | ||||
|  | ||||
| 		if (visibility !== "all") { | ||||
| 			query.andWhere("owner_user_id", userId); | ||||
| @@ -483,13 +521,17 @@ const internalCertificate = { | ||||
| 			}); | ||||
| 		}).then(() => { | ||||
| 			return new Promise((resolve, reject) => { | ||||
| 				fs.writeFile(`${dir}/privkey.pem`, certificate.meta.certificate_key, (err) => { | ||||
| 					if (err) { | ||||
| 						reject(err); | ||||
| 					} else { | ||||
| 						resolve(); | ||||
| 					} | ||||
| 				}); | ||||
| 				fs.writeFile( | ||||
| 					`${dir}/privkey.pem`, | ||||
| 					certificate.meta.certificate_key, | ||||
| 					(err) => { | ||||
| 						if (err) { | ||||
| 							reject(err); | ||||
| 						} else { | ||||
| 							resolve(); | ||||
| 						} | ||||
| 					}, | ||||
| 				); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}, | ||||
| @@ -562,7 +604,9 @@ const internalCertificate = { | ||||
| 	upload: async (access, data) => { | ||||
| 		const row = await internalCertificate.get(access, { id: data.id }); | ||||
| 		if (row.provider !== "other") { | ||||
| 			throw new error.ValidationError("Cannot upload certificates for this type of provider"); | ||||
| 			throw new error.ValidationError( | ||||
| 				"Cannot upload certificates for this type of provider", | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		const validations = await internalCertificate.validate(data); | ||||
| @@ -578,7 +622,9 @@ const internalCertificate = { | ||||
|  | ||||
| 		const certificate = await internalCertificate.update(access, { | ||||
| 			id: data.id, | ||||
| 			expires_on: moment(validations.certificate.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"), | ||||
| 			expires_on: moment(validations.certificate.dates.to, "X").format( | ||||
| 				"YYYY-MM-DD HH:mm:ss", | ||||
| 			), | ||||
| 			domain_names: [validations.certificate.cn], | ||||
| 			meta: _.clone(row.meta), // Prevent the update method from changing this value that we'll use later | ||||
| 		}); | ||||
| @@ -603,7 +649,9 @@ const internalCertificate = { | ||||
| 		}, 10000); | ||||
|  | ||||
| 		try { | ||||
| 			const result = await utils.exec(`openssl pkey -in ${filepath} -check -noout 2>&1 `); | ||||
| 			const result = await utils.exec( | ||||
| 				`openssl pkey -in ${filepath} -check -noout 2>&1 `, | ||||
| 			); | ||||
| 			clearTimeout(failTimeout); | ||||
| 			if (!result.toLowerCase().includes("key is valid")) { | ||||
| 				throw new error.ValidationError(`Result Validation Error: ${result}`); | ||||
| @@ -613,7 +661,10 @@ const internalCertificate = { | ||||
| 		} catch (err) { | ||||
| 			clearTimeout(failTimeout); | ||||
| 			fs.unlinkSync(filepath); | ||||
| 			throw new error.ValidationError(`Certificate Key is not valid (${err.message})`, err); | ||||
| 			throw new error.ValidationError( | ||||
| 				`Certificate Key is not valid (${err.message})`, | ||||
| 				err, | ||||
| 			); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| @@ -627,7 +678,10 @@ const internalCertificate = { | ||||
| 	getCertificateInfo: async (certificate, throwExpired) => { | ||||
| 		try { | ||||
| 			const filepath = await tempWrite(certificate, "/tmp"); | ||||
| 			const certData = await internalCertificate.getCertificateInfoFromFile(filepath, throwExpired); | ||||
| 			const certData = await internalCertificate.getCertificateInfoFromFile( | ||||
| 				filepath, | ||||
| 				throwExpired, | ||||
| 			); | ||||
| 			fs.unlinkSync(filepath); | ||||
| 			return certData; | ||||
| 		} catch (err) { | ||||
| @@ -647,7 +701,13 @@ const internalCertificate = { | ||||
| 		const certData = {}; | ||||
|  | ||||
| 		try { | ||||
| 			const result = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-subject", "-noout"]); | ||||
| 			const result = await utils.execFile("openssl", [ | ||||
| 				"x509", | ||||
| 				"-in", | ||||
| 				certificateFile, | ||||
| 				"-subject", | ||||
| 				"-noout", | ||||
| 			]); | ||||
| 			// Examples: | ||||
| 			// subject=CN = *.jc21.com | ||||
| 			// subject=CN = something.example.com | ||||
| @@ -657,7 +717,13 @@ const internalCertificate = { | ||||
| 				certData.cn = match[1]; | ||||
| 			} | ||||
|  | ||||
| 			const result2 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-issuer", "-noout"]); | ||||
| 			const result2 = await utils.execFile("openssl", [ | ||||
| 				"x509", | ||||
| 				"-in", | ||||
| 				certificateFile, | ||||
| 				"-issuer", | ||||
| 				"-noout", | ||||
| 			]); | ||||
| 			// Examples: | ||||
| 			// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 | ||||
| 			// issuer=C = US, O = Let's Encrypt, CN = E5 | ||||
| @@ -668,7 +734,13 @@ const internalCertificate = { | ||||
| 				certData.issuer = match2[1]; | ||||
| 			} | ||||
|  | ||||
| 			const result3 = await utils.execFile("openssl", ["x509", "-in", certificateFile, "-dates", "-noout"]); | ||||
| 			const result3 = await utils.execFile("openssl", [ | ||||
| 				"x509", | ||||
| 				"-in", | ||||
| 				certificateFile, | ||||
| 				"-dates", | ||||
| 				"-noout", | ||||
| 			]); | ||||
| 			// notBefore=Jul 14 04:04:29 2018 GMT | ||||
| 			// notAfter=Oct 12 04:04:29 2018 GMT | ||||
| 			let validFrom = null; | ||||
| @@ -680,7 +752,10 @@ const internalCertificate = { | ||||
| 				const match = regex.exec(str.trim()); | ||||
|  | ||||
| 				if (match && typeof match[2] !== "undefined") { | ||||
| 					const date = Number.parseInt(moment(match[2], "MMM DD HH:mm:ss YYYY z").format("X"), 10); | ||||
| 					const date = Number.parseInt( | ||||
| 						moment(match[2], "MMM DD HH:mm:ss YYYY z").format("X"), | ||||
| 						10, | ||||
| 					); | ||||
|  | ||||
| 					if (match[1].toLowerCase() === "notbefore") { | ||||
| 						validFrom = date; | ||||
| @@ -692,10 +767,15 @@ const internalCertificate = { | ||||
| 			}); | ||||
|  | ||||
| 			if (!validFrom || !validTo) { | ||||
| 				throw new error.ValidationError(`Could not determine dates from certificate: ${result}`); | ||||
| 				throw new error.ValidationError( | ||||
| 					`Could not determine dates from certificate: ${result}`, | ||||
| 				); | ||||
| 			} | ||||
|  | ||||
| 			if (throw_expired && validTo < Number.parseInt(moment().format("X"), 10)) { | ||||
| 			if ( | ||||
| 				throw_expired && | ||||
| 				validTo < Number.parseInt(moment().format("X"), 10) | ||||
| 			) { | ||||
| 				throw new error.ValidationError("Certificate has expired"); | ||||
| 			} | ||||
|  | ||||
| @@ -706,7 +786,10 @@ const internalCertificate = { | ||||
|  | ||||
| 			return certData; | ||||
| 		} catch (err) { | ||||
| 			throw new error.ValidationError(`Certificate is not valid (${err.message})`, err); | ||||
| 			throw new error.ValidationError( | ||||
| 				`Certificate is not valid (${err.message})`, | ||||
| 				err, | ||||
| 			); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| @@ -787,7 +870,11 @@ const internalCertificate = { | ||||
|  | ||||
| 		const credentialsLocation = `/etc/letsencrypt/credentials/credentials-${certificate.id}`; | ||||
| 		fs.mkdirSync("/etc/letsencrypt/credentials", { recursive: true }); | ||||
| 		fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, { mode: 0o600 }); | ||||
| 		fs.writeFileSync( | ||||
| 			credentialsLocation, | ||||
| 			certificate.meta.dns_provider_credentials, | ||||
| 			{ mode: 0o600 }, | ||||
| 		); | ||||
|  | ||||
| 		// Whether the plugin has a --<name>-credentials argument | ||||
| 		const hasConfigArg = certificate.meta.dns_provider !== "route53"; | ||||
| @@ -812,7 +899,10 @@ const internalCertificate = { | ||||
| 		]; | ||||
|  | ||||
| 		if (hasConfigArg) { | ||||
| 			args.push(`--${dnsPlugin.full_plugin_name}-credentials`, credentialsLocation); | ||||
| 			args.push( | ||||
| 				`--${dnsPlugin.full_plugin_name}-credentials`, | ||||
| 				credentialsLocation, | ||||
| 			); | ||||
| 		} | ||||
| 		if (certificate.meta.propagation_seconds !== undefined) { | ||||
| 			args.push( | ||||
| @@ -821,7 +911,10 @@ const internalCertificate = { | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider); | ||||
| 		const adds = internalCertificate.getAdditionalCertbotArgs( | ||||
| 			certificate.id, | ||||
| 			certificate.meta.dns_provider, | ||||
| 		); | ||||
| 		args.push(...adds.args); | ||||
|  | ||||
| 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); | ||||
| @@ -857,9 +950,13 @@ const internalCertificate = { | ||||
| 				`${internalCertificate.getLiveCertPath(certificate.id)}/fullchain.pem`, | ||||
| 			); | ||||
|  | ||||
| 			const updatedCertificate = await certificateModel.query().patchAndFetchById(certificate.id, { | ||||
| 				expires_on: moment(certInfo.dates.to, "X").format("YYYY-MM-DD HH:mm:ss"), | ||||
| 			}); | ||||
| 			const updatedCertificate = await certificateModel | ||||
| 				.query() | ||||
| 				.patchAndFetchById(certificate.id, { | ||||
| 					expires_on: moment(certInfo.dates.to, "X").format( | ||||
| 						"YYYY-MM-DD HH:mm:ss", | ||||
| 					), | ||||
| 				}); | ||||
|  | ||||
| 			// Add to audit log | ||||
| 			await internalAuditLog.add(access, { | ||||
| @@ -869,7 +966,9 @@ const internalCertificate = { | ||||
| 				meta: updatedCertificate, | ||||
| 			}); | ||||
| 		} else { | ||||
| 			throw new error.ValidationError("Only Let'sEncrypt certificates can be renewed"); | ||||
| 			throw new error.ValidationError( | ||||
| 				"Only Let'sEncrypt certificates can be renewed", | ||||
| 			); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| @@ -899,7 +998,10 @@ const internalCertificate = { | ||||
| 			"--disable-hook-validation", | ||||
| 		]; | ||||
|  | ||||
| 		const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider); | ||||
| 		const adds = internalCertificate.getAdditionalCertbotArgs( | ||||
| 			certificate.id, | ||||
| 			certificate.meta.dns_provider, | ||||
| 		); | ||||
| 		args.push(...adds.args); | ||||
|  | ||||
| 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); | ||||
| @@ -938,7 +1040,10 @@ const internalCertificate = { | ||||
| 			"--no-random-sleep-on-renew", | ||||
| 		]; | ||||
|  | ||||
| 		const adds = internalCertificate.getAdditionalCertbotArgs(certificate.id, certificate.meta.dns_provider); | ||||
| 		const adds = internalCertificate.getAdditionalCertbotArgs( | ||||
| 			certificate.id, | ||||
| 			certificate.meta.dns_provider, | ||||
| 		); | ||||
| 		args.push(...adds.args); | ||||
|  | ||||
| 		logger.info(`Command: ${certbotCommand} ${args ? args.join(" ") : ""}`); | ||||
| @@ -978,7 +1083,9 @@ const internalCertificate = { | ||||
|  | ||||
| 		try { | ||||
| 			const result = await utils.execFile(certbotCommand, args, adds.opts); | ||||
| 			await utils.exec(`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`); | ||||
| 			await utils.exec( | ||||
| 				`rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`, | ||||
| 			); | ||||
| 			logger.info(result); | ||||
| 			return result; | ||||
| 		} catch (err) { | ||||
| @@ -995,7 +1102,10 @@ const internalCertificate = { | ||||
| 	 */ | ||||
| 	hasLetsEncryptSslCerts: (certificate) => { | ||||
| 		const letsencryptPath = internalCertificate.getLiveCertPath(certificate.id); | ||||
| 		return fs.existsSync(`${letsencryptPath}/fullchain.pem`) && fs.existsSync(`${letsencryptPath}/privkey.pem`); | ||||
| 		return ( | ||||
| 			fs.existsSync(`${letsencryptPath}/fullchain.pem`) && | ||||
| 			fs.existsSync(`${letsencryptPath}/privkey.pem`) | ||||
| 		); | ||||
| 	}, | ||||
|  | ||||
| 	/** | ||||
| @@ -1009,15 +1119,24 @@ const internalCertificate = { | ||||
| 	disableInUseHosts: async (inUseResult) => { | ||||
| 		if (inUseResult?.total_count) { | ||||
| 			if (inUseResult?.proxy_hosts.length) { | ||||
| 				await internalNginx.bulkDeleteConfigs("proxy_host", inUseResult.proxy_hosts); | ||||
| 				await internalNginx.bulkDeleteConfigs( | ||||
| 					"proxy_host", | ||||
| 					inUseResult.proxy_hosts, | ||||
| 				); | ||||
| 			} | ||||
|  | ||||
| 			if (inUseResult?.redirection_hosts.length) { | ||||
| 				await internalNginx.bulkDeleteConfigs("redirection_host", inUseResult.redirection_hosts); | ||||
| 				await internalNginx.bulkDeleteConfigs( | ||||
| 					"redirection_host", | ||||
| 					inUseResult.redirection_hosts, | ||||
| 				); | ||||
| 			} | ||||
|  | ||||
| 			if (inUseResult?.dead_hosts.length) { | ||||
| 				await internalNginx.bulkDeleteConfigs("dead_host", inUseResult.dead_hosts); | ||||
| 				await internalNginx.bulkDeleteConfigs( | ||||
| 					"dead_host", | ||||
| 					inUseResult.dead_hosts, | ||||
| 				); | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| @@ -1033,50 +1152,73 @@ const internalCertificate = { | ||||
| 	enableInUseHosts: async (inUseResult) => { | ||||
| 		if (inUseResult.total_count) { | ||||
| 			if (inUseResult.proxy_hosts.length) { | ||||
| 				await internalNginx.bulkGenerateConfigs("proxy_host", inUseResult.proxy_hosts); | ||||
| 				await internalNginx.bulkGenerateConfigs( | ||||
| 					"proxy_host", | ||||
| 					inUseResult.proxy_hosts, | ||||
| 				); | ||||
| 			} | ||||
|  | ||||
| 			if (inUseResult.redirection_hosts.length) { | ||||
| 				await internalNginx.bulkGenerateConfigs("redirection_host", inUseResult.redirection_hosts); | ||||
| 				await internalNginx.bulkGenerateConfigs( | ||||
| 					"redirection_host", | ||||
| 					inUseResult.redirection_hosts, | ||||
| 				); | ||||
| 			} | ||||
|  | ||||
| 			if (inUseResult.dead_hosts.length) { | ||||
| 				await internalNginx.bulkGenerateConfigs("dead_host", inUseResult.dead_hosts); | ||||
| 				await internalNginx.bulkGenerateConfigs( | ||||
| 					"dead_host", | ||||
| 					inUseResult.dead_hosts, | ||||
| 				); | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	testHttpsChallenge: async (access, domains) => { | ||||
| 	/** | ||||
| 	 * | ||||
| 	 * @param   {Object}    payload | ||||
| 	 * @param   {string[]}  payload.domains | ||||
| 	 * @returns | ||||
| 	 */ | ||||
| 	testHttpsChallenge: async (access, payload) => { | ||||
| 		await access.can("certificates:list"); | ||||
|  | ||||
| 		if (!isArray(domains)) { | ||||
| 			throw new error.InternalValidationError("Domains must be an array of strings"); | ||||
| 		} | ||||
| 		if (domains.length === 0) { | ||||
| 			throw new error.InternalValidationError("No domains provided"); | ||||
| 		} | ||||
|  | ||||
| 		// Create a test challenge file | ||||
| 		const testChallengeDir = "/data/letsencrypt-acme-challenge/.well-known/acme-challenge"; | ||||
| 		const testChallengeDir = | ||||
| 			"/data/letsencrypt-acme-challenge/.well-known/acme-challenge"; | ||||
| 		const testChallengeFile = `${testChallengeDir}/test-challenge`; | ||||
| 		fs.mkdirSync(testChallengeDir, { recursive: true }); | ||||
| 		fs.writeFileSync(testChallengeFile, "Success", { encoding: "utf8" }); | ||||
|  | ||||
| 		async function performTestForDomain(domain) { | ||||
| 			logger.info(`Testing http challenge for ${domain}`); | ||||
| 			const url = `http://${domain}/.well-known/acme-challenge/test-challenge`; | ||||
| 			const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`; | ||||
| 			const options = { | ||||
| 				method: "POST", | ||||
| 				headers: { | ||||
| 					"User-Agent": "Mozilla/5.0", | ||||
| 					"Content-Type": "application/x-www-form-urlencoded", | ||||
| 					"Content-Length": Buffer.byteLength(formBody), | ||||
| 				}, | ||||
| 			}; | ||||
| 		const results = {}; | ||||
| 		for (const domain of payload.domains) { | ||||
| 			results[domain] = await internalCertificate.performTestForDomain(domain); | ||||
| 		} | ||||
|  | ||||
| 			const result = await new Promise((resolve) => { | ||||
| 				const req = https.request("https://www.site24x7.com/tools/restapi-tester", options, (res) => { | ||||
| 		// Remove the test challenge file | ||||
| 		fs.unlinkSync(testChallengeFile); | ||||
|  | ||||
| 		return results; | ||||
| 	}, | ||||
|  | ||||
| 	performTestForDomain: async (domain) => { | ||||
| 		logger.info(`Testing http challenge for ${domain}`); | ||||
| 		const url = `http://${domain}/.well-known/acme-challenge/test-challenge`; | ||||
| 		const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`; | ||||
| 		const options = { | ||||
| 			method: "POST", | ||||
| 			headers: { | ||||
| 				"User-Agent": "Mozilla/5.0", | ||||
| 				"Content-Type": "application/x-www-form-urlencoded", | ||||
| 				"Content-Length": Buffer.byteLength(formBody), | ||||
| 			}, | ||||
| 		}; | ||||
|  | ||||
| 		const result = await new Promise((resolve) => { | ||||
| 			const req = https.request( | ||||
| 				"https://www.site24x7.com/tools/restapi-tester", | ||||
| 				options, | ||||
| 				(res) => { | ||||
| 					let responseBody = ""; | ||||
|  | ||||
| 					res.on("data", (chunk) => { | ||||
| @@ -1107,69 +1249,66 @@ const internalCertificate = { | ||||
| 							resolve(undefined); | ||||
| 						} | ||||
| 					}); | ||||
| 				}); | ||||
|  | ||||
| 				// Make sure to write the request body. | ||||
| 				req.write(formBody); | ||||
| 				req.end(); | ||||
| 				req.on("error", (e) => { | ||||
| 					logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e); | ||||
| 					resolve(undefined); | ||||
| 				}); | ||||
| 			}); | ||||
|  | ||||
| 			if (!result) { | ||||
| 				// Some error occurred while trying to get the data | ||||
| 				return "failed"; | ||||
| 			} | ||||
| 			if (result.error) { | ||||
| 				logger.info( | ||||
| 					`HTTP challenge test failed for domain ${domain} because error was returned: ${result.error.msg}`, | ||||
| 				); | ||||
| 				return `other:${result.error.msg}`; | ||||
| 			} | ||||
| 			if (`${result.responsecode}` === "200" && result.htmlresponse === "Success") { | ||||
| 				// Server exists and has responded with the correct data | ||||
| 				return "ok"; | ||||
| 			} | ||||
| 			if (`${result.responsecode}` === "200") { | ||||
| 				// Server exists but has responded with wrong data | ||||
| 				logger.info( | ||||
| 					`HTTP challenge test failed for domain ${domain} because of invalid returned data:`, | ||||
| 					result.htmlresponse, | ||||
| 				); | ||||
| 				return "wrong-data"; | ||||
| 			} | ||||
| 			if (`${result.responsecode}` === "404") { | ||||
| 				// Server exists but responded with a 404 | ||||
| 				logger.info(`HTTP challenge test failed for domain ${domain} because code 404 was returned`); | ||||
| 				return "404"; | ||||
| 			} | ||||
| 			if ( | ||||
| 				`${result.responsecode}` === "0" || | ||||
| 				(typeof result.reason === "string" && result.reason.toLowerCase() === "host unavailable") | ||||
| 			) { | ||||
| 				// Server does not exist at domain | ||||
| 				logger.info(`HTTP challenge test failed for domain ${domain} the host was not found`); | ||||
| 				return "no-host"; | ||||
| 			} | ||||
| 			// Other errors | ||||
| 			logger.info( | ||||
| 				`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`, | ||||
| 				}, | ||||
| 			); | ||||
| 			return `other:${result.responsecode}`; | ||||
|  | ||||
| 			// Make sure to write the request body. | ||||
| 			req.write(formBody); | ||||
| 			req.end(); | ||||
| 			req.on("error", (e) => { | ||||
| 				logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e); | ||||
| 				resolve(undefined); | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		if (!result) { | ||||
| 			// Some error occurred while trying to get the data | ||||
| 			return "failed"; | ||||
| 		} | ||||
|  | ||||
| 		const results = {}; | ||||
|  | ||||
| 		for (const domain of domains) { | ||||
| 			results[domain] = await performTestForDomain(domain); | ||||
| 		if (result.error) { | ||||
| 			logger.info( | ||||
| 				`HTTP challenge test failed for domain ${domain} because error was returned: ${result.error.msg}`, | ||||
| 			); | ||||
| 			return `other:${result.error.msg}`; | ||||
| 		} | ||||
|  | ||||
| 		// Remove the test challenge file | ||||
| 		fs.unlinkSync(testChallengeFile); | ||||
|  | ||||
| 		return results; | ||||
| 		if ( | ||||
| 			`${result.responsecode}` === "200" && | ||||
| 			result.htmlresponse === "Success" | ||||
| 		) { | ||||
| 			// Server exists and has responded with the correct data | ||||
| 			return "ok"; | ||||
| 		} | ||||
| 		if (`${result.responsecode}` === "200") { | ||||
| 			// Server exists but has responded with wrong data | ||||
| 			logger.info( | ||||
| 				`HTTP challenge test failed for domain ${domain} because of invalid returned data:`, | ||||
| 				result.htmlresponse, | ||||
| 			); | ||||
| 			return "wrong-data"; | ||||
| 		} | ||||
| 		if (`${result.responsecode}` === "404") { | ||||
| 			// Server exists but responded with a 404 | ||||
| 			logger.info( | ||||
| 				`HTTP challenge test failed for domain ${domain} because code 404 was returned`, | ||||
| 			); | ||||
| 			return "404"; | ||||
| 		} | ||||
| 		if ( | ||||
| 			`${result.responsecode}` === "0" || | ||||
| 			(typeof result.reason === "string" && | ||||
| 				result.reason.toLowerCase() === "host unavailable") | ||||
| 		) { | ||||
| 			// Server does not exist at domain | ||||
| 			logger.info( | ||||
| 				`HTTP challenge test failed for domain ${domain} the host was not found`, | ||||
| 			); | ||||
| 			return "no-host"; | ||||
| 		} | ||||
| 		// Other errors | ||||
| 		logger.info( | ||||
| 			`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`, | ||||
| 		); | ||||
| 		return `other:${result.responsecode}`; | ||||
| 	}, | ||||
|  | ||||
| 	getAdditionalCertbotArgs: (certificate_id, dns_provider) => { | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import batchflow from "batchflow"; | ||||
| import dnsPlugins from "../global/certbot-dns-plugins.json" with { type: "json" }; | ||||
| import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" }; | ||||
| import { certbot as logger } from "../logger.js"; | ||||
| import errs from "./error.js"; | ||||
| import utils from "./utils.js"; | ||||
| @@ -8,7 +8,7 @@ const CERTBOT_VERSION_REPLACEMENT = "$(certbot --version | grep -Eo '[0-9](\\.[0 | ||||
|  | ||||
| /** | ||||
|  * Installs a cerbot plugin given the key for the object from | ||||
|  * ../global/certbot-dns-plugins.json | ||||
|  * ../certbot/dns-plugins.json | ||||
|  * | ||||
|  * @param   {string}  pluginKey | ||||
|  * @returns {Object} | ||||
|   | ||||
| @@ -24,16 +24,21 @@ const apiValidator = async (schema, payload /*, description*/) => { | ||||
| 		throw new errs.ValidationError("Payload is undefined"); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	const validate = ajv.compile(schema); | ||||
|  | ||||
| 	const valid = validate(payload); | ||||
|  | ||||
|  | ||||
| 	if (valid && !validate.errors) { | ||||
| 		return payload; | ||||
| 	} | ||||
|  | ||||
|  | ||||
|  | ||||
| 	const message = ajv.errorsText(validate.errors); | ||||
| 	const err = new errs.ValidationError(message); | ||||
| 	err.debug = [validate.errors, payload]; | ||||
| 	err.debug = {validationErrors: validate.errors, payload}; | ||||
| 	throw err; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import express from "express"; | ||||
| import dnsPlugins from "../../global/certbot-dns-plugins.json" with { type: "json" }; | ||||
| import dnsPlugins from "../../certbot/dns-plugins.json" with { type: "json" }; | ||||
| import internalCertificate from "../../internal/certificate.js"; | ||||
| import errs from "../../lib/error.js"; | ||||
| import jwtdecode from "../../lib/express/jwt-decode.js"; | ||||
| @@ -44,11 +44,18 @@ router | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, | ||||
| 					expand: | ||||
| 						typeof req.query.expand === "string" | ||||
| 							? req.query.expand.split(",") | ||||
| 							: null, | ||||
| 					query: typeof req.query.query === "string" ? req.query.query : null, | ||||
| 				}, | ||||
| 			); | ||||
| 			const rows = await internalCertificate.getAll(res.locals.access, data.expand, data.query); | ||||
| 			const rows = await internalCertificate.getAll( | ||||
| 				res.locals.access, | ||||
| 				data.expand, | ||||
| 				data.query, | ||||
| 			); | ||||
| 			res.status(200).send(rows); | ||||
| 		} catch (err) { | ||||
| 			logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); | ||||
| @@ -63,9 +70,15 @@ router | ||||
| 	 */ | ||||
| 	.post(async (req, res, next) => { | ||||
| 		try { | ||||
| 			const payload = await apiValidator(getValidationSchema("/nginx/certificates", "post"), req.body); | ||||
| 			const payload = await apiValidator( | ||||
| 				getValidationSchema("/nginx/certificates", "post"), | ||||
| 				req.body, | ||||
| 			); | ||||
| 			req.setTimeout(900000); // 15 minutes timeout | ||||
| 			const result = await internalCertificate.create(res.locals.access, payload); | ||||
| 			const result = await internalCertificate.create( | ||||
| 				res.locals.access, | ||||
| 				payload, | ||||
| 			); | ||||
| 			res.status(201).send(result); | ||||
| 		} catch (err) { | ||||
| 			logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); | ||||
| @@ -120,20 +133,21 @@ router | ||||
| 	.all(jwtdecode()) | ||||
|  | ||||
| 	/** | ||||
| 	 * GET /api/nginx/certificates/test-http | ||||
| 	 * POST /api/nginx/certificates/test-http | ||||
| 	 * | ||||
| 	 * Test HTTP challenge for domains | ||||
| 	 */ | ||||
| 	.get(async (req, res, next) => { | ||||
| 		if (req.query.domains === undefined) { | ||||
| 			next(new errs.ValidationError("Domains are required as query parameters")); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 	.post(async (req, res, next) => { | ||||
| 		try { | ||||
| 			const payload = await apiValidator( | ||||
| 				getValidationSchema("/nginx/certificates/test-http", "post"), | ||||
| 				req.body, | ||||
| 			); | ||||
| 			req.setTimeout(60000); // 1 minute timeout | ||||
|  | ||||
| 			const result = await internalCertificate.testHttpsChallenge( | ||||
| 				res.locals.access, | ||||
| 				JSON.parse(req.query.domains), | ||||
| 				payload, | ||||
| 			); | ||||
| 			res.status(200).send(result); | ||||
| 		} catch (err) { | ||||
| @@ -142,7 +156,6 @@ router | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Validate Certs before saving | ||||
|  * | ||||
| @@ -211,7 +224,10 @@ router | ||||
| 				}, | ||||
| 				{ | ||||
| 					certificate_id: req.params.certificate_id, | ||||
| 					expand: typeof req.query.expand === "string" ? req.query.expand.split(",") : null, | ||||
| 					expand: | ||||
| 						typeof req.query.expand === "string" | ||||
| 							? req.query.expand.split(",") | ||||
| 							: null, | ||||
| 				}, | ||||
| 			); | ||||
| 			const row = await internalCertificate.get(res.locals.access, { | ||||
|   | ||||
| @@ -65,7 +65,7 @@ router | ||||
| 			const result = await internalProxyHost.create(res.locals.access, payload); | ||||
| 			res.status(201).send(result); | ||||
| 		} catch (err) { | ||||
| 			logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`); | ||||
| 			logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err} ${JSON.stringify(err.debug, null, 2)}`); | ||||
| 			next(err); | ||||
| 		} | ||||
| 	}); | ||||
|   | ||||
							
								
								
									
										23
									
								
								backend/schema/components/dns-providers-list.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								backend/schema/components/dns-providers-list.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| { | ||||
| 	"type": "array", | ||||
| 	"description": "DNS Providers list", | ||||
| 	"items": { | ||||
| 		"type": "object", | ||||
| 		"required": ["id", "name", "credentials"], | ||||
| 		"additionalProperties": false, | ||||
| 		"properties": { | ||||
| 			"id": { | ||||
| 				"type": "string", | ||||
| 				"description": "Unique identifier for the DNS provider, matching the python package" | ||||
| 			}, | ||||
| 			"name": { | ||||
| 				"type": "string", | ||||
| 				"description": "Human-readable name of the DNS provider" | ||||
| 			}, | ||||
| 			"credentials": { | ||||
| 				"type": "string", | ||||
| 				"description": "Instructions on how to format the credentials for this DNS provider" | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| { | ||||
| 	"operationId": "getDNSProviders", | ||||
| 	"summary": "Get DNS Providers for Certificates", | ||||
| 	"tags": [ | ||||
| 		"Certificates" | ||||
| 	], | ||||
| 	"security": [ | ||||
| 		{ | ||||
| 			"BearerAuth": [ | ||||
| 				"certificates" | ||||
| 			] | ||||
| 		} | ||||
| 	], | ||||
| 	"responses": { | ||||
| 		"200": { | ||||
| 			"description": "200 response", | ||||
| 			"content": { | ||||
| 				"application/json": { | ||||
| 					"examples": { | ||||
| 						"default": { | ||||
| 							"value": [ | ||||
| 								{ | ||||
| 									"id": "vultr", | ||||
| 									"name": "Vultr", | ||||
| 									"credentials": "dns_vultr_key = YOUR_VULTR_API_KEY" | ||||
| 								}, | ||||
| 								{ | ||||
| 									"id": "websupport", | ||||
| 									"name": "Websupport.sk", | ||||
| 									"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>" | ||||
| 								}, | ||||
| 								{ | ||||
| 									"id": "wedos", | ||||
| 									"name": "Wedos", | ||||
| 									"credentials": "dns_wedos_user = <wedos_registration>\ndns_wedos_auth = <wapi_password>" | ||||
| 								}, | ||||
| 								{ | ||||
| 									"id": "zoneedit", | ||||
| 									"name": "ZoneEdit", | ||||
| 									"credentials": "dns_zoneedit_user = <login-user-id>\ndns_zoneedit_token = <dyn-authentication-token>" | ||||
| 								} | ||||
| 							] | ||||
| 						} | ||||
| 					}, | ||||
| 					"schema": { | ||||
| 						"$ref": "../../../../components/dns-providers-list.json" | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -7,18 +7,24 @@ | ||||
| 			"BearerAuth": ["certificates"] | ||||
| 		} | ||||
| 	], | ||||
| 	"parameters": [ | ||||
| 		{ | ||||
| 			"in": "query", | ||||
| 			"name": "domains", | ||||
| 			"description": "Expansions", | ||||
| 			"required": true, | ||||
| 			"schema": { | ||||
| 				"type": "string", | ||||
| 				"example": "[\"test.example.ord\",\"test.example.com\",\"nonexistent.example.com\"]" | ||||
| 	"requestBody": { | ||||
| 		"description": "Test Payload", | ||||
| 		"required": true, | ||||
| 		"content": { | ||||
| 			"application/json": { | ||||
| 				"schema": { | ||||
| 					"type": "object", | ||||
| 					"additionalProperties": false, | ||||
| 					"required": ["domains"], | ||||
| 					"properties": { | ||||
| 						"domains": { | ||||
| 							"$ref": "../../../../common.json#/properties/domain_names" | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	], | ||||
| 	}, | ||||
| 	"responses": { | ||||
| 		"200": { | ||||
| 			"description": "200 response", | ||||
| @@ -61,14 +61,19 @@ | ||||
| 				"$ref": "./paths/nginx/certificates/post.json" | ||||
| 			} | ||||
| 		}, | ||||
| 		"/nginx/certificates/dns-providers": { | ||||
| 			"get": { | ||||
| 				"$ref": "./paths/nginx/certificates/dns-providers/get.json" | ||||
| 			} | ||||
| 		}, | ||||
| 		"/nginx/certificates/validate": { | ||||
| 			"post": { | ||||
| 				"$ref": "./paths/nginx/certificates/validate/post.json" | ||||
| 			} | ||||
| 		}, | ||||
| 		"/nginx/certificates/test-http": { | ||||
| 			"get": { | ||||
| 				"$ref": "./paths/nginx/certificates/test-http/get.json" | ||||
| 			"post": { | ||||
| 				"$ref": "./paths/nginx/certificates/test-http/post.json" | ||||
| 			} | ||||
| 		}, | ||||
| 		"/nginx/certificates/{certID}": { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| #!/usr/bin/node | ||||
|  | ||||
| // Usage: | ||||
| //   Install all plugins defined in `certbot-dns-plugins.json`: | ||||
| //   Install all plugins defined in `../certbot/dns-plugins.json`: | ||||
| //    ./install-certbot-plugins | ||||
| //   Install one or more specific plugins: | ||||
| //    ./install-certbot-plugins route53 cloudflare | ||||
| @@ -10,20 +10,21 @@ | ||||
| //    docker exec npm_core /command/s6-setuidgid 1000:1000 bash -c "/app/scripts/install-certbot-plugins" | ||||
| // | ||||
|  | ||||
| import dnsPlugins from "../global/certbot-dns-plugins.json" with { type: "json" }; | ||||
| import batchflow from "batchflow"; | ||||
| import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" }; | ||||
| import { installPlugin } from "../lib/certbot.js"; | ||||
| import { certbot as logger } from "../logger.js"; | ||||
| import batchflow from "batchflow"; | ||||
|  | ||||
| let hasErrors      = false; | ||||
| let failingPlugins = []; | ||||
| let hasErrors = false; | ||||
| const failingPlugins = []; | ||||
|  | ||||
| let pluginKeys = Object.keys(dnsPlugins); | ||||
| if (process.argv.length > 2) { | ||||
| 	pluginKeys = process.argv.slice(2); | ||||
| } | ||||
|  | ||||
| batchflow(pluginKeys).sequential() | ||||
| batchflow(pluginKeys) | ||||
| 	.sequential() | ||||
| 	.each((i, pluginKey, next) => { | ||||
| 		installPlugin(pluginKey) | ||||
| 			.then(() => { | ||||
| @@ -40,10 +41,14 @@ batchflow(pluginKeys).sequential() | ||||
| 	}) | ||||
| 	.end(() => { | ||||
| 		if (hasErrors) { | ||||
| 			logger.error('Some plugins failed to install. Please check the logs above. Failing plugins: ' + '\n - ' + failingPlugins.join('\n - ')); | ||||
| 			logger.error( | ||||
| 				"Some plugins failed to install. Please check the logs above. Failing plugins: " + | ||||
| 					"\n - " + | ||||
| 					failingPlugins.join("\n - "), | ||||
| 			); | ||||
| 			process.exit(1); | ||||
| 		} else { | ||||
| 			logger.complete('Plugins installed successfully'); | ||||
| 			logger.complete("Plugins installed successfully"); | ||||
| 			process.exit(0); | ||||
| 		} | ||||
| 	}); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user