123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- // Copyright 2020 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- 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)), setting.RedirectorUseProxyProtocol)
- 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, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
- }
-
- 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)
- }
|