おっさんのダベリ

IT系の技術的な話題が多いです

LineNotify APIでsshログイン通知をする

以前、sshでログインしたことをSlackに通知するというのを書いたことがあります。

yhanada.hatenablog.com

ちょっと前にLineNotify APIがリリースされたので、こちらでも使って見るようにしてみました。 基本的な設定方法は↑のエントリと同じですが、スクリプトなどの内容だけをこのエントリでは書いておきますね。

ファイル構成

/etc/pam.d/sshdにて実行するファイルのパスなどを設定しておけば、そのスクリプトが実行されますが、今回はスクリプトとLineNotify API実行時にヘッダに設定するToken情報を記述するファイルを分けてみました。

/etc/line
├── line-token
└── login-notify.sh

tokenは、https://notify-bot.line.me/my/で発行できます。

login-notify.shの内容は次のようにしました。

#!/bin/sh

script_dir=`dirname "${0}"`
endpoint=https://notify-api.line.me/api/notify

if [[ $PAM_TYPE != "close_session" ]] ; then
  host=$(hostname)
  message="SSH Login: ${PAM_USER} from ${PAM_RHOST} on ${host}"
  token=$(cat "${script_dir}/line-token") 
  header="Authorization: Bearer ${token}"
  curl -X POST -H "${header}" -F "message=${message}" ${endpoint}
fi

Lineに送信する内容を変えたい場合には、messageを指定している部分を適宜変更してください。

基本的には上記の内容で動作するはずですが、うまく動かない場合にはcurlの出力をファイルに書き出すと内容が確認できます。

例えば、-o /tmp/res などを追加するとAPIを呼び出したレスポンスを確認できます。

参考

プログラムを教えることになる

ご無沙汰しております

ここ1ヶ月位、息子たちにプログラムを教えていました。

長男は大学の課題でC言語を 次男は高校のパソコン部の成果発表のためにUnity(C#)を

C言語

テキストは明解C言語とか。懐かしい。 https://www.amazon.co.jp/dp/479737702X

学校の環境は秀丸Cygwinだそうです。先生の苦労が忍ばれますが、今ならDockerにUbuntuとかでいいんじゃないですかね。 あるいはWindows10でbashを動かすとか。 どちらも最近の環境が必要そうですから無理なのでしょうけど。

Unity

実はまともにUnityを触ったのは今回が初めてで、親も一緒に勉強しました。 GameObjectは次男が予め作っておいていたので、それに動きをつけることを一緒に学びました。

私は普段はゲームをしないので、次々に湧いて出てくるゲーム改善の勢いに飲まれつつ、いろいろな機能を実装しました。 スマホゲームのサーバサイドは仕事で作ったことがありますが、UIとなると細かいタイミングなどの調整が大変なのですね。

これからUnityについても書こうかな。

APIのmockダミーサーバ

ユニットテストで外部のAPIを呼び出す場合などで実際にそれを呼び出しづらいことってありますよね。

また、クライアントを開発するときにサーバサイドのAPIが仕様は決まっているけどまだできていないとか困りますよね。

そういう経験が最近よくあったので、常にそれっぽいAPIのレスポンス(JSON)を返すダミーのサーバをGoで作ってみました。

https://github.com/yhanada/apimock

いままではGoのサンプルを動かす程度だったのですが、そろそろサンプル以外も作ってみようかな、と。

実行はシンプルで、Goのアプリに対してDocumentRootとなるディレクトリを指定して起動します。

$ apimock-go -root ./root

たとえば、DocumentRootで指定したrootディレクトリの構成が次のようになっているとします。

root
├── actions.json
└── v1
    └── users
        ├── 1
        │   └── actions.json
        └── actions.json

レスポンスデータは各ディレクトリの下に置かれたactions.jsonに記述しています。

例えば、/v1/users/1に対するリクエストを定義しているactions.jsonの中身は次のようになります。

{
    "GET": {
        "body": {
            "id": 1,
            "name": "Taro Yamada"
        }
    },
    "POST": {
        "status": 405
    },
    "PUT": {
        "body": {
            "id": 1,
            "name": "Taro Takahashi"
        }
    },
    "DELETE": {
        "body": {
            "result": "deleted",
            "id": 1
        }
    }
}

各HTTPメソッドの下に、statusbodyがあるだけです。 statusにはHTTPのステータスコードを記述します。上記の例だとPOSTメソッドが呼び出された場合には405であるMethod Not Allowedを返します。 HTTPメソッドが定義されていない場合には、404を返します。

上記例では、GETにはstatusが定義されていませんが、その場合には200を返します。 そしてbody以下に定義されたJSONがそのままレスポンスとして返されるようになっています。

今後はKotlinを使ったAndroidアプリも作っていこうと考えてます。そのときに使えればいいな。

Slack botを試す

そろそろ次へ

SlackのSlashコマンドで遊ぶのもそろそろ飽きてきたので、次にSlackのBotに挑戦してみたいと思います。

Slack Botで何をやりたいか

個人サーバ環境をIDCFクラウドに移行し、何かあった場合の通知はMackerelからSlackに送るように設定しています。

そこで何かあった時にSlackで(slash commandでもいいけど)対応したいな、と。

まずはお試し

goslackでもお世話になった、kyokomiさんのslackbot https://github.com/kyokomi/slackbot を使ってみました。

ここのexampleをそのままコピーして動かしてみました。 これは、SlackのReal Time Messaging APIを利用した仕組みで、サーバに(GAEも)デプロイしなくても、ローカルからでも動作確認が出来ます。

SlackのBotsを追加して、SLACK_BOT_TOKENを取得したらすぐに動かせます。

動かしてみて

家庭内LANからのためか、SlackでBotに対してコマンドを入力してからレスポンスまでが非常に時間がかかっています。 また、一度失敗すると次からは何を送っても、失敗前の最後のメッセージを返していました。

まぁこのあたりぼちぼち見ていくかな。

試したコードはこのへんに置いときます。

GAEで環境変数から値を取得する

やりたいこと

GAE上に構築したslack slash commandで、Slackから送られるtokenをチェックしたい。

どうやったか

やり方はいろいろとあると思いますが、今回は環境変数に設定したtokenを取得し、Slackから送られたtokenと比較するようにしました。

GAEの環境変数の設定方法

まずは、app.yamlに記述する方法です。

参考:app.yaml Reference  |  Go  |  Google Cloud Platform

app.yamlenv_variablesという項目を追加し、名前: 値という形式で記述します。

以下の例では、SLACK_TOKENという変数名に、slack_tokenという値を設定しています。

env_variables:
  SLACK_TOKEN: 'slack_token'

tokenは秘密にするべき値なので、リポジトリに残しておきたくない場合にはdeploy時にオプションとして指定する方法もあります。

参考:appcfg.py Command Line Arguments  |  Go  |  Google Cloud Platform

$ appcfg.py update . -E SLACK_TOKEN:"slack_token"

コードからtokenを取得

次に、コードから環境変数に設定されたSLACK_TOKENを読み出します。

os.Getenv("変数名")で取得できます。

後は、この取得したtokenをSlackから送られたtokenと比較し、異なっていればエラーレスポンスを返すだけです。

import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"

    // 省略
)

func init() {
    renderer := render.New(render.Options{})
    token := os.Getenv("SLACK_TOKEN")

    // 省略
}

詳細はこちら https://github.com/yhanada/goslash-sample-gae

GoのアプリをDockerコンテナ上でビルドする

やりたいこと

開発はMac上で行うが、サーバはLinuxなので(予定)Linux用のビルドを行いたい。 どうせならば、Docker使いたい

どうやったか

リポジトリをcloneしたプロジェクト直下のディレクトリで以下のコマンドを実行。

$ docker run -it --rm -v $PWD:/go/src/app golang:onbuild /bin/bash -c "make clean && make build"

-vオプションで、プロジェクトのディレクトリをDockerコンテナの/go/src/appにマウントしています。

そして、bashのコマンドとしてmakeを実行しています。

あら簡単

注意:Dockerのイメージ(golang:onbuild)のダウンロードに少々お時間がかかります

goslashを自前のサーバで動かしてみた

net/httpに対応させる

これまではgoslashをGAEで動かしてみましたが、goslash自体はGAEに依存しているわけではありません。

net/httpのResponseWriterやRequest、そしてClientがあれば他の環境でも動かすことが出来ます。 ということで、通常のhttpサーバとして動くようにするにはちょっとした修正で終わりました。

今回対応させたコードはこちらにあります。

GitHub - yhanada/goslash-sample-server

コマンドラインの処理について

AppEngineでは自分がローカルで起動しているかどうかを判定するためのメソッドがありましたが、自前実装ではコマンドラインオプションとして起動時に渡すようにしたいと思いました。

今回、いろいろと調べてみて「へ〜」となったのが、コマンドライン処理でUsageを表示させる方法です。

コマンドラインオプションの処理をしているところを抜粋すると以下のようになります。

ここで、flag.Usageに対してfuncを渡しているところがあるのですが、これだけで-hを指定した場合にこのfuncが実行されるようになります。

そして、flag.PrintDefaults()で、flagに定義したオプションを表示させることも出来ます。これを呼び出すようにしておけばオプションの名前を変更したとか、削除したとかをUsageに反映忘れとかがなくなって便利ですね。

var (
    port    uint
    timeout Time.Duration
    isDev   bool
)

func main() {
    flag.UintVar(&port, "port", 8080, "server port")
    flag.BoolVar(&isDev, "dev", false, "is development mode")
    flag.DurationVar(&timeout, "timeout", 10*Time.Second, "http client timeout")
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "Usage of %s:\n  %s [OPTIONS]\n", os.Args[0], os.Args[0])
        flag.PrintDefaults()
    }
    flag.Parse()