Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: default host to localhost instead of 127.0.0.1 #8543

Merged
merged 9 commits into from Jun 14, 2022
Merged
2 changes: 1 addition & 1 deletion docs/config/server-options.md
Expand Up @@ -3,7 +3,7 @@
## server.host

- **Type:** `string | boolean`
- **Default:** `'127.0.0.1'`
- **Default:** `'localhost'`

Specify which IP addresses the server should listen on.
Set this to `0.0.0.0` or `true` to listen on all addresses, including LAN and public addresses.
Expand Down
2 changes: 2 additions & 0 deletions docs/guide/migration.md
Expand Up @@ -31,6 +31,8 @@ A small fraction of users will now require using [@vitejs/plugin-legacy](https:/

Vite's default dev server port is now 5173. You can use [`server.port`](../config/server-options.md#server-port) to set it to 3000.

Vite's default dev server host is now `localhost`. You can use [`server.host`](../config/server-options.md#server-host) to set it to `127.0.0.1`.

Vite optimizes dependencies with esbuild to both convert CJS-only deps to ESM and to reduce the number of modules the browser needs to request. In v3, the default strategy to discover and batch dependencies has changed. Vite no longer pre-scans user code with esbuild to get an initial list of dependencies on cold start. Instead, it delays the first dependency optimization run until every imported user module on load is processed.

To get back the v2 strategy, you can use [`optimizeDeps.devScan`](../config/dep-optimization-options.md#optimizedepsdevscan).
Expand Down
25 changes: 23 additions & 2 deletions packages/vite/src/node/__tests__/utils.spec.ts
Expand Up @@ -49,9 +49,9 @@ describe('injectQuery', () => {
})

describe('resolveHostname', () => {
test('defaults to 127.0.0.1', () => {
test('defaults to localhost', () => {
expect(resolveHostname(undefined)).toEqual({
host: '127.0.0.1',
host: 'localhost',
name: 'localhost'
})
})
Expand All @@ -62,6 +62,27 @@ describe('resolveHostname', () => {
name: 'localhost'
})
})

test('accepts 0.0.0.0', () => {
expect(resolveHostname('0.0.0.0')).toEqual({
host: '0.0.0.0',
name: 'localhost'
})
})

test('accepts ::', () => {
expect(resolveHostname('::')).toEqual({
host: '::',
name: 'localhost'
})
})

test('accepts 0000:0000:0000:0000:0000:0000:0000:0000', () => {
expect(resolveHostname('0000:0000:0000:0000:0000:0000:0000:0000')).toEqual({
host: '0000:0000:0000:0000:0000:0000:0000:0000',
name: 'localhost'
})
})
})

test('ts import of file with .js extension', () => {
Expand Down
12 changes: 12 additions & 0 deletions packages/vite/src/node/constants.ts
Expand Up @@ -99,3 +99,15 @@ export const DEFAULT_ASSETS_RE = new RegExp(
)

export const DEP_VERSION_RE = /[\?&](v=[\w\.-]+)\b/

export const loopbackHosts = new Set([
'localhost',
'127.0.0.1',
'::1',
'0000:0000:0000:0000:0000:0000:0000:0001'
])
export const wildcardHosts = new Set([
'0.0.0.0',
'::',
'0000:0000:0000:0000:0000:0000:0000:0000'
])
12 changes: 10 additions & 2 deletions packages/vite/src/node/http.ts
Expand Up @@ -5,6 +5,7 @@ import type {
OutgoingHttpHeaders as HttpServerHeaders
} from 'http'
import type { ServerOptions as HttpsServerOptions } from 'https'
import { promises as dns } from 'dns'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm using dns.promises since dns/promises is supported from Node 15+.
https://nodejs.org/dist/latest-v16.x/docs/api/dns.html#dns-promises-api

import type { Connect } from 'types/connect'
import { isObject } from './utils'
import type { ProxyOptions } from './server/middlewares/proxy'
Expand Down Expand Up @@ -184,9 +185,16 @@ export async function httpServerStart(
logger: Logger
}
): Promise<number> {
return new Promise((resolve, reject) => {
let { port, strictPort, host, logger } = serverOptions
let { port, strictPort, host, logger } = serverOptions

// This could be removed when Vite only supports Node 17+ because verbatim=true is default
// https://github.com/nodejs/node/pull/39987
if (host === 'localhost') {
const addr = await dns.lookup('localhost', { verbatim: true })
host = addr.address
}
Comment on lines +190 to +195
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirmed this part with Node v18.3.0 + Windows.


return new Promise((resolve, reject) => {
const onError = (e: Error & { code?: string }) => {
if (e.code === 'EADDRINUSE') {
if (strictPort) {
Expand Down
36 changes: 30 additions & 6 deletions packages/vite/src/node/logger.ts
Expand Up @@ -8,6 +8,7 @@ import type { RollupError } from 'rollup'
import type { CommonServerOptions } from './http'
import type { Hostname } from './utils'
import { resolveHostname } from './utils'
import { loopbackHosts, wildcardHosts } from './constants'
import type { ResolvedConfig } from '.'

export type LogType = 'error' | 'warn' | 'info'
Expand Down Expand Up @@ -173,15 +174,23 @@ function printServerUrls(
): void {
const urls: Array<{ label: string; url: string }> = []

if (hostname.host === '127.0.0.1') {
if (hostname.host && loopbackHosts.has(hostname.host)) {
let hostnameName = hostname.name
if (
hostnameName === '::1' ||
hostnameName === '0000:0000:0000:0000:0000:0000:0000:0001'
) {
hostnameName = `[${hostnameName}]`
}

urls.push({
label: 'Local',
url: colors.cyan(
`${protocol}://${hostname.name}:${colors.bold(port)}${base}`
`${protocol}://${hostnameName}:${colors.bold(port)}${base}`
)
})

if (hostname.name !== '127.0.0.1') {
if (hostname.name === 'localhost') {
urls.push({
label: 'Network',
url: colors.dim(`use ${colors.white(colors.bold('--host'))} to expose`)
Expand Down Expand Up @@ -212,11 +221,26 @@ function printServerUrls(
(length, { label }) => Math.max(length, label.length),
0
)
urls.forEach(({ label, url: text }) => {
const print = (
iconWithColor: string,
label: string,
messageWithColor: string
) => {
info(
` ${colors.green('➜')} ${colors.bold(label)}: ${' '.repeat(
` ${iconWithColor} ${colors.bold(label)}: ${' '.repeat(
length - label.length
)}${text}`
)}${messageWithColor}`
)
}

urls.forEach(({ label, url: text }) => {
print(colors.green('➜'), label, text)
})
if (!hostname.host || wildcardHosts.has(hostname.host)) {
print(
colors.bold(colors.blue('ⓘ')),
'Note',
colors.dim('You are using a wildcard host. Ports might be overriden.')
)
}
}
14 changes: 5 additions & 9 deletions packages/vite/src/node/utils.ts
Expand Up @@ -22,7 +22,8 @@ import {
DEFAULT_EXTENSIONS,
ENV_PUBLIC_PATH,
FS_PREFIX,
VALID_ID_PREFIX
VALID_ID_PREFIX,
wildcardHosts
} from './constants'
import type { ResolvedConfig } from '.'

Expand Down Expand Up @@ -732,22 +733,17 @@ export function resolveHostname(
let host: string | undefined
if (optionsHost === undefined || optionsHost === false) {
// Use a secure default
host = '127.0.0.1'
host = 'localhost'
} else if (optionsHost === true) {
// If passed --host in the CLI without arguments
host = undefined // undefined typically means 0.0.0.0 or :: (listen on all IPs)
} else {
host = optionsHost
}

// Set host name to localhost when possible, unless the user explicitly asked for '127.0.0.1'
// Set host name to localhost when possible
const name =
(optionsHost !== '127.0.0.1' && host === '127.0.0.1') ||
host === '0.0.0.0' ||
host === '::' ||
host === undefined
? 'localhost'
: host
host === undefined || wildcardHosts.has(host) ? 'localhost' : host
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not safe.
When you listen to 0.0.0.0:3000 in Vite, it is still possible to create a server that listens to 127.0.0.1:3000, and localhost:3000 will resolve to that server instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!
Though I'm not sure how to fix this.
Opening http://0.0.0.0:3000 in browsers does not work and any other network ip's could also be overridden by other servers.

Maybe adding a warning that denotes ports might be overridden?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, a warning should be good enough

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
I implemented like this. (maybe the message text is not friendly... I didn't come up with a good message.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sapphi-red I was confused by the message, as you may know. As a Vite user, I would expect it to say "security" and be listed as a warning (not informational). Also while the "ports might be overridden" makes sense in the context of this thread, it lead me in a wrong direction.

I know this already shipped, but a suggestion:
"You are using a wildcard host. This means a service listening to 127.0.0.1:port on your system may hijack your traffic. More information: ".

The "more information" should include a way to permanently silence the warning.

My 2c... Just that you know, anyone running Vite under Docker Compose will need to enable the host: true and thus will see the message. Docker Compose port forwarding does not work without it.

Disclaimer. I don't claim to understand the whole change in play here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion!

This message is not intended as a security warning. The purpose of this message is to inform the user that a server other than Vite might respond when accessing the output address. Because it is a confusing behavior.

I'll create a PR to improve this message.


return { host, name }
}
Expand Down
2 changes: 1 addition & 1 deletion playground/css/postcss-caching/css.spec.ts
Expand Up @@ -36,7 +36,7 @@ test('postcss config', async () => {
blueApp = null

greenApp = await startServer(greenAppDir)
await page.goto(`http://localhost:${port}`)
await page.reload() // hmr reloads it automatically but reload here for consistency
const greenA = await page.$('.postcss-a')
expect(await getColor(greenA)).toBe('black')
const greenB = await page.$('.postcss-b')
Expand Down