mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-24 03:05:49 +03:00
Redirect HTTP requests to HTTPS by default
This commit is contained in:
parent
df194d567f
commit
506630200b
2 changed files with 58 additions and 13 deletions
|
@ -102,7 +102,7 @@ func Load(filename string, input io.Reader) (Group, error) {
|
||||||
log.SetFlags(flags)
|
log.SetFlags(flags)
|
||||||
|
|
||||||
// secure all the things
|
// secure all the things
|
||||||
err = initiateLetsEncrypt(configs)
|
configs, err = initiateLetsEncrypt(configs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
|
// TODO: This code is a mess but I'm cleaning it up locally and
|
||||||
|
// refactoring a bunch. It will have tests, too. Don't worry. :)
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
@ -10,11 +13,14 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mholt/caddy/app"
|
"github.com/mholt/caddy/app"
|
||||||
|
"github.com/mholt/caddy/middleware"
|
||||||
|
"github.com/mholt/caddy/middleware/redirect"
|
||||||
"github.com/mholt/caddy/server"
|
"github.com/mholt/caddy/server"
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
)
|
)
|
||||||
|
@ -35,7 +41,7 @@ const (
|
||||||
// in configs as needed. It only skips the config if the
|
// in configs as needed. It only skips the config if the
|
||||||
// cert and key are already specified or if plaintext http
|
// cert and key are already specified or if plaintext http
|
||||||
// is explicitly specified as the port.
|
// is explicitly specified as the port.
|
||||||
func initiateLetsEncrypt(configs []server.Config) error {
|
func initiateLetsEncrypt(configs []server.Config) ([]server.Config, error) {
|
||||||
// populate map of email address to server configs that use that email address for TLS.
|
// populate map of email address to server configs that use that email address for TLS.
|
||||||
// this will help us reduce roundtrips when getting the certs.
|
// this will help us reduce roundtrips when getting the certs.
|
||||||
initMap := make(map[string][]*server.Config)
|
initMap := make(map[string][]*server.Config)
|
||||||
|
@ -43,7 +49,7 @@ func initiateLetsEncrypt(configs []server.Config) error {
|
||||||
if configs[i].TLS.Certificate == "" && configs[i].TLS.Key == "" && configs[i].Port != "http" { // TODO: && !cfg.Host.IsLoopback()
|
if configs[i].TLS.Certificate == "" && configs[i].TLS.Key == "" && configs[i].Port != "http" { // TODO: && !cfg.Host.IsLoopback()
|
||||||
leEmail := getEmail(configs[i])
|
leEmail := getEmail(configs[i])
|
||||||
if leEmail == "" {
|
if leEmail == "" {
|
||||||
return errors.New("cannot serve HTTPS without email address OR certificate and key")
|
return configs, errors.New("cannot serve HTTPS without email address OR certificate and key")
|
||||||
}
|
}
|
||||||
initMap[leEmail] = append(initMap[leEmail], &configs[i])
|
initMap[leEmail] = append(initMap[leEmail], &configs[i])
|
||||||
}
|
}
|
||||||
|
@ -55,7 +61,7 @@ func initiateLetsEncrypt(configs []server.Config) error {
|
||||||
// Look up or create the LE user account
|
// Look up or create the LE user account
|
||||||
leUser, err := getLetsEncryptUser(leEmail)
|
leUser, err := getLetsEncryptUser(leEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return configs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The client facilitates our communication with the CA server.
|
// The client facilitates our communication with the CA server.
|
||||||
|
@ -66,7 +72,7 @@ func initiateLetsEncrypt(configs []server.Config) error {
|
||||||
if leUser.Registration == nil {
|
if leUser.Registration == nil {
|
||||||
reg, err := client.Register()
|
reg, err := client.Register()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("registration error: " + err.Error())
|
return configs, errors.New("registration error: " + err.Error())
|
||||||
}
|
}
|
||||||
leUser.Registration = reg
|
leUser.Registration = reg
|
||||||
|
|
||||||
|
@ -74,12 +80,12 @@ func initiateLetsEncrypt(configs []server.Config) error {
|
||||||
err = client.AgreeToTos()
|
err = client.AgreeToTos()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
saveLetsEncryptUser(leUser) // TODO: Might as well try, right? Error check?
|
saveLetsEncryptUser(leUser) // TODO: Might as well try, right? Error check?
|
||||||
return errors.New("error agreeing to terms: " + err.Error())
|
return configs, errors.New("error agreeing to terms: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = saveLetsEncryptUser(leUser)
|
err = saveLetsEncryptUser(leUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("could not save user: " + err.Error())
|
return configs, errors.New("could not save user: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +98,7 @@ func initiateLetsEncrypt(configs []server.Config) error {
|
||||||
// showtime: let's get free, trusted SSL certificates! yeah!
|
// showtime: let's get free, trusted SSL certificates! yeah!
|
||||||
certificates, err := client.ObtainCertificates(hosts)
|
certificates, err := client.ObtainCertificates(hosts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("error obtaining certs: " + err.Error())
|
return configs, errors.New("error obtaining certs: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... that's it. save the certs, keys, and update server configs.
|
// ... that's it. save the certs, keys, and update server configs.
|
||||||
|
@ -103,23 +109,23 @@ func initiateLetsEncrypt(configs []server.Config) error {
|
||||||
// Save cert
|
// Save cert
|
||||||
err = saveCertificate(cert.Certificate, filepath.Join(certFolder, cert.Domain+".crt"))
|
err = saveCertificate(cert.Certificate, filepath.Join(certFolder, cert.Domain+".crt"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return configs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save private key
|
// Save private key
|
||||||
err = ioutil.WriteFile(filepath.Join(certFolder, cert.Domain+".key"), cert.PrivateKey, 0600)
|
err = ioutil.WriteFile(filepath.Join(certFolder, cert.Domain+".key"), cert.PrivateKey, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return configs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save cert metadata
|
// Save cert metadata
|
||||||
jsonBytes, err := json.MarshalIndent(&CertificateMeta{URL: cert.CertURL, Domain: cert.Domain}, "", "\t")
|
jsonBytes, err := json.MarshalIndent(&CertificateMeta{URL: cert.CertURL, Domain: cert.Domain}, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return configs, err
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile(filepath.Join(certFolder, cert.Domain+".json"), jsonBytes, 0600)
|
err = ioutil.WriteFile(filepath.Join(certFolder, cert.Domain+".json"), jsonBytes, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return configs, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,10 +135,49 @@ func initiateLetsEncrypt(configs []server.Config) error {
|
||||||
cfg.TLS.Key = filepath.Join(app.DataFolder(), "letsencrypt", "sites", cfg.Host, cfg.Host+".key")
|
cfg.TLS.Key = filepath.Join(app.DataFolder(), "letsencrypt", "sites", cfg.Host, cfg.Host+".key")
|
||||||
cfg.TLS.Enabled = true
|
cfg.TLS.Enabled = true
|
||||||
cfg.Port = "https"
|
cfg.Port = "https"
|
||||||
|
|
||||||
|
// Is there a plaintext HTTP config for the same host? If not, make
|
||||||
|
// one and have it redirect all requests to this HTTPS host.
|
||||||
|
var plaintextHostFound bool
|
||||||
|
for _, otherCfg := range configs {
|
||||||
|
if cfg.Host == otherCfg.Host && otherCfg.Port == "http" {
|
||||||
|
plaintextHostFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !plaintextHostFound {
|
||||||
|
// Make one that redirects to HTTPS for all requests
|
||||||
|
configs = append(configs, redirPlaintextHost(cfg))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return configs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirPlaintextHost returns a new virtualhost configuration for a server
|
||||||
|
// that redirects the plaintext HTTP host of cfg to cfg, which is assumed
|
||||||
|
// to be the secure (HTTPS) host.
|
||||||
|
func redirPlaintextHost(cfg server.Config) server.Config {
|
||||||
|
redirMidware := func(next middleware.Handler) middleware.Handler {
|
||||||
|
return redirect.Redirect{Next: next, Rules: []redirect.Rule{
|
||||||
|
{
|
||||||
|
FromScheme: "http",
|
||||||
|
FromPath: "/",
|
||||||
|
To: "https://" + cfg.Host + "{uri}",
|
||||||
|
Code: http.StatusMovedPermanently,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.Config{
|
||||||
|
Host: cfg.Host,
|
||||||
|
Port: "http",
|
||||||
|
Middleware: map[string][]middleware.Middleware{
|
||||||
|
"/": []middleware.Middleware{redirMidware},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getEmail does everything it can to obtain an email
|
// getEmail does everything it can to obtain an email
|
||||||
|
|
Loading…
Reference in a new issue