-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add license check for FIPS #181187
Add license check for FIPS #181187
Changes from 52 commits
fd898bc
392bee9
eff7830
be473ba
d6f8f5c
eccc9e9
ea425ec
d2cc083
b65471c
a0b72e6
683531c
03f4b9e
ff51670
8959fbb
d50f7c5
b1fa762
9d4d035
fd58057
16d7ad8
2bee3ea
b2fc622
eb5d11f
b1cab38
abed490
15ab04b
955cf08
bc31503
80a3867
cb93a25
4816c26
4daab8c
6672ff9
27c9d15
58a9e1b
2126400
1b91334
d871efc
c37004a
90050f1
57c2f36
ff3586a
31b7546
7b1a16a
d5cd54e
19244c4
d468deb
b7f1de0
7b7ed01
50015b0
2c11610
fccc836
b82345d
33db0d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,63 @@ | ||||||
[[xpack-security-fips-140-2]] | ||||||
=== FIPS 140-2 | ||||||
|
||||||
experimental::[] | ||||||
|
||||||
The Federal Information Processing Standard (FIPS) Publication 140-2, (FIPS PUB 140-2), | ||||||
titled "Security Requirements for Cryptographic Modules" is a U.S. government computer security standard | ||||||
used to approve cryptographic modules. | ||||||
|
||||||
{kib} offers a FIPS 140-2 compliant mode and as such can run in a Node.js environment configured with a FIPS | ||||||
140-2 compliant OpenSSL3 provider. | ||||||
|
||||||
To run {kib} in FIPS mode, you must have the appropriate {subscriptions}[subscription]. | ||||||
|
||||||
[IMPORTANT] | ||||||
============================================================================ | ||||||
The Node bundled with {kib} is not configured for FIPS 140-2. You must configure a FIPS 140-2 compliant OpenSSL3 | ||||||
provider. Consult the Node.js documentation to learn how to configure your environment. | ||||||
============================================================================ | ||||||
|
||||||
For {kib}, adherence to FIPS 140-2 is ensured by: | ||||||
|
||||||
* Using FIPS approved / NIST recommended cryptographic algorithms. | ||||||
|
||||||
* Delegating the implementation of these cryptographic algorithms to a NIST validated cryptographic module | ||||||
(available via Node.js configured with an OpenSSL3 provider). | ||||||
|
||||||
* Allowing the configuration of {kib} in a FIPS 140-2 compliant manner, as documented below. | ||||||
|
||||||
==== Configuring {kib} for FIPS 140-2 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to do anything about Lines 14 to 15 in 975eeed
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good question 😅 Im not sure how that conflicts in a FIPS setup. I can do some investigation. Is there anyway the KB admin could override this currently? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can be removed, Kibana archives will have this at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's ok to leave. You can use multiple providers alongside the FIPS provider. In #183777 I ran the FIPS agent pipeline appending FIPS options to the existing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, we should be able to leave it: When I configure node with:
It runs with the legacy provider (w/o FIPS) and allows If I include
|
||||||
|
||||||
Apart from setting `xpack.security.experimental.fipsMode.enabled` to `true` in your {kib} config, a number of security related | ||||||
settings need to be reviewed and configured in order to run {kib} successfully in a FIPS 140-2 compliant Node.js | ||||||
environment. | ||||||
|
||||||
===== Kibana keystore | ||||||
|
||||||
FIPS 140-2 (via NIST Special Publication 800-132) dictates that encryption keys should at least have an effective | ||||||
strength of 112 bits. As such, the Kibana keystore that stores the application’s secure settings needs to be | ||||||
password protected with a password that satisfies this requirement. This means that the password needs to be 14 bytes | ||||||
long which is equivalent to a 14 character ASCII encoded password, or a 7 character UTF-8 encoded password. | ||||||
|
||||||
For more information on how to set this password, refer to the <<change-password,keystore documentation>>. | ||||||
|
||||||
===== TLS keystore and keys | ||||||
|
||||||
Keystores can be used in a number of General TLS settings in order to conveniently store key and trust material. | ||||||
PKCS#12 keystores cannot be used in a FIPS 140-2 compliant Node.js environment. Avoid using these types of keystores. | ||||||
Your FIPS 140-2 provider may provide a compliant keystore implementation that can be used, or you can use PEM encoded | ||||||
files. To use PEM encoded key material, you can use the relevant `\*.key` and `*.certificate` configuration options, | ||||||
and for trust material you can use `*.certificate_authorities`. | ||||||
|
||||||
As an example, avoid PKCS#12 specific settings such as: | ||||||
|
||||||
* `server.ssl.keystore.path` | ||||||
* `server.ssl.truststore.path` | ||||||
* `elasticsearch.ssl.keystore.path` | ||||||
* `elasticsearch.ssl.truststore.path` | ||||||
|
||||||
===== Limitations | ||||||
|
||||||
Configuring {kib} to run in FIPS mode is still considered to be experimental. Not all features are guaranteed to | ||||||
function as expected. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
const mockGetFipsFn = jest.fn(); | ||
jest.mock('crypto', () => ({ | ||
randomBytes: jest.fn(), | ||
constants: jest.requireActual('crypto').constants, | ||
get getFips() { | ||
return mockGetFipsFn; | ||
}, | ||
})); | ||
|
||
import { SecurityServiceConfigType } from '../utils'; | ||
import { isFipsEnabled, checkFipsConfig } from './fips'; | ||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; | ||
|
||
describe('fips', () => { | ||
let config: SecurityServiceConfigType; | ||
describe('#isFipsEnabled', () => { | ||
it('should return `true` if config.experimental.fipsMode.enabled is `true`', () => { | ||
config = { experimental: { fipsMode: { enabled: true } } }; | ||
|
||
expect(isFipsEnabled(config)).toBe(true); | ||
}); | ||
|
||
it('should return `false` if config.experimental.fipsMode.enabled is `false`', () => { | ||
config = { experimental: { fipsMode: { enabled: false } } }; | ||
|
||
expect(isFipsEnabled(config)).toBe(false); | ||
}); | ||
|
||
it('should return `false` if config.experimental.fipsMode.enabled is `undefined`', () => { | ||
expect(isFipsEnabled(config)).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('checkFipsConfig', () => { | ||
let mockExit: jest.SpyInstance; | ||
|
||
beforeAll(() => { | ||
mockExit = jest.spyOn(process, 'exit').mockImplementation((exitCode) => { | ||
throw new Error(`Fake Exit: ${exitCode}`); | ||
}); | ||
}); | ||
|
||
afterAll(() => { | ||
mockExit.mockRestore(); | ||
}); | ||
|
||
it('should log an error message if FIPS mode is misconfigured - xpack.security.experimental.fipsMode.enabled true, Nodejs FIPS mode false', async () => { | ||
config = { experimental: { fipsMode: { enabled: true } } }; | ||
const logger = loggingSystemMock.create().get(); | ||
try { | ||
checkFipsConfig(config, logger); | ||
} catch (e) { | ||
expect(mockExit).toHaveBeenNthCalledWith(1, 78); | ||
} | ||
|
||
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(` | ||
Array [ | ||
Array [ | ||
"Configuration mismatch error. xpack.security.experimental.fipsMode.enabled is set to true and the configured Node.js environment has FIPS disabled", | ||
], | ||
] | ||
`); | ||
}); | ||
|
||
it('should log an error message if FIPS mode is misconfigured - xpack.security.experimental.fipsMode.enabled false, Nodejs FIPS mode true', async () => { | ||
mockGetFipsFn.mockImplementationOnce(() => { | ||
return 1; | ||
}); | ||
|
||
config = { experimental: { fipsMode: { enabled: false } } }; | ||
const logger = loggingSystemMock.create().get(); | ||
|
||
try { | ||
checkFipsConfig(config, logger); | ||
} catch (e) { | ||
expect(mockExit).toHaveBeenNthCalledWith(1, 78); | ||
} | ||
|
||
expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(` | ||
Array [ | ||
Array [ | ||
"Configuration mismatch error. xpack.security.experimental.fipsMode.enabled is set to false and the configured Node.js environment has FIPS enabled", | ||
], | ||
] | ||
`); | ||
}); | ||
|
||
it('should log an info message if FIPS mode is properly configured - xpack.security.experimental.fipsMode.enabled true, Nodejs FIPS mode true', async () => { | ||
mockGetFipsFn.mockImplementationOnce(() => { | ||
return 1; | ||
}); | ||
|
||
config = { experimental: { fipsMode: { enabled: true } } }; | ||
const logger = loggingSystemMock.create().get(); | ||
|
||
try { | ||
checkFipsConfig(config, logger); | ||
} catch (e) { | ||
logger.error('Should not throw error!'); | ||
} | ||
|
||
expect(loggingSystemMock.collect(logger).info).toMatchInlineSnapshot(` | ||
Array [ | ||
Array [ | ||
"Kibana is running in FIPS mode.", | ||
], | ||
] | ||
`); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import type { Logger } from '@kbn/logging'; | ||
import { getFips } from 'crypto'; | ||
import { SecurityServiceConfigType } from '../utils'; | ||
|
||
export function isFipsEnabled(config: SecurityServiceConfigType): boolean { | ||
return config?.experimental?.fipsMode?.enabled ?? false; | ||
} | ||
|
||
export function checkFipsConfig(config: SecurityServiceConfigType, logger: Logger) { | ||
const isFipsConfigEnabled = isFipsEnabled(config); | ||
const isNodeRunningWithFipsEnabled = getFips() === 1; | ||
|
||
// Check if FIPS is enabled in either setting | ||
if (isFipsConfigEnabled || isNodeRunningWithFipsEnabled) { | ||
// FIPS must be enabled on both or log and error an exit Kibana | ||
if (isFipsConfigEnabled !== isNodeRunningWithFipsEnabled) { | ||
logger.error( | ||
`Configuration mismatch error. xpack.security.experimental.fipsMode.enabled is set to ${isFipsConfigEnabled} and the configured Node.js environment has FIPS ${ | ||
isNodeRunningWithFipsEnabled ? 'enabled' : 'disabled' | ||
}` | ||
); | ||
process.exit(78); | ||
} else { | ||
logger.info('Kibana is running in FIPS mode.'); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,30 +9,59 @@ | |
import type { Logger } from '@kbn/logging'; | ||
import type { CoreContext, CoreService } from '@kbn/core-base-server-internal'; | ||
import type { CoreSecurityDelegateContract } from '@kbn/core-security-server'; | ||
import { Observable, Subscription } from 'rxjs'; | ||
import { Config } from '@kbn/config'; | ||
import { isFipsEnabled, checkFipsConfig } from './fips/fips'; | ||
import type { | ||
InternalSecurityServiceSetup, | ||
InternalSecurityServiceStart, | ||
} from './internal_contracts'; | ||
import { getDefaultSecurityImplementation, convertSecurityApi } from './utils'; | ||
import { | ||
getDefaultSecurityImplementation, | ||
convertSecurityApi, | ||
SecurityServiceConfigType, | ||
} from './utils'; | ||
|
||
export class SecurityService | ||
implements CoreService<InternalSecurityServiceSetup, InternalSecurityServiceStart> | ||
{ | ||
private readonly log: Logger; | ||
private securityApi?: CoreSecurityDelegateContract; | ||
private config$: Observable<Config>; | ||
private configSubscription?: Subscription; | ||
private config: Config | undefined; | ||
private readonly getConfig = () => { | ||
if (!this.config) { | ||
throw new Error('Config is not available.'); | ||
} | ||
return this.config; | ||
}; | ||
|
||
constructor(coreContext: CoreContext) { | ||
this.log = coreContext.logger.get('security-service'); | ||
|
||
this.config$ = coreContext.configService.getConfig$(); | ||
this.configSubscription = this.config$.subscribe((config) => { | ||
this.config = config; | ||
}); | ||
} | ||
|
||
public setup(): InternalSecurityServiceSetup { | ||
const config = this.getConfig(); | ||
const securityConfig: SecurityServiceConfigType = config.get(['xpack', 'security']); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, yeah... I overlooked we would need to access the security plugin's config from our core service for that feature 🙈. With our security-in-core initiative, I think we'll want to move the security config from Without doing so, I agree that hacking around and accessing the raw config is the most pragmatic approach. It's absolutely against a dozen Core principles though, so would you mind opening a follow-up issue, just to keep track (and discuss) of moving the security config schema/registration to Core? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Follow up issue: #186863 Ill make a note to update this code once we move the config over |
||
|
||
checkFipsConfig(securityConfig, this.log); | ||
|
||
return { | ||
registerSecurityDelegate: (api) => { | ||
if (this.securityApi) { | ||
throw new Error('security API can only be registered once'); | ||
} | ||
this.securityApi = api; | ||
}, | ||
fips: { | ||
isEnabled: () => isFipsEnabled(securityConfig), | ||
}, | ||
}; | ||
} | ||
|
||
|
@@ -44,5 +73,10 @@ export class SecurityService | |
return convertSecurityApi(apiContract); | ||
} | ||
|
||
public stop() {} | ||
public stop() { | ||
if (this.configSubscription) { | ||
this.configSubscription.unsubscribe(); | ||
this.configSubscription = undefined; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heya @georgewallace ! Would you or someone on you team be able to take a look at this documentation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heya @elastic/kibana-docs Would someone mind taking a look?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make this page appear in the output, you'll need to add
include::fips-140-2.asciidoc[]
at the end of thedocs/user/security/index.asciidoc
file (or in some other navigation file, depending on where it ought to appear in the navigation tree).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the page to the navigation in c37004a. I think it could honestly go in either of these sections: https://www.elastic.co/guide/en/kibana/current/using-kibana-with-security.html or https://www.elastic.co/guide/en/kibana/current/xpack-security.html. I've added it to the latter for now but can easily move if the former makes more sense for this content.