use gertz route for std url
This commit is contained in:
78
main.go
78
main.go
@@ -404,56 +404,54 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加Recovery中间件
|
r.Use(recovery.Recovery()) // Recovery中间件
|
||||||
r.Use(recovery.Recovery())
|
r.Use(loggin.Middleware()) // log中间件
|
||||||
// 添加log中间件
|
|
||||||
r.Use(loggin.Middleware())
|
|
||||||
|
|
||||||
setupApi(cfg, r, version)
|
setupApi(cfg, r, version)
|
||||||
|
|
||||||
setupPages(cfg, r)
|
setupPages(cfg, r)
|
||||||
|
|
||||||
/*
|
r.GET("/github.com/:username/:repo/releases/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||||
// 1. GitHub Releases/Archive - Use distinct path segments for type
|
ctx = context.WithValue(ctx, "matcher", "release")
|
||||||
r.GET("/github.com/:username/:repo/releases/*filepath", func(ctx context.Context, c *app.RequestContext) { // Distinct path for releases
|
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
})
|
||||||
})
|
|
||||||
|
|
||||||
r.GET("/github.com/:username/:repo/archive/*filepath", func(ctx context.Context, c *app.RequestContext) { // Distinct path for archive
|
r.GET("/github.com/:username/:repo/archive/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
ctx = context.WithValue(ctx, "matcher", "release")
|
||||||
})
|
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
|
})
|
||||||
|
|
||||||
// 2. GitHub Blob/Raw - Use distinct path segments for type
|
r.GET("/github.com/:username/:repo/blob/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||||
r.GET("/github.com/:username/:repo/blob/*filepath", func(ctx context.Context, c *app.RequestContext) { // Distinct path for blob
|
ctx = context.WithValue(ctx, "matcher", "blob")
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
})
|
})
|
||||||
|
|
||||||
r.GET("/github.com/:username/:repo/raw/*filepath", func(ctx context.Context, c *app.RequestContext) { // Distinct path for raw
|
r.GET("/github.com/:username/:repo/raw/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
ctx = context.WithValue(ctx, "matcher", "raw")
|
||||||
})
|
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
|
})
|
||||||
|
|
||||||
r.GET("/github.com/:username/:repo/info/*filepath", func(ctx context.Context, c *app.RequestContext) { // Distinct path for info
|
r.GET("/github.com/:username/:repo/info/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
ctx = context.WithValue(ctx, "matcher", "gitclone")
|
||||||
})
|
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
r.GET("/github.com/:username/:repo/git-upload-pack", func(ctx context.Context, c *app.RequestContext) {
|
})
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
r.GET("/github.com/:username/:repo/git-upload-pack", func(ctx context.Context, c *app.RequestContext) {
|
||||||
})
|
ctx = context.WithValue(ctx, "matcher", "gitclone")
|
||||||
|
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
|
})
|
||||||
|
|
||||||
// 4. Raw GitHubusercontent - Keep as is (assuming it's distinct enough)
|
r.GET("/raw.githubusercontent.com/:username/:repo/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||||
r.GET("/raw.githubusercontent.com/:username/:repo/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
ctx = context.WithValue(ctx, "matcher", "raw")
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 5. Gist GitHubusercontent - Keep as is (assuming it's distinct enough)
|
r.GET("/gist.githubusercontent.com/:username/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||||
r.GET("/gist.githubusercontent.com/:username/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
ctx = context.WithValue(ctx, "matcher", "gist")
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 6. GitHub API Repos - Keep as is (assuming it's distinct enough)
|
r.GET("/api.github.com/repos/:username/:repo/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||||
r.GET("/api.github.com/repos/:username/:repo/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
ctx = context.WithValue(ctx, "matcher", "api")
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
})
|
})
|
||||||
*/
|
|
||||||
|
|
||||||
r.NoRoute(func(ctx context.Context, c *app.RequestContext) {
|
r.NoRoute(func(ctx context.Context, c *app.RequestContext) {
|
||||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||||
|
|||||||
120
proxy/handler.go
120
proxy/handler.go
@@ -147,3 +147,123 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RoutingHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *rate.IPRateLimiter) app.HandlerFunc {
|
||||||
|
return func(ctx context.Context, c *app.RequestContext) {
|
||||||
|
// 输出所有传入参数
|
||||||
|
logDebug("All Request Params: %v", c.Params)
|
||||||
|
logDebug("Context Params(Matcher): %v", ctx.Value("matcher"))
|
||||||
|
|
||||||
|
// 限制访问频率
|
||||||
|
if cfg.RateLimit.Enabled {
|
||||||
|
|
||||||
|
var allowed bool
|
||||||
|
|
||||||
|
switch cfg.RateLimit.RateMethod {
|
||||||
|
case "ip":
|
||||||
|
allowed = iplimiter.Allow(c.ClientIP())
|
||||||
|
case "total":
|
||||||
|
allowed = limiter.Allow()
|
||||||
|
default:
|
||||||
|
logWarning("Invalid RateLimit Method")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allowed {
|
||||||
|
c.JSON(http.StatusTooManyRequests, map[string]string{"error": "Too Many Requests"})
|
||||||
|
logWarning("%s %s %s %s %s 429-TooManyRequests", c.ClientIP(), c.Method(), c.Request.RequestURI(), c.Request.Header.UserAgent(), c.Request.Header.GetProtocol())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
rawPath string
|
||||||
|
errMsg string
|
||||||
|
)
|
||||||
|
|
||||||
|
rawPath = strings.TrimPrefix(string(c.Request.RequestURI()), "/") // 去掉前缀/
|
||||||
|
|
||||||
|
var (
|
||||||
|
user string
|
||||||
|
repo string
|
||||||
|
matcher string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
user = c.Param("user")
|
||||||
|
repo = c.Param("repo")
|
||||||
|
matcher = ctx.Value("matcher").(string)
|
||||||
|
|
||||||
|
logInfo("%s %s %s %s %s Matched-Username: %s, Matched-Repo: %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), user, repo)
|
||||||
|
// dump log 记录详细信息 c.ClientIP(), c.Method(), rawPath,c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), full Header
|
||||||
|
logDump("%s %s %s %s %s %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), c.Request.Header.Header())
|
||||||
|
|
||||||
|
// 白名单检查
|
||||||
|
if cfg.Whitelist.Enabled {
|
||||||
|
var whitelist bool
|
||||||
|
whitelist = auth.CheckWhitelist(user, repo)
|
||||||
|
if !whitelist {
|
||||||
|
errMsg = fmt.Sprintf("Whitelist Blocked repo: %s/%s", user, repo)
|
||||||
|
c.JSON(http.StatusForbidden, map[string]string{"error": errMsg})
|
||||||
|
logWarning("%s %s %s %s %s Whitelist Blocked repo: %s/%s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), user, repo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 黑名单检查
|
||||||
|
if cfg.Blacklist.Enabled {
|
||||||
|
var blacklist bool
|
||||||
|
blacklist = auth.CheckBlacklist(user, repo)
|
||||||
|
if blacklist {
|
||||||
|
errMsg = fmt.Sprintf("Blacklist Blocked repo: %s/%s", user, repo)
|
||||||
|
c.JSON(http.StatusForbidden, map[string]string{"error": errMsg})
|
||||||
|
logWarning("%s %s %s %s %s Blacklist Blocked repo: %s/%s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), user, repo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matcher == "api" && !cfg.Auth.ForceAllowApi {
|
||||||
|
if cfg.Auth.Method != "header" || !cfg.Auth.Enabled {
|
||||||
|
c.JSON(http.StatusForbidden, map[string]string{"error": "Github API Req without AuthHeader is Not Allowed"})
|
||||||
|
logWarning("%s %s %s %s %s AuthHeader Unavailable", c.ClientIP(), c.Method(), rawPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鉴权
|
||||||
|
if cfg.Auth.Enabled {
|
||||||
|
var authcheck bool
|
||||||
|
authcheck, err = auth.AuthHandler(ctx, c, cfg)
|
||||||
|
if !authcheck {
|
||||||
|
//c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
|
||||||
|
c.AbortWithStatusJSON(401, map[string]string{"error": "Unauthorized"})
|
||||||
|
logWarning("%s %s %s %s %s Auth-Error: %v", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 若匹配api.github.com/repos/用户名/仓库名/路径, 则检查是否开启HeaderAuth
|
||||||
|
|
||||||
|
// 处理blob/raw路径
|
||||||
|
if matcher == "blob" {
|
||||||
|
rawPath = strings.Replace(rawPath, "/blob/", "/raw/", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为rawpath加入https:// 头
|
||||||
|
rawPath = "https://" + rawPath
|
||||||
|
|
||||||
|
// IP METHOD URL USERAGENT PROTO MATCHES
|
||||||
|
logDebug("%s %s %s %s %s Matched: %v", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), matcher)
|
||||||
|
|
||||||
|
switch matcher {
|
||||||
|
case "releases", "blob", "raw", "gist", "api":
|
||||||
|
ChunkedProxyRequest(ctx, c, rawPath, cfg, matcher)
|
||||||
|
case "clone":
|
||||||
|
GitReq(ctx, c, rawPath, cfg, "git")
|
||||||
|
default:
|
||||||
|
c.String(http.StatusForbidden, "Invalid input.")
|
||||||
|
fmt.Println("Invalid input.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,8 +65,10 @@ func Matcher(rawPath string, cfg *config.Config) (string, string, string, error)
|
|||||||
switch parts[2] {
|
switch parts[2] {
|
||||||
case "releases", "archive":
|
case "releases", "archive":
|
||||||
matcher = "releases"
|
matcher = "releases"
|
||||||
case "blob", "raw":
|
case "blob":
|
||||||
matcher = "blob"
|
matcher = "blob"
|
||||||
|
case "raw":
|
||||||
|
matcher = "raw"
|
||||||
case "info", "git-upload-pack":
|
case "info", "git-upload-pack":
|
||||||
matcher = "clone"
|
matcher = "clone"
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user