Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] AutoTLS hosts on wrong addresses #1578

Closed
AlbinoGeek opened this issue Aug 11, 2020 · 12 comments
Closed

[BUG] AutoTLS hosts on wrong addresses #1578

AlbinoGeek opened this issue Aug 11, 2020 · 12 comments
Assignees
Labels
good first issue A user wrote a good first issue with clear instructions 🤘 status:resolved
Milestone

Comments

@AlbinoGeek
Copy link

Describe the bug
Using the example AutoTLS code results in http://localhost:443 output
Using AutoTLS code for 0.0.0.0:443 results in http://0.0.0.0 output

To Reproduce
Steps to reproduce the behavior:

  1. Use the example AutoTLS code
  2. Modify the code to listen on 0.0.0.0:443

Expected behavior
AutoTLS hosting on https://0.0.0.0 in both cases

Screenshots

  1. image
  2. image

Desktop (please complete the following information):

  • OS: Fedora 32

iris.Version

  • master
@AlbinoGeek
Copy link
Author

AlbinoGeek commented Aug 11, 2020

I can't get this working whatsoever. I get the following error trying to access the server locally:

[HTTP Server] http: TLS handshake error from 127.0.0.1:54234: acme/autocert: missing server name
[HTTP Server] http: TLS handshake error from [::1]:54704: acme/autocert: server name component count invalid

And accessing the server remotely gives me Address Unreachable v.s. using standard app.Listen

@kataras
Copy link
Owner

kataras commented Aug 11, 2020

Hello @AlbinoGeek please check my reply on your previous issue as I am reading this new one

@kataras
Copy link
Owner

kataras commented Aug 11, 2020

I think that it a log "issue", indeed the server is running on http:...:443 when accessing the https://...:80, the errors you got in your console maybe result of localhost testing instead of a domain or no? AutoTLS works on a linux (ubuntu) and windows systems here, can you give me more information please?

@AlbinoGeek
Copy link
Author

AlbinoGeek commented Aug 11, 2020

This may require #1577 to be solved first, as this is using AutoTLS which I cannot get working in any configuration at the moment. I even tried (For the sake of this ticket) changing the NAT to allow 80->80 and 443->443 specifically, then running iris as root 😨

As per more information:

$ uname -a
Linux doom.broughton.lan 5.7.11-200.fc32.x86_64 #1 SMP Wed Jul 29 17:15:52 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

$ go version
go version go1.14.6 linux/amd64

$ cat go.sum | grep iris | cut -d' ' -f1,2
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod
github.com/iris-contrib/httpexpect/v2 v2.0.5
github.com/iris-contrib/httpexpect/v2 v2.0.5/go.mod
github.com/iris-contrib/jade v1.1.4
github.com/iris-contrib/jade v1.1.4/go.mod
github.com/iris-contrib/pongo2 v0.0.1
github.com/iris-contrib/pongo2 v0.0.1/go.mod
github.com/iris-contrib/schema v0.0.2
github.com/iris-contrib/schema v0.0.2/go.mod
github.com/kataras/iris/v12 v12.1.9-0.20200809192844-da029d6f3722
github.com/kataras/iris/v12 v12.1.9-0.20200809192844-da029d6f3722/go.mod

It is fairly common in my experience for an HTTP server behind this sort of proxy/NAT to still function (just without the SSL portion) on testing domains such as localhost. It is worth noting that I have put real domains in the AutoTLS code as follows (redacted however):

app.Run(iris.AutoTLS(":443", "my.domain", "[email protected]"))

@kataras
Copy link
Owner

kataras commented Aug 12, 2020

This binds to :80 because of the automatic HTTP -> HTTPS server, the iris.TLSNoRedirect is missing from the iris.AutoTLS function. For users reading this issue, please follow this.

@AlbinoGeek I think you can close this issue now?

@AlbinoGeek
Copy link
Author

I can close #1577 , but not this issue. This issue remains as a bug in the logging that shows http://localhost:443 even when its actually listening on 0.0.0.0:443 .

@AlbinoGeek
Copy link
Author

Alright, after speaking with @kataras a bit, here's what we've come to:

  • The logging "bug" is somewhat intentional, and is not an issue at all.

For case 1) The server is correctly listening on 0.0.0.0:443 even though it displays localhost:443 in the text, this is to give the user a click-able link in their console, as opposed to the former which is not a real IP address. Totally understandable.

For case 2) The same logic as case 1 should apply, but is not currently.


In regards to no viable challenge type found found in #1577 , this is being fixed now.

@kataras
Copy link
Owner

kataras commented Aug 12, 2020

Now when using AutoTLS with known domain, something like Now listening on: https://example.com will be displayed instead:

Thanks @AlbinoGeek, your issue and contribution was one of the best we've ever had!

@kataras kataras added this to the v12.2.0 milestone Aug 12, 2020
@kataras kataras added the good first issue A user wrote a good first issue with clear instructions label Aug 12, 2020
@AlbinoGeek
Copy link
Author

This aaaaaaaaaaalmost works, just one more change (messaged you on git)

} else if !su.manuallyTLS && srv.Addr != ":80" && srv.Addr != ":http" {
return fmt.Errorf("autotls: The HTTP-01 challenge relies on http://%s:80/.well-known/acme-challenge/", netutil.ResolveVHost(su.Server.Addr))
}

This is the only remaining blocker on my NAT situation.

@kataras
Copy link
Owner

kataras commented Aug 13, 2020

@AlbinoGeek These lines are commented out (and the host:port part fixed), give it a shot

@AlbinoGeek
Copy link
Author

AlbinoGeek commented Aug 13, 2020

Commits up to and including ff5e43f have fixed this!

However, due to the way golang/crypto works, external port 80 cannot be changed, this is not iris fault.
Every other port can be changed successfully now, to work behind a NAT or proxy for example.


For anyone who finds this in the future (I'm going to PR an example), the following code works:

Solution

This allows a system where NAT or Port Forwarding creates the following rules to host iris.
As a very positive side-effect, AutoTLS effectively doesn't require root when running as configured:

Firewall Rules

Debian 9 or older,
Fedora 30 or older,
RedHat/CentOS 7 or older,
Ubuntu 16 or older,

# Forward internal 8443 to external 443
sudo iptables -t nat -A PREROUTING -p tcp --dport 8443 -j REDIRECT --to-ports 443
sudo iptables -t nat -A     OUTPUT -p tcp --dport 8443 -j REDIRECT --to-ports 443
# Forward internal 8080 to external 80
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-ports 80
sudo iptables -t nat -A     OUTPUT -p tcp --dport 8080 -j REDIRECT --to-ports 80
## You may also need to run this, if it's not working:
## echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
## and once it's all working, you need to save the firewall rules, or it's lost on reboot:
## iptables-save

Debian 10 or newer,
Fedora 32 or newer,
RedHat/CentOS 8 or newer,
Ubuntu 18 or newer

# Enable Port Forwarding
sudo firewall-cmd --zone=external --add-masquerade
# Forward internal 8443 to external 443
sudo firewall-cmd --zone=external --add-forward-port=port=443:proto=tcp:toport=8443
# Forward internal 8080 to external 80
sudo firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toport=8080
## For added security, add ":toaddr=127.0.0.1" to the end of each of the above lines:
## sudo firewall-cmd --zone=external --add-forward-port=port=443:proto=tcp:toport=8443:toaddr=127.0.0.1
## sudo firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=127.0.0.1
## Then, change `internalHost` to "127.0.0.1" below
## And again, once it's working, save the rules so they're not lost on reboot:
## sudo firewall-cmd --runtime-to-permanent

Working Code

package main

import (
    "fmt"
    "net/http"
    "time"
    
    "github.com/kataras/iris/v12"
    "github.com/spf13/pflag"
)

var (
    adminEmail         = pflag.String("admin-email", "[email protected]", "Administrator email sent to LetsEncrypt")
    domain             = pflag.String("domain", "example.com", "Canonical domain for URL generation")
    externalHost       = pflag.String("external-host", "0.0.0.0", "HTTP(S) Hostname used externally")
    internalHost       = pflag.String("internal-host", "0.0.0.0", "HTTP Hostname used internally")
    internalPortPlain  = pflag.Int("internal-port", 8080, "HTTP Port used internally")
    internalPortSecure = pflag.Int("internal-port-secure", 8443, "HTTPS Port used internally")
)

func main() {
    pflag.Parse()
    app = iris.New()

    app.Get("/", func(ctx iris.Context) {
        ctx.JSON(map[string]interface{}{
            "time":      time.Now().Unix(),
        })
    })

    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://%s/", *domain), iris.StatusTemporaryRedirect)
        })),
    }
    go srv.ListenAndServe()
    return srv
}

Test it's working

# Test Internal plaintext ip/port
$ curl http://localhost:8080
<a href="https://example.com/">Temporary Redirect</a>.
# Test external plaintext ip/port
$ curl http://example.com
<a href="https://example.com/">Temporary Redirect</a>.
# Test external secure ip/port
$ curl https://example.com
{
  "time": 1597304304
}

@kataras
Copy link
Owner

kataras commented Aug 13, 2020

Good job @AlbinoGeek! If we can dockerize this example and put it on https://github.com/kataras/iris/tree/master/_examples/http-server as nat-letsencrypt will be great, waiting for your PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue A user wrote a good first issue with clear instructions 🤘 status:resolved
Projects
None yet
Development

No branches or pull requests

2 participants