Tenant security context component (#2139)

Added edit Security Context component to Tenant Security screen, and updated API and backend to enable editing
This commit is contained in:
jinapurapu
2022-07-13 14:05:07 -07:00
committed by GitHub
parent 64b13e9dc9
commit dd2fffd3dc
10 changed files with 250 additions and 2 deletions

View File

@@ -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"`

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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"
}
}
},

View File

@@ -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),

View File

@@ -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<ICertificateInfo[]>([]);
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) => {
</Grid>
</Fragment>
)}
<Grid item xs={12} className={classes.formFieldRow}>
<SecurityContextSelector
classes={classes}
runAsGroup={runAsGroup}
runAsUser={runAsUser}
fsGroup={fsGroup}
runAsNonRoot={runAsNonRoot}
setFSGroup={(value: string) => dispatch(setFSGroup(value))}
setRunAsUser={(value: string) => dispatch(setRunAsUser(value))}
setRunAsGroup={(value: string) => dispatch(setRunAsGroup(value))}
setRunAsNonRoot={(value: boolean) =>
dispatch(setRunAsNonRoot(value))
}
/>
</Grid>
<Grid item xs={12} textAlign={"right"}>
<Button
type="submit"

View File

@@ -0,0 +1,57 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { IEditTenantSecurityContext } from "./types";
const initialState: IEditTenantSecurityContext = {
securityContextEnabled: false,
runAsUser: "1000",
runAsGroup: "1000",
fsGroup: "1000",
runAsNonRoot: true,
};
export const editTenantSecurityContextSlice = createSlice({
name: "editTenantSecurityContext",
initialState,
reducers: {
setSecurityContextEnabled: (state, action: PayloadAction<boolean>) => {
state.securityContextEnabled = action.payload;
},
setRunAsUser: (state, action: PayloadAction<string>) => {
state.runAsUser = action.payload;
},
setRunAsGroup: (state, action: PayloadAction<string>) => {
state.runAsGroup = action.payload;
},
setFSGroup: (state, action: PayloadAction<string>) => {
state.fsGroup = action.payload;
},
setRunAsNonRoot: (state, action: PayloadAction<boolean>) => {
state.runAsNonRoot = action.payload;
},
},
});
export const {
setSecurityContextEnabled,
setRunAsUser,
setRunAsGroup,
setFSGroup,
setRunAsNonRoot,
} = editTenantSecurityContextSlice.actions;
export default editTenantSecurityContextSlice.reducer;

View File

@@ -42,6 +42,7 @@ export interface ICustomCertificates {
export interface ITenantSecurityResponse {
autoCert: boolean;
customCertificates: ICustomCertificates;
securityContext: ISecurityContext;
}
export interface IVaultTLS {
@@ -346,3 +347,11 @@ export interface IEditMonitoringSecurityContext {
fsGroup: string;
runAsNonRoot: boolean;
}
export interface IEditTenantSecurityContext {
securityContextEnabled: boolean;
runAsUser: string;
runAsGroup: string;
fsGroup: string;
runAsNonRoot: boolean;
}

View File

@@ -34,6 +34,7 @@ import addPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/AddPoo
import editPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/EditPool/editPoolSlice";
import editTenantMonitoringReducer from "./screens/Console/Tenants/TenantDetails/tenantMonitoringSlice";
import editTenantAuditLoggingReducer from "./screens/Console/Tenants/TenantDetails/tenantAuditLogSlice";
import editTenantSecurityContextReducer from "./screens/Console/Tenants/tenantSecurityContextSlice";
const rootReducer = combineReducers({
system: systemReducer,
@@ -55,6 +56,7 @@ const rootReducer = combineReducers({
editPool: editPoolReducer,
editTenantMonitoring: editTenantMonitoringReducer,
editTenantLogging: editTenantAuditLoggingReducer,
editTenantSecurityContext: editTenantSecurityContextReducer,
});
export const store = configureStore({

View File

@@ -1491,6 +1491,9 @@ definitions:
type: array
items:
$ref: "#/definitions/certificateInfo"
securityContext:
type: object
$ref: "#/definitions/securityContext"
updateTenantSecurityRequest:
type: object
@@ -1512,7 +1515,10 @@ definitions:
type: array
items:
type: string
securityContext:
type: object
$ref: "#/definitions/securityContext"
certificateInfo:
type: object
properties: