Skip to content

Commit

Permalink
tunnel server eslint rules
Browse files Browse the repository at this point in the history
- use same lint rules as the rest of the project
- fix lint errors
- move root .eslintrc to build_utils to fix eslint configuration error
  • Loading branch information
Roy Razon committed Jul 26, 2023
1 parent 734510f commit 952f4f9
Show file tree
Hide file tree
Showing 24 changed files with 201 additions and 157 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
],
"eslint.format.enable": true,
"eslint.workingDirectories": [
"./tunnel-server",
{ "pattern": "./packages/*/" }
],
"eslint.useESLintClass": true,
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion packages/cli-common/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/cli/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/common/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/compose-tunnel-agent/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/core/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/driver-azure/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/driver-gce/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/driver-kube-pod/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/driver-lightsail/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
2 changes: 1 addition & 1 deletion packages/plugin-github-pr-link/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('../../.eslintrc.js')
module.exports = require('../../build_utils/eslintrc.js')
29 changes: 23 additions & 6 deletions tunnel-server/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
module.exports = {
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
root: true,
}
const deepMerge = require('../build_utils/deep_merge')

module.exports = deepMerge(require('../build_utils/eslintrc'), {
rules: {
'no-underscore-dangle': [
'warn',
{
'allow': [
'__dirname',
'__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'
],
'allowAfterThis': false,
'allowAfterSuper': false,
'enforceInMethodNames': true,
'allowAfterThisConstructor': false,
'allowFunctionParams': true,
'enforceInClassFields': false,
'allowInArrayDestructuring': true,
'allowInObjectDestructuring': true
},
],
}
})
37 changes: 19 additions & 18 deletions tunnel-server/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { inspect, promisify } from 'util'
import url from 'url'
import path from 'path'
import pino from 'pino'
import { createPublicKey } from 'crypto'
import { app as createApp } from './src/app'
import { inMemoryPreviewEnvStore } from './src/preview-env'
import { sshServer as createSshServer } from './src/ssh-server'
import { getSSHKeys } from './src/ssh-keys'
import url from 'url'
import path from 'path'
import { isProxyRequest, proxyHandlers } from './src/proxy'
import { appLoggerFromEnv } from './src/logging'
import pino from 'pino'
import { tunnelsGauge } from './src/metrics'
import { runMetricsServer } from './src/metrics'
import { tunnelsGauge, runMetricsServer } from './src/metrics'
import { numberFromEnv, requiredEnv } from './src/env'
import { replaceHostname } from './src/url'
import { createPublicKey } from 'crypto'

const __dirname = url.fileURLToPath(new URL('.', import.meta.url))

const logger = pino(appLoggerFromEnv())

const { sshPrivateKey } = await getSSHKeys({
defaultKeyLocation: path.join(__dirname, "./ssh/ssh_host_key")
defaultKeyLocation: path.join(__dirname, './ssh/ssh_host_key'),
log: logger,
})

const PORT = numberFromEnv('PORT') || 3000
Expand All @@ -33,10 +35,9 @@ const BASE_URL = (() => {

const envStore = inMemoryPreviewEnvStore()

const logger = pino(appLoggerFromEnv())
const app = createApp({
isProxyRequest: isProxyRequest(BASE_URL.hostname),
proxyHandlers: proxyHandlers({envStore, logger}),
proxyHandlers: proxyHandlers({ envStore, logger }),
logger,
})
const sshLogger = logger.child({ name: 'ssh_server' })
Expand Down Expand Up @@ -76,24 +77,24 @@ const sshServer = createSshServer({
access,
})
tunnels.set(requestId, tunnelUrl(BASE_URL, clientId, remotePath))
tunnelsGauge.inc({clientId})
tunnelsGauge.inc({ clientId })

forward.on('close', () => {
sshLogger.debug('deleting tunnel %s', key)
tunnels.delete(requestId)
void envStore.delete(key)
tunnelsGauge.dec({clientId})
tunnelsGauge.dec({ clientId })
})
})
.on('error', err => { sshLogger.warn('client error %j: %j', clientId, inspect(err)) })
.on('hello', channel => {
channel.stdout.write(JSON.stringify({
channel.stdout.write(`${JSON.stringify({
clientId,
// TODO: backwards compat, remove when we drop support for CLI v0.0.35
baseUrl: { hostname: BASE_URL.hostname, port: BASE_URL.port, protocol: BASE_URL.protocol },
rootUrl: BASE_URL.toString(),
tunnels: Object.fromEntries(tunnels.entries()),
}) + '\r\n')
})}\r\n`)
channel.exit(0)
})
})
Expand All @@ -104,20 +105,20 @@ const sshServer = createSshServer({
app.log.error('ssh server error: %j', err)
})

app.listen({ host: LISTEN_HOST, port: PORT }).catch((err) => {
app.listen({ host: LISTEN_HOST, port: PORT }).catch(err => {
app.log.error(err)
process.exit(1)
})

runMetricsServer(8888).catch((err) => {
runMetricsServer(8888).catch(err => {
app.log.error(err)
})
});

;['SIGTERM', 'SIGINT'].forEach((signal) => {
['SIGTERM', 'SIGINT'].forEach(signal => {
process.once(signal, () => {
app.log.info(`shutting down on ${signal}`)
Promise.all([promisify(sshServer.close).call(sshServer), app.close()])
.catch((err) => {
.catch(err => {
app.log.error(err)
process.exit(1)
})
Expand Down
3 changes: 2 additions & 1 deletion tunnel-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"http-proxy": "^1.18.1",
"jose": "^4.14.4",
"lodash": "^4.17.21",
"pino": "^8.11.0",
"pino-pretty": "^9.4.0",
"prom-client": "^14.2.0",
"ssh2": "^1.12.0",
Expand All @@ -31,6 +32,6 @@
"start": "ts-node ./index.ts",
"build": "tsc --noEmit",
"dev": "DEBUG=1 yarn nodemon ./index.ts",
"lint": "eslint . --ext .ts,.tsx --cache"
"lint": "eslint -c .eslintrc.cjs --no-eslintrc --ext .ts --cache ."
}
}
19 changes: 11 additions & 8 deletions tunnel-server/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,34 @@ import Fastify from 'fastify'
import { fastifyRequestContext } from '@fastify/request-context'
import http from 'http'
import internal from 'stream'
import {Logger} from 'pino'
import { Logger } from 'pino'

export const app = ({ isProxyRequest, proxyHandlers, logger }: {
isProxyRequest: (req: http.IncomingMessage) => boolean
logger: Logger
proxyHandlers: { wsHandler: (req: http.IncomingMessage, socket: internal.Duplex, head: Buffer) => void, handler: (req: http.IncomingMessage, res: http.ServerResponse) => void }
proxyHandlers: {
wsHandler: (req: http.IncomingMessage, socket: internal.Duplex, head: Buffer) => void
handler: (req: http.IncomingMessage, res: http.ServerResponse) => void
}
}) =>
Fastify({
serverFactory: (handler) => {
const {wsHandler:proxyWsHandler, handler: proxyHandler } = proxyHandlers
serverFactory: handler => {
const { wsHandler: proxyWsHandler, handler: proxyHandler } = proxyHandlers
const server = http.createServer((req, res) => {
if (isProxyRequest(req)){
if (isProxyRequest(req)) {
return proxyHandler(req, res)
}
return handler(req, res)
})
server.on('upgrade', (req, socket, head) => {
if (isProxyRequest(req)){
if (isProxyRequest(req)) {
proxyWsHandler(req, socket, head)
} else {
logger.warn('unexpected upgrade request %j', {url: req.url, host: req.headers['host']})
logger.warn('unexpected upgrade request %j', { url: req.url, host: req.headers.host })
socket.end()
}
})
return server;
return server
},
logger,
})
Expand Down
49 changes: 24 additions & 25 deletions tunnel-server/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,48 @@ export type Claims = {
exp?: number
}

function extractBasicAuth(req: IncomingMessage){
const authorization = req.headers['authorization'];
function extractBasicAuth(req: IncomingMessage) {
const { authorization } = req.headers
const [scheme, data] = authorization?.split(' ') ?? []
if (scheme !== 'Basic') {
return undefined
}
const basicAuth = Buffer.from(data, 'base64').toString('ascii');
const sep = basicAuth.indexOf(":")
if (sep === -1){
const basicAuth = Buffer.from(data, 'base64').toString('ascii')
const sep = basicAuth.indexOf(':')
if (sep === -1) {
return undefined
}
return [basicAuth.slice(0,sep), basicAuth.slice(sep+1)];
return [basicAuth.slice(0, sep), basicAuth.slice(sep + 1)]
}

export function tunnelingKeyAuthenticator(publicKey: KeyObject){
return async (req: IncomingMessage)=>{
const auth = extractBasicAuth(req);
if (!auth){
return;
export function tunnelingKeyAuthenticator(publicKey: KeyObject) {
return async (req: IncomingMessage) => {
const auth = extractBasicAuth(req)
if (!auth) {
return undefined
}
const [username, rawJWT] = auth;
if (username !== 'x-preevy-profile-key'){
return;
const [username, rawJWT] = auth
if (username !== 'x-preevy-profile-key') {
return undefined
}
const thumbprint = await calculateJwkThumbprintUri(await exportJWK(publicKey))
const token = await jwtVerify(rawJWT, publicKey, {issuer: `preevy://${thumbprint}`})
const token = await jwtVerify(rawJWT, publicKey, { issuer: `preevy://${thumbprint}` })
if (!token) {
throw new Error("invalid token");
}
return {
role: "admin"
throw new Error('invalid token')
}
return { role: 'admin' }
}
}

export function authenticator(authenticators: ((req: IncomingMessage)=> Promise<Claims | undefined>)[] ){
return async (req: IncomingMessage)=>{
for (const authorizer of authenticators){
export function authenticator(authenticators: ((req: IncomingMessage)=> Promise<Claims | undefined>)[]) {
return async (req: IncomingMessage) => {
for (const authorizer of authenticators) {
// eslint-disable-next-line no-await-in-loop
const claims = await authorizer(req)
if (claims){
if (claims) {
return claims
}
}
return;
return undefined
}
}
}
3 changes: 2 additions & 1 deletion tunnel-server/src/logging.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pino from 'pino'

type PinoParams = Parameters<(typeof pino)>[0]
const envToLogger: Record<string, PinoParams> = {
development: {
Expand All @@ -13,7 +14,7 @@ const envToLogger: Record<string, PinoParams> = {
},
production: {
level: process.env.DEBUG ? 'debug' : 'info',
}
},
}

export const appLoggerFromEnv = () => envToLogger[process.env.NODE_ENV || 'development']
16 changes: 8 additions & 8 deletions tunnel-server/src/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fastify from 'fastify'
import { Gauge , Counter,register } from 'prom-client';
import { Gauge, Counter, register } from 'prom-client'

export const tunnelsGauge = new Gauge({
name: 'tunnels',
Expand All @@ -15,15 +15,15 @@ export const requestsCounter = new Counter({

register.setDefaultLabels({ serviceName: 'preevy-tunnel-server' })

export function runMetricsServer(port: number){
const app = fastify();
export function runMetricsServer(port: number) {
const app = fastify()

app.get("/metrics", async (request, reply) => {
reply.header('Content-Type', register.contentType);
reply.send(await register.metrics())
});
app.get('/metrics', async (_request, reply) => {
await reply.header('Content-Type', register.contentType)
await reply.send(await register.metrics())
})
return app.listen({
host: "0.0.0.0",
host: '0.0.0.0',
port,
})
}
8 changes: 4 additions & 4 deletions tunnel-server/src/preview-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { KeyObject } from 'crypto'
export type PreviewEnv = {
clientId: string
target: string
publicKey: KeyObject,
publicKey: KeyObject
access: 'private' | 'public'
}

Expand All @@ -17,11 +17,11 @@ export type PreviewEnvStore = {
export const inMemoryPreviewEnvStore = (initial?: Record<string, PreviewEnv>): PreviewEnvStore => {
const map = new Map<string, PreviewEnv>(Object.entries(initial ?? {}))
return {
get: async (key) => map.get(key),
get: async key => map.get(key),
set: async (key, value) => {
map.set(key, value)
},
has: async (key) => map.has(key),
delete: async (key) => map.delete(key),
has: async key => map.has(key),
delete: async key => map.delete(key),
}
}
Loading

0 comments on commit 952f4f9

Please sign in to comment.