Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up some annoyances #1107

Merged
merged 4 commits into from
Jan 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ambassador/ambassador/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class Config:
sources: Dict[str, ACResource]

errors: Dict[str, List[dict]]
notices: Dict[str, List[dict]]
notices: Dict[str, List[str]]
fatal_errors: int
object_errors: int

Expand Down
6 changes: 4 additions & 2 deletions ambassador/ambassador/envoy/v2/v2listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ def handle_sni(self, config: 'V2Config') -> None:

# We'll assemble a list of active TLS contexts here. It may end up empty,
# of course.
envoy_contexts: List[Tuple[str, List[str], V2TLSContext]] = []
envoy_contexts: List[Tuple[str, Optional[List[str]], V2TLSContext]] = []

for tls_context in config.ir.get_tls_contexts():
if tls_context.get('hosts', None):
Expand Down Expand Up @@ -409,7 +409,9 @@ def handle_sni(self, config: 'V2Config') -> None:
# Check if filter chain and SNI route have matching hosts
config.ir.logger.info("V2Listener: SNI route check %s, route %s" %
(name, json.dumps(sni_route, indent=4, sort_keys=True)))
matched = sorted(sni_route['info']['hosts']) == sorted(hosts)

context_hosts = sorted(hosts or [])
matched = sorted(sni_route['info']['hosts']) == context_hosts

# Check for certificate match too.
for sni_key, ctx_key in [ ('cert_chain_file', 'certificate_chain'),
Expand Down
74 changes: 44 additions & 30 deletions ambassador/ambassador/envoy/v2/v2tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License

from typing import Dict, Optional, Union
from typing import Callable, Dict, List, Optional, Union
from typing import cast as typecast

from ...ir.irtlscontext import IRTLSContext

# This stuff isn't really accurate, but it'll do for now.
EnvoyCoreSource = Dict[str, str]

EnvoyTLSCert = Dict[str, EnvoyCoreSource]
ListOfCerts = List[EnvoyTLSCert]

EnvoyValidationElements = Union[EnvoyCoreSource, bool]
EnvoyValidationContext = Dict[str, EnvoyValidationElements]

EnvoyCommonTLSElements = Union[List[str], ListOfCerts, EnvoyValidationContext]
EnvoyCommonTLSContext = Dict[str, EnvoyCommonTLSElements]

ElementHandler = Callable[[str, str], None]

class V2TLSContext(Dict):
def __init__(self, ctx: Optional[IRTLSContext]=None, host_rewrite: Optional[str]=None) -> None:
Expand All @@ -23,50 +38,49 @@ def __init__(self, ctx: Optional[IRTLSContext]=None, host_rewrite: Optional[str]
if ctx:
self.add_context(ctx)

def get_common(self) -> Dict[str, str]:
def get_common(self) -> EnvoyCommonTLSContext:
return self.setdefault('common_tls_context', {})

def get_certs(self) -> Dict[str, str]:
def get_certs(self) -> ListOfCerts:
common = self.get_common()
return common.setdefault('tls_certificates', [])

def update_cert_zero(self, key, value) -> None:
# We have to explicitly cast this empty list to a list of strings.
empty_cert_list: List[str] = []
cert_list = common.setdefault('tls_certificates', empty_cert_list)

# cert_list is of type EnvoyCommonTLSElements right now, so we need to cast it.
return typecast(ListOfCerts, cert_list)

def update_cert_zero(self, key: str, value: str) -> None:
certs = self.get_certs()

if not certs:
certs.append({})

certs[0][key] = { 'filename': value }

def update_common(self, key, value) -> None:
common = self.get_common()
common[key] = value
src: EnvoyCoreSource = { 'filename': value }
certs[0][key] = src

def update_alpn(self, key, value) -> None:
def update_alpn(self, key: str, value: str) -> None:
common = self.get_common()
common[key] = [ value ]

def update_validation(self, key, value) -> None:
validation: Dict[str, str] = self.get_common().setdefault('validation_context', {})
validation[key] = { 'filename': value }
def update_validation(self, key: str, value: str) -> None:
empty_context: EnvoyValidationContext = {}
validation = typecast(EnvoyValidationContext, self.get_common().setdefault('validation_context', empty_context))

src: EnvoyCoreSource = { 'filename': value }
validation[key] = src

def add_context(self, ctx: IRTLSContext) -> None:
# This is a weird method, because the definition of a V2 TLS context in
# Envoy is weird, and because we need to manage two different inputs (which
# is silly).

assert ctx.kind == 'IRTLSContext'

if ctx.kind == 'IRTLSContext':
for secretinfokey, handler, hkey in [
( 'cert_chain_file', self.update_cert_zero, 'certificate_chain' ),
( 'private_key_file', self.update_cert_zero, 'private_key' ),
( 'cacert_chain_file', self.update_validation, 'trusted_ca' ),
]:
if secretinfokey in ctx['secret_info']:
handler(hkey, ctx['secret_info'][secretinfokey])
else:
raise TypeError("impossible? error: V2TLS handed a %s" % ctx.kind)
handler: ElementHandler = self.__setitem__

for secretinfokey, handler, hkey in [
( 'cert_chain_file', self.update_cert_zero, 'certificate_chain' ),
( 'private_key_file', self.update_cert_zero, 'private_key' ),
( 'cacert_chain_file', self.update_validation, 'trusted_ca' ),
]:
if secretinfokey in ctx['secret_info']:
handler(hkey, ctx['secret_info'][secretinfokey])

for ctxkey, handler, hkey in [
( 'alpn_protocols', self.update_alpn, 'alpn_protocols' ),
Expand Down
15 changes: 3 additions & 12 deletions ambassador/ambassador/ir/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ def __init__(self, aconf: Config, secret_reader=None, file_checker=None) -> None
# this though.

self.tls_module = None
# self.envoy_tls = {}

# OK! Start by wrangling TLS-context stuff, both from the TLS module (if any)...
TLSModuleFactory.load_all(self, aconf)
Expand Down Expand Up @@ -338,8 +337,6 @@ def as_dict(self) -> Dict[str, Any]:
for cluster_name, cluster in self.clusters.items() },
'grpc_services': { svc_name: cluster.as_dict()
for svc_name, cluster in self.grpc_services.items() },
# 'envoy_tls_contexts': { ctx_name: ctx.as_dict()
# for ctx_name, ctx in self.envoy_tls.items() },
'listeners': [ listener.as_dict() for listener in self.listeners ],
'filters': [ filt.as_dict() for filt in self.filters ],
'groups': [ group.as_dict() for group in self.ordered_groups() ],
Expand All @@ -366,15 +363,6 @@ def features(self) -> Dict[str, Any]:
using_tls_module = False
using_tls_contexts = False

# for ctx in self.envoy_tls.values():
# using_tls_module = True
#
# if ctx.get('certificate_chain_file', None) and ctx.get('valid_tls', False):
# tls_termination_count += 1
#
# if ctx.get('cacert_chain_file', None) and ctx.get('valid_tls', False):
# tls_origination_count += 1

for ctx in self.get_tls_contexts():
if ctx:
secret_info = ctx.get('secret_info', {})
Expand All @@ -388,6 +376,9 @@ def features(self) -> Dict[str, Any]:
if secret_info.get('cacert_chain_file', None):
tls_origination_count += 1

if ctx.get('_legacy', False):
using_tls_module = True

od['tls_using_module'] = using_tls_module
od['tls_using_contexts'] = using_tls_contexts
od['tls_termination_count'] = tls_termination_count
Expand Down
5 changes: 4 additions & 1 deletion ambassador/ambassador/ir/irmapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,14 @@ def _route_weight(self):

def match_tls_context(self, host: str, ir: 'IR'):
for context in ir.get_tls_contexts():
for context_host in context.get('hosts', []):
hosts = context.get('hosts') or []

for context_host in hosts:
if context_host == host:
ir.logger.info("Matched host {} with TLSContext {}".format(host, context.get('name')))
self.sni = True
return context

return None


Expand Down
5 changes: 4 additions & 1 deletion ambassador/ambassador/ir/irtlscontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class IRTLSContext(IRResource):
]

name: str
hosts: List[str]
hosts: Optional[List[str]]
alpn_protocols: Optional[str]
cert_required: Optional[bool]
redirect_cleartext_from: Optional[int]
Expand Down Expand Up @@ -316,6 +316,9 @@ def from_legacy(cls, ir: 'IR', name: str, rkey: str, location: str,
# Assume they want the 'ambassador-cacert' secret.
new_args['secret'] = 'ambassador-cacert'

# Remember that this is a legacy context.
new_args['_legacy'] = True

return IRTLSContext.from_config(ir, rkey, location,
kind="synthesized-TLS-context",
name=name, **new_args)