@@ -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,7 +34,8 @@ 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 ) {
38+ 	const  tpl  =  AuthorizedStringCommentPrefix  +  "\n "  +  `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict %s %s`  +  "\n " 
5439	sb  :=  & strings.Builder {}
5540	_  =  setting .SSH .AuthorizedKeysCommandTemplateTemplate .Execute (sb , map [string ]any {
5641		"AppPath" :     util .ShellEscape (setting .AppPath ),
@@ -60,7 +45,14 @@ func AuthorizedStringForKey(key *PublicKey) string {
6045		"Key" :         key ,
6146	})
6247
63- 	return  fmt .Sprintf (tplPublicKey , util .ShellEscape (sb .String ()), key .Content )
48+ 	pubKey , _ , _ , _ , err  :=  ssh .ParseAuthorizedKey ([]byte (key .Content ))
49+ 	if  err  !=  nil  {
50+ 		return  "" , fmt .Errorf ("invalid public key: %w" , err )
51+ 	}
52+ 	sshCommandEscaped  :=  util .ShellEscape (sb .String ())
53+ 	sshKeyMarshalled  :=  strings .TrimSpace (string (ssh .MarshalAuthorizedKey (pubKey )))
54+ 	sshKeyComment  :=  fmt .Sprintf ("user-%d" , key .OwnerID )
55+ 	return  fmt .Sprintf (tpl , sshCommandEscaped , sshKeyMarshalled , sshKeyComment ), nil 
6456}
6557
6658// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file. 
@@ -112,7 +104,12 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
112104		if  key .Type  ==  KeyTypePrincipal  {
113105			continue 
114106		}
115- 		if  _ , err  =  f .WriteString (key .AuthorizedString ()); err  !=  nil  {
107+ 		authorizedString , err  :=  AuthorizedStringForKey (key )
108+ 		if  err  !=  nil  {
109+ 			log .Debug ("AuthorizedStringForKey(%s): %v" , key , err )
110+ 			continue 
111+ 		}
112+ 		if  _ , err  =  f .WriteString (authorizedString ); err  !=  nil  {
116113			return  err 
117114		}
118115	}
@@ -122,7 +119,13 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
122119// RegeneratePublicKeys regenerates the authorized_keys file 
123120func  RegeneratePublicKeys (ctx  context.Context , t  io.StringWriter ) error  {
124121	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 ())
122+ 		key  :=  bean .(* PublicKey )
123+ 		authorizedString , err  :=  AuthorizedStringForKey (key )
124+ 		if  err  !=  nil  {
125+ 			log .Debug ("AuthorizedStringForKey(%s): %v" , key , err )
126+ 			return  nil 
127+ 		}
128+ 		_ , err  =  t .WriteString (authorizedString )
126129		return  err 
127130	}); err  !=  nil  {
128131		return  err 
@@ -144,7 +147,7 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
144147		scanner  :=  bufio .NewScanner (f )
145148		for  scanner .Scan () {
146149			line  :=  scanner .Text ()
147- 			if  strings .HasPrefix (line , tplCommentPrefix ) {
150+ 			if  strings .HasPrefix (line , AuthorizedStringCommentPrefix ) {
148151				scanner .Scan ()
149152				continue 
150153			}
0 commit comments