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

Create AuthorizedKeysCommand #5236

Merged
merged 2 commits into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ func argsSet(c *cli.Context, args ...string) error {
}

func initDB() error {
return initDBDisableConsole(false)
}

func initDBDisableConsole(disableConsole bool) error {
setting.NewContext()
models.LoadConfigs()

setting.NewXORMLogService(false)
setting.NewXORMLogService(disableConsole)
if err := models.SetEngine(); err != nil {
return fmt.Errorf("models.SetEngine: %v", err)
}
Expand Down
85 changes: 85 additions & 0 deletions cmd/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package cmd

import (
"errors"
"fmt"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"

"github.com/urfave/cli"
)

// CmdKeys represents the available keys sub-command
var CmdKeys = cli.Command{
Name: "keys",
Usage: "This command queries the Gitea database to get the authorized command for a given ssh key fingerprint",
Action: runKeys,
Flags: []cli.Flag{
cli.StringFlag{
Name: "expected, e",
Value: "git",
Usage: "Expected user for whom provide key commands",
},
cli.StringFlag{
Name: "username, u",
Value: "",
Usage: "Username trying to log in by SSH",
},
cli.StringFlag{
Name: "type, t",
Value: "",
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
},
cli.StringFlag{
Name: "content, k",
Value: "",
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
},
cli.StringFlag{
Name: "config, c",
Value: "custom/conf/app.ini",
Usage: "Custom configuration file path",
},
},
}

func runKeys(c *cli.Context) error {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
}

if !c.IsSet("username") {
return errors.New("No username provided")
}
// Check username matches the expected username
if strings.TrimSpace(c.String("username")) != strings.TrimSpace(c.String("expected")) {
return nil
}

content := ""

if c.IsSet("type") && c.IsSet("content") {
content = fmt.Sprintf("%s %s", strings.TrimSpace(c.String("type")), strings.TrimSpace(c.String("content")))
}

if content == "" {
return errors.New("No key type and content provided")
}

if err := initDBDisableConsole(true); err != nil {
return err
}

publicKey, err := models.SearchPublicKeyByContent(content)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use internal api not models directly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would have been a good spot >1 year ago... I guess we need to pop a bug fix up...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just popped up in notifications 😅

if err != nil {
return err
}
fmt.Println(publicKey.AuthorizedString())
return nil
}
3 changes: 3 additions & 0 deletions custom/conf/app.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ SSH_PORT = 22
SSH_LISTEN_PORT = %(SSH_PORT)s
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
SSH_ROOT_PATH =
; Gitea will create a authorized_keys file by default when it is not using the internal ssh server
; If you intend to use the AuthorizedKeysCommand functionality then you should turn this off.
SSH_CREATE_AUTHORIZED_KEYS_FILE = true
; For the built-in SSH server, choose the ciphers to support for SSH connections,
; for system SSH this setting has no effect
SSH_SERVER_CIPHERS = aes128-ctr, aes192-ctr, aes256-ctr, [email protected], arcfour256, arcfour128
Expand Down
21 changes: 21 additions & 0 deletions docs/content/doc/usage/command-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,24 @@ for automatic deployments.
- `gitea generate secret INTERNAL_TOKEN`
- `gitea generate secret LFS_JWT_SECRET`
- `gitea generate secret SECRET_KEY`

#### keys

Provides an SSHD AuthorizedKeysCommand. Needs to be configured in the sshd config file:

```ini
...
# The value of -e and the AuthorizedKeysCommandUser should match the
# username running gitea
AuthorizedKeysCommandUser git
AuthorizedKeysCommand /path/to/gitea keys -e git -u %u -t %t -k %k
```

The command will return the appropriate authorized_keys line for the
provided key. You should also set the value
`SSH_CREATE_AUTHORIZED_KEYS_FILE=false` in the `[server]` section of
`app.ini`.

NB: opensshd requires the gitea program to be owned by root and not
writable by group or others. The program must be specified by an absolute
path.
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/cmd"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"

// register supported doc types
_ "code.gitea.io/gitea/modules/markup/csv"
_ "code.gitea.io/gitea/modules/markup/markdown"
Expand Down Expand Up @@ -48,6 +49,7 @@ arguments - which can alternatively be run by running the subcommand web.`
cmd.CmdAdmin,
cmd.CmdGenerate,
cmd.CmdMigrate,
cmd.CmdKeys,
}
app.Flags = append(app.Flags, cmd.CmdWeb.Flags...)
app.Action = cmd.CmdWeb.Action
Expand Down
2 changes: 1 addition & 1 deletion models/ssh_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ func DeletePublicKey(doer *User, id int64) (err error) {
// outside any session scope independently.
func RewriteAllPublicKeys() error {
//Don't rewrite key if internal server
if setting.SSH.StartBuiltinServer {
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
return nil
}

Expand Down
36 changes: 19 additions & 17 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,23 +118,24 @@ var (
LetsEncryptEmail string

SSH = struct {
Disabled bool `ini:"DISABLE_SSH"`
StartBuiltinServer bool `ini:"START_SSH_SERVER"`
BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
Domain string `ini:"SSH_DOMAIN"`
Port int `ini:"SSH_PORT"`
ListenHost string `ini:"SSH_LISTEN_HOST"`
ListenPort int `ini:"SSH_LISTEN_PORT"`
RootPath string `ini:"SSH_ROOT_PATH"`
ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
ServerMACs []string `ini:"SSH_SERVER_MACS"`
KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
KeygenPath string `ini:"SSH_KEYGEN_PATH"`
AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
MinimumKeySizeCheck bool `ini:"-"`
MinimumKeySizes map[string]int `ini:"-"`
ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"`
Disabled bool `ini:"DISABLE_SSH"`
StartBuiltinServer bool `ini:"START_SSH_SERVER"`
BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
Domain string `ini:"SSH_DOMAIN"`
Port int `ini:"SSH_PORT"`
ListenHost string `ini:"SSH_LISTEN_HOST"`
ListenPort int `ini:"SSH_LISTEN_PORT"`
RootPath string `ini:"SSH_ROOT_PATH"`
ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
ServerMACs []string `ini:"SSH_SERVER_MACS"`
KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
KeygenPath string `ini:"SSH_KEYGEN_PATH"`
AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
MinimumKeySizeCheck bool `ini:"-"`
MinimumKeySizes map[string]int `ini:"-"`
CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"`
}{
Disabled: false,
StartBuiltinServer: false,
Expand Down Expand Up @@ -863,6 +864,7 @@ func NewContext() {
}
}
SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true)
SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true)
SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)

sec = Cfg.Section("server")
Expand Down