Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Gitea Doctor improvements #10943

Merged
merged 16 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 48 additions & 76 deletions cmd/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ package cmd

import (
"bufio"
"errors"
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"text/tabwriter"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/migrations"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
Expand All @@ -39,19 +39,19 @@ var CmdDoctor = cli.Command{
},
cli.BoolFlag{
Name: "default",
Usage: "Run the default checks (if neither run or all is set this is the default behaviour)",
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
},
cli.StringSliceFlag{
Name: "run",
Usage: "Run the provided checks - if default is set the default checks will also run",
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
},
cli.BoolFlag{
Name: "all",
Usage: "Run all the available checks",
},
cli.BoolFlag{
Name: "fix",
Usage: "Automatically fix if we can",
Usage: "Automatically fix what we can",
},
},
}
Expand All @@ -77,10 +77,17 @@ var checklist = []check{
skipDatabaseInit: true,
},
{
title: "Check if OpenSSH authorized_keys file id correct",
title: "Check Database Version",
name: "check-db",
isDefault: true,
f: runDoctorCheckDBVersion,
abortIfFailed: true,
},
{
title: "Check if OpenSSH authorized_keys file is correct",
name: "authorized_keys",
isDefault: true,
f: runDoctorLocationMoved,
f: runDoctorAuthorizedKeys,
},
{
zeripath marked this conversation as resolved.
Show resolved Hide resolved
title: "Recalculate merge bases",
Expand Down Expand Up @@ -174,14 +181,6 @@ func runDoctor(ctx *cli.Context) error {
return nil
}

func exePath() (string, error) {
file, err := exec.LookPath(os.Args[0])
if err != nil {
return "", err
}
return filepath.Abs(file)
}

func runDoctorPathInfo(ctx *cli.Context) ([]string, error) {

res := make([]string, 0, 10)
Expand Down Expand Up @@ -272,90 +271,63 @@ func runDoctorWritableDir(path string) error {

const tplCommentPrefix = `# gitea public key`

var giteaExpected = regexp.MustCompile(`^[ \t]*(?:command=")([^ ]+) --config='([^']+)' serv key-([^"]+)",(?:[^ ]+) ssh-rsa ([^ ]+) ([^ ]+)[ \t]*$`)

func runDoctorLocationMoved(ctx *cli.Context) ([]string, error) {
func runDoctorAuthorizedKeys(ctx *cli.Context) ([]string, error) {
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
return nil, nil
}

fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
f, err := os.Open(fPath)
if err != nil {
if ctx.Bool("fix") {
return []string{fmt.Sprintf("Error whilst opening authorized_keys: %v. Attempting regeneration", err)}, models.RewriteAllPublicKeys()
}
return nil, err
}
defer f.Close()

runFix := 0
results := make([]string, 0, 10)
linesInAuthorizedKeys := map[string]bool{}

scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, tplCommentPrefix) {
res, err := checkGiteaLine(strings.TrimSpace(scanner.Text()))
if err != nil {
if ctx.Bool("fix") {
results = append(results, err.Error())
runFix++
} else {
return nil, err
}
}
if len(res) > 0 {
if ctx.Bool("fix") {
runFix++
} else {
results = append(results, res)
}
}
continue
}
linesInAuthorizedKeys[line] = true
}
f.Close()

if runFix > 0 {
err := models.RewriteAllPublicKeys()
if err != nil {
return nil, err
// now we regenerate and check if there are any lines missing
regenerated := &bytes.Buffer{}
if err := models.RegeneratePublicKeys(regenerated); err != nil {
return nil, err
}
scanner = bufio.NewScanner(regenerated)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, tplCommentPrefix) {
continue
}
results = append(results, fmt.Sprintf("%d keys needed to be rewritten", runFix))
if ok := linesInAuthorizedKeys[line]; ok {
continue
}
if ctx.Bool("fix") {
return []string{"authorized_keys is out of date, attempting regeneration"}, models.RewriteAllPublicKeys()
}
return []string{"authorized_keys is out of date and should be regenerated with gitea admin regenerate keys"}, nil
}

return results, nil
return nil, nil
}

func checkGiteaLine(line string) (string, error) {
if len(line) == 0 {
return "", nil
}

fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")

// command="/home/user/gitea --config='/home/user/etc/app.ini' serv key-999",option-1,option-2,option-n ssh-rsa public-key-value key-name
res := giteaExpected.FindStringSubmatch(line)
if res == nil {
return "", errors.New("Unknown authorized_keys format")
}

giteaPath := res[1] // => /home/user/gitea
iniPath := res[2] // => /home/user/etc/app.ini

p, err := exePath()
if err != nil {
return "", err
}
p, err = filepath.Abs(p)
if err != nil {
return "", err
}

if len(giteaPath) > 0 && giteaPath != p {
return fmt.Sprintf("Gitea exe path wants %s but %s on %s", p, giteaPath, fPath), nil
}
if len(iniPath) > 0 && iniPath != setting.CustomConf {
return fmt.Sprintf("Gitea config path wants %s but %s on %s", setting.CustomConf, iniPath, fPath), nil
func runDoctorCheckDBVersion(ctx *cli.Context) ([]string, error) {
if err := models.NewEngine(context.Background(), migrations.EnsureUpToDate); err != nil {
if ctx.Bool("fix") {
return nil, models.NewEngine(context.Background(), migrations.Migrate)
guillep2k marked this conversation as resolved.
Show resolved Hide resolved
}
return nil, err
}

return "", nil
return nil, nil
}

func runDoctorPRMergeBase(ctx *cli.Context) ([]string, error) {
Expand Down
46 changes: 46 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,52 @@ var migrations = []Migration{
NewMigration("Add OrgID column to Labels table", addOrgIDLabelColumn),
}

// GetCurrentDBVersion returns the current db version
func GetCurrentDBVersion(x *xorm.Engine) (int64, error) {
if err := x.Sync(new(Version)); err != nil {
return -1, fmt.Errorf("sync: %v", err)
}

currentVersion := &Version{ID: 1}
has, err := x.Get(currentVersion)
if err != nil {
return -1, fmt.Errorf("get: %v", err)
}
if !has {
return -1, nil
}
return currentVersion.Version, nil
}

// ExpectedVersion returns the expected db version
func ExpectedVersion() int64 {
return int64(minDBVersion + len(migrations))
}

// EnsureUpToDate will check if the db is at the correct version
func EnsureUpToDate(x *xorm.Engine) error {
currentDB, err := GetCurrentDBVersion(x)
if err != nil {
return err
}

if currentDB < 0 {
return fmt.Errorf("Database has not been initialised")
}

if minDBVersion > currentDB {
return fmt.Errorf("DB version %d (<= %d) is too old for auto-migration. Upgrade to Gitea 1.6.4 first then upgrade to this version", currentDB, minDBVersion)
}

expected := ExpectedVersion()

if currentDB != expected {
return fmt.Errorf("Current database version %d is not equal to the expected version %d. Please run migrate", currentDB, expected)
guillep2k marked this conversation as resolved.
Show resolved Hide resolved
}

return nil
}

// Migrate database to current version
func Migrate(x *xorm.Engine) error {
if err := x.Sync(new(Version)); err != nil {
Expand Down
22 changes: 18 additions & 4 deletions models/ssh_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"os"
Expand Down Expand Up @@ -701,14 +702,29 @@ func rewriteAllPublicKeys(e Engine) error {
}
}

err = e.Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
if err := regeneratePublicKeys(e, t); err != nil {
return err
}

t.Close()
return os.Rename(tmpPath, fPath)
}

// RegeneratePublicKeys regenerates the authorized_keys file
func RegeneratePublicKeys(t io.StringWriter) error {
return regeneratePublicKeys(x, t)
}

func regeneratePublicKeys(e Engine, t io.StringWriter) error {
err := e.Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
_, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
return err
})
if err != nil {
return err
}

fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
if com.IsExist(fPath) {
f, err := os.Open(fPath)
if err != nil {
Expand All @@ -729,9 +745,7 @@ func rewriteAllPublicKeys(e Engine) error {
}
f.Close()
}

t.Close()
return os.Rename(tmpPath, fPath)
return nil
}

// ________ .__ ____ __.
Expand Down