Skip to content
Closed
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
1 change: 1 addition & 0 deletions agent/app/dto/request/mcp_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type McpServerCreate struct {
Type string `json:"type" validate:"required"`
GatewayImage string `json:"gatewayImage"`
ProtocolVersion string `json:"protocolVersion"`
OAuth2Bearer string `json:"oauth2Bearer"`
TaskID string `json:"taskID"`
}

Expand Down
1 change: 1 addition & 0 deletions agent/app/model/mcp_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ type McpServer struct {
Type string `json:"type"`
GatewayImage string `json:"gatewayImage"`
ProtocolVersion string `json:"protocolVersion"`
OAuth2Bearer string `gorm:"column:oauth2_bearer" json:"oauth2Bearer"`
}
2 changes: 1 addition & 1 deletion agent/app/service/cronjob.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ func (u *CronjobService) HandleStop(id uint) error {
if len(record.TaskID) == 0 {
return nil
}
if cancel, ok := global.TaskCtxMap[record.TaskID]; ok {
if cancel, ok := global.LoadTaskCancel(record.TaskID); ok {
cancel()
}
return nil
Expand Down
4 changes: 2 additions & 2 deletions agent/app/service/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,15 +485,15 @@ func preflightDecompressTool(decompressType files.CompressType) error {
}

func (f *FileService) StopCompress(taskID string) error {
if cancel, ok := global.TaskCtxMap[taskID]; ok {
if cancel, ok := global.LoadTaskCancel(taskID); ok {
cancel()
return nil
}
return buserr.New("TaskNotFound")
}

func (f *FileService) StopDeCompress(taskID string) error {
if cancel, ok := global.TaskCtxMap[taskID]; ok {
if cancel, ok := global.LoadTaskCancel(taskID); ok {
cancel()
return nil
}
Expand Down
5 changes: 5 additions & 0 deletions agent/app/service/mcp_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
mcpServer.Type = req.Type
mcpServer.GatewayImage = req.GatewayImage
mcpServer.ProtocolVersion = req.ProtocolVersion
mcpServer.OAuth2Bearer = req.OAuth2Bearer
normalizeMcpServerGateway(mcpServer)
if req.OutputTransport == mcpOutputTransportSSE {
mcpServer.SsePath = req.SsePath
Expand Down Expand Up @@ -191,6 +192,7 @@ func (m McpServerService) Create(create request.McpServerCreate) error {
Type: create.Type,
GatewayImage: create.GatewayImage,
ProtocolVersion: create.ProtocolVersion,
OAuth2Bearer: create.OAuth2Bearer,
}
normalizeMcpServerGateway(mcpServer)
if create.OutputTransport == mcpOutputTransportSSE {
Expand Down Expand Up @@ -745,6 +747,9 @@ func buildSupergatewayCommand(mcpServer *model.McpServer) []string {
"--outputTransport", mcpServer.OutputTransport,
"--port", strconv.Itoa(mcpServer.Port),
}
if oauth2Bearer := strings.TrimSpace(mcpServer.OAuth2Bearer); oauth2Bearer != "" {
command = append(command, "--oauth2Bearer", oauth2Bearer)
}
Comment on lines +750 to +752

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enforce bearer auth instead of only setting headers

When a user fills this new bearer-token field for a stdio-backed MCP server, this code still builds a --stdio supergateway server and merely appends --oauth2Bearer. I checked supergateway's stdio→SSE/Streamable HTTP implementation: that option is converted into headers and applied with res.setHeader(...); it does not validate the incoming request's Authorization header. As a result, any MCP server exposed through a bound domain or 0.0.0.0 remains accessible without the configured token, which defeats the security expectation of adding a bearer token here.

Useful? React with 👍 / 👎.

if mcpServer.OutputTransport == mcpOutputTransportSSE {
return append(command,
"--baseUrl", mcpServer.BaseURL,
Expand Down
14 changes: 9 additions & 5 deletions agent/app/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, e
}
taskRepo := repo.NewITaskRepo()
ctx, cancel := context.WithCancel(context.Background())
global.TaskCtxMap[taskID] = cancel
task := &Task{TaskCtx: ctx, Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: taskModel}
global.RegisterTaskCancel(taskID, cancel)
task := &Task{TaskCtx: ctx, TaskID: taskID, Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: taskModel}
return task, nil
}

Expand Down Expand Up @@ -200,7 +200,7 @@ func ReNewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task,
logger.SetOutput(logFile)
logger.Print("\n --------------------------------------------------- \n")
taskItem.Status = constant.StatusExecuting
task := &Task{Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: &taskItem}
task := &Task{TaskID: taskID, Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: &taskItem}
task.updateTask(&taskItem)
return task, nil
}
Expand Down Expand Up @@ -231,7 +231,7 @@ func (t *Task) AddSubTaskWithIgnoreErr(name string, action ActionFunc) {
}

func (s *SubTask) Execute() error {
defer delete(global.TaskCtxMap, s.RootTask.TaskID)
defer global.RemoveTaskCancel(s.RootTask.TaskID)
subTaskName := s.Name
if s.Name == "" {
subTaskName = i18n.GetMsgByKey("SubTask")
Expand Down Expand Up @@ -302,7 +302,11 @@ func (t *Task) Execute() error {
continue
}
t.Task.ErrorMsg = err.Error()
t.Task.Status = constant.StatusFailed
if errors.Is(err, context.Canceled) {
t.Task.Status = constant.StatusCanceled
} else {
t.Task.Status = constant.StatusFailed
}
for _, rollback := range t.Rollbacks {
rollback(t)
}
Expand Down
21 changes: 21 additions & 0 deletions agent/global/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package global

import (
"context"
"sync"

badger_db "github.com/1Panel-dev/1Panel/agent/init/cache/db"
"github.com/go-playground/validator/v10"
Expand Down Expand Up @@ -39,8 +40,28 @@ var (
AlertResourceJobID cron.EntryID

TaskCtxMap = make(map[string]context.CancelFunc)
taskCtxMu sync.RWMutex
)

func RegisterTaskCancel(taskID string, cancel context.CancelFunc) {
taskCtxMu.Lock()
defer taskCtxMu.Unlock()
TaskCtxMap[taskID] = cancel
}

func LoadTaskCancel(taskID string) (context.CancelFunc, bool) {
taskCtxMu.RLock()
defer taskCtxMu.RUnlock()
cancel, ok := TaskCtxMap[taskID]
return cancel, ok
}

func RemoveTaskCancel(taskID string) {
taskCtxMu.Lock()
defer taskCtxMu.Unlock()
delete(TaskCtxMap, taskID)
}

func RepoURL() string {
if CONF.Base.IsEnterprise {
return "https://resource.fit2cloud.com/1panel/package/enterprise"
Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: 'Original password is incorrect'
ErrInternalServer: 'Internal server error: {{ .detail }}'
ErrRecordExist: 'Record already exists'
ErrRecordNotFound: 'Record not found'
WafLogExport: 'WAF log export'
MonitorLogExport: 'Website monitoring log export'
LogExportNoData: 'No logs match the selected filters'
LogExportDiskSpace: 'Less than 1 GiB of disk space is available; the export was stopped'
LogExportRetentionInvalid: 'Export record retention must be between 1 and 30 days'
ErrStructTransform: 'Type conversion failed: {{ .err }}'
ErrNotSupportType: 'Unsupported type: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/es-ES.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: 'Contraseña original incorrecta'
ErrInternalServer: 'Error interno: {{ .detail }}'
ErrRecordExist: 'Registro ya existe'
ErrRecordNotFound: 'Registro no encontrado'
WafLogExport: 'Exportación de registros WAF'
MonitorLogExport: 'Exportación de registros de monitorización web'
LogExportNoData: 'Ningún registro coincide con los filtros seleccionados'
LogExportDiskSpace: 'Hay menos de 1 GiB disponible; la exportación se detuvo'
LogExportRetentionInvalid: 'La retención del historial de exportación debe ser de 1 a 30 días'
ErrStructTransform: 'Error de conversión: {{ .err }}'
ErrNotSupportType: 'Tipo no soportado: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/fa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: 'رمز عبور اصلی اشتباه است'
ErrInternalServer: 'خطای داخلی سرور: {{ .detail }}'
ErrRecordExist: 'رکورد از قبل وجود دارد'
ErrRecordNotFound: 'رکورد یافت نشد'
WafLogExport: 'خروجی لاگ WAF'
MonitorLogExport: 'خروجی لاگ پایش وب‌سایت'
LogExportNoData: 'هیچ لاگی با فیلترهای انتخاب‌شده مطابقت ندارد'
LogExportDiskSpace: 'فضای آزاد کمتر از ۱ گیگابایت است؛ خروجی متوقف شد'
LogExportRetentionInvalid: 'مدت نگهداری سوابق خروجی باید بین ۱ تا ۳۰ روز باشد'
ErrStructTransform: 'تبدیل نوع ناموفق بود: {{ .err }}'
ErrNotSupportType: 'نوع پشتیبانی نمی‌شود: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/ja.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: '元のパスワードが違います'
ErrInternalServer: '内部エラー: {{ .detail }}'
ErrRecordExist: 'レコードが既に存在します'
ErrRecordNotFound: 'レコードが見つかりません'
WafLogExport: 'WAF ログのエクスポート'
MonitorLogExport: 'Web サイト監視ログのエクスポート'
LogExportNoData: '条件に一致するログがありません'
LogExportDiskSpace: '空き容量が 1 GiB 未満のため、エクスポートを停止しました'
LogExportRetentionInvalid: 'エクスポート履歴の保持期間は 1~30 日にしてください'
ErrStructTransform: '型変換エラー: {{ .err }}'
ErrNotSupportType: '未対応のタイプ: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/ko.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: '원래 비밀번호가 틀립니다'
ErrInternalServer: '서버 오류: {{ .detail }}'
ErrRecordExist: '레코드가 이미 있습니다'
ErrRecordNotFound: '레코드를 찾을 수 없습니다'
WafLogExport: 'WAF 로그 내보내기'
MonitorLogExport: '웹사이트 모니터링 로그 내보내기'
LogExportNoData: '선택한 조건과 일치하는 로그가 없습니다'
LogExportDiskSpace: '사용 가능한 공간이 1 GiB 미만이어서 내보내기를 중지했습니다'
LogExportRetentionInvalid: '내보내기 기록 보관 기간은 1일에서 30일 사이여야 합니다'
ErrStructTransform: '형 변환 실패: {{ .err }}'
ErrNotSupportType: '지원하지 않는 타입: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/ms.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: 'Kata laluan asal salah'
ErrInternalServer: 'Ralat pelayan: {{ .detail }}'
ErrRecordExist: 'Rekod sudah wujud'
ErrRecordNotFound: 'Rekod tidak ditemui'
WafLogExport: 'Eksport log WAF'
MonitorLogExport: 'Eksport log pemantauan laman web'
LogExportNoData: 'Tiada log yang sepadan dengan penapis dipilih'
LogExportDiskSpace: 'Ruang tersedia kurang daripada 1 GiB; eksport dihentikan'
LogExportRetentionInvalid: 'Tempoh simpanan rekod eksport mestilah antara 1 hingga 30 hari'
ErrStructTransform: 'Ralat penukaran: {{ .err }}'
ErrNotSupportType: 'Jenis tidak disokong: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/pt-BR.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: 'Senha original incorreta'
ErrInternalServer: 'Erro interno: {{ .detail }}'
ErrRecordExist: 'Registro já existe'
ErrRecordNotFound: 'Registro não encontrado'
WafLogExport: 'Exportação de logs do WAF'
MonitorLogExport: 'Exportação de logs de monitoramento de sites'
LogExportNoData: 'Nenhum log corresponde aos filtros selecionados'
LogExportDiskSpace: 'Há menos de 1 GiB disponível; a exportação foi interrompida'
LogExportRetentionInvalid: 'A retenção dos registros de exportação deve ser de 1 a 30 dias'
ErrStructTransform: 'Erro de conversão: {{ .err }}'
ErrNotSupportType: 'Tipo não suportado: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/ru.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: 'Исходный пароль неверен'
ErrInternalServer: 'Внутренняя ошибка: {{ .detail }}'
ErrRecordExist: 'Запись уже существует'
ErrRecordNotFound: 'Запись не найдена'
WafLogExport: 'Экспорт журналов WAF'
MonitorLogExport: 'Экспорт журналов мониторинга веб-сайтов'
LogExportNoData: 'Нет журналов, соответствующих выбранным фильтрам'
LogExportDiskSpace: 'Доступно менее 1 GiB; экспорт остановлен'
LogExportRetentionInvalid: 'Срок хранения истории экспорта должен быть от 1 до 30 дней'
ErrStructTransform: 'Ошибка преобразования: {{ .err }}'
ErrNotSupportType: 'Тип не поддерживается: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/tr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: 'Orijinal şifre yanlış'
ErrInternalServer: 'Sunucu hatası: {{ .detail }}'
ErrRecordExist: 'Kayıt zaten var'
ErrRecordNotFound: 'Kayıt bulunamadı'
WafLogExport: 'WAF günlüğü dışa aktarma'
MonitorLogExport: 'Web sitesi izleme günlüğü dışa aktarma'
LogExportNoData: 'Seçilen filtrelerle eşleşen günlük yok'
LogExportDiskSpace: 'Kullanılabilir alan 1 GiB altında; dışa aktarma durduruldu'
LogExportRetentionInvalid: 'Dışa aktarma kayıtları 1 ile 30 gün arasında saklanmalıdır'
ErrStructTransform: 'Dönüşüm hatası: {{ .err }}'
ErrNotSupportType: 'Bu tür desteklenmiyor: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/zh-Hant.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: '原密碼錯誤'
ErrInternalServer: '服務內部錯誤: {{ .detail }}'
ErrRecordExist: '記錄已存在'
ErrRecordNotFound: '記錄未能找到'
WafLogExport: 'WAF 日誌匯出'
MonitorLogExport: '網站監控日誌匯出'
LogExportNoData: '沒有符合條件的日誌'
LogExportDiskSpace: '可用磁碟空間不足 1 GiB,匯出任務已停止'
LogExportRetentionInvalid: '匯出記錄保留天數必須介於 1 到 30 天之間'
ErrStructTransform: '型別轉換失敗: {{ .err }}'
ErrNotSupportType: '系統暫不支援目前類型: {{ .name }}'

Expand Down
5 changes: 5 additions & 0 deletions agent/i18n/lang/zh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ ErrInitialPassword: "原密码错误"
ErrInternalServer: "服务错误: {{ .detail }}"
ErrRecordExist: "记录已存在"
ErrRecordNotFound: "记录不存在"
WafLogExport: "WAF 日志导出"
MonitorLogExport: "网站监控日志导出"
LogExportNoData: "没有符合条件的日志"
LogExportDiskSpace: "可用磁盘空间不足 1 GiB,导出任务已停止"
LogExportRetentionInvalid: "导出记录保留天数必须在 1 到 30 天之间"
ErrStructTransform: "类型转换失败: {{ .err }}"
ErrNotSupportType: "不支持当前类型: {{ .name }}"

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 @@ -41,6 +41,7 @@ func InitAgentDB() {
migrations.AddQuickJump,
migrations.UpdateMcpServerAddType,
migrations.UpdateMcpServerGatewayConfig,
migrations.UpdateMcpServerOAuth2Bearer,
migrations.InitLocalSSHConn,
migrations.InitLocalSSHShow,
migrations.InitRecordStatus,
Expand Down
7 changes: 7 additions & 0 deletions agent/init/migration/migrations/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -1516,3 +1516,10 @@ var MigrateLegoV5 = &gormigrate.Migration{
return nil
},
}

var UpdateMcpServerOAuth2Bearer = &gormigrate.Migration{
ID: "20260714-update-mcp-server-oauth2-bearer",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.McpServer{})
},
}
1 change: 1 addition & 0 deletions frontend/src/api/interface/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export namespace AI {
type: string;
gatewayImage: string;
protocolVersion: string;
oauth2Bearer?: string;
taskID?: string;
}

Expand Down
39 changes: 37 additions & 2 deletions frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4686,6 +4686,26 @@ const message = {
qrCodeExpired: 'Refresh time',
apiLeakageHelper: 'Do not disclose the QR code. Ensure it is used only in trusted environments.',
},
logExport: {
title: 'Export Logs',
records: 'Export Records',
retentionDays: 'Export Record Retention',
timeRange: 'Time Range',
timeRequired: 'Select an export time range',
websiteRequired: 'Select a website',
blackStatus: 'Blacklist Status',
black: 'Blacklisted',
notBlack: 'Not Blacklisted',
fuzzy: 'Fuzzy Match',
regex: 'Regular Expression',
invalidRegex: 'Enter a valid RE2 regular expression with at most 256 characters',
created: 'Export task created',
fileName: 'File Name',
fileSize: 'File Size',
expiresAt: 'Expires At',
cancelConfirm: 'Cancel this export task?',
deleteConfirm: 'Delete this export record and file?',
},
waf: {
name: 'WAF',
blackWhite: 'Black and White List',
Expand Down Expand Up @@ -4747,7 +4767,11 @@ const message = {
'Set the method types that are allowed to access. If you want to restrict certain types of access, please turn off this type of button. For example: only GET type access is allowed, then you need to turn off other types of buttons except GET',
geoRule: 'Regional Access Restrictions',
geoHelper:
'Restrict access to your website from certain regions, for example: if access is allowed from mainland China, then requests from outside mainland China will be blocked',
'Combine region and province policies under one switch. Region rules run first, then province rules can further restrict Chinese provincial regions',
regionPolicy: 'Region policy',
provincePolicy: 'Province policy',
provinceRuleLabel: 'Province',
provinceVersionAlert: 'OpenResty must be newer than 1.31.1.1-0 for province policies to take effect.',
ipLocation: 'IP Location',
action: 'Action',
ruleType: 'Attack Type',
Expand Down Expand Up @@ -4779,6 +4803,7 @@ const message = {
fileExtHelper: 'Prohibited file extensions for upload',
deny: 'Forbidden',
allow: 'Allow',
allowOnly: 'Allow only',
field: 'Object',
pattern: 'Condition',
ruleContent: 'Content',
Expand Down Expand Up @@ -4852,7 +4877,7 @@ const message = {
geoRestrict: 'Regional access restriction',
attacklog: 'Interception Record',
unknownWebsite: 'Unauthorized domain name access',
geoRuleEmpty: 'Region cannot be empty',
geoRuleEmpty: 'Select at least one region or province',
unknown: 'Website Not Exist',
geo: 'Region Restriction',
revertHtml: 'restore {0} as the default page?',
Expand Down Expand Up @@ -4941,6 +4966,16 @@ const message = {
crlf: 'CRLF Injection',
strict: 'Strict Mode',
strictHelper: 'Use stricter rules to validate requests',
executionStrategy: 'Execution strategy',
detectionStrength: 'Detection strength',
protectionMode: 'Protection mode',
observationMode: 'Observation mode',
standardMode: 'Standard mode',
observationModeHelper: 'Detects and logs requests without blocking them',
observationModeConfirm:
'In observation mode, all WAF matches are logged without blocking requests. Continue?',
globalStrictRequired: 'Enable strict mode in global settings first',
observe: 'Observe',
saveLog: 'Save Log',
remoteURLHelper: 'The remote URL needs to ensure one IP per line and no other characters',
notFound: 'Not Found (404)',
Expand Down
Loading
Loading