feat: add LogDirWhiteList option

This commit is contained in:
Jacky 2024-10-08 12:32:57 +08:00
parent 27f751671b
commit 6d09ff5919
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
10 changed files with 185 additions and 63 deletions

1
.gitignore vendored
View file

@ -11,3 +11,4 @@ nginx-ui
resources/development/nginx
app/.env
app/.status_hash
casdoor.pub

View file

@ -2,9 +2,13 @@ package nginx
import (
"encoding/json"
"fmt"
"github.com/0xJacky/Nginx-UI/api"
"github.com/0xJacky/Nginx-UI/internal/cache"
"github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/0xJacky/Nginx-UI/internal/logger"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/hpcloud/tail"
@ -30,6 +34,7 @@ type controlStruct struct {
type nginxLogPageResp struct {
Content string `json:"content"`
Page int64 `json:"page"`
Error string `json:"error,omitempty"`
}
func GetNginxLogPage(c *gin.Context) {
@ -46,6 +51,9 @@ func GetNginxLogPage(c *gin.Context) {
logPath, err := getLogPath(&control)
if err != nil {
c.JSON(http.StatusInternalServerError, nginxLogPageResp{
Error: err.Error(),
})
logger.Error(err)
return
}
@ -53,13 +61,17 @@ func GetNginxLogPage(c *gin.Context) {
logFileStat, err := os.Stat(logPath)
if err != nil {
c.JSON(http.StatusOK, nginxLogPageResp{})
c.JSON(http.StatusInternalServerError, nginxLogPageResp{
Error: err.Error(),
})
logger.Error(err)
return
}
if !logFileStat.Mode().IsRegular() {
c.JSON(http.StatusOK, nginxLogPageResp{})
c.JSON(http.StatusInternalServerError, nginxLogPageResp{
Error: "log file is not regular file",
})
logger.Error("log file is not regular file:", logPath)
return
}
@ -67,7 +79,9 @@ func GetNginxLogPage(c *gin.Context) {
f, err := os.Open(logPath)
if err != nil {
c.JSON(http.StatusOK, nginxLogPageResp{})
c.JSON(http.StatusInternalServerError, nginxLogPageResp{
Error: err.Error(),
})
logger.Error(err)
return
}
@ -90,7 +104,9 @@ func GetNginxLogPage(c *gin.Context) {
// seek
_, err = f.Seek(offset, io.SeekStart)
if err != nil && err != io.EOF {
c.JSON(http.StatusOK, nginxLogPageResp{})
c.JSON(http.StatusInternalServerError, nginxLogPageResp{
Error: err.Error(),
})
logger.Error(err)
return
}
@ -98,7 +114,9 @@ func GetNginxLogPage(c *gin.Context) {
n, err := f.Read(buf)
if err != nil && err != io.EOF {
c.JSON(http.StatusOK, nginxLogPageResp{})
c.JSON(http.StatusInternalServerError, nginxLogPageResp{
Error: err.Error(),
})
logger.Error(err)
return
}
@ -109,7 +127,30 @@ func GetNginxLogPage(c *gin.Context) {
})
}
// isLogPathUnderWhiteList checks if the log path is under one of the paths in LogDirWhiteList
func isLogPathUnderWhiteList(path string) bool {
cacheKey := fmt.Sprintf("isLogPathUnderWhiteList:%s", path)
res, ok := cache.Get(cacheKey)
// no cache, check it
if !ok {
for _, whitePath := range settings.NginxSettings.LogDirWhiteList {
if helper.IsUnderDirectory(path, whitePath) {
cache.Set(cacheKey, true, 0)
return true
}
}
return false
}
return res.(bool)
}
func getLogPath(control *controlStruct) (logPath string, err error) {
if len(settings.NginxSettings.LogDirWhiteList) == 0 {
err = errors.New("The settings.NginxSettings.LogDirWhiteList has not been configured. " +
"For security reasons, please configure a whitelist of log directories. " +
"Please visit https://nginxui.com/guide/config-nginx.html for more information.")
return
}
switch control.Type {
case "site":
var config *nginx.NgxConfig
@ -172,6 +213,11 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
logPath = path
}
// check if logPath is under one of the paths in LogDirWhiteList
if !isLogPathUnderWhiteList(logPath) {
err = errors.New("The log path is not under the paths in LogDirWhiteList.")
return "", err
}
return
}

View file

@ -59,6 +59,9 @@ function init() {
nginx_log.page(0, control).then(r => {
page.value = r.page - 1
addLog(r.content)
openWs()
}).catch(e => {
addLog(e.error)
})
}
@ -68,11 +71,10 @@ function clearLog() {
onMounted(() => {
init()
openWs()
})
onUnmounted(() => {
websocket.close()
websocket?.close()
})
watch(auto_refresh, value => {

29
casdoor.pub Normal file
View file

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE3TCCAsWgAwIBAgIDAeJAMA0GCSqGSIb3DQEBCwUAMCgxDjAMBgNVBAoTBWFk
bWluMRYwFAYDVQQDEw1jZXJ0LWJ1aWx0LWluMB4XDTI0MDcyOTAzMDUzM1oXDTQ0
MDcyOTAzMDUzM1owKDEOMAwGA1UEChMFYWRtaW4xFjAUBgNVBAMTDWNlcnQtYnVp
bHQtaW4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh0c2zvM21NDNi
xZmSQVPOtckiH/K80mHQ99e+xzdGrZugaw00tyTOMVRot+Bv1cggcJXmFcVaa9Da
siIcIQ6jT3w7mINsrErYu4nz9tELd4BZUM6tytN+khVqo73p/NbRsnmX8ykMyrgx
YCBknNoSxh7glLSmKcj4uQ12dYakRPr0QNnDwU7fpPfB7N4O88yXWpbqWaABwqBx
S6+tYUp9Wx74mH8c917w5xXx8EI5eC+dJOAeVXrbzqD7OdC+uo8m7f06HAX1vRE/
MQS0BDx8tDGtqOvdJbVxdnS6MQ2E3vmuusFseDcKVgqTF/7b76y5uNyBBux5zfpt
G1O3bquMlQ8mSedK8BBcbI79gXYJRWazMBXvAdacaV93dF1s0EEjYXiBManAci2d
lH8zzs4TNpE0t3adYiPbGPW1F6Q+HaV52MDVpFwG9Ld0kJmKhkvoWiSXl58Db3VN
Ef8Jjo2xF+5o9685CQ2o9L0RalcHxMxy1+6wdKMSp7PReYpIiEgmkAhUsKePOVmr
JwL46/4EulcXrh+ASjobmknHzdBQEK+MHapb/XWewX4mzq777gPmP8RdILTHsc1m
/hbR8uW9iTfo9LQFvXwnIPVfX0wzFXSZzSg/zLb2tN5D7VlDenUAdCDT1zNOfz5m
9vLYwfo5GzXIkp2py0G40vrZlv7C9wIDAQABoxAwDjAMBgNVHRMBAf8EAjAAMA0G
CSqGSIb3DQEBCwUAA4ICAQBp1Bx+mShpumQiVb2hv0amSzAKADyYIX3Xcef68rPu
Eb+7HSCmQf4yyI9eU1TyvQCLbjum3U3OhDWwAiRvxOwj0oO/Q+dOUEZxTjbL9UF3
4LIrUUBMRhRgy3wK73qy3o8hAcRtQyexUW7eoFS+7L++6XQOvMkYAtLO0DQCHeKG
loiQa5RuWbzQDdP7810DLvNF8IMA8t9KKaKGybYze9WTzRUMTDbXby8pVs8DG7JI
zIW6neEmtsVbxufk/nthG1b83/yZxe0StL42xI7f4xgguhkfd68E4lpf/gp91EAM
K6MbTmCqkB68c0wOSXpWYkte7EvXTTmMSKf6FnMgOtqdxqYYMknLk4ZdI7tMqS31
rpb9XxjBgXFbB18oOSDbW64KPMjE7vuOx+o32BTHKsUWxOiDc8+0ELrbhG2Bm1Gj
CYkx9bq5iTLDwtZZlPoA8O/T0TJzBTtC/tlEdpHSkkLoEaWsx0nT9ipRWck1Kj59
NGJkArbrpq9Ee8tWJKqTN/pv0X8r+MxowIY2dKvwweokXb7R6k9nfXyGw8ji22Hv
H4iibv9FEyVFQ16HPR6fIKg9yE9u0223UhJZEwohA4DylCxpmI/YSXbUmzQJwjBP
27qvT4Y07xsdNqIbkwhb5yEQB5huivITD+SBwI5NwDfUeY6eF/BEHpRq+Uy3itx0
SA==
-----END CERTIFICATE-----

View file

@ -33,6 +33,20 @@ In Nginx UI v2, we parse the output of the `nginx -V` command to get the default
If you need to set a different path, you can use this option.
:::
### LogDirWhiteList
- Type: `[]string`
- Version`>= v2.0.0-beta.36`
- Example: `/var/log/nginx,/var/log/sites`
This option is used to set the whitelist of directories for the Nginx logs viewer in Nginx UI.
::: warning Warning
For security reasons, you must specify the directories where the logs are stored.
Only logs within these directories can be viewed online.
:::
## Service Monitoring and Control
In this section, we will introduce configuration options in Nginx UI for monitoring and controlling Nginx services.

View file

@ -7,11 +7,11 @@
"docs:preview": "vitepress preview"
},
"dependencies": {
"vitepress": "^1.3.4",
"vitepress": "^1.4.0",
"vue": "^3.5.11"
},
"devDependencies": {
"@types/node": "^22.7.4",
"@types/node": "^22.7.5",
"less": "^4.2.0"
},
"license": "AGPL-3.0",

95
docs/pnpm-lock.yaml generated
View file

@ -9,15 +9,15 @@ importers:
.:
dependencies:
vitepress:
specifier: ^1.3.4
version: 1.3.4(@algolia/client-search@4.24.0)(@types/node@22.7.4)(less@4.2.0)(postcss@8.4.47)(search-insights@2.13.0)
specifier: ^1.4.0
version: 1.4.0(@algolia/client-search@4.24.0)(@types/node@22.7.5)(less@4.2.0)(postcss@8.4.47)(search-insights@2.13.0)
vue:
specifier: ^3.5.11
version: 3.5.11
devDependencies:
'@types/node':
specifier: ^22.7.4
version: 22.7.4
specifier: ^22.7.5
version: 22.7.5
less:
specifier: ^4.2.0
version: 4.2.0
@ -350,20 +350,20 @@ packages:
cpu: [x64]
os: [win32]
'@shikijs/core@1.21.1':
resolution: {integrity: sha512-scBQo4V4O4WZLEDg11e75UPmXoCMq4Ya2A16U6efi/aTiR4o7T/GMNWZs2rq1U8dEvFKGxJZxiUy+tXgmr/4vw==}
'@shikijs/core@1.22.0':
resolution: {integrity: sha512-S8sMe4q71TJAW+qG93s5VaiihujRK6rqDFqBnxqvga/3LvqHEnxqBIOPkt//IdXVtHkQWKu4nOQNk0uBGicU7Q==}
'@shikijs/engine-javascript@1.21.1':
resolution: {integrity: sha512-29EG4KYKlAona8yikEx8uoKbK7N2YoXUO26LS1GOIxpMMIAlQS9UFONg95lkGmIfp1rRcvCvSpYYIJ/blsQxvg==}
'@shikijs/engine-javascript@1.22.0':
resolution: {integrity: sha512-AeEtF4Gcck2dwBqCFUKYfsCq0s+eEbCEbkUuFou53NZ0sTGnJnJ/05KHQFZxpii5HMXbocV9URYVowOP2wH5kw==}
'@shikijs/engine-oniguruma@1.21.1':
resolution: {integrity: sha512-PvfEtXCDbQZc9ud0SC0bPiuMbul44Cv0Ky2go4SsvVkYAAKYJsMe/Hx7nxThW8yS0r+w8USa0WfOtQKsD9DU9A==}
'@shikijs/engine-oniguruma@1.22.0':
resolution: {integrity: sha512-5iBVjhu/DYs1HB0BKsRRFipRrD7rqjxlWTj4F2Pf+nQSPqc3kcyqFFeZXnBMzDf0HdqaFVvhDRAGiYNvyLP+Mw==}
'@shikijs/transformers@1.21.1':
resolution: {integrity: sha512-97csTb0Gv8eLbglPDhNZTuAI9eCXOujNqD4qK6H0cjFNK+NBhkRIU02RgmYHZ7yNyLary6cEzY6WmUWb+al3MQ==}
'@shikijs/transformers@1.22.0':
resolution: {integrity: sha512-k7iMOYuGQA62KwAuJOQBgH2IQb5vP8uiB3lMvAMGUgAMMurePOx3Z7oNqJdcpxqZP6I9cc7nc4DNqSKduCxmdg==}
'@shikijs/types@1.21.1':
resolution: {integrity: sha512-yLuTJTCHmYznerJ0nxF+f2rBKHQf2FMAd08QL/3du2xNBy/7yQ8CjuKN4Zc+Pk0vfIFzdBoxdzvEXE4JtXoR4Q==}
'@shikijs/types@1.22.0':
resolution: {integrity: sha512-Fw/Nr7FGFhlQqHfxzZY8Cwtwk5E9nKDUgeLjZgt3UuhcM3yJR9xj3ZGNravZZok8XmEZMiYkSMTPlPkULB8nww==}
'@shikijs/vscode-textmate@9.3.0':
resolution: {integrity: sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==}
@ -386,8 +386,8 @@ packages:
'@types/mdurl@2.0.0':
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
'@types/node@22.7.4':
resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==}
'@types/node@22.7.5':
resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==}
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@ -691,8 +691,8 @@ packages:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
shiki@1.21.1:
resolution: {integrity: sha512-jSOKRHyQJxGOW3kJflmwzHJbp/kjg6hP8LYuVbCPw5oyX+fSNNoCywvcCD3w9eHbj2rvNljt7YMa5BP5Xi+nHg==}
shiki@1.22.0:
resolution: {integrity: sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
@ -784,8 +784,8 @@ packages:
terser:
optional: true
vitepress@1.3.4:
resolution: {integrity: sha512-I1/F6OW1xl3kW4PaIMC6snxjWgf3qfziq2aqsDoFc/Gt41WbcRv++z8zjw8qGRIJ+I4bUW7ZcKFDHHN/jkH9DQ==}
vitepress@1.4.0:
resolution: {integrity: sha512-JXCv4EsKTDyAFb6C/UjZr7nsGAzZ6mafVk2rx7rG5o8N+B/4QstIk+iEOe/9dKoU6V624UIC6g1pZ+K63rxhlw==}
hasBin: true
peerDependencies:
markdown-it-mathjax3: ^4
@ -1081,31 +1081,31 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.24.0':
optional: true
'@shikijs/core@1.21.1':
'@shikijs/core@1.22.0':
dependencies:
'@shikijs/engine-javascript': 1.21.1
'@shikijs/engine-oniguruma': 1.21.1
'@shikijs/types': 1.21.1
'@shikijs/engine-javascript': 1.22.0
'@shikijs/engine-oniguruma': 1.22.0
'@shikijs/types': 1.22.0
'@shikijs/vscode-textmate': 9.3.0
'@types/hast': 3.0.4
hast-util-to-html: 9.0.3
'@shikijs/engine-javascript@1.21.1':
'@shikijs/engine-javascript@1.22.0':
dependencies:
'@shikijs/types': 1.21.1
'@shikijs/types': 1.22.0
'@shikijs/vscode-textmate': 9.3.0
oniguruma-to-js: 0.4.3
'@shikijs/engine-oniguruma@1.21.1':
'@shikijs/engine-oniguruma@1.22.0':
dependencies:
'@shikijs/types': 1.21.1
'@shikijs/types': 1.22.0
'@shikijs/vscode-textmate': 9.3.0
'@shikijs/transformers@1.21.1':
'@shikijs/transformers@1.22.0':
dependencies:
shiki: 1.21.1
shiki: 1.22.0
'@shikijs/types@1.21.1':
'@shikijs/types@1.22.0':
dependencies:
'@shikijs/vscode-textmate': 9.3.0
'@types/hast': 3.0.4
@ -1131,7 +1131,7 @@ snapshots:
'@types/mdurl@2.0.0': {}
'@types/node@22.7.4':
'@types/node@22.7.5':
dependencies:
undici-types: 6.19.8
@ -1141,9 +1141,9 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@22.7.4)(less@4.2.0))(vue@3.5.11)':
'@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@22.7.5)(less@4.2.0))(vue@3.5.11)':
dependencies:
vite: 5.4.8(@types/node@22.7.4)(less@4.2.0)
vite: 5.4.8(@types/node@22.7.5)(less@4.2.0)
vue: 3.5.11
'@vue/compiler-core@3.5.11':
@ -1504,12 +1504,12 @@ snapshots:
semver@5.7.2:
optional: true
shiki@1.21.1:
shiki@1.22.0:
dependencies:
'@shikijs/core': 1.21.1
'@shikijs/engine-javascript': 1.21.1
'@shikijs/engine-oniguruma': 1.21.1
'@shikijs/types': 1.21.1
'@shikijs/core': 1.22.0
'@shikijs/engine-javascript': 1.22.0
'@shikijs/engine-oniguruma': 1.22.0
'@shikijs/types': 1.22.0
'@shikijs/vscode-textmate': 9.3.0
'@types/hast': 3.0.4
@ -1574,24 +1574,25 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
vite@5.4.8(@types/node@22.7.4)(less@4.2.0):
vite@5.4.8(@types/node@22.7.5)(less@4.2.0):
dependencies:
esbuild: 0.21.5
postcss: 8.4.47
rollup: 4.24.0
optionalDependencies:
'@types/node': 22.7.4
'@types/node': 22.7.5
fsevents: 2.3.3
less: 4.2.0
vitepress@1.3.4(@algolia/client-search@4.24.0)(@types/node@22.7.4)(less@4.2.0)(postcss@8.4.47)(search-insights@2.13.0):
vitepress@1.4.0(@algolia/client-search@4.24.0)(@types/node@22.7.5)(less@4.2.0)(postcss@8.4.47)(search-insights@2.13.0):
dependencies:
'@docsearch/css': 3.6.2
'@docsearch/js': 3.6.2(@algolia/client-search@4.24.0)(search-insights@2.13.0)
'@shikijs/core': 1.21.1
'@shikijs/transformers': 1.21.1
'@shikijs/core': 1.22.0
'@shikijs/transformers': 1.22.0
'@shikijs/types': 1.22.0
'@types/markdown-it': 14.1.2
'@vitejs/plugin-vue': 5.1.4(vite@5.4.8(@types/node@22.7.4)(less@4.2.0))(vue@3.5.11)
'@vitejs/plugin-vue': 5.1.4(vite@5.4.8(@types/node@22.7.5)(less@4.2.0))(vue@3.5.11)
'@vue/devtools-api': 7.4.6
'@vue/shared': 3.5.11
'@vueuse/core': 11.1.0(vue@3.5.11)
@ -1599,8 +1600,8 @@ snapshots:
focus-trap: 7.6.0
mark.js: 8.11.1
minisearch: 7.1.0
shiki: 1.21.1
vite: 5.4.8(@types/node@22.7.4)(less@4.2.0)
shiki: 1.22.0
vite: 5.4.8(@types/node@22.7.5)(less@4.2.0)
vue: 3.5.11
optionalDependencies:
postcss: 8.4.47

View file

@ -34,6 +34,20 @@ Nginx 日志对于监控、排查问题和维护您的 Web 服务器至关重要
如果您需要设置不同的路径,您可以使用此选项。
:::
### LogDirWhiteList
- 类型:`[]string`
- 版本:`>= v2.0.0-beta.36`
- 示例:`/var/log/nginx,/var/log/sites`
此选项用于为 Nginx UI 设置日志查看器的目录白名单。
::: warning 警告
出于安全原因,您必须指定存储日志的目录。
只有这些目录中的日志可以在线查看。
:::
## 服务监控与控制
在本节中,我们将会介绍 Nginx UI 中关于 Nginx 服务的监控和控制命令的配置选项。

View file

@ -33,6 +33,20 @@ Nginx 日誌對於監控、排查問題和維護您的 Web 伺服器至關重要
如果您需要設置不同的路徑,您可以使用此選項。
:::
### LogDirWhiteList
- 類型:`[]string`
- 版本:`>= v2.0.0-beta.36`
- 示例:`/var/log/nginx,/var/log/sites`
此選項用於為 Nginx UI 設置日誌查看器的目錄白名單。
::: warning 警告
出於安全原因,您必須指定存儲日誌的目錄。
只有這些目錄中的日誌可以在線查看。
:::
## 服務監控與控制
在本節中,我們將會介紹 Nginx UI 中關於 Nginx 服務的監控和控制命令的配置選項。

View file

@ -3,6 +3,7 @@ package settings
type Nginx struct {
AccessLogPath string `json:"access_log_path" protected:"true"`
ErrorLogPath string `json:"error_log_path" protected:"true"`
LogDirWhiteList []string `json:"log_dir_white_list" protected:"true"`
ConfigDir string `json:"config_dir" protected:"true"`
PIDPath string `json:"pid_path" protected:"true"`
TestConfigCmd string `json:"test_config_cmd" protected:"true"`