@@ -17,30 +17,14 @@ import (
1717 "code.gitea.io/gitea/modules/log"
1818 "code.gitea.io/gitea/modules/setting"
1919 "code.gitea.io/gitea/modules/util"
20- )
2120
22- // _____ __ .__ .__ .___
23- // / _ \ __ ___/ |_| |__ ___________|__|_______ ____ __| _/
24- // / /_\ \| | \ __\ | \ / _ \_ __ \ \___ // __ \ / __ |
25- // / | \ | /| | | Y ( <_> ) | \/ |/ /\ ___// /_/ |
26- // \____|__ /____/ |__| |___| /\____/|__| |__/_____ \\___ >____ |
27- // \/ \/ \/ \/ \/
28- // ____ __.
29- // | |/ _|____ ___.__. ______
30- // | <_/ __ < | |/ ___/
31- // | | \ ___/\___ |\___ \
32- // |____|__ \___ > ____/____ >
33- // \/ \/\/ \/
34- //
35- // This file contains functions for creating authorized_keys files
36- //
37- // There is a dependence on the database within RegeneratePublicKeys however most of these functions probably belong in a module
38-
39- const (
40- tplCommentPrefix = `# gitea public key`
41- tplPublicKey = tplCommentPrefix + "\n " + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict %s` + "\n "
21+ "golang.org/x/crypto/ssh"
4222)
4323
24+ // AuthorizedStringCommentPrefix is a magic tag
25+ // some functions like RegeneratePublicKeys needs this tag to skip the keys generated by Gitea, while keep other keys
26+ const AuthorizedStringCommentPrefix = `# gitea public key`
27+
4428var sshOpLocker sync.Mutex
4529
4630func WithSSHOpLocker (f func () error ) error {
@@ -50,17 +34,45 @@ func WithSSHOpLocker(f func() error) error {
5034}
5135
5236// AuthorizedStringForKey creates the authorized keys string appropriate for the provided key
53- func AuthorizedStringForKey (key * PublicKey ) string {
37+ func AuthorizedStringForKey (key * PublicKey ) ( string , error ) {
5438 sb := & strings.Builder {}
55- _ = setting .SSH .AuthorizedKeysCommandTemplateTemplate .Execute (sb , map [string ]any {
39+ _ , err := writeAuthorizedStringForKey (key , sb )
40+ return sb .String (), err
41+ }
42+
43+ // WriteAuthorizedStringForValidKey writes the authorized key for the provided key. If the key is invalid, it does nothing.
44+ func WriteAuthorizedStringForValidKey (key * PublicKey , w io.Writer ) error {
45+ validKey , err := writeAuthorizedStringForKey (key , w )
46+ if ! validKey {
47+ log .Debug ("WriteAuthorizedStringForValidKey: key %s is not valid: %v" , key , err )
48+ return nil
49+ }
50+ return err
51+ }
52+
53+ func writeAuthorizedStringForKey (key * PublicKey , w io.Writer ) (keyValid bool , err error ) {
54+ const tpl = AuthorizedStringCommentPrefix + "\n " + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict %s %s` + "\n "
55+ pubKey , _ , _ , _ , err := ssh .ParseAuthorizedKey ([]byte (key .Content ))
56+ if err != nil {
57+ return false , err
58+ }
59+ // now the key is valid, the code below could only return template/IO related errors
60+ sbCmd := & strings.Builder {}
61+ err = setting .SSH .AuthorizedKeysCommandTemplateTemplate .Execute (sbCmd , map [string ]any {
5662 "AppPath" : util .ShellEscape (setting .AppPath ),
5763 "AppWorkPath" : util .ShellEscape (setting .AppWorkPath ),
5864 "CustomConf" : util .ShellEscape (setting .CustomConf ),
5965 "CustomPath" : util .ShellEscape (setting .CustomPath ),
6066 "Key" : key ,
6167 })
62-
63- return fmt .Sprintf (tplPublicKey , util .ShellEscape (sb .String ()), key .Content )
68+ if err != nil {
69+ return true , err
70+ }
71+ sshCommandEscaped := util .ShellEscape (sbCmd .String ())
72+ sshKeyMarshalled := strings .TrimSpace (string (ssh .MarshalAuthorizedKey (pubKey )))
73+ sshKeyComment := fmt .Sprintf ("user-%d" , key .OwnerID )
74+ _ , err = fmt .Fprintf (w , tpl , sshCommandEscaped , sshKeyMarshalled , sshKeyComment )
75+ return true , err
6476}
6577
6678// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
@@ -112,18 +124,17 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
112124 if key .Type == KeyTypePrincipal {
113125 continue
114126 }
115- if _ , err = f . WriteString (key . AuthorizedString () ); err != nil {
127+ if err = WriteAuthorizedStringForValidKey (key , f ); err != nil {
116128 return err
117129 }
118130 }
119131 return nil
120132}
121133
122134// RegeneratePublicKeys regenerates the authorized_keys file
123- func RegeneratePublicKeys (ctx context.Context , t io.StringWriter ) error {
135+ func RegeneratePublicKeys (ctx context.Context , t io.Writer ) error {
124136 if err := db .GetEngine (ctx ).Where ("type != ?" , KeyTypePrincipal ).Iterate (new (PublicKey ), func (idx int , bean any ) (err error ) {
125- _ , err = t .WriteString ((bean .(* PublicKey )).AuthorizedString ())
126- return err
137+ return WriteAuthorizedStringForValidKey (bean .(* PublicKey ), t )
127138 }); err != nil {
128139 return err
129140 }
@@ -144,7 +155,7 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
144155 scanner := bufio .NewScanner (f )
145156 for scanner .Scan () {
146157 line := scanner .Text ()
147- if strings .HasPrefix (line , tplCommentPrefix ) {
158+ if strings .HasPrefix (line , AuthorizedStringCommentPrefix ) {
148159 scanner .Scan ()
149160 continue
150161 }
0 commit comments