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

caddy bind directive is broken #4508

Closed
phrazer opened this issue Jan 6, 2022 · 12 comments
Closed

caddy bind directive is broken #4508

phrazer opened this issue Jan 6, 2022 · 12 comments
Labels
discussion 💬 The right solution needs to be found documentation 📚 Improvements or additions to documentation

Comments

@phrazer
Copy link

phrazer commented Jan 6, 2022

Hey,

First of all thanks for great software, really the best. We replaced all nginx-s with caddy and is working great.

Please take this as suggestion, not attack. I really took lot of thinking about this problem.

I think the bind directive is broken. Few reasons:

  1. bind directive should be possible to setup in global directive. Most use cases want to use it globaly in configs (even most services in linux/unix work this way). Right now It is not possible, it says: run: adapting config using caddyfile: /etc/caddy/caddy.conf:39: unrecognized global option: bind

  2. at domain levels, bind is also making problems, and not get honored. Example: ```
    localhost:8080 {
    bind 0.0.0.0
    respond "Goodbye, world!"
    }

this should get you 8080 port on only IPv4, but when you run netstat it is still on IPv6: ```
[root@devbox ~]# netstat -tpl -n
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:65535           0.0.0.0:*               LISTEN      20934/sshd: /usr/sb
tcp6       0      0 :::2019                 :::*                    LISTEN      116928/caddy
tcp6       0      0 :::80                   :::*                    LISTEN      116928/caddy
tcp6       0      0 :::8080                 :::*                    LISTEN      116928/caddy
tcp6       0      0 :::443                  :::*                    LISTEN      116928/caddy
[root@devbox ~]#
  1. But then we get another problem, thats why I think bind is broken. If you write bind in two domains, it shows you this error: run: loading initial config: loading new config: http app module: start: tcp: listening on 0.0.0.0:80: listen tcp 0.0.0.0:80: bind: address already in use

Config used for this error: `localhost:8080 {
bind 0.0.0.0
respond "Goodbye, world!"
}

http://test.zoo {
bind 0.0.0.0
respond "Goodbye, world!"
}`

  1. Even admin 0.0.0.0:2019 directive in global config doesnt honor IPv4 and goes again to tcp6

  2. Second option is also annoying if you have lot of domains, you would have to repeat bind to all of them. This should be optional, and first checked global directive.

If you want I can help to debug this further, or give you more info/help.

@phrazer
Copy link
Author

phrazer commented Jan 6, 2022

I have some problems with code markdown at 3. point. Sorry.

@phrazer
Copy link
Author

phrazer commented Jan 6, 2022

Version used is latest: v2.4.6+dns module
OS is Latest Debian 11.2: Linux devbox 5.10.0-9-amd64 #1 SMP Debian 5.10.70-1 (2021-09-30) x86_64 GNU/Linux

@phrazer
Copy link
Author

phrazer commented Jan 6, 2022

If you give bind 10.0.1.31 (local ip of the box), then admin directive and 8080 works:

[root@devbox ~]# netstat -tpl -n
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 10.0.1.31:2020          0.0.0.0:*               LISTEN      117203/caddy
tcp        0      0 10.0.1.31:8080          0.0.0.0:*               LISTEN      117203/caddy
tcp        0      0 0.0.0.0:65535           0.0.0.0:*               LISTEN      20934/sshd: /usr/sb
tcp6       0      0 :::80                   :::*                    LISTEN      117203/caddy
tcp6       0      0 :::443                  :::*                    LISTEN      117203/caddy
[root@devbox ~]#

but still you get two questions: how to use all IPv4 ips and disable IPv6, and 80/443
problem still exists as seen in above netstat and caddy throwing > bind: address already in use if used in 80/443.

@francislavoie
Copy link
Member

I think you need to bind to tcp4/0.0.0.0 to only bind on IPv4. The default is to use tcp as the network type which is both v4 and v6.

Also, you'll probably need to configure the http_port and https_port global options to avoid the port 80 issue, or turn off Automatic HTTPS (which would avoid binding to the HTTP port to solve the ACME HTTP challenge). But I'm not clear on what you're trying to achieve exactly so I can only guess on this point.

@phrazer
Copy link
Author

phrazer commented Jan 6, 2022

Hey francis,

Thanks for tcp4/0.0.0.0, I didnt know that. That solves some things. But I still think bind * should be global option, at least optional. All I want to do is controll which IP caddy binds at. Also I suggest you add this ipv4/ string to docs, I think it would help anyone looking at this later.

Thanks,

@francislavoie
Copy link
Member

It's sorta mentioned in https://caddyserver.com/docs/conventions#network-addresses, but sure, we could make that clearer. You're the first to ask for this though.

As for a global option, that's not really possible, because the way it works is bind drives the listen field in the JSON config. The combination of the site address' port plus the bind address becomes the listener for that site, then all those are merged according to their listeners being the same. Unique sets of listeners will each become their own "server".

Try adapting your Caddyfile to JSON with caddy adapt --pretty and try different combinations to see how it looks.

Also Automatic HTTPS comes into play (at runtime, not visible in the adapted config), where it will create a server listening on TCP/80 to accept HTTP connections to solve the ACME HTTP challenge.

I don't think we have a way to customize auto HTTPS to use a different network type, because again, nobody's yet asked for this and it seems extremely niche. I'm personally not sure it's worth the added complexity but maybe @mholt has some other ideas to smooth it over.

@mholt
Copy link
Member

mholt commented Jan 6, 2022

I'm surprised that 0.0.0.0 tries to bind to IPv6 as well since it's an IPv4 address. That's a system or Go std lib thing though. Using tcp4/ should force its hand.

And yeah, it doesn't make sense for bind to be applied globally since it's just setting the server's listen address, which in the JSON, is explicitly configured anyway.

As for auto-HTTPS, I dunno... IIRC the redirect servers should use the same listen host as the explicitly configured servers.

@phrazer
Copy link
Author

phrazer commented Jan 9, 2022

@mholt I dont agree with you, bind is always global option. But OK what can I do if you guys decide otherwise. You could at least think of adding bind option to global to be as template for other local bind option.

I can give you example. I want default caddy to bind to IPv4 only. Right now I have to write bind ipv4/0.0.0.0 to every domain to achive this. Wouldnt it be easier if all domain level would first look to global option? Or another case you have multiple ips on box and you dont want to use default ip for caddy. You could just write global option default_bind 1.2.3.4 and all other config/domains levels would take this as default. If you define diffrent bind in domain level, that would be honored, otherwise it would use global bind, not some default system option.

And also you should use some preprocessor for bind, so if we write 0.0.0.0 it would listen to ipv4, and [::] would only use IPv6 as other daemons in *nix. For cleaner configs.

OR at least consider making global option to only use IPv4 or IPv6, but then it would be better to use above idea.

Anyway, thanks for great software

@mholt
Copy link
Member

mholt commented Jan 10, 2022

@phrazer

@mholt I dont agree with you, bind is always global option.

Ok, you don't have to agree with me, but I wrote the software and designed the config format. The listener addresses are not global, they are specified per-server: https://caddyserver.com/docs/json/apps/http/servers/#listen

It might be possible to specify a default network type in the Caddyfile options, but that's not really a thing in the JSON. In the native format you will always have to specify the bind address per-server.

@phrazer
Copy link
Author

phrazer commented Jan 11, 2022

ok, so if you wrote the config, nobody knows anything could be better diffrent except you :)

Let me just give you an example, and I rest my case (dont want to argue with you, just wanted
to help!).

Your current json config vs. below mine idea if you want to change default IP:

# Global options
{
  debug
  
  admin tcp4/0.0.0.0:2020
}

http:// {
  bind tcp4/0.0.0.0
  header Server "caddy index"

  respond * "{header.content-type} {labels.0} {query.p} {path.0}"
}

localhost:8080 {
  bind tcp4/0.0.0.0
  respond "Goodbye, world!"
}

http://test.tld {
  bind tcp4/0.0.0.0
  respond "Goodbye, world!"
}

http://second.tld {
  bind tcp4/0.0.0.0
  respond "Goodbye, world!"
}

www.dir.zoo {
  bind tcp4/0.0.0.0
  
  tls internal
  redir https://dir.zoo{uri}
}

My idea if you want to change default IP (cleaner):

# Global options
{
  debug
  
  # this is default value for admin, and all binds later on...
  bind tcp4/0.0.0.0
}

http:// {
  header Server "caddy index"

  respond * "{header.content-type} {labels.0} {query.p} {path.0}"
}

localhost:8080 {
  respond "Goodbye, world!"
}

http://test.tld {
  respond "Goodbye, world!"
}

http://second.tld {
  # you can still use custom bind if you want for some domains (would overwrite global)
  bind ipv4/1.2.3.4

  respond "Goodbye, world!"
}

www.dir.zoo {
  tls internal
  redir https://dir.zoo{uri}
}

@mholt
Copy link
Member

mholt commented Jan 11, 2022

Not arguing, just I'm trying to be productive/constructive here.

And yes, thanks for spelling out that example, it's basically what I had in mind with "It might be possible to specify a default network type in the Caddyfile options."

@francislavoie francislavoie added discussion 💬 The right solution needs to be found documentation 📚 Improvements or additions to documentation labels Jan 18, 2022
@phrazer
Copy link
Author

phrazer commented Jan 18, 2022

Really appreciate for listening I think this will help some people in the future, especially on production machines where you have variety of ip options and routings.

@mholt mholt closed this as completed Jan 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion 💬 The right solution needs to be found documentation 📚 Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

3 participants