Skip to content

Commit

Permalink
Pass port to ssh-keyscan when known_hosts not provided
Browse files Browse the repository at this point in the history
Creds-init performs an ssh-keyscan if no known_hosts file is
provided as part of a Secret. When the ssh server is using
a custom port ssh-keyscan expects the port to be provided
with the -p flag. Currently Tekton does not provide the flag
resulting in failure to generate known_hosts. The error for
this failure is also very opaque - manifesting as an "invalid
flag" message in the creds-init initContainer log and making
no mention of ssh-keyscan.

This commit:
- adds the -p flag to ssh-keyscan calls when a port is specified
in the given git URL.
- adds an additional note to auth.md mentioning ssh-keyscan
- wraps any error returned by ssh-keyscan to mention the utility,
hopefully aiding future debugging
  • Loading branch information
Scott authored and tekton-robot committed Jun 11, 2020
1 parent 3b95494 commit 1caf5bf
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ aggregates them into their respective files in `$HOME`.
data:
ssh-privatekey: <base64 encoded>
# This is non-standard, but its use is encouraged to make this more secure.
# If it is not provided then the git server's public key will be requested
# with `ssh-keyscan` during credential initialization.
known_hosts: <base64 encoded>
```
Expand Down
26 changes: 21 additions & 5 deletions pkg/credentials/gitcreds/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,13 @@ func (be *sshEntry) path(sshDir string) string {
return filepath.Join(sshDir, "id_"+be.secretName)
}

func sshKeyScan(domain string) ([]byte, error) {
c := exec.Command("ssh-keyscan", domain)
func sshKeyScan(host, port string) ([]byte, error) {
var c *exec.Cmd
if port == "" {
c = exec.Command("ssh-keyscan", host)
} else {
c = exec.Command("ssh-keyscan", host, "-p", port)
}
var output bytes.Buffer
c.Stdout = &output
c.Stderr = &output
Expand All @@ -138,11 +143,21 @@ func sshKeyScan(domain string) ([]byte, error) {
return output.Bytes(), nil
}

// sshKeyScanArgs returns the host and optional port for running ssh-keyscan
// given a url.
func sshKeyScanArgs(url string) (string, string) {
host, port, err := net.SplitHostPort(url)
if err != nil {
return url, ""
}
return host, port
}

func (be *sshEntry) Write(sshDir string) error {
return ioutil.WriteFile(be.path(sshDir), []byte(be.privateKey), 0600)
}

func newSSHEntry(u, secretName string) (*sshEntry, error) {
func newSSHEntry(url, secretName string) (*sshEntry, error) {
secretPath := credentials.VolumeName(secretName)

pk, err := ioutil.ReadFile(filepath.Join(secretPath, corev1.SSHAuthPrivateKey))
Expand All @@ -153,9 +168,10 @@ func newSSHEntry(u, secretName string) (*sshEntry, error) {

kh, err := ioutil.ReadFile(filepath.Join(secretPath, sshKnownHosts))
if err != nil {
kh, err = sshKeyScan(u)
host, port := sshKeyScanArgs(url)
kh, err = sshKeyScan(host, port)
if err != nil {
return nil, err
return nil, fmt.Errorf("ssh-keyscan error: %w", err)
}
}
knownHosts := string(kh)
Expand Down
51 changes: 51 additions & 0 deletions pkg/credentials/gitcreds/ssh_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright 2020 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gitcreds

import (
"testing"
)

func TestSSHKeyScanArgs(t *testing.T) {
for _, tc := range []struct {
url string
expectedHost string
expectedPort string
}{{
url: "github.com",
expectedHost: "github.com",
}, {
url: "customdomain.com",
expectedHost: "customdomain.com",
}, {
url: "customdomain.com:22",
expectedHost: "customdomain.com",
expectedPort: "22",
}, {
url: "git.customdomain.com:7779",
expectedHost: "git.customdomain.com",
expectedPort: "7779",
}} {
host, port := sshKeyScanArgs(tc.url)
if host != tc.expectedHost {
t.Errorf("expected host %q received %q", tc.expectedHost, host)
}
if port != tc.expectedPort {
t.Errorf("expected port %q received %q", tc.expectedPort, port)
}
}
}

0 comments on commit 1caf5bf

Please sign in to comment.