Add Mesh Support to your Charm¶
This guide explains how to add service mesh support to your charm using the ServiceMeshConsumer library. This library enables your charm to join a service mesh and automatically generate traffic authorization policies.
Add Required Relations to charmcraft.yaml
¶
To use the ServiceMeshConsumer
library, add the following relations to your charm’s charmcraft.yaml
:
requires:
service-mesh:
limit: 1
interface: service_mesh
description: |
Integrate this charm into a service mesh
require-cmr-mesh:
interface: cross_model_mesh
description: |
If this app relates to other applications on a charmed service mesh cross-model, use this relation to send that related app additional data needed to automatically generate traffic authorization policies. This is required because Juju does not natively provide all information required to build these policies when related cross-model.
provides:
provide-cmr-mesh:
interface: cross_model_mesh
description: |
If this app is generating polciies to provide access to related applications that are cross-model, relate that app to this additional relation to retrieve additional data required for these policies. This is required because Juju does not natively provide all information required to build these policies when related cross-model.
Use the ServiceMeshConsumer
library in your charm¶
Fetch the service-mesh
library and add the ServiceMeshConsumer
to your Charm. For example:
class MyCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
self._mesh = ServiceMeshConsumer(self)
This integration allows for your Charm to be integrated with a Charmed Beacon and individually added to a Juju service mesh. By default, Charmed Service Meshes deploy hardening, meaning they block any unauthorized access to your workloads. If your Charm is never accessed by other applications in the cluster (ex: a Wordpress server that simply provides a website), you’re done! But if other applications need to access your charm, such as if you’ve charmed a database that other applications will relate to or a workload that has scrapable metrics, then continue below to create access policies.
Enable Automatic, Fine-grained Access to other Charmed Applications via Policies¶
In a hardened service mesh, communication between applications must be explicitly allowed by policies. If your Charm deploys workloads that other applications consume, for example:
your charm deploys a database and other applications consume this database by relating to your application
your charm deploys any workload which generates metrics, and uses the
prometheus_scrape
interface to allow for metrics scraping
you can use the ServiceMeshConsumer
policies
argument to automate this policy generation[1]. The policies can either be an AppPolicy
or a UnitPolicy
.
An AppPolicy
can be used to control access from a source application to the target application at its Kubernetes service address. Each AppPolicy
defines:
relation
: the relation endpoint this policy applies to. A policy will be generated for each application related via this relationendpoints
: a list ofEndpoint
objects, each defining thepaths
,ports
, andmethods
that this policy allows traffic on
A UnitPolicy
can be used to control access from a source application to the target unit via at Kubernetes pod address. Each UnitPolicy
defines:
relation
: the relation endpoint this policy applies to. A policy will be generated for each application related via this relationports
: a list of port values that this policy allows traffic on
UnitPolicy
is useful when access to individual units (or workloads) from a source application is necessary. For example, prometheus
scraping individual units of an application for metrics. Without a UnitPolicy
, access to individual units of an application will be denied.
Note
A UnitPolicy can control access by ports but not by paths and methods. Hence, it is not possible provide access control through endpoints
while using UnitPolicy
unlike AppPolicy
. This limitation stems from the upstream service meshes (Istio) that are supported by Canonical Service Mesh.
For example:
class MyCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
self._mesh = ServiceMeshConsumer(
self,
policies=[
AppPolicy(
relation="database", # On the database relation
endpoints=[ # allow a related application to access...
Endpoint(
paths=["/db"], # these specific paths
ports=[DB_PORT], # on these specific ports
methods=[Method.get, Method.Post], # using only these methods
),
],
),
UnitPolicy(
relation="metrics-endpoint", # On the metrics-endpoint relations
ports=[HTTP_PORT], # allow a related application to access this charm's individual units on these specific ports
),
],
)
Exactly what should be defined for your Endpoint
s depends on the application you’ve charmed. Generally, you can look at your applications API reference or typical usage and include exactly what is needed, exposing only the necessary attack surface.
Cross-model Integrations (Optional)¶
If your Charm provides integrations that can be used cross-model, the ServiceMeshConsumer
library offers the additional provide-cmr-mesh
and require-cmr-mesh
integrations to ensure these generate policies properly. These additional integrations are required because Juju cross-model relations do not natively provide all the information needed for a service mesh authorization policy to be generated.
To use the cross-model policy generation, simply integrate your applications normally and then add the additional cmr relation. For example:
juju deploy my-db-provider
juju deploy my-db-consumer
juju relate my-db-provider:database my-db-consumer:database
juju relate my-db-provider:provide-cmr-support my-db-consumer:require-cmr-support
For a more detailed tutorial using cross-model integrations, follow the Use the Istio Mesh across different Juju models tutorial.