fix(service): explain core security failures and link repair guidance

This commit is contained in:
wonfen
2026-09-24 18:43:27 +08:00
parent fbc4b516b8
commit ae4f416766
21 changed files with 105 additions and 14 deletions
+1 -1
View File
@@ -16,7 +16,7 @@
**🖥️ Windows**
- 修复 Windows 旧版服务状态残留导致无法重装的问题,自动备份可识别的残留并恢复安装
- 修复 Windows 服务安装后权限检查不通过时,未说明原因并自动切换为普通模式的问题
- 优化 Windows 服务安全检查失败提示:用易懂的文字说明原因,并提供修复文档链接
- 修复 Windows 服务因路径或目录权限问题无法启动时,缺少重新安装服务的提示
- 修复 Windows 服务回退检查失败后状态未更新、无法重新尝试 Sidecar 的问题
+42
View File
@@ -3,6 +3,7 @@ import {
Snackbar,
Alert,
IconButton,
Link,
Box,
Stack,
type SnackbarOrigin,
@@ -18,6 +19,12 @@ import {
showNotice,
} from '@/services/notice-service'
import type { TranslationKey } from '@/types/generated/i18n-keys'
import { openExternalUrl } from '@/utils/open-external-url'
const SERVICE_PERMISSION_NOTICE =
'settings.feedback.notifications.clashService.permissionFallback'
const SERVICE_PERMISSION_GUIDE =
'https://clash-verge-rev.github.io/faq/windows.html#service-core-permissions'
type NoticePosition = NonNullable<IVergeConfig['notice_position']>
type NoticeItem = ReturnType<typeof getSnapshotNotices>[number]
@@ -59,6 +66,38 @@ const resolveNoticeMessage = (
const i18n = notice.i18n
if (!i18n) return bound(notice.message)
if (i18n.key === SERVICE_PERMISSION_NOTICE) {
const reason = i18n.params?.reason
return (
<>
{t(SERVICE_PERMISSION_NOTICE, {
reason: t(
typeof reason === 'string' && reason.includes('is writable by')
? 'settings.feedback.notifications.clashService.permissionWritableReason'
: 'settings.feedback.notifications.clashService.permissionRejectedReason',
),
})}
<Box sx={{ mt: 1 }}>
<Link
href={SERVICE_PERMISSION_GUIDE}
color="inherit"
underline="always"
onClick={(event) => {
event.preventDefault()
void openExternalUrl(SERVICE_PERMISSION_GUIDE).catch(
showNotice.error,
)
}}
>
{t(
'settings.feedback.notifications.clashService.permissionRepairGuide',
)}
</Link>
</Box>
</>
)
}
const source = (i18n.params ?? {}) as Record<string, unknown>
// Bound both parameters and their final interpolation.
const params =
@@ -132,6 +171,9 @@ const resolveNoticeCopyText = (
notice: NoticeItem,
t: TranslationFn,
): string | undefined => {
if (notice.i18n?.key === SERVICE_PERMISSION_NOTICE) {
return extractNoticeCopyText(notice.i18n.params?.reason)
}
if (
notice.i18n?.key === 'shared.feedback.notices.prefixedRaw' ||
notice.i18n?.key === 'shared.feedback.notices.raw'
@@ -78,6 +78,7 @@ it('reports the permission refusal and finishes after installation falls back to
expect(showNotice.warning).toHaveBeenCalledWith(
'settings.feedback.notifications.clashService.permissionFallback',
{ reason },
0,
)
})
@@ -116,6 +116,7 @@ export const ServiceMigrationDialog = () => {
showNotice.warning(
'settings.feedback.notifications.clashService.permissionFallback',
{ reason: outcome.reason },
0,
)
return
}
@@ -112,6 +112,7 @@ export const SysproxyPrivilegeDialog = () => {
showNotice.warning(
'settings.feedback.notifications.clashService.permissionFallback',
{ reason: outcome.reason },
0,
)
close()
return
+1
View File
@@ -31,6 +31,7 @@ export const useServiceInstaller = () => {
showNotice.warning(
'settings.feedback.notifications.clashService.permissionFallback',
{ reason: outcome.reason },
0,
)
return
}
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "تم تثبيت الخدمة بنجاح",
"permissionFallback": "لم تجتز ملفات نواة الخدمة أو مجلداتها فحص الأمان. تم التبديل إلى وضع Sidecar؛ ويتطلب TUN صلاحيات المسؤول. السبب: {{reason}}",
"permissionFallback": "{{reason}} تم التبديل إلى وضع Sidecar؛ يتطلب TUN صلاحيات المسؤول. اتبع الدليل للإصلاح وإعادة تثبيت الخدمة، أو شغّل التطبيق كمسؤول دون تثبيت الخدمة.",
"permissionWritableReason": "يمكن للحسابات العادية تعديل ملفات النواة أو أحد المجلدات الأصلية، لذا رفضت الخدمة تشغيل النواة بصلاحيات مرتفعة.",
"permissionRejectedReason": "تعذّر على الخدمة التأكد من أن ملفات النواة ومجلداتها آمنة للتشغيل بصلاحيات مرتفعة.",
"permissionRepairGuide": "عرض خطوات الإصلاح",
"sidecarFallback": "تعذّر على الخدمة تشغيل النواة. تم التبديل إلى وضع Sidecar؛ ولا تزال الخدمة غير متاحة.",
"uninstallSuccess": "تم إلغاء تثبيت الخدمة بنجاح"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "Service erfolgreich installiert",
"permissionFallback": "Die Kerndateien oder Verzeichnisse des Dienstes haben die Sicherheitsprüfung nicht bestanden. Die Anwendung läuft im Sidecar-Modus weiter; TUN benötigt Administratorrechte. Grund: {{reason}}",
"permissionFallback": "{{reason}} Zum Sidecar-Modus gewechselt; TUN benötigt Administratorrechte. Reparieren Sie den Dienst gemäß Anleitung und installieren Sie ihn neu, oder starten Sie die App als Administrator ohne Dienstinstallation.",
"permissionWritableReason": "Normale Konten können Kerndateien oder übergeordnete Ordner ändern. Daher verweigert der Dienst die Ausführung mit erhöhten Rechten.",
"permissionRejectedReason": "Der Dienst konnte nicht bestätigen, dass die Kerndateien und Ordner für die Ausführung mit erhöhten Rechten sicher sind.",
"permissionRepairGuide": "Reparaturanleitung öffnen",
"sidecarFallback": "Der Dienst konnte den Kern nicht starten. Die Anwendung läuft im Sidecar-Modus weiter; der Dienst bleibt nicht verfügbar.",
"uninstallSuccess": "Service erfolgreich deinstalliert"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "Service Installed Successfully",
"permissionFallback": "The service's core files or directories failed a security check. Switched to Sidecar mode; TUN requires administrator privileges. Reason: {{reason}}",
"permissionFallback": "{{reason}} Switched to normal mode (Sidecar); TUN requires administrator privileges. Follow the guide to fix and reinstall the service. If you prefer not to fix it or do not know how, run the app as administrator without installing the service.",
"permissionWritableReason": "Ordinary accounts can modify the core files or a parent folder, so the service refused to run the core with elevated privileges.",
"permissionRejectedReason": "The service could not confirm that the core files and folders are safe to run with elevated privileges.",
"permissionRepairGuide": "View repair instructions",
"sidecarFallback": "The service could not start the core. Switched to Sidecar mode; the service remains unavailable.",
"uninstallSuccess": "Service Uninstalled Successfully"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "Servicio instalado con éxito",
"permissionFallback": "Los archivos o directorios del núcleo del servicio no superaron la comprobación de seguridad. Se ha cambiado al modo Sidecar; TUN requiere permisos de administrador. Motivo: {{reason}}",
"permissionFallback": "{{reason}} Se ha cambiado al modo Sidecar; TUN requiere permisos de administrador. Siga la guía para reparar y reinstalar el servicio, o ejecute la aplicación como administrador sin instalar el servicio.",
"permissionWritableReason": "Las cuentas normales pueden modificar los archivos del núcleo o una carpeta superior, por lo que el servicio rechazó ejecutarlo con privilegios elevados.",
"permissionRejectedReason": "El servicio no pudo confirmar que los archivos y carpetas del núcleo sean seguros para ejecutarlos con privilegios elevados.",
"permissionRepairGuide": "Ver instrucciones de reparación",
"sidecarFallback": "El servicio no pudo iniciar el núcleo. Se ha cambiado al modo Sidecar; el servicio sigue sin estar disponible.",
"uninstallSuccess": "Servicio desinstalado con éxito"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "سرویس با موفقیت نصب شد",
"permissionFallback": "فایل‌ها یا پوشه‌های هستهٔ سرویس از بررسی امنیتی عبور نکردند. به حالت Sidecar تغییر یافت؛ TUN به دسترسی مدیر نیاز دارد. دلیل: {{reason}}",
"permissionFallback": "{{reason}} به حالت Sidecar تغییر یافت؛ TUN به دسترسی مدیر نیاز دارد. طبق راهنما مشکل را برطرف و سرویس را دوباره نصب کنید، یا برنامه را بدون نصب سرویس با دسترسی مدیر اجرا کنید.",
"permissionWritableReason": "حساب‌های عادی می‌توانند فایل‌های هسته یا پوشهٔ والد را تغییر دهند، بنابراین سرویس از اجرای هسته با دسترسی بالا خودداری کرد.",
"permissionRejectedReason": "سرویس نتوانست ایمن بودن فایل‌ها و پوشه‌های هسته را برای اجرا با دسترسی بالا تأیید کند.",
"permissionRepairGuide": "مشاهدهٔ راهنمای رفع مشکل",
"sidecarFallback": "سرویس نتوانست هسته را اجرا کند. اجرا در حالت Sidecar ادامه می‌یابد؛ سرویس همچنان در دسترس نیست.",
"uninstallSuccess": "سرویس با موفقیت حذف نصب شد"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "Layanan Berhasil Diinstal",
"permissionFallback": "Berkas atau direktori inti layanan tidak lolos pemeriksaan keamanan. Beralih ke mode Sidecar; TUN memerlukan hak administrator. Alasan: {{reason}}",
"permissionFallback": "{{reason}} Beralih ke mode Sidecar; TUN memerlukan hak administrator. Ikuti panduan untuk memperbaiki dan menginstal ulang layanan, atau jalankan aplikasi sebagai administrator tanpa menginstal layanan.",
"permissionWritableReason": "Akun biasa dapat mengubah berkas inti atau folder induknya, sehingga layanan menolak menjalankan inti dengan hak istimewa.",
"permissionRejectedReason": "Layanan tidak dapat memastikan bahwa berkas dan folder inti aman dijalankan dengan hak istimewa.",
"permissionRepairGuide": "Lihat panduan perbaikan",
"sidecarFallback": "Layanan tidak dapat memulai inti. Beralih ke mode Sidecar; layanan masih tidak tersedia.",
"uninstallSuccess": "Layanan Berhasil Dicopot"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "サービスのインストールに成功しました。",
"permissionFallback": "サービスのコアファイルまたはディレクトリがセキュリティチェックに失敗したため、Sidecar モードに切り替えました。TUN には管理者権限が必要です。理由:{{reason}}",
"permissionFallback": "{{reason}} 通常モード(Sidecar)に切り替えました。TUN には管理者権限が必要です。手順に従って修復しサービスを再インストールするか、サービスをインストールせずにアプリを管理者として実行してください。",
"permissionWritableReason": "一般アカウントがコアファイルまたは上位フォルダーを変更できるため、サービスは高い権限での実行を拒否しました。",
"permissionRejectedReason": "コアファイルとフォルダーを高い権限で安全に実行できるか確認できませんでした。",
"permissionRepairGuide": "修復方法を見る",
"sidecarFallback": "サービスでコアを起動できなかったため、Sidecar モードで動作を継続しています。サービスは引き続き利用できません。",
"uninstallSuccess": "サービスのアンインストールに成功しました。"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "서비스가 설치되었습니다",
"permissionFallback": "서비스의 코어 파일 또는 디렉터리가 보안 검사를 통과하지 못해 Sidecar 모드로 전환했습니다. TUN에는 관리자 권한이 필요합니다. 사유: {{reason}}",
"permissionFallback": "{{reason}} 일반 모드(Sidecar)로 전환했습니다. TUN에는 관리자 권한이 필요합니다. 안내에 따라 수정 후 서비스를 다시 설치하거나, 서비스 설치 없이 앱을 관리자 권한으로 실행하세요.",
"permissionWritableReason": "일반 계정이 코어 파일이나 상위 폴더를 수정할 수 있어 서비스가 높은 권한으로 실행하는 것을 거부했습니다.",
"permissionRejectedReason": "서비스가 코어 파일과 폴더를 높은 권한으로 안전하게 실행할 수 있는지 확인하지 못했습니다.",
"permissionRepairGuide": "해결 방법 보기",
"sidecarFallback": "서비스가 코어를 시작하지 못해 Sidecar 모드로 계속 실행합니다. 서비스는 여전히 사용할 수 없습니다.",
"uninstallSuccess": "서비스가 제거되었습니다"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "Служба успешно установлена",
"permissionFallback": "Файлы или каталоги ядра службы не прошли проверку безопасности. Работа продолжена в режиме Sidecar; для TUN нужны права администратора. Причина: {{reason}}",
"permissionFallback": "{{reason}} Выполнен переход в режим Sidecar; TUN требует прав администратора. Исправьте проблему по инструкции и переустановите службу либо запустите приложение от имени администратора без установки службы.",
"permissionWritableReason": "Обычные учётные записи могут изменять файлы ядра или родительскую папку, поэтому служба отказалась запускать ядро с повышенными правами.",
"permissionRejectedReason": "Служба не смогла подтвердить безопасность файлов и папок ядра для запуска с повышенными правами.",
"permissionRepairGuide": "Инструкция по исправлению",
"sidecarFallback": "Служба не смогла запустить ядро. Работа продолжена в режиме Sidecar; служба по-прежнему недоступна.",
"uninstallSuccess": "Служба успешно удалена"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "Hizmet Başarıyla Kuruldu",
"permissionFallback": "Hizmetin çekirdek dosyaları veya dizinleri güvenlik denetimini geçemedi. Sidecar moduna geçildi; TUN için yönetici yetkileri gerekir. Neden: {{reason}}",
"permissionFallback": "{{reason}} Sidecar moduna geçildi; TUN yönetici yetkisi gerektirir. Kılavuza göre sorunu düzeltip hizmeti yeniden yükleyin veya hizmeti yüklemeden uygulamayı yönetici olarak çalıştırın.",
"permissionWritableReason": "Normal hesaplar çekirdek dosyalarını veya üst klasörü değiştirebildiği için hizmet, çekirdeği yükseltilmiş yetkilerle çalıştırmayı reddetti.",
"permissionRejectedReason": "Hizmet, çekirdek dosya ve klasörlerinin yükseltilmiş yetkilerle güvenle çalıştırılabileceğini doğrulayamadı.",
"permissionRepairGuide": "Düzeltme adımlarını görüntüle",
"sidecarFallback": "Hizmet çekirdeği başlatamadı. Sidecar moduna geçildi; hizmet hâlâ kullanılamıyor.",
"uninstallSuccess": "Hizmet Başarıyla Kaldırıldı"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "Сервис уңышлы урнаштырылды",
"permissionFallback": "Хезмәт төшенең файллары яки каталоглары иминлек тикшерүен үтмәде. Sidecar режимына күчелде; TUN өчен администратор хокуклары кирәк. Сәбәп: {{reason}}",
"permissionFallback": "{{reason}} Sidecar режимына күчелде; TUN өчен администратор хокуклары кирәк. Күрсәтмәләр буенча төзәтеп, хезмәтне яңадан урнаштырыгыз яки хезмәтне урнаштырмыйча кушымтаны администратор булып эшләтегез.",
"permissionWritableReason": "Гади хисап язмалары төш файлларын яки өске папканы үзгәртә ала, шуңа күрә хезмәт төшне югары хокуклар белән эшләтүдән баш тартты.",
"permissionRejectedReason": "Хезмәт төш файлларын һәм папкаларын югары хокуклар белән имин эшләтеп булачагын раслый алмады.",
"permissionRepairGuide": "Төзәтү күрсәтмәләрен карау",
"sidecarFallback": "Хезмәт төшне эшләтеп җибәрә алмады. Эш Sidecar режимында дәвам итә; хезмәт һаман да эшләми.",
"uninstallSuccess": "Сервис уңышлы салдырылды"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "已成功安装服务",
"permissionFallback": "服务内核的文件或目录未通过安全检查,已切换到普通模式(Sidecar);普通权限下 TUN 模式不可用。原因:{{reason}}",
"permissionFallback": "{{reason}} 已切换到普通模式(Sidecar),普通权限下无法使用 TUN。可按文档修复后重装服务;不想或不会修复时,可直接以管理员身份运行软件,无需安装服务。",
"permissionWritableReason": "内核文件或上级文件夹允许普通账户修改,服务为防止内核被替换而拒绝高权限运行。",
"permissionRejectedReason": "服务无法确认内核文件及所在目录是否安全,已拒绝高权限运行。",
"permissionRepairGuide": "查看修复方法",
"sidecarFallback": "服务无法启动内核,已降级为 Sidecar 模式运行,服务仍不可用。",
"uninstallSuccess": "已成功卸载服务"
},
+4 -1
View File
@@ -684,7 +684,10 @@
},
"clashService": {
"installSuccess": "已成功安裝服務",
"permissionFallback": "服務核心的檔案或目錄未通過安全檢查,已切換至一般模式(Sidecar);一般權限下無法使用 TUN 模式。原因:{{reason}}",
"permissionFallback": "{{reason}} 已切換至一般模式(Sidecar),一般權限下無法使用 TUN。可依文件修復後重新安裝服務;不想或不會修復時,可直接以系統管理員身分執行軟體,無需安裝服務。",
"permissionWritableReason": "核心檔案或上層資料夾允許一般帳戶修改,服務為防止核心被替換而拒絕以高權限執行。",
"permissionRejectedReason": "服務無法確認核心檔案及所在目錄是否安全,已拒絕以高權限執行。",
"permissionRepairGuide": "查看修復方法",
"sidecarFallback": "服務無法啟動核心,已降級為 Sidecar 模式執行,服務仍無法使用。",
"uninstallSuccess": "已成功解除安裝服務"
},
+3
View File
@@ -805,6 +805,9 @@ export const translationKeys = [
'settings.feedback.notifications.clash.geoDataUpdated',
'settings.feedback.notifications.clashService.installSuccess',
'settings.feedback.notifications.clashService.permissionFallback',
'settings.feedback.notifications.clashService.permissionWritableReason',
'settings.feedback.notifications.clashService.permissionRejectedReason',
'settings.feedback.notifications.clashService.permissionRepairGuide',
'settings.feedback.notifications.clashService.sidecarFallback',
'settings.feedback.notifications.clashService.uninstallSuccess',
'settings.feedback.notifications.updater.withClashProxySuccess',
+3
View File
@@ -846,6 +846,9 @@ export interface TranslationResources {
clashService: {
installSuccess: string
permissionFallback: string
permissionRejectedReason: string
permissionRepairGuide: string
permissionWritableReason: string
sidecarFallback: string
uninstallSuccess: string
}