Commit 9ebc11d7 authored by Toby Allen's avatar Toby Allen Committed by GitHub

Merge branch 'master' into macros

parents 4ec5522a 689591ef
...@@ -57,7 +57,12 @@ Caddy binaries have no dependencies and are available for every platform. Get Ca ...@@ -57,7 +57,12 @@ Caddy binaries have no dependencies and are available for every platform. Get Ca
customize your build in the browser customize your build in the browser
- **[Latest release](https://github.com/mholt/caddy/releases/latest)** for - **[Latest release](https://github.com/mholt/caddy/releases/latest)** for
pre-built, vanilla binaries pre-built, vanilla binaries
- **go get** to build from source: `go get github.com/mholt/caddy/caddy` (requires Go 1.8 or newer) - to build with proper version information (required when filing issues), `cd` to the `caddy` folder and use `go get github.com/caddyserver/builds` followed by `go run build.go`.
## Build
To build from source you need **[Git](https://git-scm.com/downloads)** and **[Go](https://golang.org/doc/install)** (1.8 or newer). Follow these instruction for fast building:
- Get source `go get github.com/mholt/caddy/caddy` and then run `go get github.com/caddyserver/builds`
- Now `cd` to `$GOPATH/src/github.com/mholt/caddy/caddy` and run `go run build.go`
Then make sure the `caddy` binary is in your PATH. Then make sure the `caddy` binary is in your PATH.
......
...@@ -25,9 +25,15 @@ func TestAssetsPath(t *testing.T) { ...@@ -25,9 +25,15 @@ func TestAssetsPath(t *testing.T) {
t.Errorf("Expected path to be a .caddy folder, got: %v", actual) t.Errorf("Expected path to be a .caddy folder, got: %v", actual)
} }
os.Setenv("CADDYPATH", "testpath") err := os.Setenv("CADDYPATH", "testpath")
if err != nil {
t.Error("Could not set CADDYPATH")
}
if actual, expected := AssetsPath(), "testpath"; actual != expected { if actual, expected := AssetsPath(), "testpath"; actual != expected {
t.Errorf("Expected path to be %v, got: %v", expected, actual) t.Errorf("Expected path to be %v, got: %v", expected, actual)
} }
os.Setenv("CADDYPATH", "") err = os.Setenv("CADDYPATH", "")
if err != nil {
t.Error("Could not set CADDYPATH")
}
} }
...@@ -206,12 +206,15 @@ func (i *Instance) Restart(newCaddyfile Input) (*Instance, error) { ...@@ -206,12 +206,15 @@ func (i *Instance) Restart(newCaddyfile Input) (*Instance, error) {
// success! stop the old instance // success! stop the old instance
for _, shutdownFunc := range i.onShutdown { for _, shutdownFunc := range i.onShutdown {
err := shutdownFunc() err = shutdownFunc()
if err != nil { if err != nil {
return i, err return i, err
} }
} }
i.Stop() err = i.Stop()
if err != nil {
return i, err
}
// Execute instantiation events // Execute instantiation events
EmitEvent(InstanceStartupEvent, newInst) EmitEvent(InstanceStartupEvent, newInst)
...@@ -483,14 +486,14 @@ func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]r ...@@ -483,14 +486,14 @@ func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]r
if !IsUpgrade() && restartFds == nil { if !IsUpgrade() && restartFds == nil {
// first startup means not a restart or upgrade // first startup means not a restart or upgrade
for _, firstStartupFunc := range inst.onFirstStartup { for _, firstStartupFunc := range inst.onFirstStartup {
err := firstStartupFunc() err = firstStartupFunc()
if err != nil { if err != nil {
return err return err
} }
} }
} }
for _, startupFunc := range inst.onStartup { for _, startupFunc := range inst.onStartup {
err := startupFunc() err = startupFunc()
if err != nil { if err != nil {
return err return err
} }
...@@ -565,12 +568,7 @@ func ValidateAndExecuteDirectives(cdyfile Input, inst *Instance, justValidate bo ...@@ -565,12 +568,7 @@ func ValidateAndExecuteDirectives(cdyfile Input, inst *Instance, justValidate bo
return err return err
} }
err = executeDirectives(inst, cdyfile.Path(), stype.Directives(), sblocks, justValidate) return executeDirectives(inst, cdyfile.Path(), stype.Directives(), sblocks, justValidate)
if err != nil {
return err
}
return nil
} }
func executeDirectives(inst *Instance, filename string, func executeDirectives(inst *Instance, filename string,
...@@ -658,7 +656,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res ...@@ -658,7 +656,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res
if fdIndex, ok := loadedGob.ListenerFds["tcp"+addr]; ok { if fdIndex, ok := loadedGob.ListenerFds["tcp"+addr]; ok {
file := os.NewFile(fdIndex, "") file := os.NewFile(fdIndex, "")
ln, err = net.FileListener(file) ln, err = net.FileListener(file)
file.Close() if err != nil {
return err
}
err = file.Close()
if err != nil { if err != nil {
return err return err
} }
...@@ -666,7 +667,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res ...@@ -666,7 +667,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res
if fdIndex, ok := loadedGob.ListenerFds["udp"+addr]; ok { if fdIndex, ok := loadedGob.ListenerFds["udp"+addr]; ok {
file := os.NewFile(fdIndex, "") file := os.NewFile(fdIndex, "")
pc, err = net.FilePacketConn(file) pc, err = net.FilePacketConn(file)
file.Close() if err != nil {
return err
}
err = file.Close()
if err != nil { if err != nil {
return err return err
} }
...@@ -689,7 +693,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res ...@@ -689,7 +693,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res
if err != nil { if err != nil {
return err return err
} }
file.Close() err = file.Close()
if err != nil {
return err
}
} }
// packetconn // packetconn
if old.packet != nil { if old.packet != nil {
...@@ -701,7 +708,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res ...@@ -701,7 +708,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res
if err != nil { if err != nil {
return err return err
} }
file.Close() err = file.Close()
if err != nil {
return err
}
} }
} }
} }
......
...@@ -252,7 +252,7 @@ func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config ...@@ -252,7 +252,7 @@ func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config
for _, f := range files { for _, f := range files {
name := f.Name() name := f.Name()
for _, indexName := range staticfiles.IndexPages { for _, indexName := range config.Fs.IndexPages {
if name == indexName { if name == indexName {
hasIndexFile = true hasIndexFile = true
break break
......
...@@ -78,8 +78,9 @@ func browseParse(c *caddy.Controller) ([]Config, error) { ...@@ -78,8 +78,9 @@ func browseParse(c *caddy.Controller) ([]Config, error) {
} }
bc.Fs = staticfiles.FileServer{ bc.Fs = staticfiles.FileServer{
Root: http.Dir(cfg.Root), Root: http.Dir(cfg.Root),
Hide: cfg.HiddenFiles, Hide: cfg.HiddenFiles,
IndexPages: cfg.IndexPages,
} }
// Second argument would be the template file to use // Second argument would be the template file to use
......
...@@ -27,6 +27,7 @@ import ( ...@@ -27,6 +27,7 @@ import (
"github.com/mholt/caddy" "github.com/mholt/caddy"
"github.com/mholt/caddy/caddyfile" "github.com/mholt/caddy/caddyfile"
"github.com/mholt/caddy/caddyhttp/staticfiles"
"github.com/mholt/caddy/caddytls" "github.com/mholt/caddy/caddytls"
) )
...@@ -155,6 +156,7 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd ...@@ -155,6 +156,7 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
AltTLSSNIPort: altTLSSNIPort, AltTLSSNIPort: altTLSSNIPort,
}, },
originCaddyfile: sourceFile, originCaddyfile: sourceFile,
IndexPages: staticfiles.DefaultIndexPages,
} }
h.saveConfig(key, cfg) h.saveConfig(key, cfg)
} }
...@@ -234,7 +236,7 @@ func GetConfig(c *caddy.Controller) *SiteConfig { ...@@ -234,7 +236,7 @@ func GetConfig(c *caddy.Controller) *SiteConfig {
// we should only get here during tests because directive // we should only get here during tests because directive
// actions typically skip the server blocks where we make // actions typically skip the server blocks where we make
// the configs // the configs
cfg := &SiteConfig{Root: Root, TLS: new(caddytls.Config)} cfg := &SiteConfig{Root: Root, TLS: new(caddytls.Config), IndexPages: staticfiles.DefaultIndexPages}
ctx.saveConfig(key, cfg) ctx.saveConfig(key, cfg)
return cfg return cfg
} }
......
...@@ -142,7 +142,7 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) { ...@@ -142,7 +142,7 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) {
// Compile custom middleware for every site (enables virtual hosting) // Compile custom middleware for every site (enables virtual hosting)
for _, site := range group { for _, site := range group {
stack := Handler(staticfiles.FileServer{Root: http.Dir(site.Root), Hide: site.HiddenFiles}) stack := Handler(staticfiles.FileServer{Root: http.Dir(site.Root), Hide: site.HiddenFiles, IndexPages: site.IndexPages})
for i := len(site.middleware) - 1; i >= 0; i-- { for i := len(site.middleware) - 1; i >= 0; i-- {
stack = site.middleware[i](stack) stack = site.middleware[i](stack)
} }
......
...@@ -26,6 +26,9 @@ type SiteConfig struct { ...@@ -26,6 +26,9 @@ type SiteConfig struct {
// The address of the site // The address of the site
Addr Address Addr Address
// The list of viable index page names of the site
IndexPages []string
// The hostname to bind listener to; // The hostname to bind listener to;
// defaults to Addr.Host // defaults to Addr.Host
ListenHost string ListenHost string
......
...@@ -16,7 +16,7 @@ package index ...@@ -16,7 +16,7 @@ package index
import ( import (
"github.com/mholt/caddy" "github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/staticfiles" "github.com/mholt/caddy/caddyhttp/httpserver"
) )
func init() { func init() {
...@@ -29,6 +29,8 @@ func init() { ...@@ -29,6 +29,8 @@ func init() {
func setupIndex(c *caddy.Controller) error { func setupIndex(c *caddy.Controller) error {
var index []string var index []string
cfg := httpserver.GetConfig(c)
for c.Next() { for c.Next() {
args := c.RemainingArgs() args := c.RemainingArgs()
...@@ -40,7 +42,7 @@ func setupIndex(c *caddy.Controller) error { ...@@ -40,7 +42,7 @@ func setupIndex(c *caddy.Controller) error {
index = append(index, in) index = append(index, in)
} }
staticfiles.IndexPages = index cfg.IndexPages = index
} }
return nil return nil
......
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"testing" "testing"
"github.com/mholt/caddy" "github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver"
"github.com/mholt/caddy/caddyhttp/staticfiles" "github.com/mholt/caddy/caddyhttp/staticfiles"
) )
...@@ -31,7 +32,7 @@ func TestIndexIncompleteParams(t *testing.T) { ...@@ -31,7 +32,7 @@ func TestIndexIncompleteParams(t *testing.T) {
} }
func TestIndex(t *testing.T) { func TestIndex(t *testing.T) {
c := caddy.NewTestController("", "index a.html b.html c.html") c := caddy.NewTestController("http", "index a.html b.html c.html")
err := setupIndex(c) err := setupIndex(c)
if err != nil { if err != nil {
...@@ -40,14 +41,85 @@ func TestIndex(t *testing.T) { ...@@ -40,14 +41,85 @@ func TestIndex(t *testing.T) {
expectedIndex := []string{"a.html", "b.html", "c.html"} expectedIndex := []string{"a.html", "b.html", "c.html"}
if len(staticfiles.IndexPages) != 3 { siteConfig := httpserver.GetConfig(c)
t.Errorf("Expected 3 values, got %v", len(staticfiles.IndexPages))
if len(siteConfig.IndexPages) != len(expectedIndex) {
t.Errorf("Expected 3 values, got %v", len(siteConfig.IndexPages))
} }
// Ensure ordering is correct // Ensure ordering is correct
for i, actual := range staticfiles.IndexPages { for i, actual := range siteConfig.IndexPages {
if actual != expectedIndex[i] { if actual != expectedIndex[i] {
t.Errorf("Expected value in position %d to be %v, got %v", i, expectedIndex[i], actual) t.Errorf("Expected value in position %d to be %v, got %v", i, expectedIndex[i], actual)
} }
} }
} }
func TestMultiSiteIndexWithEitherHasDefault(t *testing.T) {
// TestIndex already covers the correctness of the directive
// when used on a single controller, so no need to verify test setupIndex again.
// This sets the stage for the actual verification.
customIndex := caddy.NewTestController("http", "index a.html b.html")
// setupIndex against customIdx should not pollute the
// index list for other controllers.
err := setupIndex(customIndex)
if err != nil {
t.Errorf("Expected no errors, got: %v", err)
}
// Represents a virtual host with no index directive.
defaultIndex := caddy.NewTestController("http", "")
// Not calling setupIndex because it guards against lack of arguments,
// and we need to ensure the site gets the default set of index pages.
siteConfig := httpserver.GetConfig(defaultIndex)
// In case the index directive is not used, the virtual host
// should receive staticfiles.DefaultIndexPages slice. The length, as checked here,
// and the values, as checked in the upcoming loop, should match.
if len(siteConfig.IndexPages) != len(staticfiles.DefaultIndexPages) {
t.Errorf("Expected %d values, got %d", len(staticfiles.DefaultIndexPages), len(siteConfig.IndexPages))
}
// Ensure values match the expected default index pages
for i, actual := range siteConfig.IndexPages {
if actual != staticfiles.DefaultIndexPages[i] {
t.Errorf("Expected value in position %d to be %v, got %v", i, staticfiles.DefaultIndexPages[i], actual)
}
}
}
func TestPerSiteIndexPageIsolation(t *testing.T) {
firstIndex := "first.html"
secondIndex := "second.html"
// Create two sites with different index page configurations
firstSite := caddy.NewTestController("http", "index first.html")
err := setupIndex(firstSite)
if err != nil {
t.Errorf("Expected no errors, got: %v", err)
}
secondSite := caddy.NewTestController("http", "index second.html")
err = setupIndex(secondSite)
if err != nil {
t.Errorf("Expected no errors, got: %v", err)
}
firstSiteConfig := httpserver.GetConfig(firstSite)
if firstSiteConfig.IndexPages[0] != firstIndex {
t.Errorf("Expected index for first site as %s, received %s", firstIndex, firstSiteConfig.IndexPages[0])
}
secondSiteConfig := httpserver.GetConfig(secondSite)
if secondSiteConfig.IndexPages[0] != secondIndex {
t.Errorf("Expected index for second site as %s, received %s", secondIndex, secondSiteConfig.IndexPages[0])
}
// They should have different index pages, as per the provided config.
if firstSiteConfig.IndexPages[0] == secondSiteConfig.IndexPages[0] {
t.Errorf("Expected different index pages for both sites, got %s for first and %s for second", firstSiteConfig.IndexPages[0], secondSiteConfig.IndexPages[0])
}
}
...@@ -19,7 +19,6 @@ import ( ...@@ -19,7 +19,6 @@ import (
"strings" "strings"
"github.com/mholt/caddy/caddyhttp/httpserver" "github.com/mholt/caddy/caddyhttp/httpserver"
"github.com/mholt/caddy/caddyhttp/staticfiles"
) )
func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
...@@ -44,7 +43,7 @@ outer: ...@@ -44,7 +43,7 @@ outer:
matches := httpserver.Path(urlPath).Matches(rule.Path) matches := httpserver.Path(urlPath).Matches(rule.Path)
// Also check IndexPages when requesting a directory // Also check IndexPages when requesting a directory
if !matches { if !matches {
indexFile, isIndexFile := httpserver.IndexFile(h.Root, urlPath, staticfiles.IndexPages) indexFile, isIndexFile := httpserver.IndexFile(h.Root, urlPath, h.indexPages)
if isIndexFile { if isIndexFile {
matches = httpserver.Path(indexFile).Matches(rule.Path) matches = httpserver.Path(indexFile).Matches(rule.Path)
} }
......
...@@ -393,7 +393,8 @@ func TestMiddlewareShouldPushIndexFile(t *testing.T) { ...@@ -393,7 +393,8 @@ func TestMiddlewareShouldPushIndexFile(t *testing.T) {
{Path: "/index.css", Method: http.MethodGet}, {Path: "/index.css", Method: http.MethodGet},
}}, }},
}, },
Root: http.Dir(root), Root: http.Dir(root),
indexPages: []string{indexFile},
} }
indexFilePath := filepath.Join(root, indexFile) indexFilePath := filepath.Join(root, indexFile)
......
...@@ -36,9 +36,10 @@ type ( ...@@ -36,9 +36,10 @@ type (
// Middleware supports pushing resources to clients // Middleware supports pushing resources to clients
Middleware struct { Middleware struct {
Next httpserver.Handler Next httpserver.Handler
Rules []Rule Rules []Rule
Root http.FileSystem Root http.FileSystem
indexPages []string // will be injected from SiteConfig on setup
} }
ruleOp func([]Resource) ruleOp func([]Resource)
......
...@@ -50,7 +50,7 @@ func setup(c *caddy.Controller) error { ...@@ -50,7 +50,7 @@ func setup(c *caddy.Controller) error {
cfg := httpserver.GetConfig(c) cfg := httpserver.GetConfig(c)
cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
return Middleware{Next: next, Rules: rules, Root: http.Dir(cfg.Root)} return Middleware{Next: next, Rules: rules, Root: http.Dir(cfg.Root), indexPages: cfg.IndexPages}
}) })
return nil return nil
......
...@@ -45,6 +45,10 @@ import ( ...@@ -45,6 +45,10 @@ import (
type FileServer struct { type FileServer struct {
Root http.FileSystem // jailed access to the file system Root http.FileSystem // jailed access to the file system
Hide []string // list of files for which to respond with "Not Found" Hide []string // list of files for which to respond with "Not Found"
// A list of pages that may be understood as the "index" files to directories.
// Injected from *SiteConfig.
IndexPages []string
} }
// ServeHTTP serves static files for r according to fs's configuration. // ServeHTTP serves static files for r according to fs's configuration.
...@@ -118,7 +122,7 @@ func (fs FileServer) serveFile(w http.ResponseWriter, r *http.Request) (int, err ...@@ -118,7 +122,7 @@ func (fs FileServer) serveFile(w http.ResponseWriter, r *http.Request) (int, err
// if an index file was explicitly requested, strip file name from the request // if an index file was explicitly requested, strip file name from the request
// ("/foo/index.html" -> "/foo/") // ("/foo/index.html" -> "/foo/")
var requestPage = path.Base(urlCopy.Path) var requestPage = path.Base(urlCopy.Path)
for _, indexPage := range IndexPages { for _, indexPage := range fs.IndexPages {
if requestPage == indexPage { if requestPage == indexPage {
urlCopy.Path = urlCopy.Path[:len(urlCopy.Path)-len(indexPage)] urlCopy.Path = urlCopy.Path[:len(urlCopy.Path)-len(indexPage)]
redir = true redir = true
...@@ -134,7 +138,7 @@ func (fs FileServer) serveFile(w http.ResponseWriter, r *http.Request) (int, err ...@@ -134,7 +138,7 @@ func (fs FileServer) serveFile(w http.ResponseWriter, r *http.Request) (int, err
// use contents of an index file, if present, for directory requests // use contents of an index file, if present, for directory requests
if d.IsDir() { if d.IsDir() {
for _, indexPage := range IndexPages { for _, indexPage := range fs.IndexPages {
indexPath := path.Join(reqPath, indexPage) indexPath := path.Join(reqPath, indexPage)
indexFile, err := fs.Root.Open(indexPath) indexFile, err := fs.Root.Open(indexPath)
if err != nil { if err != nil {
...@@ -253,9 +257,9 @@ func calculateEtag(d os.FileInfo) string { ...@@ -253,9 +257,9 @@ func calculateEtag(d os.FileInfo) string {
return `"` + t + s + `"` return `"` + t + s + `"`
} }
// IndexPages is a list of pages that may be understood as // DefaultIndexPages is a list of pages that may be understood as
// the "index" files to directories. // the "index" files to directories.
var IndexPages = []string{ var DefaultIndexPages = []string{
"index.html", "index.html",
"index.htm", "index.htm",
"index.txt", "index.txt",
......
...@@ -36,8 +36,9 @@ func TestServeHTTP(t *testing.T) { ...@@ -36,8 +36,9 @@ func TestServeHTTP(t *testing.T) {
defer afterServeHTTPTest(t, tmpWebRootDir) defer afterServeHTTPTest(t, tmpWebRootDir)
fileserver := FileServer{ fileserver := FileServer{
Root: http.Dir(filepath.Join(tmpWebRootDir, webrootName)), Root: http.Dir(filepath.Join(tmpWebRootDir, webrootName)),
Hide: []string{"dir/hidden.html"}, Hide: []string{"dir/hidden.html"},
IndexPages: DefaultIndexPages,
} }
movedPermanently := "Moved Permanently" movedPermanently := "Moved Permanently"
......
...@@ -148,6 +148,11 @@ type OnDemandState struct { ...@@ -148,6 +148,11 @@ type OnDemandState struct {
// Set from max_certs in tls config, it specifies the // Set from max_certs in tls config, it specifies the
// maximum number of certificates that can be issued. // maximum number of certificates that can be issued.
MaxObtain int32 MaxObtain int32
// The url to call to check if an on-demand tls certificate should
// be issued. If a request to the URL fails or returns a non 2xx
// status on-demand issuances must fail.
AskURL *url.URL
} }
// ObtainCert obtains a certificate for name using c, as long // ObtainCert obtains a certificate for name using c, as long
......
...@@ -19,6 +19,8 @@ import ( ...@@ -19,6 +19,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"net/http"
"net/url"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
...@@ -135,8 +137,8 @@ func (cfg *Config) getCertDuringHandshake(name string, loadIfNecessary, obtainIf ...@@ -135,8 +137,8 @@ func (cfg *Config) getCertDuringHandshake(name string, loadIfNecessary, obtainIf
name = strings.ToLower(name) name = strings.ToLower(name)
// Make sure aren't over any applicable limits // Make sure the certificate should be obtained based on config
err := cfg.checkLimitsForObtainingNewCerts(name) err := cfg.checkIfCertShouldBeObtained(name)
if err != nil { if err != nil {
return Certificate{}, err return Certificate{}, err
} }
...@@ -159,10 +161,52 @@ func (cfg *Config) getCertDuringHandshake(name string, loadIfNecessary, obtainIf ...@@ -159,10 +161,52 @@ func (cfg *Config) getCertDuringHandshake(name string, loadIfNecessary, obtainIf
return Certificate{}, fmt.Errorf("no certificate available for %s", name) return Certificate{}, fmt.Errorf("no certificate available for %s", name)
} }
// checkIfCertShouldBeObtained checks to see if an on-demand tls certificate
// should be obtained for a given domain based upon the config settings. If
// a non-nil error is returned, do not issue a new certificate for name.
func (cfg *Config) checkIfCertShouldBeObtained(name string) error {
// If the "ask" URL is defined in the config, use to determine if a
// cert should obtained
if cfg.OnDemandState.AskURL != nil {
return cfg.checkURLForObtainingNewCerts(name)
}
// Otherwise use the limit defined by the "max_certs" setting
return cfg.checkLimitsForObtainingNewCerts(name)
}
func (cfg *Config) checkURLForObtainingNewCerts(name string) error {
client := http.Client{
Timeout: 10 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return errors.New("following http redirects is not allowed")
},
}
// Copy the URL from the config in order to modify it for this request
askURL := new(url.URL)
*askURL = *cfg.OnDemandState.AskURL
query := askURL.Query()
query.Set("domain", name)
askURL.RawQuery = query.Encode()
resp, err := client.Get(askURL.String())
if err != nil {
return fmt.Errorf("error checking %v to deterine if certificate for hostname '%s' should be allowed: %v", cfg.OnDemandState.AskURL, name, err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return fmt.Errorf("certificate for hostname '%s' not allowed, non-2xx status code %d returned from %v", name, resp.StatusCode, cfg.OnDemandState.AskURL)
}
return nil
}
// checkLimitsForObtainingNewCerts checks to see if name can be issued right // checkLimitsForObtainingNewCerts checks to see if name can be issued right
// now according to mitigating factors we keep track of and preferences the // now according the maximum count defined in the configuration. If a non-nil
// user has set. If a non-nil error is returned, do not issue a new certificate // error is returned, do not issue a new certificate for name.
// for name.
func (cfg *Config) checkLimitsForObtainingNewCerts(name string) error { func (cfg *Config) checkLimitsForObtainingNewCerts(name string) error {
// User can set hard limit for number of certs for the process to issue // User can set hard limit for number of certs for the process to issue
if cfg.OnDemandState.MaxObtain > 0 && if cfg.OnDemandState.MaxObtain > 0 &&
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
...@@ -49,7 +50,7 @@ func setupTLS(c *caddy.Controller) error { ...@@ -49,7 +50,7 @@ func setupTLS(c *caddy.Controller) error {
config.Enabled = true config.Enabled = true
for c.Next() { for c.Next() {
var certificateFile, keyFile, loadDir, maxCerts string var certificateFile, keyFile, loadDir, maxCerts, askURL string
args := c.RemainingArgs() args := c.RemainingArgs()
switch len(args) { switch len(args) {
...@@ -164,6 +165,9 @@ func setupTLS(c *caddy.Controller) error { ...@@ -164,6 +165,9 @@ func setupTLS(c *caddy.Controller) error {
case "max_certs": case "max_certs":
c.Args(&maxCerts) c.Args(&maxCerts)
config.OnDemand = true config.OnDemand = true
case "ask":
c.Args(&askURL)
config.OnDemand = true
case "dns": case "dns":
args := c.RemainingArgs() args := c.RemainingArgs()
if len(args) != 1 { if len(args) != 1 {
...@@ -213,6 +217,19 @@ func setupTLS(c *caddy.Controller) error { ...@@ -213,6 +217,19 @@ func setupTLS(c *caddy.Controller) error {
config.OnDemandState.MaxObtain = int32(maxCertsNum) config.OnDemandState.MaxObtain = int32(maxCertsNum)
} }
if askURL != "" {
parsedURL, err := url.Parse(askURL)
if err != nil {
return c.Err("ask must be a valid url")
}
if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
return c.Err("ask URL must use http or https")
}
config.OnDemandState.AskURL = parsedURL
}
// don't try to load certificates unless we're supposed to // don't try to load certificates unless we're supposed to
if !config.Enabled || !config.Manual { if !config.Enabled || !config.Manual {
continue continue
......
...@@ -62,7 +62,7 @@ fi ...@@ -62,7 +62,7 @@ fi
pidfile="/var/run/${name}.pid" pidfile="/var/run/${name}.pid"
procname="${caddy_bin_path}" #enabled builtin pid checking for start / stop procname="${caddy_bin_path}" #enabled builtin pid checking for start / stop
command="/usr/sbin/daemon" command="/usr/sbin/daemon"
command_args="-u ${caddy_user} -p ${pidfile} /usr/bin/env ${caddy_env} ${procname} -cpu ${caddy_cpu} -log stdout -conf ${caddy_config_path} -agree -email ${caddy_cert_email} < /dev/null >> ${caddy_logfile} 2>&1" command_args="-p ${pidfile} /usr/bin/env ${caddy_env} ${procname} -cpu ${caddy_cpu} -log stdout -conf ${caddy_config_path} -agree -email ${caddy_cert_email} < /dev/null >> ${caddy_logfile} 2>&1"
start_precmd="caddy_startprecmd" start_precmd="caddy_startprecmd"
......
...@@ -28,10 +28,10 @@ func (cfg *Config) Hook(event caddy.EventName, info interface{}) error { ...@@ -28,10 +28,10 @@ func (cfg *Config) Hook(event caddy.EventName, info interface{}) error {
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
if nonblock { if nonblock {
log.Printf("[INFO] Nonblocking Command with ID %s: \"%s %s\"", cfg.ID, cfg.Command, strings.Join(cfg.Args, " ")) log.Printf("[INFO] Nonblocking Command \"%s %s\" with ID %s", cfg.Command, strings.Join(cfg.Args, " "), cfg.ID)
return cmd.Start() return cmd.Start()
} }
log.Printf("[INFO] Blocking Command with ID %s: \"%s %s\"", cfg.ID, cfg.Command, strings.Join(cfg.Args, " ")) log.Printf("[INFO] Blocking Command \"%s %s\" with ID %s", cfg.Command, strings.Join(cfg.Args, " "), cfg.ID)
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return err return err
......
...@@ -20,9 +20,12 @@ func setup(c *caddy.Controller) error { ...@@ -20,9 +20,12 @@ func setup(c *caddy.Controller) error {
} }
// Register Event Hooks. // Register Event Hooks.
for _, cfg := range config { c.OncePerServerBlock(func() error {
caddy.RegisterEventHook("on-"+cfg.ID, cfg.Hook) for _, cfg := range config {
} caddy.RegisterEventHook("on-"+cfg.ID, cfg.Hook)
}
return nil
})
return nil return nil
} }
......
...@@ -84,7 +84,7 @@ func trapSignalsPosix() { ...@@ -84,7 +84,7 @@ func trapSignalsPosix() {
} }
// Kick off the restart; our work is done // Kick off the restart; our work is done
inst, err = inst.Restart(caddyfileToUse) _, err = inst.Restart(caddyfileToUse)
if err != nil { if err != nil {
log.Printf("[ERROR] SIGUSR1: %v", err) log.Printf("[ERROR] SIGUSR1: %v", err)
} }
......
...@@ -36,9 +36,12 @@ func Startup(c *caddy.Controller) error { ...@@ -36,9 +36,12 @@ func Startup(c *caddy.Controller) error {
} }
// Register Event Hooks. // Register Event Hooks.
for _, cfg := range config { c.OncePerServerBlock(func() error {
caddy.RegisterEventHook("on-"+cfg.ID, cfg.Hook) for _, cfg := range config {
} caddy.RegisterEventHook("on-"+cfg.ID, cfg.Hook)
}
return nil
})
fmt.Println("NOTICE: Startup directive will be removed in a later version. Please migrate to 'on startup'") fmt.Println("NOTICE: Startup directive will be removed in a later version. Please migrate to 'on startup'")
......
...@@ -136,7 +136,10 @@ func Upgrade() error { ...@@ -136,7 +136,10 @@ func Upgrade() error {
// immediately close our dup'ed fds and the write end of our signal pipe // immediately close our dup'ed fds and the write end of our signal pipe
for _, f := range extraFiles { for _, f := range extraFiles {
f.Close() err = f.Close()
if err != nil {
return err
}
} }
// feed Caddyfile to the child // feed Caddyfile to the child
...@@ -144,11 +147,14 @@ func Upgrade() error { ...@@ -144,11 +147,14 @@ func Upgrade() error {
if err != nil { if err != nil {
return err return err
} }
wpipe.Close() err = wpipe.Close()
if err != nil {
return err
}
// determine whether child startup succeeded // determine whether child startup succeeded
answer, readErr := ioutil.ReadAll(sigrpipe) answer, readErr := ioutil.ReadAll(sigrpipe)
if answer == nil || len(answer) == 0 { if len(answer) == 0 {
cmdErr := cmd.Wait() // get exit status cmdErr := cmd.Wait() // get exit status
errStr := fmt.Sprintf("child failed to initialize: %v", cmdErr) errStr := fmt.Sprintf("child failed to initialize: %v", cmdErr)
if readErr != nil { if readErr != nil {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment