kopia lustrzana https://codeberg.org/Codeberg/pages-server
				
				
				
			fix http -> https redirect and add integration tests for it (#184)
and more logging Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/184pull/125/head^2
							rodzic
							
								
									42b3f8d1b7
								
							
						
					
					
						commit
						0adac9a5b1
					
				
							
								
								
									
										2
									
								
								Justfile
								
								
								
								
							
							
						
						
									
										2
									
								
								Justfile
								
								
								
								
							|  | @ -9,6 +9,8 @@ dev: | |||
|     export PAGES_DOMAIN=localhost.mock.directory | ||||
|     export RAW_DOMAIN=raw.localhost.mock.directory | ||||
|     export PORT=4430 | ||||
|     export HTTP_PORT=8880 | ||||
|     export ENABLE_HTTP_SERVER=true | ||||
|     export LOG_LEVEL=trace | ||||
|     go run -tags '{{TAGS}}' . | ||||
| 
 | ||||
|  |  | |||
|  | @ -48,7 +48,8 @@ func Serve(ctx *cli.Context) error { | |||
| 	mainDomainSuffix := ctx.String("pages-domain") | ||||
| 	rawInfoPage := ctx.String("raw-info-page") | ||||
| 	listeningHost := ctx.String("host") | ||||
| 	listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("port")) | ||||
| 	listeningSSLPort := ctx.Uint("port") | ||||
| 	listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, listeningSSLPort) | ||||
| 	listeningHTTPAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("http-port")) | ||||
| 	enableHTTPServer := ctx.Bool("enable-http-server") | ||||
| 
 | ||||
|  | @ -93,7 +94,7 @@ func Serve(ctx *cli.Context) error { | |||
| 	} | ||||
| 
 | ||||
| 	// Create listener for SSL connections
 | ||||
| 	log.Info().Msgf("Listening on https://%s", listeningSSLAddress) | ||||
| 	log.Info().Msgf("Create TCP listener for SSL on %s", listeningSSLAddress) | ||||
| 	listener, err := net.Listen("tcp", listeningSSLAddress) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("couldn't create listener: %v", err) | ||||
|  | @ -113,7 +114,7 @@ func Serve(ctx *cli.Context) error { | |||
| 
 | ||||
| 	if enableHTTPServer { | ||||
| 		// Create handler for http->https redirect and http acme challenges
 | ||||
| 		httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache) | ||||
| 		httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache, listeningSSLPort) | ||||
| 
 | ||||
| 		// Create listener for http and start listening
 | ||||
| 		go func() { | ||||
|  | @ -133,7 +134,7 @@ func Serve(ctx *cli.Context) error { | |||
| 		dnsLookupCache, canonicalDomainCache) | ||||
| 
 | ||||
| 	// Start the ssl listener
 | ||||
| 	log.Info().Msgf("Start listening on %s", listener.Addr()) | ||||
| 	log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr()) | ||||
| 	if err := http.Serve(listener, sslHandler); err != nil { | ||||
| 		log.Panic().Err(err).Msg("Couldn't start fastServer") | ||||
| 	} | ||||
|  |  | |||
|  | @ -193,6 +193,18 @@ func TestGetOptions(t *testing.T) { | |||
| 	assert.EqualValues(t, "GET, HEAD, OPTIONS", resp.Header.Get("Allow")) | ||||
| } | ||||
| 
 | ||||
| func TestHttpRedirect(t *testing.T) { | ||||
| 	log.Println("=== TestHttpRedirect ===") | ||||
| 	resp, err := getTestHTTPSClient().Get("http://mock-pages.codeberg-test.org:8880/README.md") | ||||
| 	assert.NoError(t, err) | ||||
| 	if !assert.NotNil(t, resp) { | ||||
| 		t.FailNow() | ||||
| 	} | ||||
| 	assert.EqualValues(t, http.StatusMovedPermanently, resp.StatusCode) | ||||
| 	assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) | ||||
| 	assert.EqualValues(t, "https://mock-pages.codeberg-test.org:4430/README.md", resp.Header.Get("Location")) | ||||
| } | ||||
| 
 | ||||
| func getTestHTTPSClient() *http.Client { | ||||
| 	cookieJar, _ := cookiejar.New(nil) | ||||
| 	return &http.Client{ | ||||
|  |  | |||
|  | @ -40,6 +40,8 @@ func startServer(ctx context.Context) error { | |||
| 	setEnvIfNotSet("PAGES_DOMAIN", "localhost.mock.directory") | ||||
| 	setEnvIfNotSet("RAW_DOMAIN", "raw.localhost.mock.directory") | ||||
| 	setEnvIfNotSet("PORT", "4430") | ||||
| 	setEnvIfNotSet("HTTP_PORT", "8880") | ||||
| 	setEnvIfNotSet("ENABLE_HTTP_SERVER", "true") | ||||
| 	setEnvIfNotSet("DB_TYPE", "sqlite3") | ||||
| 
 | ||||
| 	app := cli.NewApp() | ||||
|  |  | |||
|  | @ -1,15 +1,17 @@ | |||
| package certificates | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/go-acme/lego/v4/challenge" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 
 | ||||
| 	"codeberg.org/codeberg/pages/server/cache" | ||||
| 	"codeberg.org/codeberg/pages/server/context" | ||||
| 	"codeberg.org/codeberg/pages/server/utils" | ||||
| ) | ||||
| 
 | ||||
| type AcmeTLSChallengeProvider struct { | ||||
|  | @ -44,17 +46,38 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) http.HandlerFunc { | ||||
| func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey, sslPort uint) http.HandlerFunc { | ||||
| 	// handle custom-ssl-ports to be added on https redirects
 | ||||
| 	portPart := "" | ||||
| 	if sslPort != 443 { | ||||
| 		portPart = fmt.Sprintf(":%d", sslPort) | ||||
| 	} | ||||
| 
 | ||||
| 	return func(w http.ResponseWriter, req *http.Request) { | ||||
| 		ctx := context.New(w, req) | ||||
| 		domain := ctx.TrimHostPort() | ||||
| 
 | ||||
| 		// it's an acme request
 | ||||
| 		if strings.HasPrefix(ctx.Path(), challengePath) { | ||||
| 			challenge, ok := challengeCache.Get(utils.TrimHostPort(ctx.Host()) + "/" + strings.TrimPrefix(ctx.Path(), challengePath)) | ||||
| 			challenge, ok := challengeCache.Get(domain + "/" + strings.TrimPrefix(ctx.Path(), challengePath)) | ||||
| 			if !ok || challenge == nil { | ||||
| 				log.Info().Msgf("HTTP-ACME challenge for '%s' failed: token not found", domain) | ||||
| 				ctx.String("no challenge for this token", http.StatusNotFound) | ||||
| 			} | ||||
| 			log.Info().Msgf("HTTP-ACME challenge for '%s' succeeded", domain) | ||||
| 			ctx.String(challenge.(string)) | ||||
| 		} else { | ||||
| 			ctx.Redirect("https://"+ctx.Host()+ctx.Path(), http.StatusMovedPermanently) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// it's a normal http request that needs to be redirected
 | ||||
| 		u, err := url.Parse(fmt.Sprintf("https://%s%s%s", domain, portPart, ctx.Path())) | ||||
| 		if err != nil { | ||||
| 			log.Error().Err(err).Msg("could not craft http to https redirect") | ||||
| 			ctx.String("", http.StatusInternalServerError) | ||||
| 		} | ||||
| 
 | ||||
| 		newURL := u.String() | ||||
| 		log.Debug().Msgf("redirect http to https: %s", newURL) | ||||
| 		ctx.Redirect(newURL, http.StatusMovedPermanently) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -47,6 +47,7 @@ func TLSConfig(mainDomainSuffix string, | |||
| 					if proto != tlsalpn01.ACMETLS1Protocol { | ||||
| 						continue | ||||
| 					} | ||||
| 					log.Info().Msgf("Detect ACME-TLS1 challenge for '%s'", domain) | ||||
| 
 | ||||
| 					challenge, ok := challengeCache.Get(domain) | ||||
| 					if !ok { | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 6543
						6543