Help us understand the problem. What is going on with this article?

Growi構築メモ

More than 1 year has passed since last update.

1. 背景

個人的にMarkdownで残していた技術メモが、数が多くなってきたこと、他人と共有(他人に見せる)機会が増えたことから、Markdownメモを気軽に集積および管理できるソリューションを探していたところ、GrowiというWikiにたどり着きました。

このページでは、Windows OS(Windows 7 SP1 32bit)に、Growiを導入した際の、構築手順や注意点をまとめたメモです。なお、LinuxではなくWindows 7 32bitが対象である理由は、会社のOA PCのOSがWindows 7 SP1 32bitだからです。

なお、個人では、VPS(2core CPU、2GB RAM)を契約しCentOS 7上で、nginx(let's encryptの証明書でTLS化)+nodejs(nginxのバックエンド)+mongodb+redis+growiで運用していますが、Linuxで動かした方が遙かに快適です。そのため、Windowsで動かすことはよほどの制約が無い限りお勧めしません。

2. Growiとは

Growiは、WESEEK社が公開しているオープンソースのWikiをベースとしたコミュニケーションツール。MarkDown形式で書けるソフトウェアで、MarkDownのリアルタイムプレビューや投稿ユーザの設定ができます。

個人的には、「Qiitaのオンプレミス版を実現するソフトウェア」という印象です。

Growi画面サンプル

3. 必要なソフトウェア

以下に、最低限必要なソフトウェアを記載します。

  • Git for Windows
  • NodeJS 8.x(Windows 32bit用)
  • MongoDB 3.x(Windows 32bit用)

MongoDB公式サイトを見ると、Windows Server 2008 R2用しかダウンロードできないように見えますが、ここからWindows 32bit用がダウンロードできます。

4. 構築手順

4.1. Git for Windowsのインストール

配布元からインストーラを入手し、インストールします。社内LAN等で、インターネットアクセスにプロキシサーバを経由する必要がある場合には、gitにプロキシサーバ設定が必要です。それ以外は、特記事項はありません。

4.2. NodeJSのインストール

NodeJSを入手して、インストールします。インストーラでインストールする場合は、C:\Program Files以外に入れたほうが幸せになれると思います(設定変更に管理者権限を要求されないため)。以下では、C:\Node にNodeJSをインストールした想定で記載します。

(1) 環境変数PATHの設定

必須ではありませんが、推奨です。C:\Nodeを環境変数PATHに追加します。

(2) Proxy設定

社内LAN等で、インターネットアクセスにプロキシサーバを経由する必要がある場合には、npmに設定が必要です。以下は、プロキシサーバが http://192.168.1.200:8080/ 、ユーザ名がuser、パスワードがpasswordの場合の設定方法です。

C:\> npm config set proxy http://user:password@192.168.1.200:8080/

(3) yarnのインストール

後述のGrowiのビルド時に必要なyarnというツールをインストールします。

C:\> npm install -g yarn

4.3. MongoDBのインストール

以下は、MongoDBを C:\MongoDB 配下にインストールした想定での記述です。

(1) 設定ファイルの作成

C:\MongoDB\bin配下に、mongodb.configというファイルを作成します(特にbin配下でないいけないわけではありません)。
そこに、以下のようにDBの格納先(dbpath)と待受IPアドレス(bind_ip)を記載します。なお、dbpathに指定したフォルダが存在しない場合は、作成します。

bind_ip=127.0.0.1
dbpath=c:\MongoDB\data

(2) MongoDBサービスの起動

コマンドプロンプトから、以下のコマンドを実行します。

C:\> C:\MongoDB\bin\mongod.exe --config C:\MongoDB\bin\mongodb.config

4.4. Growiのインストール

(1) Growiのビルド

バイナリzipやインストーラは配布されていないため、Git経由でビルドおよびインストールを実施します。

C:\> cd C:\Node
C:\Node> git clone https://github.com/weseek/growi.git
…
C:\Node> cd growi
C:\Node\growi> yarn
…

(2) Growiの起動

コマンドプロンプトから、以下のコマンドを実行します。結構待たされますが、最終的には3000/tcpにWebサービスが稼動しますので、ブラウザでアクセスし、初期設定画面が表示されることを確認します。

  • 1行目の環境変数MONGO_URIは、MongoDBの稼動場所を示すもので、必須です。
  • 2行目のFILE_UPLOAD=localの環境変数FILE_UPLOADは、Growiサーバ上に画像等のファイルをアップロードする機能を有効にするために設定しています。
  • 待受ポート番号を変えたい場合は、npm startする前に環境変数PORTを設定します。例えば、18080/tcpで待ち受けたい場合は、set PORT=18080として、npm startします。
C:\> set MONGO_URI=mongodb://127.0.0.1:27017/growi
C:\> set FILE_UPLOAD=local
C:\> cd C:\Node\growi
C:\Node\growi> npm start
…

4.5. その他

4.5.1. lsxプラグインのインストール

lsxプラグインは、ページをリストアップするGrowiのプラグインです。必須ではありませんが、便利なのでインストールしておくといいでしょう。

C:\> cd C:\Node\growi
C:\> yarn add growi-plugin-lsx

4.5.2. 全文検索機能(Elasticsearch)のインストール

Growi本体には検索機能がなく、検索はElasticsearchという外部機能に頼っています。そのため、検索機能が必要な場合は、別途Elasticsearchのインストールと設定が必要です。

(1) 必要なソフトウェアの入手とインストール

  • Java (JRE)
  • Elasticsearch 5.x

Elasticsearchは、Javaアプリケーションであるため、Java (JRE)が必要です。JavaおよびElasticsearchのインストール自体は、特記事項はないため、割愛します。

(2) 分析プラグインの追加

規格化プラグイン(analysis-icu)と日本語プラグイン(analysis-kuromoji)をインストールします。Elasticsearchをインストールしたディレクトリに移り、以下のコマンドを実行します。

C:\Elasticsearch> bin\elasticsearch-plugin install analysis-icu
C:\Elasticsearch> bin\elasticsearch-plugin install analysis-kuromoji

(3) Elasticsearchサービスの起動

(4) Growi起動変数の追加

4.4. (2)で、npm startする前に設定する環境変数を1つ増やす必要があります。以下のように、環境変数ELASTICSEARCH_URIを追加設定したあとに、npm startしてください。

set ELASTICSEARCH_URI=http://127.0.0.1:9200

4.5.3. HTTPS対応

(1) サーバ証明書の作成

OpenSSLにて作成します。今回は、自己署名の証明書を使用します(参考:ろば電子が詰まっている - オレオレ証明書をopensslで作る(詳細版))。

(2) Growi本体の対応

Nginx等のリバースプロキシを立てればHTTPS通信へ対応できますが、Growi本体だけでHTTPSに対応できればと思ったため、ソース(lib/crowi/index.js 最近では src/server/crowi)を一部改造しました。

なお、最近は、バージョンアップのたびにGROWIのソースを修正するのが面倒になったのと、NodeJSではTLSの弱い暗号化設定を無効化するに限界があるのがわかったため、GROWIのソースはいじらずNginxをリバースプロキシにして運用しています。

--- growi/lib/crowi/index.js.orig       2018-06-09 14:12:33.794000000 +0900
+++ growi/lib/crowi/index.js    2018-06-09 14:17:18.367000000 +0900
@@ -354,25 +354,58 @@
     })
     .then(function(express) {
       return new Promise((resolve) => {
-        server = express.listen(self.port, function() {
-          logger.info(`[${self.node_env}] Express server listening on port ${self.port}`);
+        // In case of HTTPS ...
+        if (process.env.ENABLE_HTTPS) {
+          var https = require('https');
+          var fs = require('fs');
+          var https_options = {
+            secureProtocol: 'TLSv1_2_method',
+            key: fs.readFileSync(process.env.SERVER_KEY),
+            cert: fs.readFileSync(process.env.SERVER_CERT)
+          };

-          // setup for dev
-          if (self.node_env === 'development') {
-            const eazyLogger = require('eazy-logger').Logger({
-              prefix: '[{green:GROWI}] ',
-              useLevelPrefixes: false,
-            });
-
-            eazyLogger.info('{bold:Server URLs:}');
-            eazyLogger.unprefixed('info', '{grey:=======================================}');
-            eazyLogger.unprefixed('info', `         APP: {magenta:http://localhost:${self.port}}`);
-            eazyLogger.unprefixed('info', '{grey:=======================================}');
-
-            self.crowiDev.setup(server, express);
-          }
-          resolve(server);
-        });
+          server = https.createServer(https_options, express).listen(self.port, function() {
+            logger.info(`[${self.node_env}] Express server listening on port ${self.port}`);
+
+            // setup for dev
+            if (self.node_env === 'development') {
+              const eazyLogger = require('eazy-logger').Logger({
+                prefix: '[{green:GROWI}] ',
+                useLevelPrefixes: false,
+              });
+
+              eazyLogger.info('{bold:Server URLs:}');
+              eazyLogger.unprefixed('info', '{grey:=======================================}');
+              eazyLogger.unprefixed('info', `         APP: {magenta:https://localhost:${self.port}}`);
+              eazyLogger.unprefixed('info', '{grey:=======================================}');
+
+              self.crowiDev.setup(server, express);
+            };
+            resolve(server);
+          });
+        }
+        // In case of HTTP ...
+        else {
+          server = express.listen(self.port, function() {
+            logger.info(`[${self.node_env}] Express server listening on port ${self.port}`);
+
+            // setup for dev
+            if (self.node_env === 'development') {
+              const eazyLogger = require('eazy-logger').Logger({
+                prefix: '[{green:GROWI}] ',
+                useLevelPrefixes: false,
+              });
+
+              eazyLogger.info('{bold:Server URLs:}');
+              eazyLogger.unprefixed('info', '{grey:=======================================}');
+              eazyLogger.unprefixed('info', `         APP: {magenta:http://localhost:${self.port}}`);
+              eazyLogger.unprefixed('info', '{grey:=======================================}');
+
+              self.crowiDev.setup(server, express);
+            }
+            resolve(server);
+          });
+        };

         io = require('socket.io')(server);
         io.sockets.on('connection', function(socket) {

上記ソース(パッチ)を見ればわかりますが、以下の環境変数からHTTPSするか否かや証明書の場所を設定できるようにしています。

  • FORCE_HTTPS…値が設定されていれば、通信をHTTPSにする
  • SERVER_KEY…サーバ証明書の秘密鍵への相対パス
  • SERVER_CERT…サーバ証明書への相対パス

有効にするには、4.4. (2)で、npm startする前に設定する環境変数を3つ増やす必要があります。以下のように、追加設定したあとに、npm startしてください。

set ENABLE_HTTPS=1
set SERVER_KEY=cert\server.key
set SERVER_CERT=cert\server.crt

ここでは、4.5.3. (1)で作成した秘密鍵とサーバ証明書をgrowiをインストールしたディレクトリ配下(C:\Node\growi\cert)に保存した想定です。

4.5.4 目次のスクロール

GithubのIssue(https://github.com/weseek/growi/issues/408)
にも上がっていますが、コンテンツの目次項目数が多い(目次項目数>ブラウザの高さ)と下の方の目次が表示されません。Qiitaのように目次のスクロールバーは表示されると便利かと思うので、以下のような暫定回避策を実施しました。
現時点(どのバージョンから修正がかかっているかは追っていない)で対応されているので、以下の修正は不要となりました。

  • 「管理」→「カスタマイズ→カスタムヘッダーHTML」に、以下のJavaScriptを設定します。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jQuery-slimScroll/1.3.8/jquery.slimscroll.min.js"></script>
<script>
function DrawScrollbar() {
  // 高さ(h) = ウィンドウの内部高ーページヘッダ高ー左上のアイコン高(32固定)
  var h = window.innerHeight - document.getElementById('page-header').clientHeight - 32;
  $('#revision-toc-content').slimScroll({
    railVisible: true,
    railColor: '#f00',
    position: 'right',
    height: h,
  });
}
</script>

<script>
$(function(){
  DrawScrollbar();
});
</script>

<script>
(function () {
  var timer = 0;

  window.onresize = function () {
    if (timer > 0) {
      clearTimeout(timer);
    }

    timer = setTimeout(function () {
      DrawScrollbar();
    }, 200);
  };
}());
</script>

4.5.5 Active Directoryとの連携

「セキュリティ設定」→「LDAPコンフィギュレーション」のBind DNにxxx@domain.nameが設定できないようになっているため、以下のように該当ファイルで入力制限を解除することで、暫定対処できます。v3.1.0で対応されたので、以下の修正は不要となりました。

lib\form\admin\securityPassportLdap.js
  //field('settingForm[security:passport-ldap:bindDN]').trim()
      // https://regex101.com/r/jK8lpO/1
  //    .is(/^(,?[^,=\s]+=[^,=\s]+){1,}$/, 'Bind DN is invalid. <small><a href="https://regex101.com/r/jK8lpO/1">&gt;&gt; Regex</a></small>'),
  field('settingForm[security:passport-ldap:bindDN]'),

5. その他

5.1. Growiの脆弱性(2018/8/30時点)

v3.2.1での検証結果です。過去のバージョンでクロスサイトスクリプティング(以下、XSS)の対策が実装されていますが、一部対策漏れがあるようです。以下のURLをブラウザで叩くと、(GROWIが非公開でログインが必要な場合等以外では)「xss」のポップアップが上がります。なお、Google ChromeやMicrosoft Edge等では、ブラウザのXSSフィルタが作動するので、XSSを再現できません(Internet Explorerなら再現可能)。

http://<サーバFQDNまたはIP>/<scr<script>ipt>alert("xss")</scr</script>ipt>

XSS対策処理として、入力URLに対して<script>と</script>と除去しているようですが、除去後のURL文字列に対して更に除去ができていないようです。
(2018/12/26更新)v3.2.5 以降で修正で修正されているとの連絡を受けたので、最新版では問題ないようです。

以上

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした