Skip to content

Commit 84a1a8c

Browse files
authored
feat(config): allow server settings via config file with env precedence (#1554)
The server port, HTTPS server port, UI host, UI port and HTTPS UI port could only be set via environment variables, with defaults hard-coded in src/config/env.ts - unlike every other setting, which lives in the config schema/file. This legacy split is confusing for operators (#1553). Move these five settings into the config schema and proxy.config.json defaults, mirroring GIT_PROXY_COOKIE_SECRET: the environment variable (when set) takes precedence over the config file, which takes precedence over the built-in default. env.ts now exposes the raw environment values (undefined when unset) so the config file can supply the default. - Add serverPort, httpsServerPort, uiHost, uiPort and httpsUiPort to config.schema.json and proxy.config.json; regenerate config types and schema reference docs. - Resolve them with environment precedence in mergeConfigurations and add getServerPort/getHttpsServerPort/getUIHost/getHttpsUIPort getters; getUIPort now reads the merged config. - Switch the proxy, service, urls and auth-route call sites to the getters, reading lazily instead of at module load. This also fixes the OIDC auth redirect, which fell back to UI port 3000 instead of the configured port. Co-authored-by: Kris West <kristopher.west@natwest.com>
2 parents c841444 + 9b4f18a commit 84a1a8c

14 files changed

Lines changed: 443 additions & 171 deletions

File tree

config.schema.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,26 @@
1212
},
1313
"cookieSecret": { "type": "string" },
1414
"sessionMaxAgeHours": { "type": "number" },
15+
"serverPort": {
16+
"type": "number",
17+
"description": "Port the proxy HTTP server listens on. Can also be set with the GIT_PROXY_SERVER_PORT environment variable, which takes precedence over this value."
18+
},
19+
"httpsServerPort": {
20+
"type": "number",
21+
"description": "Port the proxy HTTPS server listens on. Can also be set with the GIT_PROXY_HTTPS_SERVER_PORT environment variable, which takes precedence over this value."
22+
},
23+
"uiHost": {
24+
"type": "string",
25+
"description": "Host of the GitProxy UI. Can also be set with the GIT_PROXY_UI_HOST environment variable, which takes precedence over this value."
26+
},
27+
"uiPort": {
28+
"type": "number",
29+
"description": "Port the GitProxy UI/service HTTP server listens on. Can also be set with the GIT_PROXY_UI_PORT environment variable, which takes precedence over this value."
30+
},
31+
"httpsUiPort": {
32+
"type": "number",
33+
"description": "Port the GitProxy UI/service HTTPS server listens on. Can also be set with the GIT_PROXY_HTTPS_UI_PORT environment variable, which takes precedence over this value."
34+
},
1535
"api": {
1636
"description": "Third party APIs",
1737
"type": "object",

proxy.config.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
{
22
"cookieSecret": "cookie secret",
33
"sessionMaxAgeHours": 12,
4+
"serverPort": 8000,
5+
"httpsServerPort": 8443,
6+
"uiHost": "http://localhost",
7+
"uiPort": 8080,
8+
"httpsUiPort": 8444,
49
"rateLimit": {
510
"windowMs": 60000,
611
"limit": 1000

src/config/env.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616

1717
import { ServerConfig } from './types';
1818

19+
// Server settings below default to `undefined` when the corresponding
20+
// environment variable is unset so that the config file can provide the
21+
// default. When an environment variable IS set it takes precedence over the
22+
// config file (see mergeConfigurations in ./index.ts), mirroring
23+
// GIT_PROXY_COOKIE_SECRET.
1924
const {
20-
GIT_PROXY_SERVER_PORT = 8000,
21-
GIT_PROXY_HTTPS_SERVER_PORT = 8443,
22-
GIT_PROXY_UI_HOST = 'http://localhost',
23-
GIT_PROXY_UI_PORT = 8080,
24-
GIT_PROXY_HTTPS_UI_PORT = 8444,
25+
GIT_PROXY_SERVER_PORT,
26+
GIT_PROXY_HTTPS_SERVER_PORT,
27+
GIT_PROXY_UI_HOST,
28+
GIT_PROXY_UI_PORT,
29+
GIT_PROXY_HTTPS_UI_PORT,
2530
GIT_PROXY_COOKIE_SECRET,
2631
GIT_PROXY_MONGO_CONNECTION_STRING = 'mongodb://localhost:27017/git-proxy',
2732
} = process.env;

src/config/generated/config.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ export interface GitProxyConfig {
5555
* Provide custom URLs for the git proxy interfaces in case it cannot determine its own URL
5656
*/
5757
domains?: Domains;
58+
/**
59+
* Port the proxy HTTPS server listens on. Can also be set with the
60+
* GIT_PROXY_HTTPS_SERVER_PORT environment variable, which takes precedence over this value.
61+
*/
62+
httpsServerPort?: number;
63+
/**
64+
* Port the GitProxy UI/service HTTPS server listens on. Can also be set with the
65+
* GIT_PROXY_HTTPS_UI_PORT environment variable, which takes precedence over this value.
66+
*/
67+
httpsUiPort?: number;
5868
/**
5969
* List of plugins to integrate on GitProxy's push or pull actions. Each value is either a
6070
* file path or a module name.
@@ -75,6 +85,11 @@ export interface GitProxyConfig {
7585
* API Rate limiting configuration.
7686
*/
7787
rateLimit?: RateLimit;
88+
/**
89+
* Port the proxy HTTP server listens on. Can also be set with the GIT_PROXY_SERVER_PORT
90+
* environment variable, which takes precedence over this value.
91+
*/
92+
serverPort?: number;
7893
sessionMaxAgeHours?: number;
7994
/**
8095
* List of database sources. The first source in the configuration with enabled=true will be
@@ -97,6 +112,16 @@ export interface GitProxyConfig {
97112
* TLS configuration for secure connections
98113
*/
99114
tls?: TLS;
115+
/**
116+
* Host of the GitProxy UI. Can also be set with the GIT_PROXY_UI_HOST environment variable,
117+
* which takes precedence over this value.
118+
*/
119+
uiHost?: string;
120+
/**
121+
* Port the GitProxy UI/service HTTP server listens on. Can also be set with the
122+
* GIT_PROXY_UI_PORT environment variable, which takes precedence over this value.
123+
*/
124+
uiPort?: number;
100125
/**
101126
* UI routes that require authentication (logged in or admin)
102127
*/
@@ -791,16 +816,21 @@ const typeMap: any = {
791816
{ json: 'cookieSecret', js: 'cookieSecret', typ: u(undefined, '') },
792817
{ json: 'csrfProtection', js: 'csrfProtection', typ: u(undefined, true) },
793818
{ json: 'domains', js: 'domains', typ: u(undefined, r('Domains')) },
819+
{ json: 'httpsServerPort', js: 'httpsServerPort', typ: u(undefined, 3.14) },
820+
{ json: 'httpsUiPort', js: 'httpsUiPort', typ: u(undefined, 3.14) },
794821
{ json: 'plugins', js: 'plugins', typ: u(undefined, a('')) },
795822
{ json: 'privateOrganizations', js: 'privateOrganizations', typ: u(undefined, a('any')) },
796823
{ json: 'proxyUrl', js: 'proxyUrl', typ: u(undefined, '') },
797824
{ json: 'rateLimit', js: 'rateLimit', typ: u(undefined, r('RateLimit')) },
825+
{ json: 'serverPort', js: 'serverPort', typ: u(undefined, 3.14) },
798826
{ json: 'sessionMaxAgeHours', js: 'sessionMaxAgeHours', typ: u(undefined, 3.14) },
799827
{ json: 'sink', js: 'sink', typ: u(undefined, a(r('Database'))) },
800828
{ json: 'sslCertPemPath', js: 'sslCertPemPath', typ: u(undefined, '') },
801829
{ json: 'sslKeyPemPath', js: 'sslKeyPemPath', typ: u(undefined, '') },
802830
{ json: 'tempPassword', js: 'tempPassword', typ: u(undefined, r('TempPassword')) },
803831
{ json: 'tls', js: 'tls', typ: u(undefined, r('TLS')) },
832+
{ json: 'uiHost', js: 'uiHost', typ: u(undefined, '') },
833+
{ json: 'uiPort', js: 'uiPort', typ: u(undefined, 3.14) },
804834
{ json: 'uiRouteAuth', js: 'uiRouteAuth', typ: u(undefined, r('UIRouteAuth')) },
805835
{ json: 'upstreamProxy', js: 'upstreamProxy', typ: u(undefined, r('UpstreamProxy')) },
806836
{ json: 'urlShortener', js: 'urlShortener', typ: u(undefined, '') },

src/config/index.ts

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,18 @@ const REQUIRED_TOP_LEVEL_CONFIG_KEYS = [
4747
'cookieSecret',
4848
'csrfProtection',
4949
'domains',
50+
'httpsServerPort',
51+
'httpsUiPort',
5052
'plugins',
5153
'privateOrganizations',
5254
'rateLimit',
55+
'serverPort',
5356
'sessionMaxAgeHours',
5457
'sink',
5558
'tempPassword',
5659
'tls',
60+
'uiHost',
61+
'uiPort',
5762
'uiRouteAuth',
5863
'upstreamProxy',
5964
'urlShortener',
@@ -106,6 +111,22 @@ function cleanUndefinedValues(obj: any): any {
106111
return cleaned;
107112
}
108113

114+
/**
115+
* Resolve a numeric server setting, giving precedence to the environment
116+
* variable (when set) over the user config file and then the default config,
117+
* mirroring the handling of GIT_PROXY_COOKIE_SECRET.
118+
*/
119+
function resolveServerPort(
120+
envValue: string | undefined,
121+
userValue: number | undefined,
122+
defaultValue: number | undefined,
123+
): number | undefined {
124+
if (envValue !== undefined) {
125+
return Number(envValue);
126+
}
127+
return userValue ?? defaultValue;
128+
}
129+
109130
/**
110131
* Load and merge default + user configuration with QuickType validation
111132
* @return {FullGitProxyConfig} The merged and validated configuration
@@ -191,6 +212,28 @@ function mergeConfigurations(
191212
serverConfig.GIT_PROXY_COOKIE_SECRET ||
192213
userSettings.cookieSecret ||
193214
defaultConfig.cookieSecret,
215+
// Server settings: environment variable takes precedence over the config file.
216+
serverPort: resolveServerPort(
217+
serverConfig.GIT_PROXY_SERVER_PORT,
218+
userSettings.serverPort,
219+
defaultConfig.serverPort,
220+
),
221+
httpsServerPort: resolveServerPort(
222+
serverConfig.GIT_PROXY_HTTPS_SERVER_PORT,
223+
userSettings.httpsServerPort,
224+
defaultConfig.httpsServerPort,
225+
),
226+
uiHost: serverConfig.GIT_PROXY_UI_HOST || userSettings.uiHost || defaultConfig.uiHost,
227+
uiPort: resolveServerPort(
228+
serverConfig.GIT_PROXY_UI_PORT,
229+
userSettings.uiPort,
230+
defaultConfig.uiPort,
231+
),
232+
httpsUiPort: resolveServerPort(
233+
serverConfig.GIT_PROXY_HTTPS_UI_PORT,
234+
userSettings.httpsUiPort,
235+
defaultConfig.httpsUiPort,
236+
),
194237
};
195238

196239
assertHasRequiredTopLevelConfig(config);
@@ -226,9 +269,29 @@ export const getAuthorisedList = () => {
226269
return config.authorisedList;
227270
};
228271

229-
// Get GIT_PROXY_UI_PORT
272+
// Get the UI/service HTTP port
230273
export const getUIPort = (): number => {
231-
return Number(serverConfig.GIT_PROXY_UI_PORT);
274+
return loadFullConfiguration().uiPort;
275+
};
276+
277+
// Get the proxy HTTP server port
278+
export const getServerPort = (): number => {
279+
return loadFullConfiguration().serverPort;
280+
};
281+
282+
// Get the proxy HTTPS server port
283+
export const getHttpsServerPort = (): number => {
284+
return loadFullConfiguration().httpsServerPort;
285+
};
286+
287+
// Get the UI host
288+
export const getUIHost = (): string => {
289+
return loadFullConfiguration().uiHost;
290+
};
291+
292+
// Get the UI/service HTTPS port
293+
export const getHttpsUIPort = (): number => {
294+
return loadFullConfiguration().httpsUiPort;
232295
};
233296

234297
// Gets a list of authorised repositories

src/config/types.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
import { GitProxyConfig } from './generated/config';
1818

1919
export type ServerConfig = {
20-
GIT_PROXY_SERVER_PORT: string | number;
21-
GIT_PROXY_HTTPS_SERVER_PORT: string | number;
22-
GIT_PROXY_UI_HOST: string;
23-
GIT_PROXY_UI_PORT: string | number;
24-
GIT_PROXY_HTTPS_UI_PORT: string | number;
20+
GIT_PROXY_SERVER_PORT: string | undefined;
21+
GIT_PROXY_HTTPS_SERVER_PORT: string | undefined;
22+
GIT_PROXY_UI_HOST: string | undefined;
23+
GIT_PROXY_UI_PORT: string | undefined;
24+
GIT_PROXY_HTTPS_UI_PORT: string | undefined;
2525
GIT_PROXY_COOKIE_SECRET: string | undefined;
2626
GIT_PROXY_MONGO_CONNECTION_STRING: string;
2727
};

src/proxy/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,13 @@ import {
2525
getTLSKeyPemPath,
2626
getTLSCertPemPath,
2727
getTLSEnabled,
28+
getServerPort,
29+
getHttpsServerPort,
2830
} from '../config';
2931
import { addUserCanAuthorise, addUserCanPush, createRepo, getRepos } from '../db';
3032
import { PluginLoader } from '../plugin';
3133
import chain from './chain';
3234
import { Repo } from '../db/types';
33-
import { serverConfig } from '../config/env';
34-
35-
const { GIT_PROXY_SERVER_PORT: proxyHttpPort, GIT_PROXY_HTTPS_SERVER_PORT: proxyHttpsPort } =
36-
serverConfig;
3735

3836
interface ServerOptions {
3937
inflate: boolean;
@@ -95,6 +93,8 @@ export class Proxy {
9593
}
9694

9795
public async start() {
96+
const proxyHttpPort = getServerPort();
97+
const proxyHttpsPort = getHttpsServerPort();
9898
await this.proxyPreparations();
9999
this.expressApp = await this.createApp();
100100
await new Promise<void>((resolve, reject) => {

src/service/index.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,12 @@ import lusca from 'lusca';
2626

2727
import * as config from '../config';
2828
import * as db from '../db';
29-
import { serverConfig } from '../config/env';
3029
import { Proxy } from '../proxy';
3130
import routes from './routes';
3231
import { configure } from './passport';
3332

3433
const limiter = rateLimit(config.getRateLimit());
3534

36-
const { GIT_PROXY_UI_PORT: uiPort, GIT_PROXY_HTTPS_UI_PORT: uiHttpsPort } = serverConfig;
37-
3835
const app: Express = express();
3936
let _httpServer: http.Server | null = null;
4037
let _httpsServer: https.Server | null = null;
@@ -199,17 +196,17 @@ async function start(proxy: Proxy) {
199196
const app = await createApp(proxy);
200197

201198
_httpServer = http.createServer(app);
202-
_httpServer.listen(uiPort);
199+
_httpServer.listen(config.getUIPort());
203200

204-
console.log(`Service Listening on ${uiPort}`);
201+
console.log(`Service Listening on ${config.getUIPort()}`);
205202
app.emit('ready');
206203

207204
if (config.getTLSEnabled()) {
208205
await new Promise<void>((resolve, reject) => {
209206
const server = https.createServer(getServiceTLSOptions(), app);
210207
server.on('error', reject);
211-
server.listen(uiHttpsPort, () => {
212-
console.log(`HTTPS Service Listening on ${uiHttpsPort}`);
208+
server.listen(config.getHttpsUIPort(), () => {
209+
console.log(`HTTPS Service Listening on ${config.getHttpsUIPort()}`);
213210
resolve();
214211
});
215212
_httpsServer = server;
@@ -228,7 +225,7 @@ async function stop(): Promise<void> {
228225
if (_httpServer) {
229226
closePromises.push(
230227
new Promise((resolve, reject) => {
231-
console.log(`Stopping Service Listening on ${uiPort}`);
228+
console.log(`Stopping Service Listening on ${config.getUIPort()}`);
232229
_httpServer!.close((err) => {
233230
if (err) {
234231
reject(err);

src/service/routes/auth.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import express, { Request, Response, NextFunction } from 'express';
1818
import { getPassport, authStrategies } from '../passport';
19-
import { getAuthMethods } from '../../config';
19+
import { getAuthMethods, getUIHost, getUIPort } from '../../config';
2020

2121
import * as db from '../../db';
2222
import * as passportLocal from '../passport/local';
@@ -31,9 +31,6 @@ import { handleErrorAndLog } from '../../utils/errors';
3131
const router = express.Router();
3232
const passport = getPassport();
3333

34-
const { GIT_PROXY_UI_HOST: uiHost = 'http://localhost', GIT_PROXY_UI_PORT: uiPort = 3000 } =
35-
process.env;
36-
3734
router.get('/', (_req: Request, res: Response) => {
3835
res.status(200).json({
3936
login: {
@@ -137,7 +134,7 @@ router.get('/openidconnect/callback', (req: Request, res: Response, next: NextFu
137134
return res.status(500).end();
138135
}
139136
console.log('Logged in successfully. User:', user);
140-
return res.redirect(`${uiHost}:${uiPort}/dashboard/profile`);
137+
return res.redirect(`${getUIHost()}:${getUIPort()}/dashboard/profile`);
141138
});
142139
},
143140
)(req, res, next);

src/service/urls.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,24 @@
1616

1717
import { Request } from 'express';
1818

19-
import { serverConfig } from '../config/env';
2019
import * as config from '../config';
2120

22-
const { GIT_PROXY_SERVER_PORT: PROXY_HTTP_PORT, GIT_PROXY_UI_PORT: UI_PORT } = serverConfig;
23-
2421
export const getProxyURL = (req: Request): string => {
2522
return (
2623
config.getDomains().proxy ??
27-
`${req.protocol}://${req.headers.host}`.replace(`:${UI_PORT}`, `:${PROXY_HTTP_PORT}`)
24+
`${req.protocol}://${req.headers.host}`.replace(
25+
`:${config.getUIPort()}`,
26+
`:${config.getServerPort()}`,
27+
)
2828
);
2929
};
3030

3131
export const getServiceUIURL = (req: Request): string => {
3232
return (
3333
config.getDomains().service ??
34-
`${req.protocol}://${req.headers.host}`.replace(`:${PROXY_HTTP_PORT}`, `:${UI_PORT}`)
34+
`${req.protocol}://${req.headers.host}`.replace(
35+
`:${config.getServerPort()}`,
36+
`:${config.getUIPort()}`,
37+
)
3538
);
3639
};

0 commit comments

Comments
 (0)