}
</ng-template>
</div>
+
+ @if (!rgwModuleEnabled) {
+ <cd-alert-panel type="warning"
+ spacingClass="mb-3"
+ actionName="Enable"
+ (action)="enableRgwModule()"
+ i18n-actionName
+ i18n>
+ The RGW mgr module must be enabled to configure S3 hostnames.
+ Enabling the module will cause temporary manager downtime while it loads.
+ </cd-alert-panel>
+ }
+ <div class="form-item">
+ <cds-checkbox i18n-label
+ formControlName="virtual_host_enabled">
+ Enable virtual-host style bucket access
+ <cd-help-text i18n>
+ Allows bucket access via hostnames such as mybucket.s3.example.com.
+ </cd-help-text>
+ </cds-checkbox>
+ </div>
+ @if (serviceForm.controls.virtual_host_enabled.value) {
+ <div class="form-item">
+ <cd-text-label-list formControlName="zonegroup_hostnames"
+ label="S3 hostname"
+ i18n-label
+ helperText="Domain names for S3 virtual-host bucket access (e.g., s3.example.com). Enables bucket URLs like bucket.s3.example.com instead of s3.example.com/bucket."
+ i18n-helperText>
+ </cd-text-label-list>
+ </div>
+ }
}
<!-- iSCSI -->
<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="internal"
- (change)="onCertificateTypeChange('internal')"
+ <cds-radio [value]="CertificateType.internal"
+ (change)="onCertificateTypeChange(CertificateType.internal)"
i18n>
Internal
</cds-radio>
- <cds-radio value="external"
- (change)="onCertificateTypeChange('external')"
+ <cds-radio [value]="CertificateType.external"
+ (change)="onCertificateTypeChange(CertificateType.external)"
i18n>
External
</cds-radio>
</cd-alert-panel>
}
- @if (serviceForm.controls.certificateType.value === 'internal') {
+ @if (serviceForm.controls.certificateType.value === CertificateType.internal) {
<cd-alert-panel type="info"
spacingClass="mb-3"
i18n>
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>
</div>
<!-- ssl_ca_cert - Only show for NFS when SSL is enabled AND certificate type is external -->
- @if (serviceForm.controls.ssl.value && serviceForm.controls.certificateType.value === 'external' && serviceForm.controls.service_type.value === 'nfs') {
+ @if (serviceForm.controls.ssl.value && serviceForm.controls.certificateType.value === CertificateType.external && serviceForm.controls.service_type.value === 'nfs') {
<div class="form-item">
<ng-container *ngTemplateOutlet="fileUploaderTextarea; context: { controlName: 'ssl_ca_cert', title: 'CA Certificate Input', helperText: 'Uploaded files will populate the CA certificate details automatically. Or paste the PEM content directly in the text area.', description: 'Upload a CA certificate file, or paste the CA certificate PEM content directly.', invalidTemplate: invalidSslCaCertError, isRequired: false }"></ng-container>
<ng-template #invalidSslCaCertError>
import { PoolService } from '~/app/shared/api/pool.service';
import { RbdService } from '~/app/shared/api/rbd.service';
import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
+import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
import { RgwRealmService } from '~/app/shared/api/rgw-realm.service';
import { RgwZoneService } from '~/app/shared/api/rgw-zone.service';
import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
import {
CephServiceCertificate,
CephServiceSpec,
+ CertificateType,
QatOptions,
QatSepcs,
CERTIFICATE_STATUS_ICON_MAP
export class ServiceFormComponent extends CdForm implements OnInit {
public sub = new Subscription();
+ readonly CertificateType = CertificateType;
readonly MDS_SVC_ID_PATTERN = /^[a-zA-Z_.-][a-zA-Z0-9_.-]*$/;
readonly SNMP_DESTINATION_PATTERN = /^[^\:]+:[0-9]/;
readonly SNMP_ENGINE_ID_PATTERN = /^[0-9A-Fa-f]{10,64}/g;
}));
showMgmtGatewayMessage: boolean = false;
showCertSourceChangeWarning: boolean = false;
+ rgwModuleEnabled = false;
qatCompressionOptions = [
{ value: QatOptions.hw, label: 'Hardware' },
{ value: QatOptions.sw, label: 'Software' },
public rgwZonegroupService: RgwZonegroupService,
public rgwZoneService: RgwZoneService,
public rgwMultisiteService: RgwMultisiteService,
+ private mgrModuleService: MgrModuleService,
private route: ActivatedRoute,
public modalService: ModalCdsService,
private location: Location
service_type: 'rgw',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.pemCert()]
),
service_type: 'iscsi',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.sslCert()]
),
service_type: 'ingress',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.pemCert()]
),
service_type: 'oauth2-proxy',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.sslCert()]
),
service_type: 'mgmt-gateway',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.sslCert()]
),
service_type: 'nfs',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.pemCert()]
)
service_type: 'iscsi',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.sslPrivKey()]
),
service_type: 'oauth2-proxy',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.sslPrivKey()]
),
service_type: 'mgmt-gateway',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.sslPrivKey()]
),
service_type: 'nfs',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.sslPrivKey()]
)
]
],
- certificateType: ['internal'],
+ certificateType: [CertificateType.internal],
custom_sans: [null],
+ virtual_host_enabled: [false],
+ zonegroup_hostnames: [null],
+ wildcard_enabled: [true],
ssl_ca_cert: [
'',
[
service_type: 'nfs',
unmanaged: false,
ssl: true,
- certificateType: 'external'
+ certificateType: CertificateType.external
},
[Validators.required, CdValidators.pemCert()]
)
this.open = true;
this.action = this.actionLabels.CREATE;
this.resolveRoute();
+ this.getRgwModuleStatus();
+ this.mgrModuleService.updateCompleted$.subscribe(() => this.getRgwModuleStatus());
this.cephServiceService
.list(new HttpParams({ fromObject: { limit: -1, offset: 0 } }))
response[0].spec?.qat
);
this.serviceForm.get('ssl').setValue(response[0].spec?.ssl);
+ if (response[0].spec?.zonegroup_hostnames?.length) {
+ this.serviceForm
+ .get('zonegroup_hostnames')
+ .setValue(response[0].spec.zonegroup_hostnames);
+ if (this.rgwModuleEnabled) {
+ this.serviceForm.get('virtual_host_enabled').setValue(true);
+ }
+ }
+ this.serviceForm
+ .get('wildcard_enabled')
+ .setValue(response[0].spec?.wildcard_enabled ?? true);
if (response[0].spec?.ssl) {
// Special case for rgw: if certificate_source is not cephadm-signed, set certificateType to external
if (response[0].spec?.certificate_source != 'cephadm-signed') {
- this.serviceForm.get('certificateType').setValue('external');
+ this.serviceForm.get('certificateType').setValue(CertificateType.external);
}
let certValue = response[0].spec?.rgw_frontend_ssl_certificate || '';
if (response[0].spec?.ssl_cert) {
}
}
this.serviceForm.get('ssl_cert').setValue(certValue);
+ if (response[0].spec?.custom_sans) {
+ this.serviceForm.get('custom_sans').setValue(response[0].spec.custom_sans);
+ }
}
break;
case 'ingress':
this.port = response[0].spec?.port;
this.serviceForm.get('ssl').setValue(true);
if (response[0].spec?.certificate_source !== 'cephadm-signed') {
- this.serviceForm.get('certificateType').setValue('external');
+ this.serviceForm.get('certificateType').setValue(CertificateType.external);
}
if (response[0].spec?.ssl_protocols) {
let selectedValues: Array<ListItem> = [];
}
}
- onCertificateTypeChange(type: string) {
+ onCertificateTypeChange(type: CertificateType) {
this.serviceForm.get('certificateType').setValue(type);
if (this.editing && this.currentCertificate?.has_certificate) {
const originalSource =
- this.currentSpecCertificateSource === 'cephadm-signed' ? 'internal' : 'external';
+ this.currentSpecCertificateSource === 'cephadm-signed'
+ ? CertificateType.internal
+ : CertificateType.external;
this.showCertSourceChangeWarning = type !== originalSource;
}
}
+ private getRgwModuleStatus(): void {
+ this.rgwMultisiteService.getRgwModuleStatus().subscribe((enabled: boolean) => {
+ this.rgwModuleEnabled = enabled;
+ const virtualHostControl = this.serviceForm.get('virtual_host_enabled');
+ if (enabled) {
+ virtualHostControl.enable({ emitEvent: false });
+ if (this.serviceForm.get('zonegroup_hostnames').value?.length) {
+ virtualHostControl.setValue(true, { emitEvent: false });
+ }
+ } else {
+ virtualHostControl.setValue(false, { emitEvent: false });
+ virtualHostControl.disable({ emitEvent: false });
+ }
+ });
+ }
+
+ enableRgwModule(): void {
+ this.mgrModuleService.updateModuleState('rgw', false, null, '', $localize`Enabled RGW Module`);
+ }
+
prePopulateId() {
const control: AbstractControl = this.serviceForm.get('service_id');
const backendService = this.serviceForm.getValue('backend_service');
serviceSpec['rgw_frontend_port'] = values['rgw_frontend_port'];
}
serviceSpec['ssl'] = values['ssl'];
+ if (values['virtual_host_enabled'] && values['zonegroup_hostnames']?.length > 0) {
+ serviceSpec['zonegroup_hostnames'] = values['zonegroup_hostnames'];
+ }
if (values['ssl']) {
this.applySslCertificateConfig(serviceSpec, values, {
sslCertField: 'rgw_frontend_ssl_certificate',
includeSslKey: false
});
+ if (
+ values['certificateType'] === CertificateType.internal &&
+ values['virtual_host_enabled'] &&
+ values['zonegroup_hostnames']?.length > 0
+ ) {
+ serviceSpec['wildcard_enabled'] = values['wildcard_enabled'];
+ }
}
break;
case 'iscsi':
get showExternalSslCert(): boolean {
const serviceType = this.serviceForm.controls.service_type?.value;
- const isExternalCert = this.serviceForm.controls.certificateType?.value === 'external';
+ const isExternalCert =
+ this.serviceForm.controls.certificateType?.value === CertificateType.external;
const isSslEnabled = this.serviceForm.controls.ssl?.value;
if (serviceType === 'mgmt-gateway') {
get showExternalSslKey(): boolean {
const serviceType = this.serviceForm.controls.service_type?.value;
- const isExternalCert = this.serviceForm.controls.certificateType?.value === 'external';
+ const isExternalCert =
+ this.serviceForm.controls.certificateType?.value === CertificateType.external;
const isSslEnabled = this.serviceForm.controls.ssl?.value;
const sslKeyServices = ['iscsi', 'grafana', 'oauth2-proxy', 'nvmeof', 'nfs', 'mgmt-gateway'];
} = options;
serviceSpec['certificate_source'] =
- values['certificateType'] === 'internal' ? 'cephadm-signed' : 'inline';
+ values['certificateType'] === CertificateType.internal ? 'cephadm-signed' : 'inline';
- if (values['certificateType'] === 'internal' && values['custom_sans']?.length > 0) {
+ if (
+ values['certificateType'] === CertificateType.internal &&
+ values['custom_sans']?.length > 0
+ ) {
serviceSpec['custom_sans'] = values['custom_sans'];
}
- if (values['certificateType'] === 'external') {
+ if (values['certificateType'] === CertificateType.external) {
serviceSpec[sslCertField] = values['ssl_cert']?.trim();
if (includeSslKey) {
serviceSpec[sslKeyField] = values['ssl_key']?.trim();