feat: Add HTTPS fallback prevention configuration in OpenResty (#9703)

This commit is contained in:
CityFun 2025-07-30 12:04:51 +08:00 committed by GitHub
parent 04b9cbd87a
commit 0316cb0662
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 277 additions and 0 deletions

View file

@ -157,3 +157,39 @@ func (b *BaseApi) GetNginxModules(c *gin.Context) {
} }
helper.SuccessWithData(c, modules) helper.SuccessWithData(c, modules)
} }
// @Tags OpenResty
// @Summary Operate default HTTPs
// @Accept json
// @Param request body request.NginxOperateReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /openresty/https [post]
func (b *BaseApi) OperateDefaultHTTPs(c *gin.Context) {
var req request.NginxOperateReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := nginxService.OperateDefaultHTTPs(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags OpenResty
// @Summary Get default HTTPs status
// @Success 200 {object} response.NginxConfigRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /openresty/https [get]
func (b *BaseApi) GetDefaultHTTPsStatus(c *gin.Context) {
res, err := nginxService.GetDefaultHttpsStatus()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}

View file

@ -126,3 +126,7 @@ type NginxModuleUpdate struct {
Enable bool `json:"enable"` Enable bool `json:"enable"`
Params string `json:"params"` Params string `json:"params"`
} }
type NginxOperateReq struct {
Operate string `json:"operate" validate:"required,oneof=enable disable"`
}

View file

@ -80,3 +80,7 @@ type NginxBuildConfig struct {
Mirror string `json:"mirror"` Mirror string `json:"mirror"`
Modules []NginxModule `json:"modules"` Modules []NginxModule `json:"modules"`
} }
type NginxConfigRes struct {
Https bool `json:"https"`
}

View file

@ -4,6 +4,8 @@ import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"io" "io"
"net/http" "net/http"
"os" "os"
@ -42,6 +44,9 @@ type INginxService interface {
Build(req request.NginxBuildReq) error Build(req request.NginxBuildReq) error
GetModules() (*response.NginxBuildConfig, error) GetModules() (*response.NginxBuildConfig, error)
UpdateModule(req request.NginxModuleUpdate) error UpdateModule(req request.NginxModuleUpdate) error
OperateDefaultHTTPs(req request.NginxOperateReq) error
GetDefaultHttpsStatus() (*response.NginxConfigRes, error)
} }
func NewINginxService() INginxService { func NewINginxService() INginxService {
@ -348,3 +353,70 @@ func (n NginxService) UpdateModule(req request.NginxModuleUpdate) error {
} }
return fileOp.SaveFileWithByte(moduleConfigPath, moduleByte, constant.DirPerm) return fileOp.SaveFileWithByte(moduleConfigPath, moduleByte, constant.DirPerm)
} }
func (n NginxService) OperateDefaultHTTPs(req request.NginxOperateReq) error {
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
websites, _ := websiteRepo.List()
hasDefaultWebsite := false
for _, website := range websites {
if website.DefaultServer {
hasDefaultWebsite = true
break
}
}
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
content, err := os.ReadFile(defaultConfigPath)
if err != nil {
return err
}
if req.Operate == "enable" {
if err := handleSSLConfig(&appInstall, hasDefaultWebsite); err != nil {
return err
}
} else if req.Operate == "disable" {
defaultConfig, err := parser.NewStringParser(string(content)).Parse()
if err != nil {
return err
}
defaultConfig.FilePath = defaultConfigPath
defaultServer := defaultConfig.FindServers()[0]
defaultServer.RemoveListen(fmt.Sprintf("%d", appInstall.HttpsPort))
defaultServer.RemoveListen(fmt.Sprintf("[::]:%d", appInstall.HttpsPort))
defaultServer.RemoveDirective("include", []string{"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf"})
defaultServer.RemoveDirective("http2", []string{"on"})
if err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {
return err
}
}
return nginxCheckAndReload(string(content), defaultConfigPath, appInstall.ContainerName)
}
func (n NginxService) GetDefaultHttpsStatus() (*response.NginxConfigRes, error) {
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return nil, err
}
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
content, err := os.ReadFile(defaultConfigPath)
if err != nil {
return nil, err
}
defaultConfig, err := parser.NewStringParser(string(content)).Parse()
if err != nil {
return nil, err
}
defaultConfig.FilePath = defaultConfigPath
defaultServer := defaultConfig.FindServers()[0]
res := &response.NginxConfigRes{}
for _, directive := range defaultServer.GetDirectives() {
if directive.GetName() == "include" && directive.GetParameters()[0] == "/usr/local/openresty/nginx/conf/ssl/root_ssl.conf" {
return &response.NginxConfigRes{
Https: true,
}, nil
}
}
return res, nil
}

View file

@ -21,5 +21,7 @@ func (a *NginxRouter) InitRouter(Router *gin.RouterGroup) {
groupRouter.POST("/build", baseApi.BuildNginx) groupRouter.POST("/build", baseApi.BuildNginx)
groupRouter.POST("/modules/update", baseApi.UpdateNginxModule) groupRouter.POST("/modules/update", baseApi.UpdateNginxModule)
groupRouter.GET("/modules", baseApi.GetNginxModules) groupRouter.GET("/modules", baseApi.GetNginxModules)
groupRouter.POST("/https", baseApi.OperateDefaultHTTPs)
groupRouter.GET("/https", baseApi.GetDefaultHTTPsStatus)
} }
} }

View file

@ -50,4 +50,12 @@ export namespace Nginx {
export interface NginxModuleUpdate extends NginxModule { export interface NginxModuleUpdate extends NginxModule {
operate: string; operate: string;
} }
export interface NginxHttpsStatus {
https: boolean;
}
export interface NginxOperateReq {
operate: string;
}
} }

View file

@ -33,3 +33,11 @@ export const getNginxModules = () => {
export const updateNginxModule = (req: Nginx.NginxModuleUpdate) => { export const updateNginxModule = (req: Nginx.NginxModuleUpdate) => {
return http.post(`/openresty/modules/update`, req); return http.post(`/openresty/modules/update`, req);
}; };
export const getHttpsStatus = () => {
return http.get<Nginx.NginxHttpsStatus>(`/openresty/https`);
};
export const operateHttps = (req: Nginx.NginxOperateReq) => {
return http.post(`/openresty/https`, req);
};

View file

@ -2566,6 +2566,8 @@ const message = {
'Scripts to execute before compilation, usually for downloading module source code, installing dependencies, etc.', 'Scripts to execute before compilation, usually for downloading module source code, installing dependencies, etc.',
buildHelper: buildHelper:
'Click build after adding/modifying a module. OpenResty will automatically restart upon successful build.', 'Click build after adding/modifying a module. OpenResty will automatically restart upon successful build.',
defaultHttps: 'HTTPS Anti-tampering',
defaultHttpsHelper1: 'Enabling this can resolve HTTPS tampering issues.',
}, },
ssl: { ssl: {
create: 'Request', create: 'Request',

View file

@ -2466,6 +2466,24 @@ const message = {
clearProxyCache: '逆プロキシキャッシュをきれいにします', clearProxyCache: '逆プロキシキャッシュをきれいにします',
clearProxyCacheWarn: clearProxyCacheWarn:
'キャッシュで構成されたすべてのWebサイトが影響を受けOpenRestyが再起動されます続けたいですか', 'キャッシュで構成されたすべてのWebサイトが影響を受けOpenRestyが再起動されます続けたいですか',
create: 'モジュールを追加',
update: 'モジュールを編集',
params: 'パラメータ',
packages: 'パッケージ',
script: 'スクリプト',
module: 'モジュール',
build: 'ビルド',
buildWarn:
'OpenRestyのビルドには一定量のCPUとメモリを確保する必要があり時間がかかる場合がありますのでお待ちください',
mirrorUrl: 'ソフトウェアソース',
paramsHelper: '--add-module=/tmp/ngx_brotli',
packagesHelper: 'git,curl カンマ区切り',
scriptHelper:
'コンパイル前に実行するスクリプト通常はモジュールソースコードのダウンロード依存関係のインストールなど',
buildHelper:
'モジュールの追加/変更後にビルドをクリックしますビルドが成功するとOpenRestyは自動的に再起動します',
defaultHttps: 'HTTPS 改ざん防止',
defaultHttpsHelper1: 'これを有効にするとHTTPS 改ざん問題を解決できます',
}, },
ssl: { ssl: {
create: 'リクエスト', create: 'リクエスト',

View file

@ -2423,6 +2423,21 @@ const message = {
clearProxyCache: '리버스 프록시 캐시 삭제', clearProxyCache: '리버스 프록시 캐시 삭제',
clearProxyCacheWarn: clearProxyCacheWarn:
'캐시가 구성된 모든 웹사이트에 영향을 미치며 OpenResty 다시 시작됩니다. 계속하시겠습니까?', '캐시가 구성된 모든 웹사이트에 영향을 미치며 OpenResty 다시 시작됩니다. 계속하시겠습니까?',
create: '모듈 추가',
update: '모듈 편집',
params: '매개변수',
packages: '패키지',
script: '스크립트',
module: '모듈',
build: '빌드',
buildWarn: 'OpenResty 빌드는 CPU와 메모리의 일정량을 예약해야 하며, 시간이 오래 걸릴 있으니 기다려 주세요.',
mirrorUrl: '소프트웨어 소스',
paramsHelper: ': --add-module=/tmp/ngx_brotli',
packagesHelper: ': git,curl 쉼표로 구분',
scriptHelper: '컴파일 전에 실행할 스크립트, 일반적으로 모듈 소스 코드 다운로드, 종속성 설치 ',
buildHelper: '모듈 추가/수정 빌드를 클릭하세요. 빌드가 성공하면 OpenResty가 자동으로 재시작됩니다.',
defaultHttps: 'HTTPS 변조 방지',
defaultHttpsHelper1: '이를 활성화하면 HTTPS 변조 문제를 해결할 있습니다.',
}, },
ssl: { ssl: {
create: '요청', create: '요청',

View file

@ -2522,6 +2522,24 @@ const message = {
clearProxyCache: 'Bersihkan cache proksi terbalik', clearProxyCache: 'Bersihkan cache proksi terbalik',
clearProxyCacheWarn: clearProxyCacheWarn:
'Semua laman web yang dikonfigurasi dengan cache akan terjejas dan "OpenResty" akan dimulakan semula. Adakah anda mahu meneruskan?', 'Semua laman web yang dikonfigurasi dengan cache akan terjejas dan "OpenResty" akan dimulakan semula. Adakah anda mahu meneruskan?',
create: 'Tambah Modul',
update: 'Edit Modul',
params: 'Parameter',
packages: 'Pakej',
script: 'Script',
module: 'Modul',
build: 'Bina',
buildWarn:
'Membina OpenResty memerlukan menyediakan sejumlah CPU dan memori, dan prosesnya mengambil masa yang lama, sila bersabar.',
mirrorUrl: 'Sumber Perisian',
paramsHelper: 'Contoh: --add-module=/tmp/ngx_brotli',
packagesHelper: 'Contoh: git,curl dipisahkan oleh koma',
scriptHelper:
'Skrip yang dilaksanakan sebelum penyusunan, biasanya untuk memuat turun sumber kod modul, memasang kebergantungan, dll.',
buildHelper:
'Klik Bina selepas menambah/mengubah suai modul. Pembinaan yang berjaya akan memulakan semula OpenResty secara automatik.',
defaultHttps: 'HTTPS Anti-tampering',
defaultHttpsHelper1: 'Mengaktifkan ini dapat menyelesaikan masalah tampering HTTPS.',
}, },
ssl: { ssl: {
create: 'Permintaan', create: 'Permintaan',

View file

@ -2523,6 +2523,24 @@ const message = {
clearProxyCache: 'Clean reverse proxy cache', clearProxyCache: 'Clean reverse proxy cache',
clearProxyCacheWarn: clearProxyCacheWarn:
'All websites that have configured with cache will be affected and "OpenResty" will be restarted. Do you want to continue?', 'All websites that have configured with cache will be affected and "OpenResty" will be restarted. Do you want to continue?',
create: 'Criar Módulo',
update: 'Editar Módulo',
params: 'Parâmetros',
packages: 'Pacotes',
script: 'Script',
module: 'Módulo',
build: 'Construir',
buildWarn:
'Construir OpenResty requer a reserva de certa quantidade de CPU e memória, e o processo pode ser demorado, por favor, seja paciente.',
mirrorUrl: 'Fonte de Software',
paramsHelper: 'Por exemplo: --add-module=/tmp/ngx_brotli',
packagesHelper: 'Por exemplo: git,curl separados por vírgulas',
scriptHelper:
'Script a ser executado antes da compilação, geralmente para baixar o código-fonte do módulo, instalar dependências, etc.',
buildHelper:
'Clique em Construir após adicionar/modificar um módulo. Construção bem-sucedida reiniciará automaticamente o OpenResty.',
defaultHttps: 'HTTPS Anti-tampering',
defaultHttpsHelper1: 'A ativação desta opção pode resolver problemas de adulteração HTTPS.',
}, },
ssl: { ssl: {
create: 'Solicitar', create: 'Solicitar',

View file

@ -2519,6 +2519,24 @@ const message = {
clearProxyCache: 'Очистить кэш обратного прокси', clearProxyCache: 'Очистить кэш обратного прокси',
clearProxyCacheWarn: clearProxyCacheWarn:
'Это повлияет на все веб-сайты с настроенным кэшем и перезапустит "OpenResty". Хотите продолжить?', 'Это повлияет на все веб-сайты с настроенным кэшем и перезапустит "OpenResty". Хотите продолжить?',
create: 'Создать модуль',
update: 'Редактировать модуль',
params: 'Параметры',
packages: 'Пакеты',
script: 'Скрипт',
module: 'Модуль',
build: 'Сборка',
buildWarn:
'Сборка OpenResty требует резервирования определенного количества CPU и памяти, процесс может занять много времени, пожалуйста, подождите.',
mirrorUrl: 'Источник программного обеспечения',
paramsHelper: 'Например: --add-module=/tmp/ngx_brotli',
packagesHelper: 'Например: git,curl разделенные запятыми',
scriptHelper:
'Скрипт, выполняемый перед компиляцией, обычно для загрузки исходного кода модуля, установки зависимостей и т.д.',
buildHelper:
'Нажмите Сборка после добавления/изменения модуля. Успешная сборка автоматически перезапустит OpenResty.',
defaultHttps: 'HTTPS Анти-вмешательство',
defaultHttpsHelper1: 'Включение этого параметра может решить проблему вмешательства в HTTPS.',
}, },
ssl: { ssl: {
create: 'Запросить', create: 'Запросить',

View file

@ -2597,6 +2597,8 @@ const message = {
'Derlemeden önce çalıştırılacak betikler, genellikle modül kaynak kodunu indirmek, bağımlılıkları kurmak vb. için', 'Derlemeden önce çalıştırılacak betikler, genellikle modül kaynak kodunu indirmek, bağımlılıkları kurmak vb. için',
buildHelper: buildHelper:
'Modül ekledikten/düzenledikten sonra oluştura tıklayın. OpenResty, başarılı oluşturma üzerine otomatik olarak yeniden başlatılacaktır.', 'Modül ekledikten/düzenledikten sonra oluştura tıklayın. OpenResty, başarılı oluşturma üzerine otomatik olarak yeniden başlatılacaktır.',
defaultHttps: 'HTTPS Anti-sızdırma',
defaultHttpsHelper1: 'Bu özelliği etkinleştirerek HTTPS sızdırma sorunlarını çözebilirsiniz.',
}, },
ssl: { ssl: {
create: 'İstek', create: 'İstek',

View file

@ -2395,6 +2395,8 @@ const message = {
packagesHelper: '例如git,curl 以逗號分割', packagesHelper: '例如git,curl 以逗號分割',
scriptHelper: '編譯之前執行的腳本通常用於下載模組原始碼安裝依賴等', scriptHelper: '編譯之前執行的腳本通常用於下載模組原始碼安裝依賴等',
buildHelper: '添加/修改模組後點擊構建構建成功後會自動重啟 OpenResty', buildHelper: '添加/修改模組後點擊構建構建成功後會自動重啟 OpenResty',
defaultHttps: 'HTTPS 防竄站',
defaultHttpsHelper1: '開啟後可以解決 HTTPS 竄站問題',
}, },
ssl: { ssl: {
create: '申請證書', create: '申請證書',

View file

@ -2384,6 +2384,8 @@ const message = {
packagesHelper: '例如git,curl ,分割', packagesHelper: '例如git,curl ,分割',
scriptHelper: '编译之前执行的脚本一般为下载模块源码安装依赖等', scriptHelper: '编译之前执行的脚本一般为下载模块源码安装依赖等',
buildHelper: '添加/修改模块之后点击构建构建成功后会自动重启 OpenResty', buildHelper: '添加/修改模块之后点击构建构建成功后会自动重启 OpenResty',
defaultHttps: 'HTTPS 防窜站',
defaultHttpsHelper1: '开启后可以解决 HTTPS 窜站问题',
}, },
ssl: { ssl: {
create: '申请证书', create: '申请证书',

View file

@ -26,6 +26,9 @@
<el-button type="primary" :plain="activeName !== '5'" @click="changeTab('5')"> <el-button type="primary" :plain="activeName !== '5'" @click="changeTab('5')">
{{ $t('runtime.module') }} {{ $t('runtime.module') }}
</el-button> </el-button>
<el-button type="primary" :plain="activeName !== '6'" @click="changeTab('6')">
{{ $t('website.other') }}
</el-button>
</template> </template>
<template #main> <template #main>
<Status v-if="activeName === '1'" :status="status" /> <Status v-if="activeName === '1'" :status="status" />
@ -33,6 +36,7 @@
<NginxPer v-if="activeName === '3'" /> <NginxPer v-if="activeName === '3'" />
<ContainerLog v-if="activeName === '4'" :container="containerName" :highlightDiff="350" /> <ContainerLog v-if="activeName === '4'" :container="containerName" :highlightDiff="350" />
<Module v-if="activeName === '5'" /> <Module v-if="activeName === '5'" />
<Other v-if="activeName === '6'" />
</template> </template>
</LayoutContent> </LayoutContent>
</template> </template>
@ -44,6 +48,7 @@ import ContainerLog from '@/components/log/container/index.vue';
import NginxPer from './performance/index.vue'; import NginxPer from './performance/index.vue';
import Status from './status/index.vue'; import Status from './status/index.vue';
import Module from './module/index.vue'; import Module from './module/index.vue';
import Other from './other/index.vue';
const activeName = ref('1'); const activeName = ref('1');

View file

@ -0,0 +1,43 @@
<template>
<div v-if="showPage" v-loading="loading">
<el-text>{{ $t('nginx.defaultHttps') }}</el-text>
<el-switch class="ml-2" v-model="enable" @change="changeStatus"></el-switch>
<div>
<el-text type="info" size="small">{{ $t('nginx.defaultHttpsHelper1') }}</el-text>
</div>
</div>
</template>
<script lang="ts" setup>
import { getHttpsStatus, operateHttps } from '@/api/modules/nginx';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
const showPage = ref(false);
const enable = ref(false);
const loading = ref(false);
const getStatus = async () => {
try {
const res = await getHttpsStatus();
enable.value = res.data.https;
showPage.value = true;
} catch {}
};
const changeStatus = async () => {
loading.value = true;
try {
await operateHttps({ operate: enable.value ? 'enable' : 'disable' });
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
} catch (error) {
enable.value = !enable.value;
} finally {
loading.value = false;
}
};
onMounted(() => {
getStatus();
});
</script>