mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 16:27:46 +00:00
Add alias field to ssh hosts for improved ux
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasAliasColumn = await knex.schema.hasColumn(TableName.SshHost, "alias");
|
||||||
|
if (!hasAliasColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.SshHost, (t) => {
|
||||||
|
t.string("alias").nullable();
|
||||||
|
t.unique(["projectId", "alias"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasAliasColumn = await knex.schema.hasColumn(TableName.SshHost, "alias");
|
||||||
|
if (hasAliasColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.SshHost, (t) => {
|
||||||
|
t.dropUnique(["projectId", "alias"]);
|
||||||
|
t.dropColumn("alias");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,8 @@ export const SshHostsSchema = z.object({
|
|||||||
userCertTtl: z.string(),
|
userCertTtl: z.string(),
|
||||||
hostCertTtl: z.string(),
|
hostCertTtl: z.string(),
|
||||||
userSshCaId: z.string().uuid(),
|
userSshCaId: z.string().uuid(),
|
||||||
hostSshCaId: z.string().uuid()
|
hostSshCaId: z.string().uuid(),
|
||||||
|
alias: z.string().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSshHosts = z.infer<typeof SshHostsSchema>;
|
export type TSshHosts = z.infer<typeof SshHostsSchema>;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import slugify from "@sindresorhus/slugify";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
@@ -96,10 +97,20 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
|||||||
hostname: z
|
hostname: z
|
||||||
.string()
|
.string()
|
||||||
.min(1)
|
.min(1)
|
||||||
|
.trim()
|
||||||
.refine((v) => isValidHostname(v), {
|
.refine((v) => isValidHostname(v), {
|
||||||
message: "Hostname must be a valid hostname"
|
message: "Hostname must be a valid hostname"
|
||||||
})
|
})
|
||||||
.describe(SSH_HOSTS.CREATE.hostname),
|
.describe(SSH_HOSTS.CREATE.hostname),
|
||||||
|
alias: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.nullable()
|
||||||
|
.default(null)
|
||||||
|
.refine((v) => v == null || slugify(v) === v, {
|
||||||
|
message: "Alias must be a valid slug"
|
||||||
|
})
|
||||||
|
.describe(SSH_HOSTS.CREATE.alias),
|
||||||
userCertTtl: z
|
userCertTtl: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||||
@@ -138,6 +149,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
|||||||
metadata: {
|
metadata: {
|
||||||
sshHostId: host.id,
|
sshHostId: host.id,
|
||||||
hostname: host.hostname,
|
hostname: host.hostname,
|
||||||
|
alias: host.alias ?? null,
|
||||||
userCertTtl: host.userCertTtl,
|
userCertTtl: host.userCertTtl,
|
||||||
hostCertTtl: host.hostCertTtl,
|
hostCertTtl: host.hostCertTtl,
|
||||||
loginMappings: host.loginMappings,
|
loginMappings: host.loginMappings,
|
||||||
@@ -166,12 +178,22 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
|||||||
body: z.object({
|
body: z.object({
|
||||||
hostname: z
|
hostname: z
|
||||||
.string()
|
.string()
|
||||||
|
.trim()
|
||||||
.min(1)
|
.min(1)
|
||||||
.refine((v) => isValidHostname(v), {
|
.refine((v) => isValidHostname(v), {
|
||||||
message: "Hostname must be a valid hostname"
|
message: "Hostname must be a valid hostname"
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.describe(SSH_HOSTS.UPDATE.hostname),
|
.describe(SSH_HOSTS.UPDATE.hostname),
|
||||||
|
alias: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.nullable()
|
||||||
|
.refine((v) => v == null || slugify(v) === v, {
|
||||||
|
message: "Alias must be a valid slug"
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.describe(SSH_HOSTS.CREATE.alias),
|
||||||
userCertTtl: z
|
userCertTtl: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||||
@@ -208,6 +230,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
|||||||
metadata: {
|
metadata: {
|
||||||
sshHostId: host.id,
|
sshHostId: host.id,
|
||||||
hostname: host.hostname,
|
hostname: host.hostname,
|
||||||
|
alias: host.alias,
|
||||||
userCertTtl: host.userCertTtl,
|
userCertTtl: host.userCertTtl,
|
||||||
hostCertTtl: host.hostCertTtl,
|
hostCertTtl: host.hostCertTtl,
|
||||||
loginMappings: host.loginMappings,
|
loginMappings: host.loginMappings,
|
||||||
|
|||||||
@@ -1494,6 +1494,7 @@ interface CreateSshHost {
|
|||||||
metadata: {
|
metadata: {
|
||||||
sshHostId: string;
|
sshHostId: string;
|
||||||
hostname: string;
|
hostname: string;
|
||||||
|
alias: string | null;
|
||||||
userCertTtl: string;
|
userCertTtl: string;
|
||||||
hostCertTtl: string;
|
hostCertTtl: string;
|
||||||
loginMappings: {
|
loginMappings: {
|
||||||
@@ -1512,6 +1513,7 @@ interface UpdateSshHost {
|
|||||||
metadata: {
|
metadata: {
|
||||||
sshHostId: string;
|
sshHostId: string;
|
||||||
hostname?: string;
|
hostname?: string;
|
||||||
|
alias?: string | null;
|
||||||
userCertTtl?: string;
|
userCertTtl?: string;
|
||||||
hostCertTtl?: string;
|
hostCertTtl?: string;
|
||||||
loginMappings?: {
|
loginMappings?: {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
||||||
db.ref("projectId").withSchema(TableName.SshHost),
|
db.ref("projectId").withSchema(TableName.SshHost),
|
||||||
db.ref("hostname").withSchema(TableName.SshHost),
|
db.ref("hostname").withSchema(TableName.SshHost),
|
||||||
|
db.ref("alias").withSchema(TableName.SshHost),
|
||||||
db.ref("userCertTtl").withSchema(TableName.SshHost),
|
db.ref("userCertTtl").withSchema(TableName.SshHost),
|
||||||
db.ref("hostCertTtl").withSchema(TableName.SshHost),
|
db.ref("hostCertTtl").withSchema(TableName.SshHost),
|
||||||
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
||||||
@@ -45,7 +46,8 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const grouped = groupBy(rows, (r) => r.sshHostId);
|
const grouped = groupBy(rows, (r) => r.sshHostId);
|
||||||
return Object.values(grouped).map((hostRows) => {
|
return Object.values(grouped).map((hostRows) => {
|
||||||
const { sshHostId, hostname, userCertTtl, hostCertTtl, userSshCaId, hostSshCaId, projectId } = hostRows[0];
|
const { sshHostId, hostname, alias, userCertTtl, hostCertTtl, userSshCaId, hostSshCaId, projectId } =
|
||||||
|
hostRows[0];
|
||||||
|
|
||||||
const loginMappingGrouped = groupBy(hostRows, (r) => r.loginUser);
|
const loginMappingGrouped = groupBy(hostRows, (r) => r.loginUser);
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
return {
|
return {
|
||||||
id: sshHostId,
|
id: sshHostId,
|
||||||
hostname,
|
hostname,
|
||||||
|
alias,
|
||||||
projectId,
|
projectId,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
hostCertTtl,
|
hostCertTtl,
|
||||||
@@ -87,6 +90,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
||||||
db.ref("projectId").withSchema(TableName.SshHost),
|
db.ref("projectId").withSchema(TableName.SshHost),
|
||||||
db.ref("hostname").withSchema(TableName.SshHost),
|
db.ref("hostname").withSchema(TableName.SshHost),
|
||||||
|
db.ref("alias").withSchema(TableName.SshHost),
|
||||||
db.ref("userCertTtl").withSchema(TableName.SshHost),
|
db.ref("userCertTtl").withSchema(TableName.SshHost),
|
||||||
db.ref("hostCertTtl").withSchema(TableName.SshHost),
|
db.ref("hostCertTtl").withSchema(TableName.SshHost),
|
||||||
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
||||||
@@ -99,7 +103,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const hostsGrouped = groupBy(rows, (r) => r.sshHostId);
|
const hostsGrouped = groupBy(rows, (r) => r.sshHostId);
|
||||||
return Object.values(hostsGrouped).map((hostRows) => {
|
return Object.values(hostsGrouped).map((hostRows) => {
|
||||||
const { sshHostId, hostname, userCertTtl, hostCertTtl, userSshCaId, hostSshCaId } = hostRows[0];
|
const { sshHostId, hostname, alias, userCertTtl, hostCertTtl, userSshCaId, hostSshCaId } = hostRows[0];
|
||||||
|
|
||||||
const loginMappingGrouped = groupBy(
|
const loginMappingGrouped = groupBy(
|
||||||
hostRows.filter((r) => r.loginUser),
|
hostRows.filter((r) => r.loginUser),
|
||||||
@@ -116,6 +120,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
return {
|
return {
|
||||||
id: sshHostId,
|
id: sshHostId,
|
||||||
hostname,
|
hostname,
|
||||||
|
alias,
|
||||||
projectId,
|
projectId,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
hostCertTtl,
|
hostCertTtl,
|
||||||
@@ -144,6 +149,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
||||||
db.ref("projectId").withSchema(TableName.SshHost),
|
db.ref("projectId").withSchema(TableName.SshHost),
|
||||||
db.ref("hostname").withSchema(TableName.SshHost),
|
db.ref("hostname").withSchema(TableName.SshHost),
|
||||||
|
db.ref("alias").withSchema(TableName.SshHost),
|
||||||
db.ref("userCertTtl").withSchema(TableName.SshHost),
|
db.ref("userCertTtl").withSchema(TableName.SshHost),
|
||||||
db.ref("hostCertTtl").withSchema(TableName.SshHost),
|
db.ref("hostCertTtl").withSchema(TableName.SshHost),
|
||||||
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
||||||
@@ -155,7 +161,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
if (rows.length === 0) return null;
|
if (rows.length === 0) return null;
|
||||||
|
|
||||||
const { sshHostId: id, projectId, hostname, userCertTtl, hostCertTtl, userSshCaId, hostSshCaId } = rows[0];
|
const { sshHostId: id, projectId, hostname, alias, userCertTtl, hostCertTtl, userSshCaId, hostSshCaId } = rows[0];
|
||||||
|
|
||||||
const loginMappingGrouped = groupBy(
|
const loginMappingGrouped = groupBy(
|
||||||
rows.filter((r) => r.loginUser),
|
rows.filter((r) => r.loginUser),
|
||||||
@@ -173,6 +179,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
id,
|
id,
|
||||||
projectId,
|
projectId,
|
||||||
hostname,
|
hostname,
|
||||||
|
alias,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
hostCertTtl,
|
hostCertTtl,
|
||||||
loginMappings,
|
loginMappings,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const sanitizedSshHost = SshHostsSchema.pick({
|
|||||||
id: true,
|
id: true,
|
||||||
projectId: true,
|
projectId: true,
|
||||||
hostname: true,
|
hostname: true,
|
||||||
|
alias: true,
|
||||||
userCertTtl: true,
|
userCertTtl: true,
|
||||||
hostCertTtl: true,
|
hostCertTtl: true,
|
||||||
userSshCaId: true,
|
userSshCaId: true,
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ export const sshHostServiceFactory = ({
|
|||||||
const createSshHost = async ({
|
const createSshHost = async ({
|
||||||
projectId,
|
projectId,
|
||||||
hostname,
|
hostname,
|
||||||
|
alias,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
hostCertTtl,
|
hostCertTtl,
|
||||||
loginMappings,
|
loginMappings,
|
||||||
@@ -192,6 +193,7 @@ export const sshHostServiceFactory = ({
|
|||||||
{
|
{
|
||||||
projectId,
|
projectId,
|
||||||
hostname,
|
hostname,
|
||||||
|
alias,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
hostCertTtl,
|
hostCertTtl,
|
||||||
userSshCaId,
|
userSshCaId,
|
||||||
@@ -265,6 +267,7 @@ export const sshHostServiceFactory = ({
|
|||||||
const updateSshHost = async ({
|
const updateSshHost = async ({
|
||||||
sshHostId,
|
sshHostId,
|
||||||
hostname,
|
hostname,
|
||||||
|
alias,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
hostCertTtl,
|
hostCertTtl,
|
||||||
loginMappings,
|
loginMappings,
|
||||||
@@ -297,6 +300,7 @@ export const sshHostServiceFactory = ({
|
|||||||
sshHostId,
|
sshHostId,
|
||||||
{
|
{
|
||||||
hostname,
|
hostname,
|
||||||
|
alias,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
hostCertTtl
|
hostCertTtl
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export type TListSshHostsDTO = Omit<TProjectPermission, "projectId">;
|
|||||||
|
|
||||||
export type TCreateSshHostDTO = {
|
export type TCreateSshHostDTO = {
|
||||||
hostname: string;
|
hostname: string;
|
||||||
|
alias: string | null;
|
||||||
userCertTtl: string;
|
userCertTtl: string;
|
||||||
hostCertTtl: string;
|
hostCertTtl: string;
|
||||||
loginMappings: {
|
loginMappings: {
|
||||||
@@ -19,6 +20,7 @@ export type TCreateSshHostDTO = {
|
|||||||
export type TUpdateSshHostDTO = {
|
export type TUpdateSshHostDTO = {
|
||||||
sshHostId: string;
|
sshHostId: string;
|
||||||
hostname?: string;
|
hostname?: string;
|
||||||
|
alias?: string | null;
|
||||||
userCertTtl?: string;
|
userCertTtl?: string;
|
||||||
hostCertTtl?: string;
|
hostCertTtl?: string;
|
||||||
loginMappings?: {
|
loginMappings?: {
|
||||||
|
|||||||
@@ -1387,6 +1387,7 @@ export const SSH_HOSTS = {
|
|||||||
CREATE: {
|
CREATE: {
|
||||||
projectId: "The ID of the project to create the SSH host in.",
|
projectId: "The ID of the project to create the SSH host in.",
|
||||||
hostname: "The hostname of the SSH host.",
|
hostname: "The hostname of the SSH host.",
|
||||||
|
alias: "The alias for the SSH host.",
|
||||||
userCertTtl: "The time to live for user certificates issued under this host.",
|
userCertTtl: "The time to live for user certificates issued under this host.",
|
||||||
hostCertTtl: "The time to live for host certificates issued under this host.",
|
hostCertTtl: "The time to live for host certificates issued under this host.",
|
||||||
loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')",
|
loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')",
|
||||||
@@ -1401,6 +1402,7 @@ export const SSH_HOSTS = {
|
|||||||
UPDATE: {
|
UPDATE: {
|
||||||
sshHostId: "The ID of the SSH host to update.",
|
sshHostId: "The ID of the SSH host to update.",
|
||||||
hostname: "The hostname of the SSH host to update to.",
|
hostname: "The hostname of the SSH host to update to.",
|
||||||
|
alias: "The alias for the SSH host to update to.",
|
||||||
userCertTtl: "The time to live for user certificates issued under this host to update to.",
|
userCertTtl: "The time to live for user certificates issued under this host to update to.",
|
||||||
hostCertTtl: "The time to live for host certificates issued under this host to update to.",
|
hostCertTtl: "The time to live for host certificates issued under this host to update to.",
|
||||||
loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')",
|
loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')",
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ require (
|
|||||||
github.com/fatih/semgroup v1.2.0
|
github.com/fatih/semgroup v1.2.0
|
||||||
github.com/gitleaks/go-gitdiff v0.8.0
|
github.com/gitleaks/go-gitdiff v0.8.0
|
||||||
github.com/h2non/filetype v1.1.3
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/infisical/go-sdk v0.5.8
|
github.com/infisical/go-sdk v0.5.92
|
||||||
github.com/infisical/infisical-kmip v0.3.5
|
github.com/infisical/infisical-kmip v0.3.5
|
||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a
|
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a
|
||||||
|
|||||||
+2
-2
@@ -277,8 +277,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
|||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/infisical/go-sdk v0.5.8 h1:bCetYLp7HWt8DnU9KPh1n8n3z5pjmunkGDB4bA3lEFs=
|
github.com/infisical/go-sdk v0.5.92 h1:PoCnVndrd6Dbkipuxl9fFiwlD5vCKsabtQo09mo8lUE=
|
||||||
github.com/infisical/go-sdk v0.5.8/go.mod h1:ExjqFLRz7LSpZpGluqDLvFl6dFBLq5LKyLW7GBaMAIs=
|
github.com/infisical/go-sdk v0.5.92/go.mod h1:ExjqFLRz7LSpZpGluqDLvFl6dFBLq5LKyLW7GBaMAIs=
|
||||||
github.com/infisical/infisical-kmip v0.3.5 h1:QM3s0e18B+mYv3a9HQNjNAlbwZJBzXq5BAJM2scIeiE=
|
github.com/infisical/infisical-kmip v0.3.5 h1:QM3s0e18B+mYv3a9HQNjNAlbwZJBzXq5BAJM2scIeiE=
|
||||||
github.com/infisical/infisical-kmip v0.3.5/go.mod h1:bO1M4YtKyutNg1bREPmlyZspC5duSR7hyQ3lPmLzrIs=
|
github.com/infisical/infisical-kmip v0.3.5/go.mod h1:bO1M4YtKyutNg1bREPmlyZspC5duSR7hyQ3lPmLzrIs=
|
||||||
github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo=
|
github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo=
|
||||||
|
|||||||
+35
-17
@@ -631,18 +631,18 @@ func sshConnect(cmd *cobra.Command, args []string) {
|
|||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
|
|
||||||
writeHostCaToFile, err := cmd.Flags().GetBool("writeHostCaToFile")
|
writeHostCaToFile, err := cmd.Flags().GetBool("write-host-ca-to-file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Unable to parse --writeHostCaToFile flag")
|
util.HandleError(err, "Unable to parse --write-host-ca-to-file flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
outFilePath, err := cmd.Flags().GetString("outFilePath")
|
outFilePath, err := cmd.Flags().GetString("out-file-path")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Unable to parse flag")
|
util.HandleError(err, "Unable to parse flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
hostname, _ := cmd.Flags().GetString("hostname")
|
hostname, _ := cmd.Flags().GetString("hostname")
|
||||||
loginUser, _ := cmd.Flags().GetString("loginUser")
|
loginUser, _ := cmd.Flags().GetString("login-user")
|
||||||
|
|
||||||
var outputDir, privateKeyPath, publicKeyPath, signedKeyPath string
|
var outputDir, privateKeyPath, publicKeyPath, signedKeyPath string
|
||||||
if outFilePath != "" {
|
if outFilePath != "" {
|
||||||
@@ -722,17 +722,24 @@ func sshConnect(cmd *cobra.Command, args []string) {
|
|||||||
} else {
|
} else {
|
||||||
hostNames := make([]string, len(hosts))
|
hostNames := make([]string, len(hosts))
|
||||||
for i, h := range hosts {
|
for i, h := range hosts {
|
||||||
hostNames[i] = h.Hostname
|
if h.Alias != "" {
|
||||||
|
hostNames[i] = h.Alias
|
||||||
|
} else {
|
||||||
|
hostNames[i] = h.Hostname
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostPrompt := promptui.Select{
|
hostPrompt := promptui.Select{
|
||||||
Label: "Select an SSH Host",
|
Label: "Select an SSH Host",
|
||||||
Items: hostNames,
|
Items: hostNames,
|
||||||
Size: 10,
|
Size: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
hostIdx, _, err := hostPrompt.Run()
|
hostIdx, _, err := hostPrompt.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Prompt failed")
|
util.HandleError(err, "Prompt failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedHost = hosts[hostIdx]
|
selectedHost = hosts[hostIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,24 +900,33 @@ func sshAddHost(cmd *cobra.Command, args []string) {
|
|||||||
util.PrintErrorMessageAndExit("You must provide --hostname")
|
util.PrintErrorMessageAndExit("You must provide --hostname")
|
||||||
}
|
}
|
||||||
|
|
||||||
writeUserCaToFile, err := cmd.Flags().GetBool("writeUserCaToFile")
|
alias, err := cmd.Flags().GetString("alias")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Unable to parse --writeUserCaToFile flag")
|
util.HandleError(err, "Unable to parse --alias flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if alias == "" {
|
||||||
|
// util.PrintErrorMessageAndExit("You must provide --alias")
|
||||||
|
// }
|
||||||
|
|
||||||
|
writeUserCaToFile, err := cmd.Flags().GetBool("write-user-ca-to-file")
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err, "Unable to parse --write-user-ca-to-file flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
userCaOutFilePath, err := cmd.Flags().GetString("userCaOutFilePath")
|
userCaOutFilePath, err := cmd.Flags().GetString("user-ca-out-file-path")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Unable to parse --userCaOutFilePath flag")
|
util.HandleError(err, "Unable to parse --user-ca-out-file-path flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
writeHostCertToFile, err := cmd.Flags().GetBool("writeHostCertToFile")
|
writeHostCertToFile, err := cmd.Flags().GetBool("write-host-cert-to-file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Unable to parse --writeHostCertToFile flag")
|
util.HandleError(err, "Unable to parse --write-host-cert-to-file flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
configureSshd, err := cmd.Flags().GetBool("configureSshd")
|
configureSshd, err := cmd.Flags().GetBool("configure-sshd")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Unable to parse --configureSshd flag")
|
util.HandleError(err, "Unable to parse --configure-sshd flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
forceOverwrite, err := cmd.Flags().GetBool("force")
|
forceOverwrite, err := cmd.Flags().GetBool("force")
|
||||||
@@ -919,7 +935,7 @@ func sshAddHost(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if configureSshd && (!writeUserCaToFile || !writeHostCertToFile) {
|
if configureSshd && (!writeUserCaToFile || !writeHostCertToFile) {
|
||||||
util.PrintErrorMessageAndExit("--configureSshd requires both --writeUserCaToFile and --writeHostCertToFile to also be set")
|
util.PrintErrorMessageAndExit("--configure-sshd requires both --write-user-ca-to-file and --write-host-cert-to-file to also be set")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-check for file overwrites before proceeding
|
// Pre-check for file overwrites before proceeding
|
||||||
@@ -927,7 +943,7 @@ func sshAddHost(cmd *cobra.Command, args []string) {
|
|||||||
if strings.HasPrefix(userCaOutFilePath, "~") {
|
if strings.HasPrefix(userCaOutFilePath, "~") {
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Unable to resolve ~ in userCaOutFilePath")
|
util.HandleError(err, "Unable to resolve ~ in user-ca-out-file-path")
|
||||||
}
|
}
|
||||||
userCaOutFilePath = strings.Replace(userCaOutFilePath, "~", homeDir, 1)
|
userCaOutFilePath = strings.Replace(userCaOutFilePath, "~", homeDir, 1)
|
||||||
}
|
}
|
||||||
@@ -998,6 +1014,7 @@ func sshAddHost(cmd *cobra.Command, args []string) {
|
|||||||
host, err := client.Ssh().AddSshHost(infisicalSdk.AddSshHostOptions{
|
host, err := client.Ssh().AddSshHost(infisicalSdk.AddSshHostOptions{
|
||||||
ProjectID: projectId,
|
ProjectID: projectId,
|
||||||
Hostname: hostname,
|
Hostname: hostname,
|
||||||
|
Alias: alias,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Failed to register SSH host")
|
util.HandleError(err, "Failed to register SSH host")
|
||||||
@@ -1112,11 +1129,12 @@ func init() {
|
|||||||
sshAddHostCmd.Flags().String("token", "", "Use a machine identity access token")
|
sshAddHostCmd.Flags().String("token", "", "Use a machine identity access token")
|
||||||
sshAddHostCmd.Flags().String("projectId", "", "Project ID the host belongs to (required)")
|
sshAddHostCmd.Flags().String("projectId", "", "Project ID the host belongs to (required)")
|
||||||
sshAddHostCmd.Flags().String("hostname", "", "Hostname of the SSH host (required)")
|
sshAddHostCmd.Flags().String("hostname", "", "Hostname of the SSH host (required)")
|
||||||
|
sshAddHostCmd.Flags().String("alias", "", "Alias for the SSH host")
|
||||||
sshAddHostCmd.Flags().Bool("write-user-ca-to-file", false, "Write User CA public key to /etc/ssh/infisical_user_ca.pub")
|
sshAddHostCmd.Flags().Bool("write-user-ca-to-file", false, "Write User CA public key to /etc/ssh/infisical_user_ca.pub")
|
||||||
sshAddHostCmd.Flags().String("user-ca-out-file-path", "/etc/ssh/infisical_user_ca.pub", "Custom file path to write the User CA public key")
|
sshAddHostCmd.Flags().String("user-ca-out-file-path", "/etc/ssh/infisical_user_ca.pub", "Custom file path to write the User CA public key")
|
||||||
sshAddHostCmd.Flags().Bool("write-host-cert-to-file", false, "Write SSH host certificate to /etc/ssh/ssh_host_<type>_key-cert.pub")
|
sshAddHostCmd.Flags().Bool("write-host-cert-to-file", false, "Write SSH host certificate to /etc/ssh/ssh_host_<type>_key-cert.pub")
|
||||||
sshAddHostCmd.Flags().Bool("configure-sshd", false, "Update TrustedUserCAKeys, HostKey, and HostCertificate in the sshd_config file")
|
sshAddHostCmd.Flags().Bool("configure-sshd", false, "Update `TrustedUserCAKeys`, `HostKey`, and `HostCertificate` in the `/etc/ssh/sshd_config` file")
|
||||||
sshAddHostCmd.Flags().Bool("force", false, "Force overwrite of existing certificate files as part of writeUserCaToFile and writeHostCertToFile")
|
sshAddHostCmd.Flags().Bool("force", false, "Force overwrite of existing certificate files as part of `--write-user-ca-to-file` and `--write-host-cert-to-file`")
|
||||||
|
|
||||||
sshCmd.AddCommand(sshAddHostCmd)
|
sshCmd.AddCommand(sshAddHostCmd)
|
||||||
|
|
||||||
|
|||||||
+35
-91
@@ -22,15 +22,15 @@ This command enables you to obtain SSH credentials used to access a remote host.
|
|||||||
<Accordion title="--hostname">
|
<Accordion title="--hostname">
|
||||||
The hostname of the SSH host to connect to. If not provided, you will be prompted to select from available hosts.
|
The hostname of the SSH host to connect to. If not provided, you will be prompted to select from available hosts.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="--loginUser">
|
<Accordion title="--login-user">
|
||||||
The login user for the SSH connection. If not provided, you will be prompted to select from available login users.
|
The login user for the SSH connection. If not provided, you will be prompted to select from available login users.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="--writeHostCaToFile">
|
<Accordion title="--write-host-ca-to-file">
|
||||||
Whether to write the Host CA public key to `~/.ssh/known_hosts` if it doesn't already exist.
|
Whether to write the Host CA public key to `~/.ssh/known_hosts` if it doesn't already exist.
|
||||||
|
|
||||||
Default value: `true`
|
Default value: `true`
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="--outFilePath">
|
<Accordion title="--out-file-path">
|
||||||
The path to write the SSH credentials to such as `~/.ssh`, `./some_folder`, `./some_folder/id_rsa-cert.pub`. If not provided, the credentials will be added to the SSH agent and used to establish an interactive SSH connection.
|
The path to write the SSH credentials to such as `~/.ssh`, `./some_folder`, `./some_folder/id_rsa-cert.pub`. If not provided, the credentials will be added to the SSH agent and used to establish an interactive SSH connection.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="--token">
|
<Accordion title="--token">
|
||||||
@@ -39,108 +39,52 @@ This command enables you to obtain SSH credentials used to access a remote host.
|
|||||||
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="infisical ssh issue-credentials">
|
<Accordion title="infisical ssh add-host">
|
||||||
This command is used to issue SSH credentials (SSH certificate, public key, and private key) against a certificate template.
|
This command is used to register a new SSH host with Infisical.
|
||||||
|
|
||||||
We recommend using the `--addToAgent` flag to automatically load issued SSH credentials to the SSH agent.
|
|
||||||
|
|
||||||
|
This command can be used with the `--write-user-ca-to-file`, `--write-host-cert-to-file`, and `--configure-sshd` flags
|
||||||
|
to also configure the host's SSH daemon with the necessary certificate authority and host certificate settings.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ infisical ssh issue-credentials --certificateTemplateId=<certificate-template-id> --principals=<principals> --addToAgent
|
$ infisical ssh add-host --projectId=<project-id> --hostname=<hostname>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Flags
|
### Flags
|
||||||
<Accordion title="--certificateTemplateId">
|
<Accordion title="--projectId">
|
||||||
The ID of the SSH certificate template to issue SSH credentials for.
|
Project ID the host belongs to (required)
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="--principals">
|
<Accordion title="--hostname">
|
||||||
A comma-separated list of principals (i.e. usernames like `ec2-user` or hostnames) to issue SSH credentials for.
|
Hostname of the SSH host (required)
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="--addToAgent">
|
<Accordion title="--write-user-ca-to-file">
|
||||||
Whether to add issued SSH credentials to the SSH agent.
|
Write User CA public key to `/etc/ssh/infisical_user_ca.pub`
|
||||||
|
|
||||||
|
Default value: `false`
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="--user-ca-out-file-path">
|
||||||
|
Custom file path to write the User CA public key
|
||||||
|
|
||||||
|
Default value: `/etc/ssh/infisical_user_ca.pub`
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="--write-host-cert-to-file">
|
||||||
|
Write SSH host certificate to `/etc/ssh/ssh_host_<type>_key-cert.pub`
|
||||||
|
|
||||||
|
Default value: `false`
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="--configure-sshd">
|
||||||
|
Update `TrustedUserCAKeys`, `HostKey`, and `HostCertificate` in the `/etc/ssh/sshd_config` file
|
||||||
|
|
||||||
Default value: `false`
|
Default value: `false`
|
||||||
|
|
||||||
Note that either the `--outFilePath` or `--addToAgent` flag must be set for the sub-command to execute successfully.
|
Note: This flag requires both --write-user-ca-to-file and --write-host-cert-to-file to be set
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="--outFilePath">
|
<Accordion title="--force">
|
||||||
The path to write the SSH credentials to such as `~/.ssh`, `./some_folder`, `./some_folder/id_rsa-cert.pub`. If not provided, the credentials will be saved to the current working directory where the command is run.
|
Force overwrite of existing certificate files as part of `--write-user-ca-to-file` and `--write-host-cert-to-file`
|
||||||
|
|
||||||
Note that either the `--outFilePath` or `--addToAgent` flag must be set for the sub-command to execute successfully.
|
Default value: `false`
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--keyAlgorithm">
|
|
||||||
The key algorithm to issue SSH credentials for.
|
|
||||||
|
|
||||||
Default value: `RSA_2048`
|
|
||||||
|
|
||||||
Available options: `RSA_2048`, `RSA_4096`, `EC_prime256v1`, `EC_secp384r1`.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--certType">
|
|
||||||
The certificate type to issue SSH credentials for.
|
|
||||||
|
|
||||||
Default value: `user`
|
|
||||||
|
|
||||||
Available options: `user` or `host`
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--ttl">
|
|
||||||
The time-to-live (TTL) for the issued SSH certificate (e.g. `2 days`, `1d`, `2h`, `1y`).
|
|
||||||
|
|
||||||
Defaults to the Default TTL value set in the certificate template.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--keyId">
|
|
||||||
A custom Key ID to issue SSH credentials for.
|
|
||||||
|
|
||||||
Defaults to the autogenerated Key ID by Infisical.
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="--token">
|
<Accordion title="--token">
|
||||||
An authenticated token to use to issue SSH credentials.
|
Use a machine identity access token
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="infisical ssh sign-key">
|
|
||||||
This command is used to sign an existing SSH public key against a certificate template; the command outputs the corresponding signed SSH certificate.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ infisical ssh sign-key --certificateTemplateId=<certificate-template-id> --publicKey=<public-key> --principals=<principals> --outFilePath=<out-file-path>
|
|
||||||
```
|
|
||||||
<Accordion title="--certificateTemplateId">
|
|
||||||
The ID of the SSH certificate template to issue the SSH certificate for.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--publicKey">
|
|
||||||
The public key to sign.
|
|
||||||
|
|
||||||
Note that either the `--publicKey` or `--publicKeyFilePath` flag must be set for the sub-command to execute successfully.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--publicKeyFilePath">
|
|
||||||
The path to the public key file to sign.
|
|
||||||
|
|
||||||
Note that either the `--publicKey` or `--publicKeyFilePath` flag must be set for the sub-command to execute successfully.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--principals">
|
|
||||||
A comma-separated list of principals (i.e. usernames like `ec2-user` or hostnames) to issue SSH credentials for.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--outFilePath">
|
|
||||||
The path to write the SSH certificate to such as `~/.ssh/id_rsa-cert.pub`; the specified file must have the `.pub` extension. If not provided, the credentials will be saved to the directory of the specified `--publicKeyFilePath` or the current working directory where the command is run.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--certType">
|
|
||||||
The certificate type to issue SSH credentials for.
|
|
||||||
|
|
||||||
Default value: `user`
|
|
||||||
|
|
||||||
Available options: `user` or `host`
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--ttl">
|
|
||||||
The time-to-live (TTL) for the issued SSH certificate (e.g. `2 days`, `1d`, `2h`, `1y`).
|
|
||||||
|
|
||||||
Defaults to the Default TTL value set in the certificate template.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--keyId">
|
|
||||||
A custom Key ID to issue SSH credentials for.
|
|
||||||
|
|
||||||
Defaults to the autogenerated Key ID by Infisical.
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="--token">
|
|
||||||
An authenticated token to use to issue SSH credentials.
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export type TSshHost = {
|
|||||||
id: string;
|
id: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
hostname: string;
|
hostname: string;
|
||||||
|
alias: string | null;
|
||||||
userCertTtl: string;
|
userCertTtl: string;
|
||||||
hostCertTtl: string;
|
hostCertTtl: string;
|
||||||
loginMappings: {
|
loginMappings: {
|
||||||
@@ -15,6 +16,7 @@ export type TSshHost = {
|
|||||||
export type TCreateSshHostDTO = {
|
export type TCreateSshHostDTO = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
hostname: string;
|
hostname: string;
|
||||||
|
alias: string | null;
|
||||||
userCertTtl?: string;
|
userCertTtl?: string;
|
||||||
hostCertTtl?: string;
|
hostCertTtl?: string;
|
||||||
loginMappings: {
|
loginMappings: {
|
||||||
@@ -28,6 +30,7 @@ export type TCreateSshHostDTO = {
|
|||||||
export type TUpdateSshHostDTO = {
|
export type TUpdateSshHostDTO = {
|
||||||
sshHostId: string;
|
sshHostId: string;
|
||||||
hostname?: string;
|
hostname?: string;
|
||||||
|
alias?: string | null;
|
||||||
userCertTtl?: string;
|
userCertTtl?: string;
|
||||||
hostCertTtl?: string;
|
hostCertTtl?: string;
|
||||||
loginMappings?: {
|
loginMappings?: {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ type Props = {
|
|||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
hostname: z.string(),
|
hostname: z.string(),
|
||||||
|
alias: z.string(),
|
||||||
userCertTtl: z
|
userCertTtl: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
@@ -81,6 +82,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
hostname: "",
|
hostname: "",
|
||||||
|
alias: "",
|
||||||
userCertTtl: "8h",
|
userCertTtl: "8h",
|
||||||
loginMappings: []
|
loginMappings: []
|
||||||
}
|
}
|
||||||
@@ -95,6 +97,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
if (sshHost) {
|
if (sshHost) {
|
||||||
reset({
|
reset({
|
||||||
hostname: sshHost.hostname,
|
hostname: sshHost.hostname,
|
||||||
|
alias: sshHost.alias ?? "",
|
||||||
userCertTtl: sshHost.userCertTtl,
|
userCertTtl: sshHost.userCertTtl,
|
||||||
loginMappings: sshHost.loginMappings.map(({ loginUser, allowedPrincipals }) => ({
|
loginMappings: sshHost.loginMappings.map(({ loginUser, allowedPrincipals }) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
@@ -108,13 +111,14 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
} else {
|
} else {
|
||||||
reset({
|
reset({
|
||||||
hostname: "",
|
hostname: "",
|
||||||
|
alias: "",
|
||||||
userCertTtl: "8h",
|
userCertTtl: "8h",
|
||||||
loginMappings: []
|
loginMappings: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [sshHost]);
|
}, [sshHost]);
|
||||||
|
|
||||||
const onFormSubmit = async ({ hostname, userCertTtl, loginMappings }: FormData) => {
|
const onFormSubmit = async ({ hostname, alias, userCertTtl, loginMappings }: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
|
|
||||||
@@ -122,7 +126,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
const existingHostnames =
|
const existingHostnames =
|
||||||
sshHosts?.filter((h) => h.id !== sshHost?.id).map((h) => h.hostname) || [];
|
sshHosts?.filter((h) => h.id !== sshHost?.id).map((h) => h.hostname) || [];
|
||||||
|
|
||||||
if (existingHostnames.includes(hostname)) {
|
if (existingHostnames.includes(hostname.trim())) {
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "A host with this hostname already exists.",
|
text: "A host with this hostname already exists.",
|
||||||
type: "error"
|
type: "error"
|
||||||
@@ -130,10 +134,28 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const processedAlias = alias.trim() || null;
|
||||||
|
|
||||||
|
// check if there is already a different host with the same non-null alias
|
||||||
|
if (processedAlias) {
|
||||||
|
const existingAliases =
|
||||||
|
sshHosts?.filter((h) => h.id !== sshHost?.id && h.alias !== null).map((h) => h.alias) ||
|
||||||
|
[];
|
||||||
|
|
||||||
|
if (existingAliases.includes(processedAlias)) {
|
||||||
|
createNotification({
|
||||||
|
text: "A host with this alias already exists.",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (sshHost) {
|
if (sshHost) {
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
sshHostId: sshHost.id,
|
sshHostId: sshHost.id,
|
||||||
hostname,
|
hostname,
|
||||||
|
alias: processedAlias,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({
|
loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
@@ -146,6 +168,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
await createMutateAsync({
|
await createMutateAsync({
|
||||||
projectId,
|
projectId,
|
||||||
hostname,
|
hostname,
|
||||||
|
alias: processedAlias,
|
||||||
userCertTtl,
|
userCertTtl,
|
||||||
loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({
|
loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
@@ -209,6 +232,16 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="alias"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Alias" isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Input {...field} placeholder="host" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export const SshHostsTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
<Table>
|
<Table>
|
||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
|
<Th>Alias</Th>
|
||||||
<Th>Hostname</Th>
|
<Th>Hostname</Th>
|
||||||
<Th>Login User - Authorized Principals Mapping</Th>
|
<Th>Login User - Authorized Principals Mapping</Th>
|
||||||
<Th />
|
<Th />
|
||||||
@@ -83,6 +84,7 @@ export const SshHostsTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
className="h-10"
|
className="h-10"
|
||||||
key={`ssh-host-${host.id}`}
|
key={`ssh-host-${host.id}`}
|
||||||
>
|
>
|
||||||
|
<Td>{host.alias ?? "-"}</Td>
|
||||||
<Td>{host.hostname}</Td>
|
<Td>{host.hostname}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{host.loginMappings.length === 0 ? (
|
{host.loginMappings.length === 0 ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user