可以创建一个Git助手或插件,以添加HTTP标头或强制Bearer令牌身份验证。

huangapple go评论57阅读模式
英文:

Is it possible to create a Git helper or plugin that adds HTTP headers or forces Bearer Token authentication

问题

背景:我们使用Bitbucket服务器(即将升级/切换到Bitbucket DataCenter)。在我们的身份验证设置中,我们禁用了用户密码(Web身份验证通过不同的方式进行),因此对于Bitbucket/Git的用途,我们使用Bitbucket个人访问令牌(PAT)。 (在我们的组织中,SSH密钥身份验证不是一个选项。)对于克隆/推送等操作,我们一直在提示输入凭据时使用我们的用户名和PAT。这种方法有效,但由于我们身份验证设置的原因,会导致我们的自动构建和大规模用户交互的本地构建性能问题。

与使用HTTP(S)基本身份验证不同,Bitbucket还支持HTTP(S) Bearer令牌身份验证,您只需提供PAT而不需要用户名。这样做可以解决性能问题。在REST API请求上这样做很容易。但在Git上这样做要复杂一些。Bitbucket文档推荐以下做法:

$ git clone -c http.extraHeader='Authorization: Bearer xxxx' https://bitbucketserver.com/scm/projectname/teamsinspace.git

这个方法有效!但是它会在命令历史记录等地方暴露PAT。还有一种更安全的方法不会暴露PAT:

$ git clone --config-env=http.extraHeader=GIT_AUTHORIZATION_HEADER https://bitbucketserver.com/scm/projectname/teamsinspace.git

其中 GIT_AUTHORIZATION_HEADERAuthorization: Bearer xxxx。但这仍然有一些缺点,即它无法使用 .gitconfig 并且必须添加到每个命令中,用户必须提前配置它,不能提示输入PAT以安全地存储在Git凭据助手中。

GitLab提供了一个"魔术用户名",OAuth2,可以通过基本身份验证与令牌一起提供,实际上告诉服务器"忽略用户名,仅使用令牌进行身份验证"。Bitbucket也提供了类似的"魔术用户名",x-token-auth,但看起来可能只限于云端或仅限于存储库访问令牌,因为我们尝试使用它时只会导致身份验证错误。如果有这样一个功能,我们可以在 .gitconfig 中硬编码魔术用户名,开发者只需提示输入"x-token-auth@our-server.com的密码",这将是PAT。但遗憾的是,我们无法使用这个功能。

大约14年前,有一个Git邮件列表上的讨论,关于支持Bearer令牌身份验证,甚至提出了一个补丁,但似乎没有进展。从理论上讲,我可以应用这个补丁的现代化版本并重新编译Git,但这对我们的开发者组织来说不是一个理想的解决方案。

我曾经研究过创建一个Git凭据助手(我以前为其他原因创建过),以及一个Git主机提供者,但Git中与凭据相关的一切都是硬编码为仅支持用户名/密码,所以这些方法不起作用。

所以我正在寻找/希望有一种方法可以创建一种助手、提供者或插件,可以指定、覆盖或替换HTTP Authorization 头,使其成为Bearer而不是Basic,可以使用内置的Git凭据信息的密码,或者如果需要,可以提示输入新的密码,而无需编译我们自己定制的Git分支。这有可能吗,还是我应该放弃并使用 --config-env

英文:

Background: We use Bitbucket Server (soon to upgrade/switch to Bitbucket DataCenter). In our authentication setup, we have user passwords disabled (web auth is via a different means), so for Bitbucket/Git purposes, we use Bitbucket Personal Access Tokens (PAT). (SSH key authentication is not an option in our organization.) For cloning/pushing/etc., we have been using our username and PAT when prompted for credentials. This works, but for reasons I can't get into regarding our authentication setup, it is creating performance issues related to our automated builds and also large user-interactive local builds.

Rather than use HTTP(S) Basic authentication, Bitbucket also supports HTTP(S) Bearer token authentication, where you supply just the PAT without the username. Doing this solves our performance issues. Doing so on REST API requests is easy. Doing so on Git is trickier. This Bitbucket documentation recommends doing this:

$ git clone -c http.extraHeader='Authorization: Bearer xxxx' https://bitbucketserver.com/scm/projectname/teamsinspace.git

This works! But it exposes the PAT to others via command history, etc. A more secure variety that doesn't expose the PAT also works:

$ git clone --config-env=http.extraHeader=GIT_AUTHORIZATION_HEADER https://bitbucketserver.com/scm/projectname/teamsinspace.git

Where GIT_AUTHORIZATION_HEADER is Authorization: Bearer xxxx. But this still has some downsides, namely that it can't employ .gitconfig and must be added to every command, and that users have to configure this ahead of time and can't be prompted to enter the PAT that gets stored securely in the Git credential helper.

GitLab provides a "magic username," OAuth2, that can be provided with the token through Basic authentication that essentially tells the server "ignore the username, authenticate with the token only." Bitbucket also provides a similar "magic username," x-token-auth, but it appears to either be Cloud-only or limited to Repository Access Tokens or both, because we have tried to use it and it just results in authentication errors. Access to such a feature would allow us to hard-code the magic username in .gitconfig and the developer would merely get prompted for the "password for x-token-auth@our-server.com," which would be the PAT. But, alas, we can't use this feature.

About 14 years ago there was a discussion on a Git mailing list about supporting Bearer token authentication, and even a patch proposed, but it doesn't look like it ever went anywhere. I could, in theory, apply a modernized version of that patch and recompile Git, but that is really not an ideal solution for our developer organization.

I looked into creating a Git credential helper (which I've actually created before for other reasons) and a Git host provider, but everything credential-related baked into Git is hard-coded to username/password only, so those don't work.

So what I'm looking for / hoping for is some kind of way to create a helper, provider, or plugin of some type that will be able to specify, override, or replace the HTTP Authorization header to make it Bearer instead of Basic, either using the password that comes from built-in Git credential stuff or prompting for something new if necessary, without having to compile our own customized Git fork. Is this at all possible, or should I just give up and use --config-env?

答案1

得分: 3

绝对不以此为荣,但在CI环境中不得不采取一些不太光彩的方法,也是因为"某些原因"...

这两种解决方案都不是理想的,但最简单的方法是创建一个Shell别名或在你的gitconfig中添加以下内容,并将其添加到你的.bashrc/.zshrc中,以从标准输入中读取令牌:

## 示例显示头部已正确设置,但不会在命令行历史中显示实际值
## 用检索令牌的命令/脚本替换`whoami`命令
$ GIT_CURL_VERBOSE=1 git -c http.extraHeader="Hello: $(whoami)" ls-remote https://github.com/deric4/github-action-runner-reference
...
15:43:52.454783 http.c:701              == Info: using HTTP/2
15:43:52.454838 http.c:701              == Info: h2h3 [:method: GET]
15:43:52.454844 http.c:701              == Info: h2h3 [:path: /deric4/github-action-runner-reference/info/refs?service=git-upload-pack]
15:43:52.454849 http.c:701              == Info: h2h3 [:scheme: https]
15:43:52.454853 http.c:701              == Info: h2h3 [:authority: github.com]
15:43:52.454857 http.c:701              == Info: h2h3 [user-agent: git/2.39.2 (Apple Git-143)]
15:43:52.454861 http.c:701              == Info: h2h3 [accept: */*]
15:43:52.454865 http.c:701              == Info: h2h3 [accept-encoding: deflate, gzip]
15:43:52.454869 http.c:701              == Info: h2h3 [hello: deric4]
15:43:52.454873 http.c:701              == Info: h2h3 [pragma: no-cache]
15:43:52.454876 http.c:701              == Info: h2h3 [git-protocol: version=2]
...
# .gitconfig
[alias]
	myalias = "!f() { GIT_CURL_VERBOSE=1 git -c http.extraHeader=\"Hello: $(whoami)\" ls-remote $1 ; }; f"

一个更激进的方法是实现一个自定义远程助手,以替代或补充凭证助手。

我找到了一个旧的示例,虽然我对此并不感到自豪,但完成了工作:

这将允许根据执行上下文动态获取和设置配置/凭证...

git clone accesstoken://some-profile@foo-bar-baz

// 注意:这是在Go 1.12中编写的,自那以来我没有再看过它...
func main() {

	remoteName := os.Args[1]
	repoUrl := os.Args[2]

	u, err := url.Parse(repoUrl)
	if err != nil {
		log.Fatal(err)
	}
    
    // 替换了复杂的步骤,只需获取环境变量
	accessToken := os.Getenv("GIT_ACCESS_TOKEN")

	// 获取用户名,以便在添加密码时重置
	username := u.User.Username()

	// 构造<username>:<password>
	u.User = url.UserPassword(username, accessToken)

	// 从accesstoken转换为https
	u.Scheme = "https"

	cmd := exec.Command("git", "remote-http", remoteName, u.String())
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
}

但在你的情况下

    // 获取持有者令牌的自定义逻辑
    
	cmd := exec.Command("git", "-c", "http.extraHeader='Bearer: <token from somewhere>'", "remote-http", remoteName, u.String())

英文:

Definitely not proud of this but have had to resort to something dirty things in CI environments, also because of "reasons"...

Neither of these solutions are ideal, but
the simplest would be to create a shell alias or in your gitconfig and add to your .bashrc/.zshrc that reads the token from stdin

## example that shows header is set correctly but does not show up actual value in command line history  
## replace `whoami` command with command/script to retrieve token
$ GIT_CURL_VERBOSE=1 git -c http.extraHeader=&quot;Hello: $(whoami)&quot; ls-remote https://github.com/deric4/github-action-runner-reference
...
15:43:52.454783 http.c:701              == Info: using HTTP/2
15:43:52.454838 http.c:701              == Info: h2h3 [:method: GET]
15:43:52.454844 http.c:701              == Info: h2h3 [:path: /deric4/github-action-runner-reference/info/refs?service=git-upload-pack]
15:43:52.454849 http.c:701              == Info: h2h3 [:scheme: https]
15:43:52.454853 http.c:701              == Info: h2h3 [:authority: github.com]
15:43:52.454857 http.c:701              == Info: h2h3 [user-agent: git/2.39.2 (Apple Git-143)]
15:43:52.454861 http.c:701              == Info: h2h3 [accept: */*]
15:43:52.454865 http.c:701              == Info: h2h3 [accept-encoding: deflate, gzip]
15:43:52.454869 http.c:701              == Info: h2h3 [hello: deric4]
15:43:52.454873 http.c:701              == Info: h2h3 [pragma: no-cache]
15:43:52.454876 http.c:701              == Info: h2h3 [git-protocol: version=2]
...
# .gitconfig
[alias]
	myalias = &quot;!f() { GIT_CURL_VERBOSE=1 git -c http.extraHeader=\&quot;Hello: $(whoami)\&quot; ls-remote $1 ; }; f&quot;

A more drastic approach would be to implement a custom remote helper in addition/place of credential helper.

I was able to dig up an old example that i'm not proud of but got the job done:

This would allow for dynamically getting and setting config/credentials
based on execution context...

git clone accesstoken://some-profile@foo-bar-baz

// Note: that this was written in go 1.12 and hadn&#39;t looked at it since then so...
func main() {

	remoteName := os.Args[1]
	repoUrl := os.Args[2]

	u, err := url.Parse(repoUrl)
	if err != nil {
		log.Fatal(err)
	}
    
    // replaced complex steps to just getting the env var
	accessToken := os.Getenv(&quot;GIT_ACCESS_TOKEN&quot;)

	// get username so it can be reset when password is added
	username := u.User.Username()

	// construct &lt;username&gt;:&lt;password&gt;
	u.User = url.UserPassword(username, accessToken)

	// convert from accesstoken -&gt; https
	u.Scheme = &quot;https&quot;

	cmd := exec.Command(&quot;git&quot;, &quot;remote-http&quot;, remoteName, u.String())
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
}

but in your case

    // custom logic to get bearer token
    
	cmd := exec.Command(&quot;git&quot;, &quot;-c&quot;, &quot;http.extraHeader=&#39;Bearer: &lt;token from somewhere&gt;&#39;&quot;, &quot;remote-http&quot;, remoteName, u.String())

答案2

得分: 0

我们使用环境变量来避免更改配置文件,请参阅此链接:https://github.com/microsoft/azure-pipelines-agent/issues/1601#issuecomment-1683714305

英文:

We use environment variables to avoid changing config files, see this https://github.com/microsoft/azure-pipelines-agent/issues/1601#issuecomment-1683714305

huangapple
  • 本文由 发表于 2023年6月15日 01:03:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76475970.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定