SELinux を有効にしたまま CentOS 7 で OpenStreetMap のタイルサーバを構築する

Qiita で OpenStreetMap のタイルサーバを構築する記事が公開されていました。

OS が CentOS なので SELinux を無効にする記述がありましたので、とりあえず SELinux を有効にしたままタイルサーバを動かせないか試してみました。
とりあえず動くことは動いたんですが、rendoer.sockSELinux コンテキストを意義的に正しく設定できてないのでセキュリティホールになっている可能性が否めません。
目的を達成できているか不明確ですが、隠して置いても正しい設定が見つかるわけじゃないのでとりあえず公開してみます。

最初には SELinux を有効にして動かす差分的なもの、後半には自分が試した記録を載せています。

SELinux の設定

先の設定方法でブラウザから地図を確認できたら SELinux を再度有効にします。

$ sudo vi /etc/selinux/config
SELINUX=enforcing
#SELINUX=disabled

再起動すると /var/run に作成したディレクトリが消されてしまうので設定ファイルを書きます。

$ sudo vi /etc/tmpfiles.d/renderd.conf
d /var/run/renderd 0777 root root

再起動

$ sudo reboot

サーバが上がってきたら動作確認のために一時的に SELinux の状態を確認して Permissive にします。

$ getenforce
Enforcing
$ sudo setenforce 0
$ getenforce
Permissive

フォアグランドで動かして試します。

$ sudo /usr/local/bin/renderd -f

http://%サーバーIP%/ へアクセスして地図が表示されれば OK。

動作していることがわかったら SELinux を Enforcing に戻します。

$ sudo setenforce 1
$ getenforce
Enforcing

そのまま http://%サーバーIP%/ へアクセスするとタイル画像へアクセスできない Openlayers の地図もどきが表示されます。

audit.log の中で `type=AVC‘ から始まる denied された renderd のログを確認できるはずです。

$ sudo cat /var/log/audit/audit.log | grep renderd

renderd.sock へのアクセスが制限されないように SELinux への登録を行います。
まずは必要なパッケージのインストー

$ sudo yum -y install policycoreutils-python

ログから renderd に関する記述を抜き出して audit2allow で設定ファイルを作成します。
できた設定ファイルを semodule に食わせると SELinux 側に登録されます。

$ cd ~/src
$ sudo ausearch -m avc | grep renderd | audit2allow -M renderd
$ sudo semodule -i renderd.pp

もう一度 renderd を動かしてタイルが表示されるか確認します。

$ sudo /usr/local/bin/renderd -f

タイルが表示されたら renderd をバックグラウンドで走らせておけばOKです。

$ sudo sh -c "/usr/local/bin/renderd -f > /var/log/renderd.log 2> /var/log/renderd.log &"

こんな感じで SELinux が有効でもタイルサーバを動かすことができました。

試した記録

ここからは自分が今回テストした環境と設定の全部です。全部見る必要はないです、長いし。
VirtualBox で新しい CentOS をセットして試しています。

環境の確認

  • CPU: Core i5 6600 の内1スレッド
  • メモリ: 2GB
  • HDD: 50GB
  • OS: CentOS 7
$ cat /etc/redhat-release 
CentOS Linux release 7.3.1611 (Core)

まずは SELinux が有効か確認します。

$ getenforce 
Enforcing

システムの更新

システムの更新と全体的に必要なパッケージのインストールをします。

$ sudo yum -y update
$ sudo yum -y install epel-release
$ sudo yum -y install wget git freetype-devel libxml2-devel bzip2-devel freetype-devel libtool-ltdl-devel libpng-devel libtiff-devel libjpeg-devel gcc-c++ bzip2 bzip2-devel libcurl-devel libpng-devel libtiff-devel libtool-ltdl-devel pycairo-devel cairomm-devel libjpeg-turbo-devel zip unzip libtool autoconf automake

PostgreSQL のインストー

postgresql は通常のパッケージをインストールしないので除外しておきます。

$ sudo vi /etc/yum.repos.d/CentOS-Base.repo
#[base]セクションの最後に追加
exclude=postgresql*

#[updates]セクションの最後に追加
exclude=postgresql*

#[extras]セクションの最後に追加
exclude=postgresql*

PostgreSQL 9.6 をインストールする rpm を取ってきてインストールします。

$ mkdir ~/src
$ cd ~/src
$ wget https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm
$ sudo rpm -ivh pgdg-centos96-9.6-3.noarch.rpm
$ sudo yum -y install postgresql96 postgresql96-contrib postgresql96-devel postgresql96-libs postgresql96-server
$ sudo /usr/pgsql-9.6/bin/postgresql96-setup initdb

$ sudo systemctl start postgresql-9.6
$ sudo systemctl enable postgresql-9.6

postgres ユーザーがデータベース gis にアクセスする際にはユーザー名のみで認証するように設定を変更します。
いくつかの説明はデータベース全体にすべてのユーザーに対して trust を与えていたので、範囲を小さくはしましたがもっと安全な設定があるのではないかと思います。

$ sudo vi /var/lib/pgsql/9.6/data/pg_hba.conf

# "local" is for Unix domain socket connections only の前に3行追加します。

local   gis             postgres                                trust
host    gis             postgres        127.0.0.1/32            trust
host    gis             postgres        ::1/128                 trust
# "local" is for Unix domain socket connections only

サービスを再起動して接続できるか確認です。

$ sudo systemctl restart postgresql-9.6
$ sudo -u postgres psql

PostGIS、pgrouting のインストー

$ sudo yum -y install postgis2_96 postgis2_96-client postgis2_96-devel postgis2_96-utils ogr_fdw96 pgrouting_96

osm2psql のインストー

cmake など追加のパッケージをインストールします。

$ sudo yum -y install cmake libpqxx libpqxx-devel boost boost-devel proj proj-devel proj-epsg lua lua-devel

ソースを取ってきてコンパイルします。

$ git clone https://github.com/openstreetmap/osm2pgsql
$ cd osm2pgsql
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install
$ sudo ldconfig

タイル作成用データベースの作成

この後インストールする mod_tile がタイル画像を作成しますが、mod_tile がタイルの元としてアクセスするデータベースを作ります。

$ cd /tmp
$ sudo -u postgres createdb template_postgis
$ sudo -u postgres psql -d template_postgis -c "UPDATE pg_database SET datistemplate=true WHERE datname='template_postgis'"
$ sudo -u postgres psql -d template_postgis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/postgis.sql
$ sudo -u postgres psql -d template_postgis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/spatial_ref_sys.sql
$ sudo -u postgres psql -d template_postgis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/postgis_comments.sql
$ sudo -u postgres psql -d template_postgis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/rtpostgis.sql
$ sudo -u postgres psql -d template_postgis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/raster_comments.sql
$ sudo -u postgres psql -d template_postgis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/topology.sql
$ sudo -u postgres psql -d template_postgis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/topology_comments.sql 
$ sudo -u postgres createdb gis
$ sudo -u postgres psql -d gis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/postgis.sql
$ sudo -u postgres psql -d gis -f /usr/pgsql-9.6/share/contrib/postgis-2.3/spatial_ref_sys.sql

OSM のデータをデータベースに投入

今回は自分でデータの切り抜きを公開している埼玉県のデータをダウンロードしてきて使います。

埼玉リージョン切り抜き実験

$ cd ~/src
$ wget http://saitama.noob.jp/bz2/saitama-2017-05-26.osm.bz2
$ sudo -u postgres /usr/local/bin/osm2pgsql -m -d gis saitama-2017-05-26.osm.bz2 -U postgres --cache 1000

元の記事では関東全域だったのでキャッシュが多いようですが、埼玉リージョンだけでファイルサイズが小さいのでキャッシュも小さくしてあります。

mapnik スタイルのインストー

地図の表示スタイルのインストールです。
同時に投入されている OSM データ以外のエリアでも陸地と海くらいはわかるようにデータを投入してるみたいです。

まずは必要なパッケージ類のインストール。

$ sudo yum -y install icu libicu libicu-devel python-pip
$ sudo pip install --upgrade pip

ライブラリをインストールします。

$ wget https://github.com/downloads/mapnik/mapnik/mapnik-v2.0.2.tar.bz2
$ tar xvf mapnik-v2.0.2.tar.bz2
$ cd mapnik-v2.0.2
$ python scons/scons.py PG_CONFIG=/usr/pgsql-9.6/bin/pg_config
$ sudo make install
$ sudo ldconfig
$ sudo ldconfig -v

$ sudo ln -s /usr/local/lib64/libmapnik.so.2.0 /usr/lib64/libmapnik.so.2.0
$ cd ~/src
$ git clone https://github.com/openstreetmap/mapnik-stylesheets.git
$ cd mapnik-stylesheets/
$ wget http://tile.openstreetmap.org/world_boundaries-spherical.tgz
$ wget http://tile.openstreetmap.org/processed_p.tar.bz2
$ wget http://tile.openstreetmap.org/shoreline_300.tar.bz2
$ tar xvf world_boundaries-spherical.tgz
$ tar xvf processed_p.tar.bz2 -C world_boundaries
$ tar xvf shoreline_300.tar.bz2 -C world_boundaries
$ cd world_boundaries
$ wget http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_populated_places.zip
$ wget http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/cultural/ne_110m_admin_0_boundary_lines_land.zip
$ unzip ne_10m_populated_places.zip
$ unzip ne_110m_admin_0_boundary_lines_land.zip
$ cd ../../mapnik-stylesheets/
$ sudo ./generate_xml.py --host localhost --user postgres --dbname gis --symbols ./symbols/ --world_boundaries=./world_boundaries/ --accept-none
$ cd ../
$ sudo mv mapnik-stylesheets /usr/local/src/

タイルレンダラーのインストー

まずは mod_tile で働いてもらう Apache2 をインストールします。

$ sudo yum -y install httpd httpd-devel

ソケットや作成されたタイル画像が置かれるディレクトリを準備します。

$ sudo mkdir -p /var/run/renderd
$ sudo mkdir -p /var/lib/mod_tile
$ sudo chown postgres:postgres /var/run/renderd
$ sudo vi /etc/tmpfiles.d/renderd.conf
d /var/run/renderd 0755 root root

mod_tile の設定ファイルを用意します。

$ sudo touch /etc/renderd.conf
$ sudo vi /etc/renderd.conf
[renderd]
socketname=/var/run/renderd/renderd.sock
num_threads=8
tile_dir=/var/lib/mod_tile
stats_file=/var/run/renderd/renderd.stats

[mapnik]
plugins_dir=/usr/local/lib64/mapnik/input
font_dir=/usr/local/lib64/mapnik/fonts
font_dir_recurse=1

[default]
URI=/osm_tiles2/
XML=/usr/local/src/mapnik-stylesheets/osm.xml
HOST=localhos

XML=の部分は環境依存です。この手順の通りならコピペで動くはずです。

mod_tile をコンパイルします。

$ cd ~/src/
$ git clone https://github.com/openstreetmap/mod_tile.git
$ cd mod_tile
$ ./autogen.sh
$ ./configure --with-apxs=/usr/bin/apxs
$ make
$ sudo make install
$ sudo make install-mod_tile
$ sudo ldconfig

Apache2 から mod_tile を利用するための設定ファイルを用意します。

$ sudo touch /etc/httpd/conf.modules.d/mod_tile.conf
$ sudo vi /etc/httpd/conf.modules.d/mod_tile.conf
LoadModule tile_module modules/mod_tile.so
LoadTileConfigFile /etc/renderd.conf
ModTileRenderdSocketName /var/run/renderd/renderd.sock
ModTileRequestTimeout 30
ModTileMissingRequestTimeout 30

動作確認用の HTML ファイルを作成します。

$ cd /var/www/html
$ sudo touch index.html
$ sudo vi index.html

中身はこんなんです。

<html>
<head>
<title>OpenLayers Demo</title>
<style type="text/css">
html, body, #basicMap {
width: 100%;
height: 100%;
margin: 0;
}
</style>
<script src="http://www.openlayers.org/api/OpenLayers.js"></script>
<script>
function init() {
var options = {
projection: new OpenLayers.Projection("EPSG:900913"),
displayProjection: new OpenLayers.Projection("EPSG:4326"),
units: "m",
maxResolution: 156543.0339,
maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34,
20037508.34, 20037508.34),
numZoomLevels: 20,
controls: [
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.PanZoomBar(),
new OpenLayers.Control.Permalink(),
new OpenLayers.Control.ScaleLine(),
new OpenLayers.Control.MousePosition(),
new OpenLayers.Control.KeyboardDefaults()

]
};
map = new OpenLayers.Map("basicMap",options);
var newL = new OpenLayers.Layer.OSM("Default", "/osm_tiles2/${z}/${x}/${y}.png", {numZoomLevels: 19});
map.addLayer(newL);
map.zoomIn();
}
</script>
</head>
<body onload="init();">
<div id="basicMap"></div>
</body>
</html>

ファイアウォールの設定

http にアクセスできるようにセットします。

$ sudo firewall-cmd --permanent --add-service=http
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-all

services に http が出てればOK。

Apache2 を動かす

$ sudo systemctl enable httpd
$ sudo systemctl start httpd

renderd の動作確認

動作確認のために一時的に SELinux を Permissive にします。

$ sudo setenforce 0
$ getenforce
Permissive

フォアグランドで動かして試します。

$ sudo /usr/local/bin/renderd -f

Using web mercator projection settings がいくつか出てくれば問題ないっぽいです。
http://%サーバーIP%/ へアクセスして地図が表示されれば OK。

動作していることがわかったら SELinux を Enforcing に戻します。

$ sudo setenforce 1
$ getenforce
Enforcing

そのまま http://%サーバーIP%/ へアクセスするとタイル画像へアクセスできない Openlayers の地図もどきが表示されます。

renderd.sock へのアクセスが SELinux によって制限されているためです。
audit.log を確認すると `type=AVC‘ から始まる denied されたログを確認できるはずです。

$ sudo cat /var/log/audit/audit.log | grep renderd

renderd.sock へのアクセスが制限されないように SELinux への登録を行います。
まずは必要なパッケージのインストー

$ sudo yum -y install policycoreutils-python

ログから renderd に関する記述を抜き出して audit2allow で設定ファイルを作成します。
できた設定ファイルを semodule に食わせると SELinux 側に登録されます。

$ cd ~/src
$ sudo ausearch -m avc | grep renderd | audit2allow -M renderd
$ sudo semodule -i renderd.pp

もう一度 renderd を動かしてタイルが表示されるか確認します。

$ sudo /usr/local/bin/renderd -f

タイルが表示されたら renderd をバックグラウンドで走らせておけばOKです。

$ sudo sh -c "/usr/local/bin/renderd -f > /var/log/renderd.log 2> /var/log/renderd.log &"

おしまい。