Ruby:HTTPクライアントライブラリを http gem にのりかえた

すごーく久しぶりに、Ruby で HTTP アクセスするってことをやった。使い慣れた httpclient を使ったんだけど、こんなエラーが出た。

C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/httpclient-2.8.3/lib/httpclient/ssl_socket.rb:103:in `connect': SSL_connect returned=1 errno=0 peeraddr=[2606:4700:3032::ac43:8791]:443 state=error: certificate verify failed (certificate has expired) (OpenSSL::SSL::SSLError)
(以下略)

SSLのエラーだ。

ググって調べてみると、httpclient gem は信頼できる証明書を独自に持っていてそれがもうメンテされていない、ということが解った。

上の記事では、システムのデフォルトの証明書を利用することで回避する方法も書かれている。けど、RubyGems.org をみると httpclient gem の最後のリリースは 2016/9 で、これはもう他のライブラリにのりかえるべきだろう。

で、さらにググって調べたところ、http という gem の評判がよさげに見えたのでこれを使ってみることにした。こんな名前よく空いてたな。

使い方は GitHub の Wiki にまとまっている。

httpclient みたいにインスタンスを作る必要がなくて 、require "http" したら HTTP モジュールの関数が使える。例えば GET の場合はこう。

irb(main):001:0> require "http"
=> true
irb(main):002:0> res = HTTP.get("https://blog.panicblanket.com")
=> #<HTTP::Response/1.1 200 OK {"Date"=>"Sat, 06 Jan 2024 20:51:15 GMT", "Content-Type"=>"text/...

レスポンスのコードは #code 、ボディは #to_s で取得できる。

irb(main):003:0> res.code
=> 200
irb(main):004:0> res.to_s
=> "<!DOCTYPE html>\n\n<html lang=\"ja\">\n\n\t<head>\n\n\t\t<meta http-equiv=\"content-type\" content=\"text/html\" charset=\"UTF-8\" />\n\t\t<meta 
(以下略)

こんな感じ。

GETPOSTPUTDELETE などの HTTP メソッドの他にも WEBDAV なんかもサポートしてる。使いやすそうだ。

Rails6の最新版までアップグレード

ローカルネットワークで運用してる Rails アプリを Rails6 系の最新版 6.1.7.3 までアップグレードした。Ruby のバージョンは 3.1.4。Docker コンテナ上で動かしている。

以下、メモ。後で時間があればもう少し詳しく書く。

Rails 5.2 以降の credentials.yml.enc について。

Active Storage について。

Rails の設定について。

Ruby:ランダムな文字列を作る

以前、Python で UUID をもとにしてランダムな文字列を作るってのをやった。

同じことを Ruby でやろうとしたら存外に手間を食ったのでメモしておく。

UUID は 標準ライブラリの SecureRandom モジュールで生成できる。

irb(main):001:0> require "securerandom"
=> true
irb(main):002:0> u = SecureRandom.uuid
=> "4ad31376-3c83-49d7-a70a-14467f2b4022"

が、Python では UUID クラスのインスタンスが返ってきてバイト列を得るのも簡単だったけど、Ruby では文字列が返ってくる。なので、16進数表示の文字列からバイト列に変換してやらないといけない。とりあえず邪魔なハイフンを取り除いておく。

irb(main):003:0> h = u.tr("-", "")
=> "4ad313763c8349d7a70a14467f2b4022"

で、バイト列に変換するメソッドを書く。

はじめは2文字ずつに切り分けて String#to_i で整数に変換してやればいいかと思ったけど、これだと得られるのは Fixnum クラスのインスタンス(の配列)であってバイト列じゃない。

結局次のようになった。2文字ずつ切り分けるのは同じだけど、切り出した2文字は別々に整数に変換して、1文字目のほうは 4bit 左シフトしてから2文字目のほうと論理OR をとる。そうすると整数16個の配列ができる。これをバイト列に変換するには Array#pack を使う。

irb(main):004:1* def hex2bstring(hex)
irb(main):005:1*   barray = []
irb(main):006:2*   hex.split("").each_slice(2) do |pair|
irb(main):007:2*     barray.push((pair[0].to_i(16) << 4) | (pair[1].to_i(16)))
irb(main):008:1*   end
irb(main):009:1*   barray.pack("C")
irb(main):010:0> end
=> :hex2bstring

このメソッドを使ってバイト列に変換。

irb(main):011:0> b = hex2bstring(h)
=> "J\xD3\x13v<\x83I\xD7\xA7\n\x14F\x7F+@\"" 

さらに Base32 エンコードするんだけど、Ruby の標準ライブラリには Base32 がないので、あらかじめ gem install base32 しておく必要がある。エンコードしたらパディングの = を取り除く。

irb(main):012:0> require "base32"
=> true
irb(main):013:0> s = Base32.encode(b)
=> "JLJRG5R4QNE5PJYKCRDH6K2AEI======"
irb(main):014:0> s2 = s.tr("=", "")
=> "JLJRG5R4QNE5PJYKCRDH6K2AEI"

最後は見た目のランダムさを増すために小文字を混ぜるようにする。if の条件式には Array#sample を使って [true, false].sample としてもよかったんだけど、せっかく UUID を生成するのに SecureRandom を使ってるのでここでも使ってみた。でもわかりにくいかも。

irb(main):015:1 def down(c)
irb(main):016:2*  if SecureRandom.random_number(2) > 0
irb(main):017:2*    c.downcase
irb(main):018:2*  else
irb(main):019:2*    c
irb(main):020:1*  end
irb(main):021:0> end
=> :down

最終的にはこうなった。

irb(main):022:0> s2.split("").map{|c| down(c) }.join
=> "JljRg5r4qnE5pjyKCRdH6k2aei"

Python より面倒だな。

古いRailsアプリをDockerコンテナに乗せた

もう1年も前になるけどこんな記事を書いた。

ローカルネットワークのサーバ置き換えに際して、古いサーバ(Ubuntu 16.04 LTS)で動かしていた Rails アプリが、新しいサーバ(Ubuntu 20.04 LTS)上で動かせなくてどうしよう……っていう記事だ。

あれから1年も過ぎてしまったけど、今年に入ってから週末ごとにちまちまと作業をして、なんとか新しいサーバの Dockerコンテナで動かせるまでになった。

ベースにした Docker イメージは、Ruby の公式イメージのうち一番古いバージョン、2.6.9。とにかく Ruby の公式 Docker イメージをベースにしたコンテナで動かせることを目標にした。

Rails は 5.0.7.2まで上げた。これだって今となっては古いバージョン(RubyGems.org を見ると2019/3/13リリース)だけど、ローカルネットワークでしか使ってない web アプリなのでひとまず妥協する。

作業は開発用メインマシン(Ubuntu 20.04 LTS)の rbenv で Ruby そのもののバージョンも変えながらやった。作業を始めた時点では Rails 4.1.4 で、最後のコミットはなんと 2016/3/3 だった。6年近くもメンテしてなかったってことだよ。アプリを使う分には不自由してなかったからだけど、だからってほったらかしだとこうやって後になってツケが来るんだよな。Rails にさわるもの6年ぶりってわけで、少しずつバージョンを上げるたびに出るエラー(主に依存関係)で苦労した。これからはもう少し面倒を見て、追いつくようにしよう。

さて、そういうわけで、動かしていたアプリがなくなって、めでたく古いサーバが空いた。春になれば Ubuntu 22.04 LTS もリリースされるだろうから、そのテスト用にしようかな。

ローカルネットワークのサーバを置き換えるんだけど、古いRailsアプリはどうすりゃいいんだ……

置き換え用にと新しく買ったサーバ用の PC が明日届く、とメールがきた。まあ、届いたからって平日なのですぐに作業できるわけでもないんだけど……

それはさておき。現状でローカルネットワークで運用してるサーバ3台を順繰りに押し出すように置き換えていって、最後に押し出される一番古い PC は万が一のときの予備にまわす。この際だからサーバは全部 Ubuntu Server 20.04 に統一しようと、すでにインストールメディアの準備は済んでいる。

で、さて。その一番古いサーバでもいくつかの web アプリを動かしているんだけど、そのひとつがだいぶ前に Ruby on Rails で作ったものなんだ。なんと Rails 4.1.4。ちなみに OS やなんかのバージョンは次の通り:

  • Ubuntu 16.04
  • Ruby 2.3.1
  • Ruby on Rails 4.1.4
  • unicorn 5.4.1

これを最新の Ubuntu 20.04 の環境に置き換えようとしてるんだけど……

とにかく、開発用マシン(Ubuntu 20.04,Ruby 2.7.1)で動かせるかどうか試してみたら、案の定ダメだった。アプリを動かすどころか bundle install の途中でコケる。どうも json とか therubyracer の gem をインストールするところでうまく行かないみたいだ。なら DockerHub にある Ubuntu 公式イメージの一番古いバージョン 16.04 の上でならどうか、と試してみたけどやっぱりダメ。

新しい OS のサーバに移行するのに合わせてアプリもバージョンアップしなきゃな、とは思ってたんだけど、開発環境で動作させられないんじゃそれも難しい。どうすりゃいいんだか…… 

ISBNを操作するgem petrarcaをリリースした

はてさて、前の記事からもうひと月も経ってしまった。

その、ひと月まえの記事に書いた、Ruby 用の ISBN を操作するライブラリを RubyGems.org にリリースした。名前は petrarca。

最初のリリース(v0.2.0)が8月30日で、その後バージョンアップして現在は v0.4.0(9月8日)。とりあえず自分では満足しているので、しばらくは大きな更新はないはず。

他のライブラリにはない(ざっと調べた限りではなさそうな)機能としては、ハイフン無しの ISBN をハイフンつきに変換する Petrarca.hyphenate メソッドが日本だけでなく世界中の ISBN に対応していること。こんな感じ。

irb(main):001:0> Petrarca.hyphenate("9780061052811")
=> "978-0-06-105281-1"

国や出版社の番号はそれぞれで桁数が違うわけだけど、International ISBN Agency のページから xml ファイルでダウンロードできるのでそれを利用している。このデータはときどき更新されるらしいので、バージョンアップするとしたらその対応だな。

RubyGems.orgにおける車輪の大発明的な話

大発明じゃなくて再発明、な。

ISBN を操作するための Ruby のライブラリを作った。

ISBN (International Standard Book Number)っていうのは、Wikipedia によると、図書の識別に使われる国際規格コードで、日本語では「国際標準図書番号」という。

別に新しいものでもなく、Ruby 用のライブラリも RubyGems.org で検索すればすでにいくつも存在することがわかる。チェックディジットのアルゴリズムも簡単だし、ちょっとやってみるのにはお手軽なのかも。そのせいかどうかは知らないけど、どのライブラリ(gem)をみても俺のニーズとずれている(約30分の調査による)。要するに気に入らない。俺のほしいのは:

  • 妥当性の検証
  • チェックディジットの計算
  • 現行規格(ISBN13)と旧規格(ISBN10)の相互変換
  • ハイフネーション

が簡潔にできる機能であって、こういうのはモジュール関数でやればいいんだよ。わざわざ ISBN クラスなんて作る必要ないんだ。

というわけで、RubyGems.org に登録されてないのも含めると何百回目だか何千回目だかわからない車輪の再発明をしたわけだ。コードは GitHub に上げてある。

で、せっかく作ったんだから RubyGems.org にも上げておこう(みんなそう思ったんだろうな)として rake release したら「そっくりな名前の gem がすでにあるんやで」みたいなメッセージが出て拒否された。一応、既存の gem とはかぶらないことを確認して isbn_utils っていう名前にしたんだけど、似ているだけでもダメみたいだ。そうなのか。知らなかった。

そういうわけなので、とりあえず RubyGems.org での公開は保留。いい名前が思いついたら再チャレンジする。かも。

Nginx + Unicorn on CentOS 8 with SELinux

前のエントリで web アプリを Unicorn で動かすことに成功したので、今度は Nginx と連携する。

その前に unicorn.conf をちょっと修正して、ポートじゃなくて UNIX ソケットを利用するようにする。

#listen "9000"
listen "/run/lcstorage/unicorn.sock"
worker_processes 2
working_directory "/home/lcstorage/lcstorage"
pid "/home/lcstorage/lcstorage/unicorn.pid"
stdout_path "/home/lcstorage/lcstorage/unicorn.log"
stderr_path "/home/lcstorage/lcstorage/unicorn.log"
preload_app true

そしてソケットファイルを置くディレクトリを作る。

[lcstorage@rollo lcstorage]$ sudo mkdir /run/lcstorage
[lcstorage@rollo lcstorage]$ sudo chown lcstorage:lcstorage /run/lcstorage

Unicorn の準備はこれで終わり。起動しておく。

[lcstorage@rollo lcstorage]$ bundle exec unicorn -c unicorn.conf -E production

さて、つぎは Nginx の設定だ。storage1 というホスト名でアクセスできるよう、/etc/nginx/conf.d の下に storage1.conf という名前で設定ファイルを作る。

upstream lcstorage {
    server unix:/run/lcstorage/unicorn.sock;
    #server 127.0.0.1:9000;
}

server {
    # port
    listen      80;

    # server name
    server_name storage1;

    # document root
    #root        /home/lcstorage/lcstorage;

    # index
    #index       index.php index.html index.htm;

    client_max_body_size    4G;

    # log files
    access_log /var/log/nginx/storage1/access.log main;
    error_log  /var/log/nginx/storage1/error.log  warn;

    keepalive_timeout     60;
    proxy_connect_timeout 60;
    proxy_read_timeout    60;
    proxy_send_timeout    60;

    location / {
        #root       /home/lcstorage/lcstorage;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://lcstorage;
    }
}

ログを書き込むディレクトリを忘れずに作っておく。

これで準備はできたはずだ。Nginx を再起動しよう。

[takatoh@rollo conf.d]$ sudo systemctl restart nginx

で、別マシンから http://storage1/ にアクセスしてみると、結果はこうだった。

502 Bad Gateway

あー、これはあれだ、SELinux のせいだ。試しに SELinux を Permissive モードにしてみる。

[takatoh@rollo ~]$ sudo setenforce 0
[takatoh@rollo ~]$ getenforce
Permissive

この状態でもう一度アクセスしていると、ちゃんと File not found. と表示された。なんどもいうけど、返すファイルがないのでこれが正しい動作だ。

つまり、SELinux をどうにかしてやらないといけないわけだ。だけどググってみると、解決策として出てくるのは SELinux を無効にしろ、っていうのばっかり。ちゃんと使い方を説明しているページはほとんど無い。そんな中で参考になったのが以下のページ。

 cf. 【ゼロから始める】初期設定のみのLinuxサーバーに既存のRailsアプリとWebサーバーを導入する – Takeshi’s Blog

と、そこからリンクされているページ。

 cf. RedmineをCentOS 7上で動かすーUnicornとNginx編 – ソフトウェアエンジニアリング

Nginx のエラーログを調べると:

2019/11/23 11:33:23 [crit] 2526#0: *1 connect() to unix:/run/lcstorage/unicorn.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.1.11, server: storage1, request: "GET / HTTP/1.1", upstream: "http://unix:/run/lcstorage/unicorn.sock:/", host: "storage1"

となっていて、ソケットファイルにアクセスできないようだ。じゃあ、ソケットファイルのパーミッションはどうなってるかというと:

[takatoh@rollo ~]$ ls -lZ /run/lcstorage
合計 0
srwxrwxrwx. 1 lcstorage lcstorage unconfined_u:object_r:var_run_t:s0 0 11月 23 11:42 unicorn.sock

Z は SELinux のアクセス権限を表示するオプション。うん、よくわからん。SELinux が原因かどうかは /var/log/audit/audit.log を調べるとわかるらしい。再度 SELinux を有効にしてアクセスしてみてから:

[takatoh@rollo ~]$ sudo cat /var/log/audit/audit.log | grep nginx | tail
type=SERVICE_STOP msg=audit(1574476367.153:1276): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=nginx comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset"
type=SERVICE_START msg=audit(1574476367.207:1277): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=nginx comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset"
type=AVC msg=audit(1574476403.391:1281): avc:  denied  { connectto } for  pid=2526 comm="nginx" path="/run/lcstorage/unicorn.sock" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=unix_stream_socket permissive=0
type=SYSCALL msg=audit(1574476403.391:1281): arch=c000003e syscall=42 success=no exit=-13 a0=e a1=561f05479e28 a2=6e a3=7ffe9aa20e0c items=0 ppid=2522 pid=2526 auid=4294967295 uid=977 gid=975 euid=977 suid=977 fsuid=977 egid=975 sgid=975 fsgid=975 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)ARCH=x86_64 SYSCALL=connect AUID="unset" UID="nginx" GID="nginx" EUID="nginx" SUID="nginx" FSUID="nginx" EGID="nginx" SGID="nginx" FSGID="nginx"
type=AVC msg=audit(1574476651.650:1290): avc:  denied  { connectto } for  pid=2526 comm="nginx" path="/run/lcstorage/unicorn.sock" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=unix_stream_socket permissive=1
type=SYSCALL msg=audit(1574476651.650:1290): arch=c000003e syscall=42 success=yes exit=0 a0=e a1=561f05479e28 a2=6e a3=7ffe9aa20e0c items=0 ppid=2522 pid=2526 auid=4294967295 uid=977 gid=975 euid=977 suid=977 fsuid=977 egid=975 sgid=975 fsgid=975 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)ARCH=x86_64 SYSCALL=connect AUID="unset" UID="nginx" GID="nginx" EUID="nginx" SUID="nginx" FSUID="nginx" EGID="nginx" SGID="nginx" FSGID="nginx"
type=AVC msg=audit(1574477463.658:1309): avc:  denied  { connectto } for  pid=2526 comm="nginx" path="/run/lcstorage/unicorn.sock" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=unix_stream_socket permissive=1
type=SYSCALL msg=audit(1574477463.658:1309): arch=c000003e syscall=42 success=yes exit=0 a0=e a1=561f05479e28 a2=6e a3=7ffe9aa20e0c items=0 ppid=2522 pid=2526 auid=4294967295 uid=977 gid=975 euid=977 suid=977 fsuid=977 egid=975 sgid=975 fsgid=975 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)ARCH=x86_64 SYSCALL=connect AUID="unset" UID="nginx" GID="nginx" EUID="nginx" SUID="nginx" FSUID="nginx" EGID="nginx" SGID="nginx" FSGID="nginx"
type=AVC msg=audit(1574477957.417:1347): avc:  denied  { connectto } for  pid=2526 comm="nginx" path="/run/lcstorage/unicorn.sock" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=unix_stream_socket permissive=0
type=SYSCALL msg=audit(1574477957.417:1347): arch=c000003e syscall=42 success=no exit=-13 a0=e a1=561f05479e28 a2=6e a3=7ffe9aa20e0c items=0 ppid=2522 pid=2526 auid=4294967295 uid=977 gid=975 euid=977 suid=977 fsuid=977 egid=975 sgid=975 fsgid=975 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)ARCH=x86_64 SYSCALL=connect AUID="unset" UID="nginx" GID="nginx" EUID="nginx" SUID="nginx" FSUID="nginx" EGID="nginx" SGID="nginx" FSGID="nginx"

さぱーりわからん。けど、どうやら↓ここらしい。

type=AVC msg=audit(1574477957.417:1347): avc:  denied  { connectto } for  pid=2526 comm="nginx" path="/run/lcstorage/unicorn.sock" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=unix_stream_socket permissive=0

nginx が /run/lcstorage/unicorn.sock にアクセスしようとして denied になってるようだ。つまりここを何とかすればいいわけ。

SELinux の上のアクセスを許可する設定は、/var/log/audit/audit.log ファイルから audit2allow コマンドで作れるようだ。まず確認。

[takatoh@rollo ~]$ sudo grep nginx /var/log/audit/audit.log | audit2allow

#============= httpd_t ==============

#!!!! This avc can be allowed using the boolean 'httpd_can_network_connect'
allow httpd_t glance_port_t:tcp_socket name_connect;

#!!!! This avc can be allowed using one of the these booleans:
#     httpd_can_network_connect, httpd_graceful_shutdown, httpd_can_network_relay, nis_enabled
allow httpd_t http_port_t:tcp_socket name_connect;
allow httpd_t unconfined_t:unix_stream_socket connectto;

設定用のファイルを作るにはこうする。

[takatoh@rollo ~]$ sudo grep nginx /var/log/audit/audit.log | audit2allow -M nginx
[sudo] takatoh のパスワード:
******************* 重要 ********************
このポリシーパッケージを有効にするには、以下を実行して下さい:

semodule -i nginx.pp

指示にあるようにして、SELinux に設定する。

[takatoh@rollo ~]$ sudo semodule -i nginx.pp

さてこれでどうだろう。はたして、別のマシンからブラウザでアクセスしてみると、ちゃんと File not found. って表示された。なんどもいうけどこれが(ry。

つぎは web アプリをサービスとして登録する。/etc/systemd/system ディレクトリの下に lcstorage.service ファイルを作る。

[Unit]
Description=LatherCraft storage service
After=network.target

[Service]
Type=forking
PIDFile=/home/lcstorage/lcstorage/unicorn.pid
ExecStart=/usr/local/bin/bundle exec unicorn -c unicorn.conf -E production -D
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -QUIT $MAINPID
WorkingDirectory=/home/lcstorage/lcstorage
User=lcstorage
Group=lcstorage

[Install]
WantedBy=multi-user.target

これで起動してみる。

[takatoh@rollo system]$ sudo systemctl start lcstorage

あれ?おかしい。プロンプトが戻ってこない。ほかのターミナルからステータスを見てみると:

[lcstorage@rollo lcstorage]$ systemctl status lcstorage
● lcstorage.service - LatherCraft storage service
   Loaded: loaded (/etc/systemd/system/lcstorage.service; disabled; vendor pres>
   Active: activating (start) since Sat 2019-11-23 12:57:57 JST; 1min 14s ago
  Process: 3931 ExecStart=/usr/local/bin/bundle exec unicorn -c unicorn.conf -E production -D (code=exited, status=0/SUCCESS)
    Tasks: 6 (limit: 26213)
   Memory: 31.4M
   CGroup: /system.slice/lcstorage.service
           ├─3936 unicorn master -c unicorn.conf -E production -D  production -D
           ├─3939 unicorn worker[0] -c unicorn.conf -E production -D  productio>
           └─3942 unicorn worker[1] -c unicorn.conf -E production -D  productio>
11月 23 12:57:57 rollo systemd[1]: Starting LatherCraft storage service…
11月 23 12:57:58 rollo systemd[1]: lcstorage.service: Can't convert PID files /home/lcstorage/lcstorage/unicorn.pid O_PATH file descriptor to proper file descriptor: Permission denied
11月 23 12:57:58 rollo systemd[1]: lcstorage.service: Can't convert PID files /home/lcstorage/lcstorage/unicorn.pid O_PATH file descriptor to proper file descriptor: Permission denied

pid ファイルにアクセスできないっぽい?じゃあ、場所を変えてみようか。/home/lcstorage/lcstorage/unicorn.conf と /etc/systemd/system/lcstorage.service の両方で pid ファイルを作る場所を /run に変えてみた。

でもまだダメ。もとに戻す。もう一度やってみてもこうなる。

[takatoh@rollo system]$ sudo systemctl start lcstorage
Job for lcstorage.service failed because a timeout was exceeded.
See "systemctl status lcstorage.service" and "journalctl -xe" for details.

で、SELinux を無効にしてやってみた。

[takatoh@rollo system]$ sudo setenforce 0
[takatoh@rollo system]$ getenforce
Permissive
[takatoh@rollo system]$ sudo systemctl start lcstorage

あ、これだとすんなりいく。ブラウザからアクセスしてもうまくいった。またお前か、SELinux!

さっきとおなじように /var/log/audit/audit.log を調べてみるとこうなってた。

type=AVC msg=audit(1574483152.115:1720): avc:  denied  { open } for  pid=1 comm="systemd" path="/home/lcstorage/lcstorage/unicorn.pid" dev="dm-2" ino=24382 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=1

やっぱり pid ファイルにアクセスできないみたいだ。ポリシーを作ってやる。

[takatoh@rollo ~]$ sudo grep unicorn /var/log/audit/audit.log | audit2allow -M unicorn
[takatoh@rollo ~]$ sudo semodule -i unicorn.pp

これでどうだろう。

[takatoh@rollo ~]$ sudo setenforce 1
[takatoh@rollo ~]$ getenforce
Enforcing
[takatoh@rollo ~]$ sudo systemctl start lcstorage

お、うまくいったっぽい。ブラウザからアクセスしても大丈夫だった。

最後に。マシンを起動した時に自動起動するようにする。

[takatoh@rollo ~]$ sudo systemctl enable lcstorage
Created symlink /etc/systemd/system/multi-user.target.wants/lcstorage.service → /etc/systemd/system/lcstorage.service.

はぁ、長かった。