プロジェクトフォルダ直下に集約した Node-RED を動かした Heroku にパスワード設定するメモ

プロジェクトフォルダ直下に集約した Node-RED を動かした Heroku にパスワード設定するメモです。

やりたいこと

プロジェクトフォルダ直下に集約した Node-RED 環境を Heroku で動かすメモ

では一旦「公開時はエディタ画面が無効で、開発時はエディタ画面で編集できる」形で対応しましたが、Heroku の環境変数の値でログイン ID とログインパスワード を用意してログインを実現します。

2021/09/21 現在の情報で進めます。

Heroku の環境変数を設定する

設定と環境設定 | Heroku Dev Center

を、参考に Heroku Dashboard で、

image

アプリの Settings タブから、

image

環境変数の値でログイン ID とログインパスワード を用意します。

  • NODERED_LOGIN_ID
    • ログイン ID
  • NODERED_LOGIN_PASSWORD
    • ログインパスワード

と設定しました。

setting.js の adminAuth を編集

認証についての挙動は セキュリティのカスタムユーザ認証 の内容が分かりやすいです。

ここを読み込んで Promise で非同期な問い合わせで認証のやり取りを作っていることが分かりました。基本的にはこちらのコードをベースにして進めていきます。

以下のように書きました。

adminAuth は、デフォルトの setting.js の場合はコメントアウトされています。

書き加えるときは慎重に行いましょう。setting.js 自体がうまく設定できているかは、ログが出しにくく検証にちょっとコツが必要になるので、自分が行うときも緊張する場面です。

    adminAuth: {
      type: "credentials",
      users: function(username) {
        return new Promise(function(resolve) {
          // ここでユーザ名が許可されたユーザかどうかを
          // チェックする処理を行う
          if (process.env.NODERED_LOGIN_ID == username) {
            // ユーザ名が存在すればユーザオブジェクトを返す。
            // 'username' と 'permissions' プロパティは必須です。
            var user = { username: process.env.NODERED_LOGIN_ID, permissions: "*" };
            resolve(user);
          } else {
            // ユーザ名が存在しない場合はnullを返す。
            resolve(null);
          }
        });
      },
      authenticate: function(username, password) {
        return new Promise(function(resolve) {
          // ここでユーザー名/パスワードの組み合わせを
          // チェックする処理を行う
          if (process.env.NODERED_LOGIN_ID == username &&
            process.env.NODERED_LOGIN_PASSWORD == password) {
              // ユーザー名/パスワードの組み合わせが有効な場合はユーザオブジェクトを返す。
              // users(username);を呼び出すのと同様です。
              var user = { username: process.env.NODERED_LOGIN_ID, permissions: "*" };
              resolve(user);
          } else {
              // ユーザー名/パスワードの組み合わせが無効な場合は
              // nullを返す。
              resolve(null);
          }
        });
      }
    },

process.env.NODERED_LOGIN_IDprocess.env.NODERED_LOGIN_PASSWORD は、先ほど設定した Heroku の環境変数を呼び出して判定しています。

package.json の変更

手元で作業するとき「だけ」認証を無効

手元で作業するとき「だけ」認証を無効にします。

scripts の node-red の呼び出しで -D adminAuth=null を加えて、起動時に認証がかかるはずのところを、認証がかからない adminAuth=null で上書きします。

"node-red": "./node_modules/.bin/node-red -p 18801 -u ./node-red_dir -s ./node-red_dir/settings.js -D adminAuth=null",

これで、手元での作業時に npm run node-redで起動すれば、認証なしで作業ができます。

image

設定出来たら動かしてみましょう。

Heroku 公開時に起動する node-red-heroku-auth コマンドを作る

以前、 node-red-no-editor というコマンドをつくって Procfile から呼び出して Heroku 公開時にはエディタ無効しましたが、新たに node-red-heroku-auth コマンドとつくります。

scripts の node-red-heroku-auth の呼び出しをつくり、以下のように設定します。

"node-red-heroku-auth": "node-red -u ./node-red_dir -s ./node-red_dir/settings.js",

設定出来たら動かしてみましょう。

npm run node-red-heroku-auth

起動時注意すべきは 18801 ポートでなく、手元でのデフォルトポート 1880 ポートで起動するのに注意してください。

image

私はこのあたりは起動ログで出てくる Server now running at ~ と書かれているアドレスを見るようにしています。別作業で 1880 ポートで起動している Node-RED とバッティングして起動が阻害されてないかも確認しておきましょう。

http://127.0.0.1:1880/ で見てみると認証が設定できてます。ローカルでは、Heroku で設定していた NODERED_LOGIN_IDNODERED_LOGIN_PASSWORD もないので、永久にログインできないですが、挙動を見ることは大事です。

ここまで設定した package.json の様子

参考までにここまで設定した package.json の様子です。

{
  "name": "package.json",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "node-red-heroku-auth": "node-red -u ./node-red_dir -s ./node-red_dir/settings.js",
    "node-red-no-editor": "node-red -u ./node-red_dir -s ./node-red_dir/settings.js -D httpAdminRoot=false",
    "node-red": "./node_modules/.bin/node-red -p 18801 -u ./node-red_dir -s ./node-red_dir/settings.js -D adminAuth=null",
    "init": "npm run init:mkdir && npm run init:get-settings.js",
    "init:mkdir": "node -e \"console.log('[START] mkdir-node-red_dir');const fs = require('fs');const path_nodered_dir = './node-red_dir';if (fs.existsSync(path_nodered_dir)) {console.log( '[ALERT] ' + path_nodered_dir + ' has existed!');} else {console.log('[OK] mkdir -> ' + path_nodered_dir );fs.mkdirSync(path_nodered_dir);}\"",
    "init:get-settings.js": "node -e \"const url = 'https://raw.githubusercontent.com/node-red/node-red/master/packages/node_modules/node-red/settings.js';const stream_output_path = './node-red_dir/settings.js';if (fs.existsSync(stream_output_path)) {console.log( '[ALERT] ' + stream_output_path + ' has existed!');} else {console.log('[START] download settings.js ...');const https = require('https');const fs = require('fs');const stream = fs.createWriteStream(stream_output_path);https.get(url, function(res){res.pipe(stream);res.on('end', function () {stream.close();console.log('[OK] got settings.js! -> ' + stream_output_path);});});}\""
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "node-red": "^2.0.6"
  }
}

Heroku の起動コマンドを Procfile で変更

以前 web: npm run node-red-no-editor にして node-red-no-editor コマンドで Heroku 公開時にはエディタ無効にしていましたが、今回対応した認証で動くようにします。

プロジェクトフォルダ直下の Procfile を以下に変更します。

web: npm run node-red-heroku-auth

node-red-heroku-auth コマンドと差し替えました。

Heroku に反映

ここまで出来たら Git で

git add .

今回更新したファイル群を add でコミット対象にして

git commit -m 'update auth'

commit でコミットします。

その上で

git push heroku master

で Heroku 側に push してアプリに反映します。

Heroku でログイン確認

ということで、Heroku にアクセスしてみると認証が動作しています。

image

Heroku 環境設定で設定した NODERED_LOGIN_ID をユーザ名に、 NODERED_LOGIN_PASSWORD はパスワードのところに入力してログインできました!

念のため、環境変数の有無で検証

NODERED_LOGIN_ID と NODERED_LOGIN_PASSWORDの環境変数の有無で検証してみました。

  • NODERED_LOGIN_ID と NODERED_LOGIN_PASSWORD ともに未設定
    • 空文字や他の文字入力でログインできず(守られている)
  • NODERED_LOGIN_ID 未設定・NODERED_LOGIN_PASSWORD 設定済
    • 空文字や他の文字入力でログインできず(守られている)
  • NODERED_LOGIN_ID 設定済・NODERED_LOGIN_PASSWORD 未設定
    • 空文字や他の文字入力でログインできず(守られている)

ということで、無事機能していました。うっかり、NODERED_LOGIN_ID と NODERED_LOGIN_PASSWORD が設定されていないときに空文字や他の文字入力でログインできたらヤダなと思っていたんですが、ちゃんと守られてました。