The router now redirects requests to localhost domains with localhost equivalents in the host header which don't quite match the destination to the proper domain.

The router now rejects host headers with the wrong port for non-standard ports.
The www. redirect now handles non-standard ports properly.
The Site.Port configuration setting is now validated on start-up to ensure it's a valid integer.

Quickly fixed up the grammar of the Port block in configuration.md
This commit is contained in:
Azareal 2019-04-01 15:44:38 +10:00
parent c8a8de95ae
commit 9d321e9f23
5 changed files with 107 additions and 21 deletions

View File

@ -795,6 +795,7 @@ func parseMediaString(data string) (media MediaEmbed, ok bool) {
port := url.Port()
query := url.Query()
// TODO: Treat 127.0.0.1 and [::1] as localhost too
var samesite = hostname == "localhost" || hostname == Site.URL
if samesite {
hostname = strings.Split(Site.URL, ":")[0]

View File

@ -27,7 +27,9 @@ type site struct {
Email string
URL string
Host string
LocalHost bool // Used internally, do not modify as it will be overwritten
Port string
PortInt int // Alias for efficiency, do not modify, will be overwritten
EnableSsl bool
EnableEmails bool
HasProxy bool
@ -81,7 +83,9 @@ type config struct {
DefaultForum int // The forum posts go in by default, this used to be covered by the Uncategorised Forum, but we want to replace it with a more robust solution. Make this a setting?
MinifyTemplates bool
BuildSlugs bool // TODO: Make this a setting?
ServerCount int
PrimaryServer bool
ServerCount int
DisableLiveTopicList bool
DisableJSAntispam bool
@ -140,7 +144,12 @@ func ProcessConfig() (err error) {
Config.Noavatar = strings.Replace(Config.Noavatar, "{site_url}", Site.URL, -1)
guestAvatar = GuestAvatar{buildNoavatar(0, 200), buildNoavatar(0, 48)}
Site.Host = Site.URL
if Site.Port != "80" && Site.Port != "443" {
Site.LocalHost = Site.Host == "localhost" || Site.Host == "127.0.0.1" || Site.Host == "::1"
Site.PortInt, err = strconv.Atoi(Site.Port)
if err != nil {
return errors.New("The port must be a valid integer")
}
if Site.PortInt != 80 && Site.PortInt != 443 {
Site.URL = strings.TrimSuffix(Site.URL, "/")
Site.URL = strings.TrimSuffix(Site.URL, "\\")
Site.URL = strings.TrimSuffix(Site.URL, ":")

View File

@ -18,7 +18,7 @@ Email - The email address you want to show up in the From: field when Gosora sen
URL - The URL for your site. Please leave out the `http://` or `https://` and the `/` at the end.
Port - The port you want Gosora to listen on. This will usually be 443 for HTTPS and 80 for HTTP. Gosora usually try to bind to both, if you're on HTTPS to redirect users from the HTTP site to the HTTPS one.
Port - The port you want Gosora to listen on. This will usually be 443 for HTTPS and 80 for HTTP. Gosora will try to bind to both, if you're on HTTPS to redirect users from the HTTP site to the HTTPS one.
EnableSsl - Determines whether HTTPS is enabled.

View File

@ -696,19 +696,61 @@ func (r *GenRouter) SuspiciousRequest(req *http.Request, prepend string) {
counters.AgentViewCounter.Bump(28)
}
func isLocalHost(host string) bool {
return host=="localhost" || host=="127.0.0.1" || host=="::1"
}
// TODO: Pass the default path or config struct to the router rather than accessing it via a package global
// TODO: SetDefaultPath
// TODO: GetDefaultPath
func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Redirect www. requests to the right place
if req.Host == "www." + common.Site.Host {
var malformedRequest = func() {
w.WriteHeader(200) // 400
w.Write([]byte(""))
r.DumpRequest(req,"Malformed Request")
counters.AgentViewCounter.Bump(27)
}
// Split the Host and Port string
var shost, sport string
if req.Host[0]=='[' {
spl := strings.Split(req.Host,"]")
if len(spl) > 2 {
malformedRequest()
return
}
shost = strings.TrimPrefix(spl[0],"[")
sport = strings.TrimPrefix(spl[1],":")
} else {
spl := strings.Split(req.Host,":")
if len(spl) > 2 {
malformedRequest()
return
}
shost = spl[0]
if len(shost)==2 {
sport = spl[1]
}
}
// TODO: Reject requests from non-local IPs, if the site host is set to localhost or a localhost IP
if common.Site.PortInt != 80 && common.Site.PortInt != 443 && sport != common.Site.Port {
malformedRequest()
return
}
// Redirect www. and local IP requests to the right place
if shost == "www." + common.Site.Host || (common.Site.LocalHost && shost != common.Site.Host && isLocalHost(shost)) {
// TODO: Abstract the redirect logic?
w.Header().Set("Connection", "close")
var s string
if common.Site.EnableSsl {
s = "s"
}
dest := "http"+s+"://" + common.Site.Host + req.URL.Path
var p string
if common.Site.PortInt != 80 && common.Site.PortInt != 443 {
p = ":"+common.Site.Port
}
dest := "http"+s+"://" + common.Site.Host+p + req.URL.Path
if len(req.URL.RawQuery) > 0 {
dest += "?" + req.URL.RawQuery
}
@ -717,12 +759,8 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
// Deflect malformed requests
shost := strings.Split(req.Host,":")
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || shost[0] != common.Site.Host || len(shost) > 2 {
w.WriteHeader(200) // 400
w.Write([]byte(""))
r.DumpRequest(req,"Malformed Request")
counters.AgentViewCounter.Bump(27)
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || shost != common.Site.Host {
malformedRequest()
return
}
if common.Dev.FullReqLog {

View File

@ -475,19 +475,61 @@ func (r *GenRouter) SuspiciousRequest(req *http.Request, prepend string) {
counters.AgentViewCounter.Bump({{.AllAgentMap.suspicious}})
}
func isLocalHost(host string) bool {
return host=="localhost" || host=="127.0.0.1" || host=="::1"
}
// TODO: Pass the default path or config struct to the router rather than accessing it via a package global
// TODO: SetDefaultPath
// TODO: GetDefaultPath
func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Redirect www. requests to the right place
if req.Host == "www." + common.Site.Host {
var malformedRequest = func() {
w.WriteHeader(200) // 400
w.Write([]byte(""))
r.DumpRequest(req,"Malformed Request")
counters.AgentViewCounter.Bump({{.AllAgentMap.malformed}})
}
// Split the Host and Port string
var shost, sport string
if req.Host[0]=='[' {
spl := strings.Split(req.Host,"]")
if len(spl) > 2 {
malformedRequest()
return
}
shost = strings.TrimPrefix(spl[0],"[")
sport = strings.TrimPrefix(spl[1],":")
} else {
spl := strings.Split(req.Host,":")
if len(spl) > 2 {
malformedRequest()
return
}
shost = spl[0]
if len(shost)==2 {
sport = spl[1]
}
}
// TODO: Reject requests from non-local IPs, if the site host is set to localhost or a localhost IP
if common.Site.PortInt != 80 && common.Site.PortInt != 443 && sport != common.Site.Port {
malformedRequest()
return
}
// Redirect www. and local IP requests to the right place
if shost == "www." + common.Site.Host || (common.Site.LocalHost && shost != common.Site.Host && isLocalHost(shost)) {
// TODO: Abstract the redirect logic?
w.Header().Set("Connection", "close")
var s string
if common.Site.EnableSsl {
s = "s"
}
dest := "http"+s+"://" + common.Site.Host + req.URL.Path
var p string
if common.Site.PortInt != 80 && common.Site.PortInt != 443 {
p = ":"+common.Site.Port
}
dest := "http"+s+"://" + common.Site.Host+p + req.URL.Path
if len(req.URL.RawQuery) > 0 {
dest += "?" + req.URL.RawQuery
}
@ -496,12 +538,8 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
// Deflect malformed requests
shost := strings.Split(req.Host,":")
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || shost[0] != common.Site.Host || len(shost) > 2 {
w.WriteHeader(200) // 400
w.Write([]byte(""))
r.DumpRequest(req,"Malformed Request")
counters.AgentViewCounter.Bump({{.AllAgentMap.malformed}})
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || shost != common.Site.Host {
malformedRequest()
return
}
if common.Dev.FullReqLog {