From 7ce507a10e03dbbce0eb4220a8e08196ad0d1cb8 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Wed, 11 Jan 2023 15:13:26 +0100 Subject: [PATCH 1/3] Add conversion script --- docs/convert/compare_screenshots.py | 16 ++++++++++ docs/convert/config.yaml | 1 + docs/convert/conversions.yaml | 1 + docs/convert/convert.sh | 12 ++++++++ docs/convert/revert.sh | 4 +++ docs/convert/screenshots.py | 47 +++++++++++++++++++++++++++++ docs/convert/shell.nix | 17 +++++++++++ 7 files changed, 98 insertions(+) create mode 100644 docs/convert/compare_screenshots.py create mode 100644 docs/convert/config.yaml create mode 100644 docs/convert/conversions.yaml create mode 100644 docs/convert/convert.sh create mode 100644 docs/convert/revert.sh create mode 100644 docs/convert/screenshots.py create mode 100644 docs/convert/shell.nix diff --git a/docs/convert/compare_screenshots.py b/docs/convert/compare_screenshots.py new file mode 100644 index 0000000000..c5b4d9eca1 --- /dev/null +++ b/docs/convert/compare_screenshots.py @@ -0,0 +1,16 @@ +#!/usr/bin/env sh + +import subprocess +import os + +output = subprocess.check_output(['find', 'screenshots', '-name', '*_dev.png']).decode('utf8') + +for dev in output.splitlines(): + ref = dev.replace('_dev.png', '_ref.png') + if os.path.exists(dev) and os.path.exists(ref): + print(dev) + cmd = ['compare', '-compose', 'src', dev, ref, dev.replace('_dev.png', '_diff.png')] + print(cmd) + subprocess.run(cmd) + else: + print(f'Cannot compare {dev}') diff --git a/docs/convert/config.yaml b/docs/convert/config.yaml new file mode 100644 index 0000000000..78f2c64c8f --- /dev/null +++ b/docs/convert/config.yaml @@ -0,0 +1 @@ +colon_fences: false diff --git a/docs/convert/conversions.yaml b/docs/convert/conversions.yaml new file mode 100644 index 0000000000..cbeb844cb6 --- /dev/null +++ b/docs/convert/conversions.yaml @@ -0,0 +1 @@ +sphinx.domains.std.Glossary: eval_rst diff --git a/docs/convert/convert.sh b/docs/convert/convert.sh new file mode 100644 index 0000000000..11a4a33fe7 --- /dev/null +++ b/docs/convert/convert.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# +set -e +# shellcheck disable=SC2044,SC3010 +for f in $(find . -type f -name '*.rst'); do + if [[ "$f" == */includes/* ]]; then + echo skipping "$f" + continue + fi + rst2myst convert -c convert/conversions.yaml --no-colon-fences "$f" + rm -f "$f" +done diff --git a/docs/convert/revert.sh b/docs/convert/revert.sh new file mode 100644 index 0000000000..df5cf912b3 --- /dev/null +++ b/docs/convert/revert.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh + +git checkout src +git clean src -f diff --git a/docs/convert/screenshots.py b/docs/convert/screenshots.py new file mode 100644 index 0000000000..ff172710d5 --- /dev/null +++ b/docs/convert/screenshots.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +from selenium import webdriver +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.common.by import By +import subprocess +import os.path + +def sanitize_name(name): + r = '' + for c in name: + if c.isalpha(): + r += c + else: + r += '_' + return r + +driver = webdriver.Firefox() + +output = subprocess.check_output(['find', 'build', '-name', '*.html']).decode('utf8') +for i, p in enumerate(output.splitlines()): + n = os.path.relpath(p, 'build') + url_dev = f'http://localhost:3000/{n}' + url_ref = f'https://docs.wire.com/{n}' + img_basename = sanitize_name(n) + '_' + str(i) + + try: + print(f'./screenshots/{i:03}-{img_basename}_dev.png') + driver.get(url_dev) + driver.get_full_page_screenshot_as_file(f'./screenshots/{i:03}-{img_basename}_dev.png') + print(url_ref) + driver.get(url_ref) + driver.get_full_page_screenshot_as_file(f'./screenshots/{i:03}-{img_basename}_ref.png') + except: + pass + +driver.close() + + + +# assert "Python" in driver.title +# elem = driver.find_element(By.NAME, "q") +# elem.clear() +# elem.send_keys("pycon") +# elem.send_keys(Keys.RETURN) +# assert "No results found." not in driver.page_source +# diff --git a/docs/convert/shell.nix b/docs/convert/shell.nix new file mode 100644 index 0000000000..130c456c6d --- /dev/null +++ b/docs/convert/shell.nix @@ -0,0 +1,17 @@ +{ pkgs ? import {} }: +(pkgs.buildFHSUserEnv { + name = "pipzone"; + targetPkgs = pkgs: (with pkgs; [ + python3 + python3Packages.pip + python3Packages.virtualenv + ]); + runScript = "bash"; +}).env + +# then +# virtualenv venv +# pip install rst-to-myst +# Fix this bug locally: https://github.com/executablebooks/rst-to-myst/issues/49 +# pip install sphinx-reredirects +# pip install sphinx-multiversion From c8dc4e3e9ca00cb24ca2a6996458d0252196ad85 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Thu, 12 Jan 2023 11:51:12 +0100 Subject: [PATCH 2/3] Refactor rst files so that the rst2myst works --- docs/src/conf.py | 3 +- .../src/developer/reference/spar-braindump.md | 2 +- docs/src/how-to/administrate/cassandra.rst | 2 + .../src/how-to/administrate/elasticsearch.rst | 7 +- docs/src/how-to/administrate/etcd.rst | 4 +- .../kubernetes/restart-machines/index.rst | 2 +- .../kubernetes/upgrade-cluster/index.rst | 2 +- docs/src/how-to/administrate/minio.rst | 6 +- docs/src/how-to/administrate/operations.rst | 23 +++---- docs/src/how-to/administrate/restund.rst | 1 + docs/src/how-to/administrate/users.rst | 8 +-- docs/src/how-to/index.rst | 10 +-- docs/src/how-to/install/ansible-VMs.rst | 4 +- docs/src/how-to/install/aws-prod.rst | 4 +- .../how-to/install/configuration-options.rst | 11 ++-- docs/src/how-to/install/dependencies.rst | 2 +- docs/src/how-to/install/helm-prod.rst | 4 +- .../install/includes/dns-federation.rst | 43 ------------- .../helm_dns-ingress-troubleshooting.inc.rst | 2 - docs/src/how-to/install/index.rst | 36 +++++------ docs/src/how-to/install/logging.rst | 2 +- docs/src/how-to/install/planning.rst | 4 +- docs/src/how-to/install/prod-intro.rst | 10 +-- docs/src/how-to/install/restund.rst | 2 +- docs/src/how-to/install/sft.rst | 4 +- docs/src/how-to/install/tls.rst | 4 +- .../how-to/install/version-requirements.rst | 2 +- docs/src/how-to/single-sign-on/azure/main.rst | 2 +- .../how-to/single-sign-on/centrify/main.rst | 2 +- .../how-to/single-sign-on/generic-setup.rst | 2 +- docs/src/how-to/single-sign-on/index.rst | 15 +++++ docs/src/how-to/single-sign-on/okta/main.rst | 6 +- .../Wire_SAML_Flow (lucidchart).svg | 0 .../understand}/Wire_SAML_Flow.png | Bin .../single-sign-on/understand}/main.rst | 24 ++++--- .../understand}/token-step-01.png | Bin .../understand}/token-step-02.png | Bin .../understand}/token-step-03.png | Bin .../understand}/token-step-04.png | Bin .../understand}/token-step-05.png | Bin .../understand}/token-step-06.png | Bin docs/src/index.rst | 14 ++-- .../api-client-perspective/authentication.rst | 60 ++++++++---------- docs/src/understand/overview.rst | 18 +++--- docs/src/understand/restund.rst | 10 +-- docs/src/understand/single-sign-on/design.rst | 3 - 46 files changed, 163 insertions(+), 197 deletions(-) delete mode 100644 docs/src/how-to/install/includes/dns-federation.rst create mode 100644 docs/src/how-to/single-sign-on/index.rst rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/Wire_SAML_Flow (lucidchart).svg (100%) rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/Wire_SAML_Flow.png (100%) rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/main.rst (96%) rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/token-step-01.png (100%) rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/token-step-02.png (100%) rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/token-step-03.png (100%) rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/token-step-04.png (100%) rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/token-step-05.png (100%) rename docs/src/{understand/single-sign-on => how-to/single-sign-on/understand}/token-step-06.png (100%) delete mode 100644 docs/src/understand/single-sign-on/design.rst diff --git a/docs/src/conf.py b/docs/src/conf.py index e7c36d04e6..1d2c7fa490 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -128,6 +128,5 @@ "security-responses/log4shell": "2021-12-15_log4shell.html", "security-responses/cve-2021-44521": "2022-02-21_cve-2021-44521.html", "security-responses/2022-05_website_outage": "2022-05-23_website_outage.html", - "how-to/single-sign-on/index": "../../understand/single-sign-on/main.html#setting-up-sso-externally", - "how-to/scim/index": "../../understand/single-sign-on/main.html#user-provisioning", + "how-to/scim/index": "../../understand/single-sign-on/main.html#user-provisioning" } diff --git a/docs/src/developer/reference/spar-braindump.md b/docs/src/developer/reference/spar-braindump.md index f32532108b..dcee5847e9 100644 --- a/docs/src/developer/reference/spar-braindump.md +++ b/docs/src/developer/reference/spar-braindump.md @@ -113,7 +113,7 @@ export IDP_ID=... Copy the new metadata file to one of your spar instances. -Ssh into it. If you can't, [the sso docs](../../understand/single-sign-on/main.rst) explain how you can create a +Ssh into it. If you can't, [the sso docs](../../how-to/single-sign-on/understand/main.rst) explain how you can create a bearer token if you have the admin's login credentials. If you follow that approach, you need to replace all mentions of `-H'Z-User ...'` with `-H'Authorization: Bearer ...'` in the following, and you won't need diff --git a/docs/src/how-to/administrate/cassandra.rst b/docs/src/how-to/administrate/cassandra.rst index 180a8f2a8c..f92fddc3a8 100644 --- a/docs/src/how-to/administrate/cassandra.rst +++ b/docs/src/how-to/administrate/cassandra.rst @@ -6,6 +6,8 @@ Cassandra This section only covers the bare minimum, for more information, see the `cassandra documentation `__ +.. _check-the-health-of-a-cassandra-node: + Check the health of a Cassandra node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/src/how-to/administrate/elasticsearch.rst b/docs/src/how-to/administrate/elasticsearch.rst index 3a101a7645..829a8526b9 100644 --- a/docs/src/how-to/administrate/elasticsearch.rst +++ b/docs/src/how-to/administrate/elasticsearch.rst @@ -51,9 +51,11 @@ You should expect some output like this from the above command: How to manually look into what is stored in elasticsearch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -See also the elasticsearch sections in :ref:`investigative_tasks`. +See also the elasticsearch sections in :ref:`investigative-tasks`. +.. _check-the-health-of-an-elasticsearch-node: + Check the health of an elasticsearch node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -112,7 +114,8 @@ Solution: sudo apt autoremove sudo reboot -As always, and as explained in the `operations/procedures page `__, make sure you `check the health of the process `__. before and after the reboot. + +As always make sure you :ref:`check the health of the process `. before and after the reboot. 3. Get the elastichsearch cluster out of *read-only* mode, run: diff --git a/docs/src/how-to/administrate/etcd.rst b/docs/src/how-to/administrate/etcd.rst index 47bce63d70..f0af9a1b83 100644 --- a/docs/src/how-to/administrate/etcd.rst +++ b/docs/src/how-to/administrate/etcd.rst @@ -5,6 +5,8 @@ Etcd This section only covers the bare minimum, for more information, see the `etcd documentation `__ +.. _how-to-see-cluster-health: + How to see cluster health ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -87,7 +89,7 @@ Now to perform a rolling restart of the cluster, do the following steps: *For more details please refer to the official documentation:* `Replacing a failed etcd member `__ -.. _etcd_backup-and-restore: +.. _etcd-backup-and-restore: Backing up and restoring ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/src/how-to/administrate/kubernetes/restart-machines/index.rst b/docs/src/how-to/administrate/kubernetes/restart-machines/index.rst index 4f4a315a93..8090b3c366 100644 --- a/docs/src/how-to/administrate/kubernetes/restart-machines/index.rst +++ b/docs/src/how-to/administrate/kubernetes/restart-machines/index.rst @@ -23,7 +23,7 @@ to take its implications into account (see :ref:`How to rolling-restart an etcd when restarting a machine. Regardless of where *etcd* is located, before turning off any machine that is part of the control plane, one should -:ref:`back up the cluster state `. +:ref:`back up the cluster state `. If a part of the control plane does not run sufficiently redundant, it is advised to prevent any mutating interaction during the procedure, until the cluster is healthy again. diff --git a/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.rst b/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.rst index 1c09a137f9..3b1b4137a0 100644 --- a/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.rst +++ b/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.rst @@ -15,7 +15,7 @@ As a result the following questions arise: Depending on the deployment method, the upgrade procedure may vary. It may be reasonable to test the upgrade in a non-production environment first. Regardless of the deployment method, it is recommended to :ref:`back up the cluster state -` before starting to upgrade the cluster. Additional background knowledge +` before starting to upgrade the cluster. Additional background knowledge can be found in the section about :ref:`restarting a machine in an kubernetes cluster `. diff --git a/docs/src/how-to/administrate/minio.rst b/docs/src/how-to/administrate/minio.rst index 6953d1355c..cf6d95fe50 100644 --- a/docs/src/how-to/administrate/minio.rst +++ b/docs/src/how-to/administrate/minio.rst @@ -170,10 +170,12 @@ role. For more information, please refer to the *Credentials* section in the `official documentation `__. +.. _check-the-health-of-a-minio-node: + Check the health of a MinIO node ================================ -This is the procedure to check a minio node's health. +This is the procedure to check a minio node's health First log into the minio server @@ -251,4 +253,4 @@ Log into minio ( repeat the steps above ), and check again. You should see a very low uptime value on two hosts now. -This is because we install minio 'twice' on each host. \ No newline at end of file +This is because we install minio 'twice' on each host. diff --git a/docs/src/how-to/administrate/operations.rst b/docs/src/how-to/administrate/operations.rst index bee240acb1..16b3cebdba 100644 --- a/docs/src/how-to/administrate/operations.rst +++ b/docs/src/how-to/administrate/operations.rst @@ -9,19 +9,19 @@ Reboot procedures The general procedure to reboot a service is as follows: -* 1. `Check the health `__ of the service. (If the health isn't good, move to `troubleshooting `__. If it is good, move to the next step.) +* 1. :ref:`Check the health ` of the service. (If the health isn't good search for "troubleshooting" in the documentation. If it is good, move to the next step.) * 2. Reboot the server the service is running on. -* 3. `Check the health `__ of the service **again**. (If the health isn't good, move to `troubleshooting `__. If it is good, your reboot was succesful.) +* 3. :ref:`Check the health ` of the service **again**. (If the health isn't good search for "troubleshooting" in the documentation. If it is good, your reboot was succesful.) -The method for checking health is different for each service type, you can find a list of those methods `here `__. +The method for checking health is different for each service type, you can find a list of those methods :ref:`here `. -The method to reset a service is the same for most services, except for ``restund``, for which the procedure is different, and can be found `here `__. +The method to reset a service is the same for most services, except for ``restund``, for which the procedure is different, and can be found :ref:`here `. For other (non-``restund``) services, the procedure is as follows: Assuming in this example you are trying to reboot a minio server, follow these steps: -First, `check the health `__ of the services. +First, :ref:`check the health ` of the services. Second, reboot the services: @@ -37,18 +37,19 @@ Third, wait until the service is up again by trying to connect to it via SSH : (``ConnectionAttempts`` will make it so it attempts to connect until the host is actually Up and the connection is succesful) -Fourth, `check the health `__ of the service again. +Fourth, :ref:`check the health ` of the service again. +.. _operations-health-checks: Health checks ------------- This is a list of the health-checking procedures currently documented, for different service types: -* `MinIO `__. -* `Cassandra `__. -* `elasticsearch `__. -* `Etcd `__. -* `Restund `__ (the health check is explained as part of the reboot procedure). +* :ref:`MinIO ` +* :ref:`Cassandra ` +* :ref:`Elasticsearch ` +* :ref:`Etcd ` +* :ref:`Restund ` (the health check is explained as part of the reboot procedure). To check the health of different services not listed here, see the documentation for that specific project, or ask your Wire contact. diff --git a/docs/src/how-to/administrate/restund.rst b/docs/src/how-to/administrate/restund.rst index 584066ab43..21c658500a 100644 --- a/docs/src/how-to/administrate/restund.rst +++ b/docs/src/how-to/administrate/restund.rst @@ -103,6 +103,7 @@ With downtime, it's very easy:: Restarting ``restund`` means any user that is currently connected to it (i.e. having a call) will lose its audio/video connection. If you wish to have no downtime, check the next section* +.. _rebooting-a-restund-node: Rebooting a Restund node ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/src/how-to/administrate/users.rst b/docs/src/how-to/administrate/users.rst index e7d1e856dc..d8f522d2f6 100644 --- a/docs/src/how-to/administrate/users.rst +++ b/docs/src/how-to/administrate/users.rst @@ -1,4 +1,4 @@ -.. _investigative_tasks: +.. _investigative-tasks: Investigative tasks (e.g. searching for users as server admin) --------------------------------------------------------------- @@ -109,7 +109,7 @@ This will give you a list of handles and IDs with no team associated: null | null | 1b5ca44a-aeb4-4a68-861b-48612438c4cc null | bob | 701b4eab-6df2-476d-a818-90dc93e8446e -You can then `delete each user with these instructions <./users.html#deleting-a-user-which-is-not-a-team-user>`__. +You can then :ref:`delete each user with these instructions `. Manual search on elasticsearch (via brig, recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -315,7 +315,7 @@ Some of the pods allow you to grab metrics by accessing their ``/i/metrics`` end * ``gundeck``: Push Notifications API * ``spar``: Single-Sign-ON and SCIM -For more details on the various services/pods, you can check out `this link <../../understand/overview.html?highlight=gundeck#focus-on-pods>`. +For more details on the various services/pods, you can check out :ref:`this link `. Before you can grab metrics from a pod, you need to find its IP address. You do this by running the following command: @@ -403,7 +403,7 @@ Output will look something like this (truncated): # TYPE gc_bytes_allocated_total gauge gc_bytes_allocated_total 4.949156056e9 -This example is for Gundeck, but you can also get metrics for other services. All k8s services are listed at `this link <../../understand/overview.html?highlight=gundeck#focus-on-pods>`__. +This example is for Gundeck, but you can also get metrics for other services. All k8s services are listed at :ref:`this link `. This is an example adapted for Cannon: diff --git a/docs/src/how-to/index.rst b/docs/src/how-to/index.rst index 1ba77e0302..c5756db287 100644 --- a/docs/src/how-to/index.rst +++ b/docs/src/how-to/index.rst @@ -7,7 +7,7 @@ server components. .. warning:: If you already installed Wire by using ``poetry``, please refer to the - `old version `__ of + `old version `__ of the installation guide. @@ -15,7 +15,7 @@ server components. :maxdepth: 2 :glob: - How to install wire-server - How to verify your wire-server installation - How to administrate servers after successful installation - How to connect the public wire clients to your wire-server installation + How to install wire-server + How to verify your wire-server installation + How to administrate servers after successful installation + How to connect the public wire clients to your wire-server installation diff --git a/docs/src/how-to/install/ansible-VMs.rst b/docs/src/how-to/install/ansible-VMs.rst index 46c818f211..ced5a5eda6 100644 --- a/docs/src/how-to/install/ansible-VMs.rst +++ b/docs/src/how-to/install/ansible-VMs.rst @@ -1,4 +1,4 @@ -.. _ansible_vms: +.. _ansible-vms: Installing kubernetes and databases on VMs with ansible ======================================================= @@ -274,4 +274,4 @@ You can now can install the helm charts. Next steps for high-available production installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Your next step will be :ref:`helm_prod` +Your next step will be :ref:`helm-prod` diff --git a/docs/src/how-to/install/aws-prod.rst b/docs/src/how-to/install/aws-prod.rst index 0cf147bc20..16cdf65c5f 100644 --- a/docs/src/how-to/install/aws-prod.rst +++ b/docs/src/how-to/install/aws-prod.rst @@ -1,4 +1,4 @@ -.. _aws_prod: +.. _aws-prod: Configuring AWS and wire-server (production) components ======================================================= @@ -12,7 +12,7 @@ Using real AWS services for SNS -------------------------------------------------------- AWS SNS is required to send notification events to clients via `FCM `__/`APNS `__ . These notification channels are useable only for clients that are connected from the public internet. Using these vendor provided communication channels allows client devices (phones) running a wire client to save a considerable amount of battery life, compared to the websockets approach. -For details on how to set up SNS in cooperation with us (We - Wire - will proxy push notifications through Amazon for you), see :ref:`pushsns`. +For details on how to set up SNS in cooperation with us (We - Wire - will proxy push notifications through Amazon for you), see :ref:`push-sns`. Using real AWS services for SES / SQS --------------------------------------------- diff --git a/docs/src/how-to/install/configuration-options.rst b/docs/src/how-to/install/configuration-options.rst index 869dc6c603..ee2302006e 100644 --- a/docs/src/how-to/install/configuration-options.rst +++ b/docs/src/how-to/install/configuration-options.rst @@ -1,5 +1,4 @@ -.. _configuration_options: - +.. _configuration-options: Part 3 - configuration options in a production setup ==================================================================== @@ -48,7 +47,7 @@ Assuming your proxy can be reached from within Kubernetes at ``http://proxy:8080 Depending on your setup, you may need to repeat this for the other services like ``brig`` as well. -.. _pushsns: +.. _push-sns: Enable push notifications using the public appstore / playstore mobile Wire clients ----------------------------------------------------------------------------------- @@ -185,7 +184,7 @@ security issue. To prevent this, there are fine-grained settings in the nginz configuration defining which upstreams should exist. Default upstreams -^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~ Upstreams for services that exist in (almost) every Wire installation are enabled by default. These are: @@ -211,7 +210,7 @@ The most common example is to disable the upstream of ``cannon``: Optional upstreams -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ There are some services that are usually not deployed on most Wire installations or are specific to the Wire cloud: @@ -234,7 +233,7 @@ The most common example is to enable the (extra) upstream of ``proxy``: Combining default and extra upstream configurations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default and extra upstream configurations are independent of each other. I.e. ``nginz.nginx_conf.ignored_upstreams`` and diff --git a/docs/src/how-to/install/dependencies.rst b/docs/src/how-to/install/dependencies.rst index 4c50f38d25..1533a113d4 100644 --- a/docs/src/how-to/install/dependencies.rst +++ b/docs/src/how-to/install/dependencies.rst @@ -49,7 +49,7 @@ checkout is pointing to. bash-4.4# ansible --version ansible 2.9.12 -Once you're in there, you can move on to `installing kubernetes `__ +Once you're in there, you can move on to :ref:`installing kubernetes `. (Alternative) Installing dependencies using Direnv and Nix diff --git a/docs/src/how-to/install/helm-prod.rst b/docs/src/how-to/install/helm-prod.rst index fb9b81841d..a6690a768a 100644 --- a/docs/src/how-to/install/helm-prod.rst +++ b/docs/src/how-to/install/helm-prod.rst @@ -1,4 +1,4 @@ -.. _helm_prod: +.. _helm-prod: Installing wire-server (production) components using Helm ========================================================= @@ -13,7 +13,7 @@ Introduction The following will install a version of all the wire-server components. These instructions are for reference, and may not set up what you would consider a production environment, due to the fact that there are varying definitions of 'production ready'. These instructions will cover what we consider to be a useful overlap of our users' production needs. They do not cover load balancing/distributing, using multiple datacenters, federating wire, or other forms of intercontinental/interplanetary distribution of the wire service infrastructure. If you deviate from these directions and need to contact us for support, please provide the deviations you made to fit your production environment along with your support request. -Some of the instructions here will present you with two options: No AWS, and with AWS. The 'No AWS' instructions will not require any AWS infrastructure, but may have a reduced feature set. The 'with AWS' instructions will assume you have completed the setup procedures in :ref:`aws_prod`. +Some of the instructions here will present you with two options: No AWS, and with AWS. The 'No AWS' instructions will not require any AWS infrastructure, but may have a reduced feature set. The 'with AWS' instructions will assume you have completed the setup procedures in :ref:`aws-prod`. What will be installed? ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/src/how-to/install/includes/dns-federation.rst b/docs/src/how-to/install/includes/dns-federation.rst deleted file mode 100644 index c25184ffbe..0000000000 --- a/docs/src/how-to/install/includes/dns-federation.rst +++ /dev/null @@ -1,43 +0,0 @@ -DNS setup for federation ------------------------- - -SRV record -^^^^^^^^^^ - -One prerequisite to enable federation is an `SRV record `__ as defined in `RFC -2782 `__ that needs to be set up to allow the wire-server to be -discovered by other Wire backends. See the documentation on :ref:`discovery in federation` for more -information on the role of discovery in federation. - -The fields of the SRV record need to be populated as follows - -* ``service``: ``wire-server-federator`` -* ``proto``: ``tcp`` -* ``name``: -* ``TTL``: e.g. 600 (10 minutes) in an initial phase. This can be set to a higher value (e.g. 86400) if your systems are stable and DNS records don't change a lot. -* ``priority``: anything. A good default value would be 0 -* ``weight``: >0 for your server to be reachable. A good default value could be 10 -* ``port``: ``443`` -* ``target``: - -To give an example, assuming - -* your federation :ref:`Backend Domain ` is ``example.com`` -* your domains for other services already set up follow the convention ``.wire.example.org`` - -then your federation :ref:`Infra Domain ` would be ``federator.wire.example.org``. - -The SRV record would look as follows: - -.. code-block:: bash - - # _service._proto.name. ttl IN SRV priority weight port target. - _wire-server-federator._tcp.example.com. 600 IN SRV 0 10 443 federator.wire.example.org. - -DNS A record for the federator -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Background: ``federator`` is the server component responsible for incoming and outgoing requests to other backend; but it is proxied on -the incoming requests by the ingress component on kubernetes as shown in :ref:`Federation Architecture` - -As mentioned in :ref:`DNS setup for Helm`, you also need a ``federator.`` record, which, alongside your other DNS records that point to the ingress component, also needs to point to the IP of your ingress, i.e. the IP you want to provide services on. diff --git a/docs/src/how-to/install/includes/helm_dns-ingress-troubleshooting.inc.rst b/docs/src/how-to/install/includes/helm_dns-ingress-troubleshooting.inc.rst index 90b9e1f3b5..610ca8c784 100644 --- a/docs/src/how-to/install/includes/helm_dns-ingress-troubleshooting.inc.rst +++ b/docs/src/how-to/install/includes/helm_dns-ingress-troubleshooting.inc.rst @@ -143,8 +143,6 @@ Next, we want to redirect port 443 to the port the nginx https ingress nodeport * Option 2: Use ansible to do that, run the `iptables playbook `__ -.. include:: ./includes/dns-federation.rst - Trying things out ----------------- diff --git a/docs/src/how-to/install/index.rst b/docs/src/how-to/install/index.rst index 03802f43c7..aa45f4b41a 100644 --- a/docs/src/how-to/install/index.rst +++ b/docs/src/how-to/install/index.rst @@ -5,26 +5,26 @@ Installing wire-server :maxdepth: 2 :glob: - How to plan an installation - Version requirements + How to plan an installation + Version requirements dependencies - (demo) How to install kubernetes - (demo) How to install wire-server using Helm - (production) Introduction - (production) How to install kubernetes and databases - (production) How to configure AWS services - (production) How to install wire-server using Helm - (production) How to monitor wire-server - (production) How to see centralized logs for wire-server - (production) Other configuration options - Server and team feature settings - Messaging Layer Security (MLS) - Web app settings + (demo) How to install kubernetes + (demo) How to install wire-server using Helm + (production) Introduction + (production) How to install kubernetes and databases + (production) How to configure AWS services + (production) How to install wire-server using Helm + (production) How to monitor wire-server + (production) How to see centralized logs for wire-server + (production) Other configuration options + Server and team feature settings + Messaging Layer Security (MLS) + Web app settings sft restund configure-federation tls - How to install and set up Legal Hold - Managing authentication with ansible - Using tinc - Troubleshooting during installation + How to install and set up Legal Hold + Managing authentication with ansible + Using tinc + Troubleshooting during installation diff --git a/docs/src/how-to/install/logging.rst b/docs/src/how-to/install/logging.rst index 5d9368c83c..dc2808f1d1 100644 --- a/docs/src/how-to/install/logging.rst +++ b/docs/src/how-to/install/logging.rst @@ -19,7 +19,7 @@ Prerequisites You need to have wire-server installed, see either of * :ref:`helm` -* :ref:`helm_prod`. +* :ref:`helm-prod`. Installing required helm charts diff --git a/docs/src/how-to/install/planning.rst b/docs/src/how-to/install/planning.rst index 29e84f97a6..eae18fb9a1 100644 --- a/docs/src/how-to/install/planning.rst +++ b/docs/src/how-to/install/planning.rst @@ -35,7 +35,7 @@ Next steps for demo installation If you already have a kubernetes cluster, your next step will be :ref:`helm`, otherwise, your next step will be :ref:`ansible-kubernetes` -.. _planning_prod: +.. _planning-prod: Production installation (persistent data, high-availability) -------------------------------------------------------------- @@ -74,4 +74,4 @@ Avoid `10.x.x.x` network address schemes, and instead use something like `192.16 Next steps for high-available production installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Your next step will be :ref:`ansible_vms` +Your next step will be :ref:`ansible-vms` diff --git a/docs/src/how-to/install/prod-intro.rst b/docs/src/how-to/install/prod-intro.rst index 420b5fc296..ed56b4ebcd 100644 --- a/docs/src/how-to/install/prod-intro.rst +++ b/docs/src/how-to/install/prod-intro.rst @@ -10,13 +10,13 @@ Introduction A production installation consists of several parts: -Part 1 - you're on your own here, and need to create a set of VMs as detailed in :ref:`planning_prod` +Part 1 - you're on your own here, and need to create a set of VMs as detailed in :ref:`planning-prod` -Part 2 (:ref:`ansible_vms`) deals with installing components directly on a set of virtual machines, such as kubernetes itself, as well as databases. It makes use of ansible to achieve that. +Part 2 (:ref:`ansible-vms`) deals with installing components directly on a set of virtual machines, such as kubernetes itself, as well as databases. It makes use of ansible to achieve that. -Part 3 (:ref:`helm_prod`) is similar to the demo installation, and uses the tool ``helm`` to install software on top of kubernetes. +Part 3 (:ref:`helm-prod`) is similar to the demo installation, and uses the tool ``helm`` to install software on top of kubernetes. -Part 4 (:ref:`configuration_options`) details other possible configuration options and settings to fit your needs. +Part 4 (:ref:`configuration-options`) details other possible configuration options and settings to fit your needs. What will be installed by following these parts? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,4 +57,4 @@ Getting support Next steps for high-available production installation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Your next step will be part 2, :ref:`ansible_vms` +Your next step will be part 2, :ref:`ansible-vms` diff --git a/docs/src/how-to/install/restund.rst b/docs/src/how-to/install/restund.rst index 732f0d0e26..dd586c4916 100644 --- a/docs/src/how-to/install/restund.rst +++ b/docs/src/how-to/install/restund.rst @@ -58,7 +58,7 @@ To Install Restund, do the following: ansible-playbook -i hosts.ini restund.yml -vv -For information on setting up and using ansible-playbook to install Wire components, see :ref:`this page `. +For information on setting up and using ansible-playbook to install Wire components, see :ref:`this page `. Private Subnets --------------- diff --git a/docs/src/how-to/install/sft.rst b/docs/src/how-to/install/sft.rst index 2824d6827a..caffb71863 100644 --- a/docs/src/how-to/install/sft.rst +++ b/docs/src/how-to/install/sft.rst @@ -55,7 +55,7 @@ Standalone ---------- The SFT component is also shipped as a separate helm chart. Installation is similar to installing -the charts as in :ref:`helm_prod`. +the charts as in :ref:`helm-prod`. Some people might want to run SFT separately, because the deployment lifecycle for the SFT is a bit more intricate. For example, if you want to avoid dropping calls during an upgrade, you'd set the ``terminationGracePeriodSeconds`` of the SFT to a high number, to wait @@ -72,7 +72,7 @@ It is important that you disable ``sftd`` in the ``wire-server`` umbrella chart, By default ``sftd`` doesn't need to set that many options, so we define them inline. However, you could of course also set these values in a ``values.yaml`` file. -SFT will deploy a Kubernetes Ingress on ``$SFTD_HOST``. Make sure that the domain name ``$SFTD_HOST`` points to your ingress IP as set up in :ref:`helm_prod`. The SFT also needs to be made aware of the domain name of the webapp that you set up in :ref:`helm_prod` for setting up the appropriate CSP headers. +SFT will deploy a Kubernetes Ingress on ``$SFTD_HOST``. Make sure that the domain name ``$SFTD_HOST`` points to your ingress IP as set up in :ref:`helm-prod`. The SFT also needs to be made aware of the domain name of the webapp that you set up in :ref:`helm-prod` for setting up the appropriate CSP headers. .. code:: shell diff --git a/docs/src/how-to/install/tls.rst b/docs/src/how-to/install/tls.rst index 8adac3d525..b726cf8446 100644 --- a/docs/src/how-to/install/tls.rst +++ b/docs/src/how-to/install/tls.rst @@ -28,7 +28,7 @@ TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 no no **modern** If you enable TLSv1.3, openssl does always enable the three default cipher suites for TLSv1.3. Therefore it is not necessary to add them to openssl based configurations. -.. _ingress traffic: +.. _ingress-traffic: Ingress Traffic (wire-server) ----------------------------- @@ -47,7 +47,7 @@ The list of TLS ciphers for incoming SFT requests (and metrics) are defined in a SFTD (kubernetes) ----------------- SFTD deployed via kubernetes uses ``kubernetes.io/ingress`` for ingress traffic, configured in `ingress.yaml `_. -Kubernetes based deployments make use of the settings from :ref:`ingress traffic`. +Kubernetes based deployments make use of the settings from :ref:`ingress-traffic`. Restund (ansible) diff --git a/docs/src/how-to/install/version-requirements.rst b/docs/src/how-to/install/version-requirements.rst index 3c204404bb..c86267010e 100644 --- a/docs/src/how-to/install/version-requirements.rst +++ b/docs/src/how-to/install/version-requirements.rst @@ -6,7 +6,7 @@ Required/Supported versions .. warning:: If you already installed Wire by using ``poetry``, please refer to the - `old version `__ of + `old version `__ of the installation guide. diff --git a/docs/src/how-to/single-sign-on/azure/main.rst b/docs/src/how-to/single-sign-on/azure/main.rst index 02115a753f..caac4266b8 100644 --- a/docs/src/how-to/single-sign-on/azure/main.rst +++ b/docs/src/how-to/single-sign-on/azure/main.rst @@ -5,7 +5,7 @@ Preprequisites -------------- - http://azure.microsoft.com account, admin access to that account -- See also :ref:`SSO generic setup`. +- See also :ref:`sso-generic-setup`. Steps ----- diff --git a/docs/src/how-to/single-sign-on/centrify/main.rst b/docs/src/how-to/single-sign-on/centrify/main.rst index 12d6d530fd..55dbb05a9f 100644 --- a/docs/src/how-to/single-sign-on/centrify/main.rst +++ b/docs/src/how-to/single-sign-on/centrify/main.rst @@ -5,7 +5,7 @@ Preprequisites -------------- - http://centrify.com account, admin access to that account -- See also :ref:`SSO generic setup`. +- See also :ref:`sso-generic-setup`. Steps ----- diff --git a/docs/src/how-to/single-sign-on/generic-setup.rst b/docs/src/how-to/single-sign-on/generic-setup.rst index 79f4d9585a..ed8c857500 100644 --- a/docs/src/how-to/single-sign-on/generic-setup.rst +++ b/docs/src/how-to/single-sign-on/generic-setup.rst @@ -1,4 +1,4 @@ -.. _SSO generic setup: +.. _sso-generic-setup: How to set up SSO integration with your IdP =========================================== diff --git a/docs/src/how-to/single-sign-on/index.rst b/docs/src/how-to/single-sign-on/index.rst new file mode 100644 index 0000000000..6992836111 --- /dev/null +++ b/docs/src/how-to/single-sign-on/index.rst @@ -0,0 +1,15 @@ +Single Sign-On and User Provisioning +------------------------------------ + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + :glob: + + Single sign-on and user provisioning + Generic setup + SSO integration with ADFS + SSO integration with Azure + SSO integration with Centrify + SSO integration with Okta + * diff --git a/docs/src/how-to/single-sign-on/okta/main.rst b/docs/src/how-to/single-sign-on/okta/main.rst index faf3799db0..3a051cc5c6 100644 --- a/docs/src/how-to/single-sign-on/okta/main.rst +++ b/docs/src/how-to/single-sign-on/okta/main.rst @@ -1,3 +1,5 @@ +.. _sso-int-with-okta: + How to set up SSO integration with Okta ======================================= @@ -5,7 +7,7 @@ Preprequisites -------------- - http://okta.com/ account, admin access to that account -- See also :ref:`SSO generic setup`. +- See also :ref:`sso-generic-setup`. Steps ----- @@ -47,7 +49,7 @@ Okta setup +=============================+==============================================================================+ | Single Sign On URL | `https://prod-nginz-https.wire.com/sso/finalize-login` | +-----------------------------+------------------------------------------------------------------------------+ -| Use this for Recipient URL | checked ✅ | +| Use this for Recipient URL | checked | | and Destination URL | | +-----------------------------+------------------------------------------------------------------------------+ | Audience URI (SP Entity ID) | `https://prod-nginz-https.wire.com/sso/finalize-login` | diff --git a/docs/src/understand/single-sign-on/Wire_SAML_Flow (lucidchart).svg b/docs/src/how-to/single-sign-on/understand/Wire_SAML_Flow (lucidchart).svg similarity index 100% rename from docs/src/understand/single-sign-on/Wire_SAML_Flow (lucidchart).svg rename to docs/src/how-to/single-sign-on/understand/Wire_SAML_Flow (lucidchart).svg diff --git a/docs/src/understand/single-sign-on/Wire_SAML_Flow.png b/docs/src/how-to/single-sign-on/understand/Wire_SAML_Flow.png similarity index 100% rename from docs/src/understand/single-sign-on/Wire_SAML_Flow.png rename to docs/src/how-to/single-sign-on/understand/Wire_SAML_Flow.png diff --git a/docs/src/understand/single-sign-on/main.rst b/docs/src/how-to/single-sign-on/understand/main.rst similarity index 96% rename from docs/src/understand/single-sign-on/main.rst rename to docs/src/how-to/single-sign-on/understand/main.rst index 8603a8fd71..9893f631e1 100644 --- a/docs/src/understand/single-sign-on/main.rst +++ b/docs/src/how-to/single-sign-on/understand/main.rst @@ -136,6 +136,8 @@ Here is what this looks from a user's perspective: 8. Wire will load your company's login page: log in with your company credentials. +.. _saml-sso: + SAML/SSO ~~~~~~~~ @@ -144,7 +146,7 @@ Introduction SSO (Single Sign-On) is technology allowing users to sign into multiple services with a single identity provider/credential. -SSO is about `authentication`, not `provisioning` (create, update, remove user accounts). To learn more about the latter, continue `below `_. +SSO is about `authentication`, not `provisioning` (create, update, remove user accounts). To learn more about the latter, continue :ref:`below `. For example, if a company already has SSO setup for some of their services, and they start using Wire, they can use Wire's SSO support to add Wire to the set of services their users will be able to sign into with their existing SSO credentials. @@ -159,7 +161,7 @@ Here is a critique of XML/DSig security (which SAML relies on): https://www.cs.a Terminology and concepts ^^^^^^^^^^^^^^^^^^^^^^^^ -* End User / Browser: The end user is generally a human, an Application (Wire Client) or a browser (agent) who accesses the Service Provider to get access to a service or a protected resource. +* End The browser carrries out all the redirections from the SP to the IdP and vice versa. * Service Provider (SP): The entity (here Wire software) that provides its protected resource when an end user tries to access this resource. To accomplish the SAML based SSO authentication, the Service Provider must have the Identity Provider's metadata. @@ -181,15 +183,11 @@ The first step is to configure the Identity Provider: you'll need to register Wi We've put together guides for registering with different providers: -.. toctree:: - :maxdepth: 1 - - Instructions for Okta <../../how-to/single-sign-on/okta/main.rst> - Instructions for Centrify <../../how-to/single-sign-on/centrify/main.rst> - Instructions for Azure <../../how-to/single-sign-on/azure/main.rst> - Some screenshots for ADFS <../../how-to/single-sign-on/adfs/main.rst> - Generic instructions (try this if none of the above are applicable) <../../how-to/single-sign-on/generic-setup.rst> - Trouble shooting & FAQ <../../how-to/single-sign-on/trouble-shooting.rst> +- Instructions for :ref:`Okta ` +- Instructions for :doc:`Centrify <../centrify/main>` +- Instructions for :doc:`Azure <../azure/main>` +- Some screenshots for :doc:`ADFS <../adfs/main>` +- :doc:`Generic instructions (try this if none of the above are applicable) <../generic-setup>` As you do this, make sure you take note of your :term:`IdP` metadata, which you will need for the next step. @@ -223,12 +221,12 @@ If you haven't set up :term:`SCIM` (`we recommend you do <#introduction>`_), you If team members already have Wire accounts, use :term:`SCIM` to associate them with the :term:`SAML` credentials. If you make a mistake here, you may end up with several accounts for the same person. -.. _User provisioning: +.. _user-provisioning-scim-ldap: User provisioning (SCIM/LDAP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -SCIM/LDAP is about `provisioning` (create, update, remove user accounts), not `authentication`. To learn more about the latter, continue `above `_. +SCIM/LDAP is about `provisioning` (create, update, remove user accounts), not `authentication`. To learn more about the latter, continue :ref:`above `. Wire supports the `SCIM `__ (`RFC 7643 `__) protocol to create, update and delete users. diff --git a/docs/src/understand/single-sign-on/token-step-01.png b/docs/src/how-to/single-sign-on/understand/token-step-01.png similarity index 100% rename from docs/src/understand/single-sign-on/token-step-01.png rename to docs/src/how-to/single-sign-on/understand/token-step-01.png diff --git a/docs/src/understand/single-sign-on/token-step-02.png b/docs/src/how-to/single-sign-on/understand/token-step-02.png similarity index 100% rename from docs/src/understand/single-sign-on/token-step-02.png rename to docs/src/how-to/single-sign-on/understand/token-step-02.png diff --git a/docs/src/understand/single-sign-on/token-step-03.png b/docs/src/how-to/single-sign-on/understand/token-step-03.png similarity index 100% rename from docs/src/understand/single-sign-on/token-step-03.png rename to docs/src/how-to/single-sign-on/understand/token-step-03.png diff --git a/docs/src/understand/single-sign-on/token-step-04.png b/docs/src/how-to/single-sign-on/understand/token-step-04.png similarity index 100% rename from docs/src/understand/single-sign-on/token-step-04.png rename to docs/src/how-to/single-sign-on/understand/token-step-04.png diff --git a/docs/src/understand/single-sign-on/token-step-05.png b/docs/src/how-to/single-sign-on/understand/token-step-05.png similarity index 100% rename from docs/src/understand/single-sign-on/token-step-05.png rename to docs/src/how-to/single-sign-on/understand/token-step-05.png diff --git a/docs/src/understand/single-sign-on/token-step-06.png b/docs/src/how-to/single-sign-on/understand/token-step-06.png similarity index 100% rename from docs/src/understand/single-sign-on/token-step-06.png rename to docs/src/how-to/single-sign-on/understand/token-step-06.png diff --git a/docs/src/index.rst b/docs/src/index.rst index 28721d822a..1ec499e9c0 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -23,13 +23,13 @@ This documentation may be expanded in the future to cover other aspects of Wire. :caption: Contents: :glob: - Release notes - Administrator's Guide - Understanding wire-server components - Administrator's manual: single-sign-on and user provisioning - Client API documentation - Security responses - Notes for developers + Release notes + Administrator's Guide + Understanding wire-server components + Single-Sign-On and user provisioning + Client API documentation + Security responses + Notes for developers .. Overview diff --git a/docs/src/understand/api-client-perspective/authentication.rst b/docs/src/understand/api-client-perspective/authentication.rst index 52630c58a6..9e34aa8e23 100644 --- a/docs/src/understand/api-client-perspective/authentication.rst +++ b/docs/src/understand/api-client-perspective/authentication.rst @@ -21,18 +21,16 @@ In order to obtain new access tokens without having to ask the user for his credentials again, so-called "user tokens" are issued which are issued in the form of a ``zuid`` HTTP `cookie `__. These cookies -have a long lifetime (if `persistent <#login-persistent>`__, typically +have a long lifetime (if :ref:`persistent ` typically at least a few months) and their use is strictly limited to the -`/access <#token-refresh>`__ endpoint used for token refresh. -`Persistent <#login-persistent>`__ access cookies are regularly -refreshed as part of an `access token refresh <#token-refresh>`__. +:ref:`/access ` endpoint used for token refresh. +:ref:`Persistent ` access cookies are regularly +refreshed as part of an :ref:`access token refresh `. -An access cookie is obtained either directly after -`registration `__ or through a -subsequent `login <#login>`__. A successful login provides both an -access cookie and and access token. Both access token and cookie must be -stored safely and kept confidential. User passwords should not be -stored. +An access cookie is obtained either directly after registration or through a +subsequent :ref:`login `. A successful login provides both an access +cookie and and access token. Both access token and cookie must be stored safely +and kept confidential. User passwords should not be stored. As of yet, there is no concept of authorising third-party applications to perform operations on the API on behalf of a user (Notable exceptions: @@ -56,12 +54,11 @@ be removed in the future. Login - ``POST /login`` ----------------------- -A login is the process of authenticating a user either through a known -secret in a `password login <#login-password>`__ or by proving ownership -of a verified phone number associated with an account in an `SMS -login <#login-sms>`__. The response to a successful login contains an -access cookie in a ``Set-Cookie`` header and an access token in the JSON -response body. +A login is the process of authenticating a user either through a known secret in +a :ref:`password login ` or by proving ownership of a verified +phone number associated with an account in an :ref:`SMS login `. The +response to a successful login contains an access cookie in a ``Set-Cookie`` +header and an access token in the JSON response body. .. _login-cookies: @@ -172,8 +169,8 @@ the ``phone`` and ``code`` as follows: "code": "123456" } -A successful response is identical to that of a `password -login <#login-password>`__. +A successful response is identical to that of a :ref:`password +login `. @@ -185,7 +182,7 @@ Persistent Logins By default, access cookies are issued as `session cookies `__ with a validity of 1 week. Furthermore, these session cookies are not -refreshed as part of an `access token refresh <#token-refresh>`__. To +refreshed as part of an :ref:`access token refresh `. To request a ``persistent`` access cookie which does get refreshed, specify the ``persist=true`` parameter during a login: @@ -313,9 +310,9 @@ registration, e.g.: "label": "Google Nexus 5" } -Specifying a label is recommended as it helps to identify the cookies in -a user-friendly way and allows `selective -revocation <#cookies-revoke>`__ based on the labels. +Specifying a label is recommended as it helps to identify the cookies in a +user-friendly way and allows :ref:`selective revocation ` based +on the labels. @@ -361,10 +358,10 @@ expire, only as new cookies are issued. Revoking Cookies - ``POST /cookies/remove`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Cookies can be removed individually or in bulk either by specifying the -full cookie structure as it is returned by `GET -/cookies <#cookies-list>`__ or only by their labels in a ``POST`` -request to ``/cookies/remove``, alongside with the user's credentials: +Cookies can be removed individually or in bulk either by specifying the full +cookie structure as it is returned by :ref:`GET /cookies ` or only +by their labels in a ``POST`` request to ``/cookies/remove``, alongside with the +user's credentials: :: @@ -388,11 +385,10 @@ password. Password Reset - ``POST /password-reset`` ----------------------------------------- -A password reset can be used to set a new password if the existing -password associated with an account has been forgotten. This is not to -be confused with the act of merely `changing your -password `__ for the purpose of password -rotation or if you suspect your current password to be compromised. +A password reset can be used to set a new password if the existing password +associated with an account has been forgotten. This is not to be confused with +the act of merely changing your password for the purpose of password rotation or +if you suspect your current password to be compromised. Initiate a Password Reset ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -451,7 +447,7 @@ after which the password reset code becomes invalid and a new password reset must be initiated. A completed password reset results in all access cookies to be revoked, -requiring the user to `login <#login>`__. +requiring the user to :ref:`login `. Related topics: SSO, Legalhold ------------------------------- diff --git a/docs/src/understand/overview.rst b/docs/src/understand/overview.rst index 71d2f2a45d..e913abf089 100644 --- a/docs/src/understand/overview.rst +++ b/docs/src/understand/overview.rst @@ -1,3 +1,5 @@ +.. _overview: + Overview ======== @@ -6,7 +8,7 @@ Introduction In a simplified way, the server components for Wire involve the following: -|arch-simplified| +.. image:: img/architecture-server-simplified.png The Wire clients (such as the Wire app on your phone) connect either directly (or via a load balancer) to the "Wire Server". By "Wire Server" we mean multiple API server components that connect to each other, and which also connect to a few databases. Both the API components and the databases are each in a "cluster", which means copies of the same program code runs multiple times. This allows any one component to fail without users noticing that there is a problem (also called "high-availability"). @@ -20,7 +22,7 @@ are installed with the rest and therefore included. Focus on internet protocols ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -|arch-proto| +.. image:: ./img/architecture-tls-on-prem-2020-09.png Focus on high-availability @@ -28,7 +30,7 @@ Focus on high-availability The following diagram shows a usual setup with multiple VMs (Virtual Machines): -|arch-ha| +.. image:: ../how-to/install/img/architecture-server-ha.png Wire clients (such as the Wire app on your phone) connect to a load balancer. @@ -45,18 +47,14 @@ Backend components startup The Wire server backend is designed to run on a kubernetes cluster. From a high level perspective the startup sequence from machine power-on to the Wire server being ready to receive requests is as follow: -1. *Kubernetes node power on*. Systemd starts the kubelet service which makes the worker node available to kubernetes. For more details about kubernetes startup refer to `the official kubernetes documentation `__. For details about the installation and configuration of kubernetes and worker nodes for Wire server see :ref:`Installing kubernetes and databases on VMs with ansible ` -2. *Kubernetes workload startup*. Kubernetes will ensure that Wire server workloads installed via helm are scheduled on available worker nodes. For more details about workload scheduling refer to `the official kubernetes documentation `__. For details about how to install Wire server with helm refer to :ref:`Installing wire-server (production) components using Helm `. -3. *Stateful workload startup*. Systemd starts the stateful services (cassandra, elasticsearch and minio). See for instance `ansible-cassandra role `__ and other database installation instructions in :ref:`Installing kubernetes and databases on VMs with ansible ` +1. *Kubernetes node power on*. Systemd starts the kubelet service which makes the worker node available to kubernetes. For more details about kubernetes startup refer to `the official kubernetes documentation `__. For details about the installation and configuration of kubernetes and worker nodes for Wire server see :ref:`Installing kubernetes and databases on VMs with ansible ` +2. *Kubernetes workload startup*. Kubernetes will ensure that Wire server workloads installed via helm are scheduled on available worker nodes. For more details about workload scheduling refer to `the official kubernetes documentation `__. For details about how to install Wire server with helm refer to :ref:`Installing wire-server (production) components using Helm `. +3. *Stateful workload startup*. Systemd starts the stateful services (cassandra, elasticsearch and minio). See for instance `ansible-cassandra role `__ and other database installation instructions in :ref:`Installing kubernetes and databases on VMs with ansible ` 4. *Other services*. Systemd starts the restund docker container. See `ansible-restund role `__. For details about docker container startup `consult the official documentation `__ .. note:: For more information about Virual Machine startup or operating system level service startup, please consult your virtualisation and operating system documentation. -.. |arch-simplified| image:: img/architecture-server-simplified.png -.. |arch-proto| image:: ./img/architecture-tls-on-prem-2020-09.png -.. |arch-ha| image:: ../how-to/install/img/architecture-server-ha.png - Focus on pods ~~~~~~~~~~~~~ diff --git a/docs/src/understand/restund.rst b/docs/src/understand/restund.rst index 35014c28bf..8935365e84 100644 --- a/docs/src/understand/restund.rst +++ b/docs/src/understand/restund.rst @@ -32,13 +32,13 @@ Restund instance may communicate with other Restund instances. You can either have restund servers directly exposed to the public internet: -|architecture-restund| +.. image:: img/architecture-restund.png Or you can have them reachable by fronting them with a firewall or load balancer machine that may have a different IP than the server where restund is installed: -|architecture-restund-lb| +.. image:: img/architecture-restund-lb.png What is it used for ~~~~~~~~~~~~~~~~~~~ @@ -158,7 +158,7 @@ Discovery and establishing a call A simplified flow of how restund servers, along with the wire-server are used to establish a call: -|flow-restund| +.. image:: img/flow-restund.png DNS ~~~ @@ -166,7 +166,3 @@ DNS Usually DNS records are used which point to the public IPs of the restund servers (or of the respective firewall or load balancer machines). These DNS names are then used when configuring wire-server. - -.. |architecture-restund| image:: img/architecture-restund.png -.. |architecture-restund-lb| image:: img/architecture-restund-lb.png -.. |flow-restund| image:: img/flow-restund.png diff --git a/docs/src/understand/single-sign-on/design.rst b/docs/src/understand/single-sign-on/design.rst deleted file mode 100644 index af2102e363..0000000000 --- a/docs/src/understand/single-sign-on/design.rst +++ /dev/null @@ -1,3 +0,0 @@ -:orphan: - -This page is gone. Please visit `this one <./main.html>`_ From ca8cf60a4054a50d29b537493be038efcda003ca Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Thu, 12 Jan 2023 16:59:00 +0100 Subject: [PATCH 3/3] Run conversion script --- docs/src/developer/developer/index.md | 10 + docs/src/developer/developer/index.rst | 10 - docs/src/developer/{index.rst => index.md} | 20 +- docs/src/developer/reference/index.md | 10 + docs/src/developer/reference/index.rst | 10 - docs/src/how-to/administrate/cassandra.md | 63 + docs/src/how-to/administrate/cassandra.rst | 67 - docs/src/how-to/administrate/elasticsearch.md | 127 ++ .../src/how-to/administrate/elasticsearch.rst | 137 -- docs/src/how-to/administrate/etcd.md | 261 ++++ docs/src/how-to/administrate/etcd.rst | 266 ---- docs/src/how-to/administrate/general-linux.md | 67 + .../src/how-to/administrate/general-linux.rst | 69 - docs/src/how-to/administrate/index.md | 12 + docs/src/how-to/administrate/index.rst | 14 - .../kubernetes/certificate-renewal/index.md | 10 + .../kubernetes/certificate-renewal/index.rst | 10 - .../scenario-1_k8s-v1.14-kubespray.md | 241 ++++ .../scenario-1_k8s-v1.14-kubespray.rst | 244 ---- .../how-to/administrate/kubernetes/index.md | 20 + .../how-to/administrate/kubernetes/index.rst | 21 - .../kubernetes/restart-machines/index.md | 42 + .../kubernetes/restart-machines/index.rst | 45 - .../kubernetes/upgrade-cluster/index.md | 75 ++ .../kubernetes/upgrade-cluster/index.rst | 82 -- .../administrate/{minio.rst => minio.md} | 254 ++-- .../{operations.rst => operations.md} | 124 +- docs/src/how-to/administrate/restund.md | 293 +++++ docs/src/how-to/administrate/restund.rst | 302 ----- docs/src/how-to/administrate/users.md | 590 +++++++++ docs/src/how-to/administrate/users.rst | 609 --------- .../custom-backend-for-desktop-client.md | 79 ++ .../custom-backend-for-desktop-client.rst | 90 -- ...ertificates.rst => custom-certificates.md} | 5 +- docs/src/how-to/associate/deeplink.md | 174 +++ docs/src/how-to/associate/deeplink.rst | 174 --- docs/src/how-to/associate/index.md | 10 + docs/src/how-to/associate/index.rst | 10 - docs/src/how-to/index.md | 20 + docs/src/how-to/index.rst | 21 - docs/src/how-to/install/ansible-VMs.md | 275 ++++ docs/src/how-to/install/ansible-VMs.rst | 277 ----- .../how-to/install/ansible-authentication.md | 63 + .../how-to/install/ansible-authentication.rst | 66 - docs/src/how-to/install/ansible-tinc.md | 54 + docs/src/how-to/install/ansible-tinc.rst | 54 - docs/src/how-to/install/aws-prod.md | 36 + docs/src/how-to/install/aws-prod.rst | 39 - .../how-to/install/configuration-options.md | 1048 ++++++++++++++++ .../how-to/install/configuration-options.rst | 1105 ----------------- docs/src/how-to/install/dependencies.md | 69 + docs/src/how-to/install/dependencies.rst | 74 -- docs/src/how-to/install/helm-prod.md | 208 ++++ docs/src/how-to/install/helm-prod.rst | 225 ---- docs/src/how-to/install/helm.md | 145 +++ docs/src/how-to/install/helm.rst | 154 --- docs/src/how-to/install/index.md | 30 + docs/src/how-to/install/index.rst | 30 - docs/src/how-to/install/kubernetes.md | 85 ++ docs/src/how-to/install/kubernetes.rst | 83 -- .../install/{logging.rst => logging.md} | 160 ++- .../install/{monitoring.rst => monitoring.md} | 14 +- .../install/{planning.rst => planning.md} | 57 +- docs/src/how-to/install/prod-intro.md | 58 + docs/src/how-to/install/prod-intro.rst | 60 - docs/src/how-to/install/restund.md | 80 ++ docs/src/how-to/install/restund.rst | 88 -- docs/src/how-to/install/{sft.rst => sft.md} | 162 ++- docs/src/how-to/install/tls.md | 52 + docs/src/how-to/install/tls.rst | 60 - docs/src/how-to/install/troubleshooting.md | 265 ++++ docs/src/how-to/install/troubleshooting.rst | 255 ---- .../how-to/install/version-requirements.md | 28 + .../how-to/install/version-requirements.rst | 35 - .../post-install/{index.rst => index.md} | 16 +- .../how-to/post-install/logrotation-check.md | 81 ++ .../how-to/post-install/logrotation-check.rst | 79 -- docs/src/how-to/post-install/ntp-check.md | 44 + docs/src/how-to/post-install/ntp-check.rst | 48 - docs/src/how-to/single-sign-on/adfs/main.md | 41 + docs/src/how-to/single-sign-on/adfs/main.rst | 19 - docs/src/how-to/single-sign-on/azure/main.md | 92 ++ docs/src/how-to/single-sign-on/azure/main.rst | 82 -- .../centrify/{main.rst => main.md} | 66 +- .../how-to/single-sign-on/generic-setup.md | 37 + .../how-to/single-sign-on/generic-setup.rst | 42 - docs/src/how-to/single-sign-on/index.md | 15 + docs/src/how-to/single-sign-on/index.rst | 15 - .../single-sign-on/okta/{main.rst => main.md} | 60 +- ...ouble-shooting.rst => trouble-shooting.md} | 221 ++-- .../how-to/single-sign-on/understand/main.md | 561 +++++++++ .../how-to/single-sign-on/understand/main.rst | 558 --------- docs/src/index.md | 46 + docs/src/index.rst | 43 - .../{release-notes.rst => release-notes.md} | 13 +- .../2021-12-15_log4shell.md | 90 ++ .../2021-12-15_log4shell.rst | 103 -- docs/src/security-responses/index.md | 14 + docs/src/security-responses/index.rst | 16 - .../api-client-perspective/authentication.md | 435 +++++++ .../api-client-perspective/authentication.rst | 472 ------- .../api-client-perspective/index.md | 15 + .../api-client-perspective/index.rst | 14 - .../{swagger.rst => swagger.md} | 17 +- docs/src/understand/federation/index.md | 24 + docs/src/understand/federation/index.rst | 25 - docs/src/understand/helm.md | 61 + docs/src/understand/helm.rst | 64 - docs/src/understand/index.md | 17 + docs/src/understand/index.rst | 17 - docs/src/understand/{minio.rst => minio.md} | 13 +- docs/src/understand/notes/port-ranges.md | 36 + docs/src/understand/notes/port-ranges.rst | 36 - docs/src/understand/overview.md | 143 +++ docs/src/understand/overview.rst | 146 --- .../understand/{restund.rst => restund.md} | 106 +- docs/src/understand/{sft.rst => sft.md} | 97 +- 117 files changed, 7018 insertions(+), 7304 deletions(-) create mode 100644 docs/src/developer/developer/index.md delete mode 100644 docs/src/developer/developer/index.rst rename docs/src/developer/{index.rst => index.md} (52%) create mode 100644 docs/src/developer/reference/index.md delete mode 100644 docs/src/developer/reference/index.rst create mode 100644 docs/src/how-to/administrate/cassandra.md delete mode 100644 docs/src/how-to/administrate/cassandra.rst create mode 100644 docs/src/how-to/administrate/elasticsearch.md delete mode 100644 docs/src/how-to/administrate/elasticsearch.rst create mode 100644 docs/src/how-to/administrate/etcd.md delete mode 100644 docs/src/how-to/administrate/etcd.rst create mode 100644 docs/src/how-to/administrate/general-linux.md delete mode 100644 docs/src/how-to/administrate/general-linux.rst create mode 100644 docs/src/how-to/administrate/index.md delete mode 100644 docs/src/how-to/administrate/index.rst create mode 100644 docs/src/how-to/administrate/kubernetes/certificate-renewal/index.md delete mode 100644 docs/src/how-to/administrate/kubernetes/certificate-renewal/index.rst create mode 100644 docs/src/how-to/administrate/kubernetes/certificate-renewal/scenario-1_k8s-v1.14-kubespray.md delete mode 100644 docs/src/how-to/administrate/kubernetes/certificate-renewal/scenario-1_k8s-v1.14-kubespray.rst create mode 100644 docs/src/how-to/administrate/kubernetes/index.md delete mode 100644 docs/src/how-to/administrate/kubernetes/index.rst create mode 100644 docs/src/how-to/administrate/kubernetes/restart-machines/index.md delete mode 100644 docs/src/how-to/administrate/kubernetes/restart-machines/index.rst create mode 100644 docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.md delete mode 100644 docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.rst rename docs/src/how-to/administrate/{minio.rst => minio.md} (59%) rename docs/src/how-to/administrate/{operations.rst => operations.md} (50%) create mode 100644 docs/src/how-to/administrate/restund.md delete mode 100644 docs/src/how-to/administrate/restund.rst create mode 100644 docs/src/how-to/administrate/users.md delete mode 100644 docs/src/how-to/administrate/users.rst create mode 100644 docs/src/how-to/associate/custom-backend-for-desktop-client.md delete mode 100644 docs/src/how-to/associate/custom-backend-for-desktop-client.rst rename docs/src/how-to/associate/{custom-certificates.rst => custom-certificates.md} (80%) create mode 100644 docs/src/how-to/associate/deeplink.md delete mode 100644 docs/src/how-to/associate/deeplink.rst create mode 100644 docs/src/how-to/associate/index.md delete mode 100644 docs/src/how-to/associate/index.rst create mode 100644 docs/src/how-to/index.md delete mode 100644 docs/src/how-to/index.rst create mode 100644 docs/src/how-to/install/ansible-VMs.md delete mode 100644 docs/src/how-to/install/ansible-VMs.rst create mode 100644 docs/src/how-to/install/ansible-authentication.md delete mode 100644 docs/src/how-to/install/ansible-authentication.rst create mode 100644 docs/src/how-to/install/ansible-tinc.md delete mode 100644 docs/src/how-to/install/ansible-tinc.rst create mode 100644 docs/src/how-to/install/aws-prod.md delete mode 100644 docs/src/how-to/install/aws-prod.rst create mode 100644 docs/src/how-to/install/configuration-options.md delete mode 100644 docs/src/how-to/install/configuration-options.rst create mode 100644 docs/src/how-to/install/dependencies.md delete mode 100644 docs/src/how-to/install/dependencies.rst create mode 100644 docs/src/how-to/install/helm-prod.md delete mode 100644 docs/src/how-to/install/helm-prod.rst create mode 100644 docs/src/how-to/install/helm.md delete mode 100644 docs/src/how-to/install/helm.rst create mode 100644 docs/src/how-to/install/index.md delete mode 100644 docs/src/how-to/install/index.rst create mode 100644 docs/src/how-to/install/kubernetes.md delete mode 100644 docs/src/how-to/install/kubernetes.rst rename docs/src/how-to/install/{logging.rst => logging.md} (60%) rename docs/src/how-to/install/{monitoring.rst => monitoring.md} (58%) rename docs/src/how-to/install/{planning.rst => planning.md} (55%) create mode 100644 docs/src/how-to/install/prod-intro.md delete mode 100644 docs/src/how-to/install/prod-intro.rst create mode 100644 docs/src/how-to/install/restund.md delete mode 100644 docs/src/how-to/install/restund.rst rename docs/src/how-to/install/{sft.rst => sft.md} (67%) create mode 100644 docs/src/how-to/install/tls.md delete mode 100644 docs/src/how-to/install/tls.rst create mode 100644 docs/src/how-to/install/troubleshooting.md delete mode 100644 docs/src/how-to/install/troubleshooting.rst create mode 100644 docs/src/how-to/install/version-requirements.md delete mode 100644 docs/src/how-to/install/version-requirements.rst rename docs/src/how-to/post-install/{index.rst => index.md} (53%) create mode 100644 docs/src/how-to/post-install/logrotation-check.md delete mode 100644 docs/src/how-to/post-install/logrotation-check.rst create mode 100644 docs/src/how-to/post-install/ntp-check.md delete mode 100644 docs/src/how-to/post-install/ntp-check.rst create mode 100644 docs/src/how-to/single-sign-on/adfs/main.md delete mode 100644 docs/src/how-to/single-sign-on/adfs/main.rst create mode 100644 docs/src/how-to/single-sign-on/azure/main.md delete mode 100644 docs/src/how-to/single-sign-on/azure/main.rst rename docs/src/how-to/single-sign-on/centrify/{main.rst => main.md} (55%) create mode 100644 docs/src/how-to/single-sign-on/generic-setup.md delete mode 100644 docs/src/how-to/single-sign-on/generic-setup.rst create mode 100644 docs/src/how-to/single-sign-on/index.md delete mode 100644 docs/src/how-to/single-sign-on/index.rst rename docs/src/how-to/single-sign-on/okta/{main.rst => main.md} (76%) rename docs/src/how-to/single-sign-on/{trouble-shooting.rst => trouble-shooting.md} (60%) create mode 100644 docs/src/how-to/single-sign-on/understand/main.md delete mode 100644 docs/src/how-to/single-sign-on/understand/main.rst create mode 100644 docs/src/index.md delete mode 100644 docs/src/index.rst rename docs/src/{release-notes.rst => release-notes.md} (51%) create mode 100644 docs/src/security-responses/2021-12-15_log4shell.md delete mode 100644 docs/src/security-responses/2021-12-15_log4shell.rst create mode 100644 docs/src/security-responses/index.md delete mode 100644 docs/src/security-responses/index.rst create mode 100644 docs/src/understand/api-client-perspective/authentication.md delete mode 100644 docs/src/understand/api-client-perspective/authentication.rst create mode 100644 docs/src/understand/api-client-perspective/index.md delete mode 100644 docs/src/understand/api-client-perspective/index.rst rename docs/src/understand/api-client-perspective/{swagger.rst => swagger.md} (67%) create mode 100644 docs/src/understand/federation/index.md delete mode 100644 docs/src/understand/federation/index.rst create mode 100644 docs/src/understand/helm.md delete mode 100644 docs/src/understand/helm.rst create mode 100644 docs/src/understand/index.md delete mode 100644 docs/src/understand/index.rst rename docs/src/understand/{minio.rst => minio.md} (86%) create mode 100644 docs/src/understand/notes/port-ranges.md delete mode 100644 docs/src/understand/notes/port-ranges.rst create mode 100644 docs/src/understand/overview.md delete mode 100644 docs/src/understand/overview.rst rename docs/src/understand/{restund.rst => restund.md} (62%) rename docs/src/understand/{sft.rst => sft.md} (77%) diff --git a/docs/src/developer/developer/index.md b/docs/src/developer/developer/index.md new file mode 100644 index 0000000000..77e35760cf --- /dev/null +++ b/docs/src/developer/developer/index.md @@ -0,0 +1,10 @@ +# Developer + +```{toctree} +:caption: 'Contents:' +:glob: true +:numbered: true +:titlesonly: true + +** +``` diff --git a/docs/src/developer/developer/index.rst b/docs/src/developer/developer/index.rst deleted file mode 100644 index a8fefaa770..0000000000 --- a/docs/src/developer/developer/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Developer -========= - -.. toctree:: - :titlesonly: - :numbered: - :caption: Contents: - :glob: - - ** diff --git a/docs/src/developer/index.rst b/docs/src/developer/index.md similarity index 52% rename from docs/src/developer/index.rst rename to docs/src/developer/index.md index b48dbecae0..59cf4fd92e 100644 --- a/docs/src/developer/index.rst +++ b/docs/src/developer/index.md @@ -1,19 +1,19 @@ -Notes for developers -==================== +# Notes for developers -If you are an on-premise operator (administrating your own self-hosted installation of wire-server), you may want to go back to `docs.wire.com `_ and ignore this section of the docs. +If you are an on-premise operator (administrating your own self-hosted installation of wire-server), you may want to go back to [docs.wire.com](https://docs.wire.com/) and ignore this section of the docs. -If you are a wire end-user, please check out our `support pages `_. +If you are a wire end-user, please check out our [support pages](https://support.wire.com/). What you need to know as a user of the Wire backend: concepts, features, and API. We want to keep these up to date. They could benefit from some re-ordering, and they are far from complete, but we hope they will still help you. -.. toctree:: - :titlesonly: - :caption: Contents: - :glob: +```{toctree} +:caption: 'Contents:' +:glob: true +:titlesonly: true - developer/index.rst - reference/index.rst +developer/index.rst +reference/index.rst +``` diff --git a/docs/src/developer/reference/index.md b/docs/src/developer/reference/index.md new file mode 100644 index 0000000000..4b6e82f195 --- /dev/null +++ b/docs/src/developer/reference/index.md @@ -0,0 +1,10 @@ +# Reference + +```{toctree} +:caption: 'Contents:' +:glob: true +:numbered: true +:titlesonly: true + +** +``` diff --git a/docs/src/developer/reference/index.rst b/docs/src/developer/reference/index.rst deleted file mode 100644 index 1eb9feedba..0000000000 --- a/docs/src/developer/reference/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Reference -========= - -.. toctree:: - :titlesonly: - :numbered: - :caption: Contents: - :glob: - - ** diff --git a/docs/src/how-to/administrate/cassandra.md b/docs/src/how-to/administrate/cassandra.md new file mode 100644 index 0000000000..c75439d626 --- /dev/null +++ b/docs/src/how-to/administrate/cassandra.md @@ -0,0 +1,63 @@ +# Cassandra + +```{eval-rst} +.. include:: includes/intro.rst +``` + +This section only covers the bare minimum, for more information, see the [cassandra +documentation](https://cassandra.apache.org/doc/latest/) + +(check-the-health-of-a-cassandra-node)= + +## Check the health of a Cassandra node + +To check the health of a Cassandra node, run the following command: + +```sh +ssh /opt/cassandra/bin/nodetool status +``` + +or if you are running a newer version of wire-server (altough it should be backwards compatibile) + +```sh +ssh /opt/cassandra/bin/nodetool -h ::FFFF:127.0.0.1 status +``` + +You should see a list of nodes like this: + +```sh +Datacenter: datacenter1 +======================= +Status=Up/Down +|/ State=Normal/Leaving/Joining/Moving +-- Address Load Tokens Owns (effective) Host ID Rack +UN 192.168.220.13 9.51MiB 256 100.0% 3dba71c8-eea7-4e35-8f35-4386e7944894 rack1 +UN 192.168.220.23 9.53MiB 256 100.0% 3af56f1f-7685-4b5b-b73f-efdaa371e96e rack1 +UN 192.168.220.33 9.55MiB 256 100.0% RANDOMLY-MADE-UUID-GOES-INTHISPLACE! rack1 +``` + +A `UN` at the begginng of the line, refers to a node that is `Up` and `Normal`. + +## How to inspect tables and data manually + +```sh +cqlsh +# from the cqlsh shell +describe keyspaces +use ; +describe tables; +select * from WHERE = LIMIT 10; +``` + +## How to rolling-restart a cassandra cluster + +For maintenance you may need to restart the cluster. + +On each server one by one: + +1. check your cluster is healthy: `nodetool status` or `nodetool -h ::FFFF:127.0.0.1 status` (in newer versions) +2. `nodetool drain && systemctl stop cassandra` (to stop accepting writes and flush data to disk; then stop the process) +3. do any operation you need, if any +4. Start the cassandra daemon process: `systemctl start cassandra` +5. Wait for your cluster to be healthy again. +6. Do the same on the next server. diff --git a/docs/src/how-to/administrate/cassandra.rst b/docs/src/how-to/administrate/cassandra.rst deleted file mode 100644 index f92fddc3a8..0000000000 --- a/docs/src/how-to/administrate/cassandra.rst +++ /dev/null @@ -1,67 +0,0 @@ -Cassandra --------------------------- - -.. include:: includes/intro.rst - -This section only covers the bare minimum, for more information, see the `cassandra -documentation `__ - -.. _check-the-health-of-a-cassandra-node: - -Check the health of a Cassandra node -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To check the health of a Cassandra node, run the following command: - -.. code:: sh - - ssh /opt/cassandra/bin/nodetool status - -or if you are running a newer version of wire-server (altough it should be backwards compatibile) - -.. code:: sh - - ssh /opt/cassandra/bin/nodetool -h ::FFFF:127.0.0.1 status - -You should see a list of nodes like this: - -.. code:: sh - - Datacenter: datacenter1 - ======================= - Status=Up/Down - |/ State=Normal/Leaving/Joining/Moving - -- Address Load Tokens Owns (effective) Host ID Rack - UN 192.168.220.13 9.51MiB 256 100.0% 3dba71c8-eea7-4e35-8f35-4386e7944894 rack1 - UN 192.168.220.23 9.53MiB 256 100.0% 3af56f1f-7685-4b5b-b73f-efdaa371e96e rack1 - UN 192.168.220.33 9.55MiB 256 100.0% RANDOMLY-MADE-UUID-GOES-INTHISPLACE! rack1 - -A ``UN`` at the begginng of the line, refers to a node that is ``Up`` and ``Normal``. - -How to inspect tables and data manually -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: sh - - cqlsh - # from the cqlsh shell - describe keyspaces - use ; - describe tables; - select * from WHERE = LIMIT 10; - -How to rolling-restart a cassandra cluster -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For maintenance you may need to restart the cluster. - -On each server one by one: - -1. check your cluster is healthy: ``nodetool status`` or ``nodetool -h ::FFFF:127.0.0.1 status`` (in newer versions) -2. ``nodetool drain && systemctl stop cassandra`` (to stop accepting writes and flush data to disk; then stop the process) -3. do any operation you need, if any -4. Start the cassandra daemon process: ``systemctl start cassandra`` -5. Wait for your cluster to be healthy again. -6. Do the same on the next server. - - diff --git a/docs/src/how-to/administrate/elasticsearch.md b/docs/src/how-to/administrate/elasticsearch.md new file mode 100644 index 0000000000..f128a0c1d6 --- /dev/null +++ b/docs/src/how-to/administrate/elasticsearch.md @@ -0,0 +1,127 @@ +# Elasticsearch + +```{eval-rst} +.. include:: includes/intro.rst +``` + +For more information, see the [elasticsearch +documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) + +(restart-elasticsearch)= + +## How to rolling-restart an elasticsearch cluster + +For maintenance you may need to restart the cluster. + +On each server one by one: + +1. check your cluster is healthy (see above) +2. stop shard allocation: + +```sh +ES_IP= +curl -sSf -XPUT http://localhost:9200/_cluster/settings -H 'Content-Type: application/json' -d "{ \"transient\" : {\"cluster.routing.allocation.exclude._ip\": \"$ES_IP\" }}"; echo; +``` + +You should expect some output like this: + +```sh +{"acknowledged":true,"persistent":{},"transient":{"cluster":{"routing":{"allocation":{"exclude":{"_ip":""}}}}}} +``` + +3. Stop the elasticsearch daemon process: `systemctl stop elasticsearch` +4. do any operation you need, if any +5. Start the elasticsearch daemon process: `systemctl start elasticsearch` +6. re-enable shard allocation: + +```sh +curl -sSf -XPUT http://localhost:9200/_cluster/settings -H 'Content-Type: application/json' -d "{ \"transient\" : {\"cluster.routing.allocation.exclude._ip\": null }}"; echo; +``` + +You should expect some output like this from the above command: + +```sh +{"acknowledged":true,"persistent":{},"transient":{}} +``` + +6. Wait for your cluster to be healthy again. +7. Do the same on the next server. + +## How to manually look into what is stored in elasticsearch + +See also the elasticsearch sections in {ref}`investigative-tasks`. + +(check-the-health-of-an-elasticsearch-node)= + +## Check the health of an elasticsearch node + +To check the health of an elasticsearch node, run the following command: + +```sh +ssh curl localhost:9200/_cat/health +``` + +You should see output looking like this: + +``` +1630250355 15:18:55 elasticsearch-directory green 3 3 17 6 0 0 0 - 100.0% +``` + +Here, the `green` denotes good node health, and the `3 3` denotes 3 running nodes. + +## Check cluster health + +This is the command to check the health of the entire cluster: + +```sh +ssh curl 'http://localhost:9200/_cluster/health?pretty' +``` + +## List cluster nodes + +This is the command to list the nodes in the cluster: + +```sh +ssh curl 'http://localhost:9200/_cat/nodes?v&h=id,ip,name' +``` + +## Troubleshooting + +Description: +**ES nodes ran out of disk space** and error message says: `"blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"` + +Solution: + +1. Connect to the node: + +```sh +ssh +``` + +2. Clean up disk (e.g. `apt autoremove` on all nodes), then restart machines and/or the elasticsearch process + +```sh +sudo apt autoremove +sudo reboot +``` + +As always make sure you {ref}`check the health of the process `. before and after the reboot. + +3. Get the elastichsearch cluster out of *read-only* mode, run: + +```sh +curl -X PUT -H 'Content-Type: application/json' http://localhost:9200/_all/_settings -d '{"index.blocks.read_only_allow_delete": null}' +``` + +4. Trigger reindexing: From a kubernetes machine, in one terminal: + +```sh +# The following depends on your namespace where you installed wire-server. By default the namespace is called 'wire'. +kubectl --namespace wire port-forward svc/brig 9999:8080 +``` + +And in a second terminal trigger the reindex: + +```sh +curl -v -X POST localhost:9999/i/index/reindex +``` diff --git a/docs/src/how-to/administrate/elasticsearch.rst b/docs/src/how-to/administrate/elasticsearch.rst deleted file mode 100644 index 829a8526b9..0000000000 --- a/docs/src/how-to/administrate/elasticsearch.rst +++ /dev/null @@ -1,137 +0,0 @@ -Elasticsearch ------------------------------- - -.. include:: includes/intro.rst - -For more information, see the `elasticsearch -documentation `__ - - -.. _restart-elasticsearch: - -How to rolling-restart an elasticsearch cluster -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For maintenance you may need to restart the cluster. - -On each server one by one: - -1. check your cluster is healthy (see above) -2. stop shard allocation: - -.. code:: sh - - ES_IP= - curl -sSf -XPUT http://localhost:9200/_cluster/settings -H 'Content-Type: application/json' -d "{ \"transient\" : {\"cluster.routing.allocation.exclude._ip\": \"$ES_IP\" }}"; echo; - -You should expect some output like this: - -.. code:: sh - - {"acknowledged":true,"persistent":{},"transient":{"cluster":{"routing":{"allocation":{"exclude":{"_ip":""}}}}}} - -3. Stop the elasticsearch daemon process: ``systemctl stop elasticsearch`` -4. do any operation you need, if any -5. Start the elasticsearch daemon process: ``systemctl start elasticsearch`` -6. re-enable shard allocation: - -.. code:: sh - - curl -sSf -XPUT http://localhost:9200/_cluster/settings -H 'Content-Type: application/json' -d "{ \"transient\" : {\"cluster.routing.allocation.exclude._ip\": null }}"; echo; - -You should expect some output like this from the above command: - -.. code:: sh - - {"acknowledged":true,"persistent":{},"transient":{}} - -6. Wait for your cluster to be healthy again. -7. Do the same on the next server. - -How to manually look into what is stored in elasticsearch -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -See also the elasticsearch sections in :ref:`investigative-tasks`. - - -.. _check-the-health-of-an-elasticsearch-node: - -Check the health of an elasticsearch node -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To check the health of an elasticsearch node, run the following command: - -.. code:: sh - - ssh curl localhost:9200/_cat/health - -You should see output looking like this: - -.. code:: - - 1630250355 15:18:55 elasticsearch-directory green 3 3 17 6 0 0 0 - 100.0% - -Here, the ``green`` denotes good node health, and the ``3 3`` denotes 3 running nodes. - -Check cluster health -~~~~~~~~~~~~~~~~~~~~ - -This is the command to check the health of the entire cluster: - -.. code:: sh - - ssh curl 'http://localhost:9200/_cluster/health?pretty' - - -List cluster nodes -~~~~~~~~~~~~~~~~~~ - -This is the command to list the nodes in the cluster: - -.. code:: sh - - ssh curl 'http://localhost:9200/_cat/nodes?v&h=id,ip,name' - - -Troubleshooting -~~~~~~~~~~~~~~~ - -Description: -**ES nodes ran out of disk space** and error message says: ``"blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"`` - -Solution: - -1. Connect to the node: - -.. code:: sh - - ssh - -2. Clean up disk (e.g. ``apt autoremove`` on all nodes), then restart machines and/or the elasticsearch process - -.. code:: sh - - sudo apt autoremove - sudo reboot - - -As always make sure you :ref:`check the health of the process `. before and after the reboot. - -3. Get the elastichsearch cluster out of *read-only* mode, run: - -.. code:: sh - - curl -X PUT -H 'Content-Type: application/json' http://localhost:9200/_all/_settings -d '{"index.blocks.read_only_allow_delete": null}' - -4. Trigger reindexing: From a kubernetes machine, in one terminal: - -.. code:: sh - - # The following depends on your namespace where you installed wire-server. By default the namespace is called 'wire'. - kubectl --namespace wire port-forward svc/brig 9999:8080 - -And in a second terminal trigger the reindex: - -.. code:: sh - - curl -v -X POST localhost:9999/i/index/reindex diff --git a/docs/src/how-to/administrate/etcd.md b/docs/src/how-to/administrate/etcd.md new file mode 100644 index 0000000000..a18c801f87 --- /dev/null +++ b/docs/src/how-to/administrate/etcd.md @@ -0,0 +1,261 @@ +# Etcd + +```{eval-rst} +.. include:: includes/intro.rst +``` + +This section only covers the bare minimum, for more information, see the [etcd documentation](https://etcd.io/) + +(how-to-see-cluster-health)= + +## How to see cluster health + +If the file `/usr/local/bin/etcd-health.sh` is available, you can run + +```sh +etcd-health.sh +``` + +which should produce an output similar to: + +``` +Cluster-Endpoints: https://127.0.0.1:2379 +cURL Command: curl -X GET https://127.0.0.1:2379/v2/members +member 7c37f7dc10558fae is healthy: got healthy result from https://10.10.1.11:2379 +member cca4e6f315097b3b is healthy: got healthy result from https://10.10.1.10:2379 +member e767162297c84b1e is healthy: got healthy result from https://10.10.1.12:2379 +cluster is healthy +``` + +If that helper file is not available, create it with the following contents: + +```bash +#!/usr/bin/env bash + +HOST=$(hostname) + +etcdctl --endpoints https://127.0.0.1:2379 --ca-file=/etc/ssl/etcd/ssl/ca.pem --cert-file=/etc/ssl/etcd/ssl/member-$HOST.pem --key-file=/etc/ssl/etcd/ssl/member-$HOST-key.pem --debug cluster-health +``` + +and then make it executable: `chmod +x /usr/local/bin/etcd-health.sh` + +## How to inspect tables and data manually + +```sh +TODO +``` + +(how-to-rolling-restart-an-etcd-cluster)= + +## How to rolling-restart an etcd cluster + +Etcd is a consistent and partition tolerant key-value store. This means that +Etcd nodes can be restarted (one by one) with no impact to the consistency of +data, but there might a small time in which the database can not process +writes. Etcd has a designated leader which decides ordering of events (and thus +writes) in the cluster. When the leader crashes, a leadership election takes +place. During the leadership election, the cluster might be briefly +unavailable for writes. Writes during this period are queued up until a new +leader is elected. Any writes that were happening during the crash of the +leader that were not acknowledged by the leader and the followers yet will be +'lost'. The client that performed this write will experience this as a write +timeout. (Source: ). Client +applications (like kubernetes) are expected to deal with this failure scenario +gracefully. + +Etcd can be restarted in a rolling fashion, by cleanly shutting down and +starting up etcd servers one by one. In Etcd 3.1 and up, when the leader is +cleanly shut down, it will hand over leadership gracefully to another node, +which will minimize the impact of write-availability as election time is +reduced. (Source : +) +Restarting follower nodes has no impact to availability. + +Etcd does load-balancing between servrvers on the client-side. This means that +if a server you were talking to is being restarted, etcd will transparently +redirect the request to another server. It's is thus safe to shut them down at +any point. + +Now to perform a rolling restart of the cluster, do the following steps: + +1. Check your cluster is healthy (see above) +2. Stop the process with `systemctl stop etcd` (this should be safe since etcd clients retry their operation if one endpoint becomes unavailable, see [this page](https://etcd.io/docs/v3.3.12/learning/client-architecture/)) +3. Do any operation you need, if any. Like rebooting +4. `systemctl start etcd` +5. Wait for your cluster to be healthy again. +6. Do the same on the next server. + +*For more details please refer to the official documentation:* [Replacing a failed etcd member](https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#replacing-a-failed-etcd-member) + +(etcd-backup-and-restore)= + +## Backing up and restoring + +Though as long as quorum is maintained in etcd there will be no dataloss, it is +still good to prepare for the worst. If a disaster takes out too many nodes, then +you might have to restore from an old backup. + +Luckily, etcd can take periodic snapshots of your cluster and these can be used +in cases of disaster recovery. Information about how to do snapshots and +restores can be found here: + + +*For more details please refer to the official documentation:* [Backing up an etcd cluster](https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#backing-up-an-etcd-cluster) + +## Troubleshooting + +### How to recover from a single unhealthy etcd node after virtual machine snapshot restore + +After restoring an etcd machine from an earlier snapshot of the machine disk, etcd members may become unable to join. + +Symptoms: That etcd process is unable to start and crashes, and other etcd nodes can't reach it: + +``` +failed to check the health of member e767162297c84b1e on https://10.10.1.12:2379: Get https://10.10.1.12:2379/health: dial tcp 10.10.1.12:2379: getsockopt: connection refused +member e767162297c84b1e is unreachable: [https://10.10.1.12:2379] are all unreachable +``` + +Logs from the crashing etcd: + +``` +(...) +Sep 25 09:27:05 node2 etcd[20288]: 2019-09-25 07:27:05.691409 I | raft: e767162297c84b1e [term: 28] received a MsgHeartbeat message with higher term from cca4e6f315097b3b [term: 30] +Sep 25 09:27:05 node2 etcd[20288]: 2019-09-25 07:27:05.691620 I | raft: e767162297c84b1e became follower at term 30 +Sep 25 09:27:05 node2 etcd[20288]: 2019-09-25 07:27:05.692423 C | raft: tocommit(16152654) is out of range [lastIndex(16061986)]. Was the raft log corrupted, truncated, or lost? +Sep 25 09:27:05 node2 etcd[20288]: panic: tocommit(16152654) is out of range [lastIndex(16061986)]. Was the raft log corrupted, truncated, or lost? +Sep 25 09:27:05 node2 etcd[20288]: goroutine 90 [running]: +(...) +``` + +Etcd will refuse nodes that run behind to join the cluster. If a node has +committed to a certain version of the raft log, it is expected not to jump back +in time after that. In this scenario, we turned an etcd server off, made a +snapshot of the virtual machine, brought it back online, and then restored the +snapshot. What went wrong is is that if you bring up a VM snapshot, it means +the etcd node will now have an older raft log than it had before; even though +it already gossiped to all other nodes that it has knowledge of newer entries. + +As a safety precaution, the other nodes will reject the node that is travelling +back in time, to avoid data corruption. A node could get corrupted for other +reasons as well. Perhaps a disk is faulty and is serving wrong data. Either +way, if you end up in a scenario where a node is unhealthy and will refuse to +rejoin the cluster, it is time to do some operations to get the cluster back in +a healthy state. + +It is not recommended to restore an etcd node from a vm snapshot, as that will +cause these kind of time-travelling behaviours which will make the node +unhealthy. To recover from this situation anyway, +I quote from the etcdv2 admin guide + +> If a member’s data directory is ever lost or corrupted then the user should +> remove the etcd member from the cluster using etcdctl tool. A user should +> avoid restarting an etcd member with a data directory from an out-of-date +> backup. Using an out-of-date data directory can lead to inconsistency as the +> member had agreed to store information via raft then re-joins saying it +> needs that information again. For maximum safety, if an etcd member suffers +> any sort of data corruption or loss, it must be removed from the cluster. +> Once removed the member can be re-added with an empty data directory. + +Note that this piece of documentation is from etcdv2 and not etcdv3. However +the etcdv3 docs describe a similar procedure here + + +The procedure to remove and add a member is documented here: + + +It is also documented in the kubernetes documentation: + + +So following the above guides step by step, we can recover our cluster to be +healthy again. + +First let us make sure our broken member is stopped by runnning this on `node`: + +```sh +systemctl stop etcd +``` + +Now from a healthy node, e.g. `node0` remove the broken node + +```sh +etcdctl3.sh member remove e767162297c84b1e +``` + +And we expect the output to be something like + +```sh +Member e767162297c84b1e removed from cluster 432c10551aa096af +``` + +By removing the member from the cluster, you signal the other nodes to not +expect it to come back with the right state. It will be considered dead and +removed from the peers. This will allow the node to come up with an empty data +directory and it not getting kicked out of the cluster. The cluster should now +be healthy, but only have 2 members, and so it is not to resistent to crashes +at the moment! As we can see if we run the health check from a healthy node. + +```sh +etcd-health.sh +``` + +And we expect only two nodes to be in the cluster: + +``` +Cluster-Endpoints: https://127.0.0.1:2379 +cURL Command: curl -X GET https://127.0.0.1:2379/v2/members +member 7c37f7dc10558fae is healthy: got healthy result from https://10.10.1.11:2379 +member cca4e6f315097b3b is healthy: got healthy result from https://10.10.1.10:2379 +cluster is healthy +``` + +Now from a healthy node, re-add the node you just removed. Make sure +to replace the IP in the snippet below with the IP of the node you just removed. + +```sh +etcdctl3.sh member add etcd_2 --peer-urls https://10.10.1.12:2380 +``` + +And it should report that it has been added: + +``` +Member e13b1d076b2f9344 added to cluster 432c10551aa096af + +ETCD_NAME="etcd_2" +ETCD_INITIAL_CLUSTER="etcd_1=https://10.10.1.11:2380,etcd_0=https://10.10.1.10:2380,etcd_2=https://10.10.1.12:2380" +ETCD_INITIAL_CLUSTER_STATE="existing" +``` + +it should now be in the list as "unstarted" instead of it not being in the list at all. + +```sh +etcdctl3.sh member list + + +7c37f7dc10558fae, started, etcd_1, https://10.10.1.11:2380, https://10.10.1.11:2379 +cca4e6f315097b3b, started, etcd_0, https://10.10.1.10:2380, https://10.10.1.10:2379 +e13b1d076b2f9344, unstarted, , https://10.10.1.12:2380, +``` + +Now on the broken node, remove the on-disk state, which was corrupted, and start etcd + +```sh +mv /var/lib/etcd /var/lib/etcd.bak +sudo systemctl start etcd +``` + +If we run the health check now, the cluster should report its healthy now again. + +```sh +etcd-health.sh +``` + +And indeed it outputs so: + +``` +Cluster-Endpoints: https://127.0.0.1:2379 +cURL Command: curl -X GET https://127.0.0.1:2379/v2/members +member 7c37f7dc10558fae is healthy: got healthy result from https://10.10.1.11:2379 +member cca4e6f315097b3b is healthy: got healthy result from https://10.10.1.10:2379 +member e13b1d076b2f9344 is healthy: got healthy result from https://10.10.1.12:2379 +cluster is healthy +``` diff --git a/docs/src/how-to/administrate/etcd.rst b/docs/src/how-to/administrate/etcd.rst deleted file mode 100644 index f0af9a1b83..0000000000 --- a/docs/src/how-to/administrate/etcd.rst +++ /dev/null @@ -1,266 +0,0 @@ -Etcd --------------------------- - -.. include:: includes/intro.rst - -This section only covers the bare minimum, for more information, see the `etcd documentation `__ - -.. _how-to-see-cluster-health: - -How to see cluster health -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If the file `/usr/local/bin/etcd-health.sh` is available, you can run - -.. code:: sh - - etcd-health.sh - -which should produce an output similar to:: - - Cluster-Endpoints: https://127.0.0.1:2379 - cURL Command: curl -X GET https://127.0.0.1:2379/v2/members - member 7c37f7dc10558fae is healthy: got healthy result from https://10.10.1.11:2379 - member cca4e6f315097b3b is healthy: got healthy result from https://10.10.1.10:2379 - member e767162297c84b1e is healthy: got healthy result from https://10.10.1.12:2379 - cluster is healthy - -If that helper file is not available, create it with the following contents: - -.. code:: bash - - #!/usr/bin/env bash - - HOST=$(hostname) - - etcdctl --endpoints https://127.0.0.1:2379 --ca-file=/etc/ssl/etcd/ssl/ca.pem --cert-file=/etc/ssl/etcd/ssl/member-$HOST.pem --key-file=/etc/ssl/etcd/ssl/member-$HOST-key.pem --debug cluster-health - -and then make it executable: ``chmod +x /usr/local/bin/etcd-health.sh`` - -How to inspect tables and data manually -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: sh - - TODO - - -.. _how-to-rolling-restart-an-etcd-cluster: - -How to rolling-restart an etcd cluster -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Etcd is a consistent and partition tolerant key-value store. This means that -Etcd nodes can be restarted (one by one) with no impact to the consistency of -data, but there might a small time in which the database can not process -writes. Etcd has a designated leader which decides ordering of events (and thus -writes) in the cluster. When the leader crashes, a leadership election takes -place. During the leadership election, the cluster might be briefly -unavailable for writes. Writes during this period are queued up until a new -leader is elected. Any writes that were happening during the crash of the -leader that were not acknowledged by the leader and the followers yet will be -'lost'. The client that performed this write will experience this as a write -timeout. (Source: https://etcd.io/docs/v3.4.0/op-guide/failures/). Client -applications (like kubernetes) are expected to deal with this failure scenario -gracefully. - -Etcd can be restarted in a rolling fashion, by cleanly shutting down and -starting up etcd servers one by one. In Etcd 3.1 and up, when the leader is -cleanly shut down, it will hand over leadership gracefully to another node, -which will minimize the impact of write-availability as election time is -reduced. (Source : -https://kubernetes.io/blog/2018/12/11/etcd-current-status-and-future-roadmap/) -Restarting follower nodes has no impact to availability. - -Etcd does load-balancing between servrvers on the client-side. This means that -if a server you were talking to is being restarted, etcd will transparently -redirect the request to another server. It's is thus safe to shut them down at -any point. - -Now to perform a rolling restart of the cluster, do the following steps: - -1. Check your cluster is healthy (see above) -2. Stop the process with ``systemctl stop etcd`` (this should be safe since etcd clients retry their operation if one endpoint becomes unavailable, see `this page `__) -3. Do any operation you need, if any. Like rebooting -4. ``systemctl start etcd`` -5. Wait for your cluster to be healthy again. -6. Do the same on the next server. - -*For more details please refer to the official documentation:* `Replacing a failed etcd member `__ - - -.. _etcd-backup-and-restore: - -Backing up and restoring -~~~~~~~~~~~~~~~~~~~~~~~~~ -Though as long as quorum is maintained in etcd there will be no dataloss, it is -still good to prepare for the worst. If a disaster takes out too many nodes, then -you might have to restore from an old backup. - -Luckily, etcd can take periodic snapshots of your cluster and these can be used -in cases of disaster recovery. Information about how to do snapshots and -restores can be found here: -https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/recovery.md - -*For more details please refer to the official documentation:* `Backing up an etcd cluster `__ - - -Troubleshooting -~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -How to recover from a single unhealthy etcd node after virtual machine snapshot restore -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -After restoring an etcd machine from an earlier snapshot of the machine disk, etcd members may become unable to join. - -Symptoms: That etcd process is unable to start and crashes, and other etcd nodes can't reach it:: - - failed to check the health of member e767162297c84b1e on https://10.10.1.12:2379: Get https://10.10.1.12:2379/health: dial tcp 10.10.1.12:2379: getsockopt: connection refused - member e767162297c84b1e is unreachable: [https://10.10.1.12:2379] are all unreachable - -Logs from the crashing etcd:: - - (...) - Sep 25 09:27:05 node2 etcd[20288]: 2019-09-25 07:27:05.691409 I | raft: e767162297c84b1e [term: 28] received a MsgHeartbeat message with higher term from cca4e6f315097b3b [term: 30] - Sep 25 09:27:05 node2 etcd[20288]: 2019-09-25 07:27:05.691620 I | raft: e767162297c84b1e became follower at term 30 - Sep 25 09:27:05 node2 etcd[20288]: 2019-09-25 07:27:05.692423 C | raft: tocommit(16152654) is out of range [lastIndex(16061986)]. Was the raft log corrupted, truncated, or lost? - Sep 25 09:27:05 node2 etcd[20288]: panic: tocommit(16152654) is out of range [lastIndex(16061986)]. Was the raft log corrupted, truncated, or lost? - Sep 25 09:27:05 node2 etcd[20288]: goroutine 90 [running]: - (...) - - -Etcd will refuse nodes that run behind to join the cluster. If a node has -committed to a certain version of the raft log, it is expected not to jump back -in time after that. In this scenario, we turned an etcd server off, made a -snapshot of the virtual machine, brought it back online, and then restored the -snapshot. What went wrong is is that if you bring up a VM snapshot, it means -the etcd node will now have an older raft log than it had before; even though -it already gossiped to all other nodes that it has knowledge of newer entries. - -As a safety precaution, the other nodes will reject the node that is travelling -back in time, to avoid data corruption. A node could get corrupted for other -reasons as well. Perhaps a disk is faulty and is serving wrong data. Either -way, if you end up in a scenario where a node is unhealthy and will refuse to -rejoin the cluster, it is time to do some operations to get the cluster back in -a healthy state. - -It is not recommended to restore an etcd node from a vm snapshot, as that will -cause these kind of time-travelling behaviours which will make the node -unhealthy. To recover from this situation anyway, -I quote from the etcdv2 admin guide https://github.com/etcd-io/etcd/blob/master/Documentation/v2/admin_guide.md - - If a member’s data directory is ever lost or corrupted then the user should - remove the etcd member from the cluster using etcdctl tool. A user should - avoid restarting an etcd member with a data directory from an out-of-date - backup. Using an out-of-date data directory can lead to inconsistency as the - member had agreed to store information via raft then re-joins saying it - needs that information again. For maximum safety, if an etcd member suffers - any sort of data corruption or loss, it must be removed from the cluster. - Once removed the member can be re-added with an empty data directory. - - -Note that this piece of documentation is from etcdv2 and not etcdv3. However -the etcdv3 docs describe a similar procedure here -https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/runtime-configuration.md#replace-a-failed-machine - - -The procedure to remove and add a member is documented here: -https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/runtime-configuration.md#remove-a-member - -It is also documented in the kubernetes documentation: -https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#replacing-a-failed-etcd-member - -So following the above guides step by step, we can recover our cluster to be -healthy again. - -First let us make sure our broken member is stopped by runnning this on ``node``: - -.. code:: sh - - systemctl stop etcd - -Now from a healthy node, e.g. ``node0`` remove the broken node - -.. code:: sh - - etcdctl3.sh member remove e767162297c84b1e - -And we expect the output to be something like - -.. code:: sh - - Member e767162297c84b1e removed from cluster 432c10551aa096af - - -By removing the member from the cluster, you signal the other nodes to not -expect it to come back with the right state. It will be considered dead and -removed from the peers. This will allow the node to come up with an empty data -directory and it not getting kicked out of the cluster. The cluster should now -be healthy, but only have 2 members, and so it is not to resistent to crashes -at the moment! As we can see if we run the health check from a healthy node. - -.. code:: sh - - etcd-health.sh - -And we expect only two nodes to be in the cluster:: - - Cluster-Endpoints: https://127.0.0.1:2379 - cURL Command: curl -X GET https://127.0.0.1:2379/v2/members - member 7c37f7dc10558fae is healthy: got healthy result from https://10.10.1.11:2379 - member cca4e6f315097b3b is healthy: got healthy result from https://10.10.1.10:2379 - cluster is healthy - -Now from a healthy node, re-add the node you just removed. Make sure -to replace the IP in the snippet below with the IP of the node you just removed. - -.. code:: sh - - etcdctl3.sh member add etcd_2 --peer-urls https://10.10.1.12:2380 - -And it should report that it has been added:: - - Member e13b1d076b2f9344 added to cluster 432c10551aa096af - - ETCD_NAME="etcd_2" - ETCD_INITIAL_CLUSTER="etcd_1=https://10.10.1.11:2380,etcd_0=https://10.10.1.10:2380,etcd_2=https://10.10.1.12:2380" - ETCD_INITIAL_CLUSTER_STATE="existing" - - -it should now be in the list as "unstarted" instead of it not being in the list at all. - -.. code:: sh - - etcdctl3.sh member list - - - 7c37f7dc10558fae, started, etcd_1, https://10.10.1.11:2380, https://10.10.1.11:2379 - cca4e6f315097b3b, started, etcd_0, https://10.10.1.10:2380, https://10.10.1.10:2379 - e13b1d076b2f9344, unstarted, , https://10.10.1.12:2380, - - -Now on the broken node, remove the on-disk state, which was corrupted, and start etcd - -.. code:: sh - - mv /var/lib/etcd /var/lib/etcd.bak - sudo systemctl start etcd - -If we run the health check now, the cluster should report its healthy now again. - -.. code:: sh - - etcd-health.sh - -And indeed it outputs so:: - - Cluster-Endpoints: https://127.0.0.1:2379 - cURL Command: curl -X GET https://127.0.0.1:2379/v2/members - member 7c37f7dc10558fae is healthy: got healthy result from https://10.10.1.11:2379 - member cca4e6f315097b3b is healthy: got healthy result from https://10.10.1.10:2379 - member e13b1d076b2f9344 is healthy: got healthy result from https://10.10.1.12:2379 - cluster is healthy - - - diff --git a/docs/src/how-to/administrate/general-linux.md b/docs/src/how-to/administrate/general-linux.md new file mode 100644 index 0000000000..e0f6b694fe --- /dev/null +++ b/docs/src/how-to/administrate/general-linux.md @@ -0,0 +1,67 @@ +# General - Linux + +```{eval-rst} +.. include:: includes/intro.rst +``` + +## Which ports and network interface is my process running on? + +The following shows open TCP ports, and the related processes. + +```sh +sudo netstat -antlp | grep LISTEN +``` + +which may yield output like this: + +```sh +tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1536/sshd +``` + +(how-to-see-tls-certs)= + +## How can I see if my TLS certificates are configured the way I expect? + +```{note} +The following assumes you're querying a server from outside (e.g. your laptop). See the next section if operating on a server from an SSH session. +``` + +You can use openssl to check, with e.g. + +```sh +DOMAIN=example.com +PORT=443 +echo Q | openssl s_client -showcerts -connect $DOMAIN:$PORT +``` + +or + +```sh +DOMAIN=example.com +PORT=443 +echo Q | openssl s_client -showcerts -connect $DOMAIN:$PORT 2>/dev/null | openssl x509 -inform pem -noout -text +``` + +To see only the validity (expiration): + +```sh +DOMAIN=example.com +PORT=443 +echo Q | openssl s_client -showcerts -connect $DOMAIN:$PORT 2>/dev/null | openssl x509 -inform pem -noout -text | grep Validity -A 2 +``` + +## How can I see if my TLS certificates are configured the way I expect (special case kubernetes from a kubernetes machine) + +When you first SSH to a kubernetes node, depending on the setup, DNS may not resolve, in which case you can use the `-servername` parameter: + +```sh +# the IP of the network interface that kubernetes is listening on. 127.0.0.1 may or may not work depending on the installation. It's one of those from +# ifconfig | grep "inet addr" +IP=1.2.3.4 +# PORT can be 443 or 31773, depending on the installation +PORT=443 +# not the root domain, but one of the 5 subdomains for which kubernetes is serving traffic +DOMAIN=app.example.com + +echo Q | openssl s_client -showcerts -servername $DOMAIN -connect $IP:$PORT 2>/dev/null | openssl x509 -inform pem -noout -text | grep Validity -A 2 +``` diff --git a/docs/src/how-to/administrate/general-linux.rst b/docs/src/how-to/administrate/general-linux.rst deleted file mode 100644 index a2c8d81d1d..0000000000 --- a/docs/src/how-to/administrate/general-linux.rst +++ /dev/null @@ -1,69 +0,0 @@ -General - Linux --------------------------- - -.. include:: includes/intro.rst - -Which ports and network interface is my process running on? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following shows open TCP ports, and the related processes. - -.. code:: sh - - sudo netstat -antlp | grep LISTEN - -which may yield output like this: - -.. code:: sh - - tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1536/sshd - -.. _how-to-see-tls-certs: - -How can I see if my TLS certificates are configured the way I expect? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. note:: - The following assumes you're querying a server from outside (e.g. your laptop). See the next section if operating on a server from an SSH session. - -You can use openssl to check, with e.g. - -.. code:: sh - - DOMAIN=example.com - PORT=443 - echo Q | openssl s_client -showcerts -connect $DOMAIN:$PORT - -or - -.. code:: sh - - DOMAIN=example.com - PORT=443 - echo Q | openssl s_client -showcerts -connect $DOMAIN:$PORT 2>/dev/null | openssl x509 -inform pem -noout -text - -To see only the validity (expiration): - -.. code:: sh - - DOMAIN=example.com - PORT=443 - echo Q | openssl s_client -showcerts -connect $DOMAIN:$PORT 2>/dev/null | openssl x509 -inform pem -noout -text | grep Validity -A 2 - - -How can I see if my TLS certificates are configured the way I expect (special case kubernetes from a kubernetes machine) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you first SSH to a kubernetes node, depending on the setup, DNS may not resolve, in which case you can use the ``-servername`` parameter: - -.. code:: sh - - # the IP of the network interface that kubernetes is listening on. 127.0.0.1 may or may not work depending on the installation. It's one of those from - # ifconfig | grep "inet addr" - IP=1.2.3.4 - # PORT can be 443 or 31773, depending on the installation - PORT=443 - # not the root domain, but one of the 5 subdomains for which kubernetes is serving traffic - DOMAIN=app.example.com - - echo Q | openssl s_client -showcerts -servername $DOMAIN -connect $IP:$PORT 2>/dev/null | openssl x509 -inform pem -noout -text | grep Validity -A 2 diff --git a/docs/src/how-to/administrate/index.md b/docs/src/how-to/administrate/index.md new file mode 100644 index 0000000000..5f6dd1ab72 --- /dev/null +++ b/docs/src/how-to/administrate/index.md @@ -0,0 +1,12 @@ +# Administrate components after successful installation + +```{toctree} +:glob: true +:maxdepth: 2 + +Kubernetes + +* +``` + +% TODO: .. include:: administration/redis.rst diff --git a/docs/src/how-to/administrate/index.rst b/docs/src/how-to/administrate/index.rst deleted file mode 100644 index 5995a82a3c..0000000000 --- a/docs/src/how-to/administrate/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -Administrate components after successful installation -===================================================== - -.. toctree:: - :maxdepth: 2 - :glob: - - Kubernetes - - * - -.. - TODO: .. include:: administration/redis.rst - diff --git a/docs/src/how-to/administrate/kubernetes/certificate-renewal/index.md b/docs/src/how-to/administrate/kubernetes/certificate-renewal/index.md new file mode 100644 index 0000000000..ae9323d55f --- /dev/null +++ b/docs/src/how-to/administrate/kubernetes/certificate-renewal/index.md @@ -0,0 +1,10 @@ +# Certificate renewal + +```{toctree} +:glob: true +:maxdepth: 1 + +* +``` + +% diff --git a/docs/src/how-to/administrate/kubernetes/certificate-renewal/index.rst b/docs/src/how-to/administrate/kubernetes/certificate-renewal/index.rst deleted file mode 100644 index b782d3b107..0000000000 --- a/docs/src/how-to/administrate/kubernetes/certificate-renewal/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Certificate renewal -=================== - -.. toctree:: - :maxdepth: 1 - :glob: - - * - -.. \ No newline at end of file diff --git a/docs/src/how-to/administrate/kubernetes/certificate-renewal/scenario-1_k8s-v1.14-kubespray.md b/docs/src/how-to/administrate/kubernetes/certificate-renewal/scenario-1_k8s-v1.14-kubespray.md new file mode 100644 index 0000000000..316b644cd0 --- /dev/null +++ b/docs/src/how-to/administrate/kubernetes/certificate-renewal/scenario-1_k8s-v1.14-kubespray.md @@ -0,0 +1,241 @@ +# How to renew certificates on kubernetes 1.14.x + +Kubernetes-internal certificates by default (see assumptions) expire after one year. Without renewal, your installation will cease to function. +This page explains how to renew certificates. + +## Assumptions + +- Kubernetes version 1.14.x + +- installed with the help of [Kubespray](https://github.com/kubernetes-sigs/kubespray) + + - This page was tested using kubespray release 2.10 branch from 2019-05-20, i.e. commit `e2f5a9748e4dbfe2fdba7931198b0b5f1f4bdc7e`. + +- setup: 3 scheduled nodes, each hosting master (control plane) + + worker (kubelet) + etcd (cluster state, key-value database) + +*NOTE: due to Kubernetes being installed with Kubespray, the Kubernetes +CAs (expire after 10yr) as well as certificates involved in etcd +communication (expire after 100yr) are not required to be renewed (any +time soon).* + +**Official documentation:** + +- [Certificate Management with kubeadm (v1.14)](https://v1-14.docs.kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/) +- [PKI certificates and requirements (v1.14)](https://v1-14.docs.kubernetes.io/docs/setup/best-practices/certificates/) + +## High-level description + +1. verify current expiration date +2. issue new certificates +3. generate new client configuration (aka kubeconfig file) +4. restart control plane +5. drain node - restart kubelet - uncordon node again +6. repeat 3-5 on all other nodes + +## Step-by-step instructions + +*Please note, that the following instructions may require privileged +execution. So, either switch to a privileged user or prepend following +statements with \`\`sudo\`\`. In any case, it is most likely that every +newly created file has to be owned by \`\`root\`\`, depending on kow +Kubernetes was installed.* + +1. Verify current expiration date on each node + +```bash +export K8S_CERT_DIR=/etc/kubernetes/pki +export ETCD_CERT_DIR=/etc/ssl/etcd/ssl +export KUBELET_CERT_DIR=/var/lib/kubelet/pki + + +for crt in ${K8S_CERT_DIR}/*.crt; do + expirationDate=$(openssl x509 -noout -text -in ${crt} | grep After | sed -e 's/^[[:space:]]*//') + echo "$(basename ${crt}) -- ${expirationDate}" +done + + +for crt in $(ls ${ETCD_CERT_DIR}/*.pem | grep -v 'key'); do + expirationDate=$(openssl x509 -noout -text -in ${crt} | grep After | sed -e 's/^[[:space:]]*//') + echo "$(basename ${crt}) -- ${expirationDate}" +done + +echo "kubelet-client-current.pem -- $(openssl x509 -noout -text -in ${KUBELET_CERT_DIR}/kubelet-client-current.pem | grep After | sed -e 's/^[[:space:]]*//')" +echo "kubelet.crt -- $(openssl x509 -noout -text -in ${KUBELET_CERT_DIR}/kubelet.crt | grep After | sed -e 's/^[[:space:]]*//')" + + +# MASTER: api-server cert +echo -n | openssl s_client -connect localhost:6443 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout | grep Not +# MASTER: controller-manager cert +echo -n | openssl s_client -connect localhost:10257 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout | grep Not +# MASTER: scheduler cert +echo -n | openssl s_client -connect localhost:10259 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout | grep Not + +# WORKER: kubelet cert +echo -n | openssl s_client -connect localhost:10250 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout | grep Not +``` + +2. Allocate a terminal session on one node and backup existing + certificates & configurations + +```bash +cd /etc/kubernetes + +cp -r ./ssl ./ssl.bkp + +cp admin.conf admin.conf.bkp +cp controller-manager.conf controller-manager.conf.bkp +cp scheduler.conf scheduler.conf.bkp +cp kubelet.conf kubelet.conf.bkp +``` + +3. Renew certificates on that very node + +```bash +kubeadm alpha certs renew apiserver +kubeadm alpha certs renew apiserver-kubelet-client +kubeadm alpha certs renew front-proxy-client +``` + +*Looking at the timestamps of the certificates, it is indicated, that apicerver, kubelet & proxy-client have been +renewed. This can be confirmed, by executing parts of (1).* + +``` +root@kubenode01:/etc/kubernetes$ ls -al ./ssl +total 56 +drwxr-xr-x 2 kube root 4096 Mar 20 17:09 . +drwxr-xr-x 5 kube root 4096 Mar 20 17:08 .. +-rw-r--r-- 1 root root 1517 Mar 20 15:12 apiserver.crt +-rw------- 1 root root 1675 Mar 20 15:12 apiserver.key +-rw-r--r-- 1 root root 1099 Mar 20 15:13 apiserver-kubelet-client.crt +-rw------- 1 root root 1675 Mar 20 15:13 apiserver-kubelet-client.key +-rw-r--r-- 1 root root 1025 Sep 23 14:53 ca.crt +-rw------- 1 root root 1679 Sep 23 14:53 ca.key +-rw-r--r-- 1 root root 1038 Sep 23 14:53 front-proxy-ca.crt +-rw------- 1 root root 1679 Sep 23 14:53 front-proxy-ca.key +-rw-r--r-- 1 root root 1058 Mar 20 15:13 front-proxy-client.crt +-rw------- 1 root root 1675 Mar 20 15:13 front-proxy-client.key +-rw------- 1 root root 1679 Sep 23 14:53 sa.key +-rw------- 1 root root 451 Sep 23 14:53 sa.pub +``` + +4. Based on those renewed certificates, generate new kubeconfig files + +The first command assumes it's being executed on a master node. You may need to swap `masters` with `nodes` in +case you are on a different sort of machines. + +```bash +kubeadm alpha kubeconfig user --org system:masters --client-name kubernetes-admin > /etc/kubernetes/admin.conf +kubeadm alpha kubeconfig user --client-name system:kube-controller-manager > /etc/kubernetes/controller-manager.conf +kubeadm alpha kubeconfig user --client-name system:kube-scheduler > /etc/kubernetes/scheduler.conf +``` + +*Again, check if ownership and permission for these files are the same +as all the others around them.* + +And, in case you are operating the cluster from the current node, you may want to replace the user's kubeconfig. +Afterwards, compare the backup version with the new one, to see if any configuration (e.g. pre-configured *namespace*) +might need to be moved over, too. + +```bash +mv ~/.kube/config ~/.kube/config.bkp +cp /etc/kubernetes/admin.conf ~/.kube/config +chown $(id -u):$(id -g) ~/.kube/config +chmod 770 ~/.kube/config +``` + +5. Now that certificates and configuration files are in place, the + control plane must be restarted. They typically run in containers, so + the easiest way to trigger a restart, is to kill the processes + running in there. Use (1) to verify, that the expiration dates indeed + have been changed. + +```bash +kill -s SIGHUP $(pidof kube-apiserver) +kill -s SIGHUP $(pidof kube-controller-manager) +kill -s SIGHUP $(pidof kube-scheduler) +``` + +6. Make *kubelet* aware of the new certificate + +1) Drain the node + +``` +kubectl drain --delete-local-data --ignore-daemonsets $(hostname) +``` + +2. Stop the kubelet process + +``` +systemctl stop kubelet +``` + +3. Remove old certificates and configuration + +``` +mv /var/lib/kubelet/pki{,old} +mkdir /var/lib/kubelet/pki +``` + +4. Generate new kubeconfig file for the kubelet + +``` +kubeadm alpha kubeconfig user --org system:nodes --client-name system:node:$(hostname) > /etc/kubernetes/kubelet.conf +``` + +5. Start kubelet again + +``` +systemctl start kubelet +``` + +6. \[Optional\] Verify kubelet has recognized certificate rotation + +``` +sleep 5 && systemctl status kubelet +``` + +7. Allow workload to be scheduled again on the node + +``` +kubectl uncordon $(hostname) +``` + +7. Copy certificates over to all the other nodes + +Option A - you can ssh from one kubernetes node to another + +```bash +# set the ip or hostname: +export NODE2=root@ip-or-hostname +export NODE3=... + +scp ./ssl/apiserver.* "${NODE2}:/etc/kubernetes/ssl/" +scp ./ssl/apiserver.* "${NODE3}:/etc/kubernetes/ssl/" + +scp ./ssl/apiserver-kubelet-client.* "${NODE2}:/etc/kubernetes/ssl/" +scp ./ssl/apiserver-kubelet-client.* "${NODE3}:/etc/kubernetes/ssl/" + +scp ./ssl/front-proxy-client.* "${NODE2}:/etc/kubernetes/ssl/" +scp ./ssl/front-proxy-client.* "${NODE3}:/etc/kubernetes/ssl/" +``` + +Option B - copy via local administrator's machine + +```bash +# set the ip or hostname: +export NODE1=root@ip-or-hostname +export NODE2= +export NODE3= + +scp -3 "${NODE1}:/etc/kubernetes/ssl/apiserver.*" "${NODE2}:/etc/kubernetes/ssl/" +scp -3 "${NODE1}:/etc/kubernetes/ssl/apiserver.*" "${NODE3}:/etc/kubernetes/ssl/" + +scp -3 "${NODE1}:/etc/kubernetes/ssl/apiserver-kubelet-client.*" "${NODE2}:/etc/kubernetes/ssl/" +scp -3 "${NODE1}:/etc/kubernetes/ssl/apiserver-kubelet-client.*" "${NODE3}:/etc/kubernetes/ssl/" + +scp -3 "${NODE1}:/etc/kubernetes/ssl/front-proxy-client.*" "${NODE2}:/etc/kubernetes/ssl/" +scp -3 "${NODE1}:/etc/kubernetes/ssl/front-proxy-client.*" "${NODE3}:/etc/kubernetes/ssl/" +``` + +8. Continue again with (4) for each node that is left diff --git a/docs/src/how-to/administrate/kubernetes/certificate-renewal/scenario-1_k8s-v1.14-kubespray.rst b/docs/src/how-to/administrate/kubernetes/certificate-renewal/scenario-1_k8s-v1.14-kubespray.rst deleted file mode 100644 index 2db6f4f178..0000000000 --- a/docs/src/how-to/administrate/kubernetes/certificate-renewal/scenario-1_k8s-v1.14-kubespray.rst +++ /dev/null @@ -1,244 +0,0 @@ -How to renew certificates on kubernetes 1.14.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Kubernetes-internal certificates by default (see assumptions) expire after one year. Without renewal, your installation will cease to function. -This page explains how to renew certificates. - -Assumptions ------------ - -- Kubernetes version 1.14.x -- installed with the help of `Kubespray `__ - - - This page was tested using kubespray release 2.10 branch from 2019-05-20, i.e. commit ``e2f5a9748e4dbfe2fdba7931198b0b5f1f4bdc7e``. -- setup: 3 scheduled nodes, each hosting master (control plane) + - worker (kubelet) + etcd (cluster state, key-value database) - -*NOTE: due to Kubernetes being installed with Kubespray, the Kubernetes -CAs (expire after 10yr) as well as certificates involved in etcd -communication (expire after 100yr) are not required to be renewed (any -time soon).* - -**Official documentation:** - -* `Certificate Management with kubeadm (v1.14) `__ -* `PKI certificates and requirements (v1.14) `__ - -High-level description ----------------------- - -1. verify current expiration date -2. issue new certificates -3. generate new client configuration (aka kubeconfig file) -4. restart control plane -5. drain node - restart kubelet - uncordon node again -6. repeat 3-5 on all other nodes - -Step-by-step instructions -------------------------- - -*Please note, that the following instructions may require privileged -execution. So, either switch to a privileged user or prepend following -statements with ``sudo``. In any case, it is most likely that every -newly created file has to be owned by ``root``, depending on kow -Kubernetes was installed.* - -1. Verify current expiration date on each node - -.. code:: bash - - - export K8S_CERT_DIR=/etc/kubernetes/pki - export ETCD_CERT_DIR=/etc/ssl/etcd/ssl - export KUBELET_CERT_DIR=/var/lib/kubelet/pki - - - for crt in ${K8S_CERT_DIR}/*.crt; do - expirationDate=$(openssl x509 -noout -text -in ${crt} | grep After | sed -e 's/^[[:space:]]*//') - echo "$(basename ${crt}) -- ${expirationDate}" - done - - - for crt in $(ls ${ETCD_CERT_DIR}/*.pem | grep -v 'key'); do - expirationDate=$(openssl x509 -noout -text -in ${crt} | grep After | sed -e 's/^[[:space:]]*//') - echo "$(basename ${crt}) -- ${expirationDate}" - done - - echo "kubelet-client-current.pem -- $(openssl x509 -noout -text -in ${KUBELET_CERT_DIR}/kubelet-client-current.pem | grep After | sed -e 's/^[[:space:]]*//')" - echo "kubelet.crt -- $(openssl x509 -noout -text -in ${KUBELET_CERT_DIR}/kubelet.crt | grep After | sed -e 's/^[[:space:]]*//')" - - - # MASTER: api-server cert - echo -n | openssl s_client -connect localhost:6443 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout | grep Not - # MASTER: controller-manager cert - echo -n | openssl s_client -connect localhost:10257 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout | grep Not - # MASTER: scheduler cert - echo -n | openssl s_client -connect localhost:10259 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout | grep Not - - # WORKER: kubelet cert - echo -n | openssl s_client -connect localhost:10250 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout | grep Not - -2. Allocate a terminal session on one node and backup existing - certificates & configurations - -.. code:: bash - - cd /etc/kubernetes - - cp -r ./ssl ./ssl.bkp - - cp admin.conf admin.conf.bkp - cp controller-manager.conf controller-manager.conf.bkp - cp scheduler.conf scheduler.conf.bkp - cp kubelet.conf kubelet.conf.bkp - -3. Renew certificates on that very node - -.. code:: bash - - kubeadm alpha certs renew apiserver - kubeadm alpha certs renew apiserver-kubelet-client - kubeadm alpha certs renew front-proxy-client - -*Looking at the timestamps of the certificates, it is indicated, that apicerver, kubelet & proxy-client have been -renewed. This can be confirmed, by executing parts of (1).* - -:: - - root@kubenode01:/etc/kubernetes$ ls -al ./ssl - total 56 - drwxr-xr-x 2 kube root 4096 Mar 20 17:09 . - drwxr-xr-x 5 kube root 4096 Mar 20 17:08 .. - -rw-r--r-- 1 root root 1517 Mar 20 15:12 apiserver.crt - -rw------- 1 root root 1675 Mar 20 15:12 apiserver.key - -rw-r--r-- 1 root root 1099 Mar 20 15:13 apiserver-kubelet-client.crt - -rw------- 1 root root 1675 Mar 20 15:13 apiserver-kubelet-client.key - -rw-r--r-- 1 root root 1025 Sep 23 14:53 ca.crt - -rw------- 1 root root 1679 Sep 23 14:53 ca.key - -rw-r--r-- 1 root root 1038 Sep 23 14:53 front-proxy-ca.crt - -rw------- 1 root root 1679 Sep 23 14:53 front-proxy-ca.key - -rw-r--r-- 1 root root 1058 Mar 20 15:13 front-proxy-client.crt - -rw------- 1 root root 1675 Mar 20 15:13 front-proxy-client.key - -rw------- 1 root root 1679 Sep 23 14:53 sa.key - -rw------- 1 root root 451 Sep 23 14:53 sa.pub - -4. Based on those renewed certificates, generate new kubeconfig files - -The first command assumes it's being executed on a master node. You may need to swap ``masters`` with ``nodes`` in -case you are on a different sort of machines. - -.. code:: bash - - kubeadm alpha kubeconfig user --org system:masters --client-name kubernetes-admin > /etc/kubernetes/admin.conf - kubeadm alpha kubeconfig user --client-name system:kube-controller-manager > /etc/kubernetes/controller-manager.conf - kubeadm alpha kubeconfig user --client-name system:kube-scheduler > /etc/kubernetes/scheduler.conf - -*Again, check if ownership and permission for these files are the same -as all the others around them.* - -And, in case you are operating the cluster from the current node, you may want to replace the user's kubeconfig. -Afterwards, compare the backup version with the new one, to see if any configuration (e.g. pre-configured *namespace*) -might need to be moved over, too. - -.. code:: bash - - mv ~/.kube/config ~/.kube/config.bkp - cp /etc/kubernetes/admin.conf ~/.kube/config - chown $(id -u):$(id -g) ~/.kube/config - chmod 770 ~/.kube/config - -5. Now that certificates and configuration files are in place, the - control plane must be restarted. They typically run in containers, so - the easiest way to trigger a restart, is to kill the processes - running in there. Use (1) to verify, that the expiration dates indeed - have been changed. - -.. code:: bash - - kill -s SIGHUP $(pidof kube-apiserver) - kill -s SIGHUP $(pidof kube-controller-manager) - kill -s SIGHUP $(pidof kube-scheduler) - -6. Make *kubelet* aware of the new certificate - -a) Drain the node - -:: - - kubectl drain --delete-local-data --ignore-daemonsets $(hostname) - -b) Stop the kubelet process - -:: - - systemctl stop kubelet - -c) Remove old certificates and configuration - -:: - - mv /var/lib/kubelet/pki{,old} - mkdir /var/lib/kubelet/pki - -d) Generate new kubeconfig file for the kubelet - -:: - - kubeadm alpha kubeconfig user --org system:nodes --client-name system:node:$(hostname) > /etc/kubernetes/kubelet.conf - -e) Start kubelet again - -:: - - systemctl start kubelet - -f) [Optional] Verify kubelet has recognized certificate rotation - -:: - - sleep 5 && systemctl status kubelet - -g) Allow workload to be scheduled again on the node - -:: - - kubectl uncordon $(hostname) - -7. Copy certificates over to all the other nodes - -Option A - you can ssh from one kubernetes node to another - -.. code:: bash - - # set the ip or hostname: - export NODE2=root@ip-or-hostname - export NODE3=... - - scp ./ssl/apiserver.* "${NODE2}:/etc/kubernetes/ssl/" - scp ./ssl/apiserver.* "${NODE3}:/etc/kubernetes/ssl/" - - scp ./ssl/apiserver-kubelet-client.* "${NODE2}:/etc/kubernetes/ssl/" - scp ./ssl/apiserver-kubelet-client.* "${NODE3}:/etc/kubernetes/ssl/" - - scp ./ssl/front-proxy-client.* "${NODE2}:/etc/kubernetes/ssl/" - scp ./ssl/front-proxy-client.* "${NODE3}:/etc/kubernetes/ssl/" - -Option B - copy via local administrator's machine - -.. code:: bash - - # set the ip or hostname: - export NODE1=root@ip-or-hostname - export NODE2= - export NODE3= - - scp -3 "${NODE1}:/etc/kubernetes/ssl/apiserver.*" "${NODE2}:/etc/kubernetes/ssl/" - scp -3 "${NODE1}:/etc/kubernetes/ssl/apiserver.*" "${NODE3}:/etc/kubernetes/ssl/" - - scp -3 "${NODE1}:/etc/kubernetes/ssl/apiserver-kubelet-client.*" "${NODE2}:/etc/kubernetes/ssl/" - scp -3 "${NODE1}:/etc/kubernetes/ssl/apiserver-kubelet-client.*" "${NODE3}:/etc/kubernetes/ssl/" - - scp -3 "${NODE1}:/etc/kubernetes/ssl/front-proxy-client.*" "${NODE2}:/etc/kubernetes/ssl/" - scp -3 "${NODE1}:/etc/kubernetes/ssl/front-proxy-client.*" "${NODE3}:/etc/kubernetes/ssl/" - -8. Continue again with (4) for each node that is left diff --git a/docs/src/how-to/administrate/kubernetes/index.md b/docs/src/how-to/administrate/kubernetes/index.md new file mode 100644 index 0000000000..cc2c6a0143 --- /dev/null +++ b/docs/src/how-to/administrate/kubernetes/index.md @@ -0,0 +1,20 @@ +# Kubernetes + +```{note} +These are not the official documentations you are looking for. +[This way](https://kubernetes.io/docs/tasks/administer-cluster/) please. + +The content referred below merely contains either some deviation from upstream or +additional information enriched here and there with shortcuts to the official documentation. +``` + +```{toctree} +:glob: true +:maxdepth: 1 + +Certificate renewal +How to restart a machine that is part of a Kubernetes cluster? +How to upgrade Kubernetes? +``` + +% diff --git a/docs/src/how-to/administrate/kubernetes/index.rst b/docs/src/how-to/administrate/kubernetes/index.rst deleted file mode 100644 index 2e6fcd71da..0000000000 --- a/docs/src/how-to/administrate/kubernetes/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -Kubernetes -========== - -.. note:: - - These are not the official documentations you are looking for. - `This way `__ please. - - The content referred below merely contains either some deviation from upstream or - additional information enriched here and there with shortcuts to the official documentation. - - -.. toctree:: - :maxdepth: 1 - :glob: - - Certificate renewal - How to restart a machine that is part of a Kubernetes cluster? - How to upgrade Kubernetes? - -.. diff --git a/docs/src/how-to/administrate/kubernetes/restart-machines/index.md b/docs/src/how-to/administrate/kubernetes/restart-machines/index.md new file mode 100644 index 0000000000..0323efcf2d --- /dev/null +++ b/docs/src/how-to/administrate/kubernetes/restart-machines/index.md @@ -0,0 +1,42 @@ +(restarting-a-machine-in-a-kubernetes-cluster)= + +# Restarting a machine in a Kubernetes cluster + +```{note} +1. Know which kind of machine is going to be restarted + + > 1. control plane (api-server, controllers, etc.) + > 2. node (runs actual workload, e.g. *Brig* or *Webapp*) + > 3. *a* and *b* combined + +2. The kind of machine in question must be deployed redundantly + +3. Take out machines in a rolling fashion (sequentially, one at a time) +``` + +## Control plane + +Depending on whether *etcd* is hosted on the same machine alongside the control plane (common practise), you need +to take its implications into account (see {ref}`How to rolling-restart an etcd cluster `) +when restarting a machine. + +Regardless of where *etcd* is located, before turning off any machine that is part of the control plane, one should +{ref}`back up the cluster state `. + +If a part of the control plane does not run sufficiently redundant, it is advised to prevent any mutating interaction +during the procedure, until the cluster is healthy again. + +```bash +kubectl get nodes +``` + +## Node + +```{rubric} High-level steps: +``` + +1. Drain the node so that all workload is rescheduled on other nodes +2. Restart / Update / Decommission +3. Mark the node as being schedulable again (if not decommissioned) + +*For more details please refer to the official documentation:* [Safely Drain a Node](https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/) diff --git a/docs/src/how-to/administrate/kubernetes/restart-machines/index.rst b/docs/src/how-to/administrate/kubernetes/restart-machines/index.rst deleted file mode 100644 index 8090b3c366..0000000000 --- a/docs/src/how-to/administrate/kubernetes/restart-machines/index.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _restarting-a-machine-in-a-kubernetes-cluster: - -Restarting a machine in a Kubernetes cluster -============================================ - -.. note:: - - 1. Know which kind of machine is going to be restarted - - a) control plane (api-server, controllers, etc.) - b) node (runs actual workload, e.g. *Brig* or *Webapp*) - c) *a* and *b* combined - - 2. The kind of machine in question must be deployed redundantly - 3. Take out machines in a rolling fashion (sequentially, one at a time) - - -Control plane -~~~~~~~~~~~~~ - -Depending on whether *etcd* is hosted on the same machine alongside the control plane (common practise), you need -to take its implications into account (see :ref:`How to rolling-restart an etcd cluster `) -when restarting a machine. - -Regardless of where *etcd* is located, before turning off any machine that is part of the control plane, one should -:ref:`back up the cluster state `. - -If a part of the control plane does not run sufficiently redundant, it is advised to prevent any mutating interaction -during the procedure, until the cluster is healthy again. - -.. code:: bash - - kubectl get nodes - - -Node -~~~~ - -.. rubric:: High-level steps: - -1. Drain the node so that all workload is rescheduled on other nodes -2. Restart / Update / Decommission -3. Mark the node as being schedulable again (if not decommissioned) - -*For more details please refer to the official documentation:* `Safely Drain a Node `__ diff --git a/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.md b/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.md new file mode 100644 index 0000000000..739ae7ee2c --- /dev/null +++ b/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.md @@ -0,0 +1,75 @@ +# Upgrading a Kubernetes cluster + +Before upgrading Kubernetes, a couple of aspects should be taken into account: + +- downtime is (not) permitted +- stateful backing services that run outside or on top of Kubernetes + +As a result the following questions arise: + +1. Is an in-place upgrade required (reuse existing machines) or is it possible to + deploy a second cluster right next to the first one and install Wire on top? +2. How was the Kubernetes cluster deployed? + +Depending on the deployment method, the upgrade procedure may vary. It may be reasonable to test +the upgrade in a non-production environment first. +Regardless of the deployment method, it is recommended to {ref}`back up the cluster state +` before starting to upgrade the cluster. Additional background knowledge +can be found in the section about {ref}`restarting a machine in an kubernetes cluster `. + +```{warning} +For an in-place upgrade, it is *NOT* recommended to go straight to the latest Kubernetes +version. Instead, one should upgrade step by step between each minor version. +``` + +## Manually + +Doing an upgrade by hand is cumbersome and error-prone, which is why there are tools and +automation for this procedure. The high-level steps would be: + +1. upgrade the control plane (also see a more detailed [list](https://kubernetes.io/docs/tasks/administer-cluster/cluster-upgrade/#manual-deployments)) + : 1. all *etcd* instances + 2. api-server on each control-plane host + 3. controllers, scheduler, +2. upgrade the nodes (order may vary, depending on whether the kube-components run in containers) + : - kubelet + - kube-proxy + - container runtime +3. then upgrade the clients (`kubectl`, e.g. on workstations or in pipelines) + +*For more details, please refer to the official documentation:* +[Upgrade A Cluster](https://kubernetes.io/docs/tasks/administer-cluster/cluster-upgrade/) + +## Kubespray (Ansible) + +Kubespray comes with a dedicated playbook that should be used to perform the upgrade: +`upgrade-cluster.yml`. Before running the playbook, make sure that the right Kubespray version +is being used. Each Kubespray version supports only a small and sliding window of Kubernetes +versions (check `kube_version` & `kube_version_min_required` in `roles/kubespray-defaults/defaults/main.yaml` +for a given [release version tag](https://github.com/kubernetes-sigs/kubespray/releases)). + +The commands may look similar to this example (assuming Kubernetes v1.18 version installed +with Kubespray 2.14): + +```bash +git clone https://github.com/kubernetes-sigs/kubespray +cd kubespray +git checkout release-2.15 +${EDITOR} roles/kubespray-defaults/defaults/main.yaml + +ansible-playbook -i ./../path/my/inventory-dir -e kube_version=v1.19.7 ./upgrade-cluster.yml +``` + +% TODO: adjust the example showing how to run this with wire-server-deploy a/o the offline toolchain container image + +% TODO: add ref to the part of this documentation that talks about the air-gapped installation + +Kubespray takes care of bringing the new binaries into position on each machine, restarting +the components, and draining/uncordon nodes. + +*For more details please refer to the official documentation:* +[Upgrading Kubernetes in Kubespray](https://kubespray.io/#/docs/upgrades) + +## Kubeadm + +Please refer to the *official documentation:* [Upgrading kubeadm clusters](https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/) diff --git a/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.rst b/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.rst deleted file mode 100644 index 3b1b4137a0..0000000000 --- a/docs/src/how-to/administrate/kubernetes/upgrade-cluster/index.rst +++ /dev/null @@ -1,82 +0,0 @@ -Upgrading a Kubernetes cluster -============================== - -Before upgrading Kubernetes, a couple of aspects should be taken into account: - -* downtime is (not) permitted -* stateful backing services that run outside or on top of Kubernetes - -As a result the following questions arise: - -1. Is an in-place upgrade required (reuse existing machines) or is it possible to - deploy a second cluster right next to the first one and install Wire on top? -2. How was the Kubernetes cluster deployed? - -Depending on the deployment method, the upgrade procedure may vary. It may be reasonable to test -the upgrade in a non-production environment first. -Regardless of the deployment method, it is recommended to :ref:`back up the cluster state -` before starting to upgrade the cluster. Additional background knowledge -can be found in the section about :ref:`restarting a machine in an kubernetes cluster `. - - -.. warning:: - - For an in-place upgrade, it is *NOT* recommended to go straight to the latest Kubernetes - version. Instead, one should upgrade step by step between each minor version. - - -Manually -~~~~~~~~ - -Doing an upgrade by hand is cumbersome and error-prone, which is why there are tools and -automation for this procedure. The high-level steps would be: - -1. upgrade the control plane (also see a more detailed `list `__) - a) all *etcd* instances - b) api-server on each control-plane host - c) controllers, scheduler, -2. upgrade the nodes (order may vary, depending on whether the kube-components run in containers) - * kubelet - * kube-proxy - * container runtime -3. then upgrade the clients (``kubectl``, e.g. on workstations or in pipelines) - -*For more details, please refer to the official documentation:* -`Upgrade A Cluster `__ - - -Kubespray (Ansible) -~~~~~~~~~~~~~~~~~~~ - -Kubespray comes with a dedicated playbook that should be used to perform the upgrade: -``upgrade-cluster.yml``. Before running the playbook, make sure that the right Kubespray version -is being used. Each Kubespray version supports only a small and sliding window of Kubernetes -versions (check ``kube_version`` & ``kube_version_min_required`` in ``roles/kubespray-defaults/defaults/main.yaml`` -for a given `release version tag `__). - -The commands may look similar to this example (assuming Kubernetes v1.18 version installed -with Kubespray 2.14): - -.. code:: bash - - git clone https://github.com/kubernetes-sigs/kubespray - cd kubespray - git checkout release-2.15 - ${EDITOR} roles/kubespray-defaults/defaults/main.yaml - - ansible-playbook -i ./../path/my/inventory-dir -e kube_version=v1.19.7 ./upgrade-cluster.yml - -.. TODO: adjust the example showing how to run this with wire-server-deploy a/o the offline toolchain container image -.. TODO: add ref to the part of this documentation that talks about the air-gapped installation - -Kubespray takes care of bringing the new binaries into position on each machine, restarting -the components, and draining/uncordon nodes. - -*For more details please refer to the official documentation:* -`Upgrading Kubernetes in Kubespray `__ - - -Kubeadm -~~~~~~~ - -Please refer to the *official documentation:* `Upgrading kubeadm clusters `__ diff --git a/docs/src/how-to/administrate/minio.rst b/docs/src/how-to/administrate/minio.md similarity index 59% rename from docs/src/how-to/administrate/minio.rst rename to docs/src/how-to/administrate/minio.md index cf6d95fe50..1a79ba648d 100644 --- a/docs/src/how-to/administrate/minio.rst +++ b/docs/src/how-to/administrate/minio.md @@ -1,20 +1,18 @@ -Minio ------- +# Minio +```{eval-rst} .. include:: includes/intro.rst +``` -This section only covers the bare minimum, for more information, see the `minio documentation `__ +This section only covers the bare minimum, for more information, see the [minio documentation](https://docs.min.io/) - -Should you be using minio? -=========================== +## Should you be using minio? Minio can be used to emulate an S3-compatible setup. When a native S3-like storage provider is already present in your network or cloud provider, we advise using that instead. -Setting up interaction with Minio -================================= +## Setting up interaction with Minio Minio can be installed on your servers using our provided ansible playbooks. The ansible playbook will also install the minio client and configure it to @@ -25,29 +23,33 @@ minio to run behind a loadbalancer like HAProxy, and configure the Minio client to point to this loadbalancer instead. Our ansible playbooks will also configure the minio client and adds the locally -reachable API under the ``local`` alias:: +reachable API under the `local` alias: - mc config host list +``` +mc config host list +``` -If it is not there, it can be added manually as follows:: +If it is not there, it can be added manually as follows: - mc config host add local http://localhost:9000 +``` +mc config host add local http://localhost:9000 +``` The status of the cluster can be requested by contacting any of the servers. In -our case we will contact the locally running server:: +our case we will contact the locally running server: - mc admin info local +``` +mc admin info local +``` -Minio maintenance -================= +## Minio maintenance There will be times where one wants to take a minio server down for maintenance. One might want to apply security patches, or want to take out a broken disk and replace it with a fixed one. Minio will not tell you the health status of disks. You should have separate alerting and monitoring in place to keep track of hardware health. For example, one could look at -S.M.A.R.T. values that the disks produce with Prometheus `node_exporter -`_ +S.M.A.R.T. values that the disks produce with Prometheus [node_exporter](https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/smartmon.sh) Special care has to be taken when restarting Minio nodes, but it should be safe to do so. Minio can operate in read-write mode with (N/2) + 1 instances @@ -62,9 +64,11 @@ interrupted and the user must retry. When you shut down a node, one should take precautions that subsequent API calls are sent to other nodes in the cluster. -To stop a server, type:: +To stop a server, type: - systemctl stop minio-server +``` +systemctl stop minio-server +``` Writes that happen during the server being down will not be synced to the server that is offline. It is important that once you bring the server back @@ -77,32 +81,40 @@ is thus recommended to heal an instance immediately once it is back up; before a restart any other instances. Now that the server is offline, perform any maintenance that you want to do. -Afterwards, restart it with:: +Afterwards, restart it with: - systemctl start minio-server +``` +systemctl start minio-server +``` -Now check:: +Now check: - mc admin info local +``` +mc admin info local +``` to see if the cluster is healthy. Now that the server is back online, it has missed writes that have happened whilst it was offline. Because of this we must heal the cluster now -A heal of the cluster is performed as follows:: +A heal of the cluster is performed as follows: - mc admin heal -r local +``` +mc admin heal -r local +``` -Which will show a result page that looks like this:: +Which will show a result page that looks like this: - ◑ bunny - 0/0 objects; 0 B in 2s - ┌────────┬───┬─────────────────────┐ - │ Green │ 2 │ 66.7% ████████ │ - │ Yellow │ 1 │ 33.3% ████ │ - │ Red │ 0 │ 0.0% │ - │ Grey │ 0 │ 0.0% │ - └────────┴───┴─────────────────────┘ +``` +◑ bunny + 0/0 objects; 0 B in 2s + ┌────────┬───┬─────────────────────┐ + │ Green │ 2 │ 66.7% ████████ │ + │ Yellow │ 1 │ 33.3% ████ │ + │ Red │ 0 │ 0.0% │ + │ Grey │ 0 │ 0.0% │ + └────────┴───┴─────────────────────┘ +``` green - all good yellow - healed partially @@ -110,33 +122,32 @@ red - quorum missing grey - more than quorum number shards are gone, means the object for some reason is not recoverable When there are any yellow items, it usually means that not all servers have seen -the node come up properly again. Running the heal command with the ``--json`` option +the node come up properly again. Running the heal command with the `--json` option will give you more verbose and precise information why the heal only happened partially. -.. code:: json - - { - "after" : { - "online" : 5, - "offline" : 1, - "missing" : 0, - "corrupted" : 0, - "drives" : [ - { - "endpoint" : "http://10.0.0.42:9091/var/lib/minio-server1", - "state" : "offline", - "uuid" : "" - }, - { - "uuid" : "", - "endpoint" : "/var/lib/minio-server1", - "state" : "ok" - } - ], - "color" : "yellow" - } - } - +```json +{ + "after" : { + "online" : 5, + "offline" : 1, + "missing" : 0, + "corrupted" : 0, + "drives" : [ + { + "endpoint" : "http://10.0.0.42:9091/var/lib/minio-server1", + "state" : "offline", + "uuid" : "" + }, + { + "uuid" : "", + "endpoint" : "/var/lib/minio-server1", + "state" : "ok" + } + ], + "color" : "yellow" + } +} +``` In our case, we see that the reason for the partial recovery was that one the server was still considered offline. Rerunning the command yielded @@ -158,96 +169,93 @@ thus important to have good monitoring in place and respond accordingly. Minio itself will auto-heal the cluster every month if the administrator doesn't trigger a heal themselves. - -Rotate root credentials -======================= +## Rotate root credentials In order to change the root credentials, one needs to restart minio once but set with the old and the new credentials at the same time. -If you installed minio with the Ansible, the `role `__ +If you installed minio with the Ansible, the [role](https://github.com/wireapp/ansible-minio) takes care of this. Just change the inventory accordingly and re-apply the role. -For more information, please refer to the *Credentials* section in the `official documentation `__. +For more information, please refer to the *Credentials* section in the [official documentation](https://docs.min.io/docs/minio-server-configuration-guide.html). -.. _check-the-health-of-a-minio-node: +(check-the-health-of-a-minio-node)= -Check the health of a MinIO node -================================ +## Check the health of a MinIO node This is the procedure to check a minio node's health -First log into the minio server - -.. code:: sh +First log into the minio server - ssh +```sh +ssh +``` There, run the following commands: -.. code:: sh - - env $(sudo grep KEY /etc/default/minio-server1 | xargs) bash - export MC_HOST_local="http://$MINIO_ACCESS_KEY:$MINIO_SECRET_KEY@127.0.0.1:9000" - mc admin info local +```sh +env $(sudo grep KEY /etc/default/minio-server1 | xargs) bash +export MC_HOST_local="http://$MINIO_ACCESS_KEY:$MINIO_SECRET_KEY@127.0.0.1:9000" +mc admin info local +``` You should see a result similar to this: -.. code:: sh - - * 192.168.0.12:9092 - Uptime: 2 months - Version: 2020-10-28T08:16:50Z - Network: 6/6 OK - Drives: 1/1 OK - - * 192.168.0.22:9000 - Uptime: 2 months - Version: 2020-10-28T08:16:50Z - Network: 6/6 OK - Drives: 1/1 OK - - * 192.168.0.22:9092 - Uptime: 2 months - Version: 2020-10-28T08:16:50Z - Network: 6/6 OK - Drives: 1/1 OK - - * 192.168.0.32:9000 - Uptime: 2 months - Version: 2020-10-28T08:16:50Z - Network: 6/6 OK - Drives: 1/1 OK - - * 192.168.0.32:9092 - Uptime: 2 months - Version: 2020-10-28T08:16:50Z - Network: 6/6 OK - Drives: 1/1 OK - - * 192.168.0.12:9000 - Uptime: 2 months - Version: 2020-10-28T08:16:50Z - Network: 6/6 OK - Drives: 1/1 OK - -Make sure you see ``Network: 6/6 OK``. +```sh +* 192.168.0.12:9092 +Uptime: 2 months +Version: 2020-10-28T08:16:50Z +Network: 6/6 OK +Drives: 1/1 OK + +* 192.168.0.22:9000 +Uptime: 2 months +Version: 2020-10-28T08:16:50Z +Network: 6/6 OK +Drives: 1/1 OK + +* 192.168.0.22:9092 +Uptime: 2 months +Version: 2020-10-28T08:16:50Z +Network: 6/6 OK +Drives: 1/1 OK + +* 192.168.0.32:9000 +Uptime: 2 months +Version: 2020-10-28T08:16:50Z +Network: 6/6 OK +Drives: 1/1 OK + +* 192.168.0.32:9092 +Uptime: 2 months +Version: 2020-10-28T08:16:50Z +Network: 6/6 OK +Drives: 1/1 OK + +* 192.168.0.12:9000 +Uptime: 2 months +Version: 2020-10-28T08:16:50Z +Network: 6/6 OK +Drives: 1/1 OK +``` + +Make sure you see `Network: 6/6 OK`. Reboot the machine with: -.. code:: sh - - sudo reboot +```sh +sudo reboot +``` Then wait at least a minute. If you go to ssh in, and get 'Connection refused', it just means you need to wait a bit longer. -Tip: You can automatically ask SSH to attempt to connect until it is succesful, by using the following command: - -.. code:: sh +Tip: You can automatically ask SSH to attempt to connect until it is succesful, by using the following command: - ssh -o 'ConnectionAttempts 3600' exit +```sh +ssh -o 'ConnectionAttempts 3600' exit +``` Log into minio ( repeat the steps above ), and check again. diff --git a/docs/src/how-to/administrate/operations.rst b/docs/src/how-to/administrate/operations.md similarity index 50% rename from docs/src/how-to/administrate/operations.rst rename to docs/src/how-to/administrate/operations.md index 16b3cebdba..9a8b8522a6 100644 --- a/docs/src/how-to/administrate/operations.rst +++ b/docs/src/how-to/administrate/operations.md @@ -1,112 +1,106 @@ - -Operational procedures -====================== +# Operational procedures This section describes common operations to be performed on operational clusters. -Reboot procedures ------------------ +## Reboot procedures The general procedure to reboot a service is as follows: -* 1. :ref:`Check the health ` of the service. (If the health isn't good search for "troubleshooting" in the documentation. If it is good, move to the next step.) -* 2. Reboot the server the service is running on. -* 3. :ref:`Check the health ` of the service **again**. (If the health isn't good search for "troubleshooting" in the documentation. If it is good, your reboot was succesful.) +- 1. {ref}`Check the health ` of the service. (If the health isn't good search for "troubleshooting" in the documentation. If it is good, move to the next step.) +- 2. Reboot the server the service is running on. +- 3. {ref}`Check the health ` of the service **again**. (If the health isn't good search for "troubleshooting" in the documentation. If it is good, your reboot was succesful.) -The method for checking health is different for each service type, you can find a list of those methods :ref:`here `. +The method for checking health is different for each service type, you can find a list of those methods {ref}`here `. -The method to reset a service is the same for most services, except for ``restund``, for which the procedure is different, and can be found :ref:`here `. +The method to reset a service is the same for most services, except for `restund`, for which the procedure is different, and can be found {ref}`here `. -For other (non-``restund``) services, the procedure is as follows: +For other (non-`restund`) services, the procedure is as follows: Assuming in this example you are trying to reboot a minio server, follow these steps: -First, :ref:`check the health ` of the services. +First, {ref}`check the health ` of the services. Second, reboot the services: -.. code:: sh - - ssh -t sudo reboot +```sh +ssh -t sudo reboot +``` Third, wait until the service is up again by trying to connect to it via SSH : -.. code:: sh +```sh +ssh -o 'ConnectionAttempts 3600' exit +``` - ssh -o 'ConnectionAttempts 3600' exit +(`ConnectionAttempts` will make it so it attempts to connect until the host is actually Up and the connection is succesful) -(``ConnectionAttempts`` will make it so it attempts to connect until the host is actually Up and the connection is succesful) +Fourth, {ref}`check the health ` of the service again. -Fourth, :ref:`check the health ` of the service again. +(operations-health-checks)= -.. _operations-health-checks: -Health checks -------------- +## Health checks This is a list of the health-checking procedures currently documented, for different service types: -* :ref:`MinIO ` -* :ref:`Cassandra ` -* :ref:`Elasticsearch ` -* :ref:`Etcd ` -* :ref:`Restund ` (the health check is explained as part of the reboot procedure). +- {ref}`MinIO ` +- {ref}`Cassandra ` +- {ref}`Elasticsearch ` +- {ref}`Etcd ` +- {ref}`Restund ` (the health check is explained as part of the reboot procedure). To check the health of different services not listed here, see the documentation for that specific project, or ask your Wire contact. -.. note:: - - If a service is running inside a Kubernetes pod, checking its health is easy: if the pod is running, it is healthy. A non-healthy pod will stop running, and will be shown as such. +```{note} +If a service is running inside a Kubernetes pod, checking its health is easy: if the pod is running, it is healthy. A non-healthy pod will stop running, and will be shown as such. +``` -Draining pods from a node for maintainance ------------------------------------------- +## Draining pods from a node for maintainance You might want to remove («drain») all pods from a specific node/server, so you can do maintainance work on it, without disrupting the entire cluster. -If you want to do this, you should follow the procudure found at: https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/ +If you want to do this, you should follow the procudure found at: In short, the procedure is essentially: First, identify the name of the node you wish to drain. You can list all of the nodes in your cluster with -.. code:: sh - - kubectl get nodes +```sh +kubectl get nodes +``` Next, tell Kubernetes to drain the node: -.. code:: sh - - kubectl drain +```sh +kubectl drain +``` Once it returns (without giving an error), you can power down the node (or equivalently, if on a cloud platform, delete the virtual machine backing the node). If you leave the node in the cluster during the maintenance operation, you need to run -.. code:: sh - - kubectl uncordon +```sh +kubectl uncordon +``` afterwards to tell Kubernetes that it can resume scheduling new pods onto the node. -Understand release tags ------------------------ +## Understand release tags We have two major release tags that you sometimes want to map on each other: *github*, and *helm chart*. -Github have a tag of the form `vYYYY-MM-DD`, and the release notes and (some build artefacts) can be found on github, eg., `here `__. Helm chart tags have the form `N.NNN.0`. The minor version `0` is for the development branch; non-zero values refer to unreleased intermediate states. +Github have a tag of the form `vYYYY-MM-DD`, and the release notes and (some build artefacts) can be found on github, eg., [here](https://github.com/wireapp/wire-server/releases/v2022-01-18). Helm chart tags have the form `N.NNN.0`. The minor version `0` is for the development branch; non-zero values refer to unreleased intermediate states. -On the command line -^^^^^^^^^^^^^^^^^^^ +### On the command line You can find the github tag for a helm chart tag like this: -.. code:: sh - - git tag --points-at v2022-01-18 | sort +```sh +git tag --points-at v2022-01-18 | sort +``` ... and the other way around like this: -.. code:: sh - - git tag --points-at chart=2.122.0,image=2.122.0 | sort +```sh +git tag --points-at chart=2.122.0,image=2.122.0 | sort +``` Note that the actual tag has the form `chart=,image=`. @@ -114,32 +108,32 @@ Unfortunately, older releases may have more helm chart tags; you need to find th A list of all releases can be produced like this: -.. code:: sh - - git log --decorate --first-parent origin/master +```sh +git log --decorate --first-parent origin/master +``` If you want to find the -In the github UI -^^^^^^^^^^^^^^^^ +### In the github UI -Consult `the changelog -`__ +Consult [the changelog](https://github.com/wireapp/wire-server/blob/develop/CHANGELOG.md) to find the github tag of the release you're interested in (say, v2022-01-18). -Visit `the release notes of that release -`__. +Visit [the release notes of that release](https://github.com/wireapp/wire-server/releases/v2022-01-18). Click on the commit hash: -.. image:: operations/fig1.png +```{image} operations/fig1.png +``` Click on the 3 dots: -.. image:: operations/fig2.png +```{image} operations/fig2.png +``` Now you can see a (possibly rather long) list of tags, some of then have the form `chart=N.NNN.0,image=N.NNN.0`. Pick the one with the largest number. -.. image:: operations/fig3.png +```{image} operations/fig3.png +``` diff --git a/docs/src/how-to/administrate/restund.md b/docs/src/how-to/administrate/restund.md new file mode 100644 index 0000000000..86bdd27e6a --- /dev/null +++ b/docs/src/how-to/administrate/restund.md @@ -0,0 +1,293 @@ +# Restund (TURN) + +```{eval-rst} +.. include:: includes/intro.rst +``` + +(allocations)= + +## Wire-Server Configuration + +The wire-server can either serve a static list of TURN servers to the clients or +it can discovery them using DNS SRV Records. + +### Static List + +To configure a static list of TURN servers to use, override +`values/wire-server/values.yaml` like this: + +```yaml +# (...) + +brig: +# (...) + turnStatic: + v1: + # v1 entries can be ignored and are not in use anymore since end of 2018. + v2: + - turn:server1.example.com:3478 # server 1 UDP + - turn:server1.example.com:3478?transport=tcp # server 1 TCP + - turns:server1.example.com:5478?transport=tcp # server 1 TLS + - turn:server2.example.com:3478 # server 2 UDP + - turn:server2.example.com:3478?transport=tcp # server 2 TCP + - turns:server2.example.com:5478?transport=tcp # server 2 TLS + turn: + serversSource: files +``` + +### DNS SRV Records + +To configure wire-server to use DNS SRV records in order to discover TURN +servers, override `values/wire-server/values.yaml` like this: + +```yaml +# (...) + +brig: +# (...) + turn: + serversSource: dns + baseDomain: prod.example.com + discoveryIntervalSeconds: 10 +``` + +When configured like this, the wire-server would look for these 3 SRV records +every 10 seconds: + +1. `_turn._udp.prod.example.com` will be used to discover UDP hostnames and port for all the + turn servers. +2. `_turn._tcp.prod.example.com` will be used to discover the TCP hostnames and port for all + the turn servers. +3. `_turns._tcp.prod.example.com` will be used to discover the TLS hostnames and port for + all the turn servers. + +Entries with weight 0 will be ignored. Example: + +``` +dig +retries=3 +short SRV _turn._udp.prod.example.com + +0 0 3478 turn36.prod.example.com +0 10 3478 turn34..prod.example.com +0 10 3478 turn35.prod.example.com +``` + +At least one of these 3 lookups must succeed for the wire-server to be able to +respond correctly when `GET /calls/config/v2` is called. All successful +responses are served in the result. + +In addition, if there are any clients using the legacy endpoint, `GET +/calls/config`, (all versions of all mobile apps since 2018 no longer use this) they will be served by the servers listed in the +`_turn._udp.prod.example.com` SRV record. This endpoint, however, will not +serve the domain names received inside the SRV record, instead it will serve the +first `A` record that is associated with each domain name in the SRV record. + +## How to see how many people are currently connected to the restund server + +You can see the count of currently ongoing calls (also called "allocations"): + +```sh +echo turnstats | nc -u 127.0.0.1 33000 -q1 | grep allocs_cur | cut -d' ' -f2 +``` + +## How to restart restund (with downtime) + +With downtime, it's very easy: + +``` +systemctl restart restund +``` + +```{warning} +Restarting `restund` means any user that is currently connected to it (i.e. having a call) will lose its audio/video connection. If you wish to have no downtime, check the next section\* +``` + +(rebooting-a-restund-node)= + +## Rebooting a Restund node + +If you want to reboot a restund node, you need to make sure the other restund nodes in the cluster are running, so that services are not interrupted by the reboot. + +```{warning} +This procedure as described here will cause downtime, even if a second restund server is up; and kill any ongoing audio/video calls. The sections further up describe a downtime and a no-downtime procedure. +``` + +Presuming your two restund nodes are called: + +- `restund-1` +- `restund-2` + +To prepare for a reboot of `restund-1`, log into the other restund server (`restund-2`, for example here), and make sure the docker service is running. + +List the running containers, to ensure restund is running, by executing: + +```sh +ssh -t sudo docker container ls +``` + +You should see the following in the results: + +```sh +CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES + quay.io/wire/restund:v0.4.16b1.0.53 22 seconds ago Up 18 seconds restund +``` + +Make sure you see this restund container, and it is running ("Up"). + +If it is not, you need to do troubleshooting work, if it is running, you can move forward and reboot restund-1. + +Now log into the restund server you wish to reboot (`restund-1` in this example), and reboot it + +```sh +ssh -t sudo reboot +``` + +Wait at least a minute for the machine to restart, you can use this command to automatically retry SSH access until it is succesful: + +```sh +ssh -o 'ConnectionAttempts 3600' exit +``` + +Then log into the restund server (`restund-1`, in this example), and make sure the docker service is running: + +```sh +ssh -t sudo docker container ls +``` + +```sh +CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES + quay.io/wire/restund:v0.4.16b1.0.53 22 seconds ago Up 18 seconds restund +``` + +Here again, make sure you see a restund container, and it is running ("Up"). + +If it is, you have succesfully reboot the restund server, and can if you need to apply the same procedure to the other restund servers in your cluster. + +## How to restart restund without having downtime + +For maintenance you may need to restart a restund server. + +1. Remove that restund server you want to restart from the list of advertised nodes, by taking it out of the turn server list that brig advertises: + +Go to the place where you store kubernetes configuration for your wire-server installation. This might be a directory on your admin laptop, or a directory on the kubernetes machine. + +If your override configuration (`values/wire-server/values.yaml`) looks like the following: + +```yaml +# (...) + +brig: +# (...) + turnStatic: + v1: + # v1 entries can be ignored and are not in use anymore since end of 2018. + v2: + - turn:server1.example.com:3478 # server 1 UDP + - turn:server1.example.com:3478?transport=tcp # server 1 TCP + - turns:server1.example.com:5478?transport=tcp # server 1 TLS + - turn:server2.example.com:3478 # server 2 UDP + - turn:server2.example.com:3478?transport=tcp # server 2 TCP + - turns:server2.example.com:5478?transport=tcp # server 2 TLS +``` + +And you want to remove server 1, then change the configuration to read + +```yaml +turnStatic: + v2: + - turn:server2.example.com:3478 # server 2 UDP + - turn:server2.example.com:3478?transport=tcp # server 2 TCP + - turns:server2.example.com:5478?transport=tcp # server 2 TLS +``` + +(or comment out lines by adding a `#` in front of the respective line) + +```yaml +turnStatic: + v2: + #- turn:server1.example.com:3478 # server 1 UDP + #- turn:server1.example.com:3478?transport=tcp # server 1 TCP + #- turns:server1.example.com:5478?transport=tcp # server 1 TLS + - turn:server2.example.com:3478 # server 2 UDP + - turn:server2.example.com:3478?transport=tcp # server 2 TCP + - turns:server2.example.com:5478?transport=tcp # server 2 TLS +``` + +Next, apply these changes to configuration with `./bin/prod-setup.sh` + +You then need to restart the `brig` pods if your code is older than September 2019 (otherwise brig will restart itself automatically): + +```bash +kubectl delete pod -l app=brig +``` + +2. Wait for traffic to drain. This can take up to 12 hours after the configuration change. Wait until current allocations (people connected to the restund server) return 0. See {ref}`allocations`. +3. It's now safe to `systemctl stop restund`, and take any necessary actions. +4. `systemctl start restund` and then add the restund server back to configuration of advertised nodes (see step 1, put the server back). + +## How to renew a certificate for restund + +1. Replace the certificate file on the server (under `/etc/restund/restund.pem` usually), either with ansible or manually. Ensure the new certificate file is a concatenation of your whole certificate chain *and* the private key: + +```text +-----BEGIN CERTIFICATE----- +... +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +... +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +... +-----END PRIVATE KEY----- +``` + +2. Restart restund (see sections above) + +## How to check which restund/TURN servers will be used by clients + +The list of turn servers contacted by clients *should* match what you added to your `turnStatic` configuration. But if you'd like to double-check, here's how: + +Terminal one: + +```sh +kubectl port-forward svc/brig 9999:8080 +``` + +Terminal two: + +```sh +UUID=$(cat /proc/sys/kernel/random/uuid) +curl -s -H "Z-User:$UUID" -H "Z-Connection:anything" "http://localhost:9999/calls/config/v2" | json_pp +``` + +May return something like: + +```json +{ + "ice_servers" : [ + { + "credential" : "ASyFLXqbmg8fuK4chJG3S1Qg4L/nnhpkN0/UctdtTFbGW1AcuuAaOqUMDhm9V2w7zKHY6PPMqjhwKZ2neSE78g==", + "urls" : [ + "turn:turn1.example.com:3478" + ], + "username" : "d=1582157904.v=1.k=0.t=s.r=mbzovplogqxbasbf" + }, + { + "credential" : "ZsxEtGWbpUZ3QWxPZtbX6g53HXu6PWfhhUfGNqRBJjrsly5w9IPAsuAWLEOP7fsoSXF13mgSPROXxMYAB/fQ6g==", + "urls" : [ + "turn:turn1.example.com:3478?transport=tcp" + ], + "username" : "d=1582157904.v=1.k=0.t=s.r=jsafnwtgqhfqjvco" + }, + { + "credential" : "ZsxEtGWbpUZ3QWxPZtbX6g53HXu6PWfhhUfGNqRBJjrsly5w9IPAsuAWLEOP7fsoSXF13mgSPROXxMYAB/fQ6g==", + "urls" : [ + "turns:turn1.example.com:5349?transport=tcp" + ], + "username" : "d=1582157904.v=1.k=0.t=s.r=jsafnwtgqhfqjvco" + } + ], + "ttl" : 3600 +} +``` + +In the above case, there is a single server configured to use UDP on port 3478, plain TCP on port 3478, and TLS over TCP on port 5349. The ordering of the list is random and will change on every request made with curl. diff --git a/docs/src/how-to/administrate/restund.rst b/docs/src/how-to/administrate/restund.rst deleted file mode 100644 index 21c658500a..0000000000 --- a/docs/src/how-to/administrate/restund.rst +++ /dev/null @@ -1,302 +0,0 @@ -Restund (TURN) --------------- - -.. include:: includes/intro.rst - -.. _allocations: - -Wire-Server Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The wire-server can either serve a static list of TURN servers to the clients or -it can discovery them using DNS SRV Records. - -Static List -+++++++++++ - -To configure a static list of TURN servers to use, override -``values/wire-server/values.yaml`` like this: - -.. code:: yaml - - # (...) - - brig: - # (...) - turnStatic: - v1: - # v1 entries can be ignored and are not in use anymore since end of 2018. - v2: - - turn:server1.example.com:3478 # server 1 UDP - - turn:server1.example.com:3478?transport=tcp # server 1 TCP - - turns:server1.example.com:5478?transport=tcp # server 1 TLS - - turn:server2.example.com:3478 # server 2 UDP - - turn:server2.example.com:3478?transport=tcp # server 2 TCP - - turns:server2.example.com:5478?transport=tcp # server 2 TLS - turn: - serversSource: files - -DNS SRV Records -+++++++++++++++ - -To configure wire-server to use DNS SRV records in order to discover TURN -servers, override ``values/wire-server/values.yaml`` like this: - -.. code:: yaml - - # (...) - - brig: - # (...) - turn: - serversSource: dns - baseDomain: prod.example.com - discoveryIntervalSeconds: 10 - -When configured like this, the wire-server would look for these 3 SRV records -every 10 seconds: - -1. ``_turn._udp.prod.example.com`` will be used to discover UDP hostnames and port for all the - turn servers. -2. ``_turn._tcp.prod.example.com`` will be used to discover the TCP hostnames and port for all - the turn servers. -3. ``_turns._tcp.prod.example.com`` will be used to discover the TLS hostnames and port for - all the turn servers. - -Entries with weight 0 will be ignored. Example: - -.. code:: - - dig +retries=3 +short SRV _turn._udp.prod.example.com - - 0 0 3478 turn36.prod.example.com - 0 10 3478 turn34..prod.example.com - 0 10 3478 turn35.prod.example.com - -At least one of these 3 lookups must succeed for the wire-server to be able to -respond correctly when ``GET /calls/config/v2`` is called. All successful -responses are served in the result. - -In addition, if there are any clients using the legacy endpoint, ``GET -/calls/config``, (all versions of all mobile apps since 2018 no longer use this) they will be served by the servers listed in the -``_turn._udp.prod.example.com`` SRV record. This endpoint, however, will not -serve the domain names received inside the SRV record, instead it will serve the -first ``A`` record that is associated with each domain name in the SRV record. - -How to see how many people are currently connected to the restund server -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can see the count of currently ongoing calls (also called "allocations"): - -.. code:: sh - - echo turnstats | nc -u 127.0.0.1 33000 -q1 | grep allocs_cur | cut -d' ' -f2 - -How to restart restund (with downtime) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -With downtime, it's very easy:: - - systemctl restart restund - -.. warning:: - - Restarting ``restund`` means any user that is currently connected to it (i.e. having a call) will lose its audio/video connection. If you wish to have no downtime, check the next section* - -.. _rebooting-a-restund-node: -Rebooting a Restund node -~~~~~~~~~~~~~~~~~~~~~~~~ - -If you want to reboot a restund node, you need to make sure the other restund nodes in the cluster are running, so that services are not interrupted by the reboot. - -.. warning:: - - This procedure as described here will cause downtime, even if a second restund server is up; and kill any ongoing audio/video calls. The sections further up describe a downtime and a no-downtime procedure. - -Presuming your two restund nodes are called: - -* ``restund-1`` -* ``restund-2`` - -To prepare for a reboot of ``restund-1``, log into the other restund server (``restund-2``, for example here), and make sure the docker service is running. - -List the running containers, to ensure restund is running, by executing: - -.. code:: sh - - ssh -t sudo docker container ls - -You should see the following in the results: - -.. code:: sh - - CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES - quay.io/wire/restund:v0.4.16b1.0.53 22 seconds ago Up 18 seconds restund - -Make sure you see this restund container, and it is running ("Up"). - -If it is not, you need to do troubleshooting work, if it is running, you can move forward and reboot restund-1. - -Now log into the restund server you wish to reboot (``restund-1`` in this example), and reboot it - -.. code:: sh - - ssh -t sudo reboot - -Wait at least a minute for the machine to restart, you can use this command to automatically retry SSH access until it is succesful: - -.. code:: sh - - ssh -o 'ConnectionAttempts 3600' exit - -Then log into the restund server (``restund-1``, in this example), and make sure the docker service is running: - -.. code:: sh - - ssh -t sudo docker container ls - -.. code:: sh - - CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES - quay.io/wire/restund:v0.4.16b1.0.53 22 seconds ago Up 18 seconds restund - -Here again, make sure you see a restund container, and it is running ("Up"). - -If it is, you have succesfully reboot the restund server, and can if you need to apply the same procedure to the other restund servers in your cluster. - -How to restart restund without having downtime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For maintenance you may need to restart a restund server. - -1. Remove that restund server you want to restart from the list of advertised nodes, by taking it out of the turn server list that brig advertises: - -Go to the place where you store kubernetes configuration for your wire-server installation. This might be a directory on your admin laptop, or a directory on the kubernetes machine. - -If your override configuration (``values/wire-server/values.yaml``) looks like the following: - -.. code:: yaml - - # (...) - - brig: - # (...) - turnStatic: - v1: - # v1 entries can be ignored and are not in use anymore since end of 2018. - v2: - - turn:server1.example.com:3478 # server 1 UDP - - turn:server1.example.com:3478?transport=tcp # server 1 TCP - - turns:server1.example.com:5478?transport=tcp # server 1 TLS - - turn:server2.example.com:3478 # server 2 UDP - - turn:server2.example.com:3478?transport=tcp # server 2 TCP - - turns:server2.example.com:5478?transport=tcp # server 2 TLS - -And you want to remove server 1, then change the configuration to read - -.. code:: yaml - - turnStatic: - v2: - - turn:server2.example.com:3478 # server 2 UDP - - turn:server2.example.com:3478?transport=tcp # server 2 TCP - - turns:server2.example.com:5478?transport=tcp # server 2 TLS - -(or comment out lines by adding a ``#`` in front of the respective line) - -.. code:: yaml - - turnStatic: - v2: - #- turn:server1.example.com:3478 # server 1 UDP - #- turn:server1.example.com:3478?transport=tcp # server 1 TCP - #- turns:server1.example.com:5478?transport=tcp # server 1 TLS - - turn:server2.example.com:3478 # server 2 UDP - - turn:server2.example.com:3478?transport=tcp # server 2 TCP - - turns:server2.example.com:5478?transport=tcp # server 2 TLS - -Next, apply these changes to configuration with ``./bin/prod-setup.sh`` - -You then need to restart the ``brig`` pods if your code is older than September 2019 (otherwise brig will restart itself automatically): - -.. code:: bash - - kubectl delete pod -l app=brig - -2. Wait for traffic to drain. This can take up to 12 hours after the configuration change. Wait until current allocations (people connected to the restund server) return 0. See :ref:`allocations`. -3. It's now safe to ``systemctl stop restund``, and take any necessary actions. -4. ``systemctl start restund`` and then add the restund server back to configuration of advertised nodes (see step 1, put the server back). - -How to renew a certificate for restund -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -1. Replace the certificate file on the server (under ``/etc/restund/restund.pem`` usually), either with ansible or manually. Ensure the new certificate file is a concatenation of your whole certificate chain *and* the private key: - -.. code:: text - - -----BEGIN CERTIFICATE----- - ... - -----END CERTIFICATE----- - -----BEGIN CERTIFICATE----- - ... - -----END CERTIFICATE----- - -----BEGIN PRIVATE KEY----- - ... - -----END PRIVATE KEY----- - - -2. Restart restund (see sections above) - - -How to check which restund/TURN servers will be used by clients -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The list of turn servers contacted by clients *should* match what you added to your `turnStatic` configuration. But if you'd like to double-check, here's how: - -Terminal one: - -.. code:: sh - - kubectl port-forward svc/brig 9999:8080 - -Terminal two: - -.. code:: sh - - UUID=$(cat /proc/sys/kernel/random/uuid) - curl -s -H "Z-User:$UUID" -H "Z-Connection:anything" "http://localhost:9999/calls/config/v2" | json_pp - - -May return something like: - -.. code:: json - - { - "ice_servers" : [ - { - "credential" : "ASyFLXqbmg8fuK4chJG3S1Qg4L/nnhpkN0/UctdtTFbGW1AcuuAaOqUMDhm9V2w7zKHY6PPMqjhwKZ2neSE78g==", - "urls" : [ - "turn:turn1.example.com:3478" - ], - "username" : "d=1582157904.v=1.k=0.t=s.r=mbzovplogqxbasbf" - }, - { - "credential" : "ZsxEtGWbpUZ3QWxPZtbX6g53HXu6PWfhhUfGNqRBJjrsly5w9IPAsuAWLEOP7fsoSXF13mgSPROXxMYAB/fQ6g==", - "urls" : [ - "turn:turn1.example.com:3478?transport=tcp" - ], - "username" : "d=1582157904.v=1.k=0.t=s.r=jsafnwtgqhfqjvco" - }, - { - "credential" : "ZsxEtGWbpUZ3QWxPZtbX6g53HXu6PWfhhUfGNqRBJjrsly5w9IPAsuAWLEOP7fsoSXF13mgSPROXxMYAB/fQ6g==", - "urls" : [ - "turns:turn1.example.com:5349?transport=tcp" - ], - "username" : "d=1582157904.v=1.k=0.t=s.r=jsafnwtgqhfqjvco" - } - ], - "ttl" : 3600 - } - -In the above case, there is a single server configured to use UDP on port 3478, plain TCP on port 3478, and TLS over TCP on port 5349. The ordering of the list is random and will change on every request made with curl. - diff --git a/docs/src/how-to/administrate/users.md b/docs/src/how-to/administrate/users.md new file mode 100644 index 0000000000..b1ec7d1c69 --- /dev/null +++ b/docs/src/how-to/administrate/users.md @@ -0,0 +1,590 @@ +(investigative-tasks)= + +# Investigative tasks (e.g. searching for users as server admin) + +This page requires that you have root access to the machines where kubernetes runs on, or have kubernetes permissions allowing you to port-forward arbitrary pods and services. + +If you have the `backoffice` pod installed, see also the [backoffice README](https://github.com/wireapp/wire-server/tree/develop/charts/backoffice). + +If you don't have `backoffice`, see below for some options: + +## Manually searching for users in cassandra + +Terminal one: + +```sh +kubectl port-forward svc/brig 9999:8080 +``` + +Terminal two: Search for your user by email: + +```sh +EMAIL=user@example.com +curl -v -G localhost:9999/i/users --data-urlencode email=$EMAIL; echo +# or, for nicer formatting +curl -v -G localhost:9999/i/users --data-urlencode email=$EMAIL | json_pp +``` + +You can also search by `handle` (unique username) or by phone: + +```sh +HANDLE=user123 +curl -v -G localhost:9999/i/users --data-urlencode handles=$HANDLE; echo + +PHONE=+490000000000000 # phone numbers must have the +country prefix and no spaces +curl -v -G localhost:9999/i/users --data-urlencode phone=$PHONE; echo +``` + +Which should give you output like: + +```json +[ + { + "managed_by" : "wire", + "assets" : [ + { + "key" : "3-2-a749af8d-a17b-4445-b360-46c93fc41bc6", + "size" : "preview", + "type" : "image" + }, + { + "size" : "complete", + "type" : "image", + "key" : "3-2-6cac6b57-9972-4aba-acbb-f078bc538b54" + } + ], + "picture" : [], + "accent_id" : 0, + "status" : "active", + "name" : "somename", + "email" : "user@example.com", + "id" : "9122e5de-b4fb-40fa-99ad-1b5d7d07bae5", + "locale" : "en", + "handle" : "user123" + } +] +``` + +The interesting part is the `id` (in the example case `9122e5de-b4fb-40fa-99ad-1b5d7d07bae5`): + +(user-deletion)= + +## Deleting a user which is not a team user + +The following will completely delete a user, its conversations, assets, etc. The only thing remaining will be an entry in cassandra indicating that this user existed in the past (only the UUID remains, all other attributes like name etc are purged) + +You can now delete that user by double-checking that the user you wish to delete is really the correct user: + +```sh +# replace the id with the id of the user you want to delete +curl -v localhost:9999/i/users/9122e5de-b4fb-40fa-99ad-1b5d7d07bae5 -XDELETE +``` + +Afterwards, the previous command (to search for a user in cassandra) should return an empty list (`[]`). + +When done, on terminal 1, ctrl+c to cancel the port-forwarding. + +## Searching and deleting users with no team + +If you require users to be part of a team, or for some other reason you need to delete all users who are not part of a team, you need to first find all such users, and then delete them. + +To find users that are not part of a team, first you need to connect via SSH to the machine where cassandra is running, and then run the following command: + +```sh +cqlsh 9042 -e "select team, handle, id from brig.user" | grep -E "^\s+null" +``` + +This will give you a list of handles and IDs with no team associated: + +```sh +null | null | bc22119f-ce11-4402-aa70-307a58fb22ec +null | tom | 8ecee3d0-47a4-43ff-977b-40a4fc350fed +null | alice | 2a4c3468-c1e6-422f-bc4d-4aeff47941ac +null | null | 1b5ca44a-aeb4-4a68-861b-48612438c4cc +null | bob | 701b4eab-6df2-476d-a818-90dc93e8446e +``` + +You can then {ref}`delete each user with these instructions `. + +## Manual search on elasticsearch (via brig, recommended) + +This should only be necessary in the case of some (suspected) data inconsistency between cassandra and elasticsearch. + +Terminal one: + +```sh +kubectl port-forward svc/brig 9999:8080 +``` + +Terminal two: Search for your user by name or handle or a prefix of that handle or name: + +```sh +NAMEORPREFIX=test7 +UUID=$(cat /proc/sys/kernel/random/uuid) +curl -H "Z-User:$UUID" "http://localhost:9999/search/contacts?q=$NAMEORPREFIX"; echo +# or, for pretty output: +curl -H "Z-User:$UUID" "http://localhost:9999/search/contacts?q=$NAMEORPREFIX" | json_pp +``` + +If no match is found, expect a query like this: + +```json +{"took":91,"found":0,"documents":[],"returned":0} +``` + +If matches are found, the result should look like this: + +```json +{ + "found" : 2, + "documents" : [ + { + "id" : "dbdbf370-48b3-4e1e-b377-76d7d4cbb4f2", + "name" : "Test", + "handle" : "test7", + "accent_id" : 7 + }, + { + "name" : "Test", + "accent_id" : 0, + "handle" : "test7476", + "id" : "a93240b0-ba89-441e-b8ee-ff4403808f93" + } + ], + "returned" : 2, + "took" : 4 +} +``` + +## How to manually search for a user on elasticsearh directly (not recommended) + +First, ssh to an elasticsearch instance. + +```sh +ssh +``` + +Then run the following: + +```sh +PREFIX=... +curl -s "http://localhost:9200/directory/_search?q=$PREFIX" | json_pp +``` + +The `id` (UUID) returned can be used when deleting (see below). + +## How to manually delete a user from elasticsearch only + +```{warning} +This is NOT RECOMMENDED. Be sure you know what you're doing. This only deletes the user from elasticsearch, but not from cassandra. Any change of e.g. the username or displayname of that user means this user will re-appear in the elasticsearch database. Instead, either fully delete a user: {ref}`user-deletion` or make use of the internal GET/PUT `/i/searchable` endpoint on brig to make this user prefix-unsearchable. +``` + +If, despite the warning, you wish to continue? + +First, ssh to an elasticsearch instance: + +```sh +ssh +``` + +Next, check that the user exists: + +```sh +UUID=... +curl -s "http://localhost:9200/directory/user/$UUID" | json_pp +``` + +That should return a `"found": true`, like this: + +```json +{ + "_type" : "user", + "_version" : 1575998428262000, + "_id" : "b3e9e445-fb02-47f3-bac0-63f5f680d258", + "found" : true, + "_index" : "directory", + "_source" : { + "normalized" : "Mr Test", + "handle" : "test12345", + "id" : "b3e9e445-fb02-47f3-bac0-63f5f680d258", + "name" : "Mr Test", + "accent_id" : 1 + } +} +``` + +Then delete it: + +```sh +UUID=... +curl -s -XDELETE "http://localhost:9200/directory/user/$UUID" | json_pp +``` + +## Mass-invite users to a team + +If you need to invite members to a specific given team, you can use the `create_team_members.sh` Bash script, located [here](https://github.com/wireapp/wire-server/blob/develop/hack/bin/create_team_members.sh). + +This script does not create users or causes them to join a team by itself, instead, it sends invites to potential users via email, and when users accept the invitation, they create their account, set their password, and are added to the team as team members. + +Input is a [CSV file](https://en.wikipedia.org/wiki/Comma-separated_values), in comma-separated format, in the form `'Email,Suggested User Name'`. + +You also need to specify the inviting admin user, the team, the URI for the Brig ([API](https://docs.wire.com/understand/federation/api.html?highlight=brig)) service (Host), and finally the input (CSV) file containing the users to invite. + +The exact format for the parameters passed to the script is [as follows](https://github.com/wireapp/wire-server/blob/develop/hack/bin/create_team_members.sh#L17): + +- `-a `: [User ID](https://docs.wire.com/understand/federation/api.html?highlight=user%20id#qualified-identifiers-and-names) in [UUID format](https://en.wikipedia.org/wiki/Universally_unique_identifier) of the inviting admin. For example `9122e5de-b4fb-40fa-99ad-1b5d7d07bae5`. +- `-t `: ID of the inviting team, same format. +- `-h `: Base URI of brig's internal endpoint. +- `-c `: file containing info on the invitees in format 'Email,UserName'. + +For example, one such execution of the script could look like: + +```sh +sh create_team_members.sh -a 9122e5de-b4fb-40fa-99ad-1b5d7d07bae5 -t 123e4567-e89b-12d3-a456-426614174000 -h http://localhost:9999 -c users_to_invite.csv +``` + +Note: the '' implies you are running the 'kubectl port-forward' given at the top of this document +. +Once the script is run, invitations will be sent to each user in the file every second until all invitations have been sent. + +If you have a lot of invitations to send and this is too slow, you can speed things up by commenting [this line](https://github.com/wireapp/wire-server/blob/develop/hack/bin/create_team_members.sh#L91). + +## How to obtain logs from an Android client to investigate issues + +Wire clients communicate with Wire servers (backend). + +Sometimes to investigate server issues, you (or the Wire team) will need client information, in the form of client logs. + +In order to obtain client logs on the Android Wire client, follow this procedure: + +- Open the Wire app (client) on your Android device +- Click on the round user icon in the top left of the screen, leading to your user Profile. +- Click on "Settings" at the bottom of the screen +- Click on "Advanced" in the menu +- Check/activate "Collect usage data" +- Now go back to using your client normally, so usage data is generated. If you have been asked to follow a specific testing regime, or log a specific problem, this is the time to do so. +- Once enough usage data is generated, go back to the "Advanced" screen (User profile > Settings > Advanced) +- Click on "Create debug report" +- A menu will open allowing you to share the debug report, you can now save it or send it via email/any other means to the Wire team. + +## How to obtain logs from an iOS client to investigate issues + +Wire clients communicate with Wire servers (backend). + +Sometimes to investigate server issues, you (or the Wire team) will need client information, in the form of client logs. + +In order to obtain client logs on the iOS Wire client, follow this procedure: + +- Open the Wire app (client) on your iOS device +- Click on the round user icon in the top left of the screen, leading to your user Profile. +- Click on "Settings" at the bottom of the screen +- Click on "Advanced" in the menu +- Check/activate "Collect usage data" +- Now go back to using your client normally, so usage data is generated. If you have been asked to follow a specific testing regime, or log a specific problem, this is the time to do so. +- Once enough usage data is generated, go back to the "Advanced" screen (User profile > Settings > Advanced) +- Click on "Send report to wire" +- A menu will open to share the debug report via email, allowing you to send it to the Wire team. + +## How to retrieve metric values manually + +Metric values are sets of data points about services, such as status and other measures, that can be retrieved at specific endpoints, typically by a monitoring system (such as Prometheus) for monitoring, diagnosis and graphing. + +Sometimes, you will want to manually obtain this data that is normally automatically grabbed by Prometheus. + +Some of the pods allow you to grab metrics by accessing their `/i/metrics` endpoint, in particular: + +- `brig`: User management API +- `cannon`: WebSockets API +- `cargohold`: Assets storage API +- `galley`: Conversations and Teams API +- `gundeck`: Push Notifications API +- `spar`: Single-Sign-ON and SCIM + +For more details on the various services/pods, you can check out {ref}`this link `. + +Before you can grab metrics from a pod, you need to find its IP address. You do this by running the following command: + +```sh +d kubectl get pods -owide +``` + +(this presumes you are already in your normal Wire environment, which you obtain by running `source ./bin/offline-env.sh`) + +Which will give you an output that looks something like this: + +``` +demo@Ubuntu-1804-bionic-64-minimal:~/Wire-Server$ d kubectl get pods -owide +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +account-pages-784f9b547c-cp444 1/1 Running 0 6d23h 10.233.113.5 kubenode3 +brig-746ddc55fd-6pltz 1/1 Running 0 6d23h 10.233.110.11 kubenode2 +brig-746ddc55fd-d59dw 1/1 Running 0 6d4h 10.233.110.23 kubenode2 +brig-746ddc55fd-zp7jl 1/1 Running 0 6d23h 10.233.113.10 kubenode3 +brig-index-migrate-data-45rm7 0/1 Completed 0 6d23h 10.233.110.9 kubenode2 +cannon-0 1/1 Running 0 3h1m 10.233.119.41 kubenode1 +cannon-1 1/1 Running 0 3h1m 10.233.113.47 kubenode3 +cannon-2 1/1 Running 0 3h1m 10.233.110.51 kubenode2 +cargohold-65bff97fc6-8b9ls 1/1 Running 0 6d4h 10.233.113.20 kubenode3 +cargohold-65bff97fc6-bkx6x 1/1 Running 0 6d23h 10.233.113.4 kubenode3 +cargohold-65bff97fc6-tz8fh 1/1 Running 0 6d23h 10.233.110.5 kubenode2 +cassandra-migrations-bjsdz 0/1 Completed 0 6d23h 10.233.110.3 kubenode2 +demo-smtp-784ddf6989-vmj7t 1/1 Running 0 6d23h 10.233.113.2 kubenode3 +elasticsearch-index-create-7r8g4 0/1 Completed 0 6d23h 10.233.110.4 kubenode2 +fake-aws-sns-6c7c4b7479-wfp82 2/2 Running 0 6d4h 10.233.110.27 kubenode2 +fake-aws-sqs-59fbfbcbd4-n4c5z 2/2 Running 0 6d23h 10.233.110.2 kubenode2 +galley-7c89c44f7b-nm2rr 1/1 Running 0 6d23h 10.233.110.8 kubenode2 +galley-7c89c44f7b-tdxz4 1/1 Running 0 6d23h 10.233.113.6 kubenode3 +galley-7c89c44f7b-tr8pm 1/1 Running 0 6d4h 10.233.110.29 kubenode2 +galley-migrate-data-g66rz 0/1 Completed 0 6d23h 10.233.110.13 kubenode2 +gundeck-7fd75c7c5f-jb8xq 1/1 Running 0 6d23h 10.233.110.6 kubenode2 +gundeck-7fd75c7c5f-lbth9 1/1 Running 0 6d23h 10.233.113.8 kubenode3 +gundeck-7fd75c7c5f-wvcw6 1/1 Running 0 6d4h 10.233.113.23 kubenode3 +nginz-5cdd8b588b-dbn86 2/2 Running 16 6d23h 10.233.113.11 kubenode3 +nginz-5cdd8b588b-gk6rw 2/2 Running 14 6d23h 10.233.110.12 kubenode2 +nginz-5cdd8b588b-jvznt 2/2 Running 11 6d4h 10.233.113.21 kubenode3 +reaper-6957694667-s5vz5 1/1 Running 0 6d4h 10.233.110.26 kubenode2 +redis-ephemeral-master-0 1/1 Running 0 6d23h 10.233.113.3 kubenode3 +spar-56d77f85f6-bw55q 1/1 Running 0 6d23h 10.233.113.9 kubenode3 +spar-56d77f85f6-mczzd 1/1 Running 0 6d4h 10.233.110.28 kubenode2 +spar-56d77f85f6-vvvfq 1/1 Running 0 6d23h 10.233.110.7 kubenode2 +spar-migrate-data-ts4sx 0/1 Completed 0 6d23h 10.233.110.14 kubenode2 +team-settings-fbbb899c-qxx7m 1/1 Running 0 6d4h 10.233.110.24 kubenode2 +webapp-d97869795-grnft 1/1 Running 0 6d4h 10.233.110.25 kubenode2 +``` + +Here presuming we need to get metrics from `gundeck`, we can see the IP of one of the gundeck pods is `10.233.110.6`. + +We can therefore connect to node `kubenode2` on which this pod runs with `ssh kubenode2.your-domain.com`, and run the following: + +```sh +curl 10.233.110.6:8080/i/metrics +``` + +Alternatively, if you don't want to, or can't for some reason, connect to kubenode2, you can use port redirect instead: + +```sh +# Allow Gundeck to be reached via the port 7777 +kubectl --kubeconfig kubeconfig.dec -n wire port-forward service/gundeck 7777:8080 +# Reach Gundeck directly at port 7777 using curl, output resulting data to stdout/terminal +curl -v http://127.0.0.1:7777/i/metrics +``` + +Output will look something like this (truncated): + +```sh +# HELP gc_seconds_wall Wall clock time spent on last GC +# TYPE gc_seconds_wall gauge +gc_seconds_wall 5481304.0 +# HELP gc_seconds_cpu CPU time spent on last GC +# TYPE gc_seconds_cpu gauge +gc_seconds_cpu 5479828.0 +# HELP gc_bytes_used_current Number of bytes in active use as of the last GC +# TYPE gc_bytes_used_current gauge +gc_bytes_used_current 1535232.0 +# HELP gc_bytes_used_max Maximum amount of memory living on the heap after the last major GC +# TYPE gc_bytes_used_max gauge +gc_bytes_used_max 2685312.0 +# HELP gc_bytes_allocated_total Bytes allocated since the start of the server +# TYPE gc_bytes_allocated_total gauge +gc_bytes_allocated_total 4.949156056e9 +``` + +This example is for Gundeck, but you can also get metrics for other services. All k8s services are listed at {ref}`this link `. + +This is an example adapted for Cannon: + +```sh +kubectl --kubeconfig kubeconfig.dec -n wire port-forward service/cannon 7777:8080 +curl -v http://127.0.0.1:7777/i/metrics +``` + +In the output of this command, `net_websocket_clients` is roughly the number of connected clients. + +(reset-session-cookies)= + +## Reset session cookies + +Remove session cookies on your system to force users to login again within the next 15 minutes (or whenever they come back online): + +```{warning} +This will cause interruptions to ongoing calls and should be timed properly. +``` + +### Reset cookies of all users + +```sh +ssh +# from the ssh session +cqlsh +# from the cqlsh shell +truncate brig.user_cookies; +``` + +### Reset cookies for a defined list of users + +```sh +ssh +# within the ssh session +cqlsh +# within the cqlsh shell: delete all users by userId +delete from brig.user_cookies where user in (c0d64244-8ab4-11ec-8fda-37788be3a4e2, ...); +``` + +(Keep reading if you want to find out which users on your system are using SSO.) + +(identify-sso-users)= + +## Identify all users using SSO + +Collect all teams configured with an IdP: + +```sh +ssh +# within the ssh session start cqlsh +cqlsh +# within the cqlsh shell export all teams with idp +copy spar.idp (team) TO 'teams_with_idp.csv' with header=false; +``` + +Close the session and proceed locally: + +```sh +# download csv file +scp :teams_with_idp.csv . +# convert to a single line, comma separated list +tr '\n' ',' < teams_with_idp.csv; echo +``` + +And use this list to get all team members in these teams: + +```sh +ssh +# within the ssh session start cqlsh +cqlsh +# within the cqlsh shell select all members of previous identified teams +# should look like this: f2207d98-8ab3-11ec-b689-07fc1fd409c9, ... +select user from galley.team_member where team in (); +# alternatively, export the list of all users (for filterling locally in eg. excel) +copy galley.team_member (user, team, sso_id) TO 'users_with_idp.csv' with header=true; +``` + +Close the session and proceed locally to generate the list of all users from teams with IdP: + +```sh +# download csv file +scp :users_with_idp.csv . +# convert to a single line, comma separated list +tr '\n' ',' < users_with_idp.csv; echo +``` + +```{note} +Don't forget to dellete the created csv files after you have downloaded/processed them. +``` + +## Create a team using the SCIM API + +If you need to create a team manually, maybe because team creation was blocked in the "teams" interface, follow this procedure: + +First download or locate this bash script: `wire-server/hack/bin/create_test_team_scim.sh ` + +Then, run it the following way: + +```sh +./create_test_team_scim.sh -h -s +``` + +Where: + +- In `-h `, replace `` with the base URL for your brig host (for example: `https://brig-host.your-domain.com`, defaults to `http://localhost:8082`) +- In `-s `, replace `` with the base URL for your spar host (for example: `https://spar-host.your-domain.com`, defaults to `http://localhost:8088`) + +You might also need to edit the admin email and admin passwords at lines `48` and `49` of the script. + +To learn more about the different pods and how to identify them, see `this page`. + +You can list your pods with `kubectl get pods --namespace wire`. + +Alternatively, you can run the series of commands manually with `curl`, like this: + +```sh +curl -i -s --show-error \ + -XPOST "$BRIG_HOST/i/users" \ + -H'Content-type: application/json' \ + -d'{"email":"$ADMIN_EMAIL","password":"$ADMIN_PASSWORD","name":"$NAME_OF_TEAM","team":{"name":"$NAME_OF_TEAM","icon":"default"}}' +``` + +Where: + +- `$BRIG_HOST` is the base URL for your brig host +- `$ADMIN_EMAIL` is the email for the admin account for the new team +- `$ADMIN_PASSWORD` is the password for the admin account for the new team +- `$NAME_OF_TEAM` is the name of the team newly created + +Out of the result of this command, you will be able to extract an `Admin UUID`, and a `Team UUID`, which you will need later. + +Then run: + +```sh +curl -X POST \ + --header 'Content-Type: application/json' \ + --header 'Accept: application/json' \ + -d '{"email":"$ADMIN_EMAIL","password":"$ADMIN_PASSWORD"}' \ + $BRIG_HOST/login'?persist=false' | jq -r .access_token +``` + +Where the values to replace are the same as the command above. + +This command should output an access token, take note of it. + +Then run: + +```sh +curl -X POST \ + --header "Authorization: Bearer $ACCESS_TOKEN" \ + --header 'Content-Type: application/json;charset=utf-8' \ + --header 'Z-User: '"$ADMIN_UUID" \ + -d '{ "description": "test '"`date`"'", "password": "'"$ADMIN_PASSWORD"'" }' \ + $SPAR_HOST/scim/auth-tokens +``` + +Where the values to replace are the same as the first command, plus `$ACCESS_TOKEN` is access token you just took note of in the previous command. + +Out of the JSON output of this command, you should be able to extract: + +- A SCIM token (`token` value in the JSON). +- A SCIM token ID (`id` value in the `info` value in the JSON) + +Equiped with those tokens, we move on to the next script, `wire-server/hack/bin/create_team.sh ` + +This script can be run the following way: + +```sh +./create_team.sh -h -o -e -p -v -t -c +``` + +Where: + +- -h \: Base URI of brig. default: `http://localhost:8080` +- -o \: user display name of the owner of the team to be created. default: "owner name n/a" +- -e \: email address of the owner of the team to be created. default: "owner email n/a" +- -p \: owner password. default: "owner pass n/a" +- -v \: validation code received by email after running the previous script/commands. default: "email code n/a" +- -t \: default: "team name n/a" +- -c \: default: "USD" + +Alternatively, you can manually run the command: + +```sh +curl -i -s --show-error \ + -XPOST "$BRIG_HOST/register" \ + -H'Content-type: application/json' \ + -d'{"name":"$OWNER_NAME","email":"$OWNER_EMAIL","password":"$OWNER_PASSWORD","email_code":"$EMAIL_CODE","team":{"currency":"$TEAM_CURRENCY","icon":"default","name":"$TEAM_NAME"}}' +``` + +Where: + +- `$BRIG_HOST` is the base URL for your brig service +- `$OWNER_NAME` is the name of the of the team to be created +- `$OWNER_PASSWORD` is the password of the owner of the team to be created +- `$EMAIL_CODE` is the validation code received by email after running the previous script/command +- `$TEAM_CURRENCY` is the currency of the team +- `$TEAM_NAME` is the name of the team diff --git a/docs/src/how-to/administrate/users.rst b/docs/src/how-to/administrate/users.rst deleted file mode 100644 index d8f522d2f6..0000000000 --- a/docs/src/how-to/administrate/users.rst +++ /dev/null @@ -1,609 +0,0 @@ -.. _investigative-tasks: - -Investigative tasks (e.g. searching for users as server admin) ---------------------------------------------------------------- - -This page requires that you have root access to the machines where kubernetes runs on, or have kubernetes permissions allowing you to port-forward arbitrary pods and services. - -If you have the `backoffice` pod installed, see also the `backoffice README `__. - -If you don't have `backoffice`, see below for some options: - -Manually searching for users in cassandra -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Terminal one: - -.. code:: sh - - kubectl port-forward svc/brig 9999:8080 - -Terminal two: Search for your user by email: - -.. code:: sh - - EMAIL=user@example.com - curl -v -G localhost:9999/i/users --data-urlencode email=$EMAIL; echo - # or, for nicer formatting - curl -v -G localhost:9999/i/users --data-urlencode email=$EMAIL | json_pp - -You can also search by ``handle`` (unique username) or by phone: - -.. code:: sh - - HANDLE=user123 - curl -v -G localhost:9999/i/users --data-urlencode handles=$HANDLE; echo - - PHONE=+490000000000000 # phone numbers must have the +country prefix and no spaces - curl -v -G localhost:9999/i/users --data-urlencode phone=$PHONE; echo - - -Which should give you output like: - -.. code:: json - - [ - { - "managed_by" : "wire", - "assets" : [ - { - "key" : "3-2-a749af8d-a17b-4445-b360-46c93fc41bc6", - "size" : "preview", - "type" : "image" - }, - { - "size" : "complete", - "type" : "image", - "key" : "3-2-6cac6b57-9972-4aba-acbb-f078bc538b54" - } - ], - "picture" : [], - "accent_id" : 0, - "status" : "active", - "name" : "somename", - "email" : "user@example.com", - "id" : "9122e5de-b4fb-40fa-99ad-1b5d7d07bae5", - "locale" : "en", - "handle" : "user123" - } - ] - -The interesting part is the ``id`` (in the example case ``9122e5de-b4fb-40fa-99ad-1b5d7d07bae5``): - -.. _user-deletion: - -Deleting a user which is not a team user -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following will completely delete a user, its conversations, assets, etc. The only thing remaining will be an entry in cassandra indicating that this user existed in the past (only the UUID remains, all other attributes like name etc are purged) - -You can now delete that user by double-checking that the user you wish to delete is really the correct user: - -.. code:: sh - - # replace the id with the id of the user you want to delete - curl -v localhost:9999/i/users/9122e5de-b4fb-40fa-99ad-1b5d7d07bae5 -XDELETE - -Afterwards, the previous command (to search for a user in cassandra) should return an empty list (``[]``). - -When done, on terminal 1, ctrl+c to cancel the port-forwarding. - -Searching and deleting users with no team -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you require users to be part of a team, or for some other reason you need to delete all users who are not part of a team, you need to first find all such users, and then delete them. - -To find users that are not part of a team, first you need to connect via SSH to the machine where cassandra is running, and then run the following command: - -.. code:: sh - - cqlsh 9042 -e "select team, handle, id from brig.user" | grep -E "^\s+null" - -This will give you a list of handles and IDs with no team associated: - -.. code:: sh - - null | null | bc22119f-ce11-4402-aa70-307a58fb22ec - null | tom | 8ecee3d0-47a4-43ff-977b-40a4fc350fed - null | alice | 2a4c3468-c1e6-422f-bc4d-4aeff47941ac - null | null | 1b5ca44a-aeb4-4a68-861b-48612438c4cc - null | bob | 701b4eab-6df2-476d-a818-90dc93e8446e - -You can then :ref:`delete each user with these instructions `. - -Manual search on elasticsearch (via brig, recommended) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This should only be necessary in the case of some (suspected) data inconsistency between cassandra and elasticsearch. - -Terminal one: - -.. code:: sh - - kubectl port-forward svc/brig 9999:8080 - -Terminal two: Search for your user by name or handle or a prefix of that handle or name: - -.. code:: sh - - NAMEORPREFIX=test7 - UUID=$(cat /proc/sys/kernel/random/uuid) - curl -H "Z-User:$UUID" "http://localhost:9999/search/contacts?q=$NAMEORPREFIX"; echo - # or, for pretty output: - curl -H "Z-User:$UUID" "http://localhost:9999/search/contacts?q=$NAMEORPREFIX" | json_pp - -If no match is found, expect a query like this: - -.. code:: json - - {"took":91,"found":0,"documents":[],"returned":0} - -If matches are found, the result should look like this: - -.. code:: json - - { - "found" : 2, - "documents" : [ - { - "id" : "dbdbf370-48b3-4e1e-b377-76d7d4cbb4f2", - "name" : "Test", - "handle" : "test7", - "accent_id" : 7 - }, - { - "name" : "Test", - "accent_id" : 0, - "handle" : "test7476", - "id" : "a93240b0-ba89-441e-b8ee-ff4403808f93" - } - ], - "returned" : 2, - "took" : 4 - } - -How to manually search for a user on elasticsearh directly (not recommended) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -First, ssh to an elasticsearch instance. - -.. code:: sh - - ssh - -Then run the following: - -.. code:: sh - - PREFIX=... - curl -s "http://localhost:9200/directory/_search?q=$PREFIX" | json_pp - -The `id` (UUID) returned can be used when deleting (see below). - -How to manually delete a user from elasticsearch only -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. warning:: - - This is NOT RECOMMENDED. Be sure you know what you're doing. This only deletes the user from elasticsearch, but not from cassandra. Any change of e.g. the username or displayname of that user means this user will re-appear in the elasticsearch database. Instead, either fully delete a user: :ref:`user-deletion` or make use of the internal GET/PUT ``/i/searchable`` endpoint on brig to make this user prefix-unsearchable. - -If, despite the warning, you wish to continue? - -First, ssh to an elasticsearch instance: - -.. code:: sh - - ssh - -Next, check that the user exists: - -.. code:: sh - - UUID=... - curl -s "http://localhost:9200/directory/user/$UUID" | json_pp - -That should return a ``"found": true``, like this: - -.. code:: json - - { - "_type" : "user", - "_version" : 1575998428262000, - "_id" : "b3e9e445-fb02-47f3-bac0-63f5f680d258", - "found" : true, - "_index" : "directory", - "_source" : { - "normalized" : "Mr Test", - "handle" : "test12345", - "id" : "b3e9e445-fb02-47f3-bac0-63f5f680d258", - "name" : "Mr Test", - "accent_id" : 1 - } - } - - -Then delete it: - -.. code:: sh - - UUID=... - curl -s -XDELETE "http://localhost:9200/directory/user/$UUID" | json_pp - -Mass-invite users to a team -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you need to invite members to a specific given team, you can use the ``create_team_members.sh`` Bash script, located `here `__. - -This script does not create users or causes them to join a team by itself, instead, it sends invites to potential users via email, and when users accept the invitation, they create their account, set their password, and are added to the team as team members. - -Input is a `CSV file `__, in comma-separated format, in the form ``'Email,Suggested User Name'``. - -You also need to specify the inviting admin user, the team, the URI for the Brig (`API `__) service (Host), and finally the input (CSV) file containing the users to invite. - -The exact format for the parameters passed to the script is `as follows `__: - -* ``-a ``: `User ID `__ in `UUID format `__ of the inviting admin. For example ``9122e5de-b4fb-40fa-99ad-1b5d7d07bae5``. -* ``-t ``: ID of the inviting team, same format. -* ``-h ``: Base URI of brig's internal endpoint. -* ``-c ``: file containing info on the invitees in format 'Email,UserName'. - -For example, one such execution of the script could look like: - -.. code:: sh - - sh create_team_members.sh -a 9122e5de-b4fb-40fa-99ad-1b5d7d07bae5 -t 123e4567-e89b-12d3-a456-426614174000 -h http://localhost:9999 -c users_to_invite.csv - -Note: the 'http://localhost:9999' implies you are running the 'kubectl port-forward' given at the top of this document -. -Once the script is run, invitations will be sent to each user in the file every second until all invitations have been sent. - -If you have a lot of invitations to send and this is too slow, you can speed things up by commenting `this line `__. - - -How to obtain logs from an Android client to investigate issues -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Wire clients communicate with Wire servers (backend). - -Sometimes to investigate server issues, you (or the Wire team) will need client information, in the form of client logs. - -In order to obtain client logs on the Android Wire client, follow this procedure: - -* Open the Wire app (client) on your Android device -* Click on the round user icon in the top left of the screen, leading to your user Profile. -* Click on "Settings" at the bottom of the screen -* Click on "Advanced" in the menu -* Check/activate "Collect usage data" -* Now go back to using your client normally, so usage data is generated. If you have been asked to follow a specific testing regime, or log a specific problem, this is the time to do so. -* Once enough usage data is generated, go back to the "Advanced" screen (User profile > Settings > Advanced) -* Click on "Create debug report" -* A menu will open allowing you to share the debug report, you can now save it or send it via email/any other means to the Wire team. - - -How to obtain logs from an iOS client to investigate issues -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Wire clients communicate with Wire servers (backend). - -Sometimes to investigate server issues, you (or the Wire team) will need client information, in the form of client logs. - -In order to obtain client logs on the iOS Wire client, follow this procedure: - -* Open the Wire app (client) on your iOS device -* Click on the round user icon in the top left of the screen, leading to your user Profile. -* Click on "Settings" at the bottom of the screen -* Click on "Advanced" in the menu -* Check/activate "Collect usage data" -* Now go back to using your client normally, so usage data is generated. If you have been asked to follow a specific testing regime, or log a specific problem, this is the time to do so. -* Once enough usage data is generated, go back to the "Advanced" screen (User profile > Settings > Advanced) -* Click on "Send report to wire" -* A menu will open to share the debug report via email, allowing you to send it to the Wire team. - -How to retrieve metric values manually -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Metric values are sets of data points about services, such as status and other measures, that can be retrieved at specific endpoints, typically by a monitoring system (such as Prometheus) for monitoring, diagnosis and graphing. - -Sometimes, you will want to manually obtain this data that is normally automatically grabbed by Prometheus. - -Some of the pods allow you to grab metrics by accessing their ``/i/metrics`` endpoint, in particular: - -* ``brig``: User management API -* ``cannon``: WebSockets API -* ``cargohold``: Assets storage API -* ``galley``: Conversations and Teams API -* ``gundeck``: Push Notifications API -* ``spar``: Single-Sign-ON and SCIM - -For more details on the various services/pods, you can check out :ref:`this link `. - -Before you can grab metrics from a pod, you need to find its IP address. You do this by running the following command: - -.. code:: sh - - d kubectl get pods -owide - -(this presumes you are already in your normal Wire environment, which you obtain by running ``source ./bin/offline-env.sh``) - -Which will give you an output that looks something like this: - -.. code:: - - demo@Ubuntu-1804-bionic-64-minimal:~/Wire-Server$ d kubectl get pods -owide - NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES - account-pages-784f9b547c-cp444 1/1 Running 0 6d23h 10.233.113.5 kubenode3 - brig-746ddc55fd-6pltz 1/1 Running 0 6d23h 10.233.110.11 kubenode2 - brig-746ddc55fd-d59dw 1/1 Running 0 6d4h 10.233.110.23 kubenode2 - brig-746ddc55fd-zp7jl 1/1 Running 0 6d23h 10.233.113.10 kubenode3 - brig-index-migrate-data-45rm7 0/1 Completed 0 6d23h 10.233.110.9 kubenode2 - cannon-0 1/1 Running 0 3h1m 10.233.119.41 kubenode1 - cannon-1 1/1 Running 0 3h1m 10.233.113.47 kubenode3 - cannon-2 1/1 Running 0 3h1m 10.233.110.51 kubenode2 - cargohold-65bff97fc6-8b9ls 1/1 Running 0 6d4h 10.233.113.20 kubenode3 - cargohold-65bff97fc6-bkx6x 1/1 Running 0 6d23h 10.233.113.4 kubenode3 - cargohold-65bff97fc6-tz8fh 1/1 Running 0 6d23h 10.233.110.5 kubenode2 - cassandra-migrations-bjsdz 0/1 Completed 0 6d23h 10.233.110.3 kubenode2 - demo-smtp-784ddf6989-vmj7t 1/1 Running 0 6d23h 10.233.113.2 kubenode3 - elasticsearch-index-create-7r8g4 0/1 Completed 0 6d23h 10.233.110.4 kubenode2 - fake-aws-sns-6c7c4b7479-wfp82 2/2 Running 0 6d4h 10.233.110.27 kubenode2 - fake-aws-sqs-59fbfbcbd4-n4c5z 2/2 Running 0 6d23h 10.233.110.2 kubenode2 - galley-7c89c44f7b-nm2rr 1/1 Running 0 6d23h 10.233.110.8 kubenode2 - galley-7c89c44f7b-tdxz4 1/1 Running 0 6d23h 10.233.113.6 kubenode3 - galley-7c89c44f7b-tr8pm 1/1 Running 0 6d4h 10.233.110.29 kubenode2 - galley-migrate-data-g66rz 0/1 Completed 0 6d23h 10.233.110.13 kubenode2 - gundeck-7fd75c7c5f-jb8xq 1/1 Running 0 6d23h 10.233.110.6 kubenode2 - gundeck-7fd75c7c5f-lbth9 1/1 Running 0 6d23h 10.233.113.8 kubenode3 - gundeck-7fd75c7c5f-wvcw6 1/1 Running 0 6d4h 10.233.113.23 kubenode3 - nginz-5cdd8b588b-dbn86 2/2 Running 16 6d23h 10.233.113.11 kubenode3 - nginz-5cdd8b588b-gk6rw 2/2 Running 14 6d23h 10.233.110.12 kubenode2 - nginz-5cdd8b588b-jvznt 2/2 Running 11 6d4h 10.233.113.21 kubenode3 - reaper-6957694667-s5vz5 1/1 Running 0 6d4h 10.233.110.26 kubenode2 - redis-ephemeral-master-0 1/1 Running 0 6d23h 10.233.113.3 kubenode3 - spar-56d77f85f6-bw55q 1/1 Running 0 6d23h 10.233.113.9 kubenode3 - spar-56d77f85f6-mczzd 1/1 Running 0 6d4h 10.233.110.28 kubenode2 - spar-56d77f85f6-vvvfq 1/1 Running 0 6d23h 10.233.110.7 kubenode2 - spar-migrate-data-ts4sx 0/1 Completed 0 6d23h 10.233.110.14 kubenode2 - team-settings-fbbb899c-qxx7m 1/1 Running 0 6d4h 10.233.110.24 kubenode2 - webapp-d97869795-grnft 1/1 Running 0 6d4h 10.233.110.25 kubenode2 - -Here presuming we need to get metrics from ``gundeck``, we can see the IP of one of the gundeck pods is ``10.233.110.6``. - -We can therefore connect to node ``kubenode2`` on which this pod runs with ``ssh kubenode2.your-domain.com``, and run the following: - -.. code:: sh - - curl 10.233.110.6:8080/i/metrics - -Alternatively, if you don't want to, or can't for some reason, connect to kubenode2, you can use port redirect instead: - -.. code:: sh - - # Allow Gundeck to be reached via the port 7777 - kubectl --kubeconfig kubeconfig.dec -n wire port-forward service/gundeck 7777:8080 - # Reach Gundeck directly at port 7777 using curl, output resulting data to stdout/terminal - curl -v http://127.0.0.1:7777/i/metrics - -Output will look something like this (truncated): - -.. code:: sh - - # HELP gc_seconds_wall Wall clock time spent on last GC - # TYPE gc_seconds_wall gauge - gc_seconds_wall 5481304.0 - # HELP gc_seconds_cpu CPU time spent on last GC - # TYPE gc_seconds_cpu gauge - gc_seconds_cpu 5479828.0 - # HELP gc_bytes_used_current Number of bytes in active use as of the last GC - # TYPE gc_bytes_used_current gauge - gc_bytes_used_current 1535232.0 - # HELP gc_bytes_used_max Maximum amount of memory living on the heap after the last major GC - # TYPE gc_bytes_used_max gauge - gc_bytes_used_max 2685312.0 - # HELP gc_bytes_allocated_total Bytes allocated since the start of the server - # TYPE gc_bytes_allocated_total gauge - gc_bytes_allocated_total 4.949156056e9 - -This example is for Gundeck, but you can also get metrics for other services. All k8s services are listed at :ref:`this link `. - -This is an example adapted for Cannon: - -.. code:: sh - - kubectl --kubeconfig kubeconfig.dec -n wire port-forward service/cannon 7777:8080 - curl -v http://127.0.0.1:7777/i/metrics - -In the output of this command, ``net_websocket_clients`` is roughly the number of connected clients. - -.. _reset session cookies: - -Reset session cookies -~~~~~~~~~~~~~~~~~~~~~ - -Remove session cookies on your system to force users to login again within the next 15 minutes (or whenever they come back online): - -.. warning:: - This will cause interruptions to ongoing calls and should be timed properly. - -Reset cookies of all users -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: sh - - ssh - # from the ssh session - cqlsh - # from the cqlsh shell - truncate brig.user_cookies; - -Reset cookies for a defined list of users -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: sh - - ssh - # within the ssh session - cqlsh - # within the cqlsh shell: delete all users by userId - delete from brig.user_cookies where user in (c0d64244-8ab4-11ec-8fda-37788be3a4e2, ...); - -(Keep reading if you want to find out which users on your system are using SSO.) - -.. _identify sso users: - -Identify all users using SSO -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Collect all teams configured with an IdP: - -.. code:: sh - - ssh - # within the ssh session start cqlsh - cqlsh - # within the cqlsh shell export all teams with idp - copy spar.idp (team) TO 'teams_with_idp.csv' with header=false; - -Close the session and proceed locally: - -.. code:: sh - - # download csv file - scp :teams_with_idp.csv . - # convert to a single line, comma separated list - tr '\n' ',' < teams_with_idp.csv; echo - -And use this list to get all team members in these teams: - -.. code:: sh - - ssh - # within the ssh session start cqlsh - cqlsh - # within the cqlsh shell select all members of previous identified teams - # should look like this: f2207d98-8ab3-11ec-b689-07fc1fd409c9, ... - select user from galley.team_member where team in (); - # alternatively, export the list of all users (for filterling locally in eg. excel) - copy galley.team_member (user, team, sso_id) TO 'users_with_idp.csv' with header=true; - -Close the session and proceed locally to generate the list of all users from teams with IdP: - -.. code:: sh - - # download csv file - scp :users_with_idp.csv . - # convert to a single line, comma separated list - tr '\n' ',' < users_with_idp.csv; echo - - -.. note:: - Don't forget to dellete the created csv files after you have downloaded/processed them. - -Create a team using the SCIM API -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you need to create a team manually, maybe because team creation was blocked in the "teams" interface, follow this procedure: - -First download or locate this bash script: `wire-server/hack/bin/create_test_team_scim.sh ` - -Then, run it the following way: - -.. code:: sh - - ./create_test_team_scim.sh -h -s - -Where: - -* In `-h `, replace `` with the base URL for your brig host (for example: `https://brig-host.your-domain.com`, defaults to `http://localhost:8082`) -* In `-s `, replace `` with the base URL for your spar host (for example: `https://spar-host.your-domain.com`, defaults to `http://localhost:8088`) - -You might also need to edit the admin email and admin passwords at lines `48` and `49` of the script. - -To learn more about the different pods and how to identify them, see `this page`. - -You can list your pods with `kubectl get pods --namespace wire`. - -Alternatively, you can run the series of commands manually with `curl`, like this: - -.. code:: sh - - curl -i -s --show-error \ - -XPOST "$BRIG_HOST/i/users" \ - -H'Content-type: application/json' \ - -d'{"email":"$ADMIN_EMAIL","password":"$ADMIN_PASSWORD","name":"$NAME_OF_TEAM","team":{"name":"$NAME_OF_TEAM","icon":"default"}}' - -Where: - -* `$BRIG_HOST` is the base URL for your brig host -* `$ADMIN_EMAIL` is the email for the admin account for the new team -* `$ADMIN_PASSWORD` is the password for the admin account for the new team -* `$NAME_OF_TEAM` is the name of the team newly created - -Out of the result of this command, you will be able to extract an `Admin UUID`, and a `Team UUID`, which you will need later. - -Then run: - -.. code:: sh - - curl -X POST \ - --header 'Content-Type: application/json' \ - --header 'Accept: application/json' \ - -d '{"email":"$ADMIN_EMAIL","password":"$ADMIN_PASSWORD"}' \ - $BRIG_HOST/login'?persist=false' | jq -r .access_token - -Where the values to replace are the same as the command above. - -This command should output an access token, take note of it. - -Then run: - -.. code:: sh - - curl -X POST \ - --header "Authorization: Bearer $ACCESS_TOKEN" \ - --header 'Content-Type: application/json;charset=utf-8' \ - --header 'Z-User: '"$ADMIN_UUID" \ - -d '{ "description": "test '"`date`"'", "password": "'"$ADMIN_PASSWORD"'" }' \ - $SPAR_HOST/scim/auth-tokens - -Where the values to replace are the same as the first command, plus `$ACCESS_TOKEN` is access token you just took note of in the previous command. - -Out of the JSON output of this command, you should be able to extract: - -* A SCIM token (`token` value in the JSON). -* A SCIM token ID (`id` value in the `info` value in the JSON) - -Equiped with those tokens, we move on to the next script, `wire-server/hack/bin/create_team.sh ` - -This script can be run the following way: - -.. code:: sh - - ./create_team.sh -h -o -e -p -v -t -c - -Where: - -* -h : Base URI of brig. default: `http://localhost:8080` -* -o : user display name of the owner of the team to be created. default: "owner name n/a" -* -e : email address of the owner of the team to be created. default: "owner email n/a" -* -p : owner password. default: "owner pass n/a" -* -v : validation code received by email after running the previous script/commands. default: "email code n/a" -* -t : default: "team name n/a" -* -c : default: "USD" - -Alternatively, you can manually run the command: - -.. code:: sh - - curl -i -s --show-error \ - -XPOST "$BRIG_HOST/register" \ - -H'Content-type: application/json' \ - -d'{"name":"$OWNER_NAME","email":"$OWNER_EMAIL","password":"$OWNER_PASSWORD","email_code":"$EMAIL_CODE","team":{"currency":"$TEAM_CURRENCY","icon":"default","name":"$TEAM_NAME"}}' - -Where: - -* `$BRIG_HOST` is the base URL for your brig service -* `$OWNER_NAME` is the name of the of the team to be created -* `$OWNER_PASSWORD` is the password of the owner of the team to be created -* `$EMAIL_CODE` is the validation code received by email after running the previous script/command -* `$TEAM_CURRENCY` is the currency of the team -* `$TEAM_NAME` is the name of the team diff --git a/docs/src/how-to/associate/custom-backend-for-desktop-client.md b/docs/src/how-to/associate/custom-backend-for-desktop-client.md new file mode 100644 index 0000000000..ad66ca975e --- /dev/null +++ b/docs/src/how-to/associate/custom-backend-for-desktop-client.md @@ -0,0 +1,79 @@ +# How to connect the desktop application to a custom backend + +## Introduction + +This page explains how to connect the Wire desktop client to a custom Backend, which can be done either via a start-up parameter or via an initialization file. + +## Prerequisites + +Install Wire either from the App Store, or download it from our website at () + +Have a running Wire backend in your infrastructure/cloud. + +Note down the full URL of the webapp served by that backend (e.g. ) + +## Using start-up parameters + +### Windows + +- Create a shortcut to the Wire application +- Edit the shortcut ( Right click > Properties ) +- Add the following command line parameters to the shortcut: `--env {URL}`, where `{URL}` is the URL of your webapp as noted down above + +### MacOS + +To create the application + +- Open Automator +- Click New application +- Add the "Run shell script" phase +- Type in the script panel the following command: `open -b com.wearezeta.zclient.mac --args --env {URL}`, where `{URL}` is the URL of your webapp as noted down above +- Save the application from Automator (e.g. on your desktop or in Application) +- To run the application: Just open the application you created in the first step + +### Linux + +- Open a Terminal +- Start the application with the command line arguments: `--env {URL}`, where `{URL}` is the URL of your webapp as noted down above + +## Using an initialization file + +By providing an initialization file the instance connection parameters and/or proxy settings for the Wire desktop application can be pre-configured. This requires Wire version >= 3.27. + +Create a file named `init.json` and set `customWebAppURL` and optionally `proxyServerURL` e.g. as follows: + +```json +{ + "customWebAppURL": "https://app.custom-wire.com", + "env": "CUSTOM", + "proxyServerURL": "http://127.0.0.1:3128", +} +``` + +The `env` setting must be set to `CUSTOM` for this to work. + +```{note} +Consult your site admin to learn what goes into these settings. The value of `customWebAppURL` can be found [here](https://github.com/wireapp/wire-server/blob/e6aa50913cdcfde1200114787baabf7896394a2f/charts/webapp/templates/deployment.yaml#L40-L41) or [resp. here](https://github.com/wireapp/wire-server/blob/e6aa50913cdcfde1200114787baabf7896394a2f/charts/webapp/values.yaml#L26). The value of `proxyServerURL` is your browser proxy. It depends on the configuration of the network your client is running in. +``` + +### Windows + +Move the `init.json` file to `%APPDATA%\Wire\config\init.json` if it does not already exist. Otherwise update it accordingly. + +### MacOS + +Move the `init.json` file to + +``` +~/Library/Containers/com.wearezeta.zclient.mac/Data/Library/Application\ Support/Wire/config/init.json +``` + +if it does not already exist. Otherwise, update it accordingly. + +### Linux + +On Linux the `init.json` file should be located in the following directory: + +``` +$HOME/.config/Wire/config/init.json +``` diff --git a/docs/src/how-to/associate/custom-backend-for-desktop-client.rst b/docs/src/how-to/associate/custom-backend-for-desktop-client.rst deleted file mode 100644 index 6f4f768345..0000000000 --- a/docs/src/how-to/associate/custom-backend-for-desktop-client.rst +++ /dev/null @@ -1,90 +0,0 @@ -How to connect the desktop application to a custom backend -========================================================== - -Introduction ------------- - -This page explains how to connect the Wire desktop client to a custom Backend, which can be done either via a start-up parameter or via an initialization file. - -Prerequisites --------------- - -Install Wire either from the App Store, or download it from our website at (https://wire.com/en/download/) - -Have a running Wire backend in your infrastructure/cloud. - -Note down the full URL of the webapp served by that backend (e.g. https://app.custom-wire.com ) - -Using start-up parameters -------------------------- - -Windows -~~~~~~~ - -- Create a shortcut to the Wire application -- Edit the shortcut ( Right click > Properties ) -- Add the following command line parameters to the shortcut: `--env {URL}`, where `{URL}` is the URL of your webapp as noted down above - -MacOS -~~~~~ - -To create the application - -- Open Automator -- Click New application -- Add the "Run shell script" phase -- Type in the script panel the following command: `open -b com.wearezeta.zclient.mac --args --env {URL}`, where `{URL}` is the URL of your webapp as noted down above -- Save the application from Automator (e.g. on your desktop or in Application) -- To run the application: Just open the application you created in the first step - -Linux -~~~~~ - -- Open a Terminal -- Start the application with the command line arguments: `--env {URL}`, where `{URL}` is the URL of your webapp as noted down above - -Using an initialization file ----------------------------- - -By providing an initialization file the instance connection parameters and/or proxy settings for the Wire desktop application can be pre-configured. This requires Wire version >= 3.27. - -Create a file named ``init.json`` and set ``customWebAppURL`` and optionally ``proxyServerURL`` e.g. as follows: - -.. code-block:: json - - { - "customWebAppURL": "https://app.custom-wire.com", - "env": "CUSTOM", - "proxyServerURL": "http://127.0.0.1:3128", - } - -The ``env`` setting must be set to ``CUSTOM`` for this to work. - -.. note:: - - Consult your site admin to learn what goes into these settings. The value of ``customWebAppURL`` can be found `here `_ or `resp. here `_. The value of ``proxyServerURL`` is your browser proxy. It depends on the configuration of the network your client is running in. - -Windows -~~~~~~~ - -Move the ``init.json`` file to ``%APPDATA%\Wire\config\init.json`` if it does not already exist. Otherwise update it accordingly. - -MacOS -~~~~~ - -Move the ``init.json`` file to - -:: - - ~/Library/Containers/com.wearezeta.zclient.mac/Data/Library/Application\ Support/Wire/config/init.json - -if it does not already exist. Otherwise, update it accordingly. - -Linux -~~~~~ - -On Linux the ``init.json`` file should be located in the following directory: - -:: - - $HOME/.config/Wire/config/init.json diff --git a/docs/src/how-to/associate/custom-certificates.rst b/docs/src/how-to/associate/custom-certificates.md similarity index 80% rename from docs/src/how-to/associate/custom-certificates.rst rename to docs/src/how-to/associate/custom-certificates.md index 3a5c15b852..2c52391570 100644 --- a/docs/src/how-to/associate/custom-certificates.rst +++ b/docs/src/how-to/associate/custom-certificates.md @@ -1,10 +1,9 @@ -Custom root certificates -------------------------- +# Custom root certificates In case you have installed wire-server using certificates signed using a custom root CA (certificate authority) which is not trusted by default by browsers and systems, then you need to ensure Wire-clients (on Android, Desktop, iOS, and the Web) trust this root certificate. The following details the procedure for Desktop and Web on Linux/Windows: -https://thomas-leister.de/en/how-to-import-ca-root-certificate/ + For Android and iOS, if you know how to trust custom certificates, please let use know so we can update this documentation. diff --git a/docs/src/how-to/associate/deeplink.md b/docs/src/how-to/associate/deeplink.md new file mode 100644 index 0000000000..d3bfc77e27 --- /dev/null +++ b/docs/src/how-to/associate/deeplink.md @@ -0,0 +1,174 @@ +# Using a Deep Link to connect an App to a Custom Backend + +## Introduction + +Once you have your own wire-server set up and configured, you may want to use a client other than the web interface (webapp). There are a few ways to accomplish this: + +- **Using a Deep Link** (which this page is all about) +- Registering your backend instance with the hosted SaaS backend for re-direction. For which you might need to talk to the folks @ Wire (the company). + +Assumptions: + +- You have wire-server installed and working +- You have a familiarity with JSON files +- You can place a JSON file on an HTTPS supporting web server somewhere your users can reach. + +Supported client apps: + +- iOS +- Android + +```{note} +Wire deeplinks can be used to redirect a mobile (Android, iOS) Wire app to a specific backend URL. Deeplinks have no further ability implemented at this stage. +``` + +## Connecting to a custom backend utilizing a Deep Link + +A deep link is a special link a user can click on after installing wire, but before setting it up. This link instructs their wire client to connect to your wire-server, rather than wire.com. + +### With Added Proxy + +In addition to connect to a custom backend a user can specify a socks proxy to add another layer to the network and make the api calls go through the proxy. + +## From a user's perspective: + +1. First, a user installs the app from the store +2. The user clicks on a deep link, which is formatted similar to: `wire://access/?config=https://eu-north2.mycustomdomain.de/configs/backend1.json` (notice the protocol prefix: `wire://`) +3. The app will ask the user to confirm that they want to connect to a custom backend. If the user cancels, the app exits. +4. Assuming the user did not cancel, the app will download the file `eu-north2.mycustomdomain.de/configs/backend1.json` via HTTPS. If it can't download the file or the file doesn't match the expected structure, the wire client will display an error message (*'sInvalid link'*). +5. The app will memorize the various hosts (REST, websocket, team settings, website, support) specified in the JSON and use those when talking to your backend. +6. In the welcome page of the app, a "pill" (header) is shown at the top, to remind the user that they are now on a custom backend. A button "Show more" shows the URL of where the configuration was fetched from. + +### With Added Proxy + +In addition to the previous points + +7. The app will remember the (proxy host, proxy port, if the proxy need authentication) +8. In the login page the user will see new section to add the proxy credentials if the proxy need authentication + +## From the administrator's (your) perspective: + +You need to host two static files, then let your users know how to connect. There are three options listed (in order of recommendation) for hosting the static files. + +Note on the meaning of the URLs used below: + +`backendURL` + +: Use the backend API entrypoint URL, by convention `https://nginz-https.` + +`backendWSURL` + +: Use the backend Websocket API entrypoint URL, by convention `https://nginz-ssl.` + +`teamsURL` + +: Use the URL to the team settings part of the webapp, by convention `https://teams.` + +`accountsURL` + +: Use the URL to the account pages part of the webapp, by convention `https://account.` + +`blackListURL` + +: is used to disable old versions of Wire clients (mobile apps). It's a prefix URL to which e.g. `/ios` or `/android` is appended. Example URL for the wire.com production servers: `https://clientblacklist.wire.com/prod` and example json files: [android](https://clientblacklist.wire.com/prod/android) and [iPhone](https://clientblacklist.wire.com/prod/ios) . + +`websiteURL` + +: Is used as a basis for a few links within the app pointing to FAQs and troubleshooting pages for end users. You can leave this as `https://wire.com` or host your own alternative pages and point this to your own website with the equivalent pages references from within the app. + +`title` + +: Arbitrary string that may show up in a few places in the app. Should be used as an identifier of the backend servers in question. + +### With Added Proxy + +`apiProxy:host (optional)` + +: Is used to specify a proxy to be added to the network engine, so the API calls will go through it to add more security layer. + +`apiProxy:port (optional)` + +: Is used to specify the port number for the proxy when we create the proxy object in the network layer. + +`apiProxy:needsAuthentication (optional)` + +: Is used to specify if the proxy need an authentication, so we can show the section during the login to enter the proxy credentials. + +#### Host a deeplink together with your Wire installation + +As of release `2.117.0` from `2021-10-29` (see `release notes`), you can configure your deeplink endpoints to match your installation and DNS records (see explanations above) + +```yaml +# override values for wire-server +# (e.g. under ./helm_vars/wire-server/values.yaml) +nginz: + nginx_conf: + deeplink: + endpoints: + backendURL: "https://nginz-https.example.com" + backendWSURL: "https://nginz-ssl.example.com" + teamsURL: "https://teams.example.com" + accountsURL: "https://account.example.com" + blackListURL: "https://clientblacklist.wire.com/prod" + websiteURL: "https://wire.com" + apiProxy: # (optional) + host: "socks5.proxy.com" + port: 1080 + needsAuthentication: true + title: "My Custom Wire Backend" +``` + +(As with any configuration changes, you need to apply them following your usual way of updating configuration (e.g. 'helm upgrade...')) + +Now both static files should become accessible at the backend domain under `/deeplink.json` and `deeplink.html`: + +- `https://nginz-https./deeplink.json` +- `https://nginz-https./deeplink.html` + +#### Host a deeplink using minio (deprecated) + +*If possible, prefer the option in the subsection above or below. This subsection is kept for backwards compatibility.* + +**If you're using minio** installed using the ansible code from [wire-server-deploy](https://github.com/wireapp/wire-server-deploy/blob/master/ansible/), then the [minio ansible playbook](https://github.com/wireapp/wire-server-deploy/blob/master/ansible/minio.yml#L75-L88) (make sure to override these variables) creates a json and a html file in the right format, and makes it accessible at `https://assets./public/deeplink.json` and at `https://assets./public/deeplink.html` + +#### Host a deeplink file using your own web server + +Otherwise you need to create a `.json` file, and host it somewhere users can get to. This `.json` file needs to specify the URLs of your backend. For the production wire server that we host, the JSON would look like: + +```json +{ + "endpoints" : { + "backendURL" : "https://prod-nginz-https.wire.com", + "backendWSURL" : "https://prod-nginz-ssl.wire.com", + "blackListURL" : "https://clientblacklist.wire.com/prod", + "teamsURL" : "https://teams.wire.com", + "accountsURL" : "https://accounts.wire.com", + "websiteURL" : "https://wire.com" + }, + "apiProxy" : { + "host" : "socks5.proxy.com", + "port" : 1080, + "needsAuthentication" : true + }, + "title" : "Production" +} +``` + +**IMPORTANT NOTE:** Clients require **ALL** keys to be present in the JSON file; if some of these keys are irrelevant to your installation (e.g., you don't have a websiteURL) you can leave these values as indicated in the above example. + +There is no requirement for these hosts to be consistent, e.g. the REST endpoint could be `wireapp.pineapple.com` and the team setting `teams.banana.com`. If you have been following this documentation closely, these hosts will likely be consistent in naming, regardless. + +You now need to get a link referring to that `.json` file to your users, prepended with `wire://access/?config=`. For example, you can save the above `.json` file as `https://example.com/wire.json`, and save the following HTML content as `https://example.com/wire.html`: + +```html + + + + link + + +``` + +## Next steps + +Now, you can e.g. email or otherwise provide a link to the deeplink HTML page to your users on their mobile devices, and they can follow the above procedure, by clicking on `link`. diff --git a/docs/src/how-to/associate/deeplink.rst b/docs/src/how-to/associate/deeplink.rst deleted file mode 100644 index 1ef53550b1..0000000000 --- a/docs/src/how-to/associate/deeplink.rst +++ /dev/null @@ -1,174 +0,0 @@ -Using a Deep Link to connect an App to a Custom Backend -======================================================= - -Introduction ------------- - -Once you have your own wire-server set up and configured, you may want to use a client other than the web interface (webapp). There are a few ways to accomplish this: - -- **Using a Deep Link** (which this page is all about) -- Registering your backend instance with the hosted SaaS backend for re-direction. For which you might need to talk to the folks @ Wire (the company). - -Assumptions: - -- You have wire-server installed and working -- You have a familiarity with JSON files -- You can place a JSON file on an HTTPS supporting web server somewhere your users can reach. - -Supported client apps: - -- iOS -- Android - -.. note:: - Wire deeplinks can be used to redirect a mobile (Android, iOS) Wire app to a specific backend URL. Deeplinks have no further ability implemented at this stage. - -Connecting to a custom backend utilizing a Deep Link ----------------------------------------------------- - -A deep link is a special link a user can click on after installing wire, but before setting it up. This link instructs their wire client to connect to your wire-server, rather than wire.com. - -With Added Proxy -~~~~~~~~~~~~~~~~ -In addition to connect to a custom backend a user can specify a socks proxy to add another layer to the network and make the api calls go through the proxy. - -From a user's perspective: --------------------------- - -1. First, a user installs the app from the store -2. The user clicks on a deep link, which is formatted similar to: ``wire://access/?config=https://eu-north2.mycustomdomain.de/configs/backend1.json`` (notice the protocol prefix: ``wire://``) -3. The app will ask the user to confirm that they want to connect to a custom backend. If the user cancels, the app exits. -4. Assuming the user did not cancel, the app will download the file ``eu-north2.mycustomdomain.de/configs/backend1.json`` via HTTPS. If it can't download the file or the file doesn't match the expected structure, the wire client will display an error message (*'sInvalid link'*). -5. The app will memorize the various hosts (REST, websocket, team settings, website, support) specified in the JSON and use those when talking to your backend. -6. In the welcome page of the app, a "pill" (header) is shown at the top, to remind the user that they are now on a custom backend. A button "Show more" shows the URL of where the configuration was fetched from. - -With Added Proxy -~~~~~~~~~~~~~~~~ -In addition to the previous points - -7. The app will remember the (proxy host, proxy port, if the proxy need authentication) -8. In the login page the user will see new section to add the proxy credentials if the proxy need authentication - - -From the administrator's (your) perspective: --------------------------------------------- - -You need to host two static files, then let your users know how to connect. There are three options listed (in order of recommendation) for hosting the static files. - -Note on the meaning of the URLs used below: - -``backendURL`` - Use the backend API entrypoint URL, by convention ``https://nginz-https.`` - -``backendWSURL`` - Use the backend Websocket API entrypoint URL, by convention ``https://nginz-ssl.`` - -``teamsURL`` - Use the URL to the team settings part of the webapp, by convention ``https://teams.`` - -``accountsURL`` - Use the URL to the account pages part of the webapp, by convention ``https://account.`` - -``blackListURL`` - is used to disable old versions of Wire clients (mobile apps). It's a prefix URL to which e.g. `/ios` or `/android` is appended. Example URL for the wire.com production servers: ``https://clientblacklist.wire.com/prod`` and example json files: `android `_ and `iPhone `_ . - -``websiteURL`` - Is used as a basis for a few links within the app pointing to FAQs and troubleshooting pages for end users. You can leave this as ``https://wire.com`` or host your own alternative pages and point this to your own website with the equivalent pages references from within the app. - -``title`` - Arbitrary string that may show up in a few places in the app. Should be used as an identifier of the backend servers in question. - -With Added Proxy -~~~~~~~~~~~~~~~~ - -``apiProxy:host (optional)`` - Is used to specify a proxy to be added to the network engine, so the API calls will go through it to add more security layer. - -``apiProxy:port (optional)`` - Is used to specify the port number for the proxy when we create the proxy object in the network layer. - -``apiProxy:needsAuthentication (optional)`` - Is used to specify if the proxy need an authentication, so we can show the section during the login to enter the proxy credentials. - -Host a deeplink together with your Wire installation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -As of release ``2.117.0`` from ``2021-10-29`` (see `release notes`), you can configure your deeplink endpoints to match your installation and DNS records (see explanations above) - -.. code:: yaml - - # override values for wire-server - # (e.g. under ./helm_vars/wire-server/values.yaml) - nginz: - nginx_conf: - deeplink: - endpoints: - backendURL: "https://nginz-https.example.com" - backendWSURL: "https://nginz-ssl.example.com" - teamsURL: "https://teams.example.com" - accountsURL: "https://account.example.com" - blackListURL: "https://clientblacklist.wire.com/prod" - websiteURL: "https://wire.com" - apiProxy: # (optional) - host: "socks5.proxy.com" - port: 1080 - needsAuthentication: true - title: "My Custom Wire Backend" - -(As with any configuration changes, you need to apply them following your usual way of updating configuration (e.g. 'helm upgrade...')) - -Now both static files should become accessible at the backend domain under ``/deeplink.json`` and ``deeplink.html``: - -* ``https://nginz-https./deeplink.json`` -* ``https://nginz-https./deeplink.html`` - -Host a deeplink using minio (deprecated) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -*If possible, prefer the option in the subsection above or below. This subsection is kept for backwards compatibility.* - -**If you're using minio** installed using the ansible code from `wire-server-deploy `__, then the `minio ansible playbook `__ (make sure to override these variables) creates a json and a html file in the right format, and makes it accessible at ``https://assets./public/deeplink.json`` and at ``https://assets./public/deeplink.html`` - -Host a deeplink file using your own web server -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Otherwise you need to create a ``.json`` file, and host it somewhere users can get to. This ``.json`` file needs to specify the URLs of your backend. For the production wire server that we host, the JSON would look like: - -.. code:: json - - { - "endpoints" : { - "backendURL" : "https://prod-nginz-https.wire.com", - "backendWSURL" : "https://prod-nginz-ssl.wire.com", - "blackListURL" : "https://clientblacklist.wire.com/prod", - "teamsURL" : "https://teams.wire.com", - "accountsURL" : "https://accounts.wire.com", - "websiteURL" : "https://wire.com" - }, - "apiProxy" : { - "host" : "socks5.proxy.com", - "port" : 1080, - "needsAuthentication" : true - }, - "title" : "Production" - } - -**IMPORTANT NOTE:** Clients require **ALL** keys to be present in the JSON file; if some of these keys are irrelevant to your installation (e.g., you don't have a websiteURL) you can leave these values as indicated in the above example. - -There is no requirement for these hosts to be consistent, e.g. the REST endpoint could be `wireapp.pineapple.com` and the team setting `teams.banana.com`. If you have been following this documentation closely, these hosts will likely be consistent in naming, regardless. - -You now need to get a link referring to that ``.json`` file to your users, prepended with ``wire://access/?config=``. For example, you can save the above ``.json`` file as ``https://example.com/wire.json``, and save the following HTML content as ``https://example.com/wire.html``: - -.. code:: html - - - - - link - - - -Next steps ----------- - -Now, you can e.g. email or otherwise provide a link to the deeplink HTML page to your users on their mobile devices, and they can follow the above procedure, by clicking on ``link``. diff --git a/docs/src/how-to/associate/index.md b/docs/src/how-to/associate/index.md new file mode 100644 index 0000000000..3dba99c8f2 --- /dev/null +++ b/docs/src/how-to/associate/index.md @@ -0,0 +1,10 @@ +# Connecting Wire Clients + +```{toctree} +:glob: true +:maxdepth: 2 + + How to associate a wire client to a custom backend using a deep link + How to use custom root certificates with wire clients + How to use a custom backend with the desktop client +``` diff --git a/docs/src/how-to/associate/index.rst b/docs/src/how-to/associate/index.rst deleted file mode 100644 index 95c7d790f5..0000000000 --- a/docs/src/how-to/associate/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Connecting Wire Clients -======================= - -.. toctree:: - :maxdepth: 2 - :glob: - - How to associate a wire client to a custom backend using a deep link - How to use custom root certificates with wire clients - How to use a custom backend with the desktop client diff --git a/docs/src/how-to/index.md b/docs/src/how-to/index.md new file mode 100644 index 0000000000..46bf098a9a --- /dev/null +++ b/docs/src/how-to/index.md @@ -0,0 +1,20 @@ +# Administrator's Guide + +Documentation on the installation, deployment and administration of Wire +server components. + +```{warning} +If you already installed Wire by using `poetry`, please refer to the +[old version](https://docs.wire.com/versions/install-with-poetry/how-to/index.html) of +the installation guide. +``` + +```{toctree} +:glob: true +:maxdepth: 2 + + How to install wire-server + How to verify your wire-server installation + How to administrate servers after successful installation + How to connect the public wire clients to your wire-server installation +``` diff --git a/docs/src/how-to/index.rst b/docs/src/how-to/index.rst deleted file mode 100644 index c5756db287..0000000000 --- a/docs/src/how-to/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -Administrator's Guide -===================== - -Documentation on the installation, deployment and administration of Wire -server components. - -.. warning:: - - If you already installed Wire by using ``poetry``, please refer to the - `old version `__ of - the installation guide. - - -.. toctree:: - :maxdepth: 2 - :glob: - - How to install wire-server - How to verify your wire-server installation - How to administrate servers after successful installation - How to connect the public wire clients to your wire-server installation diff --git a/docs/src/how-to/install/ansible-VMs.md b/docs/src/how-to/install/ansible-VMs.md new file mode 100644 index 0000000000..2627eea5d9 --- /dev/null +++ b/docs/src/how-to/install/ansible-VMs.md @@ -0,0 +1,275 @@ +(ansible-vms)= + +# Installing kubernetes and databases on VMs with ansible + +## Introduction + +In a production environment, some parts of the wire-server +infrastructure (such as e.g. cassandra databases) are best configured +outside kubernetes. Additionally, kubernetes can be rapidly set up with +kubespray, via ansible. This section covers installing VMs with ansible. + +## Assumptions + +- A bare-metal setup (no cloud provider) +- All machines run ubuntu 18.04 +- All machines have static IP addresses +- Time on all machines is being kept in sync +- You have the following virtual machines: + +```{eval-rst} +.. include:: includes/vm-table.rst +``` + +(It's up to you how you create these machines - kvm on a bare metal +machine, VM on a cloud provider, real physical machines, etc.) + +## Preparing to run ansible + +(adding-ips-to-hostsini)= + +% TODO: section header unifications/change + +### Adding IPs to hosts.ini + +Go to your checked-out wire-server-deploy/ansible folder: + +``` +cd wire-server-deploy/ansible +``` + +Copy the example hosts file: + +``` +cp hosts.example.ini hosts.ini +``` + +- Edit the hosts.ini, setting the permanent IPs of the hosts you are + setting up wire on. +- On each of the lines declaring a database service node ( + lines in the `[all]` section beginning with cassandra, elasticsearch, + or minio) replace the `ansible_host` values (`X.X.X.X`) with the + IPs of the nodes that you can connect to via SSH. these are the + 'internal' addresses of the machines, not what a client will be + connecting to. +- On each of the lines declaring a kubernetes node (lines in the `[all]` + section starting with 'kubenode') replace the `ip` values + (`Y.Y.Y.Y`) with the IPs which you wish kubernetes to provide + services to clients on, and replace the `ansible_host` values + (`X.X.X.X`) with the IPs of the nodes that you can connect to via + SSH. If the IP you want to provide services on is the same IP that + you use to connect, remove the `ip=Y.Y.Y.Y` completely. +- On each of the lines declaring an `etcd` node (lines in the `[all]` + section starting with etcd), use the same values as you used on the + coresponding kubenode lines in the prior step. +- If you are deploying Restund for voice/video services then on each of the + lines declaring a `restund` node (lines in the `[all]` section + beginning with restund), replace the `ansible_host` values (`X.X.X.X`) + with the IPs of the nodes that you can connect to via SSH. +- Edit the minio variables in `[minio:vars]` (`prefix`, `domain` and `deeplink_title`) + by replacing `example.com` with your own domain. + +There are more settings in this file that we will set in later steps. + +% TODO: remove this warning, and remove the hostname run from the cassandra playbook, or find another way to deal with it. + +```{warning} +Some of these playbooks mess with the hostnames of their targets. You +MUST pick different hosts for playbooks that rename the host. If you +e.g. attempt to run Cassandra and k8s on the same 3 machines, the +hostnames will be overwritten by the second installation playbook, +breaking the first. + +At the least, we know that the cassandra, kubernetes and restund playbooks are +guilty of hostname manipulation. +``` + +### Authentication + +```{eval-rst} +.. include:: includes/ansible-authentication-blob.rst +``` + +## Running ansible to install software on your machines + +You can install kubernetes, cassandra, restund, etc in any order. + +```{note} +In case you only have a single network interface with public IPs but wish to protect inter-database communication, you may use the `tinc.yml` playbook to create a private network interface. In this case, ensure tinc is setup BEFORE running any other playbook. See {ref}`tinc` +``` + +### Installing kubernetes + +Kubernetes is installed via ansible. + +To install kubernetes: + +From `wire-server-deploy/ansible`: + +``` +ansible-playbook -i hosts.ini kubernetes.yml -vv +``` + +When the playbook finishes correctly (which can take up to 20 minutes), you should have a folder `artifacts` containing a file `admin.conf`. Copy this file: + +``` +mkdir -p ~/.kube +cp artifacts/admin.conf ~/.kube/config +``` + +Make sure you can reach the server: + +``` +kubectl version +``` + +should give output similar to this: + +``` +Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:23:52Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"} +Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:15:20Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"} +``` + +### Cassandra + +- If you would like to change the name of the cluster, in your + 'hosts.ini' file, in the `[cassandra:vars]` section, uncomment + the line that changes 'cassandra_clustername', and change default + to be the name you want the cluster to have. +- If you want cassandra nodes to talk to each other on a specific + network interface, rather than the one you use to connect via SSH, + In your 'hosts.ini' file, in the `[all:vars]` section, + uncomment, and set 'cassandra_network_interface' to the name of + the ethernet interface you want cassandra nodes to talk to each + other on. For example: + +```ini +[cassandra:vars] +# cassandra_clustername: default + +[all:vars] +## set to True if using AWS +is_aws_environment = False +## Set the network interface name for cassandra to bind to if you have more than one network interface +cassandra_network_interface = eth0 +``` + +(see +[defaults/main.yml](https://github.com/wireapp/ansible-cassandra/blob/master/defaults/main.yml) +for a full list of variables to change if necessary) + +- Use ansible to deploy Cassandra: + +``` +ansible-playbook -i hosts.ini cassandra.yml -vv +``` + +### ElasticSearch + +- In your 'hosts.ini' file, in the `[all:vars]` section, uncomment + and set 'elasticsearch_network_interface' to the name of the + interface you want elasticsearch nodes to talk to each other on. +- If you are performing an offline install, or for some other reason + are using an APT mirror other than the default to retrieve + elasticsearch-oss packages from, you need to specify that mirror + by setting 'es_apt_key' and 'es_apt_url' in the `[all:vars]` + section of your hosts.ini file. + +```ini +[all:vars] +# default first interface on ubuntu on kvm: +elasticsearch_network_interface=ens3 + +## Set these in order to use an APT mirror other than the default. +# es_apt_key = "https:///linux/ubuntu/gpg" +# es_apt_url = "deb [trusted=yes] https:///apt bionic stable" +``` + +- Use ansible and deploy ElasticSearch: + +``` +ansible-playbook -i hosts.ini elasticsearch.yml -vv +``` + +### Minio + +Minio is used for asset storage, in the case that you are not +running on AWS infrastructure, or feel uncomfortable storing assets +in S3 in encrypted form. If you are using S3 instead of Minio, skip +this step. + +- In your 'hosts.ini' file, in the `[all:vars]` section, make sure + you set the 'minio_network_interface' to the name of the interface + you want minio nodes to talk to each other on. The default from the + playbook is not going to be correct for your machine. For example: +- In your 'hosts.ini' file, in the `[minio:vars]` section, ensure you + set minio_access_key and minio_secret key. +- If you intend to use a `deep link` to configure your clients to + talk to the backend, you need to specify your domain (and optionally + your prefix), so that links to your deep link json file are generated + correctly. By configuring these values, you fill in the blanks of + `https://{{ prefix }}assets.{{ domain }}`. + +```ini +[minio:vars] +minio_access_key = "REPLACE_THIS_WITH_THE_DESIRED_SECRET_KEY" +minio_secret_key = "REPLACE_THIS_WITH_THE_DESIRED_SECRET_KEY" +# if you want to use deep links for client configuration: +#minio_deeplink_prefix = "" +#minio_deeplink_domain = "example.com" + +[all:vars] +# Default first interface on ubuntu on kvm: +minio_network_interface=ens3 +``` + +- Use ansible, and deploy Minio: + +``` +ansible-playbook -i hosts.ini minio.yml -vv +``` + +### Restund + +For instructions on how to install Restund, see {ref}`this page `. + +### IMPORTANT checks + +> After running the above playbooks, it is important to ensure that everything is setup correctly. Please have a look at the post install checks in the section {ref}`checks` + +``` +ansible-playbook -i hosts.ini cassandra-verify-ntp.yml -vv +``` + +### Installing helm charts - prerequisites + +The `helm_external.yml` playbook is used to write or update the IPs of the +databases servers in the `values/-external/values.yaml` files, and +thus make them available for helm and the `-external` charts (e.g. +`cassandra-external`, `elasticsearch-external`, etc). + +Due to limitations in the playbook, make sure that you have defined the +network interfaces for each of the database services in your hosts.ini, +even if they are running on the same interface that you connect to via SSH. +In your hosts.ini under `[all:vars]`: + +```ini +[all:vars] +minio_network_interface = ... +cassandra_network_interface = ... +elasticsearch_network_interface = ... +# if you're using redis external... +redis_network_interface = ... +``` + +Now run the helm_external.yml playbook, to populate network values for helm: + +``` +ansible-playbook -i hosts.ini -vv --diff helm_external.yml +``` + +You can now can install the helm charts. + +#### Next steps for high-available production installation + +Your next step will be {ref}`helm-prod` diff --git a/docs/src/how-to/install/ansible-VMs.rst b/docs/src/how-to/install/ansible-VMs.rst deleted file mode 100644 index ced5a5eda6..0000000000 --- a/docs/src/how-to/install/ansible-VMs.rst +++ /dev/null @@ -1,277 +0,0 @@ -.. _ansible-vms: - -Installing kubernetes and databases on VMs with ansible -======================================================= - -Introduction ------------- - -In a production environment, some parts of the wire-server -infrastructure (such as e.g. cassandra databases) are best configured -outside kubernetes. Additionally, kubernetes can be rapidly set up with -kubespray, via ansible. This section covers installing VMs with ansible. - -Assumptions ------------ - -- A bare-metal setup (no cloud provider) -- All machines run ubuntu 18.04 -- All machines have static IP addresses -- Time on all machines is being kept in sync -- You have the following virtual machines: - -.. include:: includes/vm-table.rst - -(It's up to you how you create these machines - kvm on a bare metal -machine, VM on a cloud provider, real physical machines, etc.) - -Preparing to run ansible ------------------------- - -.. _adding-ips-to-hostsini: - -.. TODO: section header unifications/change - -Adding IPs to hosts.ini -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Go to your checked-out wire-server-deploy/ansible folder:: - - cd wire-server-deploy/ansible - -Copy the example hosts file:: - - cp hosts.example.ini hosts.ini - -- Edit the hosts.ini, setting the permanent IPs of the hosts you are - setting up wire on. -- On each of the lines declaring a database service node ( - lines in the ``[all]`` section beginning with cassandra, elasticsearch, - or minio) replace the ``ansible_host`` values (``X.X.X.X``) with the - IPs of the nodes that you can connect to via SSH. these are the - 'internal' addresses of the machines, not what a client will be - connecting to. -- On each of the lines declaring a kubernetes node (lines in the ``[all]`` - section starting with 'kubenode') replace the ``ip`` values - (``Y.Y.Y.Y``) with the IPs which you wish kubernetes to provide - services to clients on, and replace the ``ansible_host`` values - (``X.X.X.X``) with the IPs of the nodes that you can connect to via - SSH. If the IP you want to provide services on is the same IP that - you use to connect, remove the ``ip=Y.Y.Y.Y`` completely. -- On each of the lines declaring an ``etcd`` node (lines in the ``[all]`` - section starting with etcd), use the same values as you used on the - coresponding kubenode lines in the prior step. -- If you are deploying Restund for voice/video services then on each of the - lines declaring a ``restund`` node (lines in the ``[all]`` section - beginning with restund), replace the ``ansible_host`` values (``X.X.X.X``) - with the IPs of the nodes that you can connect to via SSH. -- Edit the minio variables in ``[minio:vars]`` (``prefix``, ``domain`` and ``deeplink_title``) - by replacing ``example.com`` with your own domain. - -There are more settings in this file that we will set in later steps. - -.. TODO: remove this warning, and remove the hostname run from the cassandra playbook, or find another way to deal with it. - -.. warning:: - - Some of these playbooks mess with the hostnames of their targets. You - MUST pick different hosts for playbooks that rename the host. If you - e.g. attempt to run Cassandra and k8s on the same 3 machines, the - hostnames will be overwritten by the second installation playbook, - breaking the first. - - At the least, we know that the cassandra, kubernetes and restund playbooks are - guilty of hostname manipulation. - -Authentication -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. include:: includes/ansible-authentication-blob.rst - -Running ansible to install software on your machines ------------------------------------------------------ - -You can install kubernetes, cassandra, restund, etc in any order. - -.. note:: - - In case you only have a single network interface with public IPs but wish to protect inter-database communication, you may use the ``tinc.yml`` playbook to create a private network interface. In this case, ensure tinc is setup BEFORE running any other playbook. See :ref:`tinc` - -Installing kubernetes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Kubernetes is installed via ansible. - -To install kubernetes: - -From ``wire-server-deploy/ansible``:: - - ansible-playbook -i hosts.ini kubernetes.yml -vv - -When the playbook finishes correctly (which can take up to 20 minutes), you should have a folder ``artifacts`` containing a file ``admin.conf``. Copy this file:: - - mkdir -p ~/.kube - cp artifacts/admin.conf ~/.kube/config - -Make sure you can reach the server:: - - kubectl version - -should give output similar to this:: - - Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:23:52Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"} - Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:15:20Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"} - -Cassandra -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- If you would like to change the name of the cluster, in your - 'hosts.ini' file, in the ``[cassandra:vars]`` section, uncomment - the line that changes 'cassandra_clustername', and change default - to be the name you want the cluster to have. -- If you want cassandra nodes to talk to each other on a specific - network interface, rather than the one you use to connect via SSH, - In your 'hosts.ini' file, in the ``[all:vars]`` section, - uncomment, and set 'cassandra_network_interface' to the name of - the ethernet interface you want cassandra nodes to talk to each - other on. For example: - -.. code:: ini - - [cassandra:vars] - # cassandra_clustername: default - - [all:vars] - ## set to True if using AWS - is_aws_environment = False - ## Set the network interface name for cassandra to bind to if you have more than one network interface - cassandra_network_interface = eth0 - -(see -`defaults/main.yml `__ -for a full list of variables to change if necessary) - -- Use ansible to deploy Cassandra: - -:: - - ansible-playbook -i hosts.ini cassandra.yml -vv - -ElasticSearch -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- In your 'hosts.ini' file, in the ``[all:vars]`` section, uncomment - and set 'elasticsearch_network_interface' to the name of the - interface you want elasticsearch nodes to talk to each other on. -- If you are performing an offline install, or for some other reason - are using an APT mirror other than the default to retrieve - elasticsearch-oss packages from, you need to specify that mirror - by setting 'es_apt_key' and 'es_apt_url' in the ``[all:vars]`` - section of your hosts.ini file. - -.. code:: ini - - [all:vars] - # default first interface on ubuntu on kvm: - elasticsearch_network_interface=ens3 - - ## Set these in order to use an APT mirror other than the default. - # es_apt_key = "https:///linux/ubuntu/gpg" - # es_apt_url = "deb [trusted=yes] https:///apt bionic stable" - -- Use ansible and deploy ElasticSearch: - -:: - - ansible-playbook -i hosts.ini elasticsearch.yml -vv - -Minio -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Minio is used for asset storage, in the case that you are not -running on AWS infrastructure, or feel uncomfortable storing assets -in S3 in encrypted form. If you are using S3 instead of Minio, skip -this step. - - -- In your 'hosts.ini' file, in the ``[all:vars]`` section, make sure - you set the 'minio_network_interface' to the name of the interface - you want minio nodes to talk to each other on. The default from the - playbook is not going to be correct for your machine. For example: -- In your 'hosts.ini' file, in the ``[minio:vars]`` section, ensure you - set minio_access_key and minio_secret key. -- If you intend to use a ``deep link`` to configure your clients to - talk to the backend, you need to specify your domain (and optionally - your prefix), so that links to your deep link json file are generated - correctly. By configuring these values, you fill in the blanks of - ``https://{{ prefix }}assets.{{ domain }}``. - -.. code:: ini - - [minio:vars] - minio_access_key = "REPLACE_THIS_WITH_THE_DESIRED_SECRET_KEY" - minio_secret_key = "REPLACE_THIS_WITH_THE_DESIRED_SECRET_KEY" - # if you want to use deep links for client configuration: - #minio_deeplink_prefix = "" - #minio_deeplink_domain = "example.com" - - [all:vars] - # Default first interface on ubuntu on kvm: - minio_network_interface=ens3 - -- Use ansible, and deploy Minio: - -:: - - ansible-playbook -i hosts.ini minio.yml -vv - -Restund -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -For instructions on how to install Restund, see :ref:`this page `. - - -IMPORTANT checks -^^^^^^^^^^^^^^^^ - - After running the above playbooks, it is important to ensure that everything is setup correctly. Please have a look at the post install checks in the section :ref:`checks` - -:: - - ansible-playbook -i hosts.ini cassandra-verify-ntp.yml -vv - -Installing helm charts - prerequisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``helm_external.yml`` playbook is used to write or update the IPs of the -databases servers in the ``values/-external/values.yaml`` files, and -thus make them available for helm and the ``-external`` charts (e.g. -``cassandra-external``, ``elasticsearch-external``, etc). - -Due to limitations in the playbook, make sure that you have defined the -network interfaces for each of the database services in your hosts.ini, -even if they are running on the same interface that you connect to via SSH. -In your hosts.ini under ``[all:vars]``: - -.. code:: ini - - [all:vars] - minio_network_interface = ... - cassandra_network_interface = ... - elasticsearch_network_interface = ... - # if you're using redis external... - redis_network_interface = ... - - -Now run the helm_external.yml playbook, to populate network values for helm: - -:: - - ansible-playbook -i hosts.ini -vv --diff helm_external.yml - -You can now can install the helm charts. - -Next steps for high-available production installation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Your next step will be :ref:`helm-prod` diff --git a/docs/src/how-to/install/ansible-authentication.md b/docs/src/how-to/install/ansible-authentication.md new file mode 100644 index 0000000000..9943d7514b --- /dev/null +++ b/docs/src/how-to/install/ansible-authentication.md @@ -0,0 +1,63 @@ +(ansible-authentication)= + +# Manage ansible authentication settings + +Ansible works best if + +- you use ssh keys, not passwords +- the user you use to ssh is either `root` or can become `root` (can run `sudo su -`) without entering a password + +However, other options are possible, see below: + +## How to use password authentication when you ssh to a machine with ansible + +If, instead of using ssh keys to ssh to a remote machine, you want to use passwords: + +``` +sudo apt install sshpass +``` + +- in hosts.ini, uncomment the 'ansible_user = ...' line, and change '...' to the user you want to login as. +- in hosts.ini, uncomment the 'ansible_ssh_pass = ...' line, and change '...' to the password for the user you are logging in as. +- in hosts.ini, uncomment the 'ansible_become_pass = ...' line, and change the ... to the password you'd enter to sudo. + +## Configuring SSH keys + +(from ) If you +want a bit higher security, you can copy SSH keys between the machine +you are administrating with, and the machines you are managing with +ansible. + +- Create an SSH key. + +``` +ssh-keygen -t rsa +``` + +- Install your SSH key on each of the machines you are managing with + ansible, so that you can SSH into them without a password: + +``` +ssh-copy-id -i ~/.ssh/id_rsa.pub $USERNAME@$IP +``` + +Replace `$USERNAME` with the username of the account you set up when +you installed the machine. + +## Sudo without password + +Ansible can be configured to use a password for switching from the +unpriviledged \$USERNAME to the root user. This involves having the +password lying about, so has security problems. If you want ansible to +not be prompted for any administrative command (a different security +problem!): + +- As root on each of the nodes, add the following line at the end of + the /etc/sudoers file: + +``` + ALL=(ALL) NOPASSWD:ALL +``` + +Replace `` with the username of the account +you set up when you installed the machine. diff --git a/docs/src/how-to/install/ansible-authentication.rst b/docs/src/how-to/install/ansible-authentication.rst deleted file mode 100644 index 8e549fb64c..0000000000 --- a/docs/src/how-to/install/ansible-authentication.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. _ansible-authentication: - -Manage ansible authentication settings -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Ansible works best if - -* you use ssh keys, not passwords -* the user you use to ssh is either ``root`` or can become ``root`` (can run ``sudo su -``) without entering a password - -However, other options are possible, see below: - - -How to use password authentication when you ssh to a machine with ansible -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -If, instead of using ssh keys to ssh to a remote machine, you want to use passwords:: - - sudo apt install sshpass - -* in hosts.ini, uncomment the 'ansible_user = ...' line, and change '...' to the user you want to login as. -* in hosts.ini, uncomment the 'ansible_ssh_pass = ...' line, and change '...' to the password for the user you are logging in as. -* in hosts.ini, uncomment the 'ansible_become_pass = ...' line, and change the ... to the password you'd enter to sudo. - -Configuring SSH keys -'''''''''''''''''''' - -(from https://linoxide.com/how-tos/ssh-login-with-public-key/) If you -want a bit higher security, you can copy SSH keys between the machine -you are administrating with, and the machines you are managing with -ansible. - -- Create an SSH key. - -:: - - ssh-keygen -t rsa - -- Install your SSH key on each of the machines you are managing with - ansible, so that you can SSH into them without a password: - -:: - - ssh-copy-id -i ~/.ssh/id_rsa.pub $USERNAME@$IP - -Replace ``$USERNAME`` with the username of the account you set up when -you installed the machine. - -Sudo without password -''''''''''''''''''''' - -Ansible can be configured to use a password for switching from the -unpriviledged $USERNAME to the root user. This involves having the -password lying about, so has security problems. If you want ansible to -not be prompted for any administrative command (a different security -problem!): - -- As root on each of the nodes, add the following line at the end of - the /etc/sudoers file: - -:: - - ALL=(ALL) NOPASSWD:ALL - -Replace ```` with the username of the account -you set up when you installed the machine. diff --git a/docs/src/how-to/install/ansible-tinc.md b/docs/src/how-to/install/ansible-tinc.md new file mode 100644 index 0000000000..294c1faa99 --- /dev/null +++ b/docs/src/how-to/install/ansible-tinc.md @@ -0,0 +1,54 @@ +(tinc)= + +# tinc + +Installing [tinc mesh vpn](http://tinc-vpn.org/) is *optional and +experimental*. It allows having a private network interface `vpn0` on +the target VMs. + +```{warning} +We currently only use tinc for test clusters and have not made sure if the default settings it comes with provide adequate security to protect your data. If using tinc and the following tinc.yml playbook, make your own checks first! +``` + +```{note} +Ensure to run the tinc.yml playbook first if you use tinc, before +other playbooks. +``` + +From `wire-server-deploy/ansible`, where you created a `hosts.ini` file. + +- Add a `vpn_ip=Z.Z.Z.Z` item to each entry in the hosts file with a + (fresh) IP range if you wish to use tinc. +- Add a group `vpn`: + +```ini +# this is a minimal example +[all] +server1 ansible_host=X.X.X.X vpn_ip=10.10.1.XXX +server2 ansible_host=X.X.X.X vpn_ip=10.10.1.YYY + +[cassandra] +server1 +server2 + +[vpn:children] +cassandra +# add other server groups here as necessary +``` + +Also ensure subsequent playbooks make use of the newly-created interface by setting: + +```ini +[all:vars] +minio_network_interface = vpn0 +cassandra_network_interface = vpn0 +elasticsearch_network_interface = vpn0 +redis_network_interface = vpn0 +``` + +Configure the physical network interface inside tinc.yml if it is not +`eth0`. Then: + +``` +ansible-playbook -i hosts.ini tinc.yml -vv +``` diff --git a/docs/src/how-to/install/ansible-tinc.rst b/docs/src/how-to/install/ansible-tinc.rst deleted file mode 100644 index ca5698b7ab..0000000000 --- a/docs/src/how-to/install/ansible-tinc.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. _tinc: - -tinc ----- - -Installing `tinc mesh vpn `__ is *optional and -experimental*. It allows having a private network interface ``vpn0`` on -the target VMs. - -.. warning:: - We currently only use tinc for test clusters and have not made sure if the default settings it comes with provide adequate security to protect your data. If using tinc and the following tinc.yml playbook, make your own checks first! - -.. note:: - - Ensure to run the tinc.yml playbook first if you use tinc, before - other playbooks. - -From ``wire-server-deploy/ansible``, where you created a `hosts.ini` file. - -- Add a ``vpn_ip=Z.Z.Z.Z`` item to each entry in the hosts file with a - (fresh) IP range if you wish to use tinc. -- Add a group ``vpn``: - -.. code:: ini - - # this is a minimal example - [all] - server1 ansible_host=X.X.X.X vpn_ip=10.10.1.XXX - server2 ansible_host=X.X.X.X vpn_ip=10.10.1.YYY - - [cassandra] - server1 - server2 - - [vpn:children] - cassandra - # add other server groups here as necessary - -Also ensure subsequent playbooks make use of the newly-created interface by setting: - -.. code:: ini - - [all:vars] - minio_network_interface = vpn0 - cassandra_network_interface = vpn0 - elasticsearch_network_interface = vpn0 - redis_network_interface = vpn0 - -Configure the physical network interface inside tinc.yml if it is not -``eth0``. Then: - -:: - - ansible-playbook -i hosts.ini tinc.yml -vv diff --git a/docs/src/how-to/install/aws-prod.md b/docs/src/how-to/install/aws-prod.md new file mode 100644 index 0000000000..0359d98d71 --- /dev/null +++ b/docs/src/how-to/install/aws-prod.md @@ -0,0 +1,36 @@ +(aws-prod)= + +# Configuring AWS and wire-server (production) components + +## Introduction + +The following procedures are for configuring wire-server on top of AWS. They are not required to use wire-server in AWS, but they may be a good idea, depending on the AWS features you are comfortable using. + +## Using real AWS services for SNS + +AWS SNS is required to send notification events to clients via [FCM](https://firebase.google.com/docs/cloud-messaging/)/[APNS](https://developer.apple.com/notifications/) . These notification channels are useable only for clients that are connected from the public internet. Using these vendor provided communication channels allows client devices (phones) running a wire client to save a considerable amount of battery life, compared to the websockets approach. + +For details on how to set up SNS in cooperation with us (We - Wire - will proxy push notifications through Amazon for you), see {ref}`push-sns`. + +## Using real AWS services for SES / SQS + +AWS SES and SQS are used for delivering emails to clients, and for receiving notifications of bounced emails. SQS is also used internally, in order to facilitate batch user deletion. + +FIXME: detail this step. + +## Using real AWS services for S3 + +S3-style services are used by cargohold to store encrypted files that users are sharing amongst each other, profile pics, etc. + +Defining S3 services: +Create an S3 bucket in the region you are hosting your wire servers in. For example terraform code, see: + +The S3 bucket you create should have it's contents downloadable from the internet, as clients get the content directly from S3, rather than having to talk through the wire backend. + +Using S3 services: + +There are three values in the `cargohold.config.aws` section of your 'values.yaml' that you need to provide while deploying wire-server: + +- s3Bucket: the name of the S3 bucket you have created. +- s3Endpoint: the S3 service endpoint cargohold should talk to, to place files in the S3 bucket. On AWS, this takes the form of: `https://.s3-.amazonaws.com`. +- s3DownloadEndpoint: The URL base that clients should use to get contents from the S3 bucket. On AWS, this takes the form of: `https://s3..amazonaws.com`. diff --git a/docs/src/how-to/install/aws-prod.rst b/docs/src/how-to/install/aws-prod.rst deleted file mode 100644 index 16cdf65c5f..0000000000 --- a/docs/src/how-to/install/aws-prod.rst +++ /dev/null @@ -1,39 +0,0 @@ -.. _aws-prod: - -Configuring AWS and wire-server (production) components -======================================================= - -Introduction ------------- - -The following procedures are for configuring wire-server on top of AWS. They are not required to use wire-server in AWS, but they may be a good idea, depending on the AWS features you are comfortable using. - -Using real AWS services for SNS --------------------------------------------------------- -AWS SNS is required to send notification events to clients via `FCM `__/`APNS `__ . These notification channels are useable only for clients that are connected from the public internet. Using these vendor provided communication channels allows client devices (phones) running a wire client to save a considerable amount of battery life, compared to the websockets approach. - -For details on how to set up SNS in cooperation with us (We - Wire - will proxy push notifications through Amazon for you), see :ref:`push-sns`. - -Using real AWS services for SES / SQS ---------------------------------------------- -AWS SES and SQS are used for delivering emails to clients, and for receiving notifications of bounced emails. SQS is also used internally, in order to facilitate batch user deletion. - -FIXME: detail this step. - -Using real AWS services for S3 ------------------------------- -S3-style services are used by cargohold to store encrypted files that users are sharing amongst each other, profile pics, etc. - -Defining S3 services: -Create an S3 bucket in the region you are hosting your wire servers in. For example terraform code, see: https://github.com/wireapp/wire-server-deploy/tree/develop/terraform/modules/aws-cargohold-asset-storage - -The S3 bucket you create should have it's contents downloadable from the internet, as clients get the content directly from S3, rather than having to talk through the wire backend. - -Using S3 services: - -There are three values in the ``cargohold.config.aws`` section of your 'values.yaml' that you need to provide while deploying wire-server: - -* s3Bucket: the name of the S3 bucket you have created. -* s3Endpoint: the S3 service endpoint cargohold should talk to, to place files in the S3 bucket. On AWS, this takes the form of: ``https://.s3-.amazonaws.com``. -* s3DownloadEndpoint: The URL base that clients should use to get contents from the S3 bucket. On AWS, this takes the form of: ``https://s3..amazonaws.com``. - diff --git a/docs/src/how-to/install/configuration-options.md b/docs/src/how-to/install/configuration-options.md new file mode 100644 index 0000000000..647ac5f0ee --- /dev/null +++ b/docs/src/how-to/install/configuration-options.md @@ -0,0 +1,1048 @@ +(configuration-options)= + +# Part 3 - configuration options in a production setup + +This contains instructions to configure specific aspects of your production setup depending on your needs. + +Depending on your use-case and requirements, you may need to +configure none, or only a subset of the following sections. + +## Redirect some traffic through a http(s) proxy + +In case you wish to use http(s) proxies, you can add a configuration like this to the wire-server services in question: + +Assuming your proxy can be reached from within Kubernetes at `http://proxy:8080`, add the following for each affected service (e.g. `gundeck`) to your Helm overrides in `values/wire-server/values.yaml` : + +```yaml +gundeck: + # ... + config: + # ... + proxy: + httpProxy: "http://proxy:8080" + httpsProxy: "http://proxy:8080" + noProxyList: + - "localhost" + - "127.0.0.1" + - "10.0.0.0/8" + - "elasticsearch-external" + - "cassandra-external" + - "redis-ephemeral" + - "fake-aws-sqs" + - "fake-aws-dynamodb" + - "fake-aws-sns" + - "brig" + - "cargohold" + - "galley" + - "gundeck" + - "proxy" + - "spar" + - "federator" + - "cannon" + - "cannon-0.cannon.default" + - "cannon-1.cannon.default" + - "cannon-2.cannon.default" +``` + +Depending on your setup, you may need to repeat this for the other services like `brig` as well. + +(push-sns)= + +## Enable push notifications using the public appstore / playstore mobile Wire clients + +1. You need to get in touch with us. Please talk to sales or customer support - see +2. If a contract agreement has been reached, we can set up a separate AWS account for you containing the necessary AWS SQS/SNS setup to route push notifications through to the mobile apps. We will then forward some configuration / access credentials that looks like: + +```yaml +gundeck: + config: + aws: + account: "" + arnEnv: "" + queueName: "-gundeck-events" + region: "" + snsEndpoint: "https://sns..amazonaws.com" + sqsEndpoint: "https://sqs..amazonaws.com" + secrets: + awsKeyId: "" + awsSecretKey: "" +``` + +To make use of those, first test the credentials are correct, e.g. using the `aws` command-line tool (for more information on how to configure credentials, please refer to the [official docs](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-precedence)): + +``` +AWS_REGION= +AWS_ACCESS_KEY_ID=<...> +AWS_SECRET_ACCESS_KEY=<...> +ENV= #e.g staging + +aws sqs get-queue-url --queue-name "$ENV-gundeck-events" +``` + +You should get a result like this: + +``` +{ + "QueueUrl": "https://.queue.amazonaws.com//-gundeck-events" +} +``` + +Then add them to your gundeck configuration overrides. + +Keys below `gundeck.config` belong into `values/wire-server/values.yaml`: + +```yaml +gundeck: + # ... + config: + aws: + queueName: # e.g. staging-gundeck-events + account: # , e.g. 123456789 + region: # e.g. eu-central-1 + snsEndpoint: # e.g. https://sns.eu-central-1.amazonaws.com + sqsEndpoint: # e.g. https://sqs.eu-central-1.amazonaws.com + arnEnv: # e.g. staging - this must match the environment name (first part of queueName) +``` + +Keys below `gundeck.secrets` belong into `values/wire-server/secrets.yaml`: + +```yaml +gundeck: + # ... + secrets: + awsKeyId: CHANGE-ME + awsSecretKey: CHANGE-ME +``` + +After making this change and applying it to gundeck (ensure gundeck pods have restarted to make use of the updated configuration - that should happen automatically), make sure to reset the push token on any mobile devices that you may have in use. + +Next, if you want, you can stop using the `fake-aws-sns` pods in case you ran them before: + +```yaml +# inside override values/fake-aws/values.yaml +fake-aws-sns: + enabled: false +``` + +## Controlling the speed of websocket draining during cannon pod replacement + +The 'cannon' component is responsible for persistent websocket connections. +Normally the default options would slowly and gracefully drain active websocket +connections over a maximum of `(amount of cannon replicas * 30 seconds)` during +the deployment of a new wire-server version. This will lead to a very brief +interruption for Wire clients when their client has to re-connect on the +websocket. + +You're not expected to need to change these settings. + +The following options are only relevant during the restart of cannon itself. +During a restart of nginz or ingress-controller, all websockets will get +severed. If this is to be avoided, see section {ref}`separate-websocket-traffic` + +`drainOpts`: Drain websockets in a controlled fashion when cannon receives a +SIGTERM or SIGINT (this happens when a pod is terminated e.g. during rollout +of a new version). Instead of waiting for connections to close on their own, +the websockets are now severed at a controlled pace. This allows for quicker +rollouts of new versions. + +There is no way to entirely disable this behaviour, two extreme examples below + +- the quickest way to kill cannon is to set `gracePeriodSeconds: 1` and + `minBatchSize: 100000` which would sever all connections immediately; but it's + not recommended as you could DDoS yourself by forcing all active clients to + reconnect at the same time. With this, cannon pod replacement takes only 1 + second per pod. +- the slowest way to roll out a new version of cannon without severing websocket + connections for a long time is to set `minBatchSize: 1`, + `millisecondsBetweenBatches: 86400000` and `gracePeriodSeconds: 86400` + which would lead to one single websocket connection being closed immediately, + and all others only after 1 day. With this, cannon pod replacement takes a + full day per pod. + +```yaml +# overrides for wire-server/values.yaml +cannon: + drainOpts: + # The following defaults drain a minimum of 400 connections/second + # for a total of 10000 over 25 seconds + # (if cannon holds more connections, draining will happen at a faster pace) + gracePeriodSeconds: 25 + millisecondsBetweenBatches: 50 + minBatchSize: 20 +``` + +## Control nginz upstreams (routes) into the Kubernetes cluster + +Open unterminated upstreams (routes) into the Kubernetes cluster are a potential +security issue. To prevent this, there are fine-grained settings in the nginz +configuration defining which upstreams should exist. + +### Default upstreams + +Upstreams for services that exist in (almost) every Wire installation are +enabled by default. These are: + +- `brig` +- `cannon` +- `cargohold` +- `galley` +- `gundeck` +- `spar` + +For special setups (as e.g. described in [separate-websocket-traffic]) the +upstreams of these services can be ignored (disabled) with the setting +`nginz.nginx_conf.ignored_upstreams`. + +The most common example is to disable the upstream of `cannon`: + +```yaml +nginz: + nginx_conf: + ignored_upstreams: ["cannon"] +``` + +### Optional upstreams + +There are some services that are usually not deployed on most Wire installations +or are specific to the Wire cloud: + +- `ibis` +- `galeb` +- `calling-test` +- `proxy` + +The upstreams for those are disabled by default and can be enabled by the +setting `nginz.nginx_conf.enabled_extra_upstreams`. + +The most common example is to enable the (extra) upstream of `proxy`: + +```yaml +nginz: + nginx_conf: + enabled_extra_upstreams: ["proxy"] +``` + +### Combining default and extra upstream configurations + +Default and extra upstream configurations are independent of each other. I.e. +`nginz.nginx_conf.ignored_upstreams` and +`nginz.nginx_conf.enabled_extra_upstreams` can be combined in the same +configuration: + +```yaml +nginz: + nginx_conf: + ignored_upstreams: ["cannon"] + enabled_extra_upstreams: ["proxy"] +``` + +(separate-websocket-traffic)= + +## Separate incoming websocket network traffic from the rest of the https traffic + +By default, incoming network traffic for websockets comes through these network +hops: + +Internet -> LoadBalancer -> kube-proxy -> nginx-ingress-controller -> nginz -> cannon + +In order to have graceful draining of websockets when something gets restarted, as it is not easily +possible to implement the graceful draining on nginx-ingress-controller or nginz by itself, there is +a configuration option to get the following network hops: + +Internet -> separate LoadBalancer for cannon only -> kube-proxy -> \[nginz->cannon (2 containers in the same pod)\] + +```yaml +# example on AWS when using cert-manager for TLS certificates and external-dns for DNS records +# (see wire-server/charts/cannon/values.yaml for more possible options) + +# in your wire-server/values.yaml overrides: +cannon: + service: + nginz: + enabled: true + hostname: "nginz-ssl.example.com" + externalDNS: + enabled: true + certManager: + enabled: true + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: "nlb" + service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing" +nginz: + nginx_conf: + ignored_upstreams: ["cannon"] +``` + +```yaml +# in your wire-server/secrets.yaml overrides: +cannon: + secrets: + nginz: + zAuth: + publicKeys: ... # same values as in nginz.secrets.zAuth.publicKeys +``` + +```yaml +# in your nginx-ingress-services/values.yaml overrides: +websockets: + enabled: false +``` + +## Blocking creation of personal users, new teams + +### In Brig + +There are some unauthenticated end-points that allow arbitrary users on the open internet to do things like create a new team. This is desired in the cloud, but if you run an on-prem setup that is open to the world, you may want to block this. + +Brig has a server option for this: + +```yaml +optSettings: + setRestrictUserCreation: true +``` + +If `setRestrictUserCreation` is `true`, creating new personal users or new teams on your instance from outside your backend installation is impossible. (If you want to be more technical: requests to `/register` that create a new personal account or a new team are answered with `403 forbidden`.) + +On instances with restricted user creation, the site operator with access to the internal REST API can still circumvent the restriction: just log into a brig service pod via ssh and follow the steps in `hack/bin/create_test_team_admins.sh.` + +```{note} +Once the creation of new users and teams has been disabled, it will still be possible to use the [team creation process](https://support.wire.com/hc/en-us/articles/115003858905-Create-a-team) (enter the new team name, email, password, etc), but it will fail/refuse creation late in the creation process (after the «Create team» button is clicked). +``` + +### In the WebApp + +Another way of disabling user registration is by this webapp setting, in `values.yaml`, changing this value from `true` to `false`: + +```yaml +FEATURE_ENABLE_ACCOUNT_REGISTRATION: "false" +``` + +```{note} +If you only disable the creation of users in the webapp, but do not do so in Brig/the backend, a malicious user would be able to use the API to create users, so make sure to disable both. +``` + +## You may want + +- more server resources to ensure + [high-availability](#persistence-and-high-availability) +- an email/SMTP server to send out registration emails +- depending on your required functionality, you may or may not need an + [AWS account](https://aws.amazon.com/). See details about + limitations without an AWS account in the following sections. +- one or more people able to maintain the installation +- official support by Wire ([contact us](https://wire.com/pricing/)) + +```{warning} +As of 2020-08-10, the documentation sections below are partially out of date and need to be updated. +``` + +## Metrics/logging + +- {ref}`monitoring` +- {ref}`logging` + +## SMTP server + +**Assumptions**: none + +**Provides**: + +- full control over email sending + +**You need**: + +- SMTP credentials (to allow for email sending; prerequisite for + registering users and running the smoketest) + +**How to configure**: + +- *if using a gmail account, ensure to enable* ['less secure + apps'](https://support.google.com/accounts/answer/6010255?hl=en) +- Add user, SMTP server, connection type to `values/wire-server`'s + values file under `brig.config.smtp` +- Add password in `secrets/wire-server`'s secrets file under + `brig.secrets.smtpPassword` + +## Load balancer on bare metal servers + +**Assumptions**: + +- You installed kubernetes on bare metal servers or virtual machines + that can bind to a public IP address. +- **If you are using AWS or another cloud provider, see**[Creating a + cloudprovider-based load + balancer](#load-balancer-on-cloud-provider)**instead** + +**Provides**: + +- Allows using a provided Load balancer for incoming traffic +- SSL termination is done on the ingress controller +- You can access your wire-server backend with given DNS names, over + SSL and from anywhere in the internet + +**You need**: + +- A kubernetes node with a *public* IP address (or internal, if you do + not plan to expose the Wire backend over the Internet but we will + assume you are using a public IP address) + +- DNS records for the different exposed addresses (the ingress depends + on the usage of virtual hosts), namely: + + - `nginz-https.` + - `nginz-ssl.` + - `assets.` + - `webapp.` + - `account.` + - `teams.` + +- A wildcard certificate for the different hosts (`*.`) - we + assume you want to do SSL termination on the ingress controller + +**Caveats**: + +- Note that there can be only a *single* load balancer, otherwise your + cluster might become + [unstable](https://metallb.universe.tf/installation/) + +**How to configure**: + +``` +cp values/metallb/demo-values.example.yaml values/metallb/demo-values.yaml +cp values/nginx-ingress-services/demo-values.example.yaml values/nginx-ingress-services/demo-values.yaml +cp values/nginx-ingress-services/demo-secrets.example.yaml values/nginx-ingress-services/demo-secrets.yaml +``` + +- Adapt `values/metallb/demo-values.yaml` to provide a list of public + IP address CIDRs that your kubernetes nodes can bind to. +- Adapt `values/nginx-ingress-services/demo-values.yaml` with correct URLs +- Put your TLS cert and key into + `values/nginx-ingress-services/demo-secrets.yaml`. + +Install `metallb` (for more information see the +[docs](https://metallb.universe.tf)): + +```sh +helm upgrade --install --namespace metallb-system metallb wire/metallb \ + -f values/metallb/demo-values.yaml \ + --wait --timeout 1800 +``` + +Install `nginx-ingress-[controller,services]`: + +:: +: helm upgrade --install --namespace demo demo-nginx-ingress-controller wire/nginx-ingress-controller + + : --wait + + helm upgrade --install --namespace demo demo-nginx-ingress-services wire/nginx-ingress-services + + : -f values/nginx-ingress-services/demo-values.yaml -f values/nginx-ingress-services/demo-secrets.yaml --wait + +Now, create DNS records for the URLs configured above. + +## Load Balancer on cloud-provider + +### AWS + +[Upload the required +certificates](https://aws.amazon.com/premiumsupport/knowledge-center/import-ssl-certificate-to-iam/). +Create and configure `values/aws-ingress/demo-values.yaml` from the +examples. + +``` +helm upgrade --install --namespace demo demo-aws-ingress wire/aws-ingress \ + -f values/aws-ingress/demo-values.yaml \ + --wait +``` + +To give your load balancers public DNS names, create and edit +`values/external-dns/demo-values.yaml`, then run +[external-dns](https://github.com/helm/charts/tree/master/stable/external-dns): + +``` +helm repo update +helm upgrade --install --namespace demo demo-external-dns stable/external-dns \ + --version 1.7.3 \ + -f values/external-dns/demo-values.yaml \ + --wait +``` + +Things to note about external-dns: + +- There can only be a single external-dns chart installed (one per + kubernetes cluster, not one per namespace). So if you already have + one running for another namespace you probably don't need to do + anything. +- You have to add the appropriate IAM permissions to your cluster (see + the + [README](https://github.com/helm/charts/tree/master/stable/external-dns)). +- Alternatively, use the AWS route53 console. + +### Other cloud providers + +This information is not yet available. If you'd like to contribute by +adding this information for your cloud provider, feel free to read the +[contributing guidelines](https://github.com/wireapp/wire-server-deploy/blob/master/CONTRIBUTING.md) and open a PR. + +## Real AWS services + +**Assumptions**: + +- You installed kubernetes and wire-server on AWS + +**Provides**: + +- Better availability guarantees and possibly better functionality of + AWS services such as SQS and dynamoDB. +- You can use ELBs in front of nginz for higher availability. +- instead of using a smtp server and connect with SMTP, you may use + SES. See configuration of brig and the `useSES` toggle. + +**You need**: + +- An AWS account + +**How to configure**: + +- Instead of using fake-aws charts, you need to set up the respective + services in your account, create queues, tables etc. Have a look at + the fake-aws-\* charts; you'll need to replicate a similar setup. + + - Once real AWS resources are created, adapt the configuration in + the values and secrets files for wire-server to use real endpoints + and real AWS keys. Look for comments including + `if using real AWS`. + +- Creating AWS resources in a way that is easy to create and delete + could be done using either [terraform](https://www.terraform.io/) + or [pulumi](https://pulumi.io/). If you'd like to contribute by + creating such automation, feel free to read the [contributing + guidelines](https://github.com/wireapp/wire-server-deploy/blob/master/CONTRIBUTING.md) and open a PR. + +## Persistence and high-availability + +Currently, due to the way kubernetes and cassandra +[interact](https://github.com/kubernetes/kubernetes/issues/28969), +cassandra cannot reliably be installed on kubernetes. Some people have +tried, e.g. [this +project](https://github.com/instaclustr/cassandra-operator) though at +the time of writing (Nov 2018), this does not yet work as advertised. We +recommend therefore to install cassandra, (possibly also elasticsearch +and redis) separately, i.e. outside of kubernetes (using 3 nodes each). + +For further higher-availability: + +- scale your kubernetes cluster to have separate etcd and master nodes + (3 nodes each) +- use 3 instead of 1 replica of each wire-server chart + +## Security + +For a production deployment, you should, as a minimum: + +- Ensure traffic between kubernetes nodes, etcd and databases are + confined to a private network +- Ensure kubernetes API is unreachable from the public internet (e.g. + put behind VPN/bastion host or restrict IP range) to prevent + [kubernetes + vulnerabilities](https://www.cvedetails.com/vulnerability-list/vendor_id-15867/product_id-34016/Kubernetes-Kubernetes.html) + from affecting you +- Ensure your operating systems get security updates automatically +- Restrict ssh access / harden sshd configuration +- Ensure no other pods with public access than the main ingress are + deployed on your cluster, since, in the current setup, pods have + access to etcd values (and thus any secrets stored there, including + secrets from other pods) +- Ensure developers encrypt any secrets.yaml files + +Additionally, you may wish to build, sign, and host your own docker +images to have increased confidence in those images. We haved "signed +container images" on our roadmap. + +## Sign up with a phone number (Sending SMS) + +**Provides**: + +- Registering accounts with a phone number + +**You need**: + +- a [Nexmo](https://www.nexmo.com/) account +- a [Twilio](https://www.twilio.com/) account + +**How to configure**: + +See the `brig` chart for configuration. + +(rd-party-proxying)= + +## 3rd-party proxying + +You need Giphy/Google/Spotify/Soundcloud API keys (if you want to +support previews by proxying these services) + +See the `proxy` chart for configuration. + +## Routing traffic to other namespaces via nginz + +If you have some components running in namespaces different from nginz. For +instance, the billing service (`ibis`) could be deployed to a separate +namespace, say `integrations`. But it still needs to get traffic via +`nginz`. When this is needed, the helm config can be adjusted like this: + +```yaml +# in your wire-server/values.yaml overrides: +nginz: + nginx_conf: + upstream_namespace: + ibis: integrations +``` + +## Marking an installation as self-hosted + +In case your wire installation is self-hosted (on-premise, demo installs), it needs to be aware that it is through a configuration option. As of release chart 4.15.0, `"true"` is the default behavior, and nothing needs to be done. + +If that option is not set, team-settings will prompt users about "wire for free" and associated functions. + +With that option set, all payment related functionality is disabled. + +The option is `IS_SELF_HOSTED`, and you set it in your `values.yaml` file (originally a copy of `prod-values.example.yaml` found in `wire-server-deploy/values/wire-server/`). + +In case of a demo install, replace `prod` with `demo`. + +First set the option under the `team-settings` section, `envVars` sub-section: + +```yaml +# NOTE: Only relevant if you want team-settings +team-settings: + envVars: + IS_SELF_HOSTED: "true" +``` + +Second, also set the option under the `account-pages` section: + +```yaml +# NOTE: Only relevant if you want account-pages +account-pages: + envVars: + IS_SELF_HOSTED: "true" +``` + +(auth-cookie-config)= + +## Configuring authentication cookie throttling + +Authentication cookies and the related throttling mechanism is described in the *Client API documentation*: +{ref}`login-cookies` + +The maximum number of cookies per account and type is defined by the brig option +`setUserCookieLimit`. Its default is `32`. + +Throttling is configured by the brig option `setUserCookieThrottle`. It is an +object that contains two fields: + +`stdDev` + +: The minimal standard deviation of cookie creation timestamps in + Seconds. (Default: `3000`, + [Wikipedia: Standard deviation](https://en.wikipedia.org/wiki/Standard_deviation)) + +`retryAfter` + +: Wait time in Seconds when `stdDev` is violated. (Default: `86400`) + +The default values are fine for most use cases. (Generally, you don't have to +configure them for your installation.) + +Condensed example: + +```yaml +brig: + optSettings: + setUserCookieLimit: 32 + setUserCookieThrottle: + stdDev: 3000 + retryAfter: 86400 +``` + +## Configuring searchability + +You can configure how search is limited or not based on user membership in a given team. + +There are two types of searches based on the direction of search: + +- **Inbound** searches mean that somebody is searching for you. Configuring the inbound search visibility means that you (or some admin) can configure whether others can find you or not. +- **Outbound** searches mean that you are searching for somebody. Configuring the outbound search visibility means that some admin can configure whether you can find other users or not. + +There are different types of matches: + +- **Exact handle** search means that the user is found only if the search query is exactly the user handle (e.g. searching for `mc` will find `@mc` but not `@mccaine`). This search returns zero or one results. +- **Full text** search means that the user is found if the search query contains some subset of the user display name and handle. (e.g. the query `mar` will find `Marco C`, `Omar`, `@amaro`) + +### Searching users on the same backend + +Search visibility is controlled by three parameters on the backend: + +- A team outbound configuration flag, `TeamSearchVisibility` with possible values `SearchVisibilityStandard`, `SearchVisibilityNoNameOutsideTeam` + + - `SearchVisibilityStandard` means that the user can find other people outside of the team, if the searched-person inbound search allows it + - `SearchVisibilityNoNameOutsideTeam` means that the user can not find any user outside the team by full text search (but exact handle search still works) + +- A team inbound configuration flag, `SearchVisibilityInbound` with possible values `SearchableByOwnTeam`, `SearchableByAllTeams` + + - `SearchableByOwnTeam` means that the user can be found only by users in their own team. + - `SearchableByAllTeams` means that the user can be found by users in any/all teams. + +- A server configuration flag `searchSameTeamOnly` with possible values true, false. + + - `Note`: For the same backend, this affects inbound and outbound searches (simply because all teams will be subject to this behavior) + - Setting this to `true` means that the all teams on that backend can only find users that belong to their team + +These flag are set on the backend and the clients do not need to be aware of them. + +The flags will influence the behavior of the search API endpoint; clients will only need to parse the results, that are already filtered for them by the backend. + +#### Table of possible outcomes + +```{eval-rst} ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| Is search-er (`uA`) in team (tA)? | Is search-ed (`uB`) in a team? | Backend flag `searchSameTeamOnly` | Team `tA`'s flag `TeamSearchVisibility` | Team tB's flag `SearchVisibilityInbound` | Result of exact search for `uB` | Result of full-text search for `uB` | ++====================================+=================================+====================================+==========================================+===========================================+==================================+======================================+ +| **Search within the same team** | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| Yes, `tA` | Yes, the same team `tA` | Irrelevant | Irrelevant | Irrelevant | Found | Found | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| **Outbound search unrestricted** | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| Yes, `tA` | Yes, another team tB | false | `SearchVisibilityStandard` | `SearchableByAllTeams` | Found | Found | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| Yes, `tA` | Yes, another team tB | false | `SearchVisibilityStandard` | `SearchableByOwnTeam` | Found | Not found | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| **Outbound search restricted** | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| Yes, `tA` | Yes, another team tB | true | Irrelevant | Irrelevant | Not found | Not found | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| Yes, `tA` | Yes, another team tB | false | `SearchVisibilityNoNameOutsideTeam` | Irrelevant | Found | Not found | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +| Yes, `tA` | No | false | `SearchVisibilityNoNameOutsideTeam` | There’s no team B | Found | Not found | ++------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ +``` + +#### Changing the configuration on the server + +To change the `searchSameTeamOnly` setting on the backend, edit the `values.yaml.gotmpl` file for the wire-server chart at this nested level of the configuration: + +```yaml +brig: + # ... + config: + # ... + optSettings: + # ... + setSearchSameTeamOnly: true +``` + +If `setSearchSameTeamOnly` is set to `true` then `TeamSearchVisibility` is forced be in the `SearchVisibilityNoNameOutsideTeam` setting for all teams. + +#### Changing the default configuration for all teams + +If `setSearchSameTeamOnly` is set to `false` (or missing from the configuration) then the default value `TeamSearchVisibility` can be configured at this level of the configuration of the `value.yaml.gotmpl` file of the wire-server chart: + +```yaml +galley: + #... + config: + #... + settings: + #... + featureFlags: + #... + teamSearchVisibility: enabled-by-default +``` + +This default value applies to all teams for which no explicit configuration of the `TeamSearchVisibility` has been set. + +### Searching users on another (federated) backend + +For federated search the table above does not apply, see following table. + +```{note} +Incoming federated searches (i.e. searches from one backend to another) are considered always as being performed from a team user, even if they are performed from a personal user. + +This is because the incoming search request does not carry the information whether the user performing the search was in a team or not. + +So we have to make one assumption, and we assume that they were in a team. +``` + +Allowing search is done at the backend configuration level by the sysadmin: + +- Outbound search restrictions (`searchSameTeamOnly`, `TeamSearchVisibility`) do not apply to federated searches + +- A configuration setting `FederatedUserSearchPolicy` per federating domain with these possible values: + + - `no_search` The federating backend is not allowed to search any users (either by exact handle or full-text). + - `exact_handle_search` The federating backend may only search by exact handle + - `full_search` The federating backend may search users by full text search on display name and handle. The search search results are additionally affected by `SearchVisibilityInbound` setting of each team on the backend. + +- The `SearchVisibilityInbound` setting applies. Since the default value for teams is `SearchableByOwnTeam` this means that for a team to be full-text searchable by users on a federating backend both + + - `FederatedUserSearchPolicy` needs to be set to to full_search for the federating backend + - Any team that wants to be full-text searchable needs to be set to `SearchableByAllTeams` + +The configuration value `FederatedUserSearchPolicy` is per federated domain, e.g. in the values of the wire-server chart: + +```yaml +brig: + config: + optSettings: + setFederationDomainConfigs: + - domain: a.example.com + search_policy: no_search + - domain: a.example.com + search_policy: full_search +``` + +#### Table of possible outcomes + +In the following table, user `uA` on backend A is searching for user `uB` on team `tB` on backend B. + +Any of the flags set for searching users on the same backend are ignored. + +It’s worth nothing that if two users are on two separate backend, they are also guaranteed to be on two separate teams, as teams can not spread across backends. + +| Who is searching | Backend B flag `FederatedUserSearchPolicy` | Team `tB`'s flag `SearchVisibilityInbound` | Result of exact search for `uB` | Result of full-text search for `uB` | +| ---------------------- | ------------------------------------------ | ------------------------------------------ | ------------------------------- | ----------------------------------- | +| user `uA` on backend A | `no_search` | Irrelevant | Not found | Not found | +| user `uA` on backend A | `exact_handle_search` | Irrelevant | Found | Not found | +| user `uA` on backend A | `full_search` | SearchableByOwnTeam | Found | Not found | +| user `uA` on backend A | `full_search` | SearchableByAllTeams | Found | Found | + +### Changing the settings for a given team + +If you need to change searchabilility for a specific team (rather than the entire backend, as above), you need to make specific calls to the API. + +#### Team searchVisibility + +The team flag `searchVisibility` affects the outbound search of user searches. + +If it is set to `no-name-outside-team` for a team then all users of that team will no longer be able to find users that are not part of their team when searching. + +This also includes finding other users by by providing their exact handle. By default it is set to `standard`, which doesn't put any additional restrictions to outbound searches. + +The setting can be changed via endpoint (for more details on how to make the API calls with `curl`, read further): + +``` +GET /teams/{tid}/search-visibility + -- Shows the current TeamSearchVisibility value for the given team + +PUT /teams/{tid}/search-visibility + -- Set specific search visibility for the team + +pull-down-menu "body": + "standard" + "no-name-outside-team" +``` + +The team feature flag `teamSearchVisibility` determines whether it is allowed to change the `searchVisibility` setting or not. + +The default is `disabled-by-default`. + +```{note} +Whenever this feature setting is disabled the `searchVisibility` will be reset to standard. +``` + +The default setting that applies to all teams on the instance can be defined at configuration + +```yaml +settings: + featureFlags: + teamSearchVisibility: disabled-by-default # or enabled-by-default +``` + +#### TeamFeature searchVisibilityInbound + +The team feature flag `searchVisibilityInbound` affects if the team's users are searchable by users from other teams. + +The default setting is `searchable-by-own-team` which hides users from search results by users from other teams. + +If it is set to `searchable-by-all-teams` then users of this team may be included in the results of search queries by other users. + +```{note} +The configuration of this flag does not affect search results when the search query matches the handle exactly. + +If the handle is provdided then any user on the instance can find users. +``` + +This team feature flag can only by toggled by site-administrators with direct access to the galley instance (for more details on how to make the API calls with `curl`, read further): + +``` +PUT /i/teams/{tid}/features/search-visibility-inbound +``` + +With JSON body: + +```json +{"status": "enabled"} +``` + +or + +```json +{"status": "disabled"} +``` + +Where `enabled` is equivalent to `searchable-by-all-teams` and `disabled` is equivalent to `searchable-by-own-team`. + +The default setting that applies to all teams on the instance can be defined at configuration. + +```yaml +searchVisibilityInbound: + defaults: + status: enabled # OR disabled +``` + +Individual teams can overwrite the default setting with API calls as per above. + +#### Making the API calls + +To make API calls to set an explicit configuration for\` TeamSearchVisibilityInbound\` per team, you first need to know the Team ID, which can be found in the team settings app. + +It is an `UUID` which has format like this `dcbedf9a-af2a-4f43-9fd5-525953a919e1`. + +In the following we will be using this Team ID as an example, please replace it with your own team id. + +Next find the name of a `galley` pod by looking at the output of running this command: + +```sh +kubectl -n wire get pods +``` + +The output will look something like this: + +``` +... +galley-5f4787fdc7-9l64n ... +galley-migrate-data-lzz5j ... +... +``` + +Select any of the galley pods, for example we will use `galley-5f4787fdc7-9l64n`. + +Next, set up a port-forwarding from your local machine's port `9000` to the galley's port `8080` by running: + +```sh +kubectl port-forward -n wire galley-5f4787fdc7-9l64n 9000:8080 +``` + +Keep this command running until the end of these instuctions. + +Please run the following commands in a seperate terminal while keeping the terminal which establishes the port-forwarding open. + +To see team's current setting run: + +```sh +curl -XGET http://localhost:9000/i/teams/dcbedf9a-af2a-4f43-9fd5-525953a919e1/features/searchVisibilityInbound + +# {"lockStatus":"unlocked","status":"disabled"} +``` + +Where `disabled` corresponds to `SearchableByOwnTeam` and enabled corresponds to `SearchableByAllTeams`. + +To change the `TeamSearchVisibilityInbound` to `SearchableByAllTeams` for the team run: + +```sh +curl -XPUT -H 'Content-Type: application/json' -d "{\"status\": \"enabled\"}" http://localhost:9000/i/teams/dcbedf9a-af2a-4f43-9fd5-525953a919e1/features/searchVisibilityInbound +``` + +To change the TeamSearchVisibilityInbound to SearchableByOwnTeam for the team run: + +```sh +curl -XPUT -H 'Content-Type: application/json' -d "{\"status\": \"disabled\"}" http://localhost:9000/i/teams/dcbedf9a-af2a-4f43-9fd5-525953a919e1/features/searchVisibilityInbound +``` + +## Configuring classified domains + +As a backend administrator, if you want to control which other backends (identified by their domain) are "classified", + +change the following `galley` configuration in the `value.yaml.gotmpl` file of the wire-server chart: + +```yaml +galley: + replicaCount: 1 + config: + ... + featureFlags: + ... + classifiedDomains: + status: enabled + config: + domains: ["domain-that-is-classified.link"] + ... +``` + +This is not only a `backend` configuration, but also a `team` configuration/feature. + +This means that different combinations of configurations will have different results. + +Here is a table to navigate the possible configurations: + +| Backend Config enabled/disabled | Backend Config Domains | Team Config enabled/disabled | Team Config Domains | User's view | +| ------------------------------- | ---------------------------------------------- | ---------------------------- | ----------------------- | -------------------------------- | +| Enabled | \[domain1.example.com\] | Not configured | Not configured | Enabled, \[domain1.example.com\] | +| Enabled | \[domain1.example.com\]\[domain1.example.com\] | Enabled | Not configured | Enabled, \[domain1.example.com\] | +| Enabled | \[domain1.example.com\] | Enabled | \[domain2.example.com\] | Enabled, Undefined | +| Enabled | \[domain1.example.com\] | Disabled | Anything | Undefined | +| Disabled | Anything | Not configured | Not configured | Disabled, no domains | +| Disabled | Anything | Enabled | \[domain2.example.com\] | Undefined | + +The table assumes the following: + +- When backend level config says that this feature is enabled, it is illegal to not specify domains at the backend level. +- When backend level config says that this feature is disabled, the list of domains is ignored. +- When team level feature is disabled, the accompanying domains are ignored. + +## S3 Addressing Style + +S3 can either by addressed in path style, i.e. +`https:////`, or vhost style, i.e. +`https://./`. AWS's S3 offering has deprecated +path style addressing for S3 and completely disabled it for buckets created +after 30 Sep 2020: + + +However other object storage providers (specially self-deployed ones like MinIO) +may not support vhost style addressing yet (or ever?). Users of such buckets +should configure this option to "path": + +```yaml +cargohold: + aws: + s3AddressingStyle: path +``` + +Installations using S3 service provided by AWS, should use "auto", this option +will ensure that vhost style is only used when it is possible to construct a +valid hostname from the bucket name and the bucket name doesn't contain a '.'. +Having a '.' in the bucket name causes TLS validation to fail, hence it is not +used by default: + +```yaml +cargohold: + aws: + s3AddressingStyle: auto +``` + +Using "virtual" as an option is only useful in situations where vhost style +addressing must be used even if it is not possible to construct a valid hostname +from the bucket name or the S3 service provider can ensure correct certificate +is issued for bucket which contain one or more '.'s in the name: + +```yaml +cargohold: + aws: + s3AddressingStyle: virtual +``` + +When this option is unspecified, wire-server defaults to path style addressing +to ensure smooth transition for older deployments. diff --git a/docs/src/how-to/install/configuration-options.rst b/docs/src/how-to/install/configuration-options.rst deleted file mode 100644 index ee2302006e..0000000000 --- a/docs/src/how-to/install/configuration-options.rst +++ /dev/null @@ -1,1105 +0,0 @@ -.. _configuration-options: -Part 3 - configuration options in a production setup -==================================================================== - -This contains instructions to configure specific aspects of your production setup depending on your needs. - -Depending on your use-case and requirements, you may need to -configure none, or only a subset of the following sections. - -Redirect some traffic through a http(s) proxy ---------------------------------------------- - -In case you wish to use http(s) proxies, you can add a configuration like this to the wire-server services in question: - -Assuming your proxy can be reached from within Kubernetes at ``http://proxy:8080``, add the following for each affected service (e.g. ``gundeck``) to your Helm overrides in ``values/wire-server/values.yaml`` : - -.. code:: yaml - - gundeck: - # ... - config: - # ... - proxy: - httpProxy: "http://proxy:8080" - httpsProxy: "http://proxy:8080" - noProxyList: - - "localhost" - - "127.0.0.1" - - "10.0.0.0/8" - - "elasticsearch-external" - - "cassandra-external" - - "redis-ephemeral" - - "fake-aws-sqs" - - "fake-aws-dynamodb" - - "fake-aws-sns" - - "brig" - - "cargohold" - - "galley" - - "gundeck" - - "proxy" - - "spar" - - "federator" - - "cannon" - - "cannon-0.cannon.default" - - "cannon-1.cannon.default" - - "cannon-2.cannon.default" - -Depending on your setup, you may need to repeat this for the other services like ``brig`` as well. - -.. _push-sns: - -Enable push notifications using the public appstore / playstore mobile Wire clients ------------------------------------------------------------------------------------ - -1. You need to get in touch with us. Please talk to sales or customer support - see https://wire.com -2. If a contract agreement has been reached, we can set up a separate AWS account for you containing the necessary AWS SQS/SNS setup to route push notifications through to the mobile apps. We will then forward some configuration / access credentials that looks like: - -.. code:: yaml - - gundeck: - config: - aws: - account: "" - arnEnv: "" - queueName: "-gundeck-events" - region: "" - snsEndpoint: "https://sns..amazonaws.com" - sqsEndpoint: "https://sqs..amazonaws.com" - secrets: - awsKeyId: "" - awsSecretKey: "" - -To make use of those, first test the credentials are correct, e.g. using the ``aws`` command-line tool (for more information on how to configure credentials, please refer to the `official docs `__): - -.. code:: - - AWS_REGION= - AWS_ACCESS_KEY_ID=<...> - AWS_SECRET_ACCESS_KEY=<...> - ENV= #e.g staging - - aws sqs get-queue-url --queue-name "$ENV-gundeck-events" - -You should get a result like this: - -.. code:: - - { - "QueueUrl": "https://.queue.amazonaws.com//-gundeck-events" - } - -Then add them to your gundeck configuration overrides. - -Keys below ``gundeck.config`` belong into ``values/wire-server/values.yaml``: - -.. code:: yaml - - gundeck: - # ... - config: - aws: - queueName: # e.g. staging-gundeck-events - account: # , e.g. 123456789 - region: # e.g. eu-central-1 - snsEndpoint: # e.g. https://sns.eu-central-1.amazonaws.com - sqsEndpoint: # e.g. https://sqs.eu-central-1.amazonaws.com - arnEnv: # e.g. staging - this must match the environment name (first part of queueName) - -Keys below ``gundeck.secrets`` belong into ``values/wire-server/secrets.yaml``: - -.. code:: yaml - - gundeck: - # ... - secrets: - awsKeyId: CHANGE-ME - awsSecretKey: CHANGE-ME - - -After making this change and applying it to gundeck (ensure gundeck pods have restarted to make use of the updated configuration - that should happen automatically), make sure to reset the push token on any mobile devices that you may have in use. - -Next, if you want, you can stop using the `fake-aws-sns` pods in case you ran them before: - -.. code:: yaml - - # inside override values/fake-aws/values.yaml - fake-aws-sns: - enabled: false - -Controlling the speed of websocket draining during cannon pod replacement -------------------------------------------------------------------------- - -The 'cannon' component is responsible for persistent websocket connections. -Normally the default options would slowly and gracefully drain active websocket -connections over a maximum of ``(amount of cannon replicas * 30 seconds)`` during -the deployment of a new wire-server version. This will lead to a very brief -interruption for Wire clients when their client has to re-connect on the -websocket. - -You're not expected to need to change these settings. - -The following options are only relevant during the restart of cannon itself. -During a restart of nginz or ingress-controller, all websockets will get -severed. If this is to be avoided, see section :ref:`separate-websocket-traffic` - -``drainOpts``: Drain websockets in a controlled fashion when cannon receives a -SIGTERM or SIGINT (this happens when a pod is terminated e.g. during rollout -of a new version). Instead of waiting for connections to close on their own, -the websockets are now severed at a controlled pace. This allows for quicker -rollouts of new versions. - -There is no way to entirely disable this behaviour, two extreme examples below - -* the quickest way to kill cannon is to set ``gracePeriodSeconds: 1`` and - ``minBatchSize: 100000`` which would sever all connections immediately; but it's - not recommended as you could DDoS yourself by forcing all active clients to - reconnect at the same time. With this, cannon pod replacement takes only 1 - second per pod. -* the slowest way to roll out a new version of cannon without severing websocket - connections for a long time is to set ``minBatchSize: 1``, - ``millisecondsBetweenBatches: 86400000`` and ``gracePeriodSeconds: 86400`` - which would lead to one single websocket connection being closed immediately, - and all others only after 1 day. With this, cannon pod replacement takes a - full day per pod. - -.. code:: yaml - - # overrides for wire-server/values.yaml - cannon: - drainOpts: - # The following defaults drain a minimum of 400 connections/second - # for a total of 10000 over 25 seconds - # (if cannon holds more connections, draining will happen at a faster pace) - gracePeriodSeconds: 25 - millisecondsBetweenBatches: 50 - minBatchSize: 20 - - -Control nginz upstreams (routes) into the Kubernetes cluster ------------------------------------------------------------- - -Open unterminated upstreams (routes) into the Kubernetes cluster are a potential -security issue. To prevent this, there are fine-grained settings in the nginz -configuration defining which upstreams should exist. - -Default upstreams -~~~~~~~~~~~~~~~~~ - -Upstreams for services that exist in (almost) every Wire installation are -enabled by default. These are: - -- ``brig`` -- ``cannon`` -- ``cargohold`` -- ``galley`` -- ``gundeck`` -- ``spar`` - -For special setups (as e.g. described in separate-websocket-traffic_) the -upstreams of these services can be ignored (disabled) with the setting -``nginz.nginx_conf.ignored_upstreams``. - -The most common example is to disable the upstream of ``cannon``: - -.. code:: yaml - - nginz: - nginx_conf: - ignored_upstreams: ["cannon"] - - -Optional upstreams -~~~~~~~~~~~~~~~~~~ - -There are some services that are usually not deployed on most Wire installations -or are specific to the Wire cloud: - -- ``ibis`` -- ``galeb`` -- ``calling-test`` -- ``proxy`` - -The upstreams for those are disabled by default and can be enabled by the -setting ``nginz.nginx_conf.enabled_extra_upstreams``. - -The most common example is to enable the (extra) upstream of ``proxy``: - -.. code:: yaml - - nginz: - nginx_conf: - enabled_extra_upstreams: ["proxy"] - - -Combining default and extra upstream configurations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Default and extra upstream configurations are independent of each other. I.e. -``nginz.nginx_conf.ignored_upstreams`` and -``nginz.nginx_conf.enabled_extra_upstreams`` can be combined in the same -configuration: - -.. code:: yaml - - nginz: - nginx_conf: - ignored_upstreams: ["cannon"] - enabled_extra_upstreams: ["proxy"] - - -.. _separate-websocket-traffic: - -Separate incoming websocket network traffic from the rest of the https traffic -------------------------------------------------------------------------------- - -By default, incoming network traffic for websockets comes through these network -hops: - -Internet -> LoadBalancer -> kube-proxy -> nginx-ingress-controller -> nginz -> cannon - -In order to have graceful draining of websockets when something gets restarted, as it is not easily -possible to implement the graceful draining on nginx-ingress-controller or nginz by itself, there is -a configuration option to get the following network hops: - -Internet -> separate LoadBalancer for cannon only -> kube-proxy -> [nginz->cannon (2 containers in the same pod)] - -.. code:: yaml - - # example on AWS when using cert-manager for TLS certificates and external-dns for DNS records - # (see wire-server/charts/cannon/values.yaml for more possible options) - - # in your wire-server/values.yaml overrides: - cannon: - service: - nginz: - enabled: true - hostname: "nginz-ssl.example.com" - externalDNS: - enabled: true - certManager: - enabled: true - annotations: - service.beta.kubernetes.io/aws-load-balancer-type: "nlb" - service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing" - nginz: - nginx_conf: - ignored_upstreams: ["cannon"] - -.. code:: yaml - - # in your wire-server/secrets.yaml overrides: - cannon: - secrets: - nginz: - zAuth: - publicKeys: ... # same values as in nginz.secrets.zAuth.publicKeys - -.. code:: yaml - - # in your nginx-ingress-services/values.yaml overrides: - websockets: - enabled: false - - -Blocking creation of personal users, new teams ----------------------------------------------- - -In Brig -~~~~~~~ - -There are some unauthenticated end-points that allow arbitrary users on the open internet to do things like create a new team. This is desired in the cloud, but if you run an on-prem setup that is open to the world, you may want to block this. - -Brig has a server option for this: - -.. code:: yaml - - optSettings: - setRestrictUserCreation: true - -If `setRestrictUserCreation` is `true`, creating new personal users or new teams on your instance from outside your backend installation is impossible. (If you want to be more technical: requests to `/register` that create a new personal account or a new team are answered with `403 forbidden`.) - -On instances with restricted user creation, the site operator with access to the internal REST API can still circumvent the restriction: just log into a brig service pod via ssh and follow the steps in `hack/bin/create_test_team_admins.sh.` - -.. note:: - Once the creation of new users and teams has been disabled, it will still be possible to use the `team creation process `__ (enter the new team name, email, password, etc), but it will fail/refuse creation late in the creation process (after the «Create team» button is clicked). - -In the WebApp -~~~~~~~~~~~~~ - -Another way of disabling user registration is by this webapp setting, in `values.yaml`, changing this value from `true` to `false`: - -.. code:: yaml - - FEATURE_ENABLE_ACCOUNT_REGISTRATION: "false" - -.. note:: - If you only disable the creation of users in the webapp, but do not do so in Brig/the backend, a malicious user would be able to use the API to create users, so make sure to disable both. - -You may want ------------- - -- more server resources to ensure - `high-availability <#persistence-and-high-availability>`__ -- an email/SMTP server to send out registration emails -- depending on your required functionality, you may or may not need an - `AWS account `__. See details about - limitations without an AWS account in the following sections. -- one or more people able to maintain the installation -- official support by Wire (`contact us `__) - -.. warning:: - - As of 2020-08-10, the documentation sections below are partially out of date and need to be updated. - -Metrics/logging ---------------- - -* :ref:`monitoring` -* :ref:`logging` - -SMTP server ------------ - -**Assumptions**: none - -**Provides**: - -- full control over email sending - -**You need**: - -- SMTP credentials (to allow for email sending; prerequisite for - registering users and running the smoketest) - -**How to configure**: - -- *if using a gmail account, ensure to enable* `'less secure - apps' `__ -- Add user, SMTP server, connection type to ``values/wire-server``'s - values file under ``brig.config.smtp`` -- Add password in ``secrets/wire-server``'s secrets file under - ``brig.secrets.smtpPassword`` - -Load balancer on bare metal servers ------------------------------------ - -**Assumptions**: - -- You installed kubernetes on bare metal servers or virtual machines - that can bind to a public IP address. -- **If you are using AWS or another cloud provider, see**\ `Creating a - cloudprovider-based load - balancer <#load-balancer-on-cloud-provider>`__\ **instead** - -**Provides**: - -- Allows using a provided Load balancer for incoming traffic -- SSL termination is done on the ingress controller -- You can access your wire-server backend with given DNS names, over - SSL and from anywhere in the internet - -**You need**: - -- A kubernetes node with a *public* IP address (or internal, if you do - not plan to expose the Wire backend over the Internet but we will - assume you are using a public IP address) -- DNS records for the different exposed addresses (the ingress depends - on the usage of virtual hosts), namely: - - - ``nginz-https.`` - - ``nginz-ssl.`` - - ``assets.`` - - ``webapp.`` - - ``account.`` - - ``teams.`` - -- A wildcard certificate for the different hosts (``*.``) - we - assume you want to do SSL termination on the ingress controller - -**Caveats**: - -- Note that there can be only a *single* load balancer, otherwise your - cluster might become - `unstable `__ - -**How to configure**: - -:: - - cp values/metallb/demo-values.example.yaml values/metallb/demo-values.yaml - cp values/nginx-ingress-services/demo-values.example.yaml values/nginx-ingress-services/demo-values.yaml - cp values/nginx-ingress-services/demo-secrets.example.yaml values/nginx-ingress-services/demo-secrets.yaml - -- Adapt ``values/metallb/demo-values.yaml`` to provide a list of public - IP address CIDRs that your kubernetes nodes can bind to. -- Adapt ``values/nginx-ingress-services/demo-values.yaml`` with correct URLs -- Put your TLS cert and key into - ``values/nginx-ingress-services/demo-secrets.yaml``. - -Install ``metallb`` (for more information see the -`docs `__): - -.. code:: sh - - helm upgrade --install --namespace metallb-system metallb wire/metallb \ - -f values/metallb/demo-values.yaml \ - --wait --timeout 1800 - -Install ``nginx-ingress-[controller,services]``: - -:: - helm upgrade --install --namespace demo demo-nginx-ingress-controller wire/nginx-ingress-controller \ - --wait - - helm upgrade --install --namespace demo demo-nginx-ingress-services wire/nginx-ingress-services \ - -f values/nginx-ingress-services/demo-values.yaml \ - -f values/nginx-ingress-services/demo-secrets.yaml \ - --wait - -Now, create DNS records for the URLs configured above. - - -Load Balancer on cloud-provider -------------------------------- - -AWS -~~~ - -`Upload the required -certificates `__. -Create and configure ``values/aws-ingress/demo-values.yaml`` from the -examples. - -:: - - helm upgrade --install --namespace demo demo-aws-ingress wire/aws-ingress \ - -f values/aws-ingress/demo-values.yaml \ - --wait - -To give your load balancers public DNS names, create and edit -``values/external-dns/demo-values.yaml``, then run -`external-dns `__: - -:: - - helm repo update - helm upgrade --install --namespace demo demo-external-dns stable/external-dns \ - --version 1.7.3 \ - -f values/external-dns/demo-values.yaml \ - --wait - -Things to note about external-dns: - -- There can only be a single external-dns chart installed (one per - kubernetes cluster, not one per namespace). So if you already have - one running for another namespace you probably don't need to do - anything. -- You have to add the appropriate IAM permissions to your cluster (see - the - `README `__). -- Alternatively, use the AWS route53 console. - -Other cloud providers -~~~~~~~~~~~~~~~~~~~~~ - -This information is not yet available. If you'd like to contribute by -adding this information for your cloud provider, feel free to read the -`contributing guidelines `__ and open a PR. - -Real AWS services ------------------ - -**Assumptions**: - -- You installed kubernetes and wire-server on AWS - -**Provides**: - -- Better availability guarantees and possibly better functionality of - AWS services such as SQS and dynamoDB. -- You can use ELBs in front of nginz for higher availability. -- instead of using a smtp server and connect with SMTP, you may use - SES. See configuration of brig and the ``useSES`` toggle. - -**You need**: - -- An AWS account - -**How to configure**: - -- Instead of using fake-aws charts, you need to set up the respective - services in your account, create queues, tables etc. Have a look at - the fake-aws-\* charts; you'll need to replicate a similar setup. - - - Once real AWS resources are created, adapt the configuration in - the values and secrets files for wire-server to use real endpoints - and real AWS keys. Look for comments including - ``if using real AWS``. - -- Creating AWS resources in a way that is easy to create and delete - could be done using either `terraform `__ - or `pulumi `__. If you'd like to contribute by - creating such automation, feel free to read the `contributing - guidelines `__ and open a PR. - -Persistence and high-availability ---------------------------------- - -Currently, due to the way kubernetes and cassandra -`interact `__, -cassandra cannot reliably be installed on kubernetes. Some people have -tried, e.g. `this -project `__ though at -the time of writing (Nov 2018), this does not yet work as advertised. We -recommend therefore to install cassandra, (possibly also elasticsearch -and redis) separately, i.e. outside of kubernetes (using 3 nodes each). - -For further higher-availability: - -- scale your kubernetes cluster to have separate etcd and master nodes - (3 nodes each) -- use 3 instead of 1 replica of each wire-server chart - -Security --------- - -For a production deployment, you should, as a minimum: - -- Ensure traffic between kubernetes nodes, etcd and databases are - confined to a private network -- Ensure kubernetes API is unreachable from the public internet (e.g. - put behind VPN/bastion host or restrict IP range) to prevent - `kubernetes - vulnerabilities `__ - from affecting you -- Ensure your operating systems get security updates automatically -- Restrict ssh access / harden sshd configuration -- Ensure no other pods with public access than the main ingress are - deployed on your cluster, since, in the current setup, pods have - access to etcd values (and thus any secrets stored there, including - secrets from other pods) -- Ensure developers encrypt any secrets.yaml files - -Additionally, you may wish to build, sign, and host your own docker -images to have increased confidence in those images. We haved "signed -container images" on our roadmap. - -Sign up with a phone number (Sending SMS) ------------------------------------------ - -**Provides**: - -- Registering accounts with a phone number - -**You need**: - -- a `Nexmo `__ account -- a `Twilio `__ account - -**How to configure**: - -See the ``brig`` chart for configuration. - -.. _3rd-party-proxying: - -3rd-party proxying ------------------- - -You need Giphy/Google/Spotify/Soundcloud API keys (if you want to -support previews by proxying these services) - -See the ``proxy`` chart for configuration. - -Routing traffic to other namespaces via nginz ---------------------------------------------- - -If you have some components running in namespaces different from nginz. For -instance, the billing service (``ibis``) could be deployed to a separate -namespace, say ``integrations``. But it still needs to get traffic via -``nginz``. When this is needed, the helm config can be adjusted like this: - -.. code:: yaml - - # in your wire-server/values.yaml overrides: - nginz: - nginx_conf: - upstream_namespace: - ibis: integrations - -Marking an installation as self-hosted --------------------------------------- - -In case your wire installation is self-hosted (on-premise, demo installs), it needs to be aware that it is through a configuration option. As of release chart 4.15.0, `"true"` is the default behavior, and nothing needs to be done. - -If that option is not set, team-settings will prompt users about "wire for free" and associated functions. - -With that option set, all payment related functionality is disabled. - -The option is `IS_SELF_HOSTED`, and you set it in your `values.yaml` file (originally a copy of `prod-values.example.yaml` found in `wire-server-deploy/values/wire-server/`). - -In case of a demo install, replace `prod` with `demo`. - -First set the option under the `team-settings` section, `envVars` sub-section: - -.. code:: yaml - - # NOTE: Only relevant if you want team-settings - team-settings: - envVars: - IS_SELF_HOSTED: "true" - -Second, also set the option under the `account-pages` section: - -.. code:: yaml - - # NOTE: Only relevant if you want account-pages - account-pages: - envVars: - IS_SELF_HOSTED: "true" - -.. _auth-cookie-config: - -Configuring authentication cookie throttling --------------------------------------------- - -Authentication cookies and the related throttling mechanism is described in the *Client API documentation*: -:ref:`login-cookies` - -The maximum number of cookies per account and type is defined by the brig option -``setUserCookieLimit``. Its default is ``32``. - -Throttling is configured by the brig option ``setUserCookieThrottle``. It is an -object that contains two fields: - -``stdDev`` - The minimal standard deviation of cookie creation timestamps in - Seconds. (Default: ``3000``, - `Wikipedia: Standard deviation `_) - -``retryAfter`` - Wait time in Seconds when ``stdDev`` is violated. (Default: ``86400``) - -The default values are fine for most use cases. (Generally, you don't have to -configure them for your installation.) - -Condensed example: - - -.. code:: yaml - - brig: - optSettings: - setUserCookieLimit: 32 - setUserCookieThrottle: - stdDev: 3000 - retryAfter: 86400 - - -Configuring searchability -------------------------- - -You can configure how search is limited or not based on user membership in a given team. - -There are two types of searches based on the direction of search: - -* **Inbound** searches mean that somebody is searching for you. Configuring the inbound search visibility means that you (or some admin) can configure whether others can find you or not. -* **Outbound** searches mean that you are searching for somebody. Configuring the outbound search visibility means that some admin can configure whether you can find other users or not. - -There are different types of matches: - -* **Exact handle** search means that the user is found only if the search query is exactly the user handle (e.g. searching for `mc` will find `@mc` but not `@mccaine`). This search returns zero or one results. -* **Full text** search means that the user is found if the search query contains some subset of the user display name and handle. (e.g. the query `mar` will find `Marco C`, `Omar`, `@amaro`) - -Searching users on the same backend -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Search visibility is controlled by three parameters on the backend: - -* A team outbound configuration flag, `TeamSearchVisibility` with possible values `SearchVisibilityStandard`, `SearchVisibilityNoNameOutsideTeam` - - * `SearchVisibilityStandard` means that the user can find other people outside of the team, if the searched-person inbound search allows it - * `SearchVisibilityNoNameOutsideTeam` means that the user can not find any user outside the team by full text search (but exact handle search still works) - -* A team inbound configuration flag, `SearchVisibilityInbound` with possible values `SearchableByOwnTeam`, `SearchableByAllTeams` - - * `SearchableByOwnTeam` means that the user can be found only by users in their own team. - * `SearchableByAllTeams` means that the user can be found by users in any/all teams. - -* A server configuration flag `searchSameTeamOnly` with possible values true, false. - - * ``Note``: For the same backend, this affects inbound and outbound searches (simply because all teams will be subject to this behavior) - * Setting this to `true` means that the all teams on that backend can only find users that belong to their team - -These flag are set on the backend and the clients do not need to be aware of them. - -The flags will influence the behavior of the search API endpoint; clients will only need to parse the results, that are already filtered for them by the backend. - -Table of possible outcomes -.......................... - -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| Is search-er (`uA`) in team (tA)? | Is search-ed (`uB`) in a team? | Backend flag `searchSameTeamOnly` | Team `tA`'s flag `TeamSearchVisibility` | Team tB's flag `SearchVisibilityInbound` | Result of exact search for `uB` | Result of full-text search for `uB` | -+====================================+=================================+====================================+==========================================+===========================================+==================================+======================================+ -| **Search within the same team** | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| Yes, `tA` | Yes, the same team `tA` | Irrelevant | Irrelevant | Irrelevant | Found | Found | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| **Outbound search unrestricted** | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| Yes, `tA` | Yes, another team tB | false | `SearchVisibilityStandard` | `SearchableByAllTeams` | Found | Found | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| Yes, `tA` | Yes, another team tB | false | `SearchVisibilityStandard` | `SearchableByOwnTeam` | Found | Not found | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| **Outbound search restricted** | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| Yes, `tA` | Yes, another team tB | true | Irrelevant | Irrelevant | Not found | Not found | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| Yes, `tA` | Yes, another team tB | false | `SearchVisibilityNoNameOutsideTeam` | Irrelevant | Found | Not found | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ -| Yes, `tA` | No | false | `SearchVisibilityNoNameOutsideTeam` | There’s no team B | Found | Not found | -+------------------------------------+---------------------------------+------------------------------------+------------------------------------------+-------------------------------------------+----------------------------------+--------------------------------------+ - - -Changing the configuration on the server -........................................ - -To change the `searchSameTeamOnly` setting on the backend, edit the `values.yaml.gotmpl` file for the wire-server chart at this nested level of the configuration: - -.. code:: yaml - - brig: - # ... - config: - # ... - optSettings: - # ... - setSearchSameTeamOnly: true - -If `setSearchSameTeamOnly` is set to `true` then `TeamSearchVisibility` is forced be in the `SearchVisibilityNoNameOutsideTeam` setting for all teams. - -Changing the default configuration for all teams -................................................ - -If `setSearchSameTeamOnly` is set to `false` (or missing from the configuration) then the default value `TeamSearchVisibility` can be configured at this level of the configuration of the `value.yaml.gotmpl` file of the wire-server chart: - - -.. code:: yaml - - galley: - #... - config: - #... - settings: - #... - featureFlags: - #... - teamSearchVisibility: enabled-by-default - -This default value applies to all teams for which no explicit configuration of the `TeamSearchVisibility` has been set. - - -Searching users on another (federated) backend -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For federated search the table above does not apply, see following table. - -.. note:: - - Incoming federated searches (i.e. searches from one backend to another) are considered always as being performed from a team user, even if they are performed from a personal user. - - This is because the incoming search request does not carry the information whether the user performing the search was in a team or not. - - So we have to make one assumption, and we assume that they were in a team. - -Allowing search is done at the backend configuration level by the sysadmin: - -* Outbound search restrictions (`searchSameTeamOnly`, `TeamSearchVisibility`) do not apply to federated searches -* A configuration setting `FederatedUserSearchPolicy` per federating domain with these possible values: - - * `no_search` The federating backend is not allowed to search any users (either by exact handle or full-text). - * `exact_handle_search` The federating backend may only search by exact handle - * `full_search` The federating backend may search users by full text search on display name and handle. The search search results are additionally affected by `SearchVisibilityInbound` setting of each team on the backend. -* The `SearchVisibilityInbound` setting applies. Since the default value for teams is `SearchableByOwnTeam` this means that for a team to be full-text searchable by users on a federating backend both - - * `FederatedUserSearchPolicy` needs to be set to to full_search for the federating backend - * Any team that wants to be full-text searchable needs to be set to `SearchableByAllTeams` - -The configuration value `FederatedUserSearchPolicy` is per federated domain, e.g. in the values of the wire-server chart: - -.. code:: yaml - - brig: - config: - optSettings: - setFederationDomainConfigs: - - domain: a.example.com - search_policy: no_search - - domain: a.example.com - search_policy: full_search - -Table of possible outcomes -.......................... - -In the following table, user `uA` on backend A is searching for user `uB` on team `tB` on backend B. - -Any of the flags set for searching users on the same backend are ignored. - -It’s worth nothing that if two users are on two separate backend, they are also guaranteed to be on two separate teams, as teams can not spread across backends. - -+-------------------------+---------------------------------------------+---------------------------------------------+----------------------------------+--------------------------------------+ -| Who is searching | Backend B flag `FederatedUserSearchPolicy` | Team `tB`'s flag `SearchVisibilityInbound` | Result of exact search for `uB` | Result of full-text search for `uB` | -+=========================+=============================================+=============================================+==================================+======================================+ -| user `uA` on backend A | `no_search` | Irrelevant | Not found | Not found | -+-------------------------+---------------------------------------------+---------------------------------------------+----------------------------------+--------------------------------------+ -| user `uA` on backend A | `exact_handle_search` | Irrelevant | Found | Not found | -+-------------------------+---------------------------------------------+---------------------------------------------+----------------------------------+--------------------------------------+ -| user `uA` on backend A | `full_search` | SearchableByOwnTeam | Found | Not found | -+-------------------------+---------------------------------------------+---------------------------------------------+----------------------------------+--------------------------------------+ -| user `uA` on backend A | `full_search` | SearchableByAllTeams | Found | Found | -+-------------------------+---------------------------------------------+---------------------------------------------+----------------------------------+--------------------------------------+ - -Changing the settings for a given team -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you need to change searchabilility for a specific team (rather than the entire backend, as above), you need to make specific calls to the API. - -Team searchVisibility -..................... - -The team flag `searchVisibility` affects the outbound search of user searches. - -If it is set to `no-name-outside-team` for a team then all users of that team will no longer be able to find users that are not part of their team when searching. - -This also includes finding other users by by providing their exact handle. By default it is set to `standard`, which doesn't put any additional restrictions to outbound searches. - -The setting can be changed via endpoint (for more details on how to make the API calls with `curl`, read further): - -.. code:: - - GET /teams/{tid}/search-visibility - -- Shows the current TeamSearchVisibility value for the given team - - PUT /teams/{tid}/search-visibility - -- Set specific search visibility for the team - - pull-down-menu "body": - "standard" - "no-name-outside-team" - -The team feature flag `teamSearchVisibility` determines whether it is allowed to change the `searchVisibility` setting or not. - -The default is `disabled-by-default`. - -.. note:: - - Whenever this feature setting is disabled the `searchVisibility` will be reset to standard. - -The default setting that applies to all teams on the instance can be defined at configuration - -.. code:: yaml - - settings: - featureFlags: - teamSearchVisibility: disabled-by-default # or enabled-by-default - -TeamFeature searchVisibilityInbound -................................... - -The team feature flag `searchVisibilityInbound` affects if the team's users are searchable by users from other teams. - -The default setting is `searchable-by-own-team` which hides users from search results by users from other teams. - -If it is set to `searchable-by-all-teams` then users of this team may be included in the results of search queries by other users. - -.. note:: - - The configuration of this flag does not affect search results when the search query matches the handle exactly. - - If the handle is provdided then any user on the instance can find users. - -This team feature flag can only by toggled by site-administrators with direct access to the galley instance (for more details on how to make the API calls with `curl`, read further): - -.. code:: - - PUT /i/teams/{tid}/features/search-visibility-inbound - -With JSON body: - -.. code:: json - - {"status": "enabled"} - -or - -.. code:: json - - {"status": "disabled"} - -Where `enabled` is equivalent to `searchable-by-all-teams` and `disabled` is equivalent to `searchable-by-own-team`. - -The default setting that applies to all teams on the instance can be defined at configuration. - -.. code:: yaml - - searchVisibilityInbound: - defaults: - status: enabled # OR disabled - -Individual teams can overwrite the default setting with API calls as per above. - -Making the API calls -.................... - -To make API calls to set an explicit configuration for` TeamSearchVisibilityInbound` per team, you first need to know the Team ID, which can be found in the team settings app. - -It is an `UUID` which has format like this `dcbedf9a-af2a-4f43-9fd5-525953a919e1`. - -In the following we will be using this Team ID as an example, please replace it with your own team id. - -Next find the name of a `galley` pod by looking at the output of running this command: - -.. code:: sh - - kubectl -n wire get pods - -The output will look something like this: - -.. code:: - - ... - galley-5f4787fdc7-9l64n ... - galley-migrate-data-lzz5j ... - ... - -Select any of the galley pods, for example we will use `galley-5f4787fdc7-9l64n`. - -Next, set up a port-forwarding from your local machine's port `9000` to the galley's port `8080` by running: - -.. code:: sh - - kubectl port-forward -n wire galley-5f4787fdc7-9l64n 9000:8080 - -Keep this command running until the end of these instuctions. - -Please run the following commands in a seperate terminal while keeping the terminal which establishes the port-forwarding open. - -To see team's current setting run: - -.. code:: sh - - curl -XGET http://localhost:9000/i/teams/dcbedf9a-af2a-4f43-9fd5-525953a919e1/features/searchVisibilityInbound - - # {"lockStatus":"unlocked","status":"disabled"} - -Where `disabled` corresponds to `SearchableByOwnTeam` and enabled corresponds to `SearchableByAllTeams`. - -To change the `TeamSearchVisibilityInbound` to `SearchableByAllTeams` for the team run: - -.. code:: sh - - curl -XPUT -H 'Content-Type: application/json' -d "{\"status\": \"enabled\"}" http://localhost:9000/i/teams/dcbedf9a-af2a-4f43-9fd5-525953a919e1/features/searchVisibilityInbound - -To change the TeamSearchVisibilityInbound to SearchableByOwnTeam for the team run: - -.. code:: sh - - curl -XPUT -H 'Content-Type: application/json' -d "{\"status\": \"disabled\"}" http://localhost:9000/i/teams/dcbedf9a-af2a-4f43-9fd5-525953a919e1/features/searchVisibilityInbound - - - -Configuring classified domains ------------------------------- - -As a backend administrator, if you want to control which other backends (identified by their domain) are "classified", - -change the following `galley` configuration in the `value.yaml.gotmpl` file of the wire-server chart: - -.. code:: yaml - - galley: - replicaCount: 1 - config: - ... - featureFlags: - ... - classifiedDomains: - status: enabled - config: - domains: ["domain-that-is-classified.link"] - ... - -This is not only a `backend` configuration, but also a `team` configuration/feature. - -This means that different combinations of configurations will have different results. - -Here is a table to navigate the possible configurations: - -+----------------------------------+---------------------------------------------+-------------------------------+------------------------+---------------------------------+ -| Backend Config enabled/disabled | Backend Config Domains | Team Config enabled/disabled | Team Config Domains | User's view | -+==================================+=============================================+===============================+========================+=================================+ -| Enabled | [domain1.example.com] | Not configured | Not configured | Enabled, [domain1.example.com] | -+----------------------------------+---------------------------------------------+-------------------------------+------------------------+---------------------------------+ -| Enabled | [domain1.example.com][domain1.example.com] | Enabled | Not configured | Enabled, [domain1.example.com] | -+----------------------------------+---------------------------------------------+-------------------------------+------------------------+---------------------------------+ -| Enabled | [domain1.example.com] | Enabled | [domain2.example.com] | Enabled, Undefined | -+----------------------------------+---------------------------------------------+-------------------------------+------------------------+---------------------------------+ -| Enabled | [domain1.example.com] | Disabled | Anything | Undefined | -+----------------------------------+---------------------------------------------+-------------------------------+------------------------+---------------------------------+ -| Disabled | Anything | Not configured | Not configured | Disabled, no domains | -+----------------------------------+---------------------------------------------+-------------------------------+------------------------+---------------------------------+ -| Disabled | Anything | Enabled | [domain2.example.com] | Undefined | -+----------------------------------+---------------------------------------------+-------------------------------+------------------------+---------------------------------+ - -The table assumes the following: - -* When backend level config says that this feature is enabled, it is illegal to not specify domains at the backend level. -* When backend level config says that this feature is disabled, the list of domains is ignored. -* When team level feature is disabled, the accompanying domains are ignored. - -S3 Addressing Style -------------------- - -S3 can either by addressed in path style, i.e. -`https:////`, or vhost style, i.e. -`https://./`. AWS's S3 offering has deprecated -path style addressing for S3 and completely disabled it for buckets created -after 30 Sep 2020: -https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/ - -However other object storage providers (specially self-deployed ones like MinIO) -may not support vhost style addressing yet (or ever?). Users of such buckets -should configure this option to "path": - -.. code:: yaml - - cargohold: - aws: - s3AddressingStyle: path - -Installations using S3 service provided by AWS, should use "auto", this option -will ensure that vhost style is only used when it is possible to construct a -valid hostname from the bucket name and the bucket name doesn't contain a '.'. -Having a '.' in the bucket name causes TLS validation to fail, hence it is not -used by default: - -.. code:: yaml - - cargohold: - aws: - s3AddressingStyle: auto - - -Using "virtual" as an option is only useful in situations where vhost style -addressing must be used even if it is not possible to construct a valid hostname -from the bucket name or the S3 service provider can ensure correct certificate -is issued for bucket which contain one or more '.'s in the name: - -.. code:: yaml - - cargohold: - aws: - s3AddressingStyle: virtual - -When this option is unspecified, wire-server defaults to path style addressing -to ensure smooth transition for older deployments. diff --git a/docs/src/how-to/install/dependencies.md b/docs/src/how-to/install/dependencies.md new file mode 100644 index 0000000000..43ad7f7d90 --- /dev/null +++ b/docs/src/how-to/install/dependencies.md @@ -0,0 +1,69 @@ +(dependencies)= + +# Dependencies on operator's machine + +In order to operate a wire-server installation, you'll need a bunch of software +like Ansible, `kubectl` and Helm. + +Together with a matching checkout of the wire-server-deploy repository, +containing the Ansible Roles and Playbooks, you should be good to go. + +Checkout the repository, including its submodules: + +``` +git clone --branch master https://github.com/wireapp/wire-server-deploy.git +cd wire-server-deploy +git submodule update --init --recursive +``` + +We provide a container containing all needed tools for setting up and +interacting with a wire-server cluster. + +Ensure you have Docker >= 20.10.14 installed, as the glibc version used is +incompatible with older container runtimes. + +Your Distro might ship an older version, so best see [how to install docker](https://docker.com). + +To bring the tools in scope, we run the container, and mount the local `wire-server-deploy` +checkout into it. + +Replace the container image tag with the commit id your `wire-server-deploy` +checkout is pointing to. + +``` +WSD_COMMIT_ID=cdc1c84c1a10a4f5f1b77b51ee5655d0da7f9518 # set me +WSD_CONTAINER=quay.io/wire/wire-server-deploy:$WSD_COMMIT_ID + +sudo docker run -it --network=host \ + -v ${SSH_AUTH_SOCK:-nonexistent}:/ssh-agent \ + -v $HOME/.ssh:/root/.ssh \ + -v $PWD:/wire-server-deploy \ + -e SSH_AUTH_SOCK=/ssh-agent \ + $WSD_CONTAINER bash + +# Inside the container +bash-4.4# ansible --version +ansible 2.9.12 +``` + +Once you're in there, you can move on to {ref}`installing kubernetes `. + +## (Alternative) Installing dependencies using Direnv and Nix + +```{warning} +This is an alternative approach to the above "wrapping container" one, which you should only use if you can't get above setup to work. +``` + +1. [Install Nix](https://nixos.org/download.html) +2. [Install Direnv](https://direnv.net/docs/installation.html) +3. [Optionally install the Wire cachix cache to download binaries](https://app.cachix.org/cache/wire-server) + +Now, enabling `direnv` should install all the dependencies and add them to your `PATH`. Every time you `cd` into +the `wire-server-deploy` directory, the right dependencies will be available. + +``` +direnv allow + +ansible --version +ansible 2.9.12 +``` diff --git a/docs/src/how-to/install/dependencies.rst b/docs/src/how-to/install/dependencies.rst deleted file mode 100644 index 1533a113d4..0000000000 --- a/docs/src/how-to/install/dependencies.rst +++ /dev/null @@ -1,74 +0,0 @@ -.. _dependencies: - -Dependencies on operator's machine --------------------------------------------------------------------- - -In order to operate a wire-server installation, you'll need a bunch of software -like Ansible, ``kubectl`` and Helm. - -Together with a matching checkout of the wire-server-deploy repository, -containing the Ansible Roles and Playbooks, you should be good to go. - -Checkout the repository, including its submodules: - -:: - - git clone --branch master https://github.com/wireapp/wire-server-deploy.git - cd wire-server-deploy - git submodule update --init --recursive - - -We provide a container containing all needed tools for setting up and -interacting with a wire-server cluster. - -Ensure you have Docker >= 20.10.14 installed, as the glibc version used is -incompatible with older container runtimes. - -Your Distro might ship an older version, so best see `how to install docker -`__. - -To bring the tools in scope, we run the container, and mount the local ``wire-server-deploy`` -checkout into it. - -Replace the container image tag with the commit id your ``wire-server-deploy`` -checkout is pointing to. - -:: - - WSD_COMMIT_ID=cdc1c84c1a10a4f5f1b77b51ee5655d0da7f9518 # set me - WSD_CONTAINER=quay.io/wire/wire-server-deploy:$WSD_COMMIT_ID - - sudo docker run -it --network=host \ - -v ${SSH_AUTH_SOCK:-nonexistent}:/ssh-agent \ - -v $HOME/.ssh:/root/.ssh \ - -v $PWD:/wire-server-deploy \ - -e SSH_AUTH_SOCK=/ssh-agent \ - $WSD_CONTAINER bash - - # Inside the container - bash-4.4# ansible --version - ansible 2.9.12 - -Once you're in there, you can move on to :ref:`installing kubernetes `. - - -(Alternative) Installing dependencies using Direnv and Nix -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: - - This is an alternative approach to the above "wrapping container" one, which you should only use if you can't get above setup to work. - -1. `Install Nix `__ -2. `Install Direnv `__ -3. `Optionally install the Wire cachix cache to download binaries `__ - -Now, enabling ``direnv`` should install all the dependencies and add them to your ``PATH``. Every time you ``cd`` into -the ``wire-server-deploy`` directory, the right dependencies will be available. - -:: - - direnv allow - - ansible --version - ansible 2.9.12 diff --git a/docs/src/how-to/install/helm-prod.md b/docs/src/how-to/install/helm-prod.md new file mode 100644 index 0000000000..29045f1818 --- /dev/null +++ b/docs/src/how-to/install/helm-prod.md @@ -0,0 +1,208 @@ +(helm-prod)= + +# Installing wire-server (production) components using Helm + +```{note} +Code in this repository should be considered *beta*. As of 2020, we do not (yet) +run our production infrastructure on Kubernetes (but plan to do so soon). +``` + +## Introduction + +The following will install a version of all the wire-server components. These instructions are for reference, and may not set up what you would consider a production environment, due to the fact that there are varying definitions of 'production ready'. These instructions will cover what we consider to be a useful overlap of our users' production needs. They do not cover load balancing/distributing, using multiple datacenters, federating wire, or other forms of intercontinental/interplanetary distribution of the wire service infrastructure. If you deviate from these directions and need to contact us for support, please provide the deviations you made to fit your production environment along with your support request. + +Some of the instructions here will present you with two options: No AWS, and with AWS. The 'No AWS' instructions will not require any AWS infrastructure, but may have a reduced feature set. The 'with AWS' instructions will assume you have completed the setup procedures in {ref}`aws-prod`. + +### What will be installed? + +- wire-server (API) + : - user accounts, authentication, conversations + - assets handling (images, files, ...) + - notifications over websocket +- wire-webapp, a fully functioning web client (like `https://app.wire.com/`) +- wire-account-pages, user account management (a few pages relating to e.g. password reset procedures) + +### What will not be installed? + +- team-settings page +- SSO Capabilities + +Additionally, if you opt to do the 'No AWS' route, you will not get: + +- notifications over native push notifications via [FCM](https://firebase.google.com/docs/cloud-messaging/)/[APNS](https://developer.apple.com/notifications/) + +## Prerequisites + +You need to have access to a Kubernetes cluster running a Kubernetes version , and the `helm` local binary on your PATH. +Your Kubernetes cluster needs to have internal DNS services, so that wire-server can find it's databases. +You need to have docker on the machine you are using to perform this installation with, or a secure data path to a machine that runs docker. You will be using docker to generate security credentials for your wire installation. + +- If you want calling services, you need to have + + - FIXME + +- If you don't have a Kubernetes cluster, you have two options: + + - You can get access to a managed Kubernetes cluster with the cloud provider of your choice. + - You can install one if you have ssh access to a set of sufficiently large virtual machines, see {ref}`ansible-kubernetes` + +- If you don't have `helm` yet, see [Installing helm](https://helm.sh/docs/using_helm/#installing-helm). If you followed the instructions in {ref}`dependencies` should have helm installed already. + +Type `helm version`, you should, if everything is configured correctly, see a result similar this: + +``` +version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"} +``` + +In case `kubectl version` shows both Client and Server versions, but `helm version` does not show a Server version, you may need to run `helm init`. The exact version matters less as long as both Client and Server versions match (or are very close). + +## Preparing to install charts from the internet with Helm + +If your environment is online, you need to add the remote wire Helm repository, to download wire charts. + +To enable the wire charts helm repository: + +```shell +helm repo add wire https://s3-eu-west-1.amazonaws.com/public.wire.com/charts +``` + +(You can see available helm charts by running `helm search repo wire/`. To see +new versions as time passes, you may need to run `helm repo update`) + +Great! Now you can start installing. + +There is a shell script for doing a version of the following procedure with Helm 22. For reference, examine [prod-setup.sh](https://github.com/wireapp/wire-server-deploy/blob/develop/bin/prod-setup.sh). + +## Watching changes as they happen + +Open a terminal and run: + +```shell +kubectl get pods -w +``` + +This will block your terminal and show some things happening as you proceed through this guide. Keep this terminal open and open a second terminal. + +## General installation notes + +```{note} +All helm and kubectl commands below can also take an extra `--namespace ` if you don't want to install into the default Kubernetes namespace. +``` + +## How to install charts that provide access to external databases + +Before you can deploy the helm charts that tell wire where external services +are, you need the 'values' and 'secrets' files for those charts to be +configured. Values and secrets YAML files provide helm charts with the settings +that are installed in Kubernetes. + +Assuming you have followed the procedures in the previous document, the values +and secrets files for cassandra, elasticsearch, and minio (if you are using it) +will have been filled in automatically. If not, examine the +`prod-values.example.yaml` files for each of these services in +values/\/, copy them to `values.yaml`, and then edit them. + +Once the values and secrets files for your databases have been configured, you +have to write a `values/databases-ephemeral/values.yaml` file to tell +databases-ephemeral what external database services you are using, and what +services you want databases-ephemeral to configure. We recommend you use the +'redis' component from this only, as the contents of redis are in fact +ephemeral. Look at the `values/databases-ephemeral/prod-values.example.yaml` +file + +Once you have values and secrets for your environment, open a terminal and run: + +```shell +helm upgrade --install cassandra-external wire/cassandra-external -f values/cassandra-external/values.yaml --wait +helm upgrade --install elasticsearch-external wire/elasticsearch-external -f values/elasticsearch-external/values.yaml --wait +helm upgrade --install databases-ephemeral wire/databases-ephemeral -f values/databases-ephemeral/values.yaml --wait +``` + +If you are using minio instead of AWS S3, you should also run: + +```shell +helm upgrade --install minio-external wire/minio-external -f values/minio-external/values.yaml --wait +``` + +## How to install fake AWS services for SNS / SQS + +AWS SNS is required to send notifications to clients. SQS is used to get notified of any devices that have discontinued using Wire (e.g. if you uninstall the app, the push notification token is removed, and the wire-server will get feedback for that using SQS). + +Note: *for using real SQS for real native push notifications instead, see also :ref:\`pushsns\`.* + +If you use the fake-aws version, clients will use the websocket method to receive notifications, which keeps connections to the servers open, draining battery. + +Open a terminal and run: + +```shell +cp values/fake-aws/prod-values.example.yaml values/fake-aws/values.yaml +helm upgrade --install fake-aws wire/fake-aws -f values/fake-aws/values.yaml --wait +``` + +You should see some pods being created in your first terminal as the above command completes. + +## Preparing to install wire-server + +As part of configuring wire-server, we need to change some values, and provide some secrets. We're going to copy the files for this to a new folder, so that you always have the originals for reference. + +```{note} +This part of the process makes use of overrides for helm charts. You may wish to read {ref}`understand-helm-overrides` first. +``` + +```shell +mkdir -p my-wire-server +cp values/wire-server/prod-secrets.example.yaml my-wire-server/secrets.yaml +cp values/wire-server/prod-values.example.yaml my-wire-server/values.yaml +``` + +## How to configure real SMTP (email) services + +In order for users to interact with their wire account, they need to receive mail from your wire server. + +If you are using a mail server, you will need to provide your authentication credentials before setting up wire. + +- Add your SMTP username in my-wire-server/values.yaml under `brig.config.smtp.username`. You may need to add an entry for username. +- Add your SMTP password is my-wire-server/secrets.yaml under `brig.secrets.smtpPassword`. + +## How to install fake SMTP (email) services + +If you are not making use of mail services, and are adding your users via some other means, you can use demo-smtp, as a placeholder. + +```shell +cp values/demo-smtp/prod-values.example.yaml values/demo-smtp/values.yaml +helm upgrade --install smtp wire/demo-smtp -f values/demo-smtp/values.yaml +``` + +You should see some pods being created in your first terminal as the above command completes. + +## How to install wire-server itself + +Open `my-wire-server/values.yaml` and replace `example.com` and other domains and subdomains with domains of your choosing. Look for the `# change this` comments. You can try using `sed -i 's/example.com//g' values.yaml`. + +1. If you are not using team settings, comment out `teamSettings` under `brig.config.externalURLs`. + +Generate some secrets: + +```shell +openssl rand -base64 64 | env LC_CTYPE=C tr -dc a-zA-Z0-9 | head -c 42 > my-wire-server/restund.txt +apt install docker-ce +sudo docker run --rm quay.io/wire/alpine-intermediate /dist/zauth -m gen-keypair -i 1 > my-wire-server/zauth.txt +``` + +1. Add the generated secret from my-wire-server/restund.txt to my-wire-serwer/secrets.yaml under `brig.secrets.turn.secret` +2. add **both** the public and private parts from zauth.txt to secrets.yaml under `brig.secrets.zAuth` +3. Add the public key from zauth.txt to secrets.yaml under `nginz.secrets.zAuth.publicKeys` + +Great, now try the installation: + +```shell +helm upgrade --install wire-server wire/wire-server -f my-wire-server/values.yaml -f my-wire-server/secrets.yaml --wait +``` + +(helmdns)= + +## DNS records + +```{eval-rst} +.. include:: includes/helm_dns-ingress-troubleshooting.inc.rst +``` diff --git a/docs/src/how-to/install/helm-prod.rst b/docs/src/how-to/install/helm-prod.rst deleted file mode 100644 index a6690a768a..0000000000 --- a/docs/src/how-to/install/helm-prod.rst +++ /dev/null @@ -1,225 +0,0 @@ -.. _helm-prod: - -Installing wire-server (production) components using Helm -========================================================= - -.. note:: - - Code in this repository should be considered *beta*. As of 2020, we do not (yet) - run our production infrastructure on Kubernetes (but plan to do so soon). - -Introduction ------------- - -The following will install a version of all the wire-server components. These instructions are for reference, and may not set up what you would consider a production environment, due to the fact that there are varying definitions of 'production ready'. These instructions will cover what we consider to be a useful overlap of our users' production needs. They do not cover load balancing/distributing, using multiple datacenters, federating wire, or other forms of intercontinental/interplanetary distribution of the wire service infrastructure. If you deviate from these directions and need to contact us for support, please provide the deviations you made to fit your production environment along with your support request. - -Some of the instructions here will present you with two options: No AWS, and with AWS. The 'No AWS' instructions will not require any AWS infrastructure, but may have a reduced feature set. The 'with AWS' instructions will assume you have completed the setup procedures in :ref:`aws-prod`. - -What will be installed? -^^^^^^^^^^^^^^^^^^^^^^^ - -- wire-server (API) - - user accounts, authentication, conversations - - assets handling (images, files, ...) - - notifications over websocket -- wire-webapp, a fully functioning web client (like ``https://app.wire.com/``) -- wire-account-pages, user account management (a few pages relating to e.g. password reset procedures) - -What will not be installed? -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- team-settings page -- SSO Capabilities - -Additionally, if you opt to do the 'No AWS' route, you will not get: - -- notifications over native push notifications via `FCM `__/`APNS `__ - -Prerequisites -------------- - -You need to have access to a Kubernetes cluster running a Kubernetes version , and the ``helm`` local binary on your PATH. -Your Kubernetes cluster needs to have internal DNS services, so that wire-server can find it's databases. -You need to have docker on the machine you are using to perform this installation with, or a secure data path to a machine that runs docker. You will be using docker to generate security credentials for your wire installation. - -* If you want calling services, you need to have - - * FIXME - -* If you don't have a Kubernetes cluster, you have two options: - - * You can get access to a managed Kubernetes cluster with the cloud provider of your choice. - * You can install one if you have ssh access to a set of sufficiently large virtual machines, see :ref:`ansible-kubernetes` - -* If you don't have ``helm`` yet, see `Installing helm `__. If you followed the instructions in :ref:`dependencies` should have helm installed already. - - -Type ``helm version``, you should, if everything is configured correctly, see a result similar this: - -:: - - version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"} - -In case ``kubectl version`` shows both Client and Server versions, but ``helm version`` does not show a Server version, you may need to run ``helm init``. The exact version matters less as long as both Client and Server versions match (or are very close). - - -Preparing to install charts from the internet with Helm -------------------------------------------------------- -If your environment is online, you need to add the remote wire Helm repository, to download wire charts. - -To enable the wire charts helm repository: - -.. code:: shell - - helm repo add wire https://s3-eu-west-1.amazonaws.com/public.wire.com/charts - -(You can see available helm charts by running ``helm search repo wire/``. To see -new versions as time passes, you may need to run ``helm repo update``) - -Great! Now you can start installing. - -There is a shell script for doing a version of the following procedure with Helm 22. For reference, examine `prod-setup.sh `__. - -Watching changes as they happen -------------------------------- - -Open a terminal and run: - -.. code:: shell - - kubectl get pods -w - -This will block your terminal and show some things happening as you proceed through this guide. Keep this terminal open and open a second terminal. - -General installation notes --------------------------- - -.. note:: - - All helm and kubectl commands below can also take an extra ``--namespace `` if you don't want to install into the default Kubernetes namespace. - -How to install charts that provide access to external databases ---------------------------------------------------------------- - -Before you can deploy the helm charts that tell wire where external services -are, you need the 'values' and 'secrets' files for those charts to be -configured. Values and secrets YAML files provide helm charts with the settings -that are installed in Kubernetes. - -Assuming you have followed the procedures in the previous document, the values -and secrets files for cassandra, elasticsearch, and minio (if you are using it) -will have been filled in automatically. If not, examine the -``prod-values.example.yaml`` files for each of these services in -values//, copy them to ``values.yaml``, and then edit them. - -Once the values and secrets files for your databases have been configured, you -have to write a ``values/databases-ephemeral/values.yaml`` file to tell -databases-ephemeral what external database services you are using, and what -services you want databases-ephemeral to configure. We recommend you use the -'redis' component from this only, as the contents of redis are in fact -ephemeral. Look at the ``values/databases-ephemeral/prod-values.example.yaml`` -file - -Once you have values and secrets for your environment, open a terminal and run: - -.. code:: shell - - helm upgrade --install cassandra-external wire/cassandra-external -f values/cassandra-external/values.yaml --wait - helm upgrade --install elasticsearch-external wire/elasticsearch-external -f values/elasticsearch-external/values.yaml --wait - helm upgrade --install databases-ephemeral wire/databases-ephemeral -f values/databases-ephemeral/values.yaml --wait - -If you are using minio instead of AWS S3, you should also run: - -.. code:: shell - - helm upgrade --install minio-external wire/minio-external -f values/minio-external/values.yaml --wait - -How to install fake AWS services for SNS / SQS ----------------------------------------------- - -AWS SNS is required to send notifications to clients. SQS is used to get notified of any devices that have discontinued using Wire (e.g. if you uninstall the app, the push notification token is removed, and the wire-server will get feedback for that using SQS). - -Note: *for using real SQS for real native push notifications instead, see also :ref:`pushsns`.* - -If you use the fake-aws version, clients will use the websocket method to receive notifications, which keeps connections to the servers open, draining battery. - -Open a terminal and run: - -.. code:: shell - - cp values/fake-aws/prod-values.example.yaml values/fake-aws/values.yaml - helm upgrade --install fake-aws wire/fake-aws -f values/fake-aws/values.yaml --wait - -You should see some pods being created in your first terminal as the above command completes. - - -Preparing to install wire-server --------------------------------- -As part of configuring wire-server, we need to change some values, and provide some secrets. We're going to copy the files for this to a new folder, so that you always have the originals for reference. - -.. note:: - - This part of the process makes use of overrides for helm charts. You may wish to read :ref:`understand-helm-overrides` first. - - -.. code:: shell - - mkdir -p my-wire-server - cp values/wire-server/prod-secrets.example.yaml my-wire-server/secrets.yaml - cp values/wire-server/prod-values.example.yaml my-wire-server/values.yaml - - -How to configure real SMTP (email) services -------------------------------------------- -In order for users to interact with their wire account, they need to receive mail from your wire server. - -If you are using a mail server, you will need to provide your authentication credentials before setting up wire. - -- Add your SMTP username in my-wire-server/values.yaml under ``brig.config.smtp.username``. You may need to add an entry for username. -- Add your SMTP password is my-wire-server/secrets.yaml under ``brig.secrets.smtpPassword``. - - -How to install fake SMTP (email) services ------------------------------------------ -If you are not making use of mail services, and are adding your users via some other means, you can use demo-smtp, as a placeholder. - -.. code:: shell - - cp values/demo-smtp/prod-values.example.yaml values/demo-smtp/values.yaml - helm upgrade --install smtp wire/demo-smtp -f values/demo-smtp/values.yaml - - -You should see some pods being created in your first terminal as the above command completes. - -How to install wire-server itself ---------------------------------- - -Open ``my-wire-server/values.yaml`` and replace ``example.com`` and other domains and subdomains with domains of your choosing. Look for the ``# change this`` comments. You can try using ``sed -i 's/example.com//g' values.yaml``. - -1. If you are not using team settings, comment out ``teamSettings`` under ``brig.config.externalURLs``. - - -Generate some secrets: - -.. code:: shell - - openssl rand -base64 64 | env LC_CTYPE=C tr -dc a-zA-Z0-9 | head -c 42 > my-wire-server/restund.txt - apt install docker-ce - sudo docker run --rm quay.io/wire/alpine-intermediate /dist/zauth -m gen-keypair -i 1 > my-wire-server/zauth.txt - -1. Add the generated secret from my-wire-server/restund.txt to my-wire-serwer/secrets.yaml under ``brig.secrets.turn.secret`` -2. add **both** the public and private parts from zauth.txt to secrets.yaml under ``brig.secrets.zAuth`` -3. Add the public key from zauth.txt to secrets.yaml under ``nginz.secrets.zAuth.publicKeys`` - -Great, now try the installation: - -.. code:: shell - - helm upgrade --install wire-server wire/wire-server -f my-wire-server/values.yaml -f my-wire-server/secrets.yaml --wait - -.. _helmdns: - -DNS records ------------ - -.. include:: includes/helm_dns-ingress-troubleshooting.inc.rst diff --git a/docs/src/how-to/install/helm.md b/docs/src/how-to/install/helm.md new file mode 100644 index 0000000000..75ce93eda2 --- /dev/null +++ b/docs/src/how-to/install/helm.md @@ -0,0 +1,145 @@ +(helm)= + +# Installing wire-server (demo) components using helm + +## Introduction + +The following will install a demo version of all the wire-server components including the databases. This setup is not recommended in production but will get you started. + +Demo version means + +- easy setup - only one single machine with kubernetes is needed (make sure you have at least 4 CPU cores and 8 GB of memory available) +- no data persistence (everything stored in memory, will be lost) + +### What will be installed? + +- wire-server (API) + \- user accounts, authentication, conversations + \- assets handling (images, files, ...) + \- notifications over websocket +- wire-webapp, a fully functioning web client (like `https://app.wire.com`) +- wire-account-pages, user account management (a few pages relating to e.g. password reset) + +### What will not be installed? + +- notifications over native push notifications via [FCM](https://firebase.google.com/docs/cloud-messaging/)/[APNS](https://developer.apple.com/notifications/) +- audio/video calling servers using {ref}`understand-restund`) +- team-settings page + +## Prerequisites + +You need to have access to a kubernetes cluster, and the `helm` local binary on your PATH. + +- If you don't have a kubernetes cluster, you have two options: + + - You can get access to a managed kubernetes cluster with the cloud provider of your choice. + - You can install one if you have ssh access to a virtual machine, see {ref}`ansible-kubernetes` + +- If you don't have `helm` yet, see [Installing helm](https://helm.sh/docs/using_helm/#installing-helm). + +Type `helm version`, you should, if everything is configured correctly, see a result like this: + +``` +version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"} +``` + +In case `kubectl version` shows both Client and Server versions, but `helm version` does not show a Server version, you may need to run `helm init`. The exact version (assuming `v2.X.X` - at the time of writing v3 is not yet supported) matters less as long as both Client and Server versions match (or are very close). + +## How to start installing charts from wire + +Enable the wire charts helm repository: + +```shell +helm repo add wire https://s3-eu-west-1.amazonaws.com/public.wire.com/charts +``` + +(You can see available helm charts by running `helm search repo wire/`. To see +new versions as time passes, you may need to run `helm repo update`) + +Great! Now you can start installing. + +```{note} +all commands below can also take an extra `--namespace ` if you don't want to install into the default kubernetes namespace. +``` + +## Watching changes as they happen + +Open a terminal and run + +```shell +kubectl get pods -w +``` + +This will block your terminal and show some things happening as you proceed through this guide. Keep this terminal open and open a second terminal. + +## How to install in-memory databases and external components + +In your second terminal, first install databases: + +```shell +helm upgrade --install databases-ephemeral wire/databases-ephemeral --wait +``` + +You should see some pods being created in your first terminal as the above command completes. + +You can do the following two steps (mock aws services and demo smtp +server) in parallel with the above in two more terminals, or +sequentially after database-ephemeral installation has succeeded. + +```shell +helm upgrade --install fake-aws wire/fake-aws --wait +helm upgrade --install smtp wire/demo-smtp --wait +``` + +## How to install wire-server itself + +```{note} +The following makes use of overrides for helm charts. You may wish to read {ref}`understand-helm-overrides` first. +``` + +Change back to the wire-server-deploy directory. Copy example demo values and secrets: + +```shell +mkdir -p wire-server && cd wire-server +cp ../values/wire-server/demo-secrets.example.yaml secrets.yaml +cp ../values/wire-server/demo-values.example.yaml values.yaml +``` + +Or, if you are not in wire-server-deploy, download example demo values and secrets: + +```shell +mkdir -p wire-server && cd wire-server +curl -sSL https://raw.githubusercontent.com/wireapp/wire-server-deploy/master/values/wire-server/demo-secrets.example.yaml > secrets.yaml +curl -sSL https://raw.githubusercontent.com/wireapp/wire-server-deploy/master/values/wire-server/demo-values.example.yaml > values.yaml +``` + +Open `values.yaml` and replace `example.com` and other domains and subdomains with domains of your choosing. Look for the `# change this` comments. You can try using `sed -i 's/example.com//g' values.yaml`. + +Generate some secrets (if you are using the docker image from {ref}`ansible-kubernetes`, you should open a shell on the host system for this): + +```shell +openssl rand -base64 64 | env LC_CTYPE=C tr -dc a-zA-Z0-9 | head -c 42 > restund.txt +docker run --rm quay.io/wire/alpine-intermediate /dist/zauth -m gen-keypair -i 1 > zauth.txt +``` + +1. Add the generated secret from restund.txt to secrets.yaml under `brig.secrets.turn.secret` +2. add **both** the public and private parts from zauth.txt to secrets.yaml under `brig.secrets.zAuth` +3. Add the public key from zauth.txt **also** to secrets.yaml under `nginz.secrets.zAuth.publicKeys` + +You can do this with an editor, or using sed: + +```shell +sed -i 's/secret:$/secret: content_of_restund.txt_file/' secrets.yaml +sed -i 's/publicKeys: ""/publicKeys: "public_key_from_zauth.txt_file"/' secrets.yaml +sed -i 's/privateKeys: ""/privateKeys: "private_key_from_zauth.txt_file"/' secrets.yaml +``` + +Great, now try the installation: + +```shell +helm upgrade --install wire-server wire/wire-server -f values.yaml -f secrets.yaml --wait +``` + +```{eval-rst} +.. include:: includes/helm_dns-ingress-troubleshooting.inc.rst +``` diff --git a/docs/src/how-to/install/helm.rst b/docs/src/how-to/install/helm.rst deleted file mode 100644 index 695a4c95a3..0000000000 --- a/docs/src/how-to/install/helm.rst +++ /dev/null @@ -1,154 +0,0 @@ -.. _helm: - -Installing wire-server (demo) components using helm -====================================================== - -Introduction ------------------ - -The following will install a demo version of all the wire-server components including the databases. This setup is not recommended in production but will get you started. - -Demo version means - -* easy setup - only one single machine with kubernetes is needed (make sure you have at least 4 CPU cores and 8 GB of memory available) -* no data persistence (everything stored in memory, will be lost) - -What will be installed? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- wire-server (API) - - user accounts, authentication, conversations - - assets handling (images, files, ...) - - notifications over websocket - -- wire-webapp, a fully functioning web client (like ``https://app.wire.com``) -- wire-account-pages, user account management (a few pages relating to e.g. password reset) - -What will not be installed? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- notifications over native push notifications via `FCM `__/`APNS `__ -- audio/video calling servers using :ref:`understand-restund`) -- team-settings page - -Prerequisites --------------------------------- - -You need to have access to a kubernetes cluster, and the ``helm`` local binary on your PATH. - -* If you don't have a kubernetes cluster, you have two options: - - * You can get access to a managed kubernetes cluster with the cloud provider of your choice. - * You can install one if you have ssh access to a virtual machine, see :ref:`ansible-kubernetes` - -* If you don't have ``helm`` yet, see `Installing helm `__. - -Type ``helm version``, you should, if everything is configured correctly, see a result like this: - -:: - - version.BuildInfo{Version:"v3.1.1", GitCommit:"afe70585407b420d0097d07b21c47dc511525ac8", GitTreeState:"clean", GoVersion:"go1.13.8"} - - -In case ``kubectl version`` shows both Client and Server versions, but ``helm version`` does not show a Server version, you may need to run ``helm init``. The exact version (assuming `v2.X.X` - at the time of writing v3 is not yet supported) matters less as long as both Client and Server versions match (or are very close). - -How to start installing charts from wire --------------------------------------------------- - -Enable the wire charts helm repository: - -.. code:: shell - - helm repo add wire https://s3-eu-west-1.amazonaws.com/public.wire.com/charts - -(You can see available helm charts by running ``helm search repo wire/``. To see -new versions as time passes, you may need to run ``helm repo update``) - -Great! Now you can start installing. - -.. note:: - - all commands below can also take an extra ``--namespace `` if you don't want to install into the default kubernetes namespace. - -Watching changes as they happen --------------------------------------------------- - -Open a terminal and run - -.. code:: shell - - kubectl get pods -w - -This will block your terminal and show some things happening as you proceed through this guide. Keep this terminal open and open a second terminal. - -How to install in-memory databases and external components --------------------------------------------------------------- - -In your second terminal, first install databases: - -.. code:: shell - - helm upgrade --install databases-ephemeral wire/databases-ephemeral --wait - -You should see some pods being created in your first terminal as the above command completes. - -You can do the following two steps (mock aws services and demo smtp -server) in parallel with the above in two more terminals, or -sequentially after database-ephemeral installation has succeeded. - -.. code:: shell - - helm upgrade --install fake-aws wire/fake-aws --wait - helm upgrade --install smtp wire/demo-smtp --wait - -How to install wire-server itself ---------------------------------------- - -.. note:: - - The following makes use of overrides for helm charts. You may wish to read :ref:`understand-helm-overrides` first. - -Change back to the wire-server-deploy directory. Copy example demo values and secrets: - -.. code:: shell - - mkdir -p wire-server && cd wire-server - cp ../values/wire-server/demo-secrets.example.yaml secrets.yaml - cp ../values/wire-server/demo-values.example.yaml values.yaml - -Or, if you are not in wire-server-deploy, download example demo values and secrets: - -.. code:: shell - - mkdir -p wire-server && cd wire-server - curl -sSL https://raw.githubusercontent.com/wireapp/wire-server-deploy/master/values/wire-server/demo-secrets.example.yaml > secrets.yaml - curl -sSL https://raw.githubusercontent.com/wireapp/wire-server-deploy/master/values/wire-server/demo-values.example.yaml > values.yaml - -Open ``values.yaml`` and replace ``example.com`` and other domains and subdomains with domains of your choosing. Look for the ``# change this`` comments. You can try using ``sed -i 's/example.com//g' values.yaml``. - -Generate some secrets (if you are using the docker image from :ref:`ansible-kubernetes`, you should open a shell on the host system for this): - -.. code:: shell - - openssl rand -base64 64 | env LC_CTYPE=C tr -dc a-zA-Z0-9 | head -c 42 > restund.txt - docker run --rm quay.io/wire/alpine-intermediate /dist/zauth -m gen-keypair -i 1 > zauth.txt - -1. Add the generated secret from restund.txt to secrets.yaml under ``brig.secrets.turn.secret`` -2. add **both** the public and private parts from zauth.txt to secrets.yaml under ``brig.secrets.zAuth`` -3. Add the public key from zauth.txt **also** to secrets.yaml under ``nginz.secrets.zAuth.publicKeys`` - -You can do this with an editor, or using sed: - -.. code:: shell - - sed -i 's/secret:$/secret: content_of_restund.txt_file/' secrets.yaml - sed -i 's/publicKeys: ""/publicKeys: "public_key_from_zauth.txt_file"/' secrets.yaml - sed -i 's/privateKeys: ""/privateKeys: "private_key_from_zauth.txt_file"/' secrets.yaml - -Great, now try the installation: - -.. code:: shell - - helm upgrade --install wire-server wire/wire-server -f values.yaml -f secrets.yaml --wait - -.. include:: includes/helm_dns-ingress-troubleshooting.inc.rst diff --git a/docs/src/how-to/install/index.md b/docs/src/how-to/install/index.md new file mode 100644 index 0000000000..183215c603 --- /dev/null +++ b/docs/src/how-to/install/index.md @@ -0,0 +1,30 @@ +# Installing wire-server + +```{toctree} +:glob: true +:maxdepth: 2 + +How to plan an installation +Version requirements +dependencies +(demo) How to install kubernetes +(demo) How to install wire-server using Helm +(production) Introduction +(production) How to install kubernetes and databases +(production) How to configure AWS services +(production) How to install wire-server using Helm +(production) How to monitor wire-server +(production) How to see centralized logs for wire-server +(production) Other configuration options +Server and team feature settings +Messaging Layer Security (MLS) +Web app settings +sft +restund +configure-federation +tls +How to install and set up Legal Hold +Managing authentication with ansible +Using tinc +Troubleshooting during installation +``` diff --git a/docs/src/how-to/install/index.rst b/docs/src/how-to/install/index.rst deleted file mode 100644 index aa45f4b41a..0000000000 --- a/docs/src/how-to/install/index.rst +++ /dev/null @@ -1,30 +0,0 @@ -Installing wire-server -======================= - -.. toctree:: - :maxdepth: 2 - :glob: - - How to plan an installation - Version requirements - dependencies - (demo) How to install kubernetes - (demo) How to install wire-server using Helm - (production) Introduction - (production) How to install kubernetes and databases - (production) How to configure AWS services - (production) How to install wire-server using Helm - (production) How to monitor wire-server - (production) How to see centralized logs for wire-server - (production) Other configuration options - Server and team feature settings - Messaging Layer Security (MLS) - Web app settings - sft - restund - configure-federation - tls - How to install and set up Legal Hold - Managing authentication with ansible - Using tinc - Troubleshooting during installation diff --git a/docs/src/how-to/install/kubernetes.md b/docs/src/how-to/install/kubernetes.md new file mode 100644 index 0000000000..1c4430eefd --- /dev/null +++ b/docs/src/how-to/install/kubernetes.md @@ -0,0 +1,85 @@ +(ansible-kubernetes)= + +# Installing kubernetes for a demo installation (on a single virtual machine) + +## How to set up your hosts.ini file + +Assuming a single virtual machine with a public IP address running Ubuntu 18.04, with at least 5 CPU cores and at least 8 GB of memory. + +Move to `wire-server-deploy/ansible`: + +```shell +cd ansible/ +``` + +Then: + +```{eval-rst} +.. include:: includes/ansible-authentication-blob.rst +``` + +## Passwordless authentication + +Presuming a fresh default Ubuntu 18.04 installation, the following steps will enable the Ansible playbook to run without specifying passwords. + +This presumes you named your default Ubuntu user "wire", and X.X.X.X is the IP or domain name of the target server Ansible will install Kubernetes on. + +On the client (from `wire-server-deploy/ansible`), run: + +```shell +ssh-keygen -f /root/.ssh/id_rsa -t rsa -P +ssh-copy-id wire@X.X.X.X +sed -i 's/# ansible_user = .../ansible_user = wire/g' inventory/demo/hosts.ini +``` + +And on the server (X.X.X.X), run: + +```shell +echo 'wire ALL=(ALL) NOPASSWD:ALL' | sudo tee -a /etc/sudoers +``` + +Then on the client: + +```shell +cp inventory/demo/hosts.example.ini inventory/demo/hosts.ini +``` + +Open hosts.ini and replace `X.X.X.X` with the IP address of your virtual machine that you use for ssh access. You can try using: + +```shell +sed -i 's/X.X.X.X/1.2.3.4/g' inventory/demo/hosts.ini +``` + +## Minio setup + +In the `inventory/demo/hosts.ini` file, edit the minio variables in `[minio:vars]` (`prefix`, `domain` and `deeplink_title`) +by replacing `example.com` with your own domain. + +## How to install kubernetes + +From `wire-server-deploy/ansible`: + +``` +ansible-playbook -i inventory/demo/hosts.ini kubernetes.yml -vv +``` + +When the playbook finishes correctly (which can take up to 20 minutes), you should have a folder `artifacts` containing a file `admin.conf`. Copy this file: + +``` +mkdir -p ~/.kube +cp artifacts/admin.conf ~/.kube/config +KUBECONFIG=~/.kube/config +``` + +Make sure you can reach the server: + +``` +kubectl version +``` + +should give output similar to this: + +``` +Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.2", GitCommit:"66049e3b21efe110454d67df4fa62b08ea79a19b", GitTreeState:"clean", BuildDate:"2019-05-16T16:23:09Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd64"} +Server Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.2", GitCommit:"66049e3b21efe110454d67df4fa62b08ea79a19b", GitTreeState:"clean", BuildDate:"2019-05-16T16:14:56Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd64"} +``` diff --git a/docs/src/how-to/install/kubernetes.rst b/docs/src/how-to/install/kubernetes.rst deleted file mode 100644 index d4e423dfa4..0000000000 --- a/docs/src/how-to/install/kubernetes.rst +++ /dev/null @@ -1,83 +0,0 @@ -.. _ansible-kubernetes: - -Installing kubernetes for a demo installation (on a single virtual machine) -============================================================================ - - -How to set up your hosts.ini file -------------------------------------- - -Assuming a single virtual machine with a public IP address running Ubuntu 18.04, with at least 5 CPU cores and at least 8 GB of memory. - -Move to ``wire-server-deploy/ansible``: - -.. code:: shell - - cd ansible/ - -Then: - -.. include:: includes/ansible-authentication-blob.rst - -Passwordless authentication ---------------------------- - -Presuming a fresh default Ubuntu 18.04 installation, the following steps will enable the Ansible playbook to run without specifying passwords. - -This presumes you named your default Ubuntu user "wire", and X.X.X.X is the IP or domain name of the target server Ansible will install Kubernetes on. - -On the client (from ``wire-server-deploy/ansible``), run: - -.. code:: shell - - ssh-keygen -f /root/.ssh/id_rsa -t rsa -P - ssh-copy-id wire@X.X.X.X - sed -i 's/# ansible_user = .../ansible_user = wire/g' inventory/demo/hosts.ini - -And on the server (X.X.X.X), run: - -.. code:: shell - - echo 'wire ALL=(ALL) NOPASSWD:ALL' | sudo tee -a /etc/sudoers - -Then on the client: - -.. code:: shell - - cp inventory/demo/hosts.example.ini inventory/demo/hosts.ini - -Open hosts.ini and replace `X.X.X.X` with the IP address of your virtual machine that you use for ssh access. You can try using: - -.. code:: shell - - sed -i 's/X.X.X.X/1.2.3.4/g' inventory/demo/hosts.ini - -Minio setup ------------ - -In the ``inventory/demo/hosts.ini`` file, edit the minio variables in ``[minio:vars]`` (``prefix``, ``domain`` and ``deeplink_title``) -by replacing ``example.com`` with your own domain. - -How to install kubernetes --------------------------- - -From ``wire-server-deploy/ansible``:: - - ansible-playbook -i inventory/demo/hosts.ini kubernetes.yml -vv - -When the playbook finishes correctly (which can take up to 20 minutes), you should have a folder ``artifacts`` containing a file ``admin.conf``. Copy this file:: - - mkdir -p ~/.kube - cp artifacts/admin.conf ~/.kube/config - KUBECONFIG=~/.kube/config - -Make sure you can reach the server:: - - kubectl version - -should give output similar to this:: - - Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.2", GitCommit:"66049e3b21efe110454d67df4fa62b08ea79a19b", GitTreeState:"clean", BuildDate:"2019-05-16T16:23:09Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd64"} - Server Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.2", GitCommit:"66049e3b21efe110454d67df4fa62b08ea79a19b", GitTreeState:"clean", BuildDate:"2019-05-16T16:14:56Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd64"} - - diff --git a/docs/src/how-to/install/logging.rst b/docs/src/how-to/install/logging.md similarity index 60% rename from docs/src/how-to/install/logging.rst rename to docs/src/how-to/install/logging.md index dc2808f1d1..ca4ea9341d 100644 --- a/docs/src/how-to/install/logging.rst +++ b/docs/src/how-to/install/logging.md @@ -1,182 +1,164 @@ -.. _logging: +(logging)= -Installing centralized logging dashboards using Kibana -======================================================== +# Installing centralized logging dashboards using Kibana -Introduction ------------- +## Introduction This page shows you how to install Elasticsearch, Kibana, and fluent-bit to aggregate and visualize the logs from wire-server components. -Status -------- +## Status Logging support is in active development as of September 2019, some logs may not be visible yet, and certain parts are not fully automated yet. -Prerequisites -------------- +## Prerequisites You need to have wire-server installed, see either of -* :ref:`helm` -* :ref:`helm-prod`. +- {ref}`helm` +- {ref}`helm-prod`. +## Installing required helm charts -Installing required helm charts --------------------------------- - - -Deploying Elasticsearch -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Deploying Elasticsearch Elasticsearch indexes the logs and makes them searchable. The following elasticsearch-ephemeral chart makes use of the disk space the pod happens to run on. -:: - - $ helm install --namespace wire/elasticsearch-ephemeral +``` +$ helm install --namespace wire/elasticsearch-ephemeral +``` Note that since we are not specifying a release name during helm install, it generates a 'verb-noun' pair, and uses it. Elasticsearch's chart does not use the release name of the helm chart in the pod name, sadly. -Deploying Elasticsearch-Curator -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Deploying Elasticsearch-Curator Elasticsearch-curator trims the logs that are contained in elasticsearch, so that your elasticsearch pod does not get too large, crash, and need to be re-built. -:: - - $ helm install --namespace wire/elasticsearch-curator +``` +$ helm install --namespace wire/elasticsearch-curator +``` Note that since we are not specifying a release name during helm install, it generates a 'verb-noun' pair, and uses it. If you look at your pod names, you can see this name prepended to your pods in 'kubectl -n get pods'. -Deploying Kibana -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:: +### Deploying Kibana - $ helm install --namespace wire/kibana +``` +$ helm install --namespace wire/kibana +``` Note that since we are not specifying a release name during helm install, it generates a 'verb-noun' pair, and uses it. If you look at your pod names, you can see this name prepended to your pods in 'kubectl -n get pods'. -Deploying fluent-bit -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:: +### Deploying fluent-bit - $ helm install --namespace wire/fluent-bit +``` +$ helm install --namespace wire/fluent-bit +``` -Configuring fluent-bit ----------------------- +## Configuring fluent-bit -.. note:: +```{note} +The following makes use of overrides for helm charts. You may wish to read {ref}`understand-helm-overrides` first. +``` - The following makes use of overrides for helm charts. You may wish to read :ref:`understand-helm-overrides` first. - -Per pod-template, you can specify what parsers ``fluent-bit`` needs to +Per pod-template, you can specify what parsers `fluent-bit` needs to use to interpret the pod's logs in a structured way. By default, it just parses them as plain text. But, you can change this using a pod annotation. E.g.: -:: - - apiVersion: v1 - kind: Pod - metadata: - name: brig - labels: - app: brig - annotations: - fluentbit.io/parser: json - spec: - containers: - - name: apache - image: edsiper/apache_logs - -You can also define your own custom parsers in our ``fluent-bit`` -chart's ``values.yml``. For example, we have one defined for ``nginz``. +``` +apiVersion: v1 +kind: Pod +metadata: + name: brig + labels: + app: brig + annotations: + fluentbit.io/parser: json +spec: + containers: + - name: apache + image: edsiper/apache_logs +``` + +You can also define your own custom parsers in our `fluent-bit` +chart's `values.yml`. For example, we have one defined for `nginz`. For more info, see : -https://github.com/fluent/fluent-bit-docs/blob/master/filter/kubernetes.md#kubernetes-annotations + Alternately, if there is already fluent-bit deployed in your environment, get the helm name for the deployment (verb-noun prepended to the pod name), and -:: - - $ helm upgrade --namespace wire/fluent-bit +``` +$ helm upgrade --namespace wire/fluent-bit +``` Note that since we are not specifying a release name during helm install, it generates a 'verb-noun' pair, and uses it. if you look at your pod names, you can see this name prepended to your pods in 'kubectl -n get pods'. -.. _post-install-kibana-setup: +(post-install-kibana-setup)= -Post-install kibana setup --------------------------- +## Post-install kibana setup Get the pod name for your kibana instance (not the one set up with fluent-bit), and -:: - - $ kubectl -n port-forward 5601:5601 +``` +$ kubectl -n port-forward 5601:5601 +``` go to 127.0.0.1:5601 in your web browser. 1. Click on 'discover'. -2. Use ``kubernetes_cluster-*`` as the Index pattern. +2. Use `kubernetes_cluster-*` as the Index pattern. 3. Click on 'Next step' 4. Click on the 'Time Filter field name' dropdown, and select - '@timestamp'. + '. 5. Click on 'create index patern'. - -Usage after installation -------------------------- +## Usage after installation Get the pod name for your kibana instance (not the one set up with fluent-bit), and -:: - - $ kubectl -n port-forward 5601:5601 +``` +$ kubectl -n port-forward 5601:5601 +``` Go to 127.0.0.1:5601 in your web browser. Click on 'discover' to view data. -.. _nuking-it-all: +(nuking-it-all)= -Nuking it all. --------------- +## Nuking it all. -Find the names of the helm releases for your pods (look at ``helm ls --all`` -and ``kubectl -n get pods`` , and run -``helm del --purge`` for each of them. +Find the names of the helm releases for your pods (look at `helm ls --all` +and `kubectl -n get pods` , and run +`helm del --purge` for each of them. Note: Elasticsearch does not use the name of the helm chart, and therefore is harder to identify. -Debugging ---------- - -:: +## Debugging - kubectl -n logs +``` +kubectl -n logs +``` -How this was developed -^^^^^^^^^^^^^^^^^^^^^^^^ +### How this was developed First, we deployed elasticsearch with the elasticsearch-ephemeral chart, then kibana. then we deployed fluent-bit, which set up a kibana of it's diff --git a/docs/src/how-to/install/monitoring.rst b/docs/src/how-to/install/monitoring.md similarity index 58% rename from docs/src/how-to/install/monitoring.rst rename to docs/src/how-to/install/monitoring.md index ea900526cc..18f5a8865b 100644 --- a/docs/src/how-to/install/monitoring.rst +++ b/docs/src/how-to/install/monitoring.md @@ -1,21 +1,19 @@ -.. _monitoring: +(monitoring)= -Monitoring wire-server using Prometheus and Grafana -======================================================= +# Monitoring wire-server using Prometheus and Grafana All wire-server helm charts offering prometheus metrics expose a `metrics.serviceMonitor.enabled` option. If these are set to true, the helm charts will install `ServiceMonitor` resources, which can be used to mark services for scraping by -[Prometheus Operator](https://prometheus-operator.dev/), -[Grafana Agent Operator](https://grafana.com/docs/grafana-cloud/kubernetes-monitoring/agent-k8s/), +\[Prometheus Operator\](), +\[Grafana Agent Operator\](), or similar prometheus-compatible tools. Refer to their documentation for installation. -Dashboards ------------------ +## Dashboards -Grafana dashboard configurations are included as JSON inside the ``dashboards`` +Grafana dashboard configurations are included as JSON inside the `dashboards` directory. You may import these via Grafana's web UI. diff --git a/docs/src/how-to/install/planning.rst b/docs/src/how-to/install/planning.md similarity index 55% rename from docs/src/how-to/install/planning.rst rename to docs/src/how-to/install/planning.md index eae18fb9a1..1c3b1a5f44 100644 --- a/docs/src/how-to/install/planning.rst +++ b/docs/src/how-to/install/planning.md @@ -1,10 +1,8 @@ -Implementation plan -==================================== +# Implementation plan There are two types of implementation: demo and production. -Demo installation (trying functionality out) ------------------------------------------------ +## Demo installation (trying functionality out) Please note that there is no way to migrate data from a demo installation to a production installation - it is really meant as a way @@ -14,36 +12,36 @@ Please note your data will be in-memory only and may disappear at any given mome What you need: -- a way to create **DNS records** for your domain name (e.g. - ``wire.example.com``) -- a way to create **SSL/TLS certificates** for your domain name (to allow - connecting via ``https://``) -- Either one of the following: +- a way to create **DNS records** for your domain name (e.g. + `wire.example.com`) - - A kubernetes cluster (some cloud providers offer a managed - kubernetes cluster these days). - - One single virtual machine running ubuntu 18.04 with at least 20 GB of disk, 8 GB of memory, and 8 CPU cores. +- a way to create **SSL/TLS certificates** for your domain name (to allow + connecting via `https://`) -A demo installation will look a bit like this: +- Either one of the following: + + - A kubernetes cluster (some cloud providers offer a managed + kubernetes cluster these days). + - One single virtual machine running ubuntu 18.04 with at least 20 GB of disk, 8 GB of memory, and 8 CPU cores. -.. figure:: img/architecture-demo.png +A demo installation will look a bit like this: - Demo installation (1 VM) +```{figure} img/architecture-demo.png +Demo installation (1 VM) +``` -Next steps for demo installation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Next steps for demo installation -If you already have a kubernetes cluster, your next step will be :ref:`helm`, otherwise, your next step will be :ref:`ansible-kubernetes` +If you already have a kubernetes cluster, your next step will be {ref}`helm`, otherwise, your next step will be {ref}`ansible-kubernetes` -.. _planning-prod: +(planning-prod)= -Production installation (persistent data, high-availability) --------------------------------------------------------------- +## Production installation (persistent data, high-availability) What you need: -- a way to create **DNS records** for your domain name (e.g. ``wire.example.com``) -- a way to create **SSL/TLS certificates** for your domain name (to allow connecting via ``https://wire.example.com``) +- a way to create **DNS records** for your domain name (e.g. `wire.example.com`) +- a way to create **SSL/TLS certificates** for your domain name (to allow connecting via `https://wire.example.com`) - A **kubernetes cluster with at least 3 worker nodes and at least 3 etcd nodes** (some cloud providers offer a managed kubernetes cluster these days) - minimum **17 virtual machines** for components outside kubernetes (cassandra, minio, elasticsearch, redis, restund) @@ -51,13 +49,15 @@ A recommended installation of Wire-server in any regular data centre, configured with high-availability will require the following virtual servers: +```{eval-rst} .. include:: includes/vm-table.rst +``` A production installation will look a bit like this: -.. figure:: img/architecture-server-ha.png - - Production installation in High-Availability mode +```{figure} img/architecture-server-ha.png +Production installation in High-Availability mode +``` If you use a private datacenter (not a cloud provider), the easiest is to have three physical servers, each with one virtual machine for each @@ -71,7 +71,6 @@ Ensure that your VMs have IP addresses that do not change. Avoid `10.x.x.x` network address schemes, and instead use something like `192.168.x.x` or `172.x.x.x`. This is because internally, Kubernetes already uses a `10.x.x.x` address scheme, creating a potential conflict. -Next steps for high-available production installation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Next steps for high-available production installation -Your next step will be :ref:`ansible-vms` +Your next step will be {ref}`ansible-vms` diff --git a/docs/src/how-to/install/prod-intro.md b/docs/src/how-to/install/prod-intro.md new file mode 100644 index 0000000000..a908c14c30 --- /dev/null +++ b/docs/src/how-to/install/prod-intro.md @@ -0,0 +1,58 @@ +# Introduction + +```{warning} +It is *strongly recommended* to have followed and completed the demo installation {ref}`helm` before continuing with this page. The demo installation is simpler, and already makes you aware of a few things you need (TLS certs, DNS, a VM, ...). +``` + +```{note} +All required dependencies for doing an installation can be found here {ref}`dependencies`. +``` + +A production installation consists of several parts: + +Part 1 - you're on your own here, and need to create a set of VMs as detailed in {ref}`planning-prod` + +Part 2 ({ref}`ansible-vms`) deals with installing components directly on a set of virtual machines, such as kubernetes itself, as well as databases. It makes use of ansible to achieve that. + +Part 3 ({ref}`helm-prod`) is similar to the demo installation, and uses the tool `helm` to install software on top of kubernetes. + +Part 4 ({ref}`configuration-options`) details other possible configuration options and settings to fit your needs. + +## What will be installed by following these parts? + +- highly-available and persistent databases (cassandra, elasticsearch) + +- kubernetes + +- restund (audio/video calling) servers ( see also {ref}`understand-restund`) + +- wire-server (API) + \- user accounts, authentication, conversations + \- assets handling (images, files, ...) + \- notifications over websocket + \- single-sign-on with SAML + +- wire-webapp + + - fully functioning web client (like `https://app.wire.com`) + +- wire-account-pages + + - user account management (a few pages relating to e.g. password reset) + +## What will not be installed? + +- notifications over native push notification via [FCM](https://firebase.google.com/docs/cloud-messaging/)/[APNS](https://developer.apple.com/notifications/) + +## What will not be installed by default? + +- 3rd party proxying - requires accounts with third-party providers +- team-settings page for team management (including invitations, requires access to a private repository - get in touch with us for access) + +## Getting support + +[Get in touch](https://wire.com/pricing/). + +## Next steps for high-available production installation + +Your next step will be part 2, {ref}`ansible-vms` diff --git a/docs/src/how-to/install/prod-intro.rst b/docs/src/how-to/install/prod-intro.rst deleted file mode 100644 index ed56b4ebcd..0000000000 --- a/docs/src/how-to/install/prod-intro.rst +++ /dev/null @@ -1,60 +0,0 @@ -Introduction -============= - -.. warning:: - - It is *strongly recommended* to have followed and completed the demo installation :ref:`helm` before continuing with this page. The demo installation is simpler, and already makes you aware of a few things you need (TLS certs, DNS, a VM, ...). - -.. note:: - All required dependencies for doing an installation can be found here :ref:`dependencies`. - -A production installation consists of several parts: - -Part 1 - you're on your own here, and need to create a set of VMs as detailed in :ref:`planning-prod` - -Part 2 (:ref:`ansible-vms`) deals with installing components directly on a set of virtual machines, such as kubernetes itself, as well as databases. It makes use of ansible to achieve that. - -Part 3 (:ref:`helm-prod`) is similar to the demo installation, and uses the tool ``helm`` to install software on top of kubernetes. - -Part 4 (:ref:`configuration-options`) details other possible configuration options and settings to fit your needs. - -What will be installed by following these parts? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- highly-available and persistent databases (cassandra, elasticsearch) -- kubernetes -- restund (audio/video calling) servers ( see also :ref:`understand-restund`) -- wire-server (API) - - user accounts, authentication, conversations - - assets handling (images, files, ...) - - notifications over websocket - - single-sign-on with SAML - -- wire-webapp - - - fully functioning web client (like ``https://app.wire.com``) - -- wire-account-pages - - - user account management (a few pages relating to e.g. password reset) - -What will not be installed? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- notifications over native push notification via `FCM `__/`APNS `__ - -What will not be installed by default? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -- 3rd party proxying - requires accounts with third-party providers -- team-settings page for team management (including invitations, requires access to a private repository - get in touch with us for access) - -Getting support -^^^^^^^^^^^^^^^^ - -`Get in touch `__. - -Next steps for high-available production installation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Your next step will be part 2, :ref:`ansible-vms` diff --git a/docs/src/how-to/install/restund.md b/docs/src/how-to/install/restund.md new file mode 100644 index 0000000000..90a616b105 --- /dev/null +++ b/docs/src/how-to/install/restund.md @@ -0,0 +1,80 @@ +(install-restund)= + +# Installing Restund + +## Background + +Restund servers allow two users on different networks to have a Wire audio or video call. + +Please refer to the following {ref}`section to better understand Restund and how it works `. + +## Installation instructions + +To Install Restund, do the following: + +1. In your `hosts.ini` file, in the `[restund:vars]` section, set + the `restund_network_interface` to the name of the interface + you want restund to talk to clients on. This value defaults to the + `default_ipv4_address`, with a fallback to `eth0`. +2. (optional) `restund_peer_udp_advertise_addr=Y.Y.Y.Y`: set this to + the IP to advertise for other restund servers if different than the + ip on the 'restund_network_interface'. If using + 'restund_peer_udp_advertise_addr', make sure that UDP (!) traffic + from any restund server (including itself) can reach that IP (for + `restund <-> restund` communication). This should only be necessary + if you're installing restund on a VM that is reachable on a public IP + address but the process cannot bind to that public IP address + directly (e.g. on AWS VPC VM). If unset, `restund <-> restund` UDP + traffic will default to the IP in the `restund_network_interface`. + +```ini +[all] +(...) +restund01 ansible_host=X.X.X.X + +(...) + +[all:vars] +## Set the network interface name for restund to bind to if you have more than one network interface +## If unset, defaults to the ansible_default_ipv4 (if defined) otherwise to eth0 +restund_network_interface = eth0 + +(see `defaults/main.yml `__ for a full list of variables to change if necessary) +``` + +3. Place a copy of the PEM formatted certificate and key you are going + to use for TLS communication to the restund server in + `/tmp/tls_cert_and_priv_key.pem`. Remove it after you have + completed deploying restund with ansible. +4. Use Ansible to actually install using the restund playbook: + +```bash +ansible-playbook -i hosts.ini restund.yml -vv +``` + +For information on setting up and using ansible-playbook to install Wire components, see {ref}`this page `. + +### Private Subnets + +By default, Restund is configured with a firewall that filters-out CIDR networks. + +If you need to enable Restund to connect to a CIDR addressed host or network, you can specify a list of private subnets in [CIDR format](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing), which will override Restund's firewall's default settings of filtering-out CIDR networks. + +You do this by setting the `restund_allowed_private_network_cidrs` option of the `[restund:vars]` section of the ansible inventory file ([for example this file](https://github.com/wireapp/wire-server-deploy/blob/master/ansible/inventory/prod/hosts.example.ini#L72)): + +```ini +[restund:vars] +## Set the network interface name for restund to bind to if you have more than one network interface +## If unset, defaults to the ansible_default_ipv4 (if defined) otherwise to eth0 +# restund_network_interface = eth0 +restund_allowed_private_network_cidrs=192.168.0.1/32 +``` + +This is needed, for example, to allow talking to the logging server if it is on a separate network: + +The private subnets only need to override the RFC-defined private networks, which Wire firewalls off by default: + +- 192.168.x.x +- 10.x.x.x +- 172.16.x.x - 172.31.x.x +- Etc... diff --git a/docs/src/how-to/install/restund.rst b/docs/src/how-to/install/restund.rst deleted file mode 100644 index dd586c4916..0000000000 --- a/docs/src/how-to/install/restund.rst +++ /dev/null @@ -1,88 +0,0 @@ -.. _install-restund: - -Installing Restund -================== - -Background -~~~~~~~~~~ - -Restund servers allow two users on different networks to have a Wire audio or video call. - -Please refer to the following :ref:`section to better understand Restund and how it works `. - -Installation instructions -~~~~~~~~~~~~~~~~~~~~~~~~~ - -To Install Restund, do the following: - - -1. In your ``hosts.ini`` file, in the ``[restund:vars]`` section, set - the ``restund_network_interface`` to the name of the interface - you want restund to talk to clients on. This value defaults to the - ``default_ipv4_address``, with a fallback to ``eth0``. - -2. (optional) ``restund_peer_udp_advertise_addr=Y.Y.Y.Y``: set this to - the IP to advertise for other restund servers if different than the - ip on the 'restund_network_interface'. If using - 'restund_peer_udp_advertise_addr', make sure that UDP (!) traffic - from any restund server (including itself) can reach that IP (for - ``restund <-> restund`` communication). This should only be necessary - if you're installing restund on a VM that is reachable on a public IP - address but the process cannot bind to that public IP address - directly (e.g. on AWS VPC VM). If unset, ``restund <-> restund`` UDP - traffic will default to the IP in the ``restund_network_interface``. - -.. code:: ini - - [all] - (...) - restund01 ansible_host=X.X.X.X - - (...) - - [all:vars] - ## Set the network interface name for restund to bind to if you have more than one network interface - ## If unset, defaults to the ansible_default_ipv4 (if defined) otherwise to eth0 - restund_network_interface = eth0 - - (see `defaults/main.yml `__ for a full list of variables to change if necessary) - -3. Place a copy of the PEM formatted certificate and key you are going - to use for TLS communication to the restund server in - ``/tmp/tls_cert_and_priv_key.pem``. Remove it after you have - completed deploying restund with ansible. - -4. Use Ansible to actually install using the restund playbook: - -.. code:: bash - - ansible-playbook -i hosts.ini restund.yml -vv - -For information on setting up and using ansible-playbook to install Wire components, see :ref:`this page `. - -Private Subnets ---------------- - -By default, Restund is configured with a firewall that filters-out CIDR networks. - -If you need to enable Restund to connect to a CIDR addressed host or network, you can specify a list of private subnets in `CIDR format `__, which will override Restund's firewall's default settings of filtering-out CIDR networks. - -You do this by setting the ``restund_allowed_private_network_cidrs`` option of the ``[restund:vars]`` section of the ansible inventory file (`for example this file `__): - -.. code:: ini - - [restund:vars] - ## Set the network interface name for restund to bind to if you have more than one network interface - ## If unset, defaults to the ansible_default_ipv4 (if defined) otherwise to eth0 - # restund_network_interface = eth0 - restund_allowed_private_network_cidrs=192.168.0.1/32 - -This is needed, for example, to allow talking to the logging server if it is on a separate network: - -The private subnets only need to override the RFC-defined private networks, which Wire firewalls off by default: - -* 192.168.x.x -* 10.x.x.x -* 172.16.x.x - 172.31.x.x -* Etc... - diff --git a/docs/src/how-to/install/sft.rst b/docs/src/how-to/install/sft.md similarity index 67% rename from docs/src/how-to/install/sft.rst rename to docs/src/how-to/install/sft.md index caffb71863..e4560c7216 100644 --- a/docs/src/how-to/install/sft.rst +++ b/docs/src/how-to/install/sft.md @@ -1,125 +1,116 @@ -.. _install-sft: +(install-sft)= -Installing Conference Calling 2.0 (aka SFT) -=========================================== +# Installing Conference Calling 2.0 (aka SFT) -Background -~~~~~~~~~~ +## Background -Please refer to the following :ref:`section to better understand SFT and how it works `. +Please refer to the following {ref}`section to better understand SFT and how it works `. +### As part of the wire-server umbrella chart -As part of the wire-server umbrella chart ------------------------------------------ +`` sftd` `` will be installed as part of the `wire-server` umbrella chart if you set `tags.sftd: true` -`sftd`` will be installed as part of the ``wire-server`` umbrella chart if you set `tags.sftd: true` +In your `./values/wire-server/values.yaml` file you should set the following settings: -In your ``./values/wire-server/values.yaml`` file you should set the following settings: +```yaml +tags: + sftd: true -.. code:: yaml +sftd: + host: sftd.example.com # Replace example.com with your domain + allowOrigin: webapp.example.com # Should be the address you used for the webapp deployment +``` - tags: - sftd: true +In your `secrets.yaml` you should set the TLS keys for sftd domain: - sftd: - host: sftd.example.com # Replace example.com with your domain - allowOrigin: webapp.example.com # Should be the address you used for the webapp deployment +```yaml +sftd: + tls: + crt: | + + key: | + +``` -In your ``secrets.yaml`` you should set the TLS keys for sftd domain: +You should also make sure that you configure brig to know about the SFT server in your `./values/wire-server/values.yaml` file: -.. code:: yaml - - sftd: - tls: - crt: | - - key: | - - -You should also make sure that you configure brig to know about the SFT server in your ``./values/wire-server/values.yaml`` file: - -.. code:: yaml - - brig: - optSettings: - setSftStaticUrl: "https://sftd.example.com:443" +```yaml +brig: + optSettings: + setSftStaticUrl: "https://sftd.example.com:443" +``` Now you can deploy as usual: -.. code:: shell +```shell +helm upgrade wire-server wire/wire-server --values ./values/wire-server/values.yaml +``` - helm upgrade wire-server wire/wire-server --values ./values/wire-server/values.yaml - - -Standalone ----------- +### Standalone The SFT component is also shipped as a separate helm chart. Installation is similar to installing -the charts as in :ref:`helm-prod`. +the charts as in {ref}`helm-prod`. Some people might want to run SFT separately, because the deployment lifecycle for the SFT is a bit more intricate. For example, -if you want to avoid dropping calls during an upgrade, you'd set the ``terminationGracePeriodSeconds`` of the SFT to a high number, to wait -for calls to drain before updating to the new version (See `technical documentation `__). that would cause your otherwise snappy upgrade of the ``wire-server`` chart to now take a long time, as it waits for all -the SFT servers to drain. If this is a concern for you, we advice installing ``sftd`` as a separate chart. - -It is important that you disable ``sftd`` in the ``wire-server`` umbrella chart, by setting this in your ``./values/wire-server/values.yaml`` file +if you want to avoid dropping calls during an upgrade, you'd set the `terminationGracePeriodSeconds` of the SFT to a high number, to wait +for calls to drain before updating to the new version (See [technical documentation](https://github.com/wireapp/wire-server/blob/develop/charts/sftd/README.md)). that would cause your otherwise snappy upgrade of the `wire-server` chart to now take a long time, as it waits for all +the SFT servers to drain. If this is a concern for you, we advice installing `sftd` as a separate chart. -.. code:: yaml +It is important that you disable `sftd` in the `wire-server` umbrella chart, by setting this in your `./values/wire-server/values.yaml` file - tags: - sftd: false +```yaml +tags: + sftd: false +``` +By default `sftd` doesn't need to set that many options, so we define them inline. However, you could of course also set these values in a `values.yaml` file. -By default ``sftd`` doesn't need to set that many options, so we define them inline. However, you could of course also set these values in a ``values.yaml`` file. +SFT will deploy a Kubernetes Ingress on `$SFTD_HOST`. Make sure that the domain name `$SFTD_HOST` points to your ingress IP as set up in {ref}`helm-prod`. The SFT also needs to be made aware of the domain name of the webapp that you set up in {ref}`helm-prod` for setting up the appropriate CSP headers. -SFT will deploy a Kubernetes Ingress on ``$SFTD_HOST``. Make sure that the domain name ``$SFTD_HOST`` points to your ingress IP as set up in :ref:`helm-prod`. The SFT also needs to be made aware of the domain name of the webapp that you set up in :ref:`helm-prod` for setting up the appropriate CSP headers. - -.. code:: shell - - export SFTD_HOST=sftd.example.com - export WEBAPP_HOST=webapp.example.com +```shell +export SFTD_HOST=sftd.example.com +export WEBAPP_HOST=webapp.example.com +``` Now you can install the chart: -.. code:: shell - - helm upgrade --install sftd wire/sftd --set - helm install sftd wire/sftd \ - --set host=$SFTD_HOST \ - --set allowOrigin=https://$WEBAPP_HOST \ - --set-file tls.crt=/path/to/tls.crt \ - --set-file tls.key=/path/to/tls.key - -You should also make sure that you configure brig to know about the SFT server, in the ``./values/wire-server/values.yaml`` file: +```shell +helm upgrade --install sftd wire/sftd --set +helm install sftd wire/sftd \ + --set host=$SFTD_HOST \ + --set allowOrigin=https://$WEBAPP_HOST \ + --set-file tls.crt=/path/to/tls.crt \ + --set-file tls.key=/path/to/tls.key +``` -.. code:: yaml +You should also make sure that you configure brig to know about the SFT server, in the `./values/wire-server/values.yaml` file: - brig: - optSettings: - setSftStaticUrl: "https://sftd.example.com:443" +```yaml +brig: + optSettings: + setSftStaticUrl: "https://sftd.example.com:443" +``` -And then roll-out the change to the ``wire-server`` chart +And then roll-out the change to the `wire-server` chart -.. code:: shell +```shell +helm upgrade wire-server wire/wire-server --values ./values/wire-server/values.yaml +``` - helm upgrade wire-server wire/wire-server --values ./values/wire-server/values.yaml +For more advanced setups please refer to the [technical documentation](https://github.com/wireapp/wire-server/blob/develop/charts/sftd/README.md). -For more advanced setups please refer to the `technical documentation `__. +(install-sft-firewall-rules)= +### Firewall rules -.. _install-sft-firewall-rules: - -Firewall rules --------------- - -The SFT allocates media addresses in the UDP :ref:`default port range `. Ingress and +The SFT allocates media addresses in the UDP {ref}`default port range `. Ingress and egress traffic should be allowed for this range. Furthermore the SFT needs to be -able to reach the :ref:`Restund server `, as it uses STUN and TURN in cases the client +able to reach the {ref}`Restund server `, as it uses STUN and TURN in cases the client can not directly connect to the SFT. In practise this means the SFT should -allow ingress and egress traffic on the UDP :ref:`default port range ` from and -to both, clients and :ref:`Restund servers `. +allow ingress and egress traffic on the UDP {ref}`default port range ` from and +to both, clients and {ref}`Restund servers `. -*For more information on this port range, how to read and change it, and how to configure your firewall, please see* :ref:`this note `. +*For more information on this port range, how to read and change it, and how to configure your firewall, please see* {ref}`this note `. The SFT also has an HTTP interface for initializing (allocation) or joining (signaling) a call. This is exposed through the ingress controller as an HTTPS service. @@ -131,6 +122,7 @@ An SFT instance does **not** communicate with other SFT instances, TURN does tal Recapitulation table: +```{eval-rst} +----------------------------+-------------+-------------+-----------+----------+-----------------------------------------------------------------------------+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Name | Origin | Destination | Direction | Protocol | Ports | Action (Policy) | Description | +============================+=============+=============+===========+==========+=============================================================================+======================================+===============================================================================================================================================================================================+ @@ -146,6 +138,6 @@ Recapitulation table: +----------------------------+-------------+-------------+-----------+----------+-----------------------------------------------------------------------------+--------------------------------------+ | | Allowing SFT media egress | Here | Anny | Outgoing | UDP | 32768-61000 | Allow | | +----------------------------+-------------+-------------+-----------+----------+-----------------------------------------------------------------------------+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +``` - -*For more information, please refer to the source code of the Ansible role:* `sft-server `__. +*For more information, please refer to the source code of the Ansible role:* [sft-server](https://github.com/wireapp/ansible-sft/blob/develop/roles/sft-server/tasks/traffic.yml). diff --git a/docs/src/how-to/install/tls.md b/docs/src/how-to/install/tls.md new file mode 100644 index 0000000000..f3a044597a --- /dev/null +++ b/docs/src/how-to/install/tls.md @@ -0,0 +1,52 @@ +(tls)= + +# Configure TLS ciphers + +The following table lists recommended ciphers for TLS server setups, which should be used in wire deployments. + +| Cipher | Version | Wire default | [BSI TR-02102-2] | [Mozilla TLS Guideline] | +| ----------------------------- | ------- | ------------ | ---------------- | ----------------------- | +| ECDHE-ECDSA-AES128-GCM-SHA256 | TLSv1.2 | no | **yes** | intermediate | +| ECDHE-RSA-AES128-GCM-SHA256 | TLSv1.2 | no | **yes** | intermediate | +| ECDHE-ECDSA-AES256-GCM-SHA384 | TLSv1.2 | **yes** | **yes** | intermediate | +| ECDHE-RSA-AES256-GCM-SHA384 | TLSv1.2 | **yes** | **yes** | intermediate | +| ECDHE-ECDSA-CHACHA20-POLY1305 | TLSv1.2 | no | no | intermediate | +| ECDHE-RSA-CHACHA20-POLY1305 | TLSv1.2 | no | no | intermediate | +| TLS_AES_128_GCM_SHA256 | TLSv1.3 | **yes** | **yes** | **modern** | +| TLS_AES_256_GCM_SHA384 | TLSv1.3 | **yes** | **yes** | **modern** | +| TLS_CHACHA20_POLY1305_SHA256 | TLSv1.3 | no | no | **modern** | + +```{note} +If you enable TLSv1.3, openssl does always enable the three default cipher suites for TLSv1.3. +Therefore it is not necessary to add them to openssl based configurations. +``` + +(ingress-traffic)= + +## Ingress Traffic (wire-server) + +The list of TLS ciphers for incoming requests is limited by default to the [following](https://github.com/wireapp/wire-server/blob/master/charts/nginx-ingress-controller/values.yaml#L7) (for general server-certificates, both for federation and client API), and can be overridden on your installation if needed. + +## Egress Traffic (wire-server/federation) + +The list of TLS ciphers for outgoing federation requests is currently hardcoded, the list is [here](https://github.com/wireapp/wire-server/blob/master/services/federator/src/Federator/Remote.hs#L164-L180). + +## SFTD (ansible) + +The list of TLS ciphers for incoming SFT requests (and metrics) are defined in ansible templates [sftd.vhost.conf.j2](https://github.com/wireapp/ansible-sft/blob/develop/roles/sft-server/templates/sftd.vhost.conf.j2#L19) and [metrics.vhost.conf.j2](https://github.com/wireapp/ansible-sft/blob/develop/roles/sft-server/templates/metrics.vhost.conf.j2#L13). + +## SFTD (kubernetes) + +SFTD deployed via kubernetes uses `kubernetes.io/ingress` for ingress traffic, configured in [ingress.yaml](https://github.com/wireapp/wire-server/blob/develop/charts/sftd/templates/ingress.yaml). +Kubernetes based deployments make use of the settings from {ref}`ingress-traffic`. + +## Restund (ansible) + +The list of TLS ciphers for "TLS over TCP" TURN (and metrics) are defined in ansible templates [nginx-stream.conf.j2](https://github.com/wireapp/ansible-restund/blob/master/templates/nginx-stream.conf.j2#L25) and [nginx-metrics.conf.j2](https://github.com/wireapp/ansible-restund/blob/master/templates/nginx-metrics.conf.j2#L15). + +## Restund (kubernetes) + +[Kubernetes restund](https://github.com/wireapp/wire-server/tree/develop/charts/restund) deployment does not provide TLS connectivity. + +[bsi tr-02102-2]: https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TG02102/BSI-TR-02102-2.pdf +[mozilla tls guideline]: https://wiki.mozilla.org/Security/Server_Side_TLS diff --git a/docs/src/how-to/install/tls.rst b/docs/src/how-to/install/tls.rst deleted file mode 100644 index b726cf8446..0000000000 --- a/docs/src/how-to/install/tls.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. _tls: - -Configure TLS ciphers -======================= - -The following table lists recommended ciphers for TLS server setups, which should be used in wire deployments. - - -============================= ======= ============ ================= ======================== -Cipher Version Wire default `BSI TR-02102-2`_ `Mozilla TLS Guideline`_ -============================= ======= ============ ================= ======================== -ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 no **yes** intermediate -ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 no **yes** intermediate -ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 **yes** **yes** intermediate -ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 **yes** **yes** intermediate -ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 no no intermediate -ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 no no intermediate -TLS_AES_128_GCM_SHA256 TLSv1.3 **yes** **yes** **modern** -TLS_AES_256_GCM_SHA384 TLSv1.3 **yes** **yes** **modern** -TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 no no **modern** -============================= ======= ============ ================= ======================== - - -.. _bsi tr-02102-2: https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TG02102/BSI-TR-02102-2.pdf -.. _mozilla tls guideline: https://wiki.mozilla.org/Security/Server_Side_TLS - -.. note:: - If you enable TLSv1.3, openssl does always enable the three default cipher suites for TLSv1.3. - Therefore it is not necessary to add them to openssl based configurations. - -.. _ingress-traffic: - -Ingress Traffic (wire-server) ------------------------------ -The list of TLS ciphers for incoming requests is limited by default to the `following `_ (for general server-certificates, both for federation and client API), and can be overridden on your installation if needed. - - -Egress Traffic (wire-server/federation) ---------------------------------------- -The list of TLS ciphers for outgoing federation requests is currently hardcoded, the list is `here `_. - - -SFTD (ansible) --------------- -The list of TLS ciphers for incoming SFT requests (and metrics) are defined in ansible templates `sftd.vhost.conf.j2 `_ and `metrics.vhost.conf.j2 `_. - -SFTD (kubernetes) ------------------ -SFTD deployed via kubernetes uses ``kubernetes.io/ingress`` for ingress traffic, configured in `ingress.yaml `_. -Kubernetes based deployments make use of the settings from :ref:`ingress-traffic`. - - -Restund (ansible) ------------------ - -The list of TLS ciphers for "TLS over TCP" TURN (and metrics) are defined in ansible templates `nginx-stream.conf.j2 `_ and `nginx-metrics.conf.j2 `_. - -Restund (kubernetes) --------------------- -`Kubernetes restund `_ deployment does not provide TLS connectivity. diff --git a/docs/src/how-to/install/troubleshooting.md b/docs/src/how-to/install/troubleshooting.md new file mode 100644 index 0000000000..7aa9f80479 --- /dev/null +++ b/docs/src/how-to/install/troubleshooting.md @@ -0,0 +1,265 @@ +# Troubleshooting during installation + +## Problems with CORS on the web based applications (webapp, team-settings, account-pages) + +If you have installed wire-server, but the web application page in your browser has connection problems and throws errors in the console such as `"Refused to connect to 'https://assets.example.com' because it violates the following Content Security Policies"`, make sure to check that you have configured the `CSP_EXTRA_` environment variables. + +In the file that you use as override when running `helm install/update -f ` (using the webapp as an example): + +```yaml +webapp: + # ... other settings... + envVars: + # ... other environment variables ... + CSP_EXTRA_CONNECT_SRC: "https://*.example.com, wss://*.example.com" + CSP_EXTRA_IMG_SRC: "https://*.example.com" + CSP_EXTRA_SCRIPT_SRC: "https://*.example.com" + CSP_EXTRA_DEFAULT_SRC: "https://*.example.com" + CSP_EXTRA_FONT_SRC: "https://*.example.com" + CSP_EXTRA_FRAME_SRC: "https://*.example.com" + CSP_EXTRA_MANIFEST_SRC: "https://*.example.com" + CSP_EXTRA_OBJECT_SRC: "https://*.example.com" + CSP_EXTRA_MEDIA_SRC: "https://*.example.com" + CSP_EXTRA_PREFETCH_SRC: "https://*.example.com" + CSP_EXTRA_STYLE_SRC: "https://*.example.com" + CSP_EXTRA_WORKER_SRC: "https://*.example.com" +``` + +For more info, you can have a look at respective charts values files, i.e.: + +> - [charts/account-pages/values.yaml](https://github.com/wireapp/wire-server/blob/develop/charts/account-pages/values.yaml) +> - [charts/team-settings/values.yaml](https://github.com/wireapp/wire-server/blob/develop/charts/team-settings/values.yaml) +> - [charts/webapp/values.yaml](https://github.com/wireapp/wire-server/blob/develop/charts/webapp/values.yaml) + +## Problems with ansible and python versions + +If for instance the following fails: + +``` +ansible all -i hosts.ini -m shell -a "echo hello" +``` + +If your target machine only has python 3 (not python 2.7), you can tell ansible to use python 3 by default, by specifying `ansible_python_interpreter`: + +```ini +# hosts.ini + +[all] +server1 ansible_host=1.2.3.4 + + +[all:vars] +ansible_python_interpreter=/usr/bin/python3 +``` + +(python 3 may not be supported by all ansible modules yet) + +## Flaky issues with Cassandra (failed QUORUMs, etc.) + +Cassandra is *very* picky about time! Ensure that NTP is properly set up on all nodes. Particularly for Cassandra *DO NOT* use anything else other than ntp. Here are some helpful blogs that explain why: + +> - +> - +> - + +How can I ensure that I have correctly setup NTP on my machine(s)? Have a look at [this ansible playbook](https://github.com/wireapp/wire-server-deploy/blob/develop/ansible/cassandra-verify-ntp.yml) + +## I deployed `demo-smtp` but I'm not receiving any verification emails + +1. Check whether brig deployed successfully (brig pod(s) should be in state *Running*) + + ``` + kubectl get pods -o wide + ``` + +2. Inspect Brig logs + + ``` + kubectl logs $BRING_POD_NAME + ``` + +3. The receiving email server might refuse to accept any email sent by the `demo-smtp` server, due to not being + a trusted origin. You may want to set up one of the following email verification mechanisms. + +- [SFP](https://en.wikipedia.org/wiki/Sender_Policy_Framework) +- [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) +- [DMARC](https://en.wikipedia.org/wiki/DMARC) + +4. You may want to adjust the SMTP configuration for Brig (`wire-server/[values,secrets].yaml`). + +```yaml +brig: + config: + smtp: + host: 'demo-smtp' + port: 25 + connType: 'plain' +``` + +```yaml +brig: + secrets: + smtpPassword: dummyPassword +``` + +(Don't forget to apply the changes with `helm upgrade wire-server wire/wire-server -f values.yaml -f secrets.yaml`) + +## I deployed `demo-smtp` and I want to skip email configuration and retrieve verification codes directly + +If the only thing you need demo-smtp for is sending yourself verification codes to create a test account, it might be simpler and faster to just skip SMTP configuration, and simply retrieve the code internally right after it is sent, while it is in the outbound email queue. + +To do this, click create a user/account/team, or if you already have, click on `Resend Code`: + +```{figure} img/code-input.png +The code input interface +``` + +Then run the following command + +``` +kubectl exec $(kubectl get pod -lapp=demo-smtp | grep demo | awk '{print $1;}') -- sh -c 'cat /var/spool/exim4/input/* | grep -Po "^\\d{6}$" ' +``` + +Or step by step: + +1. Get the name of the pod + + ``` + kubectl get pod -lapp=demo-smtp + ``` + +Which will give you a result that looks something like this + +``` +> kubectl get pod -lapp=demo-smtp +NAME READY STATUS RESTARTS AGE +demo-smtp-85557f6877-qxk2p 1/1 Running 0 80m +``` + +In which case, the pod name is `demo-smtp-85557f6877-qxk2p`, which replaces \ in the next command. + +2. Then get the content of emails and extract the code + + ``` + kubectl exec -- sh -c 'head -n 15 /var/spool/exim4/input/* ' + ``` + +Which will give you the content of sent emails, including the code + +``` +> kubectl exec demo-smtp-85557f6877-qxk2p -- sh -c 'head -n 15 /var/spool/exim4/input/* ' +==> /var/spool/exim4/input/1mECxm-000068-28-D <== +1mECxm-000068-28-D +--Y3mymuwB5Y +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable +[https://wire=2Ecom/p/img/email/logo-email-black=2Epng] +VERIFY YOUR EMAIL +myemail@gmail=2Ecom was used to register on Wire=2E Enter this code to v= +erify your email and create your account=2E +022515 +``` + +This means the code is `022515`, simply enter it in the interface. + +If the email has already been sent out, it's possible the queue will be empty. + +If that is the case, simply click the "Resend Code" link in the interface, then quickly re-send the command, a new email should now be present. + +## Obtaining Brig logs, and the format of different team/user events + +To obtain brig logs, simply run + +``` +kubectl logs $(kubectl get pods | grep brig | awk '{print $1;}' | head -n 1) +``` + +You will get log entries for various different types of events that happen, for example: + +1. User creation + + ``` + {"user":"24bdd52e-af33-400c-8e47-d16bf8695dbd","request":"c0575ff5a2d61bfc2be21e77260fccab","msgs":["I","Creating user"]} + ``` + +2. Activation key creation + + ``` + {"activation.code":"949721","activation.key":"p8o032Ljqhjgcea9R0AAnOeiUniGm63BrY9q_aeS1Cc=","request":"c0575ff5a2d61bfc2be21e77260fccab","msgs":["I","Activating"]} + ``` + +3. Activation of a new user + + ``` + {"user":"24bdd52e-af33-400c-8e47-d16bf8695dbd","request":"c0575ff5a2d61bfc2be21e77260fccab","msgs":["I","User activated"]} + ``` + +4. User indexing + + ``` + {"user":"24bdd52e-af33-400c-8e47-d16bf8695dbd","logger":"index.brig","msgs":["I","Indexing user"]} + ``` + +5. Team creation + + ``` + {"email_sha256":"a7ca34df62e3aa18e071e6bd4740009ce7a25278869badc1ad8f6afda792d427","team":"6ef03a2b-34b5-4b65-8d72-1e4fc7697553","user":"24bdd52e-af33-400c-8e47-d16bf8695dbd","module":"Brig.API.Public","fn":"Brig.API.Public.createUser","request":"c0575ff5a2d61bfc2be21e77260fccab","msgs":["I","Sucessfully created user"]} + ``` + +6. Invitation sent + + ``` + {"invitation_code":"hJuh1C1PzMkgtesAYZZ4SZrP5xO-xM_m","email_sha256":"eef48a690436699c653110387455a4afe93ce29febc348acd20f6605787956e6","team":"6ef03a2b-34b5-4b65-8d72-1e4fc7697553","module":"Brig.Team.API","fn":"Brig.Team.API.createInvitationPublic","request":"c43440074629d802a199464dd892cd92","msgs":["I","Succesfully created invitation"]} + ``` + +## Diagnosing and addressing bad network/disconnect issues + +### Diagnosis + +If you are experiencing bad network/disconnection issues, here is how to obtain the cause from the client log files: + +In the Web client, the connection state handler logs the disconnected state as reported by WebRTC as: + +``` +flow(...): connection_handler: disconnected, starting disconnect timer +``` + +On mobile, the output in the log is slightly different: + +``` +pf(...): ice connection state: Disconnected +``` + +And when the timer expires and the connection is not re-established: + +``` +ecall(...): mf_restart_handler: triggering restart due to network drop +``` + +If the attempt to reconnect then fails you will likely see the following: + +``` +ecall(...): connection timeout after 10000 milliseconds +``` + +If the connection to the SFT ({ref}`understand-sft`) server is considered lost due to missing ping messages from a non-functionning or delayed data channel or a failure to receive/decrypt media you will see: + +``` +ccall(...): reconnect +``` + +Then followed by these values: + +``` +cp: received CONFPART message YES/NO +da: decrypt attempted YES/NO +ds: decrypt successful YES/NO +att: number of reconnect attempts +p: the expected ping (how many pings have not returned) +``` + +### Configuration + +Question: Are the connection values for bad networks/disconnect configurable on on-prem? + +Answer: The values are not currently configurable, they are built into the clients at compile time, we do have a mechanism for sending calling configs to the clients but these values are not currently there. diff --git a/docs/src/how-to/install/troubleshooting.rst b/docs/src/how-to/install/troubleshooting.rst deleted file mode 100644 index 79adc61f52..0000000000 --- a/docs/src/how-to/install/troubleshooting.rst +++ /dev/null @@ -1,255 +0,0 @@ -Troubleshooting during installation -------------------------------------- - -Problems with CORS on the web based applications (webapp, team-settings, account-pages) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you have installed wire-server, but the web application page in your browser has connection problems and throws errors in the console such as `"Refused to connect to 'https://assets.example.com' because it violates the following Content Security Policies"`, make sure to check that you have configured the ``CSP_EXTRA_`` environment variables. - -In the file that you use as override when running ``helm install/update -f `` (using the webapp as an example): - -.. code:: yaml - - webapp: - # ... other settings... - envVars: - # ... other environment variables ... - CSP_EXTRA_CONNECT_SRC: "https://*.example.com, wss://*.example.com" - CSP_EXTRA_IMG_SRC: "https://*.example.com" - CSP_EXTRA_SCRIPT_SRC: "https://*.example.com" - CSP_EXTRA_DEFAULT_SRC: "https://*.example.com" - CSP_EXTRA_FONT_SRC: "https://*.example.com" - CSP_EXTRA_FRAME_SRC: "https://*.example.com" - CSP_EXTRA_MANIFEST_SRC: "https://*.example.com" - CSP_EXTRA_OBJECT_SRC: "https://*.example.com" - CSP_EXTRA_MEDIA_SRC: "https://*.example.com" - CSP_EXTRA_PREFETCH_SRC: "https://*.example.com" - CSP_EXTRA_STYLE_SRC: "https://*.example.com" - CSP_EXTRA_WORKER_SRC: "https://*.example.com" - -For more info, you can have a look at respective charts values files, i.e.: - - * `charts/account-pages/values.yaml `__ - * `charts/team-settings/values.yaml `__ - * `charts/webapp/values.yaml `__ - -Problems with ansible and python versions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If for instance the following fails:: - - ansible all -i hosts.ini -m shell -a "echo hello" - -If your target machine only has python 3 (not python 2.7), you can tell ansible to use python 3 by default, by specifying `ansible_python_interpreter`: - -.. code:: ini - - # hosts.ini - - [all] - server1 ansible_host=1.2.3.4 - - - [all:vars] - ansible_python_interpreter=/usr/bin/python3 - -(python 3 may not be supported by all ansible modules yet) - - -Flaky issues with Cassandra (failed QUORUMs, etc.) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cassandra is *very* picky about time! Ensure that NTP is properly set up on all nodes. Particularly for Cassandra *DO NOT* use anything else other than ntp. Here are some helpful blogs that explain why: - - * https://blog.rapid7.com/2014/03/14/synchronizing-clocks-in-a-cassandra-cluster-pt-1-the-problem/ - * https://blog.rapid7.com/2014/03/17/synchronizing-clocks-in-a-cassandra-cluster-pt-2-solutions/ - * https://www.digitalocean.com/community/tutorials/how-to-set-up-time-synchronization-on-ubuntu-16-04 - -How can I ensure that I have correctly setup NTP on my machine(s)? Have a look at `this ansible playbook `_ - - -I deployed ``demo-smtp`` but I'm not receiving any verification emails -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -1. Check whether brig deployed successfully (brig pod(s) should be in state *Running*) :: - - kubectl get pods -o wide - -2. Inspect Brig logs :: - - kubectl logs $BRING_POD_NAME - -3. The receiving email server might refuse to accept any email sent by the `demo-smtp` server, due to not being - a trusted origin. You may want to set up one of the following email verification mechanisms. - -* `SFP `__ -* `DKIM `__ -* `DMARC `__ - - -4. You may want to adjust the SMTP configuration for Brig (``wire-server/[values,secrets].yaml``). - -.. code:: yaml - - brig: - config: - smtp: - host: 'demo-smtp' - port: 25 - connType: 'plain' - - -.. code:: yaml - - brig: - secrets: - smtpPassword: dummyPassword - -(Don't forget to apply the changes with ``helm upgrade wire-server wire/wire-server -f values.yaml -f secrets.yaml``) - -I deployed ``demo-smtp`` and I want to skip email configuration and retrieve verification codes directly -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If the only thing you need demo-smtp for is sending yourself verification codes to create a test account, it might be simpler and faster to just skip SMTP configuration, and simply retrieve the code internally right after it is sent, while it is in the outbound email queue. - -To do this, click create a user/account/team, or if you already have, click on ``Resend Code``: - -.. figure:: img/code-input.png - - The code input interface - -Then run the following command :: - - kubectl exec $(kubectl get pod -lapp=demo-smtp | grep demo | awk '{print $1;}') -- sh -c 'cat /var/spool/exim4/input/* | grep -Po "^\\d{6}$" ' - -Or step by step: - -1. Get the name of the pod :: - - kubectl get pod -lapp=demo-smtp - -Which will give you a result that looks something like this :: - - > kubectl get pod -lapp=demo-smtp - NAME READY STATUS RESTARTS AGE - demo-smtp-85557f6877-qxk2p 1/1 Running 0 80m - -In which case, the pod name is ``demo-smtp-85557f6877-qxk2p``, which replaces in the next command. - -2. Then get the content of emails and extract the code :: - - kubectl exec -- sh -c 'head -n 15 /var/spool/exim4/input/* ' - -Which will give you the content of sent emails, including the code :: - - > kubectl exec demo-smtp-85557f6877-qxk2p -- sh -c 'head -n 15 /var/spool/exim4/input/* ' - ==> /var/spool/exim4/input/1mECxm-000068-28-D <== - 1mECxm-000068-28-D - --Y3mymuwB5Y - Content-Type: text/plain; charset=utf-8 - Content-Transfer-Encoding: quoted-printable - [https://wire=2Ecom/p/img/email/logo-email-black=2Epng] - VERIFY YOUR EMAIL - myemail@gmail=2Ecom was used to register on Wire=2E Enter this code to v= - erify your email and create your account=2E - 022515 - -This means the code is ``022515``, simply enter it in the interface. - -If the email has already been sent out, it's possible the queue will be empty. - -If that is the case, simply click the "Resend Code" link in the interface, then quickly re-send the command, a new email should now be present. - -Obtaining Brig logs, and the format of different team/user events -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To obtain brig logs, simply run :: - - kubectl logs $(kubectl get pods | grep brig | awk '{print $1;}' | head -n 1) - -You will get log entries for various different types of events that happen, for example: - -1. User creation :: - - {"user":"24bdd52e-af33-400c-8e47-d16bf8695dbd","request":"c0575ff5a2d61bfc2be21e77260fccab","msgs":["I","Creating user"]} - -2. Activation key creation ::  - - {"activation.code":"949721","activation.key":"p8o032Ljqhjgcea9R0AAnOeiUniGm63BrY9q_aeS1Cc=","request":"c0575ff5a2d61bfc2be21e77260fccab","msgs":["I","Activating"]} - -3. Activation of a new user :: - - {"user":"24bdd52e-af33-400c-8e47-d16bf8695dbd","request":"c0575ff5a2d61bfc2be21e77260fccab","msgs":["I","User activated"]} - -4. User indexing :: - - {"user":"24bdd52e-af33-400c-8e47-d16bf8695dbd","logger":"index.brig","msgs":["I","Indexing user"]} - -5. Team creation ::  - - {"email_sha256":"a7ca34df62e3aa18e071e6bd4740009ce7a25278869badc1ad8f6afda792d427","team":"6ef03a2b-34b5-4b65-8d72-1e4fc7697553","user":"24bdd52e-af33-400c-8e47-d16bf8695dbd","module":"Brig.API.Public","fn":"Brig.API.Public.createUser","request":"c0575ff5a2d61bfc2be21e77260fccab","msgs":["I","Sucessfully created user"]} - -6. Invitation sent :: - - {"invitation_code":"hJuh1C1PzMkgtesAYZZ4SZrP5xO-xM_m","email_sha256":"eef48a690436699c653110387455a4afe93ce29febc348acd20f6605787956e6","team":"6ef03a2b-34b5-4b65-8d72-1e4fc7697553","module":"Brig.Team.API","fn":"Brig.Team.API.createInvitationPublic","request":"c43440074629d802a199464dd892cd92","msgs":["I","Succesfully created invitation"]} - -Diagnosing and addressing bad network/disconnect issues -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Diagnosis -========= - -If you are experiencing bad network/disconnection issues, here is how to obtain the cause from the client log files: - -In the Web client, the connection state handler logs the disconnected state as reported by WebRTC as: - -.. code:: - - flow(...): connection_handler: disconnected, starting disconnect timer - -On mobile, the output in the log is slightly different: - -.. code:: - - pf(...): ice connection state: Disconnected - -And when the timer expires and the connection is not re-established: - -.. code:: - - ecall(...): mf_restart_handler: triggering restart due to network drop - -If the attempt to reconnect then fails you will likely see the following: - -.. code:: - - ecall(...): connection timeout after 10000 milliseconds - -If the connection to the SFT (:ref:`understand-sft`) server is considered lost due to missing ping messages from a non-functionning or delayed data channel or a failure to receive/decrypt media you will see: - -.. code:: - - ccall(...): reconnect - -Then followed by these values: - -.. code:: - - cp: received CONFPART message YES/NO - da: decrypt attempted YES/NO - ds: decrypt successful YES/NO - att: number of reconnect attempts - p: the expected ping (how many pings have not returned) - -Configuration -============= - -Question: Are the connection values for bad networks/disconnect configurable on on-prem? - -Answer: The values are not currently configurable, they are built into the clients at compile time, we do have a mechanism for sending calling configs to the clients but these values are not currently there. - - - - - - diff --git a/docs/src/how-to/install/version-requirements.md b/docs/src/how-to/install/version-requirements.md new file mode 100644 index 0000000000..dc9cccc8f9 --- /dev/null +++ b/docs/src/how-to/install/version-requirements.md @@ -0,0 +1,28 @@ +# Required/Supported versions + +*Updated: 26.04.2021* + +```{warning} +If you already installed Wire by using `poetry`, please refer to the +[old version](https://docs.wire.com/versions/install-with-poetry/how-to/index.html) of +the installation guide. +``` + +## Persistence + +- Cassandra: 3.11 (OpenJDK 8) +- Elasticsearch: 6.6.0 +- Minio + : - server: latest (tested v2020-03-25) + - client: latest (tested v2020-03-14) + +### Infrastructure + +- Ubuntu: 18.04 +- Docker: latest +- Kubernetes: 1.19.7 + +### Automation + +- Ansible: 2.9 +- Helm: >= v3 diff --git a/docs/src/how-to/install/version-requirements.rst b/docs/src/how-to/install/version-requirements.rst deleted file mode 100644 index c86267010e..0000000000 --- a/docs/src/how-to/install/version-requirements.rst +++ /dev/null @@ -1,35 +0,0 @@ -Required/Supported versions -=========================== - -*Updated: 26.04.2021* - -.. warning:: - - If you already installed Wire by using ``poetry``, please refer to the - `old version `__ of - the installation guide. - - -Persistence -~~~~~~~~~~~ - -- Cassandra: 3.11 (OpenJDK 8) -- Elasticsearch: 6.6.0 -- Minio - - server: latest (tested v2020-03-25) - - client: latest (tested v2020-03-14) - - -Infrastructure --------------- - -- Ubuntu: 18.04 -- Docker: latest -- Kubernetes: 1.19.7 - - -Automation ----------- - -- Ansible: 2.9 -- Helm: >= v3 diff --git a/docs/src/how-to/post-install/index.rst b/docs/src/how-to/post-install/index.md similarity index 53% rename from docs/src/how-to/post-install/index.rst rename to docs/src/how-to/post-install/index.md index 4a7420aa23..2dd2009af9 100644 --- a/docs/src/how-to/post-install/index.rst +++ b/docs/src/how-to/post-install/index.md @@ -1,15 +1,15 @@ -.. _checks: +(checks)= -Verifying your wire-server installation -======================================= +# Verifying your wire-server installation After a successful installation of wire-server and its components, there are some useful checks to be run to ensure the proper functioning of the system. Here's a non-exhaustive list of checks to run on the hosts: NOTE: This page is a work in progress, more sections to be added soon. -.. toctree:: - :maxdepth: 1 - :glob: +```{toctree} +:glob: true +:maxdepth: 1 - Verifying NTP - Verifying data retention for logs don't exceed 72 hours + Verifying NTP + Verifying data retention for logs don't exceed 72 hours +``` diff --git a/docs/src/how-to/post-install/logrotation-check.md b/docs/src/how-to/post-install/logrotation-check.md new file mode 100644 index 0000000000..17bcdde7db --- /dev/null +++ b/docs/src/how-to/post-install/logrotation-check.md @@ -0,0 +1,81 @@ +(logrotation-check)= + +# Logs and Data Protection checks + +On Wire.com, we keep logs for a maximum of 72 hours as described in the [privacy whitepaper](https://wire.com/en/security/) + +We recommend you do the same and limit the amount of logs kept on your servers. + +## How can I see how far in the past access logs are still available on my servers? + +Look at the timestamps of your earliest nginz logs: + +```sh +export NAMESPACE=default # this may be 'default' or 'wire' +kubectl -n "$NAMESPACE" get pods | grep nginz +# choose one of the resulting names, it might be named e.g. nginz-6d75755c5c-h9fwn +kubectl -n "$NAMESPACE" logs -c nginz | head -10 +``` + +If the timestamp is more than 3 days in the past, your logs are kept for unnecessary long amount of time and you should configure log rotation. + +### I used your ansible scripts and prefer to have the default 72 hour maximum log availability configured automatically. + +You can use [the kubernetes_logging.yml ansible playbook](https://github.com/wireapp/wire-server-deploy/blob/develop/ansible/kubernetes_logging.yml) + +### I am not using ansible and like to SSH into hosts and configure things manually + +SSH into one of your kubernetes worker machines. + +If you installed as per the instructions on docs.wire.com, then the default logging strategy is `json-file` with `--log-opt max-size=50m --log-opt max-file=5` storing logs in files under `/var/lib/docker/containers//.log`. You can check this with these commands: + +```sh +docker info --format '{{.LoggingDriver}}' +ps aux | grep log-opt +``` + +(Options configured in `/etc/systemd/system/docker.service.d/docker-options.conf`) + +The default will thus keep your logs around until reaching 250 MB per pod, which is far longer than three days. Since docker logs don't allow a time-based log rotation, we can instead make use of [logrotate](https://linux.die.net/man/8/logrotate) to rotate logs for us. + +Create the file `/etc/logrotate.d/podlogs` with the following contents: + +% NOTE: in case you change these docs, also make sure to update the actual code +% under https://github.com/wireapp/wire-server-deploy/blob/develop/ansible/kubernetes_logging.yml + +``` +"/var/lib/docker/containers/*/*.log" +{ + daily + missingok + rotate 2 + maxage 1 + copytruncate + nocreate + nocompress + } +``` + +Repeat the same for all the other kubernetes worker machines, the file needs to exist on all of them. + +There should already be a cron job for logrotate for other parts of the system, so this should be sufficent, you can stop here. + +You can check for the cron job with: + +``` +ls /etc/cron.daily/logrotate +``` + +And you can manually run a log rotation using: + +``` +/usr/sbin/logrotate -v /etc/logrotate.conf +``` + +If you want to clear out old logs entirely now, you can force log rotation three times (again, on all kubernetes machines): + +``` +/usr/sbin/logrotate -v -f /etc/logrotate.conf +/usr/sbin/logrotate -v -f /etc/logrotate.conf +/usr/sbin/logrotate -v -f /etc/logrotate.conf +``` diff --git a/docs/src/how-to/post-install/logrotation-check.rst b/docs/src/how-to/post-install/logrotation-check.rst deleted file mode 100644 index 6094d6d3a3..0000000000 --- a/docs/src/how-to/post-install/logrotation-check.rst +++ /dev/null @@ -1,79 +0,0 @@ -.. _logrotation-check: - -Logs and Data Protection checks -=============================== - -On Wire.com, we keep logs for a maximum of 72 hours as described in the `privacy whitepaper `_ - -We recommend you do the same and limit the amount of logs kept on your servers. - -How can I see how far in the past access logs are still available on my servers? --------------------------------------------------------------------------------- - -Look at the timestamps of your earliest nginz logs: - -.. code:: sh - - export NAMESPACE=default # this may be 'default' or 'wire' - kubectl -n "$NAMESPACE" get pods | grep nginz - # choose one of the resulting names, it might be named e.g. nginz-6d75755c5c-h9fwn - kubectl -n "$NAMESPACE" logs -c nginz | head -10 - -If the timestamp is more than 3 days in the past, your logs are kept for unnecessary long amount of time and you should configure log rotation. - -I used your ansible scripts and prefer to have the default 72 hour maximum log availability configured automatically. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can use `the kubernetes_logging.yml ansible playbook `_ - -I am not using ansible and like to SSH into hosts and configure things manually -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -SSH into one of your kubernetes worker machines. - -If you installed as per the instructions on docs.wire.com, then the default logging strategy is ``json-file`` with ``--log-opt max-size=50m --log-opt max-file=5`` storing logs in files under ``/var/lib/docker/containers//.log``. You can check this with these commands: - -.. code:: sh - - docker info --format '{{.LoggingDriver}}' - ps aux | grep log-opt - -(Options configured in ``/etc/systemd/system/docker.service.d/docker-options.conf``) - -The default will thus keep your logs around until reaching 250 MB per pod, which is far longer than three days. Since docker logs don't allow a time-based log rotation, we can instead make use of `logrotate `__ to rotate logs for us. - -Create the file ``/etc/logrotate.d/podlogs`` with the following contents: - -.. - NOTE: in case you change these docs, also make sure to update the actual code - under https://github.com/wireapp/wire-server-deploy/blob/develop/ansible/kubernetes_logging.yml -.. code:: - - "/var/lib/docker/containers/*/*.log" - { - daily - missingok - rotate 2 - maxage 1 - copytruncate - nocreate - nocompress - } - -Repeat the same for all the other kubernetes worker machines, the file needs to exist on all of them. - -There should already be a cron job for logrotate for other parts of the system, so this should be sufficent, you can stop here. - -You can check for the cron job with:: - - ls /etc/cron.daily/logrotate - -And you can manually run a log rotation using:: - - /usr/sbin/logrotate -v /etc/logrotate.conf - -If you want to clear out old logs entirely now, you can force log rotation three times (again, on all kubernetes machines):: - - /usr/sbin/logrotate -v -f /etc/logrotate.conf - /usr/sbin/logrotate -v -f /etc/logrotate.conf - /usr/sbin/logrotate -v -f /etc/logrotate.conf diff --git a/docs/src/how-to/post-install/ntp-check.md b/docs/src/how-to/post-install/ntp-check.md new file mode 100644 index 0000000000..f093998eea --- /dev/null +++ b/docs/src/how-to/post-install/ntp-check.md @@ -0,0 +1,44 @@ +(ntp-check)= + +# NTP Checks + +Ensure that NTP is properly set up on all nodes. Particularly for Cassandra **DO NOT** use anything else other than ntp. Here are some helpful blogs that explain why: + +> - +> - + +## How can I see if NTP is correctly set up? + +This is an important part of your setup, particularly for your Cassandra nodes. You should use `ntpd` and our ansible scripts to ensure it is installed correctly - but you can still check it manually if you prefer. The following 2 sub-sections explain both approaches. + +### I used your ansible scripts and prefer to have automated checks + +Then the easiest way is to use [this ansible playbook](https://github.com/wireapp/wire-server-deploy/blob/develop/ansible/cassandra-verify-ntp.yml) + +### I am not using ansible and like to SSH into hosts and checking things manually + +The following shows how to check for existing servers connected to (assumes `ntpq` is installed) + +```sh +ntpq -pn +``` + +which should yield something like this: + +```sh + remote refid st t when poll reach delay offset jitter +============================================================================== + time.example. .POOL. 16 p - 64 0 0.000 0.000 0.000 ++ 2 u 498 512 377 0.759 0.039 0.081 +* 2 u 412 512 377 1.251 -0.670 0.063 +``` + +if your output shows \_ONLY\_ the entry with a `.POOL.` as `refid` and a lot of 0s, something is probably wrong, i.e.: + +```sh + remote refid st t when poll reach delay offset jitter +============================================================================== + time.example. .POOL. 16 p - 64 0 0.000 0.000 0.000 +``` + +What should you do if this is the case? Ensure that `ntp` is installed and that the servers in the pool (typically at `/etc/ntp.conf`) are reachable. diff --git a/docs/src/how-to/post-install/ntp-check.rst b/docs/src/how-to/post-install/ntp-check.rst deleted file mode 100644 index 09b3852e62..0000000000 --- a/docs/src/how-to/post-install/ntp-check.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. _ntp-check: - -NTP Checks -========== - -Ensure that NTP is properly set up on all nodes. Particularly for Cassandra **DO NOT** use anything else other than ntp. Here are some helpful blogs that explain why: - - * https://blog.rapid7.com/2014/03/14/synchronizing-clocks-in-a-cassandra-cluster-pt-1-the-problem/ - * https://www.digitalocean.com/community/tutorials/how-to-set-up-time-synchronization-on-ubuntu-16-04 - -How can I see if NTP is correctly set up? ------------------------------------------ - -This is an important part of your setup, particularly for your Cassandra nodes. You should use `ntpd` and our ansible scripts to ensure it is installed correctly - but you can still check it manually if you prefer. The following 2 sub-sections explain both approaches. - -I used your ansible scripts and prefer to have automated checks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Then the easiest way is to use `this ansible playbook `_ - -I am not using ansible and like to SSH into hosts and checking things manually -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following shows how to check for existing servers connected to (assumes `ntpq` is installed) - -.. code:: sh - - ntpq -pn - -which should yield something like this: - -.. code:: sh - - remote refid st t when poll reach delay offset jitter - ============================================================================== - time.example. .POOL. 16 p - 64 0 0.000 0.000 0.000 - + 2 u 498 512 377 0.759 0.039 0.081 - * 2 u 412 512 377 1.251 -0.670 0.063 - -if your output shows _ONLY_ the entry with a `.POOL.` as `refid` and a lot of 0s, something is probably wrong, i.e.: - -.. code:: sh - - remote refid st t when poll reach delay offset jitter - ============================================================================== - time.example. .POOL. 16 p - 64 0 0.000 0.000 0.000 - -What should you do if this is the case? Ensure that `ntp` is installed and that the servers in the pool (typically at `/etc/ntp.conf`) are reachable. diff --git a/docs/src/how-to/single-sign-on/adfs/main.md b/docs/src/how-to/single-sign-on/adfs/main.md new file mode 100644 index 0000000000..2e48fd531b --- /dev/null +++ b/docs/src/how-to/single-sign-on/adfs/main.md @@ -0,0 +1,41 @@ +# How to set up SSO integration with ADFS + +This is being used in production by some of our customers, but not +documented. We do have a few out-of-context screenshots, which we +provide here in the hope they may help. + +```{image} fig-00.jpg +``` + +```{image} fig-01.jpg +``` + +```{image} fig-02.jpg +``` + +```{image} fig-03.jpg +``` + +```{image} fig-04.jpg +``` + +```{image} fig-05.jpg +``` + +```{image} fig-06.jpg +``` + +```{image} fig-07.jpg +``` + +```{image} fig-08.jpg +``` + +```{image} fig-09.jpg +``` + +```{image} fig-10.jpg +``` + +```{image} fig-11.jpg +``` diff --git a/docs/src/how-to/single-sign-on/adfs/main.rst b/docs/src/how-to/single-sign-on/adfs/main.rst deleted file mode 100644 index 53155b14d8..0000000000 --- a/docs/src/how-to/single-sign-on/adfs/main.rst +++ /dev/null @@ -1,19 +0,0 @@ -How to set up SSO integration with ADFS -======================================= - -This is being used in production by some of our customers, but not -documented. We do have a few out-of-context screenshots, which we -provide here in the hope they may help. - -.. image:: fig-00.jpg -.. image:: fig-01.jpg -.. image:: fig-02.jpg -.. image:: fig-03.jpg -.. image:: fig-04.jpg -.. image:: fig-05.jpg -.. image:: fig-06.jpg -.. image:: fig-07.jpg -.. image:: fig-08.jpg -.. image:: fig-09.jpg -.. image:: fig-10.jpg -.. image:: fig-11.jpg diff --git a/docs/src/how-to/single-sign-on/azure/main.md b/docs/src/how-to/single-sign-on/azure/main.md new file mode 100644 index 0000000000..dbd0338907 --- /dev/null +++ b/docs/src/how-to/single-sign-on/azure/main.md @@ -0,0 +1,92 @@ +# How to set up SSO integration with Microsoft Azure + +## Preprequisites + +- account, admin access to that account +- See also {ref}`sso-generic-setup`. + +## Steps + +### Azure setup + +Go to , and click on 'Azure Active Directory' +in the menu to your left, then on 'Enterprise Applications': + +```{image} 01.png +``` + +Click on 'New Application': + +```{image} 02.png +``` + +Select 'Non-gallery application': + +```{image} 03.png +``` + +Fill in user-facing app name, then click 'add': + +```{image} 04.png +``` + +The app is now created. If you get lost, you can always get back to +it by selecting its name from the enterprise applications list you've +already visited above. + +Click on 'Configure single sign-on'. + +```{image} 05.png +``` + +Select SAML: + +```{image} 06.png +``` + +On the next page, you find a link to a configuration guide which you +can consult if you have any azure-specific questions. Or you can go +straight to adding the two config parameters you need: + +```{image} 07.png +``` + +Enter for both identity and reply url. Save. + +```{image} 08.png +``` + +Click on 'test later': + +```{image} 09.png +``` + +Finally, you need to assign users to the newly created and configured application: + +```{image} 11.png +``` + +```{image} 12.png +``` + +```{image} 13.png +``` + +```{image} 14.png +``` + +```{image} 15.png +``` + +And that's it! You are now ready to set up your wire team for SAML SSO with the XML metadata file you downloaed above. + +## Further reading + +- technical concepts overview: + : - + - +- how to create an app: + : - +- how to configure SAML2.0 SSO: + : - + - diff --git a/docs/src/how-to/single-sign-on/azure/main.rst b/docs/src/how-to/single-sign-on/azure/main.rst deleted file mode 100644 index caac4266b8..0000000000 --- a/docs/src/how-to/single-sign-on/azure/main.rst +++ /dev/null @@ -1,82 +0,0 @@ -How to set up SSO integration with Microsoft Azure -================================================== - -Preprequisites --------------- - -- http://azure.microsoft.com account, admin access to that account -- See also :ref:`sso-generic-setup`. - -Steps ------ - -Azure setup -^^^^^^^^^^^ - -Go to https://portal.azure.com/, and click on 'Azure Active Directory' -in the menu to your left, then on 'Enterprise Applications': - -.. image:: 01.png - -Click on 'New Application': - -.. image:: 02.png - -Select 'Non-gallery application': - -.. image:: 03.png - -Fill in user-facing app name, then click 'add': - -.. image:: 04.png - -The app is now created. If you get lost, you can always get back to -it by selecting its name from the enterprise applications list you've -already visited above. - -Click on 'Configure single sign-on'. - -.. image:: 05.png - -Select SAML: - -.. image:: 06.png - -On the next page, you find a link to a configuration guide which you -can consult if you have any azure-specific questions. Or you can go -straight to adding the two config parameters you need: - -.. image:: 07.png - -Enter https://prod-nginz-https.wire.com/sso/finalize-login for both identity and reply url. Save. - -.. image:: 08.png - -Click on 'test later': - -.. image:: 09.png - -Finally, you need to assign users to the newly created and configured application: - -.. image:: 11.png -.. image:: 12.png -.. image:: 13.png -.. image:: 14.png -.. image:: 15.png - -And that's it! You are now ready to set up your wire team for SAML SSO with the XML metadata file you downloaed above. - - -Further reading ---------------- - -- technical concepts overview: - - https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-saml-protocol-reference - - https://docs.microsoft.com/en-us/azure/active-directory/develop/single-sign-on-saml-protocol - -- how to create an app: - - https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app - -- how to configure SAML2.0 SSO: - - https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/what-is-single-sign-on#saml-sso - - https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/configure-single-sign-on-non-gallery-applications diff --git a/docs/src/how-to/single-sign-on/centrify/main.rst b/docs/src/how-to/single-sign-on/centrify/main.md similarity index 55% rename from docs/src/how-to/single-sign-on/centrify/main.rst rename to docs/src/how-to/single-sign-on/centrify/main.md index 55dbb05a9f..ed88ba6668 100644 --- a/docs/src/how-to/single-sign-on/centrify/main.rst +++ b/docs/src/how-to/single-sign-on/centrify/main.md @@ -1,46 +1,48 @@ -How to set up SSO integration with Centrify -=========================================== +# How to set up SSO integration with Centrify -Preprequisites --------------- +## Preprequisites -- http://centrify.com account, admin access to that account -- See also :ref:`sso-generic-setup`. +- account, admin access to that account +- See also {ref}`sso-generic-setup`. -Steps ------ +## Steps -Centrify setup -^^^^^^^^^^^^^^ +### Centrify setup - Log in into Centrify web interface - Navigate to "Web Apps" - Click "Add Web Apps" -.. image:: 001.png +```{image} 001.png +``` ----- +______________________________________________________________________ - Create a new custom SAML application -.. image:: 002.png +```{image} 002.png +``` ----- +______________________________________________________________________ - Confirm... -.. image:: 003.png +```{image} 003.png +``` ----- +______________________________________________________________________ - Wait a few moments until the UI has rendered the `Settings` tab of your newly created Web App. - Enter at least a name, plus any other information you want to keep about this new Web App. - Then click on `Save`. -.. image:: 004.png -.. image:: 005.png +```{image} 004.png +``` ----- +```{image} 005.png +``` + +______________________________________________________________________ - Move to the `Trust` tab. This is where the SP metadata (everything centrify wants to know about wire, or Service Provider) and the IdP metadata (everything wire needs to know about centrify, or Identity Provider) can be found. - Enter `https://prod-nginz-https.wire.com/sso/finalize-login` as the SP metadata url. @@ -48,25 +50,33 @@ Centrify setup - You can see the metadata appear in the form below the `Load` button. - Click on `Save`. -.. image:: 006.png +```{image} 006.png +``` ----- +______________________________________________________________________ - Scroll down the `Trust` tab until you find the button to download the IdP metadata. - Store it in a file (eg. `my-wire-idp.xml`). You will need this file to set up your wire team for SSO. -.. image:: 007.png +```{image} 007.png +``` ----- +______________________________________________________________________ - Move to the `Permissions` tab and add at least one user. -.. image:: 008.png -.. image:: 009.png -.. image:: 010.png +```{image} 008.png +``` + +```{image} 009.png +``` + +```{image} 010.png +``` ----- +______________________________________________________________________ - If you see the status `Deployed` in the header of the `Web App` setup page, your users are ready to login. -.. image:: 011.png +```{image} 011.png +``` diff --git a/docs/src/how-to/single-sign-on/generic-setup.md b/docs/src/how-to/single-sign-on/generic-setup.md new file mode 100644 index 0000000000..d455899cca --- /dev/null +++ b/docs/src/how-to/single-sign-on/generic-setup.md @@ -0,0 +1,37 @@ +(sso-generic-setup)= + +# How to set up SSO integration with your IdP + +## Preprequisites + +- An account with your SAML IdP, admin access to that account +- Wire team, admin access to that team +- If your team is hosted at wire.com: + : - Ask customer support to enable the SSO feature flag for you. +- If you are running your own on-prem instance: + : - for handling the feature flag, you can run your own [backoffice](https://github.com/wireapp/wire-server-deploy/tree/259cd2664a4e4d890be797217cc715499d72acfc/charts/backoffice) service. + - More simply, you can configure the galley service so that sso is always enabled (just put "enabled-by-default" [here](https://github.com/wireapp/wire-server-deploy/blob/a4a35b65b2312995729b0fc2a04461508cb12de7/values/wire-server/prod-values.example.yaml#L134)). + +## Setting up your IdP + +- The SP Metadata URL: +- The SSO Login URL: +- SP Entity ID (aka Request Issuer ID): + +How you need to use this information during setting up your IdP +depends on the vendor. Let us know if you run into any trouble! + +## Setting up your wire team + +See + +## Authentication + +The team settings will show you a login code from us that looks like +eg. + +\> `wire-959b5840-3e8a-11e9-adff-0fa5314b31c0` + +See +- +on how to use this to login on wire. diff --git a/docs/src/how-to/single-sign-on/generic-setup.rst b/docs/src/how-to/single-sign-on/generic-setup.rst deleted file mode 100644 index ed8c857500..0000000000 --- a/docs/src/how-to/single-sign-on/generic-setup.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _sso-generic-setup: - -How to set up SSO integration with your IdP -=========================================== - -Preprequisites --------------- - -- An account with your SAML IdP, admin access to that account -- Wire team, admin access to that team -- If your team is hosted at wire.com: - - Ask customer support to enable the SSO feature flag for you. -- If you are running your own on-prem instance: - - for handling the feature flag, you can run your own `backoffice `_ service. - - More simply, you can configure the galley service so that sso is always enabled (just put "enabled-by-default" `here `_). - -Setting up your IdP -------------------- - -- The SP Metadata URL: https://prod-nginz-https.wire.com/sso/metadata -- The SSO Login URL: https://prod-nginz-https.wire.com/sso/finalize-login -- SP Entity ID (aka Request Issuer ID): https://prod-nginz-https.wire.com/sso/finalize-login - -How you need to use this information during setting up your IdP -depends on the vendor. Let us know if you run into any trouble! - -Setting up your wire team -------------------------- - -See https://support.wire.com/hc/en-us/articles/360001285638-Set-up-SSO-internally - -Authentication --------------- - -The team settings will show you a login code from us that looks like -eg. - -> `wire-959b5840-3e8a-11e9-adff-0fa5314b31c0` - -See -https://support.wire.com/hc/en-us/articles/360000954617-Pro-How-to-log-in-with-SSO- -on how to use this to login on wire. diff --git a/docs/src/how-to/single-sign-on/index.md b/docs/src/how-to/single-sign-on/index.md new file mode 100644 index 0000000000..2cdb939676 --- /dev/null +++ b/docs/src/how-to/single-sign-on/index.md @@ -0,0 +1,15 @@ +# Single Sign-On and User Provisioning + +```{toctree} +:caption: 'Contents:' +:glob: true +:maxdepth: 1 + +Single sign-on and user provisioning +Generic setup +SSO integration with ADFS +SSO integration with Azure +SSO integration with Centrify +SSO integration with Okta +* +``` diff --git a/docs/src/how-to/single-sign-on/index.rst b/docs/src/how-to/single-sign-on/index.rst deleted file mode 100644 index 6992836111..0000000000 --- a/docs/src/how-to/single-sign-on/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -Single Sign-On and User Provisioning ------------------------------------- - -.. toctree:: - :maxdepth: 1 - :caption: Contents: - :glob: - - Single sign-on and user provisioning - Generic setup - SSO integration with ADFS - SSO integration with Azure - SSO integration with Centrify - SSO integration with Okta - * diff --git a/docs/src/how-to/single-sign-on/okta/main.rst b/docs/src/how-to/single-sign-on/okta/main.md similarity index 76% rename from docs/src/how-to/single-sign-on/okta/main.rst rename to docs/src/how-to/single-sign-on/okta/main.md index 3a051cc5c6..6fe285c55f 100644 --- a/docs/src/how-to/single-sign-on/okta/main.rst +++ b/docs/src/how-to/single-sign-on/okta/main.md @@ -1,49 +1,50 @@ -.. _sso-int-with-okta: +(sso-int-with-okta)= -How to set up SSO integration with Okta -======================================= +# How to set up SSO integration with Okta -Preprequisites --------------- +## Preprequisites -- http://okta.com/ account, admin access to that account -- See also :ref:`sso-generic-setup`. +- account, admin access to that account +- See also {ref}`sso-generic-setup`. -Steps ------ +## Steps -Okta setup -~~~~~~~~~~ +### Okta setup - Log in into Okta web interface - Open the admin console and switch to the "Classic UI" - Navigate to "Applications" - Click "Add application" -.. image:: 001-applications-screen.png +```{image} 001-applications-screen.png +``` ----- +______________________________________________________________________ - Create a new application -.. image:: 002-add-application.png +```{image} 002-add-application.png +``` ----- +______________________________________________________________________ - Choose `Web`, `SAML 2.0` -.. image:: 003-add-application-1.png +```{image} 003-add-application-1.png +``` ----- +______________________________________________________________________ - Pick a name for the application in "Step 1" and continue -.. image:: 004-add-application-step1.png +```{image} 004-add-application-step1.png +``` ----- +______________________________________________________________________ - Add the following parameters in "Step 2" and continue +```{eval-rst} +-----------------------------+------------------------------------------------------------------------------+ + Paramenter label | Value | +=============================+==============================================================================+ @@ -58,34 +59,41 @@ Okta setup +-----------------------------+------------------------------------------------------------------------------+ | Application Username | `Email` (\*) | +-----------------------------+------------------------------------------------------------------------------+ +``` **(\*) Note**: The application username **must be** unique in your team, and should be immutable once assigned. If more than one user has the same value for the field that you select here, those two users will log in as a single user on Wire. And if the value were to change, users will be re-assigned to a new account at the next login. Usually, `email` is a safe choice but you should evaluate it for your case. -.. image:: 005-add-application-step2.png +```{image} 005-add-application-step2.png +``` ----- +______________________________________________________________________ - Give the following answer in "Step 3" and continue +```{eval-rst} +-----------------------------------+------------------------------------------------------------------------+ + Paramenter label | Value | +===================================+========================================================================+ | Are you a customer or a partner? | I'm an Okta customer | +-----------------------------------+------------------------------------------------------------------------+ +``` -.. image:: 006-add-application-step3.png +```{image} 006-add-application-step3.png +``` ----- +______________________________________________________________________ - The app has been created. Switch to the "Sign-On" tab - Find the "Identity Provider Metadata" link. Copy the link address (normally done by right-clicking on the link and selecting "Copy link location" or a similar item in the menu). - Store the link address somewhere for a future step. -.. image:: 007-application-sign-on.png +```{image} 007-application-sign-on.png +``` ----- +______________________________________________________________________ - Switch to the "Assignments" tab - Make sure that some users (or everyone) is assigned to the application. These are the users that will be allowed to log in to Wire using Single Sign On. Add the relevant users to the list with the "Assign" button. -.. image:: 008-assignment.png +```{image} 008-assignment.png +``` diff --git a/docs/src/how-to/single-sign-on/trouble-shooting.rst b/docs/src/how-to/single-sign-on/trouble-shooting.md similarity index 60% rename from docs/src/how-to/single-sign-on/trouble-shooting.rst rename to docs/src/how-to/single-sign-on/trouble-shooting.md index da0ca43210..cdc7e1204a 100644 --- a/docs/src/how-to/single-sign-on/trouble-shooting.rst +++ b/docs/src/how-to/single-sign-on/trouble-shooting.md @@ -1,32 +1,28 @@ -.. _trouble-shooting-faq: +(trouble-shooting-faq)= -Trouble shooting & FAQ -====================== +# Trouble shooting & FAQ -Reporting a problem with user provisioning or SSO authentication ----------------------------------------------------------------- +## Reporting a problem with user provisioning or SSO authentication In order for us to analyse and understand your problem, we need at least the following information up-front: - Have you followed the following instructions? - - :ref:`FAQ ` (This document) - - `Howtos `_ for supported vendors - - `General documentation on the setup flow `_ + : - {ref}`FAQ ` (This document) + - [Howtos](https://docs.wire.com/how-to/single-sign-on/index.html) for supported vendors + - [General documentation on the setup flow](https://support.wire.com/hc/en-us/articles/360001285718-Set-up-SSO-externally) - Vendor information (octa, azure, centrica, other (which one)?) - Team ID (looks like eg. `2e9a9c9c-6f83-11eb-a118-3342c6f16f4e`, can be found in team settings) - What do you expect to happen? - - eg.: "I enter login code, authenticate successfully against IdP, get redirected, and see the wire landing page." + : - eg.: "I enter login code, authenticate successfully against IdP, get redirected, and see the wire landing page." - What does happen instead? - - Screenshots + : - Screenshots - Copy the text into your report where applicable in addition to screenshots (for automatic processing). - eg.: "instead of being logged into wire, I see the following error page: ..." - Screenshots of the Configuration (both SAML and SCIM, as applicable), including, but not limited to: - - If you are using SAML: SAML IdP metadata file + : - If you are using SAML: SAML IdP metadata file - If you are using SCIM for provisioning: Which attributes in the User schema are mapped? How? - -Can I use the same SSO login code for multiple teams? ------------------------------------------------------ +## Can I use the same SSO login code for multiple teams? No, but there is a good reason for it and a work-around. @@ -45,28 +41,21 @@ still use the same user base for all teams. This has the extra advantage that a user can be part of two teams with the same credentials, which would be impossible even with the hypothetical fix. - -Can an existing user without IdP (or with a different IdP) be bound to a new IdP? ---------------------------------------------------------------------------------- +## Can an existing user without IdP (or with a different IdP) be bound to a new IdP? No. This is a feature we never fully implemented. Details / latest -updates: https://github.com/wireapp/wire-server/issues/1151 - - -Can the SSO feature be disabled for a team? -------------------------------------------- +updates: -No, this is `not implemented `_. +## Can the SSO feature be disabled for a team? +No, this is [not implemented](https://github.com/wireapp/wire-server/blob/7a97cb5a944ae593c729341b6f28dfa1dabc28e5/services/galley/src/Galley/API/Error.hs#L215). -Can you remove a SAML connection? ---------------------------------- +## Can you remove a SAML connection? It is not possible to delete a SAML connection in the Team Settings app, however it can be overwritten with a new connection. -It is possible do delete a SAML connection directly via the API endpoint ``DELETE /identity-providers/{id}``. However deleting a SAML connection also requires deleting all users that can log in with this SAML connection. To prevent accidental deletion of users this functionality is not available directly from Team Settings. +It is possible do delete a SAML connection directly via the API endpoint `DELETE /identity-providers/{id}`. However deleting a SAML connection also requires deleting all users that can log in with this SAML connection. To prevent accidental deletion of users this functionality is not available directly from Team Settings. -If you get an error when returning from your IdP ------------------------------------------------- +## If you get an error when returning from your IdP `Symptoms:` @@ -86,9 +75,7 @@ that contains a lot of machine-readable info. With all this information, please get in touch with our customer support. - -Do I need any firewall settings? --------------------------------- +## Do I need any firewall settings? No. @@ -96,9 +83,7 @@ There is nothing to be done here. There is no internet traffic between your SAML IdP and the wire service. All communication happens via the browser or app. - -Why does the team owner have to keep using password? ----------------------------------------------------- +## Why does the team owner have to keep using password? The user who creates the team cannot be authenticated via SSO. There is fundamentally no easy way around that: we need somebody to give us @@ -119,71 +104,66 @@ for IdP registration and upgrade of IdP-authenticated owners / admins. In practice, user A and some owner authenticated via IdP would then be controlled by the same person, probably. - -What should the SAML response look like? ----------------------------------------- +## What should the SAML response look like? Here is an example that works. Much of this beyond the subject's NameID is required by the SAML standard. If you can find a more minimal example that still works, we'd be love to take a look. -.. code:: xml - - - ... - - - - - - - - - - - - - ... - - - ... - - - ... - - - - - ... - - - - - - - https://prod-nginz-https.wire.com/sso/finalize-login - - - - - - urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport - - - - - -Why does the auth response not contain a reference to an auth request? (Also: can i use IdP-initiated login?) ------------------------------------------------------------------------------------------------------------------ +```xml + + ... + + + + + + + + + + + + + ... + + + ... + + + ... + + + + + ... + + + + + + + https://prod-nginz-https.wire.com/sso/finalize-login + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + + + +``` + +## Why does the auth response not contain a reference to an auth request? (Also: can i use IdP-initiated login?) tl;dr: Wire only supports SP-initiated login, where the user selects the auth method from inside the app's login screen. It does not support IdP-initiated login, where the user enters the app from a list of applications in the IdP UI. -The full story -^^^^^^^^^^^^^^ +### The full story SAML authentication can be initiated by the IdP (eg., Okta or Azure), or by the SP (Wire). @@ -206,9 +186,7 @@ impersonate rogue accounts) hard that were otherwise quite feasible. Wire therefore only supports SP-initiated login. - -How are SAML2 assertion details used in wire? ---------------------------------------------- +## How are SAML2 assertion details used in wire? Wire only uses the SAML `NameID` from the assertion, plus the information whether authentication and authorization was successful. @@ -221,9 +199,7 @@ wire user display name a default value. (The user will be allowed to change that value later; changing it does NOT affect the authentication handshake between wire and the IdP.) - -How should I map user data to SCIM attributes when provisioning users via SCIM? -------------------------------------------------------------------------------- +## How should I map user data to SCIM attributes when provisioning users via SCIM? If you are provisioning users via SCIM, the following mapping is used in your wire team: @@ -238,17 +214,16 @@ in your wire team: 3. SCIM's `preferredLanguage` is mapped to wire's user locale settings when a locale is not defined for that user. It must consist of an - ISO 639-1 language code. + ISO 639-1 language code. 4. SCIM's `externalId`: - a. If SAML SSO is used, it is mapped on the SAML `NameID`. If it + 1. If SAML SSO is used, it is mapped on the SAML `NameID`. If it parses as an email, it will have format `email`, and you can choose to validate it during provisioning (by enabeling the feature flag for your team). Otherwise, the format will be `unspecified`. - - b. If email/password authentication is used, SCIM's `externalId` is + 2. If email/password authentication is used, SCIM's `externalId` is mapped on wire's email address, and provisioning works like in team settings with invitation emails. @@ -262,29 +237,24 @@ Also note that the account will be set to `"active": false` until the user has accepted the invitation and activated the account. Please contact customer support if this causes any issues. - -Can I distribute a URL to my users that contains the login code? ----------------------------------------------------------------- +## Can I distribute a URL to my users that contains the login code? Users may find it awkward to copy and paste the login code into the form. If they are using the webapp, an alternative is to give them the following URL (fill in the login code that you can find in your team settings): -.. code:: bash +```bash +https://wire-webapp-dev.zinfra.io/auth#sso/3c4f050a-f073-11eb-b4c9-931bceeed13e +``` - https://wire-webapp-dev.zinfra.io/auth#sso/3c4f050a-f073-11eb-b4c9-931bceeed13e - - -(Theoretical) name clashes in SAML NameIDs ------------------------------------------- +## (Theoretical) name clashes in SAML NameIDs You can technically configure your SAML IdP to create name clashes in wire, ie., to map two (technically) different NameIDs to the same wire user. -How to know you're safe -^^^^^^^^^^^^^^^^^^^^^^^ +### How to know you're safe This is highly unlikely, since the distinguishing parts of `NameID` that we ignore are generally either @@ -292,16 +262,14 @@ unused or redundant. If you are confident that any two users you have assigned to the wire app can be distinguished solely by the lower-cased `NameID` content, you're safe. -Impact -^^^^^^ +### Impact If you are using SCIM for user provisioning, this may lead to errors during provisioning of new users ("user already exists"). If you use SAML auto-provisioning, this may lead to unintential account sharing instead of an error. -How to reproduce -^^^^^^^^^^^^^^^^ +### How to reproduce If you have users whose combination of `IssuerId` and `NameID` can only be distinguished by casing (upper @@ -309,30 +277,27 @@ vs. lower) or by the `NameID` qualifiers (`NameID` xml attributes `NameQualifier`, `IdPNameQualifier`, ...), those users will name clash. -Solution -^^^^^^^^ +### Solution Do not rely on case sensitivity of `IssuerID` or `NameID`, or on `NameID` qualifiers for distinguishing user identifiers. - -How to report problems ----------------------- +## How to report problems If you have a problem you cannot resolve by yourself, please get in touch. Add as much of the following details to your report as possible: -* Are you on cloud or on-prem? (If on-prem: which instance?) -* XML IdP metadata -* SSL Login code or IdP Issuer EntityID -* NameID of the account that has the problem -* SP metadata +- Are you on cloud or on-prem? (If on-prem: which instance?) +- XML IdP metadata +- SSL Login code or IdP Issuer EntityID +- NameID of the account that has the problem +- SP metadata Problem description, including, but not limited to: -* what happened? -* what did you want to happen? -* what does your idp config in the wire team management app look like? -* what does your wire config in your IdP management app look like? -* Please include screenshots *and* copied text (for cut&paste when we investigate) *and* further description and comments where feasible. +- what happened? +- what did you want to happen? +- what does your idp config in the wire team management app look like? +- what does your wire config in your IdP management app look like? +- Please include screenshots *and* copied text (for cut&paste when we investigate) *and* further description and comments where feasible. (If you can't produce some of this information of course please get in touch anyway! It'll merely be harder for us to resolve your issue quickly, and we may need to make a few extra rounds of data gathering together with you.) diff --git a/docs/src/how-to/single-sign-on/understand/main.md b/docs/src/how-to/single-sign-on/understand/main.md new file mode 100644 index 0000000000..465ef9dc48 --- /dev/null +++ b/docs/src/how-to/single-sign-on/understand/main.md @@ -0,0 +1,561 @@ +# Single sign-on and user provisioning + +```{contents} +``` + +## Introduction + +This page is intended as a manual for administrator users in need of setting up {term}`SSO` and provisionning users using {term}`SCIM` on their installation of Wire. + +Historically and by default, Wire's user authentication method is via phone or password. This has security implications and does not scale. + +Solution: {term}`SSO` with {term}`SAML`! [(Security Assertion Markup Language)](https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language) + +{term}`SSO` systems allow users to identify on multiple systems (including Wire once configured as such) using a single ID and password. + +You can find some of the advantages of {term}`SSO` over more traditional schemes [here](https://en.wikipedia.org/wiki/Single_sign-on). + +Also historically, wire has allowed team admins and owners to manage their users in the team management app. + +This does not scale as it requires a lot of manual labor for each user. + +The solution we offer to solve this issue is implementing {term}`SCIM` [(System for Cross-domain Identity Management)](https://en.wikipedia.org/wiki/System_for_Cross-domain_Identity_Management) + +{term}`SCIM` is an interface that allows both software (for example Active Directory) and custom scripts to manage Identities (users) in bulk. + +This page explains how to set up {term}`SCIM` and then use it. + +```{note} +Note that it is recommended to use both {term}`SSO` and {term}`SCIM` (as opposed to just {term}`SSO` alone). +The reason is if you only use {term}`SSO`, but do not configure/implement {term}`SCIM`, you will experience reduced functionality. +In particular, without {term}`SCIM` all Wire users will be named according their e-mail address and won't have any rich profiles. +See below in the {term}`SCIM` section for a more detailled explanation. +``` + +## Further reading + +If you can't find the answers to your questions here, we have a few +more documents. Some of them are very technical, some may not be up +to date any more, and we are planning to move many of them into this +page. But for now they may be worth checking out. + +- {ref}`Trouble shooting & FAQ ` +- +- +- + +## Definitions + +The following concepts need to be understood to use the present manual: + +```{eval-rst} +.. glossary:: + + SCIM + System for Cross-domain Identity Management (:term:`SCIM`) is a standard for automating the exchange of user identity information between identity domains, or IT systems. + + One example might be that as a company onboards new employees and separates from existing employees, they are added and removed from the company's electronic employee directory. :term:`SCIM` could be used to automatically add/delete (or, provision/de-provision) accounts for those users in external systems such as G Suite, Office 365, or Salesforce.com. Then, a new user account would exist in the external systems for each new employee, and the user accounts for former employees might no longer exist in those systems. + + See: `System for Cross-domain Identity Management at Wikipedia `_ + + In the context of Wire, SCIM is the interface offered by the Wire service (in particular the spar service) that allows for single or mass automated addition/removal of user accounts. + + SSO + + Single sign-on (:term:`SSO`) is an authentication scheme that allows a user to log in with a single ID and password to any of several organizationally related, yet independent, software systems. + + True single sign-on allows the user to log in once and access different, independent services without re-entering authentication factors. + + See: `Single-Sign-On at Wikipedia `_ + + SAML + + Security Assertion Markup Language (:term:`SAML`, pronounced SAM-el, /'sæməl/) is an open standard for exchanging authentication and authorization data between parties, in particular, between an identity provider and a service provider. :term:`SAML` is an XML-based markup language for security assertions (statements that service providers use to make access-control decisions). :term:`SAML` is also: + + * A set of XML-based protocol messages + * A set of protocol message bindings + * A set of profiles (utilizing all of the above) + + An important use case that :term:`SAML` addresses is web-browser `single sign-on (SSO) `_ . Single sign-on is relatively easy to accomplish within a security domain (using cookies, for example) but extending :term:`SSO` across security domains is more difficult and resulted in the proliferation of non-interoperable proprietary technologies. The `SAML Web Browser SSO `_ profile was specified and standardized to promote interoperability. + + See: `SAML at Wikipedia `_ + + In the context of Wire, SAML is the standard/protocol used by the Wire services (in particular the spar service) to provide the Single Sign On feature. + + IdP + + In the context of Wire, an identity provider (abbreviated :term:`IdP`) is a service that provides SAML single sign-on (:term:`SSO`) credentials that give users access to Wire. + + Curl + + :term:`Curl` (pronounced ":term:`Curl`") is a command line tool used to download files over the HTTP (web) protocol. For example, `curl http://wire.com` will download the ``wire.com`` web page. + + In this manual, it is used to contact API (Application Programming Interface) endpoints manually, where those endpoints would normally be accessed by code or other software. + + This can be used either for illustrative purposes (to "show" how the endpoints can be used) or to allow the manual execution of some simple tasks. + + For example (not a real endpoint) `curl http://api.wire.com/delete_user/thomas` would (schematically) execute the :term:`Curl` command, which would contact the wire.com API and delete the user named "thomas". + + Running this command in a terminal would cause the :term:`Curl` command to access this URL, and the API at that URL would execute the requested action. + + See: `curl at Wikipedia `__ + + + Spar + + The Wire backend software stack is composed of different services, `running as pods <../overview.html#focus-on-pods>`__ in a kubernetes cluster. + + One of those pods is the "spar" service. That service/pod is dedicated to the providing :term:`SSO` (using :term:`SAML`) and :term:`SCIM` services. This page is the manual for this service. + + In the context of :term:`SCIM`, Wire's spar service is the `Service Provider `__ that Identity Management Software + (for example Azure, Okta, Ping Identity, SailPoint, Technology Nexus, etc.) uses for user account provisioning and deprovisioning. +``` + +## User login for the first time with SSO + +{term}`SSO` allows users to register and log into Wire with their company credentials that they use on other software in their workplace. +No need to remember another password. + +When a team is set up on Wire, the administrators can provide users a login code or link that they can use to go straight to their company's login page. + +Here is what this looks from a user's perspective: + +1. Download Wire. +2. Select and copy the code that your company gave you / the administrator generated +3. Open Wire. Wire may detect the code on your clipboard and open a pop-up window with a text field. + Wire will automatically put the code into the text field. + If so, click Log in and go to step 8. +4. If no pop-up: click Login on the first screen. +5. Click Enterprise Login. +6. A pop-up will appear. In the text field, paste or type the code your company gave you. +7. Click Log in. +8. Wire will load your company's login page: log in with your company credentials. + +(saml-sso)= + +## SAML/SSO + +### Introduction + +SSO (Single Sign-On) is technology allowing users to sign into multiple services with a single identity provider/credential. + +SSO is about `authentication`, not `provisioning` (create, update, remove user accounts). To learn more about the latter, continue {ref}`below `. + +For example, if a company already has SSO setup for some of their services, and they start using Wire, they can use Wire's SSO support to add Wire to the set of services their users will be able to sign into with their existing SSO credentials. + +Here is a blog post we like about how SAML works: + +And here is a diagram that explains it in slightly more technical terms: + +```{image} Wire_SAML_Flow.png +``` + +Here is a critique of XML/DSig security (which SAML relies on): + +### Terminology and concepts + +- End + The browser carrries out all the redirections from the SP to the IdP and vice versa. +- Service Provider (SP): The entity (here Wire software) that provides its protected resource when an end user tries to access this resource. To accomplish the SAML based SSO authentication, the Service Provider + must have the Identity Provider's metadata. +- Identity Provider (IdP): Defines the entity that provides the user identities, including the ability to authenticate a user to get access to a protected resource / application from a Service Provider. To accomplish + the SAML based SSO authentication, the IdP must have the Service Provider's metadata. +- SAML Request: This is the authentication request generated by the Service Provider to request an authentication from the Identity Provider for verifying the user's identity. +- SAML Response: The SAML Response contains the cryptographically signed assertion of the authenticated user and is generated by the Identity Provider. + +(Definitons adapted from [collab.net](http://help.collab.net/index.jsp?topic=/teamforge178/action/saml.html)) + +(setting-up-sso-externally)= + +### Setting up SSO externally + +To set up {term}`SSO` for a given Wire installation, the Team owner/administrator must enable it. + +The first step is to configure the Identity Provider: you'll need to register Wire as a service provider in your Identity Provider. + +We've put together guides for registering with different providers: + +- Instructions for {ref}`Okta ` +- Instructions for {doc}`Centrify <../centrify/main>` +- Instructions for {doc}`Azure <../azure/main>` +- Some screenshots for {doc}`ADFS <../adfs/main>` +- {doc}`Generic instructions (try this if none of the above are applicable) <../generic-setup>` + +As you do this, make sure you take note of your {term}`IdP` metadata, which you will need for the next step. + +Once you are finished with registering Wire to your {term}`IdP`, move on to the next step, setting up {term}`SSO` internally. + +### Setting up SSO internally + +Now that you've registered Wire with your identity provider ({term}`IdP`), you can enable {term}`SSO` for your team on Wire. + +On Desktop: + +- Click Settings and click "Manage Team"; or go directly to teams.wire.com, or if you have an on-premise install, go to teams.\.com +- Login with your account credentials. +- Click "Customization". Here you will see the section for {term}`SSO`. +- Click the blue down arrow. +- Click "Add {term}`SAML` Connection". +- Provide the {term}`IdP` metadata. To find out more about retrieving this for your provider, see the guides in the "Setting up {term}`SSO` externally" step just above. +- Click "Save". +- Wire will now validate the document to set up the {term}`SAML` connection. +- If the data is valid, you will return to the Settings page. +- The page shows the information you need to log in with {term}`SSO`. Copy the login code or URL and send it to your team members or partners. For more information see: Logging in with {term}`SSO`. + +What to expect after {term}`SSO` is enabled: + +Anyone with a login through your {term}`SAML` identity provider ({term}`IdP`) and with access to the Wire app will be able to register and log in to your team using the {term}`SSO` Login URL and/or Code. + +Take care to share the code only with members of your team. + +If you haven't set up {term}`SCIM` ([we recommend you do](#introduction)), your team members can create accounts on Wire using {term}`SSO` simply by logging in, and will appear on the People tab of the team management page. + +If team members already have Wire accounts, use {term}`SCIM` to associate them with the {term}`SAML` credentials. If you make a mistake here, you may end up with several accounts for the same person. + +(user-provisioning-scim-ldap)= + +## User provisioning (SCIM/LDAP) + +SCIM/LDAP is about `provisioning` (create, update, remove user accounts), not `authentication`. To learn more about the latter, continue {ref}`above `. + +Wire supports the [SCIM](http://www.simplecloud.info/) ([RFC 7643](https://tools.ietf.org/html/rfc7643)) protocol to create, update and delete users. + +If your user data is stored in an LDAP data source like Active Directory or OpenLDAP, you can use our docker-base [ldap-scim-bridge](https://github.com/wireapp/ldap-scim-bridge/#use-via-docker) to connect it to wire. + +Note that connecting a SCIM client to Wire also disables the functionality to create new users in the SSO login process. This functionality is disabled when a token is created (see below) and re-enabled when all tokens have been deleted. + +To set up the connection of your SCIM client (e.g. Azure Active Directory) you need to provide + +1. The URL under which Wire's SCIM API is hosted: `https://prod-nginz-https.wire.com/scim/v2`. + If you are hosting your own instance of Wire then the URL is `https:///scim/v2`, where `` is where you are serving Wire's public endpoints. Some SCIM clients append `/v2` to the URL your provide. If this happens (check the URL mentioned in error messages of your SCIM client) then please provide the URL without the `/v2` suffix, i.e. `https://prod-nginz-https.wire.com/scim` or `https:///scim`. +2. A secret token which authorizes the use of the SCIM API. Use the [wire_scim_token.py](https://raw.githubusercontent.com/wireapp/wire-server/654b62e3be74d9dddae479178990ebbd4bc77b1e/docs/reference/provisioning/wire_scim_token.py) + script to generate a token. To run the script you need access to an user account with "admin" privileges that can login via email and password. Note that the token is independent from the admin account that created it, i.e. the token remains valid if the admin account gets deleted or changed. + +You need to configure your SCIM client to use the following mandatory SCIM attributes: + +1. Set the `userName` attribute to the desired user handle (the handle is shown + with an @ prefix in apps). It must be unique accross the entire Wire Cloud + (or unique on your own instance), and consist of the characters `a-z0-9_.-` + (no capital letters). + +2. Set the `displayName` attribute to the user's desired display name, e.g. "Jane Doe". + It must consist of 1-128 unicode characters. It does not need to be unique. + +3. The `externalId` attribute: + + 1. If you are using Wire's SAML SSO feature then set `externalId` attribute to the same identifier used for `NameID` in your SAML configuration. + 2. If you are using email/password authentication then set the `externalId` + attribute to the user's email address. The user will receive an invitation email during provisioning. Also note that the account will be set to `"active": false` until the user has accepted the invitation and activated the account. + +You can optionally make use of Wire's `urn:wire:scim:schemas:profile:1.0` extension field to store arbitrary user profile data that is shown in the users profile, e.g. department, role. See [docs](https://github.com/wireapp/wire-server/blob/develop/docs/reference/user/rich-info.md#scim-support-refrichinfoscim) for details. + +### SCIM management in Wire (in Team Management) + +#### SCIM security and authentication + +Wire uses a very basic variant of oauth, where a *bearer token* is presented to the server in header with all {term}`SCIM` requests. + +You can create such bearer tokens in team management and copy them from there into your the dashboard of your SCIM data source. + +#### Generating a SCIM token + +In order to be able to send SCIM requests to Wire, we first need to generate a SCIM token. This section explains how to do this. + +Once the token is generated, it should be noted/remembered, and it will be used in all subsequent SCIM uses/requests to authenticate the request as valid/authenticated. + +These are the steps to generate a new {term}`SCIM` token, which you will need to provide to your identity provider ({term}`IdP`), along with the target API URL, to enable {term}`SCIM` provisionning. + +- Step 1: Go to (Here replace "wire.com" with your own domain if you have an on-premise installation of Wire). + +```{image} token-step-01.png +:align: center +``` + +- Step 2: In the left menu, go to "Customization". + +```{image} token-step-02.png +:align: center +``` + +- Step 3: Go to "Automated User Management ({term}`SCIM`)" and click the "down" to expand + +```{image} token-step-03.png +:align: center +``` + +- Step 4: Click "Generate token", if your password is requested, enter it. + +```{image} token-step-04.png +:align: center +``` + +- Step 5: Once the token is generated, copy it into your clipboard and store it somewhere safe (eg., in the dashboard of your SCIM data source). + +```{image} token-step-05.png +:align: center +``` + +- Step 6: You're done! You can now view token information, delete the token, or create more tokens should you need them. + +```{image} token-step-06.png +:align: center +``` + +Tokens are now listed in this {term}`SCIM`-related area of the screen, you can generate up to 8 such tokens. + +### Using SCIM via Curl + +You can use the term:`Curl` command line HTTP tool to access tho wire backend (in particular the `spar` service) through the {term}`SCIM` API. + +This can be helpful to write your own tooling to interface with wire. + +#### Creating a SCIM token + +Before we can send commands to the {term}`SCIM` API/Spar service, we need to be authenticated. This is done through the creation of a {term}`SCIM` token. + +First, we need a little shell environment. Run the following in your terminal/shell: + +```{code-block} bash +:linenos: true + + export WIRE_BACKEND=https://prod-nginz-https.wire.com + export WIRE_ADMIN=... + export WIRE_PASSWD=... +``` + +Wire's SCIM API currently supports a variant of HTTP basic auth. + +In order to create a token in your team, you need to authenticate using your team admin credentials. + +The way this works behind the scenes in your browser or cell phone, and in plain sight if you want to use curl, is you need to get a Wire token. + +First install the `jq` command (): + +```bash +sudo apt install jq +``` + +```{note} +If you don't want to install `jq`, you can just call the `curl` command and copy the access token into the shell variable manually. +``` + +Then run: + +```{code-block} bash +:linenos: true + +export BEARER=$(curl -X POST \ +--header 'Content-Type: application/json' \ +--header 'Accept: application/json' \ +-d '{"email":"'"$WIRE_ADMIN"'","password":"'"$WIRE_PASSWD"'"}' \ +$WIRE_BACKEND/login'?persist=false' | jq -r .access_token) +``` + +This token will be good for 15 minutes; after that, just repeat the command above to get a new token. + +```{note} +SCIM requests are authenticated with a SCIM token, see below. SCIM tokens and Wire tokens are different things. + +A Wire token is necessary to get a SCIM token. SCIM tokens do not expire, but need to be deleted explicitly. +``` + +You can test that you are logged in with the following command: + +```bash +curl -X GET --header "Authorization: Bearer $BEARER" $WIRE_BACKEND/self +``` + +Now you are ready to create a SCIM token: + +```{code-block} bash +:linenos: true + +export SCIM_TOKEN_FULL=$(curl -X POST \ +--header "Authorization: Bearer $BEARER" \ +--header 'Content-Type: application/json;charset=utf-8' \ +-d '{ "description": "test '"`date`"'", "password": "'"$WIRE_PASSWD"'" }' \ +$WIRE_BACKEND/scim/auth-tokens) +export SCIM_TOKEN=$(echo $SCIM_TOKEN_FULL | jq -r .token) +export SCIM_TOKEN_ID=$(echo $SCIM_TOKEN_FULL | jq -r .info.id) +``` + +The SCIM token is now contained in the `SCIM_TOKEN` environment variable. + +You can look it up again with: + +```{code-block} bash +:linenos: true + +curl -X GET --header "Authorization: Bearer $BEARER" \ +$WIRE_BACKEND/scim/auth-tokens +``` + +And you can delete it with: + +```{code-block} bash +:linenos: true + +curl -X DELETE --header "Authorization: Bearer $BEARER" \ +$WIRE_BACKEND/scim/auth-tokens?id=$SCIM_TOKEN_ID +``` + +#### Using a SCIM token to Create Read Update and Delete (CRUD) users + +Now that you have your SCIM token, you can use it to talk to the SCIM API to manipulate (create, read, update, delete) users, either individually or in bulk. + +**JSON encoding of SCIM Users** + +In order to manipulate users using commands, you need to specify user data. + +A minimal definition of a user is written in JSON format and looks like this: + +```{code-block} json +:linenos: true + +{ + "schemas" : ["urn:ietf:params:scim:schemas:core:2.0:User"], + "externalId" : "nick@example.com", + "userName" : "nick", + "displayName" : "The Nick" +} +``` + +You can store it in a variable using this sort of command: + +```{code-block} bash +:linenos: true + +export SCIM_USER='{ + "schemas" : ["urn:ietf:params:scim:schemas:core:2.0:User"], + "externalId" : "nick@example.com", + "userName" : "nick", + "displayName" : "The Nick" +}' +``` + +The `externalId` is used to construct a SAML identity. Two cases are +currently supported: + +1. `externalId` contains a valid email address. + The SAML `NameID` has the form `me@example.com`. +2. `externalId` contains anything that is *not* an email address. + The SAML `NameID` has the form `...`. + +```{note} +It is important to configure your SAML provider to use `nameid-format:emailAddress` or `nameid-format:unspecified`. Other nameid formats are not supported at this moment. + +See [FAQ](https://docs.wire.com/how-to/single-sign-on/trouble-shooting.html#how-should-i-map-user-data-to-scim-attributes-when-provisioning-users-via-scim) +``` + +We also support custom fields that are used in rich profiles in this form (see: ): + +```{code-block} bash +:linenos: true + + export SCIM_USER='{ + "schemas" : ["urn:ietf:params:scim:schemas:core:2.0:User", "urn:wire:scim:schemas:profile:1.0"], + "externalId" : "rnick@example.com", + "userName" : "rnick", + "displayName" : "The Rich Nick", + "urn:wire:scim:schemas:profile:1.0": { + "richInfo": [ + { + "type": "Department", + "value": "Sales & Marketing" + }, + { + "type": "Favorite color", + "value": "Blue" + } + ] + } + }' +``` + +**How to create a user** + +You can create a user using the following command: + +```{code-block} bash +:linenos: true + + export STORED_USER=$(curl -X POST \ + --header "Authorization: Bearer $SCIM_TOKEN" \ + --header 'Content-Type: application/json;charset=utf-8' \ + -d "$SCIM_USER" \ + $WIRE_BACKEND/scim/v2/Users) + export STORED_USER_ID=$(echo $STORED_USER | jq -r .id) +``` + +Note that `$SCIM_USER` is in the JSON format and is declared before running this commend as described in the section above. + +**Get a specific user** + +```{code-block} bash +:linenos: true + + curl -X GET \ + --header "Authorization: Bearer $SCIM_TOKEN" \ + --header 'Content-Type: application/json;charset=utf-8' \ + $WIRE_BACKEND/scim/v2/Users/$STORED_USER_ID +``` + +**Search a specific user** + +SCIM user search is quite flexible. Wire currently only supports lookup by wire handle or email address. + +Email address (and/or SAML NameID, if /a): + +```{code-block} bash +:linenos: true + + curl -X GET \ + --header "Authorization: Bearer $SCIM_TOKEN" \ + --header 'Content-Type: application/json;charset=utf-8' \ + $WIRE_BACKEND/scim/v2/Users/'?filter=externalId%20eq%20%22me%40example.com%22' +``` + +Wire handle: same request, just replace the query part with + +```bash +'?filter=userName%20eq%20%22me%22' +``` + +**Update a specific user** + +For each put request, you need to provide the full json object. All omitted fields will be set to `null`. (If you do not have an up-to-date user present, just `GET` one right before the `PUT`.) + +```{code-block} bash +:linenos: true + + export SCIM_USER='{ + "schemas" : ["urn:ietf:params:scim:schemas:core:2.0:User"], + "externalId" : "rnick@example.com", + "userName" : "newnick", + "displayName" : "The New Nick" + }' +``` + +```{code-block} bash +:linenos: true + + curl -X PUT \ + --header "Authorization: Bearer $SCIM_TOKEN" \ + --header 'Content-Type: application/json;charset=utf-8' \ + -d "$SCIM_USER" \ + $WIRE_BACKEND/scim/v2/Users/$STORED_USER_ID +``` + +**Deactivate user** + +It is possible to temporarily deactivate an user (and reactivate him later) by setting his `active` property to `true/false` without affecting his device history. (`active=false` changes the wire user status to `suspended`.) + +**Delete user** + +```{code-block} bash +:linenos: true + + curl -X DELETE \ + --header "Authorization: Bearer $SCIM_TOKEN" \ + $WIRE_BACKEND/scim/v2/Users/$STORED_USER_ID +``` diff --git a/docs/src/how-to/single-sign-on/understand/main.rst b/docs/src/how-to/single-sign-on/understand/main.rst deleted file mode 100644 index 9893f631e1..0000000000 --- a/docs/src/how-to/single-sign-on/understand/main.rst +++ /dev/null @@ -1,558 +0,0 @@ - -Single sign-on and user provisioning ------------------------------------- - -.. contents:: - -Introduction -~~~~~~~~~~~~ - -This page is intended as a manual for administrator users in need of setting up :term:`SSO` and provisionning users using :term:`SCIM` on their installation of Wire. - -Historically and by default, Wire's user authentication method is via phone or password. This has security implications and does not scale. - -Solution: :term:`SSO` with :term:`SAML`! `(Security Assertion Markup Language) `_ - -:term:`SSO` systems allow users to identify on multiple systems (including Wire once configured as such) using a single ID and password. - -You can find some of the advantages of :term:`SSO` over more traditional schemes `here `_. - -Also historically, wire has allowed team admins and owners to manage their users in the team management app. - -This does not scale as it requires a lot of manual labor for each user. - -The solution we offer to solve this issue is implementing :term:`SCIM` `(System for Cross-domain Identity Management) `_ - -:term:`SCIM` is an interface that allows both software (for example Active Directory) and custom scripts to manage Identities (users) in bulk. - -This page explains how to set up :term:`SCIM` and then use it. - -.. note:: - Note that it is recommended to use both :term:`SSO` and :term:`SCIM` (as opposed to just :term:`SSO` alone). - The reason is if you only use :term:`SSO`, but do not configure/implement :term:`SCIM`, you will experience reduced functionality. - In particular, without :term:`SCIM` all Wire users will be named according their e-mail address and won't have any rich profiles. - See below in the :term:`SCIM` section for a more detailled explanation. - - -Further reading -~~~~~~~~~~~~~~~ - -If you can't find the answers to your questions here, we have a few -more documents. Some of them are very technical, some may not be up -to date any more, and we are planning to move many of them into this -page. But for now they may be worth checking out. - -- :ref:`Trouble shooting & FAQ ` -- https://support.wire.com/hc/en-us/sections/360000580658-Authentication -- https://github.com/wireapp/wire-server/blob/1753b790e5cfb2d35e857648c88bcad3ac329f01/docs/reference/spar-braindump.md -- https://github.com/wireapp/wire-server/tree/1753b790e5cfb2d35e857648c88bcad3ac329f01/docs/reference/provisioning/ - - -Definitions -~~~~~~~~~~~ - -The following concepts need to be understood to use the present manual: - -.. glossary:: - - SCIM - System for Cross-domain Identity Management (:term:`SCIM`) is a standard for automating the exchange of user identity information between identity domains, or IT systems. - - One example might be that as a company onboards new employees and separates from existing employees, they are added and removed from the company's electronic employee directory. :term:`SCIM` could be used to automatically add/delete (or, provision/de-provision) accounts for those users in external systems such as G Suite, Office 365, or Salesforce.com. Then, a new user account would exist in the external systems for each new employee, and the user accounts for former employees might no longer exist in those systems. - - See: `System for Cross-domain Identity Management at Wikipedia `_ - - In the context of Wire, SCIM is the interface offered by the Wire service (in particular the spar service) that allows for single or mass automated addition/removal of user accounts. - - SSO - - Single sign-on (:term:`SSO`) is an authentication scheme that allows a user to log in with a single ID and password to any of several organizationally related, yet independent, software systems. - - True single sign-on allows the user to log in once and access different, independent services without re-entering authentication factors. - - See: `Single-Sign-On at Wikipedia `_ - - SAML - - Security Assertion Markup Language (:term:`SAML`, pronounced SAM-el, /'sæməl/) is an open standard for exchanging authentication and authorization data between parties, in particular, between an identity provider and a service provider. :term:`SAML` is an XML-based markup language for security assertions (statements that service providers use to make access-control decisions). :term:`SAML` is also: - - * A set of XML-based protocol messages - * A set of protocol message bindings - * A set of profiles (utilizing all of the above) - - An important use case that :term:`SAML` addresses is web-browser `single sign-on (SSO) `_ . Single sign-on is relatively easy to accomplish within a security domain (using cookies, for example) but extending :term:`SSO` across security domains is more difficult and resulted in the proliferation of non-interoperable proprietary technologies. The `SAML Web Browser SSO `_ profile was specified and standardized to promote interoperability. - - See: `SAML at Wikipedia `_ - - In the context of Wire, SAML is the standard/protocol used by the Wire services (in particular the spar service) to provide the Single Sign On feature. - - IdP - - In the context of Wire, an identity provider (abbreviated :term:`IdP`) is a service that provides SAML single sign-on (:term:`SSO`) credentials that give users access to Wire. - - Curl - - :term:`Curl` (pronounced ":term:`Curl`") is a command line tool used to download files over the HTTP (web) protocol. For example, `curl http://wire.com` will download the ``wire.com`` web page. - - In this manual, it is used to contact API (Application Programming Interface) endpoints manually, where those endpoints would normally be accessed by code or other software. - - This can be used either for illustrative purposes (to "show" how the endpoints can be used) or to allow the manual execution of some simple tasks. - - For example (not a real endpoint) `curl http://api.wire.com/delete_user/thomas` would (schematically) execute the :term:`Curl` command, which would contact the wire.com API and delete the user named "thomas". - - Running this command in a terminal would cause the :term:`Curl` command to access this URL, and the API at that URL would execute the requested action. - - See: `curl at Wikipedia `__ - - - Spar - - The Wire backend software stack is composed of different services, `running as pods <../overview.html#focus-on-pods>`__ in a kubernetes cluster. - - One of those pods is the "spar" service. That service/pod is dedicated to the providing :term:`SSO` (using :term:`SAML`) and :term:`SCIM` services. This page is the manual for this service. - - In the context of :term:`SCIM`, Wire's spar service is the `Service Provider `__ that Identity Management Software - (for example Azure, Okta, Ping Identity, SailPoint, Technology Nexus, etc.) uses for user account provisioning and deprovisioning. - -User login for the first time with SSO -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:term:`SSO` allows users to register and log into Wire with their company credentials that they use on other software in their workplace. -No need to remember another password. - -When a team is set up on Wire, the administrators can provide users a login code or link that they can use to go straight to their company's login page. - -Here is what this looks from a user's perspective: - -1. Download Wire. -2. Select and copy the code that your company gave you / the administrator generated -3. Open Wire. Wire may detect the code on your clipboard and open a pop-up window with a text field. - Wire will automatically put the code into the text field. - If so, click Log in and go to step 8. -4. If no pop-up: click Login on the first screen. -5. Click Enterprise Login. -6. A pop-up will appear. In the text field, paste or type the code your company gave you. -7. Click Log in. -8. Wire will load your company's login page: log in with your company credentials. - - -.. _saml-sso: - -SAML/SSO -~~~~~~~~ - -Introduction -^^^^^^^^^^^^ - -SSO (Single Sign-On) is technology allowing users to sign into multiple services with a single identity provider/credential. - -SSO is about `authentication`, not `provisioning` (create, update, remove user accounts). To learn more about the latter, continue :ref:`below `. - -For example, if a company already has SSO setup for some of their services, and they start using Wire, they can use Wire's SSO support to add Wire to the set of services their users will be able to sign into with their existing SSO credentials. - -Here is a blog post we like about how SAML works: https://duo.com/blog/the-beer-drinkers-guide-to-saml - -And here is a diagram that explains it in slightly more technical terms: - -.. image:: Wire_SAML_Flow.png - -Here is a critique of XML/DSig security (which SAML relies on): https://www.cs.auckland.ac.nz/~pgut001/pubs/xmlsec.txt - -Terminology and concepts -^^^^^^^^^^^^^^^^^^^^^^^^ - -* End - The browser carrries out all the redirections from the SP to the IdP and vice versa. -* Service Provider (SP): The entity (here Wire software) that provides its protected resource when an end user tries to access this resource. To accomplish the SAML based SSO authentication, the Service Provider - must have the Identity Provider's metadata. -* Identity Provider (IdP): Defines the entity that provides the user identities, including the ability to authenticate a user to get access to a protected resource / application from a Service Provider. To accomplish - the SAML based SSO authentication, the IdP must have the Service Provider's metadata. -* SAML Request: This is the authentication request generated by the Service Provider to request an authentication from the Identity Provider for verifying the user's identity. -* SAML Response: The SAML Response contains the cryptographically signed assertion of the authenticated user and is generated by the Identity Provider. - -(Definitons adapted from `collab.net `_) - -.. _Setting up SSO externally: - -Setting up SSO externally -^^^^^^^^^^^^^^^^^^^^^^^^^ - -To set up :term:`SSO` for a given Wire installation, the Team owner/administrator must enable it. - -The first step is to configure the Identity Provider: you'll need to register Wire as a service provider in your Identity Provider. - -We've put together guides for registering with different providers: - -- Instructions for :ref:`Okta ` -- Instructions for :doc:`Centrify <../centrify/main>` -- Instructions for :doc:`Azure <../azure/main>` -- Some screenshots for :doc:`ADFS <../adfs/main>` -- :doc:`Generic instructions (try this if none of the above are applicable) <../generic-setup>` - -As you do this, make sure you take note of your :term:`IdP` metadata, which you will need for the next step. - -Once you are finished with registering Wire to your :term:`IdP`, move on to the next step, setting up :term:`SSO` internally. - -Setting up SSO internally -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Now that you've registered Wire with your identity provider (:term:`IdP`), you can enable :term:`SSO` for your team on Wire. - -On Desktop: - -* Click Settings and click "Manage Team"; or go directly to teams.wire.com, or if you have an on-premise install, go to teams..com -* Login with your account credentials. -* Click "Customization". Here you will see the section for :term:`SSO`. -* Click the blue down arrow. -* Click "Add :term:`SAML` Connection". -* Provide the :term:`IdP` metadata. To find out more about retrieving this for your provider, see the guides in the "Setting up :term:`SSO` externally" step just above. -* Click "Save". -* Wire will now validate the document to set up the :term:`SAML` connection. -* If the data is valid, you will return to the Settings page. -* The page shows the information you need to log in with :term:`SSO`. Copy the login code or URL and send it to your team members or partners. For more information see: Logging in with :term:`SSO`. - -What to expect after :term:`SSO` is enabled: - -Anyone with a login through your :term:`SAML` identity provider (:term:`IdP`) and with access to the Wire app will be able to register and log in to your team using the :term:`SSO` Login URL and/or Code. - -Take care to share the code only with members of your team. - -If you haven't set up :term:`SCIM` (`we recommend you do <#introduction>`_), your team members can create accounts on Wire using :term:`SSO` simply by logging in, and will appear on the People tab of the team management page. - -If team members already have Wire accounts, use :term:`SCIM` to associate them with the :term:`SAML` credentials. If you make a mistake here, you may end up with several accounts for the same person. - -.. _user-provisioning-scim-ldap: - -User provisioning (SCIM/LDAP) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -SCIM/LDAP is about `provisioning` (create, update, remove user accounts), not `authentication`. To learn more about the latter, continue :ref:`above `. - -Wire supports the `SCIM `__ (`RFC 7643 `__) protocol to create, update and delete users. - -If your user data is stored in an LDAP data source like Active Directory or OpenLDAP, you can use our docker-base `ldap-scim-bridge `__ to connect it to wire. - -Note that connecting a SCIM client to Wire also disables the functionality to create new users in the SSO login process. This functionality is disabled when a token is created (see below) and re-enabled when all tokens have been deleted. - -To set up the connection of your SCIM client (e.g. Azure Active Directory) you need to provide - -1. The URL under which Wire's SCIM API is hosted: ``https://prod-nginz-https.wire.com/scim/v2``. - If you are hosting your own instance of Wire then the URL is ``https:///scim/v2``, where ```` is where you are serving Wire's public endpoints. Some SCIM clients append ``/v2`` to the URL your provide. If this happens (check the URL mentioned in error messages of your SCIM client) then please provide the URL without the ``/v2`` suffix, i.e. ``https://prod-nginz-https.wire.com/scim`` or ``https:///scim``. - -2. A secret token which authorizes the use of the SCIM API. Use the `wire_scim_token.py `__ - script to generate a token. To run the script you need access to an user account with "admin" privileges that can login via email and password. Note that the token is independent from the admin account that created it, i.e. the token remains valid if the admin account gets deleted or changed. - -You need to configure your SCIM client to use the following mandatory SCIM attributes: - -1. Set the ``userName`` attribute to the desired user handle (the handle is shown - with an @ prefix in apps). It must be unique accross the entire Wire Cloud - (or unique on your own instance), and consist of the characters ``a-z0-9_.-`` - (no capital letters). - -2. Set the ``displayName`` attribute to the user's desired display name, e.g. "Jane Doe". - It must consist of 1-128 unicode characters. It does not need to be unique. - -3. The ``externalId`` attribute: - - a. If you are using Wire's SAML SSO feature then set ``externalId`` attribute to the same identifier used for ``NameID`` in your SAML configuration. - - b. If you are using email/password authentication then set the ``externalId`` - attribute to the user's email address. The user will receive an invitation email during provisioning. Also note that the account will be set to ``"active": false`` until the user has accepted the invitation and activated the account. - -You can optionally make use of Wire's ``urn:wire:scim:schemas:profile:1.0`` extension field to store arbitrary user profile data that is shown in the users profile, e.g. department, role. See `docs `__ for details. - -SCIM management in Wire (in Team Management) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -SCIM security and authentication -'''''''''''''''''''''''''''''''' - -Wire uses a very basic variant of oauth, where a *bearer token* is presented to the server in header with all :term:`SCIM` requests. - -You can create such bearer tokens in team management and copy them from there into your the dashboard of your SCIM data source. - -Generating a SCIM token -''''''''''''''''''''''' - -In order to be able to send SCIM requests to Wire, we first need to generate a SCIM token. This section explains how to do this. - -Once the token is generated, it should be noted/remembered, and it will be used in all subsequent SCIM uses/requests to authenticate the request as valid/authenticated. - -These are the steps to generate a new :term:`SCIM` token, which you will need to provide to your identity provider (:term:`IdP`), along with the target API URL, to enable :term:`SCIM` provisionning. - -* Step 1: Go to https://teams.wire.com/settings (Here replace "wire.com" with your own domain if you have an on-premise installation of Wire). - -.. image:: token-step-01.png - :align: center - -* Step 2: In the left menu, go to "Customization". - -.. image:: token-step-02.png - :align: center - -* Step 3: Go to "Automated User Management (:term:`SCIM`)" and click the "down" to expand - -.. image:: token-step-03.png - :align: center - -* Step 4: Click "Generate token", if your password is requested, enter it. - -.. image:: token-step-04.png - :align: center - -* Step 5: Once the token is generated, copy it into your clipboard and store it somewhere safe (eg., in the dashboard of your SCIM data source). - -.. image:: token-step-05.png - :align: center - -* Step 6: You're done! You can now view token information, delete the token, or create more tokens should you need them. - -.. image:: token-step-06.png - :align: center - -Tokens are now listed in this :term:`SCIM`-related area of the screen, you can generate up to 8 such tokens. - -Using SCIM via Curl -^^^^^^^^^^^^^^^^^^^ - -You can use the term:`Curl` command line HTTP tool to access tho wire backend (in particular the ``spar`` service) through the :term:`SCIM` API. - -This can be helpful to write your own tooling to interface with wire. - -Creating a SCIM token -''''''''''''''''''''' - -Before we can send commands to the :term:`SCIM` API/Spar service, we need to be authenticated. This is done through the creation of a :term:`SCIM` token. - -First, we need a little shell environment. Run the following in your terminal/shell: - -.. code-block:: bash - :linenos: - - export WIRE_BACKEND=https://prod-nginz-https.wire.com - export WIRE_ADMIN=... - export WIRE_PASSWD=... - -Wire's SCIM API currently supports a variant of HTTP basic auth. - -In order to create a token in your team, you need to authenticate using your team admin credentials. - -The way this works behind the scenes in your browser or cell phone, and in plain sight if you want to use curl, is you need to get a Wire token. - -First install the ``jq`` command (https://stedolan.github.io/jq/): - -.. code-block:: bash - - sudo apt install jq - -.. note:: - - If you don't want to install ``jq``, you can just call the ``curl`` command and copy the access token into the shell variable manually. - -Then run: - -.. code-block:: bash - :linenos: - - export BEARER=$(curl -X POST \ - --header 'Content-Type: application/json' \ - --header 'Accept: application/json' \ - -d '{"email":"'"$WIRE_ADMIN"'","password":"'"$WIRE_PASSWD"'"}' \ - $WIRE_BACKEND/login'?persist=false' | jq -r .access_token) - -This token will be good for 15 minutes; after that, just repeat the command above to get a new token. - -.. note:: - SCIM requests are authenticated with a SCIM token, see below. SCIM tokens and Wire tokens are different things. - - A Wire token is necessary to get a SCIM token. SCIM tokens do not expire, but need to be deleted explicitly. - -You can test that you are logged in with the following command: - -.. code-block:: bash - - curl -X GET --header "Authorization: Bearer $BEARER" $WIRE_BACKEND/self - -Now you are ready to create a SCIM token: - -.. code-block:: bash - :linenos: - - export SCIM_TOKEN_FULL=$(curl -X POST \ - --header "Authorization: Bearer $BEARER" \ - --header 'Content-Type: application/json;charset=utf-8' \ - -d '{ "description": "test '"`date`"'", "password": "'"$WIRE_PASSWD"'" }' \ - $WIRE_BACKEND/scim/auth-tokens) - export SCIM_TOKEN=$(echo $SCIM_TOKEN_FULL | jq -r .token) - export SCIM_TOKEN_ID=$(echo $SCIM_TOKEN_FULL | jq -r .info.id) - -The SCIM token is now contained in the ``SCIM_TOKEN`` environment variable. - -You can look it up again with: - -.. code-block:: bash - :linenos: - - curl -X GET --header "Authorization: Bearer $BEARER" \ - $WIRE_BACKEND/scim/auth-tokens - -And you can delete it with: - -.. code-block:: bash - :linenos: - - curl -X DELETE --header "Authorization: Bearer $BEARER" \ - $WIRE_BACKEND/scim/auth-tokens?id=$SCIM_TOKEN_ID - -Using a SCIM token to Create Read Update and Delete (CRUD) users -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Now that you have your SCIM token, you can use it to talk to the SCIM API to manipulate (create, read, update, delete) users, either individually or in bulk. - -**JSON encoding of SCIM Users** - -In order to manipulate users using commands, you need to specify user data. - -A minimal definition of a user is written in JSON format and looks like this: - -.. code-block:: json - :linenos: - - { - "schemas" : ["urn:ietf:params:scim:schemas:core:2.0:User"], - "externalId" : "nick@example.com", - "userName" : "nick", - "displayName" : "The Nick" - } - -You can store it in a variable using this sort of command: - -.. code-block:: bash - :linenos: - - export SCIM_USER='{ - "schemas" : ["urn:ietf:params:scim:schemas:core:2.0:User"], - "externalId" : "nick@example.com", - "userName" : "nick", - "displayName" : "The Nick" - }' - -The ``externalId`` is used to construct a SAML identity. Two cases are -currently supported: - -1. ``externalId`` contains a valid email address. - The SAML ``NameID`` has the form ``me@example.com``. -2. ``externalId`` contains anything that is *not* an email address. - The SAML ``NameID`` has the form ``...``. - -.. note:: - - It is important to configure your SAML provider to use ``nameid-format:emailAddress`` or ``nameid-format:unspecified``. Other nameid formats are not supported at this moment. - - See `FAQ `_ - -We also support custom fields that are used in rich profiles in this form (see: https://github.com/wireapp/wire-server/blob/develop/docs/reference/user/rich-info.md): - -.. code-block:: bash - :linenos: - - export SCIM_USER='{ - "schemas" : ["urn:ietf:params:scim:schemas:core:2.0:User", "urn:wire:scim:schemas:profile:1.0"], - "externalId" : "rnick@example.com", - "userName" : "rnick", - "displayName" : "The Rich Nick", - "urn:wire:scim:schemas:profile:1.0": { - "richInfo": [ - { - "type": "Department", - "value": "Sales & Marketing" - }, - { - "type": "Favorite color", - "value": "Blue" - } - ] - } - }' - -**How to create a user** - -You can create a user using the following command: - -.. code-block:: bash - :linenos: - - export STORED_USER=$(curl -X POST \ - --header "Authorization: Bearer $SCIM_TOKEN" \ - --header 'Content-Type: application/json;charset=utf-8' \ - -d "$SCIM_USER" \ - $WIRE_BACKEND/scim/v2/Users) - export STORED_USER_ID=$(echo $STORED_USER | jq -r .id) - -Note that ``$SCIM_USER`` is in the JSON format and is declared before running this commend as described in the section above. - -**Get a specific user** - -.. code-block:: bash - :linenos: - - curl -X GET \ - --header "Authorization: Bearer $SCIM_TOKEN" \ - --header 'Content-Type: application/json;charset=utf-8' \ - $WIRE_BACKEND/scim/v2/Users/$STORED_USER_ID - -**Search a specific user** - -SCIM user search is quite flexible. Wire currently only supports lookup by wire handle or email address. - -Email address (and/or SAML NameID, if /a): - -.. code-block:: bash - :linenos: - - curl -X GET \ - --header "Authorization: Bearer $SCIM_TOKEN" \ - --header 'Content-Type: application/json;charset=utf-8' \ - $WIRE_BACKEND/scim/v2/Users/'?filter=externalId%20eq%20%22me%40example.com%22' - -Wire handle: same request, just replace the query part with - -.. code-block:: bash - - '?filter=userName%20eq%20%22me%22' - -**Update a specific user** - -For each put request, you need to provide the full json object. All omitted fields will be set to ``null``. (If you do not have an up-to-date user present, just ``GET`` one right before the ``PUT``.) - -.. code-block:: bash - :linenos: - - export SCIM_USER='{ - "schemas" : ["urn:ietf:params:scim:schemas:core:2.0:User"], - "externalId" : "rnick@example.com", - "userName" : "newnick", - "displayName" : "The New Nick" - }' - -.. code-block:: bash - :linenos: - - curl -X PUT \ - --header "Authorization: Bearer $SCIM_TOKEN" \ - --header 'Content-Type: application/json;charset=utf-8' \ - -d "$SCIM_USER" \ - $WIRE_BACKEND/scim/v2/Users/$STORED_USER_ID - -**Deactivate user** - -It is possible to temporarily deactivate an user (and reactivate him later) by setting his ``active`` property to ``true/false`` without affecting his device history. (`active=false` changes the wire user status to `suspended`.) - -**Delete user** - -.. code-block:: bash - :linenos: - - curl -X DELETE \ - --header "Authorization: Bearer $SCIM_TOKEN" \ - $WIRE_BACKEND/scim/v2/Users/$STORED_USER_ID diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000000..c5b3d5b4db --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,46 @@ +% Wire documentation master file, created by +% sphinx-quickstart on Thu Jul 18 13:44:11 2019. +% You can adapt this file completely to your liking, but it should at least +% contain the root `toctree` directive. + +# Welcome to Wire's documentation! + +If you are a Wire end-user, please check out our [support pages](https://support.wire.com/). + +The targeted audience of this documentation is: + +- the curious power-user (people who want to understand how the server components of Wire work) +- on-premise operators/administrators (people who want to self-host Wire-Server on their own datacentres or cloud) +- developers (people who are working with the wire-server source code) + +If you are a developer, you may want to check out the "Notes for developers" first. + +This documentation may be expanded in the future to cover other aspects of Wire. + +```{toctree} +:caption: 'Contents:' +:glob: true +:maxdepth: 1 + +Release notes +Administrator's Guide +Understanding wire-server components +Single-Sign-On and user provisioning +Client API documentation +Security responses +Notes for developers +``` + +% Overview + +% commented out for now... + +% Indices and tables + +% ================== + +% * :ref:`genindex` + +% * :ref:`modindex` + +% * :ref:`search` diff --git a/docs/src/index.rst b/docs/src/index.rst deleted file mode 100644 index 1ec499e9c0..0000000000 --- a/docs/src/index.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. Wire documentation master file, created by - sphinx-quickstart on Thu Jul 18 13:44:11 2019. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to Wire's documentation! -=============================================== - -If you are a Wire end-user, please check out our `support pages `_. - -The targeted audience of this documentation is: - -* the curious power-user (people who want to understand how the server components of Wire work) -* on-premise operators/administrators (people who want to self-host Wire-Server on their own datacentres or cloud) -* developers (people who are working with the wire-server source code) - -If you are a developer, you may want to check out the "Notes for developers" first. - -This documentation may be expanded in the future to cover other aspects of Wire. - -.. toctree:: - :maxdepth: 1 - :caption: Contents: - :glob: - - Release notes - Administrator's Guide - Understanding wire-server components - Single-Sign-On and user provisioning - Client API documentation - Security responses - Notes for developers - -.. Overview - -.. commented out for now... - -.. Indices and tables -.. ================== - -.. * :ref:`genindex` -.. * :ref:`modindex` -.. * :ref:`search` diff --git a/docs/src/release-notes.rst b/docs/src/release-notes.md similarity index 51% rename from docs/src/release-notes.rst rename to docs/src/release-notes.md index 497af3cca3..478db87668 100644 --- a/docs/src/release-notes.rst +++ b/docs/src/release-notes.md @@ -1,14 +1,13 @@ -.. _release-notes: +(release-notes)= -Release notes -------------- +# Release notes This page previously contained the release notes for the project, and they were manually updated each time a new release was done, due to limitations in Github's «releases» feature. -However, Github since updated the feature, making this page un-necessary. +However, Github since updated the feature, making this page un-necessary. -Go to → `GitHub - wireapp/wire-server: Wire back-end services `_ +Go to → [GitHub - wireapp/wire-server: Wire back-end services](https://github.com/wireapp/wire-server/) -→ Look at releases on right hand side. They are shown by date of release. `Release Notes `_ +→ Look at releases on right hand side. They are shown by date of release. [Release Notes](https://github.com/wireapp/wire-server/releases) -→ Open the CHANGELOG.md. This will give you chart version. \ No newline at end of file +→ Open the CHANGELOG.md. This will give you chart version. diff --git a/docs/src/security-responses/2021-12-15_log4shell.md b/docs/src/security-responses/2021-12-15_log4shell.md new file mode 100644 index 0000000000..b567c21e74 --- /dev/null +++ b/docs/src/security-responses/2021-12-15_log4shell.md @@ -0,0 +1,90 @@ +# 2021-12 - log4shell + +Last updated: 2021-12-15 + +This page concerns ON-PREMISE (i.e. self-hosted) installations of wire-server as documented in and its possible vulnerability to “log4shell” / CVE-2021-44228 and CVE-2021-45046. + +## Introduction + +The “log4shell” vulnerability ([CVE-2021-44228](https://www.cve.org/CVERecord?id=CVE-2021-44228) and [CVE-2021-45046](https://www.cve.org/CVERecord?id=CVE-2021-45046)) concerns a logging library “log4j” used in Java or JVM software components. + +- Wire-server’s source code is not written in a JVM language (it's written mostly in Haskell), and as such, is not vulnerable. + +- Wire-server makes use of Cassandra, which is running on the JVM, however as of version 2.1 no longer makes use of log4j (it uses logback). Since the start of Wire’s on-premise product, we have used Cassandra versions > 3 (currently 3.11), which is not vulnerable. + +- Wire-server makes use of **Elasticsearch**, which **does use log4j. See the section below for details**. + +- All other components Wire-server’s on-premise current and near-time-future product relies on are not based on the JVM and as such are not vulnerable: + + > - Calling restund/SFT servers: written in C + > - Minio: written in Go + > - Redis: written in C + > - Nginx: written in C + > - Wire-Server: written in Haskell + > - Wire-Frontend (webapp, team settings): written in Javascript / NodeJS + > - Fake-aws components: based on localstack written in python or for SQS written in ruby + > - fake-aws-dynamodb: this component is JVM based and was used in the past on on-premise installations, but should not be in use anymore these days. If it is still in use in your environment, please stop using it: all recent versions of wire-server since June 2021 will not make use of that component anymore. Even if still in use, it does not store or log any user-provided data nor is it internet-facing and as such should pose little to no risk. + > - Upcoming releases may have wire-server-metrics: prometheus (Ruby), node-exporter (Golang) and Grafana (Golang) + > - Upcoming releases may have: Logging/Kibana: fluent-bit (C), Kibana (JavaScript), ElasticSearch (covered in section below) + +## Elasticsearch + +Wire uses Elasticsearch for for storing indexes used when searching for users in Wire. + +Elasticsearch clusters are not directly user-facing or internet-facing and it is therefore not immediately possible to inject problematic exploit strings into elasticsearch’s own logging (i.e. elasticsearch stores user-provided data, but doesn’t itself log this data). + +*Example: A Wire user display name will be stored inside elasticsearch, but not logged by elasticsearch (elasticsearch logs mostly contain information about connectivity to other elasticsearch processes)* + +Hypothetically, the log4shell exploit could be combined with another exploit which would allow an attacker to get Elasticsearch to log some of the data stored inside its cluster. As elasticsearch is not internet-facing, this doesn’t look easy to exploit. + +In addition as per Elastics’s [own information on the matter](https://discuss.elastic.co/t/apache-log4j2-remote-code-execution-rce-vulnerability-cve-2021-44228-esa-2021-31/291476) + +> "Elasticsearch 6 and 7 are not susceptible to remote code execution with this vulnerability due to our use of the Java Security Manager. Investigation into Elasticsearch 5 is ongoing. Elasticsearch running on JDK8 or below is susceptible to an information leak via DNS which is fixable by the JVM property identified below. The JVM option identified below is effective for Elasticsearch versions 5.5+, 6.5+, and 7+" + +The JVM property referred to is `-Dlog4j2.formatMsgNoLookups=true` + +[Update 15th December about CVE-2021-45046 from Elasitic](https://discuss.elastic.co/t/apache-log4j2-remote-code-execution-rce-vulnerability-cve-2021-44228-esa-2021-31/291476): + +> "Update 15 December: A further vulnerability (CVE-2021-45046) was disclosed on December 14th after it was found that the fix to address CVE-2021-44228 in Apache Log4j 2.15.0 was incomplete in certain non-default configurations. Our guidance for Elasticsearch \[...\] are unchanged by this new vulnerability" + +Wire on-premise installations contain a version of Elasticsearch between \[`6.6.0` and `6.8.18`\] at the time of writing. + +**As such, while ElasticSearch is affected, it is A. only susceptible to an information leak, not to remote code execution and B. not easily exploitable due to the way Wire uses ElasticSearch.** + +Still, if you’d like to avoid even the potential information leak problem: + +## Disable log4jLookups: + +If you have followed our official documentation on [https://docs.wire.com](https://docs.wire.com), then Elasticsearch on premise was set up using [wire-server-deploy](https://github.com/wireapp/wire-server-deploy) using the `./ansible/elasticsearch.yml` playbook, which installs a vulnerable Log4J `2.11.1`: + +``` +find / | grep -i log4j +./etc/elasticsearch/HOSTNAME/log4j2.properties +./usr/share/elasticsearch/lib/log4j-core-2.11.1.jar +./usr/share/elasticsearch/lib/log4j-1.2-api-2.11.1.jar +./usr/share/elasticsearch/lib/log4j-api-2.11.1.jar +``` + +The BSI [recommends](https://www.bsi.bund.de/SharedDocs/Cybersicherheitswarnungen/DE/2021/2021-549032-10F2.pdf?__blob=publicationFile&v=3) to mitigate setting the `log4j2.formatMsgNoLookups` to True in the JVM options. Elastic [recommends](https://discuss.elastic.co/t/apache-log4j2-remote-code-execution-rce-vulnerability-cve-2021-44228-esa-2021-31/291476) the same mitigation. + +You can do this in the concrete Wire on-premise case using: + +First, ssh to all your elasticsearch machines and do the following: + +```shell +find /etc/elasticsearch | grep jvm.options + +# set this variable with the filepath found from above, usually something like +# /etc/elasticsearch//jvm.options +JVM_OPTIONS_FILE= + +# run the following to add the mitigation log4j flag (command is idempotent) +grep "\-Dlog4j2.formatMsgNoLookups=True" "$JVM_OPTIONS_FILE" || echo "-Dlog4j2.formatMsgNoLookups=True" >> "$JVM_OPTIONS_FILE" +``` + +Next, restart your cluster using instructions provided in {ref}`restart-elasticsearch`. + +## Further information + +- A mitigation for this with fresh on-premise installations is introduced in [https://github.com/wireapp/wire-server-deploy/pull/526](https://github.com/wireapp/wire-server-deploy/pull/526) +- We have of course fully applied the above counter measures to our cloud offering. We have no evidence that this vulnerability was used to launch an attack before this. Any hypothetical undetected attack would have required additional security vulnerabilities to be successful. diff --git a/docs/src/security-responses/2021-12-15_log4shell.rst b/docs/src/security-responses/2021-12-15_log4shell.rst deleted file mode 100644 index 741d2622cc..0000000000 --- a/docs/src/security-responses/2021-12-15_log4shell.rst +++ /dev/null @@ -1,103 +0,0 @@ -2021-12 - log4shell --------------------- - -Last updated: 2021-12-15 - -This page concerns ON-PREMISE (i.e. self-hosted) installations of wire-server as documented in https://docs.wire.com and its possible vulnerability to “log4shell” / CVE-2021-44228 and CVE-2021-45046. - -Introduction -~~~~~~~~~~~~~ - -The “log4shell” vulnerability (`CVE-2021-44228 `__ and `CVE-2021-45046 `__) concerns a logging library “log4j” used in Java or JVM software components. - -* Wire-server’s source code is not written in a JVM language (it's written mostly in Haskell), and as such, is not vulnerable. - -* Wire-server makes use of Cassandra, which is running on the JVM, however as of version 2.1 no longer makes use of log4j (it uses logback). Since the start of Wire’s on-premise product, we have used Cassandra versions > 3 (currently 3.11), which is not vulnerable. - -* Wire-server makes use of **Elasticsearch**, which **does use log4j. See the section below for details**. - -* All other components Wire-server’s on-premise current and near-time-future product relies on are not based on the JVM and as such are not vulnerable: - - * Calling restund/SFT servers: written in C - - * Minio: written in Go - - * Redis: written in C - - * Nginx: written in C - - * Wire-Server: written in Haskell - - * Wire-Frontend (webapp, team settings): written in Javascript / NodeJS - - * Fake-aws components: based on localstack written in python or for SQS written in ruby - - * fake-aws-dynamodb: this component is JVM based and was used in the past on on-premise installations, but should not be in use anymore these days. If it is still in use in your environment, please stop using it: all recent versions of wire-server since June 2021 will not make use of that component anymore. Even if still in use, it does not store or log any user-provided data nor is it internet-facing and as such should pose little to no risk. - - * Upcoming releases may have wire-server-metrics: prometheus (Ruby), node-exporter (Golang) and Grafana (Golang) - - * Upcoming releases may have: Logging/Kibana: fluent-bit (C), Kibana (JavaScript), ElasticSearch (covered in section below) - -Elasticsearch -~~~~~~~~~~~~~ - -Wire uses Elasticsearch for for storing indexes used when searching for users in Wire. - -Elasticsearch clusters are not directly user-facing or internet-facing and it is therefore not immediately possible to inject problematic exploit strings into elasticsearch’s own logging (i.e. elasticsearch stores user-provided data, but doesn’t itself log this data). - -*Example: A Wire user display name will be stored inside elasticsearch, but not logged by elasticsearch (elasticsearch logs mostly contain information about connectivity to other elasticsearch processes)* - -Hypothetically, the log4shell exploit could be combined with another exploit which would allow an attacker to get Elasticsearch to log some of the data stored inside its cluster. As elasticsearch is not internet-facing, this doesn’t look easy to exploit. - -In addition as per Elastics’s `own information on the matter `__ - - "Elasticsearch 6 and 7 are not susceptible to remote code execution with this vulnerability due to our use of the Java Security Manager. Investigation into Elasticsearch 5 is ongoing. Elasticsearch running on JDK8 or below is susceptible to an information leak via DNS which is fixable by the JVM property identified below. The JVM option identified below is effective for Elasticsearch versions 5.5+, 6.5+, and 7+" - -The JVM property referred to is ``-Dlog4j2.formatMsgNoLookups=true`` - -`Update 15th December about CVE-2021-45046 from Elasitic `__: - - "Update 15 December: A further vulnerability (CVE-2021-45046) was disclosed on December 14th after it was found that the fix to address CVE-2021-44228 in Apache Log4j 2.15.0 was incomplete in certain non-default configurations. Our guidance for Elasticsearch [...] are unchanged by this new vulnerability" - -Wire on-premise installations contain a version of Elasticsearch between [``6.6.0`` and ``6.8.18``] at the time of writing. - -**As such, while ElasticSearch is affected, it is A. only susceptible to an information leak, not to remote code execution and B. not easily exploitable due to the way Wire uses ElasticSearch.** - -Still, if you’d like to avoid even the potential information leak problem: - -Disable log4jLookups: -~~~~~~~~~~~~~~~~~~~~~ - -If you have followed our official documentation on ``__, then Elasticsearch on premise was set up using `wire-server-deploy `__ using the ``./ansible/elasticsearch.yml`` playbook, which installs a vulnerable Log4J ``2.11.1``:: - - find / | grep -i log4j - ./etc/elasticsearch/HOSTNAME/log4j2.properties - ./usr/share/elasticsearch/lib/log4j-core-2.11.1.jar - ./usr/share/elasticsearch/lib/log4j-1.2-api-2.11.1.jar - ./usr/share/elasticsearch/lib/log4j-api-2.11.1.jar - -The BSI `recommends `__ to mitigate setting the ``log4j2.formatMsgNoLookups`` to True in the JVM options. Elastic `recommends `__ the same mitigation. - -You can do this in the concrete Wire on-premise case using: - -First, ssh to all your elasticsearch machines and do the following: - -.. code:: shell - - find /etc/elasticsearch | grep jvm.options - - # set this variable with the filepath found from above, usually something like - # /etc/elasticsearch//jvm.options - JVM_OPTIONS_FILE= - - # run the following to add the mitigation log4j flag (command is idempotent) - grep "\-Dlog4j2.formatMsgNoLookups=True" "$JVM_OPTIONS_FILE" || echo "-Dlog4j2.formatMsgNoLookups=True" >> "$JVM_OPTIONS_FILE" - -Next, restart your cluster using instructions provided in :ref:`restart-elasticsearch`. - -Further information -~~~~~~~~~~~~~~~~~~~ - -* A mitigation for this with fresh on-premise installations is introduced in ``__ - -* We have of course fully applied the above counter measures to our cloud offering. We have no evidence that this vulnerability was used to launch an attack before this. Any hypothetical undetected attack would have required additional security vulnerabilities to be successful. diff --git a/docs/src/security-responses/index.md b/docs/src/security-responses/index.md new file mode 100644 index 0000000000..a0c58f66ff --- /dev/null +++ b/docs/src/security-responses/index.md @@ -0,0 +1,14 @@ +(security-responses)= + +# Security responses + +% comment: The toctree directive below takes a list of the pages you want to appear in order, +% and '*' is used to include any other pages in the federation directory in alphabetical order + +```{toctree} +:glob: true +:maxdepth: 1 +:reversed: true + +* +``` diff --git a/docs/src/security-responses/index.rst b/docs/src/security-responses/index.rst deleted file mode 100644 index 1c1e3077c0..0000000000 --- a/docs/src/security-responses/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _security_responses: - -++++++++++++++++++ -Security responses -++++++++++++++++++ - -.. - comment: The toctree directive below takes a list of the pages you want to appear in order, - and '*' is used to include any other pages in the federation directory in alphabetical order - -.. toctree:: - :maxdepth: 1 - :glob: - :reversed: - - * diff --git a/docs/src/understand/api-client-perspective/authentication.md b/docs/src/understand/api-client-perspective/authentication.md new file mode 100644 index 0000000000..51cb738b85 --- /dev/null +++ b/docs/src/understand/api-client-perspective/authentication.md @@ -0,0 +1,435 @@ +# Authentication + +% useful vim replace commands when porting markdown -> restructured text: + +% :%s/.. raw:: html//g + +% :%s/ /.. _\1:/gc + +## Access Tokens + +The authentication protocol used by the API is loosely inspired by the +[OAuth2 protocol](http://oauth.net/2/). As such, API requests are +authorised through so-called [bearer +tokens](https://tools.ietf.org/html/rfc6750). For as long as a bearer +token is valid, it grants access to the API under the identity of the +user whose credentials have been used for the [login]. The +current validity of access tokens is `15 minutes`, however, that may +change at any time without prior notice. + +In order to obtain new access tokens without having to ask the user for +his credentials again, so-called "user tokens" are issued which are +issued in the form of a `zuid` HTTP +[cookie](https://en.wikipedia.org/wiki/HTTP_cookie). These cookies +have a long lifetime (if {ref}`persistent ` typically +at least a few months) and their use is strictly limited to the +{ref}`/access ` endpoint used for token refresh. +{ref}`Persistent ` access cookies are regularly +refreshed as part of an {ref}`access token refresh `. + +An access cookie is obtained either directly after registration or through a +subsequent {ref}`login `. A successful login provides both an access +cookie and and access token. Both access token and cookie must be stored safely +and kept confidential. User passwords should not be stored. + +As of yet, there is no concept of authorising third-party applications to +perform operations on the API on behalf of a user (Notable exceptions: +{ref}`sso`). Such functionality may be provided in the future through +standardised OAuth2 flows. + +To authorise an API request, the access token must be provided via the +HTTP `Authorization` header with the `Bearer` scheme as follows: + +``` +Authorization: Bearer fmmLpDSjArpksFv57r5rDrzZZlj... +``` + +While the API currently also supports passing the access token in the +query string of a request, this approach is highly discouraged as it +unnecessarily exposes access tokens (e.g. in server logs) and thus might +be removed in the future. + +(login)= + +## Login - `POST /login` + +A login is the process of authenticating a user either through a known secret in +a {ref}`password login ` or by proving ownership of a verified +phone number associated with an account in an {ref}`SMS login `. The +response to a successful login contains an access cookie in a `Set-Cookie` +header and an access token in the JSON response body. + +(login-cookies)= + +### Cookies + +There is a hard limit for the number of session-scoped access cookies and the same +amount of persistent access cookies per user account. When this number is +reached, old cookies are removed when new ones are issued. Thereby, the cookies +with the oldest expiration timestamp are removed first. The removal takes the +type of the cookie to issue into account. I.e. session cookies are replaced by +session cookies, persistent cookies are replaced by persistent cookies. + +To prevent performance issues and malicious usages of the API, there is a +throttling mechanism in place. When the maximum number of cookies of one type +are issued, it's checked that login calls don't happen too frequently (too +quickly after one another.) + +In case of throttling no cookie gets issued. The error response ([HTTP status +code 429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429)) has +a `Retry-After` header which specifies the time to wait before accepting the +next request in Seconds. + +Being throttled is a clear indicator of incorrect API usage. There is no need to +login many times in a row on the same device. Instead, the cookie should be +re-used. + +The corresponding backend configuration settings are described in: +{ref}`auth-cookie-config` . + +(login-password)= + +### Password Login + +To perform a password login, send a `POST` request to the `/login` +endpoint, providing either a verified email address or phone number and +the corresponding password. For example: + +``` +POST /login HTTP/1.1 +[headers omitted] + +{ + "email": "me@wire.com", + "password": "Quo2Booz" +} +``` + +If a phone number is used, the `phone` field is used instead of +`email`. If a @handle is used, the `handle` field is used instead of +`email` (note that the handle value should be sent *without* the `@` +symbol). Assuming the credentials are correct, the API will respond with +a `200 OK` and an access token and cookie: + +``` +HTTP/1.1 200 OK +zuid=...; Expires=Fri, 02-Aug-2024 09:15:54 GMT; Domain=zinfra.io; Path=/access; HttpOnly; Secure +[other headers omitted] + +{ + "expires_in": 900, + "access_token": "fmmLpDSjArpksFv57r5rDrzZZlj...", + "token_type": "Bearer" +} +``` + +% + +> The `Domain` of the cookie will be different depending on the +> environment. + +The value of `expires_in` is the number of seconds that the +`access_token` is valid from the moment it was issued. + +As of yet, the `token_type` is always `Bearer`. + +(login-sms)= + +### SMS Login + +To perform an SMS login, first request an SMS code to be sent to a +verified phone number: + +``` +POST /login/send HTTP/1.1 +[headers omitted] + +{ + "phone": "+1234567890" +} +``` + +An SMS with a short-lived login code will be sent. Upon receiving the +SMS and extracting the code from it, the login can be performed using +the `phone` and `code` as follows: + +``` +POST /login HTTP/1.1 +[headers omitted] + +{ + "phone": "+1234567890", + "code": "123456" +} +``` + +A successful response is identical to that of a {ref}`password +login `. + +(login-persistent)= + +### Persistent Logins + +By default, access cookies are issued as [session +cookies](https://en.wikipedia.org/wiki/HTTP_cookie#Session_cookie) +with a validity of 1 week. Furthermore, these session cookies are not +refreshed as part of an {ref}`access token refresh `. To +request a `persistent` access cookie which does get refreshed, specify +the `persist=true` parameter during a login: + +``` +POST /login?persist=true HTTP/1.1 +[headers omitted] + +{ + "phone": "+1234567890", + "code": "123456" +} +``` + +All access cookies returned on registration are persistent. + +(token-refresh)= + +### FAQ: is my cookie a persistent cookie or a session cookie? + +When you log in **without** the `persist=true` query parameter, or +with persist=false, you get a `session cookie`, which means it has no +expiration date set, and will expire when you close the browser (and on +the backend has a validity of max 1 day or 1 week (configurable, see +current config in [hegemony](https://github.com/zinfra/hegemony)). +Example **session cookie**: + +``` +POST /login?persist=false + +Set-Cookie: zuid=(redacted); Path=/access; Domain=zinfra.io; HttpOnly; Secure +``` + +When you log in **with** `persist=true`, you get a persistent cookie, +which means it has *some* expiration date. In production this is +currently 56 days (again, configurable, check current config in +[hegemony](https://github.com/zinfra/hegemony)) and can be renewed +during token refresh. Example **persistent cookie**: + +``` +POST /login?persist=true + +Set-Cookie: zuid=(redacted); Path=/access; Expires=Thu, 10-Jan-2019 10:43:28 GMT; Domain=zinfra.io; HttpOnly; Secure +``` + +## Token Refresh - `POST /access` + +Since access tokens have a relatively short lifetime to limit the time +window of abuse for a captured token, they need to be regularly +refreshed. In order to refresh an access token, send a `POST` reques +to `/access`, including the access cookie in the `Cookie` header and +the old (possibly expired) access token in the `Authorization` header: + +``` +POST /access HTTP/1.1 +Authorization: Bearer fmmLpDSjArpksFv57r5rDrzZZlj... +Cookie: zuid=... +[other headers omitted] + + +``` + +Providing the old access token is not required but strongly recommended +as it will link the new access token to the old, enabling the API to see +the new access token as a continued session of the same client. + +As part of an access token refresh, the response may also contain a new +`zuid` access cookie in form of a `Set-Cookie` header. A client must +expect a new `zuid` cookie as part of any access token refresh and +replace the existing cookie appropriately. + +(cookies-1)= + +## Cookie Management + +(cookies-logout)= + +### Logout - `POST /access/logout` + +An explicit logout effectively deletes the cookie used to perform the +operation: + +``` +POST /access/logout HTTP/1.1 +Authorization: Bearer fmmLpDSjArpksFv57r5rDrzZZlj... +Cookie: zuid=... +[other headers omitted] + + +``` + +Afterwards, the cookie that was sent as part of the `Cookie` header is +no longer valid. + +If a client offers an explicit logout, this operation must be performed. +An explicit logout is especially important for Web clients. + +(cookies-labels)= + +### Labels + +Cookies can be labeled by specifying a `label` during login or +registration, e.g.: + +``` +POST /login?persist=true HTTP/1.1 +[headers omitted] + +{ + "phone": "+1234567890", + "code": "123456", + "label": "Google Nexus 5" +} +``` + +Specifying a label is recommended as it helps to identify the cookies in a +user-friendly way and allows {ref}`selective revocation ` based +on the labels. + +(cookies-list)= + +### Listing Cookies - `GET /cookies` + +To list the cookies currently associated with an account, send a `GET` +request to `/cookies`. The response will contain a list of cookies, +e.g.: + +``` +HTTP/1.1 200 OK +[other headers omitted] + +{ + "cookies": [ + { + "time": "2015-06-04T14:29:23.000Z", + "id": 967153183, + "type": "session", + "label": null + }, + { + "time": "2015-06-04T14:44:23.000Z", + "id": 942451749, + "type": "session", + "label": null + }, + ... + ] +} +``` + +Note that expired cookies are not automatically removed when they +expire, only as new cookies are issued. + +(cookies-revoke)= + +### Revoking Cookies - `POST /cookies/remove` + +Cookies can be removed individually or in bulk either by specifying the full +cookie structure as it is returned by {ref}`GET /cookies ` or only +by their labels in a `POST` request to `/cookies/remove`, alongside with the +user's credentials: + +``` +POST /cookies/remove HTTP/1.1 +[headers omitted] + +{ + "ids": [{}, {}, ...], + "labels": ["", "", ...] + "email": "me@wire.com", + "password": "secret" +} +``` + +Cookie removal currently requires an account with an email address and +password. + +(password-reset)= + +## Password Reset - `POST /password-reset` + +A password reset can be used to set a new password if the existing password +associated with an account has been forgotten. This is not to be confused with +the act of merely changing your password for the purpose of password rotation or +if you suspect your current password to be compromised. + +### Initiate a Password Reset + +To initiate a password reset, send a `POST` request to +`/password-reset`, specifying either a verified email address or phone +number for the account in question: + +``` +POST /password-reset HTTP/1.1 +[headers omitted] + +{ + "phone": "+1234567890" +} +``` + +For a phone number, the `phone` field would be used instead. As a +result of a successful request, either a password reset key and code is +sent via email or a password reset code is sent via SMS, depending on +whether an email address or a phone number was provided. Password reset +emails will contain a link to the [wire.com](https://www.wire.com/) +website which will guide the user through the completion of the password +reset, which means that the website will perform the necessary requests +to complete the password reset. To complete a password reset initiated +with a phone number, the completion of the password reset has to happen +from the mobile client application itself. + +Once a password reset has been initiated for an email address or phone +number, no further password reset can be initiated for the same email +address or phone number before the prior reset is completed or times +out. The current timeout for an initiated password reset is +`10 minutes`. + +### Complete a Password Reset + +To complete a password reset, the password reset code, together with the +new password and the `email` or `phone` used when initiating the +reset (or the opaque `key` sent by mail) are sent to +`/password-reset/complete` in a `POST` request: + +``` +POST /password-reset/complete HTTP/1.1 +[headers omitted] + +{ + "phone": "+1234567890", + "code": "123456", + "password": "new-secret-password" +} +``` + +There is a maximum of `3` attempts at completing a password reset, +after which the password reset code becomes invalid and a new password +reset must be initiated. + +A completed password reset results in all access cookies to be revoked, +requiring the user to {ref}`login `. + +## Related topics: SSO, Legalhold + +(sso)= + +### Single Sign-On + +Users that are part of a team, for which a team admin has configured SSO (Single Sign On), authentication can happen through SAML. + +More information: + +- {ref}`FAQ ` +- [setup howtos for various IdP vendors](https://docs.wire.com/how-to/single-sign-on/index.html) +- [a few fragments that may help admins](https://github.com/wireapp/wire-server/blob/develop/docs/reference/spar-braindump.md) + +### LegalHold + +Users that are part of a team, for which a team admin has configured "LegalHold", can add a so-called "LegalHold" device. The endpoints in use to authenticate for a "LegalHold" Device are the same as for regular users, but the access tokens they get can only use a restricted set of API endpoints. See also [legalhold documentation on wire-server](https://github.com/wireapp/wire-server/blob/develop/docs/reference/team/legalhold.md) diff --git a/docs/src/understand/api-client-perspective/authentication.rst b/docs/src/understand/api-client-perspective/authentication.rst deleted file mode 100644 index 9e34aa8e23..0000000000 --- a/docs/src/understand/api-client-perspective/authentication.rst +++ /dev/null @@ -1,472 +0,0 @@ -Authentication -============== - -.. useful vim replace commands when porting markdown -> restructured text: -.. :%s/.. raw:: html//g -.. :%s/ /.. _\1:/gc - -Access Tokens -------------- - -The authentication protocol used by the API is loosely inspired by the -`OAuth2 protocol `__. As such, API requests are -authorised through so-called `bearer -tokens `__. For as long as a bearer -token is valid, it grants access to the API under the identity of the -user whose credentials have been used for the login_. The -current validity of access tokens is ``15 minutes``, however, that may -change at any time without prior notice. - -In order to obtain new access tokens without having to ask the user for -his credentials again, so-called "user tokens" are issued which are -issued in the form of a ``zuid`` HTTP -`cookie `__. These cookies -have a long lifetime (if :ref:`persistent ` typically -at least a few months) and their use is strictly limited to the -:ref:`/access ` endpoint used for token refresh. -:ref:`Persistent ` access cookies are regularly -refreshed as part of an :ref:`access token refresh `. - -An access cookie is obtained either directly after registration or through a -subsequent :ref:`login `. A successful login provides both an access -cookie and and access token. Both access token and cookie must be stored safely -and kept confidential. User passwords should not be stored. - -As of yet, there is no concept of authorising third-party applications to -perform operations on the API on behalf of a user (Notable exceptions: -:ref:`sso`). Such functionality may be provided in the future through -standardised OAuth2 flows. - -To authorise an API request, the access token must be provided via the -HTTP ``Authorization`` header with the ``Bearer`` scheme as follows: - -:: - - Authorization: Bearer fmmLpDSjArpksFv57r5rDrzZZlj... - -While the API currently also supports passing the access token in the -query string of a request, this approach is highly discouraged as it -unnecessarily exposes access tokens (e.g. in server logs) and thus might -be removed in the future. - -.. _login: - -Login - ``POST /login`` ------------------------ - -A login is the process of authenticating a user either through a known secret in -a :ref:`password login ` or by proving ownership of a verified -phone number associated with an account in an :ref:`SMS login `. The -response to a successful login contains an access cookie in a ``Set-Cookie`` -header and an access token in the JSON response body. - -.. _login-cookies: - -Cookies -~~~~~~~ - -There is a hard limit for the number of session-scoped access cookies and the same -amount of persistent access cookies per user account. When this number is -reached, old cookies are removed when new ones are issued. Thereby, the cookies -with the oldest expiration timestamp are removed first. The removal takes the -type of the cookie to issue into account. I.e. session cookies are replaced by -session cookies, persistent cookies are replaced by persistent cookies. - -To prevent performance issues and malicious usages of the API, there is a -throttling mechanism in place. When the maximum number of cookies of one type -are issued, it's checked that login calls don't happen too frequently (too -quickly after one another.) - -In case of throttling no cookie gets issued. The error response (`HTTP status -code 429 `_) has -a ``Retry-After`` header which specifies the time to wait before accepting the -next request in Seconds. - -Being throttled is a clear indicator of incorrect API usage. There is no need to -login many times in a row on the same device. Instead, the cookie should be -re-used. - -The corresponding backend configuration settings are described in: -:ref:`auth-cookie-config` . - -.. _login-password: - -Password Login -~~~~~~~~~~~~~~ - -To perform a password login, send a ``POST`` request to the ``/login`` -endpoint, providing either a verified email address or phone number and -the corresponding password. For example: - -:: - - POST /login HTTP/1.1 - [headers omitted] - - { - "email": "me@wire.com", - "password": "Quo2Booz" - } - -If a phone number is used, the ``phone`` field is used instead of -``email``. If a @handle is used, the ``handle`` field is used instead of -``email`` (note that the handle value should be sent *without* the ``@`` -symbol). Assuming the credentials are correct, the API will respond with -a ``200 OK`` and an access token and cookie: - -:: - - HTTP/1.1 200 OK - zuid=...; Expires=Fri, 02-Aug-2024 09:15:54 GMT; Domain=zinfra.io; Path=/access; HttpOnly; Secure - [other headers omitted] - - { - "expires_in": 900, - "access_token": "fmmLpDSjArpksFv57r5rDrzZZlj...", - "token_type": "Bearer" - } - -.. - - The ``Domain`` of the cookie will be different depending on the - environment. - -The value of ``expires_in`` is the number of seconds that the -``access_token`` is valid from the moment it was issued. - -As of yet, the ``token_type`` is always ``Bearer``. - - - -.. _login-sms: - -SMS Login -~~~~~~~~~ - -To perform an SMS login, first request an SMS code to be sent to a -verified phone number: - -:: - - POST /login/send HTTP/1.1 - [headers omitted] - - { - "phone": "+1234567890" - } - -An SMS with a short-lived login code will be sent. Upon receiving the -SMS and extracting the code from it, the login can be performed using -the ``phone`` and ``code`` as follows: - -:: - - POST /login HTTP/1.1 - [headers omitted] - - { - "phone": "+1234567890", - "code": "123456" - } - -A successful response is identical to that of a :ref:`password -login `. - - - -.. _login-persistent: - -Persistent Logins -~~~~~~~~~~~~~~~~~ - -By default, access cookies are issued as `session -cookies `__ -with a validity of 1 week. Furthermore, these session cookies are not -refreshed as part of an :ref:`access token refresh `. To -request a ``persistent`` access cookie which does get refreshed, specify -the ``persist=true`` parameter during a login: - -:: - - POST /login?persist=true HTTP/1.1 - [headers omitted] - - { - "phone": "+1234567890", - "code": "123456" - } - -All access cookies returned on registration are persistent. - - - -.. _token-refresh: - -FAQ: is my cookie a persistent cookie or a session cookie? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you log in **without** the ``persist=true`` query parameter, or -with persist=false, you get a ``session cookie``, which means it has no -expiration date set, and will expire when you close the browser (and on -the backend has a validity of max 1 day or 1 week (configurable, see -current config in `hegemony `__). -Example **session cookie**: - -:: - - POST /login?persist=false - - Set-Cookie: zuid=(redacted); Path=/access; Domain=zinfra.io; HttpOnly; Secure - -When you log in **with** ``persist=true``, you get a persistent cookie, -which means it has *some* expiration date. In production this is -currently 56 days (again, configurable, check current config in -`hegemony `__) and can be renewed -during token refresh. Example **persistent cookie**: - -:: - - POST /login?persist=true - - Set-Cookie: zuid=(redacted); Path=/access; Expires=Thu, 10-Jan-2019 10:43:28 GMT; Domain=zinfra.io; HttpOnly; Secure - -Token Refresh - ``POST /access`` --------------------------------- - -Since access tokens have a relatively short lifetime to limit the time -window of abuse for a captured token, they need to be regularly -refreshed. In order to refresh an access token, send a ``POST`` reques -to ``/access``, including the access cookie in the ``Cookie`` header and -the old (possibly expired) access token in the ``Authorization`` header: - -:: - - POST /access HTTP/1.1 - Authorization: Bearer fmmLpDSjArpksFv57r5rDrzZZlj... - Cookie: zuid=... - [other headers omitted] - - - -Providing the old access token is not required but strongly recommended -as it will link the new access token to the old, enabling the API to see -the new access token as a continued session of the same client. - -As part of an access token refresh, the response may also contain a new -``zuid`` access cookie in form of a ``Set-Cookie`` header. A client must -expect a new ``zuid`` cookie as part of any access token refresh and -replace the existing cookie appropriately. - - - -.. _cookies: - -Cookie Management ------------------ - - - -.. _cookies-logout: - -Logout - ``POST /access/logout`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An explicit logout effectively deletes the cookie used to perform the -operation: - -:: - - POST /access/logout HTTP/1.1 - Authorization: Bearer fmmLpDSjArpksFv57r5rDrzZZlj... - Cookie: zuid=... - [other headers omitted] - - - -Afterwards, the cookie that was sent as part of the ``Cookie`` header is -no longer valid. - -If a client offers an explicit logout, this operation must be performed. -An explicit logout is especially important for Web clients. - - - -.. _cookies-labels: - -Labels -~~~~~~ - -Cookies can be labeled by specifying a ``label`` during login or -registration, e.g.: - -:: - - POST /login?persist=true HTTP/1.1 - [headers omitted] - - { - "phone": "+1234567890", - "code": "123456", - "label": "Google Nexus 5" - } - -Specifying a label is recommended as it helps to identify the cookies in a -user-friendly way and allows :ref:`selective revocation ` based -on the labels. - - - -.. _cookies-list: - -Listing Cookies - ``GET /cookies`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To list the cookies currently associated with an account, send a ``GET`` -request to ``/cookies``. The response will contain a list of cookies, -e.g.: - -:: - - HTTP/1.1 200 OK - [other headers omitted] - - { - "cookies": [ - { - "time": "2015-06-04T14:29:23.000Z", - "id": 967153183, - "type": "session", - "label": null - }, - { - "time": "2015-06-04T14:44:23.000Z", - "id": 942451749, - "type": "session", - "label": null - }, - ... - ] - } - -Note that expired cookies are not automatically removed when they -expire, only as new cookies are issued. - - - -.. _cookies-revoke: - -Revoking Cookies - ``POST /cookies/remove`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cookies can be removed individually or in bulk either by specifying the full -cookie structure as it is returned by :ref:`GET /cookies ` or only -by their labels in a ``POST`` request to ``/cookies/remove``, alongside with the -user's credentials: - -:: - - POST /cookies/remove HTTP/1.1 - [headers omitted] - - { - "ids": [{}, {}, ...], - "labels": ["", "", ...] - "email": "me@wire.com", - "password": "secret" - } - -Cookie removal currently requires an account with an email address and -password. - - - -.. _password-reset: - -Password Reset - ``POST /password-reset`` ------------------------------------------ - -A password reset can be used to set a new password if the existing password -associated with an account has been forgotten. This is not to be confused with -the act of merely changing your password for the purpose of password rotation or -if you suspect your current password to be compromised. - -Initiate a Password Reset -~~~~~~~~~~~~~~~~~~~~~~~~~ - -To initiate a password reset, send a ``POST`` request to -``/password-reset``, specifying either a verified email address or phone -number for the account in question: - -:: - - POST /password-reset HTTP/1.1 - [headers omitted] - - { - "phone": "+1234567890" - } - -For a phone number, the ``phone`` field would be used instead. As a -result of a successful request, either a password reset key and code is -sent via email or a password reset code is sent via SMS, depending on -whether an email address or a phone number was provided. Password reset -emails will contain a link to the `wire.com `__ -website which will guide the user through the completion of the password -reset, which means that the website will perform the necessary requests -to complete the password reset. To complete a password reset initiated -with a phone number, the completion of the password reset has to happen -from the mobile client application itself. - -Once a password reset has been initiated for an email address or phone -number, no further password reset can be initiated for the same email -address or phone number before the prior reset is completed or times -out. The current timeout for an initiated password reset is -``10 minutes``. - -Complete a Password Reset -~~~~~~~~~~~~~~~~~~~~~~~~~ - -To complete a password reset, the password reset code, together with the -new password and the ``email`` or ``phone`` used when initiating the -reset (or the opaque ``key`` sent by mail) are sent to -``/password-reset/complete`` in a ``POST`` request: - -:: - - POST /password-reset/complete HTTP/1.1 - [headers omitted] - - { - "phone": "+1234567890", - "code": "123456", - "password": "new-secret-password" - } - -There is a maximum of ``3`` attempts at completing a password reset, -after which the password reset code becomes invalid and a new password -reset must be initiated. - -A completed password reset results in all access cookies to be revoked, -requiring the user to :ref:`login `. - -Related topics: SSO, Legalhold -------------------------------- - -.. _sso: - -Single Sign-On -~~~~~~~~~~~~~~~~~~ - -Users that are part of a team, for which a team admin has configured SSO (Single Sign On), authentication can happen through SAML. - -More information: - -* :ref:`FAQ ` -* `setup howtos for various IdP vendors `__ -* `a few fragments that may help admins `__ - - -LegalHold -~~~~~~~~~~ - -Users that are part of a team, for which a team admin has configured "LegalHold", can add a so-called "LegalHold" device. The endpoints in use to authenticate for a "LegalHold" Device are the same as for regular users, but the access tokens they get can only use a restricted set of API endpoints. See also `legalhold documentation on wire-server `__ diff --git a/docs/src/understand/api-client-perspective/index.md b/docs/src/understand/api-client-perspective/index.md new file mode 100644 index 0000000000..8bf19e4290 --- /dev/null +++ b/docs/src/understand/api-client-perspective/index.md @@ -0,0 +1,15 @@ +# Wire-server API documentation + +The following documentation provides information for, and takes the perspective of a Wire client developer. (wire-desktop, wire-android and wire-ios are examples of Wire Clients). This means only publicly accessible endpoints are mentioned. + +```{warning} +This section of the documentation is very incomplete at the time of writing (summer 2020) - more pages on the client API will follow in the future. +``` + +```{toctree} +:glob: true +:maxdepth: 2 +:titlesonly: true + +* +``` diff --git a/docs/src/understand/api-client-perspective/index.rst b/docs/src/understand/api-client-perspective/index.rst deleted file mode 100644 index d419508892..0000000000 --- a/docs/src/understand/api-client-perspective/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -Wire-server API documentation -============================= - -The following documentation provides information for, and takes the perspective of a Wire client developer. (wire-desktop, wire-android and wire-ios are examples of Wire Clients). This means only publicly accessible endpoints are mentioned. - -.. warning:: - This section of the documentation is very incomplete at the time of writing (summer 2020) - more pages on the client API will follow in the future. - -.. toctree:: - :maxdepth: 2 - :glob: - :titlesonly: - - * diff --git a/docs/src/understand/api-client-perspective/swagger.rst b/docs/src/understand/api-client-perspective/swagger.md similarity index 67% rename from docs/src/understand/api-client-perspective/swagger.rst rename to docs/src/understand/api-client-perspective/swagger.md index 5dd3d29e36..057d5beb2a 100644 --- a/docs/src/understand/api-client-perspective/swagger.rst +++ b/docs/src/understand/api-client-perspective/swagger.md @@ -1,5 +1,4 @@ -Swagger API documentation (all public endpoints) -================================================ +# Swagger API documentation (all public endpoints) Our staging system provides swagger documentation of our public rest API. @@ -11,21 +10,19 @@ documentation still has some endpoints, but the new one is getting more and more Please check the new docs first, and if you can't find what you're looking for, double-check the old. -New docs --------- +## New docs These docs show swagger 2.0: -`new staging swagger page `_ +[new staging swagger page](https://staging-nginz-https.zinfra.io/api/swagger-ui/) - -Old docs --------- +## Old docs Some endpoints are only shown using swagger 1.2. At the time of writing, both swagger version 1.2 and version 2.0 are in use. If you are an employee of Wire, you can log in here and try out requests in the browser; if not, you can make use of the "List Operations" button on both 1.2 and 2.0 pages to see the possible API requests. -Browse to our `old staging swagger page `_ to see rendered swagger documentation for the remaining endpoints. +Browse to our [old staging swagger page](https://staging-nginz-https.zinfra.io/swagger-ui/) to see rendered swagger documentation for the remaining endpoints. -.. image:: img/swagger.png +```{image} img/swagger.png +``` diff --git a/docs/src/understand/federation/index.md b/docs/src/understand/federation/index.md new file mode 100644 index 0000000000..48e78ea649 --- /dev/null +++ b/docs/src/understand/federation/index.md @@ -0,0 +1,24 @@ +(federation-understand)= + +# Wire Federation + +Wire Federation, once implemented, aims to allow multiple Wire-server {ref}`backends ` to federate with each other. That means that a user 1 registered on backend A and a user 2 registered on backend B should be able to interact with each other as if they belonged to the same backend. + +```{note} +Federation is as of January 2022 still work in progress, since the implementation of federation is ongoing, and certain design decision are still subject to change. Where possible documentation will indicate the state of implementation. + +Some sections of the documentation are still incomplete (indicated with a 'TODO' comment). Check back later for updates. +``` + +% comment: The toctree directive below takes a list of the pages you want to appear in order, +% and '*' is used to include any other pages in the federation directory in alphabetical order + +```{toctree} +:glob: true +:maxdepth: 2 +:numbered: true + +introduction +architecture +* +``` diff --git a/docs/src/understand/federation/index.rst b/docs/src/understand/federation/index.rst deleted file mode 100644 index 70ff484f1f..0000000000 --- a/docs/src/understand/federation/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _federation-understand: - -+++++++++++++++++ -Wire Federation -+++++++++++++++++ - -Wire Federation, once implemented, aims to allow multiple Wire-server :ref:`backends ` to federate with each other. That means that a user 1 registered on backend A and a user 2 registered on backend B should be able to interact with each other as if they belonged to the same backend. - -.. note:: - Federation is as of January 2022 still work in progress, since the implementation of federation is ongoing, and certain design decision are still subject to change. Where possible documentation will indicate the state of implementation. - - Some sections of the documentation are still incomplete (indicated with a 'TODO' comment). Check back later for updates. - -.. - comment: The toctree directive below takes a list of the pages you want to appear in order, - and '*' is used to include any other pages in the federation directory in alphabetical order - -.. toctree:: - :maxdepth: 2 - :numbered: - :glob: - - introduction - architecture - * diff --git a/docs/src/understand/helm.md b/docs/src/understand/helm.md new file mode 100644 index 0000000000..9b27659a75 --- /dev/null +++ b/docs/src/understand/helm.md @@ -0,0 +1,61 @@ +(understand-helm)= + +# Understanding helm + +See also the official [helm documentation](https://docs.helm.sh/). This page is meant to explain a few concepts directly relevant when installing wire-server helm charts. + +(understand-helm-overrides)= + +## Overriding helm configuration settings + +### Default values + +Default values are under a specific chart's `values.yaml` file, e.g. for the chart named `cassandra-ephemeral`, this file: [charts/cassandra-ephemeral/values.yaml](https://github.com/wireapp/wire-server/blob/develop/charts/cassandra-ephemeral/values.yaml). When you install or upgrade a chart, with e.g.: + +``` +helm upgrade --install my-cassandra wire/cassandra-ephemeral +``` + +Then the default values from above are used. + +### Overriding + +Overriding parts of the yaml configuration can be achieved by passing `-f path/to/override-file.yaml` when installing or upgrading a helm chart, like this: + +Create file my-file.yaml: + +```yaml +cassandra-ephemeral: + resources: + requests: + cpu: "2" +``` + +Now you can install that chart with a custom value (using 2 cpu cores): + +``` +helm upgrade --install my-cassandra wire/cassandra-ephemeral -f my-values.yaml +``` + +### Sub charts + +If a chart uses sub charts, there can be overrides in the parent +chart's `values.yaml` file, if namespaced to the sub chart. +Example: if chart `parent` includes chart `child`, and +`child`'s `values.yaml` has a default value `foo: bar`, and the +`parent` chart's `values.yaml` has a value + +```yaml +child: + foo: baz +``` + +then the value that will be used for `foo` by default is `baz` when you install the parent chart. + +Note that if you `helm install parent` but wish to override values for `child`, you need to pass them as above, indented underneath `child:` as above. + +### Multiple overrides + +If `-f ` is used multiple times, the last file wins in case keys exist +multiple times (there is no merge performed between multiple files passed to `-f`). +This can lead to unexpected results. If you use multiple files with `-f`, ensure they don't overlap. diff --git a/docs/src/understand/helm.rst b/docs/src/understand/helm.rst deleted file mode 100644 index 3899186182..0000000000 --- a/docs/src/understand/helm.rst +++ /dev/null @@ -1,64 +0,0 @@ -.. _understand-helm: - -Understanding helm -=================== - -See also the official `helm documentation `__. This page is meant to explain a few concepts directly relevant when installing wire-server helm charts. - - -.. _understand-helm-overrides: - -Overriding helm configuration settings ------------------------------------------- - -Default values -^^^^^^^^^^^^^^ - -Default values are under a specific chart's ``values.yaml`` file, e.g. for the chart named ``cassandra-ephemeral``, this file: `charts/cassandra-ephemeral/values.yaml `__. When you install or upgrade a chart, with e.g.:: - - helm upgrade --install my-cassandra wire/cassandra-ephemeral - -Then the default values from above are used. - -Overriding -^^^^^^^^^^^ - -Overriding parts of the yaml configuration can be achieved by passing ``-f path/to/override-file.yaml`` when installing or upgrading a helm chart, like this: - -Create file my-file.yaml: - -.. code:: yaml - - cassandra-ephemeral: - resources: - requests: - cpu: "2" - -Now you can install that chart with a custom value (using 2 cpu cores):: - - helm upgrade --install my-cassandra wire/cassandra-ephemeral -f my-values.yaml - -Sub charts -^^^^^^^^^^^ - -If a chart uses sub charts, there can be overrides in the parent -chart's ``values.yaml`` file, if namespaced to the sub chart. -Example: if chart ``parent`` includes chart ``child``, and -``child``'s ``values.yaml`` has a default value ``foo: bar``, and the -``parent`` chart's ``values.yaml`` has a value - -.. code:: yaml - - child: - foo: baz - -then the value that will be used for ``foo`` by default is ``baz`` when you install the parent chart. - -Note that if you ``helm install parent`` but wish to override values for ``child``, you need to pass them as above, indented underneath ``child:`` as above. - -Multiple overrides -^^^^^^^^^^^^^^^^^^^^ - -If ``-f `` is used multiple times, the last file wins in case keys exist -multiple times (there is no merge performed between multiple files passed to `-f`). -This can lead to unexpected results. If you use multiple files with `-f`, ensure they don't overlap. diff --git a/docs/src/understand/index.md b/docs/src/understand/index.md new file mode 100644 index 0000000000..f7ca56369a --- /dev/null +++ b/docs/src/understand/index.md @@ -0,0 +1,17 @@ +(understand)= + +# Understanding wire-server components + +This section is almost empty, more documentation will come soon... + +```{toctree} +:glob: true +:maxdepth: 1 + +Overview +Audio/video calling, restund servers (TURN/STUN) +Conference Calling 2.0 (SFT) +Minio +Helm +Federation +``` diff --git a/docs/src/understand/index.rst b/docs/src/understand/index.rst deleted file mode 100644 index 3cca9519a8..0000000000 --- a/docs/src/understand/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _understand: - -Understanding wire-server components -==================================== - -This section is almost empty, more documentation will come soon... - -.. toctree:: - :maxdepth: 1 - :glob: - - Overview - Audio/video calling, restund servers (TURN/STUN) - Conference Calling 2.0 (SFT) - Minio - Helm - Federation diff --git a/docs/src/understand/minio.rst b/docs/src/understand/minio.md similarity index 86% rename from docs/src/understand/minio.rst rename to docs/src/understand/minio.md index 0c8fb60c38..afd4e1cd27 100644 --- a/docs/src/understand/minio.rst +++ b/docs/src/understand/minio.md @@ -1,10 +1,8 @@ -Minio -====== +# Minio -Official minio documentation available: ``_ +Official minio documentation available: [https://docs.min.io/](https://docs.min.io/) -Minio philosophy ------------------ +## Minio philosophy Minio clusters are configured with a fixed size once, and cannot be resized afterwards. It is thus important to make a good conservative estimate about @@ -23,8 +21,7 @@ cluster is starting to get full, you will need to set up a parallel bigger cluster, mirror everything to the new cluster, swap the DNS entries to the new one, and then decommission the old one. -Hurdles from the trenches: disk usage statistics; directories vs. disks ------------------------------------------------------------------------ +## Hurdles from the trenches: disk usage statistics; directories vs. disks I have done some more go code reading and have solved more minio mysteries. tl;dr: if you want to be safe, run minio on disks, not @@ -35,7 +32,7 @@ to figure out the amount of available blocks. If it's not a mount directory, it will just call `du .` in a for loop and update some counter (which sounds like a bad strategy to me). -https://github.com/minio/minio/blob/e6d8e272ced8b54872c6df1ef2ad556092280224/cmd/posix.go#L320-L352 + so the answer is: if you use minio, e.g. with mountpoints, it will silently do the right thing and if you configure it to use two directories on the same diff --git a/docs/src/understand/notes/port-ranges.md b/docs/src/understand/notes/port-ranges.md new file mode 100644 index 0000000000..94191336da --- /dev/null +++ b/docs/src/understand/notes/port-ranges.md @@ -0,0 +1,36 @@ +--- +orphan: true +--- + +(port-ranges)= + +# Note on port ranges + +Some parts of Wire (SFT, Restund) related to conference calling and Audio/Video, establish outgoing connections in a range of UDP ports. Which ports are used is determined by the kernel using `/proc/sys/net/ipv4/ip_local_port_range`. + +The /proc/sys/net/ipv4/ip_local_port_range defines the local port range that is used by TCP and UDP traffic to choose the local port. + +You will see in the parameters of this file two numbers: The first number is the first local port allowed for TCP and UDP traffic on the server, the second is the last local port number. + +When setting up firewall rules, this entire range must be allowed for both UDP and TCP. + +This range is defined by the system, and is set by the `/proc/sys/net/ipv4/ip_local_port_range` parameter. + +You read this range for your system by running the following command: + +```bash +cat /proc/sys/net/ipv4/ip_local_port_range +``` + +Or by finding the following line in your `/etc/sysctl.conf` file, if it exists: + +``` +# Allowed local port range +net.ipv4.ip_local_port_range = 32768 61000 +``` + +To change the range, edit the `/etc/sysctl.conf` file or run the following command: + +```bash +echo "32768 61001" > /proc/sys/net/ipv4/ip_local_port_range +``` diff --git a/docs/src/understand/notes/port-ranges.rst b/docs/src/understand/notes/port-ranges.rst deleted file mode 100644 index 0d2cc4e13b..0000000000 --- a/docs/src/understand/notes/port-ranges.rst +++ /dev/null @@ -1,36 +0,0 @@ -:orphan: - -.. _port-ranges: - -Note on port ranges -=================== - -Some parts of Wire (SFT, Restund) related to conference calling and Audio/Video, establish outgoing connections in a range of UDP ports. Which ports are used is determined by the kernel using ``/proc/sys/net/ipv4/ip_local_port_range``. - -The /proc/sys/net/ipv4/ip_local_port_range defines the local port range that is used by TCP and UDP traffic to choose the local port. - -You will see in the parameters of this file two numbers: The first number is the first local port allowed for TCP and UDP traffic on the server, the second is the last local port number. - -When setting up firewall rules, this entire range must be allowed for both UDP and TCP. - -This range is defined by the system, and is set by the ``/proc/sys/net/ipv4/ip_local_port_range`` parameter. - -You read this range for your system by running the following command: - -.. code-block:: bash - - cat /proc/sys/net/ipv4/ip_local_port_range - -Or by finding the following line in your ``/etc/sysctl.conf`` file, if it exists: - -.. code-block:: - - # Allowed local port range - net.ipv4.ip_local_port_range = 32768 61000 - -To change the range, edit the ``/etc/sysctl.conf`` file or run the following command: - -.. code-block:: bash - - echo "32768 61001" > /proc/sys/net/ipv4/ip_local_port_range - diff --git a/docs/src/understand/overview.md b/docs/src/understand/overview.md new file mode 100644 index 0000000000..56f203f707 --- /dev/null +++ b/docs/src/understand/overview.md @@ -0,0 +1,143 @@ +(overview)= + +# Overview + +## Introduction + +In a simplified way, the server components for Wire involve the following: + +```{image} img/architecture-server-simplified.png +``` + +The Wire clients (such as the Wire app on your phone) connect either directly (or via a load balancer) to the "Wire Server". By "Wire Server" we mean multiple API server components that connect to each other, and which also connect to a few databases. Both the API components and the databases are each in a "cluster", which means copies of the same program code runs multiple times. This allows any one component to fail without users noticing that there is a problem (also called +"high-availability"). + +## Architecture and networking + +Note that the webapp, account pages, and team-settings, while in a way not part of the backend, +are installed with the rest and therefore included. + +### Focus on internet protocols + +```{image} ./img/architecture-tls-on-prem-2020-09.png +``` + +### Focus on high-availability + +The following diagram shows a usual setup with multiple VMs (Virtual Machines): + +```{image} ../how-to/install/img/architecture-server-ha.png +``` + +Wire clients (such as the Wire app on your phone) connect to a load balancer. + +The load balancer forwards traffic to the ingress inside the kubernetes VMs. (Restund is special, see {ref}`understand-restund` for details on how Restund works.) + +The nginx ingress pods inside kubernetes look at incoming traffic, and forward that traffic on to the right place, depending on what's inside the URL passed. For example, if a request comes in for `https://example-https.example.com`, it is forwarded to a component called `nginz`, which is the main entry point for the [wire-server API](https://github.com/wireapp/wire-server). If, however, a request comes in for `https://webapp.example.com`, it is forwarded to a component called [webapp](https://github.com/wireapp/wire-webapp), which hosts the graphical browser Wire client (as found when you open [https://app.wire.com](https://app.wire.com)). + +Wire-server needs a range of databases. Their names are: cassandra, elasticsearch, minio, redis, etcd. + +All the server components on one physical machine can connect to all the databases (also those on a different physical machine). The databases each connect to each-other, e.g. cassandra on machine 1 will connect to the cassandra VMs on machines 2 and 3. + +### Backend components startup + +The Wire server backend is designed to run on a kubernetes cluster. From a high level perspective the startup sequence from machine power-on to the Wire server being ready to receive requests is as follow: + +1. *Kubernetes node power on*. Systemd starts the kubelet service which makes the worker node available to kubernetes. For more details about kubernetes startup refer to [the official kubernetes documentation](https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/). For details about the installation and configuration of kubernetes and worker nodes for Wire server see {ref}`Installing kubernetes and databases on VMs with ansible ` +2. *Kubernetes workload startup*. Kubernetes will ensure that Wire server workloads installed via helm are scheduled on available worker nodes. For more details about workload scheduling refer to [the official kubernetes documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/). For details about how to install Wire server with helm refer to {ref}`Installing wire-server (production) components using Helm `. +3. *Stateful workload startup*. Systemd starts the stateful services (cassandra, elasticsearch and minio). See for instance [ansible-cassandra role](https://github.com/wireapp/ansible-cassandra/blob/master/tasks/systemd.yml#L10) and other database installation instructions in {ref}`Installing kubernetes and databases on VMs with ansible ` +4. *Other services*. Systemd starts the restund docker container. See [ansible-restund role](https://github.com/wireapp/ansible-restund/blob/9807313a7c72ffa40e74f69d239404fd87db65ab/templates/restund.service.j2#L12-L19). For details about docker container startup [consult the official documentation](https://docs.docker.com/get-started/overview/#docker-architecture) + +```{note} +For more information about Virual Machine startup or operating system level service startup, please consult your virtualisation and operating system documentation. +``` + +### Focus on pods + +The Wire backend runs in [a kubernetes cluster](https://kubernetes.io/), with different components running in different [pods](https://kubernetes.io/docs/concepts/workloads/pods/). + +This is a list of those pods as found in a typical installation. + +HTTPS Entry points: + +- `nginx-ingress-controller-controller`: [Ingress](https://kubernetes.github.io/ingress-nginx/) exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. +- `nginx-ingress-controller-default-backend`: [The default backend](https://kubernetes.github.io/ingress-nginx/user-guide/default-backend/) is a service which handles all URL paths and hosts the nginx controller doesn't understand (i.e., all the requests that are not mapped with an Ingress), that is 404 pages. Part of `nginx-ingress`. + +Frontend pods: + +- `webapp`: The fully functioning Web client (like ). [This pod](https://github.com/wireapp/wire-docs/blob/master/src/how-to/install/helm.rst#what-will-be-installed) serves the web interface itself, which then interfaces with other services/pods, such as the APIs. +- `account-pages`: [This pod](https://github.com/wireapp/wire-docs/blob/master/src/how-to/install/helm.rst#what-will-be-installed) serves Web pages for user account management (a few pages relating to e.g. password reset). +- `team-settings`: Team management Web interface (like ). + +Pods with an HTTP API: + +- `brig`: [The user management API service](https://github.com/wireapp/wire-server/tree/develop/services/brig). Connects to `cassandra` and `elastisearch` for user data storage, sends emails and SMS for account validation. +- `cannon`: [WebSockets API Service](https://github.com/wireapp/wire-server/blob/develop/services/cannon/). Holds WebSocket connections. +- `cargohold`: [Asset Storage API Service](https://docs.wire.com/how-to/install/aws-prod.html). Amazon-AWS-S3-style services are used by `cargohold` to store encrypted files that users are sharing amongst each other, such as images, files, and other static content, which we call assets. All assets except profile pictures are symmetrically encrypted before storage (and the keys are only known to the participants of the conversation in which an assets was shared - servers have no knowledge of the keys). +- `galley`: [Conversations and Teams API Service](https://docs.wire.com/understand/api-client-perspective/index.html). Data is stored in cassandra. Uses `gundeck` to send notifications to users. +- `nginz`: Public API Reverse Proxy (Nginx with custom libzauth module). A modified copy of nginx, compiled with a specific set of upstream extra modules, and one important additional module zauth_nginx_module. Responsible for user authentication validation. Forwards traffic to all other API services (except federator) +- `spar`: [Single Sign On (SSO)](https://en.wikipedia.org/wiki/Single_sign-on) and [SCIM](https://en.wikipedia.org/wiki/System_for_Cross-domain_Identity_Management). Stores data in cassandra. +- `gundeck`: Push Notification Hub (WebSocket/mobile push notifications). Uses redis as a temporary data store for websocket presences. Uses Amazon SNS and SQS. +- `federator`: [Connects different wire installations together](https://docs.wire.com/understand/federation/index.html). Wire Federation, once implemented, aims to allow multiple Wire-server backends to federate with each other. That means that a user 1 registered on backend A and a user 2 registered on backend B should be able to interact with each other as if they belonged to the same backend. + +Supporting pods and data storage: + +- `cassandra-ephemeral` (or `cassandra-external`): [NoSQL Database management system](https://github.com/wireapp/wire-server/tree/develop/charts/cassandra-ephemeral) (). Everything stateful in wire-server (cassandra is used by `brig`, `galley`, `gundeck` and `spar`) is stored in cassandra. + \* `cassandra-ephemeral` is for test clusters where persisting the data (i.e. loose users, conversations,...) does not matter, but this shouldn't be used in production environments. + \* `cassandra-external` is used to point to an external cassandra cluster which is installed outside of Kubernetes. +- `demo-smtp`: In "demo" installations, used to replace a proper external SMTP server for the sending of emails (for example verification codes). In production environments, an actual SMTP server is used directly instead of this pod. () +- `fluent-bit`: A log processor and forwarder, allowing collection of data such as metrics and logs from different sources. Not typically deployed. () +- `elastisearch-ephemeral` (or `elastisearch-external`): [Distributed search and analytics engines, stores some user information (name, handle, userid, teamid)](https://github.com/wireapp/wire-server/tree/develop/charts/elastisearch-external). Information is duplicated here from cassandra to allow searching for users. Information here can be re-populated from data in cassandra (albeit with some downtime for search functionality) (). + \* `elastisearch-ephemeral` is for test clusters where persisting the data doesn't matter. + \* `elastisearch-external` refers to elasticsearch IPs located outside kubernetes by specifying IPs manually. +- `fake-aws-s3`: Amazon-AWS-S3-compatible object storage using MinIO (), used by cargohold to store (encrypted) assets such as files, posted images, profile pics, etc. +- `fake-aws-s3-reaper`: Creates the default S3 bucket inside fake-aws-s3. +- `fake-aws-sns`. [Amazon Simple Notification Service (Amazon SNS)](https://docs.aws.amazon.com/AmazonS3/latest/userguide/NotificationHowTo.html), used to push messages to mobile devices or distributed services. SNS can publish a message once, and deliver it one or more times. +- `fake-aws-sqs`: [Amazon Simple Queue Service (Amazon SQS) queue](https://docs.aws.amazon.com/AmazonS3/latest/userguide/NotificationHowTo.html), used to transmit any volume of data without requiring other services to be always available. +- `redis-ephemeral`: Stores websocket connection assignments (part of the `gundeck` / `cannon` architecture). + +Short running jobs that run during installation/upgrade (these should usually be in the status 'Completed' except immediately after installation/upgrade): + +- `cassandra-migrations`: Used to initialize or upgrade the database schema in cassandra (for example when the software is upgraded to a new version). +- `galley-migrate-data`: Used to upgrade data in `cassandra` when the data model changes (for example when the software is upgraded to a new version). +- `brig-index-migrate-data`: Used to upgrade data in `cassandra` when the data model changes in brig (for example when the software is upgraded to a new version) +- `elastisearch-index-create`: [Creates](https://github.com/wireapp/wire-server/blob/develop/charts/elasticsearch-index/templates/create-index.yaml#L29) an Elastisearch index for brig. +- `spar-migrate-data`: [Used to update spar data](https://github.com/wireapp/wire-server/blob/develop/charts/cassandra-migrations/templates/spar-migrate-data.yaml) in cassandra when schema changes occur. + +As an example, this is the result of running the `kubectl get pods --namespace wire` command to obtain a list of all pods in a typical cluster: + +```shell +NAMESPACE NAME READY STATUS RESTARTS AGE +wire account-pages-54bfcb997f-hwxlf 1/1 Running 0 85d +wire brig-58bc7f844d-rp2mx 1/1 Running 0 3h54m +wire brig-index-migrate-data-s7lmf 0/1 Completed 0 3h33m +wire cannon-0 1/1 Running 0 3h53m +wire cargohold-779bff9fc6-7d9hm 1/1 Running 0 3h54m +wire cassandra-ephemeral-0 1/1 Running 0 176d +wire cassandra-migrations-66n8d 0/1 Completed 0 3h34m +wire demo-smtp-784ddf6989-7zvsk 1/1 Running 0 176d +wire elasticsearch-ephemeral-86f4b8ff6f-fkjlk 1/1 Running 0 176d +wire elasticsearch-index-create-l5zbr 0/1 Completed 0 3h34m +wire fake-aws-s3-77d9447b8f-9n4fj 1/1 Running 0 176d +wire fake-aws-s3-reaper-78d9f58dd4-kf582 1/1 Running 0 176d +wire fake-aws-sns-6c7c4b7479-nzfj2 2/2 Running 0 176d +wire fake-aws-sqs-59fbfbcbd4-ptcz6 2/2 Running 0 176d +wire federator-6d7b66f4d5-xgkst 1/1 Running 0 3h54m +wire galley-5b47f7ff96-m9zrs 1/1 Running 0 3h54m +wire galley-migrate-data-97gn8 0/1 Completed 0 3h33m +wire gundeck-76c4599845-4f4pd 1/1 Running 0 3h54m +wire nginx-ingress-controller-controller-2nbkq 1/1 Running 0 9d +wire nginx-ingress-controller-controller-8ggw2 1/1 Running 0 9d +wire nginx-ingress-controller-default-backend-dd5c45cf-jlmbl 1/1 Running 0 176d +wire nginz-77d7586bd9-vwlrh 2/2 Running 0 3h54m +wire redis-ephemeral-master-0 1/1 Running 0 176d +wire spar-8576b6845c-npb92 1/1 Running 0 3h54m +wire spar-migrate-data-lz5ls 0/1 Completed 0 3h33m +wire team-settings-86747b988b-5rt45 1/1 Running 0 50d +wire webapp-54458f756c-r7l6x 1/1 Running 0 3h54m + 1/1 Running 0 3h54m +``` + +```{note} +This list is not exhaustive, and your installation may have additional pods running depending on your configuration. +``` diff --git a/docs/src/understand/overview.rst b/docs/src/understand/overview.rst deleted file mode 100644 index e913abf089..0000000000 --- a/docs/src/understand/overview.rst +++ /dev/null @@ -1,146 +0,0 @@ -.. _overview: - -Overview -======== - -Introduction ------------- - -In a simplified way, the server components for Wire involve the following: - -.. image:: img/architecture-server-simplified.png - -The Wire clients (such as the Wire app on your phone) connect either directly (or via a load balancer) to the "Wire Server". By "Wire Server" we mean multiple API server components that connect to each other, and which also connect to a few databases. Both the API components and the databases are each in a "cluster", which means copies of the same program code runs multiple times. This allows any one component to fail without users noticing that there is a problem (also called -"high-availability"). - -Architecture and networking ----------------------------- - -Note that the webapp, account pages, and team-settings, while in a way not part of the backend, -are installed with the rest and therefore included. - -Focus on internet protocols -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. image:: ./img/architecture-tls-on-prem-2020-09.png - - -Focus on high-availability -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following diagram shows a usual setup with multiple VMs (Virtual Machines): - -.. image:: ../how-to/install/img/architecture-server-ha.png - -Wire clients (such as the Wire app on your phone) connect to a load balancer. - -The load balancer forwards traffic to the ingress inside the kubernetes VMs. (Restund is special, see :ref:`understand-restund` for details on how Restund works.) - -The nginx ingress pods inside kubernetes look at incoming traffic, and forward that traffic on to the right place, depending on what's inside the URL passed. For example, if a request comes in for ``https://example-https.example.com``, it is forwarded to a component called ``nginz``, which is the main entry point for the `wire-server API `__. If, however, a request comes in for ``https://webapp.example.com``, it is forwarded to a component called `webapp `__, which hosts the graphical browser Wire client (as found when you open ``__). - -Wire-server needs a range of databases. Their names are: cassandra, elasticsearch, minio, redis, etcd. - -All the server components on one physical machine can connect to all the databases (also those on a different physical machine). The databases each connect to each-other, e.g. cassandra on machine 1 will connect to the cassandra VMs on machines 2 and 3. - -Backend components startup -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Wire server backend is designed to run on a kubernetes cluster. From a high level perspective the startup sequence from machine power-on to the Wire server being ready to receive requests is as follow: - -1. *Kubernetes node power on*. Systemd starts the kubelet service which makes the worker node available to kubernetes. For more details about kubernetes startup refer to `the official kubernetes documentation `__. For details about the installation and configuration of kubernetes and worker nodes for Wire server see :ref:`Installing kubernetes and databases on VMs with ansible ` -2. *Kubernetes workload startup*. Kubernetes will ensure that Wire server workloads installed via helm are scheduled on available worker nodes. For more details about workload scheduling refer to `the official kubernetes documentation `__. For details about how to install Wire server with helm refer to :ref:`Installing wire-server (production) components using Helm `. -3. *Stateful workload startup*. Systemd starts the stateful services (cassandra, elasticsearch and minio). See for instance `ansible-cassandra role `__ and other database installation instructions in :ref:`Installing kubernetes and databases on VMs with ansible ` -4. *Other services*. Systemd starts the restund docker container. See `ansible-restund role `__. For details about docker container startup `consult the official documentation `__ - -.. note:: - For more information about Virual Machine startup or operating system level service startup, please consult your virtualisation and operating system documentation. - -Focus on pods -~~~~~~~~~~~~~ - -The Wire backend runs in `a kubernetes cluster `__, with different components running in different `pods `__. - -This is a list of those pods as found in a typical installation. - -HTTPS Entry points: - -* ``nginx-ingress-controller-controller``: `Ingress `__ exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. -* ``nginx-ingress-controller-default-backend``: `The default backend `__ is a service which handles all URL paths and hosts the nginx controller doesn't understand (i.e., all the requests that are not mapped with an Ingress), that is 404 pages. Part of ``nginx-ingress``. - -Frontend pods: - -* ``webapp``: The fully functioning Web client (like https://app.wire.com). `This pod `__ serves the web interface itself, which then interfaces with other services/pods, such as the APIs. -* ``account-pages``: `This pod `__ serves Web pages for user account management (a few pages relating to e.g. password reset). -* ``team-settings``: Team management Web interface (like https://teams.wire.com). - -Pods with an HTTP API: - -* ``brig``: `The user management API service `__. Connects to ``cassandra`` and ``elastisearch`` for user data storage, sends emails and SMS for account validation. -* ``cannon``: `WebSockets API Service `__. Holds WebSocket connections. -* ``cargohold``: `Asset Storage API Service `__. Amazon-AWS-S3-style services are used by ``cargohold`` to store encrypted files that users are sharing amongst each other, such as images, files, and other static content, which we call assets. All assets except profile pictures are symmetrically encrypted before storage (and the keys are only known to the participants of the conversation in which an assets was shared - servers have no knowledge of the keys). -* ``galley``: `Conversations and Teams API Service `__. Data is stored in cassandra. Uses ``gundeck`` to send notifications to users. -* ``nginz``: Public API Reverse Proxy (Nginx with custom libzauth module). A modified copy of nginx, compiled with a specific set of upstream extra modules, and one important additional module zauth_nginx_module. Responsible for user authentication validation. Forwards traffic to all other API services (except federator) -* ``spar``: `Single Sign On (SSO) `__ and `SCIM `__. Stores data in cassandra. -* ``gundeck``: Push Notification Hub (WebSocket/mobile push notifications). Uses redis as a temporary data store for websocket presences. Uses Amazon SNS and SQS. -* ``federator``: `Connects different wire installations together `__. Wire Federation, once implemented, aims to allow multiple Wire-server backends to federate with each other. That means that a user 1 registered on backend A and a user 2 registered on backend B should be able to interact with each other as if they belonged to the same backend. - -Supporting pods and data storage: - -* ``cassandra-ephemeral`` (or ``cassandra-external``): `NoSQL Database management system `__ (https://en.wikipedia.org/wiki/Apache_Cassandra). Everything stateful in wire-server (cassandra is used by ``brig``, ``galley``, ``gundeck`` and ``spar``) is stored in cassandra. - * ``cassandra-ephemeral`` is for test clusters where persisting the data (i.e. loose users, conversations,...) does not matter, but this shouldn't be used in production environments. - * ``cassandra-external`` is used to point to an external cassandra cluster which is installed outside of Kubernetes. -* ``demo-smtp``: In "demo" installations, used to replace a proper external SMTP server for the sending of emails (for example verification codes). In production environments, an actual SMTP server is used directly instead of this pod. (https://github.com/namshi/docker-smtp) -* ``fluent-bit``: A log processor and forwarder, allowing collection of data such as metrics and logs from different sources. Not typically deployed. (https://fluentbit.io/) -* ``elastisearch-ephemeral`` (or ``elastisearch-external``): `Distributed search and analytics engines, stores some user information (name, handle, userid, teamid) `__. Information is duplicated here from cassandra to allow searching for users. Information here can be re-populated from data in cassandra (albeit with some downtime for search functionality) (https://www.elastic.co/what-is/elasticsearch). - * ``elastisearch-ephemeral`` is for test clusters where persisting the data doesn't matter. - * ``elastisearch-external`` refers to elasticsearch IPs located outside kubernetes by specifying IPs manually. -* ``fake-aws-s3``: Amazon-AWS-S3-compatible object storage using MinIO (https://min.io/), used by cargohold to store (encrypted) assets such as files, posted images, profile pics, etc. -* ``fake-aws-s3-reaper``: Creates the default S3 bucket inside fake-aws-s3. -* ``fake-aws-sns``. `Amazon Simple Notification Service (Amazon SNS) `__, used to push messages to mobile devices or distributed services. SNS can publish a message once, and deliver it one or more times. -* ``fake-aws-sqs``: `Amazon Simple Queue Service (Amazon SQS) queue `__, used to transmit any volume of data without requiring other services to be always available. -* ``redis-ephemeral``: Stores websocket connection assignments (part of the ``gundeck`` / ``cannon`` architecture). - -Short running jobs that run during installation/upgrade (these should usually be in the status 'Completed' except immediately after installation/upgrade): - -* ``cassandra-migrations``: Used to initialize or upgrade the database schema in cassandra (for example when the software is upgraded to a new version). -* ``galley-migrate-data``: Used to upgrade data in ``cassandra`` when the data model changes (for example when the software is upgraded to a new version). -* ``brig-index-migrate-data``: Used to upgrade data in ``cassandra`` when the data model changes in brig (for example when the software is upgraded to a new version) -* ``elastisearch-index-create``: `Creates `__ an Elastisearch index for brig. -* ``spar-migrate-data``: `Used to update spar data `__ in cassandra when schema changes occur. - -As an example, this is the result of running the ``kubectl get pods --namespace wire`` command to obtain a list of all pods in a typical cluster: - -.. code:: shell - - NAMESPACE NAME READY STATUS RESTARTS AGE - wire account-pages-54bfcb997f-hwxlf 1/1 Running 0 85d - wire brig-58bc7f844d-rp2mx 1/1 Running 0 3h54m - wire brig-index-migrate-data-s7lmf 0/1 Completed 0 3h33m - wire cannon-0 1/1 Running 0 3h53m - wire cargohold-779bff9fc6-7d9hm 1/1 Running 0 3h54m - wire cassandra-ephemeral-0 1/1 Running 0 176d - wire cassandra-migrations-66n8d 0/1 Completed 0 3h34m - wire demo-smtp-784ddf6989-7zvsk 1/1 Running 0 176d - wire elasticsearch-ephemeral-86f4b8ff6f-fkjlk 1/1 Running 0 176d - wire elasticsearch-index-create-l5zbr 0/1 Completed 0 3h34m - wire fake-aws-s3-77d9447b8f-9n4fj 1/1 Running 0 176d - wire fake-aws-s3-reaper-78d9f58dd4-kf582 1/1 Running 0 176d - wire fake-aws-sns-6c7c4b7479-nzfj2 2/2 Running 0 176d - wire fake-aws-sqs-59fbfbcbd4-ptcz6 2/2 Running 0 176d - wire federator-6d7b66f4d5-xgkst 1/1 Running 0 3h54m - wire galley-5b47f7ff96-m9zrs 1/1 Running 0 3h54m - wire galley-migrate-data-97gn8 0/1 Completed 0 3h33m - wire gundeck-76c4599845-4f4pd 1/1 Running 0 3h54m - wire nginx-ingress-controller-controller-2nbkq 1/1 Running 0 9d - wire nginx-ingress-controller-controller-8ggw2 1/1 Running 0 9d - wire nginx-ingress-controller-default-backend-dd5c45cf-jlmbl 1/1 Running 0 176d - wire nginz-77d7586bd9-vwlrh 2/2 Running 0 3h54m - wire redis-ephemeral-master-0 1/1 Running 0 176d - wire spar-8576b6845c-npb92 1/1 Running 0 3h54m - wire spar-migrate-data-lz5ls 0/1 Completed 0 3h33m - wire team-settings-86747b988b-5rt45 1/1 Running 0 50d - wire webapp-54458f756c-r7l6x 1/1 Running 0 3h54m - 1/1 Running 0 3h54m -.. note:: - - This list is not exhaustive, and your installation may have additional pods running depending on your configuration. diff --git a/docs/src/understand/restund.rst b/docs/src/understand/restund.md similarity index 62% rename from docs/src/understand/restund.rst rename to docs/src/understand/restund.md index 8935365e84..0cb8dd6f6d 100644 --- a/docs/src/understand/restund.rst +++ b/docs/src/understand/restund.md @@ -1,25 +1,22 @@ -.. _understand-restund: +(understand-restund)= -Restund (TURN) servers -======================== +# Restund (TURN) servers -Introduction -~~~~~~~~~~~~ +## Introduction Restund servers allow two users on different networks (for example Alice who is in an office connected to an office router and Bob who is at home connected to a home router) to have a Wire audio or video call. More precisely: - Restund is a modular and flexible - `STUN `__ and - `TURN `__ - Server, with IPv4 and IPv6 support. +> Restund is a modular and flexible +> [STUN](https://en.wikipedia.org/wiki/STUN) and +> [TURN](https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT) +> Server, with IPv4 and IPv6 support. -.. _architecture-restund: +(architecture-restund)= -Architecture -~~~~~~~~~~~~ +## Architecture Since the restund servers help establishing a connection between two users, they need to be reachable by both of these users, which usually @@ -32,29 +29,28 @@ Restund instance may communicate with other Restund instances. You can either have restund servers directly exposed to the public internet: -.. image:: img/architecture-restund.png +```{image} img/architecture-restund.png +``` Or you can have them reachable by fronting them with a firewall or load balancer machine that may have a different IP than the server where restund is installed: -.. image:: img/architecture-restund-lb.png +```{image} img/architecture-restund-lb.png +``` -What is it used for -~~~~~~~~~~~~~~~~~~~ +## What is it used for Restund is used to assist in NAT-traversal. Its goal is to connect two clients who are (possibly both) behind NAT directly in a peer to peer fashion, for optimal call quality and lowest latency. - client A sends a UDP packet to Restund; which will get address-translated by the router. Restund then sends back to the client what the source IP and the source port was that Restund observed. If the client then communicates this to Client B, Client B will be able to send data to that IP,port pair over UDP if it does so quickly enough. Client A and B will then have a peer-to-peer leg. - This is not always possible (e.g. symmetric NAT makes this technique impossible, as the router will NAT a different source-port for each connection). In that case clients fall back to TURN, which asks Restund to @@ -63,17 +59,16 @@ allocate a relay address which relays packets between nodes A and B. Restund servers need to have a wide range of ports open to allocate such relay addresses. -Network -~~~~~~~ +## Network As briefly mentioned above, a TURN server functions as a bridge between networks. Networks which don't have a direct route defined between them, usually have distinct address blocks. Depending on the address block they are configured with - such block is either considered to be *public* or *private* -(aka special-purpose addresses `[RFC 6890] `__) +(aka special-purpose addresses [\[RFC 6890\]](https://tools.ietf.org/html/rfc6890)) -- `IPv4 private blocks `__ -- `IPv6 private blocks `__ +- [IPv4 private blocks](https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml) +- [IPv6 private blocks](https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml) In cases where a machine, that is hosting the TURN server, also connects to a *private* network in which other services are running, chances are @@ -81,56 +76,51 @@ that these services are being indirectly exposed through that TURN server. To prevent this kind of exposure, a TURN server has to be configured with an inclusive or exclusive list of address blocks to prevents undesired connections from being -established [1]_. At the moment (Feb. 2021), this functionality is not yet available +established [^footnote-1]. At the moment (Feb. 2021), this functionality is not yet available with *Restund* on the application-level. Instead, the system-level firewall capabilities -must be utilized. The `IP ranges `__ -mentioned in the article [1]_ should be blocked for egress and, depending on the scenario, -also for ingress traffic. Tools like ``iptables`` or ``ufw`` can be used to set this up. +must be utilized. The [IP ranges](https://www.rtcsec.com/post/2021/01/details-about-cve-2020-26262-bypass-of-coturns-default-access-control-protection/#further-concerns-what-else) +mentioned in the article [^footnote-1] should be blocked for egress and, depending on the scenario, +also for ingress traffic. Tools like `iptables` or `ufw` can be used to set this up. -.. [1] `Details about CVE-2020-26262, bypass of Coturn's default access control protection `__ +[^footnote-1]: [Details about CVE-2020-26262, bypass of Coturn's default access control protection](https://www.rtcsec.com/post/2021/01/details-about-cve-2020-26262-bypass-of-coturns-default-access-control-protection/) +(understand-restund-protocal-and-ports)= -.. _understand-restund-protocal-and-ports: - -Protocols and open ports -~~~~~~~~~~~~~~~~~~~~~~~~ +## Protocols and open ports Restund servers provide the best audio/video connections if end-user devices -can connect to them via UDP. +can connect to them via UDP. -In this case, a firewall (if any) needs to allow and/or forward the complete :ref:`default port range ` for incoming UDP traffic. +In this case, a firewall (if any) needs to allow and/or forward the complete {ref}`default port range ` for incoming UDP traffic. -Ports for allocations are allocated from the :ref:`default port range `, for more information on this port range, how to read and change it, and how to configure your firewall, see :ref:`this note `. +Ports for allocations are allocated from the {ref}`default port range `, for more information on this port range, how to read and change it, and how to configure your firewall, see {ref}`this note `. -In case e.g. office firewall rules disallow UDP traffic in this range, there is a possibility to use TCP instead, at the expense of call quality. +In case e.g. office firewall rules disallow UDP traffic in this range, there is a possibility to use TCP instead, at the expense of call quality. -Port ``3478`` is the default control port, +Port `3478` is the default control port, however one UDP port per active connection is required, so a whole port range must be available and reachable from the outside. -If *Conference Calling 2.0* (:ref:`SFT `) is enabled, a Restund instance, -additionally, must be allowed to communicate with ::ref:`SFT instances ` +If *Conference Calling 2.0* ({ref}`SFT `) is enabled, a Restund instance, +additionally, must be allowed to communicate with :{ref}`SFT instances ` on the same UDP ports mentioned above. In this scenario a Restund server becomes sort of a proxy for the client, if the client is not able to establish a media channel between itself and the SFT server. -*For more information, please refer to the source code of the Ansible role:* `restund `__. +*For more information, please refer to the source code of the Ansible role:* [restund](https://github.com/wireapp/ansible-restund/blob/master/tasks/firewall.yml). -Control ports -^^^^^^^^^^^^^ +### Control ports -Restund listens for control messages on port ``3478`` on both UDP and TCP. It -also can listen on port ``5349`` which uses TLS. One can reconfigure both ports. -For example, port ``5349`` can be reconfigured to be port ``443``; so that TURN +Restund listens for control messages on port `3478` on both UDP and TCP. It +also can listen on port `5349` which uses TLS. One can reconfigure both ports. +For example, port `5349` can be reconfigured to be port `443`; so that TURN traffic can not be distinguished from any other TLS traffic. This might help with overcoming certain firewall restrictions. You can instead use (if that's -easier with firewall rules) for example ports ``80`` and ``443`` (requires to +easier with firewall rules) for example ports `80` and `443` (requires to run restund as root) or do a redirect from a load balancer (if using one) to -redirect ``443 -> 5349`` and ``80 -> 3478``. - +redirect `443 -> 5349` and `80 -> 3478`. -Amount of users and file descriptors -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## Amount of users and file descriptors Each allocation (active connection by one participant) requires 1 or 2 file descriptors, so ensure you increase your file descriptor limits in @@ -140,28 +130,26 @@ Currently one restund server can have a maximum of 64000 allocations. If you have more users than that in an active call, you need to deploy more restund servers. -Load balancing and high-availability -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## Load balancing and high-availability Load balancing is not possible, since STUN/TURN is a stateful protocol, -so UDP packets addressed to ``restund server 1``, if by means of a load -balancer were to end up at ``restund server 2``, would get dropped, as +so UDP packets addressed to `restund server 1`, if by means of a load +balancer were to end up at `restund server 2`, would get dropped, as the second server doesn't know the source address. High-availability is nevertheless ensured by having and advertising more than one restund server. Instead of the load balancer, the clients will switch their server if it fails. -Discovery and establishing a call -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## Discovery and establishing a call A simplified flow of how restund servers, along with the wire-server are used to establish a call: -.. image:: img/flow-restund.png +```{image} img/flow-restund.png +``` -DNS -~~~ +## DNS Usually DNS records are used which point to the public IPs of the restund servers (or of the respective firewall or load balancer diff --git a/docs/src/understand/sft.rst b/docs/src/understand/sft.md similarity index 77% rename from docs/src/understand/sft.rst rename to docs/src/understand/sft.md index aec41fe742..28f2c432d6 100644 --- a/docs/src/understand/sft.rst +++ b/docs/src/understand/sft.md @@ -1,82 +1,76 @@ -.. _understand-sft: +(understand-sft)= -Conference Calling 2.0 (aka SFT) -================================ +# Conference Calling 2.0 (aka SFT) -Background ----------- +## Background Previously, Wire group calls were implemented as a mesh, where each participant was connected to each other in a peer-to-peer fashion. This meant that a client would have to upload their video and audio feeds separately for each participant. This in practice meant that the amount of participants was limited by the upload bandwidth of the clients. -Wire now has a signalling-forwarding unit called `SFT `__ which allows clients to upload once and +Wire now has a signalling-forwarding unit called [SFT](https://github.com/wireapp/wire-avs-service) which allows clients to upload once and then the SFT fans it out to the other clients. Because connections are not end-to-end anymore now, dTLS encryption offered by WebRTC is not enough anymore as the encryption is terminated at the server-side. To avoid Wire from seeing the contents of calls SFT utilises WebRTC InsertibleStreams to encrypt the packets a second time with a group key that is not known to the server. With SFT it is thus possible to have conference calls with many participants without compromising end-to-end security. -.. note:: - We will describe conferencing first in a single domain in this section. - Conferencing in an environment with Federation is described in the - :ref:`federated conferencing` section. +```{note} +We will describe conferencing first in a single domain in this section. +Conferencing in an environment with Federation is described in the +{ref}`federated conferencing` section. +``` - -Architecture ------------- +## Architecture The following diagram is centered around SFT and its role within a calling setup. Restund is seen as a mere client proxy and its relation to and interaction with a client is explained -:ref:`here `. The diagram shows that a call resides on a single SFT instance +{ref}`here `. The diagram shows that a call resides on a single SFT instance and that the instance allocates at least one port for media transport per participant in the call. -.. figure:: img/architecture-sft.png - - SFT signaling, and media sending from the perspective of one caller +```{figure} img/architecture-sft.png +SFT signaling, and media sending from the perspective of one caller +``` - -Establishing a call -------------------- +## Establishing a call 1. *Client A* wants to initiate a call. It contacts all the known SFT servers via HTTPS. The SFT server that is quickest to respond is the one that will be used by the client. - (Request 1: ``CONFCONN``) + (Request 1: `CONFCONN`) 2. *Client A* gathers connection candidates (own public IP, public IP of the network the - client is in with the help of STUN, through TURN servers) [1]_ for the SFT server to + client is in with the help of STUN, through TURN servers) [^footnote-1] for the SFT server to establish a media connection to *Client A*. These information are then being send again - from *Client A* to the chosen SFT server via HTTPS request. (Request 2: ``SETUP``) + from *Client A* to the chosen SFT server via HTTPS request. (Request 2: `SETUP`) 3. The SFT server tests which of the connection candidates actually work. Meaning, it goes through all the candidates until one leads to a successful media connection between itself and *client A* -4. *Client A* sends an end-to-end encrypted message [2]_ ``CONFSTART`` to all members of chat, which contains +4. *Client A* sends an end-to-end encrypted message [^footnote-2] `CONFSTART` to all members of chat, which contains the URL of the SFT server that is being used for the call. 5. Any other client that wants to join the call, does 1. + 2. with the exception of **only** contacting one SFT server i.e. the one that *client A* chose and told all other - potential participants about via ``CONFSTART`` message + potential participants about via `CONFSTART` message At that point a media connection between *client A* and the SFT server has been established, and they continue talking to each other by using the data-channel, which uses the media connection (i.e. no more HTTPS at that point). There are just 2 HTTPS request/response sequences per participant. -.. [1] STUN & TURN are both part of a :ref:`Restund server ` -.. [2] This encrypted message is sent in the same conversation, hidden from user's view but - interpreted by user's clients. It is sent via backend servers and forwarded to other - conversation participants, not to or via SFT. +[^footnote-1]: STUN & TURN are both part of a {ref}`Restund server ` +[^footnote-2]: This encrypted message is sent in the same conversation, hidden from user's view but + interpreted by user's clients. It is sent via backend servers and forwarded to other + conversation participants, not to or via SFT. -Prerequisites -------------- +## Prerequisites For Conference Calling to function properly, clients need to be able to reach the HTTPS interface of the SFT server(s) - either directly or through a load balancer sitting in front of the servers. This is only needed for the call initiation/joining part. Additionally, for the media connection, clients and SFT servers should be able to reach each other -via UDP (see :ref:`Firewall rules `). +via UDP (see {ref}`Firewall rules `). If that is not possible, then at least SFT servers and Restund servers should be able to reach each other via UDP - and clients may connect via UDP and/or TCP to Restund servers -(see :ref:`Protocols and open ports `), which in +(see {ref}`Protocols and open ports `), which in turn will connect to the SFT server. In the unlikely scenario where no UDP is allowed whatsoever or SFT servers may not be able to reach the Restund servers that clients are using to make themselves reachable, an SFT server itself can @@ -90,19 +84,17 @@ Due to this `hostNetwork` limitation only one SFT instance can run per node so i As a rule of thumb you will need 1vCPU of compute per 50 participants. SFT will utilise multiple cores. You can use this rule of thumb to decide how many kubernetes nodes you need to provision. -For more information about capacity planning and networking please refer to the `technical documentation `__ +For more information about capacity planning and networking please refer to the [technical documentation](https://github.com/wireapp/wire-server/blob/eab0ce1ff335889bc5a187c51872dfd0e78cc22b/charts/sftd/README.md) -.. _federated-sft: +(federated-sft)= -Federated Conference Calling -============================ +# Federated Conference Calling -Conferencing in a federated environment assumes that each domain participating in a +Conferencing in a federated environment assumes that each domain participating in a conference will use an SFT in its own domain. The SFT in the caller's domain is called -the `anchor SFT`. +the `anchor SFT`. -Multi-SFT Architecture ----------------------- +## Multi-SFT Architecture With support for federation, each domain participating in a conference is responsible to make available an SFT for users in that domain. The SFT in the domain of the caller is @@ -116,7 +108,7 @@ initiates a call in a federated conversation which contains herself, Adam also i A, and Bob and Beth in domain B. Alice's client first creates a conference and is assigned a conference URL on SFT A2. Because the SFT is configured for federation, it assumes the role of anchor and also returns an IP address and port (the `anchor SFT tuple`) -which can be used by any federated SFTs which need to connect. (Alice sets up her media +which can be used by any federated SFTs which need to connect. (Alice sets up her media connection with SFT A2 as normal). Alice's client forwards the conference URL and the anchor SFT tuple to the other @@ -128,9 +120,9 @@ to the anchor SFT using the anchor SFT tuple and provides the SFT URL. (Bob's cl also sets up media with SFT B1 normally.) At this point all paths are established and the conference call can happen normally. -.. figure:: img/multi-sft-noturn.png - - Basic Multi-SFT conference initiated by Alice in domain A, with Bob in domain B +```{figure} img/multi-sft-noturn.png +Basic Multi-SFT conference initiated by Alice in domain A, with Bob in domain B +``` Because some customers do not wish to expose their SFTs directly to hosts on the public Internet, the SFTs can allocate a port on a TURN server. In this way, only the IP @@ -140,16 +132,16 @@ this scenario. In this configuration, SFT A2 requests an allocation from the fe TURN server in domain A before responding to Alice. The anchor SFT tuple is the address allocated on the federation TURN server in domain A. -.. figure:: img/multi-sft-turn.png - - Multi-SFT conference with TURN servers between federated SFTs +```{figure} img/multi-sft-turn.png +Multi-SFT conference with TURN servers between federated SFTs +``` Finally, for extremely restrictive firewall environments, the TURN servers used for federated SFT traffic can be further secured with a TURN to TURN mutually authenticated DTLS connection. The SFTs allocate a channel inside this DTLS connection per conference. The channel number is included along with the anchor SFT tuple returned to Alice, which Alice shares with the conversation, which Bob sends to SFT B1, -and which SFT B1 uses when forming its DTLS connection to SFT A2. This DTLS connection +and which SFT B1 uses when forming its DTLS connection to SFT A2. This DTLS connection runs on a dedicated port number which is not used for regular TURN traffic. Under this configuration, only that single IP address and port is exposed for each federated TURN server with all SFT traffic multiplexed over the connection. The diagram below shows @@ -157,7 +149,6 @@ this scenario. Note that this TURN DTLS multiplexing is only used for SFT to SF communication into federated group calls, and does not affect the connectivity requirements for normal one-on-one calls. -.. figure:: img/multi-sft-turn-dtls.png - - Multi-SFT conference with federated TURN servers with DTLS multiplexing - +```{figure} img/multi-sft-turn-dtls.png +Multi-SFT conference with federated TURN servers with DTLS multiplexing +```