node-http-server – Diff between revs 6 and 7

Subversion Repositories:
Rev:
Show entire fileRegard whitespace
Rev 6 Rev 7
Line 1... Line 1...
1 #!/usr/bin/env node 1 #!/usr/bin/env node
-   2  
2 /////////////////////////////////////////////////////////////////////////// 3 ///////////////////////////////////////////////////////////////////////////
3 // Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3 // 4 // Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3 //
4 /////////////////////////////////////////////////////////////////////////// 5 ///////////////////////////////////////////////////////////////////////////
Line 5... Line 6...
5   6  
6 // Import packages. 7 // Import packages.
7 const auth = require("http-auth"); 8 const auth = require("http-auth");
-   9 const https = require('https');
8 const https = require('https'); 10 const http = require('http');
9 const path = require('path'); 11 const path = require('path');
10 const fs = require('fs'); 12 const fs = require('fs');
11 const mime = require('mime'); 13 const mime = require('mime');
12 const url = require('url'); 14 const url = require('url');
13 const moment = require('moment'); 15 const moment = require('moment');
14 const winston = require('winston'); 16 const winston = require('winston');
15 const yargs = require('yargs'); 17 const yargs = require('yargs');
-   18 const forge = require('node-forge');
Line 16... Line 19...
16 const forge = require('node-forge'); 19 const dns = require('dns');
17   20  
18 // Get command-line arguments. 21 // Get command-line arguments.
19 const argv = yargs 22 const argv = yargs
Line 25... Line 28...
25 }) 28 })
26 .help() 29 .help()
27 .argv 30 .argv
Line 28... Line 31...
28   31  
-   32 // Configuration file.
-   33 const config = require(
29 // Configuration file. 34 path
-   35 .resolve(__dirname, 'config')
Line 30... Line 36...
30 const config = require(path.resolve(__dirname, 'config')); 36 );
31   37  
32 // Check for path traversal. 38 // Check for path traversal.
33 function isRooted(userPath, rootPath, separator) { 39 function isRooted(userPath, rootPath, separator) {
34 userPath = userPath.split(separator).filter(Boolean); 40 userPath = userPath.split(separator).filter(Boolean);
35 rootPath = rootPath.split(separator).filter(Boolean); 41 rootPath = rootPath.split(separator).filter(Boolean);
36 return userPath.length >= rootPath.length && 42 return userPath.length >= rootPath.length &&
Line -... Line 43...
-   43 rootPath.every((e, i) => e === userPath[i]);
37 rootPath.every((e, i) => e === userPath[i]); 44 }
38 } 45  
-   46 // Generate certificates on the fly using incremental serials.
-   47 function generateCertificates(name, domain, size) {
-   48 // Generate 1024-bit key-pair.
39   49 const keys = forge
40 function generateCertificates(name, domain) { 50 .pki
-   51 .rsa
-   52 .generateKeyPair(size);
41 // Generate 1024-bit key-pair. 53 // Create self-signed certificate.
-   54 const cert = forge
42 var keys = forge.pki.rsa.generateKeyPair(1024); 55 .pki
-   56 .createCertificate();
-   57 cert.serialNumber = moment().format('x');
43 // Create self-signed certificate. 58 cert.publicKey = keys.publicKey;
-   59 cert
-   60 .validity
44 var cert = forge.pki.createCertificate(); 61 .notBefore = moment().toDate();
45 cert.publicKey = keys.publicKey; 62 cert
-   63 .validity
-   64 .notAfter
-   65 .setFullYear(
-   66 cert
46 cert.validity.notBefore = new Date(); 67 .validity
-   68 .notBefore
47 cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); 69 .getFullYear() + 1
48 cert.setSubject([ 70 );
49 { -  
50 name: 'commonName', 71 cert.setSubject([{
51 value: domain 72 name: 'commonName',
52 }, 73 value: domain
53 { -  
54 name: 'organizationName', 74 }, {
55 value: name 75 name: 'organizationName',
56 } -  
57 ]); 76 value: name
58 cert.setIssuer([ 77 }]);
59 { -  
60 name: 'commonName', 78 cert.setIssuer([{
61 value: name 79 name: 'commonName',
62 }, 80 value: domain
63 { -  
64 name: 'organizationName', 81 }, {
Line 65... Line 82...
65 value: name 82 name: 'organizationName',
-   83 value: name
66 } 84 }]);
-   85  
-   86 // Self-sign certificate.
-   87 cert.sign(
-   88 keys.privateKey,
-   89 forge
Line 67... Line 90...
67 ]); 90 .md
68   91 .sha256
-   92 .create()
-   93 );
69 // Self-sign certificate. 94  
-   95 // Return PEM-format keys and certificates.
-   96 return {
-   97 privateKey: forge
-   98 .pki
-   99 .privateKeyToPem(
70 cert.sign(keys.privateKey); 100 keys
-   101 .privateKey
-   102 ),
-   103 publicKey: forge
-   104 .pki
-   105 .publicKeyToPem(
71   106 keys
72 // Return PEM-format keys and certificates. 107 .publicKey
73 return { 108 ),
Line 74... Line 109...
74 privateKey: forge.pki.privateKeyToPem(keys.privateKey), 109 certificate: forge
75 publicKey: forge.pki.publicKeyToPem(keys.publicKey), 110 .pki
76 certificate: forge.pki.certificateToPem(cert) 111 .certificateToPem(cert)
77 }; 112 };
78 } 113 }
79   114  
80 // Create various logging mechanisms. 115 // Create various logging mechanisms.
81 const log = new winston.Logger({ 116 const log = new winston.Logger({
82 transports: [ 117 transports: [
83 new winston.transports.File({ 118 new winston.transports.File({
84 level: 'info', 119 level: 'info',
Line 99... Line 134...
99 }) 134 })
100 ], 135 ],
101 exitOnError: false 136 exitOnError: false
102 }); 137 });
Line 103... Line 138...
103   138  
104 fs.realpath(argv.root, (error, documentRoot) => { -  
105 if (error) { -  
106 log.error('Could not find document root: ' + argv.root); -  
107 process.exit(1); -  
108 } -  
109   -  
110 var authentication = auth.digest({ -  
111 realm: "was", -  
112 file: path.resolve(__dirname, config.password_file) -  
113 }); -  
114 -  
115 const certs = generateCertificates("was", 'localhost'); -  
116   -  
117 // HTTPs server using digest authentication. -  
118 https.createServer(authentication, { -  
119 key: certs.privateKey, -  
120 cert: certs.certificate, -  
121 }, (request, response) => { 139 function handleClient(request, response, documentRoot) {
122 const requestAddress = request.socket.address(); 140 const requestAddress = request.socket.address();
Line -... Line 141...
-   141 const requestedURL = url.parse(request.url, true);
123 const requestedURL = url.parse(request.url, true); 142  
-   143 log.info('Client: ' +
-   144 requestAddress.address + ':' +
-   145 requestAddress.port +
-   146 ' accessing: ' +
124   147 requestedURL.pathname
125 log.info('Client: ' + requestAddress.address + ':' + requestAddress.port + ' accessing: ' + requestedURL.pathname); 148 );
-   149  
-   150 const trimmedPath = requestedURL
-   151 .pathname
-   152 .split('/')
126   153 .filter(Boolean)
127 const trimmedPath = requestedURL.pathname.split('/').filter(Boolean).join('/'); 154 .join('/');
128 const filesystemPath = trimmedPath === '/' ? 155 const filesystemPath = trimmedPath === '/' ?
Line 129... Line 156...
129 path.join(documentRoot, trimmedPath) : 156 path.join(documentRoot, trimmedPath) :
130 path.resolve(documentRoot, trimmedPath); 157 path.resolve(documentRoot, trimmedPath);
-   158  
-   159 if (!isRooted(filesystemPath, documentRoot, path.sep)) {
-   160 log.warn('Attempted path traversal: ' +
-   161 requestAddress.address + ':' +
-   162 requestAddress.port +
131   163 ' requesting: ' +
132 if (!isRooted(filesystemPath, documentRoot, path.sep)) { 164 requestedURL.pathname
133 log.warn('Attempted path traversal: ' + requestAddress.address + ':' + requestAddress.port + ' requesting: ' + requestedURL.pathname); 165 );
134 response.statusCode = 403; 166 response.statusCode = 403;
Line 143... Line 175...
143 response.end(); 175 response.end();
144 return; 176 return;
145 } 177 }
Line 146... Line 178...
146   178  
147 switch (stats.isDirectory()) { 179 switch (stats.isDirectory()) {
148 case true: // Browser requesting directory. 180 case true: // Directory is requested so provide directory indexes.
149 const documentRoot = path.resolve(filesystemPath, config.default_document); 181 const documentRoot = path.resolve(filesystemPath, config.site.index);
150 fs.stat(documentRoot, (error, stats) => { 182 fs.stat(documentRoot, (error, stats) => {
151 if (error) { 183 if (error) {
152 fs.readdir(filesystemPath, (error, paths) => { 184 fs.readdir(filesystemPath, (error, paths) => {
153 if (error) { 185 if (error) {
Line 213... Line 245...
213   245  
214 }); 246 });
215 break; 247 break;
216 } 248 }
-   249 });
-   250 }
-   251  
-   252 fs.realpath(argv.root, (error, documentRoot) => {
-   253 if (error) {
-   254 log.error('Could not find document root: ' + argv.root);
-   255 process.exit(1);
Line -... Line 256...
-   256 }
-   257  
-   258 // Create digest authentication.
-   259 const authentication = auth.digest({
-   260 realm: config.auth.realm,
-   261 file: path.resolve(__dirname, config.auth.digest)
-   262 });
-   263  
-   264 // Start HTTP server.
-   265 http.createServer(
-   266 // authentication,
217 }); 267 (request, response) =>
-   268 handleClient(request, response, documentRoot)
-   269 ).listen(config.net.port, config.net.address, () => {
-   270 log.info('HTTP Server is listening on: ' +
-   271 config.net.address +
-   272 ' and port: ' +
-   273 config.net.port +
-   274 ' whilst serving files from: ' +
-   275 documentRoot
-   276 );
-   277 });
-   278  
-   279 // Start HTTPs server if enabled.
-   280 config.ssl.enable && (() => {
-   281 // Generate certificates for HTTPs.
-   282 const certs = generateCertificates(
-   283 config.site.name,
-   284 config.net.address,
-   285 config.ssl.privateKeySize
-   286 );
-   287  
-   288 https.createServer(
-   289 // authentication,
-   290 {
-   291 key: certs.privateKey,
-   292 cert: certs.certificate,
-   293 },
218   294 (request, response) =>
-   295 handleClient(request, response, documentRoot)
-   296 ).listen(config.ssl.port, config.ssl.address, () => {
-   297 // Print information on server startup.
-   298 log.info('HTTPs Server is listening on: ' +
-   299 config.net.address +
-   300 ' and port: ' +
-   301 config.net.port +
-   302 ' whilst serving files from: ' +
219 }).listen(config.port, config.address, () => { 303 documentRoot
-   304 );
-   305 });
220 log.info('Server is listening on: ' + config.address + ' and port: ' + config.port + ' whilst serving files from: ' + documentRoot); 306 })();