TORIPIYO DIARY

recent events, IoT, programming, security topics

docker-composeを使ってminioコンテナを動かす

結局、いま開発中のアプリでは利用を止めたけれど、オブジェクトストレージサーバのminioをdocker-composeで試したので内容をメモしておきます。

docker-compose.yml
gist.github.com

  • ローカルマシンの9000ポートを、minioコンテナの9000ポートに紐付ける
  • os ボリュームを作成して、minioコンテナの/data配下にマウント
  • server /data で、オブジェクトストレージサーバをコンテナの起動時に実行させるようにして、データを/data配下に保存するようにする
  • MINIO_ACCESS_KEY, MINIO_SECRET_KEYを指定してないと、ランダムな値が起動時にアサインされるので好きな値を記載して固定化させる

上記の、docker-compose.ymlファイルを含むディレクトリ内で、

docker-compose up

を実行するとminioコンテナを起動できます。
起動後、localhost:9000にブラウザなどから繋いで、MINIO_ACCESS_KEY, MINIO_SECRET_KEYの認証を通せばウェブコンソール画面が表示されるのでファイルをアップロードしたり、ダウンロードできるようになります。

f:id:ha107chan:20180824170425p:plain

package.jsonの記述を満たすようにnpmのパッケージをアップデート

ちょっと調べたことを忘れないようにブログに残しておきます。

nodejsでアプリケーションを開発していて、npmのパッケージをpackage.jsonの記述を満たす最新バージョンに更新したいことがあると思います。

例えば、package.jsonで、

  "dependencies": {
    "mongoose": "^4.13.15",
  },

と記載していた場合、^の意味は、"0ではない最も左側の数字は変更しない"という意味なので、package.jsonの記述に従うmongooseのバージョンは、メジャーバージョンの4系となります。semver | npm Documentation

npm outdatedコマンドを使うと、インストールされたパッケージでバージョンが古いものを確認できます。例えば、上記のpackage.jsonを利用していた場合は以下のように出力されます。

> npm outdated
Package          Current   Wanted  Latest  Location
mongoose         4.13.11  4.13.15   5.2.9  xxxxxx

WantedとLatestという列が表示されていますが、Wantedには、package.jsonの記述を満たすパッケージのうち、バージョン番号が最大のものが出力されます。Latestには、レジストリにlatestのタグがつけられたバージョンのバージョン番号が出力されます。outdated | npm Documentation

mongooseのバージョンをWanted列のバージョンに更新させるには、

npm update mongoose

を実行します。

npm update mongooseの実行後、もう一度、npm outdatedを見ると、

> npm outdated
Package          Current   Wanted  Latest  Location
mongoose         4.13.15  4.13.15   5.2.9  xxxxxx

と出力されて、CurrentとWantedのバージョン番号が一致していることが分かります。

これで、mongooseはpackage.jsonの記述をみたす最大のバージョン番号のものに更新されました。

新卒で入社した会社を辞める

新卒で入社して、7年と3ヵ月勤めた会社を辞めました。

入社してから2~4年目、割と色々なことに関わって仕事が出来ました。5年目を過ぎたぐらいから業務内容はより細分化され、組織全体の生産性向上が進められるようになります。一人が全ての工程に関わって一台の車を作るのではなくて、工程ごとに担当を分けて車を作るようにしました。

効率的に働けるようになれば、生産性も上がり早く帰れるようになる。この変化を歓迎しました。

仕事内容は、定型化・具体化され、仕事量は減り、早く帰れるようになりました。多部署からの依頼を以前よりも早く処理出来るようになり、成果物の品質は安定するようになりました。以前よりも仕事に余裕を持つことが出来るようになり、プライベートの時間を前よりずっと取れるようになりました。

しかし、なんだか物足りない。

能力の3~4割ぐらいしか発揮してないように感じる。楽だけど記憶に残らない。2~4年目の頃がだんだん懐かしくなる。

いまの会社は決して悪い会社ではないのですが、自分の好む働き方の再現は難しくなったようです。この状況は更に進化します。

会社を辞めました。もう少し小さな組織で働こうと思います。

pythonで開発したburpエクステンションをデバッグする

burpのpythonエクステンションを作成している時に、デバッグをしたかったのでその方法を調べました。How to use pdb to debug Python Burp Extensions の内容を参考にしています。

1. registerExtenderCallbacksメソッド内に、sys.stdout, sys.stderrを追加する
以下のように、registerExtenderCallbacksメソッド内にsys.stdout, sys.stderrを追加します。import sysを忘れないように。

    def registerExtenderCallbacks(self, callbacks):
        # debug purpose
        import sys
        sys.stdout = callbacks.getStdout()
        sys.stderr = callbacks.getStderr()

2. 実行を止めてデバッグしたい箇所に、import pdb; pdb.set_trace()を追加する
下記のように、ソースコードデバッグを実行したい箇所にimport pdb; pdb.set_trace()を追加します。

        # check var1 value
        var1 = issue.getIssueName()

        # stop program here
        import pdb; pdb.set_trace()

3. burpをコマンドラインから起動する
burpをコマンドラインから起動させます。自分の環境では、以下のパスでburpを起動することができます。burpsuite_pro.jarのパスは環境によって異なるかと思います。

java -jar /Applications/Burp\ Suite\ Professional.app/Contents//java/app/burpsuite_pro.jar

4. OutputとErrorsの向き先を"Output to system console"に変更する
開発中のエクステンションを読み込ませて、以下のようにOutputとErrorsの出力の向き先をsystem consoleに変更させます。

Output
f:id:ha107chan:20180707230231p:plain

Errors
f:id:ha107chan:20180707230255p:plain

5. エクステンションを実行してデバッグ開始
これで、デバッグの準備は出来ました。import pdb; pdb.set_trace()の箇所のソースコードが実行されるようにBurpで操作すると、コンソールからデバッグできるようになります。

~/ java -jar /Applications/Burp\ Suite\ Professional.app/Contents//java/app/burpsuite_pro.jar
(Pdb) temp_val = "aaa"
(Pdb) temp_val
'aaa'
(Pdb) import sys
(Pdb) os.name
'java'

開発が捗りますね。

JMXを利用してtomcatのJDBCコネクションプールのトラブルシューティング

セキュリティの勉強のために脆弱性のあるJavaアプリケーションを作ったのですが、スキャナをかけてしばらくするとアプリから応答が返ってこなくなります。この現象は数時間経っても変わらず、コンテナを再起動しないと直りません。
アプリケーションはローカルのdocker環境で動かしていて、

クライアントリクエスト -> apacheコンテナ -> tomcatコンテナ -> mysqlコンテナ

というフローで処理をしています。ちょっと調べてみると、tomcatコンテナまでの処理をするリクエストなら正常に返ってくるのですが、mysqlコンテナと通信する必要がある処理は応答が返ってこないことがわかりました。
ですので、原因はtomcatコンテナかmysqlコンテナにあります。さらに、mysqlコンテナ上でtcpdumpを使ってパケットはtomcatコンテナから届いているのか確認すると、何もパケットが来ていません。ということは、tomcatコンテナが正常にmysqlコンテナに接続リクエストを投げていないことが推測できます。

いろいろと調べると、tomcatコンテナにはデータベース接続プールがあり、この接続プールにあるコネクションが枯渇すると新規のリクエストがデータベースに接続出来なくなるようです。(ちょっと、細かい説明間違っているかもしれません。)ということで、tomcatのデータベースコネクションプールのコネクション数はどれぐらいあるのか確認する方法を調べました。

またまた、いろいろと調べるとJMXを利用するとtomcatのデータベースコネクションプールの様子がわかりそうです。JMXを有効にするために、docker-composeのtomcatコンテナの設定に以下を追加しました。
JMX用のポートをtomcatコンテナの9999ポートにアサインして、ローカルマシンからtomcatコンテナの9999ポートに接続できるようにしています。(ちなみに、-Djava.rmi.server.hostname=127.0.0.1を-Djava.rmi.server.hostname=0.0.0.0とするとうまくJMXに接続出来ず、一時間ぐらい悩みました。)

    image: tomcat:9.0-jre9
    ports:
      - 9999:9999
    environment:
      CATALINA_OPTS: "-Dcom.sun.management.jmxremote
        -Dcom.sun.management.jmxremote.port=9999
        -Dcom.sun.management.jmxremote.rmi.port=9999
        -Dcom.sun.management.jmxremote.ssl=false
        -Dcom.sun.management.jmxremote.authenticate=false
        -Dcom.sun.management.jmxremote.local.only=false
        -Djava.rmi.server.hostname=127.0.0.1"

docker-compose up でコンテナを立ち上げたあとは、ローカルマシンのターミナル上で
jconsole service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi
を入力すればjsonsoleが立ち上がります。

使用中のデータベースコネクションの数(numActive)と、待機中のデータベースコネクションの数(numIdle)は、MBeansタブから、
Catalina => DataSource => localhost => /app => javax.sql.DataSource => jdbc_(データベース名) => connections => Attributes
と辿って行くと見つけることが出来ます。

f:id:ha107chan:20180629003839p:plain

スキャナを実行する前は、numActiveは0だったのですが、スキャナを実行してしばらくするとnumActiveは8となって、maxTotalの数8と同じになります。この状態になるとDBに接続処理のリクエストの場合は応答が返ってこなくなりました。データベース接続プールの枯渇が原因の可能性大です。

では、なぜデータベースのコネクションは解放されないのか?ソースコードを見ると、あまりJavaに詳しくないのですがなんとなく原因を特定出来ました。

問題のあるコード

    InitialContext context;
    DataSource ds;
    try {
      context = new InitialContext();
      ds = (DataSource) context.lookup("java:comp/env/xxxx");
      Connection con = ds.getConnection();

      Statement stmt = con.createStatement();
      context.close();

      String query = "select * from items where title like '%" + word + "%';";
      ResultSet result = stmt.executeQuery(query);

      // *** skip *** //

      context.close();
      con.close();

      // *** skip *** //

    } catch (NamingException e) {
      e.printStackTrace();
    } catch (SQLException e) {
      e.printStackTrace();
    }

このアプリはセキュリティの勉強をするために作ったので、わざとSQLインジェクションが起こるように作っています。スキャナは細工したパラメータ値をリクエストで送るので、queryは正しいSQL文にならない可能性もあります。
そうすると、ResultSet result = stmt.executeQuery(query); の時点で例外を吐いて、con.close();の処理が実行されないためにコネクションが解放されていないようでした。そこで、以下のようにSQLの実行時に例外を吐いてもコネクションがcloseされるように修正しました。

修正したコード

    InitialContext context;
    Connection con = null;
    DataSource ds;
    try {
      context = new InitialContext();
      ds = (DataSource) context.lookup("java:comp/env/xxxx");
      con = ds.getConnection();

      Statement stmt = con.createStatement();
      context.close();

      String query = "select * from items where title like '%" + word + "%';";
      ResultSet result = stmt.executeQuery(query);

      // *** skip *** //

    } catch (NamingException e) {
      e.printStackTrace();
    } catch (SQLException e) {
      e.printStackTrace();
    } finally {
      if (con != null){
        try {
          con.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
        con = null;
      }
    }

たぶん、まだまだ改善できるコードなんだろうなとは思いますが、この修正後、スキャナを実行してしばらくたつと応答が返ってこなくなる現象は無くなり、なんとか問題を解決することが出来ました。

vulsをvagrantのCentOS6にインストール

こちらの記事を書くときに利用した、vulsのインストール方法を共有します。
toripiyo.hatenablog.com

基本的には、Install Manually on CentOS · Vuls に記述されている方法を踏襲しています。
vagrantのCentOS6環境にvulsをローカルインストールして、自分自身に対して脆弱性スキャンをかけます。CentOS6の場合、スキャン自体はroot権限がなくても一般ユーザで実行可能です。

以下のコマンドは、CentOS6にログインして実行しています。

# install required packages
sudo yum -y install sqlite git gcc make wget

# install go
wget https://dl.google.com/go/go1.10.1.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.10.1.linux-amd64.tar.gz
mkdir $HOME/go

export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

source /etc/profile.d/goenv.sh

# deploy go-cve-dictionary
sudo mkdir /var/log/vuls
# change owner to be vagrant
sudo chown vagrant /var/log/vuls
sudo chmod 700 /var/log/vuls

mkdir -p $GOPATH/src/github.com/kotakanbe
cd $GOPATH/src/github.com/kotakanbe
git clone https://github.com/kotakanbe/go-cve-dictionary.git
cd go-cve-dictionary
make install


# fetch NVD data
cd $HOME
for i in `seq 2002 $(date +"%Y")`; do go-cve-dictionary fetchnvd -years $i; done
ls -alh cve.sqlite3

# deploy goval-dictionary
mkdir -p $GOPATH/src/github.com/kotakanbe
cd $GOPATH/src/github.com/kotakanbe
git clone https://github.com/kotakanbe/goval-dictionary.git
cd goval-dictionary
make install

goval-dictionary fetch-redhat 6

# deploy Vuls
mkdir -p $GOPATH/src/github.com/future-architect
cd $GOPATH/src/github.com/future-architect
git clone https://github.com/future-architect/vuls.git
cd vuls
make install

# scan myself
cat > ~/config.toml << EOF
[servers]

[servers.localhost]
host = "localhost"
port = "local"
EOF

vuls configtest

vuls scan

# check report
vuls report --ovaldb-path /home/vagrant/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3

CentOSのパッケージとセキュリティ

CentOSのパッケージのバージョンとセキュリティの関係について、いままであまりちゃんと調べてなかったので確認してそこそこまとめました。CentOS6で、CentOS公式のbaseレポジトリからphpをインストールするとphpのバージョン5.3がインストールされます。php5.3はPHPの開発元では2014年の8月14日にEOLを迎えています。

PHP: Unsupported Branches

この情報のみでは、CentOS6では開発が終わっているバージョンのphpを配布しているようにみえてセキュリティ的に心配になりますが、CentOSのクローン元であるRedHatは、セキュリティの修正に対してバックポートポリシーを採用しています。

Security Backporting Practice - Red Hat Customer Portal

これはどういうことかというと、セキュリティの脆弱性が発見された際、ソフトウェアのバージョンを上げて修正するのではなくて、セキュリティの修正箇所を既存のバージョンにインポートさせるという運用のようです。こうすることで、バージョンが上がることで生じる互換性の問題を可能な限りなくしてセキュリティの穴を塞ぐことが出来ます。ということで、CentOS6のPHP5.3にもセキュリティパッチが提供されています。phpパッケージの変更履歴を見ると、php5.3がEOLを迎えた2014年8月14日以降もセキュリティの修正パッチが提供され続けていることが分かります。RedhatのRemi Colletさんが、頑張ってphpの修正パッチを当てられていることが分かります。

[vagrant@localhost ~]$ rpm -q --changelog php | head -n 20
* Mon Nov 07 2016 Remi Collet <rcollet@redhat.com> - 5.3.3-49
- fix php-soap fails to connect to HTTPS web service sporadically
  as stream_socket_enable_crypto() uses NONBLOCK #1283153

* Mon Jul 25 2016 Remi Collet <rcollet@redhat.com> - 5.3.3-48
- don't set environmental variable based on user supplied Proxy
  request header CVE-2016-5385

* Wed Dec 09 2015 Remi Collet <rcollet@redhat.com> - 5.3.3-47
- fix wrong warning in openssl_encrypt() for missing IV
  when IV is not required #1260315
- fix segfault's when you try and allocate an SplFixedArray
  with size >= 9999 #1071344
- segfault in php_pgsql_meta_data CVE-2015-4644  #1234434
- add options to enable TLS in curl #1255920
- fix segfault in gc_collect_cycles #1122681

* Fri Jul 03 2015 Remi Collet <rcollet@redhat.com> - 5.3.3-46
- fix gzfile accept paths with NUL character #1213407
- fix patch for CVE-2015-4024

Remi ColletさんはRemiパッケージのメンテナーで、remiは最新のphpパッケージなどを提供しているようです。

English : FAQ - Remi's RPM repository - Blog

CentOS6のMaintenace Updatesは、2020年11月30日まで提供されるようなので、東京オリンピックまでには余裕を持ってCentOS7に移行したいですね。

About/Product - CentOS Wiki

ちなみに、最近出てきた脆弱性スキャナのvulsでは、CentOS6のphpパッケージは脆弱性のあるパッケージとして誤検知されるかどうか確認してみました。vagrantでCentOS6.9の環境にvulsをインストールして自分自身をスキャンさせます。phpは最新のパッケージにしています。
toripiyo.hatenablog.com

[vagrant@localhost ~]$ rpm -qa | grep php
php-common-5.3.3-49.el6.x86_64
php-5.3.3-49.el6.x86_64
php-cli-5.3.3-49.el6.x86_64

[vagrant@localhost ~]$ vuls report --ovaldb-path /home/vagrant/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3  | head -n 20
[Jun 14 14:35:18]  INFO [localhost] Validating config...
[Jun 14 14:35:18]  INFO [localhost] cve-dictionary: /home/vagrant/cve.sqlite3
[Jun 14 14:35:18]  INFO [localhost] oval-dictionary: /home/vagrant/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3
[Jun 14 14:35:18]  INFO [localhost] Loaded: /home/vagrant/results/2018-06-13T13:01:02Z
localhost (centos6.9)
=====================
Total: 75 (High:24 Medium:51 Low:0 ?:0)	393 installed, 45 updatable

CVE-2017-11176  	10.0 HIGH (nvd)
                	The mq_notify function in the Linux kernel through 4.11.9 does not set the sock
                	pointer to NULL upon entry into the retry logic. During a user-space close of a
                	Netlink socket, it allows attackers to cause a denial of service
                	(use-after-free) or possibly have unspecified other impact.
                	---
                	https://nvd.nist.gov/vuln/detail/CVE-2017-11176
                	https://access.redhat.com/security/cve/CVE-2017-11176 (RHEL-CVE)
                	10.0/AV:N/AC:L/Au:N/C:C/I:C/A:C (nvd)
                	https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=CVE-2017-11176
                	7.8/CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (redhat)
                	https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=CVE-2017-11176
                	Confidence: 100 / OvalMatch

CVE-2017-18017  	10.0 HIGH (nvd)
                	The tcpmss_mangle_packet function in net/netfilter/xt_TCPMSS.c in the Linux

[vagrant@localhost ~]$ vuls report --ovaldb-path /home/vagrant/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3 2>&1 | grep -i php
[vagrant@localhost ~]$

vulsではphpのパッケージは脆弱性のあるものとして誤検知されませんでした。ただ、脆弱性スキャナの中には、php-5.3.3-49.el6.x86_64のパッケージ名のphp-5.3.3の箇所のみを見て脆弱性があると判定するものもあるそうなので注意が必要です。