@@ -531,7 +531,11 @@ pub fn execute_renewal<Client: AcmeIssuerClient>(
531531 . finalize_http_01_order ( & order, & challenge_store)
532532 . map_err ( |error| AcmeRenewalError :: Client {
533533 issuer : item. target . issuer . clone ( ) ,
534- message : error. to_string ( ) ,
534+ message : acme_client_error_message_with_http_01_context (
535+ error,
536+ & item. target . domains ,
537+ & published,
538+ ) ,
535539 } ) ?;
536540 let certificate = install_managed_certificate (
537541 storage,
@@ -713,7 +717,11 @@ async fn execute_instant_http_01_renewal(
713717 . await
714718 . map_err ( |error| AcmeRenewalError :: Client {
715719 issuer : item. target . issuer . clone ( ) ,
716- message : error. to_string ( ) ,
720+ message : acme_client_error_message_with_http_01_context (
721+ error,
722+ & item. target . domains ,
723+ & published,
724+ ) ,
717725 } ) ?;
718726 }
719727 }
@@ -724,15 +732,23 @@ async fn execute_instant_http_01_renewal(
724732 . await
725733 . map_err ( |error| AcmeRenewalError :: Client {
726734 issuer : item. target . issuer . clone ( ) ,
727- message : error. to_string ( ) ,
735+ message : acme_client_error_message_with_http_01_context (
736+ error,
737+ & item. target . domains ,
738+ & published,
739+ ) ,
728740 } ) ?;
729741 let private_key_pem = Zeroizing :: new (
730742 order
731743 . finalize ( )
732744 . await
733745 . map_err ( |error| AcmeRenewalError :: Client {
734746 issuer : item. target . issuer . clone ( ) ,
735- message : error. to_string ( ) ,
747+ message : acme_client_error_message_with_http_01_context (
748+ error,
749+ & item. target . domains ,
750+ & published,
751+ ) ,
736752 } ) ?
737753 . into_bytes ( ) ,
738754 ) ;
@@ -741,7 +757,11 @@ async fn execute_instant_http_01_renewal(
741757 . await
742758 . map_err ( |error| AcmeRenewalError :: Client {
743759 issuer : item. target . issuer . clone ( ) ,
744- message : error. to_string ( ) ,
760+ message : acme_client_error_message_with_http_01_context (
761+ error,
762+ & item. target . domains ,
763+ & published,
764+ ) ,
745765 } ) ?
746766 . into_bytes ( ) ;
747767 let certificate = install_managed_certificate (
@@ -1290,6 +1310,31 @@ fn cleanup_http_01_challenges(
12901310 Ok ( ( ) )
12911311}
12921312
1313+ fn acme_client_error_message_with_http_01_context (
1314+ error : impl fmt:: Display ,
1315+ domains : & [ String ] ,
1316+ tokens : & [ String ] ,
1317+ ) -> String {
1318+ let message = error. to_string ( ) ;
1319+ let urls = http_01_challenge_urls ( domains, tokens) ;
1320+ if urls. is_empty ( ) {
1321+ return message;
1322+ }
1323+ format ! ( "{message}; published_http_01={}" , urls. join( "," ) )
1324+ }
1325+
1326+ fn http_01_challenge_urls ( domains : & [ String ] , tokens : & [ String ] ) -> Vec < String > {
1327+ let mut urls = Vec :: new ( ) ;
1328+ for domain in domains {
1329+ for token in tokens {
1330+ urls. push ( format ! (
1331+ "http://{domain}/.well-known/acme-challenge/{token}"
1332+ ) ) ;
1333+ }
1334+ }
1335+ urls
1336+ }
1337+
12931338#[ cfg( feature = "acme-client" ) ]
12941339fn cleanup_tls_alpn_01_challenges (
12951340 store : & AcmeTlsAlpn01ChallengeStore ,
@@ -3116,6 +3161,9 @@ mod tests {
31163161 let error = execute_renewal ( & config, & item, & mut client) . unwrap_err ( ) ;
31173162
31183163 assert ! ( matches!( error, AcmeRenewalError :: Client { .. } ) ) ;
3164+ assert ! ( error. to_string( ) . contains(
3165+ "published_http_01=http://example.test/.well-known/acme-challenge/token_123"
3166+ ) ) ;
31193167 assert_eq ! ( client. finalize_calls, 1 ) ;
31203168 let store = AcmeHttp01ChallengeStore :: new ( & storage, "example" ) ;
31213169 assert_eq ! ( store. load_key_authorization( "token_123" ) . unwrap( ) , None ) ;
@@ -3124,6 +3172,21 @@ mod tests {
31243172 assert ! ( !paths. key_path. exists( ) ) ;
31253173 }
31263174
3175+ #[ test]
3176+ fn http_01_error_context_lists_all_published_urls ( ) {
3177+ let message = super :: acme_client_error_message_with_http_01_context (
3178+ "authorization failed" ,
3179+ & [ "example.test" . to_owned ( ) , "www.example.test" . to_owned ( ) ] ,
3180+ & [ "token-a" . to_owned ( ) , "token-b" . to_owned ( ) ] ,
3181+ ) ;
3182+
3183+ assert ! ( message. starts_with( "authorization failed; published_http_01=" ) ) ;
3184+ assert ! ( message. contains( "http://example.test/.well-known/acme-challenge/token-a" ) ) ;
3185+ assert ! ( message. contains( "http://example.test/.well-known/acme-challenge/token-b" ) ) ;
3186+ assert ! ( message. contains( "http://www.example.test/.well-known/acme-challenge/token-a" ) ) ;
3187+ assert ! ( message. contains( "http://www.example.test/.well-known/acme-challenge/token-b" ) ) ;
3188+ }
3189+
31273190 #[ test]
31283191 fn eab_file_secrets_are_trimmed_bounded_and_redacted ( ) {
31293192 let root = crate :: test_support:: unique_temp_path ( "acme-eab-file-secrets" ) ;
0 commit comments