2013年06月05日
最終更新 2013年06月08日

SELinux の私的メモ



  1. はじめに
  2. 先日CentOS 6.4 を入れてみたところ、インストールメニューからSELinux 有無の選択が消えていました。
    ついに消滅したかと思ったのですが、強制的にOn になったんですね。 CentOS入れるときは何も考えずSELinux はdisabled で入れろと人に言ってたのですが、 そろそろやばくなってきたのでSELinux に絡んでみました。 さわり初めてまだ1ヶ月ほどではありますが、 わかったことをここで簡単に紹介します。

    動作確認で用いた環境は仮想マシン上にセットアップしたCentOS 6.4(64bit版) で、カーネルはyum で更新した2.6.32-358.6.2.el6.x86_64 です。
    SELinux のタイプはデフォルトのtargeted を対象としてます。

    インストール時の選択によっては、 このページに記載しているプログラムがお手元のCentOS に入ってないことがあるかもしれません。 そういった場合は、例えば/usr/sbin/semanage が入ってない場合は # /usr/bin/yum provides /usr/sbin/semanageでどのrpm パッケージにそのプログラムが入っているのか検索できますので、 検索したパッケージを# /usr/bin/yum install パッケージ名 でインターネットから追加インストールすることができます。

  3. SELinux とは
  4. SELinux はSecurity-Enhanced Linux のことで、Linux カーネルの1機能を指します。 ディストリビューションの名前ではありません。
    Linux で強力なアクセス制御機構を実現するためのインタフェースとして LSM(Linux Security Modules)が定められており、SELinux はその実装の1つです。
    LSMはインタフェースなので、SELinux の他にも実装があります。TOMOYO Linux、 AppArmor などです。TOMOYO Linux は日本のNTTデータさんが開発なさってます。
    SELinux やTOMOYO Linux はLinux カーネルのソースコードに組み込まれてます。 でもどれを有効化してカーネルをコンパイルするかはディストリビューションによって異なります。 RHEL(RedHat Enterprise Linux)やCentOS で選択できるのはSELinux のみです。 Debian では自由に選択できるようになっているそうです。

    SELinux は米国の国家安全保障局が強制アクセス制御方式である Flask アーキテクチャ(http://www.cs.utah.edu/flux/fluke/html/flask.html) のリファレンス実装として、 アーキテクチャの有効性を示すために実験的に作られたものであり、 ユーザの使いやすさやわかりやすさといった実用性は重視されていなかったようです。 「Linux のために」作られたものではないんですね。
    NSA のSELinux サイト(http://www.nsa.gov/research/selinux/)

    SELinux はさわっててうれしくない、 むしろ苦痛と感じていた人が多いのではないでしょうか。
    ただ公開以降の10 年間でライバルとの競争や周辺のさまざまな状況が整理されてきたこともあり、 モノ自体は扱い易くなってきているようです。 そのことが積極的にはアピールされていないように感じます。 昔SELinuxに挫折した人(それは私です)も今やり直すとわかるかもしれません。

  5. わかりやすい資料
  6. つぎの資料(PDF)が抜群にわかりやすいようです。
    SELinuxを使ってみる 入門BoF(リンク切れてしまったようです、、、)
    著者の海外浩平さんはSELinux のソースコードにもお名前が出ている方で、 中の人のようです。

  7. 強制アクセス制御
  8. SELinux では、強制アクセス制御を実現します。 (認証や監査ログはスコープではありません)
    強制アクセス制御とはどういうことか。 例えばLinux ではユーザはネットワークにアクセスでき、 wget やssh が755 である限りユーザ(プロセス) がこれらを利用することを制限できません。
    でもSELinux の強制アクセス制御では、Web システムの脆弱性を攻撃して侵入してきたユーザがApache を踏み台としてネットワークにアクセスすることを禁止可能なのです。
    つまり、wget は755 でも、Apache が起動するCGI のプロセスがwget を呼ぶとエラーにするよう設定できるのです。 実際、CentOS6 のSELinux デフォルト設定ではそのようになってます。

    このようなアクセス制御はスーパーユーザが設定することであり、 一般ユーザは設定を変更できません。こういうのを強制アクセス制御と言うようです。

  9. ポリシーによるアクセス制御
  10. SELinux では、ポリシーの作成とコンテキストの割り当てを実施することでアクセス制御を設定します。
    ポリシーはある対象がやってもよいことをルールとして列挙したものです。
    例えば「httpd_t ドメインのプロセスはhttpd_sys_content_t タイプのファイルをread してもよい」というものです。

    コンテキストはリソース(ファイルやTCP/IPのポートやプロセスなど) につけるラベルのようなものです。Java がわかる人は、 アノテーションと考えるとよいかもしれません。
    コンテキストの割り当てとは、例えば/usr/sbin/httpd(apacheですね)はhttpd_exec_t というタイプが設定されていて、これがプロセスを起動するとhttpd_t ドメインのプロセスになります。また/var/www/html/ ディレクトリに何かファイルを 作成するとそれは自動的にhttpd_sys_content_t というタイプが設定されます。 なので、上の例のポリシーにより、apache は/var/www/html/ にあるコンテンツをread することが許可される、ということになります。

    CentOS では適切に組み立てられたSELinux のリファレンスポリシーが提供されていて、 ファイル等のリソースにも適切なコンテキストがあらかじめ設定されてます。 後で検証していきますが、結構ゆるいポリシーのようです。 従ってSELinux 配下でシステムがすぐ利用できるようになっていて、 ゆるいので普通に使う限りはSELinux チェックで何かエラーが出るということはないようです。CentOS6 でSELinux が最初からOn になっていることに気づかない人も多いと思います。

    ポリシーはプレインテキストで記述した設定のソースファイルを専用のコンパイラで バイナリ形式に変換し、モジュールパッケージにまとめてシステムに登録されてます。 /usr/share/selinux/targeted/ にポリシーパッケージ(ppファイル) があります。
    ポリシーのソースはデフォルトでは入ってないようですが、 http://vault.centos.org/6.4/os/Source/SPackages/ 等からselinux-policy-3.7.19-195.el6.src.rpm 等を取得し、 展開すると参照できます。 rpm2cpio ファイル名 | cpio -id で展開できます。 私は見てもよくわかりませんが。

    ポリシーは対象(sendmail やapache やbind など)ごとに定義されていて、 対象の機能が変わらない限り基本的に変更する必要はありません。
    システムをデフォルトの設定で利用する場合、コンテキストの設定も通常は不要です。
    しかし、Apache のCGIファイルの設置場所を変更したい場合など、 設定を変更したい場合は、Apache の設定だけではなく、 参照されるファイルやディレクトリが持つSELinux コンテキストも手動で設定する必要があります。
    chcon で個別に設定することも可能ですが、 空気を読んで自動で適切なコンテキストをファイルに一括設定してくれる restorecon という便利なコマンドがあります。後ほど紹介します。

  11. 具体的に見ていく
  12. CentOS 6.4をさわりながら具体的に見ていきたいと思います。
    まず、SELinux の基本的な設定です。
    /usr/sbin/getenforceで現在のSELinux の状態がわかります。
    $ /usr/sbin/getenforce
    Enforcing
    


    このように、Enforcing と出ればSELinux がポリシーをロードしていて、 それに基づいたアクセス制御が実施されています。
    SELinux の状態はenforcing(実施状態), permissive(黙認状態), disabled があり、 enforcing ではポリシーに違反したアクションはエラーになりログが出力されます。
    CentOS6 のデフォルトはenforcing で、この文書でもこれをベースに記述してます。
    permisive ではポリシー違反はログを出力しますが、アクション自体は実行されます。
    disabled はポリシーがロードされておらず、何も禁止されることはありません。

    SELinux をどの状態で動作させるかは/etc/sysconfig/selinux ファイルへ記述します。 このファイルは/etc/selinux/config ファイルへのシンボリックリンクになってます。

    $ cat /etc/sysconfig/selinux
    
    # This file controls the state of SELinux on the system.
    # SELINUX= can take one of these three values:
    #     enforcing - SELinux security policy is enforced.
    #     permissive - SELinux prints warnings instead of enforcing.
    #     disabled - No SELinux policy is loaded.
    SELINUX=enforcing
    # SELINUXTYPE= can take one of these two values:
    #     targeted - Targeted processes are protected,
    #     mls - Multi Level Security protection.
    SELINUXTYPE=targeted
    
    


    SELinux で違反が検出された場合、アプリがsyslogd 経由で/var/log/messages などへ自主的にログを出力することもありますが、 オフィシャルなログは/var/log/audit/audit.log へ出力されます。
    しかしここにログを出力してもらうためにはauditd が動作している必要があります。
    CentOS では、X Window システムが動作しているrunlevel 5 ではauditd はoff となっているようです。runlevel 2,3,4 ではon になるのですが。
    # /etc/init.d/auditd startでauditd を開始できます。
    システム起動時にrunlevel 5 でもauditd を起動してもらいたい場合は、 # /sbin/chkconfig --level 5 auditd onを一度実行するとOK です。

  13. 普通の作業は普通に実行できる
  14. SELinux 環境でも、一般ユーザでssh 経由でログインしたり、scp でファイルを転送することが普通にできます。
    su でroot にスイッチすることもできますし、root でJava やTomcat をセットアップしたりApache の設定ファイルを修正したり、httpd の上げ下げをしたり、yum でソフトウェアの追加等システム管理作業も普通に実施できます。

  15. Apache 2.2.15
  16. ここではCentOS 6.4 でSELinux 環境下のApache を見ていきたいと思います。
    このApache はCentOS のインストールの時に選択して最初から入れたものです。
    別マシンからCentOS のApache にアクセスしたい場合はデフォルトでOn になっているファイヤーウォールのTCP 80 番ポートを開放しておく必要があります。

    root で起動と停止は/etc/init.d/httpd start と/etc/init.d/httpd stop で普通にできました。
    動作中のプロセスのコンテキストは ps -efZ で確認できます。 apache はhttpd_t ドメインで動作していることがわかります。

    $ ps -efZ | grep httpd | grep -v grep
    unconfined_u:system_r:httpd_t:s0 root     5883     1  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5885  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5886  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5887  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5888  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5889  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5890  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5891  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5892  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    unconfined_u:system_r:httpd_t:s0 apache   5893  5883  0 11:06 ?        00:00:00 /usr/sbin/httpd
    


    次に、Apache のListen ポートを80番から8000番へ変更してみました。
    Apache を停止し、/etc/httpd/conf/httpd.conf のListen 80 をListen 8000に変更して httpd をstart しました。
    これはポリシー違反となり、 次のようなメッセージが出力されました。

    (13)Permission denied: make_sock: could not bind to address [::]:8000
    (13)Permission denied: make_sock: could not bind to address 0.0.0.0:8000

    1行目はIPv6 のIPアドレスで、2行目はIPv4 のIPアドレスですね。

    ログには次のように出力されました
    May 21 23:35:10 CentOS64teresa kernel: type=1400 audit(1369125310.195:5): avc:  denied  
    { name_bind } for  pid=10153 comm="httpd" src=8000 scontext=unconfined_u:system_r:httpd
    _t:s0 tcontext=system_u:object_r:soundd_port_t:s0 tclass=tcp_socket
    

    avc はAccess Vector Control のことのようです。
    ポート番号にもSELinux のコンテキストがついてます。 80 番のポートのコンテキストは次のように確認することができます。
    # /usr/sbin/semanage port -l | grep 80

    # /usr/sbin/semanage port -l | grep 80 | grep http_port_t
    http_port_t                    tcp      80, 443, 488, 8008, 8009, 8443
    


    TCP 8000番ポートを同じように確認したところ、soundd_port_t となっていました。
    ここでエラーログをよく見ると、httpd_t がsoundd_port_t タイプであるtcp_socket をbind しようとしてdenied となっていることが読み取れました。
    8000番からsoundd_port_t をひっぺがしてhttp_port_t タイプに設定変更するとよさそうです。
    しかし、# semanage port -d -p tcp 8000 を実行すると、 ポリシーにより8000 はsoundd_port_t にしているのだから削除できない、とエラーになってしまいました。

    別のポートならどうかと、TCP 9000 番を確認するとタイプが割り当てられてないようなので、 # semanage port -a -t http_port_t tcp 9000 を実行して9000 もhttp_port_t の扱いに組み入れ、httpd.conf を修正すると9000番でApache を起動できました。


    次にWebコンテンツの設置場所であるドキュメントルートを変更してみました。
    /var/www/html/ から/opt/app/htdocs/へhttpd.conf の設定を変更しました。
    あわせて/opt/app/htdocs ディレクトリを作成し、index.html を作成しました。
    index.html のオーナはroot で644 としました。

    ファイルやディレクトリを新規に作成した場合、 SELinux のコンテキストは親ディレクトリのものと同じものが自動的に付与されます。 ファイルのコンテキストはls に-Z オプションをつけると確認できます。
    index.html のコンテキストをls -Z で確認したところ、 unconfined_u:object_r:usr_t:s0 となりました。タイプはusr_t です。
    Apache を起動し、Webブラウザでアクセスすると設置したindex.html の内容が普通に表示されました。
    ドキュメントルートの設定変更は特別なSELinux 操作は不要みたいです。
    ただ、/var/www/html/ 配下のファイルはhttpd_sys_content_t タイプでしたので、 変更した新しいドキュメントルート配下もそのようにしておいた方がよさそうです。
    検証が済んだので、ドキュメントルートは/var/www/html へ戻しました。


    次にシェルスクリプトで単純なCGIを動作確認してみました。
    次の内容で/var/www/cgi-bin/hello.cgi を設置し、 root の755 パーミッションとしました。

    #!/bin/bash
    echo "Content-type: text/html"
    echo ""
    echo ""
    echo "<html>"
    echo "<body>"
    echo "Hello, CGI!<br>"
    echo "</body>"
    echo "</html>"
    


    ls -Z で確認したところ、/var/www/cgi-bin/ のコンテキストが継承され、 hello.cgi は自動的にhttpd_sys_script_exec_t タイプとなりました。
    タイプがこれに設定されていることは、 CGI としてhttpd_t ドメインのプロセスから実行されるために重要です。 システムのデフォルトの場所(/var/www/cgi-bin/)にファイルを設置したので、 自動で適切なコンテキストが付与されました。
    SELinux ではまらないためには、 なるべくシステムの慣習に従って行動するのがコツです。
    Web ブラウザでこのCGI にアクセスしたところ、普通にHello, CGI! が表示されました。

    ところで、CentOS v6 のSELinux ではApache がCGI を起動することがデフォルトでOK になっています。しかし、静的なコンテンツだけを公開したいサーバでは、 CGI をSELinux で禁止できた方がより安全です。
    SELinux のポリシーでは、このようなシステム要件によってon/off したいような細部の機能を簡単にカスタマイズする仕組みがあります。 これをブーリアンと呼びます。
    /usr/sbin/getsebool -aで利用可能なブーリアンと現在の設定を確認できます。 cgi をキーワードで絞ると次のようになりました。

    $ /usr/sbin/getsebool -a | grep cgi
    git_cgit_read_gitosis_content --> off
    httpd_enable_cgi --> on
    


    httpd_enable_cgi がon になっていることがわかります。
    ブーリアンを変更するには、# /usr/sbin/setsebool -P 名前 on/off のようにします。
    CGI の起動を禁止したい場合は # /usr/sbin/setsebool -P httpd_enable_cgi offです。


    Apache がCGI を起動できるといっても、 許可される挙動とされないものとがありそうです。実験してみます。
    上のCGI を次のように修正し、/tmp/cgi_tmp.txt へ現在の日付を出力するようにしてみました。
    3年前にやったときはあらかじめ出力ファイルに適切なコンテキストを 設定しておかないと書き込めなかったのですが、今回は普通に動作しました。

    #!/bin/bash
    /bin/date >> /tmp/cgi_tmp.txt
    echo "Content-type: text/html"
    echo ""
    echo ""
    echo "<html>"
    echo "<body>"
    echo "file write OK<br>"
    echo "</body>"
    echo "</html>"
    


    それではとCGI を修正し、 /usr/bin/head -1 /etc/passwd でpasswd ファイルの先頭1行を 返すようにしましたが、これも普通に実行できました。
    デフォルトの設定では、 こういうことはSELinux で保護されないことがわかります。


    SELinux でCGI がエラーになる例として、次のようなものがあります。

    #!/bin/bash
    cd /tmp
    /usr/bin/wget http://example.com/
    echo "Content-type: text/html"
    echo ""
    echo ""
    echo "<html>"
    echo "<body>"
    echo "wget OK<br>"
    echo "</body>"
    echo "</html>"
    


    CGI の中からwget でインターネット上の別サイトにアクセスしています。
    このCGIへアクセスすると、次のようなエラーがログに出力されました。

    May 24 23:00:16 CentOS64teresa kernel: type=1400 audit(1369368016.429:31135): 
    avc:  denied  { name_connect } for  pid=10743 comm="wget" dest=80 scontext=unconfined_
    u:system_r:httpd_sys_script_t:s0 tcontext=system_u:object_r:http_port_t:s0 tclass=tcp_socket
    


    httpd_sys-script_t ドメインからwget でどこかの80 番ポートにconnect しようとしたシステムコールがdenied されてます。
    安全のためにこのようなポリシーになっているのでしょうが、 システムによってはこれが必要な要件である場合もあります。
    この問題の解決は難しかったのですが、今回のSELinux との格闘の中でたまたまたどり着いたman httpd_selinux の中にいろいろ説明があり、 # /usr/sbin/setsebool -P httpd_can_network_connect on でCGI からのネットワークアクセスも実行できるようになりました。 先ほど説明したブーリアンですね。

    なお、このブーリアン設定変更の際に次の記録がログに出力されました。

    May 24 23:09:02 CentOS64teresa kernel: type=1405 audit(1369368542.481:31137): bool=
    httpd_can_network_connect val=1 old_val=0 auid=500 ses=5
    May 24 23:09:02 CentOS64teresa dbus: avc:  received policyload notice (seqno=2)
    May 24 23:09:02 CentOS64teresa dbus: [system] Reloaded configuration
    May 24 23:09:02 CentOS64teresa dbus: avc:  received policyload notice (seqno=2)
    May 24 23:09:03 CentOS64teresa setsebool: The httpd_can_network_connect policy bool
    ean was changed to on by root
    


    変更の記録がしっかり残るので安心です。

    普通の使い方だと問題ないのですが、 少し例外的なことをして違反が出た場合は上のような感じでSELinux と闘ってゆくわけです。
    man を見る時のヒントとして、まずman selinuxを見るのがよいです。 するとSEE ALSO に次の関連キーワードがリストアップされてます。
    booleans(8), setsebool(8), selinuxenabled(8), togglesebool(8),
    restore-con(8), setfiles(8), ftpd_selinux(8), named_selinux(8),
    rsync_selinux(8), httpd_selinux(8), nfs_selinux(8), samba_selinux(8),
    kerberos_selinux(8), nis_selinux(8), ypbind_selinux(8)


    Apache の最後に、WebDAV ベースでSubversion のリポジトリサーバをセットアップしてみました。
    これはコンテキストの設定が必要でした。

    /var/www/svn/が規定の場所のようなので、svnadmin でリポジトリをcreate する時はこの場所より下に作成します。

    # mkdir /var/www/svn
    # /usr/bin/svnadmin create /var/www/svn/myrepo
    # chown -R apache:apache /var/www/svn

    WebDAV をサービスするApache がコンテンツを読み書きできるように、 コンテキストを設定します。順番が前後するのですが、restorecon
    については後ほどJava の節でお話します。

    # /sbin/restorecon -r /var/www/svn/*

    すると/var/www/svn/ より下の階層はファイルのタイプとして httpd_sys_rw_content_tが設定され、SVN 用途としてWebDAV 経由で期待通りに利用できるようになります。

    Apache 側は、
    1. Apache が起動している場合は停止

    2. Apache 用WebDAV 拡張のインストール
      # yum install mod_dav_svn

    3. BASIC認証用のテスト用ユーザを作成
      # htpasswd -c /etc/httpd/conf.d/svn_myrepo.basic svntest
      DIGEST認証で作成してしまうと、最近のTortoise SVNは接続できなくなるようです。 昔はTortoiseSVN でもDIGEST認証で問題なかったのですが。
      Eclipse 等の対応のクライアントからのみ接続するのであれば、 パスワードの保護に優れたDIGEST認証でも問題ありません

    4. /etc/httpd/conf.d/subversion.conf を設定
      LoadModule dav_svn_module     modules/mod_dav_svn.so
      LoadModule authz_svn_module   modules/mod_authz_svn.so
      
      <Location /svn/>
         DAV svn
         SVNListParentPath on
         SVNParentPath /var/www/svn
      
         AuthType Basic
         AuthName "Authorization Realm"
         AuthUserFile /etc/httpd/conf.d/svn_myrepo.basic
         Require valid-user
      </Location>

      httpd.conf でInclude conf.d/*.conf となっているので、 このファイルは自動的にインクルードされます。

    5. Apache を起動

    以上でhttp://IPアドレス/svn/myrepo/ というURL でWebDAV 経由で期待通りにSVN を利用できました。


  17. Oracle11gR2 Express 版
  18. 開発用、テスト用に無償で利用できるOracle 11gR2 Express版を入れてみました。
    Linux 用は64bit版のみが提供されてます。
    ドキュメントにはSELinux について明記されてないようですが、 普通に入れることができ、普通に動作しました。

    SELinux とは関係ありませんが、/etc/init.d/oracle-xe configure がエラーになりましたが/etc/hostsのlocalhost に自分のホスト名を追記するとうまくいくようになりました。

    まとめると# rpm -ivh oracle-xe-11.2.0-1.0.x86_64.rpm でインストール(後で入れるTomcat と競合しないように、 8080ポートは9080に変更しました)して
    # /etc/init.d/oracle-xe configureで初期設定して、 外部からのアクセスが必要であればファイヤーウォールのTCP 1521 を開放するだけです。 ものすごく簡単にセットアップできます。
    手動で停止したい場合は# /etc/init.d/oracle-xe stop
    手動で起動したい場合は# /etc/init.d/oracle-xe start
    です。

    SELinux のCentOS デフォルトのtargeted タイプ動作では、対象(Targeted) となるタイプのプロセスのみが保護されます。
    Oracle のSELinux ポリシーはCentOS にないので、Oracle はSELinux に干渉されず、 従来のパーミッション制御のルールの元で何でもできるわけですね。


  19. Java 1.7.0_21
  20. 最終的にTomcat を動かしたいので、まずOracle 殿のサイトからjdk-7u21-linux-x64.tar.gz をダウンロードさせてもらい、 /opt/jdk1.7.0_21 となるように展開しました。
    次のように、/opt/jdk としてアクセスできるようにシンボリックリンクを作成しました。
    # /bin/ln -s /opt/jdk1.7.0_21 /opt/jdk

    CentOS6 がjava について、SELinux のデフォルト設定を持っているかどうかを確認するために、/etc/selinux で/bin/grep -r java *を実行してみたところ、java_exec_t などのタイプが定義されていることがわかりました。

    インターネットを検索してみると、次の情報にあるように、java をjava_exec_t に設定することは大切なようです。
    SELinux環境でInterstage Application Serverを使用する場合の注意事項について (リンクが切れてしまってます)

    yum を利用せずに自分で入れたjava に対するコンテキストの設定ですが、 /etc/selinux/targeted/contexts/files/file_contextsに次のような 設定があります。
    (1)/usr/(.*/)?bin/java.*     --  system_u:object_r:java_exec_t:s0
    (2)/opt/(.*/)?bin/java[^/]*  --  system_u:object_r:java_exec_t:s0
    
    ディレクトリ配下に対して一括でコンテキストを設定してくれるrestorecon という素敵コマンドがあります。
    このコマンドは上のような定義に従って自動でコンテキストを付与してくれます。
    上の規則では、
    (1)/usr/ の下のどこかでbin で終わる名前のディレクトリより下にある、java ではじまるファイル名のもの全て
    または
    (2)/opt/ の下のどこかでbin で終わる名前のディレクトリ直下にあるjava で始まる名前のファイル全て
    がヒットし、コンテキストがsystem_u:object_r:java_exec_t:s0 に設定されます。
    /usr と/opt で微妙に正規表現が違います。(1)では/usr/bin/javadir/foo.txt もヒットしてしまうので、foo.txt はjava_exec_t になってしまいます。 (2)ではそのようなことはありません。正規表現で[]カッコの中の「^」はnot の意味になるので、bin の下のサブディレクトリは含まれないことになります。 (2)の正規表現の方がイケてますね。

    Java は/opt/ 配下に入れてるので(2)の正規表現がうまくマッチしそうです。 次のようにrestorecon を実行します。
    # /sbin/restorecon -r /opt/jdk1.7.0_21/*
    結果はls -Z で確認できます。 ファイルのコンテキストがいい感じに自動調整されました。

    なお、こうしてセットアップしたjavac(コンパイラ)やjava(java VM) は普通に動作しました。


  21. Tomcat v7.0.40
  22. java と同じように/etc/selinux/ で検索してみましたが、tomcat 用のSELinux 定義は特にないようです。というか、Tomcat はjar ファイルの集合であり、 tomcat とうプロセスはなく、java として実行されるのでtomcat 用定義というのはナンセンスなのかもしれません。

    yum でパッケージとして入れられるtomcat もありますが、 最新のものを自分でセットアップしてみました。
    apache-tomcat-7.0.40.tar.gz をダウンロードさせてもらい、 /opt/apache-tomcat-7.0.40 として展開し、/opt/tomcat7 としてアクセスできるようにシンボリックリンクを作成しました。
    rootではなく一般ユーザでtomcat を起動したいので、 tomcat グループとtomcat ユーザを新規に作成し、/opt/tomcat7 配下の全てのファイルのオーナをtomcat:tomcat に設定しました。

    Tomcat の起動スクリプトはbin/setenv.sh が存在する場合はそれを実行し、 環境変数をそこから得るようになっているようです。 次の内容でsetenv.sh を作成し、755 にしました。

    #!/bin/sh
    export JAVA_HOME=/opt/jdk
    export CATALINA_HOME=/opt/tomcat7
    export CATALINA_OPTS="-server -Xmx1024m -XX:MaxPermSize=256m"
    


    別マシンからアクセスできるように、Linux のファイヤーウォールのTCP 8080 ポートを開放しました。

    動かしてみます。
    root 経由でtomcat にsu し、/opt/tomcat7/bin/startup.sh で起動でき、トップページにアクセスできました。
    /opt/tomcat7/bin/shutdown.sh で停止も問題ありません。

    ここでbin_t というSELinux コンテキスト定義が存在し、sh ファイルはbin_t にしておくのが行儀がよい ような気がしてきました。 また、jar ファイルもlib_t タイプにしておくのがよさそうです。 そこで試しに
    # /sbin/restorecon -r /opt/apache-tomcat-7.0.40/*
    を実行してみたところ、.sh ファイルや.jar ファイルにいい感じでコンテキストが設定されました。

    その後、Tomcat のServlet からOracle のデータベースにアクセスするようにしてみましたが何も問題は発生しませんでした。
    また、Apache にAJP(mod_proxy_ajp.so)でTomcat 連携を設定し、 Webブラウザ→Apache 80番→Tomcat 8009番という経路でServlet にアクセスしてみましたが、これも何も問題は発生しませんでした。
    Tomcat の8080ポートを別のポートに変更しましたが、これも問題なしでした。

    java VM は言語のインタプリタなので基本的にSELinux で要件を制限できず、 何でもできるわけです。その上で動作するTomcat も制限されずに従来のパーミッション制御の範囲で何でもできます。
    javaベースのシステムを動かす場合、デフォルトのSELinux の設定ではあまりセキュリティの向上は見込めないようです


  23. sendmail 8.14.4
  24. CentOS6 ではPostfix がデフォルトのメール配送エンジンになったようですが、 私は20年前からsendmail を愛用させてもらっており、今回もCentOS インストールの時にメールサーバとしてsendmail を明示的に指定しました。

    ローカルから外部へのメール送信と、周囲のPC からのメール送信の中継をするためにsendmail を設定します。
    結論を先に言うと、SELinux の干渉はありませんでした。 問題なく利用できます

    ps -eZ | grep sendmail で確認したところ、次のようになりました。

    $ ps -eZ | grep sendmail | grep -v grep
    system_u:system_r:sendmail_t:s0  2521 ?        00:00:00 sendmail
    system_u:system_r:sendmail_t:s0  2530 ?        00:00:00 sendmail
    

    3列目はTTYの名前なので、デーモンだから端末にひもづいてないために「?」 となっています。
    sendmail_t というドメインで動作しているようですね。
    $ grep -r sendmail /etc/selinux
    もいろいろヒットするようなので、SELinux の監視配下にあることが想像できます。
    規定のメールサーバはPostfix ですが、sendmail 用のポリシーも準備されてるわけですね。

    外部へメールを送信できるようにするため、 次の5点について/etc/mail/sendmail.mc を修正しました。
    1. 外部へのメール送信は全て別に設置してあるメール転送サーバへ丸投げ するために、次のように修正
      元: dnl define(`SMART_HOST', `smtp.your.provider')dnl
      新: define(`SMART_HOST', `メール転送サーバのFQDN')dnl
    2. root のメールもドメインを書き換えたいので次の行はアンコメントアウト
      元: dnl EXPOSED_USER(`root')dnl
      新: EXPOSED_USER(`root')dnl
    3. ローカルから送信するメールのFrom のドメイン名が localhost.localdomain のまま出て行かないように次の一連の修正を実施
      元: dnl MASQUERADE_AS(`mydomain.com')dnl
      新: MASQUERADE_AS(`$w.$m')dnl
      
      次の行はアンコメントアウト
      元: dnl FEATURE(masquerade_entire_domain)dnl
      新: FEATURE(masquerade_entire_domain)dnl
      次の行もアンコメントアウト
      元: dnl FEATURE(masquerade_envelope)dnl
      新: FEATURE(masquerade_envelope)dnl
    4. IPv4 で25番ポートをLISTEN し、クライアントPC(Windows) からのメール転送要求を受信できるように、次の行を修正
      元: DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')dnl
      新: DAEMON_OPTIONS(`Port=smtp, Name=MTA')dnl
    5. ホスト名とドメイン名を明示的にセットするために、 sendmail.mc ファイル末尾に次の3行を新規に追記
      Dwホスト名(wのあとには空白を空けない)
      Dmドメイン名(mのあとには空白を空けない)
      define(`confDOMAIN_NAME', `$w.$m')
    また、/etc/mail/access ファイルにメール転送を受け付ける周囲のPC のIPアドレスを設定しました。

    sendmail.mc に記述した内容を元に、新しいsendmail.cf を作成します。
    # /etc/init.d/sendmail stop
    # cd /etc/mail
    # cp sendmail.cf sendmail.cf.backup
    # make sendmail.cf
    # ls -l
    # ls -Z
    sendmail.cf はetc_mail_t タイプになってました。問題なさそうな感じです。
    これを忘れてて少々はまったのですが、ファイヤーウォールのTCP 25 番ポート開放が必要でした。開いていると勝手に思ってました。
    /etc/hosts.allow の設定は不要でした。
    sendmail を起動します。
    # /etc/init.d/sendmail start
    # tail /var/log/maillog
    # ps -eZ | grep sendmail

    ローカルの一般ユーザやroot からの外部へのメール送信、同一LAN 上の Windows XP からのメール送信中継が正常に動作しました。
    SELinux からは、特になにもエラーは出ませんでした。


  25. ファイル操作やバックアップの注意点
  26. SELinux を利用しているLinux 上で、ファイルのコピーや移動を行う場合に、 注意しないとSELinux のコンテキストが変わってしまう場合があります。
    そのため、それまで問題なくできていたことが、 アクセス制御違反になってしまう場合がありそうです。
    ファイル操作におけるSELinux の注意点を確認してみました。

    1. mv
      mv は特別なオプションなしで、SELinux コンテキストを維持してくれます。 問題ありません。

    2. cp
      cp は注意が必要です。そのまま使うと、SELinux コンテキストが変わってしまいます。 コピー先の親ディレクトリのコンテキストが設定されてしまいます。
      日本語のman cp では出てこないのですが、「-c」というオプションが追加されてます。 英語のman cp や、cp --help で説明があります。「-c」は「--preserve=context」 と同じ意味になります。
      カレントディレクトリにあるファイルを全て、コンテキストを維持したままでdir へコピーしたい場合は「cp -pRc * dir/」を実行します。

    3. tar
      tar はデフォルトではSELinux コンテキストが維持されません。「--selinux」 オプションをつけると維持されます。
      tar --selinux -zcvf prj_backup.tgz prj
      のように使用します。

    4. dump/restore
      dump/restore は特別なオプションなしで、SELinux コンテキストを維持してくれます。 問題ありません。

    5. その他、 有償のバックアップソリューションを利用する場合は個別に確認が必要となります

    dump はroot 権限で利用するものですが、cp, mv, tar は一般ユーザで利用できます。
    そこで、 ある程度ファイルのコンテキストを一般ユーザで調整することが可能です。
    例えばjava のjar で.jar ファイルを作成する場合で、コンテキストのタイプをlib_t へ設定したい場合を考えます。スーパーユーザは可能ですが、 一般ユーザ権限でやりたい場合は次のような手順が考えられます。

    1. 作成したjar ファイルをmyjar.jar とします。
      myjar.jar のタイプはuser_home_t です。

    2. lib_t タイプのサンプルをコピー
      /lib/ にあるほとんどのファイルはlib_t タイプなので、どれか適当なものを 作業場所へcp -c でコンテキストを維持してコピーします。
      例として、libutil-2.12.so をコピーします
      cp -c /lib/libutil-2.12.so .

    3. 作成したjar ファイルをコピーしてきたファイルへ上書きコピーします
      この時、jar ファイルのコンテキストで上書きしてしまわないように、「-c」 はつけません
      cp myjar.jar libutil-2.12.so

    4. myjar.jar をいったん削除します
      rm myjar.jar

    5. mv でファイル名を変更します
      mv libutil-2.12.so myjar.jar
      mv はコンテキストを維持するので、myjar.jar のタイプはlibutil-2.12.so と同じlib_t になります。

    これで一般ユーザ権限でlib_t タイプのjar ファイルを作成可能です。

    これぐらいはできないと、アプリケーション保守の担当者がroot 権限なしでプログラムや設定ファイルの修正版を差し替え作業できなくなるので、 私はよいと思います。
    ですが強制アクセス制御の考え方では、 一般ユーザがファイルのコンテキストを自由に操作できてしまうのは 問題があるかもしれません。


  27. ブーリアンの例
  28. # /usr/sbin/getsebool -aで簡単に確認できますが、 私の主観で利用されることがありそうなブーリアン値で、デフォルトがOff になっているものを貼っておきます。参考にしてください。



  29. 結局SELinux で何がうれしいのか
  30. まださわり初めて1ヶ月ほどですが、SELinux が保護してくれることとしてわかったのはApache が8000 番ポート等で勝手に口を開くことを禁止してくれるとか、 CGIがネットワークにアクセスすることを禁止してくれることに気づいたぐらいです。 これらがものすごくうれしいか、というと正直微妙です。

    比較のために例えて言うと、telnetd を利用していたシステムでsshd を利用するように変更すると、 コストに対してものすごくセキュリティが向上すると思うのです。 また、ある特定のIPアドレスからのみssh で接続するというサーバがある場合、 そのIPアドレスのみをsshd で受けるように/etc/hosts.{allow|deny} で制限すると、わずかのコストでやはりセキュリティが大きく向上します。
    SELinux 化のコストに対して、 一体どのようにセキュリティが向上するのかを考えると、、、、どうなんでしょう。
    勝手な妄想ですが、SELinux のようなシステム横断的にセキュリティを一元設定できる、 もっとすごい別の仕組みがあって、apache やsendmail やPAM で個別にセキュリティ機能を設定しなくても、 全部一元的に設定できたりするのであれば、 それは魅力的で習得にチャレンジする意欲がわきそうです。 自分自身まだSELinux のメリットを認識できていないのかもしれません。
    この文書では様々なサービスをセットアップする設定者の視点ですすめてきました。 攻撃側の視点で見るとまた違う姿が見えるのでしょうか。

    SELinux がOn(Enforcing)となった環境で、 自分のやりたいことを実現する立ち回り方はある程度つかめてきました。 仕事でSELinux 環境で何かをしなければならないことも今後は多くなると思うので、 消極的ですが自分が対応可能な環境の幅が広くなった安心感は大きいです。

  31. (参考)認証機能の強化について
  32. SELinux は強制アクセス制御機能を実現しています。 セキュリティの要件として重要なユーザ認証についてはSELinux のスコープ外となります。

    Linux のユーザやパスワードについて、 本サイトの別ページに書いた文がありますのでよければあわせてご参照ください。 こちらは具体的にいろいろとうれしいことを実現できます(^_^)
    Linux のユーザとパスワード


トップページへ戻る