diff --git a/packages/server/src/cli.ts b/packages/server/src/cli.ts index 02d12db4..aed92fc1 100644 --- a/packages/server/src/cli.ts +++ b/packages/server/src/cli.ts @@ -24,6 +24,7 @@ export class Entry extends Command { open: flags.boolean({ char: "o", description: "Open in browser on startup" }), port: flags.integer({ char: "p", default: 8080, description: "Port to bind on" }), version: flags.version({ char: "v" }), + "no-auth": flags.boolean({ default: false }), // Dev flags "bootstrap-fork": flags.string({ hidden: true }), @@ -132,41 +133,47 @@ export class Entry extends Command { const password = "023450wf0951"; const hasCustomHttps = certData && certKeyData; - const app = await createApp((app) => { - app.use((req, res, next) => { - res.on("finish", () => { - logger.trace(`\u001B[1m${req.method} ${res.statusCode} \u001B[0m${req.url}`, field("host", req.hostname), field("ip", req.ip)); + const app = await createApp({ + bypassAuth: flags["no-auth"], + registerMiddleware: (app): void => { + app.use((req, res, next) => { + res.on("finish", () => { + logger.trace(`\u001B[1m${req.method} ${res.statusCode} \u001B[0m${req.url}`, field("host", req.hostname), field("ip", req.ip)); + }); + + next(); }); - - next(); - }); - // If we're not running from the binary and we aren't serving the static - // pre-built version, use webpack to serve the web files. - if (!isCli && !serveStatic) { - const webpackConfig = require(path.join(__dirname, "..", "..", "web", "webpack.config.js")); - const compiler = require("webpack")(webpackConfig); - app.use(require("webpack-dev-middleware")(compiler, { - logger, - publicPath: webpackConfig.output.publicPath, - stats: webpackConfig.stats, - })); - app.use(require("webpack-hot-middleware")(compiler)); - } - }, { - builtInExtensionsDirectory: builtInExtensionsDir, - dataDirectory: dataDir, - workingDirectory: workingDir, - fork: (modulePath: string, args: string[], options: ForkOptions): ChildProcess => { - if (options && options.env && options.env.AMD_ENTRYPOINT) { - return forkModule(options.env.AMD_ENTRYPOINT, args, options, dataDir); + // If we're not running from the binary and we aren't serving the static + // pre-built version, use webpack to serve the web files. + if (!isCli && !serveStatic) { + const webpackConfig = require(path.join(__dirname, "..", "..", "web", "webpack.config.js")); + const compiler = require("webpack")(webpackConfig); + app.use(require("webpack-dev-middleware")(compiler, { + logger, + publicPath: webpackConfig.output.publicPath, + stats: webpackConfig.stats, + })); + app.use(require("webpack-hot-middleware")(compiler)); } - - return fork(modulePath, args, options); }, - }, password, hasCustomHttps ? { - key: certKeyData, - cert: certData, - } : undefined); + serverOptions: { + builtInExtensionsDirectory: builtInExtensionsDir, + dataDirectory: dataDir, + workingDirectory: workingDir, + fork: (modulePath: string, args: string[], options: ForkOptions): ChildProcess => { + if (options && options.env && options.env.AMD_ENTRYPOINT) { + return forkModule(options.env.AMD_ENTRYPOINT, args, options, dataDir); + } + + return fork(modulePath, args, options); + }, + }, + password, + httpsOptions: hasCustomHttps ? { + key: certKeyData, + cert: certData, + } : undefined, + }); logger.info("Starting webserver...", field("host", flags.host), field("port", flags.port)); app.server.listen(flags.port, flags.host); @@ -191,8 +198,12 @@ export class Entry extends Command { logger.warn("Documentation on securing your setup: https://coder.com/docs"); } - logger.info(" "); - logger.info(`Password:\u001B[1m ${password}`); + if (!flags["no-auth"]) { + logger.info(" "); + logger.info(`Password:\u001B[1m ${password}`); + } else { + logger.warn("Launched without authentication."); + } logger.info(" "); logger.info("Started (click the link below to open):"); logger.info(`http://localhost:${flags.port}/`); diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index a3bfb99a..e804f155 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -20,7 +20,15 @@ import { handle as handleTunnel } from "@coder/tunnel/src/server"; import { createPortScanner } from "./portScanner"; import { buildDir, isCli } from "./constants"; -export const createApp = async (registerMiddleware?: (app: express.Application) => void, options?: ServerOptions, password?: string, httpsOptions?: https.ServerOptions): Promise<{ +interface CreateAppOptions { + registerMiddleware?: (app: express.Application) => void; + serverOptions?: ServerOptions; + password?: string; + httpsOptions?: https.ServerOptions; + bypassAuth?: boolean; +} + +export const createApp = async (options: CreateAppOptions): Promise<{ readonly express: express.Application; readonly server: http.Server; readonly wss: ws.Server; @@ -38,15 +46,26 @@ export const createApp = async (registerMiddleware?: (app: express.Application) return cookies; }; + const ensureAuthed = (req: http.IncomingMessage, res: express.Response): boolean => { + if (!isAuthed(req)) { + res.status(401); + res.end(); + + return false; + } + + return true; + }; + const isAuthed = (req: http.IncomingMessage): boolean => { try { - if (!password || !isCli) { + if (!options.password || options.bypassAuth) { return true; } // Try/catch placed here just in case const cookies = parseCookies(req); - if (cookies.password && cookies.password === password) { + if (cookies.password && cookies.password === options.password) { return true; } } catch (ex) { @@ -62,8 +81,8 @@ export const createApp = async (registerMiddleware?: (app: express.Application) }; const app = express(); - if (registerMiddleware) { - registerMiddleware(app); + if (options.registerMiddleware) { + options.registerMiddleware(app); } const certs = await new Promise((res, rej): void => { @@ -134,7 +153,7 @@ export const createApp = async (registerMiddleware?: (app: express.Application) onClose: (cb): void => ws.addEventListener("close", () => cb()), }; - const server = new Server(connection, options); + const server = new Server(connection, options.serverOptions); }); const baseDir = buildDir || path.join(__dirname, ".."); @@ -150,6 +169,10 @@ export const createApp = async (registerMiddleware?: (app: express.Application) } }); app.get("/resource/:url(*)", async (req, res) => { + if (!ensureAuthed(req, res)) { + return; + } + try { const fullPath = `/${req.params.url}`; // const relative = path.relative(options!.dataDirectory, fullPath); @@ -184,6 +207,10 @@ export const createApp = async (registerMiddleware?: (app: express.Application) } }); app.post("/resource/:url(*)", async (req, res) => { + if (!ensureAuthed(req, res)) { + return; + } + try { const fullPath = `/${req.params.url}`; diff --git a/scripts/install-packages.ts b/scripts/install-packages.ts index 3569f6b4..a67fe337 100644 --- a/scripts/install-packages.ts +++ b/scripts/install-packages.ts @@ -40,3 +40,4 @@ const handlePackages = (dir: string): void => { }; handlePackages(resolve(__dirname, "..", "packages")); +handlePackages(resolve(__dirname, "..", "packages", "app"));