SSH_ASKPASSを取り扱う方法を考える

Emacsから ssh-add - を使い標準入力に鍵を渡そうとした時、その鍵にパスフレーズが設定されている場合、パスフレーズを渡す手段が必要になる。tty経由で渡したかったけれど、上手く渡す事ができなかったため、仕方がないからファイル経由で渡す事にした。

ただしパスフレーズは重要な値だから、暗号化して保存する。

askpass.el

;;; askpass --- askpass utility -*- lexical-binding: t -*-

;; Copyright (C) 2023 TakesxiSximada

;; Author: TakesxiSximada <[email protected]>
;; Maintainer: TakesxiSximada <[email protected]>
;; Repository:
;; Version: 1
;; Package-Version: 20240224.0000
;; Package-Requires: ((emacs "28.0")
;; Date: 2024-02-24

;; This file is part of askpass.el.

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Code:

(defvar askpass-delete-timer nil)

(defcustom askpass-file "~/.askpass.gpg"
  "File for storing encrypted temporary values"
  :type 'string
  :group 'askpass)

(defcustom askpass-file "~/.askpass.gpg"
  "File for storing encrypted temporary values"
  :type 'string
  :group 'askpass)

(defcustom askpass-program nil
  "askpass.el acquisition program"
  :type 'string
  :group 'askpass)

(defcustom askpass-recipient nil
  "Owner of the key used for encryption with GnuPG"
  :type 'string
  :group 'askpass)

(defun askpass-activate ()
  (interactive)
  (setenv "ASKPASS_GPG_FILE" (expand-file-name askpass-file))
  (setenv "SSH_ASKPASS" askpass-program))

(defun askpass-delete-file ()
  (message (format "File deleting...: %s" askpass-file))
  (delete-file askpass-file)
  (message (format "File deleted: %s" askpass-file))
  (setq askpass-delete-timer nil))

(defun askpass-set-delete-timer ()
  (interactive)
  (unless askpass-delete-timer
    (setq askpass-delete-timer
	  (run-at-time "10 sec" nil #'askpass-delete-file))))

(defun askpass-format-gpg-command ()
  (format "gpg --encrypt -r %s" askpass-recipient))

(defun askpass ()
  (interactive)
  (let* ((output-path (expand-file-name askpass-file))
	 (output-buf (create-file-buffer output-path)))
    (with-current-buffer output-buf
      (set-visited-file-name (expand-file-name output-path))
      (set-buffer-file-coding-system 'raw-text)
      (erase-buffer)

      (let ((file-name-handler-alist nil))
	(save-buffer)))

    (with-temp-buffer
      (insert (password-read "SSH Agent Passphase: "))
      (shell-command-on-region (point-min) (point-max)
			       (askpass-format-gpg-command)
			       output-buf))

    (with-current-buffer output-buf
      (let ((file-name-handler-alist nil))
	(save-buffer)
	(kill-buffer))))

  (askpass-activate)
  (askpass-set-delete-timer))

(defun askpass-show ()
  (interactive)
  (shell-command askpass-file))

(provide 'askpass)
;;; askpass.el ends here

取得用のプログラムのスクリプトは、単純にGnuPGを実行するだけだ。

askpass-gpg

#! /usr/local/bin/bash

exec gpg --decrypt ${ASKPASS_GPG_FILE}

正しい方法はありそうだけれど、とりえずこれでお茶を濁す。