Domain redirects: send an address to another, over HTTP and HTTPS
A website resource can now redirect to a full address (301 or 302), optionally keeping the path and query, with no target or login. The Add resource wizard has a Domain redirect choice that opens the HTTPS and HTTP sides together. Deleting a site no longer removes redirects. Uses Reef v0.1.10 and Blacktip v0.1.9. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
227c890409
commit
39659f0144
6 files changed
+211
-12
No files matched your search
+45
-2
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -97,7 +99,16 @@ func (s *Server) validateMapping(c *orgCtx, m *model.Mapping) error {
|
||||
if edge == nil || !s.edgeAllowedFor(edge, c.Org.ID) {
|
||||
return errors.New("choose an edge")
|
||||
}
|
||||
if len(m.Targets) == 0 {
|
||||
redirect := strings.TrimSpace(m.RedirectTo) != ""
|
||||
if redirect {
|
||||
if !model.Web(m.Proto) {
|
||||
return errors.New("only websites (http or https) can redirect")
|
||||
}
|
||||
m.Targets = []model.Target{} // a redirect has nothing behind it
|
||||
} else {
|
||||
m.RedirectTo, m.RedirectPermanent, m.RedirectKeepPath = "", false, false
|
||||
}
|
||||
if len(m.Targets) == 0 && !redirect {
|
||||
return errors.New("add at least one target")
|
||||
}
|
||||
if m.Proto == model.ProtoUDP && len(m.Targets) > 1 {
|
||||
@@ -173,9 +184,19 @@ func (s *Server) validateMapping(c *orgCtx, m *model.Mapping) error {
|
||||
return errors.New("PROXY protocol must be v1, v2 or off")
|
||||
}
|
||||
// web-only options
|
||||
if !model.Web(m.Proto) {
|
||||
if redirect {
|
||||
to, err := cleanRedirect(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.RedirectTo = to
|
||||
}
|
||||
if !model.Web(m.Proto) || redirect {
|
||||
m.Auth, m.Rules, m.RewriteHost = model.Auth{}, nil, ""
|
||||
m.RedirectHTTP, m.BackendHTTPS, m.HealthPath = false, false, ""
|
||||
if redirect {
|
||||
m.HealthSeconds = -1 // nothing to check
|
||||
}
|
||||
} else {
|
||||
a := &m.Auth
|
||||
a.AllowedRoles, a.AllowedUsers, a.AllowedMails = listFromCSV(a.AllowedRoles), listFromCSV(a.AllowedUsers), listFromCSV(a.AllowedMails)
|
||||
@@ -262,6 +283,28 @@ func (s *Server) validateMapping(c *orgCtx, m *model.Mapping) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanRedirect checks where a redirect sends visitors and refuses a destination that would
|
||||
// send them straight back to the same address.
|
||||
func cleanRedirect(m *model.Mapping) (string, error) {
|
||||
to := strings.TrimSpace(m.RedirectTo)
|
||||
u, err := url.Parse(to)
|
||||
if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Hostname() == "" || u.User != nil {
|
||||
return "", errors.New("Redirect to a full address such as https://www.example.com.")
|
||||
}
|
||||
if len(to) > 2048 || strings.ContainsAny(to, " \t\r\n$") {
|
||||
return "", errors.New("The redirect address is too long or contains spaces or a dollar sign.")
|
||||
}
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
port = map[string]string{"http": "80", "https": "443"}[u.Scheme]
|
||||
}
|
||||
same := u.Scheme == m.Proto && strings.EqualFold(u.Hostname(), m.Hostname) && port == strconv.Itoa(m.Port)
|
||||
if same && (m.RedirectKeepPath || u.Path == "" || u.Path == "/") {
|
||||
return "", errors.New("That redirect would send visitors back to the same address.")
|
||||
}
|
||||
return to, nil
|
||||
}
|
||||
|
||||
// portLabel is "25565" or, for a range, "2456-2458".
|
||||
func portLabel(m model.Mapping) string {
|
||||
if m.LastPort() > m.Port {
|
||||
|
||||
Reference in new issue
Block a user