Skip to content

Commit 3dbbf16

Browse files
committed
fix
1 parent 43c252d commit 3dbbf16

File tree

16 files changed

+353
-260
lines changed

16 files changed

+353
-260
lines changed

cmd/serv.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,10 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error
111111
if !setting.IsProd {
112112
_, _ = fmt.Fprintln(os.Stderr, "Gitea:", logMsg)
113113
}
114-
if userMessage != "" {
115-
if unicode.IsPunct(rune(userMessage[len(userMessage)-1])) {
116-
logMsg = userMessage + " " + logMsg
117-
} else {
118-
logMsg = userMessage + ". " + logMsg
119-
}
114+
if unicode.IsPunct(rune(userMessage[len(userMessage)-1])) {
115+
logMsg = userMessage + " " + logMsg
116+
} else {
117+
logMsg = userMessage + ". " + logMsg
120118
}
121119
_ = private.SSHLog(ctx, true, logMsg)
122120
}
@@ -288,10 +286,10 @@ func runServ(c *cli.Context) error {
288286
if allowedCommands.Contains(verb) {
289287
if allowedCommandsLfs.Contains(verb) {
290288
if !setting.LFS.StartServer {
291-
return fail(ctx, "Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
289+
return fail(ctx, "LFS Server is not enabled", "")
292290
}
293291
if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
294-
return fail(ctx, "Unknown git command", "LFS SSH transfer connection denied, pure SSH protocol is disabled")
292+
return fail(ctx, "LFS SSH transfer is not enabled", "")
295293
}
296294
if len(words) > 2 {
297295
lfsVerb = words[2]

models/fixtures/lfs_meta_object.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
# These are the LFS objects in user2/lfs.git
2+
# user2/lfs is an INVALID repository
3+
#
4+
# commit e9c32647bab825977942598c0efa415de300304b (HEAD -> master)
5+
# Author: Rowan Bohde <[email protected]>
6+
# Date: Thu Aug 1 14:38:23 2024 -0500
7+
#
8+
# add invalid lfs file
29
-
310

411
id: 1
@@ -11,7 +18,7 @@
1118

1219
id: 2
1320
oid: 2eccdb43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c
14-
size: 107
21+
size: 107 # real size is 2048
1522
repository_id: 54
1623
created_unix: 1671607299
1724

@@ -30,3 +37,12 @@
3037
size: 25
3138
repository_id: 54
3239
created_unix: 1671607299
40+
41+
# this file is missing
42+
#-
43+
#
44+
# id: 5
45+
# oid: 9d178b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351
46+
# size: 25
47+
# repository_id: 54
48+
# created_unix: 1671607299

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/lfstransfer/backend/backend.go

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ var _ transfer.Backend = &GiteaBackend{}
3333

3434
// GiteaBackend is an adapter between git-lfs-transfer library and Gitea's internal LFS API
3535
type GiteaBackend struct {
36-
ctx context.Context
37-
server *url.URL
38-
op string
39-
token string
40-
itoken string
41-
logger transfer.Logger
36+
ctx context.Context
37+
server *url.URL
38+
op string
39+
authToken string
40+
internalAuth string
41+
logger transfer.Logger
4242
}
4343

4444
func New(ctx context.Context, repo, op, token string, logger transfer.Logger) (transfer.Backend, error) {
@@ -48,7 +48,7 @@ func New(ctx context.Context, repo, op, token string, logger transfer.Logger) (t
4848
return nil, err
4949
}
5050
server = server.JoinPath("api/internal/repo", repo, "info/lfs")
51-
return &GiteaBackend{ctx: ctx, server: server, op: op, token: token, itoken: fmt.Sprintf("Bearer %s", setting.InternalToken), logger: logger}, nil
51+
return &GiteaBackend{ctx: ctx, server: server, op: op, authToken: token, internalAuth: fmt.Sprintf("Bearer %s", setting.InternalToken), logger: logger}, nil
5252
}
5353

5454
// Batch implements transfer.Backend
@@ -73,10 +73,10 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
7373
}
7474
url := g.server.JoinPath("objects/batch").String()
7575
headers := map[string]string{
76-
headerAuthorisation: g.itoken,
77-
headerAuthX: g.token,
78-
headerAccept: mimeGitLFS,
79-
headerContentType: mimeGitLFS,
76+
headerAuthorisation: g.authToken,
77+
headerGiteaInternalAuth: g.internalAuth,
78+
headerAccept: mimeGitLFS,
79+
headerContentType: mimeGitLFS,
8080
}
8181
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
8282
resp, err := req.Response()
@@ -183,9 +183,9 @@ func (g *GiteaBackend) Download(oid string, args transfer.Args) (io.ReadCloser,
183183
}
184184
url := action.Href
185185
headers := map[string]string{
186-
headerAuthorisation: g.itoken,
187-
headerAuthX: g.token,
188-
headerAccept: mimeOctetStream,
186+
headerAuthorisation: g.authToken,
187+
headerGiteaInternalAuth: g.internalAuth,
188+
headerAccept: mimeOctetStream,
189189
}
190190
req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
191191
resp, err := req.Response()
@@ -229,10 +229,10 @@ func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer
229229
}
230230
url := action.Href
231231
headers := map[string]string{
232-
headerAuthorisation: g.itoken,
233-
headerAuthX: g.token,
234-
headerContentType: mimeOctetStream,
235-
headerContentLength: strconv.FormatInt(size, 10),
232+
headerAuthorisation: g.authToken,
233+
headerGiteaInternalAuth: g.internalAuth,
234+
headerContentType: mimeOctetStream,
235+
headerContentLength: strconv.FormatInt(size, 10),
236236
}
237237
reqBytes, err := io.ReadAll(r)
238238
if err != nil {
@@ -279,10 +279,10 @@ func (g *GiteaBackend) Verify(oid string, size int64, args transfer.Args) (trans
279279
}
280280
url := action.Href
281281
headers := map[string]string{
282-
headerAuthorisation: g.itoken,
283-
headerAuthX: g.token,
284-
headerAccept: mimeGitLFS,
285-
headerContentType: mimeGitLFS,
282+
headerAuthorisation: g.authToken,
283+
headerGiteaInternalAuth: g.internalAuth,
284+
headerAccept: mimeGitLFS,
285+
headerContentType: mimeGitLFS,
286286
}
287287
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
288288
resp, err := req.Response()

modules/lfstransfer/backend/lock.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ import (
2121
var _ transfer.LockBackend = &giteaLockBackend{}
2222

2323
type giteaLockBackend struct {
24-
ctx context.Context
25-
g *GiteaBackend
26-
server *url.URL
27-
token string
28-
itoken string
29-
logger transfer.Logger
24+
ctx context.Context
25+
g *GiteaBackend
26+
server *url.URL
27+
authToken string
28+
internalAuth string
29+
logger transfer.Logger
3030
}
3131

3232
func newGiteaLockBackend(g *GiteaBackend) transfer.LockBackend {
3333
server := g.server.JoinPath("locks")
34-
return &giteaLockBackend{ctx: g.ctx, g: g, server: server, token: g.token, itoken: g.itoken, logger: g.logger}
34+
return &giteaLockBackend{ctx: g.ctx, g: g, server: server, authToken: g.authToken, internalAuth: g.internalAuth, logger: g.logger}
3535
}
3636

3737
// Create implements transfer.LockBackend
@@ -45,10 +45,10 @@ func (g *giteaLockBackend) Create(path, refname string) (transfer.Lock, error) {
4545
}
4646
url := g.server.String()
4747
headers := map[string]string{
48-
headerAuthorisation: g.itoken,
49-
headerAuthX: g.token,
50-
headerAccept: mimeGitLFS,
51-
headerContentType: mimeGitLFS,
48+
headerAuthorisation: g.authToken,
49+
headerGiteaInternalAuth: g.internalAuth,
50+
headerAccept: mimeGitLFS,
51+
headerContentType: mimeGitLFS,
5252
}
5353
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
5454
resp, err := req.Response()
@@ -97,10 +97,10 @@ func (g *giteaLockBackend) Unlock(lock transfer.Lock) error {
9797
}
9898
url := g.server.JoinPath(lock.ID(), "unlock").String()
9999
headers := map[string]string{
100-
headerAuthorisation: g.itoken,
101-
headerAuthX: g.token,
102-
headerAccept: mimeGitLFS,
103-
headerContentType: mimeGitLFS,
100+
headerAuthorisation: g.authToken,
101+
headerGiteaInternalAuth: g.internalAuth,
102+
headerAccept: mimeGitLFS,
103+
headerContentType: mimeGitLFS,
104104
}
105105
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
106106
resp, err := req.Response()
@@ -180,10 +180,10 @@ func (g *giteaLockBackend) queryLocks(v url.Values) ([]transfer.Lock, string, er
180180
urlq.RawQuery = v.Encode()
181181
url := urlq.String()
182182
headers := map[string]string{
183-
headerAuthorisation: g.itoken,
184-
headerAuthX: g.token,
185-
headerAccept: mimeGitLFS,
186-
headerContentType: mimeGitLFS,
183+
headerAuthorisation: g.authToken,
184+
headerGiteaInternalAuth: g.internalAuth,
185+
headerAccept: mimeGitLFS,
186+
headerContentType: mimeGitLFS,
187187
}
188188
req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
189189
resp, err := req.Response()

modules/lfstransfer/backend/util.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ import (
2020

2121
// HTTP headers
2222
const (
23-
headerAccept = "Accept"
24-
headerAuthorisation = "Authorization"
25-
headerAuthX = "X-Auth"
26-
headerContentType = "Content-Type"
27-
headerContentLength = "Content-Length"
23+
headerAccept = "Accept"
24+
headerAuthorisation = "Authorization"
25+
headerGiteaInternalAuth = "X-Gitea-Internal-Auth"
26+
headerContentType = "Content-Type"
27+
headerContentLength = "Content-Length"
2828
)
2929

3030
// MIME types

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,

routers/common/lfs.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package common
2+
3+
import (
4+
"code.gitea.io/gitea/modules/web"
5+
"code.gitea.io/gitea/services/lfs"
6+
)
7+
8+
func AddOwnerRepoGitLFSRoutes(m *web.Router, middlewares ...any) {
9+
// shared by web and internal routers
10+
m.Group("/{username}/{reponame}/info/lfs", func() {
11+
m.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler)
12+
m.Put("/objects/{oid}/{size}", lfs.UploadHandler)
13+
m.Get("/objects/{oid}/{filename}", lfs.DownloadHandler)
14+
m.Get("/objects/{oid}", lfs.DownloadHandler)
15+
m.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler)
16+
m.Group("/locks", func() {
17+
m.Get("/", lfs.GetListLockHandler)
18+
m.Post("/", lfs.PostLockHandler)
19+
m.Post("/verify", lfs.VerifyLockHandler)
20+
m.Post("/{lid}/unlock", lfs.UnLockHandler)
21+
}, lfs.CheckAcceptMediaType)
22+
}, middlewares...)
23+
}

routers/private/internal.go

Lines changed: 15 additions & 36 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

@@ -14,28 +15,30 @@ import (
1415
"code.gitea.io/gitea/modules/web"
1516
"code.gitea.io/gitea/routers/common"
1617
"code.gitea.io/gitea/services/context"
17-
"code.gitea.io/gitea/services/lfs"
1818

1919
"gitea.com/go-chi/binding"
2020
chi_middleware "github.com/go-chi/chi/v5/middleware"
2121
)
2222

23-
// CheckInternalToken check internal token is set
24-
func CheckInternalToken(next http.Handler) http.Handler {
23+
const RouterMockPointInternalLFS = "internal-lfs"
24+
25+
func authInternal(next http.Handler) http.Handler {
2526
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
26-
tokens := req.Header.Get("Authorization")
27-
fields := strings.SplitN(tokens, " ", 2)
2827
if setting.InternalToken == "" {
2928
log.Warn(`The INTERNAL_TOKEN setting is missing from the configuration file: %q, internal API can't work.`, setting.CustomConf)
3029
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
3130
return
3231
}
33-
if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken {
32+
33+
tokens := req.Header.Get("X-Gitea-Internal-Auth") // TODO: use something like JWT or HMAC to avoid passing the token in the clear
34+
after, found := strings.CutPrefix(tokens, "Bearer ")
35+
authSucceeded := found && subtle.ConstantTimeCompare([]byte(after), []byte(setting.InternalToken)) == 0
36+
if !authSucceeded {
3437
log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens)
3538
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
36-
} else {
37-
next.ServeHTTP(w, req)
39+
return
3840
}
41+
next.ServeHTTP(w, req)
3942
})
4043
}
4144

@@ -48,20 +51,12 @@ func bind[T any](_ T) any {
4851
}
4952
}
5053

51-
// SwapAuthToken swaps Authorization header with X-Auth header
52-
func swapAuthToken(next http.Handler) http.Handler {
53-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
54-
req.Header.Set("Authorization", req.Header.Get("X-Auth"))
55-
next.ServeHTTP(w, req)
56-
})
57-
}
58-
5954
// Routes registers all internal APIs routes to web application.
6055
// These APIs will be invoked by internal commands for example `gitea serv` and etc.
6156
func Routes() *web.Router {
6257
r := web.NewRouter()
6358
r.Use(context.PrivateContexter())
64-
r.Use(CheckInternalToken)
59+
r.Use(authInternal)
6560
// Log the real ip address of the request from SSH is really helpful for diagnosing sometimes.
6661
// Since internal API will be sent only from Gitea sub commands and it's under control (checked by InternalToken), we can trust the headers.
6762
r.Use(chi_middleware.RealIP)
@@ -90,25 +85,9 @@ func Routes() *web.Router {
9085
r.Post("/restore_repo", RestoreRepo)
9186
r.Post("/actions/generate_actions_runner_token", GenerateActionsRunnerToken)
9287

93-
r.Group("/repo/{username}/{reponame}", func() {
94-
r.Group("/info/lfs", func() {
95-
r.Post("/objects/batch", lfs.CheckAcceptMediaType, lfs.BatchHandler)
96-
r.Put("/objects/{oid}/{size}", lfs.UploadHandler)
97-
r.Get("/objects/{oid}/{filename}", lfs.DownloadHandler)
98-
r.Get("/objects/{oid}", lfs.DownloadHandler)
99-
r.Post("/verify", lfs.CheckAcceptMediaType, lfs.VerifyHandler)
100-
r.Group("/locks", func() {
101-
r.Get("/", lfs.GetListLockHandler)
102-
r.Post("/", lfs.PostLockHandler)
103-
r.Post("/verify", lfs.VerifyLockHandler)
104-
r.Post("/{lid}/unlock", lfs.UnLockHandler)
105-
}, lfs.CheckAcceptMediaType)
106-
r.Any("/*", func(ctx *context.Context) {
107-
ctx.NotFound("", nil)
108-
})
109-
}, swapAuthToken)
110-
}, common.Sessioner(), context.Contexter())
111-
// end "/repo/{username}/{reponame}": git (LFS) API mirror
88+
r.Group("/repo", func() {
89+
common.AddOwnerRepoGitLFSRoutes(r, web.RouterMockPoint(RouterMockPointInternalLFS))
90+
})
11291

11392
return r
11493
}

0 commit comments

Comments
 (0)