diff --git a/operatorapi/tenants.go b/operatorapi/tenants.go
index f3734a48d..b12efbdc7 100644
--- a/operatorapi/tenants.go
+++ b/operatorapi/tenants.go
@@ -572,32 +572,44 @@ func parseTenantCertificates(ctx context.Context, clientSet K8sClientI, namespac
}
// Extract public key from certificate TLS secret
if rawCert, ok := keyPair.Data[publicKey]; ok {
- block, _ := pem.Decode(rawCert)
- if block == nil {
- // If certificate failed to decode skip
- continue
+ var blocks []byte
+ for {
+ var block *pem.Block
+ block, rawCert = pem.Decode(rawCert)
+ if block == nil {
+ break
+ }
+ if block.Type == "CERTIFICATE" {
+ blocks = append(blocks, block.Bytes...)
+ }
}
- cert, err := x509.ParseCertificate(block.Bytes)
+ // parse all certificates we found on this k8s secret
+ certs, err := x509.ParseCertificates(blocks)
if err != nil {
return nil, err
}
- domains := []string{}
- // append certificate domain names
- if len(cert.DNSNames) > 0 {
- domains = append(domains, cert.DNSNames...)
- }
- // append certificate IPs
- if len(cert.IPAddresses) > 0 {
- for _, ip := range cert.IPAddresses {
- domains = append(domains, ip.String())
+ for _, cert := range certs {
+ var domains []string
+ if cert.Subject.CommonName != "" {
+ domains = append(domains, cert.Subject.CommonName)
}
+ // append certificate domain names
+ if len(cert.DNSNames) > 0 {
+ domains = append(domains, cert.DNSNames...)
+ }
+ // append certificate IPs
+ if len(cert.IPAddresses) > 0 {
+ for _, ip := range cert.IPAddresses {
+ domains = append(domains, ip.String())
+ }
+ }
+ certificates = append(certificates, &models.CertificateInfo{
+ SerialNumber: cert.SerialNumber.String(),
+ Name: secret.Name,
+ Domains: domains,
+ Expiry: cert.NotAfter.Format(time.RFC3339),
+ })
}
- certificates = append(certificates, &models.CertificateInfo{
- SerialNumber: cert.SerialNumber.String(),
- Name: secret.Name,
- Domains: domains,
- Expiry: cert.NotAfter.Format(time.RFC3339),
- })
}
}
return certificates, nil
@@ -627,8 +639,6 @@ func getTenantSecurityResponse(session *models.Principal, params operator_api.Te
// 5 seconds timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
- //ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- //defer cancel()
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
if err != nil {
return nil, prepareError(err)
diff --git a/portal-ui/src/icons/CertificateIcon.tsx b/portal-ui/src/icons/CertificateIcon.tsx
new file mode 100644
index 000000000..d75d7e80d
--- /dev/null
+++ b/portal-ui/src/icons/CertificateIcon.tsx
@@ -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 .
+
+import * as React from "react";
+import { SVGProps } from "react";
+
+const CertificateIcon = (props: SVGProps) => {
+ return (
+
+ );
+};
+export default CertificateIcon;
diff --git a/portal-ui/src/screens/Console/Common/TLSCertificate/TLSCertificate.tsx b/portal-ui/src/screens/Console/Common/TLSCertificate/TLSCertificate.tsx
new file mode 100644
index 000000000..9a79b5604
--- /dev/null
+++ b/portal-ui/src/screens/Console/Common/TLSCertificate/TLSCertificate.tsx
@@ -0,0 +1,171 @@
+// 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 .
+
+import React from "react";
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import { ICertificateInfo } from "../../Tenants/types";
+import LanguageIcon from "@mui/icons-material/Language";
+import Chip from "@mui/material/Chip";
+import {
+ Typography,
+ Divider,
+ Box,
+ Grid,
+ Container,
+ ListItemText,
+ List,
+ ListItem,
+ ListItemAvatar,
+} from "@mui/material";
+import EventBusyIcon from "@mui/icons-material/EventBusy";
+import Moment from "react-moment";
+import CertificateIcon from "../../../../icons/CertificateIcon";
+
+const styles = (theme: Theme) =>
+ createStyles({
+ root: {
+ padding: 0,
+ margin: 0,
+ border: 0,
+ backgroundColor: "transparent",
+ textDecoration: "underline",
+ cursor: "pointer",
+ fontSize: "inherit",
+ color: theme.palette.info.main,
+ fontFamily: "Lato, sans-serif",
+ },
+ certificateIcon: {
+ float: "left",
+ paddingTop: "5px !important",
+ paddingRight: "10px !important",
+ },
+ certificateInfo: { float: "right" },
+ certificateWrapper: {
+ height: "auto",
+ margin: 5,
+ border: "1px solid #E2E2E2",
+ userSelect: "text",
+ borderRadius: 4,
+ "& h6": {
+ fontWeight: "bold",
+ },
+ "& div": {
+ padding: 0,
+ },
+ },
+ certificateExpiry: {
+ color: "#616161",
+ display: "flex",
+ alignItems: "center",
+ flexWrap: "wrap",
+ marginBottom: 5,
+ "& .label": {
+ fontWeight: "bold",
+ },
+ },
+ certificateDomains: {
+ color: "#616161",
+ "& .label": {
+ fontWeight: "bold",
+ },
+ },
+ certificatesList: {
+ border: "1px solid #E2E2E2",
+ borderRadius: 4,
+ color: "#616161",
+ textTransform: "lowercase",
+ overflowY: "scroll",
+ maxHeight: 145,
+ marginBottom: 10,
+ },
+ certificatesListItem: {
+ padding: "0px 16px",
+ borderBottom: "1px solid #E2E2E2",
+ "& div": {
+ minWidth: 0,
+ },
+ "& svg": {
+ fontSize: 12,
+ marginRight: 10,
+ opacity: 0.5,
+ },
+ "& span": {
+ fontSize: 12,
+ },
+ },
+ });
+
+interface ITLSCertificate {
+ classes: any;
+ certificateInfo: ICertificateInfo;
+ onDelete: any;
+}
+
+const TLSCertificate = ({
+ classes,
+ certificateInfo,
+ onDelete = () => {},
+}: ITLSCertificate) => {
+ const certificates = certificateInfo.domains || [];
+ return (
+
+
+
+
+
+
+ {certificateInfo.name}
+
+
+
+
+ Expiry:
+
+ {certificateInfo.expiry}
+
+
+
+
+
+ {`${certificates.length} Domain (s):`}
+
+
+ {certificates.map((dom) => (
+
+
+
+
+
+
+ ))}
+
+
+
+ }
+ onDelete={onDelete}
+ />
+ );
+};
+
+export default withStyles(styles)(TLSCertificate);
diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEncryption.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEncryption.tsx
index f7e40826b..a5f7382d7 100644
--- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEncryption.tsx
+++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEncryption.tsx
@@ -46,9 +46,7 @@ import Grid from "@mui/material/Grid";
import FileSelector from "../../Common/FormComponents/FileSelector/FileSelector";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
-import { Button, DialogContentText, Typography } from "@mui/material";
-import Chip from "@mui/material/Chip";
-import Moment from "react-moment";
+import { Button, DialogContentText } from "@mui/material";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye";
import { KeyPair } from "../ListTenants/utils";
@@ -58,6 +56,7 @@ import {
IValidation,
} from "../../../../utils/validationFunctions";
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
+import TLSCertificate from "../../Common/TLSCertificate/TLSCertificate";
interface ITenantEncryption {
classes: any;
@@ -913,47 +912,8 @@ const TenantEncryption = ({
Mutual TLS authentication with KMS (optional)
{vaultClientCertificateSecret ? (
-
-
- {vaultClientCertificateSecret.name}
-
-
- {vaultClientCertificateSecret.domains &&
- vaultClientCertificateSecret.domains.map(
- (dom) => {
- return {dom}
;
- }
- )}
-
-
- Expiry:
-
-
-
- {vaultClientCertificateSecret.expiry}
-
-
-
- }
+
removeCertificate(vaultClientCertificateSecret)
}
@@ -1002,47 +962,8 @@ const TenantEncryption = ({
KMS CA certificate (optional)
{vaultCACertificateSecret ? (
-
-
- {vaultCACertificateSecret.name}
-
-
- {vaultCACertificateSecret.domains &&
- vaultCACertificateSecret.domains.map(
- (dom) => {
- return {dom}
;
- }
- )}
-
-
- Expiry:
-
-
-
- {vaultCACertificateSecret.expiry}
-
-
-
- }
+
removeCertificate(vaultCACertificateSecret)
}
@@ -1617,47 +1538,8 @@ const TenantEncryption = ({
value={""}
/>
{gemaltoCACertificateSecret ? (
-
-
- {gemaltoCACertificateSecret.name}
-
-
- {gemaltoCACertificateSecret.domains &&
- gemaltoCACertificateSecret.domains.map(
- (dom) => {
- return {dom}
;
- }
- )}
-
-
- Expiry:
-
-
-
- {gemaltoCACertificateSecret.expiry}
-
-
-
- }
+
removeCertificate(gemaltoCACertificateSecret)
}
@@ -1709,47 +1591,8 @@ const TenantEncryption = ({
Encryption Service Certificates
{serverTLSCertificateSecret ? (
-
-
- {serverTLSCertificateSecret.name}
-
-
- {serverTLSCertificateSecret.domains &&
- serverTLSCertificateSecret.domains.map(
- (dom) => {
- return {dom}
;
- }
- )}
-
-
- Expiry:
-
-
-
- {serverTLSCertificateSecret.expiry}
-
-
-
- }
+
removeCertificate(serverTLSCertificateSecret)
}
@@ -1804,45 +1647,8 @@ const TenantEncryption = ({
Mutual TLS authentication with MinIO
{mTLSCertificateSecret ? (
-
-
- {mTLSCertificateSecret.name}
-
-
- {mTLSCertificateSecret.domains &&
- mTLSCertificateSecret.domains.map((dom) => {
- return {dom}
;
- })}
-
-
- Expiry:
-
-
-
- {mTLSCertificateSecret.expiry}
-
-
-
- }
+
removeCertificate(mTLSCertificateSecret)
}
diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx
index e653e9f22..624026a2e 100644
--- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx
+++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx
@@ -26,11 +26,9 @@ import {
} from "../../Common/FormComponents/common/styleLibrary";
import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid";
-import Chip from "@mui/material/Chip";
import React, { Fragment, useCallback, useEffect, useState } from "react";
-import Moment from "react-moment";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
-import { Button, DialogContentText, Typography } from "@mui/material";
+import { Button, DialogContentText } from "@mui/material";
import { KeyPair } from "../ListTenants/utils";
import FileSelector from "../../Common/FormComponents/FileSelector/FileSelector";
import api from "../../../../common/api";
@@ -42,6 +40,7 @@ import { setTenantDetailsLoad } from "../actions";
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
import { AddIcon, ConfirmModalIcon } from "../../../../icons";
import Loader from "../../Common/Loader/Loader";
+import TLSCertificate from "../../Common/TLSCertificate/TLSCertificate";
interface ITenantSecurity {
classes: any;
@@ -66,10 +65,6 @@ const styles = (theme: Theme) =>
paperContainer: {
padding: "15px 15px 15px 50px",
},
- certificateInfo: {
- height: "auto",
- margin: 5,
- },
fileItem: {
marginRight: 10,
display: "flex",
@@ -388,45 +383,8 @@ const TenantSecurity = ({
{minioTLSCertificateSecrets.map(
(certificateInfo: ICertificateInfo) => (
-
-
- {certificateInfo.name}
-
-
- {certificateInfo.domains &&
- certificateInfo.domains.map((dom) => {
- return {dom}
;
- })}
-
-
- Expiry:
-
-
-
- {certificateInfo.expiry}
-
-
-
- }
+ removeCertificate(certificateInfo)}
/>
)
@@ -508,47 +466,8 @@ const TenantSecurity = ({
{minioTLSCaCertificateSecrets.map(
(certificateInfo: ICertificateInfo) => (
-
-
- {certificateInfo.name}
-
-
- {certificateInfo.domains &&
- certificateInfo.domains.map((dom) => {
- return (
- {dom}
- );
- })}
-
-
- Expiry:
-
-
-
- {certificateInfo.expiry}
-
-
-
- }
+ removeCertificate(certificateInfo)}
/>
)