Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support cloning over SSH via private key auth #170

Merged
merged 19 commits into from
May 3, 2024

Conversation

johnstcn
Copy link
Member

@johnstcn johnstcn commented May 2, 2024

Relates to #31

Currently running into issues with testing end-to-end flow.
However, I've tested with a private git repo:

$ docker run -it --rm -e GIT_URL="ssh://git@github.com/johnstcn/test-private-devcontainer" -e INIT_SCRIPT=bash -e GIT_SSH_PRIVATE_KEY_PATH=/.ssh/id_ed25519 -v /Users/cian/.ssh/id_ed25519:/.ssh/id_ed25519 envbuilder:latest
envbuilder - Build development environments from repositories in a container
ssh keyscan: github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
#1: 📦 Cloning ssh://git@github.com/johnstcn/test-private-devcontainer to /workspaces/test-private-devcontainer...
#1: Enumerating objects: 6, done.
#1: Counting objects:  16% (1/6)
#1: Counting objects:  33% (2/6)
#1: Counting objects:  50% (3/6)
#1: Counting objects:  66% (4/6)
#1: Counting obj
#1: ects:  83% (5/6)
#1: Counting objects: 100% (6/6)
#1: Counting objects: 100% (6/6), done.
#1: Compressing objects:  16% (1/6)
#1: Compressing objects:  33% (2/6)
#1: Compressing objects:  50% (3/6)
#1: Compressing objects:  66% (4/6)
#1: Compressing objects:  83% (5/6)
#1: Compressing objects: 100% (6/6)
#1: Compressing objects: 100% (6/6), done.
#1: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
#1: 📦 Cloned repository! [1.434724587s]

One potential issue for which I don't have a solution is that passing an SSH key in as a volume will result in the file being available in the resulting workspace. This may not be desirable; an alternative would be to pass in the SSH key as a base64-encoded file (similar to known_hosts) and clean it up after the build completes.

After this is merged, I'd like to do a follow-up PR to refactor some of the envbuilder setup code.

@johnstcn johnstcn self-assigned this May 2, 2024
Copy link
Member

@mafredri mafredri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an alternative would be to pass in the SSH key as a base64-encoded file (similar to known_hosts) and clean it up after the build completes.

I think this would be a solid first step at least. We can always iterate/add support for other use-cases later.

envbuilder.go Outdated Show resolved Hide resolved
envbuilder.go Outdated Show resolved Hide resolved
envbuilder.go Outdated Show resolved Hide resolved
git.go Outdated Show resolved Hide resolved
git.go Outdated Show resolved Hide resolved
git.go Outdated Show resolved Hide resolved
git_test.go Show resolved Hide resolved
envbuilder.go Outdated Show resolved Hide resolved
envbuilder.go Outdated Show resolved Hide resolved
git.go Outdated Show resolved Hide resolved
Copy link
Contributor

@BrunoQuaresma BrunoQuaresma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few comments but mostly are for my own learning 🙏

@johnstcn johnstcn marked this pull request as ready for review May 2, 2024 20:46
Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Mostly nits

envbuilder.go Outdated Show resolved Hide resolved
envbuilder.go Outdated Show resolved Hide resolved
func TestCloneRepoSSH(t *testing.T) {
t.Parallel()

t.Run("AuthSuccess", func(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe I'm missing something but since all of these tests share a basic structure, you could reduce them down to table tests to more clearly express the intent of what each test is doing and what its result should be; the boilerplate impedes readability.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed in principle; but I'd prefer to do this when I have more test cases from which to abstract.

git_test.go Outdated Show resolved Hide resolved
options.go Outdated Show resolved Hide resolved
@johnstcn
Copy link
Member Author

johnstcn commented May 3, 2024

I've simplifed the logic by removing the need to parse the git URL at all and removing the known_hosts business; if folks need to validate host keys they can simply mount in a known_hosts file and set SSH_KNOWN_HOSTS.
I've also extracted the logic for setting up repo auth into a separate function and added dedicated tests for this.

@johnstcn
Copy link
Member Author

johnstcn commented May 3, 2024

Bonus points: SSH_AUTH_SOCK also works!

docker run -it --rm -e GIT_URL="ssh://git@github.com/johnstcn/test-private-devcontainer" -e SSH_AUTH_SOCK=/tmp/ssh-Vsi58Zsh72/agent.1567 -v /tmp/ssh-Vsi58Zsh72/agent.1567:/tmp/ssh-Vsi58Zsh72/agent.1567 -e INIT_SCRIPT=bash envbuilder:latest
envbuilder - Build development environments from repositories in a container
#1: 📦 Cloning ssh://git@github.com/johnstcn/test-private-devcontainer to /workspaces/test-private-devcontainer...
#1: 🔑 Using SSH authentication!
#1: 🔑 No SSH key found, falling back to agent!
#1: 🔓 SSH_KNOWN_HOSTS not set, accepting all host keys!
#1: 🔑 Got host key: github.com,20.26.156.215 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=

Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slick! Love the changes, last comment from me

git_test.go Show resolved Hide resolved
func TestParseGitURL(t *testing.T) {
t.Parallel()
// nolint:paralleltest // t.Setenv for SSH_AUTH_SOCK
func TestSetupRepoAuth(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super!!

Copy link
Member

@mafredri mafredri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

git.go Show resolved Hide resolved
options.Logger(codersdk.LogLevelError, "#1: ❌ Failed to connect to SSH agent: %s", err.Error())
return nil // nothing else we can do
}
if os.Getenv("SSH_KNOWN_HOSTS") == "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: If this was moved higher up, we could avoid duplicating it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried doing this, but we need to either return *gitssh.PublicKeys or *gitssh.PublicKeysCallback. We can't just assign HostKeyCallback to transport.AuthMethods as it's an interface type :(

require.Nil(t, auth)
})

t.Run("HTTP/NoAuth", func(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: could avoid prefix if these were nested in an otherwise empty t.Run. Feel free to disregard if you don't want to do the change. 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one level of nesting is as deep as we need here in the test :-)

git.go Outdated Show resolved Hide resolved
@johnstcn johnstcn merged commit c08151b into main May 3, 2024
4 checks passed
@johnstcn johnstcn deleted the cj/ssh-private-key branch May 3, 2024 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants