Compare commits
No commits in common. "main" and "chore(docs)ipv6-docu" have entirely different histories.
main
...
chore(docs
12 changed files with 8 additions and 296 deletions
|
|
@ -20,16 +20,9 @@ This repository contains documentation, guides, and reference material.
|
||||||
Documentation and guides related to Keycloak configuration and best practices.
|
Documentation and guides related to Keycloak configuration and best practices.
|
||||||
- [Enforce OTP 2FA for Internal Users](./keycloak/enforce-otp-internal.md)
|
- [Enforce OTP 2FA for Internal Users](./keycloak/enforce-otp-internal.md)
|
||||||
Step-by-step instructions for enforcing OTP-based two-factor authentication for internal users, while excluding external Microsoft Entra users.
|
Step-by-step instructions for enforcing OTP-based two-factor authentication for internal users, while excluding external Microsoft Entra users.
|
||||||
- [Integrate MS Entra in Keycloak as IDP](./keycloak/idp-ms-entra.md)
|
|
||||||
Step-by-step instructions for integrating MS Entra as identity-provider.
|
|
||||||
|
|
||||||
- **[Microsoft Entra](./ms-entra/)**
|
- **[Microsoft Entra](./ms-entra/)**
|
||||||
Documentation and guides related to Microsft Entra configuration and best practices.
|
Documentation and guides related to Microsft Entra configuration and best practices.
|
||||||
- [Enterprise App Integration with Keycloak](./ms-entra/enterprise-app-keycloak.md)
|
- [Enterprise App Integration with Keycloak](./ms-entra/enterprise-app-keycloak.md)
|
||||||
Step-by-step instructions for creating an Enterprise Application in Microsoft Entra (Azure AD) as an identity provider for Keycloak.
|
Step-by-step instructions for creating an Enterprise Application in Microsoft Entra (Azure AD) as an identity provider for Keycloak.
|
||||||
|
|
||||||
- **[Troubleshooting](./troubleshooting/)**
|
|
||||||
Encountered & solved problems.
|
|
||||||
- [Nextcloud File Locking](./troubleshooting/nextcloud-file-locking.md)
|
|
||||||
Preventing sync conflicts when multiple users edit the same file via the Nextcloud desktop client.
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1 @@
|
||||||
# ACME DNS Challenges
|
#ACME
|
||||||
## Summary
|
|
||||||
We agreed to use **ACME DNS-01 challenges** for issuing certificates for **both public-facing and internal services**. A key benefit is that DNS-01 **enables internal certificate issuance** in the first place, since the CA only needs to verify TXT records in DNS (no inbound HTTP/ALPN access to the service). To keep our primary DNS zones clean, we will create a **separate, dedicated zone** for ACME challenges and **delegate** challenge records to it via **CNAME**.
|
|
||||||
|
|
||||||
## Decisions
|
|
||||||
- Use **ACME DNS-01** as the challenge type for **both external/public and internal** certificate issuance.
|
|
||||||
- Create a **dedicated DNS zone** for ACME challenges (e.g., `_acme.example.com`).
|
|
||||||
- For each certificate FQDN, publish a **CNAME** at `_acme-challenge.<fqdn>` that points into the dedicated challenge zone.
|
|
||||||
- Store the **TXT token(s)** only in the dedicated challenge zone to avoid cluttering primary zones.
|
|
||||||
- Keep **low TTLs** (e.g., 60-120s) on both CNAME and TXT records to speed up renewals.
|
|
||||||
- Restrict write access to the challenge zone to the ACME automation only.
|
|
||||||
|
|
||||||
## Meetings
|
|
||||||
- 05.08.2025: Bert-Jan Fikse, Tobias Schaller, Tobias Wüst, Tom Jampen (inital version)
|
|
||||||
|
|
||||||
## Background
|
|
||||||
The following article explains how DNS-01 challenges can be effectively used to issue Let's Encrypt certificates for servers with internal IP addresses:
|
|
||||||
|
|
||||||
- https://lists.bfh.science/pipermail/bfh-linux-announce/2021-September/000134.html
|
|
||||||
|
|
||||||
The following manpage explains important implementation details for correctly handling DNS-01 challenges:
|
|
||||||
|
|
||||||
- https://sources.debian.org/src/open-infrastructure-service-tools/20250626-2/dehydrated/share/man/dehydrated-nsupdate.1.rst#L20
|
|
||||||
|
|
||||||
## Reference Design
|
|
||||||
**Dedicated zone:**
|
|
||||||
`_acme.digitalboard.ch`
|
|
||||||
|
|
||||||
**Dedicated zone for each managed school:**
|
|
||||||
`gymkirchenfeld._acme.digitalboard.ch`
|
|
||||||
|
|
||||||
**For a service FQDN:**
|
|
||||||
Target certificate: `app1.gymkirchenfeld.ch`, `app2.kinet.ch`
|
|
||||||
|
|
||||||
**Publish in the primary zone:**
|
|
||||||
```dns
|
|
||||||
; Delegate the challenge to the dedicated zone
|
|
||||||
_acme-challenge.app1.gymkirchenfeld.ch. IN CNAME
|
|
||||||
app1.gymkirchenfeld.ch.gymkirchenfeld._acme.digitalboard.ch.
|
|
||||||
|
|
||||||
_acme-challenge.app2.kinet.ch. IN CNAME
|
|
||||||
app2.kinet.ch.gymkirchenfeld._acme.digitalboard.ch.
|
|
||||||
|
|
||||||
_acme-challenge.app.example.com. IN CNAME
|
|
||||||
app.example.com.school-a._acme.digitalboard.ch.
|
|
||||||
```
|
|
||||||
|
|
||||||
> During validation, the CA will follow the CNAME from the primary zone to the dedicated zone and read the TXT record there.
|
|
||||||
|
|
@ -1,18 +1,5 @@
|
||||||
# IPv6 – Overview and Best Practices
|
# IPv6 – Overview and Best Practices
|
||||||
|
|
||||||
## Summary
|
|
||||||
We agreed to setup dual stack by default as IPv6 is essential for modern IT infrastructures and significantly simplifies network management in the long term. By relying on **DNS names instead of raw IP addresses**, operating an **own, globally valid IPv6 stack**, using **Dual Stack during the migration phase**, and providing a **Jump Host for IPv6-only zones**, networks become more robust, scalable, and future-proof.
|
|
||||||
|
|
||||||
## Decisions
|
|
||||||
- Use **Dual Stack** (IPv4 and IPv6 addresses)
|
|
||||||
- Rely on **DNS names instead of raw IP addresses**
|
|
||||||
- **Each school is responsible for its DNS records** and must manage them for IPv4/IPv6 (including CNAME records for ACME)
|
|
||||||
- The Digitalboard provides an optional service (dynamic DNS zone for acme challenge responses) as described in the [ACME documentation](./acme.md)
|
|
||||||
- The Digitalboard might act as a RIPE customer and provide a `/32` or `/48` IPv6 network for interested schools
|
|
||||||
|
|
||||||
## Meetings
|
|
||||||
- 05.08.2025: Bert-Jan Fikse, Tobias Schaller, Tobias Wüst, Tom Jampen (inital version)
|
|
||||||
|
|
||||||
## Why IPv6?
|
## Why IPv6?
|
||||||
IPv6 was introduced to address the limitations of IPv4, most notably the shortage of available addresses. It provides an almost unlimited address space, improved support for modern networking, and forms the foundation for future-proof infrastructures.
|
IPv6 was introduced to address the limitations of IPv4, most notably the shortage of available addresses. It provides an almost unlimited address space, improved support for modern networking, and forms the foundation for future-proof infrastructures.
|
||||||
|
|
||||||
|
|
@ -23,14 +10,11 @@ IPv6 was introduced to address the limitations of IPv4, most notably the shortag
|
||||||
|
|
||||||
## Own IPv6 Stack
|
## Own IPv6 Stack
|
||||||
- The **RFC4193 range** (`fd00::/8`) is reserved for **local, private use**, similar to private IPv4 networks (e.g. `192.168.x.x`).
|
- The **RFC4193 range** (`fd00::/8`) is reserved for **local, private use**, similar to private IPv4 networks (e.g. `192.168.x.x`).
|
||||||
→ Disadvantages:
|
|
||||||
- In a dual stack environment (IPv4 and IPv6 with `fd00::/8` addresses) IPv4 is used by default, so IPv6 is never used!
|
|
||||||
- For production environments, it is preferable to use **public, globally routable IPv6 prefixes** obtained from an ISP or an own IPv6 allocation.
|
- For production environments, it is preferable to use **public, globally routable IPv6 prefixes** obtained from an ISP or an own IPv6 allocation.
|
||||||
→ Advantages:
|
→ Advantages:
|
||||||
- Unique addressing without overlaps
|
- Unique addressing without overlaps
|
||||||
- Direct reachability and routability on the Internet
|
- Direct reachability and routability on the Internet
|
||||||
- Sustainable, future-oriented network design
|
- Sustainable, future-oriented network design
|
||||||
- As a direct RIPE customer an institution can get one `/29` IPv6 network (resulting in 8 `/32` IPv6 networks) for < CHF 2'000.-/year (e.g. one `/32` network for CHF 250.-/year)
|
|
||||||
|
|
||||||
## Dual Stack as a Transition Strategy
|
## Dual Stack as a Transition Strategy
|
||||||
- In many environments, IPv4 cannot be replaced immediately.
|
- In many environments, IPv4 cannot be replaced immediately.
|
||||||
|
|
@ -43,3 +27,7 @@ IPv6 was introduced to address the limitations of IPv4, most notably the shortag
|
||||||
- A **Jump Host** with both IPv4 and IPv6 connectivity can serve as an entry point.
|
- A **Jump Host** with both IPv4 and IPv6 connectivity can serve as an entry point.
|
||||||
- It enables access from IPv4-based networks into IPv6-only segments, acting as a controlled and secure bridge during the transition phase.
|
- It enables access from IPv4-based networks into IPv6-only segments, acting as a controlled and secure bridge during the transition phase.
|
||||||
- This approach ensures operability while gradually phasing out IPv4.
|
- This approach ensures operability while gradually phasing out IPv4.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
IPv6 is essential for modern IT infrastructures and significantly simplifies network management in the long term.
|
||||||
|
By relying on **DNS names instead of raw IP addresses**, operating an **own, globally valid IPv6 stack**, using **Dual Stack during the migration phase**, and providing a **Jump Host for IPv6-only zones**, networks become more robust, scalable, and future-proof.
|
||||||
|
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
# Switch Your Digitalboard Login to Your Home Tenant
|
|
||||||
|
|
||||||
> **Goal:** If you have been signing in to a Digitalboard service as a **guest user** of another organization's tenant, and your **own (home) tenant** is now connected to Digitalboard, this guide walks you through linking your home tenant login to your existing Digitalboard account ? so you keep your data and switch to signing in through your own organization.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## When you need this guide
|
|
||||||
You should follow these steps if **all** of the following apply:
|
|
||||||
- You have used a Digitalboard service before, signed in via a **guest invitation** in another organization's tenant.
|
|
||||||
- Your **own organization's tenant** has just been connected to Digitalboard, **or** you have been authorized to sign in through your own tenant.
|
|
||||||
- You want to keep using the same Digitalboard account, but sign in through your home organization from now on.
|
|
||||||
|
|
||||||
> The email address used in your home tenant must match the email address that was used for the guest invitation. If they differ, contact your administrator before continuing.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What will happen
|
|
||||||
1. You sign out of any existing Digitalboard session.
|
|
||||||
2. You sign in the **new way** ? through your home tenant.
|
|
||||||
3. Digitalboard recognizes that an account with your email address already exists and offers to **link** the new login method to it.
|
|
||||||
4. You confirm the link by clicking a link in a verification email.
|
|
||||||
5. From that point on, you use your home tenant to sign in. Your existing data, settings, and history remain intact.
|
|
||||||
|
|
||||||
> **No data is lost.** Linking only adds a second login method to your existing Digitalboard account; it does not create a new account or delete the old one.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
- Working access to **both** login methods:
|
|
||||||
- The original guest login (other organization's tenant).
|
|
||||||
- The new login through your home tenant.
|
|
||||||
- Access to your **email inbox** for the address used in both tenants. You will need to click a confirmation link.
|
|
||||||
- A modern browser. Using a **private/incognito window** is recommended so existing sessions do not interfere.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 1 Sign out of any existing Digitalboard session
|
|
||||||
1. Open the Digitalboard service you normally use.
|
|
||||||
2. If you are signed in, **sign out** completely.
|
|
||||||
3. Close the browser window.
|
|
||||||
|
|
||||||
This makes sure the next login starts cleanly and the linking flow can run.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 2 Open the service in a private window
|
|
||||||
1. Open a **new private/incognito browser window**.
|
|
||||||
2. Navigate to the Digitalboard service login page.
|
|
||||||
|
|
||||||
You should now see the login page with the available identity provider buttons, including the new one for your home tenant.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 3 Sign in through your home tenant
|
|
||||||
1. Click the login button for your **home tenant** (the new one ? for example, **Sign in with WKS Bern**).
|
|
||||||
2. Complete the Microsoft sign-in with your **home tenant credentials**.
|
|
||||||
|
|
||||||
Because the email address matches the one already on file from your guest login, Digitalboard now detects the existing account and shows the **Account already exists** screen.
|
|
||||||
|
|
||||||

|
|
||||||
*Figure 1: The "Account already exists" screen.*
|
|
||||||
|
|
||||||
3. Click **Add to existing account**.
|
|
||||||
|
|
||||||
> Do **not** click **Review profile** ? that option creates a separate new account instead of linking to your existing one. If you click it by mistake, see the troubleshooting section below.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 4 Verify your email address
|
|
||||||
To prove that the email address belongs to you, Digitalboard sends a confirmation email to the address on file.
|
|
||||||
|
|
||||||

|
|
||||||
*Figure 2: The email verification step.*
|
|
||||||
|
|
||||||
1. Leave this browser tab open.
|
|
||||||
2. Open your email inbox in a **separate tab or window**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 5 Open the confirmation email
|
|
||||||
Look for an email with the subject **Link \<Provider Name\>** (for example, *Link WKS Bern*) from `auth@digitalboard.ch`. If it does not appear within a minute, check your spam/junk folder.
|
|
||||||
|
|
||||||

|
|
||||||
*Figure 3: The confirmation email sent by Digitalboard.*
|
|
||||||
|
|
||||||
1. Click **Link to confirm account linking**.
|
|
||||||
2. The link is valid for **5 minutes** only. If it expires, return to the verification tab and click **Click here to re-send the email**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 6 ? Confirm the link
|
|
||||||
The link opens a confirmation page in your browser.
|
|
||||||
|
|
||||||
1. Click **I confirm**.
|
|
||||||
2. You are now signed in to your existing Digitalboard account through your home tenant.
|
|
||||||
|
|
||||||
Your home tenant login is now linked to the same Digitalboard account you used as a guest before. From now on, sign in through your home tenant.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## After linking
|
|
||||||
- **Use your home tenant login from now on.** The guest login from the other tenant may still work technically, but should no longer be used. Your administrator may revoke the guest invitation in the other tenant once linking is confirmed.
|
|
||||||
- Your existing data, settings, and history remain attached to the same account.
|
|
||||||
- If anything looks wrong (missing data, wrong name displayed), contact your administrator before signing in again.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
- **I clicked "Review profile" by mistake and a new account was created** ? Do not try to fix it by signing in again. Contact your administrator and report which email address is affected. The duplicate account needs to be removed before linking can be retried.
|
|
||||||
- **I never received the confirmation email** ? Check your spam/junk folder. If it is not there, return to the verification page (still open in the other tab) and click **Click here to re-send the email**. If emails still do not arrive after a few minutes, contact your administrator.
|
|
||||||
- **The confirmation link says it expired** ? The link is valid for 5 minutes only. Click **Click here to re-send the email** on the verification page, then open the new email link promptly.
|
|
||||||
- **The email link opened in a different browser than the one I started in** ? Go back to the original incognito tab where you started the login and click **If you already verified the email in different browser, Click here to continue**.
|
|
||||||
- **I did not see the "Account already exists" screen ? I just ended up signed in to a new, empty account** ? The email address in your home tenant does not match the one used for the guest invitation. Contact your administrator; the email addresses must be aligned before linking can work.
|
|
||||||
- **I see the login button for my home tenant but the sign-in fails at Microsoft** ? This is unrelated to linking. Verify that your administrator has authorized you (or your group) to access the Digitalboard application in your home tenant.
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
# Add Microsoft Entra ID as an Identity Provider in Keycloak
|
|
||||||
|
|
||||||
> **Goal:** Connect your Microsoft Entra application (from the previous guide) to Keycloak using OpenID Connect so users can sign in with their Microsoft accounts.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
From the [Entra guide](../ms-entra/enterprise-app-keycloak.md) you should have:
|
|
||||||
- **OpenID Connect metadata document URL**
|
|
||||||
- **Application (client) ID**
|
|
||||||
- **Client secret (Value)**
|
|
||||||
- (Optional) **Tenant ID** — useful to verify you used the correct discovery URL
|
|
||||||
|
|
||||||
You'll also need:
|
|
||||||
- Access to the **Keycloak Admin Console**
|
|
||||||
- The **realm** where you want to add the provider (e.g., `Digitalboard`)
|
|
||||||
- The **alias** you decided on (this must match the alias in the Entra Redirect URI)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 1 — Open Identity Providers in your realm
|
|
||||||
1. In Keycloak, switch to the target realm (e.g., `Digitalboard`).
|
|
||||||
2. Navigate to **Configure → Identity providers**.
|
|
||||||
3. Click **Add provider** → **OpenID Connect v1.0**.
|
|
||||||
|
|
||||||

|
|
||||||
*Figure 1: Adding an OIDC provider.*
|
|
||||||

|
|
||||||
*Figure 1: Adding an OIDC provider.*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 2 — Fill the basic settings
|
|
||||||
On the **Add identity provider** form:
|
|
||||||
|
|
||||||
1. **Alias**: enter your organization alias (must match the alias used in the Entra Redirect URI).
|
|
||||||
Example: `test-schule`
|
|
||||||
2. **Display name**: a friendly label users will see on the login page.
|
|
||||||
Example: `Test Schule`
|
|
||||||
3. **Use discovery endpoint**: **On**.
|
|
||||||
4. **Discovery endpoint**: paste the **OpenID Connect metadata document URL** you copied in Entra (Step 7).
|
|
||||||
Example: `https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration`
|
|
||||||
5. **Client authentication**: keep **Client secret sent as post**.
|
|
||||||
6. **Client ID**: paste the **Application (client) ID** from Entra.
|
|
||||||
7. **Client Secret**: paste the **client secret Value** from Entra.
|
|
||||||
8. **Client assertion signature algorithm**: leave **Algorithm not specified** (default).
|
|
||||||
9. Click **Add** (or **Save**).
|
|
||||||
|
|
||||||
> As soon as you set the **Alias**, Keycloak shows the **Redirect URI** at the top (read-only). It must exactly match the Redirect URI you registered in Entra.
|
|
||||||
|
|
||||||

|
|
||||||
*Figure 2: Basic OIDC settings in Keycloak.*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 3 — Set recommended options
|
|
||||||
After saving, on the provider's **Settings** tab, adjust:
|
|
||||||
|
|
||||||
- **Default Scopes**: `openid profile email`
|
|
||||||
(ensures Entra returns the claims you added in the [Entra guide](../ms-entra/enterprise-app-keycloak.md))
|
|
||||||
- **Trust email**: **On** (lets Keycloak trust verified email from Entra)
|
|
||||||
- **Sync Mode**: **Import** (default; copies basic attributes into Keycloak)
|
|
||||||
- **Disable User Info**: **Off** (keep it off so Keycloak can fetch claims from the UserInfo endpoint)
|
|
||||||
- **Backchannel logout**: **On** (optional but recommended)
|
|
||||||
|
|
||||||
Click **Save**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 3 — Check the provider appears on the login page
|
|
||||||
Back on **Configure → Identity providers**, you should see your new provider listed.
|
|
||||||
|
|
||||||
Open your realm's login page (or log out of the Admin Console and choose **Sign in with <Provider-Display-Name>**). You should be redirected to Microsoft, then back to Keycloak, and end up authenticated.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
- **`invalid_redirect_uri` (on Microsoft)**: The Redirect URI in Entra must match exactly what Keycloak shows (including realm name and **alias**).
|
|
||||||
- **`AADSTS50105`**: Access to the Enterprise App is restricted. Follow Steps 10-11 in the [Entra guide](../ms-entra/enterprise-app-keycloak.md) to assign the user/group.
|
|
||||||
- **No name/email in Keycloak user**: Check **Default Scopes** include `profile email`, verify Entra **Token configuration** (claims) and Keycloak **Mappers**.
|
|
||||||
- **Issuer/metadata errors**: Ensure the **Discovery endpoint** uses your real **tenant ID** and is reachable over HTTPS from Keycloak.
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 107 KiB |
|
|
@ -1,26 +0,0 @@
|
||||||
# Nextcloud File Locking
|
|
||||||
|
|
||||||
## Problem
|
|
||||||
|
|
||||||
When two users open the same file simultaneously via the desktop sync client, both can write to it, resulting in sync conflicts.
|
|
||||||
|
|
||||||
## Solution
|
|
||||||
|
|
||||||
Two plugins work together to prevent this:
|
|
||||||
|
|
||||||
- [**files_lock**](https://apps.nextcloud.com/apps/files_lock) — implements WebDAV locking (RFC 4918). When a user opens a file via the desktop client, a lock is acquired server-side. Other clients see the file as locked and cannot write to it.
|
|
||||||
- [**notify_push**](https://apps.nextcloud.com/apps/notify_push) — pushes lock state changes to clients in real time, so they don't have to wait for the next poll cycle to discover a lock.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker exec -u www-data <nextcloud-container> php occ app:install files_lock
|
|
||||||
docker exec -u www-data <nextcloud-container> php occ app:install notify_push
|
|
||||||
```
|
|
||||||
|
|
||||||
Follow the `notify_push` setup guide to expose the push daemon via Traefik.
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- Conflicts are theoretically still possible, but the chances are minimized.
|
|
||||||
- Locks are per-session; if a client crashes without releasing the lock, the file may appear locked until the lock expires (default: 30 minutes, can be configured).
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue