Skip to content

Commit

Permalink
Add issuer override (#202)
Browse files Browse the repository at this point in the history
* NOJIRA adding support to issuer_name override

* Fix tests and fix implementation

* Fix style

* Add tests for the new feature

* Fix dev hose auth

* Acknowledge review comments

* Fix style

* Add issuer-name override in operator guide

* Fix typo

---------

Co-authored-by: Fabián Sellés Rosa <[email protected]>
Co-authored-by: herodes1991 <[email protected]>
  • Loading branch information
3 people authored Mar 10, 2023
1 parent ca90632 commit 408daa2
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 37 deletions.
1 change: 1 addition & 0 deletions docs/operator_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ Used to configure how the ingress will be annotated for issuing a TLS certificat
* `tls-certificate-issuer` sets the _value_ of the annotation, for example to decide between production and staging versions of an issuer in different namespaces
* `tls-certificate-issuer-type-default` sets the default for the _key_ of the annotation, for example to use either `certmanager.k8s.io/cluster-issuer` (the default) or `certmanager.k8s.io/issuer`
* `tls-certificate-issuer-type-overrides` allows specifying a mapping between the suffix of a domain and the issuer-type, to override the default. For example, assuming the 'cluster-issuer' type as the default, then specifying `--tls-certificate-issuer-type-overrides foo.example.com=certmanager.k8s.io/issuer` would mean that foo.example.com and any of its subdomains will use the 'issuer' type instead. In the case of multiple matching suffixes, the more specific (i.e. longest) will be used.
* `tls-certificate-issuer-overrides` allows specifying a mapping between the suffix of a domain and the issuer-name, to override the default. For example, assuming the 'letsencrypt' issuer name as the default, then specifying `--tls-certificate-issuer-type-overrides foo.example.com=issuer-2` would mean that foo.example.com and any of its subdomains will use the 'issuer-2' as issuer name instead. In the case of multiple matching suffixes, the more specific (i.e. longest) will be used.

### use-in-memory-emptydirs

Expand Down
11 changes: 11 additions & 0 deletions fiaas_deploy_daemon/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,14 @@ def _parse_args(self, args):
help="Certificate issuer to use with cert-manager to provision certificates",
default=None,
)
tls_parser.add_argument(
"--tls-certificate-issuer-overrides",
help="Issuers name to use for specified domain suffixes",
default=[],
action="append",
type=KeyValue,
dest="tls_certificate_issuer_overrides",
)
tls_parser.add_argument(
"--tls-certificate-issuer-type-default",
help="The annotation to set for cert-manager to provision certificates",
Expand All @@ -394,6 +402,9 @@ def _parse_args(self, args):
self.tls_certificate_issuer_type_overrides = {
issuer_type.key: issuer_type.value for issuer_type in self.tls_certificate_issuer_type_overrides
}
self.tls_certificate_issuer_overrides = {
issuer_name.key: issuer_name.value for issuer_name in self.tls_certificate_issuer_overrides
}

def _resolve_env(self):
image = os.getenv("IMAGE")
Expand Down
58 changes: 44 additions & 14 deletions fiaas_deploy_daemon/deployer/kubernetes/ingress.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@ def __init__(self, config, default_app_spec, ingress_adapter):
self._ingress_suffixes = config.ingress_suffixes
self._host_rewrite_rules = config.host_rewrite_rules
self._ingress_adapter = ingress_adapter
self._tls_issuer_type_default = config.tls_certificate_issuer_type_default
self._tls_issuer_overrides = sorted(
iter(config.tls_certificate_issuer_overrides.items()), key=lambda k_v: len(k_v[0]), reverse=True
)
self._tls_issuer_type_overrides = sorted(
iter(config.tls_certificate_issuer_type_overrides.items()), key=lambda k_v: len(k_v[0]), reverse=True
)
self._tls_issuer_type_default = self._get_issuer_type_default_ingress(config)
self._tls_issuer_name_default = config.tls_certificate_issuer

def deploy(self, app_spec, labels):
if self._should_have_ingress(app_spec):
Expand Down Expand Up @@ -96,13 +100,39 @@ def _resolve_default_path(self):
default_ingress_item = next(ingress_item for ingress_item in self._default_app_spec().ingresses)
return next(pathmapping.path for pathmapping in default_ingress_item.pathmappings)

def _get_issuer_type_default_ingress(self, config):
for ingress_suffix in self._ingress_suffixes:
for (suffix, issuer_type) in self._tls_issuer_type_overrides:
if ingress_suffix and (ingress_suffix == suffix or ingress_suffix.endswith("." + suffix)):
return issuer_type

return config.tls_certificate_issuer_type_default

def _get_issuer_name_default_ingress(self, app_spec):
for ingress_suffix in self._ingress_suffixes:
if ingress_suffix:
for (suffix, issuer_name) in self._tls_issuer_overrides:
if ingress_suffix == suffix or ingress_suffix.endswith("." + suffix):
return issuer_name

return app_spec.ingress_tls.certificate_issuer if app_spec.ingress_tls.certificate_issuer else self._tls_issuer_name_default

def _get_issuer_type(self, host):
for (suffix, issuer_type) in self._tls_issuer_type_overrides:
if host and (host == suffix or host.endswith("." + suffix)):
return issuer_type
if host:
for (suffix, issuer_type) in self._tls_issuer_type_overrides:
if (host == suffix or host.endswith("." + suffix)):
return issuer_type

return self._tls_issuer_type_default

def _get_issuer_name(self, host, app_spec):
if host:
for (suffix, issuer_name) in self._tls_issuer_overrides:
if (host == suffix or host.endswith("." + suffix)):
return issuer_name

return app_spec.ingress_tls.certificate_issuer if app_spec.ingress_tls.certificate_issuer else self._tls_issuer_name_default

def _group_ingresses(self, app_spec):
"""Group the ingresses so that those with annotations are individual, and so that those using non-default TLS-issuers
are separated
Expand All @@ -114,20 +144,23 @@ def _group_ingresses(self, app_spec):
ingress_items += self._expand_default_hosts(app_spec)

AnnotatedIngress = namedtuple(
"AnnotatedIngress", ["name", "ingress_items", "annotations", "explicit_host", "issuer_type", "default"]
"AnnotatedIngress", ["name", "ingress_items", "annotations", "explicit_host", "issuer_type", "issuer_name", "default"]
)
tls_issuer_name_default = self._get_issuer_name_default_ingress(app_spec)
default_ingress = AnnotatedIngress(
name=app_spec.name,
ingress_items=[],
annotations={},
explicit_host=explicit_host,
issuer_type=self._tls_issuer_type_default,
issuer_name=tls_issuer_name_default,
default=True,
)
ingresses = [default_ingress]
override_issuer_ingresses = {}
for ingress_item in ingress_items:
issuer_type = self._get_issuer_type(ingress_item.host)
issuer_name = self._get_issuer_name(ingress_item.host, app_spec)
next_name = "{}-{}".format(app_spec.name, len(ingresses) + len(override_issuer_ingresses))
if ingress_item.annotations:
annotated_ingresses = AnnotatedIngress(
Expand All @@ -136,18 +169,20 @@ def _group_ingresses(self, app_spec):
annotations=ingress_item.annotations,
explicit_host=True,
issuer_type=issuer_type,
issuer_name=issuer_name,
default=False,
)
ingresses.append(annotated_ingresses)
elif issuer_type != self._tls_issuer_type_default:
elif issuer_type != self._tls_issuer_type_default or issuer_name != tls_issuer_name_default:
annotated_ingress = override_issuer_ingresses.setdefault(
issuer_type,
"{}:{}".format(issuer_type, issuer_name),
AnnotatedIngress(
name=next_name,
ingress_items=[],
annotations={},
explicit_host=explicit_host,
issuer_type=issuer_type,
issuer_name=issuer_name,
default=False,
),
)
Expand Down Expand Up @@ -211,16 +246,11 @@ def __init__(self, config, ingress_tls):
self.enable_deprecated_tls_entry_per_host = config.enable_deprecated_tls_entry_per_host
self.ingress_tls = ingress_tls

def apply(self, ingress, app_spec, hosts, issuer_type, use_suffixes=True):
def apply(self, ingress, app_spec, hosts, issuer_type, issuer_name, use_suffixes=True):
if self._should_have_ingress_tls(app_spec):
tls_annotations = {}
if self._cert_issuer or app_spec.ingress_tls.certificate_issuer:
issuer = (
app_spec.ingress_tls.certificate_issuer
if app_spec.ingress_tls.certificate_issuer
else self._cert_issuer
)
tls_annotations[issuer_type] = issuer
tls_annotations[issuer_type] = issuer_name
else:
tls_annotations["kubernetes.io/tls-acme"] = "true"
ingress.metadata.annotations = merge_dicts(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def create_ingress(self, app_spec, annotated_ingress, labels):

hosts_for_tls = [rule.host for rule in per_host_ingress_rules]
self._ingress_tls_deployer.apply(
ingress, app_spec, hosts_for_tls, annotated_ingress.issuer_type, use_suffixes=use_suffixes
ingress, app_spec, hosts_for_tls, annotated_ingress.issuer_type, annotated_ingress.issuer_name, use_suffixes=use_suffixes
)
self._owner_references.apply(ingress, app_spec)
self._extension_hook.apply(ingress, app_spec)
Expand Down
2 changes: 1 addition & 1 deletion fiaas_deploy_daemon/deployer/kubernetes/ingress_v1beta1.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def create_ingress(self, app_spec, annotated_ingress, labels):

hosts_for_tls = [rule.host for rule in per_host_ingress_rules]
self._ingress_tls_deployer.apply(
ingress, app_spec, hosts_for_tls, annotated_ingress.issuer_type, use_suffixes=use_suffixes
ingress, app_spec, hosts_for_tls, annotated_ingress.issuer_type, annotated_ingress.issuer_name, use_suffixes=use_suffixes
)
self._owner_references.apply(ingress, app_spec)
self._extension_hook.apply(ingress, app_spec)
Expand Down
2 changes: 1 addition & 1 deletion fiaas_deploy_daemon/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def log_request_response(resp, *args, **kwargs):
return # k8s library already does its own dumping, we don't need to do it here
log = logging.getLogger(__name__)
data = dump_all(resp, "<<<", ">>>")
log.debug("Request/Response\n" + data)
log.debug("Request/Response\n" + data.decode('utf-8'))


class IterableQueue(Queue, Iterator):
Expand Down
6 changes: 3 additions & 3 deletions fiaas_deploy_daemon/usage_reporting/dev_hose_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
class DevHoseAuth(AuthBase):
def __init__(self, key, tenant):
self._key = base64.b64decode(key.strip())
self._auth_context = base64.b64encode(json.dumps({"type": tenant}).encode("utf-8")).decode("utf-8")
self._auth_context = base64.b64encode(json.dumps({"type": tenant}).encode("utf-8"))

def __call__(self, r):
timestamp = time.time()
nonce = str(uuid.uuid4())
r.headers["Content-Signature"] = self._calculate_signature(r, timestamp, nonce)
r.headers["DevHose-AuthContext"] = self._auth_context
r.headers["DevHose-AuthContext"] = self._auth_context.decode("utf-8")
r.headers["DevHose-Nonce"] = nonce
r.headers["Date"] = email.utils.formatdate(timestamp)
return r
Expand All @@ -53,7 +53,7 @@ def _create_string_to_sign(self, r, timestamp, nonce):
quote_plus(nonce.encode("utf-8")),
str(int(timestamp) * 1000),
quote_plus(self._auth_context),
quote_plus(r.body.encode("utf-8")),
quote_plus(r.body),
"",
)
)
Loading

0 comments on commit 408daa2

Please sign in to comment.