2020-12-02 07:56:04 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-12-02 07:56:04 +03:00
package doctor
import (
"bufio"
"bytes"
2022-01-20 02:26:57 +03:00
"context"
2020-12-02 07:56:04 +03:00
"fmt"
"os"
"path/filepath"
"strings"
2021-12-10 11:14:24 +03:00
asymkey_model "code.gitea.io/gitea/models/asymkey"
2022-10-12 08:18:26 +03:00
"code.gitea.io/gitea/modules/container"
2020-12-02 07:56:04 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
const tplCommentPrefix = ` # gitea public key `
2022-01-20 02:26:57 +03:00
func checkAuthorizedKeys ( ctx context . Context , logger log . Logger , autofix bool ) error {
2020-12-02 07:56:04 +03:00
if setting . SSH . StartBuiltinServer || ! setting . SSH . CreateAuthorizedKeysFile {
return nil
}
fPath := filepath . Join ( setting . SSH . RootPath , "authorized_keys" )
f , err := os . Open ( fPath )
if err != nil {
if ! autofix {
logger . Critical ( "Unable to open authorized_keys file. ERROR: %v" , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Unable to open authorized_keys file. ERROR: %w" , err )
2020-12-02 07:56:04 +03:00
}
logger . Warn ( "Unable to open authorized_keys. (ERROR: %v). Attempting to rewrite..." , err )
2023-09-25 16:17:37 +03:00
if err = asymkey_model . RewriteAllPublicKeys ( ctx ) ; err != nil {
2020-12-02 07:56:04 +03:00
logger . Critical ( "Unable to rewrite authorized_keys file. ERROR: %v" , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Unable to rewrite authorized_keys file. ERROR: %w" , err )
2020-12-02 07:56:04 +03:00
}
}
defer f . Close ( )
2022-10-12 08:18:26 +03:00
linesInAuthorizedKeys := make ( container . Set [ string ] )
2020-12-02 07:56:04 +03:00
scanner := bufio . NewScanner ( f )
for scanner . Scan ( ) {
line := scanner . Text ( )
if strings . HasPrefix ( line , tplCommentPrefix ) {
continue
}
2022-10-12 08:18:26 +03:00
linesInAuthorizedKeys . Add ( line )
2020-12-02 07:56:04 +03:00
}
2024-03-22 14:17:30 +03:00
if err = scanner . Err ( ) ; err != nil {
2024-03-19 05:20:36 +03:00
return fmt . Errorf ( "scan: %w" , err )
}
2024-03-22 14:17:30 +03:00
// although there is a "defer close" above, here close explicitly before the generating, because it needs to open the file for writing again
_ = f . Close ( )
2020-12-02 07:56:04 +03:00
// now we regenerate and check if there are any lines missing
regenerated := & bytes . Buffer { }
2022-05-20 17:08:52 +03:00
if err := asymkey_model . RegeneratePublicKeys ( ctx , regenerated ) ; err != nil {
2020-12-02 07:56:04 +03:00
logger . Critical ( "Unable to regenerate authorized_keys file. ERROR: %v" , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Unable to regenerate authorized_keys file. ERROR: %w" , err )
2020-12-02 07:56:04 +03:00
}
scanner = bufio . NewScanner ( regenerated )
for scanner . Scan ( ) {
line := scanner . Text ( )
if strings . HasPrefix ( line , tplCommentPrefix ) {
continue
}
2022-10-12 08:18:26 +03:00
if linesInAuthorizedKeys . Contains ( line ) {
2020-12-02 07:56:04 +03:00
continue
}
if ! autofix {
logger . Critical (
"authorized_keys file %q is out of date.\nRegenerate it with:\n\t\"%s\"\nor\n\t\"%s\"" ,
fPath ,
"gitea admin regenerate keys" ,
2022-04-24 21:06:33 +03:00
"gitea doctor --run authorized-keys --fix" )
return fmt . Errorf ( ` authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized-keys --fix" ` )
2020-12-02 07:56:04 +03:00
}
logger . Warn ( "authorized_keys is out of date. Attempting rewrite..." )
2023-09-25 16:17:37 +03:00
err = asymkey_model . RewriteAllPublicKeys ( ctx )
2020-12-02 07:56:04 +03:00
if err != nil {
logger . Critical ( "Unable to rewrite authorized_keys file. ERROR: %v" , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Unable to rewrite authorized_keys file. ERROR: %w" , err )
2020-12-02 07:56:04 +03:00
}
}
return nil
}
func init ( ) {
Register ( & Check {
Title : "Check if OpenSSH authorized_keys file is up-to-date" ,
Name : "authorized-keys" ,
IsDefault : true ,
Run : checkAuthorizedKeys ,
Priority : 4 ,
} )
}