批量导入密码到 iTerm 密码管理器

Posted by Echo on October 22, 2016

定期修改密码是非常常见的安全策略,我们这每隔几个月会将所有线上服务器的密码统一更新。

起初我是将服务器密码直接存储在 Keychain 里。

每次要登录服务器的时候,打开 Keychain,搜索相关服务器的信息,然后点击「将密码复制到剪贴板」,再回到终端里粘贴。

当时觉得虽然不太方便,但是总比从 Excel 里粘密码要好吧。

后来,iTerm 升到 3,多了一项密码管理器的功能。

身为 iTerm 的重度使用者,我觉得这项功能真的超级实用。

只要按下快捷键 ⌘ + P ,设定的关键字一输,密码就可以直接上屏。

所以在我发现这个功能之后,我就把 Keychain 里所有常用服务器的密码都一条条粘到了 iTerm 里。

然而,最近到了公司定期修改密码的日子,当系统组的同事拿着 U 盘过来找我的时候,想到又要一条条的粘,我的内心其实是崩溃的。

我试着 Google ,但是并没有发现特别好将密码导入到 iTerm 的方法,于是我去 iTerm 的论坛发了一张求助帖

很快就有人回复了。

因为 iTerm 中的密码也是存在 Keychain 的,可以通过命令行将密码添加到 Keychain :

On Wednesday, October 19, 2016 at 9:10:07 PM UTC-4, Flare Han wrote: Is there any way to import a lot of passwords to the Password Manager?

While googling a similar topic a couple months ago, I found

Save the password in keychain, either by the GUI (see https://www.netmeister.org/blog/keychain-passwords.html) or:

security add-generic-password -a ${USER} -s LDAPbindPW -w '<your-password-goes-here>'

The thing I was playing with at the time would have used the above via something like this in my .bashrc:

LDAP_PW=$(security find-generic-password -a ${USER} -s LDAPbindPW -w)

I also found you can use variations of the security command to list and see what passwords iTerm2 has created. From that, you can figure out how to use a security command to create/update what iTerm2 uses.

security add-generic-password -U -s iTerm2 -T /Applications/iTerm.app -a '<hostname> <userid>' -w '<userid's password>' -l 'a label'

iTerm2’s password manager shows the account name (the -a switch). So you don’t really need the -l label. The -l label makes it easier to see in Keychain Access.

于是我写了个脚本将密码表导进了 iTerm 。

其实 iTerm 的密码管理器仍有需要改进的地方,比如它不支持类似 Sublime Text 中的全文匹配,不能跳着输入,所以要输入 ops@zone-application-server 这种名字很长的超麻烦。

我只能给每组账号都设置了一个关键字用来进行快速输入,比如上面这条记录可缩写为 zasop 。

#!/usr/bin/env ruby -w
# encoding: utf-8
# data csv format
# jcjz,bj,user1,user1_password,user2,user2_password,...

# command
# security add-generic-password -U -s iTerm2 -T /Applications/iTerm.app -a 'user' -w 'password'

require 'pathname'
require 'csv'

class ItermPassword

  def initialize(*args)
    @long_name  = args.shift
    short_name = args.shift
    @short_name = short_name.nil? ? @long_name[0..1] : short_name
    @users_and_passwordes = Hash[*args]
  end

  COMMAND_PREFIX = "security add-generic-password -U -s iTerm2 -T /Applications/iTerm.app"

  def write_password(user_name,user_pass)
    system "#{COMMAND_PREFIX} -a '#{@short_name}#{user_name[0..1]} #{user_name}@#{@long_name}' -w '#{user_pass.gsub(/'/,"'\"'\"'")}'" if user_name && user_pass
  end

  def write
    @users_and_passwordes.each do |user,pass|
      write_password(user,pass)
    end
  end
end

data_file = Pathname(ARGV[0])
CSV.foreach(data_file, :headers => false) do |row|
  iterm_passes = ItermPassword.new(*row)
  iterm_passes.write
end

(修改于 2017.04.02)