Octopassで簡単ユーザ管理
きっかけ
ちょっとした技術コミュニティを作りたくなって友人と学内で一つ非公式サークルを立ち上げました.
当然のようにVPSを借り,Webサーバを立ち上げ,Wikiを建て…なんていうのを考えているとき,ふと「ユーザ管理面倒だな.LDAPほど大げさじゃない何か良いものはないだろうか」と考えてググったら出てきました.
Githubをバックエンドとして使用してユーザ認証できるなんてステキ!と飛びついてみたら簡単にできて最高だったという話です.
環境
メンバーもまだ少なく,ほとんど創立メンバーの友人と二人で折半するので費用は抑えたい…ということでさくらのVPSのメモリ1GBプランを契約.年額一括1万円程度で固定IPのあるサーバが使えるのはありがたいです.
OSには使い慣れているUbuntuを選択しました.
導入
基本は下記ページ及びgithubのREADMEに従うだけです.
下記操作は全てさくらのVPSのコントロールパネルからアクセスできるシリアルコンソールから行っています.
Octopassのインストール
UbuntuやCentOSならソースからコンパイルしなくてもpackagecloudに既に用意されています.ありがたい.
$ sudo apt-get update $ sudo apt-get upgrade $ curl -s https://packagecloud.io/install/repositories/linyows/octopass/script.deb.sh | sudo bash $ sudo apt-get install octopass
Personal Access Tokenの取得
https://github.com/settings/tokens/newからログインして新しいトークンを作成します.
このとき,必要な権限はadmin:org
の中のread:org
のみで良いようです.
設定ファイルの作成
設定ファイルのテンプレートが用意されているのでmv
します.
$ sudo mv /etc/octopass.conf.example /etc/octopass.conf $ sudo vim /etc/octopass.conf
設定ファイルの必要な項目を埋めます.僕は下記のコメントを外しているところのみを埋めています.
# O C T O P A S S # Required Token = "{Your token}" ## Use team Organization = "{Your org}" Team = "{Your team}" ## Use collaborators #Owner = "yourname" #Repository = "yourrepository" # Default #Endpoint = "https://api.github.com/" #Group = "" Home = "/home/%s" Shell = "/bin/bash" #UidStarts = 2000 #Gid = 2000 #Cache = 300 Syslog = true # Advanced #SharedUsers = [ "admin", "deploy" ]
情報が取れるか確認してみます.
$ sudo octopass passwd hoge:x:120000:2000:managed by octopass:/home/hoge:/bin/bash fuga:x:130000:2000:managed by octopass:/home/fuga:/bin/bash
こんな感じでチームに所属しているメンバーが一覧で表示できるはずです.
sshdやpamの設定
これらをsshdやpamから叩けるようにします.とはいってもgithubとかにあるコードをそのままコピペすれば動きます.ステキ.
/etc/ssh/sshd_config
デフォルトではパスワードによるsshなどが可能なので制限します.
# rootログイン禁止 PermitRootLogin no # 鍵認証にoctopassをrootから叩いて使用 AuthorizedKeysCommand /usr/bin/octopass AuthorizedKeysCommandUser root # 公開鍵認証を有効に RSAAuthentication yes PubkeyAuthentication yes # パスワード認証を無効 PasswordAuthentication no # PAMを使用 UsePAM yes
/etc/pam.d/sshd
@include common-auth
は元から記述されているのでコメントアウトします.
#@include common-auth
auth requisite pam_exec.so quiet expose_authtok /usr/bin/octopass pam
auth optional pam_unix.so not_set_pass use_first_pass nodelay
session required pam_mkhomedir.so skel=/etc/skel/ umask=0022
/etc/nsswitch.conf
これを触ったのは初めてだったのですが,名前解決の順序を決定するための設定のようです*1.
passwd
, shadow
, group
の3項目を下記のように変更.
passwd: files octopass sss shadow: files octopass sss group: files octopass sss
Githubへの鍵の登録
既にやってるひとは飛ばして構わないです.
ここの操作はMacBook Pro上で行っていますが,やることとしてはWindowsやLinuxでも同じで
の2つだけです.
鍵ペアの作成
ssh-keygen
コマンドを使用します.-C
はコメントで,末尾に付加されるユーザ名@ホスト名
を上書きします(オプション).
$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/{key name} -C "youremail@hoge.com"
公開鍵の中身をクリップボードにコピーします.
$ pbcopy < ~/.ssh/{key name}.pub
登録
- https://github.com/settings/keys からログイン
- 右上の
New SSH key
をクリック - わかりやすいTitleを入力して
Key
セクションに先ほどコピーした公開鍵をペースト Add SSH key
をクリック
SSHしてみる
クライアントPCからアクセスしてみます.今後のためにまず~/.ssh/config
に設定を追記しておきます.sshするポートを変更している人などは適宜読み替えてください.
ServerAliveInterval 60 Host hoge HostName {address of your server} User {your github name} Port 22 IdentityFile ~/.ssh/{key name}
いざログイン(初回はAre you sure you want to continue connecting (yes/no)?
みたいなメッセージが出ます)
$ ssh hoge Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-62-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage SAKURA Internet [Virtual Private Server SERVICE] Last login: Sun Dec 10 00:00:00 2017 from 000.00.00.0 {your github name}@{your host}:
わーいやったー!
sudoしたい
ここまででsshはできましたが,実はこのままではsudo
できません./etc/octopass.conf
のGroups
がユーザの所属するグループを決定するのですが,これをsudo
とかにしてもsudo
はできません.どうやるんだーーーーとなってissueで質問させていただきました.
まさかその日中にレスポンスを頂けるとは…ありがたい.
というわけで,/etc/sudoers.d/
以下に設定を追記します.
sudoerへのグループの追加
/etc/octopass.conf
のGroups
を設定しなかった場合,初期値はGithub Teamのチーム名が使用されます(ここではhoge
とします).
このhoge
グループに対してパスワードを要求せずsudo
する権限を与える必要があるようです.(ここからはサーバ上での作業です.さくらのVPSのシリアルコンソールから作業しました*2)
$ sudo visudo -f /etc/sudoers.d/hoge
立ち上がるのがvimじゃなくていつも戸惑うのですが,下記のように記述します.
%hoge ALL=(ALL) NOPASSWD: ALL
これで保存するとhoge
グループのユーザはパスワード無しでsudo
できるようになります*3.
まとめ
Team単位で認証できるため,例えば新しく鯖管を育成したいとなったときもユーザ作って〜鍵登録して〜とかせず,Teamにメンバーを追加するだけで済みます.また,複数サーバを建てた際にも最初にこれを導入さえしてしまえば他のメンバがログインして作業できるため効率的です.
サーバ数台程度の規模ならばGithubのAPI制限にかかる可能性は低いですし,速度面も特に気になりませんでした.Githubが落ちてるときはどうせまともに作業できないのでゆっくりコーヒーでも飲んで待ちます.
あとは複数ロールへの対応が欲しい…sudo
不可のTeamを追加して運用できれば小規模チームにとってはかなり最高なソリューションなのでは.
以上,導入してみた所感でした.
ghq管理下のリポジトリを色々するAlfredWorkflowを作った
色々するとは言っても基本的に何かで開くだけです.
きっかけ
最近ghq*1でリポジトリを管理,pecoで開くというスキーム*2が流行りだという情報を耳にしたので導入してみたところかなり快適になりました.
Windowsでも同様のことが出来るというのが嬉しくて,ついつい散らばりがちなリポジトリたちをちゃんと管理していこうという気持ちが芽生えました.
開発でメインで使用している端末はMacBook Proなのですが,せっかくAlfredがあるのだからterminal以外からでも叩けると嬉しいなという気持ちで作り始めました.
環境
今回もGoで書いています.Workflowを使うだけであればGoの環境もGlideも必要ありません.ghqは必要です.
ghq-alfred
リポジトリは以下になります.
準備
現在最新のworkflowは以下からダウンロードできます.
Release v0.2.0: Actual 'First Release' · pddg/go-ghq-alfred · GitHub
インストールしたら設定画面を開き,Workflow Environment Variable
のghq
の値を環境に合わせて設定してください*3.
また,こちらでは使用するエディタとしてVS Code,ターミナルアプリにiTermを指定していますが,環境に合わせて指定してください.
できること
ghq list -p
の結果と入力したクエリを元にリポジトリを絞り込みます.ghq {query}
で検索を掛けます.
Finderで開く
選択してEnter
またはCommand
+Enter
でFinderで開きます.
ブラウザで開く
選択してShift
+Enter
でデフォルトブラウザでそのリポジトリのページを開きます.
ターミナルで開く
選択してFn
+Enter
でターミナルで開きます.僕はiTermを指定していますが,標準のterminalでも問題なく表示できるかと思います.
エディタで開く
選択してControl
+Enter
でエディタでそのリポジトリのディレクトリを開きます.僕はVS Codeを指定していますが,大抵のエディタで可能かと思います.
Googleで検索
{user名}/{repository名}
で検索をかけます.
できないこと
- 上に書いたこと以外の全て.
既知の不具合
ghqではgit config --global --add ghq.root path/to/dir
でデフォルトのroot以外にもリポジトリの入ったディレクトリを指定し参照できます.
が,このときghqの形式,つまり{root}/{site}/{user}/{repo}
の形を取っていないディレクトリも含めることが出来てしまいます.
このWorkflowではフルパスから推察してそのリポジトリをbitbucketやgithubで開く設計になっているため,そういった形式のリポジトリは
- ブラウザで開くことが出来ません.
- Google検索も上手く機能しません.
- Alfredでの表示が
{user}/{repo}
になりません. - Finderで開く,エディタで開く,などの動作は愚直にパスを渡すだけの設定になっているため問題なく動作いたします.
今のところghqのディレクトリ形式を取らないリポジトリに対する動作を保証する予定はありません.
改善したいこと
アイコンをflaticonからお借りしているのですが,黒の部分以外が透過処理になっているため見づらい…
探してはいるのですが,あまりよい代替品を見つけられていません.何か良いアイコンをお知りでしたら,教えて頂ければ幸いです.
あと1日で書いたコードなのでバグなどがあるかと思います.見つけたらできればgithubの方まで…
まとめ
pythonやrubyでScript Filterを頑張って書くよりも,Goで書いたバイナリを叩く方が気兼ねなく色々できて良いんじゃ無いかと思います.
ghqは便利なツールなのでid:motemen氏に感謝しつつ使わせて頂いています.
Alfredはいいぞ.
Entrykitのrenderで遊ぶ -後編-
頑張りすぎじゃ無いか感が溢れてきます.
発展的な記法
環境変数からの値の取得しか存在しないわけでは無く,色々できます.
include
これは比較的便利に扱えるのでは無いでしょうか,templateのモジュール化ができます.また,第二引数以降に"VAR=VALUE"
の形で渡すと,include
するtemplateの中からvar "VAR"
などとして値を取り出すことが出来ます.
ここではecho '{{ var "INCLUDED" }}' > include_test.tmpl
としてinclude
するtemplateを作成しておきます.
{{ include "include_test.tmpl" "INCLUDED=True" }} # -> True
file, text
ファイルの中身を読みとって文字列として展開します.例えばecho "hello world" > hello.txt
としてテンプレートと同じ階層に置いたとします.
# file {{ file "hello.txt" }} # -> hello world # text {{ text "hello.txt" }} # -> hello world
ソースを読む限りこの二つの内部的な実装は全く同じに見えます*1.
dir, dirs, files
引数として任意のディレクトリへのパスを渡します.文字列を返すわけでは無いことに注意してください.
# 指定されたディレクトリ中のディレクトリ・ファイルの名称の配列を返す {{ dir "./" }} # 指定されたディレクトリ中のディレクトリのみの名称の配列を返す {{ dirs "./" }} # 指定されたディレクトリ中のファイルのみの名称の配列を返す {{ files "./" }}
存在しないディレクトリを参照しようとするとnot found
なエラーを発して中断します.
httpget, urlquery
APIリクエストしたりすることができます*2.ca-certificates
をインストールしていないとエラーを吐いて死にます.
{{ httpget "http://search.twitter.com/search.json?lang=ja&rpp=20&q=%23poyo" }}
注意すべきなのは,返すオブジェクトが文字列では無いことでしょう.Goのオブジェクトが返却されるため,htmlがそのまま出力されるわけではありません.
json, tojson
ではAPIリクエストしたところでどうするんだという話ですが,これでjsonオブジェクトに変換できます.
{{ httpget "http://search.twitter.com/search.json?lang=ja&rpp=20&q=%23poyo" | json }}
これもまた返り値はGoのオブジェクトなのでノイズが混ざっています.純粋にレスポンスを記述するだけなら以下の様にします.
{{ httpget "http://search.twitter.com/search.json?lang=ja&rpp=20&q=%23poyo" | json | tojson }}
tojson
はgoのオブジェクトを受取り,jsonをstringにして返します.
yaml, toyaml
json
,tojson
のyaml版です.
split, join
string
から配列を生成することができます.
# 配列の生成 {{ split "a,b,c,d" }} # -> [a, b, c, d] # 任意のセパレータで区切った文字列に変換 {{ split "a,b,c,d" | join ":" }} # -> a:b:c:d
splitkv, joinkv
少し難解で使いどころがよく分からないですが,split
,join
が配列を扱うのに対してmap
を扱います.
# mapの生成 {{ split "\n" "a=A,b=B,c=C" | splitkv "=" }} # -> [a=A b=B c=C] # mapの値へアクセス ## mapを変数へ代入 {{ $map_var := (split "\n" "a=A,b=B,c=C" | splitkv "=") }} ## アクセス {{ $map_var.a }} # -> A {{ $map_var.d }} # -> <no value> ## 代入はできないようでエラーを吐く {{ $map_var.d := "D" }} # -> unexpected ":=" in operand # 任意のセパレータで文字列にする {{ joinkv ":" $map_var }} # -> [a:A b:B c:C]
joinkv
は直接文字列を返すわけではなく,key{{任意のセパレータ}}value
の形で文字列化し,その配列を返します.全体を文字列にするにはさらにjoin
をはさむ必要があります,
seq
数値の配列を作成します.引数に数字(intまたはstring)をとり,0からその値までの配列を作って返します.
{{ seq 10 }} # -> [0 1 2 3 4 5 6 7 8 9 10]
append, drop
配列のへの値の追加,削除ができます.
# 数値でも {{ seq 10 | append 11 | drop 1 }} #-> [0 2 3 4 5 6 7 8 9 10 11] # 文字列でも {{ split "," "a,b,c,d" | append "e" | drop "a" }} # -> [b c d e]
index
配列への添字アクセスができます.
{{ index (seq 5) 1 }} # -> 1
第一引数に配列を取ることに注意が必要です.また,index {{任意の多次元配列}} 1 2 3
などは{{任意の多次元配列}}[1][2][3]
と同義になります.
sh
もはやなんでもありですが任意のコマンド実行が出来ます.出力された値が文字列として取得できます.
{{ sh "ping -c 1 www.google.com" }}
sigilにはあるが使えなかったもの
- jmespath
jsonからクエリを使って値を取り出すことができるようなのですが,render
ではnot defined
と言われてしまいました.
組み合わせる
ファイル一覧でループ
配列の生成ができることから,色々なループ処理が可能になります.例えば任意のディレクトリ内のファイル一覧に対して,*.log
の場合には監視対象に加える,などといった処理が可能になります.
{{ range $index, $f := (files "/path/to/log") }} {{ if match $f "*.log" }} something $f to do {{ end }}
環境変数を判定
()
を用いるとだいたいなんでもできるので,例えば環境変数も(どっかのQiitaではできないとか書いてましたが)ifで用いることが出来ます
{{ if eq "test" (var "TEST") }} This is test. {{ else }} This is not test. {{ end }}
まとめ
もっと色々やろうかと思ったけど力尽きた.