diff --git a/setup/start/cloudron-firewall.sh b/setup/start/cloudron-firewall.sh index 55bd37c5f..838fd0d9e 100755 --- a/setup/start/cloudron-firewall.sh +++ b/setup/start/cloudron-firewall.sh @@ -36,6 +36,10 @@ if allowed_udp_ports=$(node -e "console.log(JSON.parse(fs.readFileSync('${ports_ done fi +# ldap server +iptables -t filter -A CLOUDRON -p tcp -m multiport --dports 636 -j ACCEPT +iptables -A CLOUDRON_RATELIMIT -p tcp --syn --dport 636 -m connlimit --connlimit-above 5000 -j CLOUDRON_RATELIMIT_LOG + # turn and stun service iptables -t filter -A CLOUDRON -p tcp -m multiport --dports 3478,5349 -j ACCEPT iptables -t filter -A CLOUDRON -p udp -m multiport --dports 3478,5349 -j ACCEPT diff --git a/src/constants.js b/src/constants.js index 749fe6d4d..54e5519aa 100644 --- a/src/constants.js +++ b/src/constants.js @@ -30,6 +30,8 @@ exports = module.exports = { LDAP_PORT: 3002, DOCKER_PROXY_PORT: 3003, + LDAPS_PORT: 636, // exposed LDAP with TLS + NGINX_DEFAULT_CONFIG_FILE_NAME: 'default.conf', DEFAULT_TOKEN_EXPIRATION_MSECS: 365 * 24 * 60 * 60 * 1000, // 1 year diff --git a/src/ldap.js b/src/ldap.js index 5bab99170..2736ed727 100644 --- a/src/ldap.js +++ b/src/ldap.js @@ -3,6 +3,8 @@ exports = module.exports = { start, stop, + startExposed, + stopExposed, _MOCK_APP: null }; @@ -13,15 +15,22 @@ const addonConfigs = require('./addonconfigs.js'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), debug = require('debug')('box:ldap'), + debugExposed = require('debug')('box:ldapExposed'), + dns = require('./dns.js'), + domains = require('./domains.js'), eventlog = require('./eventlog.js'), + fs = require('fs'), groups = require('./groups.js'), ldap = require('ldapjs'), mail = require('./mail.js'), + reverseproxy = require('./reverseproxy.js'), safe = require('safetydance'), + settings = require('./settings.js'), users = require('./users.js'), util = require('util'); var gServer = null; +var gExposedServer = null; const NOOP = function () {}; @@ -656,3 +665,48 @@ async function start() { async function stop() { if (gServer) gServer.close(); } + +// FIXME this needs to be restarted if settings changes or dashboard cert got renewed +async function startExposed() { + const logger = { + trace: NOOP, + debug: NOOP, + info: debugExposed, + warn: debugExposed, + error: debugExposed, + fatal: debugExposed + }; + + const domainObject = await domains.get(settings.dashboardDomain()); + const dashboardFqdn = dns.fqdn(constants.DASHBOARD_LOCATION, domainObject); + const bundle = await reverseproxy.getCertificatePath(dashboardFqdn, domainObject.domain); + + gExposedServer = ldap.createServer({ + certificate: fs.readFileSync(bundle.certFilePath, 'utf8'), + key: fs.readFileSync(bundle.keyFilePath, 'utf8'), + log: logger + }); + + gExposedServer.on('error', function (error) { + debugExposed('start: server error ', error); + }); + + gExposedServer.search('ou=users,dc=cloudron', authenticateApp, userSearch); + gExposedServer.search('ou=groups,dc=cloudron', authenticateApp, groupSearch); + gExposedServer.bind('ou=users,dc=cloudron', authenticateApp, authenticateUser, authorizeUserForApp); + + gExposedServer.compare('cn=users,ou=groups,dc=cloudron', authenticateApp, groupUsersCompare); + gExposedServer.compare('cn=admins,ou=groups,dc=cloudron', authenticateApp, groupAdminsCompare); + + // just log that an attempt was made to unknown route, this helps a lot during app packaging + gExposedServer.use(function(req, res, next) { + debugExposed('not handled: dn %s, scope %s, filter %s (from %s)', req.dn ? req.dn.toString() : '-', req.scope, req.filter ? req.filter.toString() : '-', req.connection.ldap.id); + return next(); + }); + + await util.promisify(gExposedServer.listen.bind(gExposedServer))(constants.LDAPS_PORT, '0.0.0.0'); +} + +async function stopExposed() { + if (gExposedServer) gExposedServer.close(); +}