10.3. Обеспечиваем безопасность FreeBSD

Команда или протокол?: Далее по тексту, мы будем использовать полужирный шрифт для обозначения команды или программы. Это делается для того, чтобы отличать команды или утилиты от соответствующих протоколов (например, ssh).

В этой секции мы расскажем об основных методах защиты и обеспечения безопасности Вашей системы FreeBSD, которые были упомянуты в предыдущей секции этой главы.

10.3.1. Защита аккаунтов суперпользователя и служебного персонала

В первую очередь нужно обеспечить безопасность суперпользовательского аккаунта, и уже после принимать аналогичные меры в отношении прочих привилегированных аккаунтов. Во многих системах аккаунт суперпользователя защищен паролем. Имейте в виду, что этот пароль крайне важен, а поэтому нелишним будет относиться к нему с очень большой осторожностью. Прежде всего, желательно не использовать его кроме как за консолью, даже применяя команду su(1). В частности, удостоверьтесь, что терминалы, перечисленные в файле /etc/ttys, помечены как insecure (небезопасные), чтобы запретить прямой доступ посредством команд telnet или rlogin. При использовании других сервисов, таких, например, как sshd, также следует запретить непосредственный вход в систему как суперпользователя. Проверьте все возможные подступы к системе - сервисы типа FTP часто оказываются подверженными атакам. Непосредственный вход в систему с правами суперпользователя следует разрешить только с консоли.

Конечно, Вам как системному администратору, необходимо иметь возможность получить права суперпользователя, поэтому несколько путей все же есть. Однако, очень важно защитить их использование дополнительными паролями. Один из способов дать какому-либо пользователю повышенные привилегии - это перечислить его в группе wheel (в файле /etc/group). Пользователь, входящий в эту группу, может вызвать su для получения прав суперпользователя. Не следует включать всех членов служебного персонала в группу wheel, задавая ее как основную группу пользователя в файле паролей. Вместо этого, нужно включить их в специальную группу staff, и затем, только в случае необходимости, некоторых из них включить в группу wheel, сделав соответствующую запись в файле /etc/group. Также возможно, при использовании методов аутентификации типа Kerberos, использовать файл .k5login для того, чтобы разрешить получение привилегий суперпользователя посредством команды ksu(1) без необходимости включать кого-либо в группу wheel. Это может оказаться лучшим решением, так как предыдущий метод позволяет хакеру получить права суперпользователя, если ему удастся получить доступ к служебному аккаунту. Тем не менее, все же это лучше, чем ничего.

Аккаунт суперпользователя можно защитить косвенным путем, посредством использования альтернативных методов аутентификации и замены хэшированных паролей служебных аккаунтов на символ "*" (англ. "starring") в файле паролей. Используя команду vipw(8), Вы можете заменить зашифрованный пароль на символ "*". Эта команда обновит /etc/master.passwd и базу пользователей/паролей (заблокирует обычный доступ по паролю).

Служебные аккаунты, такие как, например:

    foobar:R9DT/Fa1/LV9U:1000:1000::0:0:Foo
    Bar:/home/foobar:/usr/local/bin/tcsh

должны быть изменены следующим образом:

    foobar:*:1000:1000::0:0:Foo
    Bar:/home/foobar:/usr/local/bin/tcsh

В этом случае, даже если взломщик получит этот файл, он не сможет извлечь оттуда пароли, в том числе пароль суперпользователя. Сотрудники служебного персонала будут сходить в систему при помощи защищенного механизма аутентификации, например, kerberos(1) или ssh(1), используя специальную пару ключей (приватный/публичный). При использовании систем типа Kerberos, Вам потребуется обеспечить безопасность Kerberos-серверов и Вашей рабочей станции. При использовании механизма публичного/приватного ключей, например, при работе с ssh, Вам необходимо обеспечить безопасность машины, с которой Вы будете заходить в систему (обычно это Ваша рабочая станция), но можно также обеспечить себе дополнительную безопасность, защитив пару ключей паролем в момент ее генерации (ssh-keygen(1)). Возможность заменять реальные хэши паролей на символ "*" в файле паролей (это особенно важно для аккаунтов служебного персонала, обладающего повышенными привилегиями по сравнению с обычными пользователями) также гарантирует, что они будут пользоваться только безопасными методами аутентификации, которые Вы предварительно настроили. Это хорошо тем, что закрывает наиболее часто используемую взломщиками дыру - прослушивание ("сниффинг") сети с менее защищенной машины (на которой у него уже есть все необходимые привилегии).

Еще стоит обратить внимание на следующие моменты: если Ваш основной сервер предоставляет всевозможные сетевые сервисы, то на Вашей рабочей станции по возможности максимальное число сетевых служб должно быть отключено (а в идеале - все), и нелишним будет использование хранителя экрана, защищенного паролем. Конечно, если у злоумышленника есть физический доступ к машине, то практически никакие методы защиты не помогут (хотя и могут значительно увеличить время взлома), однако большинство атак все же происходят снаружи, по сети, когда у атакующего нет непосредственного доступа к системе.

При использовании систем аутентификации типа Kerberos, у Вас есть возможность централизованно менять пароли или блокировать доступ пользователей. Это очень полезно при подозрении, что пароль какого-либо (например, администраторского) аккаунта стал известен постороннему лицу - в этом случае можно очень быстро запретить вход на все машины, где был заведен данный пользователь. Представьте себе, насколько труднее и дольше было бы менять пароли на каждой из N машин отдельно! Kerberos предоставляет и другие возможности (принудительная смена пароля по истечении определенного промежутка времени, например, раз в месяц).

10.3.2. Сервисы, запускаемые с привилегиями суперпользователя, и программы с установленными SUID/SGID битами

Сознательный и осторожный системный администратор оставляет только те сервисы, которые ему нужны, не больше, не меньше. Имейте в виду, что сервисы независимых сторонних производителей часто являются наиболее потенциально уязвимыми. Например, запуская старую версию imapd или popper, Вы тем самым отдаете Вашу систему в полное распоряжение любому желающему извне. Никогда не запускайте сервисы, в безопасности которых Вы не уверены. Так же стоит иметь в виду, что многим сервисам вовсе не нужны привилегированные права. Часть сервисов можно (и нужно) запускать в так называемой песочнице, например, это относится к ntalk, comsat и finger. Конечно, это решение не является панацеей безопасности, но и здесь применим уже знакомый нам уровневый принцип: если кто-то сумел взломать сервис, запущенный внутри "песочницы", ему еще нужно суметь сломать саму "песочницу". Увеличивая число таких промежуточных этапов, Вы оставляете потенциальному взломщику меньше шансов на успех. Дыры, позволяющие (в том числе удаленно) получить привилегии суперпользователя, в свое время были найдены практические во всех сервисах, работающих с такими привилегиями, включая базовые системные сервисы. В частности, если на Вашей системе установлен и используется пакет sshd, а telnetd, rshd и rlogind никому не нужны, то лучше выключить эти сервисы вообще!

По умолчанию FreeBSD запускает следующие сервисы в "песочнице": ntalkd, comsat и finger. Если Вы планируете использовать named(8), то его тоже будет нелишним обезопасить таким же образом. Пример как это сделать можно посмотреть в /etc/defaults/rc.conf (в закомментированном виде). В зависимости от того, устанавливаете ли Вы систему "с нуля" или обновляете уже существующую, специальные пользовательские аккаунты могут быть или не быть созданы. Общее правило таково: везде, где это возможно, лучше запускать сервис внутри "песочницы".

Другие сервисы, напротив, обычно запускают "просто так" (то есть, не в "песочнице"): sendmail, popper, imapd, ftpd, и другие. Для некоторых из них есть альтернативы, но, если Вы решили их использовать, будьте готовы к тому, чтобы потратить на их настройку больше времени, чем Вам хотелось бы (за удобства надо платить). Вам также может потребоваться запускать эти сервисы из-под суперпользователя, и полагаться на различные механизмы обнаружения атак, которые потенциально допускают выбранные Вами сервисы.

Другая большая потенциальная дыра в системе это suid- и sgid-программы (имеется ввиду suid на суперпользователя (uid 0) и sgid на супергруппу (gid 0)). Большинство таких программ, как, например, rlogin, расположены в каталогах /bin, /sbin, /usr/bin или /usr/sbin. Хотя абсолютно надежных систем не существует, те suid- и sgid-программы, которые входят в базовую поставку системы, могут считаться достаточно безопасными. Тем не менее, дыры иногда (пусть достаточно редко) находят даже в них. Так, в 1998 г. такая уязвимость была обнаружена в Xlib, что повлекло за собой уязвимость xterm, который обычно является suid-приложением на суперпользователя. В любом случае, лучше спать спокойно, чем постоянно бояться взлома, поэтому стоит разрешить доступ (и запуск) к suid-приложениям только служебному персоналу (например, создав специальную группу, в которую входят доверенные лица), и полностью запретить всяческий доступ (chmod 000) к тем suid-программам, которыми не пользуется никто. На сервере без дисплея (монитора) обычно не нужна программа xterm (хотя есть исключения). Sgid-программы могут быть столь же опасны. Если взломщик сумеет каким-либо образом "поломать" программу sgid на kmem, это может дать ему доступ на чтение /dev/kmem, что теоретически компрометирует пароль от любого аккаунта. Или, атакующий, получив доступ к группе kmem, может отслеживать все нажатия клавиш, посылаемые через pty'и, в том числе и через те, через которые пользователи аутентифицировались с помощью безопасных методов. Если же атакующий получил группу tty, то у него появляется возможность писать на tty практически любого пользователя. Если пользователь запускает терминальную утилиту (или эмулятор терминала) с возможностью эмулировать нажатие клавиш, взломщик теоретически может послать последовательность данных, что приведет к выполнению команды от имени скомпрометированного пользователя.

10.3.3. Защита аккаунтов обычных пользователей

Пользовательские аккаунты, как правило, наиболее сложно полностью обезопасить. Если в случае административных аккаунтов Вы могли придерживаться весьма строгих правил в отношении безопасности, то для обычных пользователей такие меры могут оказаться неприемлемыми. Вам придется выбирать: принимать ли эти строгие меры по отношению к Вашим пользователям, или же просто более внимательно следить за пользовательскими аккаунтами. Использование ssh и Kerberos для этих аккаунтов представляется более проблематичным из-за дополнительных административных и технических мер, но все же это намного безопаснее, чем просто зашифрованный файл паролей.

10.3.4. Защита файла паролей

Самым действенным будет заменить как можно больше паролей на "*" и пользоваться ssh или Kerberos для доступа к этим аккаунтам. Хотя зашифрованный файл паролей (/etc/spwd.db) может быть прочитан только суперпользователем, есть вероятность, что взломщик получит право читать этот файл, что компрометирует все пароли, даже если писать туда он не сможет.

Ваши специально написанные сценарии (скрипты) для поддержки безопасности всегда должны проверять и сообщать обо всех изменениях в файлах паролей (see Проверка целостности файлов ниже).

10.3.5. Защита ядра, непосредственных ("raw") устройств и файловых систем

Если атакующий получает привилегии суперпользователя, он может делать в системе практически все, что ему угодно, однако скорее всего это будет нечто разумное и, часто, уже известное. Например, большинство современных ядер включают в себя драйверы устройств для прослушивания сетевого трафика. Во FreeBSD это устройство называется bpf. Взломщик, скорее всего, попытается запустить прослушивающую программу ("сниффер"), так что лучше не давать (даже теоретически) ему такую возможность. В большинстве случаев Вам вряд ли понадобится bpf в ядре, так что надежнее будет его выключить.

Но, даже если Вы выключили bpf, остаются еще /dev/mem и /dev/kmem, о которых тоже нужно позаботиться, например, чтобы не дать взломщику возможность писать в непосредственные ("raw") дисковые устройства. Хакер также может догадаться воспользоваться возможностью ядра загружать внешние модули (kldload(8)), и, таким образом, проинсталлировать свой собственный драйвер bpf или прослушивающего сеть устройства, внедрив новую функциональность в ядро (без пересборки и перезагрузки). Во избежании этого, ядро должно работать на более высоком уровне безопасности ("securelevel"), по крайней мере, 1-м. Уровень безопасности может быть установлен с помощью команды sysctl через переменную kern.securelevel. Выставив ее в 1, доступ на запись в непосредственные устройства будет запрещен и специальные флаги команды chflags, например, schg, будут форсированы. Вам также следует удостовериться, что флаг schg установлен на все критичные для запуска системы программы, каталоги и файлы сценариев. Конечно, при высоких уровнях безопасности становится чрезвычайно трудно обновлять систему и/или ядро, и в таком случае Вам, скорее всего, придется искать компромисс между безопасностью и удобством (например, устанавливать флаг schg не на все программы, а только на некоторые, и т.п.). Другой вариант - монтировать / и /usr в режиме "только для чтения". Но не следует забывать, что, приняв слишком сильные меры "безопасности", Вы рискуете не заметить, что Вашу систему пытались "взломать", что очень важно уже само по себе.

10.3.6. Проверка целостности файлов: исполнимых, конфигурационных, и других

Если Вы слишком сильно обезопасите свою систему, рано или поздно Вы неизбежно столкнетесь с определенными неудобствами в ее использовании. Например, установив с помощью утилиты chflags флаг schg на большинство файлов в / и /usr, Вы сильно уменьшаете вероятность обнаружить атаку на Вашу систему (хотя файлы, безусловно, будут защищены). Последний уровень защиты системы, являющийся, пожалуй, наиболее важным, это обнаружить атаку. Если попытка нападения на Вашу систему осталась незамеченной, эффективность остальных средств защиты сразу снижается, и, что самое неприятное, вселяет в Вас ложное чувство безопасности. Замедлить атаку - половина работы всей системы безопасности, чтобы дать возможность собрать как можно больше информации об атаке (и атакующем), и, в конечном итоге, поймать его "на месте преступления" во всеоружии.

Лучший способ определить вторжение - это заметить модификацию, удаление или неожиданное появление файлов. Лучше всего такой поиск проводить с удаленной (часто централизованной) машины с ограниченным доступом. Написание сценариев (скриптов) безопасности на машине с повышенной защищенностью и ограниченным доступом делает их недоступными для потенциальных взломщиков, и это очень важно. При этом, чтобы добиться максимального преимущества, Вам скорее всего потребуется разрешить достаточно широкий доступ к управляемым Вами системам, например, при помощи монтирования их файловых систем через NFS (в режиме "только для чтения") или с помощью парных ключей ssh, чтобы дать возможность доступа на удаленные системы по ssh. За исключением сетевого трафика, NFS является наименее детектируемым способом мониторинга файловых систем на удаленных машинах. Если Ваш сервер с ограниченным доступом соединен с контролируемыми Вами системам через свитч (switch), NFS чаще всего будет лучшим вариантом, однако, если, в случае хаба (hub), NFS может оказаться слишком небезопасным, так что лучшим решением будет воспользоваться ssh, даже с учетом того, что в этом случае мониторинг легче отследить извне.

Теперь, когда Вы имеете доступ (по крайней мере, на чтение) к клиентским машинам, которые должны контролироваться, Вам следует написать сценарии (скрипты) для собственно мониторинга. При использовании NFS, Вы можете воспользоваться утилитами find(1) и md5(1). Лучше всего ежедневно проверять MD5 суммы всех файлов, а конфигурационных (которые находятся в /etc и /usr/local/etc) даже чаще. В случае нахождения отличий суммы от контрольной следует немедленно оповестить системного администратора "подозрительной" машины. Хороший сценарий мониторинга безопасности должен также проверять все suid- и sgid-программы, их появление и исчезновение, а также отслеживать появление и исчезновение любых файлов на системных разделах, таких как / и /usr.

При использовании ssh все значительно усложняется: ведь Вам придется копировать сценарии мониторинга на удаленные системы (при помощи команды scp), что уже намного легче отследить, плюс Вам также придется копировать сами утилиты find(1) и md5(1), так как никогда нельзя быть полностью уверенными в том, что они не скомпрометированы. Более того, сам демон sshd на клиентской машине может быть подменен. Учитывая все это, можно сказать, что использование ssh оправдано при работе через небезопасные сетевые соединения, но значительно сложнее в эксплуатации.

Не стоит забывать о таких важных пользовательских файлах как .rhosts, .shosts, .ssh/authorized_keys и т.д., которые могли не быть включены в стандартную проверку MD5.

В случае очень больших объемов дисков для пользовательских нужд, проверка каждого файла на этих разделах будет занимать слишком много времени. Для решения этой проблемы можно выставить дополнительные флаги монтирования пользовательских разделов, такие как nodev и nosuid (подробнее смотрите mount(8)). Вне зависимости от этого, раз в неделю все же следует делать полную проверку, так как на данном этапе очень важно обнаружить любые попытки несанкционированного проникновения в систему.

Аккаунтинг (мониторинг) процессов (see accton(8)) - относительно нересурсоемкий способ, предоставляемый FreeBSD для анализа ситуации уже после вторжения. Он особенно полезен для установления точного пути, каким взломщик проник в систему (естественно, исходя из предположения что файлы аккаунтинга не были несанкционировано изменены).

Наконец, сценарии мониторинга безопасности должны анализировать файлы журнала, которые также лучше создавать в как можно более безопасной манере, например, вести их удаленно. После успешной атаки, взломщик постарается как можно быстрее вскрыть свое пребывание в системе, и системный журнал заинтересует его в первую очередь. Надежнее всего перенаправлять сообщения журнала на последовательный порт (к которому подключена другая машина) или принтер.

10.3.7. Паранойя

Немного паранойи никогда не повредит. Как правило, системный администратор может принимать какие угодно меры безопасности, не доставляя при этом неудобств для пользователей. Еще более важно то, что любые рекомендации, о которых Вы прочитаете в этом документе, не должны быть использованы "как есть". Применяя их, постарайтесь их хоть как-то изменить, иначе вы только поможете потенциальному взломщику, который, скорее всего, уже прочитал эту статью и может догадываться о Ваших действиях.

10.3.8. Атаки типа "отказ от обслуживания"

В этой секции мы расскажем об атаках на отказ от обслуживания. Такие атаки чаще всего являются удаленными (сетевыми). Хотя панацеи от в данном случае не существует, Вы можете по крайней мере защитить Ваши сервера от падения или просто приостановки их работы.

  1. Ограничение порождения новых процессов.

  2. Ограничение массовых атак (ICMP атаки, широковещательный ping и т.п.).

  3. Таблицы маршрутизации (на уровне ядра).

Обычная атака на отказ от обслуживания на процесс, порождающий свою копию при каждом новом запросе (через системный вызов fork(2)) пытается заставить процесс занять всю доступную память, процессы (места в таблице процессов ядра) и/или файловые дескрипторы пока машина не зависнет, не перегрузится или не перестанет отвечать на запросы. Inetd (смотрите inetd(8)) имеет средства, позволяющие противостоять атакам подобного типа. Следует отметить, что, даже если Вам удастся защитить систему от полной остановки или перезагрузки, нормальная работа атакуемого сервиса (или вообще всех) будет нарушена. Внимательно прочитайте страницу справочника inetd(8), обратив особое внимание на опции -c, -C и -R. Имейте в виду, что особым образом сгенерированные пакеты смогут обойти опцию -C, так что лучше всего использовать некоторую их комбинацию. Также, некоторые сервисы позволяют независимо контролировать подобные параметры (разумеется, только свои).

Sendmail имеет опцию -OMaxDaemonChildren, которая, как показывает практика, дает гораздо лучшие результаты, чем собственная же система ограничения загрузки (из-за инертности последней). Вам следует установить параметр MaxDaemonChildren при старте sendmail в достаточно высокое значение, чтобы справляться с ожидаемой нагрузкой, но не выше, чем физически способен Ваш сервер. Весьма разумно также запускать sendmail в режиме обработки очереди (опция -ODeliveryMode=queued, команда sendmail -q15m) отдельно от основного демона (sendmail -bd). Если же Вам требуется как можно более быстрая доставка, можно значительно понизить интервал проверки очереди (-q1m), но удостоверьтесь при этом, что значение MaxDaemonChildren достаточно для того, чтобы предотвратить последовательные сбои в работе почтовой системы.

Syslogd может быть атакован напрямую, и мы очень рекомендуем Вам использовать опцию -s всегда, когда это возможно, или опцию -a.

You should also be fairly careful with connect-back services such as tcpwrapper's reverse-identd, which can be attacked directly. You generally do not want to use the reverse-ident feature of tcpwrappers for this reason.

It is a very good idea to protect internal services from external access by firewalling them off at your border routers. The idea here is to prevent saturation attacks from outside your LAN, not so much to protect internal services from network-based root compromise. Always configure an exclusive firewall, i.e., "firewall everything except ports A, B, C, D, and M-Z". This way you can firewall off all of your low ports except for certain specific services such as named (if you are primary for a zone), ntalkd, sendmail, and other Internet-accessible services. If you try to configure the firewall the other way - as an inclusive or permissive firewall, there is a good chance that you will forget to "close" a couple of services or that you will add a new internal service and forget to update the firewall. You can still open up the high-numbered port range on the firewall to allow permissive-like operation without compromising your low ports. Also take note that FreeBSD allows you to control the range of port numbers used for dynamic binding via the various net.inet.ip.portrange sysctl's (sysctl -a | fgrep portrange), which can also ease the complexity of your firewall's configuration. For example, you might use a normal first/last range of 4000 to 5000, and a hiport range of 49152 to 65535, then block everything under 4000 off in your firewall (except for certain specific Internet-accessible ports, of course).

Another common DOS attack is called a springboard attack - to attack a server in a manner that causes the server to generate responses which then overload the server, the local network, or some other machine. The most common attack of this nature is the ICMP ping broadcast attack. The attacker spoofs ping packets sent to your LAN's broadcast address with the source IP address set to the actual machine they wish to attack. If your border routers are not configured to stomp on ping's to broadcast addresses, your LAN winds up generating sufficient responses to the spoofed source address to saturate the victim, especially when the attacker uses the same trick on several dozen broadcast addresses over several dozen different networks at once. Broadcast attacks of over a hundred and twenty megabits have been measured. A second common springboard attack is against the ICMP error reporting system. By constructing packets that generate ICMP error responses, an attacker can saturate a server's incoming network and cause the server to saturate its outgoing network with ICMP responses. This type of attack can also crash the server by running it out of mbuf's, especially if the server cannot drain the ICMP responses it generates fast enough. The FreeBSD kernel has a new kernel compile option called ICMP_BANDLIM which limits the effectiveness of these sorts of attacks. The last major class of springboard attacks is related to certain internal inetd services such as the udp echo service. An attacker simply spoofs a UDP packet with the source address being server A's echo port, and the destination address being server B's echo port, where server A and B are both on your LAN. The two servers then bounce this one packet back and forth between each other. The attacker can overload both servers and their LANs simply by injecting a few packets in this manner. Similar problems exist with the internal chargen port. A competent sysadmin will turn off all of these inetd-internal test services.

Spoofed packet attacks may also be used to overload the kernel route cache. Refer to the net.inet.ip.rtexpire, rtminexpire, and rtmaxcache sysctl parameters. A spoofed packet attack that uses a random source IP will cause the kernel to generate a temporary cached route in the route table, viewable with netstat -rna | fgrep W3. These routes typically timeout in 1600 seconds or so. If the kernel detects that the cached route table has gotten too big it will dynamically reduce the rtexpire but will never decrease it to less than rtminexpire. There are two problems:

  1. The kernel does not react quickly enough when a lightly loaded server is suddenly attacked.

  2. The rtminexpire is not low enough for the kernel to survive a sustained attack.

If your servers are connected to the Internet via a T3 or better it may be prudent to manually override both rtexpire and rtminexpire via sysctl(8). Never set either parameter to zero (unless you want to crash the machine :-). Setting both parameters to 2 seconds should be sufficient to protect the route table from attack.

10.3.9. Access Issues with Kerberos and SSH

There are a few issues with both kerberos and ssh that need to be addressed if you intend to use them. Kerberos V is an excellent authentication protocol but there are bugs in the kerberized telnet and rlogin applications that make them unsuitable for dealing with binary streams. Also, by default kerberos does not encrypt a session unless you use the -x option. ssh encrypts everything by default.

ssh works quite well in every respect except that it forwards encryption keys by default. What this means is that if you have a secure workstation holding keys that give you access to the rest of the system, and you ssh to an unsecure machine, your keys becomes exposed. The actual keys themselves are not exposed, but ssh installs a forwarding port for the duration of your login and if a attacker has broken root on the unsecure machine he can utilize that port to use your keys to gain access to any other machine that your keys unlock.

Мы рекомендуем Вам пользоваться ssh в комбинации с Kerberos combination with kerberos whenever possible for staff logins. ssh can be compiled with kerberos support. This reduces your reliance on potentially exposable ssh keys while at the same time protecting passwords via kerberos. ssh keys should only be used for automated tasks from secure machines (something that kerberos is unsuited to). We also recommend that you either turn off key-forwarding in the ssh configuration, or that you make use of the from=IP/DOMAIN option that ssh allows in its authorized_keys file to make the key only usable to entities logging in from specific machines.