mirror of
https://github.com/ceph/ceph
synced 2026-08-02 07:03:18 +00:00
mgr/dashboard : Enable cephadm-signed TLS for NVMeOf
fixes: https://tracker.ceph.com/issues/77406 Signed-off-by: Abhishek Desai <abhishek.desai1@ibm.com> (cherry picked from commit 9623f6847cecf7f2e79f461420fce4821fd1b159)
This commit is contained in:
parent
440cba650a
commit
517f6da8a9
@ -1153,112 +1153,8 @@
|
||||
}
|
||||
|
||||
<!-- Certificate Management UI -->
|
||||
@if (serviceForm.controls.ssl.value && ['rgw', 'ingress', 'iscsi', 'grafana', 'oauth2-proxy', 'mgmt-gateway', 'nvmeof', 'nfs'].includes(serviceForm.controls.service_type.value)) {
|
||||
<div>
|
||||
<!-- Current Certificate Section - Only shown in Edit mode when certificate exists -->
|
||||
@if (editing && currentCertificate?.has_certificate) {
|
||||
<div class="form-item">
|
||||
<label class="cds--label fw-bold"
|
||||
i18n>Current Certificate</label>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label class="cds--label"
|
||||
i18n>Certificate</label>
|
||||
<div>{{ currentCertificate.cert_name }}</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="cds--label"
|
||||
i18n>Valid Until</label>
|
||||
<div>{{ currentCertificate.expiry_date | cdDate }} • {{ currentCertificate.days_to_expiration }} <span i18n>days left</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-6">
|
||||
<label class="cds--label"
|
||||
i18n>Status</label>
|
||||
<div class="align-items-center">
|
||||
<cd-icon [type]="statusIconMap[currentCertificate.status] || statusIconMap['default']"></cd-icon>
|
||||
@switch (currentCertificate.status) {
|
||||
@case ('valid') { <span i18n>Valid</span> }
|
||||
@case ('expiring') { <span i18n>Expiring soon</span> }
|
||||
@case ('expired') { <span i18n>Expired</span> }
|
||||
@case ('not_configured') { <span i18n>Not configured</span> }
|
||||
@case ('invalid') { <span i18n>Invalid</span> }
|
||||
@default { {{ currentCertificate.status }} }
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="cds--label"
|
||||
i18n>Issuer</label>
|
||||
<div>
|
||||
@if (currentCertificate.signed_by === 'cephadm') {
|
||||
<span i18n>Internal (Cephadm CA)</span>
|
||||
} @else {
|
||||
{{ currentCertificate.issuer || 'External' }}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Certificate Authority Selection -->
|
||||
<div class="form-item">
|
||||
<label class="cds--label fw-bold"
|
||||
i18n>Choose Certificate Authority</label>
|
||||
<cds-radio-group formControlName="certificateType"
|
||||
orientation="horizontal"
|
||||
helperText="Select how certificates will be signed for this service. Choose internal to use the cluster’s CA, or external to upload certificates signed by your organization.">
|
||||
<cds-radio [value]="CertificateType.internal"
|
||||
(change)="onCertificateTypeChange(CertificateType.internal)"
|
||||
i18n>
|
||||
Internal
|
||||
</cds-radio>
|
||||
<cds-radio [value]="CertificateType.external"
|
||||
(change)="onCertificateTypeChange(CertificateType.external)"
|
||||
i18n>
|
||||
External
|
||||
</cds-radio>
|
||||
</cds-radio-group>
|
||||
</div>
|
||||
|
||||
@if (showCertSourceChangeWarning) {
|
||||
<cd-alert-panel type="warning"
|
||||
spacingClass="mb-3"
|
||||
i18n>
|
||||
Changing the certificate source will redeploy the service daemons to apply the new certificate configuration.
|
||||
</cd-alert-panel>
|
||||
}
|
||||
|
||||
@if (serviceForm.controls.certificateType.value === CertificateType.internal) {
|
||||
<cd-alert-panel type="info"
|
||||
spacingClass="mb-3"
|
||||
i18n>
|
||||
Certificate will be generated automatically by Cephadm CA for internal certificate type.
|
||||
</cd-alert-panel>
|
||||
<div class="form-item">
|
||||
<cd-text-label-list formControlName="custom_sans"
|
||||
label="Custom SAN Entries"
|
||||
i18n-label
|
||||
helperText="Optional list of Subject Alternative Names (hostnames, IPs, or DNS names) to include in the auto-generated certificate."
|
||||
i18n-helperText>
|
||||
</cd-text-label-list>
|
||||
</div>
|
||||
@if (serviceForm.controls.service_type.value === 'rgw'
|
||||
&& serviceForm.controls.virtual_host_enabled.value) {
|
||||
<div class="form-item">
|
||||
<cds-checkbox i18n-label
|
||||
formControlName="wildcard_enabled">
|
||||
Include wildcard certificate for bucket subdomains
|
||||
<cd-help-text i18n>
|
||||
Add wildcard certificates (*.domain) to allow SSL for all bucket subdomains. Required for virtual-host style with SSL.
|
||||
</cd-help-text>
|
||||
</cds-checkbox>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@if (serviceForm.controls.ssl.value && ['rgw', 'ingress', 'iscsi', 'grafana', 'oauth2-proxy', 'mgmt-gateway', 'nfs'].includes(serviceForm.controls.service_type.value)) {
|
||||
<ng-container *ngTemplateOutlet="certificateAuthorityManagement"></ng-container>
|
||||
}
|
||||
|
||||
<!-- ssl_cert - Only show when SSL is enabled AND certificate type is external -->
|
||||
@ -1396,6 +1292,9 @@
|
||||
</fieldset>
|
||||
</div>
|
||||
@if (serviceForm.controls.enable_mtls.value) {
|
||||
<ng-container *ngTemplateOutlet="certificateAuthorityManagement"></ng-container>
|
||||
|
||||
@if (serviceForm.controls.certificateType.value === CertificateType.external) {
|
||||
<!-- root_ca_cert -->
|
||||
<div class="form-item">
|
||||
<ng-container *ngTemplateOutlet="fileUploaderTextarea; context: { controlName: 'root_ca_cert', title: 'Root CA Certificate Input', helperText: 'Uploaded files will populate the Root CA certificate details automatically. Or paste the PEM content directly in the text area.', description: 'Upload a Root CA certificate file, or paste the Root CA certificate PEM content directly.', invalidTemplate: invalidRootCaCertError, isRequired: true }"></ng-container>
|
||||
@ -1452,6 +1351,7 @@
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</form>
|
||||
</section>
|
||||
<cd-form-button-panel (submitActionEvent)="onSubmit()"
|
||||
@ -1467,6 +1367,115 @@
|
||||
</span>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #certificateAuthorityManagement>
|
||||
<div [formGroup]="serviceForm">
|
||||
<!-- Current Certificate Section - Only shown in Edit mode when certificate exists -->
|
||||
@if (editing && currentCertificate?.has_certificate) {
|
||||
<div class="form-item">
|
||||
<label class="cds--label cds--type-heading-compact-01"
|
||||
i18n>Current Certificate</label>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label class="cds--label"
|
||||
i18n>Certificate</label>
|
||||
<div>{{ currentCertificate.cert_name }}</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="cds--label"
|
||||
i18n>Valid Until</label>
|
||||
<div>{{ currentCertificate.expiry_date | cdDate }} • {{ currentCertificate.days_to_expiration }} <span i18n>days left</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-6">
|
||||
<label class="cds--label"
|
||||
i18n>Status</label>
|
||||
<div class="align-items-center">
|
||||
<cd-icon [type]="statusIconMap[currentCertificate.status] || statusIconMap['default']"></cd-icon>
|
||||
@switch (currentCertificate.status) {
|
||||
@case ('valid') { <span i18n>Valid</span> }
|
||||
@case ('expiring') { <span i18n>Expiring soon</span> }
|
||||
@case ('expired') { <span i18n>Expired</span> }
|
||||
@case ('not_configured') { <span i18n>Not configured</span> }
|
||||
@case ('invalid') { <span i18n>Invalid</span> }
|
||||
@default { {{ currentCertificate.status }} }
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="cds--label"
|
||||
i18n>Issuer</label>
|
||||
<div>
|
||||
@if (currentCertificate.signed_by === 'cephadm') {
|
||||
<span i18n>Internal (Cephadm CA)</span>
|
||||
} @else {
|
||||
{{ currentCertificate.issuer || 'External' }}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Certificate Authority Selection -->
|
||||
<div class="form-item">
|
||||
<label class="cds--label cds--type-heading-compact-01"
|
||||
i18n>Choose Certificate Authority</label>
|
||||
<cds-radio-group formControlName="certificateType"
|
||||
orientation="horizontal"
|
||||
helperText="Select how certificates will be signed for this service. Choose internal to use the cluster’s CA, or external to upload certificates signed by your organization."
|
||||
i18n-helperText>
|
||||
<cds-radio [value]="CertificateType.internal"
|
||||
(change)="onCertificateTypeChange(CertificateType.internal)"
|
||||
i18n>
|
||||
Internal
|
||||
</cds-radio>
|
||||
<cds-radio [value]="CertificateType.external"
|
||||
(change)="onCertificateTypeChange(CertificateType.external)"
|
||||
i18n>
|
||||
External
|
||||
</cds-radio>
|
||||
</cds-radio-group>
|
||||
</div>
|
||||
|
||||
@if (showCertSourceChangeWarning) {
|
||||
<cd-alert-panel type="warning"
|
||||
spacingClass="mb-3"
|
||||
i18n>
|
||||
Changing the certificate source will redeploy the service daemons to apply the new certificate configuration.
|
||||
</cd-alert-panel>
|
||||
}
|
||||
|
||||
@if (serviceForm.controls.certificateType.value === CertificateType.internal) {
|
||||
<cd-alert-panel type="info"
|
||||
spacingClass="mb-3"
|
||||
i18n>
|
||||
Certificate will be generated automatically by Cephadm CA for internal certificate type.
|
||||
</cd-alert-panel>
|
||||
<div class="form-item">
|
||||
<cd-text-label-list formControlName="custom_sans"
|
||||
label="Custom SAN Entries"
|
||||
i18n-label
|
||||
helperText="Optional list of Subject Alternative Names (hostnames, IPs, or DNS names) to include in the auto-generated certificate."
|
||||
i18n-helperText>
|
||||
</cd-text-label-list>
|
||||
</div>
|
||||
@if (serviceForm.controls.service_type.value === 'rgw'
|
||||
&& serviceForm.controls.virtual_host_enabled.value) {
|
||||
<div class="form-item">
|
||||
<cds-checkbox i18n-label
|
||||
formControlName="wildcard_enabled">
|
||||
Include wildcard certificate for bucket subdomains
|
||||
<cd-help-text i18n>
|
||||
Add wildcard certificates (*.domain) to allow SSL for all bucket subdomains. Required for virtual-host style with SSL.
|
||||
</cd-help-text>
|
||||
</cds-checkbox>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #fileUploaderTextarea
|
||||
let-controlName="controlName"
|
||||
let-title="title"
|
||||
|
||||
@ -558,6 +558,22 @@ x4Ea7kGVgx9kWh5XjWz9wjZvY49UKIT5ppIAWPMbLl3UpfckiuNhTA==
|
||||
expect(server_key).toBeNull();
|
||||
});
|
||||
|
||||
it('should not show certs and keys field with internal mTLS', () => {
|
||||
formHelper.setValue('enable_mtls', true);
|
||||
formHelper.setValue('certificateType', 'internal');
|
||||
fixture.detectChanges();
|
||||
const root_ca_cert = fixture.debugElement.query(By.css('#root_ca_cert'));
|
||||
const client_cert = fixture.debugElement.query(By.css('#client_cert'));
|
||||
const client_key = fixture.debugElement.query(By.css('#client_key'));
|
||||
const server_cert = fixture.debugElement.query(By.css('#server_cert'));
|
||||
const server_key = fixture.debugElement.query(By.css('#server_key'));
|
||||
expect(root_ca_cert).toBeNull();
|
||||
expect(client_cert).toBeNull();
|
||||
expect(client_key).toBeNull();
|
||||
expect(server_cert).toBeNull();
|
||||
expect(server_key).toBeNull();
|
||||
});
|
||||
|
||||
it('should submit nvmeof without mTLS', () => {
|
||||
component.onSubmit();
|
||||
expect(cephServiceService.create).toHaveBeenCalledWith({
|
||||
@ -572,6 +588,7 @@ x4Ea7kGVgx9kWh5XjWz9wjZvY49UKIT5ppIAWPMbLl3UpfckiuNhTA==
|
||||
|
||||
it('should submit nvmeof with mTLS', () => {
|
||||
formHelper.setValue('enable_mtls', true);
|
||||
formHelper.setValue('certificateType', 'external');
|
||||
formHelper.setValue('root_ca_cert', 'root_ca_cert');
|
||||
formHelper.setValue('client_cert', 'client_cert');
|
||||
formHelper.setValue('client_key', 'client_key');
|
||||
@ -585,6 +602,8 @@ x4Ea7kGVgx9kWh5XjWz9wjZvY49UKIT5ppIAWPMbLl3UpfckiuNhTA==
|
||||
unmanaged: false,
|
||||
group: 'default',
|
||||
enable_auth: true,
|
||||
ssl: true,
|
||||
certificate_source: 'inline',
|
||||
root_ca_cert: 'root_ca_cert',
|
||||
client_cert: 'client_cert',
|
||||
client_key: 'client_key',
|
||||
@ -592,6 +611,23 @@ x4Ea7kGVgx9kWh5XjWz9wjZvY49UKIT5ppIAWPMbLl3UpfckiuNhTA==
|
||||
server_key: 'server_key'
|
||||
});
|
||||
});
|
||||
|
||||
it('should submit nvmeof with internal mTLS', () => {
|
||||
formHelper.setValue('enable_mtls', true);
|
||||
formHelper.setValue('certificateType', 'internal');
|
||||
component.onSubmit();
|
||||
expect(cephServiceService.create).toHaveBeenCalledWith({
|
||||
service_type: 'nvmeof',
|
||||
service_id: 'rbd.default',
|
||||
placement: {},
|
||||
unmanaged: false,
|
||||
pool: 'rbd',
|
||||
group: 'default',
|
||||
enable_auth: true,
|
||||
ssl: true,
|
||||
certificate_source: 'cephadm-signed'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should test service smb', () => {
|
||||
|
||||
@ -238,7 +238,8 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
CdValidators.composeIf(
|
||||
{
|
||||
service_type: 'nvmeof',
|
||||
enable_mtls: true
|
||||
enable_mtls: true,
|
||||
certificateType: CertificateType.external
|
||||
},
|
||||
[Validators.required]
|
||||
)
|
||||
@ -250,7 +251,8 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
CdValidators.composeIf(
|
||||
{
|
||||
service_type: 'nvmeof',
|
||||
enable_mtls: true
|
||||
enable_mtls: true,
|
||||
certificateType: CertificateType.external
|
||||
},
|
||||
[Validators.required]
|
||||
)
|
||||
@ -262,7 +264,8 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
CdValidators.composeIf(
|
||||
{
|
||||
service_type: 'nvmeof',
|
||||
enable_mtls: true
|
||||
enable_mtls: true,
|
||||
certificateType: CertificateType.external
|
||||
},
|
||||
[Validators.required]
|
||||
)
|
||||
@ -274,7 +277,8 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
CdValidators.composeIf(
|
||||
{
|
||||
service_type: 'nvmeof',
|
||||
enable_mtls: true
|
||||
enable_mtls: true,
|
||||
certificateType: CertificateType.external
|
||||
},
|
||||
[Validators.required]
|
||||
)
|
||||
@ -286,7 +290,8 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
CdValidators.composeIf(
|
||||
{
|
||||
service_type: 'nvmeof',
|
||||
enable_mtls: true
|
||||
enable_mtls: true,
|
||||
certificateType: CertificateType.external
|
||||
},
|
||||
[Validators.required]
|
||||
)
|
||||
@ -781,11 +786,19 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
case 'nvmeof':
|
||||
this.serviceForm.get('group').setValue(response[0].spec.group);
|
||||
this.serviceForm.get('enable_mtls').setValue(response[0].spec?.enable_auth);
|
||||
this.serviceForm.get('root_ca_cert').setValue(response[0].spec?.root_ca_cert);
|
||||
this.serviceForm.get('client_cert').setValue(response[0].spec?.client_cert);
|
||||
this.serviceForm.get('client_key').setValue(response[0].spec?.client_key);
|
||||
this.serviceForm.get('server_cert').setValue(response[0].spec?.server_cert);
|
||||
this.serviceForm.get('server_key').setValue(response[0].spec?.server_key);
|
||||
if (response[0].spec?.enable_auth) {
|
||||
if (response[0].spec?.certificate_source !== 'cephadm-signed') {
|
||||
this.serviceForm.get('certificateType').setValue(CertificateType.external);
|
||||
}
|
||||
if (response[0].spec?.['custom_sans']) {
|
||||
this.serviceForm.get('custom_sans').setValue(response[0].spec['custom_sans']);
|
||||
}
|
||||
this.serviceForm.get('root_ca_cert').setValue(response[0].spec?.root_ca_cert);
|
||||
this.serviceForm.get('client_cert').setValue(response[0].spec?.client_cert);
|
||||
this.serviceForm.get('client_key').setValue(response[0].spec?.client_key);
|
||||
this.serviceForm.get('server_cert').setValue(response[0].spec?.server_cert);
|
||||
this.serviceForm.get('server_key').setValue(response[0].spec?.server_key);
|
||||
}
|
||||
break;
|
||||
case 'rgw':
|
||||
this.serviceForm
|
||||
@ -1329,11 +1342,22 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
serviceSpec['group'] = values['group'];
|
||||
serviceSpec['enable_auth'] = values['enable_mtls'];
|
||||
if (values['enable_mtls']) {
|
||||
serviceSpec['root_ca_cert'] = values['root_ca_cert'];
|
||||
serviceSpec['client_cert'] = values['client_cert'];
|
||||
serviceSpec['client_key'] = values['client_key'];
|
||||
serviceSpec['server_cert'] = values['server_cert'];
|
||||
serviceSpec['server_key'] = values['server_key'];
|
||||
serviceSpec['ssl'] = true;
|
||||
serviceSpec['certificate_source'] =
|
||||
values['certificateType'] === CertificateType.internal ? 'cephadm-signed' : 'inline';
|
||||
if (
|
||||
values['certificateType'] === CertificateType.internal &&
|
||||
values['custom_sans']?.length > 0
|
||||
) {
|
||||
serviceSpec['custom_sans'] = values['custom_sans'];
|
||||
}
|
||||
if (values['certificateType'] === CertificateType.external) {
|
||||
serviceSpec['root_ca_cert'] = values['root_ca_cert'];
|
||||
serviceSpec['client_cert'] = values['client_cert'];
|
||||
serviceSpec['client_key'] = values['client_key'];
|
||||
serviceSpec['server_cert'] = values['server_cert'];
|
||||
serviceSpec['server_key'] = values['server_key'];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'iscsi':
|
||||
@ -1583,7 +1607,7 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
return isExternalCert;
|
||||
}
|
||||
|
||||
const sslCertServices = ['rgw', 'ingress', 'iscsi', 'grafana', 'oauth2-proxy', 'nvmeof', 'nfs'];
|
||||
const sslCertServices = ['rgw', 'ingress', 'iscsi', 'grafana', 'oauth2-proxy', 'nfs'];
|
||||
return isSslEnabled && isExternalCert && sslCertServices.includes(serviceType);
|
||||
}
|
||||
|
||||
@ -1593,7 +1617,7 @@ export class ServiceFormComponent extends CdForm implements OnInit {
|
||||
this.serviceForm.controls.certificateType?.value === CertificateType.external;
|
||||
const isSslEnabled = this.serviceForm.controls.ssl?.value;
|
||||
|
||||
const sslKeyServices = ['iscsi', 'grafana', 'oauth2-proxy', 'nvmeof', 'nfs', 'mgmt-gateway'];
|
||||
const sslKeyServices = ['iscsi', 'grafana', 'oauth2-proxy', 'nfs', 'mgmt-gateway'];
|
||||
return isSslEnabled && isExternalCert && sslKeyServices.includes(serviceType);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user