import { ByteSizeValue , schema , TypeOf } from '@osd/config-schema' ; import { hostname } from 'os' ; import { CspConfigType , CspConfig , ICspConfig } from '../csp' ; import { SslConfig , sslSchema } from './ssl_config' ; const validBasePathRegex = /^\/.*[^\/]$/ ; const uuidRegexp = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i ; const match = ( regex , errorMsg ) => ( str ) => regex . test ( str ) ? undefined : errorMsg ; export const config = { path : 'server' , schema : schema . object ( { name : schema . string ( { defaultValue : ( ) => hostname ( ) } ) , autoListen : schema . boolean ( { defaultValue : true } ) , basePath : schema . maybe ( schema . string ( { validate : match ( validBasePathRegex , "must start with a slash, don't end with one" ) , } ) ) , cors : schema . boolean ( { defaultValue : false } ) , customResponseHeaders : schema . recordOf ( schema . string ( ) , schema . any ( ) , { defaultValue : { } , } ) , host : schema . string ( { defaultValue : 'localhost' , hostname : true , } ) , maxPayload : schema . byteSize ( { defaultValue : '1048576b' , } ) , port : schema . number ( { defaultValue : 5601 , } ) , rewriteBasePath : schema . boolean ( { defaultValue : false } ) , ssl : sslSchema , keepaliveTimeout : schema . number ( { defaultValue : 120000 , } ) , socketTimeout : schema . number ( { defaultValue : 120000 , } ) , compression : schema . object ( { enabled : schema . boolean ( { defaultValue : true } ) , referrerWhitelist : schema . maybe ( schema . arrayOf ( schema . string ( { hostname : true , } ) , { minSize : 1 } ) ) , } ) , uuid : schema . maybe ( schema . string ( { validate : match ( uuidRegexp , 'must be a valid uuid' ) , } ) ) , xsrf : schema . object ( { disableProtection : schema . boolean ( { defaultValue : false } ) , whitelist : schema . arrayOf ( schema . string ( { validate : match ( /^\// , 'must start with a slash' ) } ) , { defaultValue : [ ] } ) , } ) , requestId : schema . object ( { allowFromAnyIp : schema . boolean ( { defaultValue : false } ) , ipAllowlist : schema . arrayOf ( schema . ip ( ) , { defaultValue : [ ] } ) , } , { validate ( value ) { if ( value . allowFromAnyIp === true && value . ipAllowlist ?. length > 0 ) { return `allowFromAnyIp must be set to 'false' if any values are specified in ipAllowlist` ; } } , } ) , } , { validate : ( rawConfig ) => { if ( ! rawConfig . basePath && rawConfig . rewriteBasePath ) { return 'cannot use [rewriteBasePath] when [basePath] is not specified' ; } if ( ! rawConfig . compression . enabled && rawConfig . compression . referrerWhitelist ) { return 'cannot use [compression.referrerWhitelist] when [compression.enabled] is set to false' ; } if ( rawConfig . ssl . enabled && rawConfig . ssl . redirectHttpFromPort !== undefined && rawConfig . ssl . redirectHttpFromPort === rawConfig . port ) { return ( 'OpenSearch Dashboards does not accept http traffic to [port] when ssl is ' + 'enabled (only https is allowed), so [ssl.redirectHttpFromPort] ' + `cannot be configured to the same value. Both are [${ rawConfig . port }].` ) ; } } , } ) , } ; export type HttpConfigType = TypeOf < typeof config . schema > ; export class HttpConfig { public name : string ; public autoListen : boolean ; public host : string ; public keepaliveTimeout : number ; public socketTimeout : number ; public port : number ; public cors : boolean | { origin : string [ ] ; } ; public customResponseHeaders : Record < string , string | string [ ] > ; public maxPayload : ByteSizeValue ; public basePath ? : string ; public rewriteBasePath : boolean ; public ssl : SslConfig ; public compression : { enabled : boolean ; referrerWhitelist ? : string [ ] ; } ; public csp : ICspConfig ; public xsrf : { disableProtection : boolean ; whitelist : string [ ] ; } ; public requestId : { allowFromAnyIp : boolean ; ipAllowlist : string [ ] ; } ; internal constructor ( rawHttpConfig , rawCspConfig ) { this . autoListen = rawHttpConfig . autoListen ; this . host = rawHttpConfig . host ; this . port = rawHttpConfig . port ; this . cors = rawHttpConfig . cors ; this . customResponseHeaders = Object . entries ( rawHttpConfig . customResponseHeaders ?? { } ) . reduce ( ( headers , [ key , value ] ) => { return { ... headers , [ key ] : Array . isArray ( value ) ? value . map ( ( e ) => convertHeader ( e ) ) : convertHeader ( value ) , } ; } , { } ) ; this . maxPayload = rawHttpConfig . maxPayload ; this . name = rawHttpConfig . name ; this . basePath = rawHttpConfig . basePath ; this . keepaliveTimeout = rawHttpConfig . keepaliveTimeout ; this . socketTimeout = rawHttpConfig . socketTimeout ; this . rewriteBasePath = rawHttpConfig . rewriteBasePath ; this . ssl = new SslConfig ( rawHttpConfig . ssl || { } ) ; this . compression = rawHttpConfig . compression ; this . csp = new CspConfig ( rawCspConfig ) ; this . xsrf = rawHttpConfig . xsrf ; this . requestId = rawHttpConfig . requestId ; } } const convertHeader = ( entry ) : string => { return typeof entry === 'object' ? JSON . stringify ( entry ) : String ( entry ) ; } ;