fix: Security Audit SSH Errors #1377

- Fixed SSH key authentication detection in server-audit.ts
- Added proper handling for prohibit-password and other secure root login options
- Fixed typos in security audit UI labels
- Improved error handling with optional chaining
This commit is contained in:
Jason Parks
2025-03-22 14:26:40 -06:00
parent ff3d444b89
commit fc7eff94b6
2 changed files with 86 additions and 30 deletions

View File

@@ -26,6 +26,16 @@ export const SecurityAudit = ({ serverId }: Props) => {
}, },
); );
const _utils = api.useUtils(); const _utils = api.useUtils();
// Helper function to check if root login is securely configured
const isRootLoginSecure = () => {
if (!data?.ssh?.permitRootLogin) return false;
// These are secure options for PermitRootLogin
const secureOptions = ['no', 'prohibit-password', 'without-password', 'forced-commands-only'];
return secureOptions.includes(data.ssh.permitRootLogin);
};
return ( return (
<CardContent className="p-0"> <CardContent className="p-0">
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
@@ -36,10 +46,10 @@ export const SecurityAudit = ({ serverId }: Props) => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<LockKeyhole className="size-5" /> <LockKeyhole className="size-5" />
<CardTitle className="text-xl"> <CardTitle className="text-xl">
Setup Security Sugestions Setup Security Suggestions
</CardTitle> </CardTitle>
</div> </div>
<CardDescription>Check the security sugestions</CardDescription> <CardDescription>Check the security suggestions</CardDescription>
</div> </div>
<Button <Button
isLoading={isRefreshing} isLoading={isRefreshing}
@@ -120,36 +130,45 @@ export const SecurityAudit = ({ serverId }: Props) => {
<div className="grid gap-2.5"> <div className="grid gap-2.5">
<StatusRow <StatusRow
label="Enabled" label="Enabled"
isEnabled={data?.ssh.enabled} isEnabled={data?.ssh?.enabled}
description={ description={
data?.ssh.enabled data?.ssh?.enabled
? "Enabled" ? "Enabled"
: "Not Enabled (SSH should be enabled)" : "Not Enabled (SSH should be enabled)"
} }
/> />
<StatusRow <StatusRow
label="Key Auth" label="Key Auth"
isEnabled={data?.ssh.keyAuth} isEnabled={data?.ssh?.keyAuth}
description={ description={
data?.ssh.keyAuth data?.ssh?.keyAuth
? "Enabled (Recommended)" ? "Enabled (Recommended)"
: "Not Enabled (Key Authentication should be enabled)" : "Not Enabled (Key Authentication should be enabled)"
} }
/> />
<StatusRow <StatusRow
label="Password Auth" label="Password Auth"
isEnabled={data?.ssh.passwordAuth === "no"} isEnabled={data?.ssh?.passwordAuth === "no"}
description={ description={
data?.ssh.passwordAuth === "no" data?.ssh?.passwordAuth === "no"
? "Disabled (Recommended)" ? "Disabled (Recommended)"
: "Enabled (Password Authentication should be disabled)" : "Enabled (Password Authentication should be disabled)"
} }
/> />
<StatusRow <StatusRow
label="Use PAM" label="Root Login"
isEnabled={data?.ssh.usePam === "no"} isEnabled={isRootLoginSecure()}
description={ description={
data?.ssh.usePam === "no" isRootLoginSecure()
? `${data?.ssh?.permitRootLogin} (Secure)`
: `${data?.ssh?.permitRootLogin || "yes"} (Should be set to 'prohibit-password' or 'no')`
}
/>
<StatusRow
label="Use PAM"
isEnabled={data?.ssh?.usePam === "no"}
description={
data?.ssh?.usePam === "no"
? "Disabled (Recommended for key-based auth)" ? "Disabled (Recommended for key-based auth)"
: "Enabled (Should be disabled when using key-based auth)" : "Enabled (Should be disabled when using key-based auth)"
} }
@@ -166,9 +185,9 @@ export const SecurityAudit = ({ serverId }: Props) => {
<div className="grid gap-2.5"> <div className="grid gap-2.5">
<StatusRow <StatusRow
label="Installed" label="Installed"
isEnabled={data?.fail2ban.installed} isEnabled={data?.fail2ban?.installed}
description={ description={
data?.fail2ban.installed data?.fail2ban?.installed
? "Installed (Recommended)" ? "Installed (Recommended)"
: "Not Installed (Fail2Ban should be installed for protection against brute force attacks)" : "Not Installed (Fail2Ban should be installed for protection against brute force attacks)"
} }
@@ -176,18 +195,18 @@ export const SecurityAudit = ({ serverId }: Props) => {
<StatusRow <StatusRow
label="Enabled" label="Enabled"
isEnabled={data?.fail2ban.enabled} isEnabled={data?.fail2ban?.enabled}
description={ description={
data?.fail2ban.enabled data?.fail2ban?.enabled
? "Enabled (Recommended)" ? "Enabled (Recommended)"
: "Not Enabled (Fail2Ban service should be enabled)" : "Not Enabled (Fail2Ban service should be enabled)"
} }
/> />
<StatusRow <StatusRow
label="Active" label="Active"
isEnabled={data?.fail2ban.active} isEnabled={data?.fail2ban?.active}
description={ description={
data?.fail2ban.active data?.fail2ban?.active
? "Active (Recommended)" ? "Active (Recommended)"
: "Not Active (Fail2Ban service should be running)" : "Not Active (Fail2Ban service should be running)"
} }
@@ -195,9 +214,9 @@ export const SecurityAudit = ({ serverId }: Props) => {
<StatusRow <StatusRow
label="SSH Protection" label="SSH Protection"
isEnabled={data?.fail2ban.sshEnabled === "true"} isEnabled={data?.fail2ban?.sshEnabled === "true"}
description={ description={
data?.fail2ban.sshEnabled === "true" data?.fail2ban?.sshEnabled === "true"
? "Enabled (Recommended)" ? "Enabled (Recommended)"
: "Not Enabled (SSH protection should be enabled to prevent brute force attacks)" : "Not Enabled (SSH protection should be enabled to prevent brute force attacks)"
} }
@@ -205,11 +224,11 @@ export const SecurityAudit = ({ serverId }: Props) => {
<StatusRow <StatusRow
label="SSH Mode" label="SSH Mode"
isEnabled={data?.fail2ban.sshMode === "aggressive"} isEnabled={data?.fail2ban?.sshMode === "aggressive"}
description={ description={
data?.fail2ban.sshMode === "aggressive" data?.fail2ban?.sshMode === "aggressive"
? "Aggressive Mode (Recommended)" ? "Aggressive Mode (Recommended)"
: `Mode: ${data?.fail2ban.sshMode || "Not Set"} (Aggressive mode recommended for better protection)` : `Mode: ${data?.fail2ban?.sshMode || "Not Set"} (Aggressive mode recommended for better protection)`
} }
/> />
</div> </div>
@@ -221,4 +240,4 @@ export const SecurityAudit = ({ serverId }: Props) => {
</div> </div>
</CardContent> </CardContent>
); );
}; };

View File

@@ -14,13 +14,50 @@ const validateUfw = () => `
`; `;
const validateSsh = () => ` const validateSsh = () => `
if systemctl is-active --quiet sshd; then if systemctl is-active --quiet sshd || systemctl is-active --quiet ssh; then
isEnabled=true isEnabled=true
hasKeyAuth=$(find "$HOME/.ssh" -type f -name "authorized_keys" 2>/dev/null | grep -q . && echo true || echo false)
permitRootLogin=$(sudo sshd -T | grep -i "^PermitRootLogin" | awk '{print $2}') # Get the sshd config file path
passwordAuth=$(sudo sshd -T | grep -i "^PasswordAuthentication" | awk '{print $2}') sshd_config=$(sudo sshd -T 2>/dev/null | grep -i "^configfile" | awk '{print $2}')
usePam=$(sudo sshd -T | grep -i "^UsePAM" | awk '{print $2}')
echo "{\\"enabled\\": $isEnabled, \\"keyAuth\\": $hasKeyAuth, \\"permitRootLogin\\": \\"$permitRootLogin\\", \\"passwordAuth\\": \\"$passwordAuth\\", \\"usePam\\": \\"$usePam\\"}" # If we couldn't get the path, use the default
if [ -z "$sshd_config" ]; then
sshd_config="/etc/ssh/sshd_config"
fi
# Check for key authentication
# SSH key auth is enabled by default unless explicitly disabled
pubkey_line=$(sudo grep -i "^PubkeyAuthentication" "$sshd_config" 2>/dev/null | grep -v "#")
if [ -z "$pubkey_line" ] || echo "$pubkey_line" | grep -q -i "yes"; then
keyAuth=true
else
keyAuth=false
fi
# Get the exact PermitRootLogin value from config
# This preserves values like "prohibit-password" without normalization
permitRootLogin=$(sudo grep -i "^PermitRootLogin" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}')
if [ -z "$permitRootLogin" ]; then
# Default is prohibit-password in newer versions
permitRootLogin="prohibit-password"
fi
# Get the exact PasswordAuthentication value from config
passwordAuth=$(sudo grep -i "^PasswordAuthentication" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}')
if [ -z "$passwordAuth" ]; then
# Default is yes
passwordAuth="yes"
fi
# Get the exact UsePAM value from config
usePam=$(sudo grep -i "^UsePAM" "$sshd_config" 2>/dev/null | grep -v "#" | awk '{print $2}')
if [ -z "$usePam" ]; then
# Default is yes in most distros
usePam="yes"
fi
# Return the results with exact values from config file
echo "{\\"enabled\\": $isEnabled, \\"keyAuth\\": $keyAuth, \\"permitRootLogin\\": \\"$permitRootLogin\\", \\"passwordAuth\\": \\"$passwordAuth\\", \\"usePam\\": \\"$usePam\\"}"
else else
echo "{\\"enabled\\": false, \\"keyAuth\\": false, \\"permitRootLogin\\": \\"unknown\\", \\"passwordAuth\\": \\"unknown\\", \\"usePam\\": \\"unknown\\"}" echo "{\\"enabled\\": false, \\"keyAuth\\": false, \\"permitRootLogin\\": \\"unknown\\", \\"passwordAuth\\": \\"unknown\\", \\"usePam\\": \\"unknown\\"}"
fi fi
@@ -111,4 +148,4 @@ export const serverAudit = async (serverId: string) => {
privateKey: server.sshKey?.privateKey, privateKey: server.sshKey?.privateKey,
}); });
}); });
}; };