Skip to content

Commit 5218b78

Browse files
committed
backport Refactor LFS SSH and internal routers (go-gitea#32473)
1 parent b48df10 commit 5218b78

File tree

5 files changed

+28
-15
lines changed

5 files changed

+28
-15
lines changed

models/migrations/v1_21/v276.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"code.gitea.io/gitea/modules/git"
1313
giturl "code.gitea.io/gitea/modules/git/url"
1414
"code.gitea.io/gitea/modules/setting"
15+
"code.gitea.io/gitea/modules/util"
1516

1617
"xorm.io/xorm"
1718
)
@@ -163,7 +164,9 @@ func migratePushMirrors(x *xorm.Engine) error {
163164

164165
func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
165166
repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(ownerName), strings.ToLower(repoName)+".git")
166-
167+
if exist, _ := util.IsExist(repoPath); !exist {
168+
return "", nil
169+
}
167170
remoteURL, err := git.GetRemoteAddress(context.Background(), repoPath, remoteName)
168171
if err != nil {
169172
return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)

modules/git/batch_reader.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,8 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
146146
}
147147

148148
// ReadBatchLine reads the header line from cat-file --batch
149-
// We expect:
150-
// <sha> SP <type> SP <size> LF
151-
// sha is a hex encoded here
149+
// We expect: <oid> SP <type> SP <size> LF
150+
// then leaving the rest of the stream "<contents> LF" to be read
152151
func ReadBatchLine(rd *bufio.Reader) (sha []byte, typ string, size int64, err error) {
153152
typ, err = rd.ReadString('\n')
154153
if err != nil {

modules/private/internal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Ensure you are running in the correct environment or set the correct configurati
4343
req := httplib.NewRequest(url, method).
4444
SetContext(ctx).
4545
Header("X-Real-IP", getClientIP()).
46-
Header("Authorization", fmt.Sprintf("Bearer %s", setting.InternalToken)).
46+
Header("X-Gitea-Internal-Auth", fmt.Sprintf("Bearer %s", setting.InternalToken)).
4747
SetTLSClientConfig(&tls.Config{
4848
InsecureSkipVerify: true,
4949
ServerName: setting.Domain,

modules/web/route.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package web
55

66
import (
77
"net/http"
8+
"reflect"
89
"strings"
910

1011
"code.gitea.io/gitea/modules/web/middleware"
@@ -80,15 +81,23 @@ func (r *Route) getPattern(pattern string) string {
8081
return strings.TrimSuffix(newPattern, "/")
8182
}
8283

84+
func isNilOrFuncNil(v any) bool {
85+
if v == nil {
86+
return true
87+
}
88+
r := reflect.ValueOf(v)
89+
return r.Kind() == reflect.Func && r.IsNil()
90+
}
91+
8392
func (r *Route) wrapMiddlewareAndHandler(h []any) ([]func(http.Handler) http.Handler, http.HandlerFunc) {
8493
handlerProviders := make([]func(http.Handler) http.Handler, 0, len(r.curMiddlewares)+len(h)+1)
8594
for _, m := range r.curMiddlewares {
86-
if m != nil {
95+
if !isNilOrFuncNil(m) {
8796
handlerProviders = append(handlerProviders, toHandlerProvider(m))
8897
}
8998
}
9099
for _, m := range h {
91-
if h != nil {
100+
if !isNilOrFuncNil(m) {
92101
handlerProviders = append(handlerProviders, toHandlerProvider(m))
93102
}
94103
}

routers/private/internal.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package private
66

77
import (
8+
"crypto/subtle"
89
"net/http"
910
"strings"
1011

@@ -18,22 +19,23 @@ import (
1819
chi_middleware "github.com/go-chi/chi/v5/middleware"
1920
)
2021

21-
// CheckInternalToken check internal token is set
22-
func CheckInternalToken(next http.Handler) http.Handler {
22+
func authInternal(next http.Handler) http.Handler {
2323
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
24-
tokens := req.Header.Get("Authorization")
25-
fields := strings.SplitN(tokens, " ", 2)
2624
if setting.InternalToken == "" {
2725
log.Warn(`The INTERNAL_TOKEN setting is missing from the configuration file: %q, internal API can't work.`, setting.CustomConf)
2826
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
2927
return
3028
}
31-
if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken {
29+
30+
tokens := req.Header.Get("X-Gitea-Internal-Auth") // TODO: use something like JWT or HMAC to avoid passing the token in the clear
31+
after, found := strings.CutPrefix(tokens, "Bearer ")
32+
authSucceeded := found && subtle.ConstantTimeCompare([]byte(after), []byte(setting.InternalToken)) == 1
33+
if !authSucceeded {
3234
log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens)
3335
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
34-
} else {
35-
next.ServeHTTP(w, req)
36+
return
3637
}
38+
next.ServeHTTP(w, req)
3739
})
3840
}
3941

@@ -51,7 +53,7 @@ func bind[T any](_ T) any {
5153
func Routes() *web.Route {
5254
r := web.NewRoute()
5355
r.Use(context.PrivateContexter())
54-
r.Use(CheckInternalToken)
56+
r.Use(authInternal)
5557
// Log the real ip address of the request from SSH is really helpful for diagnosing sometimes.
5658
// Since internal API will be sent only from Gitea sub commands and it's under control (checked by InternalToken), we can trust the headers.
5759
r.Use(chi_middleware.RealIP)

0 commit comments

Comments
 (0)