public_keys.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright 2023 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package db
  5. import (
  6. "os"
  7. "path/filepath"
  8. "github.com/pkg/errors"
  9. "gorm.io/gorm"
  10. "gogs.io/gogs/internal/conf"
  11. "gogs.io/gogs/internal/osutil"
  12. )
  13. // PublicKeysStore is the persistent interface for public keys.
  14. //
  15. // NOTE: All methods are sorted in alphabetical order.
  16. type PublicKeysStore interface {
  17. // RewriteAuthorizedKeys rewrites the "authorized_keys" file under the SSH root
  18. // path with all public keys stored in the database.
  19. RewriteAuthorizedKeys() error
  20. }
  21. var PublicKeys PublicKeysStore
  22. var _ PublicKeysStore = (*publicKeys)(nil)
  23. type publicKeys struct {
  24. *gorm.DB
  25. }
  26. // NewPublicKeysStore returns a persistent interface for public keys with given
  27. // database connection.
  28. func NewPublicKeysStore(db *gorm.DB) PublicKeysStore {
  29. return &publicKeys{DB: db}
  30. }
  31. func authorizedKeysPath() string {
  32. return filepath.Join(conf.SSH.RootPath, "authorized_keys")
  33. }
  34. func (db *publicKeys) RewriteAuthorizedKeys() error {
  35. sshOpLocker.Lock()
  36. defer sshOpLocker.Unlock()
  37. err := os.MkdirAll(conf.SSH.RootPath, os.ModePerm)
  38. if err != nil {
  39. return errors.Wrap(err, "create SSH root path")
  40. }
  41. fpath := authorizedKeysPath()
  42. tempPath := fpath + ".tmp"
  43. f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
  44. if err != nil {
  45. return errors.Wrap(err, "create temporary file")
  46. }
  47. defer func() {
  48. _ = f.Close()
  49. _ = os.Remove(tempPath)
  50. }()
  51. // NOTE: More recently updated keys are more likely to be used more frequently,
  52. // putting them in the earlier lines could speed up the key lookup by SSHD.
  53. rows, err := db.Model(&PublicKey{}).Order("updated_unix DESC").Rows()
  54. if err != nil {
  55. return errors.Wrap(err, "iterate public keys")
  56. }
  57. defer func() { _ = rows.Close() }()
  58. for rows.Next() {
  59. var key PublicKey
  60. err = db.ScanRows(rows, &key)
  61. if err != nil {
  62. return errors.Wrap(err, "scan rows")
  63. }
  64. _, err = f.WriteString(key.AuthorizedString())
  65. if err != nil {
  66. return errors.Wrapf(err, "write key %d", key.ID)
  67. }
  68. }
  69. if err = rows.Err(); err != nil {
  70. return errors.Wrap(err, "check rows.Err")
  71. }
  72. err = f.Close()
  73. if err != nil {
  74. return errors.Wrap(err, "close temporary file")
  75. }
  76. if osutil.IsExist(fpath) {
  77. err = os.Remove(fpath)
  78. if err != nil {
  79. return errors.Wrap(err, "remove")
  80. }
  81. }
  82. err = os.Rename(tempPath, fpath)
  83. if err != nil {
  84. return errors.Wrap(err, "rename")
  85. }
  86. return nil
  87. }