diff --git a/docs/UAA-Configuration-Reference.md b/docs/UAA-Configuration-Reference.md index 7fc4a0a401f..6dfeccefd21 100644 --- a/docs/UAA-Configuration-Reference.md +++ b/docs/UAA-Configuration-Reference.md @@ -93,7 +93,7 @@ or `$CLOUDFOUNDRY_CONFIG_PATH/uaa.yml`. | `servlet.session-cookie.encode-base64` | `true`| Base64-encode session cookie| | `servlet.session-cookie.max-age` | — (null)| Session cookie max age| | `servlet.idle-timeout` | `1800`| Session idle timeout (s)| -| `servlet.filtered-headers` | `X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, X-Forwarded-Prefix, Forwarded`| Headers to filter| +| `servlet.filtered-headers` | `X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, X-Forwarded-Prefix, Forwarded, X-Client-IP, X-Real-IP`| Headers to filter| ### JWT Token Policy @@ -379,7 +379,6 @@ or `$CLOUDFOUNDRY_CONFIG_PATH/uaa.yml`. |----------|---------|-------------| | `logging.file.name.path` | —| Log file directory| - --- ## Detailed Property Descriptions @@ -433,6 +432,7 @@ other contexts where the login page URL is needed. Typically the same as `uaa.ur **Type:** `String` (comma-separated) Controls which Spring profiles are active. Common values: + - `hsqldb` — in-memory HSQLDB (default for testing) - `postgresql` — PostgreSQL database - `mysql` — MySQL/MariaDB database @@ -554,6 +554,7 @@ Set to `false` to require email verification before allowing authentication. **Type:** `String` The fully-qualified JDBC driver class name. Typically auto-resolved from the active profile: + - HSQLDB: `org.hsqldb.jdbc.JDBCDriver` - PostgreSQL: `org.postgresql.Driver` - MySQL: `org.mariadb.jdbc.Driver` @@ -812,6 +813,7 @@ Use this to work around database driver parameter limits. **Type:** `String` The session storage mechanism. Accepted values: + - `memory` — In-memory sessions (default, suitable for multi-instance deployments that support sticky sessions) - `database` — Database-backed sessions via Spring Session JDBC @@ -859,7 +861,7 @@ Session idle timeout in seconds. After this period of inactivity, the session ex ### `servlet.filtered-headers` -**Default:** `[X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, X-Forwarded-Prefix, Forwarded]` +**Default:** `[X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto, X-Forwarded-Prefix, Forwarded, X-Client-IP, X-Real-IP]` **Source:** `@ConfigurationProperties(prefix = "servlet")` in [`UaaProperties.Servlet`](../server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java) **Type:** `List` @@ -956,6 +958,7 @@ enabling key rotation. **Type:** `Map>` A map of key IDs to signing key configurations. Each entry has: + - `signingKey` — RSA private key in PEM format - `signingAlg` — Algorithm (e.g. `RS256`) @@ -1032,6 +1035,7 @@ Global default for refresh token lifetime in seconds. Applies to all zones unles **Type:** `String` Format of issued refresh tokens. Accepted values: + - `opaque` — Opaque, randomly generated token stored in the database - `jwt` — Self-contained JWT refresh token @@ -1125,6 +1129,7 @@ the `authorities` claim: `exclude: [authorities]`. **Type:** `Map` Bootstrap client definitions loaded at startup. Each entry is keyed by client ID and contains: + - `id` — Client ID - `secret` — Client secret - `authorized-grant-types` — Comma-separated grant types @@ -1563,6 +1568,7 @@ Bootstrap users created at startup. Each entry is a pipe-delimited string: `username|password|email|firstName|lastName|groups[|origin]` Example: + ```yaml scim: users: @@ -1807,6 +1813,7 @@ The label text displayed for the password input field on the login page. **Type:** `Map` Customizes the visual branding of the login UI. Supports the following sub-keys: + - `companyName` — Company name displayed in the UI - `productLogo` — Base64-encoded product logo image - `squareLogo` — Base64-encoded square logo @@ -1820,6 +1827,7 @@ Customizes the visual branding of the login UI. Supports the following sub-keys: - `loginConsent` — Login consent modal configuration (see sub-properties below) **Login Consent Configuration (`loginConsent`):** + - `enabled` — (boolean, default: `false`) When `true`, displays a mandatory consent modal on login - `title` — (string, default: `"Terms and Conditions"`) Modal heading text - `text` — (string, required when enabled) Body content; HTML is allowed @@ -1913,6 +1921,7 @@ When `true`, enables identity provider and client alias entities across identity **Type:** `Map` External OAuth 2.0 and OIDC provider definitions. Each provider entry includes: + - `type` — `oauth2.0` or `oidc1.0` - `authUrl` / `discoveryUrl` — Authorization or discovery URL - `tokenUrl` — Token endpoint @@ -1959,6 +1968,7 @@ signed with this key. **Type:** `Map` Map of key IDs to SAML key configurations. Each key has: + - `key` — RSA private key in PEM format - `certificate` — X.509 certificate in PEM format - `passphrase` — Optional key passphrase @@ -2128,6 +2138,7 @@ The value of `idpMetadata` determines how UAA parses the IDP's entity ID: Legacy property for the SAML SP private key. Use `login.saml.keys` instead. The key, password, and certificate are grouped together: + - `login.serviceProviderKey` — RSA private key in PEM - `login.serviceProviderKeyPassword` — Key passphrase - `login.serviceProviderCertificate` — X.509 certificate in PEM @@ -2517,6 +2528,7 @@ When `true`, verifies SSL certificates when connecting to the notification servi **Type:** `String` Path to the LDAP profile configuration file. Determines the bind mode: + - `ldap/ldap-simple-bind.xml` — Simple bind (user DN pattern) - `ldap/ldap-search-and-bind.xml` — Search then bind - `ldap/ldap-search-and-compare.xml` — Search then compare @@ -2706,6 +2718,7 @@ Used for encrypting new data. All listed keys can be used for decryption. **Type:** `List` List of encryption keys. Each key has: + - `label` — Unique identifier - `passphrase` — Encryption passphrase @@ -2733,6 +2746,7 @@ old data encrypted with any listed key can still be decrypted. **Type:** `String` Controls rate limiting log verbosity. Accepted values: + - `OnlyLimited` — Only log when a request is rate-limited - `AllCalls` — Log all calls with rate-limit info - `AllCallsWithDetails` — Log all calls with full details @@ -2761,6 +2775,7 @@ rate limiting. Example: `'JWT:Claims+"sub"\s*:\s*"(.*?)"'` **Type:** `List` List of rate limiting rules. Each mapping has: + - `name` — Rule name - `withCallerRemoteAddressID` — Per-IP rate limit (e.g. `50r/s`) - `withCallerCredentialsID` — Per-credential rate limit @@ -3008,6 +3023,7 @@ This allows multiple zones to be accessed through the same hostname using differ URL paths instead of different subdomains. **Examples:** + - Subdomain access (default): `https://myzone.uaa.example.com/login` - Path access (when enabled): `https://uaa.example.com/z/myzone/login` diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlSecurityConfiguration.java b/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlSecurityConfiguration.java index fd3b5f56383..0a0dcdc348c 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlSecurityConfiguration.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/SpringServletXmlSecurityConfiguration.java @@ -201,12 +201,12 @@ SecurityFilterChainPostProcessor securityFilterChainPostProcessor( bean.setIgnore(Arrays.asList("secFilterOpen05Healthz")); int filterPos = 0; Map additionalFilters = new LinkedHashMap<>(); + additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), headerFilter.getFilter()); additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), rateLimitingFilter.getFilter()); additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), springRequestContextFilter.getFilter()); additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), httpHeaderSecurityFilter.getFilter()); additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), tracingFilter.getFilter()); additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), metricsFilter.getFilter()); - additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), headerFilter.getFilter()); additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), new BackwardsCompatibleScopeParsingFilter()); additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), contentSecurityPolicyFilter.getFilter()); additionalFilters.put(SecurityFilterChainPostProcessor.FilterPosition.position(filterPos++), utf8ConversionFilter); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java b/server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java index 0ad7618e791..ca90eda69a7 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/UaaProperties.java @@ -100,7 +100,7 @@ public record Servlet( SessionCookie sessionCookie, @DefaultValue("1800") int idleTimeout, - @DefaultValue({"X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Proto", "X-Forwarded-Prefix", "Forwarded"}) + @DefaultValue({"X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Proto", "X-Forwarded-Prefix", "Forwarded", "X-Client-IP", "X-Real-IP"}) List filteredHeaders ) { public Servlet { diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/UaaPropertiesTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/UaaPropertiesTest.java index 8f93d2d8f5f..0761e02b8ff 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/UaaPropertiesTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/UaaPropertiesTest.java @@ -73,7 +73,9 @@ void whenNoServletPropertiesAreSet() { "X-Forwarded-Host", "X-Forwarded-Proto", "X-Forwarded-Prefix", - "Forwarded" + "Forwarded", + "X-Client-IP", + "X-Real-IP" ); });