✕ סגור 
צור קשר
תודה על ההתעניינות .

Thank you! Your submission has been received!

Oops! Something went wrong while submitting the form

עבודה עם דוקר: הרמת מכונה שתומכת ב- HTTPS

רן בר זיק
|
קלה
|
Feb 18, 2019
alt="facebook"alt="linkedin"
להרשמה לניוזלטר

במאמרים הקודמים הדגמנו איך מרימים קונטיינרים של דוקר על מנת לעבוד עם וורדפרס. במאמר הזה נדגים איך להרים מכונת 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 אני יכול להתחיל לעבוד.

בכניסה לשרת מאובטח בפעם הראשונה, אפשר לראות שאני מקבל הודעת שגיאה (שאפשר לעקוף).

אפשר לגרום להודעה המרגיזה להעלם ולאפשר לדפדפן שלי לקבל את האישור הזה. אבל זה סיפור למאמר אחר.

מאת: רן בר זיק, מתכנת ובלוגר

?DevOps רוצים להתעדכן בתכנים נוספים בנושאי

הירשמו עכשיו לניוזלטר שלנו ותמיד תישארו בעניינים > להרשמה

במאמרים הקודמים הדגמנו איך מרימים קונטיינרים של דוקר על מנת לעבוד עם וורדפרס. במאמר הזה נדגים איך להרים מכונת 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 אני יכול להתחיל לעבוד.

בכניסה לשרת מאובטח בפעם הראשונה, אפשר לראות שאני מקבל הודעת שגיאה (שאפשר לעקוף).

אפשר לגרום להודעה המרגיזה להעלם ולאפשר לדפדפן שלי לקבל את האישור הזה. אבל זה סיפור למאמר אחר.

מאת: רן בר זיק, מתכנת ובלוגר

?DevOps רוצים להתעדכן בתכנים נוספים בנושאי

הירשמו עכשיו לניוזלטר שלנו ותמיד תישארו בעניינים > להרשמה

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
רן בר זיק
בואו נעבוד ביחד
צרו קשר