Skip to content
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

Kms cluster encryption #7153

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.ocs-install-kms {
&__form-url {
display: inline-flex;
width: 100%;
}

&__form-address {
width: 70%;
}

&__form-port {
width: 30%;
}
}
Original file line number Diff line number Diff line change
@@ -1,67 +1,201 @@
import * as React from 'react';
import { FormGroup, TextInput } from '@patternfly/react-core';
import { State, Action } from '../ocs-install/attached-devices/create-sc/state';
import * as _ from 'lodash';

import {
FormGroup,
TextInput,
FormSelect,
FormSelectOption,
Button,
ValidatedOptions,
} from '@patternfly/react-core';
import { global_palette_blue_300 as blueInfoColor } from '@patternfly/react-tokens/dist/js/global_palette_blue_300';
import { PencilAltIcon } from '@patternfly/react-icons';

import { advancedKMSModal } from '../modals/advanced-kms-modal/advanced-kms-modal';
import {
InternalClusterState,
InternalClusterAction,
ActionType,
} from '../ocs-install/internal-mode/reducer';
import {
setDispatch,
Validation,
VALIDATIONS,
ValidationMessage,
} from '../../utils/common-ocs-install-el';
import { KMSProviders } from '../../constants/ocs-install';
import { KMSConfig } from '../ocs-install/types';
import { State, Action } from '../ocs-install/attached-devices/create-sc/state';
import { setEncryptionDispatch, parseURL } from './utils';

const validate = (valid: boolean): Validation => {
let validation: Validation;
if (!valid) {
validation = VALIDATIONS.REQUIRED_FIELD_KMS;
}
return validation;
};
import './kms-config.scss';

/* @TODO - complete KMS View in follow up PR */
export const KMSConfigure: React.FC<KMSConfigureProps> = ({ state, dispatch, mode }) => {
const { kms } = state;
const validation: Validation = validate(kms.hasHandled);
const [kmsProvider, setKMSProvider] = React.useState<string>(KMSProviders[0].name);

React.useEffect(() => {
if (!kms.name) {
setDispatch(ActionType.SET_KMS_ENCRYPTION, { ...kms, hasHandled: false }, mode, dispatch);
} else {
setDispatch(ActionType.SET_KMS_ENCRYPTION, { ...kms, hasHandled: true }, mode, dispatch);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [kms.name]);
const setServiceName = (name: string) => {
const kmsObj: KMSConfig = kms;
kmsObj.name.value = name;
name !== '' ? (kms.name.valid = true) : (kms.name.valid = false);
kmsObj.hasHandled = kms.name.valid;
setEncryptionDispatch(ActionType.SET_KMS_ENCRYPTION, mode, dispatch, kmsObj);
};

const getServiceName = (name: string) => {
setDispatch(ActionType.SET_KMS_ENCRYPTION, { ...kms, name }, mode, dispatch);
const setAddress = (address: string) => {
const kmsObj: KMSConfig = kms;
kmsObj.address.value = address;
address !== '' && parseURL(address.trim())
? (kms.address.valid = true)
: (kms.address.valid = false);
kmsObj.hasHandled = kms.address.valid;
setEncryptionDispatch(ActionType.SET_KMS_ENCRYPTION, mode, dispatch, kmsObj);
};

const setAddressPort = (port: string) => {
const kmsObj: KMSConfig = kms;
kmsObj.port.value = port;
port !== '' && !_.isNaN(Number(port)) && Number(port) > 0
? (kms.port.valid = true)
: (kms.port.valid = false);
kmsObj.hasHandled = kms.port.valid;
setEncryptionDispatch(ActionType.SET_KMS_ENCRYPTION, mode, dispatch, kmsObj);
};

const setToken = (token: string) => {
const kmsObj: KMSConfig = kms;
kmsObj.token.value = token;
token !== '' ? (kms.token.valid = true) : (kms.token.valid = false);
kmsObj.hasHandled = kms.token.valid;
setEncryptionDispatch(ActionType.SET_KMS_ENCRYPTION, mode, dispatch, kmsObj);
};

const validateAddressMessage = () =>
kms.address.value === '' ? 'This is a required field' : 'Please enter a URL';

const validatePortMessage = () =>
kms.port.value === '' ? 'This is a required field' : 'Please enter a valid port';

const openAdvancedModal = () =>
advancedKMSModal({
state,
dispatch,
mode,
});

const isValid = (value: boolean) => (value ? ValidatedOptions.default : ValidatedOptions.error);

return (
<>
<div className="co-m-pane__form">
<FormGroup
fieldId="kms-provider"
label="Key Management Service Provider"
className="ocs-install-encryption__form-body"
>
<FormSelect
value={kmsProvider}
onChange={setKMSProvider}
id="kms-provider"
name="kms-provider-name"
aria-label="kms-provider-name"
isDisabled
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are not setting the isDisabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For 4.7, it will always be disabled.

>
{KMSProviders.map((provider) => (
<FormSelectOption value={provider.value} label={provider.name} />
))}
</FormSelect>
</FormGroup>
<FormGroup
fieldId="kms-service-name"
label="Service Name"
className="co-m-pane__form ocs-install-encryption__form-body"
className="ocs-install-encryption__form-body"
helperTextInvalid="This is a required field"
validated={isValid(kms.name.valid)}
isRequired
>
<TextInput
value={kms.name}
onChange={getServiceName}
isRequired
value={kms.name.value}
onChange={setServiceName}
type="text"
id="kms-service-name"
name="kms-service-name"
isRequired
validated={isValid(kms.name.valid)}
/>
{validation && <ValidationMessage validation={validation} />}
</FormGroup>
</>
<div className="ocs-install-kms__form-url">
<FormGroup
fieldId="kms-address"
label="Address"
className="ocs-install-kms__form-address ocs-install-encryption__form-body"
helperTextInvalid={validateAddressMessage()}
validated={isValid(kms.address.valid)}
isRequired
>
<TextInput
value={kms.address.value}
onChange={setAddress}
className="ocs-install-kms__form-address--padding"
type="url"
id="kms-address"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here as well.

Suggested change
id="kms-address"
id="kms-service-address"

name="kms-address"
isRequired
validated={isValid(kms.address.valid)}
/>
</FormGroup>
<FormGroup
fieldId="kms-address-port"
label="Port"
className="ocs-install-kms__form-port ocs-install-encryption__form-body--small-padding"
helperTextInvalid={validatePortMessage()}
validated={isValid(kms.port.valid)}
isRequired
>
<TextInput
value={kms.port.value}
onChange={setAddressPort}
type="text"
id="kms-address-port"
name="kms-address-port"
isRequired
validated={isValid(kms.port.valid)}
/>
</FormGroup>
</div>
<FormGroup
fieldId="kms-token"
label="Token"
className="ocs-install-encryption__form-body"
helperTextInvalid="This is a required field"
validated={isValid(kms.token.valid)}
isRequired
>
<TextInput
value={kms.token.value}
onChange={setToken}
type="password"
id="kms-token"
name="kms-token"
isRequired
validated={isValid(kms.token.valid)}
/>
</FormGroup>
<Button
variant="link"
className="ocs-install-encryption__form-body"
onClick={openAdvancedModal}
>
Advanced Settings{' '}
{(kms.backend ||
kms.caCert ||
kms.tls ||
kms.clientCert ||
kms.clientKey ||
kms.providerNamespace) && (
<PencilAltIcon data-test="edit-icon" size="sm" color={blueInfoColor.value} />
)}
</Button>
</div>
);
};

type KMSConfigureProps = {
state: State | InternalClusterState;
state: InternalClusterState | State;
dispatch: React.Dispatch<Action | InternalClusterAction>;
mode: string;
mode?: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as React from 'react';
import * as _ from 'lodash';
import { K8sResourceKind, ConfigMapKind, SecretKind } from '@console/internal/module/k8s/types';
import { k8sCreate } from '@console/internal/module/k8s/resource';
import { ConfigMapModel, SecretModel } from '@console/internal/models';
import { CEPH_STORAGE_NAMESPACE, MODES, KMSConfigMapName, KMSSecretName } from '../../constants';
import { Action } from '../ocs-install/attached-devices/create-sc/state';
import { InternalClusterAction } from '../ocs-install/internal-mode/reducer';
import { KMSConfig, KMSConfigMap } from '../ocs-install/types';

export const parseURL = (url: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can be put in a shared folder. Will do in next PR.

try {
return new URL(url);
} catch (e) {
return null;
}
};

export const createKmsResources = (kms: KMSConfig) => {
const tokenSecret: SecretKind = {
apiVersion: SecretModel.apiVersion,
kind: SecretModel.kind,
metadata: {
name: KMSSecretName,
namespace: CEPH_STORAGE_NAMESPACE,
},
stringData: {
data: kms.token.value,
},
};
const resources: Promise<K8sResourceKind>[] = [];

const parsedAddress = parseURL(kms.address.value);

const configData: KMSConfigMap = {
KMS_PROVIDER: 'vault',
VAULT_ADDR: `${`${parsedAddress.protocol}//${parsedAddress.hostname}`}:${kms.port.value}`,
VAULT_BACKEND_PATH: kms.backend,
VAULT_CACERT: kms.caCert?.metadata.name,
VAULT_TLS_SERVER_NAME: kms.tls,
VAULT_CLIENT_CERT: kms.clientCert?.metadata.name,
VAULT_CLIENT_KEY: kms.clientKey?.metadata.name,
VAULT_NAMESPACE: kms.providerNamespace,
};
const configMapObj: ConfigMapKind = {
apiVersion: ConfigMapModel.apiVersion,
kind: ConfigMapModel.kind,
data: {
...configData,
},
metadata: {
name: KMSConfigMapName,
namespace: CEPH_STORAGE_NAMESPACE,
},
};

if (kms.caCert) {
resources.push(k8sCreate(SecretModel, kms.caCert, CEPH_STORAGE_NAMESPACE));
}

if (kms.clientCert) {
resources.push(k8sCreate(SecretModel, kms.clientCert, CEPH_STORAGE_NAMESPACE));
}

if (kms.clientKey) {
resources.push(k8sCreate(SecretModel, kms.clientKey, CEPH_STORAGE_NAMESPACE));
}

resources.push(k8sCreate(SecretModel, tokenSecret, CEPH_STORAGE_NAMESPACE));
resources.push(k8sCreate(ConfigMapModel, configMapObj, CEPH_STORAGE_NAMESPACE));
return resources;
};

export const setEncryptionDispatch = (
keyType: any,
mode: string,
dispatch: React.Dispatch<Action | InternalClusterAction>,
valueType?: any,
) => {
const stateType = mode === MODES.ATTACHED_DEVICES ? _.camelCase(keyType) : keyType;

if (valueType) {
const stateValue =
mode === MODES.ATTACHED_DEVICES ? { value: valueType } : { payload: valueType };
dispatch({ type: stateType, ...stateValue });
} else {
dispatch({ type: stateType });
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.ceph-advanced-kms {
&__form-body {
padding: var(--pf-global--spacer--md) 0 !important;
}
}