admin/home: proxy warning will last only for one day after last triggering request

Massimo Melina committed Apr 9, 2023 at 20:27 UTC e9df9472d8cb04c5a1be92831988621e4c87db00
3 files changed +20 -15
admin/src/HomePage.ts
+2 -2
@@ -22,7 +22,7 @@ export default function HomePage() {
22 const { data: status, reload: reloadStatus, element: statusEl } = useApiEx<Dict<ServerStatus>>('get_status')
23 const { data: vfs } = useApiEx<{ root?: VfsNode }>('get_vfs')
24 const [account] = useApi<Account>(username && 'get_account')
25 - const { data: cfg, reload: reloadCfg } = useApiEx('get_config', { only: ['https_port', 'cert', 'private_key', 'proxies', 'ignore_proxies'] })
25 + const { data: cfg, reload: reloadCfg } = useApiEx('get_config', { only: ['https_port', 'cert', 'private_key', 'proxies'] })
26 const { list: plugins } = useApiList('get_plugins')
27 if (statusEl || !status)
28 return statusEl
@@ -98,5 +98,5 @@ function cfgLink(text=`Options page`) {
98 }
99
100 export function proxyWarning(cfg: any, status: any) {
101 - return cfg && !cfg.proxies && !cfg.ignore_proxies && status?.proxyDetected
101 + return cfg && !cfg.proxies && status?.proxyDetected
102 }
src/adminApis.ts
+1 -1
@@ -165,7 +165,7 @@ export const favicon = defineConfig('favicon', '')
165 export const title = defineConfig('title', "File server")
166
167 export function ctxAdminAccess(ctx: Koa.Context) {
168 - return !ctx.state.proxiedFor // we consider localhost_admin only if no proxy is detected
168 + return !ctx.ips.length // we consider localhost_admin only if no proxy is being usedø
169 && localhostAdmin.get() && isLocalHost(ctx)
170 || getFromAccount(ctx.state.account, a => a.admin)
171 }
src/middlewares.ts
+17 -12
@@ -4,11 +4,11 @@ import compress from 'koa-compress'
4 import Koa from 'koa'
5 import session from 'koa-session'
6 import {
7 - ADMIN_URI,
7 + ADMIN_URI, API_URI,
8 BUILD_TIMESTAMP,
9 - DEV,
9 + DEV, DAY,
10 SESSION_DURATION,
11 - HTTP_FORBIDDEN, HTTP_NOT_FOUND, HTTP_FOOL, API_URI,
11 + HTTP_FORBIDDEN, HTTP_NOT_FOUND, HTTP_FOOL,
12 } from './const'
13 import { FRONTEND_URI } from './const'
14 import { statusCodeForMissingPerm, nodeIsDirectory, urlToNode, vfs } from './vfs'
@@ -34,6 +34,7 @@ import { getHttpsWorkingPort } from './listen'
34 import { defineConfig } from './config'
35
36 const forceHttps = defineConfig('force_https', true)
37 +const ignoreProxies = defineConfig('ignore_proxies', false)
38
39 export const gzipper = compress({
40 threshold: 2048,
@@ -148,20 +149,21 @@ export const serveGuiAndSharedFiles: Koa.Middleware = async (ctx, next) => {
149 : serveFrontendFiles(ctx, next)
150 }
151
151 -let proxyDetected = false
152 +let proxyDetected: undefined | Koa.Context
153 export const someSecurity: Koa.Middleware = async (ctx, next) => {
154 ctx.request.ip = normalizeIp(ctx.ip)
155 try {
155 - let proxy = ctx.get('X-Forwarded-For')
156 - // we have some dev-proxies to ignore
157 - if (DEV && proxy && [process.env.FRONTEND_PROXY, process.env.ADMIN_PROXY].includes(ctx.get('X-Forwarded-port')))
158 - proxy = ''
156 if (dirTraversal(decodeURI(ctx.path)))
157 return ctx.status = HTTP_FOOL
158 if (applyBlock(ctx.socket, ctx.ip))
159 return
163 - proxyDetected ||= proxy > ''
164 - ctx.state.proxiedFor = proxy
160 +
161 + if (!ctx.ips.length && ctx.get('X-Forwarded-For') // empty ctx.ips implies we didn't configure for proxies
162 + // we have some dev-proxies to ignore
163 + && !(DEV && [process.env.FRONTEND_PROXY, process.env.ADMIN_PROXY].includes(ctx.get('X-Forwarded-port')))) {
164 + proxyDetected = ctx
165 + ctx.state.when = new Date()
166 + }
167 }
168 catch {
169 return ctx.status = HTTP_FOOL
@@ -169,9 +171,12 @@ export const someSecurity: Koa.Middleware = async (ctx, next) => {
171 return next()
172 }
173
172 -// this is only about http proxies
174 +// limited to http proxies
175 export function getProxyDetected() {
174 - return proxyDetected
176 + if (proxyDetected?.state.when < Date.now() - DAY)
177 + proxyDetected = undefined
178 + return !ignoreProxies.get() && proxyDetected
179 + && { from: proxyDetected.ip, for: proxyDetected.get('X-Forwarded-For') }
180 }
181 export const prepareState: Koa.Middleware = async (ctx, next) => {
182 // calculate these once and for all