11const _ = require ( 'lodash' ) ;
22const fs = require ( 'fs' ) ;
3+ const https = require ( 'https' ) ;
34const tempWrite = require ( 'temp-write' ) ;
45const moment = require ( 'moment' ) ;
56const logger = require ( '../logger' ) . ssl ;
@@ -15,6 +16,7 @@ const letsencryptConfig = '/etc/letsencrypt.ini';
1516const certbotCommand = 'certbot' ;
1617const archiver = require ( 'archiver' ) ;
1718const path = require ( 'path' ) ;
19+ const { isArray } = require ( 'lodash' ) ;
1820
1921function omissions ( ) {
2022 return [ 'is_deleted' ] ;
@@ -1124,6 +1126,94 @@ const internalCertificate = {
11241126 } else {
11251127 return Promise . resolve ( ) ;
11261128 }
1129+ } ,
1130+
1131+ testHttpsChallenge : async ( access , domains ) => {
1132+ await access . can ( 'certificates:list' ) ;
1133+
1134+ if ( ! isArray ( domains ) ) {
1135+ throw new error . InternalValidationError ( 'Domains must be an array of strings' ) ;
1136+ }
1137+ if ( domains . length === 0 ) {
1138+ throw new error . InternalValidationError ( 'No domains provided' ) ;
1139+ }
1140+
1141+ // Create a test challenge file
1142+ const testChallengeDir = '/data/letsencrypt-acme-challenge/.well-known/acme-challenge' ;
1143+ const testChallengeFile = testChallengeDir + '/test-challenge' ;
1144+ fs . mkdirSync ( testChallengeDir , { recursive : true } ) ;
1145+ fs . writeFileSync ( testChallengeFile , 'Success' , { encoding : 'utf8' } ) ;
1146+
1147+ async function performTestForDomain ( domain ) {
1148+ logger . info ( 'Testing http challenge for ' + domain ) ;
1149+ const url = `http://${ domain } /.well-known/acme-challenge/test-challenge` ;
1150+ const formBody = `method=G&url=${ encodeURI ( url ) } &bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false` ;
1151+ const options = {
1152+ method : 'POST' ,
1153+ headers : {
1154+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
1155+ 'Content-Length' : Buffer . byteLength ( formBody )
1156+ }
1157+ } ;
1158+
1159+ const result = await new Promise ( ( resolve ) => {
1160+
1161+ const req = https . request ( 'https://www.site24x7.com/tools/restapi-tester' , options , function ( res ) {
1162+ let responseBody = '' ;
1163+
1164+ res . on ( 'data' , ( chunk ) => responseBody = responseBody + chunk ) ;
1165+ res . on ( 'end' , function ( ) {
1166+ const parsedBody = JSON . parse ( responseBody + '' ) ;
1167+ if ( res . statusCode !== 200 ) {
1168+ logger . warn ( `Failed to test HTTP challenge for domain ${ domain } ` , res ) ;
1169+ resolve ( undefined ) ;
1170+ }
1171+ resolve ( parsedBody ) ;
1172+ } ) ;
1173+ } ) ;
1174+
1175+ // Make sure to write the request body.
1176+ req . write ( formBody ) ;
1177+ req . end ( ) ;
1178+ req . on ( 'error' , function ( e ) { logger . warn ( `Failed to test HTTP challenge for domain ${ domain } ` , e ) ;
1179+ resolve ( undefined ) ; } ) ;
1180+ } ) ;
1181+
1182+ if ( ! result ) {
1183+ // Some error occurred while trying to get the data
1184+ return 'failed' ;
1185+ } else if ( `${ result . responsecode } ` === '200' && result . htmlresponse === 'Success' ) {
1186+ // Server exists and has responded with the correct data
1187+ return 'ok' ;
1188+ } else if ( `${ result . responsecode } ` === '200' ) {
1189+ // Server exists but has responded with wrong data
1190+ logger . info ( `HTTP challenge test failed for domain ${ domain } because of invalid returned data:` , result . htmlresponse ) ;
1191+ return 'wrong-data' ;
1192+ } else if ( `${ result . responsecode } ` === '404' ) {
1193+ // Server exists but responded with a 404
1194+ logger . info ( `HTTP challenge test failed for domain ${ domain } because code 404 was returned` ) ;
1195+ return '404' ;
1196+ } else if ( `${ result . responsecode } ` === '0' || ( typeof result . reason === 'string' && result . reason . toLowerCase ( ) === 'host unavailable' ) ) {
1197+ // Server does not exist at domain
1198+ logger . info ( `HTTP challenge test failed for domain ${ domain } the host was not found` ) ;
1199+ return 'no-host' ;
1200+ } else {
1201+ // Other errors
1202+ logger . info ( `HTTP challenge test failed for domain ${ domain } because code ${ result . responsecode } was returned` ) ;
1203+ return `other:${ result . responsecode } ` ;
1204+ }
1205+ }
1206+
1207+ const results = { } ;
1208+
1209+ for ( const domain of domains ) {
1210+ results [ domain ] = await performTestForDomain ( domain ) ;
1211+ }
1212+
1213+ // Remove the test challenge file
1214+ fs . unlinkSync ( testChallengeFile ) ;
1215+
1216+ return results ;
11271217 }
11281218} ;
11291219
0 commit comments