go」カテゴリーアーカイブ

Linuxのユーザー管理基盤ミドルウェアのSTNSで公開鍵認証を実装できるライブラリを公開しました

STNSとは

ざっくりSTNSを利用するとこれまでLDAPなどで管理されてきたLinuxのユーザーをTOMLフォーマットの設定ファイルやDynamoDB、S3で管理できるようになります。またSSHログインの公開鍵認証などをかんたんに実現できるのがSTNSの優位性でした。

libstns-go

今回そのSTNSを利用して、下記のライブラリを活用するとあらゆるWEBサービスやCLIツールで公開鍵認証が可能になります。

サンプルコードとしては下記のようなものになります。

package main

import (
	"fmt"

	"github.com/STNS/libstns-go/libstns"
	"github.com/k0kubun/pp"
)

func main() {
	stns, err := libstns.NewSTNS("https://stns.lolipop.io/v1/", nil)
	if err != nil {
		panic(err)
	}

	user, err := stns.GetUserByName("pyama")
	if err != nil {
		panic(err)
	}
	pp.Println(user)
        // id_rsaキーで署名する
	signature, err := stns.Sign([]byte("secret test"))
	if err != nil {
		panic(err)
	}

	// pyamaの公開鍵で署名検証できた
	fmt.Println(stns.VerifyWithUser("pyama", []byte("secret test"), signature))

	// 文字列が異なるので署名検証できない
	fmt.Println(stns.VerifyWithUser("pyama", []byte("make error"), signature))

}

実装としては任意の値を ~/.ssh/id_rsa などの秘密鍵で署名し、その署名をSTNSで公開されている公開鍵で検証し、検証成功すればそれはSTNSに登録されたユーザーであると認証することができます。

勘の良い方は気づいたかと思いますが、SSHのログイン時に利用されている公開鍵認証相当のことをあらゆるシステムで可能にしたというのが今回の実装です。

ユースケース

まず公開鍵認証を利用することで一般的なID/Password認証よりも比較的暗号強度が高い認証を行うことができます。さらには多要素認証の一つとして活用いただくのも良いと思います。

あとは社内で利用するツールを開発するときに、OAuth認証は容易に利用できるので僕も重宝しているのですが、CLIツールから利用するとなると非常に手間がかかります。そういった場合に、この仕組を利用するとCLIで完結するかつ、強力な認証を利用できます。

最後に

今回実装してみて思ったのですが世の中意外とそんなに公開鍵認証って実装されてるものって少ないなぁと思ったりもしました。今回の実装の追加でSTNSを利用したおもしろ隙間ソフトウェアとかもできるようになると思うので、是非活用いただいて、ブログなど書いていただけると大変に嬉しく思います。

ちょっとかんたんに試してみたいのだけど!という方がいらっしゃいましたら、STNS as a Serviceもあるのでお試しください。

VaultからTLS認証に利用する情報を安全に取得するKagianaというソフトウェアを書いた

ペパボでは新型コロナウィルスをきっかけに、全社でのリモートワークを前提とした勤務体制を取っています。それにあたり、いろいろな工夫をしているわけですが、開発者が利用するイントラAPIなどを安全に利用するために、VPNを利用するケースは非常に多いと思います。しかし、VPNについてはネットワークによる認可の細かい制御が難しく、基本的にVPNのエンドポイントにつなげると何でもできてしまうという状態になってしまいがちです。そういった課題を解決するために、僕が社内に提供するサービスはなるべくTLSクライアント認証を利用して、ネットワークの認証を行ったあとに、更にアプリケーションで認証・認可を行うようにしています。

またTLSクライアント認証に利用するクライアント証明書、鍵のセットはHashicorp VaultのPKI基盤を利用して払い出しており、こちらもGHEのOrganization/Repository/Teamの情報とリンクする形式で認可を行っています。

一方でHashicorp VaultはAPIを実行したり、クライアントCLIを利用したり、または管理者向けのWEBUIはあれど、この手のミドルウェアを利用したことがないエンジニアに、ここから認証後に鍵取得して、あとはよろーというには少々厳しい現実があります。

それらを解決するために開発したのがKagianaです。

Kagiana自体はGitHub(厳密にはGitHub Enterpriseを利用しています) の提供するOAuth認証を行ったあとに、Kagianaで指定したPKIエンドポイントから証明書と鍵のダウンロードを行います。イメージとしては下記のとおりです。要するにKagianaの設定ファイルで指定してある証明書、鍵をWEBUIで認証し、払い出すことができる仕組みです。

ペパボではKagianaで認証後に、Vaultに接続が可能な証明書、鍵をダウンロードすることで、利用者をVaultに接続可能にします。その後、Vaultで認証・認可を行うことで、そのユーザーが利用可能な証明書と鍵がダウンロードできるようになります。

更に、上記図の5にあたる証明書の管理については consul-template を手元の端末で起動できるスクリプトをLinux、MacOSの環境それぞれ準備しており、インターナルなKagianaの管理リポジトリで、 make install と実行するだけで、利用者の手元で consul-template が起動し、自動で証明書の取得、更新管理を行ってくれます。配布している consul-temlate の設定は下記のようなものです。

max_stale = "10m"
log_level = "info"
pid_file = "/tmp/consul-template.pid"

vault {
  address = "https://vault.example.com"
  renew_token = true

  ssl {
    enabled = true
    verify = true
    ca_path = "~/.kagiana/vault.example.com.ca"
    cert = "~/.kagiana/vault.example.com.cert"
    key = "~/.kagiana/vault.example.com.key"
  }
}

template {
contents    = "{{ with secret "example.com/issue/example.com" "common_name=vault.example.com" }}{{ .Data.issuing_ca }}{{ end }}"
  destination = "~/.kagiana/vault.example.com.ca"
}

template {
  contents    = "{{ with secret "example.com/issue/example.com" "common_name=vault.example.com" }}{{ .Data.certificate }}{{ end }}"
  destination = "~/.kagiana/vault.example.com.cert"
}

template {
  contents    = "{{ with secret "example.com/issue/example.com" "common_name=vault.example.com" }}{{ .Data.private_key }}{{ end }}"
  destination = "~/.kagiana/vault.example.com.key"
}

このような設定ファイルを定義して起動すると、Vaultから取得した証明書の有効期限を consul-template が監視し、自動で証明書を失効前に更新してくれます。さらには必要に応じて、更新したタイミングで何かしらのコマンドを実行することができます。例えばWEBサーバであれば証明書を更新したあとにWEBサーバをリロードするというような定義をすることはよくあります。

ちなみに、なぜ consul-template を利用するのかというと、これらの仕組みで提供する証明書の有効期限を1日程度と非常に短い値にすることで、主に退職時などにおける鍵の失効管理をなくしたいという狙いがあるためです。実態としてはVaultでアカウントが認証できなくなることで鍵の更新ができなくなり、自動的に失効するような運用を行っています。一方であまりに短い期間で証明書を手動で更新するのは非常に手間なので、 consul-template で更新を自動化しているということです。更にこの仕組を利用すればリポジトリにおいてある、 consul-template の設定ファイルを更新、配布するだけで全開発者に利用を許可する証明書を一括で配布することができるので管理者としても非常に運用が楽です。

ここで一つ与太話なのですが、Linux、MacOSの各環境の consul-template の起動スクリプト生成に Foreman を利用しています。僕も今回知ったのですが、 Foreman には export というサブコマンドがあり、下記のようなProcfileをまず定義します。

consul-template: /usr/bin/env PATH=$PATH:/usr/local/bin consul-template -config=./consul-template/my-config.hcl

そして foreman export systemd <dest> <option> -f myProcfile と実行するとsystemd の起動スクリプトを出力してくれます。そしてこれが launchd などにも対応しているので、各環境の起動スクリプトのグルー技術として非常に便利でした。下記のリンクにある通り、いろいろな出力が可能なので知っておくと、今後Linuxデスクトップも増えてくると思うので便利だと思います。

最後に

今回はTLSクライアント認証に利用する証明書、鍵を何かしらの認証後に取得し、その証明書を利用して必要な認証、認可をしていくというようなまさに鍵穴となるようなソフトウェアを開発しました。おそらくこの仕組はしばらくペパボ内でレバレッジを効かせながら使われていくソフトウェアになると思うので継続開発していく予定です。同じような課題をお持ちの方、認証方法の追加のご希望などあればIssueを書いていただければ諸々検討するので、ぜひお気軽にご連絡ください。

カテゴリー: go

k8sでクラスタの誤操作を防ぐラッパーをリリースしました(Check the cluster with kubectl)

解決したい課題

僕は現在GMOペパボの技術基盤チームに所属しており、ペパボの商材であるロリポップやColormeなどで動作する基盤の開発に携わっています。昨今はもっぱらk8sを触っているのですが、その際に起こりうるオペミスは意図していないClusterやNamespaceへの操作が考えられます。下記のようなオペミスをして、意図しないNamespaceのポッドを全消ししたなんて経験、皆さんありますよね????

$ kubectl delete pod --all   

これを解決するために、kubectl コマンドのラッパー(1.12移行ではプラグインとしても機能)を開発しました。

使い方

僕は下記のように利用しています。

まずコマンドが長いのでaliasに登録します。これは実際には .zshrc に書いています。

$ alias kc='kubectl-cluster-caution'

次に、作業するリポジトリで下記を実行してください。これを実行すると、現在のk8sコンテキスト、Namespaceをリポジトリトップの .kubectl-cluster-caution ファイルに出力します。このファイルを元に、現在のコンテキストを突合します。

$ kc --add-context

この操作のあとに例えばクラスタを変更して、k8sコマンドを実行してみます。

$ kc config use-context other.example.com
Switched to context "other.example.com".
$ kc get pod
Repository configuration is different from cluster or namespace.
Do you want to continue?(Y/n) (yes/no) [yes]: 

このように定義された情報と異なる場合は、yes/noの確認が入るので、一回気づくチャンスが増えます。

導入方法

brewを提供しています。

$ brew tap pyama86/ptools   
$ brew install cluster-caution

最後に

今後、k8sの導入が進んでくると、僕のような働き方じゃなくても、起こり得るオペミスだと思うのでぜひ導入していただき、皆さんの運用に貢献できたら大変嬉しいです。

カテゴリー: go

Apache httpdのコンフィグをパースしてJSONにする

昨夜書くぞーという気持ちが急に生まれて、久しぶりに6時間くらいぶっ通しで書いた。

解決したかった課題

筆者に所属するGMOペパボでは数百万サイトをホスティングしており、それらの配信はhttpdにより行われています。httpdの設定は設定をAPI経由で配布されるデーモンによって行われておりますが、古いアーキテクチャに起因し、一部の情報が配布した設定ファイルにしかないという課題がありました。例えばある設定をAPI経由でサーバに設定したが、どの値が設定されているかはDBなどに情報はなく、設定ファイルをみるしかないという感じです。

そういった状況だと普段運用している場合にはほとんど困ることがないのですが、例えばスケジューラーによって動的に配信ホストを変えたりだとか、FUSEを利用して設定ファイルをプラガブルに配信しようと思った場合に、設定ファイルの取り回しがどうしてもネックになってしまいます。

FUSEについては同僚のインフラ・ジーニアス @harasouの書いたブログがロックです。

Panalysisが提供すること

Panalysisではhttpdのコンフィグを読み込み、JSONに構造化することと、JSONからhttpdのコンフィグを生成することをサポートしています。なぜ双方向をサポートしているかというと、Panalysis自体の動作正常性を見る場合に、既存のコンフィグとJSONの一致化検証をするに当たり、差分を見たかったからです。

要はこういうことがやりたい

# configからjson作る
$ cat misc/sample.conf | pkg/darwin_amd64/panalysis -c | jq > /tmp/conf_to_json;
# jsonからconfig作る
$ cat /tmp/conf_to_json | pkg/darwin_amd64/panalysis -j > /tmp/json_to_conf;
# コメントと空白を無視してDIFFとる
$ diff -I '^\s*\#' -Bw misc/sample.conf /tmp/json_to_conf;
194,195d67
<       #PHPIniDir "C:/Windows"
< 

Panalysisを使って今後、何をやるか

まずは既存のhttpdの設定ファイルをすべてDBに吸い上げ、FUSEで管理できるようにします。その後は、アーキテクチャを組み換え、動的な配信サーバのスケジューリングや、 止まらない超高集積WEBサーバを実現していきたいと思っています。僕と一緒に要素技術を生み出しながら世の中にないアーキテクチャを作ること、コードの力で運用を変えつつ、その先の世界を変える仕事を一緒にやりたい仲間を募集しています。

カテゴリー: go

Linuxユーザーと公開鍵を統合管理するサーバ&クライアントのSTNSを書き直した

この度、Linuxユーザーと公開鍵を統合管理するサーバ&クライアントのSTNSをすべて書き直し、v2をリリースしたので、お知らせします。

v1リリース時の記事は下記にあります。

はじめに

STNS v2のリリースに当たり、改めてLinuxの名前解決について紹介します。
Linuxはファイルのパーミッションやログイン権限の管理にユーザー名やグループ名が使われています。しかし、実際にパーミッションに利用されている値はID値であり、それらを名前と紐付けるためにいわゆる名前解決が必要です。

$ id example
uid=2000(example) gid=2000(example) groups=2000(example)

この例ですと、exampleユーザーはID 2000を持つユーザーであることがわかります。

これらの名前解決を行うにあたり、Linux標準の仕組みですとuseraddコマンドでユーザーを追加すると、通常は/etc/passwd/etc/group/etc/shadowファイルにユーザーやグループの情報が書き込まれるのですが、昨今、デザイナーによるデプロイのためのログインなど多くの職種がサーバにアクセスする機会が増えており、それらを手で管理したり、プロビジョニングツールで管理するのはあまりベターな仕組みとは言えません。また人材の流動性も上がっており、入退職もしやすくなったため、ワンストップでユーザー管理ができる必要があります。

このようなユーザー管理を行うにあたり、ある程度サービス規模が大きくなってくるとLDAPやDBを利用してユーザー管理を行うのが常であったと思いますが、LDAPやDBでの管理はWeb業界で好まれるコード管理やGitHub WorkFlowと必ずしも相性がよくありません。

それらを解決するために、筆者が開発したのがSimple Toml Name Serviceです。

v2のアーキテクチャはこちらの図のとおりです。

STNSを利用すると下記のようなシンプルな設定ファイルでユーザー・グループの統合管理を行うことができます。


[users.pyama]
id = 1001
group_id = 1001
directory = "/home/pyama"
shell = "/bin/bash"
keys = ["ssh xxxxxxx"]

[groups.pyama]
id = 1001
users = ["pyama"]

筆者の所属するGMOペパボでは、この設定ファイルをThorスクリプトを利用して、GitHubAPIから自動で生成し、ユーザー・グループ管理を行っています。この運用の便利なところはGitHub上のチームとサーバのアクセス権が一致させやすいことや、GitHubには公開鍵情報があり、そのまま利用できること、GitHubの情報をもとにしているので権限の剥奪漏れが少ないことです。

一方でファイル生成の方法はユースケースによって異なると思うので、この記事ではインストールから名前解決までを解説し、v2の変更点を紹介したいと思います。

各種ドキュメントは下記のサイトをご確認ください。

サーバのインストール

Rhel系、Debian系ともにパッケージを提供しています。この手順はUbuntu xenialで進めます。まずクライアントからのクエリに応答するサーバをインストールします。まずはパッケージリポジトリを追加します。


$ curl -fsSL https://repo.stns.jp/scripts/apt-repo.sh | sh

リポジトリをインストールしたらサーバをインストールします。


$ apt install -qqy stns-v2 

次に設定ファイルを編集し、このようにしてみました。

$ cat /etc/stns/server/stns.conf 
[users.example]
id = 1001
group_id = 1001
keys = ["ssh-rsa XXXXX…"]

[groups.example]
id = 1001
users = ["example"]

設定を編集したらrestartしてください。


$ service stns restart

curlを利用して動作確認しましょう。

$ curl -s http://localhost:1104/v1/users | jq
[
  {
    "id": 1001,
    "name": "example",
    "password": "",
    "group_id": 1001,
    "directory": "",
    "shell": "",
    "gecos": "",
    "keys": [
      "ssh-rsa XXXXX…"
    ]
  }
]

たったこれだけでサーバの構築は完了です。次にクライアントをインストールします。

クライアントのインストール

クライアントもサーバと同じくパッケージからインストール可能です。


$ apt install -qqy libnss-stns-v2

サーバと同じように設定ファイルを書きます。

$ cat /etc/stns/client/stns.conf 
api_endpoint     = "http://localhost:1104/v1/"

STNS自体の設定はこれで完了です。次に nsswitch.conf を下記のように編集し、Linuxの名前解決にSTNSを利用するようにします。下記のように passwd, group,shadowstnsを追加してください。

$ grep -e passwd -e shadow -e group /etc/nsswitch.conf 
passwd:         compat stns
group:          compat stns
shadow:         compat stns

ここまでの作業でSTNSで名前解決ができるようになります。

$ id example
uid=1001(example) gid=1001(example) groups=1001(example)

STNSを利用してSSHログインする

STNSはLinuxのユーザー、グループの名前解決だけではなく、SSHの公開鍵ログインもサポートしています。公開鍵ログインを行うには、 /etc/ssh/sshd_config に下記の設定を行います。

PubkeyAuthentication yes
AuthorizedKeysCommand /usr/lib/stns/stns-key-wrapper
AuthorizedKeysCommandUser root

次にsshdをrestartします。

$ service ssh restart

テストするために公開鍵をSTNSに設定します。

$ mkdir /home/example && chown example /home/example
$ su - example
example@localhost:~$ ssh-keygen   
...
example@localhost:~$ cat ~/.ssh/id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkokLd+sFWbKuqepOVNhStZA55MYQ+RiXiS9Ub1PhS5aPDh06MHf80oLg9mKoG883ZsjD7zgYKunUnafG3NdFqHHTgMLS3By0IPi3ZNKVZStvjvCEahqoAs7gUEzOuNqa9hW9f7f5BfausSSuJ1s00WglrPmCMLvfBf9Y54o3xZm+/Uw8ehFHi8s1x5p/7I25WlEwbkL24CmugvvKuWh/bIcK84xRJO43m2Q/u3IArQjOum8Xim+Psj1xDc6DkbToHmoVCnFDoyMUM+VjnLPYpE4gh/8DBFlzNnRi4FrahJMJGbG/wGP6uovbcbmoj46/nYXGMlzBvU3WnhZbOskf/ example@localhost

$ cat /etc/stns/server/stns.conf 
[users.example]
id = 1001
group_id = 1001
# ★作成した公開鍵を設定する
keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkokLd+sFWbKuqepOVNhStZA55MYQ+RiXiS9Ub1PhS5aPDh06MHf80oLg9mKoG883ZsjD7zgYKunUnafG3NdFqHHTgMLS3By0IPi3ZNKVZStvjvCEahqoAs7gUEzOuNqa9hW9f7f5BfausSSuJ1s00WglrPmCMLvfBf9Y54o3xZm+/Uw8ehFHi8s1x5p/7I25WlEwbkL24CmugvvKuWh/bIcK84xRJO43m2Q/u3IArQjOum8Xim+Psj1xDc6DkbToHmoVCnFDoyMUM+VjnLPYpE4gh/8DBFlzNnRi4FrahJMJGbG/wGP6uovbcbmoj46/nYXGMlzBvU3WnhZbOskf/"]

[groups.example]
id = 1001
users = ["example"]

$ service stns restart

ここまででsshログインが可能になるので試してみます。

$ ssh localhost -l example 
Last login: Sun Sep  9 02:11:43 2018 from ::1
example@localhost:~$ 

無事ログインできましたね。このように比較的少ない手番でLinuxの統合ユーザー管理が可能になるので、中小規模でお困りの方がいたらぜひ利用してみてください。またサーバサイドは普通のHTTPサーバなのでnginxやkeepalivedと組み合わせて冗長化することも可能ですし、Basic認証やToken認証に対応しているのでHTTPS化してセキュアな運用も可能です。

さて次にv1からの変更点をいくつか紹介します。

v1からの変更点

nscdが不要

従来のv1ではSTNS自体にキャッシュ機構がなかったため、nscdを利用したキャッシュが必要だったのですが、v2からは独自のキャッシュ機構が組み込まれているためnscdなしで高速動作が可能です。

インターフェースがRESTfulに

v1ではmapのようなデータ構造であったのですが、Arrayデータ構造へ変更し、よりサーバ側の開発が行いやすくなりました。またエンドポイントも全て見直しています。

before

{
  "pyama": {
    "id": 1001,
    "group_id": 1001,
    "directory": "/home/pyama",
    "shell": "/bin/bash",
    "gecos": "",
    "keys": [
      "ssh-rsa xxxx"
    ]
  }
}

afterr

[{
  "name": "pyama",
  "id": 1001,
  "group_id": 1001,
  "directory": "/home/pyama",
  "shell": "/bin/bash",
  "gecos": "",
  "keys": [
    "ssh-rsa xxxx"
  ]
}]

libnss-stnsがGolangからC言語へ

v1では全てGolangで開発されて追ったのですがv2からはクライアントサイドはC言語で開発されています。理由としてはv1はCGOを利用してクライアントサイドの処理を行っていたのですが、clone時のメモリの持ち回り方などが怪しく、SEGVの要因になっていたので、C言語で書き直しています。

コードの見通しが良くなった

もともとSTNSは筆者が2年ほど前にGolangの学習用途で開発したプロダクトだったのでどちらかというとバッドノウハウの塊のようなコードになっていました。それらを今の力ですべて書き直したので以前よりだいぶ見通しが良くなっています。

パラメーター項目の変更

  • api_end_point → api_endpoint

区切り文字の位置の変更及び、データが配列から、文字列へと変更されています。["http://xxxx"] → “http://xxxx”

v1からの移行パス

サーバ側はVirtualHostなりポートを変えるなりで起動すれば良さそうです。クライアント側は下記のように削除してから、インストールしてください。

$ apt remove libnss-stns
$ apt install libnss-stns-v2

最後に

公開から2年ほど経過し、GitHub Starは180を超え、商用サービスからお声がかかるほど成長した反面、コードベースがあまり良くなくメンテナンスするモチベーションが低くなっていtので、C言語の勉強を兼ねてフルスクラッチで書き直しました。サーバサイドもlabstack/echoを採用し、ミドルウェアを利用した拡張を行いやすくしています。

今後についてはSlackを利用した2FAや監査の仕組みなど機能拡張を進めていく予定です。まだ利用されてない方は利用していただいて、既に利用中の方はv2への移行を進めていただければ幸いです。

gliderlabs/sshを利用してSSHをプラガブルに操作する

今日帰る前に tsurubee3 と、SSHのコネクションアレコレできたら楽しそうじゃんみたいな話をしていて、gliderlabs/sshを利用したら割とシュッとできたので紹介します。

gliderlabs/sshはSSHの認証や各ステップをミドルウェアパターンで拡張できるSSHサーバのライブラリです。今回は、このライブラリを利用して、SSHのパスワード認証をコンテキストに保持して使いまわしたり、PTYの入力を標準出力にパイプする実装を書いてみました。

動作としてはこんな感じ。

サーバ側

% make server
==> Installing Dependencies
go get -u github.com/golang/dep/...
dep ensure
go run main.go

クライアント側

% ssh localhost -p2222
yamashitakazuhiko@localhost's password:

パスワードは testにしておるので、入力した段階で、contextに保存され、サーバ側で下記のように表示されます。

user password: test

あとは、クライアント側でタイプした内容が次々と標準出力に出る感じです。

user password: testpyama saikou

改行してないからあれな感じになっちゃいましたね・・・。

Golang、標準ライブラリやそれを利用したミドルウェア実装が豊富で、アイディアさえれば何でも出来ちゃう状況にあるので、こういうおもしろハックを突き詰めていくと事業を差別化する技術シュッと作れそうで便利な世の中になったなぁと思いました。

ただこの実装だとSCPがカバーできてないのと、SCPのGolang実装あまりなさそうだから、RFP読むなり、sshd読むなりして実装すると楽しそう。

カテゴリー: go

gothreeというサーバのログをS3にアップロードするコマンドを書いた

ペパボのサービスで利用しているサーバにおいて、ログの保存期間を長期化するに当たり、超長期の保管場所をS3に選定した。世の中ではSHELLスクリプト書いてアップロード処理が実装されている例が多そうなのだが、awscliに依存したくない気持ちがあったのでGolangで実装した。

gzipに自動で圧縮したりdateextを自動で付与したり、いい感じにアップロードされるようになっている。使用方法はAWSのIDなどを環境変数かオプションで渡してあげて、logrotateの設定のpostrotateに定義するだけで動く。

$ cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
  daily
  missingok
  rotate 52
  compress
  delaycompress
  notifempty
  create 640 nginx adm
  sharedscripts
  postrotate
    if [ -f /var/run/nginx.pid ]; then
            kill -USR1 `cat /var/run/nginx.pid`
    fi
    ★これだけでOK
    /bin/bash -c -l &quot;source /root/.aws &amp;&amp; /usr/local/bin/gothree $1&quot;
  endscript
}
$ cat /root/.aws
export AWS_ACCESS_KEY_ID=***********
export AWS_SECRET_ACCESS_KEY=***********
export AWS_REGION=your region
export AWS_BUCKET=your buket name

作成されるファイル名はこんな感じ。

昨今セキュリティの観点から保全範囲が広がってきていると思うので、ぜひ活用してみてください。

カテゴリー: go

Apache httpd + PHPな環境で閲覧できるファイルを調べるpmrというコマンドを開発した

このブログの対象読者

Apache httpd + PHPな環境で、本番運用をしているプログラマ、インフラエンジニア。

このブログが解決すること

Apache httpd + PHPな環境ではファイルの配置や、サーバの設定によっては、想定外のファイルにアクセスされることがあります。それは例えばpassword.conf のようなファイルが閲覧されたり、任意のphpファイルが意図しない実行をされたりすることにつながります。このブログでは、そういったアクセスが可能であるかどうかをチェックする手段を提供します。

pmr

pmrとは筆者が開発した、コマンドで、 Pyama Multi Request の略です。アーキテクチャとしては、標準入力から受けたファイルリスト + URLで並列的にアクセスを行い、レスポンスボディの10行とファイルの10行を比較し、一致している場合に、そのファイルが見えていると判定します。

例を示します。

$ wget https://pepabo.com
...
2018-02-24 10:35:47 (59.7 MB/s) - ‘index.html’ saved [33941]
$ echo index.html | pmr -url https://pepabo.com
INFO[0000] request: https://pepabo.com 200 OK           
INFO[0000] request: https://pepabo.com/index.html 200 OK 
WARN[0000] This file is published index.html          

↑の実行例では、 https://pepabo.com にアクセスし、index.html を保存後、pmr に対して、標準入力から index.htmlを渡して実行しています。 pmrhttps://pepabo.com/index.html のように連結したURLでhttpアクセスを行い、レスポンスボディを比較した結果、内容が一致しているので、 WARN[0000] This file is published index.html のようにwarning表示を行っています。

また、応答が HTTP StatusOK(200) ではない場合もwarning表示が行われるので、意図しないphpにアクセスされて、 HTTP IntrenalServerError(500)などの場合も検知することが可能です。

pmrの具体的な使い方

pmrを実行するのは、WEBサービスのリポジトリのDocumentRootに相当するディレクトリで下記のように実行することを想定しています。

$ pwd
/path/to/service/public
$ find ./ -type f |grep -v -e '\.gif|\.png|\.jpg|\.js|\.css|\.html|\.ico|\.svg|\.pdf' | \
pmr -u https://example.com  2>&1 | grep WARN
# passwordっぽいのが見えっちゃってる
WARN[0000] This file is published password.html  

このように pmr では標準出力からファイルリストをもらうことで、柔軟なアクセスを実現しています。例えば、本番サーバのDocumentRootからファイルリストを持ってくることで、ログファイルなどの本番サーバにしか無いファイルも調査することが可能になるでしょう。

pmr

最後に

昨今のWEBシステムにおいては、WEBアプリケーションフレームワークに則って開発すると、意図しないファイルの公開は限りなく少なくなると思いますが、黎明期の手違いや、複数人で開発している状況だと、意図しないファイルの公開が行われているかもしれません。そういったことを自動でチェックする仕組みとして pmr を是非活用いただければ嬉しいです。

カテゴリー: go

スクリーンショットをgithub.comやGHEで管理する

このブログの対象読者

Gyazoがセキュリティポリシー上利用できず、また社内Gyazoなども何らかの理由で利用できないような環境で、GitのWikiや、ソースのREADMEなどに画像を利用するときに、一度issueに画像を貼り付けたりしたりするのがうんざりな人。

このブログの内容で何ができるようになるか

スクリーンショットの画像がgithub.com、GHEの指定したリポジトリに自動でアップロードされ、クリップボードに所謂マークダウンの画像表示記法 ![](画像URL) がコピーされるようになります。副次的に過去のスクリーンショットが自動でGit管理されるようになるので、過去に撮ったデータを探すのが便利になったりします。

手順

nrmをダウンロードする

github.comのreleaseから最新のバイナリをダウンロードして、 /usr/local/bin/ あたりにおいてください。

$ wget https://github.com/pyama86/nrm/releases/download/v0.1.0-d21aa37/darwin_amd64.zip
$ unzip darwin_amd64.zip
$ mv nrm /usr/local/bin

Automatorでシェルスクリプトとしてサービス登録する

Automatorを利用すると、シェルスクリプトをサービスとして登録したり、色々なワークフローを定義することが出来ます。

automator

まずサービスとしてnrmを登録します。

service

シェルスクリプトを実行を検索して選択

shell

サービスは次の項目を受け取ります の項目は 入力なし であとは下記の感じで入力します。
もしGHEを利用する場合は、トークンに加えて GITHUB_API_ENDPOINT も指定してください。また環境変数を .zshenv などで指定している場合は、ここでの指定は不要です。各項目の意味は -o はリポジトリのオーナー、 -r はリポジトリを指定しています。この場合、僕のgithub.comアカウントのcaptureというリポジトリに画像データが送信されます。

zsh
GITHUB_API_ENDPOINT=https://your.ghe.local/api/v3/ 
GITHUB_AUTH_TOKEN=xxxxxxxxxxxxxxxx
/usr/local/bin/nrm -o <organization> -r <repository>

入力ができたら nrm_shell として登録してください。

nrm_shell

キーボードショートカットを設定する

登録したnrm_shellはショートカットを定義することでどこからでも呼び出すことが出来ます。

[システム環境設定]->[キーボード]->[ショートカット]->[サービス]

下までスクロールすると nrm_shell があるので、ショートカットを割り当ててください。
僕の場合は Command + Alt + n で起動するようにしています。

alt

ここまでの作業が終わると、ショートカットを入力するだけで、いつでもスクリーンショットがgithub.comに送信されるようになります。

まとめ

それなりに限られた環境で力を発揮する nrm コマンドですが、このブログを書くにあたり自分で利用してみたところ、めちゃくちゃ便利だったので、ブロガーの方々も活用いただけるかもしれません。

P.S.
nrmコマンドの由来は、雰囲気で同僚の名字から取りました。

カテゴリー: go

NetAPPのquotaを自動で適用するデーモンを開発した@福岡Ruby会議

皆さん!福岡Ruby会議02楽しんでますか!?
僕はというもののCFP期限を逃し、全く何も生み出していないので、セッションの合間にデーモンを書いていました。

NetAPPではqtreeやVolumeに対してファイル数やディスク容量のクォータを設定することが出来ますが、クォータを設定するだけでは有効にならないので、一度クォータをVolume単位でOFF,ONして上げる必要があります。

今回開発したデーモンを利用すると、指定秒数間隔でクォータをOFFし、指定秒数間隔でクォータがONされます。それぞれの処理はgoroutineで並列実行されるため、300秒間隔で、OFFし、10秒間隔でONするようにすると実質5分毎にクォータが有効になるような実装です。

例外時は停止するよりも通知だけ行い、処理は継続すべきと考え、エラーログを出力し、Slack通知だけして処理は継続する設計です。実運用時はsystemdなどと組み合わせて利用するのがよいと思います。

このあとは @k1LoW さんと @matsumotory の発表が被って悩ましい時間になってきましたが引き続きRuby会議やっていきましょう!
MockMock!!!1

カテゴリー: go