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' ] ;
@@ -1119,6 +1121,87 @@ const internalCertificate = {
11191121 } else {
11201122 return Promise . resolve ( ) ;
11211123 }
1124+ } ,
1125+
1126+ testHttpsChallenge : async ( access , domains ) => {
1127+ await access . can ( 'certificates:list' ) ;
1128+
1129+ if ( ! isArray ( domains ) ) {
1130+ throw new error . InternalValidationError ( 'Domains must be an array of strings' ) ;
1131+ }
1132+ if ( domains . length === 0 ) {
1133+ throw new error . InternalValidationError ( 'No domains provided' ) ;
1134+ }
1135+
1136+ // Create a test challenge file
1137+ const testChallengeDir = '/data/letsencrypt-acme-challenge/.well-known/acme-challenge' ;
1138+ const testChallengeFile = testChallengeDir + '/test-challenge' ;
1139+ fs . mkdirSync ( testChallengeDir , { recursive : true } ) ;
1140+ fs . writeFileSync ( testChallengeFile , 'Success' , { encoding : 'utf8' } ) ;
1141+
1142+ async function performTestForDomain ( domain ) {
1143+ logger . info ( 'Testing http challenge for ' + domain ) ;
1144+ const url = `http://${ domain } /.well-known/acme-challenge/test-challenge` ;
1145+ const formBody = `method=G&url=${ encodeURI ( url ) } &bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false` ;
1146+ const options = {
1147+ method : 'POST' ,
1148+ headers : {
1149+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
1150+ 'Content-Length' : Buffer . byteLength ( formBody )
1151+ }
1152+ } ;
1153+
1154+ const result = await new Promise ( ( resolve ) => {
1155+
1156+ const req = https . request ( 'https://www.site24x7.com/tools/restapi-tester' , options , function ( res ) {
1157+ let responseBody = '' ;
1158+
1159+ res . on ( 'data' , ( chunk ) => responseBody = responseBody + chunk ) ;
1160+ res . on ( 'end' , function ( ) {
1161+ const parsedBody = JSON . parse ( responseBody + '' ) ;
1162+ if ( res . statusCode !== 200 ) {
1163+ logger . warn ( `Failed to test HTTP challenge for domain ${ domain } ` , res ) ;
1164+ resolve ( undefined ) ;
1165+ }
1166+ resolve ( parsedBody ) ;
1167+ } ) ;
1168+ } ) ;
1169+
1170+ // Make sure to write the request body.
1171+ req . write ( formBody ) ;
1172+ req . end ( ) ;
1173+ req . on ( 'error' , function ( e ) { logger . warn ( `Failed to test HTTP challenge for domain ${ domain } ` , e ) ;
1174+ resolve ( undefined ) ; } ) ;
1175+ } ) ;
1176+
1177+ if ( ! result ) {
1178+ // Some error occurred while trying to get the data
1179+ return 'failed' ;
1180+ } else if ( `${ result . responsecode } ` === '200' && result . htmlresponse === 'Success' ) {
1181+ // Server exists and has responded with the correct data
1182+ return 'ok' ;
1183+ } else if ( `${ result . responsecode } ` === '404' ) {
1184+ // Server exists but responded with a 404
1185+ return '404' ;
1186+ } else if ( `${ result . responsecode } ` === '0' || result . reason . toLowerCase ( ) === 'host unavailable' ) {
1187+ // Server does not exist at domain
1188+ return 'no-host' ;
1189+ } else {
1190+ // Other errors
1191+ return `other:${ result . responsecode } ` ;
1192+ }
1193+ }
1194+
1195+ const results = { } ;
1196+
1197+ for ( const domain of domains ) {
1198+ results [ domain ] = await performTestForDomain ( domain ) ;
1199+ }
1200+
1201+ // Remove the test challenge file
1202+ fs . unlinkSync ( testChallengeFile ) ;
1203+
1204+ return results ;
11221205 }
11231206} ;
11241207
0 commit comments