mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-26 02:09:47 +08:00
c9980fd367
Use piles from which to draw config values. Module values can return their name, so now we can do two-way mapping from value to name and name to value; whereas before we could only map name to value. This was problematic with the Caddyfile adapter since it receives values and needs to know the name to put in the config.
256 lines
6.0 KiB
Go
256 lines
6.0 KiB
Go
// Copyright 2015 Matthew Holt and The Caddy Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package httpcaddyfile
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"html"
|
|
"net/http"
|
|
"reflect"
|
|
|
|
"github.com/caddyserver/caddy/caddyconfig"
|
|
"github.com/caddyserver/caddy/modules/caddyhttp"
|
|
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
|
)
|
|
|
|
func init() {
|
|
RegisterDirective("bind", parseBind)
|
|
RegisterDirective("root", parseRoot)
|
|
RegisterDirective("tls", parseTLS)
|
|
RegisterHandlerDirective("redir", parseRedir)
|
|
}
|
|
|
|
func parseBind(h Helper) ([]ConfigValue, error) {
|
|
var lnHosts []string
|
|
for h.Next() {
|
|
lnHosts = append(lnHosts, h.RemainingArgs()...)
|
|
}
|
|
return h.NewBindAddresses(lnHosts), nil
|
|
}
|
|
|
|
func parseRoot(h Helper) ([]ConfigValue, error) {
|
|
if !h.Next() {
|
|
return nil, h.ArgErr()
|
|
}
|
|
|
|
matcherSet, ok, err := h.MatcherToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !ok {
|
|
// no matcher token; oops
|
|
h.Dispenser.Prev()
|
|
}
|
|
|
|
if !h.NextArg() {
|
|
return nil, h.ArgErr()
|
|
}
|
|
root := h.Val()
|
|
if h.NextArg() {
|
|
return nil, h.ArgErr()
|
|
}
|
|
|
|
varsHandler := caddyhttp.VarsMiddleware{"root": root}
|
|
route := caddyhttp.Route{
|
|
HandlersRaw: []json.RawMessage{
|
|
caddyconfig.JSONModuleObject(varsHandler, "handler", "vars", nil),
|
|
},
|
|
}
|
|
if matcherSet != nil {
|
|
route.MatcherSetsRaw = []map[string]json.RawMessage{matcherSet}
|
|
}
|
|
|
|
return h.NewVarsRoute(route), nil
|
|
}
|
|
|
|
func parseTLS(h Helper) ([]ConfigValue, error) {
|
|
var configVals []ConfigValue
|
|
|
|
cp := new(caddytls.ConnectionPolicy)
|
|
var fileLoader caddytls.FileLoader
|
|
var folderLoader caddytls.FolderLoader
|
|
var mgr caddytls.ACMEManagerMaker
|
|
var off bool
|
|
|
|
for h.Next() {
|
|
// file certificate loader
|
|
firstLine := h.RemainingArgs()
|
|
switch len(firstLine) {
|
|
case 0:
|
|
case 1:
|
|
if firstLine[0] == "off" {
|
|
off = true
|
|
} else {
|
|
mgr.Email = firstLine[0]
|
|
}
|
|
case 2:
|
|
fileLoader = append(fileLoader, caddytls.CertKeyFilePair{
|
|
Certificate: firstLine[0],
|
|
Key: firstLine[1],
|
|
// TODO: add tags, for enterprise module's certificate selection
|
|
})
|
|
default:
|
|
return nil, h.ArgErr()
|
|
}
|
|
|
|
var hasBlock bool
|
|
for h.NextBlock() {
|
|
hasBlock = true
|
|
|
|
switch h.Val() {
|
|
|
|
// connection policy
|
|
case "protocols":
|
|
args := h.RemainingArgs()
|
|
if len(args) == 0 {
|
|
return nil, h.SyntaxErr("one or two protocols")
|
|
}
|
|
if len(args) > 0 {
|
|
if _, ok := caddytls.SupportedProtocols[args[0]]; !ok {
|
|
return nil, h.Errf("Wrong protocol name or protocol not supported: '%s'", args[0])
|
|
}
|
|
cp.ProtocolMin = args[0]
|
|
}
|
|
if len(args) > 1 {
|
|
if _, ok := caddytls.SupportedProtocols[args[1]]; !ok {
|
|
return nil, h.Errf("Wrong protocol name or protocol not supported: '%s'", args[1])
|
|
}
|
|
cp.ProtocolMax = args[1]
|
|
}
|
|
case "ciphers":
|
|
for h.NextArg() {
|
|
if _, ok := caddytls.SupportedCipherSuites[h.Val()]; !ok {
|
|
return nil, h.Errf("Wrong cipher suite name or cipher suite not supported: '%s'", h.Val())
|
|
}
|
|
cp.CipherSuites = append(cp.CipherSuites, h.Val())
|
|
}
|
|
case "curves":
|
|
for h.NextArg() {
|
|
if _, ok := caddytls.SupportedCurves[h.Val()]; !ok {
|
|
return nil, h.Errf("Wrong curve name or curve not supported: '%s'", h.Val())
|
|
}
|
|
cp.Curves = append(cp.Curves, h.Val())
|
|
}
|
|
case "alpn":
|
|
args := h.RemainingArgs()
|
|
if len(args) == 0 {
|
|
return nil, h.ArgErr()
|
|
}
|
|
cp.ALPN = args
|
|
|
|
// certificate folder loader
|
|
case "load":
|
|
folderLoader = append(folderLoader, h.RemainingArgs()...)
|
|
|
|
// automation policy
|
|
case "ca":
|
|
arg := h.RemainingArgs()
|
|
if len(arg) != 1 {
|
|
return nil, h.ArgErr()
|
|
}
|
|
mgr.CA = arg[0]
|
|
|
|
// TODO: other properties for automation manager
|
|
}
|
|
}
|
|
|
|
// a naked tls directive is not allowed
|
|
if len(firstLine) == 0 && !hasBlock {
|
|
return nil, h.ArgErr()
|
|
}
|
|
}
|
|
|
|
// connection policy
|
|
configVals = append(configVals, ConfigValue{
|
|
Class: "tls.connection_policy",
|
|
Value: cp,
|
|
})
|
|
|
|
// certificate loaders
|
|
if len(fileLoader) > 0 {
|
|
configVals = append(configVals, ConfigValue{
|
|
Class: "tls.certificate_loader",
|
|
Value: fileLoader,
|
|
})
|
|
}
|
|
if len(folderLoader) > 0 {
|
|
configVals = append(configVals, ConfigValue{
|
|
Class: "tls.certificate_loader",
|
|
Value: folderLoader,
|
|
})
|
|
}
|
|
|
|
// automation policy
|
|
if off {
|
|
configVals = append(configVals, ConfigValue{
|
|
Class: "tls.off",
|
|
Value: true,
|
|
})
|
|
} else if !reflect.DeepEqual(mgr, caddytls.ACMEManagerMaker{}) {
|
|
configVals = append(configVals, ConfigValue{
|
|
Class: "tls.automation_manager",
|
|
Value: mgr,
|
|
})
|
|
}
|
|
|
|
return configVals, nil
|
|
}
|
|
|
|
func parseRedir(h Helper) (caddyhttp.MiddlewareHandler, error) {
|
|
if !h.Next() {
|
|
return nil, h.ArgErr()
|
|
}
|
|
|
|
if !h.NextArg() {
|
|
return nil, h.ArgErr()
|
|
}
|
|
to := h.Val()
|
|
|
|
var code string
|
|
if h.NextArg() {
|
|
code = h.Val()
|
|
}
|
|
if code == "permanent" {
|
|
code = "301"
|
|
}
|
|
if code == "temporary" || code == "" {
|
|
code = "307"
|
|
}
|
|
var body string
|
|
if code == "meta" {
|
|
// Script tag comes first since that will better imitate a redirect in the browser's
|
|
// history, but the meta tag is a fallback for most non-JS clients.
|
|
const metaRedir = `<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Redirecting...</title>
|
|
<script>window.location.replace("%s");</script>
|
|
<meta http-equiv="refresh" content="0; URL='%s'">
|
|
</head>
|
|
<body>Redirecting to <a href="%s">%s</a>...</body>
|
|
</html>
|
|
`
|
|
safeTo := html.EscapeString(to)
|
|
body = fmt.Sprintf(metaRedir, safeTo, safeTo, safeTo, safeTo)
|
|
}
|
|
|
|
return caddyhttp.StaticResponse{
|
|
StatusCode: caddyhttp.WeakString(code),
|
|
Headers: http.Header{"Location": []string{to}},
|
|
Body: body,
|
|
}, nil
|
|
}
|