FreeBSD で h2o + Gunicorn で PowerDNS Admin を実行する

PowerDNS を PostgreSQL バックエンドでインストールして、PowerDNS Admin を動かしたいということで、 順を追って手順を記録していきます。

もくじ

  • PostgreSQL をインストールする
  • PowerDNS をインストールする
  • PowerDNS Admin をインストールする
  • h2o + Gunicorn で実行する ← 今回

Gunicorn の設定と実行

WSGI の実装でよく使われているものは uWSGI と Gunicorn があるそうなのですが、PowerDNSAdmin の requirements.txt に Gunicorn が入っていてインストールされているので、そちらを使います。

Gunicorn を経由するため、Flask を 127.0.0.1 にのみ bind するように変更します。 前回作成した standalone.py を production.py にコピーして以下を変更します。

BIND_ADDRESS="127.0.0.1"

また、PowerDNSAdmin に付属の run.py は Gunicorn からの実行に使えないので、それ用の web.py を powerdns-admin ディレクトリ(run.py と同じ)に作成します。

from powerdnsadmin import create_app

app = create_app()

/usr/home/pdnsadm/gunicorn_config.py を作成し、次のように Gunicorn の設定を記述します。基本的には自分しか使わないので、workers と threads は控えめに。また、h2o との接続には Unix ソケットを使います。

chdir = "/usr/home/pdnsadm/powerdns-admin"
raw_env = [
        "FLASK_CONF=/usr/home/pdnsadm/powerdns-admin/configs/production.py",
        "FLASK_APP=powerdnsadmin/__init__.py",
        ]
bind = "unix:/tmp/pdnsadmin.sock"
backlog = 2048
workers = 1
threads = 3
worker_class = "sync"
worker_connections = 1000
timeout = 30
keepalive = 2

max_requests = 512
spew = False
daemon = False
pidfile = None
umask = 0
user = "pdnsadm"
group = "pdns"
tmp_upload_dir = None

access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
loglevel = "info"

pdnsadmin のホームディレクトリで以下を実行し、Gunicorn が実行できることを確認します。

$ ./webpy39/bin/gunicorn 

/usr/local/etc/rc.d/pdnsadmin

service コマンドから Gunicorn が実行できるように、次のような rc.d ファイルを作成します。 できたら実行権限を chmod 555 pdnsadmin でつけるのを忘れずに。 sleep 3 をつけたのは、そうしないと restart 時に、シグナルが発行されてから Gunicorn が終了するまでに時間がかかり、その前に start_cmd が実行されてしまって restart がうまくいかないからです。

#!/bin/sh

# PROVIDE: pdnsadmin
# REQUIRE: DAEMON pdns fluent-bit
# KEYWORD: shutdown

#
# Add the following line to /etc/rc.conf to enable pdnsadmin:
# pdnsadmin_enable (bool):      Set to "NO" by default.
#                               Set it to "YES" to enable pdnsadmin.
#

. /etc/rc.subr

name=pdnsadmin
desc="PowerDNS Admin web UI daemon"
rcvar=${name}_enable

load_rc_config ${name}

command="/usr/home/pdnsadm/webpy39/bin/gunicorn"
config="/usr/home/pdnsadm/gunicorn_config.py"
pidfile="/var/run/pdnsadmin.pid"
command_interpreter="/usr/home/pdnsadm/webpy39/bin/python"

start_cmd=start_cmd
stop_cmd='pkill -TERM -U pdnsadm -F ${pidfile}; sleep 3'

pdnsadmin_enable=${pdnsadmin_enable:-NO}

start_cmd()
{
        check_startmsgs && echo "Starting ${name}."
        daemon -f -u pdnsadm -p ${pidfile} ${command} web:app --config ${config} --log-syslog
}

run_rc_command $1

h2o の設定と実行

関係ありそうなところはこんな感じです。

hosts:
  www.example.net:
    paths:
      "/":
        proxy.reverse.url: http://[unix:/tmp/pdnsadmin.sock]/
        proxy.preserve-host: ON
      "/static":
        file.dir: /usr/home/pdnsadm/powerdns-admin/powerdnsadmin/static

サブディレクトリで実行する

ここまでの設定だと http(s)://www.example.net/ のルートを取られてしまうので、できればサブディレクトリを切ってそこで実行するようにしたいです。http(s)://www.example.net/powerdns/ とか。

変更点は2箇所あって、まず web.py で SCRIPT_NAME を設定します。

import os
from powerdnsadmin import create_app

os.environ["SCRIPT_NAME"] = "/powerdns"
app = create_app()

次に、h2o.conf を次のように変更します。

hosts:
  www.example.net:
    paths:
      "/powerdns/static":
        file.dir: /usr/home/pdnsadm/powerdns-admin/powerdnsadmin/static
      "/powerdns":
        proxy.reverse.url: http://[unix:/tmp/pdnsadmin.sock]/powerdns
        proxy.preserve-host: ON

これで、http(s)://www.example.net/powerdns/ で PowerDNSAdmin を使えるようになりました。

FreeBSD に PowerDNSAdmin をインストールする

PowerDNS を PostgreSQL バックエンドでインストールして、PowerDNS Admin を動かしたいということで、 順を追って手順を記録していきます。

もくじ

  • PostgreSQL をインストールする
  • PowerDNS をインストールする
  • PowerDNS Admin をインストールする ← 今回
  • h2o + Gunicorn で実行する

参考

github.com

準備

pdnsadm ユーザーの作成

PowerDNSAdmin を実行するユーザーを用意します。

# pw useradd pdnsadm -u 5120 -g pdns -s /usr/sbin/nologin -c "PowerDNSAdmin unpriviledged user"
# mkdir -p /usr/home/pdnsadm
# chown pdnsadm:pdns /usr/home/pdnsadm

ホームディレクトリは、本来 pw コマンドが自動で作ってくれるはずなのですが、なぜか作られませんでした。

PowerDNSAdmin v0.3.0 の取得

pdnsadm ユーザーになってホームディレクトリで

$ fetch https://github.com/PowerDNS-Admin/PowerDNS-Admin/archive/refs/tags/v0.3.0.zip
$ tar -xvs /PowerDNS-Admin-0.3.0/powerdns-admin/ -f v0.3.0.zip

必要なパッケージのインストール

まずは FreeBSD のパッケージをインストールします。

# pkg install python39 node16 yarn rust
# pkg install libxml2 libxslt pkgconf py39-bcrypt py39-cffi py39-ldap py39-xmlsec 

本当は py39-cryptography を入れることで rust の必要性を回避したいところですが、なかなか ports が 3.4.8 から上がらないので、仕方なく rust も入れます。 次に Python の virtualenv を用意しますが、上でインストールしたものは pip でのインストールが面倒なので、--system-site-packages オプションをつけます。pdnsadm ユーザーで、ホームディレクトリにて

$ python3.9 -m venv webpy39 --system-site-packages
$ . ./webpy39/bin/activate
(webpy39) $ python -m pip install --upgrade pip wheel

shell を tcsh にしている場合は

source ./webpy39/bin/activate.csh

そのまま PowerDNSAdmin のディレクトリに入って、要求されている python ライブラリをインストールします。

pip install の前に、mysqlclient==2.0.1コメントアウトします。また、PostgreSQL を利用するので、psycopg2 をインストールします。細かいですが、shell を上記の通りに /usr/sbin/nologin に設定している場合、su -m か何かで pdnsadm ユーザー権限になっていると思うので、HOME 環境変数の設定を忘れないでください。(tcsh だと setenv ですね)

(webpy39) $ export HOME=/usr/home/pdnsadm
(webpy39) $ cd powerdns-admin
(webpy39) $ pip install -r requirements.txt
(webpy39) $ pip install psycopg2

PowerDNSAdmin の設定と実行

PowerDNSAdmin の設定

powerdns-admin/powerdnsadmin/default_config.py に設定があるので、それを書き換えるという手もありますが、ひとまず powerdns-admin/configs/standalone.py にコピーしていじります。

import urllib.parse

# Basic App Configuration
SALT = "なんかランダム文字列"
SECRET_KEY = "なんかランダム文字列"
BIND_ADDRESS = "0.0.0.0"
PORT = 9191
OFFLINE_MODE = False

# Database Configuration
SQLA_DB_USER = "pdns"
SQLA_DB_PASSWORD = "DBに設定したpdns roleのパスワード"
SQLA_DB_HOST = "[::1]"
SQLA_DB_NAME = "pdns"
SQALCHEMY_TRACK_MODIFICATIONS = True

# Database - PostgreSQL
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{}:{}@{}/{}".format(
        urllib.parse.quote_plus(SQLA_DB_USER),
        urllib.parse.quote_plus(SQLA_DB_PASSWORD),
        SQLA_DB_HOST,
        SQLA_DB_NAME
        )

SAML_ENABLED = False
SAML_ASSERTION_ENCRYPTED = True

PostgreSQL への接続の設定については公式サイトに記述があります。 パスワードというかランダム文字列の生成については、専用のプログラムを使う方法や、汎用の方法があるので、参考にしてください。

Flask の設定

環境変数 FLASK_CONF と FLASK_APP を設定します。

$ cd /usr/home/pdnsadm/powerdns-admin
$ export FLASK_CONF=/usr/home/pdnsadm/powerdns-admin/configs/standalone.py
$ export FLASK_APP=powerdnsadmin/__init__.py

次にデータベースを初期化して web assets をビルドします。(よく分かっていませんが、参考サイトの言うがままに)

$ flask db upgrade
$ yarn install --pure-lockfile
$ flask assets build

実行

これで PowerDNSAdmin のインストールと設定ができたはずなので、

./run.py

を実行して 9191 番ポートで PowerDNSAdmin が動いているかを確認します。

h2o + Gunicorn で実行する

ちょっと長くなるので、次回に。

FreeBSD に PowerDNS をインストールする

PowerDNSPostgreSQL バックエンドでインストールして、PowerDNS Admin を動かしたいということで、 順を追って手順を記録していきます。

もくじ

  • PostgreSQL をインストールする
  • PowerDNS をインストールする ← 今回
  • PowerDNS Admin をインストールする

インストールと設定

パッケージから PowerDNS をインストールします。

# pkg install powerdns

PostgreSQL の設定

PostgreSQL を操作するための postgres というユーザーは既にできているはずなので、PostgreSQL に pdns というユーザーと pdns というデータベースを作成します。

# psql postgres
postgres=# CREATE ROLE pdns WITH LOGIN PASSWORD 'password';
CREATE ROLE
postgres=# CREATE DATABASE pdns OWNER pdns ENCODING 'UTF8';
CREATE DATABASE

同じことを shell のコマンドラインからは次のように行なえます。

# sudo -u postgres createuser -P -s -e pdns
# sudo -u postgres createdb -O pdns -E UTF8 pdns "Database for PowerDNS"

次に、データベースにスキーマを適用します。スキーマの定義は /usr/local/share/doc/powerdns/schema.pgsql.sql にあるはずです。

# psql -U pdns -d pdns -a -f /usr/local/share/doc/powerdns/schema.pgsql.sql

pdns.conf の設定

/usr/local/etc/pdns/pdns.conf を以下のように変更します。PowerDNSAdmin が API を使うので、API も有効にします。

launch=gpgsql
gpgsql-host=/tmp/.s.PGSQL.5432  # listen している UNIX ソケットを確認のこと
gpgsql-dbname=pdns
gpgsql-user=pdns
gpgsql-password=password

api=yes
api-key=なんかランダムな文字列

PowerDNS 実行

/etc/rc.conf に

pdns_enable="YES"

を入れて、

# service pdns start

を実行します。

FreeBSD に PostgreSQL をインストールする

PowerDNS を PostgreSQL バックエンドでインストールして、PowerDNS Admin を動かしたいということで、 順を追って手順を記録していきます。

もくじ

  • PostgreSQL をインストールする ← 今回
  • PowerDNS をインストールする
  • PowerDNS Admin をインストールする

インストールの前に

PostgreSQL メジャーバージョンアップの場合は、忘れずに pg_dump しましょう。 でも、この記事は新規インストールを前提としているので、その辺は書きません。

インストールと設定

パッケージから postgresql14-server をインストールします*1

# pkg install postgresql14-server

サーバーの設定に関する注意はインストール時のパッケージメッセージでも出てきますが、/usr/local/share/doc/postgresql/README-server にあります。 こちらを参照すると、/etc/login.conf を設定するように書かれているので、次の項目を追記します。

postgres:\
        :lang=en_US.UTF-8:\
        :setenv=LC_COLLATE=C:\
        :tc=default:

追記したら cap_mkdb /etc/login.conf を実行します。

/etc/login.conf の設定を反映してサーバーが起動するように、/etc/rc.conf を設定します。

postgresql_login_class="postgres"
postgresql_enable="YES"

その上で、データベースの初期化を行います。

# service postgresql initdb
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locales
  COLLATE:  C
  CTYPE:    C.UTF-8
  MESSAGES: C.UTF-8
  MONETARY: C.UTF-8
  NUMERIC:  C.UTF-8
  TIME:     C.UTF-8
The default text search configuration will be set to "english".

Data page checksums are disabled.

creating directory /var/db/postgres/data14 ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Japan
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    /usr/local/bin/pg_ctl -D /var/db/postgres/data14 -l logfile start

データベース関連のファイルと設定ファイルが /var/db/postgres/data14 に出来ていますので、必要に応じて pg_hba.conf と postgresql.conf を設定します。 また、log は syslog に送られますので、/var/log/postgresql.log に記録されるように syslog の設定を行います。 /usr/local/etc/syslog.d/postgresql.conf に

!postgres
*.*                                                                     /var/log/postgresql.log

と記述し、root で

# touch /var/log/postgresql.log

を実行します。さらに newsyslog の設定を /usr/local/etc/newsyslog.conf.d/postgresql.conf に次のように設定しておきます。

/var/log/postgresql.conf    644    7    10000    *    J

PowerDNS のバックエンドにするということで、slave への反映を PowerDNS 推奨の DB レプリケーションで行う場合は、localhost 以外にも bind するように設定します。PostgreSQL 14 の場合は /var/db/postgres/data14/postgresql.conf で

listen_addresses = '*'

特定のアドレスだけ listen する場合はカンマで区切ってアドレスを列挙します。

以上の設定が終わったら、

service postgresql start

postgresql server を実行します。

*1:なんて書いてぼーっとしているうちに postgresql15 が出てしまいました

jail 内で net/samba416 を動かす

一ヶ月ほど前に samba416 が ports に入ったが、以前のバージョンにはなかった注意点がある。

fdescfs を(デフォルトでは) /var/run/samba4/fd にマウントして使うようになったらしく*1、マウントしていない場合に起動スクリプトでマウントするようになっている。しかし、jail 内では mount コマンドが使えないので、そのまま jail 内で動かそうとするとエラーになる。というわけで、jail 起動前に上記の path に fdescfs をマウントする必要がある。

jail.conf に

mount.fstab = fstab."${name}"

としていたら(おすすめ)、 /etc/fstab.jailname に

fdescfs  /usr/jails/jailname/var/run/samba4/fd  fdescfs  rw,nodup  0  0

と記述する。 /usr/jails/jailname は、host 側での jail の root にあたる path に置き換えること。つまり、host 側で mount し、それが jail 内で /var/run/samba4/fd と見えるようにする。 なお、/var/run/samba4 の部分を変えたい場合、(jail の)rc.conf に

samba_server_piddir="/some/where/in/the/jail"

とか書く。詳しくは /usr/local/etc/rc.d/samba_server を参照のこと。

*1:たぶんなんかの Linuxism

福祉作業所のちょっとおもしろい事業

障害者支援はしたいけど

作業所でグッズを作って売っているところは多いけど、 家にものが溢れているのでそういうのは正直なところいらないなあと思っている。 「実用的」というのだとパンを作って売っているところが結構あるけれども、近所にないと買いに行きにくい。

そういうものに対するアンテナも低いのだけど、それでも生きていると 遠くからでも通販的に利用できて機会があれば利用してみたいなという事業を行っているところを知ったりする。

数としては3箇所くらいしか知らないけど、そういうところを紹介してみよう。

福祉作業所のちょっとおもしろい事業

ココロスキップ: 点字名刺プロジェクト

埼玉県越谷市の福祉作業所で、名刺を送ると点字を刻印してくれる サービスを行っている。 視覚障害者に最低賃金を払いたいというのでお金をとれるサービスをいろいろ考えた感じ。

共働学舎: トイレットペーパー

東京都町田市の共働学舎 では、 雑紙をリサイクルしたトイレットペーパーを販売している。 なかなかリサイクル用途のない雑紙を活用しているという点もなんだかよい。 (私はエネルギー収支的にやる価値のあるリサイクルなのかどうかは理解していないが。) 大口の利用がなくなってしまって生産能力に大幅な余剰があるらしい。

のんの: ダイヤルキー解錠

東京都足立区ののんのでは、 4桁までのダイヤルキーを解錠するサービスを行っていたが、 今はお休みしているみたい

ほかにもあるんだろうな

こういう、機会を見つけて利用してみたいと思うサービスを提供している福祉作業所は ほかにもあるのだと思う。アンテナは低いけど、出会ったら大切にしたい。

GLX モジュールが動かない

現象

X11 の GLXモジュールが動かない。 x11-toolkits/nanogui を利用したプログラムを動かそうとすると、 GLXモジュールが有効でないから動かないよという感じのメッセージが出てくる。

NVIDIAGPUを使っていたけど、ほかのGPUに変えた場合にこの問題に当たりがち。

エラーメッセージ

GLX モジュールが動いていないと言われるので GLX で /var/log/Xorg.0.log を検索すると、 私の場合はこういうログがあった。

[    76.231] (II) LoadModule: "glx"
[    76.281] (II) Loading /usr/local/lib/xorg/modules/extensions/libglx.so
[    77.680] (II) Module glx: vendor="NVIDIA Corporation"
[    77.680]    compiled for 4.0.2, module version = 1.0.0
[    77.680]    Module class: X.Org Server Extension
[    77.680] (II) NVIDIA GLX Module  390.144  Wed Jun  2 22:59:08 UTC 2021
...
[    79.604] (II) Initializing extension GLX
[    79.604] (EE) Failed to initialize GLX extension (Compatible NVIDIA X driver not found)

ここで「あれ? NVIDIAGPUから乗り換えたのに」と思えていれば解決は早かったのだろうが、 StackExchangeのQ&A で説明を見るまではなんでか理解していなかった。

対処法

x11/nvidia-driver* と x11-servers/xorg-server をアンインストールして、 x11-servers/xorg-server をインストールしなおす。

これでうまく行くはずだけど、それでもログのエラーメッセージが変わらなかったら、 /usr/local/lib/xorg/modules/extensions/.xorg/libglx.so を /usr/local/lib/xorg/modules/extensions/libglx.so にコピーすればよい。

要するに、/usr/local/lib/xorg/modules/extensions/libglx.so が nvidia-server のもので、 xorg ではなくてそちらがロードされてしまうのが問題なのだ。

そうすると /var/log/Xorg.0.log のメッセージは次のようになるはず。

[    74.516] (II) LoadModule: "glx"
[    74.520] (II) Loading /usr/local/lib/xorg/modules/extensions/libglx.so
[    75.449] (II) Module glx: vendor="X.Org Foundation"
[    75.449]    compiled for 1.20.14, module version = 1.0.0
[    75.449]    ABI class: X.Org Server Extension, version 10.0
...
[    77.773] (II) Initializing extension GLX
[    77.778] (II) AIGLX: Loaded and initialized radeonsi
[    77.778] (II) GLX: Initialized DRI2 GL provider for screen 0