AlmaLinux 8にRStudio serverをインストールしてみる


AlmaLinux 8環境にRStudio serverをセットアップする必要がある、ということで検証してみた。

Rのインストール

まずはRをインストールする。

Install R」を参考に作業。

必要なレポジトリとして「CodeReady」と「EPEL」と書かれている。

AlmaLinux/RockyLinuxではCodeReadyは「powertools」であるため「dnf config-manager –enable powertools」を実行してPowerToolsレポジトリを有効化する

# dnf config-manager --enable powertools
#

続いてEPELを有効化するため「dnf install epel-release」を実行する。

# dnf install epel-release
メタデータの期限切れの最終確認: 0:00:21 時間前の 2022年08月05日 17時05分19秒 に実施しました。
依存関係が解決しました。
==============================================================================================================
 パッケージ                   アーキテクチャー       バージョン                  リポジトリー           サイズ
==============================================================================================================
インストール:
 epel-release                 noarch                 8-10.el8                    extras                  22 k

トランザクションの概要
==============================================================================================================
インストール  1 パッケージ

ダウンロードサイズの合計: 22 k
インストール後のサイズ: 32 k
これでよろしいですか? [y/N]: y
<略>
インストール済み:
  epel-release-8-10.el8.noarch

完了しました!
#

現在有効になっているレポジトリを確認するため「dnf repolist」を実行

# dnf repolist
repo id                           repo の名前
appstream                         AlmaLinux 8 - AppStream
baseos                            AlmaLinux 8 - BaseOS
epel                              Extra Packages for Enterprise Linux 8 - x86_64
epel-modular                      Extra Packages for Enterprise Linux Modular 8 - x86_64
extras                            AlmaLinux 8 - Extras
powertools                        AlmaLinux 8 - PowerTools
# dnf repolist --all

続いてR本体をインストールする。

まず、インストールできるRバージョンを「https://cran.r-project.org/src/base/R-4/」にアクセスして確認する

バージョン「4.1.3」をインストールする場合は環境変数「R_VERSION」を「4.1.3」と指定して実行する

# export R_VERSION=4.1.3
# curl -O https://cdn.rstudio.com/r/centos-8/pkgs/R-${R_VERSION}-1-1.x86_64.rpm
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 61.6M  100 61.6M    0     0  4741k      0  0:00:13  0:00:13 --:--:-- 3486k
# ls -l R-4.1.3-1-1.x86_64.rpm
-rw-r--r--. 1 root root 64609131  8月  5 17:08 R-4.1.3-1-1.x86_64.rpm
# dnf install R-4.1.3-1-1.x86_64.rpm
メタデータの期限切れの最終確認: 0:02:34 時間前の 2022年08月05日 17時06分28秒 に実施しました。
依存関係が解決しました。
==============================================================================================================
 パッケージ                       Arch            バージョン                      リポジトリー          サイズ
==============================================================================================================
インストール:
 R-4.1.3                          x86_64          1-1                             @commandline           62 M
依存関係のインストール:
 binutils                         x86_64          2.30-113.el8                    baseos                5.8 M
 bzip2-devel                      x86_64          1.0.6-26.el8                    baseos                224 k
 cairo                            x86_64          1.15.12-6.el8                   appstream             718 k
 cpp                              x86_64          8.5.0-10.1.el8_6.alma           appstream              10 M
 dejavu-fonts-common              noarch          2.35-7.el8                      baseos                 73 k
 dejavu-sans-fonts                noarch          2.35-7.el8                      baseos                1.5 M
 fontconfig                       x86_64          2.13.1-4.el8                    baseos                273 k
 fontpackages-filesystem          noarch          1.44-22.el8                     baseos                 16 k
 fribidi                          x86_64          1.0.4-8.el8                     appstream              89 k
 gcc                              x86_64          8.5.0-10.1.el8_6.alma           appstream              23 M
 gcc-c++                          x86_64          8.5.0-10.1.el8_6.alma           appstream              12 M
 gcc-gfortran                     x86_64          8.5.0-10.1.el8_6.alma           appstream              12 M
 glibc-devel                      x86_64          2.28-189.5.el8_6                baseos                 78 k
 glibc-headers                    x86_64          2.28-189.5.el8_6                baseos                482 k
 graphite2                        x86_64          1.3.10-10.el8                   appstream             121 k
 harfbuzz                         x86_64          1.7.5-3.el8                     appstream             295 k
 isl                              x86_64          0.16.1-6.el8                    appstream             841 k
 jbigkit-libs                     x86_64          2.1-14.el8                      appstream              54 k
 kernel-headers                   x86_64          4.18.0-372.16.1.el8_6           baseos                9.3 M
 libICE                           x86_64          1.0.9-15.el8                    appstream              73 k
 libSM                            x86_64          1.2.3-1.el8                     appstream              47 k
 libX11                           x86_64          1.6.8-5.el8                     appstream             610 k
 libX11-common                    noarch          1.6.8-5.el8                     appstream             157 k
 libXau                           x86_64          1.0.9-3.el8                     appstream              37 k
 libXext                          x86_64          1.3.4-1.el8                     appstream              45 k
 libXft                           x86_64          2.3.3-1.el8                     appstream              66 k
 libXmu                           x86_64          1.1.3-1.el8                     appstream              75 k
 libXrender                       x86_64          0.9.10-7.el8                    appstream              33 k
 libXt                            x86_64          1.1.5-12.el8                    appstream             185 k
 libcurl-devel                    x86_64          7.61.1-22.el8_6.3               baseos                833 k
 libdatrie                        x86_64          0.2.9-7.el8                     appstream              33 k
 libgfortran                      x86_64          8.5.0-10.1.el8_6.alma           baseos                643 k
 libicu                           x86_64          60.3-2.el8_1                    baseos                8.8 M
 libicu-devel                     x86_64          60.3-2.el8_1                    baseos                922 k
 libjpeg-turbo                    x86_64          1.5.3-12.el8                    appstream             156 k
 libmpc                           x86_64          1.1.0-9.1.el8                   appstream              60 k
 libpkgconf                       x86_64          1.4.2-1.el8                     baseos                 35 k
 libquadmath                      x86_64          8.5.0-10.1.el8_6.alma           baseos                170 k
 libquadmath-devel                x86_64          8.5.0-10.1.el8_6.alma           appstream              23 k
 libstdc++-devel                  x86_64          8.5.0-10.1.el8_6.alma           appstream             2.0 M
 libthai                          x86_64          0.1.27-2.el8                    appstream             203 k
 libtiff                          x86_64          4.0.9-21.el8                    appstream             187 k
 libxcb                           x86_64          1.13.1-1.el8                    appstream             231 k
 libxcrypt-devel                  x86_64          4.1.1-6.el8                     baseos                 24 k
 make                             x86_64          1:4.2.1-11.el8                  baseos                497 k
 openblas-threads                 x86_64          0.3.15-3.el8                    appstream             4.9 M
 pango                            x86_64          1.42.4-8.el8                    appstream             296 k
 pcre2-devel                      x86_64          10.32-2.el8                     baseos                604 k
 pcre2-utf16                      x86_64          10.32-2.el8                     baseos                228 k
 pcre2-utf32                      x86_64          10.32-2.el8                     baseos                220 k
 pixman                           x86_64          0.38.4-2.el8                    appstream             256 k
 pkgconf                          x86_64          1.4.2-1.el8                     baseos                 38 k
 pkgconf-m4                       noarch          1.4.2-1.el8                     baseos                 17 k
 pkgconf-pkg-config               x86_64          1.4.2-1.el8                     baseos                 15 k
 tcl                              x86_64          1:8.6.8-2.el8                   baseos                1.1 M
 tk                               x86_64          1:8.6.8-1.el8                   appstream             1.6 M
 unzip                            x86_64          6.0-46.el8                      baseos                195 k
 xz-devel                         x86_64          5.2.4-4.el8_6                   baseos                 62 k
 zip                              x86_64          3.0-23.el8                      baseos                270 k
 zlib-devel                       x86_64          1.2.11-18.el8_5                 baseos                 57 k

トランザクションの概要
==============================================================================================================
インストール  61 パッケージ

合計サイズ: 165 M
ダウンロードサイズの合計: 103 M
インストール後のサイズ: 416 M
これでよろしいですか? [y/N]: y
<略>
完了しました!
#

R本体が必要としているパッケージ群が追加でインストールされる。

Rは/opt以下にインストールされる

# ls -l /opt
合計 0
drwxr-xr-x. 3 root root 19  8月  5 17:10 R
# ls -l /opt/R/
合計 0
drwxr-xr-x. 5 root root 41  8月  5 17:10 4.1.3
# ls -l /opt/R/4.1.3/
合計 0
drwxr-xr-x. 2 root root 30  8月  5 17:10 bin
drwxr-xr-x. 4 root root 32  8月  5 17:10 lib
drwxr-xr-x. 3 root root 17  8月  5 17:10 share
# du -ks /opt/R/
157716  /opt/R/
#

/opt/R/bin/ は標準的なパスに入っていないので、標準的なパスである /usr/local/bin 以下にRを配置する。

# sudo ln -s /opt/R/${R_VERSION}/bin/R /usr/local/bin/R
# sudo ln -s /opt/R/${R_VERSION}/bin/Rscript /usr/local/bin/Rscript
# which R
/usr/local/bin/R
# R --version
R version 4.1.3 (2022-03-10) -- "One Push-Up"
Copyright (C) 2022 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under the terms of the
GNU General Public License versions 2 or 3.
For more information about these matters see
https://www.gnu.org/licenses/.

#

Rstudio serverのインストール

Download RStudio Server for Red Hat/CentOS の手順に従い、rpmファイルをダウンロードし、インストールする。

# curl -O https://download2.rstudio.org/server/rhel8/x86_64/rstudio-server-rhel-2022.07.1-554-x86_64.rpm
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 73.7M  100 73.7M    0     0  4257k      0  0:00:17  0:00:17 --:--:-- 7049k
# ls -l rstudio-server-rhel-2022.07.1-554-x86_64.rpm
-rw-r--r--. 1 root root 77282076  8月  5 17:16 rstudio-server-rhel-2022.07.1-554-x86_64.rpm
# dnf install rstudio-server-rhel-2022.07.1-554-x86_64.rpm
メタデータの期限切れの最終確認: 0:11:33 時間前の 2022年08月05日 17時06分28秒 に実施しました。
依存関係が解決しました。
==============================================================================================================
 パッケージ                  アーキテクチャー    バージョン                   リポジトリー              サイズ
==============================================================================================================
インストール:
 rstudio-server              x86_64              2022.07.1+554-1              @commandline               74 M
依存関係のインストール:
 libpq                       x86_64              13.5-1.el8                   appstream                 197 k
 sqlite                      x86_64              3.26.0-15.el8                baseos                    667 k

トランザクションの概要
==============================================================================================================
インストール  3 パッケージ

合計サイズ: 75 M
ダウンロードサイズの合計: 864 k
インストール後のサイズ: 346 M
これでよろしいですか? [y/N]: y
<略>
Created symlink /etc/systemd/system/multi-user.target.wants/rstudio-server.service → /usr/lib/systemd/system/rstudio-server.service.
● rstudio-server.service - RStudio Server
   Loaded: loaded (/usr/lib/systemd/system/rstudio-server.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2022-08-05 17:19:06 JST; 1s ago
  Process: 29307 ExecStart=/usr/lib/rstudio-server/bin/rserver (code=exited, status=0/SUCCESS)
 Main PID: 29308 (rserver)
    Tasks: 4 (limit: 49426)
   Memory: 2.4M
   CGroup: /system.slice/rstudio-server.service
           mq29308 /usr/lib/rstudio-server/bin/rserver

 8月 05 17:19:06 rserver.adosakana.local systemd[1]: Starting RStudio Server...
 8月 05 17:19:06 rserver.adosakana.local systemd[1]: Started RStudio Server.

  検証             : sqlite-3.26.0-15.el8.x86_64                                                          1/3
  検証             : libpq-13.5-1.el8.x86_64                                                              2/3
  検証             : rstudio-server-2022.07.1+554-1.x86_64                                                3/3

インストール済み:
  libpq-13.5-1.el8.x86_64       rstudio-server-2022.07.1+554-1.x86_64       sqlite-3.26.0-15.el8.x86_64

完了しました!
#

インストールするとrstudio-serverがsystemdに登録され、起動している。

firewalldへの設定

rstudio-server の標準設定では port 8787 となっている。

これをfirewalldに登録して外部からアクセスできるようにする。

# firewall-cmd --permanent --add-port=8787/tcp
success
# firewall-cmd --reload
success
# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens192
  sources:
  services: cockpit dhcpv6-client ssh
  ports: 8787/tcp
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
#

設定後にブラウザからアクセスすると下記の様な表示が確認出来る

Rstudio Serverを使う為のSELinux設定

Rstudio Serverは一般ユーザのアカウントでログインして使用するため、SELinux側の設定を調整しないとログインもできない。

SELinuxのポリシーをそれなりに設定しようとするのであればaudit2allowコマンドとSELinuxのteファイルをppファイルにコンパイルできる環境が必要になる。

そのためには「policycoreutils-python-utils」と「selinux-policy-devel」パッケージを追加する

# dnf install policycoreutils-python-utils selinux-policy-devel

で・・・具体的にはログインを繰り返して、/var/log/audit/audit.log の出力を確認しつつ、許可する要素を増やしていく形となる

まずは「ausearch -m AVC |grep “denied”」を実行して、SELinuxで拒否されているものを確認する。

# ausearch -m AVC |grep "denied"
type=AVC msg=audit(1659948002.217:181): avc:  denied  { setpgid } for  pid=48278 comm="rserver" scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process permissive=0
type=AVC msg=audit(1659948020.309:182): avc:  denied  { setpgid } for  pid=48279 comm="rserver" scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process permissive=0
type=AVC msg=audit(1659948380.864:88): avc:  denied  { setpgid } for  pid=5723 comm="rserver" scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process permissive=1
type=AVC msg=audit(1659948381.339:89): avc:  denied  { setpgid } for  pid=5723 comm="rsession" scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process permissive=1
type=AVC msg=audit(1659950719.013:139): avc:  denied  { setpgid } for  pid=6574 comm="rsession" scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process permissive=1
type=AVC msg=audit(1659950719.419:140): avc:  denied  { name_connect } for  pid=6574 comm="R" dest=443 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:http_port_t:s0 tclass=tcp_socket permissive=1
type=AVC msg=audit(1659950783.684:141): avc:  denied  { write open } for  pid=5723 comm="rsession" path="/home/osakanataro/sample.R" dev="dm-2" ino=148 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=1
type=AVC msg=audit(1659950783.684:141): avc:  denied  { create } for  pid=5723 comm="rsession" name="sample.R" scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=1
type=AVC msg=audit(1659950783.684:142): avc:  denied  { read } for  pid=5723 comm="rsession" name="sample.R" dev="dm-2" ino=148 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=1
type=AVC msg=audit(1660013043.066:93): avc:  denied  { setpgid } for  pid=1774 comm="rserver" scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process permissive=0
type=AVC msg=audit(1660014586.546:175): avc:  denied  { rename } for  pid=2783 comm="rsession" name="sample.R" dev="dm-2" ino=148 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1660014641.065:176): avc:  denied  { rmdir } for  pid=2783 comm="rsession" name="test" dev="dm-2" ino=134217876 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=dir permissive=0
type=AVC msg=audit(1660017628.367:179): avc:  denied  { rename } for  pid=2783 comm="rsession" name="Untitled" dev="dm-2" ino=159 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1660018249.703:187): avc:  denied  { rename } for  pid=2783 comm="rsession" name="test" dev="dm-2" ino=134217876 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=dir permissive=0
type=AVC msg=audit(1660019006.786:116): avc:  denied  { reparent } for  pid=1809 comm="rsession" name="test2" dev="dm-2" ino=159 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=dir permissive=0
type=AVC msg=audit(1660028279.975:127): avc:  denied  { map } for  pid=1978 comm="rsession" path="/home/osakanataro/R/x86_64-pc-linux-gnu-library/4.1/digest/libs/digest.so" dev="dm-2" ino=177 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1660028329.771:128): avc:  denied  { map } for  pid=1978 comm="rsession" path="/home/osakanataro/R/x86_64-pc-linux-gnu-library/4.1/digest/libs/digest.so" dev="dm-2" ino=177 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1660028356.760:129): avc:  denied  { map } for  pid=1978 comm="rsession" path="/home/osakanataro/R/x86_64-pc-linux-gnu-library/4.1/digest/libs/digest.so" dev="dm-2" ino=177 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1660028659.078:132): avc:  denied  { execute } for  pid=1978 comm="rsession" path="/home/osakanataro/R/x86_64-pc-linux-gnu-library/4.1/digest/libs/digest.so" dev="dm-2" ino=177 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1660028775.153:135): avc:  denied  { append } for  pid=21355 comm="R" name="sample.spin.R" dev="dm-2" ino=10541 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1660028911.809:138): avc:  denied  { setattr } for  pid=21400 comm="R" name="sample.spin.R" dev="dm-2" ino=10541 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1660029171.607:143): avc:  denied  { name_connect } for  pid=1365 comm="rserver" dest=6687 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0
type=AVC msg=audit(1660029171.607:144): avc:  denied  { name_connect } for  pid=1365 comm="rserver" dest=6687 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0
type=AVC msg=audit(1660029171.630:145): avc:  denied  { read write } for  pid=1978 comm="rsession" name="ptmx" dev="devtmpfs" ino=1120 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file permissive=0
type=AVC msg=audit(1660029288.326:148): avc:  denied  { open } for  pid=1978 comm="rsession" path="/dev/ptmx" dev="devtmpfs" ino=1120 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file permissive=0
type=AVC msg=audit(1660029372.850:151): avc:  denied  { ioctl } for  pid=1978 comm="rsession" path="/dev/ptmx" dev="devtmpfs" ino=1120 ioctlcmd=0x5401 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:ptmx_t:s0 tclass=chr_file permissive=0
type=AVC msg=audit(1660029433.944:154): avc:  denied  { open } for  pid=1978 comm="rsession" path="/dev/pts/2" dev="devpts" ino=5 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:devpts_t:s0 tclass=chr_file permissive=0
type=AVC msg=audit(1660029433.944:155): avc:  denied  { open } for  pid=1978 comm="rsession" path="/dev/pts/2" dev="devpts" ino=5 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:devpts_t:s0 tclass=chr_file permissive=0
type=AVC msg=audit(1660029551.674:158): avc:  denied  { execmem } for  pid=21641 comm="deno" scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process permissive=0
type=AVC msg=audit(1660029594.331:162): avc:  denied  { execmem } for  pid=21664 comm="deno" scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=process permissive=0
type=AVC msg=audit(1660029676.859:168): avc:  denied  { link } for  pid=21714 comm="rsession" name=".rstudio-lock-41c29-rserver.adosakana.local-21714-7f2b6802e980" dev="dm-2" ino=17682 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:user_home_t:s0 tclass=file permissive=0
#

特に問題ないようであれば、これをSELinux用のモジュールとする。

# ausearch -m AVC |grep "denied" | audit2allow -M rstudio
******************** 重要 ***********************
このポリシーパッケージを有効にするには、以下を実行して下さい:

semodule -i rstudio.pp

#

このコマンド実行によりrstudio.te というテキストファイルと、rstudio.pp というバイナリファイルが出力される。rstudio.te は下記のような内容となっている。

# cat rstudio.te

module rstudio 1.0;

require {
        type devpts_t;
        type init_t;
        type http_port_t;
        type ptmx_t;
        type user_home_t;
        type unreserved_port_t;
        class process { execmem setpgid };
        class tcp_socket name_connect;
        class file { append create execute link map open read rename setattr write };
        class dir { rename reparent rmdir };
        class chr_file { ioctl open read write };
}

#============= init_t ==============

#!!!! This avc is allowed in the current policy
allow init_t devpts_t:chr_file open;

#!!!! This avc is allowed in the current policy
allow init_t http_port_t:tcp_socket name_connect;

#!!!! This avc is allowed in the current policy
#!!!! This av rule may have been overridden by an extended permission av rule
allow init_t ptmx_t:chr_file { ioctl open read write };

#!!!! This avc is allowed in the current policy
allow init_t self:process { execmem setpgid };

#!!!! This avc is allowed in the current policy
allow init_t unreserved_port_t:tcp_socket name_connect;

#!!!! This avc is allowed in the current policy
allow init_t user_home_t:dir { rename reparent rmdir };
allow init_t user_home_t:file link;

#!!!! This avc is allowed in the current policy
allow init_t user_home_t:file { append create execute map open read rename setattr write };
#

作成されたモジュールを読み込み、モジュール一覧に表示されることを確認する。

# semodule -l | grep rstu
# semodule -i rstudio.pp
# semodule -l | grep rstu
rstudio
#

これで一通りは動作すると思われる

確認した内容
・ファイルの新規作成、削除、リネーム、移動
・ディレクトリの新規作成、削除、リネーム、移動
・Comnpile Reportでのレポート出力
cranパッケージを各ユーザディレクトリにインストールすること
・rstudio上でのshellコマンド実行
・rstudio上でのgitによるバージョン管理(別途 dnf install git でインストールしておくこと)

SELinuxのteファイルをppファイルにする

以下のteファイルをppにします。

# ls
rstudio.te
# cat rstudio.te

module rstudio 1.1;

require {
        type devpts_t;
        type init_t;
        type http_port_t;
        type ptmx_t;
        type user_home_t;
        type unreserved_port_t;
        class process { execmem setpgid };
        class tcp_socket name_connect;
        class file { append create execute link map open read rename setattr write };
        class dir { rename reparent rmdir };
        class chr_file { ioctl open read write };
}

#============= init_t ==============

#!!!! This avc is allowed in the current policy
allow init_t devpts_t:chr_file open;

#!!!! This avc is allowed in the current policy
allow init_t http_port_t:tcp_socket name_connect;

#!!!! This avc is allowed in the current policy
#!!!! This av rule may have been overridden by an extended permission av rule
allow init_t ptmx_t:chr_file { ioctl open read write };

#!!!! This avc is allowed in the current policy
allow init_t self:process { execmem setpgid };

#!!!! This avc is allowed in the current policy
allow init_t unreserved_port_t:tcp_socket name_connect;

#!!!! This avc is allowed in the current policy
allow init_t user_home_t:dir { rename reparent rmdir };
allow init_t user_home_t:file link;

#!!!! This avc is allowed in the current policy
allow init_t user_home_t:file { append create execute map open read rename setattr write };
#

teファイルがある場所で「make -f /usr/share/selinux/devel/Makefile」を実行すると、ppファイルが出力されます。

# make -f /usr/share/selinux/devel/
Makefile    example.if  html/       policy.dtd
example.fc  example.te  include/    policy.xml
# make -f /usr/share/selinux/devel/Makefile
Compiling targeted rstudio module
Creating targeted rstudio.pp policy package
rm tmp/rstudio.mod tmp/rstudio.mod.fc
# ls -l
合計 20
-rw-r--r--. 1 root root    0  8月  9 17:55 rstudio.fc
-rw-r--r--. 1 root root   23  8月  9 17:55 rstudio.if
-rw-r--r--. 1 root root 8589  8月  9 17:55 rstudio.pp
-rw-r--r--. 1 root root 1258  8月  9 17:54 rstudio.te
drwxr-xr-x. 2 root root   70  8月  9 17:55 tmp
#

こうしてできたppファイルを「semodule -i rstudio.pp」で読み込みます。


アレな実験

検証用ということで借りたサーバ実機がRHEL8でインストールされていたが、登録とくにされておらず、CodeReadyレポジトリなどが使えない状態となっていた。

なんかごまかせるかなーというのをチャレンジ

その1: BaseOSとAppStreamはISOファイルから取得する

BaseOSとAppStreamはrhel-8.6-x86_64-dvd.iso に含まれているため適当なディレクトリに置いてそれをレポジトリとする

参考URL:Oracle® Linux 8 Oracle Linuxでのソフトウェアの管理 1.8 ISOイメージを使用したローカルYumリポジトリの作成

ISOファイルを /var/isos/rhel-8.6-x86_64-dvd.iso に置いて、マウントポイントを /isos/rhel86 とし、/etc/fstabに下記を追加する

/var/isos/rhel-8.6-x86_64-dvd.iso       /isos/rhel86    iso9660 loop,ro 0 0

これでマウントすると下記の様になる。

# mount -a
# df -h
ファイルシス          サイズ  使用  残り 使用% マウント位置
devtmpfs                3.8G     0  3.8G    0% /dev
tmpfs                   3.8G     0  3.8G    0% /dev/shm
tmpfs                   3.8G  8.7M  3.8G    1% /run
tmpfs                   3.8G     0  3.8G    0% /sys/fs/cgroup
/dev/mapper/rhel-root    70G   14G   57G   20% /
/dev/sda2              1014M  206M  809M   21% /boot
/dev/sda1               599M  5.9M  594M    1% /boot/efi
/dev/mapper/rhel-home    41G  322M   41G    1% /home
tmpfs                   777M     0  777M    0% /run/user/0
/dev/loop0               11G   11G     0  100% /isos/rhel86
#

これをレポジトリとして登録するために /etc/yum.repos.d/media.repo として下記を作成する。

[media-baseos]
name=media-baseos
baseurl=file:///isos/rhel86/BaseOS/
gpgcheck=1
enabled=1

[media-appstream]
name=media-appstream
baseurl=file:///isos/rhel86/AppStream/
gpgcheck=0
enabled=1

(手動で入力することを想定しているため、入力が面倒くさい gpgkeyに関する記述を省略している)

その2: EPELとCodeReadyはAlmaLinuxから持ってくる

RHEL8からは持って来れないので、AlmaLinuxかRockyLinuxから持ってくる

今回は社内テストをAlmaLinuxで行っていたため、そこで登録されているレポジトリ設定を流用した。

また、RHEL側は8.6で固定したいので、 /etc/yum/vars/releasever を作成し、8.6 で固定する設定とした。

# cat /etc/yum/vars/releasever
8.6
#

EPELの内容 /etc/yum.repos.d/epel.repo

# cat /etc/yum.repos.d/epel.repo
[epel]
name=Extra Packages for Enterprise Linux 8 - $basearch
# It is much more secure to use the metalink, but if you wish to use a local mirror
# place its address here.
#baseurl=https://download.example/pub/epel/8/Everything/$basearch
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-8&arch=$basearch&infra=$infra&content=$contentdir
enabled=1
gpgcheck=0
countme=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8
#

powertoolsの設定 /etc/yum.repos.d/powertools.repo

# cat /etc/yum.repos.d/powertools.repo
[powertools]
name=AlmaLinux $releasever - PowerTools
#mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/powertools
baseurl=https://repo.almalinux.org/almalinux/$releasever/PowerTools/$basearch/os/
enabled=1
gpgcheck=0
countme=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux
#

なお、今後8.7が出た場合でも8.6固定を維持したい場合は https://repo.almalinux.org/almalinux/を https://repo.almalinux.org/vault/ に書き換える。

とりあえず、これで、目的はひとまず達成できた。

PowerShellにパスワード文字列を直接書かない手法


PowerShellで用意されているGet-Credential 関連を駆使して、PowerShellスクリプト内に直接パスワードを書かないで済む手法でスクリプトを作成した。

スクリプトを配置したディレクトリにscript.cred というファイルを作り、そこに暗号化されたパスワード文字列を配置する、という仕組み。

$authfile=$PSScriptRoot+".\script.cred"
$username="admin" #ユーザ名
$passwdstr="" # この変数にパスワード文字列が入る。
if((Test-Path $authfile) -eq $false){
    $creds = Get-Credential -UserName $username -Message $($username+"ユーザのパスワードを入力してください")
    $creds.Password | ConvertFrom-SecureString | Set-Content -Path $authfile | Out-Null
    $passwdstr=[Runtime.InteropServices.Marshal]::PtrToStringBSTR([Runtime.InteropServices.Marshal]::SecureStringToBSTR($creds.Password))
}else{
    $passwdstr=[Runtime.InteropServices.Marshal]::PtrToStringBSTR([Runtime.InteropServices.Marshal]::SecureStringToBSTR($(Get-Content $authfile | ConvertTo-SecureString)))
}

# この段階の $passwdstr には暗号化されていないパスワード文字列が入っている

なお、メモリ上の$passwdstr には暗号化されていない文字列が保存されているため、そこは注意が必要。

CommvaultのREST APIをPowerShellで使って見る


Commvaultバックアップの操作にREST APIを使うことができる。

・Commvault V11SP24の「REST API Overview
Commvault Rest APIの出力サンプル群

ログインして、指定したクライアントのサブクライアント情報を取得するPowerShellで作成してみた。

# CommCellユーザ情報
$restapiuser="admin"
$restapipasswd="パスワード"

# REST APIで使用するURL
# どちらを使うかについて https://documentation.commvault.com/11.24/expert/45592_available_web_services_for_rest_api.html
#$restapiurlbase="http://~:81/SearchSvc/CVWebService.svc/"
$restapiurlbase="http://~/webconsole/api/"

# 検索用文字列
$clientname="クライアント名"

# CommCell環境にログイン
$restapipasswdbase64=[Convert]::ToBase64String(([System.Text.Encoding]::Default).GetBytes($restapipasswd))
$headers=@{
    "Accept"="application/json"
    "Content-Type"="application/json"
}
$loginReq = @{
    username=$restapiuser
    password=$restapipasswdbase64
}
$loginresponse=Invoke-RestMethod -Method post -Uri $($restapiurlbase+"Login") -Body $($loginReq|ConvertTo-Json) -ContentType 'application/json' -Headers $headers

# 指定クライアントのサブクライアント情報取得
$headers=@{
    "Accept"="application/json"
    "Authtoken"=$loginresponse.token
}
$response=Invoke-RestMethod -Method Get -Uri $($restapiurlbase+"Subclient?clientName="+$clientname) -Headers $headers

# $response.subClientProperties.subClientEntity にサブクライアントの情報が入っているが
# 複数のサブクライアントがあると $response.subClientProperties.subClientEntity.subclientName が複数行になるので注意

# 表示
$response.subClientProperties.subClientEntity | ForEach-Object {
    $subcliententory=$_
    Write-host $subcliententory
 }

# CommCellログアウト
$headers=@{
    "Accept"="application/json"
    "Authtoken"=$loginresponse.token
}
$logoutresponse=Invoke-RestMethod -Method post -Uri $($restapiurlbase+"Logout") -Headers $headers

ただ、このスクリプトを使うとCommvault Event viewerに表示されるログイン情報が「Machine:unknown Locale:unknown」となる。

これを設定できるか調べて見たが、無いように思える。

TeraTermマクロでコマンド出力結果を使って別のコマンドを実行する


NetAppの設定をTeraTermマクロで取得出来るようにマクロを作成中し https://github.com/osakanataro/get-ontap-config で公開中です。

物理ディスク一覧を作成する際、単純に「storage disk show」で取得すると、2ノードの表示が入り交じり、どちらに所属している物理ディスクなのか判別が面倒。

なので、「storage disk show -nodelist ノード名」という形で出力を分けたいのだが、NetAppに対して「system node show -fields node」で取得したノード一覧からノード名を抜き出して、それぞれに対してコマンドを実行するためには、TeraTermマクロで実装できるのかを調査した。

調査

まず、NetAppのノード名一覧の出力を確認

netapp01::> system node show -fields node
node
-------------
netapp01-01
netapp01-02
2 entries were displayed.

netapp01::>

ノードとして「netapp01-01」と「netapp01-02」があることを確認。

また、system node showコマンドの出力のうち最初の2行はヘッダ、そして、ノード名出力後に「~ entries were displayed.」と出力がある。これらは使わないので除外が必要。

スクリプト

サンプルとして、ノード一覧を表示して、各ノードに対して「system node run -node ノード名 hostname」を実行する、というものを作成した。

setsync 1
timeout=60
strdim nodenames 20
I=0
sendln 'system node show -fields node'
recvln ; コマンド入力 のecho出力  をスキップ
recvln ; ヘッダ部分 をスキップ
recvln ; --- 部分 をスキップ

endflag=1
while endflag=1
	recvln
	strscan inputstr 'displayed.'
	if result>0 then
		endflag=0
	else
		nodenames[I] = inputstr
		I=I+1
	endif
endwhile
wait '::> '

J=0
while I > J
	sendln 'system node run -node ' nodenames[J] ' hostname'
	wait '::> '
	J=J+1
endwhile

setsync 0

スクリプトの解説

今回のようなコマンド出力結果を受け取る場合は、「setsync 1」を実行して、確実に出力結果を受け取る設定(同期)とする必要がある。

次に「recvln」する際、TeraTermマクロからNetAppに送信した文字列もrecvlnで受け取ることになっているため、最初の1行は捨てている。(recvlnして、その後、何の処理もしていない)

続けて2行がヘッダなので、「recvln」で2行分受け取り、何も処理していない。

「2 entries were displayed.」というエントリ以降の行は不要なので、whileを終了する処理を入れるが、1台だったりすると1 entry wasとかになったりしそうなのでdisplayed.だけで終了検出。

whileを抜けたあとは、コマンドプロンプトの「::> 」が来るまで待ちを入れる。

スクリプト実行例

netapp01::> system node show -fields node
node
-------------
netapp01-01
netapp01-02
2 entries were displayed.

netapp01::> system node run -node netapp01-01 hostname
netapp01-01
netapp01::> system node run -node netapp01-02 hostname
netapp01-02
netapp01::>

はてなブログからwordpressへぶっこぬきで移行した話


現在はてなブログで運用しているけど、画像付きでのエクスポートができない、とかいう話を聞いた。

ローカルに静的コンテンツとして取り込もう

とりあえず、本文と画像をダウンロードすればいいだろう、と、まずは簡単に下記でコンテンツ取得をかけた。

wget --mirror \
    --page-requisites \
    --quiet --show-progress \
    --no-parent \
    --convert-links \
    --adjust-extension \
    --execute robots=off \
    https://retoge-mag.hatenablog.com/

適当なWebサーバに配置して見てみると、HTMLのみで、そしてサイト内リンクもhttpsからフルで指定されているタイプだったので、URL書き換えを雑に実施。

他にもHTML書き換えが必要だろう、ということでダウンロードしてきたやつではなく、閲覧用のHTMLを別に用意することにして、下記の様に実行した。

cp -r retoge-mag.hatenablog.com viewpage

for file in `find ./viewpage -name \*.html -print`
do
 sed -i -e "s/retoge-mag.hatenablog.com/retoge-mag.websa.jp\/old\/viewpage/g" $file
done

これでまずはHTMLのみがコピーされた状態だが、リンクをいくつかクリックしてみると404 not foundとなるものがある。

日付ごとのまとめはダウンロードされていないが、これについては無視することとした。

個別記事のリンクで、末尾が数字で終わっているものがいくつかあり、それで404が発生していた。それらのコンテンツは数字.htmlという形でダウンロードされていたので .htaccess に下記の設定を行った。

<IfModule mod_rewrite.c>
RewriteEngine On

RewriteCond     %{REQUEST_FILENAME} !-f
RewriteRule     ^(.*)$ $1.html [L]

</IfModule>

次に画像のダウンロードを行った。

HTMLからhttp か httpsで始まるリンクっぽいものを集めてみると画像は st-hatena.com というドメイン名の所に置いてあるようだった。

なので、下記スクリプトを実行して、該当するURLを全てダウンロードしてきた。

for file in `find ./viewpage -name \*.html -print`
do
 grep -oE 'http(s?)://[0-9a-zA-Z?=#+_&:;/.%\-]+' $file >> urllists.txt
done
sort urllists.txt | uniq > urllists2.txt
grep st-hatena.com urllists2.txt > urllists3.txt
wget --mirror     --page-requisites     --quiet --show-progress     --no-parent     --convert-links     --adjust-extension -i urllists3.txt

なお、最初は「grep -oE ‘http(s?)://[0-9a-zA-Z?=#+_&:/.%-]+’ $file >> urllists.txt」だったのだが、縮小処理をかけた画像のURLに;が含まれていたので、追加している。

画像はローカルにダウンロードできたものの、HTMLの方は書き換わっていないため、以下で書き換えを実施した。

for file in `find ./viewpage -name \*.html -print`
do
 sed -i -e "s/b.st-hatena.com/retoge-mag.websa.jp\/old\/b.st-hatena.com/g" $file
 sed -i -e "s/cdn-ak.f.st-hatena.com/retoge-mag.websa.jp\/old\/cdn-ak.f.st-hatena.com/g" $file
 sed -i -e "s/cdn.blog.st-hatena.com/retoge-mag.websa.jp\/old\/cdn.blog.st-hatena.com/g" $file
 sed -i -e "s/cdn.pool.st-hatena.com/retoge-mag.websa.jp\/old\/cdn.pool.st-hatena.com/g" $file
 sed -i -e "s/cdn.profile-image.st-hatena.com/retoge-mag.websa.jp\/old\/cdn.profile-image.st-hatena.com/g" $file
 sed -i -e "s/cdn.user.blog.st-hatena.com/retoge-mag.websa.jp\/old\/cdn.user.blog.st-hatena.com/g" $file
 sed -i -e "s/usercss.blog.st-hatena.com/retoge-mag.websa.jp\/old\/usercss.blog.st-hatena.com/g" $file
done

これで、見栄えは悪いものの画像ファイル含めてローカルにコンテンツ自体はコピーされた。

WordPressに取り込もう

あまりにも見栄えが悪いので、やっぱりWordpressに取り込むべきか、とインポート用XMLファイルを作ることにする。

WordPressのサイトを調べたのだがインポート用XMLファイルの仕様がよくわからなかった。

まず、Wordpressで適当に記事とカテゴリを作成して、それをエクスポートしたXMLを作成した。

結構いらない要素が多い感じがしたので最小限の記述はなにか探したところ「Simple WordPress Import Structure XML Example」を発見。

<?xml version="1.0" encoding="UTF-8" ?>

<rss version="2.0"
	xmlns:excerpt="http://wordpress.org/export/1.2/excerpt/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:wp="http://wordpress.org/export/1.2/"
>

<channel>
	<wp:wxr_version>1.2</wp:wxr_version>
	<wp:author></wp:author>

	<item>
		<title>Test 1</title>
		<description></description>
		<content:encoded><![CDATA[Test Post 1]]></content:encoded>
		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
		<wp:status>publish</wp:status>
		<wp:post_type>post</wp:post_type>
		<category domain="post_tag" nicename="redy"><![CDATA[redy]]></category>
		<category domain="category" nicename="teory"><![CDATA[Teory]]></category>
		<category domain="category" nicename="tree"><![CDATA[Tree]]></category>
	</item>
	<item>
		<title>Test 2</title>
		<description></description>
		<content:encoded><![CDATA[Test Post 2]]></content:encoded>
		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
		<wp:status>publish</wp:status>
		<wp:post_type>post</wp:post_type>
		<category domain="category" nicename="test-category"><![CDATA[Test Category]]></category>
	</item>
</channel>
</rss>

これをベースに作成することにした。

はてなブログが出力したHTMLから下記の要素を抜き出してXMLタグを出力する test.plを作成した。

#!/bin/perl
#

use strict;
use warnings;

my $sourcefile=$ARGV[0];

if(!-f $sourcefile){
        print "file not found\n";
        exit 1;
}

my $i=0;
my @bodys;

open(FILE,$sourcefile);
while(<FILE>){
        $bodys[$i]=$_;
        $i++;
}
close(FILE);

my $maxline=$i;
my $line;

my ($st,$ed,$tmp);

my $createdate="";
my $modifydate="";
my $title="";
my $categorys="";
my $categoryssub="";
my $categorysterm="";
my $author="";
my $contents="";
my $description="";

$i=0;
while($i<$maxline){
        $line=$bodys[$i];
        if(($line =~ /<time datetime=/)&&($createdate eq "")){
                # 作成日取得
                # 下の方に他のページのdatetimeもあるので1個目だけ採用
                $st=index($line,"datetime=");
                $st=index($line,"\"",$st)+length("\"");
                $ed=index($line,"\"",$st);
                $createdate=substr($line,$st,$ed-$st);
        #}elsif(($line =~ /entry-footer-time/)&&($line =~ /class=\"updated\"/)){
        }elsif($line =~ /dateModified/){
                # 更新日取得
                $st=index($line,"dateModified");
                $st=index($line,":",$st);
                $st=index($line,"\"",$st)+length("\"");
                $ed=index($line,"\"",$st);
                $modifydate=substr($line,$st,$ed-$st);
        }elsif($line =~ /entry-title-link/){
                # タイトル取得
                $st=index($line,">")+length(">");
                $ed=index($line,"</",$st);
                $title=substr($line,$st,$ed-$st);
        }elsif($line =~ /entry-categories/){
                # カテゴリ取得
                my $flag=0;
                while($flag==0){
                        $i++;
                        $line=$bodys[$i];
                        if($line =~/<\/div>/){
                                # カテゴリ終了
                                $flag=1;
                        }elsif($line =~ /entry-category-link/){
                                $st=index($line,">")+length(">");
                                $ed=index($line,"</",$st);
                                $tmp=substr($line,$st,$ed-$st);
                                $categorys.="<category domain=\"category\" nicename=\"".$tmp."\" ><![CDATA[".$tmp."]]></category>";
                        }
                }
        }elsif($line =~ /author vcard/){
                # 作者取得
                $ed=index($line,"</");
                $st=rindex($line,">",$ed)+length(">");
                $author=substr($line,$st,$ed-$st);
        }elsif($line =~ /entry-content/){
                # 本文取得
                my $flag=0;
                while($flag==0){
                        $i++;
                        $line=$bodys[$i];
                        if($line =~/entry-footer/){
                                # 本文終了
                                $flag=1;
                        }else{
                                $contents.=$line;
                        }
                }
        }elsif($line =~ /og:description/){
                # description取得
                $st=index($line,"content=\"")+length("content=\"");
                $ed=index($line,"\"",$st);
                $description=substr($line,$st,$ed-$st);
        }


        $i++;
}
# URL変換処理
$contents =~ s/b.st-hatena.com/retoge-mag.websa.jp\/old\/b.st-hatena.com/g;
$contents =~ s/cdn-ak.f.st-hatena.com/retoge-mag.websa.jp\/old\/cdn-ak.f.st-hatena.com/g;
$contents =~ s/cdn.blog.st-hatena.com/retoge-mag.websa.jp\/old\/cdn.blog.st-hatena.com/g;
$contents =~ s/cdn.pool.st-hatena.com/retoge-mag.websa.jp\/old\/cdn.pool.st-hatena.com/g;
$contents =~ s/cdn.profile-image.st-hatena.com/retoge-mag.websa.jp\/old\/cdn.profile-image.st-hatena.com/g;
$contents =~ s/cdn.user.blog.st-hatena.com/retoge-mag.websa.jp\/old\/cdn.user.blog.st-hatena.com/g;
$contents =~ s/usercss.blog.st-hatena.com/retoge-mag.websa.jp\/old\/usercss.blog.st-hatena.com/g;

#


print "<item>\n";
print "<description>".$description."</description>\n";
print "<excerpt:encoded><![CDATA[]]></excerpt:encoded>\n";
print "<wp:status>publish</wp:status>\n";
print "<wp:post_type>post</wp:post_type>\n";
print "<wp:post_date_gmt>".$createdate."</wp:post_date_gmt>\n";
#print "<wp:post_modified_gmt>".$modifydate."</wp:post_modified_gmt>\n";
print "<wp:post_modified>".$modifydate."</wp:post_modified>\n";
print "<title><![CDATA[".$title."]]></title>\n";
print $categorys."\n";
#print "<dc:creator><![CDATA[".$author."]]></dc:creator>\n";
print "<dc:creator><![CDATA[pc88multi2]]></dc:creator>\n";
#print "<content:encoded><![CDATA[<!-- wp:paragraph -->\n".$contents."<!-- /wp:paragraph -->]]></content:encoded>\n";
print "<content:encoded><![CDATA[".$contents."]]></content:encoded>\n";
print "</item>\n";

このtest.pl は単体ファイルに対してのみ実行されるので、対象ファイル群に対して実行するために下記の別スクリプトを用意して実行した。

echo '<?xml version="1.0" encoding="UTF-8" ?>' > wordpress.xml
echo '<rss version="2.0" xmlns:excerpt="http://wordpress.org/export/1.2/excerpt/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"  xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:wp="http://wordpress.org/export/1.2/" >' >> wordpress.xml

echo '<channel><wp:wxr_version>1.2</wp:wxr_version>' >> wordpress.xml
echo '<wp:author></wp:author>' >> wordpress.xml

for file in `find retoge-mag.hatenablog.com/entry/* -name \*.html`
do echo --- $file ---
        ./test.pl $file >> wordpress.xml
done

echo '</channel>' >> wordpress.xml
echo '</rss>' >> wordpress.xml

こうして出力されたwordpress.xmlをWordpressにてインポートすることで取り込みは完了した。

なお、画像ファイルについてはXMLへどうかけばいいのかが分からなかったため、ローカルにコピーした際のURLをそのまま利用し、Wordpress管理下には置かなかった。


2021/11/30追記

この手法で取り込むとWordpress上は「クラシック」形式での状態となっていた。

各記事で「ブロックへ変換」を行ったあと

各画像でを選択し「↑」ボタンをクリックすると画像がWordpress管理下にあるwp-contentsディレクトリ内にダウンロードされ、Wordpressで管理できるようになった。

画像