Iris: [BUG] `app.SubdomainRedirect` does not trigger, or wrong hostname (main -> subdomain)

Created on 14 Aug 2020  ·  7Comments  ·  Source: kataras/iris

Summary

Using the 1st included code, the SubdomainRedirect do not trigger.

Using the 2nd included code, the SubdomainRedirect triggers, but to a bad hostname 0.0.0.0

This may have to do with my NAT set-up, however, this could happen even without it (such as the user not supplying an FQDN in the Addr or Listen function that starts up the server.) -- As such, I recommend either a way to detect or specify the server's hostname (such as how Apache checks reverse IP, and allows you to configure one to override this check.)

Expected Result

Requests on http://example.com should redirect to https://your.example.com/

Actual Result

Using 1st code

  • Request to http://example.com/ hits a "Not Found" page at https://example.com/

Using 2nd code

  • Request to http://example.com/ redirects to https://your.0.0.0.0/

Versions, etc.

|Thing|Version|
|-|-|
|Golang|1.14.6 linux/amd64|
|Iris|master|
|Kernel|5.7.14-200.fc32.x86_64|
|OS|Fedora 32 Workstation|

Example Code 1

func main() {
// ...
    your := app.Subdomain("your")
    app.SubdomainRedirect(app, your) // <-- docs say this would work, but does not

    tlsAddr := fmt.Sprintf("%s:%d", *internalHost, *internalPortSecure)
    domains := fmt.Sprintf("%s www.%s your.%s", *domain, *domain, *domain)
    app.Run(iris.AutoTLS(tlsAddr, domains, *adminEmail, iris.AutoTLSNoRedirect(fallbackServer)))
}

// Without this, the ACME HTTP-01 challenge would fail
func fallbackServer(acme func(http.Handler) http.Handler) *http.Server {
    srv := &http.Server{
        Addr: fmt.Sprintf("%s:%d", *externalHost, *internalPortPlain),
        Handler: acme(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            http.Redirect(w, r, fmt.Sprintf("https://your.%s/", *domain), iris.StatusTemporaryRedirect)
        })),
    }
    go srv.ListenAndServe()
    return srv
}

Example Code 2

// ... same code as Example Code 1
    app.SubdomainRedirect(app, your) // <-- docs say this would work, but does not
    app.SubdomainRedirect(app.WildcardSubdomain(), your) // <-- this actually makes it trigger
// ... same code as Example Code 1
resolved bug

All 7 comments

Yes, that was broken 2days ago by https://github.com/kataras/iris/commit/0761bc35eeeb9d8a8be711a4597aa0fab129272b#diff-15cce7299aae8810bcab9b0bf9a2fdb1R774. Let me find a solution because we need both of them I think.

OK @AlbinoGeek this should be fixed, can you try it?

    your := app.Subdomain("your")
    app.SubdomainRedirect(app, your)

    pages := your.Party("/")

Results in a "Not Found" being served at the root domain


    your := app.Subdomain("your")
    app.SubdomainRedirect(app, your)
    app.SubdomainRedirect(app.WildcardSubdomain(), your)

    pages := your.Party("/")

Results in the https://your.0.0.0.0/ behaviour described above. (no change.)

I have confirmed this time that I have updated to commit:

$ grep v12 go.mod
        github.com/kataras/iris/v12 v12.1.9-0.20200814100448-dc35391ceb02

@AlbinoGeek, the SubdomainRedirect uses the Configuration.vhost which is resolved through the app.Run/Listen Listener's address, so if you try with a domain, it will work:

package main

import "github.com/kataras/iris/v12"

func main() {
    app := iris.New()

    app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
        ctx.WriteString("not found from root")
    })
    app.Get("/", func(ctx iris.Context) {
        ctx.WriteString("this should never happen")
    })

    test := app.Subdomain("test")
    app.SubdomainRedirect(app, test) // <--
    test.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
        ctx.WriteString("not found from test.")
    })
    test.Get("/", func(ctx iris.Context) {
        ctx.WriteString("index of test.")
    })

    // app.Listen("0.0.0.0:80")
    app.Listen("mydomain.com:80")
}

However, you try with 0.0.0.0:80 and that's why is failing, source: https://github.com/kataras/iris/blob/dc35391ceb026fce0db856735ad4402c8c8749d0/core/router/router_subdomain_redirect_wrapper.go#L125-L152 (and its below which matches the expected subdomain with dots)

Let me find a way to fix that too.

As with the whole "internal IP != external IP" situation (consistent with Docker, Proxies or NAT) -- I cannot set the hostname in the Listen directive, since I am using AutoTLS as discussed in another ticket.

Thank you so much for looking into these strange issues I bring up.

Basically, thank you for putting up with me, haha.

@AlbinoGeek no worries, I actually enjoy it! With your help servers behind proxies and NAT will take advandage of all Iris subdomains features. Test it now, it should be fixed with the last commit.

Alright so, we're very close now:

Scenario 1

your := app.Subdomain("your")
app.SubdomainRedirect(app, your)

Works, except it does it in 2 redirects perhaps this could be reduced? If we're on https already, why are we sent to http only to be sent back to https? Perhaps the schema should be considered when constructing the new Location header.

|Entry URL|First Redirect|
|-|-|
|https://example.com/|http://your.example.com/

Scenario 2

your := app.Subdomain("your")
app.SubdomainRedirect(app.WildcardSubdomain(), your)

Same thing about the double redirect, and it doesn't catch the root domain (also expected, the root domain != a subdomain, so I would not expect it to be caught by a wildcard.)

|Entry URL|First Redirect|
|-|-|
|https://www.example.com/|http://your.example.com/

Was this page helpful?
0 / 5 - 0 ratings