grant core permition

This commit is contained in:
pompurin404 2024-08-04 11:31:07 +08:00
parent 454ce10a9d
commit 2e194e9d35
No known key found for this signature in database
8 changed files with 132 additions and 36 deletions

View File

@ -7,13 +7,15 @@ import {
mihomoWorkDir mihomoWorkDir
} from '../utils/dirs' } from '../utils/dirs'
import { generateProfile } from '../resolve/factory' import { generateProfile } from '../resolve/factory'
import { getAppConfig } from '../config' import { getAppConfig, setAppConfig } from '../config'
import { safeStorage } from 'electron'
import fs from 'fs' import fs from 'fs'
let child: ChildProcess let child: ChildProcess
export function startCore(): void { export function startCore(): void {
const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo') const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo')
grantCorePermition(corePath)
generateProfile() generateProfile()
checkProfile() checkProfile()
stopCore() stopCore()
@ -51,3 +53,26 @@ export function checkProfile(): void {
const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo') const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo')
execFileSync(corePath, ['-t', '-f', mihomoWorkConfigPath(), '-d', mihomoTestDir()]) execFileSync(corePath, ['-t', '-f', mihomoWorkConfigPath(), '-d', mihomoTestDir()])
} }
export function grantCorePermition(corePath: string): void {
if (getAppConfig().encryptedPassword && isEncryptionAvailable()) {
const password = safeStorage.decryptString(Buffer.from(getAppConfig().encryptedPassword ?? []))
try {
if (process.platform === 'linux') {
execSync(
`echo "${password}" | sudo -S setcap cap_net_bind_service,cap_net_admin,cap_dac_override,cap_net_raw=+ep ${corePath}`
)
}
if (process.platform === 'darwin') {
execSync(`echo "${password}" | sudo -S chown root:admin ${corePath}`)
execSync(`echo "${password}" | sudo -S chmod +sx ${corePath}`)
}
} catch (e) {
setAppConfig({ encryptedPassword: undefined })
}
}
}
export function isEncryptionAvailable(): boolean {
return safeStorage.isEncryptionAvailable()
}

View File

@ -23,8 +23,6 @@ if (!gotTheLock) {
app.quit() app.quit()
} else { } else {
init() init()
startCore()
app.on('second-instance', () => { app.on('second-instance', () => {
window?.show() window?.show()
window?.focusOnWebView() window?.focusOnWebView()
@ -51,7 +49,7 @@ if (!gotTheLock) {
app.whenReady().then(() => { app.whenReady().then(() => {
// Set app user model id for windows // Set app user model id for windows
electronApp.setAppUserModelId('party.mihomo.app') electronApp.setAppUserModelId('party.mihomo.app')
startCore()
// Default open or close DevTools by F12 in development // Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production. // and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils

View File

@ -10,7 +10,6 @@ export function generateProfile(): void {
const { tun: controledTun } = controledMihomoConfig const { tun: controledTun } = controledMihomoConfig
const tun = Object.assign(profileTun, controledTun) const tun = Object.assign(profileTun, controledTun)
const profile = Object.assign(currentProfile, controledMihomoConfig) const profile = Object.assign(currentProfile, controledMihomoConfig)
console.log('profile', profile)
profile.tun = tun profile.tun = tun
fs.writeFileSync(mihomoWorkConfigPath(), yaml.stringify(profile)) fs.writeFileSync(mihomoWorkConfigPath(), yaml.stringify(profile))
} }

View File

@ -1,4 +1,4 @@
import { app, ipcMain } from 'electron' import { app, ipcMain, safeStorage } from 'electron'
import { import {
mihomoChangeProxy, mihomoChangeProxy,
mihomoCloseAllConnections, mihomoCloseAllConnections,
@ -25,7 +25,7 @@ import {
addProfileItem, addProfileItem,
removeProfileItem removeProfileItem
} from '../config' } from '../config'
import { restartCore } from '../core/manager' import { isEncryptionAvailable, restartCore } from '../core/manager'
import { triggerSysProxy } from '../resolve/sysproxy' import { triggerSysProxy } from '../resolve/sysproxy'
import { changeCurrentProfile } from '../config/profile' import { changeCurrentProfile } from '../config/profile'
@ -57,5 +57,7 @@ export function registerIpcMainHandlers(): void {
ipcMain.handle('removeProfileItem', (_e, id) => removeProfileItem(id)) ipcMain.handle('removeProfileItem', (_e, id) => removeProfileItem(id))
ipcMain.handle('restartCore', restartCore) ipcMain.handle('restartCore', restartCore)
ipcMain.handle('triggerSysProxy', (_e, enable) => triggerSysProxy(enable)) ipcMain.handle('triggerSysProxy', (_e, enable) => triggerSysProxy(enable))
ipcMain.handle('isEncryptionAvailable', isEncryptionAvailable)
ipcMain.handle('encryptString', (_e, str) => safeStorage.encryptString(str))
ipcMain.handle('quitApp', () => app.quit()) ipcMain.handle('quitApp', () => app.quit())
} }

View File

@ -0,0 +1,41 @@
import {
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Button,
Input
} from '@nextui-org/react'
import React, { useState } from 'react'
interface Props {
onCancel: () => void
onConfirm: (script: string) => void
}
const BasePasswordModal: React.FC<Props> = (props) => {
const { onCancel, onConfirm } = props
const [password, setPassword] = useState('')
return (
<Modal hideCloseButton isOpen={true}>
<ModalContent>
<ModalHeader className="flex">root密码</ModalHeader>
<ModalBody>
<Input fullWidth type="password" value={password} onValueChange={setPassword} />
</ModalBody>
<ModalFooter>
<Button variant="light" onPress={onCancel}>
</Button>
<Button color="primary" onPress={() => onConfirm(password)}>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
)
}
export default BasePasswordModal

View File

@ -3,50 +3,73 @@ import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-c
import BorderSwitch from '@renderer/components/base/border-swtich' import BorderSwitch from '@renderer/components/base/border-swtich'
import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb' import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { patchMihomoConfig } from '@renderer/utils/ipc' import { encryptString, patchMihomoConfig, isEncryptionAvailable } from '@renderer/utils/ipc'
import React from 'react' import React, { useState } from 'react'
import { useAppConfig } from '@renderer/hooks/use-app-config'
import BasePasswordModal from '../base/base-password-modal'
const TunSwitcher: React.FC = () => { const TunSwitcher: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/tun') const match = location.pathname.includes('/tun')
const [openPasswordModal, setOpenPasswordModal] = useState(false)
const { appConfig, patchAppConfig } = useAppConfig()
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig(true) const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig(true)
const { tun } = controledMihomoConfig || {} const { tun } = controledMihomoConfig || {}
const { enable } = tun || {} const { enable } = tun || {}
const onChange = async (enable: boolean): Promise<void> => { const onChange = async (enable: boolean): Promise<void> => {
const encryptionAvailable = await isEncryptionAvailable()
if (!appConfig?.encryptedPassword && encryptionAvailable) {
setOpenPasswordModal(true)
return
}
if (!encryptionAvailable) {
alert('加密不可用,请手动给内核授权')
}
await patchControledMihomoConfig({ tun: { enable } }) await patchControledMihomoConfig({ tun: { enable } })
await patchMihomoConfig({ tun: { enable } }) await patchMihomoConfig({ tun: { enable } })
} }
return ( return (
<Card <>
className={`w-[50%] ml-1 ${match ? 'bg-primary' : ''}`} {openPasswordModal && (
isPressable <BasePasswordModal
onPress={() => navigate('/tun')} onCancel={() => setOpenPasswordModal(false)}
> onConfirm={async (password: string) => {
<CardBody className="pb-1 pt-0 px-0"> const encrypted = await encryptString(password)
<div className="flex justify-between"> patchAppConfig({ encryptedPassword: encrypted })
<Button setOpenPasswordModal(false)
isIconOnly }}
className="bg-transparent pointer-events-none" />
variant="flat" )}
color="default" <Card
> className={`w-[50%] ml-1 ${match ? 'bg-primary' : ''}`}
<TbDeviceIpadHorizontalBolt color="default" className="text-[24px] font-bold" /> isPressable
</Button> onPress={() => navigate('/tun')}
<BorderSwitch >
isShowBorder={match && enable} <CardBody className="pb-1 pt-0 px-0">
isSelected={enable} <div className="flex justify-between">
onValueChange={onChange} <Button
/> isIconOnly
</div> className="bg-transparent pointer-events-none"
</CardBody> variant="flat"
<CardFooter className="pt-1"> color="default"
<h3 className="select-none text-md font-bold"></h3> >
</CardFooter> <TbDeviceIpadHorizontalBolt color="default" className="text-[24px] font-bold" />
</Card> </Button>
<BorderSwitch
isShowBorder={match && enable}
isSelected={enable}
onValueChange={onChange}
/>
</div>
</CardBody>
<CardFooter className="pt-1">
<h3 className="select-none text-md font-bold"></h3>
</CardFooter>
</Card>
</>
) )
} }

View File

@ -106,6 +106,13 @@ export async function triggerSysProxy(enable: boolean): Promise<void> {
return await window.electron.ipcRenderer.invoke('triggerSysProxy', enable) return await window.electron.ipcRenderer.invoke('triggerSysProxy', enable)
} }
export async function isEncryptionAvailable(): Promise<boolean> {
return await window.electron.ipcRenderer.invoke('isEncryptionAvailable')
}
export async function encryptString(str: string): Promise<Buffer> {
return await window.electron.ipcRenderer.invoke('encryptString', str)
}
export async function quitApp(): Promise<void> { export async function quitApp(): Promise<void> {
return await window.electron.ipcRenderer.invoke('quitApp') return await window.electron.ipcRenderer.invoke('quitApp')
} }

View File

@ -136,6 +136,7 @@ interface IAppConfig {
userAgent?: string userAgent?: string
delayTestUrl?: string delayTestUrl?: string
delayTestTimeout?: number delayTestTimeout?: number
encryptedPassword?: Buffer
} }
interface IMihomoTunConfig { interface IMihomoTunConfig {