<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        node.js實現web終端操作多用戶

        來源:懂視網 責編:小采 時間:2020-11-27 19:55:01
        文檔

        node.js實現web終端操作多用戶

        node.js實現web終端操作多用戶:這次給大家帶來node.js實現web終端操作多用戶,node.js實現web終端操作多用戶的注意事項有哪些,下面就是實戰案例,一起來看一下。terminal(命令行)作為本地IDE普遍擁有的功能,對項目的git操作以及文件操作有著非常強大的支持。對于WebIDE,在沒有w
        推薦度:
        導讀node.js實現web終端操作多用戶:這次給大家帶來node.js實現web終端操作多用戶,node.js實現web終端操作多用戶的注意事項有哪些,下面就是實戰案例,一起來看一下。terminal(命令行)作為本地IDE普遍擁有的功能,對項目的git操作以及文件操作有著非常強大的支持。對于WebIDE,在沒有w

        這次給大家帶來node.js實現web終端操作多用戶,node.js實現web終端操作多用戶的注意事項有哪些,下面就是實戰案例,一起來看一下。

        terminal(命令行)作為本地IDE普遍擁有的功能,對項目的git操作以及文件操作有著非常強大的支持。對于WebIDE,在沒有web偽終端的情況下,僅僅提供封裝的命令行接口是完全不能滿足開發者使用,因此為了更好的用戶體驗,web偽終端的開發也就提上日程。

        調研

        終端,在我們認知范圍內略同于命令行工具,通俗點說就是可以執行shell的進程。每次在命令行中輸入一串命令,敲入回車,終端進程都會fork一個子進程,用來執行輸入的命令,終端進程通過系統調用wait4()監聽子進程退出,同時通過暴露的stdout輸出子進程執行信息。

        如果在web端實現一個類似于本地化的終端功能,需要做的可能會更多:網絡時延及可靠性保證、shell用戶體驗盡量接近本地化、web終端UI寬高與輸出信息適配、安全準入控制與權限管理等。在具體實現web終端之前,需要評估這些功能那些是最核心的,很明確:shell的功能實現及用戶體驗、安全性(web終端是在線上服務器中提供的一個功能,因此安全性是必須要保證的)。只有在保證這兩個功能的前提下,web偽終端才可以正式上線。

        下面首先針對這兩個功能考慮下技術實現(服務端技術采用nodejs):

        node原生模塊提供了repl模塊,它可以用來實現交互式輸入并執行輸出,同時提供tab補全功能,自定義輸出樣式等功能,可是它只能執行node相關命令,因此無法達到我們想要執行系統shell的目的 node原生模塊child_porcess,它提供了spawn這種封裝了底層libuv的uv_spawn函數,底層執行系統調用fork和execvp,執行shell命令。但是它未提供偽終端的其它特點,如tab自動補全、方向鍵顯示歷史命令等操作

        因此,服務端采用node的原生模塊是無法實現一個偽終端的,需要繼續探索偽終端的原理和node端的實現方向。

        偽終端

        偽終端不是真正的終端,而是內核提供的一個“服務”。終端服務通常包括三層:

        最頂層提供給字符設備的輸入輸出接口中間層的線路規程(line discipline)底層的硬件驅動

        其中,最頂層的接口往往通過系統調用函數實現,如(read,write);而底層的硬件驅動程序則負責偽終端的主從設備通信,它由內核提供;線路規程看起來則比較抽象,但是實際上從功能上說它負責輸入輸出信息的“加工”,如處理輸入過程中的中斷字符(ctrl + c)以及一些回退字符(backspace 和 delete)等,同時轉換輸出的換行符n為rn等。

        一個偽終端分為兩部分:主設備和從設備,他們底層通過實現默認線路規程的雙向管道連接(硬件驅動)。偽終端主設備的任何輸入都會反映到從設備上,反之亦然。從設備的輸出信息也通過管道發送給主設備,這樣可以在偽終端的從設備中執行shell,完成終端的功能。

        偽終端的從設備中,可以真實的模擬終端的tab補全和其他的shell特殊命令,因此在node原生模塊不能滿足需求的前提下,我們需要把目光放到底層,看看OS提供了什么功能。目前,glibc庫提供了posix_openpt接口,不過流程有些繁瑣:

        使用posix_openpt打開一個偽終端主設備 grantpt設置從設備的權限 unlockpt解鎖對應的從設備獲取從設備名稱(類似 /dev/pts/123)主(從)設備讀寫,執行操作

        因此出現了封裝更好的pty庫,僅僅通過一個forkpty函數便可以實現上述所有功能。通過編寫一個node的c++擴展模塊,搭配pty庫實現一個在偽終端從設備執行命令行的terminal。

        關于偽終端安全性的問題,我們在文章的最后在進行討論。

        偽終端實現思路

        根據偽終端的主從設備的特性,我們在主設備所在的父進程中管理偽終端的生命周期及其資源,在從設備所在的子進程中執行shell,執行過程中的信息及結果通過雙向管道傳輸給主設備,由主設備所在的進程向外提供stdout。

        在此處借鑒pty.js的實現思路:

        pid_t pid = pty_forkpty(&master, name, NULL, &winp);
         switch (pid) {
         case -1:
         return Nan::ThrowError("forkpty(3) failed.");
         case 0:
         if (strlen(cwd)) chdir(cwd);
         if (uid != -1 && gid != -1) {
         if (setgid(gid) == -1) {
         perror("setgid(2) failed.");
         _exit(1);
         }
         if (setuid(uid) == -1) {
         perror("setuid(2) failed.");
         _exit(1);
         }
         }
         pty_execvpe(argv[0], argv, env);
         perror("execvp(3) failed.");
         _exit(1);
         default:
         if (pty_nonblock(master) == -1) {
         return Nan::ThrowError("Could not set master fd to nonblocking.");
         }
         Local<Object> obj = Nan::New<Object>();
         Nan::Set(obj,
         Nan::New<String>("fd").ToLocalChecked(),
         Nan::New<Number>(master));
         Nan::Set(obj,
         Nan::New<String>("pid").ToLocalChecked(),
         Nan::New<Number>(pid));
         Nan::Set(obj,
         Nan::New<String>("pty").ToLocalChecked(),
         Nan::New<String>(name).ToLocalChecked());
         pty_baton *baton = new pty_baton();
         baton->exit_code = 0;
         baton->signal_code = 0;
         baton->cb.Reset(Local<Function>::Cast(info[8]));
         baton->pid = pid;
         baton->async.data = baton;
         uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
         uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
         return info.GetReturnValue().Set(obj);
         }

        首先通過pty_forkpty(forkpty的posix實現,兼容 sunOS和 unix等系統)創建主從設備,然后在子進程中設置權限之后(setuid、setgid),執行系統調用pty_execvpe(execvpe的封裝),此后主設備的輸入信息都會在此得到執行(子進程執行的文件為sh,會偵聽stdin);

        父進程則向node層暴露相關對象,如主設備的fd(通過該fd可以創建net.Socket對象進行數據雙向傳輸),同時注冊libuv的消息隊列&baton->async,當子進程退出時觸發&baton->async消息,執行pty_after_waitpid函數;

        最后父進程通過調用uv_thread_create創建一個子進程,用于偵聽上一個子進程的退出消息(通過執行系統調用wait4,阻塞偵聽特定pid的進程,退出信息存放在第三個參數中),pty_waitpid函數封裝了wait4函數,同時在函數末尾執行uv_async_send(&baton->async)觸發消息。

        在底層實現pty模型后,在node層需要做一些stdio的操作。由于偽終端主設備是在父進程中執行系統調用的創建的,而且主設備的文件描述符通過fd暴露給node層,那么偽終端的輸入輸出也就通過讀寫根據fd創建對應的文件類型如PIPE、FILE來完成。其實,在OS層面就是把偽終端主設備看為一個PIPE,雙向通信。在node層通過net.Socket(fd)創建一個套接字實現數據流的雙向IO,偽終端的從設備也有著主設備相同的輸入,從而在子進程中執行對應的命令,子進程的輸出也會通PIPE反應在主設備中,進而觸發node層Socket對象的data事件。

        此處關于父進程、主設備、子進程、從設備的輸入輸出描述有些讓人迷惑,在此解釋。父進程與主設備的關系是:父進程通過系統調用創建主設備(可看做是一個PIPE),并獲取主設備的fd。父進程通過創建該fd的connect socket實現向子進程(從設備)的輸入輸出。 而子進程通過forkpty 創建后執行login_tty操作,重置了子進程的stdin、stderr和stderr,全部復制為從設備的fd(PIPE的另一端)。因此子進程輸入輸出都是與從設備的fd相關聯的,子進程輸出數據走的是PIPE,并從PIPE中讀入父進程的命令。詳情請看參考文獻之forkpty實現

        另外,pty庫提供了偽終端的大小設置,因此我們通過參數可以調整偽終端輸出信息的布局信息,因此這也提供了在web端調整命令行寬高的功能,只需在pty層設置偽終端窗口大小即可,該窗口是以字符為單位。

        web終端安全性保證

        基于glibc提供的pty庫實現偽終端后臺,是沒有任何安全性保證的。我們想通過web終端直接操作服務端的某個目錄,但是通過偽終端后臺可以直接獲取root權限,這對服務而言是不可容忍的,因為它直接影響著服務器的安全,所有需要實現一個:可多用戶同時在線、可配置每個用戶訪問權限、可訪問特定目錄的、可選擇配置bash命令、用戶間相互隔離、用戶無感知當前環境且環境簡單易部署的“系統”。

        最為適合的技術選型是docker,作為一種內核層面的隔離,它可以充分利用硬件資源,且十分方便映射宿主機的相關文件。但是docker并不是萬能的,如果程序運行在docker容器中,那么為每個用戶再分配一個容器就會變得復雜得多,而且不受運維人員掌控,這就是所謂的DooD(docker out of docker)-- 通過volume “/usr/local/bin/docker”等二進制文件,使用宿主機的docker命令,開啟兄弟鏡像運行構建服務。而采用業界經常討論的docker-in-docker模式會存在諸多缺點,特別是文件系統層面的,這在參考文獻中可以找到。因此,docker技術并不適合已經運行在容器中的服務解決用戶訪問安全問題。

        接下來需要考慮單機上的解決方案。目前筆者只想到兩種方案:

        命令ACL,通過命令白名單的方式實現 restricted bash chroot,針對每個用戶創建一個系統用戶,監禁用戶訪問范圍

        首先,命令白名單的方式是最應該排除的,首先無法保證不同release的linux的bash是相同的;其次無法有效窮舉所有的命令;最后由于偽終端提供的tab命令補全功能以及特殊字符如delete的存在,無法有效匹配當前輸入的命令。因此白名單方式漏洞太多,放棄。

        restricted bash,通過/bin/bash -r觸發,可以限制使用者顯式“cd directory”,但有這諸多缺點:

        不足以允許執行完全不受信任的軟件。當一個被發現是shell腳本的命令被執行時,rbash會關閉在shell中生成的任何限制來執行腳本。當用戶從rbash運行bash或dash,那么他們獲得了無限制的shell。有很多方法來打破受限制的bash shell,這是不容易預測的。

        最后,貌似只有一個解決方案了,即chroot。chroot修改了用戶的根目錄,在制定的根目錄下運行指令。在指定根目錄下無法跳出該目錄,因此無法訪問原系統的所有目錄;同時chroot會創建一個與原系統隔離的系統目錄結構,因此原系統的各種命令無法在“新系統”中使用,因為它是全新的、空的;最后,多個用戶使用時他們是隔離的、透明的,完全滿足我們的需求。

        因此,我們最終選擇chroot作為web終端的安全性解決方案。但是,使用chroot需要做非常多的額外處理,不僅包括新用戶的創建,還包括命令的初始化。上文也提到“新系統”是空的,所有可執行二進制文件都沒有,如“ls,pmd”等,因此初始化“新系統”是必須的。可是許多二進制文件不僅僅靜態鏈接了許多庫,還在運行時依賴動態鏈接庫(dll),為此還需要找到每個命令依賴的諸多dll,異常繁瑣。為了幫助使用者從這種無趣的過程中解脫出來,jailkit應運而生。

        jailkit,真好用

        jailkit,顧名思義用來監禁用戶。jailkit內部使用chroot實現創建用戶根目錄,同時提供了一系列指令來初始化、拷貝二進制文件及其所有的dll,而這些功能都可以通過配置文件進行操作。因此,在實際開發中采用jailkit搭配初始化shell腳本來實現文件系統隔離。

        此處的初始化shell指的是預處理腳本,由于chroot需要針對每個用戶設置根目錄,因此在shell中為每個開通命令行權限的使用者創建對應的user,并通過jailkit配置文件拷貝基本的二進制文件及其dll,如基本的shell指令、git、vim、ruby等;最后再針對某些命令做額外的處理,以及權限重置。

        在處理“新系統”與原系統的文件映射過程中,還是需要一些技巧。筆者曾經將chroot設定的用戶根目錄之外的其他目錄通過軟鏈接的形式建立映射,可是在jail監獄中訪問軟鏈接時仍會報錯,找不到該文件,這還是由于chroot的特性導致的,沒有權限訪問根目錄之外的文件系統;如果通過硬鏈接建立映射,則針對chroot設定的用戶根目錄中的硬鏈接文件做修改是可以的,但是涉及到刪除、創建等操作是無法正確映射到原系統的目錄的,而且硬鏈接無法連接目錄,因此硬鏈接不滿足需求;最后通過mount --bind實現,如 mount --bind /home/ttt/abc /usr/local/abc它通過屏蔽被掛載的目錄(/usr/local/abc)的目錄信息(block),并在內存中維護被掛載目錄與掛載目錄的映射關系,對/usr/local/abc的訪問都會通過傳內存的映射表查詢/home/ttt/abc的block,然后進行操作,實現目錄的映射。

        最后,初始化“新系統”完畢后,就需要通過偽終端執行jail相關命令:

        sudo jk_chrootlaunch -j /usr/local/jailuser/${creater} -u ${creater} -x /bin/bashr

        開啟bash程序之后便通過PIPE與主設備接收到的web終端輸入(通過websocket)進行通信即可。

        相信看了本文案例你已經掌握了方法,更多精彩請關注Gxl網其它相關文章!

        推薦閱讀:

        在angularjs中怎樣實現echart圖表

        js怎么實現隔行變色

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        node.js實現web終端操作多用戶

        node.js實現web終端操作多用戶:這次給大家帶來node.js實現web終端操作多用戶,node.js實現web終端操作多用戶的注意事項有哪些,下面就是實戰案例,一起來看一下。terminal(命令行)作為本地IDE普遍擁有的功能,對項目的git操作以及文件操作有著非常強大的支持。對于WebIDE,在沒有w
        推薦度:
        標簽: 用戶 多用戶 web
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 在线日韩av永久免费观看| 外国成人网在线观看免费视频| 久草视频免费在线观看| 亚洲av最新在线网址| 一个人免费视频在线观看www| 亚洲一区日韩高清中文字幕亚洲| 日韩精品亚洲专区在线影视| 日本特黄特色aa大片免费| 亚洲av日韩综合一区久热| 国产精品嫩草影院免费| 美女黄频a美女大全免费皮| 国产hs免费高清在线观看| 污视频网站免费观看| 亚洲色欲久久久综合网东京热| 免费av片在线观看网站| 亚洲最新视频在线观看| 一个人免费观看www视频在线| 亚洲综合精品第一页| 免费人成无码大片在线观看| 久久免费观看视频| 亚洲激情在线视频| 97视频热人人精品免费| 处破女第一次亚洲18分钟| 久久久精品国产亚洲成人满18免费网站 | 亚洲精品成人片在线播放| 99国产精品免费视频观看| 91亚洲精品麻豆| 国产成人免费a在线视频色戒| 一区二区三区免费在线观看| 亚洲精品V欧洲精品V日韩精品| 免费观看无遮挡www的小视频| 亚洲精品又粗又大又爽A片| 亚洲国产精品人人做人人爱| 久久青草91免费观看| 色老板亚洲视频免在线观| 四虎亚洲国产成人久久精品| 最近免费最新高清中文字幕韩国 | 中文字幕无码免费久久99| 免费一区二区三区在线视频| 亚洲电影中文字幕| 国产精品无码一二区免费 |