diff --git a/docs/installation/docker.md b/docs/installation/docker.md index 84db22b4..07c38071 100644 --- a/docs/installation/docker.md +++ b/docs/installation/docker.md @@ -15,6 +15,8 @@ services: volumes: - /path/to/config:/app/config # Make sure your local config directory exists - /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations + environment: + HOMEPAGE_ALLOWED_HOSTS: gethomepage.dev # required when deploying via public URL ``` ### Running as non-root @@ -36,6 +38,7 @@ services: - /path/to/config:/app/config # Make sure your local config directory exists - /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations, see alternative methods environment: + HOMEPAGE_ALLOWED_HOSTS: gethomepage.dev # required when deploying via public URL PUID: $PUID PGID: $PGID ``` @@ -43,7 +46,7 @@ services: ### With Docker Run ```bash -docker run -p 3000:3000 -v /path/to/config:/app/config -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/gethomepage/homepage:latest +docker run -p 3000:3000 -e HOMEPAGE_ALLOWED_HOSTS=gethomepage.dev -v /path/to/config:/app/config -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/gethomepage/homepage:latest ``` ### Using Environment Secrets diff --git a/docs/installation/source.md b/docs/installation/source.md index 573711a8..38fcc2c5 100644 --- a/docs/installation/source.md +++ b/docs/installation/source.md @@ -21,7 +21,7 @@ If this is your first time starting, copy the `src/skeleton` directory to `confi Finally, run the server: ```bash -pnpm start +HOMEPAGE_ALLOWED_HOSTS=gethomepage.dev pnpm start ``` When updating homepage versions you will need to re-build the static files i.e. repeat the process above. diff --git a/src/middleware.js b/src/middleware.js new file mode 100644 index 00000000..a32581cb --- /dev/null +++ b/src/middleware.js @@ -0,0 +1,23 @@ +import { NextResponse } from "next/server"; + +export function middleware(req) { + // Check the Host header, if HOMEPAGE_ALLOWED_HOSTS is set + const host = req.headers.get("host"); + const port = process.env.PORT || 3000; + let allowedHosts = [`localhost:${port}`]; + if (process.env.HOMEPAGE_ALLOWED_HOSTS) { + allowedHosts = allowedHosts.concat(process.env.HOMEPAGE_ALLOWED_HOSTS.split(",")); + } + if (!host || !allowedHosts.includes(host)) { + // eslint-disable-next-line no-console + console.error( + `Host validation failed for: ${host}. Hint: Set HOMEPAGE_ALLOWED_HOSTS to allow requests from this host.`, + ); + return NextResponse.json({ error: "Host validation failed. See logs for more details." }, { status: 400 }); + } + return NextResponse.next(); +} + +export const config = { + matcher: "/api/:path*", +}; diff --git a/src/pages/index.jsx b/src/pages/index.jsx index acf23340..0b1de2f4 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -86,6 +86,7 @@ function Index({ initialSettings, fallback }) { const windowFocused = useWindowFocus(); const [stale, setStale] = useState(false); const { data: errorsData } = useSWR("/api/validate"); + const { error: validateError } = errorsData || {}; const { data: hashData, mutate: mutateHash } = useSWR("/api/hash"); useEffect(() => { @@ -117,6 +118,24 @@ function Index({ initialSettings, fallback }) { } }, [hashData]); + if (validateError) { + return ( +
{validateError}+