Рубрики
Блог

Об опасности использования eval в bash-скриптах.

Преамбула
В недавнем примере я допустил, что называется, «детский мат», грубую и далеко неочевидную новичку ошибку, о которой, впрочем, все знают. Но прописные истины неплохо повторять, так что не стоит кидаться на эту заметку с криками «боян!!!111пыщ». Для кого-то может и боян, а кому-то это может быть неизвестно. Или вот я, зная в теории об ошибке, все равно ее допустил, и даже не сразу понял, что я так грубо наебался, и мой скрипт стал небезопасным от слова «совсем».
Исходная задача
Имелся следующий набор данных в виде форматированного текста (таблицы в текстовом файле): verb666,Misha Verbitsky,+415314499922,42,11:00-16:00 ktvs421,Vasiliy Kotov,+415314499966,77a,00:00-06:00 dkldn89,Dmitry Kaledin,+415314499949,65b,22:00-00:00 vfurry1,Veniamin Furman,+415314499900,99,12:20-19:25 tpunk56,Tolik Punkoff,+415314499911,59,00:00-11:00 Исходная задача состояла в том, чтобы раскидать вывод awk в переменные bash, так, чтоб значение из соответствующей колонки таблицы попало в указанную переменную bash, и изначальный код был таким:
# ... цикл по строкам
for TMPSTRING in $(cat "testfile.txt")
do
    eval $(echo "$TMPSTRING"|awk -F "," '{print "LOGIN=" dq $1 dq;
    print "FULLNAME=" dq $2 dq; print "PHONE=" dq $3 dq; print"ROOM=" dq $4 dq;
    print "WORKTIME=" dq $5 dq}' dq='"')
# ... делаем что-то с данными в переменных
done
# ...
Т.е. тут мы сначала получаем строчку текстового файла, передаем ее awk, awk, в свою очередь, возвращает строки: ... LOGIN="vfurry1" FULLNAME="Veniamin Furman" PHONE="+415314499922" ROOM="99" WORKTIME="12:20-19:25" ... Похоже на команду присваивания значений переменным. Да так оно и есть! Далее, полученная строка передается команде eval.
Опасность eval
В чем опасность eval? Да в том, что эта команда преобразует любую переданную ей строку в команду или команды bash, и немедленно их выполняет. Любую, абсолютно любую строку. И тут возникает ошибка безопасности. Мы получаем данные из внешнего источника. Это может быть файл с внешнего носителя, или скачанный из Интернета. В нем в худшем случае, который всегда надо держать в голове, работая с внешними источниками данных, может содержаться все, что угодно. И злоумышленнику не надо получать доступ к скрипту, достаточно изменить входные данные: verb666,Misha Verbitsky,+415314499922,42,11:00-16:00 ktvs421,Vasiliy Kotov,+415314499966,77a,00:00-06:00 dkldn89,Dmitry Kaledin,+415314499949,65b,22:00-00:00 vfurry1,Veniamin Furman `rm -rf ./testdir`,+415314499900,99,12:20-19:25 tpunk56,Tolik Punkoff,+415314499911,59,00:00-11:00 Тут, после Veniamin Furman вписана команда удаления директории ./testdir в текущем каталоге, а могло быть вписано и $HOME или даже /. Строки, взятые в двойные кавычки, интерпретируются bash’ем не только, как строки с чисто тестовыми данными. В двойных кавычках можно и нужно сделать подстановку, если это возможно. А символы `` (обратные кавычки) значат, что нужно выполнить команду внктри них, а потом подставить результат в то место, где указано выражение в обратных кавычках. Таким образом, команда FULLNAME="Veniamin Furman `rm -rf ./testdir`" выполнится так: 1. В переменную записывается строка Veniamin Furman 2. Выполняется команда rm -rf ./testdir 3. Вывод команды дописывается в переменную Соответственно, каталог ./testdir в процессе будет удален.
Общие рекомендации
Всегда держите в голове, что данные внутри eval$() будут исполнены bash’ем, как код (команды оболочки).
Исправление
1. Заменить двойные кавычки (") на одинарные ('). Строки в одинарных кавычках будут интерпретированы просто как текстовые последовательности, и без всякого выполнения и изменения записаны в переменные, так как есть: eval $(echo "$TMPSTRING"|awk -F "," '{print "LOGIN=" sq $1 sq; print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq; print "WORKTIME=" sq $5 sq}' sq="'") Вывод: vfurry1 Veniamin Furman `rm -rf ./testdir` +415314499900 99 12:20-19:25 2. Но и это еще не конец, злоумышленник может обмануть и одинарные кавычки, обернув свою команду в …одинарные кавычки: vfurry1,Veniamin Furman '`rm -rf ./testdir`',+415314499900,99,12:20-19:25 Тогда выполнение будет происходить так. Когда очередь дойдет до FULLNAME awk добросовестно выведет: FULLNAME='Veniamin Furman '`rm -rf ./testdir`' eval же это также добросовестно интерпретирует. В $FULLNAME будет записано Veniamin Furman, далее закроется одинарная кавычка, т.е. больше ничего в переменную писаться не будет, а далее выполнится rm -rf ./testdir. Решение таково — удалить или заменить на что-либо безобидное, одинарные кавычки из входной строки перед передачей ее awk. Можно удалить sed‘ом: sed -e 's~'\''~~g' Код целиком: eval $(echo "$TMPSTRING"|sed -e 's~'\''~~g'|awk -F "," '{print "LOGIN=" sq $1 sq; print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq; print "WORKTIME=" sq $5 sq}' sq="'") Вывод зловредной сткроки будет: vfurry1 Veniamin Furman `rm -rf ./testdir` +415314499900 99 12:20-19:25
Тестовый пример
Файлы: maketestdir — скрипт для создания тестовых каталогов. testfile.txt — Тестовый файл. Ошибку безопасности можно обойти, заменив двойные кавычки одинарными в коде. testfile2.txt — Замена двойных кавычек одинарными не поможет. Необходимо дополнительно удалять одинарные кавычки во входной строке. wrongcode — файл без исправлений кода (в коде используются двойные кавычки) wrongcode2 — только первое исправление (в коде используются одинарные кавычки) goodcode — окончательно исправленный код. Вызов скриптов: <скрипт> <тестовый файл> Например: wrongcode testfile2.txt Пример на GitHub]]>

Рубрики
Блог

Записать вывод awk в несколько переменных bash

Преамбула
Предположим, у нас есть некоторая таблица в виде файла CSV с набором полей, например таких Login,FullName,Phone,Room,WorkTime и разделителем полей , (запятая): verb666,Misha Verbitsky,+415314499922,42,11:00-16:00 ktvs421,Vasiliy Kotov,+415314499966,77a,00:00-06:00 dkldn89,Dmitry Kaledin,+415314499949,65b,22:00-00:00 vfurry1,Veniamin Furman,+415314499900,99,12:20-19:25 tpunk56,Tolik Punkoff,+415314499911,59,00:00-11:00 Нужно вытащить из нее некоторые данные, и далее как-либо обработать. Вытащить данные можно с помощью awk, используя оператор print, но возникает вопрос, как передать данные обратно в bash. Предположим, что заголовок удален, в файле остались только данные.
awk и eval
В bash есть встроенная команда eval, преобразующая переданную ей строку в команду или набор команд оболочки, и запускающая ее на выполнение. Этим и воспользуемся. 1. Организуем цикл, в котором будем производить обработку данных: IFS_=$IFS IFS=$'\n' for TMPSTRING in $(cat "demotable.txt") do #тут будет код done IFS=$IFS_ Перед циклом я подправил переменную $IFS содержащую глобальные разделители, в нее, в частности, «смотрят» операторы циклов, чтобы определить, где начинается следующий элемент. По умолчанию переменная $IFS содержит пробел, табуляцию и перевод строки, но поскольку у нас есть данные с пробелом, то это не подходит, цикл будет работать неверно. Потому сохраняем старое значение во временную переменную, устанавливаем новое значение в перевод строки (\n). После цикла возвращаем значение на место. В цикле организуем разбор данных: echo "$TMPSTRING"|awk -F "," '{print "LOGIN=" $1; print "FULLNAME=" $2 print "PHONE=" $3; print "ROOM=" $4; print "WORKTIME=" $5 }' Если запустить скрипт сейчас, то он выведет следующее: LOGIN=verb666 FULLNAME=Misha Verbitsky PHONE=+415314499922 ROOM=42 WORKTIME=11:00-16:00 Т.е. уже похоже на присваивание значений переменным bash, но есть проблема. Если мы сейчас скормим вывод awk eval‘у, то получим ошибку, например такую: ./awk2vars01: line 8: Verbitsky: command not found А если бы и не получили, то в переменных могла бы оказаться всякая ерунда, строки необходимо экранировать кавычками.
awk print и вывод кавычки
Кавычки для оператора print awk являются служебными символами, в двойные кавычки берутся строковые литералы, т.е. те строки, которые нужно вывести без изменений, как например, "LOGIN=" в коде выше, а в одинарные — вся программа awk. Экранирование (\" или \') в операторе print приведет к ошибке. Решение — завести внутреннюю переменную awk, содержащую кавычку, и печатать ее в нужном месте:
echo "$TMPSTRING"|sed -e 's~'\''~~g'|awk -F "," '{print "LOGIN=" sq $1 sq;
    print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq; print"ROOM=" sq $4 sq;
    print "WORKTIME=" sq $5 sq}' sq="'"
Поскольку данные строки далее будут переданы в eval и обработаны как команды оболочки, то необходимо позаботиться о безопасности, и использовать только одинарные кавычки, а также удалять одинарные кавычки из входных строк, при передаче их awk: Об опасности использования eval в bash-скриптах. Копия Вывод: LOGIN='verb666' FULLNAME='Misha Verbitsky' PHONE='+415314499922' ROOM='42' Теперь можно обернуть все это в eval, чтобы раскидать результат работы awk по переменным.
eval $(echo "$TMPSTRING"|sed -e 's~'\''~~g'|awk -F "," '{print "LOGIN=" sq $1 sq;
    print "FULLNAME=" sq $2 sq; print "PHONE=" sq $3 sq;
    print"ROOM=" sq $4 sq;
    print "WORKTIME=" sq $5 sq}' sq="'")
В демо-скрипте я просто вывожу данные на консоль, в реальном скрипте, что понятно, можно делать обработку данных в переменных bash. echo "Login: $LOGIN" echo "Full name: $FULLNAME" echo "Phone: $PHONE" echo "Room: $ROOM" echo "Work time: $WORKTIME" Вывод: Login: verb666 Full name: Misha Verbitsky Phone: +415314499922 Room: 42 Work time: 11:00-16:00 ... Скрипты полностью можно посмотреть на GitHub [lj-cut text=" Немного об оптимизации " unicancor="optimisation"]
Немного об оптимизации
На самом деле циклы в bash работают довольно медленно, и на реальной производственной задаче такой код довольно сильно тормозил, отрабатывая на таблице в 100 записей примерно 1 секунду:
IFS_=$IFS
IFS=$'\n'
J=0
for TMPSTRING in $(cat "data/servers")
do
let "J+=1"
#extract data
eval $(echo "$TMPSTRING"|sed -e 's~'\''~~g'|awk -F "," '{print "HOST_NAME="dq $1 dq;
     print "IP="dq $2 dq;print "SCORE=" dq $3 dq;print "PING=" dq $4 dq;
     print "SPEED=" dq $5 dq;print "COUNTRY=" dq $6 dq;
     print "COUNTRYSHORT=" dq $7 dq; print "NUMVPNSESSION=" dq $8 dq;
     print "UPTIME=" dq $9 dq;print "TOTALUSERS=" dq $10 dq;
     print "TOTALTRAFFIC=" dq $11 dq;print "LOGTYPE=" dq $12 dq;
     print "OPERATOR=" dq $13 dq;print "MSG=" dq $14 dq }' dq='"')
MENUSTR="\"$J $HOST_NAME($IP,$COUNTRYSHORT)\" \
\"$SCORE|$PING|$SPEED|$NUMVPNSESSION\" \
\"Uptime:$UPTIME Users:$TOTALUSERS Traffic:$TOTALTRAFFIC Log:$LOGTYPE\" \\"
echo "$MENUSTR" >> "vpnmenu.txt"
done
IFS=$IFS_
Его удалось оптимизировать до такого, без использования цикла и переменных bash:
cat data/servers | awk -F, \
'{
    HOST_NAME       = $1;
    IP              = $2;
    SCORE           = $3;
    PING            = $4;
    SPEED           = $5;
    COUNTRY         = $6;
    COUNTRYSHORT    = $7;
    NUMVPNSESSION   = $8;
    UPTIME          = $9;
    TOTALUSERS      = $10;
    TOTALTRAFFIC    = $11;
    LOGTYPE         = $12;
    OPERATOR        = $13;
    MSG             = $14;
    printf \
    "\"%i %s(%s,%s)\" \"%s|%s|%s|%s\"" \
    " \"Uptime:%s Users:%s Traffic:%s Log:%s\" \\\n",
    ++j, HOST_NAME, IP, COUNTRYSHORT, SCORE, PING, SPEED, NUMVPNSESSION,
    UPTIME, TOTALUSERS, TOTALTRAFFIC, LOGTYPE;
}' > vpnmenu.txt
Но в данном случае мне просто повезло, нужно было перекодировать данные из одного формата в другой.[/lj-cut] ]]>

Рубрики
Блог

Получение строки с определенным номером из файла в Linux

С помощью sed
sed -n 5p /path/to/file Получить 5 строку из текстового файла с путем /path/to/file
С помощью awk
awk 'NR == 5' /path/to/file На мой взгляд sed‘ом несколько проще, потому что не нужны лишние движения для подстановки переменных shell/bash-скрипта: TEXTFILE="/etc/group" STRNO=5 TMPSTRING=`sed -n "$STRNO"p "$TEXTFILE"`]]>

Рубрики
Блог

Open-AL library for Slackware (библиотека OpenAL для Slackware)

openal-soft-1.17.1.tar.bz2 SlackBuild: OpenAL.tar.gz Он, кстати, почему-то отрабатывает не до конца, библиотеку собирает, а готовый пакет нет. Готовый пакет: openal-soft-1.17.1.txz]]>

Рубрики
Блог

libpng-12 legacy for slackware (libpng-12 legacy для Slackware)

Библиотека libpng для Slackware, качать здесь (готовый пакет)]]>

Рубрики
Блог

Dialog — псевдографический оконный интерфейс в bash-скриптах

Краткий мануал на английском Копия в PDF Примеры скриптов на GitHub Скачать в архиве]]>

Рубрики
Блог

Установка и настройка I2P в Slackware Linux

Преамбула I2P это такая скрытая пиринговая сеть, подробнее можно узнать в Википедии или на официальном сайте. В отличии от TOR, она обеспечивает не анонимный доступ в Интернет, а доступ к собственным ресурсам (сайтам, файлообменникам и т.д.), скрытым из обычной сети. Гейты в «большой» Интернет в ней есть, но они кривые, косые и пользоваться ими не рекомендуется. Расскажу, как ее установить и настроить на Slackware, заодно разрешив пользователям из локальной сети также получать к ней доступ.

Установка Java
Клиент сети написан на Java, потому ее надо сначала установить. На официальном сайте есть рекомендации о необходимой версии: Java Runtime Version 7 or higher. (Oracle, OpenJDK, or IcedTea Java Version 7 or 8 recommended. Сама установка довольно проста: 1. Идем, например сюда 2. Скачиваем jdk-8u162-i586-2gds.txz 3. Устанавливаем стандартным образом: installpkg jdk-8u162-i586-2gds.txz 4. Добавляем в автозагрузку (если вы не сделали отдельного скрипта для запуска сервера, то /etc/rc.d/rc.local) пути к java: echo "Add JAVA paths..." export JAVA_HOME=/usr/lib/java export MANPATH="${MANPATH}:${JAVA_HOME}/man" export PATH="${PATH}:${JAVA_HOME}/bin:${JAVA_HOME}/jre/bin" 5. Перезагружаем машину. 6. Вводим в консоли команду java без параметров. Если все было сделано правильно, то вы должны увидеть вывод краткой справки по параметрам java.
Создание пользователя для I2P
Понятно, что операции с пользователями надо делать под root или с sudo. 1. Создаем отдельную группу для I2P: groupadd i2pgrp 2. Создаем интерактивного (пока, потом мы вырубим ему всю интерактивность) пользователя, например i2psrv: useradd -g i2pgrp -m -d /home/i2psrv -s /bin/bash i2psrv где: -g i2pgrp — группа пользователя (i2pgrp) -m — создать домашний каталог -d /home/i2psrv — путь к домашнему каталогу (/home/i2psrv) -s /bin/bash — установить пользователю оболочку, в данном случае фиктивную (/bin/bash) i2psrv — имя пользователя 3. Задаем ему пароль: passwd i2psrv В интерактивном режиме повторяем пароль 2 раза, о сложности можно не беспокоиться — все равно потом отключим.
Установка
1. Скачиваем установщик для Linux с официального сайта 2. Копируем в директорию пользователя (/home/i2psrv) и меняем владельца файла: chown i2psrv:i2pgrp i2pinstall_0.9.44.jar 3. Логинимся под новым пользователем. 4. Запускаем установку (через консоль): java -jar i2pinstall_0.9.44.jar -console 5. Приводят возможность выбрать язык: Select your language 0 [x] eng 1 [ ] bra 2 [ ] cat 3 [ ] ces ... Оставляем английский (нажимая ENTER). 6. Далее предлагают продолжить установку: Нажимаем 1 [ENTER] 7. Опять лицензия и всякая хрень: Нажимаем 1 [ENTER] 8. Выбор пути для установки. Поскольку устанавливаем в каталоге нового пользователя, пусть так и делает — нажимаем [ENTER] 9. Далее нажимаем O и [ENTER] (в прошлых версиях было меньше гемора и подтверждений, а сейчас словно анкету в ментовку работать заполняешь 🙂 10. Еще раз предлагают подтвердить, что все ОК (1 и [ENTER]) 11. Наконец, тебе говорят, что все ОК: ==================== Installation started Framework: 5.1.3-84aaf (IzPack) Platform: linux,version=4.4.14-smp,arch=x86,symbolicName=null,javaVersion=1.8.0_232 [ Starting to unpack ] [ Processing package: Base (1/1) ] [ Unpacking finished ] Installation finished On most systems, I2P can be started with: /home/i2psrv/i2p/i2prouter start If I2P does not start, please try: /home/i2psrv/i2p/runplain.sh 12. Тебе говорят, что все совсем ОК: Installation was successful Application installed on /home/i2psrv/i2p [ Writing the uninstaller data ... ] [ Console installation done ]
Расшаривание I2P-сервисов в локальной сети.
Если вы намерены использовать I2P только на локальном компьютере, этот шаг можно пропустить. 1. Идем в файл /home/i2psrv/i2p/i2ptunnel.config, предварительно его куда-нибудь скопировав, и меняем в нем все 127.0.0.1 на адрес нашего компьютера в локальной сети (например на 192.168.0.20, см. адрес вашего компьютера), таким образом все сервисы I2P будут доступны с любой машины в локальной сети. 2. В том же файле смотрим порты, если где-то у нас какой-то порт для чего-то занят, например на том же порту уже висит HTTP или TOR-proxy, смело меняем порт.
Переключение пользователя
I2P — порядочный сервис, и сам умеет переключать пользователя, под которым запускается. Некоторые даже рекомендуют завести двух пользователей, одного для запуска, другого для установки I2P, но мы, ради экономии времени, делать этого не будем — и запуск и установка будут под одним пользователем. Тем не менее, необходимо отредактировать скрипт i2prouter/home/ip2srv/i2p): 1. Ищем строку, содержащую RUN_AS_USER= 2. Раскомментируем ее (удаляем # в начале строки). 3. Дописываем после знака = имя пользователя (в этом примере «i2psrv").
Расшаривание Web-консоли
Отслеживание статуса соединения с сетью I2P, ошибки и основные настройки доступны через Web-интерфейс, который запускается на локальной машине на порту 7657, но если физического доступа к той машине, на которой установлен I2P нет (или лень), то можно управлять ей и с любого компьютера локальной сети. Внимание! Это не очень одобряется официальными инструкциями. По идее можно сделать безопасный SSL-туннель, как это описывалось в одной из старых инструкций. Но я опишу самый простой вариант, просто поменяю конфиг. Заходим в clients.config (/home/i2psrv/i2p), предварительно сделав бэкап. И опять меняем 127.0.0.1 на 192.168.0.20, т.е. меняем адрес машины, по которому должна появиться консоль. Далее меняем clientApp.4.startOnLoad=true на clientApp.4.startOnLoad=false, т.к. на сервере нет смысла открывать панель управления после старта I2P. [lj-cut text="Дальнейшая настройка под катом" unicancor="i2psetup"]
Запуск I2P
I2P предполагает 2 способа запуска — через скрипт запуска сервиса: i2prouter start Остановка: i2prouter stop И, если не работает, то можно организовать запуск через runplain.sh: sh /path/to/script/runplain.sh Запуск в network namespace под названием provns: ip netns exec provns /home/i2psrv/server/i2prouter start или: ip netns exec provns sudo -u i2psrv sh /home/i2psrv/server/runplain.sh Далее заходим с браузера по адресу http://192.168.0.20:7657 В последней версии I2P мы увидим мастер настройки. Аккуратно проходим все этапы, читая инструкции на экране. Настраиваем пропускную способность (чем больше, тем лучше):
Наконец, попадаем в консоль:
Устранение ошибок
Сеть: ОШИБКА — Часы сбиты (расхождение NNNN): Настраиваем часы. копия Сеть: ОШИБКА — Нет активных узлов, проверьте подключение к сети и брандмауэр и Начальная загрузка не удалась. Смотрите страница настройки начальной загрузки для получения помощи
Кроме действительного отсутствия подключения, например при неправильной настройке брандмауэра, это может случиться из-за кривого пакета java, мне вот пришлось искать правильный. Вот он В любом случае при такой ошибке идем в log (http://192.168.0.20:7657/logs) Если в логах встретилось ошибки java — надо переустановить java из другого пакета: Dec 13, 2019 9:51:08 AM INFO [JobQueue 3/4] networkdb.reseed.ReseedChecker: Downloading peer router information for a new I2P installation Dec 13, 2019 9:51:09 AM ERROR [Reseed ] uter.networkdb.reseed.Reseeder: Error reseeding from https://i2p.mooo.com/netDb/i2pseeds.su3?netid=2 java.lang.RuntimeException: Cannot load SunEC provider at sun.security.pkcs11.P11ECKeyFactory.getSunECProvider(P11ECKeyFactory.java:55) at sun.security.pkcs11.P11ECKeyFactory.generatePublic(P11ECKeyFactory.java:226) at sun.security.pkcs11.P11ECKeyFactory.implTranslatePublicKey(P11ECKeyFactory.java:110) at sun.security.pkcs11.P11ECKeyFactory.engineGeneratePublic(P11ECKeyFactory.java:171) ... Сеть: Заблокирован брандмауэром: Надо проверить настройки IPTABLES и/или пробросить порт I2P на роутере. I2P выбирает случайный порт для соединения с другими пирами сети при первой загрузке. Порт можно посмотреть в сетевых настройках: http://192.168.0.20:7657/confignet Описание других ошибок и предупреждений есть во внутренней справке I2P http://192.168.0.20:7657/help#reachabilityhelp Примечание: Замените адрес 192.168.0.20:7657 на ваш адрес сервера.
Настройка клиента на примере Firefox
1. Заводим отдельный профиль для I2P. Заходим на about:profiles, нажимаем кнопку «Создать новый профиль» и следуем инструкциям мастера. 2. Ставим дополнение FoxyProxy Standart 3. Заходим в Options
4. Нажимаем Add и заполняем форму. — Title or Description: I2P — Do not use for localhost and intarnet/private IP addresses: Ставим переключатель в OnProxy Type: HTTP — Proxy IP address or DNS name: 192.168.0.20Port: 4444 (по умолчанию, можно изменить в i2ptunnel.config или в консоли: http://192.168.0.20:7657/i2ptunnelmgr, щелкнув по ссылке I2P HTTP Proxy в таблице Клиентские I2P туннели) — Нажимаем Save
5. Включаем созданный профиль
6. Переключите FoxyProxy в режим Use Enabled Proxies By Patterns And Order
Иначе можно получить ошибку HTTP-proxy I2P: Ваш браузер не поддерживает iFrames Не используйте прокси-сервер для доступа к консоли маршрутизатора, локальному компьютеру или локальной сети. 7. В качестве домашней страницы можно установить Панель управления I2P (http://192.168.0.20:7657)[/lj-cut]
Проверка
Переходим по адресу http://i2p-projekt.i2p
I2P довольно медленная сеть и работает с некоторыми перебоями, так что если вместо сайта вы увидите надпись «Сайт недоступен. Возможно, сайт отключен, сеть перегружена или ваш маршрутизатор недостаточно интегрирован с другими пирами. Вы можете повторить операцию.«, то действительно, скорее всего, операцию нужно повторить.
Отключаем интерактив пользователю i2psrv
На самом деле интерактивный доступ к системе для пользователя i2psrv был нужен только на этапе установки сервиса I2P, и я оставлял его до конца настройки на случай, если I2P понадобится переустановить. Теперь консоль пользователю i2psrv более не нужна. Отключаем: Редактируем (под root‘ом) файл /etc/passwd. Находим в этом файле строку, начинающуюся с имени пользователя (i2psrv) и в конце строки меняем /bin/bash на /bin/false. Сохраняем файл.
Что дальше?
Остались нераскрытыми темы подписок (как добавлять I2P-сайты неизвестные вашему маршрутизатору I2P), интересные I2P-сайты, другие сервисы I2P, кроме WWW. Может быть когда-нибудь вернусь к этому вопросу, а пока рекомендую детально ознакомиться с документацией на сайте https://geti2p.net/, http://i2p-projekt.i2p и в консоли I2P.]]>

Рубрики
Блог

Настройка времени через консоль Linux

date —set «<строка>» Например: date --set "Dec 12 5:08:00 2019" Сохранение: hwclock -w Эту команду надо выполнить после установки времени командой date. Просмотр времени и даты — команда date без параметров. Пример вывода: Wed Dec 18 16:53:28 AKST 2019 Источник, копия статьи в PDF, там еще есть и другие способы настройки.]]>

Рубрики
Блог

Установка и настройка tor-ноды

Установка tor
1. Загружаем исходники tor отсюда я взял версию 0.4.2.4-rc посвежее. 2. Собираем пакет: — Распаковываем из архива каталог tor-0.4.2.4-rc со всеми подкаталогами — Переходим в этот каталог и выполняем последовательно: ./configure make checkinstall checkinstall нас спросит, какой пакет хотим создать (Slackware [S], RPM [R] or Debian [D]?), отвечаем s, далее вводим описание пакета, например tor и нажимаем два раза ENTER, после чего подтверждаем, что все верно (нажав ENTER еще раз) и checkinstall создаст пакет. 3. Можно сразу его установить: installpkg tor-0.4.2.4-rc-i386-1.tgz
Создаем пользователя для tor
Негоже запускать tor под обычным пользователем, а тем более под root‘ом, потому создадим для него отдельного пользователя. Пользователь будет неинтерактивный, т.е. войти в систему с терминала он не сможет. 1. Добавляем группу, например torgroup: groupadd torgroup 2. Смотрим файл /etc/shells и проверяем, чтоб в файле была строка /bin/false, если ее нет — смело дописываем. 3. Создаем пользователя с именем, например, torusr: useradd -g torgroup -m -d /home/torusr -s /bin/false torusr где: -g torgroup — группа пользователя (torgroup) -m — создать домашний каталог -d /home/torusr — путь к домашнему каталогу (/home/torusr) -s /bin/false — установить пользователю оболочку, в данном случае фиктивную (/bin/false) torusr — имя пользователя 4. Создаем каталог для данных tor: cd /home/torusr mkdir .tordata и изменяем владельца созданного каталога на torusr chown torusr:torgroup .tordata
Настройка tor-ноды
В каталоге /home/torusr создаем файл torrc и записываем в него следующие строки: SocksPort 9050 — порт на localhost, где будет висеть tor, и принимать на него запросы. Проще говоря, это порт прокси-сервера, который надо будет прописать, чтоб соединиться с сетью tor с локальной машины. Порт 9050 является портом по умолчанию, и назначается на localhost (127.0.0.1), если строки SocksPort будут отсутствовать в конфигурации. SocksPort 192.168.0.20:9660 — то же самое, только внутри локальной сети. Клиенты из локальной сети должны будут подключаться по адресу 192.168.0.20 и порту 9666, чтобы использовать сеть tor. Tor организует только SOCKS5 прокси, как быть с браузерами, которым нужен HTTP, рассмотрим далее. SocksPolicy accept 192.168.0.0/24 SocksPolicy accept 127.0.0.1 SocksPolicy reject * Принимать запросы только из локальной сети и localhost’а. Log notice file /home/torusr/notices.log — файл, куда будет писаться лог tor’а RunAsDaemon 1 — запуск tor в режиме демона. DataDirectory /home/torusr/.tordata — путь для данных tor ORPort 9001 — порт, используемый для пересылки пакетов с других узлов. DirPort 9030 — порт, используемый для сервиса каталогов, т.е узел будет позволять получать информацию о других узлах tor, а не только транслировать трафик. Конечно, стоило бы облегчить жизнь пользователям, сидящим за сетевыми экранами и пустить трафик по портам 80 (8080) или 443, но я пока не стал. Впрочем, если вам будет надо — смотрите статью на Хабре (копия) RelayBandwidthRate 1000 MB RelayBandwidthBurst 2000 MB — ограничение пропускной способности. Чем больше, тем лучше. Но настраивайте эти числа под свой канал, чтоб не загадить его трафиком tor совсем. Nickname PersonalChaosRelay — имя вашего релея (ноды) в базе tor. Можете придумать любое, но советую сначала проверить придуманное имя здесь, введя его в поиске. Если ничего не найдется, проздрабляю, ваш релей будет уникальным. ExitPolicy reject *:* ExitPolicy reject6 *:* — запрет использовать tor-ноду в качестве выходной. Для ГОРФ параметр строго обязательный. Примечание: в /usr/local/etc/tor находится файл torrc.sample с подробным описанием конфигурации. Пример файла конфигурации на PasteBin Устанавливаем файлу правильного владельца: chown torusr:torgroup torrc
Запуск ноды
Запуск осуществляется следующей командой: sudo -u torusr tor -f /home/torusr/torrc Если помещать запуск в автозагрузку, то желательно указать полный путь к файлу tor: sudo -u torusr /usr/local/bin/tor -f /home/torusr/torrc Запуск в отдельном network namespace (копия): ip netns exec provns sudo -u torusr /usr/local/bin/tor -f /home/torusr/torrc provns — имя network namespace
Проверка работоспособности
Минут через 15-20 после запуска можно заглянуть в /home/torusr/notices.log, если в последних строках файла написано что-то типа: Self-testing indicates your ORPort is reachable from the outside. Excellent. Publishing server descriptor. Tor has successfully opened a circuit. Looks like client functionality is working. Self-testing indicates your DirPort is reachable from the outside. Excellent. Performing bandwidth self-test...done. значит порты ORPort и DirPort доступны извне, и нода нормально функционирует. Если же в логе что-то типа: Your server (x.x.x.x:9001) has not managed to confirm that its ORPort is reachable. Relays do not publish descriptors until their ORPort and DirPort are reachable. Please check your firewalls, ports, address, /etc/hosts file, etc. Your server (x.x.x.x:9030) has not managed to confirm that its DirPort is reachable. Relays do not publish descriptors until their ORPort and DirPort are reachable. Please check your firewalls, ports, address, /etc/hosts file, etc. значит порты недоступны из интернета (закрыты фаерволлом, нужно сделать проброс портов на роутере, порты закрыты провайдером) и нода не появится в списке нод и не будет принимать соединения. В режиме клиента все будет работать, т.е. клиенты из локальной сети смогут ей пользоваться для выхода в сеть tor Если все в порядке, то через несколько часов нода появится в базе данных, и можно будет проверить ее наличие здесь введя в поисковой строке на странице Nickname, который указали в конфиге. [lj-cut text="Настройка для клиентов под катом" unicancor="clients"]
Настройка прокси для браузера-клиента
Как я уже говорил, Tor представляется исключительно Socks5 прокси. Для корректной работы браузерам потребуется прокси-прослойка. Их есть несколько, я использовал легковесный и простой Polipo. 1. Ставим Polipo, я не стал далеко ходить, воспользовался sbopkg. 2. Копируем файл /etc/polipo/config в /home/torusr/polipo.conf 3. Меняем владельца файла: chown torusr:torgroup polipo.conf 4. Вносим изменения в конфигурацию, раскомментируем строчку proxyAddress, прописываем свой адрес сервера (0.0.0.0 для localhost): proxyAddress = "192.168.0.20" # IPv4 only 5. Меняем порт, указываем, чтоб запускался в режиме демона, указываем путь к файлу PID и к логам. Поскольку запускаться он будет также от имени toruser, то меняем все пути на домашний каталог. proxyPort = 9666 daemonise = true pidFile = /home/torusr/polipo.pid logFile = /home/torusr/polipo.log 6. Раскомментируем и проверяем строки: socksParentProxy = "localhost:9050" socksProxyType = socks5 Это как раз наш tor-прокси 7. Настройка кэширования. Если оставить настройку кэширования по умолчанию, то при запуске из-под пользователя torusr polipo не сможет писать в кэш /var/cache/polipo/ и загадит свой log сообщениями вида Couldn't create directory /var/cache/polipo/2ip.ru/: Permission denied Есть два варианта решения этой проблемы. Наиболее предпочтительный для пользования Tor и большей анонимности — отключить кэширование вообще. Для этого в конфиге polipo.conf находим строку # diskCacheRoot = "" и раскомментируем ее (или просто добавляем diskCacheRoot = "" в конфиг). Другой вариант — создать отдельную директорию для кэша в каталоге пользователя torusr: cd /home/torusr mkdir .polipo-cache chown torusr:torgroup .polipo-cache и раскомментировать (добавить) строку diskCacheRoot = "~/.polipo-cache/" в конфиге. 8. Устранение глюка с PID-файлом. У меня случилась довольно странная ситуация. При неправильном завершении работы polipo не смог удалить свой PID-файл и свалился с ошибкой Couldn't create pid file /home/torusr/polipo.pid: File exists На самом деле это довольно необычно для линуксовых программ, но на всякий случай можно добавить в скрипт запуска (ниже) перед запуском polipo, команду: rm "/home/torusr/polipo.pid" 9. Запуск: sudo -u torusr /usr/bin/polipo -c /home/torusr/polipo.conf В network namespace: ip netns exec provns sudo -u torusr /usr/bin/polipo -c /home/torusr/polipo.conf Конфиг Polipo на PasteBin
Скрипт для запуска tor и polipo (в отдельном network namespace)
#!/bin/bash
echo "Start tor relay..."
ip netns exec provns sudo -u torusr /usr/local/bin/tor -f /home/torusr/torrc
sleep 5
echo "Starting Polipo proxy..."
if [ -f "/home/torusr/polipo.pid" ]; then
    echo "Removing PID file..."
    rm "/home/torusr/polipo.pid"
else
    echo "Old PID file not found. OK."
fi
ip netns exec provns sudo -u torusr /usr/bin/polipo -c /home/torusr/polipo.conf
На PasteBin
Настройка клиента в локальной сети на примере Firefox
Приятно, что нода не только поддерживает сеть Tor, но и может служить в качестве прокси для клиентов из локальной сети. Конечно же есть максимально безопасно и оптимизировано под Tor настроенный Tor-браузер, но иногда использовать его неудобно. Конечно же, при настройке браузера вручную, стоит установить некоторые плагины, используемые в Tor-браузере, но это тема отдельного разговора. Тут покажу только простую начальную настройку. 1. Заходим в Firefox и набираем в адресной строке about:profiles 2. Создаем новый профиль. Местоположение файлов профиля выбираем по вкусу. Я обычно не выбираю стандартное, а все профили Firefox хранятся в отдельном каталоге. 3. Запускаем еще один браузер с новым профилем и идем в настройки, выбираем, чтобы Firefox не сохранял историю, а в качестве домашней страницы указываем https://check.torproject.org/ 4. Закрываем профиль и вновь открываем его, и пока видим, что мы не в сети Tor, о чем нам сообщает https://check.torproject.org/ 5. Ищем в расширениях и устанавливаем FoxyProxy standart 6. Кликаем по иконке расширения и нажимаем Options. 7. Слева нажимаем Add и добавляем конфигурацию: Proxy IP address: 192.168.0.20 Port: 9666 Title: Tor
8. Сохраняем настройки, закрываем их, опять кликаем на иконку расширения, выбираем наши настройки (включаем FoxyProxy)
7. Обновляем страницу, и видим, что мы в сети Tor
[/lj-cut]]]>

Рубрики
Блог

Network namespaces или несколько виртуальных сетевых карт (интерфейсов) с разными IP на одной машине.

Для чего.
Например мы хотим организовать следующую схему: 1. После запуска сервер устанавливает соединение с VPN-провайдером, и весь трафик идет через VPN. 2. Некоторым программам VPN не нужен, пусть это например будут TOR, I2P (у них и так шифрование, плюс им бы желательно скорость работы повыше, а напрямую соединяться быстрее, чем через VPN). Плюс, например, мы хотим ходить на некоторые сайты в обход VPN, либо иметь возможность запускать в обход VPN какие-либо трафикоемкие программы, например торрент-клиент. Ну пусть еще будет отдельный локальный сервис, например FTP, также висящий на отдельном IP.
Как.
В Linux есть замечательный механизм network namespaces, который на одной машине позволяет создать несколько изолированных друг от друга сетевых подсистем, почти как сеть в виртуальной машине. Не бойтесь, все делается на уровне ядра, потому тормозов, характерных для виртуальных машин не будет. И пусть слово «изолированных» вас тоже не пугает, когда надо — изолированных, а когда надо — мы их свяжем. Эмулируется полностью сетевой стек: сетевые интерфейсы, таблица маршрутизации, файрволл и т.д. Так, как это работает на уровне ядра и для определенных процессов, то вполне можно получить рабочую конфигурации, когда на одной машине есть несколько сетевых интерфейсов с одинаковыми IP-адресами и ничто не конфликтует.
Подготовка
Поскольку в процессе нам придется поднимать/опускать физические и виртуальные сетевые карты, настоятельно советую отключить Network Manager, а основную сеть настроить вручную. С включенным Network Manager может ничего не получиться, т.к. он в самый ответственный момент влезет и напоганит в конфигурации. Как на Slackware отключить Network Manager и настроить сеть вручную (копия)
Создание и конфигурация network namespace.
Конфигурация namespace’ов производится с помощью iproute2. 1. Создаем новый неймспейс с именем provns: ip netns add provns 2. Создаем две связанных между собой виртуальных сетевых карты (интерфейса) ip link add veth0 type veth peer name veth1 3. Поднимаем интерфейс veth0: ifconfig veth0 0.0.0.0 up 4. Ассоциируем вторую виртуальную карту с созданным нэймспейсом: ip link set veth1 netns provns Получился один дополнительный namespace, связанный с основным виртуальными сетевыми интерфейсами veth0 и veth1.
Теперь надо связать виртуальный интерфейс veth0 с физической сетевой картой (интерфейсом) eth0. Сделать это можно разными способами, например, присвоить интерфейсам разные IP-адреса из разных подсетей и сделать маскарадинг с помощью IPTABLES, а можно воспользоваться мостами. Мост — специальное сетевое устройство (в данном случае не физическое, а логическое) позволяющее связать два (и более) сетевых интерфейса на канальном уровне, как в маршрутизаторе. Подробнее про мосты: — Linux BridgeВиртуальные сетевые устройства в Linux: Linux Bridge10 примеров команды brctl в Linux для сетевого Ethernet мостаЭти статьи в PDF Итак: 5. Отключаем физический интерфейс eth0: ifconfig eth0 down 6. Включаем его, но IP-адрес не присваиваем (IP-адрес будет присвоен мосту): ifconfig eth0 0.0.0.0 up 7. Создаем мост br0: brctl addbr br0 8. Соединяем (добавляем интерфейсы к мосту) eth0 и veth0: brctl addif br0 eth0 veth0 9. Поднимаем (включаем) мост, присваивая ему IP-адрес и маску подсети: ifconfig br0 192.168.0.15 netmask 255.255.255.0 up Получилось как на схеме:
10. Устанавливаем маршрут по умолчанию в основном namespace: ip route add default via 192.168.0.1 dev br0 src 192.168.0.15 Далее конфигурируем namespace: Выполнение любых команд в указанном namespace производится командой ip netns exec <имя_namespace> <команда> [параметры_команды] Например, если мы хотим выполнить ifconfig в namespace с именем provns, то команда будет: ip netns exec provns ifconfig 11. Поднимаем (включаем) сетевой интерфейс veth1 внутри нэймспейса provns, и присваиваем ему адрес 192.168.0.20: ip netns exec provns ifconfig veth1 192.168.0.20 netmask 255.255.255.0 12. Прописываем внутри нэймспейса маршрут по умолчанию: ip netns exec provns ip route add default via 192.168.0.1 dev veth1 src 192.168.0.20 13. Поднимаем внутри namespace’а loopback-интерфейс: ip netns exec provns ifconfig lo 127.0.0.1
Скрипт для автоматизации настройки network namespace.
[lj-cut text="Под катом" unicancor="setupscript"]Поскольку на включение/выключение сетевых устройств требуется время (немного, но требуется), то если автоматизировать создание и настройку namespaces в скрипте, что я и буду делать, то потребуется как-то контролировать, поднялось/опустилось ли то или иное сетевое устройство. Для такой задачи есть скрипт ndstatus, который можно взять здесь Описание скрипта (копия). Положите его в каталог где будет скрипт, настраивающий namespaces. В самом скрипте настройки namespaces заводим функцию wait_to(), которая будет выводить уведомление на экран, раз в секунду вызывать скрипт ndstatus и проверять статус сетевого устройства. Если он изменился на указанный при вызове функции, то функция передает управление далее, если нет — отсчитывает время, а если вышло максимальное время, указанное в параметрах функции (при вызове), значит что-то пошло не так, функция уведомляет об ошибке.
wait_to()
{
    L_MESS=$1
    L_DEV=$2
    L_ST=$3
    L_TIME=$4
    L_NS=$5
    L_LOOP=0
    L_RETCODE=666
    echo -n $L_MESS
    while [ $L_LOOP -eq 0 ];do
	echo -n "."
	if [ -z $L_NS  ]; then
	    ./ndstatus $L_DEV -s
	    L_RETCODE=$?
	else
	    ip netns exec $L_NS ./ndstatus $L_DEV -s
	    L_RETCODE=$?
	fi
	if [[ "$L_ST"=="up" ]]; then
	    if [ $L_RETCODE -eq 0 ]; then
		L_LOOP=1
		echo
	    fi
	fi
	if [[ "$L_ST"=="down" ]]; then
	    if [ $L_RETCODE -eq 1 ]; then
		L_LOOP=1
		echo
	    fi
	fi
	sleep 1
	let L_TIME=L_TIME-1
	if [ $L_TIME -lt 0 ]; then
	    echo
	    echo "Oops, something wrong..."
	    sleep 10
	    L_LOOP=1
	fi
    done
}
В остальном, скрипт делает то же самое, что было описано выше, только после команд включения/выключения сетевых устройств вызывается функция wait_to() echo "Adding namespace..." ip netns add provns echo "Configure virtual network card..." ip link add veth0 type veth peer name veth1 ifconfig veth0 0.0.0.0 up ip link set veth1 netns provns ifconfig eth0 down wait_to "Down network card..." eth0 down 5 ifconfig eth0 0.0.0.0 up wait_to "Up network card..." eth0 up 5 echo "Configure bridge..." brctl addbr br0 brctl addif br0 eth0 veth0 ifconfig br0 192.168.0.15 netmask 255.255.255.0 up wait_to "Up bridge..." br0 up 7 #echo "Delete default route..." #ip route del default echo "Add default route..." ip route add default via 192.168.0.1 dev br0 src 192.168.0.15 ip netns exec provns ifconfig veth1 192.168.0.20 netmask 255.255.255.0 wait_to "Configure namespace. Up virtual network card..." veth1 up 5 provns echo "Set default route for namespace..." ip netns exec provns ip route add default via 192.168.0.1 dev veth1 src 192.168.0.20 echo "Configure lo interface..." ip netns exec provns ifconfig lo 127.0.0.1 echo "Network namespace configured!"[/lj-cut] Скрипт целиком на GitHub]]>