11const os = require ( 'os' ) ;
2+ const fs = require ( 'fs' ) ;
3+ const crypto = require ( 'crypto' ) ;
24const jwt = require ( 'jsonwebtoken' ) ;
35const http = require ( 'http' )
46const https = require ( 'https' )
@@ -9,6 +11,207 @@ const { promisify } = require('util');
911const promBundle = require ( "express-prom-bundle" ) ;
1012const zlib = require ( "zlib" ) ;
1113
14+ // Get HTTPS credentials - either from files or generate self-signed in memory
15+ function getHttpsCredentials ( ) {
16+ const keyFile = process . env . HTTPS_KEY_FILE ;
17+ const certFile = process . env . HTTPS_CERT_FILE ;
18+
19+ // If both files are specified and exist, use them
20+ if ( keyFile && certFile ) {
21+ try {
22+ return {
23+ key : fs . readFileSync ( keyFile ) ,
24+ cert : fs . readFileSync ( certFile )
25+ } ;
26+ } catch ( err ) {
27+ console . log ( `Could not read cert files (${ err . message } ), generating self-signed certificate...` ) ;
28+ }
29+ }
30+
31+ // Try default file locations for backward compatibility
32+ try {
33+ return {
34+ key : fs . readFileSync ( 'privkey.pem' ) ,
35+ cert : fs . readFileSync ( 'fullchain.pem' )
36+ } ;
37+ } catch ( err ) {
38+ // Generate self-signed certificate in memory
39+ console . log ( 'Generating self-signed certificate in memory...' ) ;
40+ return generateSelfSignedCertificate ( ) ;
41+ }
42+ }
43+
44+ // Generate a self-signed certificate entirely in memory
45+ function generateSelfSignedCertificate ( ) {
46+ const { privateKey } = crypto . generateKeyPairSync ( 'rsa' , {
47+ modulusLength : 2048 ,
48+ publicKeyEncoding : { type : 'spki' , format : 'pem' } ,
49+ privateKeyEncoding : { type : 'pkcs8' , format : 'pem' }
50+ } ) ;
51+
52+ const publicKey = crypto . createPublicKey ( crypto . createPrivateKey ( privateKey ) ) ;
53+
54+ // Get the public key in DER format for the certificate
55+ const publicKeyDer = publicKey . export ( { type : 'spki' , format : 'der' } ) ;
56+
57+ // Create certificate structure
58+ const serialNumber = crypto . randomBytes ( 8 ) ;
59+ const now = new Date ( ) ;
60+ const notBefore = now ;
61+ const notAfter = new Date ( now . getTime ( ) + 365 * 24 * 60 * 60 * 1000 ) ;
62+
63+ // Build ASN.1 TBSCertificate
64+ const tbsCert = buildTBSCertificate ( serialNumber , notBefore , notAfter , publicKeyDer ) ;
65+
66+ // Sign the TBSCertificate
67+ const sign = crypto . createSign ( 'SHA256' ) ;
68+ sign . update ( tbsCert ) ;
69+ const signature = sign . sign ( privateKey ) ;
70+
71+ // Build the full certificate
72+ const cert = buildCertificate ( tbsCert , signature ) ;
73+
74+ // Convert to PEM
75+ const certBase64 = cert . toString ( 'base64' ) ;
76+ const certPem = '-----BEGIN CERTIFICATE-----\n' +
77+ certBase64 . match ( / .{ 1 , 64 } / g) . join ( '\n' ) +
78+ '\n-----END CERTIFICATE-----\n' ;
79+
80+ return { key : privateKey , cert : certPem } ;
81+ }
82+
83+ // ASN.1 DER encoding helpers
84+ function encodeLength ( len ) {
85+ if ( len < 128 ) return Buffer . from ( [ len ] ) ;
86+ if ( len < 256 ) return Buffer . from ( [ 0x81 , len ] ) ;
87+ if ( len < 65536 ) return Buffer . from ( [ 0x82 , ( len >> 8 ) & 0xff , len & 0xff ] ) ;
88+ throw new Error ( 'Length too long' ) ;
89+ }
90+
91+ function encodeSequence ( contents ) {
92+ const len = encodeLength ( contents . length ) ;
93+ return Buffer . concat ( [ Buffer . from ( [ 0x30 ] ) , len , contents ] ) ;
94+ }
95+
96+ function encodeInteger ( buf ) {
97+ // Add leading zero if high bit is set
98+ if ( buf [ 0 ] & 0x80 ) {
99+ buf = Buffer . concat ( [ Buffer . from ( [ 0x00 ] ) , buf ] ) ;
100+ }
101+ const len = encodeLength ( buf . length ) ;
102+ return Buffer . concat ( [ Buffer . from ( [ 0x02 ] ) , len , buf ] ) ;
103+ }
104+
105+ function encodeOID ( oid ) {
106+ const parts = oid . split ( '.' ) . map ( Number ) ;
107+ const bytes = [ parts [ 0 ] * 40 + parts [ 1 ] ] ;
108+ for ( let i = 2 ; i < parts . length ; i ++ ) {
109+ let val = parts [ i ] ;
110+ if ( val < 128 ) {
111+ bytes . push ( val ) ;
112+ } else {
113+ const encoded = [ ] ;
114+ while ( val > 0 ) {
115+ encoded . unshift ( ( val & 0x7f ) | ( encoded . length ? 0x80 : 0 ) ) ;
116+ val >>= 7 ;
117+ }
118+ bytes . push ( ...encoded ) ;
119+ }
120+ }
121+ const buf = Buffer . from ( bytes ) ;
122+ return Buffer . concat ( [ Buffer . from ( [ 0x06 ] ) , encodeLength ( buf . length ) , buf ] ) ;
123+ }
124+
125+ function encodeUTCTime ( date ) {
126+ const str = date . toISOString ( ) . replace ( / [ - : T ] / g, '' ) . slice ( 2 , 14 ) + 'Z' ;
127+ const buf = Buffer . from ( str , 'ascii' ) ;
128+ return Buffer . concat ( [ Buffer . from ( [ 0x17 ] ) , encodeLength ( buf . length ) , buf ] ) ;
129+ }
130+
131+ function encodePrintableString ( str ) {
132+ const buf = Buffer . from ( str , 'ascii' ) ;
133+ return Buffer . concat ( [ Buffer . from ( [ 0x13 ] ) , encodeLength ( buf . length ) , buf ] ) ;
134+ }
135+
136+ function encodeSet ( contents ) {
137+ const len = encodeLength ( contents . length ) ;
138+ return Buffer . concat ( [ Buffer . from ( [ 0x31 ] ) , len , contents ] ) ;
139+ }
140+
141+ function encodeBitString ( buf ) {
142+ // Prepend with 0x00 to indicate no unused bits
143+ const content = Buffer . concat ( [ Buffer . from ( [ 0x00 ] ) , buf ] ) ;
144+ return Buffer . concat ( [ Buffer . from ( [ 0x03 ] ) , encodeLength ( content . length ) , content ] ) ;
145+ }
146+
147+ function buildRDN ( oid , value ) {
148+ const attrType = encodeOID ( oid ) ;
149+ const attrValue = encodePrintableString ( value ) ;
150+ const attrTypeAndValue = encodeSequence ( Buffer . concat ( [ attrType , attrValue ] ) ) ;
151+ return encodeSet ( attrTypeAndValue ) ;
152+ }
153+
154+ function buildName ( ) {
155+ // CN=my.example.com,O=Mendhak,L=London,ST=London,C=GB
156+ const cn = buildRDN ( '2.5.4.3' , 'my.example.com' ) ; // commonName
157+ const o = buildRDN ( '2.5.4.10' , 'Mendhak' ) ; // organizationName
158+ const l = buildRDN ( '2.5.4.7' , 'London' ) ; // localityName
159+ const st = buildRDN ( '2.5.4.8' , 'London' ) ; // stateOrProvinceName
160+ const c = buildRDN ( '2.5.4.6' , 'GB' ) ; // countryName
161+ return encodeSequence ( Buffer . concat ( [ c , st , l , o , cn ] ) ) ;
162+ }
163+
164+ function buildValidity ( notBefore , notAfter ) {
165+ return encodeSequence ( Buffer . concat ( [
166+ encodeUTCTime ( notBefore ) ,
167+ encodeUTCTime ( notAfter )
168+ ] ) ) ;
169+ }
170+
171+ function buildAlgorithmIdentifier ( ) {
172+ // sha256WithRSAEncryption
173+ const oid = encodeOID ( '1.2.840.113549.1.1.11' ) ;
174+ const params = Buffer . from ( [ 0x05 , 0x00 ] ) ; // NULL
175+ return encodeSequence ( Buffer . concat ( [ oid , params ] ) ) ;
176+ }
177+
178+ function buildTBSCertificate ( serialNumber , notBefore , notAfter , publicKeyDer ) {
179+ // Version (v3 = 2)
180+ const version = Buffer . concat ( [
181+ Buffer . from ( [ 0xa0 , 0x03 , 0x02 , 0x01 , 0x02 ] )
182+ ] ) ;
183+
184+ const serial = encodeInteger ( serialNumber ) ;
185+ const signatureAlg = buildAlgorithmIdentifier ( ) ;
186+ const issuer = buildName ( ) ;
187+ const validity = buildValidity ( notBefore , notAfter ) ;
188+ const subject = buildName ( ) ;
189+
190+ // SubjectPublicKeyInfo is already in DER format
191+ const subjectPublicKeyInfo = publicKeyDer ;
192+
193+ return encodeSequence ( Buffer . concat ( [
194+ version ,
195+ serial ,
196+ signatureAlg ,
197+ issuer ,
198+ validity ,
199+ subject ,
200+ subjectPublicKeyInfo
201+ ] ) ) ;
202+ }
203+
204+ function buildCertificate ( tbsCert , signature ) {
205+ const signatureAlg = buildAlgorithmIdentifier ( ) ;
206+ const signatureValue = encodeBitString ( signature ) ;
207+
208+ return encodeSequence ( Buffer . concat ( [
209+ tbsCert ,
210+ signatureAlg ,
211+ signatureValue
212+ ] ) ) ;
213+ }
214+
12215const {
13216 PROMETHEUS_ENABLED = false ,
14217 PROMETHEUS_METRICS_PATH = '/metrics' ,
@@ -40,7 +243,15 @@ if(PROMETHEUS_ENABLED === 'true') {
40243}
41244
42245if ( process . env . DISABLE_REQUEST_LOGS !== 'true' ) {
43- app . use ( morgan ( 'combined' ) ) ;
246+ app . use ( morgan ( 'combined' , {
247+ skip : function ( req , res ) {
248+ // Skip logging for paths matching LOG_IGNORE_PATH
249+ if ( process . env . LOG_IGNORE_PATH && new RegExp ( process . env . LOG_IGNORE_PATH ) . test ( req . path ) ) {
250+ return true ;
251+ }
252+ return false ;
253+ }
254+ } ) ) ;
44255}
45256
46257app . use ( function ( req , res , next ) {
@@ -197,9 +408,10 @@ let httpOpts = {
197408 maxHeaderSize : maxHeaderSize
198409}
199410
411+ const httpsCredentials = getHttpsCredentials ( ) ;
200412let httpsOpts = {
201- key : require ( 'fs' ) . readFileSync ( process . env . HTTPS_KEY_FILE || 'privkey.pem' ) ,
202- cert : require ( 'fs' ) . readFileSync ( process . env . HTTPS_CERT_FILE || 'fullchain.pem' ) ,
413+ key : httpsCredentials . key ,
414+ cert : httpsCredentials . cert ,
203415 maxHeaderSize : maxHeaderSize
204416} ;
205417
0 commit comments