diff options
Diffstat (limited to 'internal/db/public_keys.go')
-rw-r--r-- | internal/db/public_keys.go | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/internal/db/public_keys.go b/internal/db/public_keys.go new file mode 100644 index 00000000..d2f8307d --- /dev/null +++ b/internal/db/public_keys.go @@ -0,0 +1,103 @@ +// Copyright 2023 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package db + +import ( + "os" + "path/filepath" + + "github.com/pkg/errors" + "gorm.io/gorm" + + "gogs.io/gogs/internal/conf" + "gogs.io/gogs/internal/osutil" +) + +// PublicKeysStore is the persistent interface for public keys. +// +// NOTE: All methods are sorted in alphabetical order. +type PublicKeysStore interface { + // RewriteAuthorizedKeys rewrites the "authorized_keys" file under the SSH root + // path with all public keys stored in the database. + RewriteAuthorizedKeys() error +} + +var PublicKeys PublicKeysStore + +var _ PublicKeysStore = (*publicKeys)(nil) + +type publicKeys struct { + *gorm.DB +} + +// NewPublicKeysStore returns a persistent interface for public keys with given +// database connection. +func NewPublicKeysStore(db *gorm.DB) PublicKeysStore { + return &publicKeys{DB: db} +} + +func authorizedKeysPath() string { + return filepath.Join(conf.SSH.RootPath, "authorized_keys") +} + +func (db *publicKeys) RewriteAuthorizedKeys() error { + sshOpLocker.Lock() + defer sshOpLocker.Unlock() + + err := os.MkdirAll(conf.SSH.RootPath, os.ModePerm) + if err != nil { + return errors.Wrap(err, "create SSH root path") + } + fpath := authorizedKeysPath() + tempPath := fpath + ".tmp" + f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return errors.Wrap(err, "create temporary file") + } + defer func() { + _ = f.Close() + _ = os.Remove(tempPath) + }() + + // NOTE: More recently updated keys are more likely to be used more frequently, + // putting them in the earlier lines could speed up the key lookup by SSHD. + rows, err := db.Model(&PublicKey{}).Order("updated_unix DESC").Rows() + if err != nil { + return errors.Wrap(err, "iterate public keys") + } + defer func() { _ = rows.Close() }() + + for rows.Next() { + var key PublicKey + err = db.ScanRows(rows, &key) + if err != nil { + return errors.Wrap(err, "scan rows") + } + + _, err = f.WriteString(key.AuthorizedString()) + if err != nil { + return errors.Wrapf(err, "write key %d", key.ID) + } + } + if err = rows.Err(); err != nil { + return errors.Wrap(err, "check rows.Err") + } + + err = f.Close() + if err != nil { + return errors.Wrap(err, "close temporary file") + } + if osutil.IsExist(fpath) { + err = os.Remove(fpath) + if err != nil { + return errors.Wrap(err, "remove") + } + } + err = os.Rename(tempPath, fpath) + if err != nil { + return errors.Wrap(err, "rename") + } + return nil +} |