1# Copyright 2018 Datawire. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License
14
15from typing import TYPE_CHECKING
16
17from ..config import Config
18from .irresource import IRResource as IRResource
19from .irtlscontext import IRTLSContext
20
21if TYPE_CHECKING:
22 from .ir import IR # pragma: no cover
23
24
25#############################################################################
26## tls.py -- the tls_context configuration object for Ambassador
27##
28## IRAmbassadorTLS represents an Ambassador TLS configuration: it's the way
29## we unify the TLS module and the 'tls' block in the Ambassador module. This
30## class is pretty much all about managing priority between the two -- any
31## important information here gets turned into IRTLSContext objects before
32## TLS configuration actually happens.
33##
34## There's a fair amount of logic around making priority decisions between
35## the 'tls' block and the TLS module at present. Probably that logic should
36## migrate here, or this class should go away.
37
38
39class IRAmbassadorTLS(IRResource):
40 def __init__(
41 self,
42 ir: "IR",
43 aconf: Config,
44 rkey: str = "ir.tlsmodule",
45 kind: str = "IRTLSModule",
46 name: str = "ir.tlsmodule",
47 enabled: bool = True,
48 **kwargs
49 ) -> None:
50 """
51 Initialize an IRAmbassadorTLS from the raw fields of its Resource.
52 """
53
54 ir.logger.debug("IRAmbassadorTLS __init__ (%s %s %s)" % (kind, name, kwargs))
55
56 super().__init__(
57 ir=ir, aconf=aconf, rkey=rkey, kind=kind, name=name, enabled=enabled, **kwargs
58 )
59
60
61class TLSModuleFactory:
62 @classmethod
63 def load_all(cls, ir: "IR", aconf: Config) -> None:
64 assert ir
65
66 tls_module = aconf.get_module("tls")
67
68 if tls_module:
69 # ir.logger.debug("TLSModuleFactory saving TLS module: %s" % tls_module.as_json())
70
71 # XXX What a hack. IRAmbassadorTLS.from_resource() should be able to make
72 # this painless.
73 new_args = dict(tls_module.as_dict())
74 new_rkey = new_args.pop("rkey", tls_module.rkey)
75 new_kind = new_args.pop("kind", tls_module.kind)
76 new_name = new_args.pop("name", tls_module.name)
77 new_location = new_args.pop("location", tls_module.location)
78
79 ir.tls_module = IRAmbassadorTLS(
80 ir,
81 aconf,
82 rkey=new_rkey,
83 kind=new_kind,
84 name=new_name,
85 location=new_location,
86 **new_args
87 )
88
89 ir.logger.debug("TLSModuleFactory saved TLS module: %s" % ir.tls_module.as_json())
90
91 # Next, a TLS module in the Ambassador module overrides any other TLS Module.
92 amod = aconf.get_module("ambassador")
93
94 if amod:
95 ir.ambassador_module.sourced_by(amod)
96 ir.ambassador_module.referenced_by(amod)
97
98 amod_tls = amod.get("tls", None)
99
100 # Check for an Ambassador module tls field so that we can warn the user that this field is deprecated!
101 if amod_tls:
102 ir.post_error(
103 "The 'tls' field on the Ambassador module is deprecated! Please use a TLSContext instead https://www.getambassador.io/docs/edge-stack/latest/topics/running/tls/#tlscontext"
104 )
105
106 # Finally, if we have a TLS Module, turn it into a TLSContext.
107 if ir.tls_module:
108 ir.logger.debug("TLSModuleFactory translating TLS module to TLSContext")
109
110 # Stash a sane rkey and location for contexts we create.
111 ctx_rkey = ir.tls_module.get("rkey", ir.ambassador_module.rkey)
112 ctx_location = ir.tls_module.get("location", ir.ambassador_module.location)
113
114 # The TLS module 'server' and 'client' blocks are actually a _single_ TLSContext
115 # to Ambassador.
116
117 server = ir.tls_module.pop("server", None)
118 client = ir.tls_module.pop("client", None)
119
120 if server and server.get("enabled", True):
121 # We have a server half. Excellent.
122
123 ctx = IRTLSContext.from_legacy(
124 ir,
125 "server",
126 ctx_rkey,
127 ctx_location,
128 cert=server,
129 termination=True,
130 validation_ca=client,
131 )
132
133 if ctx.is_active():
134 ir.save_tls_context(ctx)
135
136 # Other blocks in the TLS module weren't ever really documented, so I seriously doubt
137 # that they're a factor... but, weirdly, we have a test for them...
138
139 for legacy_name, legacy_ctx in ir.tls_module.as_dict().items():
140 if (
141 legacy_name.startswith("_")
142 or (legacy_name == "name")
143 or (legacy_name == "namespace")
144 or (legacy_name == "metadata_labels")
145 or (legacy_name == "location")
146 or (legacy_name == "kind")
147 or (legacy_name == "enabled")
148 ):
149 continue
150
151 ctx = IRTLSContext.from_legacy(
152 ir,
153 legacy_name,
154 ctx_rkey,
155 ctx_location,
156 cert=legacy_ctx,
157 termination=False,
158 validation_ca=None,
159 )
160
161 if ctx.is_active():
162 ir.save_tls_context(ctx)
163
164 @classmethod
165 def finalize(cls, ir: "IR", aconf: Config) -> None:
166 pass
View as plain text