diff --git a/.changeset/wise-scissors-help.md b/.changeset/wise-scissors-help.md new file mode 100644 index 0000000000..c8fd2d4c44 --- /dev/null +++ b/.changeset/wise-scissors-help.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-defaults': patch +'@backstage/backend-app-api': patch +'@backstage/backend-common': patch +'@backstage/backend-plugin-api': patch +--- + +Updates to the config schema to match reality diff --git a/packages/backend-app-api/config.d.ts b/packages/backend-app-api/config.d.ts index 76d22db8b2..b5e56af67c 100644 --- a/packages/backend-app-api/config.d.ts +++ b/packages/backend-app-api/config.d.ts @@ -16,297 +16,12 @@ export interface Config { backend?: { - auth?: { - /** - * This disables the otherwise default auth policy, which requires all - * requests to be authenticated with either user or service credentials. - * - * Disabling this check means that the backend will no longer block - * unauthenticated requests, but instead allow them to pass through to - * plugins. - * - * If permissions are enabled, unauthenticated requests will be treated - * exactly as such, leaving it to the permission policy to determine what - * permissions should be allowed for an unauthenticated identity. Note - * that this will also apply to service-to-service calls between plugins - * unless you configure credentials for service calls. - */ - dangerouslyDisableDefaultAuthPolicy?: boolean; - - /** Controls how to store keys for plugin-to-plugin auth */ - pluginKeyStore?: - | { type: 'database' } - | { - type: 'static'; - static: { - /** - * Must be declared at least once and the first one will be used for signing. - */ - keys: Array<{ - /** - * Path to the public key file in the SPKI format. Should be an absolute path. - */ - publicKeyFile: string; - /** - * Path to the matching private key file in the PKCS#8 format. Should be an absolute path. - * - * The first array entry must specify a private key file, the rest must not. - */ - privateKeyFile?: string; - /** - * ID to uniquely identify this key within the JWK set. - */ - keyId: string; - /** - * JWS "alg" (Algorithm) Header Parameter value. Defaults to ES256. - * Must match the algorithm used to generate the keys in the provided files - */ - algorithm?: string; - }>; - }; - }; - - /** - * Configures methods of external access, ie ways for callers outside of - * the Backstage ecosystem to get authorized for access to APIs that do - * not permit unauthorized access. - */ - externalAccess?: Array< - | { - /** - * This is the legacy service-to-service access method, where a set - * of static keys were shared among plugins and used for symmetric - * signing and verification. These correspond to the old - * `backend.auth.keys` set and retain their behavior for backwards - * compatibility. Please migrate to other access methods when - * possible. - * - * Callers generate JWT tokens with the following payload: - * - * ```json - * { - * "sub": "backstage-plugin", - * "exp": - * } - * ``` - * - * And sign them with HS256, using the base64 decoded secret. The - * tokens are then passed along with requests in the Authorization - * header: - * - * ``` - * Authorization: Bearer eyJhbGciOiJIUzI... - * ``` - */ - type: 'legacy'; - options: { - /** - * Any set of base64 encoded random bytes to be used as both the - * signing and verification key. Should be sufficiently long so as - * not to be easy to guess by brute force. - * - * Can be generated eg using - * - * ```sh - * node -p 'require("crypto").randomBytes(24).toString("base64")' - * ``` - * - * @visibility secret - */ - secret: string; - - /** - * Sets the subject of the principal, when matching this token. - * Useful for debugging and tracking purposes. - */ - subject: string; - }; - /** - * Restricts what types of access that are permitted for this access - * method. If no access restrictions are given, it'll have unlimited - * access. This access restriction applies for the framework level; - * individual plugins may have their own access control mechanisms - * on top of this. - */ - accessRestrictions?: Array<{ - /** - * Permit access to make requests to this plugin. - * - * Can be further refined by setting additional fields below. - */ - plugin: string; - /** - * If given, this method is limited to only performing actions - * with these named permissions in this plugin. - * - * Note that this only applies where permissions checks are - * enabled in the first place. Endpoints that are not protected by - * the permissions system at all, are not affected by this - * setting. - */ - permission?: string | Array; - /** - * If given, this method is limited to only performing actions - * whose permissions have these attributes. - * - * Note that this only applies where permissions checks are - * enabled in the first place. Endpoints that are not protected by - * the permissions system at all, are not affected by this - * setting. - */ - permissionAttribute?: { - /** - * One of more of 'create', 'read', 'update', or 'delete'. - */ - action?: string | Array; - }; - }>; - } - | { - /** - * This access method consists of random static tokens that can be - * handed out to callers. - * - * The tokens are then passed along verbatim with requests in the - * Authorization header: - * - * ``` - * Authorization: Bearer eZv5o+fW3KnR3kVabMW4ZcDNLPl8nmMW - * ``` - */ - type: 'static'; - options: { - /** - * A raw token that can be any string, but for security reasons - * should be sufficiently long so as not to be easy to guess by - * brute force. - * - * Can be generated eg using - * - * ```sh - * node -p 'require("crypto").randomBytes(24).toString("base64")' - * ``` - * - * Since the tokens can be any string, you are free to add - * additional identifying data to them if you like. For example, - * adding a `freben-local-dev-` prefix for debugging purposes to a - * token that you know will be handed out for use as a personal - * access token during development. - * - * @visibility secret - */ - token: string; - - /** - * Sets the subject of the principal, when matching this token. - * Useful for debugging and tracking purposes. - */ - subject: string; - }; - /** - * Restricts what types of access that are permitted for this access - * method. If no access restrictions are given, it'll have unlimited - * access. This access restriction applies for the framework level; - * individual plugins may have their own access control mechanisms - * on top of this. - */ - accessRestrictions?: Array<{ - /** - * Permit access to make requests to this plugin. - * - * Can be further refined by setting additional fields below. - */ - plugin: string; - /** - * If given, this method is limited to only performing actions - * with these named permissions in this plugin. - * - * Note that this only applies where permissions checks are - * enabled in the first place. Endpoints that are not protected by - * the permissions system at all, are not affected by this - * setting. - */ - permission?: string | Array; - /** - * If given, this method is limited to only performing actions - * whose permissions have these attributes. - * - * Note that this only applies where permissions checks are - * enabled in the first place. Endpoints that are not protected by - * the permissions system at all, are not affected by this - * setting. - */ - permissionAttribute?: { - /** - * One of more of 'create', 'read', 'update', or 'delete'. - */ - action?: string | Array; - }; - }>; - } - | { - /** - * This access method consists of a JWKS endpoint that can be used to - * verify JWT tokens. - * - * Callers generate JWT tokens via 3rd party tooling - * and pass them in the Authorization header: - * - * ``` - * Authorization: Bearer eZv5o+fW3KnR3kVabMW4ZcDNLPl8nmMW - * ``` - */ - type: 'jwks'; - options: { - /** - * The full URL of the JWKS endpoint. - */ - url: string; - /** - * Sets the algorithm(s) that should be used to verify the JWT tokens. - * The passed JWTs must have been signed using one of the listed algorithms. - */ - algorithm?: string | string[]; - /** - * Sets the issuer(s) that should be used to verify the JWT tokens. - * Passed JWTs must have an `iss` claim which matches one of the specified issuers. - */ - issuer?: string | string[]; - /** - * Sets the audience(s) that should be used to verify the JWT tokens. - * The passed JWTs must have an "aud" claim that matches one of the audiences specified, - * or have no audience specified. - */ - audience?: string | string[]; - /** - * Sets an optional subject prefix. Passes the subject to called plugins. - * Useful for debugging and tracking purposes. - */ - subjectPrefix?: string; - }; - } - >; - }; - packages?: 'all' | { include?: string[]; exclude?: string[] }; - }; - - /** Discovery options. */ - discovery?: { - /** - * Endpoints - * - * A list of target baseUrls and the associated plugins. - */ - endpoints: { - /** - * The target baseUrl to use for the plugin - * - * Can be either a string or an object with internal and external keys. - * Targets with `{{pluginId}}` or `{{ pluginId }} in the url will be replaced with the pluginId. - */ - target: string | { internal: string; external: string }; - /** Array of plugins which use the target baseUrl. */ - plugins: string[]; - }[]; + /** Used by the feature discovery service */ + packages?: + | 'all' + | { + include?: string[]; + exclude?: string[]; + }; }; } diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 8844b07044..6146adfe83 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -210,6 +210,9 @@ export interface Config { defaultTtl?: number | HumanDuration; }; + /** + * Properties returned upon CORS requests to the backend, including the app-backend. + */ cors?: { origin?: string | string[]; methods?: string | string[]; @@ -221,6 +224,16 @@ export interface Config { optionsSuccessStatus?: number; }; + /** + * Content Security Policy options. + * + * The keys are the plain policy ID, e.g. "upgrade-insecure-requests". The + * values are on the format that the helmet library expects them, as an + * array of strings. There is also the special value false, which means to + * remove the default value that Backstage puts in place for that policy. + */ + csp?: { [policyId: string]: string[] | false }; + /** * Configuration related to URL reading, used for example for reading catalog info * files, scaffolder templates, and techdocs content. @@ -248,15 +261,5 @@ export interface Config { paths?: string[]; }>; }; - - /** - * Content Security Policy options. - * - * The keys are the plain policy ID, e.g. "upgrade-insecure-requests". The - * values are on the format that the helmet library expects them, as an - * array of strings. There is also the special value false, which means to - * remove the default value that Backstage puts in place for that policy. - */ - csp?: { [policyId: string]: string[] | false }; }; } diff --git a/packages/backend-defaults/config.d.ts b/packages/backend-defaults/config.d.ts index 1582cf1a39..fee8846c4b 100644 --- a/packages/backend-defaults/config.d.ts +++ b/packages/backend-defaults/config.d.ts @@ -14,8 +14,51 @@ * limitations under the License. */ +import { HumanDuration } from '@backstage/types'; + export interface Config { + app: { + baseUrl: string; // defined in core, but repeated here without doc + }; + backend?: { + /** + * The full base URL of the backend, as seen from the browser's point of + * view as it makes calls to the backend. + */ + baseUrl: string; + + /** Address that the backend should listen to. */ + listen?: + | string + | { + /** Address of the interface that the backend should bind to. */ + host?: string; + /** Port that the backend should listen to. */ + port?: string | number; + }; + + /** + * HTTPS configuration for the backend. If omitted the backend will serve HTTP. + * + * Setting this to `true` will cause self-signed certificates to be generated, which + * can be useful for local development or other non-production scenarios. + */ + https?: + | true + | { + /** Certificate configuration */ + certificate?: { + /** PEM encoded certificate. Use $file to load in a file */ + cert: string; + /** + * PEM encoded certificate key. Use $file to load in a file. + * @visibility secret + */ + key: string; + }; + }; + /** * Options used by the default auth, httpAuth and userInfo services. */ @@ -353,4 +396,178 @@ export interface Config { plugins: string[]; }>; }; + + /** Database connection configuration, select base database type using the `client` field */ + database: { + /** Default database client to use */ + client: 'better-sqlite3' | 'sqlite3' | 'pg'; + /** + * Base database connection string, or object with individual connection properties + * @visibility secret + */ + connection: + | string + | { + /** + * Password that belongs to the client User + * @visibility secret + */ + password?: string; + /** + * Other connection settings + */ + [key: string]: unknown; + }; + /** Database name prefix override */ + prefix?: string; + /** + * Whether to ensure the given database exists by creating it if it does not. + * Defaults to true if unspecified. + */ + ensureExists?: boolean; + /** + * Whether to ensure the given database schema exists by creating it if it does not. + * Defaults to false if unspecified. + * + * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + */ + ensureSchemaExists?: boolean; + /** + * How plugins databases are managed/divided in the provided database instance. + * + * `database` -> Plugins are each given their own database to manage their schemas/tables. + * + * `schema` -> Plugins will be given their own schema (in the specified/default database) + * to manage their tables. + * + * NOTE: Currently only supported by the `pg` client. + * + * @default database + */ + pluginDivisionMode?: 'database' | 'schema'; + /** Configures the ownership of newly created schemas in pg databases. */ + role?: string; + /** + * Arbitrary config object to pass to knex when initializing + * (https://knexjs.org/#Installation-client). Most notable is the debug + * and asyncStackTraces booleans + */ + knexConfig?: object; + /** Plugin specific database configuration and client override */ + plugin?: { + [pluginId: string]: { + /** Database client override */ + client?: 'better-sqlite3' | 'sqlite3' | 'pg'; + /** + * Database connection string or Knex object override + * @visibility secret + */ + connection?: string | object; + /** + * Whether to ensure the given database exists by creating it if it does not. + * Defaults to base config if unspecified. + */ + ensureExists?: boolean; + /** + * Whether to ensure the given database schema exists by creating it if it does not. + * Defaults to false if unspecified. + * + * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + */ + ensureSchemaExists?: boolean; + /** + * Arbitrary config object to pass to knex when initializing + * (https://knexjs.org/#Installation-client). Most notable is the + * debug and asyncStackTraces booleans. + * + * This is merged recursively into the base knexConfig + */ + knexConfig?: object; + /** Configures the ownership of newly created schemas in pg databases. */ + role?: string; + }; + }; + }; + + /** Cache connection configuration, select cache type using the `store` field */ + cache?: + | { + store: 'memory'; + /** An optional default TTL (in milliseconds). */ + defaultTtl?: number | HumanDuration; + } + | { + store: 'redis'; + /** + * A redis connection string in the form `redis://user:pass@host:port`. + * @visibility secret + */ + connection: string; + /** An optional default TTL (in milliseconds). */ + defaultTtl?: number | HumanDuration; + /** + * Whether or not [useRedisSets](https://github.com/jaredwray/keyv/tree/main/packages/redis#useredissets) should be configured to this redis cache. + * Defaults to true if unspecified. + */ + useRedisSets?: boolean; + } + | { + store: 'memcache'; + /** + * A memcache connection string in the form `user:pass@host:port`. + * @visibility secret + */ + connection: string; + /** An optional default TTL (in milliseconds). */ + defaultTtl?: number | HumanDuration; + }; + + cors?: { + origin?: string | string[]; + methods?: string | string[]; + allowedHeaders?: string | string[]; + exposedHeaders?: string | string[]; + credentials?: boolean; + maxAge?: number; + preflightContinue?: boolean; + optionsSuccessStatus?: number; + }; + + /** + * Content Security Policy options. + * + * The keys are the plain policy ID, e.g. "upgrade-insecure-requests". The + * values are on the format that the helmet library expects them, as an + * array of strings. There is also the special value false, which means to + * remove the default value that Backstage puts in place for that policy. + */ + csp?: { [policyId: string]: string[] | false }; + + /** + * Configuration related to URL reading, used for example for reading catalog info + * files, scaffolder templates, and techdocs content. + */ + reading?: { + /** + * A list of targets to allow outgoing requests to. Users will be able to make + * requests on behalf of the backend to the targets that are allowed by this list. + */ + allow?: Array<{ + /** + * A host to allow outgoing requests to, being either a full host or + * a subdomain wildcard pattern with a leading `*`. For example `example.com` + * and `*.example.com` are valid values, `prod.*.example.com` is not. + * The host may also contain a port, for example `example.com:8080`. + */ + host: string; + + /** + * An optional list of paths. In case they are present only targets matching + * any of them will are allowed. You can use trailing slashes to make sure only + * subdirectories are allowed, for example `/mydir/` will allow targets with + * paths like `/mydir/a` but will block paths like `/mydir2`. + */ + paths?: string[]; + }>; + }; } diff --git a/packages/backend-plugin-api/config.d.ts b/packages/backend-plugin-api/config.d.ts new file mode 100644 index 0000000000..21122d7238 --- /dev/null +++ b/packages/backend-plugin-api/config.d.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + backend?: { + /** + * An absolute path to a directory that can be used as a working dir, for + * example as scratch space for large operations. + * + * @remarks + * + * Note that this must be an absolute path. + * + * If not set, the operating system's designated temporary directory is + * commonly used, but that is implementation defined per plugin. + * + * Plugins are encouraged to heed this config setting if present, to allow + * deployment in severely locked-down or limited environments. + */ + workingDirectory?: string; + }; +} diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 94793e1e49..3a64b29317 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -40,7 +40,8 @@ } }, "files": [ - "dist" + "dist", + "config.d.ts" ], "scripts": { "build": "backstage-cli package build", @@ -67,5 +68,6 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^" - } + }, + "configSchema": "config.d.ts" }