diff --git a/_snippets/common-links.md b/_snippets/common-links.md index 4a52d80..39661fe 100644 --- a/_snippets/common-links.md +++ b/_snippets/common-links.md @@ -1,3 +1,4 @@ + [archivebox]: /recipes/archivebox/ [authelia]: /ha-docker-swarm/authelia/ [autopirate]: /recipes/autopirate/ @@ -30,8 +31,8 @@ [photoprism]: /recipes/photoprism/ [plex]: /recipes/plex/ [portainer]: /recipes/portainer/ -[radarr]: /recipes/autopirate/radarr/ [prowlarr]: /recipes/autopirate/prowlarr/ +[radarr]: /recipes/autopirate/radarr/ [readarr]: /recipes/autopirate/readarr/ [rss-bridge]: /recipes/rss-bridge/ [rtorrent]: /recipes/autopirate/rtorrent/ diff --git a/manuscript/ha-docker-swarm/docker-swarm-mode.md b/manuscript/ha-docker-swarm/docker-swarm-mode.md index 7754294..3312666 100644 --- a/manuscript/ha-docker-swarm/docker-swarm-mode.md +++ b/manuscript/ha-docker-swarm/docker-swarm-mode.md @@ -102,7 +102,7 @@ KEEP_IMAGES=traefik,keepalived,docker-mailserver DEBUG=1 ``` -Then create a docker-compose.yml as follows: +Then create a docker-compose.yml as per the following example: ```yaml version: "3" @@ -135,7 +135,7 @@ Launch the cleanup stack by running ```docker stack deploy docker-cleanup -c (_obviously, customized for your domain and having created a DNS record_), and all going according to plan, you'll be redirected to a CoreOS Dex login. Once successfully logged in, you'll be directed to the basic whoami page :thumbsup: +Browse to (*obviously, customized for your domain and having created a DNS record*), and all going according to plan, you'll be redirected to a CoreOS Dex login. Once successfully logged in, you'll be directed to the basic whoami page :thumbsup: ### Protect services -To protect any other service, ensure the service itself is exposed by Traefik (_if you were previously using an oauth_proxy for this, you may have to migrate some labels from the oauth_proxy serivce to the service itself_). Add the following label: +To protect any other service, ensure the service itself is exposed by Traefik. Add the following label: ```yaml - "traefik.http.routers.radarr.middlewares=forward-auth" diff --git a/manuscript/ha-docker-swarm/traefik-forward-auth/google.md b/manuscript/ha-docker-swarm/traefik-forward-auth/google.md index fbd2980..1b8eb0c 100644 --- a/manuscript/ha-docker-swarm/traefik-forward-auth/google.md +++ b/manuscript/ha-docker-swarm/traefik-forward-auth/google.md @@ -1,6 +1,10 @@ +--- +title: Log into traefik forward auth with Google authentication +description: Traefik forward auth needs an authentication backend, and one of the simplest to setup, allows users to login with their Google account +--- # Traefik Forward Auth using Google -[Traefik Forward Auth](/ha-docker-swarm/traefik-forward-auth/) is incredibly useful to secure services with an additional layer of authentication, provided by an OIDC-compatible provider. The simplest possible provider is a self-hosted instance of [Dex][dex], configured with a static username and password. This is not much use if you want to provide "normies" access to your services though - a better solution would be to validate their credentials against an existing trusted public source. +[Traefik Forward Auth][tfa] is incredibly useful to secure services with an additional layer of authentication, provided by an OIDC-compatible provider. The simplest possible provider is a self-hosted instance of [Dex][tfa-dex-static], configured with a static username and password. This is not much use if you want to provide "normies" access to your services though - a better solution would be to validate their credentials against an existing trusted public source. This recipe will illustrate how to point Traefik Forward Auth to Google, confirming that the requestor has a valid Google account (*and that said account is permitted to access your services!*) @@ -25,7 +29,7 @@ Here's a [screencast I recorded](https://static.funkypenguin.co.nz/2021/screenca ### Prepare environment -Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.env` as follows: +Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.env` as per the following example: ```bash PROVIDERS_GOOGLE_CLIENT_ID= @@ -39,7 +43,7 @@ WHITELIST=you@yourdomain.com, me@mydomain.com ### Prepare the docker service config -Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.yml` as follows: +Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.yml` as per the following example: ```yaml traefik-forward-auth: diff --git a/manuscript/ha-docker-swarm/traefik-forward-auth/index.md b/manuscript/ha-docker-swarm/traefik-forward-auth/index.md index 2b4e956..34a85bf 100644 --- a/manuscript/ha-docker-swarm/traefik-forward-auth/index.md +++ b/manuscript/ha-docker-swarm/traefik-forward-auth/index.md @@ -1,12 +1,16 @@ +--- +title: Add oauth2 and oidc SSO to Docker with Traefik Forward Auth +description: Traefik Forward Auth protects services running in Docker Swarm with an additional layer of authentication, and can be integrated into Google, GitHub, even Active Directory auth! +--- # Traefik Forward Auth -Now that we have Traefik deployed, automatically exposing SSL access to our Docker Swarm services using LetsEncrypt wildcard certificates, let's pause to consider that we may not _want_ some services exposed directly to the internet... +Now that we have Traefik deployed, automatically exposing SSL access to our Docker Swarm services using LetsEncrypt wildcard certificates, let's pause to consider that we may not *want* some services exposed directly to the internet... -..Wait, why not? Well, Traefik doesn't provide any form of authentication, it simply secures the **transmission** of the service between Docker Swarm and the end user. If you were to deploy a service with no native security (*[Radarr](/recipes/autopirate/radarr/) or [Sonarr](/recipes/autopirate/sonarr/) come to mind*), then anybody would be able to use it! Even services which _may_ have a layer of authentication **might** not be safe to expose publically - often open source projects may be maintained by enthusiasts who happily add extra features, but just pay lip service to security, on the basis that "*it's the user's problem to secure it in their own network*". +..Wait, why not? Well, Traefik doesn't provide any form of authentication, it simply secures the **transmission** of the service between Docker Swarm and the end user. If you were to deploy a service with no native security (*[Radarr][radarr] or [Sonarr][sonarr] come to mind*), then anybody would be able to use it! Even services which *may* have a layer of authentication **might** not be safe to expose publically - often open source projects may be maintained by enthusiasts who happily add extra features, but just pay lip service to security, on the basis that "*it's the user's problem to secure it in their own network*". Some of the platforms we use on our swarm may have strong, proven security to prevent abuse. Techniques such as rate-limiting (*to defeat brute force attacks*) or even support 2-factor authentication (*tiny-tiny-rss or Wallabag support this)*. -Other platforms may provide **no authentication** (Traefik's web UI for example), or minimal, un-proven UI authentication which may have been added as an afterthought. +Other platforms may provide **no authentication** (*Traefik's own web UI for example*), or minimal, un-proven UI authentication which may have been added as an afterthought. Still other platforms may hold such sensitive data (*i.e., NextCloud*), that we'll feel more secure by putting an additional authentication layer in front of them. @@ -16,37 +20,37 @@ This is the role of Traefik Forward Auth. **Normally**, Traefik proxies web requests directly to individual web apps running in containers. The user talks directly to the webapp, and the webapp is responsible for ensuring appropriate authentication. -When employing Traefik Forward Auth as "[middleware](https://doc.traefik.io/traefik/middlewares/forwardauth/)", the forward-auth process sits in the middle of this transaction - traefik receives the incoming request, "checks in" with the auth server to determine whether or not further authentication is required. If the user is authenticated, the auth server returns a 200 response code, and Traefik is authorized to forward the request to the backend. If not, traefik passes the auth server response back to the user - this process will usually direct the user to an authentication provider (_GitHub, Google, etc_), so that they can perform a login. +When employing Traefik Forward Auth as "[middleware](https://doc.traefik.io/traefik/middlewares/forwardauth/)", the forward-auth process sits in the middle of this transaction - traefik receives the incoming request, "checks in" with the auth server to determine whether or not further authentication is required. If the user is authenticated, the auth server returns a 200 response code, and Traefik is authorized to forward the request to the backend. If not, traefik passes the auth server response back to the user - this process will usually direct the user to an authentication provider (*[Google][tfa-google], [Keycloak][tfa-keycloak], and [Dex][tfa-dex-static] are common examples*), so that they can perform a login. Illustrated below: ![Traefik Forward Auth](../../images/traefik-forward-auth.png) -The advantage under this design is additional security. If I'm deploying a web app which I expect only an authenticated user to require access to (*unlike something intended to be accessed publically, like [Linx](/recipes/linx/)*), I'll pass the request through Traefik Forward Auth. The overhead is negligible, and the additional layer of security is well-worth it. +The advantage under this design is additional security. If I'm deploying a web app which I expect only an authenticated user to require access to (*unlike something intended to be accessed publically, like [Linx][linx]*), I'll pass the request through Traefik Forward Auth. The overhead is negligible, and the additional layer of security is well-worth it. -## What is AuthHost mode +## AuthHost mode -Under normal OIDC auth, you have to tell your auth provider which URLs it may redirect an authenticated user back to, post-authentication. This is a security feture of the OIDC spec, preventing a malicious landing page from capturing your session and using it to impersonate you. When you're securing many URLs though, explicitly listing them can be a PITA. +Under normal Oauth2 / OIDC auth, you have to tell your auth provider which URLs it may redirect an authenticated user back to, post-authentication. This is a security feture of the OIDC spec, preventing a malicious landing page from capturing your session and using it to impersonate you. When you're securing many URLs though, explicitly listing them can be a PITA. [@thomaseddon's traefik-forward-auth](https://github.com/thomseddon/traefik-forward-auth) includes an ingenious mechanism to simulate an "_auth host_" in your OIDC authentication, so that you can protect an unlimited amount of DNS names (_with a common domain suffix_), without having to manually maintain a list. ### How does it work? -Say you're protecting **radarr.example.com**. When you first browse to ****, Traefik forwards your session to traefik-forward-auth, to be authenticated. Traefik-forward-auth redirects you to your OIDC provider's login (_KeyCloak, in this case_), but instructs the OIDC provider to redirect a successfully authenticated session **back** to ****, rather than to ****. +Say for example, you're protecting **radarr.example.com**. When you first browse to ****, Traefik forwards your session to traefik-forward-auth, to be authenticated. Traefik-forward-auth redirects you to your OIDC provider's login, but instructs the OIDC provider to redirect a successfully authenticated session **back** to ****, rather than to ****. -When you successfully authenticate against the OIDC provider, you are redirected to the "_redirect_uri_" of . Again, your request hits Traefik, which forwards the session to traefik-forward-auth, which **knows** that you've just been authenticated (_cookies have a role to play here_). Traefik-forward-auth also knows the URL of your **original** request (_thanks to the X-Forwarded-Whatever header_). Traefik-forward-auth redirects you to your original destination, and everybody is happy. +When you successfully authenticate against the OIDC provider, you are redirected to the "_redirect_uri_" of . Again, your request hits Traefik, which forwards the session to traefik-forward-auth, which **knows** that you've just been authenticated (*cookies have a role to play here*). Traefik-forward-auth also knows the URL of your **original** request (*thanks to the X-Forwarded-Host header*). Traefik-forward-auth redirects you to your original destination, and everybody is happy. This clever workaround only works under 2 conditions: -1. Your "auth host" has the same domain name as the hosts you're protecting (_i.e., auth.example.com protecting radarr.example.com_) -2. You explictly tell traefik-forward-auth to use a cookie authenticating your **whole** domain (_i.e. example.com_) +1. Your "auth host" has the same domain name as the hosts you're protecting (*i.e., auth.example.com protecting radarr.example.com*) +2. You explictly tell traefik-forward-auth to use a cookie authenticating your **whole** domain (*i.e. example.com*) ## Authentication Providers -Traefik Forward Auth needs to authenticate an incoming user against a provider. A provider can be something as simple as a self-hosted [dex][tfa-dex] instance with a single static username/password, or as complex as a [KeyCloak][keycloak] instance backed by [OpenLDAP][openldap]. Here are some options, in increasing order of complexity... +Traefik Forward Auth needs to authenticate an incoming user against a provider. A provider can be something as simple as a self-hosted [dex][tfa-dex-static] instance with a single static username/password, or as complex as a [KeyCloak][keycloak] instance backed by [OpenLDAP][openldap]. Here are some options, in increasing order of complexity... -* [Authenticate against a self-hosted Dex instance with static usernames and passwords][tfa-dex-static] -* [Authenticate against a whitelist of Google accounts][tfa-google] -* [Authenticate against a self-hosted KeyCloak instance][tfa-keycloak] +* [Authenticate Traefik Forward Auth against a self-hosted Dex instance with static usernames and passwords][tfa-dex-static] +* [Authenticate Traefik Forward Auth against a whitelist of Google accounts][tfa-google] +* [Authenticate Traefik Forward Auth against a self-hosted KeyCloak instance][tfa-keycloak] with an optional [OpenLDAP backend][openldap] --8<-- "recipe-footer.md" diff --git a/manuscript/ha-docker-swarm/traefik-forward-auth/keycloak.md b/manuscript/ha-docker-swarm/traefik-forward-auth/keycloak.md index 3682207..c0278eb 100644 --- a/manuscript/ha-docker-swarm/traefik-forward-auth/keycloak.md +++ b/manuscript/ha-docker-swarm/traefik-forward-auth/keycloak.md @@ -1,9 +1,13 @@ +--- +title: Authenticate traefik forward auth with Keycloak +description: Traefik forward auth needs an authentication backend, and a perfect match for the self-hosted enthusiast is Keycloak +--- # Using Traefik Forward Auth with KeyCloak While the [Traefik Forward Auth](/ha-docker-swarm/traefik-forward-auth/) recipe demonstrated a quick way to protect a set of explicitly-specified URLs using OIDC credentials from a Google account, this recipe will illustrate how to use your own KeyCloak instance to secure **any** URLs within your DNS domain. !!! tip "Keycloak with Traefik" - Did you land here from Google, looking for information about using Keycloak with Traefik? All this and more is covered in the [Keycloak][keycloak] recipe! + Did you land here from a search, looking for information about using Keycloak with Traefik? All this and more is covered in the [Keycloak][keycloak] recipe! --8<-- "recipe-tfa-ingredients.md" @@ -11,7 +15,7 @@ While the [Traefik Forward Auth](/ha-docker-swarm/traefik-forward-auth/) recipe ### Setup environment -Create `/var/data/config/traefik/traefik-forward-auth.env` as follows (_change "master" if you created a different realm_): +Create `/var/data/config/traefik/traefik-forward-auth.env` as per the following example (_change "master" if you created a different realm_): ```bash CLIENT_ID= diff --git a/manuscript/ha-docker-swarm/traefik.md b/manuscript/ha-docker-swarm/traefik.md index eb20319..0124a38 100644 --- a/manuscript/ha-docker-swarm/traefik.md +++ b/manuscript/ha-docker-swarm/traefik.md @@ -34,7 +34,7 @@ To deal with these gaps, we need a front-end load-balancer, and in this design, While it's possible to configure traefik via docker command arguments, I prefer to create a config file (`traefik.toml`). This allows me to change traefik's behaviour by simply changing the file, and keeps my docker config simple. -Create `/var/data/traefikv2/traefik.toml` as follows: +Create `/var/data/traefikv2/traefik.toml` as per the following example: ```bash [global] @@ -85,7 +85,7 @@ Create `/var/data/traefikv2/traefik.toml` as follows: !!! tip "We'll want an overlay network, independent of our traefik stack, so that we can attach/detach all our other stacks (including traefik) to the overlay network. This way, we can undeploy/redepoly the traefik stack without having to bring down every other stack first!" - voice of hard-won experience -Create `/var/data/config/traefik/traefik.yml` as follows: +Create `/var/data/config/traefik/traefik.yml` as per the following example: ```yaml version: "3.2" @@ -126,7 +126,7 @@ AWS_SECRET_ACCESS_KEY= # CLOUDFLARE_API_KEY= ``` -Create `/var/data/config/traefikv2/traefikv2.yml` as follows: +Create `/var/data/config/traefikv2/traefikv2.yml` as per the following example: ```yaml version: "3.2" diff --git a/manuscript/kubernetes/traefik.md b/manuscript/kubernetes/traefik.md index a221fb1..6f8be80 100644 --- a/manuscript/kubernetes/traefik.md +++ b/manuscript/kubernetes/traefik.md @@ -99,7 +99,7 @@ metrics: Since we deployed Traefik using helm, we need to take a slightly different approach, so we'll create a pod with an affinity which ensures it runs on the same host which runs the Traefik container (_more precisely, containers with the label app=traefik_). -Create phone-home.yaml as follows: +Create phone-home.yaml as per the following example: ```yaml apiVersion: v1 diff --git a/manuscript/recipes/autopirate/index.md b/manuscript/recipes/autopirate/index.md index fefd193..8fa5454 100644 --- a/manuscript/recipes/autopirate/index.md +++ b/manuscript/recipes/autopirate/index.md @@ -6,7 +6,7 @@ description: A fully-featured recipe to automate finding, downloading, and organ Once the cutting edge of the "internet" (_pre-world-wide-web and mosiac days_), Usenet is now a murky, geeky alternative to torrents for file-sharing. However, it's **cool** geeky, especially if you're into having a fully automated media platform. -A good starter for the usenet scene is . Because it's so damn complicated, a host of automated tools exist to automate the process of finding, downloading, and managing content. The tools included in this recipe are as follows: +A good starter for the usenet scene is . Because it's so damn complicated, a host of automated tools exist to automate the process of finding, downloading, and managing content. The tools included in this recipe are as per the following example: ![Autopirate Screenshot](../../images/autopirate.png) diff --git a/manuscript/recipes/collabora-online.md b/manuscript/recipes/collabora-online.md index 7ebb70d..88799e7 100644 --- a/manuscript/recipes/collabora-online.md +++ b/manuscript/recipes/collabora-online.md @@ -69,7 +69,7 @@ termination=true ### Create docker-compose.yml -Create ```/var/data/config/collabora/docker-compose.yml``` as follows: +Create ```/var/data/config/collabora/docker-compose.yml``` as per the following example: ```yaml version: "3.0" @@ -91,7 +91,7 @@ services: ### Create nginx.conf -Create ```/var/data/config/collabora/nginx.conf``` as follows, changing the ```server_name``` value to match the environment variable you established above: +Create ```/var/data/config/collabora/nginx.conf``` as per the following example, changing the ```server_name``` value to match the environment variable you established above: ```ini upstream collabora-upstream { @@ -155,7 +155,7 @@ Create an empty `/var/data/collabora/loolwsd.xml` by running `touch /var/data/co ### Setup Docker Swarm -Create `/var/data/config/collabora/collabora.yml` as follows, changing the traefik frontend_rule as necessary: +Create `/var/data/config/collabora/collabora.yml` as per the following example, changing the traefik frontend_rule as necessary: --8<-- "premix-cta.md" diff --git a/manuscript/recipes/duplicati.md b/manuscript/recipes/duplicati.md index 9055be3..a64693f 100644 --- a/manuscript/recipes/duplicati.md +++ b/manuscript/recipes/duplicati.md @@ -30,7 +30,7 @@ Similar to the other backup options in the Cookbook, we can use Duplicati to bac ### Setup data locations -We'll need a folder to store a docker-compose configuration file and an associated environment file. If you're following my filesystem layout, create `/var/data/config/duplicati` (*for the config*), and `/var/data/duplicati` (*for the metadata*) as follows: +We'll need a folder to store a docker-compose configuration file and an associated environment file. If you're following my filesystem layout, create `/var/data/config/duplicati` (*for the config*), and `/var/data/duplicati` (*for the metadata*) as per the following example: ```bash mkdir /var/data/config/duplicati diff --git a/manuscript/recipes/duplicity.md b/manuscript/recipes/duplicity.md index 19670ba..fad47e2 100644 --- a/manuscript/recipes/duplicity.md +++ b/manuscript/recipes/duplicity.md @@ -35,7 +35,7 @@ So what does this mean for our stack? It means we can leverage Duplicity to back ### Setup data locations -We'll need a folder to store a docker-compose .yml file, and an associated .env file. If you're following my filesystem layout, create `/var/data/config/duplicity` (_for the config_), and `/var/data/duplicity` (_for the metadata_) as follows: +We'll need a folder to store a docker-compose .yml file, and an associated .env file. If you're following my filesystem layout, create `/var/data/config/duplicity` (_for the config_), and `/var/data/duplicity` (_for the metadata_) as per the following example: ```bash mkdir /var/data/config/duplicity @@ -72,7 +72,7 @@ See the [data layout reference](/reference/data_layout/) for an explanation of t ### Run a test backup -Before we launch the automated daily backups, let's run a test backup, as follows: +Before we launch the automated daily backups, let's run a test backup, as per the following example: ```bash docker run --env-file duplicity.env -it --rm -v \ diff --git a/manuscript/recipes/keycloak/index.md b/manuscript/recipes/keycloak/index.md index 27513f4..1a8db46 100644 --- a/manuscript/recipes/keycloak/index.md +++ b/manuscript/recipes/keycloak/index.md @@ -16,7 +16,7 @@ KeyCloak's OpenID provider can also be used in combination with [Traefik Forward ### Setup data locations -We'll need several directories to bind-mount into our container for both runtime and backup data, so create them as follows +We'll need several directories to bind-mount into our container for both runtime and backup data, so create them as per the following example ```bash mkdir -p /var/data/runtime/keycloak/database diff --git a/manuscript/recipes/minio.md b/manuscript/recipes/minio.md index 48f3395..d5b760b 100644 --- a/manuscript/recipes/minio.md +++ b/manuscript/recipes/minio.md @@ -184,7 +184,7 @@ Having setup your buckets, users, and policies - you can give out your minio ext I tested the S3 mount using [goofys](https://github.com/kahing/goofys), "a high-performance, POSIX-ish Amazon S3 file system written in Go". -First, I created ~/.aws/credentials, as follows: +First, I created ~/.aws/credentials, as per the following example: ```ini [default] diff --git a/manuscript/recipes/openldap.md b/manuscript/recipes/openldap.md index e2a6658..26aa5bf 100644 --- a/manuscript/recipes/openldap.md +++ b/manuscript/recipes/openldap.md @@ -53,7 +53,7 @@ LDAP_TLS=false The Dockerized version of LDAP Account Manager is a little fiddly. In order to maintain a config file which persists across container restarts, we need to present the container with a copy of /var/www/html/config/lam.conf, tweaked for our own requirements. -Create ```/var/data/openldap/lam/config/config.cfg``` as follows: +Create ```/var/data/openldap/lam/config/config.cfg``` as per the following example: ???+ note "Much scroll, very text. Click here to collapse it for better readability" @@ -123,7 +123,7 @@ Create ```/var/data/openldap/lam/config/config.cfg``` as follows: While config.cfg (_above_) defines application-level configuration, .cfg is used to configure "domain-specific" configuration. You probably only need a single profile, but LAM could theoretically be used to administer several totally unrelated LDAP servers, ergo the concept of "profiles". -Create yours profile (_you chose a default profile in config.cfg above, remember?_) by creating ```/var/data/openldap/lam/config/.conf```, as follows: +Create yours profile (_you chose a default profile in config.cfg above, remember?_) by creating ```/var/data/openldap/lam/config/.conf```, as per the following example: ???+ note "Much scroll, very text. Click here to collapse it for better readability" diff --git a/manuscript/recipes/restic.md b/manuscript/recipes/restic.md index 96d7613..691fbac 100644 --- a/manuscript/recipes/restic.md +++ b/manuscript/recipes/restic.md @@ -173,7 +173,7 @@ Of note above is =="Repository successfully initialized"== - this indicates that Repeat after me : "**It's not a backup unless you've tested a restore**" -The simplest way to test your restore is to run the container once, using the variables you're already prepared, with custom arguments, as follows: +The simplest way to test your restore is to run the container once, using the variables you're already prepared, with custom arguments, as per the following example: ```bash docker run --rm -it --name restic-restore --env-file /var/data/config/restic/restic-backup.env \ diff --git a/manuscript/reference/data_layout.md b/manuscript/reference/data_layout.md index 77ddb46..ccf5b90 100644 --- a/manuscript/reference/data_layout.md +++ b/manuscript/reference/data_layout.md @@ -2,7 +2,7 @@ The applications deployed in the stack utilize a combination of data-at-rest (_static config, files, etc_) and runtime data (_live database files_). The realtime data can't be [backed up](/recipes/duplicity) with a simple copy-paste, so where we employ databases, we also include containers to perform a regular export of database data to a filesystem location. -So that we can confidently backup all our data, I've setup a data layout as follows: +So that we can confidently backup all our data, I've setup a data layout as per the following example: ## Configuration data