예제 #1
0
func singleArg(c *caddy.Controller, desc string) (string, error) {
	args := c.RemainingArgs()
	if len(args) != 1 {
		return "", c.Errf("%s expects exactly one argument", desc)
	}
	return args[0], nil
}
예제 #2
0
func parse(m *module, c *caddy.Controller) (err error) {
	args := c.RemainingArgs()
	if len(args) == 1 && args[0] == "cloudflare" {
		addCloudflareIps(m)
		if c.NextBlock() {
			return c.Err("No realip subblocks allowed if using preset.")
		}
	} else if len(args) != 0 {
		return c.ArgErr()
	}
	for c.NextBlock() {
		var err error
		switch c.Val() {
		case "header":
			m.Header, err = StringArg(c)
		case "from":
			var cidr *net.IPNet
			cidr, err = CidrArg(c)
			m.From = append(m.From, cidr)
		case "strict":
			m.Strict, err = BoolArg(c)
		default:
			return c.Errf("Unknown realip arg: %s", c.Val())
		}
		if err != nil {
			return err
		}
	}
	return nil
}
예제 #3
0
파일: setup.go 프로젝트: joshix/caddy
// statusParse parses status directive
func statusParse(c *caddy.Controller) ([]httpserver.HandlerConfig, error) {
	var rules []httpserver.HandlerConfig

	for c.Next() {
		hadBlock := false
		args := c.RemainingArgs()

		switch len(args) {
		case 1:
			status, err := strconv.Atoi(args[0])
			if err != nil {
				return rules, c.Errf("Expecting a numeric status code, got '%s'", args[0])
			}

			for c.NextBlock() {
				hadBlock = true
				basePath := c.Val()

				for _, cfg := range rules {
					rule := cfg.(*Rule)
					if rule.Base == basePath {
						return rules, c.Errf("Duplicate path: '%s'", basePath)
					}
				}

				rule := NewRule(basePath, status)
				rules = append(rules, rule)

				if c.NextArg() {
					return rules, c.ArgErr()
				}
			}

			if !hadBlock {
				return rules, c.ArgErr()
			}
		case 2:
			status, err := strconv.Atoi(args[0])
			if err != nil {
				return rules, c.Errf("Expecting a numeric status code, got '%s'", args[0])
			}

			basePath := args[1]
			for _, cfg := range rules {
				rule := cfg.(*Rule)
				if rule.Base == basePath {
					return rules, c.Errf("Duplicate path: '%s'", basePath)
				}
			}

			rule := NewRule(basePath, status)
			rules = append(rules, rule)
		default:
			return rules, c.ArgErr()
		}
	}

	return rules, nil
}
예제 #4
0
func BoolArg(c *caddy.Controller) (bool, error) {
	args := c.RemainingArgs()
	if len(args) > 1 {
		return false, c.ArgErr()
	}
	if len(args) == 0 {
		return true, nil
	}
	switch args[0] {
	case "false":
		return false, nil
	case "true":
		return true, nil
	default:
		return false, c.Errf("Unexpected bool value: %s", args[0])
	}
}
예제 #5
0
파일: setup.go 프로젝트: anthonybrown/caddy
func loadParams(c *caddy.Controller, mdc *Config) error {
	cfg := httpserver.GetConfig(c.Key)

	switch c.Val() {
	case "ext":
		for _, ext := range c.RemainingArgs() {
			mdc.Extensions[ext] = struct{}{}
		}
		return nil
	case "css":
		if !c.NextArg() {
			return c.ArgErr()
		}
		mdc.Styles = append(mdc.Styles, c.Val())
		return nil
	case "js":
		if !c.NextArg() {
			return c.ArgErr()
		}
		mdc.Scripts = append(mdc.Scripts, c.Val())
		return nil
	case "template":
		tArgs := c.RemainingArgs()
		switch len(tArgs) {
		default:
			return c.ArgErr()
		case 1:
			fpath := filepath.ToSlash(filepath.Clean(cfg.Root + string(filepath.Separator) + tArgs[0]))

			if err := SetTemplate(mdc.Template, "", fpath); err != nil {
				c.Errf("default template parse error: %v", err)
			}
			return nil
		case 2:
			fpath := filepath.ToSlash(filepath.Clean(cfg.Root + string(filepath.Separator) + tArgs[1]))

			if err := SetTemplate(mdc.Template, tArgs[0], fpath); err != nil {
				c.Errf("template parse error: %v", err)
			}
			return nil
		}
	case "templatedir":
		if !c.NextArg() {
			return c.ArgErr()
		}
		_, err := mdc.Template.ParseGlob(c.Val())
		if err != nil {
			c.Errf("template load error: %v", err)
		}
		if c.NextArg() {
			return c.ArgErr()
		}
		return nil
	default:
		return c.Err("Expected valid markdown configuration property")
	}
}
예제 #6
0
파일: root.go 프로젝트: FiloSottile/caddy
func setupRoot(c *caddy.Controller) error {
	config := httpserver.GetConfig(c)

	for c.Next() {
		if !c.NextArg() {
			return c.ArgErr()
		}
		config.Root = c.Val()
	}

	// Check if root path exists
	_, err := os.Stat(config.Root)
	if err != nil {
		if os.IsNotExist(err) {
			// Allow this, because the folder might appear later.
			// But make sure the user knows!
			log.Printf("[WARNING] Root path does not exist: %s", config.Root)
		} else {
			return c.Errf("Unable to access root path '%s': %v", config.Root, err)
		}
	}

	return nil
}
예제 #7
0
파일: setup.go 프로젝트: FiloSottile/caddy
func basicAuthParse(c *caddy.Controller) ([]Rule, error) {
	var rules []Rule
	cfg := httpserver.GetConfig(c)

	var err error
	for c.Next() {
		var rule Rule

		args := c.RemainingArgs()

		switch len(args) {
		case 2:
			rule.Username = args[0]
			if rule.Password, err = passwordMatcher(rule.Username, args[1], cfg.Root); err != nil {
				return rules, c.Errf("Get password matcher from %s: %v", c.Val(), err)
			}

			for c.NextBlock() {
				rule.Resources = append(rule.Resources, c.Val())
				if c.NextArg() {
					return rules, c.Errf("Expecting only one resource per line (extra '%s')", c.Val())
				}
			}
		case 3:
			rule.Resources = append(rule.Resources, args[0])
			rule.Username = args[1]
			if rule.Password, err = passwordMatcher(rule.Username, args[2], cfg.Root); err != nil {
				return rules, c.Errf("Get password matcher from %s: %v", c.Val(), err)
			}
		default:
			return rules, c.ArgErr()
		}

		rules = append(rules, rule)
	}

	return rules, nil
}
예제 #8
0
파일: setup.go 프로젝트: FiloSottile/caddy
func errorsParse(c *caddy.Controller) (*ErrorHandler, error) {
	// Very important that we make a pointer because the startup
	// function that opens the log file must have access to the
	// same instance of the handler, not a copy.
	handler := &ErrorHandler{ErrorPages: make(map[int]string)}

	cfg := httpserver.GetConfig(c)

	optionalBlock := func() (bool, error) {
		var hadBlock bool

		for c.NextBlock() {
			hadBlock = true

			what := c.Val()
			if !c.NextArg() {
				return hadBlock, c.ArgErr()
			}
			where := c.Val()

			if what == "log" {
				if where == "visible" {
					handler.Debug = true
				} else {
					handler.LogFile = where
					if c.NextArg() {
						if c.Val() == "{" {
							c.IncrNest()
							logRoller, err := httpserver.ParseRoller(c)
							if err != nil {
								return hadBlock, err
							}
							handler.LogRoller = logRoller
						}
					}
				}
			} else {
				// Error page; ensure it exists
				where = filepath.Join(cfg.Root, where)
				f, err := os.Open(where)
				if err != nil {
					log.Printf("[WARNING] Unable to open error page '%s': %v", where, err)
				}
				f.Close()

				if what == "*" {
					if handler.GenericErrorPage != "" {
						return hadBlock, c.Errf("Duplicate status code entry: %s", what)
					}
					handler.GenericErrorPage = where
				} else {
					whatInt, err := strconv.Atoi(what)
					if err != nil {
						return hadBlock, c.Err("Expecting a numeric status code or '*', got '" + what + "'")
					}

					if _, exists := handler.ErrorPages[whatInt]; exists {
						return hadBlock, c.Errf("Duplicate status code entry: %s", what)
					}

					handler.ErrorPages[whatInt] = where
				}
			}
		}
		return hadBlock, nil
	}

	for c.Next() {
		// weird hack to avoid having the handler values overwritten.
		if c.Val() == "}" {
			continue
		}
		// Configuration may be in a block
		hadBlock, err := optionalBlock()
		if err != nil {
			return handler, err
		}

		// Otherwise, the only argument would be an error log file name or 'visible'
		if !hadBlock {
			if c.NextArg() {
				if c.Val() == "visible" {
					handler.Debug = true
				} else {
					handler.LogFile = c.Val()
				}
			}
		}
	}

	return handler, nil
}
예제 #9
0
파일: setup.go 프로젝트: FiloSottile/caddy
// setupTLS sets up the TLS configuration and installs certificates that
// are specified by the user in the config file. All the automatic HTTPS
// stuff comes later outside of this function.
func setupTLS(c *caddy.Controller) error {
	configGetter, ok := configGetters[c.ServerType()]
	if !ok {
		return fmt.Errorf("no caddytls.ConfigGetter for %s server type; must call RegisterConfigGetter", c.ServerType())
	}
	config := configGetter(c)
	if config == nil {
		return fmt.Errorf("no caddytls.Config to set up for %s", c.Key)
	}

	config.Enabled = true

	for c.Next() {
		var certificateFile, keyFile, loadDir, maxCerts string

		args := c.RemainingArgs()
		switch len(args) {
		case 1:
			// even if the email is one of the special values below,
			// it is still necessary for future analysis that we store
			// that value in the ACMEEmail field.
			config.ACMEEmail = args[0]

			// user can force-disable managed TLS this way
			if args[0] == "off" {
				config.Enabled = false
				return nil
			}

			// user might want a temporary, in-memory, self-signed cert
			if args[0] == "self_signed" {
				config.SelfSigned = true
			}
		case 2:
			certificateFile = args[0]
			keyFile = args[1]
			config.Manual = true
		}

		// Optional block with extra parameters
		var hadBlock bool
		for c.NextBlock() {
			hadBlock = true
			switch c.Val() {
			case "key_type":
				arg := c.RemainingArgs()
				value, ok := supportedKeyTypes[strings.ToUpper(arg[0])]
				if !ok {
					return c.Errf("Wrong key type name or key type not supported: '%s'", c.Val())
				}
				config.KeyType = value
			case "protocols":
				args := c.RemainingArgs()
				if len(args) == 1 {
					value, ok := supportedProtocols[strings.ToLower(args[0])]
					if !ok {
						return c.Errf("Wrong protocol name or protocol not supported: '%s'", args[0])
					}

					config.ProtocolMinVersion, config.ProtocolMaxVersion = value, value
				} else {
					value, ok := supportedProtocols[strings.ToLower(args[0])]
					if !ok {
						return c.Errf("Wrong protocol name or protocol not supported: '%s'", args[0])
					}
					config.ProtocolMinVersion = value
					value, ok = supportedProtocols[strings.ToLower(args[1])]
					if !ok {
						return c.Errf("Wrong protocol name or protocol not supported: '%s'", args[1])
					}
					config.ProtocolMaxVersion = value
					if config.ProtocolMinVersion > config.ProtocolMaxVersion {
						return c.Errf("Minimum protocol version cannot be higher than maximum (reverse the order)")
					}
				}
			case "ciphers":
				for c.NextArg() {
					value, ok := supportedCiphersMap[strings.ToUpper(c.Val())]
					if !ok {
						return c.Errf("Wrong cipher name or cipher not supported: '%s'", c.Val())
					}
					config.Ciphers = append(config.Ciphers, value)
				}
			case "clients":
				clientCertList := c.RemainingArgs()
				if len(clientCertList) == 0 {
					return c.ArgErr()
				}

				listStart, mustProvideCA := 1, true
				switch clientCertList[0] {
				case "request":
					config.ClientAuth = tls.RequestClientCert
					mustProvideCA = false
				case "require":
					config.ClientAuth = tls.RequireAnyClientCert
					mustProvideCA = false
				case "verify_if_given":
					config.ClientAuth = tls.VerifyClientCertIfGiven
				default:
					config.ClientAuth = tls.RequireAndVerifyClientCert
					listStart = 0
				}
				if mustProvideCA && len(clientCertList) <= listStart {
					return c.ArgErr()
				}

				config.ClientCerts = clientCertList[listStart:]
			case "load":
				c.Args(&loadDir)
				config.Manual = true
			case "max_certs":
				c.Args(&maxCerts)
				config.OnDemand = true
			case "dns":
				args := c.RemainingArgs()
				if len(args) != 1 {
					return c.ArgErr()
				}
				dnsProvName := args[0]
				if _, ok := dnsProviders[dnsProvName]; !ok {
					return c.Errf("Unsupported DNS provider '%s'", args[0])
				}
				config.DNSProvider = args[0]
			case "storage":
				args := c.RemainingArgs()
				if len(args) != 1 {
					return c.ArgErr()
				}
				storageProvName := args[0]
				if _, ok := storageProviders[storageProvName]; !ok {
					return c.Errf("Unsupported Storage provider '%s'", args[0])
				}
				config.StorageProvider = args[0]
			default:
				return c.Errf("Unknown keyword '%s'", c.Val())
			}
		}

		// tls requires at least one argument if a block is not opened
		if len(args) == 0 && !hadBlock {
			return c.ArgErr()
		}

		// set certificate limit if on-demand TLS is enabled
		if maxCerts != "" {
			maxCertsNum, err := strconv.Atoi(maxCerts)
			if err != nil || maxCertsNum < 1 {
				return c.Err("max_certs must be a positive integer")
			}
			config.OnDemandState.MaxObtain = int32(maxCertsNum)
		}

		// don't try to load certificates unless we're supposed to
		if !config.Enabled || !config.Manual {
			continue
		}

		// load a single certificate and key, if specified
		if certificateFile != "" && keyFile != "" {
			err := cacheUnmanagedCertificatePEMFile(certificateFile, keyFile)
			if err != nil {
				return c.Errf("Unable to load certificate and key files for '%s': %v", c.Key, err)
			}
			log.Printf("[INFO] Successfully loaded TLS assets from %s and %s", certificateFile, keyFile)
		}

		// load a directory of certificates, if specified
		if loadDir != "" {
			err := loadCertsInDir(c, loadDir)
			if err != nil {
				return err
			}
		}
	}

	SetDefaultTLSParams(config)

	// generate self-signed cert if needed
	if config.SelfSigned {
		err := makeSelfSignedCert(config)
		if err != nil {
			return fmt.Errorf("self-signed: %v", err)
		}
	}

	return nil
}
예제 #10
0
파일: setup.go 프로젝트: FiloSottile/caddy
// loadCertsInDir loads all the certificates/keys in dir, as long as
// the file ends with .pem. This method of loading certificates is
// modeled after haproxy, which expects the certificate and key to
// be bundled into the same file:
// https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#5.1-crt
//
// This function may write to the log as it walks the directory tree.
func loadCertsInDir(c *caddy.Controller, dir string) error {
	return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Printf("[WARNING] Unable to traverse into %s; skipping", path)
			return nil
		}
		if info.IsDir() {
			return nil
		}
		if strings.HasSuffix(strings.ToLower(info.Name()), ".pem") {
			certBuilder, keyBuilder := new(bytes.Buffer), new(bytes.Buffer)
			var foundKey bool // use only the first key in the file

			bundle, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			for {
				// Decode next block so we can see what type it is
				var derBlock *pem.Block
				derBlock, bundle = pem.Decode(bundle)
				if derBlock == nil {
					break
				}

				if derBlock.Type == "CERTIFICATE" {
					// Re-encode certificate as PEM, appending to certificate chain
					pem.Encode(certBuilder, derBlock)
				} else if derBlock.Type == "EC PARAMETERS" {
					// EC keys generated from openssl can be composed of two blocks:
					// parameters and key (parameter block should come first)
					if !foundKey {
						// Encode parameters
						pem.Encode(keyBuilder, derBlock)

						// Key must immediately follow
						derBlock, bundle = pem.Decode(bundle)
						if derBlock == nil || derBlock.Type != "EC PRIVATE KEY" {
							return c.Errf("%s: expected elliptic private key to immediately follow EC parameters", path)
						}
						pem.Encode(keyBuilder, derBlock)
						foundKey = true
					}
				} else if derBlock.Type == "PRIVATE KEY" || strings.HasSuffix(derBlock.Type, " PRIVATE KEY") {
					// RSA key
					if !foundKey {
						pem.Encode(keyBuilder, derBlock)
						foundKey = true
					}
				} else {
					return c.Errf("%s: unrecognized PEM block type: %s", path, derBlock.Type)
				}
			}

			certPEMBytes, keyPEMBytes := certBuilder.Bytes(), keyBuilder.Bytes()
			if len(certPEMBytes) == 0 {
				return c.Errf("%s: failed to parse PEM data", path)
			}
			if len(keyPEMBytes) == 0 {
				return c.Errf("%s: no private key block found", path)
			}

			err = cacheUnmanagedCertificatePEMBytes(certPEMBytes, keyPEMBytes)
			if err != nil {
				return c.Errf("%s: failed to load cert and key for '%s': %v", path, c.Key, err)
			}
			log.Printf("[INFO] Successfully loaded TLS assets from %s", path)
		}
		return nil
	})
}
예제 #11
0
파일: setup.go 프로젝트: anthonybrown/caddy
func redirParse(c *caddy.Controller) ([]Rule, error) {
	var redirects []Rule

	cfg := httpserver.GetConfig(c.Key)

	// setRedirCode sets the redirect code for rule if it can, or returns an error
	setRedirCode := func(code string, rule *Rule) error {
		if code == "meta" {
			rule.Meta = true
		} else if codeNumber, ok := httpRedirs[code]; ok {
			rule.Code = codeNumber
		} else {
			return c.Errf("Invalid redirect code '%v'", code)
		}
		return nil
	}

	// checkAndSaveRule checks the rule for validity (except the redir code)
	// and saves it if it's valid, or returns an error.
	checkAndSaveRule := func(rule Rule) error {
		if rule.FromPath == rule.To {
			return c.Err("'from' and 'to' values of redirect rule cannot be the same")
		}

		for _, otherRule := range redirects {
			if otherRule.FromPath == rule.FromPath {
				return c.Errf("rule with duplicate 'from' value: %s -> %s", otherRule.FromPath, otherRule.To)
			}
		}

		redirects = append(redirects, rule)
		return nil
	}

	for c.Next() {
		args := c.RemainingArgs()

		var hadOptionalBlock bool
		for c.NextBlock() {
			hadOptionalBlock = true

			var rule Rule

			if cfg.TLS.Enabled {
				rule.FromScheme = "https"
			} else {
				rule.FromScheme = "http"
			}

			// Set initial redirect code
			// BUG: If the code is specified for a whole block and that code is invalid,
			// the line number will appear on the first line inside the block, even if that
			// line overwrites the block-level code with a valid redirect code. The program
			// still functions correctly, but the line number in the error reporting is
			// misleading to the user.
			if len(args) == 1 {
				err := setRedirCode(args[0], &rule)
				if err != nil {
					return redirects, err
				}
			} else {
				rule.Code = http.StatusMovedPermanently // default code
			}

			// RemainingArgs only gets the values after the current token, but in our
			// case we want to include the current token to get an accurate count.
			insideArgs := append([]string{c.Val()}, c.RemainingArgs()...)

			switch len(insideArgs) {
			case 1:
				// To specified (catch-all redirect)
				// Not sure why user is doing this in a table, as it causes all other redirects to be ignored.
				// As such, this feature remains undocumented.
				rule.FromPath = "/"
				rule.To = insideArgs[0]
			case 2:
				// From and To specified
				rule.FromPath = insideArgs[0]
				rule.To = insideArgs[1]
			case 3:
				// From, To, and Code specified
				rule.FromPath = insideArgs[0]
				rule.To = insideArgs[1]
				err := setRedirCode(insideArgs[2], &rule)
				if err != nil {
					return redirects, err
				}
			default:
				return redirects, c.ArgErr()
			}

			err := checkAndSaveRule(rule)
			if err != nil {
				return redirects, err
			}
		}

		if !hadOptionalBlock {
			var rule Rule

			if cfg.TLS.Enabled {
				rule.FromScheme = "https"
			} else {
				rule.FromScheme = "http"
			}

			rule.Code = http.StatusMovedPermanently // default

			switch len(args) {
			case 1:
				// To specified (catch-all redirect)
				rule.FromPath = "/"
				rule.To = args[0]
			case 2:
				// To and Code specified (catch-all redirect)
				rule.FromPath = "/"
				rule.To = args[0]
				err := setRedirCode(args[1], &rule)
				if err != nil {
					return redirects, err
				}
			case 3:
				// From, To, and Code specified
				rule.FromPath = args[0]
				rule.To = args[1]
				err := setRedirCode(args[2], &rule)
				if err != nil {
					return redirects, err
				}
			default:
				return redirects, c.ArgErr()
			}

			err := checkAndSaveRule(rule)
			if err != nil {
				return redirects, err
			}
		}
	}

	return redirects, nil
}
예제 #12
0
func parseRules(c *caddy.Controller) ([]*corsRule, error) {
	rules := []*corsRule{}

	for c.Next() {
		rule := &corsRule{Path: "/", Conf: cors.Default()}
		args := c.RemainingArgs()

		anyOrigins := false
		if len(args) > 0 {
			rule.Path = args[0]
		}
		for i := 1; i < len(args); i++ {
			if !anyOrigins {
				rule.Conf.AllowedOrigins = nil
			}
			rule.Conf.AllowedOrigins = append(rule.Conf.AllowedOrigins, strings.Split(args[i], ",")...)
			anyOrigins = true
		}
		for c.NextBlock() {
			switch c.Val() {
			case "origin":
				if !anyOrigins {
					rule.Conf.AllowedOrigins = nil
				}
				args := c.RemainingArgs()
				for _, domain := range args {
					rule.Conf.AllowedOrigins = append(rule.Conf.AllowedOrigins, strings.Split(domain, ",")...)
				}
				anyOrigins = true
			case "methods":
				if arg, err := singleArg(c, "methods"); err != nil {
					return nil, err
				} else {
					rule.Conf.AllowedMethods = arg
				}
			case "allow_credentials":
				if arg, err := singleArg(c, "allow_credentials"); err != nil {
					return nil, err
				} else {
					var b bool
					if arg == "true" {
						b = true
					} else if arg != "false" {
						return nil, c.Errf("allow_credentials must be true or false.")
					}
					rule.Conf.AllowCredentials = &b
				}
			case "max_age":
				if arg, err := singleArg(c, "max_age"); err != nil {
					return nil, err
				} else {
					i, err := strconv.Atoi(arg)
					if err != nil {
						return nil, c.Err("max_age must be valid int")
					}
					rule.Conf.MaxAge = i
				}
			case "allowed_headers":
				if arg, err := singleArg(c, "allowed_headers"); err != nil {
					return nil, err
				} else {
					rule.Conf.AllowedHeaders = arg
				}
			case "exposed_headers":
				if arg, err := singleArg(c, "exposed_headers"); err != nil {
					return nil, err
				} else {
					rule.Conf.ExposedHeaders = arg
				}
			default:
				return nil, c.Errf("Unknown cors config item: %s", c.Val())
			}
		}
		rules = append(rules, rule)
	}
	return rules, nil
}
예제 #13
0
파일: setup.go 프로젝트: abiosoft/caddy-git
func parse(c *caddy.Controller) (Git, error) {
	var git Git

	config := httpserver.GetConfig(c)
	for c.Next() {
		repo := &Repo{Branch: "master", Interval: DefaultInterval, Path: config.Root}

		args := c.RemainingArgs()

		clonePath := func(s string) string {
			if filepath.IsAbs(s) {
				return filepath.Clean(s)
			}
			return filepath.Join(config.Root, s)
		}

		switch len(args) {
		case 2:
			repo.Path = clonePath(args[1])
			fallthrough
		case 1:
			u, err := validateURL(args[0])
			if err != nil {
				return nil, err
			}
			repo.URL = u
		}

		for c.NextBlock() {
			switch c.Val() {
			case "repo":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				u, err := validateURL(c.Val())
				if err != nil {
					return nil, err
				}
				repo.URL = u
			case "path":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				repo.Path = clonePath(c.Val())
			case "branch":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				repo.Branch = c.Val()
			case "key":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				repo.KeyPath = c.Val()
			case "interval":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				t, _ := strconv.Atoi(c.Val())
				if t > 0 {
					repo.Interval = time.Duration(t) * time.Second
				}
			case "args":
				repo.Args = c.RemainingArgs()
			case "hook":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				repo.Hook.URL = c.Val()

				// optional secret for validation
				if c.NextArg() {
					repo.Hook.Secret = c.Val()
				}
			case "hook_type":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				t := c.Val()
				if _, ok := handlers[t]; !ok {
					return nil, c.Errf("invalid hook type %v", t)
				}
				repo.Hook.Type = t
			case "then":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				command := c.Val()
				args := c.RemainingArgs()
				repo.Then = append(repo.Then, NewThen(command, args...))
			case "then_long":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				command := c.Val()
				args := c.RemainingArgs()
				repo.Then = append(repo.Then, NewLongThen(command, args...))
			default:
				return nil, c.ArgErr()
			}
		}

		// if repo is not specified, return error
		if repo.URL == "" {
			return nil, c.ArgErr()
		}

		// if private key is not specified, convert repository URL to https
		// to avoid ssh authentication
		// else validate git URL
		// Note: private key support not yet available on Windows
		var err error
		if repo.KeyPath == "" {
			repo.URL, repo.Host, err = sanitizeHTTP(repo.URL)
		} else {
			repo.URL, repo.Host, err = sanitizeSSH(repo.URL)
			// TODO add Windows support for private repos
			if runtime.GOOS == "windows" {
				return nil, fmt.Errorf("private repository not yet supported on Windows")
			}
		}

		if err != nil {
			return nil, err
		}

		// validate git requirements
		if err = Init(); err != nil {
			return nil, err
		}

		// prepare repo for use
		if err = repo.Prepare(); err != nil {
			return nil, err
		}

		git = append(git, repo)

	}

	return git, nil
}
예제 #14
0
파일: setup.go 프로젝트: FiloSottile/caddy
func redirParse(c *caddy.Controller) ([]Rule, error) {
	var redirects []Rule

	cfg := httpserver.GetConfig(c)

	initRule := func(rule *Rule, defaultCode string, args []string) error {
		if cfg.TLS.Enabled {
			rule.FromScheme = "https"
		} else {
			rule.FromScheme = "http"
		}

		var (
			from = "/"
			to   string
			code = defaultCode
		)
		switch len(args) {
		case 1:
			// To specified (catch-all redirect)
			// Not sure why user is doing this in a table, as it causes all other redirects to be ignored.
			// As such, this feature remains undocumented.
			to = args[0]
		case 2:
			// From and To specified
			from = args[0]
			to = args[1]
		case 3:
			// From, To, and Code specified
			from = args[0]
			to = args[1]
			code = args[2]
		default:
			return c.ArgErr()
		}

		rule.FromPath = from
		rule.To = to
		if code == "meta" {
			rule.Meta = true
			code = defaultCode
		}
		if codeNumber, ok := httpRedirs[code]; ok {
			rule.Code = codeNumber
		} else {
			return c.Errf("Invalid redirect code '%v'", code)
		}

		return nil
	}

	// checkAndSaveRule checks the rule for validity (except the redir code)
	// and saves it if it's valid, or returns an error.
	checkAndSaveRule := func(rule Rule) error {
		if rule.FromPath == rule.To {
			return c.Err("'from' and 'to' values of redirect rule cannot be the same")
		}

		for _, otherRule := range redirects {
			if otherRule.FromPath == rule.FromPath {
				return c.Errf("rule with duplicate 'from' value: %s -> %s", otherRule.FromPath, otherRule.To)
			}
		}

		redirects = append(redirects, rule)
		return nil
	}

	const initDefaultCode = "301"

	for c.Next() {
		args := c.RemainingArgs()
		matcher, err := httpserver.SetupIfMatcher(c)
		if err != nil {
			return nil, err
		}

		var hadOptionalBlock bool
		for c.NextBlock() {
			if httpserver.IfMatcherKeyword(c) {
				continue
			}

			hadOptionalBlock = true

			rule := Rule{
				RequestMatcher: matcher,
			}

			defaultCode := initDefaultCode
			// Set initial redirect code
			if len(args) == 1 {
				defaultCode = args[0]
			}

			// RemainingArgs only gets the values after the current token, but in our
			// case we want to include the current token to get an accurate count.
			insideArgs := append([]string{c.Val()}, c.RemainingArgs()...)
			err := initRule(&rule, defaultCode, insideArgs)
			if err != nil {
				return redirects, err
			}

			err = checkAndSaveRule(rule)
			if err != nil {
				return redirects, err
			}
		}

		if !hadOptionalBlock {
			rule := Rule{
				RequestMatcher: matcher,
			}
			err := initRule(&rule, initDefaultCode, args)
			if err != nil {
				return redirects, err
			}

			err = checkAndSaveRule(rule)
			if err != nil {
				return redirects, err
			}
		}
	}

	return redirects, nil
}
예제 #15
0
파일: setup.go 프로젝트: DenBeke/caddy
func fastcgiParse(c *caddy.Controller) ([]Rule, error) {
	var rules []Rule

	for c.Next() {
		var rule Rule

		args := c.RemainingArgs()

		if len(args) < 2 || len(args) > 3 {
			return rules, c.ArgErr()
		}

		rule.Path = args[0]
		upstreams := []string{args[1]}

		if len(args) == 3 {
			if err := fastcgiPreset(args[2], &rule); err != nil {
				return rules, err
			}
		}

		var dialers []dialer
		var poolSize = -1

		for c.NextBlock() {
			switch c.Val() {
			case "ext":
				if !c.NextArg() {
					return rules, c.ArgErr()
				}
				rule.Ext = c.Val()
			case "split":
				if !c.NextArg() {
					return rules, c.ArgErr()
				}
				rule.SplitPath = c.Val()
			case "index":
				args := c.RemainingArgs()
				if len(args) == 0 {
					return rules, c.ArgErr()
				}
				rule.IndexFiles = args

			case "upstream":
				args := c.RemainingArgs()

				if len(args) != 1 {
					return rules, c.ArgErr()
				}

				upstreams = append(upstreams, args[0])
			case "env":
				envArgs := c.RemainingArgs()
				if len(envArgs) < 2 {
					return rules, c.ArgErr()
				}
				rule.EnvVars = append(rule.EnvVars, [2]string{envArgs[0], envArgs[1]})
			case "except":
				ignoredPaths := c.RemainingArgs()
				if len(ignoredPaths) == 0 {
					return rules, c.ArgErr()
				}
				rule.IgnoredSubPaths = ignoredPaths

			case "pool":
				if !c.NextArg() {
					return rules, c.ArgErr()
				}
				pool, err := strconv.Atoi(c.Val())
				if err != nil {
					return rules, err
				}
				if pool >= 0 {
					poolSize = pool
				} else {
					return rules, c.Errf("positive integer expected, found %d", pool)
				}
			}
		}

		for _, rawAddress := range upstreams {
			network, address := parseAddress(rawAddress)
			if poolSize >= 0 {
				dialers = append(dialers, &persistentDialer{size: poolSize, network: network, address: address})
			} else {
				dialers = append(dialers, basicDialer{network: network, address: address})
			}
		}

		rule.dialer = &loadBalancingDialer{dialers: dialers}
		rule.Address = strings.Join(upstreams, ",")
		rules = append(rules, rule)
	}

	return rules, nil
}
예제 #16
0
파일: setup.go 프로젝트: ollie314/caddy
func fastcgiParse(c *caddy.Controller) ([]Rule, error) {
	var rules []Rule

	for c.Next() {
		var rule Rule

		args := c.RemainingArgs()

		switch len(args) {
		case 0:
			return rules, c.ArgErr()
		case 1:
			rule.Path = "/"
			rule.Address = args[0]
		case 2:
			rule.Path = args[0]
			rule.Address = args[1]
		case 3:
			rule.Path = args[0]
			rule.Address = args[1]
			err := fastcgiPreset(args[2], &rule)
			if err != nil {
				return rules, c.Err("Invalid fastcgi rule preset '" + args[2] + "'")
			}
		}

		network, address := parseAddress(rule.Address)
		rule.dialer = basicDialer{network: network, address: address}

		for c.NextBlock() {
			switch c.Val() {
			case "ext":
				if !c.NextArg() {
					return rules, c.ArgErr()
				}
				rule.Ext = c.Val()
			case "split":
				if !c.NextArg() {
					return rules, c.ArgErr()
				}
				rule.SplitPath = c.Val()
			case "index":
				args := c.RemainingArgs()
				if len(args) == 0 {
					return rules, c.ArgErr()
				}
				rule.IndexFiles = args
			case "env":
				envArgs := c.RemainingArgs()
				if len(envArgs) < 2 {
					return rules, c.ArgErr()
				}
				rule.EnvVars = append(rule.EnvVars, [2]string{envArgs[0], envArgs[1]})
			case "except":
				ignoredPaths := c.RemainingArgs()
				if len(ignoredPaths) == 0 {
					return rules, c.ArgErr()
				}
				rule.IgnoredSubPaths = ignoredPaths
			case "pool":
				if !c.NextArg() {
					return rules, c.ArgErr()
				}
				pool, err := strconv.Atoi(c.Val())
				if err != nil {
					return rules, err
				}
				if pool >= 0 {
					rule.dialer = &persistentDialer{size: pool, network: network, address: address}
				} else {
					return rules, c.Errf("positive integer expected, found %d", pool)
				}
			}
		}

		rules = append(rules, rule)
	}

	return rules, nil
}