長野高専 Advent Calendar 2021 22日目の記事になります.皆さんすごい記事書いてる...
この記事では,Discord上で動くLinuxターミナル...風のbotを作ってみました.
こんな感じのターミナルを作りました.
任意コマンド実行を他人に許可することにより,予期せぬ問題が起こる可能性があります.使う際は必ず信頼できるメンバー間でのみにしましょう.
(画像にあるF-langについては以下)
きっかけ
黒板があれば学生が数式を落書きのように書いていく...ああいうノリのTerminal版がほしいなと思ったので作りました.
弊クラスにはDiscord鯖があるので,そこに#Terminalチャンネルを作って遊んでもらうことにしました.
仕組み
せっかくならroot権限を付与して徹底的に破壊してもらいたいものです.ですが,それだけで毎回鯖を復旧させるのは大変なので,Dockerでイメージ,そこからコンテナを作り,必要であればそれを壊してイメージからコンテナを再構築することで,sudo rm /* -rf
1を実行されてもすぐに復活する環境を用意します.
実装
作りましょう.
コンテナづくり
早速コンテナを作りましょう.とりあえず自分が一番慣れているUbuntuで作ります.
SSH導入
Discord Botとのコマンドの受け渡しはSSHで行います.
ユーザー名
コンテナ内のユーザー名は,DiscordのIDに準ずるものにします.ただし,人によってはLinuxのユーザー名に使えない記号を使っていたり,日本語名になっている人もいます.そういう人のためにも,どんな名前が来てもできるような仕組みを作ります.
✝ほねつきにく✝
の場合
- ローマ字に変換(✝honetsukiniku✝)
-
[a-z0-9_]
以外を消す(honetsukiniku)
LANへのアクセスを無効化
宅鯖などでこいつを運用する場合,家にいる人は当然家のLANにアクセスできることになります.流石にそれは困るので,iptablesで無効化させます.
DockerコンテナのネットワークルールはDOCKER-USER
チェインをiptablesにより設定することで反映させられます2.(わりと最低限しか設定していないため,もしかしたら漏れがあるかも)
優先度 | 対象 | 理由 |
---|---|---|
1 | デフォルトゲートウェイのTCP/80を却下 | デフォルトゲートウェイの端末設定を開けなくするため |
2 | デフォルトゲートウェイを許可(192.168.1.1) | ここを許可しないとグローバルに出れないため |
3 | LAN全体をブロック(192.168.1.0/24) | LAN内の別の端末のアクセスを防ぐため |
sudo iptables -I DOCKER-USER -s 192.168.1.0/24 -j DROP
sudo iptables -I DOCKER-USER -s 192.168.1.1 -j ACCEPT
sudo iptables -I DOCKER-USER -s 192.168.1.1 -p tcp --dport 80 -j DROP
コンテナに対応する設定ファイル
config.json
に定義できるようにすることで,とりあえずsshが通るコンテナであれば使えるようにします.
{
"containers": Array<{
"name": string,
"port": number,
"image": string,
"dockerConfig": Object<dockerConfig>, //The option to create container. See https://docs.docker.com/engine/api/v1.37/#operation/ContainerCreate
"discordIcon": string //Example: "<:windows:762682464966803476>"
"authMethod": {
"type": string,
"user": string,
"password": string
}
}>
}
リセットボタン
Discordに便利な機能が追加されました3.これでリアクションで頑張る必要はなさそうですね.
クリックされた後はコンテナの停止,削除,イメージからのコンテナ再作成を行います.
ドラッグ&ドロップでアップロード
送信されたファイルをコンテナにSCPで転送します.
送信されたファイルをBufferで格納したら,node-scp2のLow Level APIであるwriteを使って,そのBufferを直接転送します.
ファイルのダウンロード
コンテナ内のファイルをDiscord側にSCPで転送します.
アップロードと同じく,SCPでBufferを受け取ってそのままDiscordに転送しようと思ったのですが,どうやらnode-scp2には直接Bufferで受け取るメソッドがない4ようなので,Low Level APIであるreadを追加したnode-scp2パッケージを作成しました5.
"dependencies": {
- "scp2": "^0.5.0",
+ "scp2": "github:BonyChops/node-scp2",
}
Workflow
任意チャンネルで%ワークフロー名 引数...
を打つと発動できるWorkflowという機能もつけてみました.これで会話も盛り上がること間違いなし!
設定はこんな感じにします
使える引数は以下です
引数 | 説明 |
---|---|
$n | 指定された文字列引数(n=1,2,3,...) |
$FILEn | アップロードされたファイル(のパス)(コードブロックも含む)(n=1,2,3,...) |
また,ファイルを/home/workflow/files/ に出力すると,出力結果として返ってきます |
例: Gnuplot
PLOTCONFIG=$FILE2
if [ -z $FILE1 ]; then
echo "プロットデータが必要です"
exit
fi
if [ -z "$PLOTCONFIG" ]; then
PLOTCONFIG=config.gnuplot
echo "set terminal png\nset output \"/home/workflow/files/out.png\"\nplot inputFile with lines notitle" > config.gnuplot
fi
gnuplot -e "inputFile='$FILE1'" "$PLOTCONFIG"
実際の評判
クラス内で運用してしばらく経ちますが,その際のユースケースを紹介します
今後やりたいこと
ちょっと忙しいのでできる見通しがたっていませんが,やりたいことがあります.
-
表示結果を見やすくする
textimgを使って出力を画像として出すことにより,より見やすい結果を表示したいなと思っています.画像はとりあえずお試しでやってみたものなのでバグり散らかしてます6.
7 -
OS(コンテナ)切り替え
Twitterの方ではできるようになりました!!とか言ってましたが,まだ完璧ではないです...安定してできるようにしたいですね
-
Readme.mdの充実化
面倒臭がって適当なことしか書いてないので,いつかしっかりとした事を書いておきたいです. -
汚ソースのリファクタリング
ソースが汚すぎるのできれいにしたいです(Dockerfile含む)...