Add support for tracking more SFTP specific events

This commit is contained in:
DaneEveritt 2022-07-09 19:30:38 -04:00
parent 2e01891074
commit 33ab762f5a
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
5 changed files with 38 additions and 11 deletions

View file

@ -6,6 +6,7 @@ use Illuminate\Http\Request;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Models\Permission; use Pterodactyl\Models\Permission;
use phpseclib3\Crypt\PublicKeyLoader; use phpseclib3\Crypt\PublicKeyLoader;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
@ -51,6 +52,8 @@ class SftpAuthenticationController extends Controller
if ($request->input('type') !== 'public_key') { if ($request->input('type') !== 'public_key') {
if (!password_verify($request->input('password'), $user->password)) { if (!password_verify($request->input('password'), $user->password)) {
Activity::event('auth:sftp.fail')->property('method', 'password')->subject($user)->log();
$this->reject($request); $this->reject($request);
} }
} else { } else {
@ -62,13 +65,29 @@ class SftpAuthenticationController extends Controller
} }
if (!$key || !$user->sshKeys()->where('fingerprint', $key->getFingerprint('sha256'))->exists()) { if (!$key || !$user->sshKeys()->where('fingerprint', $key->getFingerprint('sha256'))->exists()) {
// We don't log here because of the way the SFTP system works. This endpoint
// will get hit for every key the user provides, which could be 4 or 5. That is
// a lot of unnecessary log noise.
//
// For now, we'll only log failures due to a bad password as those are not likely
// to occur more than once in a session for the user, and are more likely to be of
// value to the end user.
$this->reject($request, is_null($key)); $this->reject($request, is_null($key));
} }
} }
$this->validateSftpAccess($user, $server); $this->validateSftpAccess($user, $server);
Activity::event('auth:sftp.success')->actor($user)
->subject($user)
->property(array_filter([
'method' => isset($key) ? 'ssh_key' : 'password',
'fingerprint' => isset($key) ? 'SHA256:' . $key->getFingerprint('sha256') : null,
]))
->log();
return new JsonResponse([ return new JsonResponse([
'user' => $user->uuid,
'server' => $server->uuid, 'server' => $server->uuid,
'permissions' => $this->permissions->handle($server, $user), 'permissions' => $this->permissions->handle($server, $user),
]); ]);
@ -136,6 +155,8 @@ class SftpAuthenticationController extends Controller
$permissions = $this->permissions->handle($server, $user); $permissions = $this->permissions->handle($server, $user);
if (!in_array(Permission::ACTION_FILE_SFTP, $permissions)) { if (!in_array(Permission::ACTION_FILE_SFTP, $permissions)) {
Activity::event('server:sftp.denied')->actor($user)->subject($server)->log();
throw new HttpForbiddenException('You do not have permission to access SFTP for this server.'); throw new HttpForbiddenException('You do not have permission to access SFTP for this server.');
} }
} }

View file

@ -92,7 +92,7 @@ class ActivityLogTransformer extends BaseClientTransformer
$str = trans('activity.' . str_replace(':', '.', $model->event)); $str = trans('activity.' . str_replace(':', '.', $model->event));
preg_match_all('/:(?<key>[\w.-]+\w)(?:[^\w:]?|$)/', $str, $matches); preg_match_all('/:(?<key>[\w.-]+\w)(?:[^\w:]?|$)/', $str, $matches);
$exclude = array_merge($matches['key'], ['ip', 'useragent']); $exclude = array_merge($matches['key'], ['ip', 'useragent', 'using_sftp']);
foreach ($model->properties->keys() as $key) { foreach ($model->properties->keys() as $key) {
if (!in_array($key, $exclude, true)) { if (!in_array($key, $exclude, true)) {
return true; return true;

View file

@ -16,6 +16,10 @@ return [
'recovery-token' => 'Used two-factor recovery token', 'recovery-token' => 'Used two-factor recovery token',
'token' => 'Solved two-factor challenge', 'token' => 'Solved two-factor challenge',
'ip-blocked' => 'Blocked request from unlisted IP address for :identifier', 'ip-blocked' => 'Blocked request from unlisted IP address for :identifier',
'sftp' => [
'success' => 'Logged in using SFTP',
'fail' => 'Failed SFTP log in',
],
], ],
'user' => [ 'user' => [
'account' => [ 'account' => [
@ -96,6 +100,9 @@ return [
'update' => 'Updated the ":action" task for the :name schedule', 'update' => 'Updated the ":action" task for the :name schedule',
'delete' => 'Deleted a task for the :name schedule', 'delete' => 'Deleted a task for the :name schedule',
], ],
'sftp' => [
'denied' => 'Blocked SFTP access due to permissions',
],
'settings' => [ 'settings' => [
'rename' => 'Renamed the server from :old to :new', 'rename' => 'Renamed the server from :old to :new',
], ],

View file

@ -5,7 +5,7 @@ import Translate from '@/components/elements/Translate';
import { format, formatDistanceToNowStrict } from 'date-fns'; import { format, formatDistanceToNowStrict } from 'date-fns';
import { ActivityLog } from '@definitions/user'; import { ActivityLog } from '@definitions/user';
import ActivityLogMetaButton from '@/components/elements/activity/ActivityLogMetaButton'; import ActivityLogMetaButton from '@/components/elements/activity/ActivityLogMetaButton';
import { TerminalIcon } from '@heroicons/react/solid'; import { FolderOpenIcon, TerminalIcon } from '@heroicons/react/solid';
import classNames from 'classnames'; import classNames from 'classnames';
import style from './style.module.css'; import style from './style.module.css';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
@ -65,10 +65,13 @@ export default ({ activity, children }: Props) => {
</Link> </Link>
<div className={classNames(style.icons, 'group-hover:text-gray-300')}> <div className={classNames(style.icons, 'group-hover:text-gray-300')}>
{activity.isApi && ( {activity.isApi && (
<Tooltip placement={'top'} content={'Performed using API Key'}> <Tooltip placement={'top'} content={'Using API Key'}>
<span>
<TerminalIcon /> <TerminalIcon />
</span> </Tooltip>
)}
{activity.properties.using_sftp && (
<Tooltip placement={'top'} content={'Using SFTP'}>
<FolderOpenIcon />
</Tooltip> </Tooltip>
)} )}
{children} {children}

View file

@ -1,12 +1,8 @@
.icons { .icons {
@apply flex space-x-1 mx-2 transition-colors duration-100 text-gray-400; @apply flex space-x-1 mx-2 transition-colors duration-100 text-gray-400;
& > span {
@apply px-1 py-px cursor-pointer hover:text-gray-50;
}
& svg { & svg {
@apply w-4 h-4; @apply px-1 py-px cursor-pointer hover:text-gray-50 h-5 w-auto;
} }
} }