diff --git a/src/datastore/AppsDataStore.ts b/src/datastore/AppsDataStore.ts index 1111e28..dd98aa6 100644 --- a/src/datastore/AppsDataStore.ts +++ b/src/datastore/AppsDataStore.ts @@ -169,7 +169,7 @@ class AppsDataStore { ) { // we have required info passwordToBeEncrypted = pushWebhook.repoInfo.password - sshKeyToBeEncrypted = pushWebhook.repoInfo.sshKey + sshKeyToBeEncrypted = pushWebhook.repoInfo.sshKey || '' pushWebhook.repoInfo.password = '' pushWebhook.repoInfo.sshKey = '' } else { @@ -776,62 +776,6 @@ class AppsDataStore { }) } - getAppsServerConfig( - defaultAppNginxConfig: string, - hasRootSsl: boolean, - rootDomain: string - ) { - const self = this - - const servers: IServerBlockDetails[] = [] - - return self.getAppDefinitions().then(function(apps) { - Object.keys(apps).forEach(function(appName) { - const webApp = apps[appName] - - if (webApp.notExposeAsWebApp) { - return - } - - const localDomain = self.getServiceName(appName) - const forceSsl = !!webApp.forceSsl - const nginxConfigTemplate = - webApp.customNginxConfig || defaultAppNginxConfig - - const serverWithSubDomain = {} as IServerBlockDetails - serverWithSubDomain.hasSsl = - hasRootSsl && webApp.hasDefaultSubDomainSsl - serverWithSubDomain.publicDomain = appName + '.' + rootDomain - serverWithSubDomain.localDomain = localDomain - serverWithSubDomain.forceSsl = forceSsl - const httpPort = webApp.containerHttpPort || 80 - serverWithSubDomain.containerHttpPort = httpPort - serverWithSubDomain.nginxConfigTemplate = nginxConfigTemplate - - servers.push(serverWithSubDomain) - - // adding custom domains - const customDomainArray = webApp.customDomain - if (customDomainArray && customDomainArray.length > 0) { - for (let idx = 0; idx < customDomainArray.length; idx++) { - const d = customDomainArray[idx] - servers.push({ - containerHttpPort: httpPort, - hasSsl: d.hasSsl, - forceSsl: forceSsl, - publicDomain: d.publicDomain, - localDomain: localDomain, - nginxConfigTemplate: nginxConfigTemplate, - staticWebRoot: '', - customErrorPagesDirectory: '', - }) - } - } - }) - - return servers - }) - } } export = AppsDataStore diff --git a/src/datastore/DataStore.ts b/src/datastore/DataStore.ts index 8062580..deb73cd 100644 --- a/src/datastore/DataStore.ts +++ b/src/datastore/DataStore.ts @@ -136,38 +136,6 @@ class DataStore { return !!this.data.get(CUSTOM_DOMAIN) } - getServerList() { - const self = this - - let hasRootSsl: boolean - let rootDomain: string - - return Promise.resolve() - .then(function() { - return self.getHasRootSsl() - }) - .then(function(val: boolean) { - hasRootSsl = val - - return self.getRootDomain() - }) - .then(function(val) { - rootDomain = val - }) - .then(function() { - return self.getDefaultAppNginxConfig() - }) - .then(function(defaultAppNginxConfig) { - return self - .getAppsDataStore() - .getAppsServerConfig( - defaultAppNginxConfig, - hasRootSsl, - rootDomain - ) - }) - } - getAppsDataStore() { return this.appsDataStore } diff --git a/src/models/AppDefinition.ts b/src/models/AppDefinition.ts index 9a7c20a..a59bf1a 100644 --- a/src/models/AppDefinition.ts +++ b/src/models/AppDefinition.ts @@ -24,7 +24,7 @@ interface RepoInfo { repo: string branch: string user: string - sshKey: string + sshKey?: string password: string } @@ -32,7 +32,7 @@ interface RepoInfoEncrypted { repo: string branch: string user: string - sshKeyEncrypted: string + sshKeyEncrypted?: string passwordEncrypted: string } @@ -54,6 +54,7 @@ interface IAppDefinitionBase { hasPersistentData: boolean hasDefaultSubDomainSsl: boolean containerHttpPort?: number + httpBasicAuth?: string captainDefinitionRelativeFilePath: string forceSsl: boolean nodeId?: string diff --git a/src/models/IServerBlockDetails.ts b/src/models/IServerBlockDetails.ts index f78c77c..986530a 100644 --- a/src/models/IServerBlockDetails.ts +++ b/src/models/IServerBlockDetails.ts @@ -5,6 +5,8 @@ interface IServerBlockDetails { localDomain: string nginxConfigTemplate: string containerHttpPort: number + httpBasicAuth?: string + httpBasicAuthPath?: string crtPath?: string keyPath?: string diff --git a/src/user/ImageMaker.ts b/src/user/ImageMaker.ts index 6672609..f246767 100644 --- a/src/user/ImageMaker.ts +++ b/src/user/ImageMaker.ts @@ -311,7 +311,7 @@ export default class ImageMaker { return GitHelper.clone( srcRepo.user, srcRepo.password, - srcRepo.sshKey, + srcRepo.sshKey || '', srcRepo.repo, srcRepo.branch, destDirectory diff --git a/src/user/system/LoadBalancerManager.ts b/src/user/system/LoadBalancerManager.ts index ca2bd12..7defa68 100644 --- a/src/user/system/LoadBalancerManager.ts +++ b/src/user/system/LoadBalancerManager.ts @@ -10,11 +10,14 @@ import DataStore = require('../../datastore/DataStore') import CertbotManager = require('./CertbotManager') import { AnyError } from '../../models/OtherTypes' import LoadBalancerInfo from '../../models/LoadBalancerInfo' +import * as path from 'path' const defaultPageTemplate = fs .readFileSync(__dirname + '/../../../template/default-page.ejs') .toString() +const CONTAINER_PATH_OF_CONFIG = '/etc/nginx/conf.d' + class LoadBalancerManager { private reloadInProcess: boolean private requestedReloadPromises: { @@ -90,7 +93,7 @@ class LoadBalancerManager { return fs.remove(FUTURE) }) .then(function() { - return dataStore.getServerList() + return self.getServerList(dataStore) }) .then(function(servers) { const promises: Promise[] = [] @@ -113,8 +116,23 @@ class LoadBalancerManager { CaptainConstants.nginxStaticRootDir + CaptainConstants.nginxDefaultHtmlDir + const pathOfAuthInHost = + configFilePathBase + '-' + s.publicDomain + '.auth' + promises.push( Promise.resolve() + .then(function() { + if (s.httpBasicAuth) { + s.httpBasicAuthPath = path.join( + CONTAINER_PATH_OF_CONFIG, + path.basename(pathOfAuthInHost) + ) + return fs.outputFile( + pathOfAuthInHost, + s.httpBasicAuth + ) + } + }) .then(function() { return ejs.render(s.nginxConfigTemplate, { s: s, @@ -162,6 +180,108 @@ class LoadBalancerManager { }) } + getServerList(dataStore: DataStore) { + const self = this + + let hasRootSsl: boolean + let rootDomain: string + + return Promise.resolve() + .then(function() { + return dataStore.getHasRootSsl() + }) + .then(function(val: boolean) { + hasRootSsl = val + + return dataStore.getRootDomain() + }) + .then(function(val) { + rootDomain = val + }) + .then(function() { + return dataStore.getDefaultAppNginxConfig() + }) + .then(function(defaultAppNginxConfig) { + return self.getAppsServerConfig( + dataStore, + defaultAppNginxConfig, + hasRootSsl, + rootDomain + ) + }) + } + + getAppsServerConfig( + dataStore: DataStore, + defaultAppNginxConfig: string, + hasRootSsl: boolean, + rootDomain: string + ) { + const self = this + + const servers: IServerBlockDetails[] = [] + + return dataStore + .getAppsDataStore() + .getAppDefinitions() + .then(function(apps) { + Object.keys(apps).forEach(function(appName) { + const webApp = apps[appName] + const httpBasicAuth = webApp.httpBasicAuth || '' + + if (webApp.notExposeAsWebApp) { + return + } + + const localDomain = dataStore + .getAppsDataStore() + .getServiceName(appName) + const forceSsl = !!webApp.forceSsl + const nginxConfigTemplate = + webApp.customNginxConfig || defaultAppNginxConfig + + const serverWithSubDomain = {} as IServerBlockDetails + serverWithSubDomain.hasSsl = + hasRootSsl && webApp.hasDefaultSubDomainSsl + serverWithSubDomain.publicDomain = + appName + '.' + rootDomain + serverWithSubDomain.localDomain = localDomain + serverWithSubDomain.forceSsl = forceSsl + const httpPort = webApp.containerHttpPort || 80 + serverWithSubDomain.containerHttpPort = httpPort + serverWithSubDomain.nginxConfigTemplate = nginxConfigTemplate + serverWithSubDomain.httpBasicAuth = httpBasicAuth + + servers.push(serverWithSubDomain) + + // adding custom domains + const customDomainArray = webApp.customDomain + if (customDomainArray && customDomainArray.length > 0) { + for ( + let idx = 0; + idx < customDomainArray.length; + idx++ + ) { + const d = customDomainArray[idx] + servers.push({ + containerHttpPort: httpPort, + hasSsl: d.hasSsl, + forceSsl: forceSsl, + publicDomain: d.publicDomain, + localDomain: localDomain, + nginxConfigTemplate: nginxConfigTemplate, + staticWebRoot: '', + customErrorPagesDirectory: '', + httpBasicAuth: httpBasicAuth, + }) + } + } + }) + + return servers + }) + } + sendReloadSignal() { return this.dockerApi.sendSingleContainerKillHUP( CaptainConstants.nginxServiceName @@ -512,7 +632,7 @@ class LoadBalancerManager { hostPath: CaptainConstants.baseNginxConfigPath, }, { - containerPath: '/etc/nginx/conf.d', + containerPath: CONTAINER_PATH_OF_CONFIG, hostPath: CaptainConstants.perAppNginxConfigPathBase, }, diff --git a/template/server-block-conf.ejs b/template/server-block-conf.ejs index fd385ee..9b27510 100644 --- a/template/server-block-conf.ejs +++ b/template/server-block-conf.ejs @@ -60,6 +60,16 @@ if (!s.forceSsl || s.hasSsl) { set $upstream http://<%-s.localDomain%>:<%-s.containerHttpPort%>; location / { + + <% + if (s.httpBasicAuthPath) { + %> + auth_basic "Restricted Access"; + auth_basic_user_file <%-s.httpBasicAuthPath%>; + <% + } + %> + proxy_pass $upstream; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;