Skip to content

Commit fcd3fe4

Browse files
Copilotcpuguy83
authored andcommitted
Add SSH known hosts support for git sources
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
1 parent e01b8f4 commit fcd3fe4

4 files changed

Lines changed: 65 additions & 5 deletions

File tree

docs/spec.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,6 +1680,12 @@
16801680
"boolean"
16811681
]
16821682
},
1683+
"sshKnownHosts": {
1684+
"type": [
1685+
"string",
1686+
"null"
1687+
]
1688+
},
16831689
"url": {
16841690
"type": [
16851691
"string"

source_git.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ import (
1212
)
1313

1414
type SourceGit struct {
15-
URL string `yaml:"url" json:"url"`
16-
Commit string `yaml:"commit" json:"commit"`
17-
KeepGitDir bool `yaml:"keepGitDir,omitempty" json:"keepGitDir,omitempty"`
18-
Auth GitAuth `yaml:"auth,omitempty" json:"auth,omitempty"`
15+
URL string `yaml:"url" json:"url"`
16+
Commit string `yaml:"commit" json:"commit"`
17+
KeepGitDir bool `yaml:"keepGitDir,omitempty" json:"keepGitDir,omitempty"`
18+
Auth GitAuth `yaml:"auth,omitempty" json:"auth,omitempty"`
19+
SSHKnownHosts string `yaml:"sshKnownHosts,omitempty" json:"sshKnownHosts,omitempty"`
1920
}
2021

2122
type GitAuth struct {
@@ -116,6 +117,10 @@ func (src *SourceGit) baseState(opts fetchOptions) llb.State {
116117
gOpts = append(gOpts, WithConstraints(opts.Constraints...))
117118
gOpts = append(gOpts, &src.Auth)
118119

120+
if src.SSHKnownHosts != "" {
121+
gOpts = append(gOpts, llb.KnownSSHHosts(src.SSHKnownHosts))
122+
}
123+
119124
return llb.Git(src.URL, src.Commit, gOpts...)
120125
}
121126

@@ -156,7 +161,8 @@ func (src *SourceGit) processBuildArgs(lex *shell.Lex, args map[string]string, a
156161
if err != nil {
157162
errs = append(errs, err)
158163
}
159-
if len(errs) > 1 {
164+
165+
if len(errs) > 0 {
160166
return fmt.Errorf("failed to process build args for git source: %w", stderrors.Join(errs...))
161167
}
162168
return nil

source_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,20 @@ func TestSourceGitSSH(t *testing.T) {
9797
checkGitOp(t, ops, &src)
9898
})
9999

100+
t.Run("with known hosts", func(t *testing.T) {
101+
knownHosts := "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7"
102+
src := Source{
103+
Git: &SourceGit{
104+
URL: fmt.Sprintf("user@%s:test.git", addr),
105+
Commit: t.Name(),
106+
SSHKnownHosts: knownHosts,
107+
},
108+
}
109+
110+
ops := getSourceOp(ctx, t, src)
111+
checkGitOp(t, ops, &src)
112+
})
113+
100114
}
101115

102116
func TestSourceGitHTTP(t *testing.T) {
@@ -1061,6 +1075,18 @@ func checkGitOp(t *testing.T, ops []*pb.Op, src *Source) {
10611075
}
10621076
assert.Check(t, cmp.Equal(op.Attrs["git.mountsshsock"], ssh), op)
10631077
}
1078+
1079+
// Check known hosts if set
1080+
if src.Git.SSHKnownHosts != "" {
1081+
// BuildKit's KnownSSHHosts option may add formatting like newlines
1082+
actualKnownHosts := op.Attrs["git.knownsshhosts"]
1083+
expectedKnownHosts := src.Git.SSHKnownHosts
1084+
1085+
// Remove trailing whitespace for comparison since BuildKit may add formatting
1086+
actualTrimmed := strings.TrimSpace(actualKnownHosts)
1087+
expectedTrimmed := strings.TrimSpace(expectedKnownHosts)
1088+
assert.Check(t, cmp.Equal(actualTrimmed, expectedTrimmed), "Expected: %q, Got: %q", expectedKnownHosts, actualKnownHosts)
1089+
}
10641090
}
10651091

10661092
func checkGitAuth(t *testing.T, m map[string]*pb.Op, ops []*pb.Op, src *Source, expectedNumSecrets, expectedNumSSH int) {

website/docs/sources.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,28 @@ git auth section (shown below with default values):
9595
Note: These are secret names which are used to reference the secrets provided
9696
by the client, not the actual secret values.
9797

98+
#### SSH Known Hosts
99+
100+
For SSH-based git URLs, you can specify known SSH host keys to improve security
101+
and avoid Trust-On-First-Use (TOFU) behavior. When known hosts are provided,
102+
the SSH connection will verify the host key against the specified keys and fail
103+
if they don't match.
104+
105+
```yaml
106+
someSource1:
107+
git:
108+
url: git@github.com:myOrg/myRepo.git
109+
commit: 1234567890abcdef
110+
sshKnownHosts: |
111+
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7vbqbLJofwIHMHnSVPP0k+aLU6X5OtN6a1r9K4kS...
112+
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
113+
```
114+
115+
When known hosts are not specified, BuildKit will use Trust-On-First-Use (TOFU)
116+
behavior, where it will connect to the SSH server, retrieve the host key, and
117+
use that for the connection. Specifying known hosts avoids this extra connection
118+
and provides better security by ensuring you're connecting to the expected server.
119+
98120
### HTTP
99121

100122
HTTP sources fetch a file from an HTTP URL. The HTTP source type is considered to be a "file" source.

0 commit comments

Comments
 (0)