diff --git a/backend/src/ee/routes/v1/ssh-host-router.ts b/backend/src/ee/routes/v1/ssh-host-router.ts index 4193b80f2..9d4c33d8b 100644 --- a/backend/src/ee/routes/v1/ssh-host-router.ts +++ b/backend/src/ee/routes/v1/ssh-host-router.ts @@ -345,7 +345,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => { schema: { description: "Issue SSH certificate for host", params: z.object({ - sshHostId: z.string().describe(SSH_HOSTS.DELETE.sshHostId) + sshHostId: z.string().describe(SSH_HOSTS.ISSUE_HOST_CERT.sshHostId) }), body: z.object({ publicKey: z.string().describe(SSH_HOSTS.ISSUE_HOST_CERT.publicKey) @@ -430,7 +430,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => { schema: { description: "Get public key of the host SSH CA linked to the host", params: z.object({ - sshHostId: z.string().trim().describe(SSH_HOSTS.GET_USER_CA_PUBLIC_KEY.sshHostId) + sshHostId: z.string().trim().describe(SSH_HOSTS.GET_HOST_CA_PUBLIC_KEY.sshHostId) }), response: { 200: z.string() diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index 542349360..7d2847432 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -71,7 +71,6 @@ export enum ProjectPermissionSshHostActions { Create = "create", Edit = "edit", Delete = "delete", - IssueUserCert = "issue-user-cert", IssueHostCert = "issue-host-cert" } diff --git a/backend/src/ee/services/ssh-host/ssh-host-service.ts b/backend/src/ee/services/ssh-host/ssh-host-service.ts index 91d77cf5f..b582eb7db 100644 --- a/backend/src/ee/services/ssh-host/ssh-host-service.ts +++ b/backend/src/ee/services/ssh-host/ssh-host-service.ts @@ -214,14 +214,6 @@ export const sshHostServiceFactory = ({ tx ); - await sshHostLoginUserDAL.insertMany( - loginMappings.map(({ loginUser }) => ({ - sshHostId: host.id, - loginUser - })), - tx - ); - // (dangtony98): room to optimize for await (const { loginUser, allowedPrincipals } of loginMappings) { const sshHostLoginUser = await sshHostLoginUserDAL.create( @@ -524,7 +516,7 @@ export const sshHostServiceFactory = ({ await sshCertificateDAL.transaction(async (tx) => { const cert = await sshCertificateDAL.create( { - sshCaId: host.hostSshCaId, + sshCaId: host.userSshCaId, sshHostId: host.id, serialNumber, certType: SshCertType.USER, diff --git a/backend/src/ee/services/ssh/ssh-certificate-authority-fns.ts b/backend/src/ee/services/ssh/ssh-certificate-authority-fns.ts index 53eeaddec..ce5048c48 100644 --- a/backend/src/ee/services/ssh/ssh-certificate-authority-fns.ts +++ b/backend/src/ee/services/ssh/ssh-certificate-authority-fns.ts @@ -543,7 +543,7 @@ export const createSshCaHelper = async ({ // use external SSH CA key pair if (!externalPk || !externalSk) { throw new BadRequestError({ - message: "Public and private keys are required if generateSigningKey is false" + message: "Public and private keys are required when key source is external" }); } publicKey = externalPk; diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index fc8ae151f..a39e9b7e7 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -1375,6 +1375,7 @@ export const SSH_HOSTS = { publicKey: "The public key of the issued SSH certificate." }, ISSUE_HOST_CERT: { + sshHostId: "The ID of the SSH host to issue the SSH certificate for.", publicKey: "The SSH public key to issue the SSH certificate for.", serialNumber: "The serial number of the issued SSH certificate.", signedKey: "The SSH certificate or signed SSH public key." diff --git a/cli/packages/cmd/ssh.go b/cli/packages/cmd/ssh.go index f74fa2071..274b9bd7c 100644 --- a/cli/packages/cmd/ssh.go +++ b/cli/packages/cmd/ssh.go @@ -827,7 +827,7 @@ func sshAddHost(cmd *cobra.Command, args []string) { util.PrintErrorMessageAndExit("No supported SSH host public key found at /etc/ssh") } - if _, err := os.Stat(certOutPath); err == nil && !forceOverwrite && writeHostCertToFile { + if _, err := os.Stat(certOutPath); err == nil && !forceOverwrite { util.PrintErrorMessageAndExit("File already exists at " + certOutPath + ". Use --force to overwrite.") } } diff --git a/docs/documentation/platform/ssh-old.mdx b/docs/documentation/platform/ssh-old.mdx index 14c6d93e9..260f9d7c1 100644 --- a/docs/documentation/platform/ssh-old.mdx +++ b/docs/documentation/platform/ssh-old.mdx @@ -282,13 +282,13 @@ If the remote host does not have an existing SSH key pair, you can generate a ne 2.2. Create a file containing the certificate in the SSH folder of the remote host; we'll call it `ssh_host_key-cert.pub`. - 2.2. Set permissions on the certificate to be `0640`: + 2.3. Set permissions on the certificate to be `0640`: ```bash sudo chmod 0640 /etc/ssh/ssh_host_key-cert.pub ``` - 2.3. Next, add the following lines to the `/etc/ssh/sshd_config` file on the remote host. + 2.4. Next, add the following lines to the `/etc/ssh/sshd_config` file on the remote host. ```bash HostKey /etc/ssh/ssh_host_rsa_key @@ -299,7 +299,7 @@ If the remote host does not have an existing SSH key pair, you can generate a ne You should adjust the `HostKey` directive to match the path to the host's SSH private key as used in step 1. - 2.4. Finally, reload the SSH daemon on the remote host to apply the changes. + 2.5. Finally, reload the SSH daemon on the remote host to apply the changes. ```bash sudo systemctl reload sshd diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx index a53e12ff1..8017ab1cb 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx @@ -255,7 +255,6 @@ type TConditionalFields = | ProjectPermissionSub.SecretFolders | ProjectPermissionSub.SecretImports | ProjectPermissionSub.DynamicSecrets - | ProjectPermissionSub.Identity | ProjectPermissionSub.SshHosts | ProjectPermissionSub.SecretRotation | ProjectPermissionSub.Identity; diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/SshPermissionConditions.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/SshPermissionConditions.tsx deleted file mode 100644 index 46a92c73b..000000000 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/SshPermissionConditions.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { Controller, useFieldArray, useFormContext } from "react-hook-form"; -import { faInfoCircle, faPlus, faTrash, faWarning } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -import { - Button, - FormControl, - IconButton, - Input, - Select, - SelectItem, - Tooltip -} from "@app/components/v2"; -import { PermissionConditionOperators } from "@app/context/ProjectPermissionContext/types"; - -import { - getConditionOperatorHelperInfo, - renderOperatorSelectItems -} from "./PermissionConditionHelpers"; -import { TFormSchema } from "./ProjectRoleModifySection.utils"; - -type Props = { - position?: number; - isDisabled?: boolean; -}; - -export const SshPermissionConditions = ({ position = 0, isDisabled }: Props) => { - const { - control, - watch, - setValue, - formState: { errors } - } = useFormContext(); - const items = useFieldArray({ - control, - name: `permissions.secrets.${position}.conditions` - }); - - const conditionErrorMessage = - errors?.permissions?.secrets?.[position]?.conditions?.message || - errors?.permissions?.secrets?.[position]?.conditions?.root?.message; - - return ( -
-

Conditions

-

- Conditions determine when a policy will be applied (always if no conditions are present). -

-

- All conditions must evaluate to true for the policy to take effect. -

-
- {items.fields.map((el, index) => { - const condition = watch(`permissions.secrets.${position}.conditions.${index}`) as { - lhs: string; - rhs: string; - operator: string; - }; - return ( -
-
- ( - - - - )} - /> -
-
- ( - - - - )} - /> -
- - - -
-
-
- ( - - - - )} - /> -
-
- items.remove(index)} - > - - -
-
- ); - })} -
- {conditionErrorMessage && ( -
- - {conditionErrorMessage} -
- )} -
- -
-
- ); -}; diff --git a/frontend/src/pages/ssh/SshCasPage/SshCasPage.tsx b/frontend/src/pages/ssh/SshCasPage/SshCasPage.tsx index 80549cbec..669e26404 100644 --- a/frontend/src/pages/ssh/SshCasPage/SshCasPage.tsx +++ b/frontend/src/pages/ssh/SshCasPage/SshCasPage.tsx @@ -10,7 +10,7 @@ export const SshCasPage = () => { return ( <> - {t("common.head-title", { title: "Certificates" })} + {t("common.head-title", { title: "SSH" })}
diff --git a/frontend/src/pages/ssh/SshCasPage/components/SshCaModal.tsx b/frontend/src/pages/ssh/SshCasPage/components/SshCaModal.tsx index 184ca66d4..1d9fdb8cf 100644 --- a/frontend/src/pages/ssh/SshCasPage/components/SshCaModal.tsx +++ b/frontend/src/pages/ssh/SshCasPage/components/SshCaModal.tsx @@ -144,7 +144,7 @@ export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => { } catch (err) { console.error(err); createNotification({ - text: "Failed to create SSH CA", + text: `Failed to ${ca ? "update" : "create"} SSH CA`, type: "error" }); } diff --git a/frontend/src/pages/ssh/SshCertsPage/components/SshCertificatesSection.tsx b/frontend/src/pages/ssh/SshCertsPage/components/SshCertificatesSection.tsx index 88bb04ae9..02e38b2ee 100644 --- a/frontend/src/pages/ssh/SshCertsPage/components/SshCertificatesSection.tsx +++ b/frontend/src/pages/ssh/SshCertsPage/components/SshCertificatesSection.tsx @@ -1,15 +1,21 @@ +import { faPlus } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { ProjectPermissionCan } from "@app/components/permissions"; +import { Button } from "@app/components/v2"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; import { usePopUp } from "@app/hooks/usePopUp"; import { SshCertificateModal } from "../../SshCaByIDPage/components/SshCertificateModal"; import { SshCertificatesTable } from "./SshCertificatesTable"; export const SshCertificatesSection = () => { - const { popUp, handlePopUpToggle } = usePopUp(["sshCertificate"] as const); + const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp(["sshCertificate"] as const); return (

Certificates

- {/* @@ -24,7 +30,7 @@ export const SshCertificatesSection = () => { Request )} - */} +
diff --git a/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx b/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx index 40f470c7b..6bc9fdae4 100644 --- a/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx +++ b/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx @@ -152,7 +152,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => { } catch (err) { console.error(err); createNotification({ - text: "Failed to add SSH host", + text: `Failed to ${sshHost ? "update" : "add"} SSH host`, type: "error" }); } @@ -206,7 +206,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => { errorText={error?.message} isRequired > - + )} /> @@ -328,7 +328,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => { {(value.length === 0 ? [""] : value).map( (principal: string, principalIndex: number) => (
diff --git a/frontend/src/pages/ssh/SshHostsPage/components/SshHostsSection.tsx b/frontend/src/pages/ssh/SshHostsPage/components/SshHostsSection.tsx index c15cf5d04..a3f9917ba 100644 --- a/frontend/src/pages/ssh/SshHostsPage/components/SshHostsSection.tsx +++ b/frontend/src/pages/ssh/SshHostsPage/components/SshHostsSection.tsx @@ -1,4 +1,4 @@ -import { faPlus } from "@fortawesome/free-solid-svg-icons"; +import { faArrowUpRightFromSquare, faPlus } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { createNotification } from "@app/components/notifications"; @@ -42,27 +42,44 @@ export const SshHostsSection = () => {

Hosts

- - {(isAllowed) => - isAllowed && ( +
+ + + Documentation{" "} + + + + + {(isAllowed) => ( - ) - } - + )} + +
handlePopUpToggle("deleteSshHost", isOpen)} deleteKey="confirm" onDeleteApproved={() =>