Белые пятна в работе с SSH

    SSH — очень мощный и гибкий инструмент, но, как показывает практика, не все понимают, как он работает, и правильно его используют. Слово Secure входит в аббревиатуру SSH и является одним из ключевых аспектов протокола, но часто именно безопасности уделяется недостаточное внимание. В этой статье я хочу рассказать о нескольких типичных ошибках при работе с SSH, а также о моментах, о которых часто забывают.

    image

    Существует несколько способов аутентификации пользователя:

    1. По паролю — стандартный механизм, но не самый надежный, я не буду заострять на нем внимание
    2. По ключу — самый надежный способ аутентификации, но это при условии, если правильно его использовать
    3. По IP адресу — режим совместимости. Привожу просто для справки, сомневаюсь, что кто-то будет использовать его в трезвом уме.

    Асимметричное шифрование


    SSH использует ассиметричное шифрование, суть которого заключается в том, что есть два ключа: открытый и закрытый. Зашифровать сообщение можно открытым ключом, а расшифровать — закрытым. Закрытый ключ хранится в сохранности, а публичный доступен всем. Кроме того, подписать сообщение можно закрытым ключом, а проверить эту подпись — открытым.

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

    Кроме того, аутентификация пользователя может производиться при помощи этой самой пары ключей.

    Публичный ключ пользователя необходимо добавить в файл $HOME/.ssh/authorized_keys
    В этом файле может быть несколько публичных ключей — все они будут работать.

    image

    Защита от MITM атак


    MITM-атака — это вид атаки в криптографии, когда злоумышленник тайно ретранслирует и при необходимости изменяет связь между двумя сторонами, которые считают, что они непосредственно общаются друг с другом. Является методом компрометации канала связи, при котором взломщик, подключившись к каналу между контрагентами, осуществляет вмешательство в протокол передачи, удаляя или искажая информацию.

    Одним из основных нововведений во второй версии протокола стала встроенная защита от MITM-атак.

    image

    Суть защиты заключается в том, что каждая сторона должна убедиться, что с другой стороны находится именно тот, кто ожидается, а не злоумышленник.

    За MITM-защиту на стороне клиента (т.е. за то, чтобы определить, что ты подключаешься именно к своему серверу) отвечает параметр StrictHostKeyChecking и файл known_hosts.

    Публичные ключи серверов хранятся в файлах:
    $HOME/.ssh/known_hosts — пользовательский файл,
    /etc/ssh/ssh_known_hosts— системный файл.

    Каждая запись в known_hosts является строкой, содержащей поля и использующей пробел в качестве разделителя:

    1. одно или несколько имен серверов или IP-адресов, разделенных запятыми
    2. тип ключа
    3. открытый ключ
    4. любые дополнительные комментарии

    Именно здесь происходит «идентификация» сервера

    StrictHostKeyChecking=no|ask|yes 

    Значения могут быть следующие:

    • no — подключение произойдет в любом случае, максимум мы получим предупреждение о том, что соединение небезопасно. Часто можно встретить во всяких автоматических скриптах типа автотестов и автодеплоев, тут и кроется основная проблема — если скрипт отработает нормально, то пользователь (администратор) и не заметит неладное, а заражение/компрометация произойдет.
    • ask — спрашивать при новом подключении (значение по-умолчанию), если записи еще нет в known_hosts, и, в случае согласия с подключением добавляется новая запись
    • yes — выполнять проверку всегда, самый безопасный вариант, который настоятельно рекомендую использовать в автоматизированных системах. Подключение производится только при наличии записи в known_hosts

    Что нам это дает:

    1. При первом подключении мы можем определить, не представился ли сервером злоумышленник
    2. Гарантию, что при последующих подключениях к серверу не произойдет подмены добропорядочного сервера злоумышленником

    Если со вторым пунктом все понятно, то как быть с первым?

    Тут можно выделить как минимум три варианта:

    1. Довериться случаю и согласиться на добавление нового ключа сервера
    2. Добавить флаг VisualHostKey=key, при этом клиент отобразит визуальное представление открытого ключа сервера, это представление уникально и облегчит запоминание ключа
    3. Флешка или открытые источники: это вариант для параноиков, однако, если компрометация системы может стоить десятков тысяч долларов, то это не лишне.

    image
    Использование визуализации открытого ключа

    image
    Если открытый ключ изменился

    Сообщение о смене ключа может происходить по нескольким причинам:

    1. Изменился адрес сервера
    2. Изменился ключ сервера, например, при переустановке системы
    3. MITM атака

    Из моей практики 1 и 2 варианты в сумме стремятся к 100%, но и пункт 3 нельзя исключать тоже.

    Вот пример лайфхака, который объединяет в себе веб- и ssh-подходы

    curl https://server.com/ssh_fingerprint | tee -a ~/.ssh/known_hosts

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

    ssh-keyscan example.com

    Однако, в первом варианте мы дополнительно проверяем подлинность полученного ключа при помощи HTTPS.

    В критически важных CI/CD использование комбинации

    ssh-keyscan example.com >> ~/.ssh/known_hosts

    во время инициализации совместно с

    StrictHostKeyChecking=yes

    позволит надежно защититься от MITM-атак.

    Множество ключей


    Часто можно встретить такую картину:

    image

    Для каждого сервера на клиенте сгенерированы свои пары ключей, и в $HOME/.ssh/ творится полнейший беспорядок.

    С одной стороны, это безопаснее, нежели использование одной пары клиентских ключей — при компрометации одного ключа становится скомпрометированой только одна система, а не все, с которыми работал пользователь.

    Однако, как показывает практика, этим руководствуются пользователи в последнюю очередь, зачастую они просто выполняют все инструкции по шагам:

    
    ssh-keygen
    ...
    ...
    

    Иногда я наблюдал ситуацию, когда одни ключи перетирались другими без какой либо мысли в голове.

    image

    Напомню: если вы гонитесь параноидально за безопасностью, то достаточно иметь одну пару ключей, но при этом ее надо правильно защищать.

    Кстати, о защите ключей


    image

    Расценивайте свой приватный SSH-ключ, как ключ от квартиры, где деньги лежат. Более беспечного отношения, чем к SSH-ключам, я не видел. Если серьезность сохранения паролей пользователи еще более или менее понимают, то о том, что никому нельзя давать доступ к приватному ключу, задумываются единицы.

    Что тут можно выделить:

    1. Хранить ключ надо в безопасном месте, в идеале это шифрованное хранилище
    2. Никому не предоставлять доступ к ключу, скомпрометированный ключ компрометирует все системы где разрешен доступ по нему
    3. Шифрование приватного ключа увеличивает безопасность, т.к. при потере ключа необходимо еще получить пароль для его дешифрации, либо потратить время и вычислительные мощности на его взлом.
    4. Не забывать про установку корректных прав на файл ключа 400, кстати, это распространенная ошибка, когда клиент отказывается использовать ключ

    Изменить или добавить пароль можно при помощи команды

    ssh-keygen -p

    SSH-Agent


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

    1. Скопировать свой приватный ключ на сервер
    2. Сгенерировать новую пару ключей и прописать ее в качестве варианта доступа
    3. Использовать ssh-agent

    Из моей практики, пользователи в 90% случаев выбирают первые два варианта, а, как мы уже говорили: нет ничего более страшного, чем компрометация ключа.

    Я же рекомендую воспользоваться третьим способом

    image

    SSH-agent хранит секретные ключи и, когда нужно, пользуется ими. Программа (например, ssh), когда ей понадобится воспользоваться секретным ключом, не делает этого сама, а обращается к ssh-agent 'у, который, в свою очередь, уже сам пользуется известными только ему данными о секретных ключах. Таким образом, секретные ключи не разглашаются никому, даже программам, принадлежащим самому пользователю.

    Команда ssh-agent создает файл-сокет с именем /tmp/ssh-XXXXXXXX/agent.ppid, через который осуществляется взаимодействие с агентом. Всем дочерним процессам агент при помощи переменных окружения SSH_AUTH_SOCK (в которой хранится имя файла-сокета) и SSH_AGENT_PID (в которой хранится идентификатор процесс агента) сообщает информацию о том, как с ним можно связаться.

    Агент выдает информацию в виде, удобном для использования командным интерпретатором.

    SSH_AUTH_SOCK=/tmp/ssh-XXt4pHNr/agent.5087; export SSH_AUTH_SOCK;
    SSH_AGENT_PID=5088; export SSH_AGENT_PID;
    echo Agent pid 5088;

    При указании ключа -c агент использует синтаксис C Shell. По умолчанию (и при явном указании ключа -s) используется синтаксис Bourne Shell. Эти переменные следует установить в текущем командном интерпретаторе, поэтому обычно вызов ssh-agent комбинируется с командой eval.

    $ eval `ssh-agent`
    Agent pid 5088

    Агент работает до тех пор, пока не будет явно завершен сигналом либо вызовом

    $ ssh-agent -k

    Список известных агенту секретных ключей можно посмотреть той же командой ssh-add с ключом командной строки -l. Команда сообщит и отпечаток для каждого ключа.

    $ ssh-add -l
    1024 46:88:64:82:a7:f9:aa:ea:3b:21:9e:aa:75:be:35:80 /home/user/.ssh/id_rsa (RSA)

    Пример работы с ssh-agent можно увидеть ниже

    image

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

    Локальный конфиг


    Если бы каждый раз, когда я наблюдал за набором строки вида

    ssh -p 2022 user@server.com -o StrictHostKeyChecking=yes -i ~/.ssh/server.pem

    мне давали рубль, то я бы уже давно жил в Сочи.

    На локальной машине можно найти файл $HOME/.ssh/config

    Host server
    	User root
    	HostName 192.168.0.3
    	IdentityFile ~/.ssh/server.pem
    
    Host example.com
    	User admin
    	IdentityFile ~/.ssh/production.pem
    

    Соответственно, можно прописать большинство параметров для хоста в этот файл и не вводить каждый раз.

    Как еще можно улучшить безопасность


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

    Можно выделить несколько решений:

    • Задать очень сложный пароль, который будет сложно подобрать и хранить его в защищенном месте. Это позволит иметь альтернативный способ входа.
    • Удалить пароль совсем, но тогда могут возникнуть проблемы с sudo и прочим
    • Запретить на сервере аутентификацию по паролю, собственно, как правило, для пользователя root это и запрещается. Остается запретить для всех пользователей.

    PasswordAuthentication no в /etc/ssh/sshd_config

    2. Еще можно запретить использование ssh v1
    Protocol 2 в /etc/ssh/sshd_config

    3. И обязательно закрывать на фаерволе доступ к SSH для ненадежных источников

    4. Раньше бы я посоветовал сменить порт со стандартного 22 на другой, но наблюдая за тем, сколько бывает попыток авторизоваться на нестандартных портах я могу сказать однозначно: это не является защитой от взлома.

    5. Если уже рассматривать подбор пароля, то не лишним будет настроить fail2ban для того, чтобы ограничить попытки подбора пароля.

    6. В протоколе и библиотеках периодически находят проблемы и уязвимости, поэму тут работает универсальный подход — следите за обновлениям ПО и старайтесь регулярно устанавливать как минимум заплатки безопасности.

    7. ssh-agent безопасней, чем просто копирование файлов, однако, его тоже можно взломать, например, root пользователь на сервере. Поэтому рекомендуется включать форвардинг только в тех случаях, когда это необходимо.

    Выводы


    Читайте документацию, в ней все написано, как бы это пошло не звучало.
    SSH очень безопасный протокол, но, зачастую, именно человеческий фактор является основным источником проблем — следите за безопасностью.

    Использованные материалы
    Защита с помощью SSH ключей
    Управление ключами SSH с помощью агента

    Lodoss Team

    59,25

    Сложные системы, мобильные приложения, дизайн и AR

    Поделиться публикацией

    Похожие публикации

    Комментарии 53
      +1
      а где комментарий о том что в заголовке опечатка?
        +1
        Он достается вам ;-)
        Спасибо за замечание, исправили.
        0
        Но часто бывает такая ситуация, когда необходимо со своим ключем перейти на сервер и там уже им воспользоваться. Существует три решения данной задачи

        Если ssh-agent запускается на удаленном сервере то четыре.
        Локальный ssh-agent + ForwardAgent yes
          0
          Ну по факту это развитие 3 пункта: проброс ssh-agent по цепочке
            0
            Не согласен. Кидать свой приватный ключ на удаленный сервер — рисково. Пробрасывать с локальной машины — безопасно. Большая разница.
              0
              Мне кажется, что мы друг друга не поняли.
              Локальный ssh-agent + ForwardAgent yes — я это и указывал

              А цепочку можно построить так:
              $ ssh-add
              $ ssh -A server
              connected
                $ ssh-add # будет добавлен ключ из первого шага
                $ ssh -A server2
                connected
                  $ ssh-add # будет добавлен ключ из первого шага
                  $ ssh -A server3 

                +1
                Пробрасывать ssh-agent — настолько же не безопасно, насколько и кидать приватный ключ на удаленный сервер. root на сервере может как утянуть файлик с приватным ключом, так и утянуть сокет ssh-agent. И защиты от этого нет.
                  +1
                  он будет иметь доступ к ключу ровно на время сеанса, и не более. В случае прокидывания ключа — утекает сам файл (который может быть запаролен, и все же).
                    +2
                    В очень большом проценте случаев этого достаточно, чтобы, воспользовавшись агентом, добраться до машины пользователя (где этот же ключ лежит в authorized_keys) и дальше с правами пользователя выполнить что угодно:
                    — стянуть приватный ключ, если он не запаролен
                    — прописать свой ключ в authorized_keys и ходить на пользовательскую машину пользоваться уже локальным агентом
                    — запустить любые туннели или сервисы
                      0
                      Вообще вроде как совсем у малого процента пользователей внешний айпи ведущий на их машину. Еще меньше пользователей которые прописывают свой же ключ в authorized_keys. К примеру на рабочем ноутбуке у меня такого файла нет.
                        0
                        У достаточно большого, не статический, но внешний.
                        Не могу сейчас найти, но был даже публичный сервис, который предлагал всем желающим ssh-нуться к ним и показывал проблемы и уязвимости вашего клиентсвкого конфига. Они как раз статистику собирали.
              0

              А там им воспользоваться зачем? Залогиниться на локальный (для удалённого сервера) узел?
              Лично мне хватает пары записей в конфиге:


              host server1
                User vasya
                Port 9222
              
              host server2
                User petya
                ProxyCommand ssh server1 -W %h:%p

              Открытый ключ прописан на обоих серверах в authorized_keys.
              И всё, на server2 заходится в один клик, без всяких манипуляций с агентами и т.д.
              Это что, способ 4?

                +1

                Кто скажет насколько безопасен данный способ с ProxyCommand, если на промежуточном server1 сидит злодей под рутом и жаждет расширить сферу своего влияния на тебя.
                Как я понимаю это получше, чем агента прокидывать, но безопасно ли проделывать такое с не доверенными хостами?

                  –1
                  Специально не пишу ответ, читайте (раздел «проброс авторизации»).

                  тыц
                    +1

                    Дык приватный ключ никуда не пробрасывается.
                    ProxyCommand логинит на первый сервер по публичному ключу. И через него на второй — по тому же публичному ключу. Приватный остаётся у себя и никуда не пробрасывается.
                    Подозреваю, что это ничуть не более опасно, чем просто дать злодею напрямую прослушивать трафик (который зашифрован).

                +1
                > Как еще можно улучшить безопасность
                Отказаться уже наконец в 2018-м году от доступа по ключам и перейти к доступу по сертификатам. Например github.com/gravitational/teleport (ну или убера есть похожая штука)
                А ещё приватные ключи можно хранить например на аппаратных кошельках криптовалют (например ledger nano s это умеет).
                  0
                  Чем данный подход принципиально отличается от традиционного?
                  У сертификатов тоже вроде приватная часть есть.
                    0
                    Принципиально он отличается тем что
                    1. на все машины не надо разливать ключи тех кому туда надо ходить
                    2. у ключей появляется срок действия и понятная логика их обновления
                  +4
                  Можно еще добавить о ciphers-ах, MACs, KexAlgorithms и о важности отключения древних.
                    +4
                    Опять двадцать пять! Очередная статья об одном и том же.
                    Расскажите, как вы будете управлять ключами десяти тысяч сотрудников на трёх сотнях тысяч серверов. Доступы у сотрудников добавляются и удаляются сотнями в день, сервера добавляются и исчезают тоже сотнями в день. Подсказка:
                    Читайте документацию, в ней все написано, как бы это пошло не звучало.
                    Вот это будет интересный пост. Заранее спасибо.
                      0
                      Тоже интересно. Любое увольнение должно инвалидировать ключ, а как это сделать — непонятно.
                        0
                        в рамках данной статьи удалить его из $HOME/.ssh/authorized_keys
                        А в энтерпрайзе другие подходы
                          +5
                          Хранить ключи в LDAP`е же. И использовать pam_ldap.
                            0
                            Это кстати так себе подход. ЛДАП ложится и всё становится плохо.
                            0
                            использовать вещи вроде chef/ansible.
                              0
                              Использовать Hashicorp Vault в связке со LDAP например, для генерации SSH OTP. В таком случае сертификаты вообще не нужны. Правда на хостах должен стоять ssh-helper, так что решение не для всех подойдет.
                              0

                              Я для этого писал кучу скриптов и хакал ssh_agent :) Но у меня были тепличные условия: 50 сотрудников делились на примерно 5 груп и коннектились на сервера клиентов (~1k машин) на 2-3 аккаунта.

                                +1
                                асскажите, как вы будете управлять ключами десяти тысяч сотрудников на трёх сотнях тысяч серверов.

                                PAM?

                                На хабре была статья про использование промежуточных SSH серверов, через них уже ппоизводился выход на целевые сервера. Опять же, если рассматривать тысячи серверов, то это уже enterprise решения и выходит за рамки этой статьи

                                У меня опыт был использования PAM + ldap для управления пользователями
                                0
                                Пф. Тоже мне белые пятна.
                                Белое пятно — что root на сервере, куда вы пришли с форвардом ключа, может реюзнуть сокет и фактически воспользоваться ключем, пока открыта ваша сессия.
                                  –1
                                  скомпрометированный root на машине без selinux и прочих защит (да и с ними тоже) — это компрометация всей системы, «ЭТО ФИАСКО, БРАТАН» (с)

                                  Дальше о безопасности говорить уже смысла нет
                                    0
                                    Бла-бла-бла.
                                    Вы не подумали о ситуации, когда вполне себе штатный рут захочет воспользоваться ключем? Или когда машина вовсе чужая и вы не можете быть в курсе, что она похакана?

                                    Форвард ключа штука опасная, а все её советуют включить по дефолту.
                                      0
                                      Какие варианты?
                                      Я могу предложить только временные ключи на время сессии
                                        +2
                                        Как минимум, не включать форвард на все хосты, а заходить с форвардом только по необходимости.
                                          0
                                          Пожалуй, стоит это вынести в пункты улучшения безопасности
                                      +1
                                      Взлом одной из машин, через которую вы прошли, означает компрометацию всех серверов в которые вы могли зайти с помощью своего агента. Действительно, если вы пробросили агента, дальше о безопасности говорить нет смысла.
                                      Есть смысл говорить о том, стоит ли вообще пробрасывать агента куда-либо. Я считаю, что не стоит этого делать, от слова совсем.
                                    +1
                                    А еще ключи можно прописывать в файл authorized_keys2, а не только в authorized_keys, ключи будут читаться из обоих файлов.
                                    Если вы накладываете на файл authorized_keys immatable атрибут, что можно сделать только рутом, то не забывайте делать authorized_keys2 и его тоже защищать.
                                      0
                                      authorized_keys2 ещё в 2001 году был с пометкой deprecated, но проверю на своих системах его поддержку
                                        +1
                                        Debian 9, man sshd
                                        AUTHORIZED_KEYS FILE FORMAT
                                        AuthorizedKeysFile specifies the files containing public keys for public key authentication; if this option is not specified, the default is ~/.ssh/authorized_keys and ~/.ssh/authorized_keys2.


                                          +2
                                          Проверил еще раз, работает.
                                        0
                                        А что мешает сделать первую сесиию на удаленный хост с пробросом удаленного порта 2го хоста на локальный сервер и потом просто зайти на этот(локальный, проброшенный) порт?
                                        Никаких тебе агентов и у рута на удаленной машине нет доступа к агенту.
                                          0

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


                                          $ git pull

                                          Или scp или rsync да и много чего ещё (на самом деле нет).
                                          Можно использовать парольную аутентификацию или извращаться, а можно воспользоваться агентом.
                                          Часто встречаю ситуацию когда на серверах разработки код стянут по https, ты вызываешь


                                          git pull

                                          А у тебя GIT спрашивает пароль какого-то разраба. Почему нельзя было склонить по SSH? Секурность? Нет — глу… незнание

                                          +1

                                          По поводу компрометации сокета ssh-agent:
                                          Например, под Windows, есть агент, встроенный в KeePass (в виде плагина) и он выдает запрос на каждое использование ключей.


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

                                            0
                                            А для линя\мака можно самому такой скрипт написать или взять готовый github.com/theseal/ssh-askpass
                                            0
                                            На локальной машине можно найти файл $HOME/.ssh/config
                                            А если бы мне давали рубль за каждый случай «я забыл пароль/адрес/параметр» я жил бы недалече от вас.
                                            $HOME/.ssh/config и ключи это удобно но это сильно провоцирует забывчивость.
                                              +1

                                              Ещё в конфигурационном файле вы можете использовать маски, чтобы не писать много раз одно и то-же:

                                                +1
                                                Например:
                                                Host vagrant
                                                    HostName 127.0.0.1
                                                    User vagrant
                                                    IdentityFile ~/.vagrant/machines/default/virtualbox/private_key
                                                    Port 2222
                                                Host * !bitbucket.org !10.2.* !10.1.*
                                                    User ubuntu
                                                    IdentityFile ~/.ssh/id_rsa
                                                Host *
                                                    StrictHostKeyChecking=no
                                                    UserKnownHostsFile=/dev/null
                                                  0
                                                  Думал расписать более подробно конфиг, пожалуйста, стоило это сделать. Надо будет дополнить.
                                                +1
                                                В критически важных CI/CD использование комбинации
                                                ssh-keyscan example.com >> ~/.ssh/known_hosts
                                                Во время инициализации совместно с
                                                StrictHostKeyChecking=yes
                                                Позволит надежно защититься от MITM-атак

                                                Это же совсем не так, man ssh-keyscan говорит:


                                                SECURITY
                                                     If an ssh_known_hosts file is constructed using ssh-keyscan without verifying the keys, users will be vulnerable to man in the middle attacks. On the other hand, if the security model allows such a risk, ssh-keyscan can help in the detection of tampered keyfiles or man in the middle attacks which have begun after the ssh_known_hosts file was created.

                                                Т.е. без верификации ключа ты уязвим к MITM.
                                                Или я что-то недопонимаю в вашем высказывании?

                                                  0
                                                  Или я что-то недопонимаю в вашем высказывании?

                                                  Ключевое слово было инициализации, т.е. когда вы точно уверены, что MITM атаки нет, а если уверенности нет, то можно использовать отдельное хранилище или пример с https, что я приводил выше.
                                                  +1

                                                  А ещё SSH-ключи можно записывать на хардварные носители, чтобы их не держать на машинах: https://evilmartians.com/chronicles/stick-with-security-yubikey-ssh-gnupg-macos

                                                    +1
                                                    Спасибо за статью. Приватные ключи еще можно держать в менеджерах паролей. Например KeepassXC с версии 2.3.2 прикидывается ssh-agent на всех платформах (правда на Mac-ах требует использования упомянутого выше ssh-askpass
                                                      0
                                                      На маке вместо KeepassXC решил использовать MacPass — интерфейс нативный и функционал неплох.
                                                      0
                                                      Читаешь такой статью, думаешь как интересно, а потом видишь слово «дешифрация» в тематической статье и охватывает настоящий фейспалм.

                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                      Самое читаемое