diff --git a/src/features/os-config/index.ts b/src/features/os-config/index.ts index 4885a972b..5a4ef9861 100644 --- a/src/features/os-config/index.ts +++ b/src/features/os-config/index.ts @@ -1,27 +1,45 @@ -import type { Application, RequestHandler } from 'express'; +import type { Application } from 'express'; import { DEVICE_CONFIG_OPENVPN_CA, DEVICE_CONFIG_OPENVPN_CONFIG, DEVICE_CONFIG_SSH_AUTHORIZED_KEYS, + LOGS_HOST, } from '../../lib/config'; import { b64decode } from '../../lib/utils'; -const getOsConfiguration: RequestHandler = (_req, res) => { - res.json({ - services: { - openvpn: { - config: DEVICE_CONFIG_OPENVPN_CONFIG, - ca: b64decode(DEVICE_CONFIG_OPENVPN_CA), - }, - ssh: { - authorized_keys: DEVICE_CONFIG_SSH_AUTHORIZED_KEYS, - }, - }, - schema_version: '1.0.0', - }); +// OS service configurations +const services = { + openvpn: { + config: DEVICE_CONFIG_OPENVPN_CONFIG, + ca: b64decode(DEVICE_CONFIG_OPENVPN_CA), + }, + ssh: { + authorized_keys: DEVICE_CONFIG_SSH_AUTHORIZED_KEYS, + }, +}; + +// Config.json migrations: Changes should be evaluated for security risks before applying. +// +// - A field may not be deleted from config.json. +// - A field with a value of non-null tells os-config that the value will be updated +// to the new value if it's different. +// - A field not found in the whitelist of the os-config schema will be ignored. +const config = { + overrides: { + ...(LOGS_HOST != null && { logsEndpoint: `https://${LOGS_HOST}` }), + }, }; export const setup = (app: Application) => { - app.get('/os/v1/config/', getOsConfiguration); + app.get('/os/v1/config/', (_req, res) => { + res.json({ + services, + // Older os-configs don't know how to handle the config field, but + // luckily serde-rs ignores unknown fields by default. + config, + /** @deprecated schema_version is an outdated field kept for compatibility with legacy os-configs */ + schema_version: '1.0.0', + }); + }); }; diff --git a/test/22_os-config.ts b/test/22_os-config.ts new file mode 100644 index 000000000..00d36b182 --- /dev/null +++ b/test/22_os-config.ts @@ -0,0 +1,35 @@ +import { expect } from 'chai'; + +import { supertest } from './test-lib/supertest'; +import { LOGS_HOST } from '../src/lib/config'; + +describe('OS configuration endpoints', () => { + describe('/os/v1/config', () => { + it('should return a valid JSON response', async () => { + const { body } = await supertest().get('/os/v1/config').expect(200); + + // Service configurations should be valid for their respective services + expect(body) + .to.have.property('services') + .that.has.all.keys('openvpn', 'ssh'); + expect(body.services.openvpn).to.have.all.keys('config', 'ca'); + expect(body.services.openvpn.config).to.be.a('string'); + expect(body.services.openvpn.ca).to.be.a('string'); + expect(body.services.ssh) + .to.have.property('authorized_keys') + .that.is.a('string'); + + // schema_version is kept for backwards compatibility + expect(body).to.have.property('schema_version').that.equals('1.0.0'); + + // Config should contain config.json overrides + expect(body) + .to.have.property('config') + .that.deep.equals({ + overrides: { + ...(LOGS_HOST != null && { logsEndpoint: `https://${LOGS_HOST}` }), + }, + }); + }); + }); +});