i18n>
Authentication must be enabled in an active `mgtm-gateway` service to enable Single Sign-On(SSO) with `oauth2-proxy`
</cd-alert-panel>
+ <cd-alert-panel *ngIf="serviceForm.controls.service_type.value === 'mgmt-gateway'"
+ type="info"
+ spacingClass="mb-3"
+ i18n>
+ With an active mgmt-gateway service, the dashboard will continue to be served on {{currentURL}}:{{port}} and all other services will be accessible from {{currentURL}}:{{port}}/service_name
+ </cd-alert-panel>
<!-- Service type -->
<div class="form-group row">
</div>
</ng-container>
- <!-- RGW, Ingress, iSCSI & Oauth2-proxy -->
- <ng-container *ngIf="!serviceForm.controls.unmanaged.value && ['rgw', 'iscsi', 'ingress', 'oauth2-proxy'].includes(serviceForm.controls.service_type.value)">
- <!-- ssl -->
+ <ng-container *ngIf="!serviceForm.controls.unmanaged.value && ['mgmt-gateway'].includes(serviceForm.controls.service_type.value)">
+ <!-- port -->
<div class="form-group row">
- <div class="cd-col-form-offset">
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- id="ssl"
- type="checkbox"
- formControlName="ssl">
- <label class="custom-control-label"
- for="ssl"
- i18n>SSL</label>
- </div>
+ <label i18n
+ class="cd-col-form-label"
+ for="port">Port</label>
+ <div class="cd-col-form-input">
+ <input id="port"
+ class="form-control"
+ type="number"
+ formControlName="port"
+ min="1"
+ max="65535">
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('port', frm, 'pattern')"
+ i18n>The entered value needs to be a number.</span>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('port', frm, 'min')"
+ i18n>The value must be at least 1.</span>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('port', frm, 'max')"
+ i18n>The value cannot exceed 65535.</span>
</div>
</div>
-
+ <!-- enable_auth -->
+ <div class="form-item">
+ <fieldset>
+ <label class="cds--label"
+ for="pools"
+ i18n>Authentication</label>
+ <cds-checkbox i18n-label
+ id="enable_auth"
+ name="enable_auth"
+ formControlName="enable_auth">
+ Enable
+ <cd-help-text i18n>
+ Allows to enable authentication through an external Identity Provider (IdP) using Single Sign-On (SSO)
+ </cd-help-text>
+ </cds-checkbox>
+ </fieldset>
+ </div>
+ <!-- ssl_protocols -->
+ <div class="form-item">
+ <cds-combo-box type="multi"
+ label="SSL protocols"
+ selectionFeedback="top-after-reopen"
+ for="ssl_protocols"
+ name="ssl_protocols"
+ formControlName="ssl_protocols"
+ id="ssl_protocols"
+ placeholder="Select protocols..."
+ [appendInline]="true"
+ [items]="sslProtocolsItems"
+ i18n-placeholder
+ i18n>
+ <cds-dropdown-list></cds-dropdown-list>
+ </cds-combo-box>
+ </div>
+ <!-- ssl_ciphers -->
+ <div class="form-group row">
+ <label class="cd-col-form-label"
+ for="ssl_ciphers">
+ <span i18n>SSL ciphers</span>
+ </label>
+ <div class="cd-col-form-input">
+ <div class="input-group">
+ <input id="ssl_ciphers"
+ class="form-control"
+ type="text"
+ formControlName="ssl_ciphers"
+ placeholder="ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256">
+ </div>
+ <cd-help-text i18n>Default cipher list used: <a href="https://ssl-config.mozilla.org/#server=nginx"
+ target="_blank">https://ssl-config.mozilla.org/#server=nginx</a></cd-help-text>
+ <span class="invalid-feedback"
+ *ngIf="serviceForm.showError('ssl_ciphers', frm, 'invalidPattern')"
+ i18n>Invalid cipher suite. Each cipher must be separated by '-' and each cipher suite must be separated by ':'</span>
+ </div>
+ </div>
+ </ng-container>
+ <!-- RGW, Ingress, iSCSI, Oauth2-proxy & mgmt-gateway -->
+ <ng-container *ngIf="!serviceForm.controls.unmanaged.value && ['rgw', 'iscsi', 'ingress', 'oauth2-proxy', 'mgmt-gateway'].includes(serviceForm.controls.service_type.value)">
+ <!-- ssl -->
+ <ng-container *ngIf="!['mgmt-gateway'].includes(serviceForm.controls.service_type.value)">
+ <div class="form-group row">
+ <div class="cd-col-form-offset">
+ <div class="custom-control custom-checkbox">
+ <input class="custom-control-input"
+ id="ssl"
+ type="checkbox"
+ formControlName="ssl">
+ <label class="custom-control-label"
+ for="ssl"
+ i18n>SSL</label>
+ </div>
+ </div>
+ </div>
+ </ng-container>
<!-- ssl_cert -->
- <div *ngIf="serviceForm.controls.ssl.value"
+ <div *ngIf="serviceForm.controls.ssl.value || ['mgmt-gateway'].includes(serviceForm.controls.service_type.value)"
class="form-group row">
<label class="cd-col-form-label"
for="ssl_cert">
</div>
<!-- ssl_key -->
- <div *ngIf="serviceForm.controls.ssl.value && !(['rgw', 'ingress'].includes(serviceForm.controls.service_type.value))"
+ <div *ngIf="(serviceForm.controls.ssl.value && !(['rgw', 'ingress'].includes(serviceForm.controls.service_type.value))) || ['mgmt-gateway'].includes(serviceForm.controls.service_type.value)"
class="form-group row">
<label class="cd-col-form-label"
for="ssl_key">
</div>
</div>
</ng-container>
+
+ <cd-alert-panel *ngIf="serviceForm.controls.service_type.value === 'mgmt-gateway' && showMgmtGatewayMessage"
+ type="warning"
+ spacingClass="mb-3"
+ i18n>
+ Modifying the default settings could lead to a weaker security configuration
+ </cd-alert-panel>
</div>
<div class="modal-footer">
import { ActivatedRoute, Router } from '@angular/router';
import { NgbActiveModal, NgbModalRef, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap';
+import { ListItem } from 'carbon-components-angular';
import _ from 'lodash';
import { forkJoin, merge, Observable, Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, map } from 'rxjs/operators';
import {
ActionLabelsI18n,
TimerServiceInterval,
- URLVerbs
+ URLVerbs,
+ SSL_PROTOCOLS,
+ SSL_CIPHERS
} from '~/app/shared/constants/app.constants';
import { CdForm } from '~/app/shared/forms/cd-form';
import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
readonly INGRESS_SUPPORTED_SERVICE_TYPES = ['rgw', 'nfs'];
readonly SMB_CONFIG_URI_PATTERN = /^(http:|https:|rados:|rados:mon-config-key:)/;
readonly OAUTH2_ISSUER_URL_PATTERN = /^(https?:\/\/)?([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+)(:[0-9]{1,5})?(\/.*)?$/;
+ readonly SSL_CIPHERS_PATTERN = /^[a-zA-Z0-9\-:]+$/;
+ readonly DEFAULT_SSL_PROTOCOL_ITEM = [{ content: 'TLSv1.3', selected: true }];
@ViewChild(NgbTypeahead, { static: false })
typeahead: NgbTypeahead;
zonegroupNames: string[];
zoneNames: string[];
smbFeaturesList = ['domain'];
+ currentURL: string;
+ port: number = 443;
+ sslProtocolsItems: Array<ListItem> = Object.values(SSL_PROTOCOLS).map((protocol) => ({
+ content: protocol,
+ selected: true
+ }));
+ sslCiphersItems: Array<ListItem> = Object.values(SSL_CIPHERS).map((cipher) => ({
+ content: cipher,
+ selected: false
+ }));
+ showMgmtGatewayMessage: boolean = false;
constructor(
public actionLabels: ActionLabelsI18n,
]
],
virtual_interface_networks: [null],
+ ssl_protocols: [this.DEFAULT_SSL_PROTOCOL_ITEM],
+ ssl_ciphers: [
+ null,
+ [
+ CdValidators.custom('invalidPattern', (ciphers: string) => {
+ if (_.isEmpty(ciphers)) {
+ return false;
+ }
+ return !this.SSL_CIPHERS_PATTERN.test(ciphers);
+ })
+ ]
+ ],
// RGW, Ingress & iSCSI
ssl: [false],
ssl_cert: [
ssl: true
},
[Validators.required, CdValidators.sslCert()]
+ ),
+ CdValidators.composeIf(
+ {
+ service_type: 'mgmt-gateway',
+ unmanaged: false,
+ ssl: false
+ },
+ [CdValidators.sslCert()]
)
]
],
ssl: true
},
[Validators.required, CdValidators.sslPrivKey()]
+ ),
+ CdValidators.composeIf(
+ {
+ service_type: 'mgmt-gateway',
+ unmanaged: false,
+ ssl: false
+ },
+ [CdValidators.sslPrivKey()]
)
]
],
+ // mgmt-gateway
+ enable_auth: [null],
+ port: [443, [CdValidators.number(false)]],
// snmp-gateway
snmp_version: [
null,
this.serviceForm.get('ssl_key').setValue(response[0].spec?.ssl_key);
}
break;
+ case 'mgmt-gateway':
+ let hrefSplitted = window.location.href.split(':');
+ this.currentURL = hrefSplitted[0] + hrefSplitted[1];
+ this.port = response[0].spec?.port;
+
+ if (response[0].spec?.ssl_protocols) {
+ let selectedValues: Array<ListItem> = [];
+ for (const value of response[0].spec.ssl_protocols) {
+ selectedValues.push({ content: value, selected: true });
+ }
+ this.serviceForm.get('ssl_protocols').setValue(selectedValues);
+ }
+ if (response[0].spec?.ssl_ciphers) {
+ this.serviceForm
+ .get('ssl_ciphers')
+ .setValue(response[0].spec?.ssl_ciphers.join(':'));
+ }
+ if (response[0].spec?.ssl_cert) {
+ this.serviceForm.get('ssl_cert').setValue(response[0].spec.ssl_certificate);
+ }
+ if (response[0].spec?.ssl_key) {
+ this.serviceForm.get('ssl_key').setValue(response[0].spec.ssl_certificate_key);
+ }
+ if (response[0].spec?.enable_auth) {
+ this.serviceForm.get('enable_auth').setValue(response[0].spec.enable_auth);
+ }
+ if (response[0].spec?.port) {
+ this.serviceForm.get('port').setValue(response[0].spec.port);
+ }
+ break;
case 'smb':
const smbSpecKeys = [
'cluster_id',
}
});
}
+ this.detectChanges();
+ }
+
+ detectChanges(): void {
+ const service_type = this.serviceForm.get('service_type');
+ if (service_type) {
+ service_type.valueChanges.subscribe((value) => {
+ if (value === 'mgmt-gateway') {
+ const port = this.serviceForm.get('port');
+ if (port) {
+ port.valueChanges.subscribe((_) => {
+ this.showMgmtGatewayMessage = true;
+ });
+ }
+ const ssl_protocols = this.serviceForm.get('ssl_protocols');
+ if (ssl_protocols) {
+ ssl_protocols.valueChanges.subscribe((_) => {
+ this.showMgmtGatewayMessage = true;
+ });
+ }
+ const ssl_ciphers = this.serviceForm.get('ssl_ciphers');
+ if (ssl_ciphers) {
+ ssl_ciphers.valueChanges.subscribe((_) => {
+ this.showMgmtGatewayMessage = true;
+ });
+ }
+ }
+ });
+ }
}
getDefaultsEntitiesForRgw(
case 'jaeger-query':
case 'smb':
case 'oauth2-proxy':
+ case 'mgmt-gateway':
this.serviceForm.get('count').setValue(1);
break;
default:
if (selectedServiceType === 'rgw') {
this.setRgwFields();
}
+ if (selectedServiceType === 'mgmt-gateway') {
+ let hrefSplitted = window.location.href.split(':');
+ this.currentURL = hrefSplitted[0] + hrefSplitted[1];
+ // mgmt-gateway lacks HA for now
+ this.serviceForm.get('count').disable();
+ } else {
+ this.serviceForm.get('count').enable();
+ }
}
onPlacementChange(selected: string) {
}
serviceSpec['virtual_interface_networks'] = values['virtual_interface_networks'];
break;
+ case 'mgmt-gateway':
+ serviceSpec['ssl_certificate'] = values['ssl_cert']?.trim();
+ serviceSpec['ssl_certificate_key'] = values['ssl_key']?.trim();
+ serviceSpec['enable_auth'] = values['enable_auth'];
+ serviceSpec['port'] = values['port'];
+ if (serviceSpec['port'] === (443 || 80)) {
+ // omit port default values due to issues with redirect_url on the backend
+ delete serviceSpec['port'];
+ }
+ serviceSpec['ssl_protocols'] = [];
+ if (values['ssl_protocols'] != this.DEFAULT_SSL_PROTOCOL_ITEM) {
+ for (const key of Object.keys(values['ssl_protocols'])) {
+ serviceSpec['ssl_protocols'].push(values['ssl_protocols'][key]['content']);
+ }
+ }
+ serviceSpec['ssl_ciphers'] = values['ssl_ciphers']?.trim().split(':');
+ break;
case 'grafana':
serviceSpec['port'] = values['grafana_port'];
serviceSpec['initial_admin_password'] = values['grafana_admin_password'];
serviceSpec['oidc_issuer_url'] = values['oidc_issuer_url']?.trim();
serviceSpec['https_address'] = values['https_address']?.trim();
serviceSpec['redirect_url'] = values['redirect_url']?.trim();
- serviceSpec['allowlist_domains'] = values['allowlist_domains']?.trim().split(',');
+ serviceSpec['allowlist_domains'] = values['allowlist_domains']
+ .split(',')
+ .map((domain: string) => {
+ return domain.trim();
+ });
if (values['ssl']) {
serviceSpec['ssl_cert'] = values['ssl_cert']?.trim();
serviceSpec['ssl_key'] = values['ssl_key']?.trim();