forked from Gitlink/gitea_hat
新增:路由定义
This commit is contained in:
parent
3e5b2a738c
commit
da9cd18f74
|
|
@ -0,0 +1,273 @@
|
||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/process"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/routers"
|
||||||
|
"code.gitea.io/gitea/routers/install"
|
||||||
|
hat_routers "code.gitlink.org.cn/Gitlink/gitea_hat.git/routers"
|
||||||
|
"github.com/felixge/fgprof"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
ini "gopkg.in/ini.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdWeb represents the available web sub-command.
|
||||||
|
var CmdWeb = cli.Command{
|
||||||
|
Name: "web",
|
||||||
|
Usage: "Start Gitea web server",
|
||||||
|
Description: `Gitea web server is the only thing you need to run,
|
||||||
|
and it takes care of all the other things for you`,
|
||||||
|
Action: runWeb,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "port, p",
|
||||||
|
Value: "3000",
|
||||||
|
Usage: "Temporary port number to prevent conflict",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "install-port",
|
||||||
|
Value: "3000",
|
||||||
|
Usage: "Temporary port number to run the install page on to prevent conflict",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "pid, P",
|
||||||
|
Value: setting.PIDFile,
|
||||||
|
Usage: "Custom pid file path",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "quiet, q",
|
||||||
|
Usage: "Only display Fatal logging errors until logging is set-up",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "verbose",
|
||||||
|
Usage: "Set initial logging to TRACE level until logging is properly set-up",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHTTPRedirector() {
|
||||||
|
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: HTTP Redirector", process.SystemProcessType, true)
|
||||||
|
defer finished()
|
||||||
|
|
||||||
|
source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect)
|
||||||
|
dest := strings.TrimSuffix(setting.AppURL, "/")
|
||||||
|
log.Info("Redirecting: %s to %s", source, dest)
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
target := dest + r.URL.Path
|
||||||
|
if len(r.URL.RawQuery) > 0 {
|
||||||
|
target += "?" + r.URL.RawQuery
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||||
|
})
|
||||||
|
|
||||||
|
err := runHTTP("tcp", source, "HTTP Redirector", handler)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to start port redirection: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWeb(ctx *cli.Context) error {
|
||||||
|
if ctx.Bool("verbose") {
|
||||||
|
_ = log.DelLogger("console")
|
||||||
|
log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "trace", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
|
||||||
|
} else if ctx.Bool("quiet") {
|
||||||
|
_ = log.DelLogger("console")
|
||||||
|
log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "fatal", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if panicked := recover(); panicked != nil {
|
||||||
|
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
managerCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
graceful.InitManager(managerCtx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 {
|
||||||
|
log.Info("Restarting Gitea on PID: %d from parent PID: %d", os.Getpid(), os.Getppid())
|
||||||
|
} else {
|
||||||
|
log.Info("Starting Gitea on PID: %d", os.Getpid())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set pid file setting
|
||||||
|
if ctx.IsSet("pid") {
|
||||||
|
setting.PIDFile = ctx.String("pid")
|
||||||
|
setting.WritePIDFile = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform pre-initialization
|
||||||
|
needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext())
|
||||||
|
if needsInstall {
|
||||||
|
// Flag for port number in case first time run conflict
|
||||||
|
if ctx.IsSet("port") {
|
||||||
|
if err := setPort(ctx.String("port")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ctx.IsSet("install-port") {
|
||||||
|
if err := setPort(ctx.String("install-port")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c := install.Routes()
|
||||||
|
err := listen(c, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Critical("Unable to open listener for installer. Is Gitea already running?")
|
||||||
|
graceful.GetManager().DoGracefulShutdown()
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-graceful.GetManager().IsShutdown():
|
||||||
|
<-graceful.GetManager().Done()
|
||||||
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||||
|
log.Close()
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NoInstallListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
if setting.EnablePprof {
|
||||||
|
go func() {
|
||||||
|
http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
|
||||||
|
_, _, finished := process.GetManager().AddTypedContext(context.Background(), "Web: PProf Server", process.SystemProcessType, true)
|
||||||
|
log.Info("Starting pprof server on localhost:6060")
|
||||||
|
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
|
||||||
|
finished()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Global init")
|
||||||
|
// Perform global initialization
|
||||||
|
setting.LoadFromExisting()
|
||||||
|
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
|
||||||
|
|
||||||
|
// We check that AppDataPath exists here (it should have been created during installation)
|
||||||
|
// We can't check it in `GlobalInitInstalled`, because some integration tests
|
||||||
|
// use cmd -> GlobalInitInstalled, but the AppDataPath doesn't exist during those tests.
|
||||||
|
if _, err := os.Stat(setting.AppDataPath); err != nil {
|
||||||
|
log.Fatal("Can not find APP_DATA_PATH '%s'", setting.AppDataPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override the provided port number within the configuration
|
||||||
|
if ctx.IsSet("port") {
|
||||||
|
if err := setPort(ctx.String("port")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up Chi routes
|
||||||
|
c := routers.NormalRoutes()
|
||||||
|
hat_routers.InitHatRouters(c)
|
||||||
|
err := listen(c, true)
|
||||||
|
<-graceful.GetManager().Done()
|
||||||
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||||
|
log.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setPort(port string) error {
|
||||||
|
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1)
|
||||||
|
setting.HTTPPort = port
|
||||||
|
|
||||||
|
switch setting.Protocol {
|
||||||
|
case setting.HTTPUnix:
|
||||||
|
case setting.FCGI:
|
||||||
|
case setting.FCGIUnix:
|
||||||
|
default:
|
||||||
|
defaultLocalURL := string(setting.Protocol) + "://"
|
||||||
|
if setting.HTTPAddr == "0.0.0.0" {
|
||||||
|
defaultLocalURL += "localhost"
|
||||||
|
} else {
|
||||||
|
defaultLocalURL += setting.HTTPAddr
|
||||||
|
}
|
||||||
|
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||||
|
|
||||||
|
// Save LOCAL_ROOT_URL if port changed
|
||||||
|
setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
|
||||||
|
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listen(m http.Handler, handleRedirector bool) error {
|
||||||
|
listenAddr := setting.HTTPAddr
|
||||||
|
if setting.Protocol != setting.HTTPUnix && setting.Protocol != setting.FCGIUnix {
|
||||||
|
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
||||||
|
}
|
||||||
|
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: Gitea Server", process.SystemProcessType, true)
|
||||||
|
defer finished()
|
||||||
|
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||||
|
// This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy.
|
||||||
|
// A user may fix the configuration mistake when he sees this log.
|
||||||
|
// And this is also very helpful to maintainers to provide help to users to resolve their configuration problems.
|
||||||
|
log.Info("AppURL(ROOT_URL): %s", setting.AppURL)
|
||||||
|
|
||||||
|
if setting.LFS.StartServer {
|
||||||
|
log.Info("LFS server enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch setting.Protocol {
|
||||||
|
case setting.HTTP:
|
||||||
|
if handleRedirector {
|
||||||
|
NoHTTPRedirector()
|
||||||
|
}
|
||||||
|
err = runHTTP("tcp", listenAddr, "Web", m)
|
||||||
|
case setting.HTTPS:
|
||||||
|
if setting.EnableAcme {
|
||||||
|
err = runACME(listenAddr, m)
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
if handleRedirector {
|
||||||
|
if setting.RedirectOtherPort {
|
||||||
|
go runHTTPRedirector()
|
||||||
|
} else {
|
||||||
|
NoHTTPRedirector()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m)
|
||||||
|
}
|
||||||
|
case setting.FCGI:
|
||||||
|
if handleRedirector {
|
||||||
|
NoHTTPRedirector()
|
||||||
|
}
|
||||||
|
err = runFCGI("tcp", listenAddr, "FCGI Web", m)
|
||||||
|
case setting.HTTPUnix:
|
||||||
|
if handleRedirector {
|
||||||
|
NoHTTPRedirector()
|
||||||
|
}
|
||||||
|
err = runHTTP("unix", listenAddr, "Web", m)
|
||||||
|
case setting.FCGIUnix:
|
||||||
|
if handleRedirector {
|
||||||
|
NoHTTPRedirector()
|
||||||
|
}
|
||||||
|
err = runFCGI("unix", listenAddr, "Web", m)
|
||||||
|
default:
|
||||||
|
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Critical("Failed to start server: %v", err)
|
||||||
|
}
|
||||||
|
log.Info("HTTP Listener: %s Closed", listenAddr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/process"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/caddyserver/certmagic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getCARoot(path string) (*x509.CertPool, error) {
|
||||||
|
r, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
block, _ := pem.Decode(r)
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("no PEM found in the file %s", path)
|
||||||
|
}
|
||||||
|
caRoot, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
certPool.AddCert(caRoot)
|
||||||
|
return certPool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runACME(listenAddr string, m http.Handler) error {
|
||||||
|
// If HTTP Challenge enabled, needs to be serving on port 80. For TLSALPN needs 443.
|
||||||
|
// Due to docker port mapping this can't be checked programmatically
|
||||||
|
// TODO: these are placeholders until we add options for each in settings with appropriate warning
|
||||||
|
enableHTTPChallenge := true
|
||||||
|
enableTLSALPNChallenge := true
|
||||||
|
altHTTPPort := 0
|
||||||
|
altTLSALPNPort := 0
|
||||||
|
|
||||||
|
if p, err := strconv.Atoi(setting.PortToRedirect); err == nil {
|
||||||
|
altHTTPPort = p
|
||||||
|
}
|
||||||
|
if p, err := strconv.Atoi(setting.HTTPPort); err == nil {
|
||||||
|
altTLSALPNPort = p
|
||||||
|
}
|
||||||
|
|
||||||
|
magic := certmagic.NewDefault()
|
||||||
|
magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
|
||||||
|
// Try to use private CA root if provided, otherwise defaults to system's trust
|
||||||
|
var certPool *x509.CertPool
|
||||||
|
if setting.AcmeCARoot != "" {
|
||||||
|
var err error
|
||||||
|
certPool, err = getCARoot(setting.AcmeCARoot)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
|
||||||
|
CA: setting.AcmeURL,
|
||||||
|
TrustedRoots: certPool,
|
||||||
|
Email: setting.AcmeEmail,
|
||||||
|
Agreed: setting.AcmeTOS,
|
||||||
|
DisableHTTPChallenge: !enableHTTPChallenge,
|
||||||
|
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
||||||
|
ListenHost: setting.HTTPAddr,
|
||||||
|
AltTLSALPNPort: altTLSALPNPort,
|
||||||
|
AltHTTPPort: altHTTPPort,
|
||||||
|
})
|
||||||
|
|
||||||
|
magic.Issuers = []certmagic.Issuer{myACME}
|
||||||
|
|
||||||
|
// this obtains certificates or renews them if necessary
|
||||||
|
err := magic.ManageSync(graceful.GetManager().HammerContext(), []string{setting.Domain})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig := magic.TLSConfig()
|
||||||
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||||
|
|
||||||
|
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
|
||||||
|
tlsConfig.MinVersion = version
|
||||||
|
}
|
||||||
|
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
|
||||||
|
tlsConfig.MaxVersion = version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set curve preferences
|
||||||
|
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
|
||||||
|
tlsConfig.CurvePreferences = curves
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set cipher suites
|
||||||
|
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
|
||||||
|
tlsConfig.CipherSuites = ciphers
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableHTTPChallenge {
|
||||||
|
go func() {
|
||||||
|
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: ACME HTTP challenge server", process.SystemProcessType, true)
|
||||||
|
defer finished()
|
||||||
|
|
||||||
|
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||||
|
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||||
|
err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "GET" && r.Method != "HEAD" {
|
||||||
|
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Remove the trailing slash at the end of setting.AppURL, the request
|
||||||
|
// URI always contains a leading slash, which would result in a double
|
||||||
|
// slash
|
||||||
|
target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI()
|
||||||
|
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/fcgi"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runHTTP(network, listenAddr, name string, m http.Handler) error {
|
||||||
|
return graceful.HTTPListenAndServe(network, listenAddr, name, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
||||||
|
func NoHTTPRedirector() {
|
||||||
|
graceful.GetManager().InformCleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
|
||||||
|
// for our main HTTP/HTTPS service
|
||||||
|
func NoMainListener() {
|
||||||
|
graceful.GetManager().InformCleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
|
||||||
|
// for our install HTTP/HTTPS service
|
||||||
|
func NoInstallListener() {
|
||||||
|
graceful.GetManager().InformCleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
||||||
|
// This needs to handle stdin as fcgi point
|
||||||
|
fcgiServer := graceful.NewServer(network, listenAddr, name)
|
||||||
|
|
||||||
|
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
||||||
|
return fcgi.Serve(listener, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
if setting.AppSubURL != "" {
|
||||||
|
req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL)
|
||||||
|
}
|
||||||
|
m.ServeHTTP(resp, req)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to start FCGI main server: %v", err)
|
||||||
|
}
|
||||||
|
log.Info("FCGI Listener: %s Closed", listenAddr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/klauspost/cpuid/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tlsVersionStringMap = map[string]uint16{
|
||||||
|
"": tls.VersionTLS12, // Default to tls.VersionTLS12
|
||||||
|
"tlsv1.0": tls.VersionTLS10,
|
||||||
|
"tlsv1.1": tls.VersionTLS11,
|
||||||
|
"tlsv1.2": tls.VersionTLS12,
|
||||||
|
"tlsv1.3": tls.VersionTLS13,
|
||||||
|
}
|
||||||
|
|
||||||
|
func toTLSVersion(version string) uint16 {
|
||||||
|
tlsVersion, ok := tlsVersionStringMap[strings.TrimSpace(strings.ToLower(version))]
|
||||||
|
if !ok {
|
||||||
|
log.Warn("Unknown tls version: %s", version)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return tlsVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
var curveStringMap = map[string]tls.CurveID{
|
||||||
|
"x25519": tls.X25519,
|
||||||
|
"p256": tls.CurveP256,
|
||||||
|
"p384": tls.CurveP384,
|
||||||
|
"p521": tls.CurveP521,
|
||||||
|
}
|
||||||
|
|
||||||
|
func toCurvePreferences(preferences []string) []tls.CurveID {
|
||||||
|
ids := make([]tls.CurveID, 0, len(preferences))
|
||||||
|
for _, pref := range preferences {
|
||||||
|
id, ok := curveStringMap[strings.TrimSpace(strings.ToLower(pref))]
|
||||||
|
if !ok {
|
||||||
|
log.Warn("Unknown curve: %s", pref)
|
||||||
|
}
|
||||||
|
if id != 0 {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
var cipherStringMap = map[string]uint16{
|
||||||
|
"rsa_with_rc4_128_sha": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||||
|
"rsa_with_3des_ede_cbc_sha": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
"rsa_with_aes_128_cbc_sha": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"rsa_with_aes_256_cbc_sha": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"rsa_with_aes_128_cbc_sha256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
"rsa_with_aes_128_gcm_sha256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
"rsa_with_aes_256_gcm_sha384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
"ecdhe_ecdsa_with_rc4_128_sha": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||||
|
"ecdhe_ecdsa_with_aes_128_cbc_sha": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"ecdhe_ecdsa_with_aes_256_cbc_sha": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"ecdhe_rsa_with_rc4_128_sha": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||||
|
"ecdhe_rsa_with_3des_ede_cbc_sha": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
"ecdhe_rsa_with_aes_128_cbc_sha": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"ecdhe_rsa_with_aes_256_cbc_sha": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"ecdhe_ecdsa_with_aes_128_cbc_sha256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
"ecdhe_rsa_with_aes_128_cbc_sha256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
"ecdhe_rsa_with_aes_128_gcm_sha256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
"ecdhe_ecdsa_with_aes_128_gcm_sha256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
"ecdhe_rsa_with_aes_256_gcm_sha384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
"ecdhe_ecdsa_with_aes_256_gcm_sha384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
"ecdhe_rsa_with_chacha20_poly1305_sha256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
"ecdhe_ecdsa_with_chacha20_poly1305_sha256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
"ecdhe_rsa_with_chacha20_poly1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||||
|
"ecdhe_ecdsa_with_chacha20_poly1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||||
|
"aes_128_gcm_sha256": tls.TLS_AES_128_GCM_SHA256,
|
||||||
|
"aes_256_gcm_sha384": tls.TLS_AES_256_GCM_SHA384,
|
||||||
|
"chacha20_poly1305_sha256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||||
|
}
|
||||||
|
|
||||||
|
func toTLSCiphers(cipherStrings []string) []uint16 {
|
||||||
|
ciphers := make([]uint16, 0, len(cipherStrings))
|
||||||
|
for _, cipherString := range cipherStrings {
|
||||||
|
cipher, ok := cipherStringMap[strings.TrimSpace(strings.ToLower(cipherString))]
|
||||||
|
if !ok {
|
||||||
|
log.Warn("Unknown cipher: %s", cipherString)
|
||||||
|
}
|
||||||
|
if cipher != 0 {
|
||||||
|
ciphers = append(ciphers, cipher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ciphers
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultCiphers uses hardware support to check if AES is specifically
|
||||||
|
// supported by the CPU.
|
||||||
|
//
|
||||||
|
// If AES is supported AES ciphers will be preferred over ChaCha based ciphers
|
||||||
|
// (This code is directly inspired by the certmagic code.)
|
||||||
|
func defaultCiphers() []uint16 {
|
||||||
|
if cpuid.CPU.Supports(cpuid.AESNI) {
|
||||||
|
return defaultCiphersAESfirst
|
||||||
|
}
|
||||||
|
return defaultCiphersChaChaFirst
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultCiphersAES = []uint16{
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultCiphersChaCha = []uint16{
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultCiphersAESfirst = append(defaultCiphersAES, defaultCiphersChaCha...)
|
||||||
|
defaultCiphersChaChaFirst = append(defaultCiphersChaCha, defaultCiphersAES...)
|
||||||
|
)
|
||||||
|
|
||||||
|
// runHTTPs listens on the provided network address and then calls
|
||||||
|
// Serve to handle requests on incoming TLS connections.
|
||||||
|
//
|
||||||
|
// Filenames containing a certificate and matching private key for the server must
|
||||||
|
// be provided. If the certificate is signed by a certificate authority, the
|
||||||
|
// certFile should be the concatenation of the server's certificate followed by the
|
||||||
|
// CA's certificate.
|
||||||
|
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
|
||||||
|
tlsConfig := &tls.Config{}
|
||||||
|
if tlsConfig.NextProtos == nil {
|
||||||
|
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
|
||||||
|
tlsConfig.MinVersion = version
|
||||||
|
}
|
||||||
|
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
|
||||||
|
tlsConfig.MaxVersion = version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set curve preferences
|
||||||
|
tlsConfig.CurvePreferences = []tls.CurveID{
|
||||||
|
tls.X25519,
|
||||||
|
tls.CurveP256,
|
||||||
|
}
|
||||||
|
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
|
||||||
|
tlsConfig.CurvePreferences = curves
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set cipher suites
|
||||||
|
tlsConfig.CipherSuites = defaultCiphers()
|
||||||
|
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
|
||||||
|
tlsConfig.CipherSuites = ciphers
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig.Certificates = make([]tls.Certificate, 1)
|
||||||
|
|
||||||
|
certPEMBlock, err := os.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to load https cert file %s for %s:%s: %v", certFile, network, listenAddr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPEMBlock, err := os.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to load https key file %s for %s:%s: %v", keyFile, network, listenAddr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig.Certificates[0], err = tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to create certificate from cert file %s and key file %s for %s:%s: %v", certFile, keyFile, network, listenAddr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
|
||||||
|
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||||
|
}
|
||||||
4
main.go
4
main.go
|
|
@ -10,6 +10,8 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/cmd"
|
"code.gitea.io/gitea/cmd"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
hat_cmd "code.gitlink.org.cn/Gitlink/gitea_hat.git/cmd"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -40,7 +42,7 @@ func main() {
|
||||||
arguments - which can alternatively be run by running the subcommand web.`
|
arguments - which can alternatively be run by running the subcommand web.`
|
||||||
app.Version = Version + formatBuiltWith()
|
app.Version = Version + formatBuiltWith()
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
cmd.CmdWeb,
|
hat_cmd.CmdWeb,
|
||||||
cmd.CmdServ,
|
cmd.CmdServ,
|
||||||
cmd.CmdHook,
|
cmd.CmdHook,
|
||||||
cmd.CmdDump,
|
cmd.CmdDump,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package hat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
"code.gitea.io/gitea/routers/api/v1/misc"
|
||||||
|
"code.gitea.io/gitea/services/auth"
|
||||||
|
"github.com/go-chi/cors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Routers() *web.Route {
|
||||||
|
m := web.NewRoute()
|
||||||
|
|
||||||
|
m.Use(securityHeaders())
|
||||||
|
if setting.CORSConfig.Enabled {
|
||||||
|
m.Use(cors.Handler(cors.Options{
|
||||||
|
// Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option
|
||||||
|
AllowedOrigins: setting.CORSConfig.AllowDomain,
|
||||||
|
// setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option
|
||||||
|
AllowedMethods: setting.CORSConfig.Methods,
|
||||||
|
AllowCredentials: setting.CORSConfig.AllowCredentials,
|
||||||
|
AllowedHeaders: []string{"Authorization", "X-Gitea-OTP"},
|
||||||
|
MaxAge: int(setting.CORSConfig.MaxAge.Seconds()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
m.Use(context.APIContexter())
|
||||||
|
|
||||||
|
group := buildAuthGroup()
|
||||||
|
if err := group.Init(); err != nil {
|
||||||
|
log.Error("Could not initialize '%s' auth method, error: %s", group.Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user from session if logged in.
|
||||||
|
m.Use(context.APIAuth(group))
|
||||||
|
|
||||||
|
m.Use(context.ToggleAPI(&context.ToggleOptions{
|
||||||
|
SignInRequired: setting.Service.RequireSignInView,
|
||||||
|
}))
|
||||||
|
|
||||||
|
m.Use(context.ToggleAPI(&context.ToggleOptions{
|
||||||
|
SignInRequired: setting.Service.RequireSignInView,
|
||||||
|
}))
|
||||||
|
|
||||||
|
m.Group("", func() {
|
||||||
|
m.Get("/version", misc.Version)
|
||||||
|
})
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func securityHeaders() func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
// CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers
|
||||||
|
// http://stackoverflow.com/a/3146618/244009
|
||||||
|
resp.Header().Set("x-content-type-options", "nosniff")
|
||||||
|
next.ServeHTTP(resp, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAuthGroup() *auth.Group {
|
||||||
|
group := auth.NewGroup(
|
||||||
|
&auth.OAuth2{},
|
||||||
|
&auth.HTTPSign{},
|
||||||
|
&auth.Basic{}, // FIXME: this should be removed once we don't allow basic auth in API
|
||||||
|
)
|
||||||
|
if setting.Service.EnableReverseProxyAuth {
|
||||||
|
group.Add(&auth.ReverseProxy{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TagNameSet(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/tag_name_set repository repoTagNameSet
|
||||||
|
// ---
|
||||||
|
// summary: List a repository's tag name***
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: name
|
||||||
|
// in: query
|
||||||
|
// description: name of the tag
|
||||||
|
// type: string
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/BranchNameSet"
|
||||||
|
|
||||||
|
searchName := ctx.Params("name")
|
||||||
|
|
||||||
|
tags, err := ctx.Repo.GitRepo.GetTags(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetTags", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if searchName == "" {
|
||||||
|
ctx.JSON(http.StatusOK, tags)
|
||||||
|
} else {
|
||||||
|
var tagNameSet []string
|
||||||
|
for _, tag := range tags {
|
||||||
|
|
||||||
|
log.Info("tag is \n", tag)
|
||||||
|
if strings.Contains(tag, searchName) {
|
||||||
|
tagNameSet = append(tagNameSet, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, tagNameSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
api_hat "code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitHatRouters(e *web.Route) *web.Route {
|
||||||
|
|
||||||
|
e.Mount("/api/hat", api_hat.Routers())
|
||||||
|
return e
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue