1111
1212import requests
1313
14- from certapi import AcmeCertIssuer , CertApiException , CloudflareChallengeSolver , InMemoryChallengeSolver , Key
14+ from certapi import (
15+ AcmeCertIssuer ,
16+ CertApiException ,
17+ CloudflareChallengeSolver ,
18+ FileSystemKeyStore ,
19+ InMemoryChallengeSolver ,
20+ )
21+ from certapi .crypto import certs_from_pem
1522
1623
1724def is_root () -> bool :
@@ -95,14 +102,29 @@ def obtain_certificate(domains: List[str], api_key: Optional[str] = None):
95102 print ("Starting HTTP challenge server on port 80..." )
96103 server , _ = _start_http_challenge_server (challenge_solver , port = 80 )
97104
98- cert_issuer = AcmeCertIssuer (Key .generate ("ecdsa" ), challenge_solver )
105+ keystore_path = "/etc/ssl"
106+ key_store = FileSystemKeyStore (keystore_path )
107+ cert_issuer = AcmeCertIssuer .with_keystore (
108+ key_store ,
109+ challenge_solver ,
110+ account_key_name = "acme_account" ,
111+ )
99112 cert_issuer .setup ()
100113 try :
101- key , cert = cert_issuer .generate_key_and_cert_for_domains (domains )
102- print ("------ Private Key -----" )
103- print (key .to_pem ().decode ("utf-8" ))
104- print ("------- Certificate ------" )
105- print (cert )
114+ key , cert = cert_issuer .generate_key_and_cert_for_domains (domains , key_type = "rsa" )
115+ key_name = domains [0 ]
116+ key_id = key_store .save_key (key , key_name )
117+ key_store .save_cert (key_id , cert , domains , name = key_name )
118+
119+ cert_chain = certs_from_pem (cert .encode ("utf-8" ))
120+ leaf_cert = cert_chain [0 ] if cert_chain else None
121+ expiry = leaf_cert .not_valid_after .isoformat () if leaf_cert else "unknown"
122+
123+ key_path = os .path .join (key_store .keys_dir , f"{ key_name } .key" )
124+ cert_path = os .path .join (key_store .certs_dir , f"{ key_name } .crt" )
125+ print (f"\n Certificate expires at: { expiry } " )
126+ print (f" Key path: { key_path } " )
127+ print (f" Cert path: { cert_path } " )
106128 except CertApiException as e :
107129 print ("An error occurred:" )
108130 print (e .json_obj ())
@@ -113,60 +135,66 @@ def obtain_certificate(domains: List[str], api_key: Optional[str] = None):
113135
114136
115137def verify_environment (domains : List [str ], api_key : Optional [str ] = None ) -> None :
116- print ("certapi is installed and CLI is working. " )
138+ print ("[verify] certapi CLI ready " )
117139 if api_key :
118- print ("Cloudflare API key detected. DNS challenge is available. " )
140+ print ("[verify] Cloudflare API key detected; DNS-01 challenge available" )
119141 if domains :
120142 solver = CloudflareChallengeSolver (api_key = api_key )
121143 unsupported = [domain for domain in domains if not solver .supports_domain (domain )]
122144 if unsupported :
123- print ("Warning: Cloudflare account does not appear to manage:" )
145+ print ("[verify] Warning: Cloudflare account does not appear to manage:" )
124146 for domain in unsupported :
125- print (f"- { domain } " )
147+ print (f"[verify] - { domain } " )
126148 else :
127- print ("Cloudflare account appears to manage the provided domain(s). " )
149+ print ("[verify] Cloudflare account appears to manage the provided domain(s)" )
128150 else :
129- print ("Cloudflare API key not detected. HTTP challenge will be used. " )
151+ print ("[verify] Cloudflare API key not detected; HTTP-01 challenge will be used" )
130152 if not domains :
131- print ("No domains provided. Skipping HTTP routing check. " )
153+ print ("[verify] No domains provided; skipping HTTP routing check" )
132154 return
133155 if is_root ():
134156 pids = find_process_on_port (80 )
135157 if pids :
136- print (f"Warning: port 80 is in use by process(es): { ', ' .join (pids )} " )
158+ print (f"[verify] Warning: port 80 is in use by process(es): { ', ' .join (pids )} " )
137159 else :
138- print ("Port 80 is available. " )
160+ print ("[verify] Port 80 is available" )
139161 else :
140- print ("Warning: not running as root, port 80 binding will fail. " )
162+ print ("[verify] Warning: not running as root; port 80 binding will fail" )
141163 return
142164
143165 _ensure_port_80_available ()
144166 challenge_solver = InMemoryChallengeSolver ()
145- print ("Starting HTTP challenge server on port 80..." )
167+ print ("[verify] Starting HTTP challenge server on port 80..." )
146168 server , _ = _start_http_challenge_server (challenge_solver , port = 80 )
169+ ok = 0
170+ failed = 0
147171 try :
148172 for domain in domains :
149173 token = secrets .token_urlsafe (24 )
150174 value = secrets .token_urlsafe (32 )
151175 challenge_solver .save_challenge (token , value , domain )
152176 url = f"http://{ domain } /.well-known/acme-challenge/{ token } "
153- print (f"Verifying HTTP routing for { domain } ..." )
177+ print (f"[verify] Checking HTTP routing for { domain } ..." )
154178 try :
155179 response = requests .get (url , allow_redirects = False , timeout = 5 )
156180 if response .status_code == 200 and response .text .strip () == value :
157- print (f"OK: { domain } routes to this server." )
181+ ok += 1
182+ print (f"[verify] OK: { domain } routes to this server" )
158183 else :
184+ failed += 1
159185 print (
160- f"Failed : { domain } returned status { response .status_code } "
186+ f"[verify] FAILED : { domain } returned status { response .status_code } "
161187 f"with body '{ response .text .strip ()} '"
162188 )
163189 except requests .RequestException as exc :
164- print (f"Failed: { domain } request error: { exc } " )
190+ failed += 1
191+ print (f"[verify] FAILED: { domain } request error: { exc } " )
165192 finally :
166193 challenge_solver .delete_challenge (token , domain )
167194 finally :
168195 server .shutdown ()
169196 server .server_close ()
197+ print (f"\n Summary: { ok } OK, { failed } FAILED" )
170198
171199
172200def main ():
0 commit comments