mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-29 08:29:57 +00:00
TUN-528: Move cloudflared into a separate repo
This commit is contained in:
310
vendor/github.com/mholt/caddy/caddytls/tls.go
generated
vendored
Normal file
310
vendor/github.com/mholt/caddy/caddytls/tls.go
generated
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// 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 caddytls facilitates the management of TLS assets and integrates
|
||||
// Let's Encrypt functionality into Caddy with first-class support for
|
||||
// creating and renewing certificates automatically. It also implements
|
||||
// the tls directive.
|
||||
//
|
||||
// This package is meant to be used by Caddy server types. To use the
|
||||
// tls directive, a server type must import this package and call
|
||||
// RegisterConfigGetter(). The server type must make and keep track of
|
||||
// the caddytls.Config structs that this package produces. It must also
|
||||
// add tls to its list of directives. When it comes time to make the
|
||||
// server instances, the server type can call MakeTLSConfig() to convert
|
||||
// a []caddytls.Config to a single tls.Config for use in tls.NewListener().
|
||||
// It is also recommended to call RotateSessionTicketKeys() when
|
||||
// starting a new listener.
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// HostQualifies returns true if the hostname alone
|
||||
// appears eligible for automatic HTTPS. For example:
|
||||
// localhost, empty hostname, and IP addresses are
|
||||
// not eligible because we cannot obtain certificates
|
||||
// for those names. Wildcard names are allowed, as long
|
||||
// as they conform to CABF requirements (only one wildcard
|
||||
// label, and it must be the left-most label).
|
||||
func HostQualifies(hostname string) bool {
|
||||
return hostname != "localhost" && // localhost is ineligible
|
||||
|
||||
// hostname must not be empty
|
||||
strings.TrimSpace(hostname) != "" &&
|
||||
|
||||
// only one wildcard label allowed, and it must be left-most
|
||||
(!strings.Contains(hostname, "*") ||
|
||||
(strings.Count(hostname, "*") == 1 &&
|
||||
strings.HasPrefix(hostname, "*."))) &&
|
||||
|
||||
// must not start or end with a dot
|
||||
!strings.HasPrefix(hostname, ".") &&
|
||||
!strings.HasSuffix(hostname, ".") &&
|
||||
|
||||
// cannot be an IP address, see
|
||||
// https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt
|
||||
net.ParseIP(hostname) == nil
|
||||
}
|
||||
|
||||
// saveCertResource saves the certificate resource to disk. This
|
||||
// includes the certificate file itself, the private key, and the
|
||||
// metadata file.
|
||||
func saveCertResource(storage Storage, cert acme.CertificateResource) error {
|
||||
// Save cert, private key, and metadata
|
||||
siteData := &SiteData{
|
||||
Cert: cert.Certificate,
|
||||
Key: cert.PrivateKey,
|
||||
}
|
||||
var err error
|
||||
siteData.Meta, err = json.MarshalIndent(&cert, "", "\t")
|
||||
if err == nil {
|
||||
err = storage.StoreSite(cert.Domain, siteData)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Revoke revokes the certificate for host via ACME protocol.
|
||||
// It assumes the certificate was obtained from the
|
||||
// CA at DefaultCAUrl.
|
||||
func Revoke(host string) error {
|
||||
client, err := newACMEClient(new(Config), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.Revoke(host)
|
||||
}
|
||||
|
||||
// TODO: tls-sni challenge was removed in January 2018, but a variant of it might return
|
||||
// // tlsSNISolver is a type that can solve TLS-SNI challenges using
|
||||
// // an existing listener and our custom, in-memory certificate cache.
|
||||
// type tlsSNISolver struct {
|
||||
// certCache *certificateCache
|
||||
// }
|
||||
|
||||
// // Present adds the challenge certificate to the cache.
|
||||
// func (s tlsSNISolver) Present(domain, token, keyAuth string) error {
|
||||
// cert, acmeDomain, err := acme.TLSSNI01ChallengeCert(keyAuth)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// certHash := hashCertificateChain(cert.Certificate)
|
||||
// s.certCache.Lock()
|
||||
// s.certCache.cache[acmeDomain] = Certificate{
|
||||
// Certificate: cert,
|
||||
// Names: []string{acmeDomain},
|
||||
// Hash: certHash, // perhaps not necesssary
|
||||
// }
|
||||
// s.certCache.Unlock()
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // CleanUp removes the challenge certificate from the cache.
|
||||
// func (s tlsSNISolver) CleanUp(domain, token, keyAuth string) error {
|
||||
// _, acmeDomain, err := acme.TLSSNI01ChallengeCert(keyAuth)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// s.certCache.Lock()
|
||||
// delete(s.certCache.cache, acmeDomain)
|
||||
// s.certCache.Unlock()
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// distributedHTTPSolver allows the HTTP-01 challenge to be solved by
|
||||
// an instance other than the one which initiated it. This is useful
|
||||
// behind load balancers or in other cluster/fleet configurations.
|
||||
// The only requirement is that this (the initiating) instance share
|
||||
// the $CADDYPATH/acme folder with the instance that will complete
|
||||
// the challenge. Mounting the folder locally should be sufficient.
|
||||
//
|
||||
// Obviously, the instance which completes the challenge must be
|
||||
// serving on the HTTPChallengePort to receive and handle the request.
|
||||
// The HTTP server which receives it must check if a file exists, e.g.:
|
||||
// $CADDYPATH/acme/challenge_tokens/example.com.json, and if so,
|
||||
// decode it and use it to serve up the correct response. Caddy's HTTP
|
||||
// server does this by default.
|
||||
//
|
||||
// So as long as the folder is shared, this will just work. There are
|
||||
// no other requirements. The instances may be on other machines or
|
||||
// even other networks, as long as they share the folder as part of
|
||||
// the local file system.
|
||||
//
|
||||
// This solver works by persisting the token and keyauth information
|
||||
// to disk in the shared folder when the authorization is presented,
|
||||
// and then deletes it when it is cleaned up.
|
||||
type distributedHTTPSolver struct {
|
||||
// The distributed HTTPS solver only works if an instance (either
|
||||
// this one or another one) is already listening and serving on the
|
||||
// HTTPChallengePort. If not -- for example: if this is the only
|
||||
// instance, and it is just starting up and hasn't started serving
|
||||
// yet -- then we still need a listener open with an HTTP server
|
||||
// to handle the challenge request. Set this field to have the
|
||||
// standard HTTPProviderServer open its listener for the duration
|
||||
// of the challenge. Make sure to configure its listen address
|
||||
// correctly.
|
||||
httpProviderServer *acme.HTTPProviderServer
|
||||
}
|
||||
|
||||
type challengeInfo struct {
|
||||
Domain, Token, KeyAuth string
|
||||
}
|
||||
|
||||
// Present adds the challenge certificate to the cache.
|
||||
func (dhs distributedHTTPSolver) Present(domain, token, keyAuth string) error {
|
||||
if dhs.httpProviderServer != nil {
|
||||
err := dhs.httpProviderServer.Present(domain, token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("presenting with standard HTTP provider server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
err := os.MkdirAll(dhs.challengeTokensBasePath(), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
infoBytes, err := json.Marshal(challengeInfo{
|
||||
Domain: domain,
|
||||
Token: token,
|
||||
KeyAuth: keyAuth,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(dhs.challengeTokensPath(domain), infoBytes, 0644)
|
||||
}
|
||||
|
||||
// CleanUp removes the challenge certificate from the cache.
|
||||
func (dhs distributedHTTPSolver) CleanUp(domain, token, keyAuth string) error {
|
||||
if dhs.httpProviderServer != nil {
|
||||
err := dhs.httpProviderServer.CleanUp(domain, token, keyAuth)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Cleaning up standard HTTP provider server: %v", err)
|
||||
}
|
||||
}
|
||||
return os.Remove(dhs.challengeTokensPath(domain))
|
||||
}
|
||||
|
||||
func (dhs distributedHTTPSolver) challengeTokensPath(domain string) string {
|
||||
domainFile := strings.Replace(strings.ToLower(domain), "*", "wildcard_", -1)
|
||||
return filepath.Join(dhs.challengeTokensBasePath(), domainFile+".json")
|
||||
}
|
||||
|
||||
func (dhs distributedHTTPSolver) challengeTokensBasePath() string {
|
||||
return filepath.Join(caddy.AssetsPath(), "acme", "challenge_tokens")
|
||||
}
|
||||
|
||||
// ConfigHolder is any type that has a Config; it presumably is
|
||||
// connected to a hostname and port on which it is serving.
|
||||
type ConfigHolder interface {
|
||||
TLSConfig() *Config
|
||||
Host() string
|
||||
Port() string
|
||||
}
|
||||
|
||||
// QualifiesForManagedTLS returns true if c qualifies for
|
||||
// for managed TLS (but not on-demand TLS specifically).
|
||||
// It does NOT check to see if a cert and key already exist
|
||||
// for the config. If the return value is true, you should
|
||||
// be OK to set c.TLSConfig().Managed to true; then you should
|
||||
// check that value in the future instead, because the process
|
||||
// of setting up the config may make it look like it doesn't
|
||||
// qualify even though it originally did.
|
||||
func QualifiesForManagedTLS(c ConfigHolder) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
tlsConfig := c.TLSConfig()
|
||||
if tlsConfig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return (!tlsConfig.Manual || tlsConfig.OnDemand) && // user might provide own cert and key
|
||||
|
||||
// if self-signed, we've already generated one to use
|
||||
!tlsConfig.SelfSigned &&
|
||||
|
||||
// user can force-disable managed TLS
|
||||
c.Port() != "80" &&
|
||||
tlsConfig.ACMEEmail != "off" &&
|
||||
|
||||
// we get can't certs for some kinds of hostnames, but
|
||||
// on-demand TLS allows empty hostnames at startup
|
||||
(HostQualifies(c.Host()) || tlsConfig.OnDemand)
|
||||
}
|
||||
|
||||
// ChallengeProvider defines an own type that should be used in Caddy plugins
|
||||
// over acme.ChallengeProvider. Using acme.ChallengeProvider causes version mismatches
|
||||
// with vendored dependencies (see https://github.com/mattfarina/golang-broken-vendor)
|
||||
//
|
||||
// acme.ChallengeProvider is an interface that allows the implementation of custom
|
||||
// challenge providers. For more details, see:
|
||||
// https://godoc.org/github.com/xenolf/lego/acme#ChallengeProvider
|
||||
type ChallengeProvider acme.ChallengeProvider
|
||||
|
||||
// DNSProviderConstructor is a function that takes credentials and
|
||||
// returns a type that can solve the ACME DNS challenges.
|
||||
type DNSProviderConstructor func(credentials ...string) (ChallengeProvider, error)
|
||||
|
||||
// dnsProviders is the list of DNS providers that have been plugged in.
|
||||
var dnsProviders = make(map[string]DNSProviderConstructor)
|
||||
|
||||
// RegisterDNSProvider registers provider by name for solving the ACME DNS challenge.
|
||||
func RegisterDNSProvider(name string, provider DNSProviderConstructor) {
|
||||
dnsProviders[name] = provider
|
||||
caddy.RegisterPlugin("tls.dns."+name, caddy.Plugin{})
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultEmail represents the Let's Encrypt account email to use if none provided.
|
||||
DefaultEmail string
|
||||
|
||||
// Agreed indicates whether user has agreed to the Let's Encrypt SA.
|
||||
Agreed bool
|
||||
|
||||
// DefaultCAUrl is the default URL to the CA's ACME directory endpoint.
|
||||
// It's very important to set this unless you set it in every Config.
|
||||
DefaultCAUrl string
|
||||
|
||||
// DefaultKeyType is used as the type of key for new certificates
|
||||
// when no other key type is specified.
|
||||
DefaultKeyType = acme.RSA2048
|
||||
|
||||
// DisableHTTPChallenge will disable all HTTP challenges.
|
||||
DisableHTTPChallenge bool
|
||||
|
||||
// DisableTLSSNIChallenge will disable all TLS-SNI challenges.
|
||||
DisableTLSSNIChallenge bool
|
||||
)
|
||||
|
||||
var storageProviders = make(map[string]StorageConstructor)
|
||||
|
||||
// RegisterStorageProvider registers provider by name for storing tls data
|
||||
func RegisterStorageProvider(name string, provider StorageConstructor) {
|
||||
storageProviders[name] = provider
|
||||
caddy.RegisterPlugin("tls.storage."+name, caddy.Plugin{})
|
||||
}
|
Reference in New Issue
Block a user