1  版权声明
2 起步
2.1  关于版本控制
2.2  Git 简史
2.3  Git 基础
2.4  命令行
2.5  安装 Git
2.6  初次运行Git前的配置
2.7  获取帮助
2.8  总结
3 Git 基础
3.1  获取 Git 仓库
3.2  记录每次更新到仓库
3.3  查看提交历史
3.4  撤消操作
3.5  远程仓库的使用
3.6  打标签
3.7  Git 别名
3.8  总结
4 Git 分支
4.1  分支简介
4.2  分支的新建与合并
4.3  分支管理
4.4  分支开发工作流
4.5  远程分支
4.6  变基
4.7  总结
5 服务器上的 Git
5.1  协议
5.2  在服务器上搭建 Git
5.3  生成 SSH 公钥
5.4  配置服务器
5.5  Git 守护进程
5.6  Smart HTTP
5.7  GitWeb
5.8  GitLab
5.9  第三方托管的选择
5.10  总结
6 分布式 Git
6.1  分布式工作流程
6.2  向一个项目贡献
6.3  维护项目
6.4  总结
7 GitHub
7.1  账户的创建和配置
7.2  对项目做出贡献
7.3  维护项目
7.4  管理组织
7.5  脚本 GitHub
7.6  总结
8 Git工具
8.1  选择修订版本
8.2  交互式暂存
8.3  储藏与清理
8.4  签署工作
8.5  搜索
8.6  重写历史
8.7  重置揭密
8.8  高级合并
8.9  Rerere
8.10  使用 Git 调试
8.11  子模块
8.12  打包
8.13  替换
8.14  凭证存储
8.15  总结
9 自定义Git
9.1  配置 Git
9.2  Git 属性
9.3  Git 钩子
9.4  使用强制策略的一个例子
10 Git与其他系统
10.1  作为客户端的 Git
10.2  迁移到 Git
10.3  总结
11 Git内部原理
11.1  底层命令和高层命令
11.2  Git 对象
11.3  Git 引用
11.4  包文件
11.5  引用规格
11.6  传输协议
11.7  维护与数据恢复
11.8  环境变量
11.9  总结

凭证存储

2018-06-21 10:20:42
git
1477
最后编辑:GavinHsueh 于 2018-06-21 10:38:19

如果你使用的是 SSH 方式连接远端,并且设置了一个没有口令的密钥,这样就可以在不输入用户名和密码的情况下安全地传输数据。 然而,这对 HTTP 协议来说是不可能的 —— 每一个连接都是需要用户名和密码的。 这在使用双重认证的情况下会更麻烦,因为你需要输入一个随机生成并且毫无规律的 token 作为密码。

幸运的是,Git 拥有一个凭证系统来处理这个事情。 下面有一些 Git 的选项:

  • 默认所有都不缓存。 每一次连接都会询问你的用户名和密码。

  • “cache” 模式会将凭证存放在内存中一段时间。 密码永远不会被存储在磁盘中,并且在15分钟后从内存中清除。

  • “store” 模式会将凭证用明文的形式存放在磁盘中,并且永不过期。 这意味着除非你修改了你在 Git 服务器上的密码,否则你永远不需要再次输入你的凭证信息。 这种方式的缺点是你的密码是用明文的方式存放在你的 home 目录下。

  • 如果你使用的是 Mac,Git 还有一种 “osxkeychain” 模式,它会将凭证缓存到你系统用户的钥匙串中。 这种方式将凭证存放在磁盘中,并且永不过期,但是是被加密的,这种加密方式与存放 HTTPS 凭证以及 Safari 的自动填写是相同的。

  • 如果你使用的是 Windows,你可以安装一个叫做 “winstore” 的辅助工具。 这和上面说的 “osxkeychain” 十分类似,但是是使用 Windows Credential Store 来控制敏感信息。 可以在  https://gitcredentialstore.codeplex.com 下载。

你可以设置 Git 的配置来选择上述的一种方式

$ git config --global credential.helper cache

部分辅助工具有一些选项。 “store” 模式可以接受一个 --file <path> 参数,可以自定义存放密码的文件路径(默认是`~/.git-credentials`)。 “cache” 模式有 --timeout <seconds> 参数,可以设置后台进程的存活时间(默认是 “900”,也就是 15 分钟)。 下面是一个配置 “store” 模式自定义路径的例子:

$ git config --global credential.helper store --file ~/.my-credentials

Git 甚至允许你配置多个辅助工具。 当查找特定服务器的凭证时,Git 会按顺序查询,并且在找到第一个回答时停止查询。 当保存凭证时,Git 会将用户名和密码发送给 所有 配置列表中的辅助工具,它们会按自己的方式处理用户名和密码。 如果你在闪存上有一个凭证文件,但又希望在该闪存被拔出的情况下使用内存缓存来保存用户名密码,.gitconfig 配置文件如下:

[credential]
    helper = store --file /mnt/thumbdrive/.git-credentials
    helper = cache --timeout 30000

底层实现

这些是如何实现的呢? Git 凭证辅助工具系统的命令是 git credential,这个命令接收一个参数,并通过标准输入获取更多的参数。

举一个例子更容易理解。 我们假设已经配置好一个凭证辅助工具,这个辅助工具保存了 mygithost 的凭证信息。 下面是一个使用 “fill” 命令的会话,当 Git 尝试寻找一个服务器的凭证时就会被调用。

$ git credential fill (1) protocol=https (2) host=mygithost (3) protocol=https (4) host=mygithost
username=bob
password=s3cre7
$ git credential fill (5) protocol=https
host=unknownhost
Username for 'https://unknownhost': bob
Password for 'https://bob@unknownhost':
protocol=https
host=unknownhost
username=bob
password=s3cre7
  1. 这是开始交互的命令。

  2. Git-credential 接下来会等待标准输入。 我们提供我们所知道的信息:协议和主机名。

  3. 一个空行代表输入已经完成,凭证系统应该输出它所知道的信息。

  4. 接下来由 Git-credential 接管,并且将找到的信息打印到标准输出。

  5. 如果没有找到对应的凭证,Git 会询问用户的用户名和密码,我们将这些信息输入到在标准输出的地方(这个例子中是同一个控制台)。

凭证系统实际调用的程序和 Git 本身是分开的;具体是哪一个以及如何调用与 credential.helper 配置的值有关。 这个配置有多种格式:

配置值 行为

foo

执行 git-credential-foo

foo -a --opt=bcd

执行 git-credential-foo -a --opt=bcd

/absolute/path/foo -xyz

执行 /absolute/path/foo -xyz

!f() { echo "password=s3cre7"; }; f

! 后面的代码会在shell执行

上面描述的辅助工具可以被称做 git-credential-cache、git-credential-store 之类,我们可以配置它们来接受命令行参数。 通常的格式是 “git-credential-foo [args] <action>.” 标准输入/输出协议和 git-credential 一样,但它们使用的是一套稍微不太一样的行为:

  • get 是请求输入一对用户名和密码。

  • store 是请求保存一个凭证到辅助工具的内存。

  • erase 会将给定的证书从辅助工具内存中清除。

对于 store 和 erase 两个行为是不需要返回数据的(Git 也会忽略掉)。 然而对于 get,Git 对辅助工具的返回信息十分感兴趣。

如果辅助工具没有任何有用的信息,它可以直接退出而不需要输出任何东西,但如果它有这些信息,它在提供的信息后面增加它所拥有的信息。 这些输出会被视为一系列的赋值语句;每一个提供的数据都会将 Git 已有的数据替换掉。

这有一个和上面一样的例子,但是跳过了 git-credential 这一步,直接到 git-credential-store:

$ git credential-store --file ~/git.store store (1) protocol=https
host=mygithost
username=bob
password=s3cre7
$ git credential-store --file ~/git.store get (2) protocol=https
host=mygithost
username=bob (3) password=s3cre7
  1. 我们告诉 git-credential-store 去保存凭证:当访问 https://mygithost 时使用用户名 “bob”,密码是 “s3cre7”。

  2. 现在我们取出这个凭证。 我们提供连接这部分的信息(https://mygithost)以及一个空行。

  3. git-credential-store 输出我们之前保存的用户名和密码。

~/git.store 文件的内容类似:

https://bob:s3cre7@mygithost

仅仅是一系列包含凭证信息URL组成的行。 osxkeychain 和 winstore 辅助工具使用它们后端存储的原生格式,而 cache 使用它的内存格式(其他进程无法读取)。

自定义凭证缓存

已经知道 git-credential-store 之类的是和 Git 是相互独立的程序,就不难理解 Git 凭证辅助工具可以是 任意 程序。 虽然 Git 提供的辅助工具覆盖了大多数常见的使用场景,但并不能满足所有情况。 比如,假设你的整个团队共享一些凭证,也许是在部署时使用。 这些凭证是保存在一个共享目录里,由于这些凭证经常变更,所以你不想把它们复制到你自己的凭证仓库中。 现有的辅助工具无法满足这种情况;来看看我们如何自己实现一个。 这个程序应该拥有几个核心功能:

  1. 我们唯一需要关注的行为是 get;store 和 erase 是写操作,所以当接受到这两个请求时我们直接退出即可。

  2. 共享的凭证文件格式和 git-credential-store 使用的格式相同。

  3. 凭证文件的路径一般是固定的,但我们应该允许用户传入一个自定义路径以防万一。

我们再一次使用 Ruby 来编写这个扩展,但只要 Git 能够执行最终的程序,任何语言都是可以的。 这是我们的凭证辅助工具的完整代码:

#!/usr/bin/env ruby
require 'optparse'
path = File.expand_path '~/.git-credentials' (1) OptionParser.new do |opts|
    opts.banner = 'USAGE: git-credential-read-only [options] <action>'
    opts.on('-f', '--file PATH', 'Specify path for backing store') do |argpath|
        path = File.expand_path argpath
    end
end.parse!
exit(0) unless ARGV[0].downcase == 'get' (2) exit(0) unless File.exists? path
known = {} (3) while line = STDIN.gets
    break if line.strip == ''
    k,v = line.strip.split '=', 2
    known[k] = v
end
File.readlines(path).each do |fileline| (4) prot,user,pass,host = fileline.scan(/^(.*?):\/\/(.*?):(.*?)@(.*)$/).first
    if prot == known['protocol'] and host == known['host'] then
        puts "protocol=#{prot}"
        puts "host=#{host}"
        puts "username=#{user}"
        puts "password=#{pass}"
        exit(0)
    end
end
  1. 我们在这里解析命令行参数,允许用户指定输入文件,默认是 ~/.git-credentials.

  2. 这个程序只有在接受到 get 行为的请求并且后端存储的文件存在时才会有输出。

  3. 这个循环从标准输入读取数据,直到读取到第一个空行。 输入的数据被保存到 known 哈希表中,之后需要用到。

  4. 这个循环读取存储文件中的内容,寻找匹配的行。 如果 known 中的协议和主机名与该行相匹配,这个程序输出结果并退出。

我们把这个辅助工具保存为 git-credential-read-only,放到我们的 PATH 路径下并且给予执行权限。 一个交互式会话类似:

$ git credential-read-only --file=/mnt/shared/creds get
protocol=https
host=mygithost
protocol=https
host=mygithost
username=bob
password=s3cre7

由于这个的名字是 “git-” 开头,所以我们可以在配置值中使用简便的语法:

$ git config --global credential.helper read-only --file /mnt/shared/creds

正如你看到的,扩展这个系统是相当简单的,并且可以为你和你的团队解决一些常见问题。

发表评论
评论通过审核后显示。