mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-29 20:24:11 +08:00
Merge branch 'master' into getcertificate
This commit is contained in:
commit
178c4d11d9
|
@ -25,6 +25,7 @@ func saveRSAPrivateKey(key *rsa.PrivateKey, file string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keyOut.Chmod(0600)
|
||||
defer keyOut.Close()
|
||||
return pem.Encode(keyOut, &pemKey)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -28,13 +29,26 @@ func TestSaveAndLoadRSAPrivateKey(t *testing.T) {
|
|||
t.Fatal("error saving private key:", err)
|
||||
}
|
||||
|
||||
// it doesn't make sense to test file permission on windows
|
||||
if runtime.GOOS != "windows" {
|
||||
// get info of the key file
|
||||
info, err := os.Stat(keyFile)
|
||||
if err != nil {
|
||||
t.Fatal("error stating private key:", err)
|
||||
}
|
||||
// verify permission of key file is correct
|
||||
if info.Mode().Perm() != 0600 {
|
||||
t.Error("Expected key file to have permission 0600, but it wasn't")
|
||||
}
|
||||
}
|
||||
|
||||
// test load
|
||||
loadedKey, err := loadRSAPrivateKey(keyFile)
|
||||
if err != nil {
|
||||
t.Error("error loading private key:", err)
|
||||
}
|
||||
|
||||
// very loaded key is correct
|
||||
// verify loaded key is correct
|
||||
if !rsaPrivateKeysSame(privateKey, loadedKey) {
|
||||
t.Error("Expected key bytes to be the same, but they weren't")
|
||||
}
|
||||
|
|
|
@ -322,7 +322,7 @@ func newClientPort(leEmail, port string) (*acme.Client, error) {
|
|||
client.SetHTTPAddress(":" + port)
|
||||
client.SetTLSAddress(":" + port)
|
||||
}
|
||||
client.ExcludeChallenges([]string{"tls-sni-01", "dns-01"}) // We can only guarantee http-01 at this time, but tls-01 should work if port is not custom!
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.TLSSNI01, acme.DNS01}) // We can only guarantee http-01 at this time, but tls-01 should work if port is not custom!
|
||||
|
||||
// If not registered, the user must register an account with the CA
|
||||
// and agree to terms
|
||||
|
|
|
@ -32,6 +32,8 @@ func init() {
|
|||
// work if executing with `go run`, since the binary is cleaned up
|
||||
// when `go run` sees the initial parent process exit.
|
||||
func Restart(newCaddyfile Input) error {
|
||||
log.Println("[INFO] Restarting")
|
||||
|
||||
if newCaddyfile == nil {
|
||||
caddyfileMu.Lock()
|
||||
newCaddyfile = caddyfile
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package caddy
|
||||
|
||||
import "log"
|
||||
|
||||
// Restart restarts Caddy forcefully using newCaddyfile,
|
||||
// or, if nil, the current/existing Caddyfile is reused.
|
||||
func Restart(newCaddyfile Input) error {
|
||||
log.Println("[INFO] Restarting")
|
||||
|
||||
if newCaddyfile == nil {
|
||||
caddyfileMu.Lock()
|
||||
newCaddyfile = caddyfile
|
||||
|
|
|
@ -214,6 +214,7 @@ td .name {
|
|||
margin-left: 1.75em;
|
||||
word-break: break-all;
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
footer {
|
||||
|
|
|
@ -47,7 +47,7 @@ func gzipParse(c *Controller) ([]gzip.Config, error) {
|
|||
return configs, c.ArgErr()
|
||||
}
|
||||
for _, e := range exts {
|
||||
if !strings.HasPrefix(e, ".") && e != gzip.ExtWildCard {
|
||||
if !strings.HasPrefix(e, ".") && e != gzip.ExtWildCard && e != "" {
|
||||
return configs, fmt.Errorf(`gzip: invalid extension "%v" (must start with dot)`, e)
|
||||
}
|
||||
extFilter.Exts.Add(e)
|
||||
|
|
|
@ -50,6 +50,9 @@ func TestGzip(t *testing.T) {
|
|||
level 1
|
||||
}
|
||||
gzip`, false},
|
||||
{`gzip {
|
||||
ext ""
|
||||
}`, false},
|
||||
{`gzip { not /file
|
||||
ext .html
|
||||
level 1
|
||||
|
|
6
dist/CHANGES.txt
vendored
6
dist/CHANGES.txt
vendored
|
@ -1,9 +1,9 @@
|
|||
CHANGES
|
||||
|
||||
<master>
|
||||
0.8.1 (January 12, 2016)
|
||||
- Improved OCSP stapling
|
||||
- Can reload config with new hosts that need certs from Let's Encrypt
|
||||
- If pidfile is created, it is deleted when Caddy exits
|
||||
- Better graceful reload when new hosts need certificates from Let's Encrypt
|
||||
- Current pidfile is now deleted when Caddy exits
|
||||
- browse: New default template
|
||||
- gzip: Added min_length setting
|
||||
- import: Support for glob patterns (*) to import multiple files
|
||||
|
|
2
dist/README.txt
vendored
2
dist/README.txt
vendored
|
@ -1,4 +1,4 @@
|
|||
CADDY 0.8
|
||||
CADDY 0.8.1
|
||||
|
||||
Website
|
||||
https://caddyserver.com
|
||||
|
|
2
main.go
2
main.go
|
@ -27,7 +27,7 @@ var (
|
|||
|
||||
const (
|
||||
appName = "Caddy"
|
||||
appVersion = "0.8"
|
||||
appVersion = "0.8.1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -196,7 +196,7 @@ func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root s
|
|||
fileCount++
|
||||
}
|
||||
|
||||
url := url.URL{Path: name}
|
||||
url := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name
|
||||
|
||||
fileinfos = append(fileinfos, FileInfo{
|
||||
IsDir: f.IsDir(),
|
||||
|
|
|
@ -147,11 +147,11 @@ func TestBrowseTemplate(t *testing.T) {
|
|||
|
||||
<h1>/photos/</h1>
|
||||
|
||||
<a href="test.html">test.html</a><br>
|
||||
<a href="./test.html">test.html</a><br>
|
||||
|
||||
<a href="test2.html">test2.html</a><br>
|
||||
<a href="./test2.html">test2.html</a><br>
|
||||
|
||||
<a href="test3.html">test3.html</a><br>
|
||||
<a href="./test3.html">test3.html</a><br>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -209,7 +209,7 @@ func TestBrowseJson(t *testing.T) {
|
|||
name += "/"
|
||||
}
|
||||
|
||||
url := url.URL{Path: name}
|
||||
url := url.URL{Path: "./" + name}
|
||||
|
||||
fileinfos = append(fileinfos, FileInfo{
|
||||
IsDir: f.IsDir(),
|
||||
|
|
|
@ -15,7 +15,7 @@ type RequestFilter interface {
|
|||
}
|
||||
|
||||
// defaultExtensions is the list of default extensions for which to enable gzipping.
|
||||
var defaultExtensions = []string{"", ".txt", ".htm", ".html", ".css", ".php", ".js", ".json", ".md", ".xml"}
|
||||
var defaultExtensions = []string{"", ".txt", ".htm", ".html", ".css", ".php", ".js", ".json", ".md", ".xml", ".svg"}
|
||||
|
||||
// DefaultExtFilter creates an ExtFilter with default extensions.
|
||||
func DefaultExtFilter() ExtFilter {
|
||||
|
|
|
@ -57,6 +57,10 @@ func (uh *UpstreamHost) Down() bool {
|
|||
return uh.CheckDown(uh)
|
||||
}
|
||||
|
||||
// tryDuration is how long to try upstream hosts; failures result in
|
||||
// immediate retries until this duration ends or we get a nil host.
|
||||
var tryDuration = 60 * time.Second
|
||||
|
||||
// ServeHTTP satisfies the middleware.Handler interface.
|
||||
func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
|
||||
|
@ -68,7 +72,7 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
|||
|
||||
// Since Select() should give us "up" hosts, keep retrying
|
||||
// hosts until timeout (or until we get a nil host).
|
||||
for time.Now().Sub(start) < (60 * time.Second) {
|
||||
for time.Now().Sub(start) < tryDuration {
|
||||
host := upstream.Select()
|
||||
if host == nil {
|
||||
return http.StatusBadGateway, errUnreachable
|
||||
|
|
|
@ -4,10 +4,13 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -15,6 +18,70 @@ import (
|
|||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tryDuration = 50 * time.Millisecond // prevent tests from hanging
|
||||
}
|
||||
|
||||
func TestReverseProxy(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
var requestReceived bool
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requestReceived = true
|
||||
w.Write([]byte("Hello, client"))
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
// set up proxy
|
||||
p := &Proxy{
|
||||
Upstreams: []Upstream{newFakeUpstream(backend.URL, false)},
|
||||
}
|
||||
|
||||
// create request and response recorder
|
||||
r, err := http.NewRequest("GET", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create request: %v", err)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
p.ServeHTTP(w, r)
|
||||
|
||||
if !requestReceived {
|
||||
t.Error("Expected backend to receive request, but it didn't")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseProxyInsecureSkipVerify(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
var requestReceived bool
|
||||
backend := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requestReceived = true
|
||||
w.Write([]byte("Hello, client"))
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
// set up proxy
|
||||
p := &Proxy{
|
||||
Upstreams: []Upstream{newFakeUpstream(backend.URL, true)},
|
||||
}
|
||||
|
||||
// create request and response recorder
|
||||
r, err := http.NewRequest("GET", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create request: %v", err)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
p.ServeHTTP(w, r)
|
||||
|
||||
if !requestReceived {
|
||||
t.Error("Even with insecure HTTPS, expected backend to receive request, but it didn't")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebSocketReverseProxyServeHTTPHandler(t *testing.T) {
|
||||
// No-op websocket backend simply allows the WS connection to be
|
||||
// accepted then it will be immediately closed. Perfect for testing.
|
||||
|
@ -93,18 +160,24 @@ func TestWebSocketReverseProxyFromWSClient(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// newWebSocketTestProxy returns a test proxy that will
|
||||
// redirect to the specified backendAddr. The function
|
||||
// also sets up the rules/environment for testing WebSocket
|
||||
// proxy.
|
||||
func newWebSocketTestProxy(backendAddr string) *Proxy {
|
||||
return &Proxy{
|
||||
Upstreams: []Upstream{&fakeUpstream{name: backendAddr}},
|
||||
func newFakeUpstream(name string, insecure bool) *fakeUpstream {
|
||||
uri, _ := url.Parse(name)
|
||||
u := &fakeUpstream{
|
||||
name: name,
|
||||
host: &UpstreamHost{
|
||||
Name: name,
|
||||
ReverseProxy: NewSingleHostReverseProxy(uri, ""),
|
||||
},
|
||||
}
|
||||
if insecure {
|
||||
u.host.ReverseProxy.Transport = InsecureTransport
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
type fakeUpstream struct {
|
||||
name string
|
||||
host *UpstreamHost
|
||||
}
|
||||
|
||||
func (u *fakeUpstream) From() string {
|
||||
|
@ -112,6 +185,32 @@ func (u *fakeUpstream) From() string {
|
|||
}
|
||||
|
||||
func (u *fakeUpstream) Select() *UpstreamHost {
|
||||
return u.host
|
||||
}
|
||||
|
||||
func (u *fakeUpstream) IsAllowedPath(requestPath string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// newWebSocketTestProxy returns a test proxy that will
|
||||
// redirect to the specified backendAddr. The function
|
||||
// also sets up the rules/environment for testing WebSocket
|
||||
// proxy.
|
||||
func newWebSocketTestProxy(backendAddr string) *Proxy {
|
||||
return &Proxy{
|
||||
Upstreams: []Upstream{&fakeWsUpstream{name: backendAddr}},
|
||||
}
|
||||
}
|
||||
|
||||
type fakeWsUpstream struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (u *fakeWsUpstream) From() string {
|
||||
return "/"
|
||||
}
|
||||
|
||||
func (u *fakeWsUpstream) Select() *UpstreamHost {
|
||||
uri, _ := url.Parse(u.name)
|
||||
return &UpstreamHost{
|
||||
Name: u.name,
|
||||
|
@ -122,7 +221,7 @@ func (u *fakeUpstream) Select() *UpstreamHost {
|
|||
}
|
||||
}
|
||||
|
||||
func (u *fakeUpstream) IsAllowedPath(requestPath string) bool {
|
||||
func (u *fakeWsUpstream) IsAllowedPath(requestPath string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -103,6 +104,16 @@ var hopHeaders = []string{
|
|||
"Upgrade",
|
||||
}
|
||||
|
||||
var InsecureTransport http.RoundTripper = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request, extraHeaders http.Header) error {
|
||||
transport := p.Transport
|
||||
if transport == nil {
|
||||
|
|
|
@ -23,6 +23,7 @@ type staticUpstream struct {
|
|||
proxyHeaders http.Header
|
||||
Hosts HostPool
|
||||
Policy Policy
|
||||
insecureSkipVerify bool
|
||||
|
||||
FailTimeout time.Duration
|
||||
MaxFails int32
|
||||
|
@ -90,6 +91,9 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
|||
}
|
||||
if baseURL, err := url.Parse(uh.Name); err == nil {
|
||||
uh.ReverseProxy = NewSingleHostReverseProxy(baseURL, uh.WithoutPathPrefix)
|
||||
if upstream.insecureSkipVerify {
|
||||
uh.ReverseProxy.Transport = InsecureTransport
|
||||
}
|
||||
} else {
|
||||
return upstreams, err
|
||||
}
|
||||
|
@ -175,6 +179,8 @@ func parseBlock(c *parse.Dispenser, u *staticUpstream) error {
|
|||
return c.ArgErr()
|
||||
}
|
||||
u.IgnoredSubPaths = ignoredPaths
|
||||
case "insecure_skip_verify":
|
||||
u.insecureSkipVerify = true
|
||||
default:
|
||||
return c.Errf("unknown property '%s'", c.Val())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user