Skip to content

Commit

Permalink
Surface Kibana security route deprecations in Upgrade Assistant (elas…
Browse files Browse the repository at this point in the history
…tic#199656)

Closes elastic#197389

## Summary

Uses the `deprecated` route configuration option on all Kibana Security
"v1" endpoints. This will surface deprecation information in the Upgrade
Assistant.

## Related PRs
- elastic#50695 - `7.6.0`, deprecated
    - `/api/security/v1/me`
    - `/api/security/v1/logout`
    - `/api/security/v1/oidc/implicit`
    - `/api/security/v1/oidc` (POST)
- elastic#53886 - `7.6.0`, deprecated
`/api/security/v1/oidc` (GET)
- elastic#47929 - `8.0.0`, dropped
`/api/security/v1/saml` (`breaking` release note)
- elastic#106665 - restored
`/api/security/v1/saml` but warned as deprecated (no release note)

## Testing
1.  Start ES & Kibana in trial license mode
2. Make several calls to one or more of the deprecated endpoints
3. Navigate to `Stack Management`->`Upgrade Assistant`
4. Click on Kibana warnings
<img width="1003" alt="Screenshot 2024-11-18 at 10 01 10 AM"
src="https://github.com/user-attachments/assets/da44af13-57eb-4ffd-a507-e423eb767a11">
5. Confirm the called endpoints are displayed as warnings in the Upgrade
Assistant
<img width="1274" alt="Screenshot 2024-11-18 at 9 59 34 AM"
src="https://github.com/user-attachments/assets/4c054fbe-6838-48cf-8b1b-8c161252db0f">

## Previous release notes 
### v7.6.0

https://www.elastic.co/guide/en/kibana/7.6/release-notes-7.6.0.html#deprecation-7.6.0
https://www.elastic.co/guide/en/kibana/7.6/breaking-changes-7.6.html
The deprecations are not listed in the release notes or breaking changes
notes.

### v8.0.0

https://www.elastic.co/guide/en/kibana/current/release-notes-8.0.0-alpha1.html#rest-api-changes
SAML endpoint deprecation only appears in the 8.0.0-alpha1 release
notes, and was reverted in 8.0.0-alpha2

# Release note
See `docs/upgrade-notes.asciidoc` in file changes

# Follow-up
A follow-up PR must be created to create and backfill the docLinks.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
jeramysoucy and kibanamachine authored Nov 19, 2024
1 parent b8e6bbd commit 742ae9f
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 28 deletions.
38 changes: 26 additions & 12 deletions docs/upgrade-notes.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@ For Elastic Security release information, refer to {security-guide}/release-note
[float]
==== Kibana APIs

[discrete]
[[breaking-199656]]
.Removed all security v1 endpoints (9.0.0)
[%collapsible]
====
*Details* +
All `v1` Kibana security HTTP endpoints have been removed.
`GET /api/security/v1/logout` has been replaced by `GET /api/security/logout`
`GET /api/security/v1/oidc/implicit` has been replaced by `GET /api/security/oidc/implicit`
`GET /api/security/v1/oidc` has been replaced by GET `/api/security/oidc/callback`
`POST /api/security/v1/oidc` has been replaced by POST `/api/security/oidc/initiate_login`
`POST /api/security/v1/saml` has been replaced by POST `/api/security/saml/callback`
`GET /api/security/v1/me` has been removed with no replacement.
For more information, refer to {kibana-pull}199656[#199656].
*Impact* +
Any HTTP API calls to the `v1` Kibana security endpoints will fail with a 404 status code starting from version 9.0.0.
Third party OIDC and SAML identity providers configured with `v1` endpoints will no longer work.
*Action* +
Update any OIDC and SAML identity providers to reference the corresponding replacement endpoint listed above.
Remove references to the `/api/security/v1/me` endpoint from any automations, applications, tooling, and scripts.
====

[discrete]
[[breaking-193792]]
.Access to all internal APIs is blocked (9.0.0)
Expand Down Expand Up @@ -814,18 +840,6 @@ The legacy audit logger has been removed. For more information, refer to {kibana
Audit logs will be written to the default location in the new ECS format. To change the output file, filter events, and more, use the <<audit-logging-settings, audit logging settings>>.
====

[discrete]
[[breaking-47929]]
.[Security] Removed `/api/security/v1/saml` route. (8.0)
[%collapsible]
====
*Details* +
The `/api/security/v1/saml` route has been removed and is reflected in the kibana.yml `server.xsrf.whitelist` setting, {es}, and the Identity Provider SAML settings. For more information, refer to {kibana-pull}47929[#47929]
*Impact* +
Use the `/api/security/saml/callback` route, or wait to upgrade to 8.0.0-alpha2 when the `/api/security/saml/callback` route breaking change is reverted.
====

[discrete]
[[breaking-41700]]
.[Security] Legacy browsers rejected by default. (8.0)
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/get_doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
mappingRoles: `${ELASTICSEARCH_DOCS}mapping-roles.html`,
mappingRolesFieldRules: `${ELASTICSEARCH_DOCS}role-mapping-resources.html#mapping-roles-rule-field`,
runAsPrivilege: `${ELASTICSEARCH_DOCS}security-privileges.html#_run_as_privilege`,
deprecatedV1Endpoints: `${KIBANA_DOCS}breaking-changes-summary.html#breaking-199656`,
},
spaces: {
kibanaLegacyUrlAliases: `${KIBANA_DOCS}legacy-url-aliases.html`,
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ export interface DocLinks {
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
deprecatedV1Endpoints: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/security/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ export class SecurityPlugin
getUserProfileService: this.getUserProfileService,
analyticsService: this.analyticsService.setup({ analytics: core.analytics }),
buildFlavor: this.initializerContext.env.packageInfo.buildFlavor,
docLinks: core.docLinks,
});

return Object.freeze<SecurityPluginSetup>({
Expand Down
43 changes: 38 additions & 5 deletions x-pack/plugins/security/server/routes/authentication/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type { TypeOf } from '@kbn/config-schema';
import { schema } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';
import { parseNextURL } from '@kbn/std';

import type { RouteDefinitionParams } from '..';
Expand All @@ -33,13 +34,15 @@ export function defineCommonRoutes({
license,
logger,
buildFlavor,
docLinks,
}: RouteDefinitionParams) {
// Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used.
// For a serverless build, do not register deprecated versioned routes
for (const path of [
'/api/security/logout',
...(buildFlavor !== 'serverless' ? ['/api/security/v1/logout'] : []),
]) {
const isDeprecated = path === '/api/security/v1/logout';
router.get(
{
path,
Expand All @@ -57,13 +60,29 @@ export function defineCommonRoutes({
excludeFromOAS: true,
authRequired: false,
tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW],
...(isDeprecated && {
deprecated: {
documentationUrl: docLinks.links.security.deprecatedV1Endpoints,
severity: 'warning',
message: i18n.translate('xpack.security.deprecations.logoutRouteMessage', {
defaultMessage:
'The "{path}" URL is deprecated and will be removed in the next major version. Use "/api/security/logout" instead.',
values: { path },
}),
reason: {
type: 'migrate',
newApiMethod: 'GET',
newApiPath: '/api/security/logout',
},
},
}),
},
},
async (context, request, response) => {
const serverBasePath = basePath.serverBasePath;
if (path === '/api/security/v1/logout') {
if (isDeprecated) {
logger.warn(
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version, please use "${serverBasePath}/api/security/logout" URL instead.`,
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version. Use "${serverBasePath}/api/security/logout" URL instead.`,
{ tags: ['deprecation'] }
);
}
Expand Down Expand Up @@ -96,7 +115,7 @@ export function defineCommonRoutes({
'/internal/security/me',
...(buildFlavor !== 'serverless' ? ['/api/security/v1/me'] : []),
]) {
const deprecated = path === '/api/security/v1/me';
const isDeprecated = path === '/api/security/v1/me';
router.get(
{
path,
Expand All @@ -107,10 +126,24 @@ export function defineCommonRoutes({
},
},
validate: false,
options: { access: deprecated ? 'public' : 'internal' },
options: {
access: isDeprecated ? 'public' : 'internal',
...(isDeprecated && {
deprecated: {
documentationUrl: docLinks.links.security.deprecatedV1Endpoints,
severity: 'warning',
message: i18n.translate('xpack.security.deprecations.meRouteMessage', {
defaultMessage:
'The "{path}" endpoint is deprecated and will be removed in the next major version.',
values: { path },
}),
reason: { type: 'remove' },
},
}),
},
},
createLicensedRouteHandler(async (context, request, response) => {
if (deprecated) {
if (isDeprecated) {
logger.warn(
`The "${basePath.serverBasePath}${path}" endpoint is deprecated and will be removed in the next major version.`,
{ tags: ['deprecation'] }
Expand Down
71 changes: 63 additions & 8 deletions x-pack/plugins/security/server/routes/authentication/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ export function defineOIDCRoutes({
logger,
getAuthenticationService,
basePath,
docLinks,
}: RouteDefinitionParams) {
// Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used.
for (const path of ['/api/security/oidc/implicit', '/api/security/v1/oidc/implicit']) {
const isDeprecated = path === '/api/security/v1/oidc/implicit';
/**
* The route should be configured as a redirect URI in OP when OpenID Connect implicit flow
* is used, so that we can extract authentication response from URL fragment and send it to
Expand All @@ -37,13 +39,32 @@ export function defineOIDCRoutes({
{
path,
validate: false,
options: { authRequired: false, excludeFromOAS: true },
options: {
authRequired: false,
excludeFromOAS: true,
...(isDeprecated && {
deprecated: {
documentationUrl: docLinks.links.security.deprecatedV1Endpoints,
severity: 'warning',
message: i18n.translate('xpack.security.deprecations.oidcImplicitRouteMessage', {
defaultMessage:
'The "{path}" URL is deprecated and will be removed in the next major version. Use "/api/security/oidc/implicit" instead.',
values: { path },
}),
reason: {
type: 'migrate',
newApiMethod: 'GET',
newApiPath: '/api/security/oidc/implicit',
},
},
}),
},
},
(context, request, response) => {
const serverBasePath = basePath.serverBasePath;
if (path === '/api/security/v1/oidc/implicit') {
if (isDeprecated) {
logger.warn(
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version, please use "${serverBasePath}/api/security/oidc/implicit" URL instead.`,
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version. Use "${serverBasePath}/api/security/oidc/implicit" URL instead.`,
{ tags: ['deprecation'] }
);
}
Expand Down Expand Up @@ -84,6 +105,7 @@ export function defineOIDCRoutes({

// Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used.
for (const path of ['/api/security/oidc/callback', '/api/security/v1/oidc']) {
const isDeprecated = path === '/api/security/v1/oidc';
router.get(
{
path,
Expand Down Expand Up @@ -117,6 +139,22 @@ export function defineOIDCRoutes({
excludeFromOAS: true,
authRequired: false,
tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW],
...(isDeprecated && {
deprecated: {
documentationUrl: docLinks.links.security.deprecatedV1Endpoints,
severity: 'warning',
message: i18n.translate('xpack.security.deprecations.oidcCallbackRouteMessage', {
defaultMessage:
'The "{path}" URL is deprecated and will be removed in the next major version. Use "/api/security/oidc/callback" instead.',
values: { path },
}),
reason: {
type: 'migrate',
newApiMethod: 'GET',
newApiPath: '/api/security/oidc/callback',
},
},
}),
},
},
createLicensedRouteHandler(async (context, request, response) => {
Expand All @@ -133,9 +171,9 @@ export function defineOIDCRoutes({
authenticationResponseURI: request.query.authenticationResponseURI,
};
} else if (request.query.code || request.query.error) {
if (path === '/api/security/v1/oidc') {
if (isDeprecated) {
logger.warn(
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version, please use "${serverBasePath}/api/security/oidc/callback" URL instead.`,
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version. Use "${serverBasePath}/api/security/oidc/callback" URL instead.`,
{ tags: ['deprecation'] }
);
}
Expand All @@ -150,7 +188,7 @@ export function defineOIDCRoutes({
};
} else if (request.query.iss) {
logger.warn(
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version, please use "${serverBasePath}/api/security/oidc/initiate_login" URL for Third-Party Initiated login instead.`,
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version. Use "${serverBasePath}/api/security/oidc/initiate_login" URL for Third-Party Initiated login instead.`,
{ tags: ['deprecation'] }
);
// An HTTP GET request with a query parameter named `iss` as part of a 3rd party initiated authentication.
Expand All @@ -175,6 +213,7 @@ export function defineOIDCRoutes({

// Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used.
for (const path of ['/api/security/oidc/initiate_login', '/api/security/v1/oidc']) {
const isDeprecated = path === '/api/security/v1/oidc';
/**
* An HTTP POST request with the payload parameter named `iss` as part of a 3rd party initiated authentication.
* See more details at https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin
Expand Down Expand Up @@ -206,13 +245,29 @@ export function defineOIDCRoutes({
authRequired: false,
xsrfRequired: false,
tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW],
...(isDeprecated && {
deprecated: {
documentationUrl: docLinks.links.security.deprecatedV1Endpoints,
severity: 'warning',
message: i18n.translate('xpack.security.deprecations.oidcInitiateRouteMessage', {
defaultMessage:
'The "{path}" URL is deprecated and will be removed in the next major version. Use "/api/security/oidc/initiate_login" instead.',
values: { path },
}),
reason: {
type: 'migrate',
newApiMethod: 'POST',
newApiPath: '/api/security/oidc/initiate_login',
},
},
}),
},
},
createLicensedRouteHandler(async (context, request, response) => {
const serverBasePath = basePath.serverBasePath;
if (path === '/api/security/v1/oidc') {
if (isDeprecated) {
logger.warn(
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version, please use "${serverBasePath}/api/security/oidc/initiate_login" URL for Third-Party Initiated login instead.`,
`The "${serverBasePath}${path}" URL is deprecated and will stop working in the next major version. Use "${serverBasePath}/api/security/oidc/initiate_login" URL for Third-Party Initiated login instead.`,
{ tags: ['deprecation'] }
);
}
Expand Down
23 changes: 21 additions & 2 deletions x-pack/plugins/security/server/routes/authentication/saml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { schema } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';

import type { RouteDefinitionParams } from '..';
import { SAMLAuthenticationProvider, SAMLLogin } from '../../authentication';
Expand All @@ -20,13 +21,15 @@ export function defineSAMLRoutes({
basePath,
logger,
buildFlavor,
docLinks,
}: RouteDefinitionParams) {
// Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used.
// For a serverless build, do not register deprecated versioned routes
for (const path of [
'/api/security/saml/callback',
...(buildFlavor !== 'serverless' ? ['/api/security/v1/saml'] : []),
]) {
const isDeprecated = path === '/api/security/v1/saml';
router.post(
{
path,
Expand All @@ -48,14 +51,30 @@ export function defineSAMLRoutes({
authRequired: false,
xsrfRequired: false,
tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW],
...(isDeprecated && {
deprecated: {
documentationUrl: docLinks.links.security.deprecatedV1Endpoints,
severity: 'warning',
message: i18n.translate('xpack.security.deprecations.samlPostRouteMessage', {
defaultMessage:
'The "{path}" URL is deprecated and will be removed in the next major version. Use "/api/security/saml/callback" instead.',
values: { path },
}),
reason: {
type: 'migrate',
newApiMethod: 'POST',
newApiPath: '/api/security/saml/callback',
},
},
}),
},
},
async (context, request, response) => {
if (path === '/api/security/v1/saml') {
if (isDeprecated) {
const serverBasePath = basePath.serverBasePath;
logger.warn(
// When authenticating using SAML we _expect_ to redirect to the SAML Identity provider.
`The "${serverBasePath}${path}" URL is deprecated and might stop working in a future release. Please use "${serverBasePath}/api/security/saml/callback" URL instead.`
`The "${serverBasePath}${path}" URL is deprecated and might stop working in a future release. Use "${serverBasePath}/api/security/saml/callback" URL instead.`
);
}

Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/security/server/routes/index.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
httpServiceMock,
loggingSystemMock,
} from '@kbn/core/server/mocks';
import { getDocLinks } from '@kbn/doc-links';
import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
import type { DeeplyMockedKeys } from '@kbn/utility-types-jest';

Expand Down Expand Up @@ -50,6 +51,8 @@ export const routeDefinitionParamsMock = {
getAnonymousAccessService: jest.fn(),
getUserProfileService: jest.fn().mockReturnValue(userProfileServiceMock.createStart()),
analyticsService: analyticsServiceMock.createSetup(),
buildFlavor: 'traditional',
docLinks: { links: getDocLinks({ kibanaBranch: 'main', buildFlavor: 'traditional' }) },
} as unknown as DeeplyMockedKeys<RouteDefinitionParams>;
},
};
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/security/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import type { Observable } from 'rxjs';

import type { BuildFlavor } from '@kbn/config/src/types';
import type { HttpResources, IBasePath, Logger } from '@kbn/core/server';
import type { DocLinksServiceSetup, HttpResources, IBasePath, Logger } from '@kbn/core/server';
import type { KibanaFeature } from '@kbn/features-plugin/server';
import type { SubFeaturePrivilegeIterator } from '@kbn/features-plugin/server/feature_privilege_iterator';
import type { PublicMethodsOf } from '@kbn/utility-types';
Expand Down Expand Up @@ -59,6 +59,7 @@ export interface RouteDefinitionParams {
getAnonymousAccessService: () => AnonymousAccessServiceStart;
analyticsService: AnalyticsServiceSetup;
buildFlavor: BuildFlavor;
docLinks: DocLinksServiceSetup;
}

export function defineRoutes(params: RouteDefinitionParams) {
Expand Down
Loading

0 comments on commit 742ae9f

Please sign in to comment.