Skip to content

Commit 1af7cb3

Browse files
committed
add license and readme
1 parent ee8f1bc commit 1af7cb3

File tree

3 files changed

+106
-4
lines changed

3 files changed

+106
-4
lines changed

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Sam Davidoff
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# git-credential-forwarder
2+
3+
A helper that allows forwarding git credential requests from one git instance (the "client") to another (the "server"). The purpose is to allow the client to use the git credentials or credential helper present on the host.
4+
5+
This was created for the specific use case of being able to run git inside a docker container while using the credential helper configured on the docker host. This, for example, allows development inside a linux-based docker development container while relying on credentials being managed by [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager) on the host. This helper is particularly useful in this scenario because the tools for using git-credential-manager inside a CLI-only linux container have [some](https://github.com/git-ecosystem/git-credential-manager/blob/release/docs/credstores.md) [limitations](https://github.com/git-ecosystem/git-credential-manager/issues/1549), particularly when used with [MSAL oauth2](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/3033).
6+
7+
Similar functionality is provided by VS Code's [Remote Containers extension](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials#_using-a-credential-helper), however using that requires using VS Code and allowing that extension to manage the containers. This helper allows this type of functionality with any Docker containers.
8+
9+
Because this helper is designed to allow sharing credentials on the same machine, it is setup to share credentials either over a file socket or a tcp connection on `localhost` using http. There is nothing in principle that would prevent listening for tcp connections from arbitrary locations on the network, and it would be trivial to edit this code to do that. But unless you take additional protections, this is a _bad idea_. The server's connections are not encrypted and note protected and so, if it is exposed on your network, it will accept any request for credentials and respond with the credentials in plaintext. See the Security section for more information.
10+
11+
## Installation and Usage
12+
13+
This helper is written in Typescript and compiles down to two javascript scripts, one for the server and one for the client. At this point, there is no installation tooling or bundling, and so the simplest way to run it is to clone this repo on both the host and inside the container. Once that is done, do the following:
14+
15+
### On the host
16+
17+
Run `pnpm install & pnpm build` or your favorite package tool to install dependencies and compile the app.
18+
19+
At the root of the repository, run `node dist/server/index.js`. This will launch the server and it will listen for TCP connections on localhost at a random port which will be displayed in the console. You will need to keep this console/terminal open.
20+
21+
Notes:
22+
23+
- You can tell it to use a specific port by setting the environmental variable `GIT_CREDENTIAL_FORWARDER_PORT`
24+
25+
### On the host
26+
27+
Run `pnpm install & pnpm build` or your favorite package tool to install dependencies and compile the app.
28+
29+
Run `export GIT_CREDENTIAL_FORWARDER_SERVER="host.docker.internal:PORT` where PORT is replaced with the port displayed when you ran the server.
30+
31+
Edit your git configuration file to call the client you just complied as a git credential helper, as follows:
32+
33+
```
34+
[credential]
35+
helper = "!f() { node ~/git-credential-forwarder/dist/client/index.js $*; }; f"
36+
```
37+
38+
Run git normally and all requests for credentials should be passed through to the host which will handle appropriately on the host side.
39+
40+
Notes:
41+
42+
- You can turn on more verbose debugging information by setting the environmental variable `GIT_CREDENTIAL_FORWARDER_DEBUG` to `true`
43+
44+
## Using a File Socket
45+
46+
By default the server uses a tcp server listening on `localhost`. You can tell it to use a file socket instead of tcp by setting the environmental variable `GIT_CREDENTIAL_FORWARDER_SOCKET` to the location you want the socket created. You must have permission to create a socket at that location, naturally.
47+
48+
On the client/container side, you need to bind mount the socket into your container and then run `export GIT_CREDENTIAL_FORWARDER_SERVER="/path/to/socket"`.
49+
50+
Note that this will not work from a Mac OS host per [this docker issue](https://github.com/docker/for-mac/issues/483)), which is a signficant limiation.
51+
52+
## Debugging
53+
54+
You can enable debugging on either the server or the client but setting the environmental variable GIT_CREDENTIAL_FORWARDER_DEBUG to true.
55+
56+
## Security
57+
58+
Nothing is perfectly secure, but I have tried to think through the security implications of running a helper like this. Here are some thoughts and I would definitely welcome any others in the issues or discussions sections:
59+
60+
- This app shouldn't expose your credentials outside of your machine, unless you go into the script and manually force the server to listen on an interface other than the localhost. That means that to gain access to your credentials an attacker would need to make a request on your local machine. If they can do that they could also run `git credential fill` and get your credentials, so I don't think this changes the threat model.
61+
62+
- OK, the above point isn't entirely correct. At least when the app is running in tcp mode (which is the default), in theory it is less secure than the `git credential fill` scenario because an attacker only needs to be running as _any user_ on your machine that has access to localhost, rather than as you (which they would need to run the direct `git credential` attack). I don't think that's a huge expansion of the threat model, at least for a developer on their own machine, but I'm interested in thinking of ways to make that harder.
63+
64+
- A key aspect of this utility is that the credentials will only exist in memory, i.e., the chain of container-git <-> client-helper <-> server-helper <-> host-git is ephemeral and will be destroyed when the processes shut down. That should limit the threat exposure to the same as when you normally run git (i.e., if someone can dump your memory they could dump your git memory as well). But it is worth thinking through places where the credentials could get accidentally stored to disk:
65+
- When you have debug mode turned on. The helper tries to sanitize debug login to avoid this, but it is still a risk.
66+
- Crash dumps. I'm not aware of anyway to crash this app in a way that will dump credentials, but I can't rule it out.
67+
- Other? I can't think of any other way, but let me know if you can...
68+
69+
## To Do
70+
71+
- Test more extensively. Mostly it's only be tested on Azure Devops and GitHub using git credential manager as the host credential helper.
72+
- Bundle the scripts and make the install easier.
73+
- More tests.
74+
- Maybe some utilities to make setting up the docker part easier to do as part of a Dockerfile

src/server/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,27 @@ if (serverType === "tcp" && portEnv) {
7676
case "ipc":
7777
appOutput(`Starting IPC server listening on socket ${deps.socketPath}`)
7878
instructions(
79-
`Bind mount this socket into your docker container and run the following command on your docker container (assuming you bind the socket at the same path):`
79+
`Bind mount this socket into your docker container and run the following command on your docker container (assuming you bind the socket at the same path):\n`
8080
)
81-
instructions(`\n export ${EnvKey.SERVER}="${deps.socketPath}"\n`)
81+
instructions(` export ${EnvKey.SERVER}="${deps.socketPath}"\n`)
8282
break
8383
case "tcp":
8484
appOutput(`Starting TCP server listening on ${deps.host}:${deps.port}`)
85-
instructions(`Run the following command in your docker container:`)
85+
instructions(`Run the following command in your docker container:\n`)
8686
instructions(
87-
`\n export ${EnvKey.SERVER}="${
87+
` export ${EnvKey.SERVER}="${
8888
deps.host === LOCALHOST ? DOCKER_HOST_IP : deps.host
8989
}:${deps.port}"\n`
9090
)
9191
break
9292
}
93+
instructions(
94+
`Edit your git configuration file inside your docker container to call the git-credential-forwarder client script, for example:\n`
95+
)
96+
instructions(` [credential]`)
97+
instructions(
98+
` helper = "!f() { node ~/git-credential-forwarder/dist/client/index.js $*; }; f"`
99+
)
93100

94101
try {
95102
await credentialReceiver()

0 commit comments

Comments
 (0)