2020-12-02 10:26:04 +05:30
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 23:50:29 +05:30
// SPDX-License-Identifier: MIT
2020-12-02 10:26:04 +05:30
package doctor
import (
"bufio"
"bytes"
2022-01-20 04:56:57 +05:30
"context"
2020-12-02 10:26:04 +05:30
"fmt"
"os"
"path/filepath"
"strings"
2021-12-10 13:44:24 +05:30
asymkey_model "code.gitea.io/gitea/models/asymkey"
2022-10-12 10:48:26 +05:30
"code.gitea.io/gitea/modules/container"
2020-12-02 10:26:04 +05:30
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
const tplCommentPrefix = ` # gitea public key `
2022-01-20 04:56:57 +05:30
func checkAuthorizedKeys ( ctx context . Context , logger log . Logger , autofix bool ) error {
2020-12-02 10:26:04 +05:30
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-25 00:59:17 +05:30
return fmt . Errorf ( "Unable to open authorized_keys file. ERROR: %w" , err )
2020-12-02 10:26:04 +05:30
}
logger . Warn ( "Unable to open authorized_keys. (ERROR: %v). Attempting to rewrite..." , err )
2023-09-25 18:47:37 +05:30
if err = asymkey_model . RewriteAllPublicKeys ( ctx ) ; err != nil {
2020-12-02 10:26:04 +05:30
logger . Critical ( "Unable to rewrite authorized_keys file. ERROR: %v" , err )
2022-10-25 00:59:17 +05:30
return fmt . Errorf ( "Unable to rewrite authorized_keys file. ERROR: %w" , err )
2020-12-02 10:26:04 +05:30
}
}
defer f . Close ( )
2022-10-12 10:48:26 +05:30
linesInAuthorizedKeys := make ( container . Set [ string ] )
2020-12-02 10:26:04 +05:30
scanner := bufio . NewScanner ( f )
for scanner . Scan ( ) {
line := scanner . Text ( )
if strings . HasPrefix ( line , tplCommentPrefix ) {
continue
}
2022-10-12 10:48:26 +05:30
linesInAuthorizedKeys . Add ( line )
2020-12-02 10:26:04 +05:30
}
2024-03-22 16:47:30 +05:30
if err = scanner . Err ( ) ; err != nil {
2024-03-19 07:50:36 +05:30
return fmt . Errorf ( "scan: %w" , err )
}
2024-03-22 16:47:30 +05:30
// 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 10:26:04 +05:30
// now we regenerate and check if there are any lines missing
regenerated := & bytes . Buffer { }
2022-05-20 19:38:52 +05:30
if err := asymkey_model . RegeneratePublicKeys ( ctx , regenerated ) ; err != nil {
2020-12-02 10:26:04 +05:30
logger . Critical ( "Unable to regenerate authorized_keys file. ERROR: %v" , err )
2022-10-25 00:59:17 +05:30
return fmt . Errorf ( "Unable to regenerate authorized_keys file. ERROR: %w" , err )
2020-12-02 10:26:04 +05:30
}
scanner = bufio . NewScanner ( regenerated )
for scanner . Scan ( ) {
line := scanner . Text ( )
if strings . HasPrefix ( line , tplCommentPrefix ) {
continue
}
2022-10-12 10:48:26 +05:30
if linesInAuthorizedKeys . Contains ( line ) {
2020-12-02 10:26:04 +05:30
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 23:36:33 +05:30
"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 10:26:04 +05:30
}
logger . Warn ( "authorized_keys is out of date. Attempting rewrite..." )
2023-09-25 18:47:37 +05:30
err = asymkey_model . RewriteAllPublicKeys ( ctx )
2020-12-02 10:26:04 +05:30
if err != nil {
logger . Critical ( "Unable to rewrite authorized_keys file. ERROR: %v" , err )
2022-10-25 00:59:17 +05:30
return fmt . Errorf ( "Unable to rewrite authorized_keys file. ERROR: %w" , err )
2020-12-02 10:26:04 +05:30
}
}
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 ,
} )
}