From 6dce6ad972f3a8973dd12b827e87f77f6a2d4d62 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 17 Oct 2025 12:21:29 -0700 Subject: [PATCH 1/3] Fix template repository expansion for non-regular files --- models/asymkey/ssh_key_authorized_keys.go | 39 ++++++++----- .../asymkey/ssh_key_authorized_keys_test.go | 16 ++++++ services/repository/generate.go | 56 +++++++++++-------- 3 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 models/asymkey/ssh_key_authorized_keys_test.go diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go index 2e4cd62e5cf76..b3c071043280b 100644 --- a/models/asymkey/ssh_key_authorized_keys.go +++ b/models/asymkey/ssh_key_authorized_keys.go @@ -17,21 +17,9 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "golang.org/x/crypto/ssh" ) -// _____ __ .__ .__ .___ -// / _ \ __ ___/ |_| |__ ___________|__|_______ ____ __| _/ -// / /_\ \| | \ __\ | \ / _ \_ __ \ \___ // __ \ / __ | -// / | \ | /| | | Y ( <_> ) | \/ |/ /\ ___// /_/ | -// \____|__ /____/ |__| |___| /\____/|__| |__/_____ \\___ >____ | -// \/ \/ \/ \/ \/ -// ____ __. -// | |/ _|____ ___.__. ______ -// | <_/ __ < | |/ ___/ -// | | \ ___/\___ |\___ \ -// |____|__ \___ > ____/____ > -// \/ \/\/ \/ -// // This file contains functions for creating authorized_keys files // // There is a dependence on the database within RegeneratePublicKeys however most of these functions probably belong in a module @@ -49,6 +37,23 @@ func WithSSHOpLocker(f func() error) error { return f() } +// removeSSHKeyComment removes the trailing comment from an SSH public key line. +func removeSSHKeyComment(pubKeyLine string) (string, error) { + pubKeyLine = strings.TrimSpace(pubKeyLine) + if pubKeyLine == "" || strings.HasPrefix(pubKeyLine, "#") { + return pubKeyLine, nil + } + + pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubKeyLine)) + if err != nil { + return "", fmt.Errorf("invalid public key: %w", err) + } + + // MarshalAuthorizedKey returns " \n" + key := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey))) + return key, nil +} + // AuthorizedStringForKey creates the authorized keys string appropriate for the provided key func AuthorizedStringForKey(key *PublicKey) string { sb := &strings.Builder{} @@ -60,7 +65,13 @@ func AuthorizedStringForKey(key *PublicKey) string { "Key": key, }) - return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), key.Content) + content, err := removeSSHKeyComment(key.Content) + if err != nil { + log.Error("Failed to remove comment from SSH key ID %d: %v", key.ID, err) + content = key.Content + } + + return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), content) } // appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file. diff --git a/models/asymkey/ssh_key_authorized_keys_test.go b/models/asymkey/ssh_key_authorized_keys_test.go new file mode 100644 index 0000000000000..ef7b34ac598d0 --- /dev/null +++ b/models/asymkey/ssh_key_authorized_keys_test.go @@ -0,0 +1,16 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package asymkey + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_removeSSHKeyComment(t *testing.T) { + content, err := removeSSHKeyComment("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINna5Jd6FTG4d87pUHnd/uLBr/6zGOVVFEQmdTs6k21L user@hostname") + assert.NoError(t, err) + assert.Equal(t, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINna5Jd6FTG4d87pUHnd/uLBr/6zGOVVFEQmdTs6k21L", content) +} diff --git a/services/repository/generate.go b/services/repository/generate.go index 8e7f9a45b0dfa..05894e8cc759d 100644 --- a/services/repository/generate.go +++ b/services/repository/generate.go @@ -153,33 +153,43 @@ func processGiteaTemplateFile(ctx context.Context, tmpDir string, templateRepo, if d.IsDir() { return nil } + fInfo, err := d.Info() + if err != nil { + return err + } + mode := fInfo.Mode() + if !mode.IsRegular() { // for symlinks and other special files, just skip + return nil + } base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash) for _, g := range giteaTemplateFile.Globs() { - if g.Match(base) { - content, err := os.ReadFile(path) - if err != nil { - return err - } - - generatedContent := []byte(generateExpansion(ctx, string(content), templateRepo, generateRepo, false)) - if err := os.WriteFile(path, generatedContent, 0o644); err != nil { - return err - } - - substPath := filepath.FromSlash(filepath.Join(tmpDirSlash, generateExpansion(ctx, base, templateRepo, generateRepo, true))) - - // Create parent subdirectories if needed or continue silently if it exists - if err = os.MkdirAll(filepath.Dir(substPath), 0o755); err != nil { - return err - } - - // Substitute filename variables - if err = os.Rename(path, substPath); err != nil { - return err - } - break + if !g.Match(base) { + continue + } + + content, err := os.ReadFile(path) + if err != nil { + return err + } + + generatedContent := []byte(generateExpansion(ctx, string(content), templateRepo, generateRepo, false)) + if err := os.WriteFile(path, generatedContent, 0o644); err != nil { + return err + } + + substPath := filepath.FromSlash(filepath.Join(tmpDirSlash, generateExpansion(ctx, base, templateRepo, generateRepo, true))) + + // Create parent subdirectories if needed or continue silently if it exists + if err = os.MkdirAll(filepath.Dir(substPath), 0o755); err != nil { + return err + } + + // Substitute filename variables + if err = os.Rename(path, substPath); err != nil { + return err } + break } return nil }) // end: WalkDir From c04b917af148209648f6704929beaefb157c6676 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 17 Oct 2025 12:32:06 -0700 Subject: [PATCH 2/3] Fix lint --- models/asymkey/ssh_key_authorized_keys.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go index b3c071043280b..bd453f084f0a5 100644 --- a/models/asymkey/ssh_key_authorized_keys.go +++ b/models/asymkey/ssh_key_authorized_keys.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "golang.org/x/crypto/ssh" ) From 9aaf6c38d7773317b7a388bd857494aaaff81243 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 17 Oct 2025 19:48:29 -0700 Subject: [PATCH 3/3] Fix test --- tests/integration/cmd_keys_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/cmd_keys_test.go b/tests/integration/cmd_keys_test.go index 3878302ef0836..d13ea1f44fd9d 100644 --- a/tests/integration/cmd_keys_test.go +++ b/tests/integration/cmd_keys_test.go @@ -30,7 +30,7 @@ func Test_CmdKeys(t *testing.T) { "with_key", []string{"keys", "-e", "git", "-u", "git", "-t", "ssh-rsa", "-k", "AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM="}, false, - "# gitea public key\ncommand=\"" + setting.AppPath + " --config=" + util.ShellEscape(setting.CustomConf) + " serv key-1\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM= user2@localhost\n", + "# gitea public key\ncommand=\"" + setting.AppPath + " --config=" + util.ShellEscape(setting.CustomConf) + " serv key-1\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM=\n", }, {"invalid", []string{"keys", "--not-a-flag=git"}, true, "Incorrect Usage: flag provided but not defined: -not-a-flag\n\n"}, }