במאמרים הקודמים הדגמנו איך מרימים קונטיינרים של דוקר על מנת לעבוד עם וורדפרס. במאמר הזה נדגים איך להרים מכונת node\express ולהכין אותה לעבודה עם https כדי לדמות לחלוטין שרת node. כמובן שאפשר להשתמש בעקרונות האלו כדי להקים כל שרת אחר ולאפשר לו לעבוד עם https.
אז קודם כל, בואו ונרים שרת express. איך עושים את זה? עם קובץ compose שמכיל קונטיינר. איזה קונטיינר? ובכן, בואו נבחר באחד פופולרי של אקספרס ובו ביצעתי התאמות מסוימות – כמו להעיף את mongo למשל שאותו אני לא צריך.
version: '2'
services:
myapp:
tty: true # Enables debugging capabilities when attached to this container.
image: 'bitnami/express:latest'
labels:
kompose.service.type: nodeport
command: npm run development
environment:
- PORT=3000
- NODE_ENV=development
- SKIP_DB_WAIT=0
- SKIP_DB_MIGRATION=0
- SKIP_NPM_INSTALL=0
- SKIP_BOWER_INSTALL=0
ports:
- 80:3000
- 443:8000
- 3000:3000
volumes:
- .:/app
להורדת הסקריפט לחץ כאן
כדאי למקם מספר דברים חשובים פה. הדבר החשוב ביותר הוא שיש לי כאן הוא מיפויים ב-ports. הכנסתי את 443 שימופה לפורט 8000, שהוא הפורט של האפליקציה שלי. כשיש לנו node\express. הוא עובד מאחורי נתב שמעביר את כל התנועה מהפורטים השונים (80, 8080 ו-443) לפורט 3000 או לכל פורט אחר שבחרנו. פורט 443 הוא חשוב במיוחד כי הוא הפורט של הפרוטוקול המאובטח HTTPS ואני מפנה אותו לפורט הפנימי של 8000.
מיפוי פורטים בדוקר – פורט 80 הולך לפורט 3000 ופורט 443, שמשמש את ה-https, הולך לפורט 8000.
אני אשמור את הקובץ הזה בתיקיה ריקה ואריץ בטרמינל של docker:
docker-compose up
האפליקציה תיבנה הישר בתיקיית app ואני בעצם אוכל לראות אותה אם אכנס ל-IP. אבל אני רוצה תמיכה מלאה ב-https. איך עושים את זה? מבחינת דוקר עשיתי את כל מה שאני יכול לעשות. עכשיו צריך לטפל באפליקציה.
בניגוד ל-Apache\NginX וחבריהם שיודעים לעבוד עם HTTPS ישר מהקופסה, עם node אני צריך לגרום ל-https לעבוד. איך? קודם כל אני צריך ליצור לעצמי מפתח.
למי שלא יודע, תקשורת https עובדת עם RSA (החיבור הראשוני עובד כך, התקשורת עצמה עובדת בהצפנה סימטרית) שמחייבת מפתח פומבי ומפתח פרטי. אני צריך ליצור אותם, אבל אל חשש - זה לא כזה מסובך. אצור תיקיה בפרויקט שלי שתקרא sslcert ואריץ בה את הפקודה הזו:
openssl req -newkey rsa:2048 -new -nodes -keyout key.pem -out csr.pem
מה שהפקודה הזו עושה זה ליצור מפתח הצפנה פרטי ופומבי. מפתח ההצפנה הפרטי הוא key.pem ואם תפתחו אותו תוכלו לראות שיש בו private key. מפתח ההצפנה הפומבי הוא csr.pem שמשמש אותנו ל- Certificate. זה כל מה שאנחנו צריכים.
עכשיו רק צריך לגרום לשרת לעבוד עם https וגם זה די פשוט. ישנן כמה גישות שאפשר לעבוד איתן, אך אנו נתמקד בגישה הפשוטה יותר, ליצור בקובץ האתחול שרת https נוסף זהה לחלוטין לזה של http. הוא מאזין ל-8000 ויתפוס את כל התקשורת המאובטחת. ככה זה נראה:
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var appSecured = require('../app'); // Creating appSecured
var debug = require('debug')('app:server');
var http = require('http');
var https = require('https'); // require native node's native https module
var fs = require('fs');
/**
* Get port from environment and store in Express.
*/
var privateKey = fs.readFileSync('/app/sslcert/key.pem', 'utf8');
var certificate = fs.readFileSync('/app/sslcert/server.crt', 'utf8');
/**
* Prepare the credentials
*/
var credentials = {key: privateKey, cert: certificate};
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
var portSecured = normalizePort(process.env.PORT_SECURED || '8000'); // defining port secured
app.set('port', port);
appSecured.set('port', portSecured); // appSecured listen to 8000 port
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Create HTTPS server.
*/
var serverSecured = https.createServer(credentials, appSecured);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* HTTPS Listen on provided port, on all network interfaces.
*/
serverSecured.listen(portSecured);
serverSecured.on('error', onError);
serverSecured.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
להורדת הסקריפט לחץ כאן
ממש לא צריך להיבהל. זה קובץ ההגדרות הרגיל שמגיע יחד עם הקונטיינר והוספתי לתוכו פשוט את הקוד שיוצר את השרת המאובטח שמאזין לפורט 800. זה כל הקוד שהוספתי:
var appSecured = require('../app'); // Creating appSecured
var https = require('https'); // require native node's native https module
var fs = require('fs');
/**
* Get port from environment and store in Express.
*/
var privateKey = fs.readFileSync('/app/sslcert/key.pem', 'utf8');
var certificate = fs.readFileSync('/app/sslcert/server.crt', 'utf8');
/**
* Prepare the credentials
*/
var credentials = {key: privateKey, cert: certificate};
var portSecured = normalizePort(process.env.PORT_SECURED || '8000'); // defining port secured
appSecured.set('port', portSecured); // appSecured listen to 8000 port
/**
* Create HTTPS server.
*/
var serverSecured = https.createServer(credentials, appSecured);
/**
* HTTPS Listen on provided port, on all network interfaces.
*/
serverSecured.listen(portSecured);
serverSecured.on('error', onError);
serverSecured.on('listening', onListening);
להורדת הסקריפט לחץ כאן
כשאני אכנס לכתובת של המכונה שלי עם https, אני אקבל שגיאת https כמובן שמראה שהכל תקין. זאת מכיוון שיש חיבור. מן הסתם הדפדפן שלי לא מכיר ולא סומך על ה- Certificate שלי, אבל אם אני אעשה proceed אני יכול להתחיל לעבוד.
בכניסה לשרת מאובטח בפעם הראשונה, אפשר לראות שאני מקבל הודעת שגיאה (שאפשר לעקוף).
אפשר לגרום להודעה המרגיזה להעלם ולאפשר לדפדפן שלי לקבל את האישור הזה. אבל זה סיפור למאמר אחר.
במאמרים הקודמים הדגמנו איך מרימים קונטיינרים של דוקר על מנת לעבוד עם וורדפרס. במאמר הזה נדגים איך להרים מכונת node\express ולהכין אותה לעבודה עם https כדי לדמות לחלוטין שרת node. כמובן שאפשר להשתמש בעקרונות האלו כדי להקים כל שרת אחר ולאפשר לו לעבוד עם https.
אז קודם כל, בואו ונרים שרת express. איך עושים את זה? עם קובץ compose שמכיל קונטיינר. איזה קונטיינר? ובכן, בואו נבחר באחד פופולרי של אקספרס ובו ביצעתי התאמות מסוימות – כמו להעיף את mongo למשל שאותו אני לא צריך.
version: '2'
services:
myapp:
tty: true # Enables debugging capabilities when attached to this container.
image: 'bitnami/express:latest'
labels:
kompose.service.type: nodeport
command: npm run development
environment:
- PORT=3000
- NODE_ENV=development
- SKIP_DB_WAIT=0
- SKIP_DB_MIGRATION=0
- SKIP_NPM_INSTALL=0
- SKIP_BOWER_INSTALL=0
ports:
- 80:3000
- 443:8000
- 3000:3000
volumes:
- .:/app
להורדת הסקריפט לחץ כאן
כדאי למקם מספר דברים חשובים פה. הדבר החשוב ביותר הוא שיש לי כאן הוא מיפויים ב-ports. הכנסתי את 443 שימופה לפורט 8000, שהוא הפורט של האפליקציה שלי. כשיש לנו node\express. הוא עובד מאחורי נתב שמעביר את כל התנועה מהפורטים השונים (80, 8080 ו-443) לפורט 3000 או לכל פורט אחר שבחרנו. פורט 443 הוא חשוב במיוחד כי הוא הפורט של הפרוטוקול המאובטח HTTPS ואני מפנה אותו לפורט הפנימי של 8000.
מיפוי פורטים בדוקר – פורט 80 הולך לפורט 3000 ופורט 443, שמשמש את ה-https, הולך לפורט 8000.
אני אשמור את הקובץ הזה בתיקיה ריקה ואריץ בטרמינל של docker:
docker-compose up
האפליקציה תיבנה הישר בתיקיית app ואני בעצם אוכל לראות אותה אם אכנס ל-IP. אבל אני רוצה תמיכה מלאה ב-https. איך עושים את זה? מבחינת דוקר עשיתי את כל מה שאני יכול לעשות. עכשיו צריך לטפל באפליקציה.
בניגוד ל-Apache\NginX וחבריהם שיודעים לעבוד עם HTTPS ישר מהקופסה, עם node אני צריך לגרום ל-https לעבוד. איך? קודם כל אני צריך ליצור לעצמי מפתח.
למי שלא יודע, תקשורת https עובדת עם RSA (החיבור הראשוני עובד כך, התקשורת עצמה עובדת בהצפנה סימטרית) שמחייבת מפתח פומבי ומפתח פרטי. אני צריך ליצור אותם, אבל אל חשש - זה לא כזה מסובך. אצור תיקיה בפרויקט שלי שתקרא sslcert ואריץ בה את הפקודה הזו:
openssl req -newkey rsa:2048 -new -nodes -keyout key.pem -out csr.pem
מה שהפקודה הזו עושה זה ליצור מפתח הצפנה פרטי ופומבי. מפתח ההצפנה הפרטי הוא key.pem ואם תפתחו אותו תוכלו לראות שיש בו private key. מפתח ההצפנה הפומבי הוא csr.pem שמשמש אותנו ל- Certificate. זה כל מה שאנחנו צריכים.
עכשיו רק צריך לגרום לשרת לעבוד עם https וגם זה די פשוט. ישנן כמה גישות שאפשר לעבוד איתן, אך אנו נתמקד בגישה הפשוטה יותר, ליצור בקובץ האתחול שרת https נוסף זהה לחלוטין לזה של http. הוא מאזין ל-8000 ויתפוס את כל התקשורת המאובטחת. ככה זה נראה:
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var appSecured = require('../app'); // Creating appSecured
var debug = require('debug')('app:server');
var http = require('http');
var https = require('https'); // require native node's native https module
var fs = require('fs');
/**
* Get port from environment and store in Express.
*/
var privateKey = fs.readFileSync('/app/sslcert/key.pem', 'utf8');
var certificate = fs.readFileSync('/app/sslcert/server.crt', 'utf8');
/**
* Prepare the credentials
*/
var credentials = {key: privateKey, cert: certificate};
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
var portSecured = normalizePort(process.env.PORT_SECURED || '8000'); // defining port secured
app.set('port', port);
appSecured.set('port', portSecured); // appSecured listen to 8000 port
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Create HTTPS server.
*/
var serverSecured = https.createServer(credentials, appSecured);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* HTTPS Listen on provided port, on all network interfaces.
*/
serverSecured.listen(portSecured);
serverSecured.on('error', onError);
serverSecured.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
להורדת הסקריפט לחץ כאן
ממש לא צריך להיבהל. זה קובץ ההגדרות הרגיל שמגיע יחד עם הקונטיינר והוספתי לתוכו פשוט את הקוד שיוצר את השרת המאובטח שמאזין לפורט 800. זה כל הקוד שהוספתי:
var appSecured = require('../app'); // Creating appSecured
var https = require('https'); // require native node's native https module
var fs = require('fs');
/**
* Get port from environment and store in Express.
*/
var privateKey = fs.readFileSync('/app/sslcert/key.pem', 'utf8');
var certificate = fs.readFileSync('/app/sslcert/server.crt', 'utf8');
/**
* Prepare the credentials
*/
var credentials = {key: privateKey, cert: certificate};
var portSecured = normalizePort(process.env.PORT_SECURED || '8000'); // defining port secured
appSecured.set('port', portSecured); // appSecured listen to 8000 port
/**
* Create HTTPS server.
*/
var serverSecured = https.createServer(credentials, appSecured);
/**
* HTTPS Listen on provided port, on all network interfaces.
*/
serverSecured.listen(portSecured);
serverSecured.on('error', onError);
serverSecured.on('listening', onListening);
להורדת הסקריפט לחץ כאן
כשאני אכנס לכתובת של המכונה שלי עם https, אני אקבל שגיאת https כמובן שמראה שהכל תקין. זאת מכיוון שיש חיבור. מן הסתם הדפדפן שלי לא מכיר ולא סומך על ה- Certificate שלי, אבל אם אני אעשה proceed אני יכול להתחיל לעבוד.
בכניסה לשרת מאובטח בפעם הראשונה, אפשר לראות שאני מקבל הודעת שגיאה (שאפשר לעקוף).
אפשר לגרום להודעה המרגיזה להעלם ולאפשר לדפדפן שלי לקבל את האישור הזה. אבל זה סיפור למאמר אחר.
הודעתך לא התקבלה - נסה שוב מאוחר יותר
Oops! Something went wrong while submitting the form