facebook廣告





123
顯示具有 Nodejs 標籤的文章。 顯示所有文章
顯示具有 Nodejs 標籤的文章。 顯示所有文章

2017年1月29日 星期日

變更Node.js執行的user身份 → Node.js To Lower Privilege

變更Node.js執行的user身份 → Node.js To Lower Privilege


http://blog.xuite.net/zack_pan/blog/72322002-%E8%AE%8A%E6%9B%B4Node.js%E5%9F%B7%E8%A1%8C%E7%9A%84user%E8%BA%AB%E4%BB%BD+%E2%86%92+Node.js+To+Lower+Privilege
大家都知道HTTP是default 80 port,
但只有root執行的程式,才可以bind 0 ~ 1024的port。

可是反過來說,如果每支要用到這些port的程式都用root,
又root可以對系統做任何的事,如果這些程式有"禾斗禾斗"的code,
你的OS可能就一起"禾斗禾斗"了~ XD
所以適當地更改執行的user、降權限是必要的。

不過這又與一開始的敘述矛盾啦!!!
不是root user怎麼bind 80 port!(翻桌

這時候就會有人想到Apache HTTP Server它又是如何達到上述兩個條件的呢?
看了systemd service(我用Archlinux)的設定檔,嗯... 沒有看到有關使用者的指定
後來想想,Apache HTTP Server指定user的地方是http.conf。
也就是說,是daemon自己更改執行的的user!!!(咦

那Node.js又要如何更改執行的user呢?於是乎,有請Google大神
果然,在官網果然有相關的document:process.setuid(id)

稍微整理一下流程:
  1. 由root執行Node.js HTTP Server程式 → HTTP daemon bind 80 port
  2. Node.js HTTP Server程式更改自己的執行user and group為http

以下是Example Code server.js:
/* Just have a web server linstening on 80 port. */
var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
    console.log('One user request!');
}).listen(80);

/* Set & get user & group id. */
if (process.getuid && process.setuid) {
    console.log('Current uid: ' + process.getuid());
    console.log('Current gid: ' + process.getgid());
    console.log('This process is pid: ' + process.pid);
    try {
        /* Change group to http. */
        process.setgid("http");
        console.log('New gid: ' + process.getgid());
        /* Change user to http. */
        process.setuid("http");
        console.log('New uid: ' + process.getuid());
    }
    catch (err) {
        console.log('Failed to set uid: ' + err);
    }
}
用root身份執行node,而code是server.js。從執行的結果可以看到PID 1186一開始的user和group都是root,bind 80 port之後,user和group就改成http(UID & GID都是33)了。Client連進HTTP服務,可以Server有相對應的console log,以及Client有GET正常的Response。

可以從 top 來看PID和User,如下圖可以確認PID 1186執行的身份是http
sudo netstat -antp | head -n 3  可以看到PID 1186的node listening on 80 port。

Node.js 執行程式 & 環境介紹 - node.js day 5

http://ithelp.ithome.com.tw/articles/10103131
環境目前已經都建置完成,也對於基本的架構有個認識,在這邊還是要花個章節,跟大家介紹怎麼樣執行 node.js 程式,凡事總是要有個 hello world.
#Node.js 執行程式 & 環境介紹
前言
環境目前已經都建置完成,也對於基本的架構有個認識,在這邊還是要花個章節,跟大家介紹怎麼樣執行 node.js 程式,凡事總是要有個 hello world.
執行程式
所有的 Node.js 程式都必須要在『終端機』模式底下進行執行(沒錯,就是那個黑色的畫面,請慢慢習慣跟黑色畫面的鍵盤輸入對話框當好朋友。)
而我們從 node.js 偷來第一個簡單的 Web 簡易程式。
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
上面這串程式,儲存成 server.js,接著執行
 node server.js
就可以開啟瀏覽器,連結 url: http://127.0.0.1:1337/ ,可以看到畫面顯示,
 Hello World
恭喜完成第一個 node module
Node.js 程式說明
Node.js 的程式架構中,採用了 CommonJS 為規範,所以我們可以看到許多和其前端開發有所差異的用法。
require
require 這個關鍵字,主要是用於某個模組載入,例如上面的程式,就是直接讀取 http 模組,而 Node module 分為三種,原生,和之後透過 npm 安裝,還有另外一種自己寫的。
 var http = require('http');
上面的程式就是直接載入 Node.js 原生的 http 模組,至於要怎麼知道這個模組是不是原生,可以在 nodejs.org 上面找 API 就可以找到答案。
 var express = require('express');
上面的程式,如果沒有進行安裝任何 express 套件就執行此程式的話,就會顯示,
 Error: Cannot find module 'express'
 //...
需要使用這類的模組就需要透過 npm install module_name 才可以找到相對的模組。
 var md = require('./md');
上面的載入方式,為目前專案底下自行編寫的 node module ,而 md 可以為一個資料夾(起始檔案為 index.js),也可以是一個 js 檔案(後面的 .js可以被省略),如此一來就可以載入自行編寫的 module.
全域模組
全域模組目前有
 console
 
 process
這兩個模組都是 Node.js 剛起來的時候,原生的模組,當然還有 JS 本身原生的模組,例如 String, Array, JSON 等,這些在後面的章節都會介紹。
後記
相信介紹到這邊,對於 node.js 程式怎麼啟動,以及剛開始目前有哪些模組,對於 node module 也有一個基本的認知。
接著會開始介紹 JavaScript 基礎,讓大家對於 JavaScript 的基本概念有個清晰的印象

node.js入门 - 12.api:进程(process)

http://www.cnblogs.com/softlover/archive/2012/10/03/2707139.html
虽然node对操作系统做了很多抽象的工作,但是你还是可以直接和他交互,比如和系统中已经存在的进程进行交互,创建工作子进程。node是一个用于事件循环的线程,但是你可以在这个事件循环之外创建其他的进程(线程)参与工作。
  进程模块
  process模块允许你获得或者修改当前node进程的设置,不想其他的模块,process是一个全局进程,你可以直接通过process变量直接访问它。
  process实现了EventEmitter接口,exit方法会在当进程退出的时候执行。因为进程退出之后将不再执行事件循环,所有只有那些没有回调函数的代码才会被执行。在下面例子中,setTimeout里面的语句是没有办法执行到的。
process.on('exit', function () {
  setTimeout(function () {
    console.log('This will not run');
  }, 100);
  console.log('Bye.');
});
  在你接触node之后,你就会发现那些影响了主事件循环的异常会把整个node进程宕掉的。这会是相当严重的问题,所以process提供了另外一个有用的事件uncaughtException来解决这个问题,他会把异常抓取出来供你处理。
复制代码
process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});
setTimeout(function () {
  console.log('This will still run.');
}, 500);
// Intentionally cause an exception, but don't catch it.
nonexistentFunc();
console.log('This will not run.');
复制代码
   我们来看上面的例子,我们注册了uncaughtException事件来捕捉系统异常。执行到nonexistentFunc()时,因为该函数没有定义所以会抛出异常。因为javascript是解释性的语言,nonexistentFunc()方法上面的语句不会被影响到,他下面的语句不会被执行。所以他的执行结果如下:
Caught exception: ReferenceError: nonexistentFunc is not defined
This will still run.
  我们再看一个例子。
复制代码
var http = require('http');
var server = http.createServer(function(req,res) {
  res.writeHead(200, {});
  res.end('response');
  badLoggingCall('sent response');
  console.log('sent response');
});
process.on('uncaughtException', function(e) {
  console.log(e);
});
server.listen(8080);
复制代码
   在这里例子中我们创建了一个web服务器,当处理完请求之后,我们会执行badLoggingCall()方法。因为这个方法不存在,所以会有异常抛出。但是我们注册的uncaughtException事件会对异常做出处理,这样服务器不会受到影响得以继续运行。我们会在服务器端记录错误日志。
[ReferenceError: badLoggingCall is not defined]

   与当前进程交互
  node提供了一些process的属性,如下:
process.version:包含当前node实例的版本号;
process.installPrefix:包含安装路径;
process.platform:列举node运行的操作系统的环境,只会显示内核相关的信息,如:linux2, darwin,而不是“Redhat ES3” ,“Windows 7”,“OSX 10.7”等;
process.uptime():包含当前进程运行的时长(秒);
process.getgid(), process.setgid():获取或者设置group id;
process.getuid(), process.setuid():获取或者设计user id;
process.pid:获取进程id;
process.title:设置进程名称;
process.execPath:当前node进程的执行路径,如:/usr/local/bin/node;
process.cwd():当前工作目录;
process.memoryUsage():node进程内存的使用情况,rss代表ram的使用情况,vsize代表总内存的使用大小,包括ram和swap;
process.heapTotal,process.heapUsed:分别代表v8引擎内存分配和正在使用的大小。

   事件循环和ticker
  node中提供了process.nextTick()方法,允许你访问事件循环和延时那你的工作。他有点类似于setTimeout(),他会在下次tick的时候执行,而且每隔一段事件就会执行一次。我们这里有个例子:
复制代码
var http = require('http');
var s = http.createServer(function(req, res) {
  res.writeHead(200, {});
  res.end('foo');
  console.log('http response');
  process.nextTick(function(){console.log('tick')});
});
s.listen(8000);
复制代码
  当请求来的时候,会记录日志‘http response’和‘tick’,当没有请求的时候,每隔一段事件会执行事件循环,会输出tick。
  此外,nextTick创建的回调函数具有隔离性,他们之间不会相互影响。
复制代码
process.on('uncaughtException', function(e) {
  console.log(e);
});
process.nextTick(function() {
  console.log('tick');
});
process.nextTick(function() {
  iAmAMistake();
  console.log('tock');
});
process.nextTick(function() {
  console.log('tick tock');
});
console.log('End of 1st loop');
复制代码
  在这个例子中,首先输出‘End of 1st loop’,然后顺序的输出nextTick的回调函数,第一个会正常输出‘tick’,第二个是一个故意设置的异常会输出异常信息,不会输出‘tock’,因为nextTick回调函数的隔离性,第三个任然会输出‘tick tock’。结果如下:
End of 1st loop
tick
[ReferenceError: iAmAMistake is not defined]
tick tock

  子进程
  node提供了child_process模块,允许你为主进程创建子进程,这样你就可以使用更多的服务器资源,使用更多的cpu,这些概念在前面的章节有介绍。node提供了child_process. spawn()和child_process. exec()为你实现这一功能,下面我们就单独介绍。
  
  child_process.exec( )
  我们来看exec的一个简单例子,他创建了一个子进程,第一个参数是一个shell命令,第二个参数是回调函数,处理返回结果。
复制代码
var cp = require('child_process');
cp.exec('ls -l', function(e, stdout, stderr) {
  if(!e) {
    console.log(stdout);
    console.log(stderr);
  }
});
复制代码
exec()还可以传options的参数:
复制代码
var options = { 
    encoding: 'utf8',
    timeout: 0,
    maxBuffer: 200 * 1024,
    killSignal: 'SIGTERM',
    setsid: false,
    cwd: null,
    env: null 
};
var cp = require('child_process');
cp.exec('ls -l', options, function(e, stdout, stderr) {
  if(!e) {
    console.log(stdout);
    console.log(stderr);
  }
});
 
复制代码
encoding:I/O流的编码格式;
timeout:进程超时时间;
killSignal:当时间或者缓冲区超限时终止进程的信号;
maxBuffer:stdout或stderr可增长的最大值;
setsid:决定在进程中是否创建一个新的会话;
cwd:进程的初始工作目录,为null时表示使用node的当前工作目录;
env:进程的环境变量。

  child_process.spawn( )
  child_process.spawn( )比child_process.exec( )更加强大和灵活,例子如下:
复制代码
var cp = require('child_process');
var cat = cp.spawn('cat');
cat.stdout.on('data', function(d) {
  console.log(d.toString());
});
cat.on('exit', function() {
  console.log('kthxbai');
});
cat.stdin.write('meow');
cat.stdin.end();
复制代码