Emacsの設定ではパスワードとかは直書きせずauth-source使うと良いよ

先日、関東Emacs勉強会に行って来ました。
そこでちらっと、設定ファイルにパスワードとかのベタ書きは
auth-source使えばしなくて済むよって@kawabataさんが言ってたので、
その辺のやり方をまとめておくことにしました。

機密情報をgpgファイルから取得

auth-source-searchという関数を使うと、パスワードなどの見せたくない情報を
外部のgpgファイルに記述しておき、必要に応じて取得することができます。
これは、Emacs標準添付のauth-sourceという機能で、私も以前記事を書いています。

Emacsでsudoを介してファイル編集する時、EasyPGを使ってパスワード入力を省略する

上記では、パスワードの取得をTrampが行なっていますが、手動で行うと以下のようになります。

まず、gpgファイルには、以下のように1行に1エントリを記述します。

machine example.com login my-user password xxx port 22

auth-source-searchは、渡されたキーに合致する全エントリをリストで返します。
各エントリの情報はplist形式になっており、passwordの値は、復号化されたパスワードを返す関数なので、
例えば、以下のコードでhoge変数にパスワードxxxが格納されます。

(setq hoge
      (funcall
       (plist-get
        (nth 0 (auth-source-search :machine "example.com" :login "my-user" :port "22"))
        :secret)))

設定ファイル内で繰り返し使う場合などは特に、以下のように関数化しておくのが良いかと。

(defun* my:auth-source-get-passwd (&rest spec &allow-other-keys)
  (let ((founds (apply 'auth-source-search spec)))
    (when founds
      (funcall (plist-get (nth 0 founds) :secret)))))

例えば、org-gcal.elだと、GoogleAPIのid/secretが必要ですが、
以下のようにすることで、secretを書かないようにできます。

gpgファイルのエントリ

machine localhost login xxx.apps.googleusercontent.com password xxx port org-gcal

設定ファイル

(setq org-gcal-client-id "xxx.apps.googleusercontent.com")
(setq org-gcal-client-secret
      (my:auth-source-get-passwd :port "org-gcal" :login org-gcal-client-id))

ちなみに、最初にauth-source-searchが実行される時、
gpgファイルの復号化のためのパスワードを聞かれるので、上記の場合であれば、
org-gcal-client-secretへのsetqは、必要になるタイミングで行うのが良いと思います。

指定可能なキー

auth-source-searchで、エントリ検索のために指定できるキーはhost/port/userで、
指定されたキーが合致するエントリが取得される仕様のようです。(ちゃんとは調べてない)
キーには以下のようにエイリアスがあり、内部で統一されます。

  • machine → host
  • login/account → user
  • protocol → port
  • password → secret

独自のキーで検索する

ここまでで、見せたくない情報を隠すことはできました。
ただ、auth-source-searchはTrampなどの機能からもhost/port/userのキーで
検索されることがあるので、上記のorg-gcalの場合には、
Trampとかが使わないキー(例えばapp/idとか)でエントリを検索したいところです。

auth-source-searchでは、host/port/userでgpgファイルのエントリを検索する処理は、
auth-source-netrc-searchという関数が呼び出されて行われるのですが、
auth-sourcesの設定の仕方により、任意のキーでエントリを検索する
auth-source-plstore-searchという関数を利用するように変更することができます。

plstore.el

Emacsにはデフォルトで、plistのデータを外部ファイルに必要に応じて暗号化して、
保存したり読み込んだりするためのplstoreというライブラリがあります。
auth-source-plstore-searchは、その機能を利用して、
外部ファイルに保存されたplistのエントリから指定された条件に合致するエントリを検索してくれます。

そのためには、gpgファイルではなく、plistファイルに該当のエントリを記述する必要があります。

plistファイル作成

新規ファイルバッファを開く

Emacsからfind-fileなどで、拡張子plistの新規ファイルのバッファを開きます。
例では、~/.emacs.d/.authinfo.test.plistとしています。
開かれたバッファのモードは、設定されていなければ恐らくfundamental-modeだと思います。
であれば、M-x plstore-mode として、モードをplstore-modeにします。

f:id:aki2o:20140920092556p:plain

  • モードラインに"PLSTORE"と表示されます
  • 拡張子は必ずplistでないと動作しないです(悔しいことに)

設定を記述する

例えば、org-gcalの設定の場合、以下のようなコードを記述します。

(("org-gcal"
  :app "org-gcal"
  :id "xxx.apps.googleusercontent.com"
  :secret-secret "xxx"))
  • バッファの内容はplistのリストです
  • 各plistの先頭の要素は、エントリを表す文字列です(文字列なら多分何でもOK)
  • 2番目以降にキーとその値を記述します
  • 暗号化したいキーは、キー名の前に"secret-"を記述します

f:id:aki2o:20140920092657p:plain

記述したら、バッファを保存します。
暗号化する値がある場合には、EasyPGのダイアログまたはプロンプトが表示されると思うので、
任意のパスワードを入力して下さい。(auth-source-searchした時に入力することになります)

もし、暗号化する値があるのに、EasyPGが動作しない場合は、
M-x describe-variable "plstore-encoded" として変数の値を確認し、
もし"t"でない場合は、M-x set-variable "plstore-encoded" "t" を実行して、再度保存してみて下さい。

暗号化の確認

保存されたら、plstore-mode-toggle-display(C-c C-c)を実行して下さい。
以下のように暗号化したいキーの値が隠されたバッファになるはずです。

f:id:aki2o:20140920092715p:plain

これが実際のファイル内容です。

再編集するには

再度、plstore-mode-toggle-display(C-c C-c)を実行すると、
EasyPGのダイアログまたはプロンプトが表示され、保存時に設定したパスワードを入力すると、
以下のように復号化されたバッファになります。

f:id:aki2o:20140920092742p:plain

例えば、secretだけではなくidも暗号化してみます。

f:id:aki2o:20140920092805p:plain

保存して(再度パスワードを設定)、plstore-mode-toggle-display(C-c C-c)すれば、
以下のようになります。

f:id:aki2o:20140920093013p:plain

設定

auth-sourcesに設定追加

作成したplistファイルをauth-sourcesに追加します。

(add-to-list 'auth-sources (concat user-emacs-directory ".authinfo.test.plist"))

取得用関数定義

上記のmy:auth-source-get-passwdではsecretの値しか取得できません。
gpgファイルであれば、それで十分ですが、plistファイルを扱うのであれば、
任意のキーの値を取得できるようにした方が良いと思います。

(defun* my:auth-source-get-property (prop-name &rest spec &allow-other-keys)
  (let* ((founds (apply 'auth-source-search spec))
         (pkey (intern (concat ":" (format "%s" prop-name))))
         (ret (when founds (plist-get (nth 0 founds) pkey))))
    (if (functionp ret)
        (funcall ret)
      ret)))

この関数を使って、以下のようにorg-gcalの設定が記述できるようになります。

(setq org-gcal-client-id (my:auth-source-get-property 'id :app "org-gcal"))
(setq org-gcal-client-secret
      (my:auth-source-get-property 'secret :app "org-gcal" :id org-gcal-client-id))

Enjoy!!!