mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Improve instance wide ssh commit signing (#34341)
* Signed SSH commits can look in the UI like on GitHub, just like gpg keys today in Gitea * SSH format can be added in gitea config * SSH Signing worked before with DEFAULT_TRUST_MODEL=committer `TRUSTED_SSH_KEYS` can be a list of additional ssh public key contents to trust for every user of this instance Closes #34329 Related #31392 --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: techknowlogick <techknowlogick@gitea.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -47,6 +47,7 @@ type Command struct {
|
||||
globalArgsLength int
|
||||
brokenArgs []string
|
||||
cmd *exec.Cmd // for debug purpose only
|
||||
configArgs []string
|
||||
}
|
||||
|
||||
func logArgSanitize(arg string) string {
|
||||
@@ -196,6 +197,16 @@ func (c *Command) AddDashesAndList(list ...string) *Command {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Command) AddConfig(key, value string) *Command {
|
||||
kv := key + "=" + value
|
||||
if !isSafeArgumentValue(kv) {
|
||||
c.brokenArgs = append(c.brokenArgs, key)
|
||||
} else {
|
||||
c.configArgs = append(c.configArgs, "-c", kv)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// ToTrustedCmdArgs converts a list of strings (trusted as argument) to TrustedCmdArgs
|
||||
// In most cases, it shouldn't be used. Use NewCommand().AddXxx() function instead
|
||||
func ToTrustedCmdArgs(args []string) TrustedCmdArgs {
|
||||
@@ -321,7 +332,7 @@ func (c *Command) run(ctx context.Context, skip int, opts *RunOpts) error {
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
cmd := exec.CommandContext(ctx, c.prog, c.args...)
|
||||
cmd := exec.CommandContext(ctx, c.prog, append(c.configArgs, c.args...)...)
|
||||
c.cmd = cmd // for debug purpose only
|
||||
if opts.Env == nil {
|
||||
cmd.Env = os.Environ()
|
||||
|
15
modules/git/key.go
Normal file
15
modules/git/key.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
// Based on https://git-scm.com/docs/git-config#Documentation/git-config.txt-gpgformat
|
||||
const (
|
||||
SigningKeyFormatOpenPGP = "openpgp" // for GPG keys, the expected default of git cli
|
||||
SigningKeyFormatSSH = "ssh"
|
||||
)
|
||||
|
||||
type SigningKey struct {
|
||||
KeyID string
|
||||
Format string
|
||||
}
|
@@ -28,6 +28,7 @@ type GPGSettings struct {
|
||||
Email string
|
||||
Name string
|
||||
PublicKeyContent string
|
||||
Format string
|
||||
}
|
||||
|
||||
const prettyLogFormat = `--pretty=format:%H`
|
||||
|
@@ -6,6 +6,7 @@ package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
@@ -13,6 +14,14 @@ import (
|
||||
|
||||
// LoadPublicKeyContent will load the key from gpg
|
||||
func (gpgSettings *GPGSettings) LoadPublicKeyContent() error {
|
||||
if gpgSettings.Format == SigningKeyFormatSSH {
|
||||
content, err := os.ReadFile(gpgSettings.KeyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read SSH public key file: %s, %w", gpgSettings.KeyID, err)
|
||||
}
|
||||
gpgSettings.PublicKeyContent = string(content)
|
||||
return nil
|
||||
}
|
||||
content, stderr, err := process.GetManager().Exec(
|
||||
"gpg -a --export",
|
||||
"gpg", "-a", "--export", gpgSettings.KeyID)
|
||||
@@ -44,6 +53,9 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
|
||||
signingKey, _, _ := NewCommand("config", "--get", "user.signingkey").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
|
||||
gpgSettings.KeyID = strings.TrimSpace(signingKey)
|
||||
|
||||
format, _, _ := NewCommand("config", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
|
||||
gpgSettings.Format = strings.TrimSpace(format)
|
||||
|
||||
defaultEmail, _, _ := NewCommand("config", "--get", "user.email").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
|
||||
gpgSettings.Email = strings.TrimSpace(defaultEmail)
|
||||
|
||||
|
@@ -15,7 +15,7 @@ import (
|
||||
type CommitTreeOpts struct {
|
||||
Parents []string
|
||||
Message string
|
||||
KeyID string
|
||||
Key *SigningKey
|
||||
NoGPGSign bool
|
||||
AlwaysSign bool
|
||||
}
|
||||
@@ -43,8 +43,13 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
|
||||
_, _ = messageBytes.WriteString(opts.Message)
|
||||
_, _ = messageBytes.WriteString("\n")
|
||||
|
||||
if opts.KeyID != "" || opts.AlwaysSign {
|
||||
cmd.AddOptionFormat("-S%s", opts.KeyID)
|
||||
if opts.Key != nil {
|
||||
if opts.Key.Format != "" {
|
||||
cmd.AddConfig("gpg.format", opts.Key.Format)
|
||||
}
|
||||
cmd.AddOptionFormat("-S%s", opts.Key.KeyID)
|
||||
} else if opts.AlwaysSign {
|
||||
cmd.AddOptionFormat("-S")
|
||||
}
|
||||
|
||||
if opts.NoGPGSign {
|
||||
|
@@ -100,11 +100,13 @@ var (
|
||||
SigningKey string
|
||||
SigningName string
|
||||
SigningEmail string
|
||||
SigningFormat string
|
||||
InitialCommit []string
|
||||
CRUDActions []string `ini:"CRUD_ACTIONS"`
|
||||
Merges []string
|
||||
Wiki []string
|
||||
DefaultTrustModel string
|
||||
TrustedSSHKeys []string `ini:"TRUSTED_SSH_KEYS"`
|
||||
} `ini:"repository.signing"`
|
||||
}{
|
||||
DetectedCharsetsOrder: []string{
|
||||
@@ -242,20 +244,24 @@ var (
|
||||
SigningKey string
|
||||
SigningName string
|
||||
SigningEmail string
|
||||
SigningFormat string
|
||||
InitialCommit []string
|
||||
CRUDActions []string `ini:"CRUD_ACTIONS"`
|
||||
Merges []string
|
||||
Wiki []string
|
||||
DefaultTrustModel string
|
||||
TrustedSSHKeys []string `ini:"TRUSTED_SSH_KEYS"`
|
||||
}{
|
||||
SigningKey: "default",
|
||||
SigningName: "",
|
||||
SigningEmail: "",
|
||||
SigningFormat: "openpgp", // git.SigningKeyFormatOpenPGP
|
||||
InitialCommit: []string{"always"},
|
||||
CRUDActions: []string{"pubkey", "twofa", "parentsigned"},
|
||||
Merges: []string{"pubkey", "twofa", "basesigned", "commitssigned"},
|
||||
Wiki: []string{"never"},
|
||||
DefaultTrustModel: "collaborator",
|
||||
TrustedSSHKeys: []string{},
|
||||
},
|
||||
}
|
||||
RepoRootPath string
|
||||
|
Reference in New Issue
Block a user