diff --git a/models/tenant_monitoring_info.go b/models/tenant_monitoring_info.go index b3770458a..5e415f09a 100644 --- a/models/tenant_monitoring_info.go +++ b/models/tenant_monitoring_info.go @@ -42,6 +42,9 @@ type TenantMonitoringInfo struct { // disk capacity g b DiskCapacityGB string `json:"diskCapacityGB,omitempty"` + // fs group + FsGroup string `json:"fsGroup,omitempty"` + // image Image string `json:"image,omitempty"` diff --git a/models/tenant_security_response.go b/models/tenant_security_response.go index 97afff420..b36579157 100644 --- a/models/tenant_security_response.go +++ b/models/tenant_security_response.go @@ -41,6 +41,9 @@ type TenantSecurityResponse struct { // custom certificates CustomCertificates *TenantSecurityResponseCustomCertificates `json:"customCertificates,omitempty"` + + // security context + SecurityContext *SecurityContext `json:"securityContext,omitempty"` } // Validate validates this tenant security response @@ -51,6 +54,10 @@ func (m *TenantSecurityResponse) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateSecurityContext(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -76,6 +83,25 @@ func (m *TenantSecurityResponse) validateCustomCertificates(formats strfmt.Regis return nil } +func (m *TenantSecurityResponse) validateSecurityContext(formats strfmt.Registry) error { + if swag.IsZero(m.SecurityContext) { // not required + return nil + } + + if m.SecurityContext != nil { + if err := m.SecurityContext.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("securityContext") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("securityContext") + } + return err + } + } + + return nil +} + // ContextValidate validate this tenant security response based on the context it is used func (m *TenantSecurityResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error @@ -84,6 +110,10 @@ func (m *TenantSecurityResponse) ContextValidate(ctx context.Context, formats st res = append(res, err) } + if err := m.contextValidateSecurityContext(ctx, formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -106,6 +136,22 @@ func (m *TenantSecurityResponse) contextValidateCustomCertificates(ctx context.C return nil } +func (m *TenantSecurityResponse) contextValidateSecurityContext(ctx context.Context, formats strfmt.Registry) error { + + if m.SecurityContext != nil { + if err := m.SecurityContext.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("securityContext") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("securityContext") + } + return err + } + } + + return nil +} + // MarshalBinary interface implementation func (m *TenantSecurityResponse) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/models/update_tenant_security_request.go b/models/update_tenant_security_request.go index c85b7387f..07b19fe94 100644 --- a/models/update_tenant_security_request.go +++ b/models/update_tenant_security_request.go @@ -41,6 +41,9 @@ type UpdateTenantSecurityRequest struct { // custom certificates CustomCertificates *UpdateTenantSecurityRequestCustomCertificates `json:"customCertificates,omitempty"` + + // security context + SecurityContext *SecurityContext `json:"securityContext,omitempty"` } // Validate validates this update tenant security request @@ -51,6 +54,10 @@ func (m *UpdateTenantSecurityRequest) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateSecurityContext(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -76,6 +83,25 @@ func (m *UpdateTenantSecurityRequest) validateCustomCertificates(formats strfmt. return nil } +func (m *UpdateTenantSecurityRequest) validateSecurityContext(formats strfmt.Registry) error { + if swag.IsZero(m.SecurityContext) { // not required + return nil + } + + if m.SecurityContext != nil { + if err := m.SecurityContext.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("securityContext") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("securityContext") + } + return err + } + } + + return nil +} + // ContextValidate validate this update tenant security request based on the context it is used func (m *UpdateTenantSecurityRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error @@ -84,6 +110,10 @@ func (m *UpdateTenantSecurityRequest) ContextValidate(ctx context.Context, forma res = append(res, err) } + if err := m.contextValidateSecurityContext(ctx, formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -106,6 +136,22 @@ func (m *UpdateTenantSecurityRequest) contextValidateCustomCertificates(ctx cont return nil } +func (m *UpdateTenantSecurityRequest) contextValidateSecurityContext(ctx context.Context, formats strfmt.Registry) error { + + if m.SecurityContext != nil { + if err := m.SecurityContext.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("securityContext") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("securityContext") + } + return err + } + } + + return nil +} + // MarshalBinary interface implementation func (m *UpdateTenantSecurityRequest) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/operatorapi/embedded_spec.go b/operatorapi/embedded_spec.go index bf28e0028..68aed379d 100644 --- a/operatorapi/embedded_spec.go +++ b/operatorapi/embedded_spec.go @@ -4274,6 +4274,9 @@ func init() { "diskCapacityGB": { "type": "string" }, + "fsGroup": { + "type": "string" + }, "image": { "type": "string" }, @@ -4381,6 +4384,10 @@ func init() { } } } + }, + "securityContext": { + "type": "object", + "$ref": "#/definitions/securityContext" } } }, @@ -4556,6 +4563,10 @@ func init() { } } } + }, + "securityContext": { + "type": "object", + "$ref": "#/definitions/securityContext" } } }, @@ -9655,6 +9666,9 @@ func init() { "diskCapacityGB": { "type": "string" }, + "fsGroup": { + "type": "string" + }, "image": { "type": "string" }, @@ -9762,6 +9776,10 @@ func init() { } } } + }, + "securityContext": { + "type": "object", + "$ref": "#/definitions/securityContext" } } }, @@ -9937,6 +9955,10 @@ func init() { } } } + }, + "securityContext": { + "type": "object", + "$ref": "#/definitions/securityContext" } } }, diff --git a/operatorapi/tenants.go b/operatorapi/tenants.go index 659944656..5475b4b17 100644 --- a/operatorapi/tenants.go +++ b/operatorapi/tenants.go @@ -658,6 +658,7 @@ func parseTenantCertificates(ctx context.Context, clientSet K8sClientI, namespac func getTenantSecurity(ctx context.Context, clientSet K8sClientI, tenant *miniov2.Tenant) (response *models.TenantSecurityResponse, err error) { var minioExternalCertificates []*models.CertificateInfo var minioExternalCaCertificates []*models.CertificateInfo + var tenantSecurityContext *models.SecurityContext // Certificates used by MinIO server if minioExternalCertificates, err = parseTenantCertificates(ctx, clientSet, tenant.Namespace, tenant.Spec.ExternalCertSecret); err != nil { return nil, err @@ -666,12 +667,17 @@ func getTenantSecurity(ctx context.Context, clientSet K8sClientI, tenant *miniov if minioExternalCaCertificates, err = parseTenantCertificates(ctx, clientSet, tenant.Namespace, tenant.Spec.ExternalCaCertSecret); err != nil { return nil, err } + // Security Context used by MinIO server + if tenant.Spec.Pools[0].SecurityContext != nil { + tenantSecurityContext = convertK8sSCToModelSC(tenant.Spec.Pools[0].SecurityContext) + } return &models.TenantSecurityResponse{ AutoCert: tenant.AutoCert(), CustomCertificates: &models.TenantSecurityResponseCustomCertificates{ Minio: minioExternalCertificates, MinioCAs: minioExternalCaCertificates, }, + SecurityContext: tenantSecurityContext, }, nil } @@ -1026,6 +1032,12 @@ func updateTenantSecurity(ctx context.Context, operatorClient OperatorClientI, c } newMinIOExternalCaCertSecret = append(newMinIOExternalCaCertSecret, certificateSecrets...) } + + // set Security Context + var newTenantSecurityContext *corev1.PodSecurityContext + newTenantSecurityContext, _ = convertModelSCToK8sSC(params.Body.SecurityContext) + minInst.Spec.Pools[0].SecurityContext = newTenantSecurityContext + // Update External Certificates minInst.Spec.ExternalCertSecret = newMinIOExternalCertSecret minInst.Spec.ExternalCaCertSecret = newMinIOExternalCaCertSecret @@ -1033,6 +1045,7 @@ func updateTenantSecurity(ctx context.Context, operatorClient OperatorClientI, c if err != nil { return err } + // restart all MinIO pods at the same time err = client.deletePodCollection(ctx, namespace, metav1.DeleteOptions{}, metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s=%s", miniov2.TenantLabel, minInst.Name), diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx index 18bf70803..faa3b0c08 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx @@ -44,6 +44,13 @@ import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog"; import Loader from "../../Common/Loader/Loader"; import TLSCertificate from "../../Common/TLSCertificate/TLSCertificate"; import SectionTitle from "../../Common/SectionTitle"; +import SecurityContextSelector from "../securityContextSelector"; +import { + setRunAsUser, + setFSGroup, + setRunAsGroup, + setRunAsNonRoot, +} from "../tenantSecurityContextSlice"; interface ITenantSecurity { classes: any; @@ -100,6 +107,19 @@ const TenantSecurity = ({ classes }: ITenantSecurity) => { const [minioTLSCaCertificateSecrets, setMinioTLSCaCertificateSecrets] = useState([]); + const runAsGroup = useSelector( + (state: AppState) => state.editTenantSecurityContext.runAsGroup + ); + const runAsUser = useSelector( + (state: AppState) => state.editTenantSecurityContext.runAsUser + ); + const fsGroup = useSelector( + (state: AppState) => state.editTenantSecurityContext.fsGroup + ); + const runAsNonRoot = useSelector( + (state: AppState) => state.editTenantSecurityContext.runAsNonRoot + ); + const getTenantSecurityInfo = useCallback(() => { api .invoke( @@ -113,6 +133,10 @@ const TenantSecurity = ({ classes }: ITenantSecurity) => { } setMinioTLSCertificateSecrets(res.customCertificates.minio || []); setMinioTLSCaCertificateSecrets(res.customCertificates.minioCAs || []); + dispatch(setRunAsGroup(res.securityContext.runAsGroup)); + dispatch(setRunAsUser(res.securityContext.runAsUser)); + dispatch(setFSGroup(res.securityContext.fsGroup)); + dispatch(setRunAsNonRoot(res.securityContext.runAsNonRoot)); }) .catch((err: ErrorResponseHandler) => { dispatch(setErrorSnackMessage(err)); @@ -130,6 +154,12 @@ const TenantSecurity = ({ classes }: ITenantSecurity) => { let payload = { autoCert: enableAutoCert, customCertificates: {}, + securityContext: { + runAsGroup: runAsGroup, + runAsUser: runAsUser, + runAsNonRoot: runAsNonRoot, + fsGroup: fsGroup, + }, }; if (enableCustomCerts) { payload["customCertificates"] = { @@ -512,7 +542,21 @@ const TenantSecurity = ({ classes }: ITenantSecurity) => { )} - + + dispatch(setFSGroup(value))} + setRunAsUser={(value: string) => dispatch(setRunAsUser(value))} + setRunAsGroup={(value: string) => dispatch(setRunAsGroup(value))} + setRunAsNonRoot={(value: boolean) => + dispatch(setRunAsNonRoot(value)) + } + /> +