From c73c1d850751bb1511bb10f32fbc70624a8f6e8c Mon Sep 17 00:00:00 2001 From: pooneyy <85266337+pooneyy@users.noreply.github.com> Date: Sun, 1 Feb 2026 13:47:30 +0800 Subject: [PATCH] feat(keycloak): add keycloak application with version 26.5.2 --- apps/keycloak/26.5.2/data.yml | 172 ++++++++++++++++++++++++ apps/keycloak/26.5.2/docker-compose.yml | 26 ++++ apps/keycloak/README.md | 27 ++++ apps/keycloak/README_en.md | 26 ++++ apps/keycloak/data.yml | 32 +++++ apps/keycloak/logo.png | Bin 0 -> 4431 bytes 6 files changed, 283 insertions(+) create mode 100644 apps/keycloak/26.5.2/data.yml create mode 100644 apps/keycloak/26.5.2/docker-compose.yml create mode 100644 apps/keycloak/README.md create mode 100644 apps/keycloak/README_en.md create mode 100644 apps/keycloak/data.yml create mode 100644 apps/keycloak/logo.png diff --git a/apps/keycloak/26.5.2/data.yml b/apps/keycloak/26.5.2/data.yml new file mode 100644 index 000000000..8526ae65f --- /dev/null +++ b/apps/keycloak/26.5.2/data.yml @@ -0,0 +1,172 @@ +additionalProperties: + formFields: + - default: 8080 + envKey: PANEL_APP_PORT_HTTP + labelZh: HTTP 端口 + labelEn: HTTP Port + label: + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + en: HTTP Port + ja: HTTP ポート + ko: HTTP 포트 + ms: Port HTTP + pt-br: Porta HTTP + ru: HTTP Порт + tr: HTTP Portu + description: + zh: "设置应用的 HTTP 访问端口,有效范围: 1-65535" + zh-Hant: "設定應用程式的 HTTP 存取連接埠,有效範圍: 1-65535" + en: "Set the HTTP access port for the application, valid range: 1-65535" + ja: "アプリケーションのHTTPアクセスポートを設定します。有効範囲: 1-65535" + ko: "애플리케이션의 HTTP 접근 포트를 설정합니다. 유효 범위: 1-65535" + ms: "Tetapkan port akses HTTP untuk aplikasi, julat sah: 1-65535" + pt-br: "Defina a porta de acesso HTTP para o aplicativo, intervalo válido: 1-65535" + ru: "Установите порт доступа HTTP для приложения, допустимый диапазон: 1-65535" + tr: "Uygulama için HTTP erişim portunu ayarlayın, geçerli aralık: 1-65535" + required: true + type: number + edit: true + rule: paramPort + - default: "" + envKey: KC_HOSTNAME + labelZh: 计划为 Keycloak 分配的域名 + labelEn: Domain name planned for Keycloak + label: + zh: 计划为 Keycloak 分配的域名 + zh-Hant: 計劃為 Keycloak 分配的網域名稱 + en: Domain name planned for Keycloak + ja: Keycloak に計画されているドメイン名 + ko: Keycloak에 계획된 도메인 이름 + ms: Nama domain yang dirancang untuk Keycloak + pt-br: Nome de domínio planejado para o Keycloak + ru: Запланированное доменное имя для Keycloak + tr: Keycloak için planlanan alan adı + description: + zh: "keycloak 的生产环境不支持通过 http 访问。\n本应用安装向导仅支持通过反向代理配置SSL来访问 keycloak,因此你必须填写你计划为 keycloak 分配的域名。" + zh-Hant: "keycloak 的生產環境不支援透過 http 存取。\n本應用安裝精靈僅支援透過反向代理設定SSL來存取 keycloak,因此你必須填寫你計劃為 keycloak 分配的網域名稱。" + en: "The production environment of keycloak does not support access via HTTP.\nThis application installation wizard only supports accessing keycloak by configuring SSL through a reverse proxy. Therefore, you must fill in the domain name you plan to assign to keycloak." + ja: "keycloakの本番環境は、httpを介したアクセスをサポートしていません。\nこのアプリケーションインストールウィザードは、リバースプロキシを介したSSL設定によるkeycloakへのアクセスのみをサポートしています。そのため、keycloakに割り当てる予定のドメイン名を入力する必要があります。" + ko: "keycloak의 프로덕션 환경은 http를 통한 접근을 지원하지 않습니다.\n이 애플리케이션 설치 마법사는 리버스 프록시를 통한 SSL 구성으로 keycloak에 접근하는 것만 지원합니다. 따라서 keycloak에 할당할 도메인 이름을 입력해야 합니다." + ms: "Persekitaran pengeluaran keycloak tidak menyokong akses melalui HTTP.\nPembina pemasangan aplikasi ini hanya menyokong akses ke keycloak dengan mengkonfigurasi SSL melalui proksi terbalik. Oleh itu, anda mesti mengisi nama domain yang anda rancang untuk tugaskan kepada keycloak." + pt-br: "O ambiente de produção do keycloak não suporta acesso via HTTP.\nEste assistente de instalação do aplicativo suporta apenas o acesso ao keycloak configurando SSL por meio de um proxy reverso. Portanto, você deve preencher o nome de domínio que planeja atribuir ao keycloak." + ru: "Рабочая среда keycloak не поддерживает доступ по HTTP.\nМастер установки этого приложения поддерживает доступ к keycloak только путем настройки SSL через обратный прокси. Следовательно, вы должны указать доменное имя, которое планируете назначить для keycloak." + tr: "keycloak'ın üretim ortamı HTTP üzerinden erişimi desteklemez.\nBu uygulama kurulum sihirbazı, yalnızca bir ters proxy aracılığıyla SSL yapılandırarak keycloak'a erişimi destekler. Bu nedenle, keycloak'a atamayı planladığınız alan adını doldurmalısınız." + required: true + type: text + edit: true + - default: mariadb + envKey: PANEL_DB_TYPE + labelZh: 数据库 + labelEn: Database + label: + zh: 数据库 + zh-Hant: 資料庫 + en: Database + ja: データベース + ko: 데이터베이스 + ms: Pangkalan Data + pt-br: Banco de Dados + ru: База данных + tr: Veritabanı + required: true + type: apps + edit: true + values: + - label: MariaDB + value: mariadb + - label: MySQL + value: mysql + - label: PostgreSQL + value: postgres + child: + default: "" + envKey: PANEL_DB_HOST + required: true + type: service + - default: keycloak_user + envKey: PANEL_DB_USER + labelZh: 数据库用户名 + labelEn: Database Username + label: + zh: 数据库用户名 + zh-Hant: 資料庫使用者名稱 + en: Database Username + ja: データベースユーザー名 + ko: 데이터베이스 사용자 이름 + ms: Nama Pengguna Pangkalan Data + pt-br: Nome de Usuário do Banco de Dados + ru: Имя пользователя базы данных + tr: Veritabanı Kullanıcı Adı + required: true + type: text + edit: true + rule: paramCommon + random: true + - default: keycloak_pass + envKey: PANEL_DB_USER_PASSWORD + labelZh: 数据库密码 + labelEn: Database Password + label: + zh: 数据库密码 + zh-Hant: 資料庫密碼 + en: Database Password + ja: データベースパスワード + ko: 데이터베이스 비밀번호 + ms: Kata Laluan Pangkalan Data + pt-br: Senha do Banco de Dados + ru: Пароль базы данных + tr: Veritabanı Parolası + required: true + type: password + edit: true + rule: paramComplexity + random: true + - default: keycloak + envKey: PANEL_DB_NAME + labelZh: 数据库名 + labelEn: Database Name + label: + zh: 数据库名 + zh-Hant: 資料庫名稱 + en: Database Name + ja: データベース名 + ko: 데이터베이스 이름 + ms: Nama Pangkalan Data + pt-br: Nome do Banco de Dados + ru: Имя базы данных + tr: Veritabanı Adı + required: true + type: text + edit: true + rule: paramCommon + random: true + - default: Keycloak_admin_default + envKey: ADMIN_PASSWORD + labelZh: 默认管理员账户密码 + labelEn: Default Administrator Account Password + label: + zh: 默认管理员账户密码 + zh-Hant: 預設管理員帳戶密碼 + en: Default Administrator Account Password + ja: デフォルト管理者アカウントパスワード + ko: 기본 관리자 계정 비밀번호 + ms: Kata Laluan Akaun Pentadbir Lalai + pt-br: Senha Padrão da Conta de Administrador + ru: Пароль учётной записи администратора по умолчанию + tr: Varsayılan Yönetici Hesap Parolası + description: + zh: 此用户为临时管理员用户。为加强安全性,请创建一个永久管理员账户并删除此临时账户。 + zh-Hant: 此使用者為臨時管理員使用者。為加強安全性,請建立一個永久管理員帳戶並刪除此臨時帳戶。 + en: This user is a temporary administrator user. To harden security, create a permanent admin account and delete the temporary one. + ja: このユーザーは一時的な管理者ユーザーです。セキュリティを強化するには、恒久的管理者アカウントを作成し、一時的なアカウントを削除してください。 + ko: 이 사용자는 임시 관리자 사용자입니다. 보안을 강화하려면 영구 관리자 계정을 생성하고 임시 계정을 삭제하십시오. + ms: Pengguna ini adalah pengguna pentadbir sementara. Untuk mengukuhkan keselamatan, buat akaun admin kekal dan padamkan akaun sementara tersebut. + pt-br: Este usuário é um usuário administrador temporário. Para reforçar a segurança, crie uma conta administrativa permanente e exclua a conta temporária. + ru: Этот пользователь является временным администратором. Для усиления безопасности создайте постоянную учётную запись администратора и удалите временную. + tr: Bu kullanıcı geçici bir yönetici kullanıcıdır. Güvenliği artırmak için kalıcı bir yönetici hesabı oluşturun ve geçici olanı silin. + required: false + type: text + disabled: true + rule: paramComplexity + random: true diff --git a/apps/keycloak/26.5.2/docker-compose.yml b/apps/keycloak/26.5.2/docker-compose.yml new file mode 100644 index 000000000..9b8e5fb11 --- /dev/null +++ b/apps/keycloak/26.5.2/docker-compose.yml @@ -0,0 +1,26 @@ +services: + keycloak: + image: keycloak/keycloak:26.5.2 + environment: + - KC_BOOTSTRAP_ADMIN_PASSWORD=${ADMIN_PASSWORD} + - KC_BOOTSTRAP_ADMIN_USERNAME=admin + - KC_DB=${PANEL_DB_TYPE} + - KC_DB_USERNAME=${PANEL_DB_USER} + - KC_DB_PASSWORD=${PANEL_DB_USER_PASSWORD} + - KC_DB_URL_HOST=${PANEL_DB_HOST} + - KC_DB_URL_PORT=${PANEL_DB_PORT} + - KC_DB_URL_DATABASE=${PANEL_DB_NAME} + - KC_HOSTNAME=${KC_HOSTNAME} + - KC_HTTP_ENABLED=true + - KC_PROXY_HEADERS=xforwarded + ports: + - ${PANEL_APP_PORT_HTTP}:8080 + command: start + networks: + - 1panel-network + labels: + createdBy: Apps + container_name: ${CONTAINER_NAME} +networks: + 1panel-network: + external: true diff --git a/apps/keycloak/README.md b/apps/keycloak/README.md new file mode 100644 index 000000000..89c2ebdd3 --- /dev/null +++ b/apps/keycloak/README.md @@ -0,0 +1,27 @@ +## 默认账户密码 + +用户名: `admin` + +密码: 前往应用商店,在应用的参数配置中获取 + +## 配置和使用说明 + +- admin 是临时管理员账户,首次登录后请新建一个账户,**并为其分配管理员角色**,再将 admin 账户删除。 + 1. 左侧栏找到 Users, Add Users 创建一个账户 + 2. 选择 Credentials 页签为账户设置密码 + 3. 选择 Role mapping 页签,点击 Assign role,选择 Realm roles + 4. 勾选 admin (role_admin) 后 Assign + 5. 删除 admin 账户 +- Keycloak 支持 i18n,但需要手动启用 + - 左侧栏找到 Realm settings, 选择 Localization 页签,启用 Internationalization 后选择你的支持区域 + +## 产品介绍 + +Keycloak 是一个开源的 **身份和访问管理 (IAM)** 平台,专为现代应用程序和服务设计。它的核心目标是简化应用程序的身份验证和授权流程,使开发者无需自行处理用户存储、认证逻辑等复杂的安全问题。 + +## 主要功能 + +- **标准化身份协议支持**: 原生支持 OpenID Connect、OAuth 2.0 和 SAML 等主流身份协议,使应用程序能够轻松集成单点登录 (SSO) 和社会化登录 (如 Google、GitHub 等) 。 +- **用户联邦与集中管理**: 允许从外部用户存储 (如 LDAP、Active Directory、Kerberos) 同步用户,并提供统一的用户管理界面,简化用户生命周期管理 (创建、更新、删除、角色分配) 。 +- **细粒度授权与访问控制**: 提供基于角色 (RBAC) 和属性 (ABAC) 的精细化授权策略,支持动态权限管理和策略决策,确保应用程序和服务的安全访问。 +- **强身份验证机制**: 内置多因素认证 (MFA) 、条件访问、密码策略和身份验证流程定制,增强账户安全性,满足合规性要求。 \ No newline at end of file diff --git a/apps/keycloak/README_en.md b/apps/keycloak/README_en.md new file mode 100644 index 000000000..f658259c4 --- /dev/null +++ b/apps/keycloak/README_en.md @@ -0,0 +1,26 @@ +## Default Credentials + +username: `admin` + +password: Go to the App Store and get it from the app's parameter settings + +## Configuration and Usage Instructions + +- **admin** is a temporary administrator account. After the initial login, please create a new account, **assign it the administrator role**, and then delete the **admin** account. + 1. Find **Users** in the left sidebar, click **Add Users** to create an account. + 2. Click on the new account to enter **User details**, select the **Role mapping** tab, click **Assign role**, and choose **Realm roles**. + 3. Check the box for **admin (role_admin)** and click **Assign**. + 4. Delete the **admin** account. +- Keycloak supports i18n, but it needs to be enabled manually. + - Find **Realm settings** in the left sidebar, select the **Localization** tab, enable **Internationalization**, and then select your supported locale(s). + +## Introduction + +Keycloak is an open-source **Identity and Access Management (IAM)** platform designed for modern applications and services. Its core objective is to simplify the authentication and authorization processes for applications, freeing developers from the complexities of handling user storage, authentication logic, and other intricate security concerns. + +## Features + +- **Support for Standardized Identity Protocols**: Natively supports mainstream identity protocols such as OpenID Connect, OAuth 2.0, and SAML, enabling applications to easily integrate Single Sign-On (SSO) and social logins (e.g., Google, GitHub). +- **User Federation and Centralized Management**: Allows synchronization of users from external user stores (e.g., LDAP, Active Directory, Kerberos) and provides a unified user management interface, simplifying user lifecycle management (creation, update, deletion, role assignment). +- **Fine-Grained Authorization and Access Control**: Offers detailed authorization policies based on roles (RBAC) and attributes (ABAC), supports dynamic permission management and policy decision-making, ensuring secure access to applications and services. +- **Strong Authentication Mechanisms**: Built-in Multi-Factor Authentication (MFA), conditional access, password policies, and customizable authentication flows enhance account security and meet compliance requirements. \ No newline at end of file diff --git a/apps/keycloak/data.yml b/apps/keycloak/data.yml new file mode 100644 index 000000000..85a2d3d6b --- /dev/null +++ b/apps/keycloak/data.yml @@ -0,0 +1,32 @@ +name: Keycloak +tags: + - 实用工具 +title: 面向现代应用与服务的开源身份和访问管理器 +description: 面向现代应用与服务的开源身份和访问管理器 +additionalProperties: + key: keycloak + name: Keycloak + tags: + - Tool + shortDescZh: 面向现代应用与服务的开源身份和访问管理器 + shortDescEn: Open Source Identity and Access Management For Modern Applications and Services + description: + en: Open Source Identity and Access Management For Modern Applications and Services + zh: 面向现代应用与服务的开源身份和访问管理器 + zh-Hant: 面向現代應用與服務的開源身份和存取管理器 + ja: モダンなアプリケーションとサービスのためのオープンソースのアイデンティティおよびアクセス管理 + ms: Pengurusan Identiti dan Akses Sumber Terbuka Untuk Aplikasi dan Perkhidmatan Moden + pt-br: Gerenciamento de Identidade e Acesso de Código Aberto Para Aplicações e Serviços Modernos + ru: Управление идентификацией и доступом с открытым исходным кодом для современных приложений и сервисов + ko: 현대 애플리케이션 및 서비스를 위한 오픈 소스 신원 및 액세스 관리 + type: website + crossVersionUpdate: true + limit: 0 + website: https://www.keycloak.org + github: https://github.com/keycloak/keycloak + document: https://www.keycloak.org/getting-started/getting-started-docker + memoryRequired: 1536 + architectures: + - amd64 + - arm64 + - ppc64le diff --git a/apps/keycloak/logo.png b/apps/keycloak/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2a0cfe030cea5acb444a8bf5eb30ef84b27db5ee GIT binary patch literal 4431 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