Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 50 additions & 31 deletions agent/app/api/v2/setting.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package v2

import (
"encoding/base64"
"encoding/json"
"os"
"os/user"
"path"

"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -71,6 +70,35 @@ func (b *BaseApi) LoadBaseDir(c *gin.Context) {
helper.SuccessWithData(c, global.Dir.DataDir)
}

// @Tags System Setting
// @Summary Load local conn
// @Success 200 {object} dto.SSHConnData
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/ssh [get]
func (b *BaseApi) LoadLocalConn(c *gin.Context) {
connInfoInDB, err := settingService.GetSSHInfo()
if err != nil {
helper.InternalServer(c, err)
return
}
var data dto.SSHConnData
if err := json.Unmarshal([]byte(connInfoInDB), &data); err != nil {
helper.InternalServer(c, err)
return
}
if len(data.Password) != 0 {
data.Password = base64.StdEncoding.EncodeToString([]byte(data.Password))
}
if len(data.PrivateKey) != 0 {
data.PrivateKey = base64.StdEncoding.EncodeToString([]byte(data.PrivateKey))
}
if len(data.PassPhrase) != 0 {
data.PassPhrase = base64.StdEncoding.EncodeToString([]byte(data.PassPhrase))
}
helper.SuccessWithData(c, data)
}

func (b *BaseApi) CheckLocalConn(c *gin.Context) {
_, err := loadLocalConn()
helper.SuccessWithData(c, err == nil)
Expand All @@ -87,6 +115,7 @@ func (b *BaseApi) CheckLocalConnByInfo(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

helper.SuccessWithData(c, settingService.TestConnByInfo(req))
}

Expand All @@ -96,51 +125,41 @@ func (b *BaseApi) CheckLocalConnByInfo(c *gin.Context) {
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/ssh [post]
func (b *BaseApi) SaveLocalConnInfo(c *gin.Context) {
func (b *BaseApi) SaveLocalConn(c *gin.Context) {
var req dto.SSHConnData
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
helper.SuccessWithData(c, settingService.SaveConnInfo(req))
}

func loadLocalConn() (*ssh.SSHClient, error) {
itemPath := ""
currentInfo, _ := user.Current()
if len(currentInfo.HomeDir) == 0 {
itemPath = "/root/.ssh/id_ed25519_1panel"
} else {
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
}
if _, err := os.Stat(itemPath); err != nil {
_ = sshService.CreateRootCert(dto.CreateRootCert{EncryptionMode: "ed25519", Name: "id_ed25519_1panel", Description: "1Panel Terminal"})
}

privateKey, _ := os.ReadFile(itemPath)
connWithKey := ssh.ConnInfo{
Addr: "127.0.0.1",
User: "root",
Port: 22,
AuthMode: "key",
PrivateKey: privateKey,
}
client, err := ssh.NewClient(connWithKey)
if err == nil {
return client, nil
if err := settingService.SaveConnInfo(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}

func loadLocalConn() (*ssh.SSHClient, error) {
connInfoInDB, err := settingService.GetSSHInfo()
if err != nil {
return nil, err
}
if len(connInfoInDB) == 0 {
return nil, errors.New("no such ssh conn info in db!")
}
var connInDB ssh.ConnInfo
var connInDB model.LocalConnInfo
if err := json.Unmarshal([]byte(connInfoInDB), &connInDB); err != nil {
return nil, err
}
return ssh.NewClient(connInDB)
sshInfo := ssh.ConnInfo{
Addr: connInDB.Addr,
Port: int(connInDB.Port),
User: connInDB.User,
AuthMode: connInDB.AuthMode,
Password: connInDB.Password,
PrivateKey: []byte(connInDB.PrivateKey),
PassPhrase: []byte(connInDB.PassPhrase),
}
return ssh.NewClient(sshInfo)
}

// @Tags System Setting
Expand Down
10 changes: 10 additions & 0 deletions agent/app/model/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ type NodeInfo struct {
ServerCrt string `json:"serverCrt"`
ServerKey string `json:"serverKey"`
}

type LocalConnInfo struct {
Addr string `json:"addr"`
Port uint `json:"port"`
User string `json:"user"`
AuthMode string `json:"authMode"`
Password string `json:"password"`
PrivateKey string `json:"privateKey"`
PassPhrase string `json:"passPhrase"`
}
7 changes: 7 additions & 0 deletions agent/app/service/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ func (u *SettingService) GetSettingByKey(key string) string {
case "SystemIP":
value, _ := settingRepo.GetValueByKey(key)
return value
case "LocalSSHConn":
value, _ := settingRepo.GetValueByKey(key)
if len(value) == 0 {
return ""
}
itemStr, _ := encrypt.StringDecryptWithBase64(value)
return itemStr
default:
return ""
}
Expand Down
1 change: 1 addition & 0 deletions agent/init/migration/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func InitAgentDB() {
migrations.UpdateWebsiteSSL,
migrations.AddQuickJump,
migrations.UpdateMcpServerAddType,
migrations.InitLocalSSHConn,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
Expand Down
43 changes: 43 additions & 0 deletions agent/init/migration/migrations/init.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package migrations

import (
"encoding/json"
"fmt"
"os"
"os/user"
"path"
"time"

"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/service"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/copier"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"

"github.com/go-gormigrate/gormigrate/v2"
Expand Down Expand Up @@ -510,3 +516,40 @@ var UpdateMcpServerAddType = &gormigrate.Migration{
return nil
},
}

var InitLocalSSHConn = &gormigrate.Migration{
ID: "20250905-init-local-ssh",
Migrate: func(tx *gorm.DB) error {
itemPath := ""
currentInfo, _ := user.Current()
if len(currentInfo.HomeDir) == 0 {
itemPath = "/root/.ssh/id_ed25519_1panel"
} else {
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
}
if _, err := os.Stat(itemPath); err != nil {
_ = service.NewISSHService().CreateRootCert(dto.CreateRootCert{EncryptionMode: "ed25519", Name: "id_ed25519_1panel", Description: "1Panel Terminal"})
}
privateKey, _ := os.ReadFile(itemPath)
connWithKey := ssh.ConnInfo{
Addr: "127.0.0.1",
User: "root",
Port: 22,
AuthMode: "key",
PrivateKey: privateKey,
}
if _, err := ssh.NewClient(connWithKey); err != nil {
return nil
}
var conn model.LocalConnInfo
_ = copier.Copy(&conn, &connWithKey)
conn.PrivateKey = string(privateKey)
conn.PassPhrase = ""
localConn, _ := json.Marshal(&conn)
connAfterEncrypt, _ := encrypt.StringEncrypt(string(localConn))
if err := tx.Model(&model.Setting{}).Where("key = ?", "LocalSSHConn").Updates(map[string]interface{}{"value": connAfterEncrypt}).Error; err != nil {
return err
}
return nil
},
}
3 changes: 2 additions & 1 deletion agent/router/ro_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
settingRouter.GET("/basedir", baseApi.LoadBaseDir)

settingRouter.POST("/ssh/check", baseApi.CheckLocalConn)
settingRouter.POST("/ssh", baseApi.SaveLocalConnInfo)
settingRouter.GET("/ssh/conn", baseApi.LoadLocalConn)
settingRouter.POST("/ssh", baseApi.SaveLocalConn)
settingRouter.POST("/ssh/check/info", baseApi.CheckLocalConnByInfo)
}
}
1 change: 1 addition & 0 deletions frontend/src/api/interface/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export namespace Host {
user: string;
authMode: string;
privateKey: string;
passPhrase: string;
password: string;
}
export interface GroupChange {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/api/modules/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export const deleteHost = (params: { ids: number[] }) => {
};

// agent
export const loadLocalConn = () => {
return http.get<Host.HostConnTest>(`/settings/ssh/conn`);
};
export const testLocalConn = () => {
return http.post<boolean>(`/settings/ssh/check`);
};
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,8 @@ const message = {
},
terminal: {
local: 'Local',
defaultConn: 'Default Connection',
defaultConnHelper: 'Whether to connect to the host by default after opening the terminal',
localHelper: 'The `local` name is used only for system local identification',
connLocalErr: 'Unable to automatically authenticate, please fill in the local server login information.',
testConn: 'Test connection',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,8 @@ const message = {
},
terminal: {
local: 'ローカル',
defaultConn: 'デフォルト接続',
defaultConnHelper: 'ターミナルを開いた後にデフォルトでホストに接続するかどうか',
localHelper: 'ローカル名はシステムのローカル識別にのみ使用されます。',
connLocalErr: '自動的に認証できない場合は、ローカルサーバーのログイン情報を入力してください。',
testConn: 'テスト接続',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,8 @@ const message = {
},
terminal: {
local: '로컬',
defaultConn: '기본 연결',
defaultConnHelper: '터미널을 연 후 기본적으로 호스트에 연결할지 여부',
localHelper: '로컬 이름은 시스템 로컬 식별에만 사용됩니다.',
connLocalErr: '자동 인증에 실패했습니다. 로컬 서버 로그인 정보를 입력해주세요.',
testConn: '연결 테스트',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/ms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,8 @@ const message = {
},
terminal: {
local: 'Tempatan',
defaultConn: 'Sambungan Lalai',
defaultConnHelper: 'Sama ada untuk menyambung ke hos secara lalai selepas membuka terminal',
localHelper: 'Nama tempatan hanya digunakan untuk pengenalan sistem tempatan.',
connLocalErr: 'Tidak dapat mengesahkan secara automatik, sila isi maklumat log masuk pelayan tempatan.',
testConn: 'Uji sambungan',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/pt-br.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,8 @@ const message = {
},
terminal: {
local: 'Local',
defaultConn: 'Conexão Padrão',
defaultConnHelper: 'Se deve conectar ao host por padrão após abrir o terminal',
localHelper: 'O nome local é usado apenas para identificação local do sistema.',
connLocalErr:
'Невозможно автоматически аутентифицироваться, пожалуйста, заполните информацию для входа на локальный сервер.',
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/lang/modules/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1149,8 +1149,11 @@ const message = {
},
terminal: {
local: 'Локальный',
defaultConn: 'Соединение по умолчанию',
defaultConnHelper: 'Подключаться ли к хосту по умолчанию после открытия терминала',
localHelper: 'Локальное имя используется только для локальной идентификации системы.',
connLocalErr: '無法自動認證,請填寫本地服務器的登錄信息!',
connLocalErr:
'Невозможно выполнить автоматическую аутентификацию, пожалуйста, заполните информацию для входа на локальный сервер!',
testConn: 'Проверить подключение',
saveAndConn: 'Сохранить и подключиться',
connTestOk: 'Информация о подключении доступна',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,8 @@ const message = {
},
terminal: {
local: 'Yerel',
defaultConn: 'Varsayılan Bağlantı',
defaultConnHelper: 'Terminal açıldıktan sonra varsayılan olarak ana bilgisayara bağlanılsın mı',
localHelper: '`local` adı sadece sistem yerel tanımlaması için kullanılır',
connLocalErr: 'Otomatik kimlik doğrulama yapılamıyor, lütfen yerel sunucu giriş bilgilerini doldurun.',
testConn: 'Bağlantıyı test et',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/zh-Hant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,8 @@ const message = {
},
terminal: {
local: '本機',
defaultConn: '預設連接',
defaultConnHelper: '開啟終端後是否預設連線到主機',
localHelper: 'local 名稱僅用於系統本機標識',
connLocalErr: '無法自動認證,請填寫本地服務器的登錄信息!',
testConn: '連接測試',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,8 @@ const message = {
},
terminal: {
local: '本机',
defaultConn: '默认连接',
defaultConnHelper: '打开终端后是否默认连接主机',
localHelper: 'local 名称仅用于系统本机标识',
connLocalErr: '无法自动认证,请填写本地服务器的登录信息!',
testConn: '连接测试',
Expand Down
Loading
Loading