From: Abhishek Desai Date: Mon, 15 Jun 2026 11:12:15 +0000 (+0530) Subject: mgr/dashboard : Enable cephadm-signed TLS for NVMeOf X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=517f6da8a9fa7b4c423a6082eb7d21893a4d8e4b;p=ceph.git mgr/dashboard : Enable cephadm-signed TLS for NVMeOf fixes: https://tracker.ceph.com/issues/77406 Signed-off-by: Abhishek Desai (cherry picked from commit 9623f6847cecf7f2e79f461420fce4821fd1b159) --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html index 0e7ecb82f24..1fc6559d020 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html @@ -1153,112 +1153,8 @@ } - @if (serviceForm.controls.ssl.value && ['rgw', 'ingress', 'iscsi', 'grafana', 'oauth2-proxy', 'mgmt-gateway', 'nvmeof', 'nfs'].includes(serviceForm.controls.service_type.value)) { -
- - @if (editing && currentCertificate?.has_certificate) { -
- -
-
- -
{{ currentCertificate.cert_name }}
-
-
- -
{{ currentCertificate.expiry_date | cdDate }} • {{ currentCertificate.days_to_expiration }} days left
-
-
-
-
- -
- - @switch (currentCertificate.status) { - @case ('valid') { Valid } - @case ('expiring') { Expiring soon } - @case ('expired') { Expired } - @case ('not_configured') { Not configured } - @case ('invalid') { Invalid } - @default { {{ currentCertificate.status }} } - } -
-
-
- -
- @if (currentCertificate.signed_by === 'cephadm') { - Internal (Cephadm CA) - } @else { - {{ currentCertificate.issuer || 'External' }} - } -
-
-
-
- } - - -
- - - - Internal - - - External - - -
- - @if (showCertSourceChangeWarning) { - - Changing the certificate source will redeploy the service daemons to apply the new certificate configuration. - - } - - @if (serviceForm.controls.certificateType.value === CertificateType.internal) { - - Certificate will be generated automatically by Cephadm CA for internal certificate type. - -
- - -
- @if (serviceForm.controls.service_type.value === 'rgw' - && serviceForm.controls.virtual_host_enabled.value) { -
- - Include wildcard certificate for bucket subdomains - - Add wildcard certificates (*.domain) to allow SSL for all bucket subdomains. Required for virtual-host style with SSL. - - -
- } - } -
+ @if (serviceForm.controls.ssl.value && ['rgw', 'ingress', 'iscsi', 'grafana', 'oauth2-proxy', 'mgmt-gateway', 'nfs'].includes(serviceForm.controls.service_type.value)) { + } @@ -1396,6 +1292,9 @@ @if (serviceForm.controls.enable_mtls.value) { + + + @if (serviceForm.controls.certificateType.value === CertificateType.external) {
@@ -1452,6 +1351,7 @@
} } + } + +
+ + @if (editing && currentCertificate?.has_certificate) { +
+ +
+
+ +
{{ currentCertificate.cert_name }}
+
+
+ +
{{ currentCertificate.expiry_date | cdDate }} • {{ currentCertificate.days_to_expiration }} days left
+
+
+
+
+ +
+ + @switch (currentCertificate.status) { + @case ('valid') { Valid } + @case ('expiring') { Expiring soon } + @case ('expired') { Expired } + @case ('not_configured') { Not configured } + @case ('invalid') { Invalid } + @default { {{ currentCertificate.status }} } + } +
+
+
+ +
+ @if (currentCertificate.signed_by === 'cephadm') { + Internal (Cephadm CA) + } @else { + {{ currentCertificate.issuer || 'External' }} + } +
+
+
+
+ } + + +
+ + + + Internal + + + External + + +
+ + @if (showCertSourceChangeWarning) { + + Changing the certificate source will redeploy the service daemons to apply the new certificate configuration. + + } + + @if (serviceForm.controls.certificateType.value === CertificateType.internal) { + + Certificate will be generated automatically by Cephadm CA for internal certificate type. + +
+ + +
+ @if (serviceForm.controls.service_type.value === 'rgw' + && serviceForm.controls.virtual_host_enabled.value) { +
+ + Include wildcard certificate for bucket subdomains + + Add wildcard certificates (*.domain) to allow SSL for all bucket subdomains. Required for virtual-host style with SSL. + + +
+ } + } +
+
+ { + 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', () => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts index 86a074fef8c..6c10ff5a2ce 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.ts @@ -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); }