feat: add openBaseDir for php website config (#8866)

This commit is contained in:
CityFun 2025-05-28 18:20:22 +08:00 committed by GitHub
parent a4d63434cb
commit 71d92c30e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 127 additions and 22 deletions

View file

@ -1101,3 +1101,23 @@ func (b *BaseApi) ClearProxyCache(c *gin.Context) {
}
helper.Success(c)
}
// @Tags Website
// @Summary Operate Cross Site Access
// @Accept json
// @Param request body request.CrossSiteAccessOp true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/crosssite [post]
func (b *BaseApi) OperateCrossSiteAccess(c *gin.Context) {
var req request.CrossSiteAccessOp
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.OperateCrossSiteAccess(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}

View file

@ -289,3 +289,8 @@ type WebsiteProxyDel struct {
ID uint `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
}
type CrossSiteAccessOp struct {
WebsiteID uint `json:"websiteID" validate:"required"`
Operation string `json:"operation" validate:"required,oneof=Enable Disable"`
}

View file

@ -15,6 +15,7 @@ type WebsiteDTO struct {
RuntimeName string `json:"runtimeName"`
RuntimeType string `json:"runtimeType"`
SiteDir string `json:"siteDir"`
OpenBaseDir bool `json:"openBaseDir"`
}
type WebsiteRes struct {

View file

@ -124,6 +124,8 @@ type IWebsiteService interface {
GetWebsiteResource(websiteID uint) ([]response.Resource, error)
ListDatabases() ([]response.Database, error)
ChangeDatabase(req request.ChangeDatabase) error
OperateCrossSiteAccess(req request.CrossSiteAccessOp) error
}
func NewIWebsiteService() IWebsiteService {
@ -428,7 +430,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
return err
}
if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceAppstore {
createPHPConfig(website)
createOpenBasedirConfig(website)
}
}
tx, ctx := helper.GetTxAndContext()
@ -573,6 +575,9 @@ func (w WebsiteService) GetWebsite(id uint) (response.WebsiteDTO, error) {
}
res.RuntimeType = runtime.Type
res.RuntimeName = runtime.Name
if runtime.Type == constant.RuntimePHP {
res.OpenBaseDir = files.NewFileOp().Stat(path.Join(GetSitePath(website, SiteIndexDir), ".user.ini"))
}
}
return res, nil
}
@ -3278,3 +3283,18 @@ func (w WebsiteService) ChangeDatabase(req request.ChangeDatabase) error {
website.DbType = req.DatabaseType
return websiteRepo.Save(context.Background(), &website)
}
func (w WebsiteService) OperateCrossSiteAccess(req request.CrossSiteAccessOp) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
if req.Operation == constant.StatusEnable {
createOpenBasedirConfig(&website)
}
if req.Operation == constant.StatusDisable {
fileOp := files.NewFileOp()
return fileOp.DeleteFile(path.Join(GetSitePath(website, SiteIndexDir), ".user.ini"))
}
return nil
}

View file

@ -349,11 +349,11 @@ func createAllWebsitesWAFConfig(websites []model.Website) error {
return nil
}
func createPHPConfig(website *model.Website) {
func createOpenBasedirConfig(website *model.Website) {
fileOp := files.NewFileOp()
userIniPath := path.Join(GetSitePath(*website, SiteIndexDir), ".user.ini")
_ = fileOp.CreateFile(userIniPath)
_ = fileOp.SaveFile(userIniPath, fmt.Sprintf("open_basedir=/www/sites/%s/index", website.Alias), 0644)
_ = fileOp.SaveFile(userIniPath, fmt.Sprintf("open_basedir=/www/sites/%s/index:/tmp/", website.Alias), 0644)
}
func createWafConfig(website *model.Website, domains []model.WebsiteDomain) error {

View file

@ -84,5 +84,7 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
websiteRouter.GET("/resource/:id", baseApi.GetWebsiteResource)
websiteRouter.GET("/databases", baseApi.GetWebsiteDatabase)
websiteRouter.POST("/databases", baseApi.ChangeWebsiteDatabase)
websiteRouter.POST("/crosssite", baseApi.OperateCrossSiteAccess)
}
}

View file

@ -36,6 +36,7 @@ export namespace Website {
appName: string;
runtimeName: string;
runtimeType: string;
openBaseDir: boolean;
}
export interface WebsiteRes extends CommonModel {
protocol: string;
@ -646,4 +647,9 @@ export namespace Website {
databaseID: number;
databaseType: string;
}
export interface CrossSiteAccessOp {
websiteID: number;
operation: string;
}
}

View file

@ -343,3 +343,7 @@ export const operateCustomRewrite = (req: Website.CustomRewirte) => {
export const listCustomRewrite = () => {
return http.get<string[]>(`/websites/rewrite/custom`);
};
export const operateCrossSiteAccess = (req: Website.CrossSiteAccessOp) => {
return http.post(`/websites/crosssite`, req);
};

View file

@ -2476,6 +2476,9 @@ const message = {
useProxy: 'Use Proxy',
useProxyHelper: 'Use the proxy server address in the panel settings',
westCN: 'West Digital',
openBaseDir: 'Prevent Cross-Site Attacks',
openBaseDirHelper:
'open_basedir is used to restrict the PHP file access path, which helps prevent cross-site access and enhance security',
},
php: {
short_open_tag: 'Short tag support',

View file

@ -2382,6 +2382,9 @@ const message = {
useProxy: 'プロキシを使用',
useProxyHelper: 'パネル設定のプロキシサーバーアドレスを使用',
westCN: '西部デジタル',
openBaseDir: 'クロスサイト攻撃を防ぐ',
openBaseDirHelper:
'open_basedir PHP ファイルのアクセスパスを制限しクロスサイトアクセスを防ぎセキュリティを向上させるために使用されます',
},
php: {
short_open_tag: '短いタグサポート',

View file

@ -2341,6 +2341,9 @@ const message = {
useProxy: '프록시 사용',
useProxyHelper: '패널 설정의 프록시 서버 주소 사용',
westCN: '서부 디지털',
openBaseDir: '사이트 공격 방지',
openBaseDirHelper:
'open_basedir는 PHP 파일 액세스 경로를 제한하여 사이트 액세스를 방지하고 보안을 향상시키는 사용됩니다',
},
php: {
short_open_tag: '짧은 태그 지원',

View file

@ -2436,6 +2436,9 @@ const message = {
useProxy: 'Gunakan Proksi',
useProxyHelper: 'Gunakan alamat pelayan proksi dalam tetapan panel',
westCN: 'West Digital',
openBaseDir: 'Pencegahan Serangan Lintas Situs',
openBaseDirHelper:
'open_basedir digunakan untuk membatasi jalur akses file PHP, yang membantu mencegah akses lintas situs dan meningkatkan keamanan',
},
php: {
short_open_tag: 'Sokongan tag pendek',

View file

@ -2433,6 +2433,9 @@ const message = {
useProxy: 'Usar Proxy',
useProxyHelper: 'Usar o endereço do servidor proxy nas configurações do painel',
westCN: 'West Digital',
openBaseDir: 'Prevenir Ataques entre Sites',
openBaseDirHelper:
'open_basedir é usado para restringir o caminho de acesso a arquivos PHP, ajudando a prevenir acesso entre sites e aumentar a segurança',
},
php: {
short_open_tag: 'Suporte para short tags',

View file

@ -2432,6 +2432,9 @@ const message = {
useProxy: 'Использовать прокси',
useProxyHelper: 'Использовать адрес прокси-сервера в настройках панели',
westCN: 'Западный цифровой',
openBaseDir: 'Предотвращение межсайтовых атак',
openBaseDirHelper:
'open_basedir используется для ограничения пути доступа к файлам PHP, что помогает предотвратить межсайтовый доступ и повысить безопасность',
},
php: {
short_open_tag: 'Поддержка коротких тегов',

View file

@ -2296,6 +2296,8 @@ const message = {
useProxy: '使用代理',
useProxyHelper: '使用面板設置中的代理服務器地址',
westCN: '西部數碼',
openBaseDir: '防跨站攻擊',
openBaseDirHelper: 'open_basedir 用於限制 PHP 文件訪問路徑有助於防止跨站訪問和提升安全性',
},
php: {
short_open_tag: '短標簽支持',

View file

@ -2286,6 +2286,8 @@ const message = {
useProxy: '使用代理',
useProxyHelper: '使用面板设置中的代理服务器地址',
westCN: '西部数码',
openBaseDir: '防跨站攻击',
openBaseDirHelper: 'open_basedir 用于限制 PHP 文件访问路径有助于防止跨站访问和提升安全性',
},
php: {
short_open_tag: '短标签支持',

View file

@ -2,28 +2,39 @@
<div v-loading="loading">
<el-row>
<el-col :xs="20" :sm="12" :md="10" :lg="10" :xl="8">
<el-form label-position="right" label-width="80px">
<el-form label-position="right" label-width="120px">
<el-form-item>
<div v-if="website.type === 'static'">
<el-text type="info">{{ $t('website.staticChangePHPHelper') }}</el-text>
</div>
<el-text type="info" v-if="website.type === 'static'">
{{ $t('website.staticChangePHPHelper') }}
</el-text>
</el-form-item>
<el-form-item :label="$t('website.changeVersion')">
<el-select v-model="versionReq.runtimeID" class="w-full">
<el-option :key="-1" :label="$t('website.static')" :value="0"></el-option>
<el-option
v-for="(item, index) in versions"
:key="index"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<el-row :gutter="20">
<el-col :span="20">
<el-select v-model="versionReq.runtimeID" class="p-w-200">
<el-option :key="-1" :label="$t('website.static')" :value="0"></el-option>
<el-option
v-for="(item, index) in versions"
:key="index"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-col>
<el-col :span="4">
<el-button
type="primary"
@click="submit()"
:disabled="versionReq.runtimeID === oldRuntimeID"
>
{{ $t('commons.button.save') }}
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit()" :disabled="versionReq.runtimeID === oldRuntimeID">
{{ $t('commons.button.save') }}
</el-button>
<el-form-item :label="$t('website.openBaseDir')">
<el-switch v-model="openBaseDir" @change="operateCrossSite"></el-switch>
<span class="input-help">{{ $t('website.openBaseDirHelper') }}</span>
</el-form-item>
</el-form>
</el-col>
@ -36,7 +47,7 @@ import { SearchRuntimes } from '@/api/modules/runtime';
import { onMounted, reactive, ref } from 'vue';
import { Runtime } from '@/api/interface/runtime';
import { Website } from '@/api/interface/website';
import { changePHPVersion, getWebsite } from '@/api/modules/website';
import { changePHPVersion, getWebsite, operateCrossSiteAccess } from '@/api/modules/website';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
const props = defineProps({
@ -56,7 +67,9 @@ const loading = ref(false);
const oldRuntimeID = ref(0);
const website = ref({
type: '',
openBaseDir: false,
});
const openBaseDir = ref(false);
const getRuntimes = async () => {
try {
@ -95,6 +108,18 @@ const getWebsiteDetail = async () => {
versionReq.runtimeID = res.data.runtimeID;
oldRuntimeID.value = res.data.runtimeID;
website.value = res.data;
openBaseDir.value = res.data.openBaseDir || false;
};
const operateCrossSite = async () => {
try {
await operateCrossSiteAccess({
websiteID: props.id,
operation: openBaseDir.value ? 'Enable' : 'Disable',
});
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
getWebsiteDetail();
} catch (error) {}
};
onMounted(() => {