func fingerprintCallback(opts *options.SSHOptions, expectedFingerprint string) hostKeyCallback { if opts.SkipHostValidation { return nil } return func(hostname string, remote net.Addr, key ssh.PublicKey) error { switch len(expectedFingerprint) { case helpers.SHA1_FINGERPRINT_LENGTH: fingerprint := helpers.SHA1Fingerprint(key) if fingerprint != expectedFingerprint { return fmt.Errorf("Host key verification failed.\n\nThe fingerprint of the received key was %q.", fingerprint) } case helpers.MD5_FINGERPRINT_LENGTH: fingerprint := helpers.MD5Fingerprint(key) if fingerprint != expectedFingerprint { return fmt.Errorf("Host key verification failed.\n\nThe fingerprint of the received key was %q.", fingerprint) } case 0: fingerprint := helpers.MD5Fingerprint(key) return fmt.Errorf("Unable to verify identity of host.\n\nThe fingerprint of the received key was %q.", fingerprint) default: return errors.New("Unsupported host key fingerprint format") } return nil } }
func NewClientConn(logger lager.Logger, permissions *ssh.Permissions) (ssh.Conn, <-chan ssh.NewChannel, <-chan *ssh.Request, error) { if permissions == nil || permissions.CriticalOptions == nil { err := errors.New("Invalid permissions from authentication") logger.Error("permissions-and-critical-options-required", err) return nil, nil, nil, err } targetConfigJson := permissions.CriticalOptions["proxy-target-config"] logger = logger.Session("new-client-conn", lager.Data{ "proxy-target-config": targetConfigJson, }) var targetConfig TargetConfig err := json.Unmarshal([]byte(permissions.CriticalOptions["proxy-target-config"]), &targetConfig) if err != nil { logger.Error("unmarshal-failed", err) return nil, nil, nil, err } nConn, err := net.Dial("tcp", targetConfig.Address) if err != nil { logger.Error("dial-failed", err) return nil, nil, nil, err } clientConfig := &ssh.ClientConfig{} if targetConfig.User != "" { clientConfig.User = targetConfig.User } if targetConfig.PrivateKey != "" { key, err := ssh.ParsePrivateKey([]byte(targetConfig.PrivateKey)) if err != nil { logger.Error("parsing-key-failed", err) return nil, nil, nil, err } clientConfig.Auth = append(clientConfig.Auth, ssh.PublicKeys(key)) } if targetConfig.User != "" && targetConfig.Password != "" { clientConfig.Auth = append(clientConfig.Auth, ssh.Password(targetConfig.Password)) } if targetConfig.HostFingerprint != "" { clientConfig.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error { expectedFingerprint := targetConfig.HostFingerprint var actualFingerprint string switch utf8.RuneCountInString(expectedFingerprint) { case helpers.MD5_FINGERPRINT_LENGTH: actualFingerprint = helpers.MD5Fingerprint(key) case helpers.SHA1_FINGERPRINT_LENGTH: actualFingerprint = helpers.SHA1Fingerprint(key) } if expectedFingerprint != actualFingerprint { err := errors.New("Host fingerprint mismatch") logger.Error("host-key-fingerprint-mismatch", err) return err } return nil } } conn, ch, req, err := ssh.NewClientConn(nConn, targetConfig.Address, clientConfig) if err != nil { logger.Error("handshake-failed", err) return nil, nil, nil, err } return conn, ch, req, nil }
"proxy-target-config": string(targetConfigJson), }, } proxyAuthenticator.AuthenticateReturns(permissions, nil) }) It("handshakes with the target using the provided configuration", func() { Eventually(daemonAuthenticator.AuthenticateCallCount).Should(Equal(1)) }) }) Context("when the host fingerprint is a sha1 hash", func() { BeforeEach(func() { targetConfigJson, err := json.Marshal(proxy.TargetConfig{ Address: sshdListener.Addr().String(), HostFingerprint: helpers.SHA1Fingerprint(TestHostKey.PublicKey()), User: "******", Password: "******", }) Expect(err).NotTo(HaveOccurred()) permissions := &ssh.Permissions{ CriticalOptions: map[string]string{ "proxy-target-config": string(targetConfigJson), }, } proxyAuthenticator.AuthenticateReturns(permissions, nil) }) It("handshakes with the target using the provided configuration", func() { Eventually(daemonAuthenticator.AuthenticateCallCount).Should(Equal(1))
}) Describe("MD5 Fingerprint", func() { BeforeEach(func() { fingerprint = helpers.MD5Fingerprint(publicKey) }) It("should have the correct length", func() { Expect(utf8.RuneCountInString(fingerprint)).To(Equal(helpers.MD5_FINGERPRINT_LENGTH)) }) It("should match the expected fingerprint", func() { Expect(fingerprint).To(Equal(ExpectedMD5Fingerprint)) }) }) Describe("SHA1 Fingerprint", func() { BeforeEach(func() { fingerprint = helpers.SHA1Fingerprint(publicKey) }) It("should have the correct length", func() { Expect(utf8.RuneCountInString(fingerprint)).To(Equal(helpers.SHA1_FINGERPRINT_LENGTH)) }) It("should match the expected fingerprint", func() { Expect(fingerprint).To(Equal(ExpectedSHA1Fingerprint)) }) }) })