diff options
author | Unknwon <u@gogs.io> | 2016-02-27 20:48:39 -0500 |
---|---|---|
committer | Unknwon <u@gogs.io> | 2016-02-27 20:48:39 -0500 |
commit | 8055a0bdac005189077df69870cd2ed0b7eebd56 (patch) | |
tree | bf3fa2bf419006ea846315eef48fb5e83378376c /models | |
parent | 83c74878df3f6edec4fca51e9cec299d2a1bb897 (diff) | |
download | gitea-8055a0bdac005189077df69870cd2ed0b7eebd56.tar.gz gitea-8055a0bdac005189077df69870cd2ed0b7eebd56.zip |
Post work for #2637
Improve test cases, config settings, also show SSH config settings on admin config panel.
Diffstat (limited to 'models')
-rw-r--r-- | models/repo.go | 6 | ||||
-rw-r--r-- | models/ssh_key.go | 114 | ||||
-rw-r--r-- | models/ssh_key_test.go | 64 |
3 files changed, 92 insertions, 92 deletions
diff --git a/models/repo.go b/models/repo.go index 089a5998e7..75a4e66033 100644 --- a/models/repo.go +++ b/models/repo.go @@ -457,10 +457,10 @@ func (repo *Repository) cloneLink(isWiki bool) *CloneLink { repo.Owner = repo.MustOwner() cl := new(CloneLink) - if setting.SSHPort != 22 { - cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.SSHDomain, setting.SSHPort, repo.Owner.Name, repoName) + if setting.SSH.Port != 22 { + cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.SSH.Domain, setting.SSH.Port, repo.Owner.Name, repoName) } else { - cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.SSHDomain, repo.Owner.Name, repoName) + cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.SSH.Domain, repo.Owner.Name, repoName) } cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.Name, repoName) return cl diff --git a/models/ssh_key.go b/models/ssh_key.go index c22b931c88..86c1a25fe2 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -16,7 +16,6 @@ import ( "os" "path" "path/filepath" - "strconv" "strings" "sync" "time" @@ -35,10 +34,7 @@ const ( _TPL_PUBLICK_KEY = `command="%s serv key-%d --config='%s'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n" ) -var ( - sshOpLocker = sync.Mutex{} - SSHUnknownKeyType = fmt.Errorf("unknown key type") -) +var sshOpLocker = sync.Mutex{} type KeyType int @@ -83,19 +79,18 @@ func (key *PublicKey) GetAuthorizedString() string { func extractTypeFromBase64Key(key string) (string, error) { b, err := base64.StdEncoding.DecodeString(key) if err != nil || len(b) < 4 { - return "", errors.New("Invalid key format") + return "", fmt.Errorf("Invalid key format: %v", err) } keyLength := int(binary.BigEndian.Uint32(b)) - if len(b) < 4+keyLength { - return "", errors.New("Invalid key format") + return "", fmt.Errorf("Invalid key format: not enough length") } return string(b[4 : 4+keyLength]), nil } -// parseKeyString parses any key string in openssh or ssh2 format to clean openssh string (rfc4253) +// parseKeyString parses any key string in OpenSSH or SSH2 format to clean OpenSSH string (RFC4253) func parseKeyString(content string) (string, error) { // Transform all legal line endings to a single "\n" s := strings.Replace(strings.Replace(strings.TrimSpace(content), "\r\n", "\n", -1), "\r", "\n", -1) @@ -158,50 +153,53 @@ func parseKeyString(content string) (string, error) { return keyType + " " + keyContent + " " + keyComment, nil } -// extract key type and length using ssh-keygen +// writeTmpKeyFile writes key content to a temporary file +// and returns the name of that file, along with any possible errors. +func writeTmpKeyFile(content string) (string, error) { + tmpFile, err := ioutil.TempFile(setting.SSH.KeyTestPath, "gogs_keytest") + if err != nil { + return "", fmt.Errorf("TempFile: %v", err) + } + defer tmpFile.Close() + + if _, err = tmpFile.WriteString(content); err != nil { + return "", fmt.Errorf("tmpFile.WriteString: %v", err) + } + return tmpFile.Name(), nil +} + +// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen. func SSHKeyGenParsePublicKey(key string) (string, int, error) { // The ssh-keygen in Windows does not print key type, so no need go further. if setting.IsWindows { return "", 0, nil } - tmpFile, err := ioutil.TempFile(setting.SSHWorkPath, "gogs_keytest") + tmpName, err := writeTmpKeyFile(key) if err != nil { - return "", 0, err + return "", 0, fmt.Errorf("writeTmpKeyFile: %v", err) } - tmpName := tmpFile.Name() defer os.Remove(tmpName) - if ln, err := tmpFile.WriteString(key); err != nil { - tmpFile.Close() - return "", 0, err - } else if ln != len(key) { - tmpFile.Close() - return "", 0, fmt.Errorf("could not write complete public key (written: %d, should be: %d): %s", ln, len(key), key) - } - tmpFile.Close() - - stdout, stderr, err := process.Exec("CheckPublicKeyString", setting.SSHKeyGenPath, "-lf", tmpName) + stdout, stderr, err := process.Exec("SSHKeyGenParsePublicKey", setting.SSH.KeygenPath, "-lf", tmpName) if err != nil { - return "", 0, fmt.Errorf("public key check failed with error '%s': %s", err, stderr) + return "", 0, fmt.Errorf("Fail to parse public key: %s - %s", err, stderr) } - if strings.HasSuffix(stdout, "is not a public key file.") { - return "", 0, SSHUnknownKeyType + if strings.Contains(stdout, "is not a public key file") { + return "", 0, ErrKeyUnableVerify{stdout} } + fields := strings.Split(stdout, " ") if len(fields) < 4 { - return "", 0, fmt.Errorf("invalid public key line: %s", stdout) + return "", 0, fmt.Errorf("Invalid public key line: %s", stdout) } - length, err := strconv.Atoi(fields[0]) - if err != nil { - return "", 0, err - } keyType := strings.Trim(fields[len(fields)-1], "()\r\n") - return strings.ToLower(keyType), length, nil + return strings.ToLower(keyType), com.StrTo(fields[0]).MustInt(), nil } -// extract the key type and length using the golang ssh library +// SSHNativeParsePublicKey extracts the key type and length using the golang SSH library. +// NOTE: ed25519 is not supported. func SSHNativeParsePublicKey(keyLine string) (string, int, error) { fields := strings.Fields(keyLine) if len(fields) < 2 { @@ -215,14 +213,13 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) { pkey, err := ssh.ParsePublicKey(raw) if err != nil { - if strings.HasPrefix(err.Error(), "ssh: unknown key algorithm") { - return "", 0, SSHUnknownKeyType + if strings.Contains(err.Error(), "ssh: unknown key algorithm") { + return "", 0, ErrKeyUnableVerify{err.Error()} } - return "", 0, err + return "", 0, fmt.Errorf("ssh.ParsePublicKey: %v", err) } - // The ssh library can parse the key, so next we find out what key exactly we - // have. + // The ssh library can parse the key, so next we find out what key exactly we have. switch pkey.Type() { case ssh.KeyAlgoDSA: rawPub := struct { @@ -253,16 +250,18 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) { return "ecdsa", 521, nil case "ssh-ed25519": // TODO replace with ssh constant when available return "ed25519", 256, nil - default: - return "", 0, fmt.Errorf("no support for key length detection for type %s", pkey.Type()) } - return "", 0, fmt.Errorf("SSHNativeParsePublicKey failed horribly, please investigate why") + return "", 0, fmt.Errorf("Unsupported key length detection for type: %s", pkey.Type()) } // CheckPublicKeyString checks if the given public key string is recognized by SSH. // // The function returns the actual public key line on success. func CheckPublicKeyString(content string) (_ string, err error) { + if setting.SSH.Disabled { + return "", errors.New("SSH is disabled") + } + content, err = parseKeyString(content) if err != nil { return "", err @@ -280,30 +279,25 @@ func CheckPublicKeyString(content string) (_ string, err error) { keyType string length int ) - if setting.SSHPublicKeyCheck == setting.SSH_PUBLICKEY_CHECK_NATIVE { + if setting.SSH.StartBuiltinServer { keyType, length, err = SSHNativeParsePublicKey(content) - } else if setting.SSHPublicKeyCheck == setting.SSH_PUBLICKEY_CHECK_KEYGEN { - keyType, length, err = SSHKeyGenParsePublicKey(content) } else { - log.Error(4, "invalid public key check type: %s", setting.SSHPublicKeyCheck) - return "", fmt.Errorf("invalid public key check type") + keyType, length, err = SSHKeyGenParsePublicKey(content) } - if err != nil { - log.Trace("invalid public key of type '%s' with length %d: %s", keyType, length, err) return "", fmt.Errorf("ParsePublicKey: %v", err) } - log.Trace("Key type: %s", keyType) + log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length) - if !setting.Service.EnableMinimumKeySizeCheck { + if !setting.SSH.MinimumKeySizeCheck { return content, nil } - if minLen, found := setting.Service.MinimumKeySizes[keyType]; found && length >= minLen { + if minLen, found := setting.SSH.MinimumKeySizes[keyType]; found && length >= minLen { return content, nil } else if found && length < minLen { - return "", fmt.Errorf("key not large enough - got %d, needs %d", length, minLen) + return "", fmt.Errorf("Key length is not enough: got %d, needs %d", length, minLen) } - return "", fmt.Errorf("key type '%s' is not allowed", keyType) + return "", fmt.Errorf("Key type is not allowed: %s", keyType) } // saveAuthorizedKeyFile writes SSH key content to authorized_keys file. @@ -311,7 +305,7 @@ func saveAuthorizedKeyFile(keys ...*PublicKey) error { sshOpLocker.Lock() defer sshOpLocker.Unlock() - fpath := filepath.Join(setting.SSHRootPath, "authorized_keys") + fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys") f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return err @@ -379,7 +373,7 @@ func addKey(e Engine, key *PublicKey) (err error) { } // Don't need to rewrite this file if builtin SSH server is enabled. - if setting.StartSSHServer { + if setting.SSH.StartBuiltinServer { return nil } return saveAuthorizedKeyFile(key) @@ -529,12 +523,12 @@ func deletePublicKey(e *xorm.Session, keyID int64) error { } // Don't need to rewrite this file if builtin SSH server is enabled. - if setting.StartSSHServer { + if setting.SSH.StartBuiltinServer { return nil } - fpath := filepath.Join(setting.SSHRootPath, "authorized_keys") - tmpPath := filepath.Join(setting.SSHRootPath, "authorized_keys.tmp") + fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys") + tmpPath := fpath + ".tmp" if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil { return err } else if err = os.Remove(fpath); err != nil { @@ -576,7 +570,8 @@ func RewriteAllPublicKeys() error { sshOpLocker.Lock() defer sshOpLocker.Unlock() - tmpPath := filepath.Join(setting.SSHRootPath, "authorized_keys.tmp") + fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys") + tmpPath := fpath + ".tmp" f, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return err @@ -592,7 +587,6 @@ func RewriteAllPublicKeys() error { return err } - fpath := filepath.Join(setting.SSHRootPath, "authorized_keys") if com.IsExist(fpath) { if err = os.Remove(fpath); err != nil { return err diff --git a/models/ssh_key_test.go b/models/ssh_key_test.go index cfb8554ae2..61f2e18be4 100644 --- a/models/ssh_key_test.go +++ b/models/ssh_key_test.go @@ -1,39 +1,45 @@ package models import ( - "github.com/gogits/gogs/modules/setting" + "fmt" "testing" + + . "github.com/smartystreets/goconvey/convey" + + "github.com/gogits/gogs/modules/setting" ) -func TestSSHKeyVerification(t *testing.T) { - setting.SSHWorkPath = "/tmp" - setting.SSHKeyGenPath = "/usr/bin/ssh-keygen" - - keys := map[string]string{ - "dsa-1024": string("ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL+wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgPaguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982daopE7zQ/NPAnJfag= nocomment"), - "rsa-1024": string("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n"), - "rsa-2048": string("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMZXh+1OBUwSH9D45wTaxErQIN9IoC9xl7MKJkqvTvv6O5RR9YW/IK9FbfjXgXsppYGhsCZo1hFOOsXHMnfOORqu/xMDx4yPuyvKpw4LePEcg4TDipaDFuxbWOqc/BUZRZcXu41QAWfDLrInwsltWZHSeG7hjhpacl4FrVv9V1pS6Oc5Q1NxxEzTzuNLS/8diZrTm/YAQQ/+B+mzWI3zEtF4miZjjAljWd1LTBPvU23d29DcBmmFahcZ441XZsTeAwGxG/Q6j8NgNXj9WxMeWwxXV2jeAX/EBSpZrCVlCQ1yJswT6xCp8TuBnTiGWYMBNTbOZvPC4e0WI2/yZW/s5F nocomment"), - "ecdsa-256": string("ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFQacN3PrOll7PXmN5B/ZNVahiUIqI05nbBlZk1KXsO3d06ktAWqbNflv2vEmA38bTFTfJ2sbn2B5ksT52cDDbA= nocomment"), - "ecdsa-384": string("ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"), - "ecdsa-512": string("ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACGt3UG3EzRwNOI17QR84l6PgiAcvCE7v6aXPj/SC6UWKg4EL8vW9ZBcdYL9wzs4FZXh4MOV8jAzu3KRWNTwb4k2wFNUpGOt7l28MztFFEtH5BDDrtAJSPENPy8pvPLMfnPg5NhvWycqIBzNcHipem5wSJFN5PdpNOC2xMrPWKNqj+ZjQ== nocomment"), +func init() { + setting.NewContext() +} + +func Test_SSHParsePublicKey(t *testing.T) { + testKeys := map[string]struct { + typeName string + length int + content string + }{ + "dsa-1024": {"dsa", 1024, "ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL+wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgPaguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982daopE7zQ/NPAnJfag= nocomment"}, + "rsa-1024": {"rsa", 1024, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n"}, + "rsa-2048": {"rsa", 2048, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMZXh+1OBUwSH9D45wTaxErQIN9IoC9xl7MKJkqvTvv6O5RR9YW/IK9FbfjXgXsppYGhsCZo1hFOOsXHMnfOORqu/xMDx4yPuyvKpw4LePEcg4TDipaDFuxbWOqc/BUZRZcXu41QAWfDLrInwsltWZHSeG7hjhpacl4FrVv9V1pS6Oc5Q1NxxEzTzuNLS/8diZrTm/YAQQ/+B+mzWI3zEtF4miZjjAljWd1LTBPvU23d29DcBmmFahcZ441XZsTeAwGxG/Q6j8NgNXj9WxMeWwxXV2jeAX/EBSpZrCVlCQ1yJswT6xCp8TuBnTiGWYMBNTbOZvPC4e0WI2/yZW/s5F nocomment"}, + "ecdsa-256": {"ecdsa", 256, "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFQacN3PrOll7PXmN5B/ZNVahiUIqI05nbBlZk1KXsO3d06ktAWqbNflv2vEmA38bTFTfJ2sbn2B5ksT52cDDbA= nocomment"}, + "ecdsa-384": {"ecdsa", 384, "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"}, + "ecdsa-521": {"ecdsa", 521, "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACGt3UG3EzRwNOI17QR84l6PgiAcvCE7v6aXPj/SC6UWKg4EL8vW9ZBcdYL9wzs4FZXh4MOV8jAzu3KRWNTwb4k2wFNUpGOt7l28MztFFEtH5BDDrtAJSPENPy8pvPLMfnPg5NhvWycqIBzNcHipem5wSJFN5PdpNOC2xMrPWKNqj+ZjQ== nocomment"}, } - for name, pubkey := range keys { - keyTypeN, lengthN, errN := SSHNativeParsePublicKey(pubkey) - if errN != nil { - if errN != SSHUnknownKeyType { - t.Errorf("error parsing public key '%s': %s", name, errN) - continue - } - } - keyTypeK, lengthK, errK := SSHKeyGenParsePublicKey(pubkey) - if errK != nil { - t.Errorf("error parsing public key '%s': %s", name, errK) - continue - } - // we know that ed25519 is currently not supported by native and returns SSHUnknownKeyType - if (keyTypeN != keyTypeK || lengthN != lengthK) && errN != SSHUnknownKeyType { - t.Errorf("key mismatch for '%s': native: %s(%d), ssh-keygen: %s(%d)", name, keyTypeN, lengthN, keyTypeK, lengthK) + Convey("Parse public keys in both native and ssh-keygen", t, func() { + for name, key := range testKeys { + fmt.Println("\nTesting key:", name) + + keyTypeN, lengthN, errN := SSHNativeParsePublicKey(key.content) + So(errN, ShouldBeNil) + So(keyTypeN, ShouldEqual, key.typeName) + So(lengthN, ShouldEqual, key.length) + + keyTypeK, lengthK, errK := SSHKeyGenParsePublicKey(key.content) + So(errK, ShouldBeNil) + So(keyTypeK, ShouldEqual, key.typeName) + So(lengthK, ShouldEqual, key.length) } - } + }) } |