TLSConfig returns a standard-lib-compatible TLS configuration which selects the first matching policy based on the ClientHello.
(ctx caddy.Context)
| 96 | // TLSConfig returns a standard-lib-compatible TLS configuration which |
| 97 | // selects the first matching policy based on the ClientHello. |
| 98 | func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config { |
| 99 | // using ServerName to match policies is extremely common, especially in configs |
| 100 | // with lots and lots of different policies; we can fast-track those by indexing |
| 101 | // them by SNI, so we don't have to iterate potentially thousands of policies |
| 102 | // (TODO: this map does not account for wildcards, see if this is a problem in practice? look for reports of high connection latency with wildcard certs but low latency for non-wildcards in multi-thousand-cert deployments) |
| 103 | indexedBySNI := make(map[string]ConnectionPolicies) |
| 104 | if len(cp) > 30 { |
| 105 | for _, p := range cp { |
| 106 | for _, m := range p.matchers { |
| 107 | if sni, ok := m.(MatchServerName); ok { |
| 108 | for _, sniName := range sni { |
| 109 | // index for fast lookups during handshakes |
| 110 | indexName := asciiServerNameForMatch(sniName) |
| 111 | indexedBySNI[indexName] = append(indexedBySNI[indexName], p) |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | getConfigForClient := func(hello *tls.ClientHelloInfo) (*tls.Config, error) { |
| 119 | // filter policies by SNI first, if possible, to speed things up |
| 120 | // when there may be lots of policies |
| 121 | possiblePolicies := cp |
| 122 | if indexedPolicies, ok := indexedBySNI[asciiServerNameForMatch(hello.ServerName)]; ok { |
| 123 | possiblePolicies = indexedPolicies |
| 124 | } |
| 125 | |
| 126 | policyLoop: |
| 127 | for _, pol := range possiblePolicies { |
| 128 | for _, matcher := range pol.matchers { |
| 129 | if !matcher.Match(hello) { |
| 130 | continue policyLoop |
| 131 | } |
| 132 | } |
| 133 | if pol.Drop { |
| 134 | return nil, fmt.Errorf("dropping connection") |
| 135 | } |
| 136 | return pol.TLSConfig, nil |
| 137 | } |
| 138 | |
| 139 | return nil, fmt.Errorf("no server TLS configuration available for ClientHello: %+v", hello) |
| 140 | } |
| 141 | |
| 142 | tlsCfg := &tls.Config{ |
| 143 | MinVersion: tls.VersionTLS12, |
| 144 | GetConfigForClient: getConfigForClient, |
| 145 | } |
| 146 | |
| 147 | // enable ECH, if configured |
| 148 | if tlsAppIface, err := ctx.AppIfConfigured("tls"); err == nil { |
| 149 | tlsApp := tlsAppIface.(*TLS) |
| 150 | |
| 151 | if tlsApp.EncryptedClientHello != nil && len(tlsApp.EncryptedClientHello.configs) > 0 { |
| 152 | // if no publication was configured, we apply ECH to all server names by default, |
| 153 | // but the TLS app needs to know what they are in this case, since they don't appear |
| 154 | // in its config (remember, TLS connection policies are used by *other* apps to |
| 155 | // run TLS servers) -- we skip names with placeholders |