mirror of
https://github.com/fosrl/pangolin.git
synced 2026-07-06 12:19:50 +00:00
Add logging for access for new public resources
This commit is contained in:
@@ -342,7 +342,7 @@ export default function GeneralPage() {
|
||||
return (
|
||||
<Link
|
||||
href={
|
||||
row.original.type === "ssh"
|
||||
row.original.siteResourceId != null
|
||||
? `/${row.original.orgId}/settings/resources/private?query=${row.original.resourceNiceId}`
|
||||
: `/${row.original.orgId}/settings/resources/public/${row.original.resourceNiceId}`
|
||||
}
|
||||
@@ -369,7 +369,9 @@ export default function GeneralPage() {
|
||||
value: "whitelistedEmail",
|
||||
label: "Whitelisted Email"
|
||||
},
|
||||
{ value: "ssh", label: "SSH" }
|
||||
{ value: "ssh", label: "SSH" },
|
||||
{ value: "rdp", label: "RDP" },
|
||||
{ value: "vnc", label: "VNC" }
|
||||
]}
|
||||
label={t("type")}
|
||||
selectedValue={filters.type}
|
||||
@@ -384,8 +386,10 @@ export default function GeneralPage() {
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const typeLabel =
|
||||
row.original.type === "ssh"
|
||||
? "SSH"
|
||||
row.original.type === "ssh" ||
|
||||
row.original.type === "rdp" ||
|
||||
row.original.type === "vnc"
|
||||
? row.original.type.toUpperCase()
|
||||
: row.original.type.charAt(0).toUpperCase() +
|
||||
row.original.type.slice(1);
|
||||
return <span>{typeLabel || "-"}</span>;
|
||||
@@ -513,7 +517,15 @@ export default function GeneralPage() {
|
||||
|
||||
function generateSampleAccessLogs(): QueryAccessAuditLogResponse["log"] {
|
||||
const locations = ["US", "DE", "GB", "FR", "JP", "CA", "AU"];
|
||||
const types = ["password", "pincode", "login", "whitelistedEmail", "ssh"];
|
||||
const types = [
|
||||
"password",
|
||||
"pincode",
|
||||
"login",
|
||||
"whitelistedEmail",
|
||||
"ssh",
|
||||
"rdp",
|
||||
"vnc"
|
||||
];
|
||||
const actors = [
|
||||
"alice@example.com",
|
||||
"bob@example.com",
|
||||
@@ -538,6 +550,7 @@ function generateSampleAccessLogs(): QueryAccessAuditLogResponse["log"] {
|
||||
actor,
|
||||
actorId: actor ? `user-${i}` : null,
|
||||
resourceId: Math.floor(Math.random() * 5) + 1,
|
||||
siteResourceId: null,
|
||||
resourceNiceId: `resource-${(i % 3) + 1}`,
|
||||
resourceName: `Resource ${(i % 3) + 1}`,
|
||||
ip: `${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`,
|
||||
|
||||
@@ -42,6 +42,8 @@ import {
|
||||
loadEncryptedLocalStorage,
|
||||
saveEncryptedLocalStorage
|
||||
} from "@app/lib/secureLocalStorage";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
|
||||
declare module "react" {
|
||||
namespace JSX {
|
||||
@@ -96,6 +98,7 @@ export default function RdpClient({
|
||||
primaryColor?: string | null;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const STORAGE_KEY = "pangolin_rdp_credentials";
|
||||
const resourceName = target?.name?.trim() || null;
|
||||
|
||||
@@ -311,6 +314,11 @@ export default function RdpClient({
|
||||
values,
|
||||
target.authToken
|
||||
);
|
||||
void api.post(`/org/${target.orgId}/logs/access/attempt`, {
|
||||
resourceId: target.resourceId,
|
||||
action: true,
|
||||
type: "rdp"
|
||||
});
|
||||
setConnecting(false);
|
||||
setShowLogin(false);
|
||||
userInteraction.setVisibility(true);
|
||||
@@ -320,6 +328,11 @@ export default function RdpClient({
|
||||
fileTransferRef.current = null;
|
||||
setShowLogin(true);
|
||||
} catch (err) {
|
||||
void api.post(`/org/${target.orgId}/logs/access/attempt`, {
|
||||
resourceId: target.resourceId,
|
||||
action: false,
|
||||
type: "rdp"
|
||||
});
|
||||
setConnecting(false);
|
||||
setShowLogin(true);
|
||||
if (isIronError(err)) {
|
||||
|
||||
@@ -36,6 +36,8 @@ import {
|
||||
loadEncryptedLocalStorage,
|
||||
saveEncryptedLocalStorage
|
||||
} from "@app/lib/secureLocalStorage";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
|
||||
type AuthTab = "password" | "privateKey";
|
||||
|
||||
@@ -73,6 +75,7 @@ export default function SshClient({
|
||||
}) {
|
||||
const STORAGE_KEY = "pangolin_ssh_credentials";
|
||||
const t = useTranslations();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const resourceName = target?.name?.trim() || null;
|
||||
|
||||
const passwordTabSchema = z.object({
|
||||
@@ -263,6 +266,17 @@ export default function SshClient({
|
||||
let authConfirmed = false;
|
||||
let authErrorShown = false;
|
||||
let socketOpened = false;
|
||||
let auditLogged = false;
|
||||
|
||||
const logAudit = (action: boolean) => {
|
||||
if (auditLogged || !target) return;
|
||||
auditLogged = true;
|
||||
void api.post(`/org/${target.orgId}/logs/access/attempt`, {
|
||||
resourceId: target.resourceId,
|
||||
action,
|
||||
type: "ssh"
|
||||
});
|
||||
};
|
||||
|
||||
ws.onopen = () => {
|
||||
socketOpened = true;
|
||||
@@ -294,6 +308,7 @@ export default function SshClient({
|
||||
if (msg.type === "data" && msg.data) {
|
||||
if (!authConfirmed) {
|
||||
authConfirmed = true;
|
||||
logAudit(true);
|
||||
setConnecting(false);
|
||||
setConnected(true);
|
||||
}
|
||||
@@ -301,6 +316,7 @@ export default function SshClient({
|
||||
} else if (msg.type === "error") {
|
||||
if (!authConfirmed) {
|
||||
authErrorShown = true;
|
||||
logAudit(false);
|
||||
setConnecting(false);
|
||||
setConnectError(
|
||||
msg.error ?? t("sshErrorAuthFailed")
|
||||
@@ -323,6 +339,7 @@ export default function SshClient({
|
||||
evt.data.text().then((text) => {
|
||||
if (!authConfirmed) {
|
||||
authConfirmed = true;
|
||||
logAudit(true);
|
||||
setConnecting(false);
|
||||
setConnected(true);
|
||||
}
|
||||
@@ -332,6 +349,7 @@ export default function SshClient({
|
||||
};
|
||||
|
||||
ws.onerror = () => {
|
||||
logAudit(false);
|
||||
setConnecting(false);
|
||||
setConnected(false);
|
||||
setConnectError(t("sshErrorWebSocket"));
|
||||
@@ -355,6 +373,7 @@ export default function SshClient({
|
||||
);
|
||||
}
|
||||
if (!authConfirmed && !authErrorShown) {
|
||||
logAudit(false);
|
||||
setConnectError(t("sshErrorConnectionClosed"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -33,6 +33,8 @@ import {
|
||||
loadEncryptedLocalStorage,
|
||||
saveEncryptedLocalStorage
|
||||
} from "@app/lib/secureLocalStorage";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
|
||||
type VncCredentialsForm = {
|
||||
password: string;
|
||||
@@ -52,6 +54,7 @@ export default function VncClient({
|
||||
primaryColor?: string | null;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const STORAGE_KEY = "pangolin_vnc_credentials";
|
||||
const resourceName = target?.name?.trim() || null;
|
||||
|
||||
@@ -179,6 +182,7 @@ export default function VncClient({
|
||||
}
|
||||
|
||||
let authConfirmed = false;
|
||||
let auditLogged = false;
|
||||
|
||||
rfb.scaleViewport = true;
|
||||
rfb.resizeSession = true;
|
||||
@@ -190,6 +194,12 @@ export default function VncClient({
|
||||
target.authToken
|
||||
);
|
||||
authConfirmed = true;
|
||||
auditLogged = true;
|
||||
void api.post(`/org/${target.orgId}/logs/access/attempt`, {
|
||||
resourceId: target.resourceId,
|
||||
action: true,
|
||||
type: "vnc"
|
||||
});
|
||||
setConnecting(false);
|
||||
setConnected(true);
|
||||
});
|
||||
@@ -201,6 +211,17 @@ export default function VncClient({
|
||||
setConnecting(false);
|
||||
setConnected(false);
|
||||
if (!authConfirmed && !e.detail.clean) {
|
||||
if (!auditLogged) {
|
||||
auditLogged = true;
|
||||
void api.post(
|
||||
`/org/${target.orgId}/logs/access/attempt`,
|
||||
{
|
||||
resourceId: target.resourceId,
|
||||
action: false,
|
||||
type: "vnc"
|
||||
}
|
||||
);
|
||||
}
|
||||
setConnectError(t("sshErrorConnectionClosed"));
|
||||
}
|
||||
}
|
||||
@@ -209,6 +230,12 @@ export default function VncClient({
|
||||
rfb.addEventListener(
|
||||
"securityfailure",
|
||||
(e: { detail: { status: number; reason?: string } }) => {
|
||||
auditLogged = true;
|
||||
void api.post(`/org/${target.orgId}/logs/access/attempt`, {
|
||||
resourceId: target.resourceId,
|
||||
action: false,
|
||||
type: "vnc"
|
||||
});
|
||||
disconnect();
|
||||
setConnectError(
|
||||
e.detail.reason ??
|
||||
|
||||
Reference in New Issue
Block a user