diff --git a/docs/adr/0002-use-kubespray-for-cluster-lifecycle.md b/docs/adr/0002-use-kubespray-for-cluster-lifecycle.md index af52f242e85..26f994c4560 100644 --- a/docs/adr/0002-use-kubespray-for-cluster-lifecycle.md +++ b/docs/adr/0002-use-kubespray-for-cluster-lifecycle.md @@ -17,7 +17,7 @@ Compliant Kubernetes promises: "Multi-cloud. Open source. Compliant". So far, we ## Considered Options -* [Rancher](https://rancher.com/) +* [Rancher](https://www.rancher.com/) * [kubeadm via in-house tools (ck8s-cluster)](https://github.com/elastisys/ck8s-cluster) * [kubespray](https://github.com/kubernetes-sigs/kubespray) * [kops](https://github.com/kubernetes/kops) diff --git a/docs/adr/0007-make-monitoring-forwarders-storage-independent.md b/docs/adr/0007-make-monitoring-forwarders-storage-independent.md index 527b109ce64..b553cccfabb 100644 --- a/docs/adr/0007-make-monitoring-forwarders-storage-independent.md +++ b/docs/adr/0007-make-monitoring-forwarders-storage-independent.md @@ -102,4 +102,4 @@ Similar to Local Persistent Volumes, but ## Links -* [Prometheus Operator Storage](https://github.com/prometheus-operator/prometheus-operator/blob/master/Documentation/user-guides/storage.md) +* [Prometheus Operator Storage](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/storage.md) diff --git a/docs/adr/0008-use-hostnetwork-or-loadbalancer-for-ingress.md b/docs/adr/0008-use-hostnetwork-or-loadbalancer-for-ingress.md index 47a53b955f0..4a30f83284b 100644 --- a/docs/adr/0008-use-hostnetwork-or-loadbalancer-for-ingress.md +++ b/docs/adr/0008-use-hostnetwork-or-loadbalancer-for-ingress.md @@ -8,7 +8,7 @@ Technical Story: [Ingress configuration](https://github.com/elastisys/compliantk ## Context and Problem Statement -Many regulations require traffic to be encrypted over public Internet. Compliant Kubernetes solves this problem via an [Ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/) and [cert-manager](https://github.com/jetstack/cert-manager). As of February 2021, Compliant Kubernetes comes by default with [nginx-ingress](https://kubernetes.github.io/ingress-nginx/), but [Ambassador](https://www.getambassador.io/docs/latest/topics/running/ingress-controller/) is planned as an alternative. The question is, how does traffic arrive at the Ingress controller? +Many regulations require traffic to be encrypted over public Internet. Compliant Kubernetes solves this problem via an [Ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/) and [cert-manager](https://github.com/cert-manager/cert-manager). As of February 2021, Compliant Kubernetes comes by default with [nginx-ingress](https://kubernetes.github.io/ingress-nginx/), but [Ambassador](https://www.getambassador.io/) is planned as an alternative. The question is, how does traffic arrive at the Ingress controller? ## Decision Drivers diff --git a/docs/adr/0011-let-upstream-projects-handle-crds.md b/docs/adr/0011-let-upstream-projects-handle-crds.md index 48674e34795..a9742c548a4 100644 --- a/docs/adr/0011-let-upstream-projects-handle-crds.md +++ b/docs/adr/0011-let-upstream-projects-handle-crds.md @@ -53,7 +53,7 @@ A detailed analysis is listed below: ### cert-manager -* Installation: The cert-manager Helm Chart includes the [`installCRDs`](https://github.com/jetstack/cert-manager/blob/master/deploy/charts/cert-manager/values.yaml#L42) value -- by default it is set to `false`. If set to `true`, then CRDs are automatically installed when installing cert-manager, albeit not using the CRDs mechanism provided by Helm. +* Installation: The cert-manager Helm Chart includes the [`installCRDs`](https://github.com/cert-manager/cert-manager/blob/master/deploy/charts/cert-manager/values.yaml#L42) value -- by default it is set to `false`. If set to `true`, then CRDs are automatically installed when installing cert-manager, albeit not using the CRDs mechanism provided by Helm. * Upgrade: CRDs are supposed to be [upgraded manually](https://cert-manager.io/docs/installation/upgrading/#upgrading-with-helm). ### dex @@ -73,4 +73,4 @@ Dex can be configured without CRDs. [ADR-0012](https://github.com/elastisys/comp ### Velero * Installation: Velero install CRDs using [standard Helm mechanism](https://github.com/vmware-tanzu/helm-charts/tree/main/charts/velero/crds). -* Upgrade: Velero includes [magic to upgrade CRDs](https://github.com/vmware-tanzu/helm-charts/blob/main/charts/velero/templates/upgrade-crds/). +* Upgrade: Velero includes [magic to upgrade CRDs](https://github.com/vmware-tanzu/helm-charts/tree/main/charts/velero/templates/upgrade-crds). diff --git a/docs/adr/0013-configure-alerts-in-omt.md b/docs/adr/0013-configure-alerts-in-omt.md index 63abf4c786c..a2e3942fb2a 100644 --- a/docs/adr/0013-configure-alerts-in-omt.md +++ b/docs/adr/0013-configure-alerts-in-omt.md @@ -64,4 +64,4 @@ Configuration of alerts happens in the OMT. * [Opsgenie documentation](https://docs.opsgenie.com/) * [Alertmanager documentation](https://prometheus.io/docs/alerting/latest/alertmanager/) * [Terraform Opsgenie provider](https://registry.terraform.io/providers/opsgenie/opsgenie/latest/docs) -* [Pulumni Opsgenie module](https://www.pulumi.com/docs/reference/pkg/opsgenie/) +* [Pulumni Opsgenie module](https://www.pulumi.com/registry/packages/opsgenie/api-docs/) diff --git a/docs/adr/0021-tls-for-additional-services.md b/docs/adr/0021-tls-for-additional-services.md index 4a59ec4d63d..e1d3d6bf2df 100644 --- a/docs/adr/0021-tls-for-additional-services.md +++ b/docs/adr/0021-tls-for-additional-services.md @@ -9,7 +9,7 @@ We run additional services in the Workload Cluster, currently databases (PostgreSQL), in-memory caches (Redis) and message queues (RabbitMQ). Traditionally, when these services are provided as managed services, they are exposed via a TLS-encrypted endpoint. See examples for: -* [Redis](https://docs.microsoft.com/en-us/azure/azure-cache-for-redis/cache-nodejs-get-started#create-a-new-nodejs-app) -- notice `rediss://`; +* [Redis](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-nodejs-get-started#create-a-new-nodejs-app) -- notice `rediss://`; * [RabbitMQ](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/data-protection.html#data-protection-encryption-in-transit); * [PostgreSQL](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL.Concepts.General.SSL.html). diff --git a/docs/ciso-guide/faq.md b/docs/ciso-guide/faq.md index f93b7e9e2c9..3e7c192f126 100644 --- a/docs/ciso-guide/faq.md +++ b/docs/ciso-guide/faq.md @@ -61,7 +61,7 @@ Note that, according to [GDPR Art. 4](https://gdpr.fan/a4) any information that There are court rulings clarifying that: - email addresses and user IDs are personal data; -- [IP addresses are personal data](http://curia.europa.eu/juris/document/document.jsf?docid=184668&doclang=EN&cid=1095511); +- [IP addresses are personal data](https://curia.europa.eu/juris/document/document.jsf?docid=184668&doclang=EN&cid=1095511); - [browser-generated information](https://www.judiciary.uk/wp-content/uploads/2018/10/lloyd-v-google-judgment.pdf) (e.g., cookies, URLs, fingerprints, user agents) can be personal data. According to the [so-called "Schrems II" ruling](https://www.europarl.europa.eu/RegData/etudes/ATAG/2020/652073/EPRS_ATA(2020)652073_EN.pdf), US law -- in particular [US CLOUD Act](https://en.wikipedia.org/wiki/CLOUD_Act) and [US FISA](https://en.wikipedia.org/wiki/Foreign_Intelligence_Surveillance_Act) are incompatible -- with EU GDPR and personal data processing. diff --git a/docs/index.md b/docs/index.md index 6a7871c2444..3ca3f9904c9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -48,11 +48,10 @@ Elastisys Compliant Kubernetes enables organizations across Europe to accelerate :fontawesome-solid-user-check: The platform you would build yourself -
+
Built with CNCF projects, - public Architectural Decision Records, - as well as great documentation for - application developers. + public [Architectural Decision Records](adr/index.md), + as well as great documentation for [application developers](user-guide/index.md).
diff --git a/docs/operator-manual/clock-synchronization.md b/docs/operator-manual/clock-synchronization.md index 4203ce685b9..c0456c12a16 100644 --- a/docs/operator-manual/clock-synchronization.md +++ b/docs/operator-manual/clock-synchronization.md @@ -20,4 +20,4 @@ Clock synchronization is important for the following reasons: Be aware of the following: - Some regulations require synchronization with **at least two** clock sources. This could be two different NTP servers which can be traced to two different atomic clocks. -- Some regulations require synchronization with a specific time source. For example, MSBFS 2020:7 4 kap. 13 § specifically requires synchronization with [ntp.se](https://www.ntp.se). +- Some regulations require synchronization with a specific time source. For example, MSBFS 2020:7 4 kap. 13 § specifically requires synchronization with [ntp.se](https://www.netnod.se/swedish-distributed-time-service). diff --git a/docs/operator-manual/getting-started.md b/docs/operator-manual/getting-started.md index c25715fc937..0db89bbacd0 100644 --- a/docs/operator-manual/getting-started.md +++ b/docs/operator-manual/getting-started.md @@ -7,9 +7,9 @@ Setting up Compliant Kubernetes consists of two parts: setting up [at least two In theory, any vanilla Kubernetes cluster can be used for Compliant Kubernetes. We suggest the [kubespray](https://github.com/kubernetes-sigs/kubespray) way. To this end, you need: * [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [Python3 pip](https://packaging.python.org/guides/installing-using-linux-tools/#installing-pip-setuptools-wheel-with-linux-package-managers) -* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli#install-terraform) -* [Ansible](https://ansible.com) +* [Python3 pip](https://packaging.python.org/en/latest/guides/installing-using-linux-tools/) +* [Terraform](https://developer.hashicorp.com/terraform/downloads) +* [Ansible](https://ansible.com/) * [pwgen](https://manpages.ubuntu.com/manpages/trusty/man1/pwgen.1.html) Ansible is best installed as follows: @@ -45,7 +45,7 @@ Compliant Kubernetes relies on SSH for accessing nodes. If you haven't already d ssh-keygen ``` -Configuration secrets in Compliant Kubernetes are encrypted using [SOPS](https://github.com/mozilla/sops). +Configuration secrets in Compliant Kubernetes are encrypted using [SOPS](https://github.com/getsops/sops). We currently only support using PGP when encrypting secrets. If you haven't already done so, generate your own PGP key as follows: diff --git a/docs/operator-manual/ingress.md b/docs/operator-manual/ingress.md index 4a0ec7db87f..1215fbd3e40 100644 --- a/docs/operator-manual/ingress.md +++ b/docs/operator-manual/ingress.md @@ -44,7 +44,7 @@ ingressNginx: If the apps repository is initiated with the correct Infrastructure Provider these config options will get the correct defaults. -For more ways to install the Nginx Ingress controller see +For more ways to install the Nginx Ingress controller see [the upstream documentation](https://kubernetes.github.io/ingress-nginx/deploy/). ## Ingress resource diff --git a/docs/operator-manual/maintenance.md b/docs/operator-manual/maintenance.md index 6f7e82c1c98..8040cdbe623 100644 --- a/docs/operator-manual/maintenance.md +++ b/docs/operator-manual/maintenance.md @@ -36,7 +36,7 @@ Let's go through them one by one. Security patches for the underlying OS on the nodes is constantly being released, and to ensure your environment is secured, the nodes that run Compliant Kubernetes must be updated with these patches. We recommend that you use the [AutomaticSecurityUpdates](https://help.ubuntu.com/community/AutomaticSecurityUpdates) feature that is available in Ubuntu (similar feature exist in other distros) to install these updates. Note that the nodes still need to be rebooted for some of these updates to be applied. -In order to reboot the nodes, you can either use a tool like [kured](https://github.com/weaveworks/kured) or you can do it manually by logging on to the nodes and rebooting them manually. +In order to reboot the nodes, you can either use a tool like [kured](https://github.com/kubereboot/kured) or you can do it manually by logging on to the nodes and rebooting them manually. When doing that, reboot one node at the time and make sure that the rebooted node is 'Ready' and that pods are scheduled to it before you move on to the next, or you risk downtime. There is a playbook in the compliantkubernetes-kubespray repo that can assist with the reboot of nodes. diff --git a/docs/operator-manual/troubleshooting.md b/docs/operator-manual/troubleshooting.md index 705e59b1e32..2ef7a2388bc 100644 --- a/docs/operator-manual/troubleshooting.md +++ b/docs/operator-manual/troubleshooting.md @@ -355,8 +355,8 @@ Re-apply `apps` according to documentation. Follow cert-manager's troubleshooting, specifically: -* [Troubleshooting](https://cert-manager.io/docs/faq/troubleshooting/) -* [Troubleshooting Issuing ACME Certificates](https://cert-manager.io/docs/faq/acme/) +* [Troubleshooting](https://cert-manager.io/docs/troubleshooting/) +* [Troubleshooting Issuing ACME Certificates](https://cert-manager.io/docs/troubleshooting/acme/) ### Failed to perform self check: no such host diff --git a/docs/user-guide/additional-services/redis.md b/docs/user-guide/additional-services/redis.md index c4bd85e8801..ad8876db26f 100644 --- a/docs/user-guide/additional-services/redis.md +++ b/docs/user-guide/additional-services/redis.md @@ -23,7 +23,7 @@ Redis™ This page will help you succeed in connecting your application to a low-latency in-memory cache Redis which meets your security and compliance requirements. !!!important "Important: Improve Access Control with NetworkPolicies" - Please note the follow information about [Redis access control](https://redis.io/topics/security) from the upstream documentation: + Please note the follow information about [Redis access control](https://redis.io/topics/security/) from the upstream documentation: > Redis is designed to be accessed by trusted clients inside trusted environments. @@ -37,7 +37,7 @@ This page will help you succeed in connecting your application to a low-latency * Session state: If this is lost, the user experience might be impacted -- e.g., the user needs to re-login -- but no data should be lost. !!!important "Important: Sentinel support" - We recommend a highly available setup with at minimum three instances. The Redis client library that you use in your application needs to support [Redis Sentinel](https://redis.io/topics/sentinel). Notice that clients with Sentinel support need [extra steps to discover the Redis primary](https://redis.io/topics/sentinel-clients). + We recommend a highly available setup with at minimum three instances. The Redis client library that you use in your application needs to support [Redis Sentinel](https://redis.io/topics/sentinel/). Notice that clients with Sentinel support need [extra steps to discover the Redis primary](https://redis.io/topics/sentinel-clients/). ## Install Prerequisites @@ -149,7 +149,7 @@ The default `timeout` for our Managed Redis is set to `0`, which means the idle ## Further Reading -* [Redis Sentinel](https://redis.io/topics/sentinel) -* [Guidelines for Redis clients with support for Redis Sentinel](https://redis.io/topics/sentinel-clients) -* [Redis Commands](https://redis.io/commands) -* [Kubernetes ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap) +* [Redis Sentinel](https://redis.io/topics/sentinel/) +* [Guidelines for Redis clients with support for Redis Sentinel](https://redis.io/topics/sentinel-clients/) +* [Redis Commands](https://redis.io/commands/) +* [Kubernetes ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) diff --git a/docs/user-guide/delegation.md b/docs/user-guide/delegation.md index 3f18e55ebb1..25be0ae8ea7 100644 --- a/docs/user-guide/delegation.md +++ b/docs/user-guide/delegation.md @@ -115,7 +115,7 @@ kubectl edit rolebinding extra-workload-admins -n user-namespace ## Application Metrics (Grafana) Your administrator will have mapped your IdP groups to the Grafana viewer, editor and admin roles. -Please read the [upstream documentation](https://grafana.com/docs/grafana/latest/permissions/organization_roles/) to learn more. +Please read the [upstream documentation](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/) to learn more. ## Application Logs (OpenSearch Dashboards) diff --git a/docs/user-guide/demarcation.md b/docs/user-guide/demarcation.md index 3158ca74310..72b1cc6722b 100644 --- a/docs/user-guide/demarcation.md +++ b/docs/user-guide/demarcation.md @@ -35,7 +35,8 @@ If you used Kubernetes before, especially if you acted as a Platform Administrat Why? ---- -As previously reported, [Kubernetes is not secure by default, nor by itself](https://searchitoperations.techtarget.com/news/252487963/Kubernetes-security-defaults-prompt-upstream-dilemma). This is due to the fact that Kubernetes prefers to keep its "wow, it just works" experience. This might be fine for a company that does not process personal data. However, if you are in a regulated industry, for example, because you process personal data or health information, your regulators will be extremely unhappy to learn that your platform does not conform to security best practices. +As previously reported, [Kubernetes is not secure by default, nor by itself](https://www.techtarget.com/searchitoperations/news/252487963/Kubernetes-security-defaults-prompt-upstream-dilemma). +This is due to the fact that Kubernetes prefers to keep its "wow, it just works" experience. This might be fine for a company that does not process personal data. However, if you are in a regulated industry, for example, because you process personal data or health information, your regulators will be extremely unhappy to learn that your platform does not conform to security best practices. In case of Compliant Kubernetes this implies a clear separation of roles and responsibilities between Compliant Kubernetes users and administrators. The mission of administrators is to make you, the Compliant Kubernetes user, succeed. Besides allowing you to develop features as fast as possible, the administrator also needs to ensure that you build on top of a platform that lives up to regulatory requirements, specifically data privacy and data security regulations. @@ -53,7 +54,7 @@ More technically, Compliant Kubernetes does not allow users to: * change the Kubernetes API through [CustomResourceDefinitions](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) or [Dynamic Webhooks](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#what-are-admission-webhooks); -* gain more container execution permissions by mutating [PodSecurityPolicies](https://kubernetes.io/docs/concepts/policy/pod-security-policy/); this implies that you cannot run container images as root or mount [hostPaths](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath); +* gain more container execution permissions by mutating [PodSecurityPolicies](https://kubernetes.io/docs/concepts/security/pod-security-policy/); this implies that you cannot run container images as root or mount [hostPaths](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath); * mutate ClusterRoles or Roles so as to [escalate privileges](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping); * mutate Kubernetes resources in administrator-owned namespaces, such as `monitoring` or `kube-system`; * re-configure system Pods, such as Prometheus or fluentd; diff --git a/docs/user-guide/faq.md b/docs/user-guide/faq.md index d2b392fd03a..ce59f7148db 100644 --- a/docs/user-guide/faq.md +++ b/docs/user-guide/faq.md @@ -138,7 +138,7 @@ Get in touch with your administrator to check the status. They are responsible f * Non-option 2: Let admins type the encryption key on the VM's console. Asking admins to do this is time-consuming, error-prone, effectivly jeopardizing uptime. Instead, Compliant Kubernetes recommends automatic VM reboots during application "quiet times", such as at night, to ensure the OS is patched without sacrificing uptime. - * Non-option 3: Let the VM pull the encryption key via instance metadata or [instance configuration](https://cloudinit.readthedocs.io/en/latest/topics/format.html#cloud-config-data). This would imply storing the encryption key on the Infrastructure Provider. If the Infrastructure Provider doesn't have encryption-at-rest, then the encryption key is also stored unencrypted, likely on the same server as the VM is running. Hence, this quickly ends up being security theatre. + * Non-option 3: Let the VM pull the encryption key via instance metadata or [instance configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#cloud-config-data). This would imply storing the encryption key on the Infrastructure Provider. If the Infrastructure Provider doesn't have encryption-at-rest, then the encryption key is also stored unencrypted, likely on the same server as the VM is running. Hence, this quickly ends up being security theatre. * Non-option 4: Let the VM pull the encryption key from an external location which features encryption-at-rest. This would imply that the VM needs some kind of credentials to authenticate to the external location. Again these credentials are stored unencrypted on the Infrastructure Provider, so we are back to non-option 3. diff --git a/docs/user-guide/metrics.md b/docs/user-guide/metrics.md index e35ed1ac414..263f88bb3fa 100644 --- a/docs/user-guide/metrics.md +++ b/docs/user-guide/metrics.md @@ -90,7 +90,7 @@ The screenshot below shows Grafana in "Explore" mode (the compass icon to the le ![Example of User Demo Metrics](../img/user-demo-metrics.jpeg) -The "Explore" mode is great for developing queries and exploring the data set. If you want to save a query so you can refer back to it, you can create a Dashboard instead. Dashboards consist of multiple Panels, each of which, can display the results of running queries. Learn more [about Grafana panels](https://grafana.com/docs/grafana/latest/panels/). +The "Explore" mode is great for developing queries and exploring the data set. If you want to save a query so you can refer back to it, you can create a Dashboard instead. Dashboards consist of multiple Panels, each of which, can display the results of running queries. Learn more [about Grafana panels](https://grafana.com/docs/grafana/latest/panels-visualizations/). !!!note You may want to save frequently used Dashboards. Compliant Kubernetes saves and backs these up for you. diff --git a/docs/user-guide/prepare.md b/docs/user-guide/prepare.md index 5521c9bbe2e..4c3b5447be1 100644 --- a/docs/user-guide/prepare.md +++ b/docs/user-guide/prepare.md @@ -36,7 +36,7 @@ Required software: Your cluster management software of choice, of which you can choose either or both: -* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/), a command-line tool to help manage your Kubernetes resources +* [kubectl](https://kubernetes.io/docs/tasks/tools/), a command-line tool to help manage your Kubernetes resources * [OpenLens](https://github.com/MuhammedKalkan/OpenLens/releases), a graphical user interface to help manage your Kubernetes resources (see also our [dedicated page on Lens integration](kubernetes-ui.md)) Optional, but very useful, tools for developers and DevOps engineers: diff --git a/mkdocs-ciso-controls/ciso_controls/__init__.py b/mkdocs-ciso-controls/ciso_controls/__init__.py index ff7bb0460c8..c753b1fe5b6 100644 --- a/mkdocs-ciso-controls/ciso_controls/__init__.py +++ b/mkdocs-ciso-controls/ciso_controls/__init__.py @@ -111,7 +111,7 @@ def __render_tag_links(self, tag, pages, self_page): def _render_tag(self, tag): tags_index = self._tag_to_tags_index(tag) url = self.root_url + \ - self.slugify(tags_index) + "#" + \ + self.slugify(tags_index) + "/#" + \ self.slugify(tag) return dict(name = tag, type = type, url = url) diff --git a/mkdocs-ciso-controls/setup.py b/mkdocs-ciso-controls/setup.py index 53274c249f2..e5a95c62fe7 100644 --- a/mkdocs-ciso-controls/setup.py +++ b/mkdocs-ciso-controls/setup.py @@ -6,7 +6,7 @@ setup( name="mkdocs-ciso-controls", - version="0.0.1", + version="0.0.2", author="Cristian Klein", author_email="cristian.klein@elastisys.com", description="Use tags to capture ISMS controls and group them by source.", diff --git a/mkdocs.yml b/mkdocs.yml index f5b5b27c288..7a6d9a8867a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -220,4 +220,4 @@ nav: - 'Support': 'https://elastisys.com/' - 'Training': 'https://elastisys.com/training/' - 'Blog': 'https://elastisys.com/blog/' - - 'Privacy Policy': 'https://elastisys.com/privacy-policy/' + - 'Privacy Policy': 'https://elastisys.com/legal/privacy-policy/'