mirror of
				https://github.com/1Panel-dev/1Panel.git
				synced 2025-10-27 01:05:57 +08:00 
			
		
		
		
	
							parent
							
								
									b9c5fee411
								
							
						
					
					
						commit
						6b491710c9
					
				
					 7 changed files with 138 additions and 63 deletions
				
			
		|  | @ -48,7 +48,7 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) { | |||
| 		Status:                 constant.StatusEnable, | ||||
| 		Message:                "", | ||||
| 		Port:                   "22", | ||||
| 		ListenAddress:          "0.0.0.0", | ||||
| 		ListenAddress:          "", | ||||
| 		PasswordAuthentication: "yes", | ||||
| 		PubkeyAuthentication:   "yes", | ||||
| 		PermitRootLogin:        "yes", | ||||
|  | @ -90,7 +90,12 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) { | |||
| 			data.Port = strings.ReplaceAll(line, "Port ", "") | ||||
| 		} | ||||
| 		if strings.HasPrefix(line, "ListenAddress ") { | ||||
| 			data.ListenAddress = strings.ReplaceAll(line, "ListenAddress ", "") | ||||
| 			itemAddr := strings.ReplaceAll(line, "ListenAddress ", "") | ||||
| 			if len(data.ListenAddress) != 0 { | ||||
| 				data.ListenAddress += ("," + itemAddr) | ||||
| 			} else { | ||||
| 				data.ListenAddress = itemAddr | ||||
| 			} | ||||
| 		} | ||||
| 		if strings.HasPrefix(line, "PasswordAuthentication ") { | ||||
| 			data.PasswordAuthentication = strings.ReplaceAll(line, "PasswordAuthentication ", "") | ||||
|  | @ -366,33 +371,34 @@ func sortFileList(fileNames []sshFileItem) []sshFileItem { | |||
| 	return fileNames | ||||
| } | ||||
| 
 | ||||
| func updateSSHConf(oldFiles []string, param string, value interface{}) []string { | ||||
| 	hasKey := false | ||||
| func updateSSHConf(oldFiles []string, param string, value string) []string { | ||||
| 	var valueItems []string | ||||
| 	if param != "ListenAddress" { | ||||
| 		valueItems = append(valueItems, value) | ||||
| 	} else { | ||||
| 		if value != "" { | ||||
| 			valueItems = strings.Split(value, ",") | ||||
| 		} | ||||
| 	} | ||||
| 	var newFiles []string | ||||
| 	for _, line := range oldFiles { | ||||
| 		if strings.HasPrefix(line, param+" ") { | ||||
| 			newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value)) | ||||
| 			hasKey = true | ||||
| 		lineItem := strings.TrimSpace(line) | ||||
| 		if (strings.HasPrefix(lineItem, param) || strings.HasPrefix(lineItem, fmt.Sprintf("#%s", param))) && len(valueItems) != 0 { | ||||
| 			newFiles = append(newFiles, fmt.Sprintf("%s %s", param, valueItems[0])) | ||||
| 			valueItems = valueItems[1:] | ||||
| 			continue | ||||
| 		} | ||||
| 		if strings.HasPrefix(lineItem, param) && len(valueItems) == 0 { | ||||
| 			newFiles = append(newFiles, fmt.Sprintf("#%s", line)) | ||||
| 			continue | ||||
| 		} | ||||
| 		newFiles = append(newFiles, line) | ||||
| 	} | ||||
| 	if !hasKey { | ||||
| 		newFiles = []string{} | ||||
| 		for _, line := range oldFiles { | ||||
| 			if strings.HasPrefix(line, fmt.Sprintf("#%s ", param)) && !hasKey { | ||||
| 				newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value)) | ||||
| 				hasKey = true | ||||
| 				continue | ||||
| 			} | ||||
| 			newFiles = append(newFiles, line) | ||||
| 	if len(valueItems) != 0 { | ||||
| 		for _, item := range valueItems { | ||||
| 			newFiles = append(newFiles, fmt.Sprintf("%s %s", param, item)) | ||||
| 		} | ||||
| 	} | ||||
| 	if !hasKey { | ||||
| 		newFiles = []string{} | ||||
| 		newFiles = append(newFiles, oldFiles...) | ||||
| 		newFiles = append(newFiles, fmt.Sprintf("%s %v", param, value)) | ||||
| 	} | ||||
| 	return newFiles | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1120,8 +1120,10 @@ const message = { | |||
|             'Modifying the configuration file may cause service availability. Exercise caution when performing this operation. Do you want to continue?', | ||||
|         portHelper: 'Specifies the port number monitored by the SSH service. The default port number is 22.', | ||||
|         listenAddress: 'Listening address', | ||||
|         addressHelper: | ||||
|             'Specify the IP address monitored by the SSH service. The default value is 0.0.0.0. That is, all network interfaces are monitored.', | ||||
|         allV4V6: '0.0.0.0:{0}(IPv4) and :::{0}(IPv6)', | ||||
|         listenHelper: | ||||
|             'Canceling IPv4 and IPv6 settings simultaneously will listen on both 0.0.0.0:{0}(IPv4) and :::{0}(IPv6)', | ||||
|         addressHelper: 'Specify the IP address on which the SSH service will listen', | ||||
|         permitRootLogin: 'root user', | ||||
|         rootSettingHelper: 'The default login mode is SSH for user root.', | ||||
|         rootHelper1: 'Allow SSH login', | ||||
|  |  | |||
|  | @ -1066,7 +1066,9 @@ const message = { | |||
|         port: '連接端口', | ||||
|         portHelper: '指定 SSH 服務監聽的端口號,默認為 22。', | ||||
|         listenAddress: '監聽地址', | ||||
|         addressHelper: '指定 SSH 服務監聽的 IP 地址,默認為 0.0.0.0,即所有的網絡接口都會被監聽。', | ||||
|         allV4V6: '0.0.0.0:{0}(IPv4)和 :::{0}(IPv6)', | ||||
|         listenHelper: '同時取消 IPv4 和 IPv6 設置,將會同時監聽 0.0.0.0:{0}(IPv4) 和 :::{0}(IPv6)', | ||||
|         addressHelper: '指定 SSH 服務監聽的 IP 地址', | ||||
|         permitRootLogin: 'root 用戶', | ||||
|         rootSettingHelper: 'root 用戶 SSH 登錄方式,默認所有 SSH 登錄。', | ||||
|         rootHelper1: '允許 SSH 登錄', | ||||
|  |  | |||
|  | @ -1067,7 +1067,9 @@ const message = { | |||
|         port: '连接端口', | ||||
|         portHelper: '指定 SSH 服务监听的端口号,默认为 22。', | ||||
|         listenAddress: '监听地址', | ||||
|         addressHelper: '指定 SSH 服务监听的 IP 地址,默认为 0.0.0.0,即所有的网络接口都会被监听。', | ||||
|         allV4V6: '0.0.0.0:{0}(IPv4) 和 :::{0}(IPv6)', | ||||
|         listenHelper: '同时取消 IPv4 和 IPv6 设置,将会同时监听 0.0.0.0:{0}(IPv4) 和 :::{0}(IPv6)', | ||||
|         addressHelper: '指定 SSH 服务监听的 IP 地址', | ||||
|         permitRootLogin: 'root 用户', | ||||
|         rootSettingHelper: 'root 用户 SSH 登录方式,默认所有 SSH 登录。', | ||||
|         rootHelper1: '允许 SSH 登录', | ||||
|  |  | |||
|  | @ -150,7 +150,7 @@ import { ElForm } from 'element-plus'; | |||
| import { createNetwork } from '@/api/modules/container'; | ||||
| import DrawerHeader from '@/components/drawer-header/index.vue'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { checkIpV6 } from '@/utils/util'; | ||||
| import { checkIp, checkIpV6 } from '@/utils/util'; | ||||
| 
 | ||||
| const loading = ref(false); | ||||
| 
 | ||||
|  | @ -214,9 +214,7 @@ function checkGateway(rule: any, value: any, callback: any) { | |||
|     if (value === '') { | ||||
|         callback(); | ||||
|     } | ||||
|     const reg = | ||||
|         /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/; | ||||
|     if (!reg.test(value) && value !== '') { | ||||
|     if (checkIp(value)) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     callback(); | ||||
|  | @ -225,27 +223,11 @@ function checkGateway(rule: any, value: any, callback: any) { | |||
| function checkGatewayV6(rule: any, value: any, callback: any) { | ||||
|     if (value === '') { | ||||
|         callback(); | ||||
|     } else { | ||||
|         const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'; | ||||
|         const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`; | ||||
|         const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})'; | ||||
|         const IPv6AddressRegExp = new RegExp( | ||||
|             '^(' + | ||||
|                 `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` + | ||||
|                 `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` + | ||||
|                 ')(%[0-9a-zA-Z-.:]{1,})?$', | ||||
|         ); | ||||
|         if (!IPv6AddressRegExp.test(value) && value !== '') { | ||||
|             return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|         } | ||||
|         callback(); | ||||
|     } | ||||
|     if (checkIpV6(value)) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     callback(); | ||||
| } | ||||
| 
 | ||||
| function checkCidr(rule: any, value: any, callback: any) { | ||||
|  |  | |||
|  | @ -10,15 +10,40 @@ | |||
|             <template #header> | ||||
|                 <DrawerHeader :header="$t('ssh.listenAddress')" :back="handleClose" /> | ||||
|             </template> | ||||
|             <el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading"> | ||||
|             <el-form | ||||
|                 ref="formRef" | ||||
|                 label-position="top" | ||||
|                 :rules="rules" | ||||
|                 :model="form" | ||||
|                 @submit.prevent | ||||
|                 v-loading="loading" | ||||
|             > | ||||
|                 <el-row type="flex" justify="center"> | ||||
|                     <el-col :span="22"> | ||||
|                         <el-form-item | ||||
|                             :label="$t('ssh.listenAddress')" | ||||
|                             prop="listenAddress" | ||||
|                             :rules="Rules.ipV4V6OrDomain" | ||||
|                         > | ||||
|                             <el-input clearable v-model="form.listenAddress" /> | ||||
|                         <el-alert class="common-prompt" :closable="false" type="error"> | ||||
|                             <template #default> | ||||
|                                 <span> | ||||
|                                     {{ $t('ssh.listenHelper', [form.port]) }} | ||||
|                                 </span> | ||||
|                             </template> | ||||
|                         </el-alert> | ||||
|                         <el-form-item label="IPv4" prop="listenAddressV4"> | ||||
|                             <el-checkbox | ||||
|                                 v-model="form.ipv4All" | ||||
|                                 @change="form.listenAddressV4 = form.ipv4All ? '0.0.0.0' : form.listenAddressV4" | ||||
|                             > | ||||
|                                 {{ $t('setting.bindAll') }} | ||||
|                             </el-checkbox> | ||||
|                             <el-input :disabled="form.ipv4All" clearable v-model="form.listenAddressV4"></el-input> | ||||
|                         </el-form-item> | ||||
|                         <el-form-item label="IPv6" prop="listenAddressV6"> | ||||
|                             <el-checkbox | ||||
|                                 v-model="form.ipv6All" | ||||
|                                 @change="form.listenAddressV6 = form.ipv6All ? '::' : form.listenAddressV6" | ||||
|                             > | ||||
|                                 {{ $t('setting.bindAll') }} | ||||
|                             </el-checkbox> | ||||
|                             <el-input :disabled="form.ipv6All" clearable v-model="form.listenAddressV6"></el-input> | ||||
|                         </el-form-item> | ||||
|                     </el-col> | ||||
|                 </el-row> | ||||
|  | @ -39,26 +64,66 @@ import { reactive, ref } from 'vue'; | |||
| import i18n from '@/lang'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { ElMessageBox, FormInstance } from 'element-plus'; | ||||
| import { Rules } from '@/global/form-rules'; | ||||
| import { updateSSH } from '@/api/modules/host'; | ||||
| import DrawerHeader from '@/components/drawer-header/index.vue'; | ||||
| import { checkIp, checkIpV6 } from '@/utils/util'; | ||||
| 
 | ||||
| const emit = defineEmits<{ (e: 'search'): void }>(); | ||||
| 
 | ||||
| interface DialogProps { | ||||
|     port: number; | ||||
|     address: string; | ||||
| } | ||||
| const drawerVisible = ref(); | ||||
| const loading = ref(); | ||||
| 
 | ||||
| const form = reactive({ | ||||
|     listenAddress: '0.0.0.0', | ||||
|     port: 22, | ||||
|     ipv4All: false, | ||||
|     ipv6All: false, | ||||
|     listenAddressV4: '', | ||||
|     listenAddressV6: '', | ||||
| }); | ||||
| 
 | ||||
| const rules = reactive({ | ||||
|     listenAddressV4: [{ validator: checkIPv4, trigger: 'blur' }], | ||||
|     listenAddressV6: [{ validator: checkIPv6, trigger: 'blur' }], | ||||
| }); | ||||
| 
 | ||||
| function checkIPv4(rule: any, value: any, callback: any) { | ||||
|     if (value === '') { | ||||
|         callback(); | ||||
|     } | ||||
|     if (checkIp(value)) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.ip'))); | ||||
|     } | ||||
|     callback(); | ||||
| } | ||||
| 
 | ||||
| function checkIPv6(rule: any, value: any, callback: any) { | ||||
|     if (value === '') { | ||||
|         callback(); | ||||
|     } | ||||
|     if (checkIpV6(value)) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.ip'))); | ||||
|     } | ||||
|     callback(); | ||||
| } | ||||
| 
 | ||||
| const formRef = ref<FormInstance>(); | ||||
| 
 | ||||
| const acceptParams = (params: DialogProps): void => { | ||||
|     form.listenAddress = params.address; | ||||
|     let items = params.address.split(','); | ||||
|     for (const item of items) { | ||||
|         if (item.indexOf(':') !== -1) { | ||||
|             form.listenAddressV6 = item; | ||||
|             form.ipv6All = item === '::'; | ||||
|             continue; | ||||
|         } | ||||
|         form.listenAddressV4 = item; | ||||
|         form.ipv4All = item === '0.0.0.0'; | ||||
|     } | ||||
|     form.port = params.port; | ||||
|     drawerVisible.value = true; | ||||
| }; | ||||
| 
 | ||||
|  | @ -66,8 +131,19 @@ const onSave = async (formEl: FormInstance | undefined) => { | |||
|     if (!formEl) return; | ||||
|     formEl.validate(async (valid) => { | ||||
|         if (!valid) return; | ||||
|         let itemAddr = []; | ||||
|         if (form.listenAddressV4 !== '') { | ||||
|             itemAddr.push(form.listenAddressV4); | ||||
|         } | ||||
|         if (form.listenAddressV6 !== '') { | ||||
|             itemAddr.push(form.listenAddressV6); | ||||
|         } | ||||
|         let addr = | ||||
|             itemAddr.join(',') === '' || itemAddr.join(',') === '0.0.0.0,::' | ||||
|                 ? i18n.global.t('ssh.allV4V6', [form.port]) | ||||
|                 : itemAddr.join(','); | ||||
|         ElMessageBox.confirm( | ||||
|             i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('ssh.listenAddress'), form.listenAddress]), | ||||
|             i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('ssh.listenAddress'), addr]), | ||||
|             i18n.global.t('ssh.sshChange'), | ||||
|             { | ||||
|                 confirmButtonText: i18n.global.t('commons.button.confirm'), | ||||
|  | @ -79,7 +155,7 @@ const onSave = async (formEl: FormInstance | undefined) => { | |||
|                 let params = { | ||||
|                     key: 'ListenAddress', | ||||
|                     oldValue: '', | ||||
|                     newValue: form.listenAddress, | ||||
|                     newValue: itemAddr.join(','), | ||||
|                 }; | ||||
|                 loading.value = true; | ||||
|                 await updateSSH(params) | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ | |||
|                                 <span class="input-help">{{ $t('ssh.portHelper') }}</span> | ||||
|                             </el-form-item> | ||||
|                             <el-form-item :label="$t('ssh.listenAddress')" prop="listenAddress"> | ||||
|                                 <el-input disabled v-model="form.listenAddress"> | ||||
|                                 <el-input disabled v-model="form.listenAddressItem"> | ||||
|                                     <template #append> | ||||
|                                         <el-button @click="onChangeAddress" icon="Setting"> | ||||
|                                             {{ $t('commons.button.set') }} | ||||
|  | @ -184,6 +184,7 @@ const form = reactive({ | |||
|     message: '', | ||||
|     port: 22, | ||||
|     listenAddress: '', | ||||
|     listenAddressItem: '', | ||||
|     passwordAuthentication: 'yes', | ||||
|     pubkeyAuthentication: 'yes', | ||||
|     encryptionMode: '', | ||||
|  | @ -222,7 +223,7 @@ const onChangeRoot = () => { | |||
|     rootsRef.value.acceptParams({ permitRootLogin: form.permitRootLogin }); | ||||
| }; | ||||
| const onChangeAddress = () => { | ||||
|     addressRef.value.acceptParams({ address: form.listenAddress }); | ||||
|     addressRef.value.acceptParams({ address: form.listenAddress, port: form.port }); | ||||
| }; | ||||
| 
 | ||||
| const onOperate = async (operation: string) => { | ||||
|  | @ -325,6 +326,10 @@ const search = async () => { | |||
|     form.port = Number(res.data.port); | ||||
|     autoStart.value = res.data.autoStart ? 'enable' : 'disable'; | ||||
|     form.listenAddress = res.data.listenAddress; | ||||
|     form.listenAddressItem = | ||||
|         form.listenAddress === '' || form.listenAddress === '0.0.0.0,::' | ||||
|             ? i18n.global.t('ssh.allV4V6', [form.port]) | ||||
|             : form.listenAddress; | ||||
|     form.passwordAuthentication = res.data.passwordAuthentication; | ||||
|     form.pubkeyAuthentication = res.data.pubkeyAuthentication; | ||||
|     form.permitRootLogin = res.data.permitRootLogin; | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue