From e1c846a9ae49c18149f58555a05a9c6487a482e9 Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 27 Jul 2018 16:27:31 -0500 Subject: [PATCH 1/3] Start cleaning up github auth routes - Move github token debug route to separate module - Use crypto.timingSafeEqual - Rename getTokenDebugInfo -> serializeDebugInfo This picks some more of the work from #1205. --- lib/github-auth.js | 35 +++-------------------------------- server.js | 4 +++- services/github/admin.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 33 deletions(-) create mode 100644 services/github/admin.js diff --git a/lib/github-auth.js b/lib/github-auth.js index 55f4b284099c3..9082c8122596a 100644 --- a/lib/github-auth.js +++ b/lib/github-auth.js @@ -108,33 +108,13 @@ function setRoutes(server) { }); server.route(/^\/github-auth\/add-token$/, function(data, match, end, ask) { - if (!constEq(data.shieldsSecret, serverSecrets.shieldsSecret)) { + if (!crypto.timingSafeEqual(data.shieldsSecret, serverSecrets.shieldsSecret)) { // An unknown entity tries to connect. Let the connection linger for 10s. return setTimeout(function() { end('Invalid secret.'); }, 10000); } addGithubToken(data.token); end('Thanks!'); }); - - // Allow the admin to obtain the tokens for operational and debugging - // purposes. This could be used to: - // - // - Ensure tokens have been propagated to all servers - // - Debug GitHub badge failures - // - // The admin can authenticate with HTTP Basic Auth, with an empty/any - // username and the shields secret in the password and an empty/any - // password. - // - // e.g. - // curl -u ':very-very-secret' 'https://example.com/$github-auth/tokens' - server.ajax.on('github-auth/tokens', (json, end, ask) => { - if (! constEq(ask.password, serverSecrets.shieldsSecret)) { - // An unknown entity tries to connect. Let the connection linger for a minute. - return setTimeout(function() { end('Invalid secret.'); }, 10000); - } - end(getTokenDebugInfo({ sanitize: false })); - }); } function sendTokenToAllServers(token) { @@ -265,7 +245,7 @@ function sha(str) { .digest('hex'); } -function getTokenDebugInfo(options) { +function serializeDebugInfo(options) { // Apply defaults. const { sanitize } = Object.assign({ sanitize: true }, options); @@ -339,19 +319,10 @@ function githubRequest(request, url, query, cb) { }); } -function constEq(a, b) { - if (a.length !== b.length) { return false; } - let zero = 0; - for (let i = 0; i < a.length; i++) { - zero |= a.charCodeAt(i) ^ b.charCodeAt(i); - } - return (zero === 0); -} - module.exports = { scheduleAutosaving, cancelAutosaving, request: githubRequest, setRoutes, - getTokenDebugInfo, + serializeDebugInfo, }; diff --git a/server.js b/server.js index 476ee49416209..bb4bff4e7160e 100644 --- a/server.js +++ b/server.js @@ -23,6 +23,7 @@ const { checkErrorResponse } = require('./lib/error-helper'); const analytics = require('./lib/analytics'); const config = require('./lib/server-config'); const githubAuth = require('./lib/github-auth'); +const { setRoutes: setGithubAdminRoutes } = require('./services/github/auth/admin'); const sysMonitor = require('./lib/sys/monitor'); const log = require('./lib/log'); const { makeMakeBadgeFn } = require('./lib/make-badge'); @@ -165,6 +166,7 @@ analytics.load(); analytics.scheduleAutosaving(); analytics.setRoutes(camp); +setGithubAdminRoutes(camp); githubAuth.scheduleAutosaving({ dir: config.persistence.dir }); if (serverSecrets && serverSecrets.gh_client_id) { githubAuth.setRoutes(camp); @@ -176,7 +178,7 @@ if (serverSecrets && serverSecrets.shieldsSecret) { let githubDebugInterval; if (config.services.github.debug.enabled) { githubDebugInterval = setInterval(() => { - log(githubAuth.getTokenDebugInfo()); + log(githubAuth.serializeDebugInfo()); }, 1000 * config.services.github.debug.intervalSeconds); } diff --git a/services/github/admin.js b/services/github/admin.js new file mode 100644 index 0000000000000..6846b4a09831c --- /dev/null +++ b/services/github/admin.js @@ -0,0 +1,33 @@ +'use strict'; + +const crypto = require('crypto'); +const { serializeDebugInfo } = require('../../../github-auth'); +const serverSecrets = require('../../../server-secrets'); + +function setRoutes(server) { + // Allow the admin to obtain the tokens for operational and debugging + // purposes. This could be used to: + // + // - Ensure tokens have been propagated to all servers + // - Debug GitHub badge failures + // + // The admin can authenticate with HTTP Basic Auth, with an empty/any + // username and the shields secret in the password and an empty/any + // password. + // + // e.g. + // curl -u ':very-very-secret' 'https://example.com/$github-auth/tokens' + server.ajax.on('github-auth/tokens', (json, end, ask) => { + if (!crypto.timingSafeEqual(ask.password, serverSecrets.shieldsSecret)) { + // An unknown entity tries to connect. Let the connection linger for a minute. + return setTimeout(function() { + end('Invalid secret.'); + }, 10000); + } + end(serializeDebugInfo({ sanitize: false })); + }); +} + +module.exports = { + setRoutes, +}; From 9169c3e6b8011ea105b93fbac9d0a49bcbf9cc67 Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 27 Jul 2018 16:31:25 -0500 Subject: [PATCH 2/3] Fix path --- services/github/{ => auth}/admin.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename services/github/{ => auth}/admin.js (100%) diff --git a/services/github/admin.js b/services/github/auth/admin.js similarity index 100% rename from services/github/admin.js rename to services/github/auth/admin.js From f23945472f4f18a670af570f466a6264d95d0057 Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Wed, 1 Aug 2018 14:22:39 -0400 Subject: [PATCH 3/3] Fix imports --- services/github/auth/admin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/github/auth/admin.js b/services/github/auth/admin.js index 6846b4a09831c..7a7ee054bab9a 100644 --- a/services/github/auth/admin.js +++ b/services/github/auth/admin.js @@ -1,8 +1,8 @@ 'use strict'; const crypto = require('crypto'); -const { serializeDebugInfo } = require('../../../github-auth'); -const serverSecrets = require('../../../server-secrets'); +const { serializeDebugInfo } = require('../../../lib/github-auth'); +const serverSecrets = require('../../../lib/server-secrets'); function setRoutes(server) { // Allow the admin to obtain the tokens for operational and debugging