2021-07-24 11:16:34 +01:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2021-07-24 11:16:34 +01:00
2021-12-10 16:14:24 +08:00
package asymkey
2021-07-24 11:16:34 +01:00
import (
"bufio"
2022-05-20 22:08:52 +08:00
"context"
2021-07-24 11:16:34 +01:00
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
2024-03-04 16:57:39 +08:00
asymkey_model "code.gitea.io/gitea/models/asymkey"
2021-09-19 19:49:59 +08:00
"code.gitea.io/gitea/models/db"
2021-07-24 11:16:34 +01:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
// This file contains functions for creating authorized_principals files
//
// There is a dependence on the database within RewriteAllPrincipalKeys & RegeneratePrincipalKeys
// The sshOpLocker is used from ssh_key_authorized_keys.go
2024-03-04 16:57:39 +08:00
const (
authorizedPrincipalsFile = "authorized_principals"
tplCommentPrefix = ` # gitea public key `
)
2021-07-24 11:16:34 +01:00
// RewriteAllPrincipalKeys removes any authorized principal and rewrite all keys from database again.
2023-10-15 23:46:06 +08:00
// Note: db.GetEngine(ctx).Iterate does not get latest data after insert/delete, so we have to call this function
2021-07-24 11:16:34 +01:00
// outside any session scope independently.
2022-05-20 22:08:52 +08:00
func RewriteAllPrincipalKeys ( ctx context . Context ) error {
2021-07-24 11:16:34 +01:00
// Don't rewrite key if internal server
if setting . SSH . StartBuiltinServer || ! setting . SSH . CreateAuthorizedPrincipalsFile {
return nil
}
2024-03-04 16:57:39 +08:00
return asymkey_model . WithSSHOpLocker ( func ( ) error {
return rewriteAllPrincipalKeys ( ctx )
} )
}
2021-07-24 11:16:34 +01:00
2024-03-04 16:57:39 +08:00
func rewriteAllPrincipalKeys ( ctx context . Context ) error {
2021-07-24 11:16:34 +01:00
if setting . SSH . RootPath != "" {
// First of ensure that the RootPath is present, and if not make it with 0700 permissions
// This of course doesn't guarantee that this is the right directory for authorized_keys
// but at least if it's supposed to be this directory and it doesn't exist and we're the
// right user it will at least be created properly.
err := os . MkdirAll ( setting . SSH . RootPath , 0 o700 )
if err != nil {
log . Error ( "Unable to MkdirAll(%s): %v" , setting . SSH . RootPath , err )
return err
}
}
fPath := filepath . Join ( setting . SSH . RootPath , authorizedPrincipalsFile )
tmpPath := fPath + ".tmp"
t , err := os . OpenFile ( tmpPath , os . O_RDWR | os . O_CREATE | os . O_TRUNC , 0 o600 )
if err != nil {
return err
}
defer func ( ) {
t . Close ( )
os . Remove ( tmpPath )
} ( )
if setting . SSH . AuthorizedPrincipalsBackup {
isExist , err := util . IsExist ( fPath )
if err != nil {
log . Error ( "Unable to check if %s exists. Error: %v" , fPath , err )
return err
}
if isExist {
bakPath := fmt . Sprintf ( "%s_%d.gitea_bak" , fPath , time . Now ( ) . Unix ( ) )
if err = util . CopyFile ( fPath , bakPath ) ; err != nil {
return err
}
}
}
2022-05-20 22:08:52 +08:00
if err := regeneratePrincipalKeys ( ctx , t ) ; err != nil {
2021-07-24 11:16:34 +01:00
return err
}
t . Close ( )
return util . Rename ( tmpPath , fPath )
}
2022-05-20 22:08:52 +08:00
func regeneratePrincipalKeys ( ctx context . Context , t io . StringWriter ) error {
2024-03-04 16:57:39 +08:00
if err := db . GetEngine ( ctx ) . Where ( "type = ?" , asymkey_model . KeyTypePrincipal ) . Iterate ( new ( asymkey_model . PublicKey ) , func ( idx int , bean any ) ( err error ) {
_ , err = t . WriteString ( ( bean . ( * asymkey_model . PublicKey ) ) . AuthorizedString ( ) )
2021-07-24 11:16:34 +01:00
return err
} ) ; err != nil {
return err
}
fPath := filepath . Join ( setting . SSH . RootPath , authorizedPrincipalsFile )
isExist , err := util . IsExist ( fPath )
if err != nil {
log . Error ( "Unable to check if %s exists. Error: %v" , fPath , err )
return err
}
if isExist {
f , err := os . Open ( fPath )
if err != nil {
return err
}
2024-03-22 19:17:30 +08:00
defer f . Close ( )
2021-07-24 11:16:34 +01:00
scanner := bufio . NewScanner ( f )
for scanner . Scan ( ) {
line := scanner . Text ( )
if strings . HasPrefix ( line , tplCommentPrefix ) {
scanner . Scan ( )
continue
}
_ , err = t . WriteString ( line + "\n" )
if err != nil {
return err
}
}
2024-03-22 19:17:30 +08:00
if err = scanner . Err ( ) ; err != nil {
return fmt . Errorf ( "regeneratePrincipalKeys scan: %w" , err )
2024-03-19 10:20:36 +08:00
}
2021-07-24 11:16:34 +01:00
}
return nil
}