Merge pull request #22 from thomiceli/fix/ssh-keys

Fix SSH pubkey detection
This commit is contained in:
Thomas Miceli 2023-05-01 03:04:36 +02:00 committed by GitHub
commit 0362cd8a6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 21 additions and 28 deletions

View file

@ -48,7 +48,7 @@ func GetSSHKeyByID(sshKeyId uint) (*SSHKey, error) {
return sshKey, err return sshKey, err
} }
func GetSSHKeyByContent(sshKeyContent string) (*SSHKey, error) { func SSHKeyDoesExists(sshKeyContent string) (*SSHKey, error) {
sshKey := new(SSHKey) sshKey := new(SSHKey)
err := db. err := db.
Where("content like ?", sshKeyContent+"%"). Where("content like ?", sshKeyContent+"%").
@ -65,9 +65,9 @@ func (sshKey *SSHKey) Delete() error {
return db.Delete(&sshKey).Error return db.Delete(&sshKey).Error
} }
func SSHKeyLastUsedNow(sshKeyID uint) error { func SSHKeyLastUsedNow(sshKeyContent string) error {
return db.Model(&SSHKey{}). return db.Model(&SSHKey{}).
Where("id = ?", sshKeyID). Where("content = ?", sshKeyContent).
Update("last_used_at", time.Now().Unix()).Error Update("last_used_at", time.Now().Unix()).Error
} }

View file

@ -81,15 +81,14 @@ func GetUserById(userId uint) (*User, error) {
return user, err return user, err
} }
func GetUserBySSHKeyID(sshKeyId uint) (*User, error) { func SSHKeyExistsForUser(sshKey string, userId uint) (*SSHKey, error) {
user := new(User) key := new(SSHKey)
err := db. err := db.
Preload("SSHKeys"). Where("content = ?", sshKey).
Joins("join ssh_keys on users.id = ssh_keys.user_id"). Where("user_id = ?", userId).
Where("ssh_keys.id = ?", sshKeyId). First(&key).Error
First(&user).Error
return user, err return key, err
} }
func GetUserByProvider(id string, provider string) (*User, error) { func GetUserByProvider(id string, provider string) (*User, error) {

View file

@ -12,7 +12,7 @@ import (
"strings" "strings"
) )
func runGitCommand(ch ssh.Channel, gitCmd string, keyID uint, ip string) error { func runGitCommand(ch ssh.Channel, gitCmd string, key string, ip string) error {
verb, args := parseCommand(gitCmd) verb, args := parseCommand(gitCmd)
if !strings.HasPrefix(verb, "git-") { if !strings.HasPrefix(verb, "git-") {
verb = "" verb = ""
@ -43,7 +43,7 @@ func runGitCommand(ch ssh.Channel, gitCmd string, keyID uint, ip string) error {
} }
if verb == "receive-pack" || requireLogin == "1" { if verb == "receive-pack" || requireLogin == "1" {
user, err := models.GetUserBySSHKeyID(keyID) pubKey, err := models.SSHKeyExistsForUser(key, gist.UserID)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
log.Warn().Msg("Invalid SSH authentication attempt from " + ip) log.Warn().Msg("Invalid SSH authentication attempt from " + ip)
@ -52,14 +52,8 @@ func runGitCommand(ch ssh.Channel, gitCmd string, keyID uint, ip string) error {
errorSsh("Failed to get user by SSH key id", err) errorSsh("Failed to get user by SSH key id", err)
return errors.New("internal server error") return errors.New("internal server error")
} }
_ = models.SSHKeyLastUsedNow(pubKey.Content)
if user.ID != gist.UserID {
log.Warn().Msg("Invalid SSH authentication attempt from " + ip)
return errors.New("unauthorized")
} }
}
_ = models.SSHKeyLastUsedNow(keyID)
repositoryPath := git.RepositoryPath(gist.User.Username, gist.Uuid) repositoryPath := git.RepositoryPath(gist.User.Username, gist.Uuid)

View file

@ -12,7 +12,6 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"syscall" "syscall"
) )
@ -24,7 +23,8 @@ func Start() {
sshConfig := &ssh.ServerConfig{ sshConfig := &ssh.ServerConfig{
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
pkey, err := models.GetSSHKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))) strKey := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))
_, err := models.SSHKeyDoesExists(strKey)
if err != nil { if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) { if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err return nil, err
@ -33,7 +33,7 @@ func Start() {
log.Warn().Msg("Invalid SSH authentication attempt from " + conn.RemoteAddr().String()) log.Warn().Msg("Invalid SSH authentication attempt from " + conn.RemoteAddr().String())
return nil, errors.New("unknown public key") return nil, errors.New("unknown public key")
} }
return &ssh.Permissions{Extensions: map[string]string{"key-id": strconv.Itoa(int(pkey.ID))}}, nil return &ssh.Permissions{Extensions: map[string]string{"key": strKey}}, nil
}, },
} }
@ -71,13 +71,12 @@ func listen(serverConfig *ssh.ServerConfig) {
} }
go ssh.DiscardRequests(reqs) go ssh.DiscardRequests(reqs)
keyID, _ := strconv.Atoi(sConn.Permissions.Extensions["key-id"]) go handleConnexion(channels, sConn.Permissions.Extensions["key"], sConn.RemoteAddr().String())
go handleConnexion(channels, uint(keyID), sConn.RemoteAddr().String())
}() }()
} }
} }
func handleConnexion(channels <-chan ssh.NewChannel, keyID uint, ip string) { func handleConnexion(channels <-chan ssh.NewChannel, key string, ip string) {
for channel := range channels { for channel := range channels {
if channel.ChannelType() != "session" { if channel.ChannelType() != "session" {
_ = channel.Reject(ssh.UnknownChannelType, "Unknown channel type") _ = channel.Reject(ssh.UnknownChannelType, "Unknown channel type")
@ -109,7 +108,7 @@ func handleConnexion(channels <-chan ssh.NewChannel, keyID uint, ip string) {
payloadCmd = payloadCmd[i:] payloadCmd = payloadCmd[i:]
} }
if err = runGitCommand(ch, payloadCmd, keyID, ip); err != nil { if err = runGitCommand(ch, payloadCmd, key, ip); err != nil {
_, _ = ch.Stderr().Write([]byte("Opengist: " + err.Error() + "\r\n")) _, _ = ch.Stderr().Write([]byte("Opengist: " + err.Error() + "\r\n"))
} }
_, _ = ch.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) _, _ = ch.SendRequest("exit-status", false, []byte{0, 0, 0, 0})

View file

@ -74,11 +74,12 @@ func sshKeysProcess(ctx echo.Context) error {
key.UserID = user.ID key.UserID = user.ID
_, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content)) pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content))
if err != nil { if err != nil {
addFlash(ctx, "Invalid SSH key", "error") addFlash(ctx, "Invalid SSH key", "error")
return redirect(ctx, "/settings") return redirect(ctx, "/settings")
} }
key.Content = strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey)))
if err := key.Create(); err != nil { if err := key.Create(); err != nil {
return errorRes(500, "Cannot add SSH key", err) return errorRes(500, "Cannot add SSH key", err)

View file

@ -83,7 +83,7 @@
Add SSH Key Add SSH Key
</h2> </h2>
<h3 class="text-sm text-gray-400 italic mb-4"> <h3 class="text-sm text-gray-400 italic mb-4">
Used only to push gists using Git via SSH Used only to pull/push gists using Git via SSH
</h3> </h3>
<form class="space-y-6" action="/settings/ssh-keys" method="post"> <form class="space-y-6" action="/settings/ssh-keys" method="post">
<div> <div>