1. 記事一覧 >
  2. ブログ記事
category logo

OS.jsにiframeアプリを組み込んでnginx(エンジンエックス)で振り分ける

(更新) (公開)

はじめに

前回、OS.jsインストールと公式アプリの追加をやってみましたが、 今回、既存のWebサイトをOS.jsのアプリのように組み込む方法とnginxで振り分ける方法を実践していきます。

【 Nginx 】

Nginx(エンジンエックス)は、オープンソースのWebサーバです。処理性能・高い並行性・メモリ使用量の小ささに焦点を当てて開発されており、HTTP, HTTPS, SMTP, POP3, IMAPのリバースプロキシの機能や、ロードバランサ、HTTPキャッシュなどの機能も持ちます。


OS.jsバックグラウンド起動設定
Rocket.Chat組み込み
Wekanの組み込み
GitLabの組み込み
nginxインストール
動作確認


RocketChat、Wekan、GitLabをOS.jsのiframeアプリとして組み込んでみます。
仕組みは、以下の図のようになります。


パソコンからは、
192.168.▲.▲ osjs.itccorporation.jp
192.168.▲.▲ chat.itccorporation.jp
192.168.▲.▲ wekan.itccorporation.jp
192.168.▲.▲ gitlab.itccorporation.jp
と名前解決できるものとします。


今回、OS.jsに直接アプリを組み込むのではなく、iframeで表示するだけになります。


今回の検証環境は、

CentOS Linux release 7.6.1810

node v12.18.3

OS.js 3.1.11

nginx-1.19.10

になります。


OS.jsバックグラウンド起動設定

OS.jsを起動するときは、 npm run serve ですが、フォアグラウンド起動のため、ターミナルを開けていないといけません。そこで、systemdを使ったバックグラウンド起動をまず設定します。

# vi /usr/lib/systemd/system/osjs.service
[Unit]
Description=OS.js Node Server
Documentation=https://manual.os-js.org
After=network.target

[Service]
Environment=NODE_ENV=production
Type=simple
User=osjs
ExecStart=/usr/bin/node /opt/OS.js/src/server/index.js
Restart=on-failure
WorkingDirectory=/opt/OS.js

[Install]
WantedBy=multi-user.target

User=osjsに設定したosjsユーザーを追加します。

# adduser --system --no-create-home --user-group -s /sbin/nologin osjs
# chown -R osjs /opt/OS.js

systemd の設定を反映して、起動します。

# systemctl daemon-reload
# systemctl enable osjs
# systemctl start osjs

/usr/lib/systemd/system/osjs.service について、公式サイトのマニュアルでは、ExecStart=node /opt/OS.js/src/server/index.jsですが、nodeが相対パスのため、以下のエラーになり起動しませんでした。

[/usr/lib/systemd/system/osjs.service:10] Executable path is not absolute, ignoring: node /opt/OS.js/src/server/index.js

osjs.service lacks both ExecStart= and ExecStop= setting. Refusing.


Rocket.Chat組み込み

Rocket.ChatをOS.jsに認識させて、メニューに加えます。
Rocket.Chatは、既に chat.itccorporation.jp(192.168.●.●:3000) で動いているものとします。

# cd /opt/OS.js
# npm run make:iframe-application
? Enter name of package ([A-z0-9_]) rocketchat
? Destination src/packages/rocketchat
? Are you sure you want to write to 'src/packages/rocketchat' Yes
# npm run package:discover

src/packages/rocketchat
が作成されますので、
src/packages/rocketchat/icon.png
を追加、
src/packages/rocketchat/index.js
src/packages/rocketchat/metadata.json
src/packages/rocketchat/webpack.config.js
を更新します。


icon.pngは、デザインは任意ですが、今回は、Rocket.Chatのアイコンを置きます。
更新内容は、以下の内容です。


・index.js

クリックしてソースを表示
import osjs from 'osjs';
import {name as applicationName} from './metadata.json';

// Our launcher
const register = (core, args, options, metadata) => {
  // Create a new Application instance
  const proc = core.make('osjs/application', {args, options, metadata});

  // Create  a new Window instance
  proc
    .createWindow({
      id: 'rocketchatWindow',
      title: metadata.title.en_EN,
      dimension: {width: 800, height: 600},
      position: {left: 80, top: 60}
    })
    .on('destroy', () => proc.destroy())
    .render($content => {
      const iframe = document.createElement('iframe');
      iframe.style.width = '100%';
      iframe.style.height = '100%';
      iframe.src = '//chat.itccorporation.jp/';
      iframe.setAttribute('border', '0');
      $content.appendChild(iframe);
    });

  return proc;
};

// Creates the internal callback function when OS.js launches an application
osjs.register(applicationName, register);

iframe.src = '//chat.itccorporation.jp/';
によって、見る先を http://chat.itccorporation.jp/ (192.168.▲.▲)にしています。


・metadata.json

クリックして内容を表示
{
  "type": "application",
  "name": "rocketchat",
  "category": "office",
  "server": null,
  "icon": "icon.png",
  "title": {
    "en_EN": "Rocket.Chat"
  },
  "description": {
    "en_EN": "Rocket.Chat"
  },
  "files": [
    "main.js"
  ]
}

・webpack.config.js

クリックして内容を表示
const path = require('path');
const mode = process.env.NODE_ENV || 'development';
const minimize = mode === 'production';
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  mode,
  devtool: 'source-map',
  entry: [
    path.resolve(__dirname, 'index.js'),
  ],
  optimization: {
    minimize,
  },
  externals: {
    osjs: 'OSjs'
  },
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        {from: 'data', to: 'data'},
        {from: 'icon.png', to: 'icon.png'}
      ]
    })
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  }
};

変更を適用します。

# cd src/packages/rocketchat
# npm run build
# cd ../../..
# npm run package:discover

Wekanの組み込み

WekanをOS.jsに認識させて、メニューに加えます。
Wekanは、既に wekan.itccorporation.jp(192.168.■.■:3001) で動いているものとします。

# npm run make:iframe-application
? Enter name of package ([A-z0-9_]) wekan
? Destination src/packages/wekan
? Are you sure you want to write to 'src/packages/wekan' Yes
# npm run package:discover

src/packages/wekan
が作成されますので、
src/packages/wekan/icon.png
を追加、
src/packages/wekan/index.js
src/packages/wekan/metadata.json
src/packages/wekan/webpack.config.js
を更新します。


icon.pngは、デザインは任意ですが、今回は、Wekanのアイコンを置きます。
更新内容は、以下の内容です。


・index.js

クリックしてソースを表示
import osjs from 'osjs';
import {name as applicationName} from './metadata.json';

// Our launcher
const register = (core, args, options, metadata) => {
  // Create a new Application instance
  const proc = core.make('osjs/application', {args, options, metadata});

  // Create  a new Window instance
  proc
    .createWindow({
      id: 'wekanWindow',
      title: metadata.title.en_EN,
        dimension: {width: 800, height: 600},
        position: {left: 100, top: 80}
    })
    .on('destroy', () => proc.destroy())
    .render($content => {
      const iframe = document.createElement('iframe');
      iframe.style.width = '100%';
      iframe.style.height = '100%';
      iframe.src = '//wekan.itccorporation.jp/';
      iframe.setAttribute('border', '0');
      $content.appendChild(iframe);
    });

  return proc;
};

// Creates the internal callback function when OS.js launches an application
osjs.register(applicationName, register);

iframe.src = '//wekan.itccorporation.jp/';
によって、見る先を http://wekan.itccorporation.jp/ (192.168.▲.▲)にしています。


・metadata.json

クリックして内容を表示
{
  "type": "application",
  "name": "wekan",
  "category": "office",
  "singleton": true,
  "icon": "icon.png",
  "server": null,
  "title": {
    "en_EN": "Wekan"
  },
  "description": {
    "en_EN": "Wekan"
  },
  "files": [
    "main.js"
  ]
}

・webpack.config.js

クリックして内容を表示
const path = require('path');
const mode = process.env.NODE_ENV || 'development';
const minimize = mode === 'production';
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  mode,
  devtool: 'source-map',
  entry: [
    path.resolve(__dirname, 'index.js'),
  ],
  optimization: {
    minimize,
  },
  externals: {
    osjs: 'OSjs'
  },
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        {from: 'data', to: 'data'},
        {from: 'icon.png', to: 'icon.png'}
      ]
    })
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  }
};

変更を適用します。

# cd src/packages/wekan
# npm run build
# cd ../../..
# npm run package:discover

GitLabの組み込み

GitLabをOS.jsに認識させて、メニューに加えます。
GitLabは、既に gitlab.itccorporation.jp(192.168.◆.◆) で動いているものとします。

# npm run make:iframe-application
? Enter name of package ([A-z0-9_]) gitlab
? Destination src/packages/gitlab
? Are you sure you want to write to 'src/packages/gitlab' Yes
# npm run package:discover

src/packages/gitlab
が作成されますので、
src/packages/gitlab/icon.png
を追加、
src/packages/gitlab/index.js
src/packages/gitlab/metadata.json
src/packages/gitlab/webpack.config.js
を更新します。


icon.pngは、デザインは任意ですが、今回は、GitLabのアイコンを置きます。
更新内容は、以下の内容です。


・index.js

クリックして内容を表示
import osjs from 'osjs';
import {name as applicationName} from './metadata.json';

// Our launcher
const register = (core, args, options, metadata) => {
  // Create a new Application instance
  const proc = core.make('osjs/application', {args, options, metadata});

  // Create  a new Window instance
  proc
    .createWindow({
        id: 'rocketchatWindow',
        title: metadata.title.en_EN,
        icon: proc.resource( metadata.icon ),
        dimension: {width: 800, height: 600},
        position: {left: 120, top: 60}
    })
    .on('destroy', () => proc.destroy())
    .render($content => {
      const iframe = document.createElement('iframe');
      iframe.style.width = '100%';
      iframe.style.height = '100%';
      iframe.src = '//gitlab.itccorporation.jp/';
      iframe.setAttribute('border', '0');
      $content.appendChild(iframe);
    });

  return proc;
};

// Creates the internal callback function when OS.js launches an application
osjs.register(applicationName, register);

iframe.src = '//gitlab.itccorporation.jp/';
によって、見る先を http://gitlab.itccorporation.jp/ (192.168.▲.▲)にしています。


・metadata.json

クリックして内容を表示
{
  "type": "application",
  "name": "gitlab",
  "category": "development",
  "server": null,
  "icon": "icon.png",
  "title": {
    "en_EN": "gitlab"
  },
  "description": {
    "en_EN": "gitlab"
  },
  "files": [
    "main.js"
  ]
}

・webpack.config.js

クリックして内容を表示
const path = require('path');
const mode = process.env.NODE_ENV || 'development';
const minimize = mode === 'production';
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  mode,
  devtool: 'source-map',
  entry: [
    path.resolve(__dirname, 'index.js'),
  ],
  optimization: {
    minimize,
  },
  externals: {
    osjs: 'OSjs'
  },
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        {from: 'data', to: 'data'},
        {from: 'icon.png', to: 'icon.png'}
      ]
    })
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  }
};

変更を適用します。

# cd src/packages/gitlab
# npm run build
# cd ../../..
# npm run package:discover

nginxインストール

nginxをインストールして、リクエストを仲介させます。


ソースコード
nginx-1.19.10.tar.gz
headers-more-nginx-module-0.33.tar.gz
が/home/admin/に置かれているものとします。


入手元は、各々以下になります。
http://nginx.org/download/nginx-1.19.10.tar.gz
https://github.com/openresty/headers-more-nginx-module/archive/refs/tags/v0.33.tar.gz


インストール先は、/usr/share/nginx
設定ファイルは、/etc/nginx/
ログ出力先は、/var/log/nginx/
とします。

# cd /home/admin
# adduser --system --no-create-home --user-group -s /sbin/nologin nginx
# yum install -y pcre-devel
# yum install -y zlib-devel
# yum install -y openssl-devel
# tar zxf headers-more-nginx-module-0.33.tar.gz
# tar zxf nginx-1.19.10.tar.gz
# cd nginx-1.19.10
# ./configure \
--add-dynamic-module=/home/admin/headers-more-nginx-module-0.33 \
--prefix=/usr/share/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/etc/nginx/nginx.conf  \
--error-log-path=/var/log/nginx/error.log  \
--http-log-path=/var/log/nginx/access.log  \
--pid-path=/run/nginx.pid  \
--lock-path=/run/lock/subsys/nginx  \
--user=nginx  \
--group=nginx  \
--with-http_ssl_module \
--with-http_v2_module
# make && make install

今回の検証環境は、

yum install -y pcre-devel

yum install -y zlib-devel

yum install -y openssl-devel

が無い場合、nginxのconfigureでエラーになるため、必要でした。

場合によっては、必要無かったり、他のインストールが必要だったりするかもしれません。


systemdの設定を追加します。

# vi /usr/lib/systemd/system/nginx.service
[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/sh -c "/bin/kill -s HUP $(/bin/cat /var/run/nginx.pid)"
ExecStop=/bin/sh -c "/bin/kill -s TERM $(/bin/cat /var/run/nginx.pid)"

[Install]
WantedBy=multi-user.target

nginx.confを書き換えます。
load_module "/usr/lib64/nginx/modules/ngx_http_headers_more_filter_module.so"; を追加します。
proxy_pass http://localhost:8000/; の設定を追加します。
include /etc/nginx/conf.d/*.conf; を追加します。

# vi /etc/nginx/nginx.conf
クリックして内容を表示
load_module "/usr/lib64/nginx/modules/ngx_http_headers_more_filter_module.so";
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

server {
    listen 80;
    server_name osjs.itccorporation.jp;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://localhost:8000/;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
#    server {
#        listen       80;
#        server_name  localhost;
#
#        #charset koi8-r;
#
#        #access_log  logs/host.access.log  main;
#
#        location / {
#            root   html;
#            index  index.html index.htm;
#        }
#
#        #error_page  404              /404.html;
#
#        # redirect server error pages to the static page /50x.html
#        #
#        error_page   500 502 503 504  /50x.html;
#        location = /50x.html {
#            root   html;
#        }
#
#        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
#        #
#        #location ~ \.php$ {
#        #    proxy_pass   http://127.0.0.1;
#        #}
#
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
#    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    include /etc/nginx/conf.d/*.conf;
}

プロキシの設定を追加します。

# mkdir /etc/nginx/conf.d
# vi /etc/nginx/conf.d/server.conf
クリックして内容を表示
server {
    listen 80;
    server_name    chat.itccorporation.jp;
    location / {
        more_clear_headers 'X-Frame-Options';
        proxy_pass http://192.168.●.●:3000/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forward-Proto http;
        proxy_set_header X-Nginx-Proxy true;
        proxy_redirect off;
    }
}

server {
    listen 80;
    server_name    wekan.itccorporation.jp;
    location / {
        proxy_pass http://192.168.■.■:3001/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forward-Proto http;
        proxy_set_header X-Nginx-Proxy true;
        proxy_redirect off;
    }
}

server {
    listen 80;
    server_name    gitlab.itccorporation.jp;
    location / {
        more_clear_headers 'X-Frame-Options';
        proxy_pass http://192.168.◆.◆/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forward-Proto http;
        proxy_set_header X-Nginx-Proxy true;
        proxy_redirect off;
    }
}

more_clear_headers 'X-Frame-Options';

は、X-Frame-Options: sameorigin

ヘッダーが返ってきて、エラーになるため、X-Frame-Optionsヘッダーを取り除く設定です。

headers-more-nginx-module-0.33

はこれのためにインストールしています。

X-Frame-Options: sameoriginは同一ドメインの場合のみiframe表示を許可するというヘッダーになります。Rocket.Chat、GitLabは、これを返す仕様になっているようです。


ファイアウォールのhttpポートを開けて、nginxを再起動します。

# firewall-cmd --permanent --zone=public --add-service=http
# firewall-cmd --reload
# systemctl enable nginx
# systemctl start nginx

動作確認

繰り返しになりますが、仕組みは、以下の図のようになりますので、パソコンからは、
192.168.▲.▲ osjs.itccorporation.jp
192.168.▲.▲ chat.itccorporation.jp
192.168.▲.▲ wekan.itccorporation.jp
192.168.▲.▲ gitlab.itccorporation.jp
と名前解決できるものとします。



できました!

loading...