ssh_config形式のファイルからVulsのconfig.tomlを生成するコマンドを作った

今、脆弱性検出ツールでかなりアツいVulsですが、対象のリモートホストへの接続のためにはTOML形式の設定ファイルが必要です。

なんかssh_configに似ているなーと思ったので、作ってみました。

github.com

Usage

まずはインストール。

$ gem install ssh_config_to_vuls_config

インストールすると sc2vc というコマンドが使えるようになるので、あとはSTDINにssh_config形式のファイルをかませるだけです。

$ cat ~/.ssh/config | sc2vc > vuls_config.toml

あれ?なんか koma に使い方が似ていますね。

というわけで

本家のREADMEにも載せてもらえました!嬉しい!

https://github.com/future-architect/vuls#related-projects

エージェントレスでプラットフォームのインベントリ情報を取得するツールkomaを作った

Ansibleにはsetup module、Chefにはohai、Puppetにはfacterというプラットフォームのインベントリ情報を取得するライブラリや機構があります。

ただ、リモートホストのインベントリ情報を取得したいときに、ohaiやfacterはリモートホスト側にインストールが必要だったり、setup moduleは単体で使いにくかったりするので、

「気軽にエージェントレスでプラットフォームのインベントリ情報を取得できるようにしたいなー」

ということでkomaというgemを作りました。

github.com

Installation

$ gem install koma

Usage

まず、公開鍵認証でSSH接続できるリモートホストを用意します。

例えば、

$ ssh k1low@example.com

でログインできるようなホストですね。

で、そのssh コマンドを koma ssh に変えて実行します。

$ koma ssh k1low@example.com

そうすると、インベントリ情報をJSON形式でSTDOUTに出力します。

さらに複数のホストのインベントリ情報を一気に取得したい場合はカンマ区切りで指定できます。

$ koma ssh example.com,k1low@example.jp
{
  "example.com": {
    ...
  },
  "example.jp": {
    ...
  }
}

取得できるインベントリ情報

取得できるインベントリ情報はkoma keysで確認できます(リモートホストディストリビューションによって取得できる情報にはばらつきがあります)。

$ koma keys
memory
ec2 (disabled)
hostname
domain
fqdn
platform
platform_version
filesystem
cpu
virtualization
kernel
block_device
package
user
group
service

インベントリ情報を指定したいときは --key オプションで指定可能です。

$ koma ssh k1low@example.com --key cpu,kernel

(disabled) になっている ec2 などは --enable-ec2 オプションで有効になります。

Pro Tip: ssh_config形式でリモートホストを指定する

komaはssh_config形式のSTDINを入力とすることができます。

$ cat <<EOF > ssh_config_tmp
Host example_com
  User k1low
  Hostname example.com
  PreferredAuthentications publickey
  IdentityFile /path/to/example_rsa

Host example_jp
  User someone
  Hostname example.jp
  PreferredAuthentications publickey
  IdentityFile /path/to/more/example_jp_rsa
EOF

$ cat ssh_config_tmp | koma ssh --key platform,platform_version

ssh_config形式がいけるということは、

$ vagrant ssh-config | koma ssh --key cpu,kernel

みたいに、Vagrantのサーバのインベントリ情報を取得したり、

$ sconb dump example_* | sconb restore | koma ssh --key platform,platform_version

みたいに、sconb~/.ssh/config をフィルタリングしてインベントリ情報を取得できたりします。

というわけで、エージェントレスが好きな皆様におかれましては是非ご利用ください。

以下余談

komaRuby製なのですが、awspecと同様にFukuoka.rbで作りました。

もともと「エージェントレスでohai」みたいなツールはずっと欲しかったのです。

ふとFukuoka.rbのSlackチャンネルでつぶやいたら、うずらさん が反応してくれてやる気になり、ひとりで作ろうとして途方に暮れていたら、やまださん が「Specinfraに Specinfra::HostInventory という求めている機能がある」というところを教えてもらって、一緒にSpecinfraを読んで、プロトタイプまで一気に作ることができました。

というわけでkomaは実はただの Specinfra::HostInventory の薄いラッパーです(一気に信頼度が上がった感。

とはいえ、 Specinfra::HostInventory を拡張しているところがあるので(user group package serviceのインベントリ情報。主にRedHat系しか作っていない)、最終的に拡張部分をSpecinfraに取り込んでもらうところまで頑張ろうと思っています(komaはそのためのユースケースでもある)。

Fukuoka.rb、最近仕事が忙しくて行けてないので、また消え去り認定されないようにまた参加したい。

PHPカンファレンス福岡2016に参加してきました | "ドキュメントを支える技術"

phpcon.fukuoka.jp

PHPカンファレンス福岡2016に運営メンバーとして参加しつつ、発表もしてきました。

あと、Fusicとしてゴールドスポンサーとして協賛させてもらいました!

ドキュメント=紙という話しをしました

ドキュメントというのは印刷が必要な時があって、すぐにOfficeを開くのもいいけれども、そのまえにプログラムで楽ができないか考えてみよう。そして、PHPは意外にも印刷へのアプローチに有効だよ。という話をしたかったのでした。

発表後、Excel系の話題( k1low/xlsxti.mefra.me )に感想が集中したのが予想外でした。皆さんも苦労しているんですね。。。

個人的には実は koma が1番の力作なので、また別エントリで紹介したいと思います。

運営の話し

ここらへんは他の人に譲ります。ちなみに運営も印刷係でした。紙づいています。

スポンサーの話し

いろいろ嬉しいこともあったのですが、会社としては「スポンサーして良かった」ということに決着していることをここに報告しておきます。

というわけで

皆さんお疲れ様でした!!!

CakePHP3にステートマシンなEnumの仕組みを組み込むStatefulEnumを作った

k1low.hatenablog.com

と、CakePHP3にEnumの仕組みを組み込んだのですが、

さらに、ActiveRecord::Enumにステートマシンの仕組みを組み込むgemが

github.com

「これは便利そう!!!全ての機能をポートするのは難しそうだけど、Enumで管理されているフィールドにステートマシンの制約を組み込むくらいはできるかも。」

ということで作ってみました。

github.com

使い方

基本的には https://github.com/k1LoW/property-enum と合わせて使ってもらうことを想定していますが、特に制限はありません。

<?php
class BugsTable extends Table
{
    const STATUS_UNASSIGNED = 'unassigned';
    const STATUS_ASSIGNED = 'assigned';
    const STATUS_RESOLVED = 'resolved';
    const STATUS_CLOSED = 'closed';

    public $transitions = [
        'status' => [
            'assign' => [
                'from' => self::STATUS_UNASSIGNED,
                'to' => self::STATUS_ASSIGNED
            ],
            'resolve' => [
                [self::STATUS_ASSIGNED, self::STATUS_UNASSIGNED], // from
                self::STATUS_RESOLVED // to
            ],
            'close' => [
                [self::STATUS_ASSIGNED, self::STATUS_UNASSIGNED, self::STATUS_RESOLVED],
                self::STATUS_CLOSED
            ],
        ]
    ];

    public function initialize(array $config)
    {
        $this->primaryKey('id');
        $this->addBehavior('StatefulEnum.StatefulEnum');
    }
}

上記のように $transitions を設定することでフィールドのBugsTable.statusのステートマシンの仕組みを組み込みます。 amatsuda/stateful_enumtransition の制約だけを実現した形になります(メソッドまではない)。

これだけでもシンプルにステートマシンを定義できるので便利かと。

というわけで

機能追加のプルリクなどお待ちしております。

CakePHP3でもActiveRecord::Enumを使いたいので、CakeDC/EnumベースでPropertyEnumを作った

k1low.hatenablog.com

CakePHP2では GitHub - k1LoW/Enumm: Enumm: Enum(model) plugin for CakePHP を作って使っていたのですが、CakePHP3では動きません。

CakePHP3で使える同じようなPluginを探して、CakeDC/Enum を見つけたのですが、CONSTベースとConfigureベース、データベースのテーブルベースしかありませんでした。

というわけで、Enummと同じようにプロパティベースで動くようにCakeDC/Enumにストラテジを追加する形でPropertyEnumを作りました。

github.com

使い方

Tableに $enums プロパティを設定して、 ['strategy' => 'property'] することでCakeDC/Enumの他のストラテジと同じように利用可能です。

<?php

class ArticlesTable extends Table
{
    public $enums = [
        'status' => [
            'public' = 'Published';
            'draft' = 'Drafted';
            'archive' = 'Archived';
        ],
    ];

    public function initialize(array $config)
    {
        $this->addBehavior('PropertyEnum.Enum', ['lists' => [
            'status' => [
                'strategy' => 'property',
            ]
        ]]);
    }
}

AutoSetComponent

Enummの時と同様に $enums をViewに自動でsetしたいときは PropertyEnum\AutoSetComponent を読み込むことで可能です。

<?php
namespace App\Controller;

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('PropertyEnum.AutoSet');
    }
}

というわけで

CakePHP3でも気楽にEmunを使えるようになりました。

CakePHP3でもバリデーションをパターンでまとめられるようにPatternableValidatorを作った

CakePHP3になって、CakePHP2では MultivalidatableBehavior などで実現していたバリデーションのルールセットの切り替えがコア機能に入りました。これはうれしい。

ただ、Kagasawa-sanが作っていたcakeplus/ValidationPatternsBehavior.php で実現していたバリデーションのパターンの設定はコア機能ではできなかったので作ってみました。

github.com

Before

<?php
namespace App\Model\Table;

class UsersTable extends AppTable
{
    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmpty('id', 'create');

        $validator
            ->allowEmpty('username')
            ->add('username', 'minLength4', [
                'rule' => ['minLength', 4],
                'message' => __('Validation Error: minLength4'),
            ])
            ->add('username', 'maxLength10', [
                'rule' => ['maxLength', 10],
                'message' => __('Validation Error: maxLength10'),
            ]);

        $validator
            ->allowEmpty('password');
    }
}

After

<?php
namespace App\Model\Table;

class UsersTable extends AppTable
{
    public function validationDefault(Validator $validator)
    {
        $validator
            ->addPattern('id', ['integer', 'allowEmptyWhenCreate']);

        $validator
            ->addPattern('username', ['allowEmpty', 'username_length']);

        $validator
            ->appPattern('password', ['allowEmpty']);
    }
}

非常にスッキリしますね。

使い方

AppTableなどで、以下のように_validatorClassを変更して、あとはcakeplus/ValidationPatternsBehavior.phpと同じように、バリデーションのパターンを登録していけばOKです。

<?php
namespace App\Model\Table;

class AppTable extends Table
{
    public function initialize(array $config)
    {
        parent::initialize($config);
        $this->_validatorClass = '\PatternedValidator\Validation\Validator';
        \PatternedValidator\Validation\Validator::$validationPatterns = [
            'username_length' => [
                'minLength4' => [
                    'rule' => ['minLength', 4],
                    'message' => __('Validation Error: minLength4'),
                ],
                'maxLength10' => [
                    'rule' => ['maxLength', 10],
                    'message' => __('Validation Error: maxLength10'),
                ]
            ],
        ];
    }
}

これで多くなりがちなバリデーションライフもバッチリです。

というわけで、私のCakePHP3 Plugin生活、スタートです!

fluent-plugin-s3 + fluent-plugin-dstatのフォーマットで保存されていたS3上のログをEmbulkを使ってMySQLに投入してみた

というわけでEmbulkを使ってみたのでメモです。

対象のログ

  • Fluentdで収集
  • dstatの情報を fluent-plugin-dstat で取得
  • fluent-plugin-s3 を利用してS3に保存
  • gzip圧縮

つまりこんなログです。

2016-01-31T15:24:42Z  examplekey  {"hostname":"example.com","dstat":{"total cpu usage":{"usr":"0.201","sys":"0.134","idl":"99.532","wai":"0.134","hiq":"0.0","siq":"0.0"},"mem\
ory usage":{"used":"1212170240.0","buff":"313139200.0","cach":"2034069504.0","free":"246816768.0"},"load avg":{"1m":"0.090","5m":"0.080","15m":"0.090"},"net/total":{"recv":"108.\
867","send":"212.200"}}}

Embulkのインストール

まずはEmbulkをインストールします。

$ brew cask install java
$ brew install embulk

次に必要そうなPluginをインストールします。

以下は、とりあえず最終的に必要になったもの。

$ embulk gem install embulk-input-s3
$ embulk gem install embulk-output-mysql
$ embulk gem install embulk-parser-fluent-s3-log
$ embulk gem install embulk-filter-expand_json
$ embulk gem install embulk-filter-column

inとoutを適当にかいて embulk guess

まずは in がS3なのはわかります。

github.com

tmp.yml とかに

in:
  type: s3
  bucket: my-logs
  path_prefix: web/
  access_key_id: XXXXXXXXXXXXXXXXXXXXXX
  secret_access_key: XXXXXXXXXXXXXXXXXXXXXX
out:
  type: stdout

くらいを書いて

$ embulk guess tmp.yml -o config.yml

と実行するとあら不思議、「いい感じに」設定用ファイルのフォーマットを config.yml として生成してくれます。

Embulkの半分は優しさでできているのか?

生成された config.yml をPluginのドキュメントを見ながら修正して、 embulk preview config.yml でプレビュー。うまくいかないときは embulk guess で微調整していきます。

fluent-plugin-s3のフォーマットのファイルをいい感じにパースしてくれる embulk-parser-fluent-s3-log

fluent-plugin-s3で保存したログのフォーマットはタイムスタンプ、キー、JSONと並んでいたので、さてどうしたものかと思ったら「まさに」なプラグインがありました。

github.com

in:
  type: s3
  bucket: my-logs
  path_prefix: web/
  access_key_id: XXXXXXXXXXXXXXXXXXXXXX
  secret_access_key: XXXXXXXXXXXXXXXXXXXXXX
  decoders:
  - {type: gzip}
  parser:
    type: fluent-s3-log
    columns:
      - {name: hostname, type: string}
      - {name: dstat, type: string}
out:
  type: stdout

ネストされたJSONを展開してくれるembulk-filter-expand_json

カラム dstat の部分はネストしたJSONのままなので展開する必要があります。

こちらも「まさに」なプラグインがありました。

github.com

今回は dstat カラム部分を展開するので以下のように記述しました。

in:
  type: s3
  bucket: my-logs
  path_prefix: web/
  access_key_id: XXXXXXXXXXXXXXXXXXXXXX
  secret_access_key: XXXXXXXXXXXXXXXXXXXXXX
  decoders:
  - {type: gzip}
  parser:
    type: fluent-s3-log
    columns:
      - {name: hostname, type: string}
      - {name: dstat, type: string}
filters:
  - type: expand_json
    json_column_name: dstat
    root: "$."
    expanded_columns:
      - {name: "['total cpu usage'].usr", type: double}
      - {name: "['total cpu usage'].sys", type: double}
      - {name: "['total cpu usage'].idl", type: double}
      - {name: "['total cpu usage'].wai", type: double}
      - {name: "['total cpu usage'].hiq", type: double}
      - {name: "['total cpu usage'].siq", type: double}
      - {name: "['memory usage'].used", type: double}
      - {name: "['memory usage'].buff", type: double}
      - {name: "['memory usage'].cach", type: double}
      - {name: "['memory usage'].free", type: double}
      - {name: "['load avg'].1m", type: double}
      - {name: "['load avg'].5m", type: double}
      - {name: "['load avg'].15m", type: double}
      - {name: "['net/total'].recv", type: double}
      - {name: "['net/total'].send", type: double}
out:
  type: stdout

MySQLカラム名に合わせるために in 側のカラム名のリネーム

カラム名"['total cpu usage'].usr" のままだとMySQLカラム名にするのには微妙なので、カラム名をリネームします。

こちらも「まさに」なプラグインがありました。

github.com

in:
  type: s3
  bucket: my-logs
  path_prefix: web/
  access_key_id: XXXXXXXXXXXXXXXXXXXXXX
  secret_access_key: XXXXXXXXXXXXXXXXXXXXXX
  decoders:
  - {type: gzip}
  parser:
    type: fluent-s3-log
    columns:
      - {name: hostname, type: string}
      - {name: dstat, type: string}
filters:
  - type: expand_json
    json_column_name: dstat
    root: "$."
    expanded_columns:
      - {name: "['total cpu usage'].usr", type: double}
      - {name: "['total cpu usage'].sys", type: double}
      - {name: "['total cpu usage'].idl", type: double}
      - {name: "['total cpu usage'].wai", type: double}
      - {name: "['total cpu usage'].hiq", type: double}
      - {name: "['total cpu usage'].siq", type: double}
      - {name: "['memory usage'].used", type: double}
      - {name: "['memory usage'].buff", type: double}
      - {name: "['memory usage'].cach", type: double}
      - {name: "['memory usage'].free", type: double}
      - {name: "['load avg'].1m", type: double}
      - {name: "['load avg'].5m", type: double}
      - {name: "['load avg'].15m", type: double}
      - {name: "['net/total'].recv", type: double}
      - {name: "['net/total'].send", type: double}
  - type: column
    columns:
      - {name: time}
      - {name: key}
      - {name: hostname}
      - {name: cpu_usr, src: "['total cpu usage'].usr"}
      - {name: cpu_sys, src: "['total cpu usage'].sys"}
      - {name: cpu_idl, src: "['total cpu usage'].idl"}
      - {name: cpu_wai, src: "['total cpu usage'].wai"}
      - {name: cpu_hiq, src: "['total cpu usage'].hiq"}
      - {name: cpu_siq, src: "['total cpu usage'].siq"}
      - {name: mem_used, src: "['memory usage'].used"}
      - {name: mem_buff, src: "['memory usage'].buff"}
      - {name: mem_cach, src: "['memory usage'].cach"}
      - {name: mem_free, src: "['memory usage'].free"}
      - {name: load_1m, src: "['load avg'].1m"}
      - {name: load_5m, src: "['load avg'].5m"}
      - {name: load_15m, src: "['load avg'].15m"}
      - {name: net_recv, src: "['net/total'].recv"}
      - {name: net_send, src: "['net/total'].send"}
out:
  type: stdout

out の設定

outMySQLなので、MySQLでテーブルを作成して、設定したら終わりです。

github.com

以下が最終形態の config.yml です。

in:
  type: s3
  bucket: my-logs
  path_prefix: web/
  access_key_id: XXXXXXXXXXXXXXXXXXXXXX
  secret_access_key: XXXXXXXXXXXXXXXXXXXXXX
  decoders:
  - {type: gzip}
  parser:
    type: fluent-s3-log
    columns:
      - {name: hostname, type: string}
      - {name: dstat, type: string}
filters:
  - type: expand_json
    json_column_name: dstat
    root: "$."
    expanded_columns:
      - {name: "['total cpu usage'].usr", type: double}
      - {name: "['total cpu usage'].sys", type: double}
      - {name: "['total cpu usage'].idl", type: double}
      - {name: "['total cpu usage'].wai", type: double}
      - {name: "['total cpu usage'].hiq", type: double}
      - {name: "['total cpu usage'].siq", type: double}
      - {name: "['memory usage'].used", type: double}
      - {name: "['memory usage'].buff", type: double}
      - {name: "['memory usage'].cach", type: double}
      - {name: "['memory usage'].free", type: double}
      - {name: "['load avg'].1m", type: double}
      - {name: "['load avg'].5m", type: double}
      - {name: "['load avg'].15m", type: double}
      - {name: "['net/total'].recv", type: double}
      - {name: "['net/total'].send", type: double}
  - type: column
    columns:
      - {name: time}
      - {name: key}
      - {name: hostname}
      - {name: cpu_usr, src: "['total cpu usage'].usr"}
      - {name: cpu_sys, src: "['total cpu usage'].sys"}
      - {name: cpu_idl, src: "['total cpu usage'].idl"}
      - {name: cpu_wai, src: "['total cpu usage'].wai"}
      - {name: cpu_hiq, src: "['total cpu usage'].hiq"}
      - {name: cpu_siq, src: "['total cpu usage'].siq"}
      - {name: mem_used, src: "['memory usage'].used"}
      - {name: mem_buff, src: "['memory usage'].buff"}
      - {name: mem_cach, src: "['memory usage'].cach"}
      - {name: mem_free, src: "['memory usage'].free"}
      - {name: load_1m, src: "['load avg'].1m"}
      - {name: load_5m, src: "['load avg'].5m"}
      - {name: load_15m, src: "['load avg'].15m"}
      - {name: net_recv, src: "['net/total'].recv"}
      - {name: net_send, src: "['net/total'].send"}
out:
  type: mysql
  host: localhost
  user: username
  password: userpassword
  database: my_s3_logs
  table: dstat
  mode: insert

実行

$ embulk run config.yml

約80万レコードをS3からの取得から開始して数十分でMySQLに投入できました。これは早い(自分で書かなくてよかった。。。)。

Embulk便利!!

プラグインも今あるもので大抵のことは実現出来そうです。

これからは積極的使っていこうと思います。