mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-23 22:27:38 -05:00
Better error handling at startup and fixed some bugs
Fixed bug where manually specifying port 443 disabled TLS (whoops); otherHostHasScheme was the culprit, since it would return true even if it was the same config that had that scheme. Also, an error at startup (if not a restart) is now fatal, rather than keeping a half-alive zombie server.
This commit is contained in:
parent
e8006acf80
commit
5e1573dd84
4 changed files with 57 additions and 41 deletions
|
@ -127,7 +127,7 @@ func Start(cdyfile Input) error {
|
|||
}
|
||||
|
||||
// Close remaining file descriptors we may have inherited that we don't need
|
||||
if isRestart() {
|
||||
if IsRestart() {
|
||||
for _, fdIndex := range loadedGob.ListenerFds {
|
||||
file := os.NewFile(fdIndex, "")
|
||||
fln, err := net.FileListener(file)
|
||||
|
@ -138,7 +138,7 @@ func Start(cdyfile Input) error {
|
|||
}
|
||||
|
||||
// Show initialization output
|
||||
if !Quiet && !isRestart() {
|
||||
if !Quiet && !IsRestart() {
|
||||
var checkedFdLimit bool
|
||||
for _, group := range groupings {
|
||||
for _, conf := range group.Configs {
|
||||
|
@ -159,7 +159,7 @@ func Start(cdyfile Input) error {
|
|||
}
|
||||
|
||||
// Tell parent process that we got this
|
||||
if isRestart() {
|
||||
if IsRestart() {
|
||||
ppipe := os.NewFile(3, "") // parent is listening on pipe at index 3
|
||||
ppipe.Write([]byte("success"))
|
||||
ppipe.Close()
|
||||
|
@ -174,6 +174,7 @@ func Start(cdyfile Input) error {
|
|||
// until the servers are listening.
|
||||
func startServers(groupings Group) error {
|
||||
var startupWg sync.WaitGroup
|
||||
errChan := make(chan error)
|
||||
|
||||
for _, group := range groupings {
|
||||
s, err := server.New(group.BindAddr.String(), group.Configs)
|
||||
|
@ -183,7 +184,7 @@ func startServers(groupings Group) error {
|
|||
s.HTTP2 = HTTP2 // TODO: This setting is temporary
|
||||
|
||||
var ln server.ListenerFile
|
||||
if isRestart() {
|
||||
if IsRestart() {
|
||||
// Look up this server's listener in the map of inherited file descriptors;
|
||||
// if we don't have one, we must make a new one.
|
||||
if fdIndex, ok := loadedGob.ListenerFds[s.Addr]; ok {
|
||||
|
@ -215,11 +216,7 @@ func startServers(groupings Group) error {
|
|||
|
||||
// "use of closed network connection" is normal if doing graceful shutdown...
|
||||
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
if isRestart() {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
log.Println(err)
|
||||
}
|
||||
errChan <- err
|
||||
}
|
||||
}(s, ln)
|
||||
|
||||
|
@ -234,8 +231,16 @@ func startServers(groupings Group) error {
|
|||
serversMu.Unlock()
|
||||
}
|
||||
|
||||
// Wait for all servers to finish starting
|
||||
startupWg.Wait()
|
||||
|
||||
// Return the first error, if any
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return err
|
||||
default:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -269,7 +274,7 @@ func Wait() {
|
|||
func LoadCaddyfile(loader func() (Input, error)) (cdyfile Input, err error) {
|
||||
// If we are a fork, finishing the restart is highest priority;
|
||||
// piped input is required in this case.
|
||||
if isRestart() {
|
||||
if IsRestart() {
|
||||
err := gob.NewDecoder(os.Stdin).Decode(&loadedGob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -46,9 +46,9 @@ type caddyfileGob struct {
|
|||
Caddyfile Input
|
||||
}
|
||||
|
||||
// isRestart returns whether this process is, according
|
||||
// IsRestart returns whether this process is, according
|
||||
// to env variables, a fork as part of a graceful restart.
|
||||
func isRestart() bool {
|
||||
func IsRestart() bool {
|
||||
return os.Getenv("CADDY_RESTART") == "true"
|
||||
}
|
||||
|
||||
|
|
|
@ -57,8 +57,8 @@ func Activate(configs []server.Config) ([]server.Config, error) {
|
|||
// we already have certs and keys in storage from last time.
|
||||
configLen := len(configs) // avoid infinite loop since this loop appends plaintext to the slice
|
||||
for i := 0; i < configLen; i++ {
|
||||
if existingCertAndKey(configs[i].Host) && configQualifies(configs[i], configs) {
|
||||
configs = autoConfigure(&configs[i], configs)
|
||||
if existingCertAndKey(configs[i].Host) && configQualifies(configs, i) {
|
||||
configs = autoConfigure(configs, i)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ func Activate(configs []server.Config) ([]server.Config, error) {
|
|||
|
||||
// it all comes down to this: turning on TLS with all the new certs
|
||||
for i := 0; i < len(serverConfigs); i++ {
|
||||
configs = autoConfigure(serverConfigs[i], configs)
|
||||
configs = autoConfigure(configs, i)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,10 +151,11 @@ func Deactivate() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// configQualifies returns true if cfg qualifes for automatic LE activation,
|
||||
// but it does require the list of all configs to be passed in as well.
|
||||
// It does NOT check to see if a cert and key already exist for cfg.
|
||||
func configQualifies(cfg server.Config, allConfigs []server.Config) bool {
|
||||
// configQualifies returns true if the config at cfgIndex (within allConfigs)
|
||||
// qualifes for automatic LE activation. It does NOT check to see if a cert
|
||||
// and key already exist for the config.
|
||||
func configQualifies(allConfigs []server.Config, cfgIndex int) bool {
|
||||
cfg := allConfigs[cfgIndex]
|
||||
return cfg.TLS.Certificate == "" && // user could provide their own cert and key
|
||||
cfg.TLS.Key == "" &&
|
||||
|
||||
|
@ -167,11 +168,11 @@ func configQualifies(cfg server.Config, allConfigs []server.Config) bool {
|
|||
cfg.Host != "" &&
|
||||
cfg.Host != "0.0.0.0" &&
|
||||
cfg.Host != "::1" &&
|
||||
!strings.HasPrefix(cfg.Host, "127.") && // to use a boulder on your own machine, add fake domain to hosts file
|
||||
!strings.HasPrefix(cfg.Host, "127.") && // to use boulder on your own machine, add fake domain to hosts file
|
||||
// not excluding 10.* and 192.168.* hosts for possibility of running internal Boulder instance
|
||||
|
||||
// make sure an HTTPS version of this config doesn't exist in the list already
|
||||
!hostHasOtherScheme(cfg.Host, "https", allConfigs)
|
||||
// make sure another HTTPS version of this config doesn't exist in the list already
|
||||
!otherHostHasScheme(allConfigs, cfgIndex, "https")
|
||||
}
|
||||
|
||||
// groupConfigsByEmail groups configs by user email address. The returned map is
|
||||
|
@ -186,7 +187,7 @@ func groupConfigsByEmail(configs []server.Config) (map[string][]*server.Config,
|
|||
// that we won't be obtaining certs for - this way we won't
|
||||
// bother the user for an email address unnecessarily and
|
||||
// we don't obtain new certs for a host we already have certs for.
|
||||
if existingCertAndKey(configs[i].Host) || !configQualifies(configs[i], configs) {
|
||||
if existingCertAndKey(configs[i].Host) || !configQualifies(configs, i) {
|
||||
continue
|
||||
}
|
||||
leEmail := getEmail(configs[i])
|
||||
|
@ -311,12 +312,14 @@ func saveCertsAndKeys(certificates []acme.CertificateResource) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// autoConfigure enables TLS on cfg and appends, if necessary, a new config
|
||||
// to allConfigs that redirects plaintext HTTP to its new HTTPS counterpart.
|
||||
// It expects the certificate and key to already be in storage. It returns
|
||||
// the new list of allConfigs, since it may append a new config. This function
|
||||
// assumes that cfg was already set up for HTTPS.
|
||||
func autoConfigure(cfg *server.Config, allConfigs []server.Config) []server.Config {
|
||||
// autoConfigure enables TLS on allConfigs[cfgIndex] and appends, if necessary,
|
||||
// a new config to allConfigs that redirects plaintext HTTP to its new HTTPS
|
||||
// counterpart. It expects the certificate and key to already be in storage. It
|
||||
// returns the new list of allConfigs, since it may append a new config. This
|
||||
// function assumes that allConfigs[cfgIndex] is already set up for HTTPS.
|
||||
func autoConfigure(allConfigs []server.Config, cfgIndex int) []server.Config {
|
||||
cfg := &allConfigs[cfgIndex]
|
||||
|
||||
bundleBytes, err := ioutil.ReadFile(storage.SiteCertFile(cfg.Host))
|
||||
// TODO: Handle these errors better
|
||||
if err == nil {
|
||||
|
@ -344,28 +347,32 @@ func autoConfigure(cfg *server.Config, allConfigs []server.Config) []server.Conf
|
|||
acmeHandlers[cfg.Host] = handler
|
||||
}
|
||||
|
||||
// Set up http->https redirect as long as there isn't already
|
||||
// a http counterpart in the configs
|
||||
if !hostHasOtherScheme(cfg.Host, "http", allConfigs) {
|
||||
// Set up http->https redirect as long as there isn't already a http counterpart
|
||||
// in the configs and this isn't, for some reason, already on port 80
|
||||
if !otherHostHasScheme(allConfigs, cfgIndex, "http") &&
|
||||
cfg.Port != "80" && cfg.Port != "http" { // (would not be http port with current program flow, but just in case)
|
||||
allConfigs = append(allConfigs, redirPlaintextHost(*cfg))
|
||||
}
|
||||
|
||||
return allConfigs
|
||||
}
|
||||
|
||||
// hostHasOtherScheme tells you whether there is another config in the list
|
||||
// for the same host but with the port equal to scheme. For example, to see
|
||||
// if example.com has a https variant already, pass in example.com and
|
||||
// "https" along with the list of configs. This function considers "443"
|
||||
// and "https" to be the same scheme, as well as "http" and "80".
|
||||
func hostHasOtherScheme(host, scheme string, allConfigs []server.Config) bool {
|
||||
// otherHostHasScheme tells you whether there is ANOTHER config in allConfigs
|
||||
// for the same host but with the port equal to scheme as allConfigs[cfgIndex].
|
||||
// This function considers "443" and "https" to be the same scheme, as well as
|
||||
// "http" and "80". It does not tell you whether there is ANY config with scheme,
|
||||
// only if there's a different one with it.
|
||||
func otherHostHasScheme(allConfigs []server.Config, cfgIndex int, scheme string) bool {
|
||||
if scheme == "80" {
|
||||
scheme = "http"
|
||||
} else if scheme == "443" {
|
||||
scheme = "https"
|
||||
}
|
||||
for _, otherCfg := range allConfigs {
|
||||
if otherCfg.Host == host {
|
||||
for i, otherCfg := range allConfigs {
|
||||
if i == cfgIndex {
|
||||
continue // has to be a config OTHER than the one we're comparing against
|
||||
}
|
||||
if otherCfg.Host == allConfigs[cfgIndex].Host {
|
||||
if (otherCfg.Port == scheme) ||
|
||||
(scheme == "https" && otherCfg.Port == "443") ||
|
||||
(scheme == "http" && otherCfg.Port == "80") {
|
||||
|
|
4
main.go
4
main.go
|
@ -79,8 +79,12 @@ func main() {
|
|||
// Start your engines
|
||||
err = caddy.Start(caddyfile)
|
||||
if err != nil {
|
||||
if caddy.IsRestart() {
|
||||
log.Println("error starting servers:", err)
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Twiddle your thumbs
|
||||
caddy.Wait()
|
||||
|
|
Loading…
Reference in a new issue