Мастерская интернет-разработчикаhttps://adw0rd.com/2022-01-15T21:19:51+03:00adw0rdБлог Михаила Андреева, ака adw0rdDocker Compose. Восстановление доступа к PostgreSQL
2022-01-15T21:19:51+03:00https://adw0rd.com/2022/01/15/docker-compose-restore-access-to-postgres/В случае потери доступа к базе данных появится ошибка "PostgreSQL: role is not permitted to log in". Вы можете попробовать восстановить доступ через следующий трюк: postgres: image: postgres user: postgres command: > sh -c 'postgres --single << "ALTER ROLE <USERNAME> WITH LOGIN"' После чего запустите сервис: docker-compose up -d postgres…
<p>В случае потери доступа к базе данных появится ошибка "PostgreSQL: role is not permitted to log in". Вы можете попробовать восстановить доступ через следующий трюк:</p>
<pre><code>postgres:
image: postgres
user: postgres
command: >
sh -c 'postgres --single << "ALTER ROLE <USERNAME> WITH LOGIN"'
</code></pre>
<p>После чего запустите сервис:</p>
<p><code>docker-compose up -d postgres</code></p>
<p>Таким образом у вас появится доступ для авторизации вашего пользователя.</p>
Отладка в docker-compose
2021-05-19T08:33:58+03:00https://adw0rd.com/2021/05/19/debugging-in-docker-compose/Зачастую необходимо воспользоваться инструментами отладки внутри контейнера, но для этого надо получить полноценный tty с для взаимодействия с интерфейсом отладчика через stdin/stdout. В заметке ниже покажу как это сделать, с помощью данного способа я запускаю pudb , pdb, ipdb и т.п. Нужно добавить stdin_open: true и tty: true для вашего…
<p>Зачастую необходимо воспользоваться инструментами отладки внутри контейнера, но для этого надо получить полноценный tty с для взаимодействия с интерфейсом отладчика через stdin/stdout.</p>
<p>В заметке ниже покажу как это сделать, с помощью данного способа я запускаю <a rel="nofollow" href="https://adw0rd.com/2012/3/24/python-django-pudb/">pudb</a> , <a rel="nofollow" href="https://adw0rd.com/2012/10/7/python-pdb/">pdb</a>, <a rel="nofollow" href="https://github.com/gotcha/ipdb">ipdb</a> и т.п.</p>
<p><a name="more"></a></p>
<p>Нужно добавить <tt>stdin_open: true</tt> и <tt>tty: true</tt> для вашего сервиса:</p>
<pre><code>version: "3.9"
services:
api:
build: .
stdin_open: true
tty: true
ports:
- "8000:8000"
volumes:
- .:/app
</code></pre>
<p>После чего запустить его следующим образом:</p>
<pre><code>docker-compose run --service-ports api
</code></pre>
<p>Если по какой-то причине у вас не получается это сделать, то вы можете воспользоваться отладчиком <a rel="nofollow" href="https://github.com/Kozea/wdb">wdb</a></p>
Кратко о настройке ProFTPd
2021-04-27T15:26:11+03:00https://adw0rd.com/2021/04/27/proftpd/Установка ProFTPD (актуально для версии 1.3.6): sudo apt update sudo apt install proftpd Добавить в /etc/proftpd/proftpd.conf следующие строки: Port 10109 DefaultRoot ~ RequireValidShell off AuthUserFile /etc/proftpd/ftpd.passwd AuthGroupFile /etc/proftpd/ftpd.group AuthOrder mod_auth_file.c Создаем пользователя: sudo ftpasswd --passwd --file=/etc/proftpd/ftpd.passwd --name=web --change-password Создаем группу: sudo ftpasswd --group --name=www-data --file=/etc/proftpd/ftpd.group --gid=60 --member web gid должен…
<p>Установка <strong>ProFTPD</strong> (актуально для версии 1.3.6):</p>
<pre><code>sudo apt update
sudo apt install proftpd
</code></pre>
<p>Добавить в <tt>/etc/proftpd/proftpd.conf</tt> следующие строки:</p>
<pre><code>Port 10109
DefaultRoot ~
RequireValidShell off
AuthUserFile /etc/proftpd/ftpd.passwd
AuthGroupFile /etc/proftpd/ftpd.group
AuthOrder mod_auth_file.c
</code></pre>
<p>Создаем пользователя:</p>
<pre><code>sudo ftpasswd --passwd --file=/etc/proftpd/ftpd.passwd --name=web --change-password
</code></pre>
<p>Создаем группу:</p>
<pre><code>sudo ftpasswd --group --name=www-data --file=/etc/proftpd/ftpd.group --gid=60 --member web
</code></pre>
<blockquote class="info">
gid должен совпадать с gid из файла /etc/group
</blockquote>
<p>Выставляем права на файлы:</p>
<pre><code>chmod 440 /etc/proftpd/ftpd.*
</code></pre>
<p>Тестим конфиг:</p>
<pre><code>sudo proftpd -t
</code></pre>
<p>Запускаем:</p>
<pre><code>sudo systemctl start proftpd
</code></pre>
Снифинг HTTPS трафика в Chromium с помощью Wireshark
2020-12-02T22:33:04+03:00https://adw0rd.com/2020/12/02/chromium-https-ssl-tls-sniffing-with-wireshark/Для того чтобы расшифровать трафик из Chromium (или Chrome) нужно настроить запись SSL-логов в файл, после чего настроить Wireshark, чтобы он читал этот файл. Настройка Chromium /usr/local/bin/chromium --ssl-version-max=tls1.3 --ssl-key-log-file=/tmp/sslkeylog.log (удобнее будет указать alias для вашей командной оболочки) Настройка Wireshark Перейдите в Preferences > Protocols > TLS, укажите путь до файла:…
<p><img src="/media/uploads/wireshark.png" class="alignright whitespace"></p>
<p>Для того чтобы расшифровать трафик из <strong>Chromium</strong> (или Chrome) нужно настроить запись SSL-логов в файл, после чего настроить <strong>Wireshark</strong>, чтобы он читал этот файл.</p>
<p><a name="more"></a></p>
<h3>Настройка Chromium</h3>
<pre><code>/usr/local/bin/chromium --ssl-version-max=tls1.3 --ssl-key-log-file=/tmp/sslkeylog.log
</code></pre>
<p>(удобнее будет указать alias для вашей командной оболочки)</p>
<h3>Настройка Wireshark</h3>
<p>Перейдите в <tt>Preferences > Protocols > TLS</tt>, укажите путь до файла:</p>
<p><img src="/media/uploads/wireshark-tls.png" alt="wireshark-tls-settings.png" /></p>
<p>В результате вы сможете увидеть расшифрованные данные:</p>
<p><img src="/media/uploads/wireshark-ssl.png" alt="wireshark-ssl.png" /></p>
<h3>Рекомендую к просмотру</h3>
<p>Детально рассказано в видео Андрея Созыкина:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/1r1iWq67v3c" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<iframe width="560" height="315" src="https://www.youtube.com/embed/VIFJFHWlxzo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
Простой healthcheck для Kafka под docker-compose
2020-11-25T12:36:26+03:00https://adw0rd.com/2020/11/25/kafka-healthcheck-by-topic/version: "2.1" services: kafka: image: 'bitnami/kafka:latest' ports: - '9092:9092' - '9093:9093' environment: - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 - ALLOW_PLAINTEXT_LISTENER=yes - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CLIENT:PLAINTEXT,EXTERNAL:PLAINTEXT - KAFKA_CFG_LISTENERS=CLIENT://:9093,EXTERNAL://:9092 - KAFKA_CFG_ADVERTISED_LISTENERS=CLIENT://kafka:9093,EXTERNAL://localhost:9092 - KAFKA_INTER_BROKER_LISTENER_NAME=CLIENT depends_on: - zookeeper healthcheck: test: ["CMD-SHELL", "kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic <TOPIC_NAME> --describe"] interval: 2s timeout: 2s retries: 15 someservice: image: ... depends_on: kafka: condition: service_healthy links:…
<pre><code>version: "2.1"
services:
kafka:
image: 'bitnami/kafka:latest'
ports:
- '9092:9092'
- '9093:9093'
environment:
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- ALLOW_PLAINTEXT_LISTENER=yes
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CLIENT:PLAINTEXT,EXTERNAL:PLAINTEXT
- KAFKA_CFG_LISTENERS=CLIENT://:9093,EXTERNAL://:9092
- KAFKA_CFG_ADVERTISED_LISTENERS=CLIENT://kafka:9093,EXTERNAL://localhost:9092
- KAFKA_INTER_BROKER_LISTENER_NAME=CLIENT
depends_on:
- zookeeper
healthcheck:
test: ["CMD-SHELL", "kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic <TOPIC_NAME> --describe"]
interval: 2s
timeout: 2s
retries: 15
someservice:
image: ...
depends_on:
kafka:
condition: service_healthy
links:
- kafka
command: ['...']
</code></pre>
Darktooth: Приятная тема для Emacs
2020-09-08T08:56:09+03:00https://adw0rd.com/2020/09/08/emacs-darktooth/Для установки можно вызвать: M-x package-install darktooth-theme Или установить через: M-x package-list-packages В ~/.emacs впишите выбранную тему: (load-theme 'darktooth t) Пример: Репозиторий тут https://github.com/emacsfodder/emacs-theme-darktooth
<p>Для установки можно вызвать:</p>
<pre><code>M-x package-install darktooth-theme
</code></pre>
<p>Или установить через:</p>
<pre><code>M-x package-list-packages
</code></pre>
<p>В <tt>~/.emacs</tt> впишите выбранную тему:</p>
<pre><code>(load-theme 'darktooth t)
</code></pre>
<p>Пример:<br />
<img src="https://adw0rd.com/media/uploads/dark_BZ4XOy9.png" alt="" /> </p>
<p>Репозиторий тут https://github.com/emacsfodder/emacs-theme-darktooth</p>
Установка Python 3.7.7 на Debian 9
2020-05-12T16:47:40+03:00https://adw0rd.com/2020/05/12/python-377-to-debian-9-stretch/Очень короткая заметка о том как установить Python 3.7.7 на Debian 9 (stretch): apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libcrypto++-dev libssl-dev libreadline-dev libffi-dev curl libbz2-dev libsqlite3-dev libffi-dev curl -O https://www.python.org/ftp/python/3.7.7/Python-3.7.7.tar.xz tar -xf Python-3.7.7.tar.xz cd Python-3.7.7 ./configure --enable-loadable-sqlite-extensions --enable-optimizations make make install python3.7 --version На основе этой статьи
<p>Очень короткая заметка о том как установить Python 3.7.7 на Debian 9 (stretch):</p>
<pre><code>apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libcrypto++-dev libssl-dev libreadline-dev libffi-dev curl libbz2-dev libsqlite3-dev libffi-dev
curl -O https://www.python.org/ftp/python/3.7.7/Python-3.7.7.tar.xz
tar -xf Python-3.7.7.tar.xz
cd Python-3.7.7
./configure --enable-loadable-sqlite-extensions --enable-optimizations
make
make install
python3.7 --version
</code></pre>
<p>На основе <a rel="nofollow" href="https://linuxize.com/post/how-to-install-python-3-7-on-debian-9/">этой статьи</a></p>
Запуск Sentry On-Premise в Docker
2019-02-21T09:51:09+03:00https://adw0rd.com/2019/02/21/sentry-on-premise-docker/Я давно пользуюсь Sentry. Продолжительное время использовал sentry.io, но стал упираться в лимиты и решил вернуться на On-Premise (Standalone) версию. Сегодня я вам расскажу как быстро запустить Sentry на своём сервере, само собой под Docker, так как это уже стандарт де-факто в современном мире. Установка Если у вас ещё не…
<p><img src="/media/uploads/docker-sentry.png" class="alignright whitespace" style="width:160px"></p>
<p>Я давно пользуюсь <a rel="nofollow" href="/tag/sentry/">Sentry</a>. Продолжительное время использовал <a rel="nofollow" href="https://sentry.io/">sentry.io</a>, но стал упираться в лимиты и решил вернуться на On-Premise (Standalone) версию. Сегодня я вам расскажу как быстро запустить Sentry на своём сервере, само собой под <a rel="nofollow" href="/tag/docker/">Docker</a>, так как это уже стандарт де-факто в современном мире.</p>
<p><a name="more"></a></p>
<h2>Установка</h2>
<p>Если у вас ещё не установлен Docker, то прежде всего сделайте это:</p>
<pre><code>sudo apt update
sudo apt install docker.io docker-compose
</code></pre>
<p>Склонируйте репозиторий OnPremise на свой сервер, где будет запущен Sentry:</p>
<pre><code>git clone https://github.com/getsentry/onpremise.git
cd onpremise
</code></pre>
<h2>Настройка</h2>
<p>Объявите файл окружения, в нём будут храниться ваши настройки:</p>
<pre><code>mv .env.example .env
</code></pre>
<p>Создайте Volume для вашего контейнера, что бы между перезапусками контейнера ваши данные не потерялись (там будут храниться файлы от Sentry и файлы БД от Postgres).</p>
<pre><code>docker volume create --name=sentry-data
docker volume create --name=sentry-postgres
</code></pre>
<p>Сгенерируйте секретный ключ:</p>
<pre><code>docker-compose run --rm web config generate-secret-key
</code></pre>
<p>В самом конце вывода команды вы увидите ключ, сохраните его в файл <tt>.env</tt> в качестве значения для <tt>SENTRY_SECRET_KEY</tt>.</p>
<blockquote class="info">
Если вам необходимо использовать сторонний SMTP-сервер, то откройте на редактирование файл <tt>config.yml</tt> и заполните настройки. Например для Gmail это выглядит так:
<code>
mail.host: 'smtp.gmail.com'
mail.port: 587
mail.username: 'example@gmail.com'
mail.password: 'my_password'
mail.use-tls: true
</code>
</blockquote>
<p>Теперь проинициализируйте базу данных:</p>
<pre><code>docker-compose run --rm web upgrade
</code></pre>
<p>В процессе вас попросят создать первый аккаунт, заполните данные, чтобы попасть в веб-интерфейс. Когда все миграции пройдут, Sentry будет готов к запуску.</p>
<blockquote class="info">
Если вы будете вносить изменения в <tt>config.yml</tt> позже, тогда вам придётся пересобрать контейнеры:
<code>
docker-compose up --build -d
</code>
</blockquote>
<h2>Запуск</h2>
<p>Вы можете запустить с флагом "-d" (detach, запуск в фоне):</p>
<pre><code>docker-compose up -d
</code></pre>
<p>Но лично я доверяю больше systemd, поэтому предлагаю добавить конфиг (указанный ниже) и запустить полноценный сервис с внешним супервайзором:</p>
<p>Создайте файл <tt>/etc/systemd/system/docker-sentry-onpremise.service</tt>:</p>
<pre><code>[Unit]
Description=Sentry OnPremise: Docker Compose Application Service
Requires=docker.service
After=docker.service
[Service]
WorkingDirectory=</path/to/dir/onpremise>
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose down
TimeoutStartSec=0
Restart=on-failure
[Install]
WantedBy=multi-user.target
</code></pre>
<p>Где "</path/to/dir/onpremise>" это путь до каталога <tt>onpremise</tt>, который вы создали в самом начале.</p>
<p>Теперь активируем и запустим наш сервис:</p>
<pre><code>sudo systemctl enable docker-sentry-onpremise
sudo systemctl start docker-sentry-onpremise
</code></pre>
<h2>Настройка Nginx</h2>
<p>По-умолчанию веб-сервер Sentry запустится на 9000 порту, я подготовил Nginx-конфиг для проксирования на этот порт:</p>
<pre><code>server {
listen 443 ssl;
server_name sentry.example.org;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
auth_basic "closed site";
auth_basic_user_file htpasswd;
location / {
proxy_pass http://localhost:9000;
}
}
server {
listen 80;
server_name sentry.example.org;
return 301 https://$host$request_uri;
}
</code></pre>
<p>Как получить бесплатные SSL-сертификаты от LetsEncypt читаем в <a rel="nofollow" href="/2018/10/16/https-certbot-letsencrypt-ssl-tls-nginx/">статье</a>.</p>
<p>Если у вас нет файла <tt>/etc/nginx/htpasswd</tt>, то сгенерируйте его:</p>
<pre><code>echo "<USERNAME>:`openssl passwd -apr1`" > /etc/nginx/htpasswd
</code></pre>
<p>Укажите нужный вам <USERNAME>.</p>
<h2>Обновление</h2>
<p>Если вы захотите обновить Sentry, то это сделать довольно просто:</p>
<pre><code>cd onpremise
git pull
docker-compose build
docker-compose run --rm web upgrade
</code></pre>
<p>После чего перезапустите сервис, для systemd это делается так:</p>
<pre><code>sudo systemctl restart docker-sentry-onpremise
</code></pre>
<p>Это всё, что я хотел рассказать, пишите свои вопросы в комментариях к этой статье.</p>
<h2>Если вы используете Gmail</h2>
<p>В случае, если вы используете Gmail как SMTP сервер, то можете столкнуться с проблемой блокировки вашего сервиса Sentry серверами Google.</p>
<p>Для решения проблемы необходимо перейти в раздел "Настройки Безопасности" вашей учетной записи Gmail <a rel="nofollow" href="https://myaccount.google.com/security">https://myaccount.google.com/security</a> и разрешить доступ от "Ненадежных приложений":</p>
<p><img src="/media/uploads/security.png" alt="Gmail Security" /></p>
Запуск кастомных хуков в Gitolite3
2019-02-06T10:47:46+03:00https://adw0rd.com/2019/02/06/vref-hooks-gitolite3/Иногда появляется необходимость в использовании хуков, но хуки настраиваются свои для каждого определённого репозитория. Но иногда необходимо иметь общий хук для всех заданных репозиториев. Такое можно реализовать средствами всё того же Gitolite3. Настройка локального gitolite.conf Откройте файл conf/gitolite.conf репозитория gitolite-admin на редактирование и укажите для каких репозиториев (в нашем примере…
<p><img src="/media/uploads/git.png" class="alignright whitespace"></p>
<p>Иногда появляется необходимость в использовании хуков, но хуки настраиваются свои для каждого определённого репозитория. Но иногда необходимо иметь общий хук для всех заданных репозиториев. Такое можно реализовать средствами всё того же Gitolite3.</p>
<p><a name="more"></a></p>
<h2>Настройка локального gitolite.conf</h2>
<p>Откройте файл <tt>conf/gitolite.conf</tt> репозитория <tt>gitolite-admin</tt> на редактирование и укажите для каких репозиториев (в нашем примере будет для всех) нужно вызвать хук и для каких пользователей:</p>
<pre><code>repo @all
- VREF/my_hook = @all
</code></pre>
<h2>Настройка .gitolite.rc на сервере</h2>
<p>Войдите на сервер и откройте на редактирование файл <tt>~/.gitolite.rc</tt>, найдите строку <tt>LOCAL_CODE</tt> и раскомментируйте её:</p>
<pre><code># this one is managed directly on the server
LOCAL_CODE => "$ENV{HOME}/local",
</code></pre>
<p>Создайте скрипт хука, например, со следующим содержимым и разместите по пути <tt>~/local/VREF/my_hook</tt>:</p>
<pre><code>#!/bin/sh
if [ $GL_REPO != "gitolite-admin" ]; then
echo "-----------------------------------------------------------"
echo "You repository here: https://git.example.org/$GL_REPO/"
echo "-----------------------------------------------------------"
fi
</code></pre>
<p>Чтобы Gitolite принял ваши изменения надо его перекомпилировать:<br />
<code>gitolite compile</code></p>
Готовим Jenkins CI в Docker
2019-01-30T13:30:37+03:00https://adw0rd.com/2019/01/30/jenkins-ci-docker/Для большей изоляции и переносимости Jenkins рекомендую запускать его в контейнере, для этого проще всего использовать готовое решение docker-jenkinsci. Ниже расскажу как запустить его, настроить Nginx и перенести данные. Установка Можете использовать мой немного исправленный форк docker-jenkinsci, но лучше сделайте свой со своими настройками. После чего склонируйте к себе на…
<p><img src="/media/uploads/docker_jenkins.png" class="alignright" style="transform: scale(-1, 1);width:200px" /></p>
<p>Для большей изоляции и переносимости Jenkins рекомендую запускать его в контейнере, для этого проще всего использовать готовое решение <a rel="nofollow" href="https://github.com/adw0rd/docker-jenkinsci">docker-jenkinsci</a>. Ниже расскажу как запустить его, настроить Nginx и перенести данные.</p>
<p><a name="more"></a></p>
<h2>Установка</h2>
<p>Можете использовать мой немного исправленный форк <a rel="nofollow" href="https://github.com/adw0rd/docker-jenkinsci">docker-jenkinsci</a>, но лучше сделайте свой со своими настройками. После чего склонируйте к себе на сервер:</p>
<pre><code>cd ~example
git clone https://github.com/adw0rd/docker-jenkinsci.git
cd docker-jenkinsci
</code></pre>
<p>Доустановите Nginx, Docker и Docker Compose:</p>
<pre><code>sudo apt install nginx docker.io docker-compose
</code></pre>
<h2>Настройка Nginx</h2>
<p>Подключим сертификаты и будем проксировать на localhost:8080 (или какой угодно другой адрес, указанный в docker-compose.yml)</p>
<pre><code>server {
listen 443 ssl;
server_name jenkins.example.org;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
auth_basic "closed site"; # Рекомендую прятать Jenkins за base-auth, в Jenkins часто находят уязвимости, продукт популярный. Общая Basic Auth это дополнительная безопасность
auth_basic_user_file htpasswd; # Ниже показываю как сгенерировать этот файл, который Nginx будет искать в /etc/nginx
location / {
proxy_set_header Authorization ""; # Тут мы сбрасываем заголовок с Basic Auth, иначе Jenkins пытается его использовать для авторизации пользователя в своем интерфейсе. Я разделяю эти авторизации для себя
proxy_pass http://localhost:8080;
}
}
server {
listen 80;
server_name jenkins.example.org;
return 301 https://$host$request_uri;
}
</code></pre>
<p>Как получить бесплатные SSL-сертификаты от LetsEncypt читаем в <a rel="nofollow" href="/2018/10/16/https-certbot-letsencrypt-ssl-tls-nginx/">статье</a>.</p>
<p>Для генерации htpasswd файла выполните:</p>
<pre><code>echo "<USERNAME>:`openssl passwd -apr1`" > /etc/nginx/htpasswd
</code></pre>
<p>Укажите нужный вам <USERNAME>.</p>
<h2>Установка Jenkins в Docker</h2>
<p>Запускаем Jenkins:</p>
<p><code>docker-compose up</code></p>
<p>Во время запуска, в консоли отобразиться пароль, скопируйте и введите его в поле "Administrator password" веб-интерфейса (по адресу https://jenkins.example.org ).</p>
<p>Вас спросят какие плагины установить, советую выбрать "Select plugins to install" и выбрать минимальный подходящий вам набор (Git, Build Timeout и прочие на которых стоит галочка и вы осознаете для чего выбранный плагин вам нужен, остальное доустановите потом).</p>
<p>Далее вас попросят создать учетную запись "Create First Admin User", думаю справитесь.</p>
<h2>Авто-запуск Jenkins в Docker</h2>
<p>Теперь вы можете запускать Jenkins в бекграунде:</p>
<p><code>docker-compose up -d</code></p>
<p>Или лучше разместите сервис-файл для systemd по пути <tt>/etc/systemd/system/docker-jenkinsci.service</tt>:</p>
<pre><code>[Unit]
Description=Docker Compose Service for Jenkins CI
Requires=docker.service
After=docker.service
[Service]
WorkingDirectory=/home/example/docker-jenkinsci
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose down
TimeoutStartSec=0
Restart=on-failure
[Install]
WantedBy=multi-user.target
</code></pre>
<p>Теперь активируем и запустим наш сервис:</p>
<p><code>sudo systemctl enable docker-jenkinsci
sudo systemctl start docker-jenkinsci</code></p>
<p>Вы также можете узнать статус, остановить или перезапустить сервис:</p>
<p><code>sudo systemctl status docker-jenkinsci
sudo systemctl stop docker-jenkinsci
sudo systemctl restart docker-jenkinsci</code></p>
<p>Не забывайте после каждого изменения service-файла перезапускать systemd:</p>
<p><code>systemctl daemon-reload</code></p>
<h2>Перенос рабочих файлов для Jenkins</h2>
<p>По умолчанию имя запущенного контейнера "docker-jenkinsci_master_1", поэтому ниже будет использовано именно это имя. </p>
<p>Если у вас имеется сожержимое каталога от прежнего Jenkins (jobs, workspaces, конфиги), то можно закинуть все эти данные в volume:</p>
<pre><code>cat var_lib_jenkins.tgz | docker exec -i docker-jenkinsci_master_1 tar Cxzf /var/jenkins_home/ -
</code></pre>
<p>После чего, вы можете войти в контейнер и проверить правильно ли вы разместили данные:</p>
<pre><code>docker exec -uroot -ti docker-jenkinsci_master_1 bash
cd ~jenkins
</code></pre>
<p>Далее перезапустите Jenkins и проверьте корректность работы через веб-интерфейс:</p>
<p><code>systemctl restart docker-jenkinsci</code></p>
<h2>Бекап данных</h2>
<p>Чтобы сдампить все файлы из volume контейнера можно использовать <tt>docker cp</tt>:</p>
<p><code>docker cp -a docker-jenkinsci_master_1:/var/jenkins_home backup_dir</code></p>
<p>Задавайте свои вопросы в комментариях.</p>
Как в Gitolite3 изменить имя пользователя или путь до домашнего каталога?
2019-01-18T07:13:26+03:00https://adw0rd.com/2019/01/18/gitolite3-change-username-or-home-dir/Бывает необходимо, во время переезда данных на другой сервер, указать отличную от дефолтной директорию где расположены рабочие файлы Gitolite и каталоги с репозиториями. Решил записать как это сделать, а то всё время забываю. Установим gitolite3: sudo apt install gitolite3 Когда запросит ключ - прервите установку, после чего запустите "dpkg-reconfigure", чтобы…
<p><img src="/media/uploads/git.png" class="alignright whitespace"></p>
<p>Бывает необходимо, во время переезда данных на другой сервер, указать отличную от дефолтной директорию где расположены рабочие файлы Gitolite и каталоги с репозиториями. Решил записать как это сделать, а то всё время забываю.</p>
<p><a name="more"></a></p>
<p>Установим gitolite3:</p>
<pre><code>sudo apt install gitolite3
</code></pre>
<p>Когда запросит ключ - прервите установку, после чего запустите "dpkg-reconfigure", чтобы настроить пакет:</p>
<pre><code>sudo dpkg-reconfigure gitolite3
</code></pre>
<p>Там вы сможете изменить имя, путь до домашнего каталога и указать публичный ключ администратора.</p>
<p>Если у вас уже был установлен и настроен пакет, а хотите только переименовать пользователя и его группу, как было в моем случае, то достаточно выполнить эти команды:</p>
<pre><code>sudo usermod gitolite3 --login git
sudo groupmod gitolite3 -n git
</code></pre>
<p>Вся информация была взята <a rel="nofollow" href="https://askubuntu.com/questions/853140/how-do-i-configure-gitolite3-to-use-the-git-account-on-ubuntu-lts-16">отсюда</a>.</p>
Опровержение по обвинению в киберпреступлениях
2018-12-13T11:42:11+03:00https://adw0rd.com/2018/12/13/refute/Фото с wikipedia Некоторое время назад моё имя стало всплывать в СМИ, указывая на мою причастность к противозаконным действиям, к которым я не имею никакого отношения. Я всегда осознаю чем я занимаюсь и никогда не занимался, не занимаюсь и не планирую заниматься ничем противозаконным, это могут подтвердить все те с…
<div style="float:right;text-align:right">
<img src="/media/uploads/fbi.svg" width="150"><br>
<small>Фото с <a rel="nofollow" href="https://en.wikipedia.org/wiki/Federal_Bureau_of_Investigation">wikipedia</a></small>
</div>
<p>Некоторое время назад моё имя стало всплывать в СМИ, указывая на мою причастность к противозаконным действиям, к которым я не имею никакого отношения. Я всегда осознаю чем я занимаюсь и никогда не занимался, не занимаюсь и не планирую заниматься ничем противозаконным, это могут подтвердить все те с кем я когда-либо работал или общался. </p>
<p><a name="more"></a></p>
<p>Прошу <strong>дать опровержение</strong> в СМИ (такие как редакция <a rel="nofollow" href="https://www.buzzfeednews.com/article/craigsilverman/who-ran-methbot-3ve-ad-fraud">BuzzFeed</a>, <a rel="nofollow" href="https://krebsonsecurity.com/2016/12/report-3-5m-in-ad-fraud-daily-from-methbot/#more-37299">Krebs on Security</a>, <a rel="nofollow" href="https://www.forbes.com/sites/daveywinder/2018/11/28/how-russian-pornhub-hackers-pulled-off-30m-advertising-scam/">Forbes</a>) в связи с <strong>ошибочным или необоснованным</strong> внесением меня в списки обвиняемых в нарушении законодательства.</p>
<p>Прошу ФБР исключить моё имя из списка подозреваемых, так как я никогда никаких противозаконных действий (описанных <a rel="nofollow" href="https://www.justice.gov/usao-edny/pr/two-international-cybercriminal-rings-dismantled-and-eight-defendants-indicted-causing">в документе</a> или каких-либо других) <strong>не совершал</strong>. </p>
<p>В 2016 году я уже реагировал на подобные обвинения со стороны редакции <a rel="nofollow" href="https://krebsonsecurity.com/2016/12/report-3-5m-in-ad-fraud-daily-from-methbot/#more-37299">Krebs on Security</a>, давал интервью журналу <a rel="nofollow" href="https://republic.ru/posts/77732">The Republic</a> с опровержением какой-либо с моей стороны противозаконной деятельности. Прилагаю к этой статье скриншоты переписки с журналистом издания <strong>The Republic</strong>:</p>
<div style="float:left;margin-right:20px"><img src="/media/uploads/1_NDiH2gU.PNG" width="300"></div>
<div><img src="/media/uploads/2_e8WSbpJ.PNG" width="300"></div>
<div style="float:left;margin-right:20px"><img src="/media/uploads/3_rLYsx3B.PNG" width="300"></div>
<div><img src="/media/uploads/4_fdYElQD.PNG" width="300"></div>
<p><br></p>
<p>В дополнение к вышесказанному хочу написать ещё несколько слов о себе: я веду открытый публичный образ жизни, занимаюсь спортом и скейтбордингом, работаю на аутсорсинге программистом, моё направление это разработка веб-приложений на Python, Erlang и JavaScript, анализ данных и консалтинговые услуги. В свободное время <a rel="nofollow" href="https://adw0rd.com/">веду технический блог</a>. По своей натуре являюсь открытым, позитивным и законопослушным человеком. Любая противоправная деятельность для меня не естественна по своей природе. </p>
<p>Уверен, что люди и организации, которые имеют доступ ко всей моей личной, конфиденциальной информации и занимаются данным расследованием - легко могут убедиться в том что я НЕВИНОВЕН.</p>
<p><strong>P.S.:</strong> С большим уважением отношусь к профессии репортера, но прошу не беспокоить меня по вышеописанному вопросу, так как я не могу к этому ещё что-либо добавить.</p>
<p>С уважением, Михаил Андреев.</p>
Запуск связки Elasticsearch + Logstash + Kibana на Docker Compose
2018-10-24T02:26:09+03:00https://adw0rd.com/2018/10/24/docker-elk-elasticsearch-logstash-kibana/В этой статье мы запустим связку для удобной работы с вашими логами. Для реализации нам понадобится: Elasticsearch - одно из лучших решений по полнотекстовому поиску и фильтрации данных, Logstash - удобный интерфейс к Elasticsearch для записи логов (фильтрация, сбор и трансформация), ну а Kibana - это веб-интерфейс для визуализации и…
<p>В этой статье мы запустим связку для удобной работы с вашими логами. Для реализации нам понадобится: <strong>Elasticsearch</strong> - одно из лучших решений по полнотекстовому поиску и фильтрации данных, <strong>Logstash</strong> - удобный интерфейс к Elasticsearch для записи логов (фильтрация, сбор и трансформация), ну а <strong>Kibana</strong> - это веб-интерфейс для визуализации и чтения данных из Elasticsearch.</p>
<p><a name="more"></a></p>
<p>Данную связку <a rel="nofollow" href="https://smappi.org/documentation/team/">мы</a> используем для <a rel="nofollow" href="https://smappi.org/documentation/logging/">хранения логов запущенных API на Smappi</a>. </p>
<p>Всю эту связку проще запустить в контейнерах, которые будет контролировать Docker Compose. Для этих целей хорошо подходит проект <a rel="nofollow" href="https://github.com/deviantony/docker-elk">docker-elk</a>, в который входит всё что нам нужно, настроим его и запустим.</p>
<h3>Установка Docker Compose и ELK</h3>
<p>Помимо <strong>docker.io</strong> и <strong>docker-compose</strong> нам ещё понадобится <strong>git</strong>:<br />
<code>sudo apt update
sudo apt install docker.io docker-compose git</code></p>
<p>Теперь скачаем <strong>docker-elk</strong>:<br />
<code>cd ~
git clone https://github.com/deviantony/docker-elk</code></p>
<p>Я использую свой приватный форк, в котором переопределены настройки для Elasticsearch и Logstash, рекомендую вам иметь тоже свой форк, а не использовать оригинал. Так вы сможете хранить свои изменения и делиться ими со своей командой.</p>
<h3>Настройка ELK</h3>
<p>Чтобы была возможность работы с Docker от вашего пользователя, необходимо его добавить в группу "docker":</p>
<p><code>sudo usermod -a -G docker <USERNAME></code></p>
<p>Чтобы после перезапуска контейнера не потерялись данные, которые хранятся в Elasticsearch, рекомендую подключить свой Volume. Для этого надо добавить запись в <tt>volumes</tt> секции <tt>elasticsearch</tt> файла <tt>docker-compose.yml</tt>:</p>
<p><code>services:
elasticsearch:
volumes:
- ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro
- <Абсолютный путь до вашего хранилища>elasticsearch/data:/usr/share/elasticsearch/data:rw</code></p>
<p>Теперь если перезапустить контейнер данные подцепятся к вашему Elasticsearch автоматически.</p>
<h3>Запуск ELK</h3>
<p>Перейдём в каталог "docker-elk" и запустим команду, которая поднимет нам все 3 сервиса, объединенные в одну сеть:</p>
<p><code>cd docker-elk
docker-compose up</code></p>
<p>Если все запустилось без ошибок, то остановим контейнеры по Ctrl+C и ниже напишем service-файл для systemd, чтобы после перезагрузки системы он сам запускал и следил за состоянием запущенных контейнеров.</p>
<h3>Настроим Nginx</h3>
<p>Сначала создадим htpasswd-файл для Базовой HTTP авторизации. Укажите свой <strong>USERNAME</strong> и по запросу введите пароль:</p>
<p><code>echo "<USERNAME>:`openssl passwd -apr1`" > /etc/nginx/htpasswd</code></p>
<p>Для примера хост для Elasticsearch будет "elastic.example.org", а хост для Kibana "kibana.example.org". Теперь создайте файл <tt>/etc/nginx/sites-available/elk.conf</tt> и разместите в нём следующее содержимое:</p>
<pre><code>server {
listen 80;
server_name elastic.example.org kibana.example.org;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate /etc/nginx/certs/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/nginx/certs/privkey.pem; # managed by Certbot
ssl_session_cache shared:SSL:10m;
client_max_body_size 20M;
server_name elastic.example.org;
satisfy any;
allow <TRUSTED IP ADDRESS HERE>; # Укажите IP адрес, которому вы доверяете (например соседний сервер откуда вы отправляете запросы к Elastic)
allow <LOCAL IP ADDRESS HERE>; # Если вы на одном сервере запустились, то укажите локальный адрес (127.0.0.1, 192.168.0.1 или какой-либо другой, посмотрите через какой интерфейс осуществяется передача данных)
# allow 172.17.0.0/24;
deny all;
auth_basic "closed site";
auth_basic_user_file htpasswd;
location / {
proxy_pass http://localhost:9200;
}
}
server {
listen 443 ssl;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate /etc/nginx/certs/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/nginx/certs/privkey.pem; # managed by Certbot
ssl_session_cache shared:SSL:10m;
client_max_body_size 20M;
server_name kibana.example.org;
auth_basic "closed site";
auth_basic_user_file htpasswd;
location / {
proxy_pass http://localhost:5601;
}
}
</code></pre>
<p>Как получить SSL/TSL ключи читайте мою другую статью "<a rel="nofollow" href="/2018/10/16/https-certbot-letsencrypt-ssl-tls-nginx/">Получаем бесплатный SSL/TLS-сертификат от Let's Encrypt и настраиваем Nginx</a>".</p>
<blockquote class="info">
Если по какой-либо причине вы не хотите использовать HTTPS, то можно использовать вот такой конфиг:
<pre><code>client_max_body_size 20M;
auth_basic "closed site";
auth_basic_user_file htpasswd;
server {
listen 80;
server_name elastic.example.org;
location / {
proxy_pass http://localhost:9200;
}
}
server {
listen 80;
server_name kibana.example.org;
location / {
proxy_pass http://localhost:5601;
}
}
</code></pre>
</blockquote>
<p>Создадим симлинк для активации конфига и перезапустим Nginx:<br />
<code>ln -s /etc/nginx/sites-available/elk.conf /etc/nginx/sites-enabled/elk.conf
nginx -s reload</code></p>
<p>Зайдём браузером по адресу <tt>kibana.example.org</tt>, вы должны увидеть примерно следующий интерфейс:</p>
<p><img src="https://adw0rd.com/media/uploads/kibana_smappi.png" width="100%"></p>
<p>Перейдите в <tt>Management > Index Patterns</tt> и нажмите на кнопку <tt>Create Index Pattern</tt>, введите "logstash-*" в поле <tt>Index pattern</tt>, а в качестве значения для поля <tt>Time Filter field name</tt> укажите "datetime", таким образом вы настроите шаблон для матчинга всех данных от Logstash. </p>
<h3>Автозапуск ELK</h3>
<p>Создадим service-файл в каталоге <strong>systemd</strong>:</p>
<p><code>sudo emacs /etc/systemd/system/docker-compose-elk.service</code></p>
<p>И внесём следующее содержимое:</p>
<pre><code>[Unit]
Description=ELK: Docker Compose Application Service
Requires=docker.service
After=docker.service
[Service]
WorkingDirectory=/home/<USERNAME>/docker-elk
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose down
TimeoutStartSec=0
Restart=on-failure
[Install]
WantedBy=multi-user.target
</code></pre>
<p>Теперь активируем и запустим наш сервис:</p>
<p><code>sudo systemctl enable docker-compose-elk
sudo systemctl start docker-compose-elk</code></p>
<p>Вы также можете узнать статус, остановить или перезапустить сервис:</p>
<p><code>sudo systemctl status docker-compose-elk
sudo systemctl stop docker-compose-elk
sudo systemctl restart docker-compose-elk</code></p>
<p>Не забывайте после каждого изменения service-файла перезапускать systemd:</p>
<p><code>systemctl daemon-reload</code></p>
<p>Собственно это всё что требуется знать для начального запуска ELK, свои вопросы можете оставить в комментариях к этой статье.</p>
Миграция БД из MySQL в PostgreSQL с помощью pgloader
2018-10-17T14:37:37+03:00https://adw0rd.com/2018/10/17/mysql-to-postgres-migration-pgloader/Почти все мои сервисы работают на Postgres, пришло время и этому блогу переехать с MySQL. Для миграции я использовал программу pgloader, которая написана на Сommon Lisp, мне она показалась довольно простой и удобной в использовании. Установка pgloader на Ubuntu apt update apt install pgloader Миграция структуры и данных Войдем как…
<p>Почти все мои сервисы работают на Postgres, пришло время и этому блогу переехать с MySQL. Для миграции я использовал программу <a rel="nofollow" href="https://github.com/dimitri/pgloader">pgloader</a>, которая написана на Сommon Lisp, мне она показалась довольно простой и удобной в использовании.</p>
<p><a name="more"></a></p>
<h3>Установка pgloader на Ubuntu</h3>
<p><code>apt update
apt install pgloader</code></p>
<h3>Миграция структуры и данных</h3>
<p>Войдем как пользователь postgres и запустим клиент:</p>
<p><code>sudo su - postgres
psql</code></p>
<p>В клиенте создадим пользователя <strong>USERNAME</strong> с паролем <strong>PASSWORD</strong>, наделим его правами (LOGIN - может логинится, NOSUPERUSER - не является суперпользователем, CREATEDB - может создавать БД). А также создадим одноименную с MySQL базу данных и по Ctrl+D выйдем:</p>
<p><code>postgres=# create role <USERNAME> password '<PASSWORD>' LOGIN NOSUPERUSER CREATEDB;
postgres=# create database <DBNAME> owner <USERNAME>;
Ctrl+D</code></p>
<p>Под пользователем postgres выполним:</p>
<p><code>pgloader "mysql://root:<MYSQL ROOT PASSWORD>@localhost/<DBNAME>" pgsql:///<DBNAME>
psql -d <DBNAME> -c 'ALTER DATABASE <DBNAME> SET search_path TO <DBNAME>, public;'
</code></p>
<p>Всё, миграция закончена. С подробностями можно ознакомится в <a rel="nofollow" href="https://tapoueh.org/blog/2017/07/from-mysql-to-postgresql/">статье автора pgloader</a></p>
Получаем бесплатный SSL/TLS-сертификат от Let's Encrypt и настраиваем Nginx
2018-10-16T10:05:40+03:00https://adw0rd.com/2018/10/16/https-certbot-letsencrypt-ssl-tls-nginx/Сегодня расскажу как получить бесплатный SSL/TLS-сертификат от Let's Encrypt для своих сайтов под Nginx с помощью ACME клиента certbot (программа для получения и обновления сертификата от Let's Encrypt). Проект Let’s Encrypt создан для того, чтобы большая часть интернет-сайтов смогла перейти к шифрованным подключениям (HTTPS). В отличие от коммерческих центров сертификации,…
<p>Сегодня расскажу как получить бесплатный SSL/TLS-сертификат от Let's Encrypt для своих сайтов под Nginx с помощью ACME клиента <strong>certbot</strong> (программа для получения и обновления сертификата от Let's Encrypt).</p>
<blockquote>
<p>Проект <a rel="nofollow" href="https://letsencrypt.org/">Let’s Encrypt</a> создан для того, чтобы большая часть интернет-сайтов смогла перейти к шифрованным подключениям (HTTPS). В отличие от коммерческих центров сертификации, в данном проекте не требуется оплата... <a rel="nofollow" href="https://ru.wikipedia.org/wiki/Let%E2%80%99s_Encrypt">продолжить читать на Википедии</a></p>
</blockquote>
<p><a name="more"></a></p>
<h3>Установка зависимостей</h3>
<p>Установим certbot и плагин для Nginx, чтобы он сам настроил нужные хосты в Nginx:</p>
<pre><code>apt install certbot python3-certbot-nginx
</code></pre>
<h3>Получаем сертификат для одного домена</h3>
<p>Вызываем certbot и передаем ему информацию для генерации сертификата:</p>
<pre><code>certbot --nginx -n --agree-tos -m <Ваш Email> -d <Ваше Доменное Имя>
</code></pre>
<p>где:</p>
<ul>
<li><strong>--nginx</strong> - Заставляет плагин для Nginx перенастраивать хосты в конфигах Nginx (указывает пути до сертификатов, если нет секции для ssl, то создаст их для указанного домена)</li>
<li><strong>-n</strong> - Не интерактивный режим, чаще используется для вставки в скрипты деплоя</li>
<li><strong>--agree-tos</strong> - Согласие с ACME server's Subscriber Agreement (ToS - Terms of service)</li>
<li><strong>-m</strong> - Ваш Email</li>
<li><strong>-d</strong> - Ваше доменное имя</li>
</ul>
<p>Пример:</p>
<pre><code>certbot --nginx -n --agree-tos -m example@example.org -d example.com
certbot --nginx -n --agree-tos -m example@example.org -d or.sub.domain.example.com
</code></pre>
<h3>Подтверждение прав на домен</h3>
<p>Теперь вам необходимо подтвердить права на указанное доменное имя. Для этого можно создать location в конфиге Nginx:</p>
<p><code>
server {
server_name <Ваше Доменное Имя>;
location ~* ^/\.well-known/acme-challenge/<AUTH TOKEN BY CERTBOT>/$ {
return 200;
}
}</code></p>
<p>Либо создать TXT-запись для домена, что более удобно:</p>
<pre><code>_acme-challenge.<Ваше Доменное Имя>. IN TXT "<AUTH TOKEN BY CERTBOT>"
</code></pre>
<p>Пример:</p>
<pre><code>_acme-challenge.example.org. IN TXT "4sV67H271Eil3VHA-9TuXjANP6cP_uXmyJoTInalPaY"
</code></pre>
<p>Некоторые подробности можно посмотреть <a rel="nofollow" href="https://serverfault.com/questions/750902/how-to-use-lets-encrypt-dns-challenge-validation">на ServerFault</a></p>
<h3>Получаем Wildcard-сертификат</h3>
<p>Если у вас много поддоменов, то проще получить Wildcard-сертификат (на все поддомены), эта возможность появилась у Let’s Encrypt весной 2018 года.</p>
<pre><code>certbot certonly --preferred-challenges=dns --agree-tos -m <Ваш Email> -d *.<Ваше Доменное Имя> -d <Ваше Доменное Имя> --manual --server https://acme-v02.api.letsencrypt.org/directory
</code></pre>
<p>где:</p>
<ul>
<li><strong>certonly</strong> - Получить только сертификат (но не устанавливать/применять его)</li>
<li><strong>--preferred-challenges</strong> - Подтверждение прав на домен удобнее проводить через DNS</li>
<li><strong>--agree-tos</strong> - Согласие с ACME server's Subscriber Agreement (ToS - Terms of service)</li>
<li><strong>-m</strong> - Ваш Email</li>
<li><strong>-d</strong> - Ваше доменное имя с префиксом "*."</li>
<li><strong>-d</strong> - Сразу следом указываем каноническое имя домена, если вы им пользуетесь</li>
<li><strong>--manual</strong> - Получить сертификат вручную (без помощи плагина для nginx)</li>
<li><strong>--server</strong> - Для Wildcard надо указать endpoint (возможные можно посмотреть <a rel="nofollow" href="https://letsencrypt.org/docs/acme-protocol-updates/">тут</a>)</li>
</ul>
<p>Пример:</p>
<pre><code>certbot certonly --preferred-challenges=dns --agree-tos -m example@example.org -d *.example.org -d example.org --manual --server https://acme-v02.api.letsencrypt.org/directory
</code></pre>
<p>Во время запуска этой команды добавьте по запросу две TXT-записи к своему домену, например:</p>
<pre><code>_acme-challenge.example.org. 60 IN TXT "4RMgK51t1OMNHPJVmOsIPIIkBnad5oUxk_WK6FTmo1w"
_acme-challenge.example.org. 60 IN TXT "M5QE7dXdS0RDEZ2uAno-PEfkc_6x5HftXtNJpEd87mI"
</code></pre>
<p>После того как добавили указанные <strong>certbot</strong> ключи, подтвердите через него их корректность, если все верно, то он сохранит ваши сертификаты в своем каталоге.</p>
<h3>Посмотреть список полученных сертификатов</h3>
<p>После ввода команды:</p>
<pre><code>certbot certificates
</code></pre>
<p>можно посмотреть список полученных сертификатов и срок их инвалидации</p>
<h3>Проверка сертификатов домена</h3>
<pre><code>openssl s_client -connect example.org:443 -servername example.org
</code></pre>
<p>Если в ответ получили:</p>
<pre><code>no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 5 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Start Time: 1630349404
Timeout : 7200 (sec)
Verify return code: 0 (ok)
</code></pre>
<p>То может помочь установка ssl в секции listen:</p>
<pre><code>server {
listen 443 ssl;
server_name example.org;
ssl_certificate fullchain.pem;
ssl_certificate_key privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}
</code></pre>
Монтируем Google Drive диск через gdfs на Ubuntu 17.04
2017-12-29T10:28:12+03:00https://adw0rd.com/2017/12/29/google-drive-ubuntu-gdfs/Я использую Google Drive для хранения бэкапов на некоторых своих серверах, но можно выдумать множество сценариев использования этого облачного хранилища. Для этого нам необходимо будет установить клиентскую библиотеку для работы с API Google, драйвер gdfs, получить код авторизации и настроить автоматическое монтирование при загрузке сервера. Установка Google API клиента Клиент…
<p>Я использую Google Drive для хранения бэкапов на некоторых своих серверах, но можно выдумать множество сценариев использования этого облачного хранилища.</p>
<p>Для этого нам необходимо будет установить клиентскую библиотеку для работы с API Google, драйвер gdfs, получить код авторизации и настроить автоматическое монтирование при загрузке сервера.</p>
<p><a name="more"></a></p>
<h3>Установка Google API клиента</h3>
<p>Клиент нужен для авторизации gdfs</p>
<pre><code>sudo apt install -y python-pip
git clone https://github.com/google/google-api-python-client
cd google-api-python-client
sudo python setup.py install install_egg_info
</code></pre>
<h3>Установка Google Drive FS</h3>
<p>Установим gdrivefs и сделаем симлинк для удобства</p>
<pre><code>sudo pip install gdrivefs
cd /sbin
sudo ln -s `which gdfs` mount.gdfs
</code></pre>
<h3>Настройка</h3>
<p>Надо пройти авторизацию для Google API Client и создать каталог для монтирования диска</p>
<pre><code>gdfstool auth -u
# Откройте ссылку в вашем браузере, пройдите авторизацию
# и скопируйте полученный ключ как <code>
gdfstool auth -a /var/.gdfs.creds "<code>"
mkdir /mnt/gdrivefs
</code></pre>
<h3>Монтирование через /etc/fstab</h3>
<p>Вы можете добавить следующую запись в <tt>/etc/fstab</tt></p>
<pre><code>echo "/var/.gdfs.creds /mnt/gdrivefs gdfs allow_other,big_writes 0 0" >> /etc/fstab
mount /mnt/gdrivefs
</code></pre>
<p>Но монтирование происходит до сетевого подключения сервера, а без сети вы не сможете подключить Google Drive, так что после перезагрузки сервера у вас возникнут проблемы с монтированием. Можете добавить опцию _netdev:</p>
<pre><code>echo "/var/.gdfs.creds /mnt/gdrivefs gdfs allow_other,big_writes,_netdev 0 0" >> /etc/fstab
mount /mnt/gdrivefs
</code></pre>
<p>Но у меня не получилось использовать эту опцию:</p>
<pre><code>fuse: unknown option `_netdev'
</code></pre>
<h3>Монтирование через /etc/network/interfaces</h3>
<p>Я монтирую диск следующим способом</p>
<pre><code>echo "post-up /bin/mount /var/.gdfs.creds /mnt/gdrivefs -t gdfs -o allow_other,big_writes" >> /etc/network/interfaces
</code></pre>
<p>После перезагрузки сервера у вас запустится post-up и смонтирует диск.</p>
<h3>Проверяем</h3>
<p>Смонтируем вручную и посмотрим файлы:</p>
<pre><code>mount /var/.gdfs.creds /mnt/gdrivefs -t gdfs -o allow_other,big_writes
ls -la /mnt/gdrivefs
</code></pre>
<p>Рекомендую перезагрузится и проверить что все работает.</p>
Django. Запуск проекта в связке uWSGI и Nginx
2016-02-02T19:42:47+03:00https://adw0rd.com/2016/02/02/django-nginx-uwsgi/Очень часто меня просят рассказать как запустить django-проект в продакшен. Я обычно кидаю несколько конфигов со своих проектов и пишу как все установить и запустить. В 2012-ом начал писать эту статью, но как и многие другие мои статьи она попала в бездну прокрастинации. Пришло время дописать эту статью, чтобы больше…
<p><img src="http://adw0rd.com/media/uploads/logo_uWSGI.png" class="alignright"><br />
Очень часто меня просят рассказать как запустить django-проект в продакшен. Я обычно кидаю несколько конфигов со своих проектов и пишу как все установить и запустить. В 2012-ом начал писать эту статью, но как и многие другие мои статьи она попала в бездну прокрастинации.</p>
<p>Пришло время дописать эту статью, чтобы больше не объяснять всем и каждому почему и как запустить проект на Django под uWSGI.</p>
<p><a name="more"></a></p>
<h3>Почему uWSGI?</h3>
<p>Вкратце, он стабильный, гибкий в настройке и работает довольно быстро. По крайне мере, в большинстве бенчмарков, что я смотрел в своё время, он был быстрее его конкурентов. Например, в сравнении с Gunicorn:</p>
<p><a rel="nofollow" href="https://ivan-site.com/2012/09/benchmark-uwsgi-vs-gunicorn-for-async-workers/"><img src="http://adw0rd.com/media/uploads/pong_results.png"></a></p>
<p>Гуглятся результаты тестов производительности довольно быстро, так что оставлю за вами выбор источников информации. Либо можете самостоятельно написать тесты и мы вместе позапускаем их :-)</p>
<h3>Настройка uWSGI</h3>
<p>Сначала установим uWSGI в ваше <a rel="nofollow" href="/2012/6/19/python-virtualenv/ ">виртуальное окружение</a>:</p>
<pre><code>cd ~/<project_name>
source ~/<venv_path>/bin/activate
pip install uWSGI
</code></pre>
<p>зафиксируем версию в файл зависимостей</p>
<pre><code>pip freeze|grep -i uwsgi >> requirements.txt
</code></pre>
<p>Добавим в проект файл <tt>uwsgi/production.ini</tt> (каталог "uwsgi" нужен, потому что у меня обычно несколько конфигов, например "test.conf" для тестового стенда):</p>
<pre><code>[uwsgi]
chdir=/home/<username>/<project_name>
pidfile=/home/<username>/<project_name>_uwsgi.pid
socket=/home/<username>/<project_name>_uwsgi.sock
chmod-socket=750
virtualenv=/home/<username>/<venv_path>
pythonpath=.
pythonpath=<project_name>
module=<project_name>.wsgi:application
callable=app
master=true
processes=2
harakiri=30
buffer-size=32768
</code></pre>
<p>Разберём опции:</p>
<ul>
<li><tt>chdir</tt> - путь до каталога с проектом;</li>
<li><tt>pidfile</tt> - путь до pid-файла;</li>
<li><tt>socket</tt> - файл UNIX или TCP сокета, рекомендую UNIX (работает немногим быстрее);</li>
<li><tt>chmod-socket</tt> - права на сокет-файл, позже добавим <strong>nginx</strong> в группу "<usergroup>" пользователя "<username>";</li>
<li><tt>virtualenv</tt> - путь до каталога окружения;</li>
<li><tt>pythonpath=.</tt> - добавим текущий от "chdir" каталог в "sys.path";</li>
<li><tt>pythonpath=<project_name></tt> - и каталог с кодом Django-проекта тоже;</li>
<li><tt>module</tt> - наш wsgi-модуль (идет в поставке с Django-проектом)</li>
<li><tt>callable</tt> - WSGI имя;</li>
<li><tt>master</tt> - запуск в режиме master: отдельный процесс будет контроллировать воркер-процессы;</li>
<li><tt>processes</tt> - количество воркер-процессов, которые будут обрабатывать поступающие запросы;</li>
<li><tt>harakiri</tt> - количество секунд после которых подвисший процес будет перезапущен; </li>
<li><tt>buffer-size</tt> - максимальный размер буфера для запроса (заголовки, например cookie и query string), не включает в себя размер тела запроса.</li>
</ul>
<p>Ещё больше опций можете найти в <a rel="nofollow" href="http://uwsgi-docs.readthedocs.org/en/latest/Options.html">оф. документации</a></p>
<h3>Настройка Nginx</h3>
<p>Если вы еще не установили <strong>Nginx</strong>, то сделайте это:</p>
<pre><code>sudo apt-get install nginx
</code></pre>
<p>Создадим в корне проекта файл <tt>nginx/production.conf</tt>, со следующим содержимым:</p>
<pre><code>server {
listen 80;
server_name example.org;
location /media/ {
root /home/<username>/<project_name>_storage;
}
location /static/ {
root /home/<username>/<project_name>_storage;
}
# Иногда бывает нужно отдавать статику не из хранилища,
# а прямо из каталога проекта. Но тогда престанет работать админ-панель Django,
# поэтому надо будет добавить вот такой локейшен:
# location /static/admin/ {
# root /home/<username>/<project_name>_storage;
# }
location / {
uwsgi_pass unix:///home/<username>/<project_name>_uwsgi.sock;
include uwsgi_params;
}
}
</code></pre>
<p>для того, чтобы <strong>Nginx</strong> смог работать с файлом uWSGI-сокета добавим его в группу вашего проекта, в Ubuntu это можно сделать так:</p>
<pre><code>sudo usermod -a -G <usergroup> www-data
</code></pre>
<h3>Настройка Supervisor</h3>
<p>Если он у вас ещё не установлен, то это можно сделать так:</p>
<pre><code>sudo apt-get install supervisor
sudo service supervisor start
</code></pre>
<p>Более <a rel="nofollow" href="http://adw0rd.com/tag/supervisor/">подробно про работу с Supervisor</a> я писал ранее.</p>
<p>На самом деле <strong>uWSGI</strong> может перезапускаться самостоятельно и его не надо контроллировать. Но я предпочитаю чтобы процессы контроллировались внешними утилитами, дабы избежать проблем автономности и всё унифицировать.</p>
<p>Создадим такой конфиг <tt>supervisor/production.conf</tt>:</p>
<pre><code>[program:<project_name>_uwsgi]
environment=PATH="/home/<username>/<venv_path>/bin"
numprocs=1
directory=/home/<username>/<project_name>
command=/home/<username>/<venv_path>/bin/uwsgi uwsgi/production.ini
user=<username>
autostart=true
autorestart=true
redirect_stderr=true
stopwaitsecs=60
stopsignal=INT
stderr_logfile=/home/<username>/logs/%(program_name)s_err.log
stdout_logfile=/home/<username>/logs/%(program_name)s_out.log
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=30
stdout_capture_maxbytes=1MB
</code></pre>
<h3>Запускаем проект</h3>
<p>Создадим для логов каталог в директории пользователя:</p>
<pre><code>mkdir -p /home/<username>/logs
</code></pre>
<p>Сделаем симлинк на конфиг супервайзора нашего проекта, после чего дадим ему знать чтобы он обновился, он запустит по конфигу наш uWSGI-сервис:</p>
<pre><code>sudo ln -s /home/<username>/<project_name>/supervisor/production.conf /etc/supervisor/conf.d/<project_name>.conf
sudo supervisorctl update
sudo supervisorctl status
</code></pre>
<p>После чего сделаем симлинк на Nginx-конфиг и перезапустим его тоже:</p>
<pre><code>sudo ln -s /home/<username>/<project_name>/nginx/production.conf /etc/nginx/sites-enable/<project_name>.conf
sudo nginx -t
sudo nginx -s reload
</code></pre>
<p>Теперь должно работать, српашивайте что непонятно</p>
<h3>Мониторинг и статистика работы uWSGI воркеров</h3>
<p>Чуть позже расскажу про "uwsgi-top". Статья в доработке.</p>
Django. Знакомство с ElasticSearch
2015-12-16T14:30:24+03:00https://adw0rd.com/2015/12/16/django-elasticsearch/Понадобилось на одном из своих проектов установить FTS сервис. Я достаточно долгое время пользовался SphinxSearch, но решил поинтересоваться у общественности (тут и тут) какой сейчас инструмент более популярен и отвечает следующим требованиям: Русская морфология и стемминг Фасетный поиск (аттрибуты) Удобное API для поиска и индексации Простота маштабирования Желательно иметь real-time…
<p>Понадобилось на одном из своих проектов установить <a rel="nofollow" href="https://en.wikipedia.org/wiki/Full_text_search">FTS</a> сервис. Я достаточно долгое время пользовался <a rel="nofollow" href="/tag/sphinxsearch/">SphinxSearch</a>, но решил поинтересоваться у общественности (<a rel="nofollow" href="https://plus.google.com/+MikhailAndreev-adw0rd/posts/Fv7PpzoBKbb">тут</a> и <a rel="nofollow" href="https://www.facebook.com/adw0rd/posts/10153751086558544">тут</a>) какой сейчас инструмент более популярен и отвечает следующим требованиям:</p>
<p><img src="/media/uploads/logo-elastic.png" class="alignright"></p>
<ul>
<li>Русская морфология и стемминг</li>
<li>Фасетный поиск (аттрибуты)</li>
<li>Удобное API для поиска и индексации</li>
<li>Простота маштабирования</li>
<li>Желательно иметь real-time индексы</li>
<li>Желательно наличие админки для анализа и управления индексами</li>
</ul>
<p><a name="more"></a></p>
<p>Большинство коллег использует <a rel="nofollow" href="https://www.elastic.co/">ElasticSearch</a>, почитав про него несколько статей и посмотрев несколько видео с презентациями решил попробовать вторую версию, а именно <strong>ElasticSearch 2.1</strong>. Также вспомнил, что <a rel="nofollow" href="https://www.elastic.co/products/logstash">LogStash</a> стал частью <strong>elastic</strong>, а я на него давно уже гляжу в качестве замены <a rel="nofollow" href="/tag/sentry/">Sentry</a>.</p>
<p>Если кратко, то ElasticSearch, как и Solr, представляет из себя некую оболочку вокруг Lucene. Но именно в этой оболечке все вкусности этого замечательного поискового движка: фасеты, HTTP-JSON API, multitenant (возможность кастомизации поискового индекса под разные группы пользователей), масштабирование, удобный дашборд.</p>
<h2>Установка ElasticSearch 2.1</h2>
<p>В пакетах <strong>Ubuntu 15.10</strong> лежит старая версия 1.6.2, указывать какой-либо ppa я не стал, решил для тестового использования скачать просто архив с официального сайта:</p>
<pre><code>curl -L -O https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.1.0/elasticsearch-2.1.0.tar.gz
tar -xzf elasticsearch-2.1.0.tar.gz
cd elasticsearch-2.1.0/
</code></pre>
<p>Поставим сразу два полезных плагина:</p>
<pre><code># Anticipate Issues & Scale Faster
# https://www.elastic.co/products/marvel
./bin/plugin install elasticsearch/marvel/latest
# Monitoring and Management of instances and clusters
# https://github.com/royrusso/elasticsearch-HQ
./bin/plugin install royrusso/elasticsearch-HQ
</code></pre>
<blockquote class="info">
Если у вас Linux запущен из под VirtualBox, то надо указать в настройках <tt>conf/elasticsearch.yml</tt>:
<code>
network.host: 0.0.0.0
discovery.zen.ping.unicast.hosts: ["10.0.2.2", "127.0.0.1", "[::1]"]</code> где <tt>10.0.2.2</tt> адрес хостовой машины. Узнать его можно в настройках сети VirtualBox, либо посмотреть откуда идут соединения с гостевой машиной <tt>netstat -an| grep SOMEPORT</tt>.
</blockquote>
<p>Отключаем агента марвела в <tt>conf/elasticsearch.yml</tt>, т.к. он нам сейчас не нужен:</p>
<pre><code>marvel.agent.enabled: false
</code></pre>
<p>Запускаем ElasticSearch и проверяем, что сервис запущен:</p>
<pre><code>./bin/elasticsearch
# Должен вернуться JSON
curl http://localhost:9200/
# Проверяем что работает веб-интерфейс Elastic HQ
curl http://localhost:9200/_plugin/hq/
</code></pre>
<p>Если что-то не работает, смотрите STDOUT запущенного инстанса <tt>elasticsearch</tt>.</p>
<h2>Использование под Django</h2>
<p>Загуглив готовое решение для удобной индексации моделей в <strong>Django</strong>, наткнулся на<br />
<a rel="nofollow" href="https://github.com/liberation/django-elasticsearch">django-elasticsearch</a>, поставим его:</p>
<pre><code>pip install elasticsearch
pip install git+https://github.com/liberation/django_elasticsearch.git
</code></pre>
<p>Дальше по документации в <tt>README.md</tt> меняем нашу модель:</p>
<pre><code>from django.db import models
from django_elasticsearch.models import EsIndexable
class Tag(EsIndexable, models.Model):
name = models.CharField(max_length=64)
[...]
</code></pre>
<p>Укажем имя индекса в <tt>settings.py</tt>:</p>
<pre><code>ELASTICSEARCH_DEFAULT_INDEX = '<project_name>'
</code></pre>
<p>Запускаем индекс:</p>
<pre><code> ./manage.py shell
>>> from products.models import Tag
>>> Tag.es.create_index()
>>> Tag.es.reindex_all()
</code></pre>
<p>Удобно при этом смотреть в <strong>Elastic HQ</strong> и видеть как наполнился наш индекс.</p>
<p>Дальше пробуем поискать:</p>
<pre><code>>>> Tag.es.search('something')
[{u'description': u'', u'name': u'Something', u'id': 1}, {u'description': u'', u'name': u'SomeThing2', u'id': 2}, ...]
>>> Tag.es.search('something').deserialize()
[<Tag: 1 Something>, <Tag: 2 SomeThing2>]
</code></pre>
<h2>Вешаемся на сигналы и меняем в реалтайме индекс</h2>
<p>Для этого надо в <tt>settings.py</tt> указать: </p>
<pre><code>ELASTICSEARCH_AUTO_INDEX = True
</code></pre>
<p>Теперь используя сигналы "post_syncdb", "post_save" и "post_delete" мы будем уведомлять поисковый механизм о новых изменениях в модели. Делать ничего не надо, любое изменение или удаление объекта в подключенной через миксин <strong>EsIndexable</strong> модели будет немедленно применено в ElasticSearch.</p>
<h2>Что ещё почитать</h2>
<ul>
<li><a rel="nofollow" href="https://xakep.ru/2015/06/11/elasticsearch-tutorial/">Учимся работать с Elasticsearch</a></li>
<li><a rel="nofollow" href="http://habrahabr.ru/company/wargaming/blog/267503/">Python Meetup 28.08.15: полнотекстовый поиск и Europython 2015</a></li>
</ul>
Git. Разграничения прав по каталогам в репозитории
2015-06-26T18:38:17+03:00https://adw0rd.com/2015/06/26/git-directory-permissions/Из-за соображений безопасности и простоты работы с репозиторием для фрилансеров-верстальщиков, необходимо разграничить права по каталогам. Решить этот вопрос с помощью gitolite никак не получилось, он всеравно оставляет права на чтение файлов. Пришлось создавать два репозитория и через хук гита post-update симметрично обновлять оба репозитория. Создаем дополнительный репозиторий В gitolite это…
<p><img src="http://adw0rd.com/media/uploads/git.png" class="alignright whitespace"></p>
<p>Из-за соображений безопасности и простоты работы с репозиторием для фрилансеров-верстальщиков, необходимо разграничить права по каталогам. Решить этот вопрос с помощью <a rel="nofollow" href="http://adw0rd.com/2012/3/22/freebsd-debian-gitolite/">gitolite</a> никак не получилось, он всеравно оставляет права на чтение файлов. Пришлось создавать два репозитория и через хук гита <strong>post-update</strong> симметрично обновлять оба репозитория.</p>
<p><a name="more"></a></p>
<h3>Создаем дополнительный репозиторий</h3>
<p>В <a rel="nofollow" href="http://adw0rd.com/2012/3/22/freebsd-debian-gitolite/">gitolite</a> это выглядит так:</p>
<pre><code>repo example
RW+ = developer
repo example_markup
RW+ = freelancer
</code></pre>
<p>Локально клонируем репозиторий <tt>example_markup.git</tt>, копируем в него из <tt>example.git</tt> нужные каталоги и коммитим всё это добро.</p>
<h3>Подключаемся на сервер с gitolite и создаем хуки</h3>
<p>Переходим в каталог репозитория <tt>example_markup.git</tt> и клонируем туда оба репозитория, для того, чтобы можно было удобно из них коммитить:</p>
<pre><code>sudo su - git
cd ~/repositories/example_markup.git
git clone file:///home/git/repositories/example_markup.git
git clone file:///home/git/repositories/example.git
</code></pre>
<blockquote class="info">Если вы до этого не настраивали <b>git</b> под пользователем "git", то вам необходимо указать как минимум почту и имя для коммитов, а также воркфлоу при пушинге (я выбрал "simple", детальнее можете ознакомиться в "man git-config"):
<code>git config --global user.email "git@example.org"
git config --global user.name "Mr. Git"
git config --global push.default simple</code></blockquote>
<p>После чего, создадим хук <strong>post-update</strong>:</p>
<pre><code>sudo su - git
cd ~/repositories/example_markup.git/hooks
touch post-update
chmod +x post-update
emacs post-update
</code></pre>
<p>Помещаем туда следующее содержимое:</p>
<pre><code>#!/bin/sh
GIT=`/usr/bin/which git`
echo "Update example_markup.git..."
unset GIT_DIR
cd example_markup/ && $GIT pull && cd -
echo "Copy files from example_markup.git to example.git..."
cp -R example_markup/* example/
echo "Сommit and push changes to example.git"
unset GIT_DIR
cd example && $GIT add . && $GIT commit -am "Auto-update from example_markup.git" && $GIT push
echo "Finished!"
exec git-update-server-info
</code></pre>
<p>Теперь противоположные действия для репозитория <tt>example.git</tt>:</p>
<pre><code>sudo su - git
cd ~/repositories/example.git/hooks
touch post-update
chmod +x post-update
emacs post-update
</code></pre>
<p>Помещаем туда следующее содержимое:</p>
<pre><code>#!/bin/sh
GIT=`/usr/bin/which git`
echo "Update example.git..."
unset GIT_DIR
cd ../example_markup.git/example && $GIT pull && cd -
echo "Copy files from example_markup.git to example.git..."
cp -R ../example_markup.git/example/templates/* ../example_markup.git/example_markup/templates/
cp -R ../example_markup.git/example/static/* ../example_markup.git/example_markup/static/
echo "Сommit and push changes to example_markup.git"
unset GIT_DIR
cd ../example_markup.git/example_markup && $GIT add . && $GIT commit -am "Auto-update from example.git" && $GIT push
echo "Finished!"
exec git-update-server-info
</code></pre>
<p>Попробуем локально что-нибудь пушнуть в <tt>example_markup.git</tt>:</p>
<pre><code>Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 362 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Update example_markup.git...
remote: From file:///home/git/repositories/example_markup
remote: 840b8e9..2a9be4e master -> origin/master
remote: Updating 840b8e9..2a9be4e
remote: Fast-forward
remote: README | 0
remote: 1 file changed, 0 insertions(+), 0 deletions(-)
remote: create mode 100644 README
remote: /home/git/repositories/example_markup.git
remote: Copy files from example_markup.git to example.git...
remote: Сommit and push changes to example.git
remote: [master 2dbf0be] Auto-update from example_markup.git
remote: 5 files changed, 104 insertions(+), 250 deletions(-)
remote: remote: Update example.git...
remote: remote: From file:///home/git/repositories/example
remote: remote: 663fee4..2dbf0be master -> origin/master
remote: remote: Already up-to-date.
remote: remote: /home/git/repositories/example.git
remote: remote: Copy files from example_markup.git to example.git...
remote: remote: Сommit and push changes to example_markup.git
remote: remote: On branch master
remote: remote: Your branch is up-to-date with 'origin/master'.
remote: remote:
remote: remote: nothing to commit, working directory clean
remote: remote: Finished!
remote: To file:///home/git/repositories/example.git
remote: 663fee4..2dbf0be master -> master
remote: Finished!
To git@git.example.org:example_markup
840b8e9..2a9be4e master -> master
</code></pre>
<p>Ну всё, теперь верстальщик сможет изменять только свои файлы, коммитить их, а остальные участники проекта будут автоматически наблюдать изменения в своем репозитории, без необходимости вручную копировать изменения.</p>
<p>Если знаете более правильный способ разграничить права по файлам или как настроить <strong>gitolite</strong>, то пишите комментарии, всегда рад конструктиву!</p>
<p>ps. Способ подразумевает работу только с установленой веткой, в нашем случае <strong>master</strong>. Если понадобится использовать верстальщику ветки, то надо будет немного доделать хуки.</p>
Настройка OpenVPN с форвардингом на роутере Zyxel Keenetic Giga II
2015-06-11T10:59:22+03:00https://adw0rd.com/2015/06/11/openvpn-forwarding-zyxel-keenetic-giga/Хотите получить из роутера практически полноценный Linux сервер с некоторым набором базовых программ на базе NDMS? В свое время, мой выбор пал на "Zyxel Keenetic Giga", он бюджетный и простой в настройке, да и в целом я в нем не разочаровался. Однако, два года назад я так и не написал…
<p><img src="http://adw0rd.com/media/uploads/zyxel_keenetic_giga_2_m30.jpg" class="alignright whitespace"></p>
<p>Хотите получить из роутера практически полноценный Linux сервер с некоторым набором базовых программ на базе NDMS? В свое время, мой выбор пал на "Zyxel Keenetic Giga", он бюджетный и простой в настройке, да и в целом я в нем не разочаровался.</p>
<p>Однако, два года назад я так и не написал статью о том как получить из него полноценный Linux. Теперь же, мы купили себе вторую версию этого роутера "Zyxel Keenetic Giga II" и я всетаки заставил себя написать о настройке статью, поехали!</p>
<p><a name="more"></a></p>
<h2>О прошивке</h2>
<p>Сразу скажу, я усердно пробовал работать с прошивкой <strong>V2</strong> (конкретно 2.04), но как бы я не пытался, так и не смог завести <strong>dropbear</strong>.</p>
<p>Почитав в интернетах о прошивке "V2", я понял что она сырая и не годится для расширения. В итоге я наткнулся на <a rel="nofollow" href="http://4pda.ru/forum/index.php?s=&showtopic=535079&view=findpost&p=36132936">комментарий на 4pda</a> и установил себе на Giga II прошивку "V1.11" (брал <a rel="nofollow" href="https://www.dropbox.com/sh/idacrb5n9icxp0o/0t621uvn9j/1.11final2">от сюда</a>, ставил "Firmware-KEENETIC_GIGA_II-V1.11.RU.NDMS_140108210221.bin"), если файл уже не доступен, то пишите и я расшарю. Некоторые подробности о работе <strong>V1</strong> на <strong>Giga II</strong> можно <a rel="nofollow" href="http://forum.zyxmon.org/topic481-proshivki-v1-dlya-novoi-serii-keenetic-ii-giga-ii-ultra.html">подчерпунть тут</a>. Прошивку можно поменять в разделе "Система > Конфигурация", подробнее можно <a rel="nofollow" href="http://zyxel.ru/kb/2100">ознакомится тут</a>.</p>
<p>Из-за того что мы установили прошивку V1.11 — дальнейшие инструкции также корректны и для первой версии <strong>Giga</strong>.</p>
<h2>Расширяем Keenetic, добавляем sshd и opkg</h2>
<p>Берем флешку от 1Гб, форматируем её в ext3 (например в GParted). Можете ознакомится с официальными <a rel="nofollow" href="http://code.google.com/p/zyxel-keenetic-packages/wiki/usb_hdd">требованиями к USB носителю</a>.</p>
<p>Далее, идем на страницу <a rel="nofollow" href="https://code.google.com/p/zyxel-keenetic-packages/wiki/opkg_setup">документации о системе opkg</a>, действуем по их инструкциям и скачиваем нужный <a rel="nofollow" href="https://code.google.com/p/zyxel-keenetic-packages/downloads/list">архив тут</a>, у меня это был "ext_init.sh-r2.tar.gz".</p>
<p>Теперь создаем каталоги и копируем содержимое архива:</p>
<pre><code>cd </path/to/you/flash/drive>
mkdir -p system/bin
cd system/bin
wget https://zyxel-keenetic-packages.googlecode.com/files/ext_init.sh-r2.tar.gz
tar -xzf ext_init.sh-r2.tar.gz
chmod +x ext_init.sh
</code></pre>
<p>Проверяем права на файл "ext_init.sh", главное чтобы он был исполняемым (это мы сделали в последней строчке).</p>
<p>Теперь вставляем флешку в первый USB-слот (определится как "DISK_A1"), на главной странице панели администрирования Zyxel, в разделе "USB-накопитель" должна появится наша флешка. Переходим в раздел "Система > Журнал" и ожидаем примерно такую запись:</p>
<pre><code>root Starting opkg/linux install
root All errors are logged in a file /media/DISK_A1/tmpinstall/err.log
root Extracting busybox
root Unpacking busybox
root Extracting system
root Unpacking system
root Generating rsa/dss keys for dropbear
root Starting dropbear
dropbear[2428] Running in background
root Connect to keenetic using ssh and run finish_install.sh to finish installation
</code></pre>
<p>Теперь вы можете подключится по SSH.</p>
<h2>Завершаем установку</h2>
<p>Подключаемся по ssh:</p>
<pre><code>ssh root@192.168.1.1
пароль "zyxel"
</code></pre>
<p>Теперь перво-наперво сменим пароль:</p>
<pre><code># passwd
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
</code></pre>
<p>Запускаем <tt>finish_install.sh</tt> для завершения установки:</p>
<pre><code># finish_install.sh
Creating swap file. Please wait...
128+0 records in
128+0 records out
Setting up swapspace version 1, size = 134213633 bytes
Loading current packages list
Downloading http://zyxel-keenetic-packages.googlecode.com/svn/binary-packages-r2/Packages.gz.
Inflating http://zyxel-keenetic-packages.googlecode.com/svn/binary-packages-r2/Packages.gz.
Updated list of available packages in /media/DISK_A1/system/var/opkg-lists/packages.
</code></pre>
<h2>Обновление системы и установка нужных приложений</h2>
<p>Обновляем установленные пакеты:</p>
<pre><code># opkg update
# opkg upgrade
</code></pre>
<p>И доустановливаем нужные:</p>
<pre><code># opkg list | grep <нужный пакет>
# opkg install mc screen nano htop openvpn-openssl
</code></pre>
<h2>Теперь настроим OpenVPN</h2>
<p>Создадим файл <tt>/media/DISK_A1/system/etc/firewall.d/fw.sh</tt> с правилами для <strong>iptables</strong> со следующим содержимым:</p>
<pre><code>#!/bin/sh
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp -j ACCEPT
iptables -A INPUT -p udp -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -t nat -F
iptables -I FORWARD 1 -i br0 -o tun0 -j ACCEPT
iptables -I FORWARD 2 -i tun0 -o br0 -j ACCEPT
iptables -I FORWARD 3 -s 192.168.1.0/24 -j ACCEPT
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
</code></pre>
<p>Чтобы вы могли зайти на свой сервер из под <strong>VPN</strong>, добавьте перед последней строчкой:</p>
<pre><code>iptables -t nat -A POSTROUTING -o eth2.2 -d "АДРЕС_VPN_СЕРВЕРА" -j MASQUERADE
</code></pre>
<p>Сделаем этот файл исполняемым:</p>
<pre><code># chmod +x /media/DISK_A1/system/etc/firewall.d/fw.sh
</code></pre>
<p>В каталоге <tt>/media/DISK_A1/system/etc/openvpn</tt> надо создать конфиг клиента <tt>openvpn.conf</tt> и положить туда предварительно сгенерированные (или полученные от какого-либо сервиса) ключи. Вот пример возможного конфига:</p>
<pre><code>client
remote <IP-адрес OpenVPN-сервера>
port 1194
dev tun
proto udp
resolv-retry infinite
nobind
pull
user nobody
group nogroup
persist-key
persist-tun
ca /media/DISK_A1/system/etc/openvpn/ca.crt
cert /media/DISK_A1/system/etc/openvpn/example.crt
key /media/DISK_A1/system/etc/openvpn/example.key
tls-client
tls-auth /media/DISK_A1/system/etc/openvpn/ta.key 1
cipher DES-EDE3-CBC
comp-lzo
mute 10
verb 0
log openvpn.log
</code></pre>
<p>Подробности опций и как сгенерировать нужные ключи можно почитать в <a rel="nofollow" href="http://adw0rd.com/2013/1/10/openvpn/">моей статье о OpenVPN</a>.</p>
<blockquote class="info">Если вы захотите запустить <tt>openvpn</tt> без init-скрипта, то столнетесь с проблемой с устройством <tt>tun</tt>:<code>Wed Jun 10 12:41:09 2015 ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such file or directory (errno=2)</code>Эту проблему я быстро решил при помощи <a rel="nofollow" href="http://dedicatesupport.com/content/oshibka-openvpn-cannot-open-tuntap-dev-devnettun">этой статьи</a>, для этого надо создать каталог <tt>/dev/net</tt> и создать там файл устройства:<code>
# mkdir /dev/net
# mknod /dev/net/tun c 10 200
</code>Но после этого произошла новая проблема:
<code>Wed Jun 10 12:41:50 2015 ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such device (errno=19)
</code>Я проверил через <tt>lsmod</tt> наличие модуля "tun" и не обнаружил его: <code># lsmod | grep tun
</code>Значит надо его подгрузить, т.к. <tt>modprobe</tt> не идет в поставку с нашим Linux, то я воспользовался <tt>insmod</tt>. Я нашел подходящий модуль и загрузил его: <code># find / -name "*tun*.ko"
/lib/modules/2.6.22-tc/tun.ko
/media/DISK_A1/system/lib/modules/2.6.23-rt/tun.ko
# insmod /lib/modules/2.6.22-tc/tun.ko</code> Теперь все впорядке, <tt>lsmod</tt> говорит что модуль подгружен.
</blockquote>
<h2>Запускаем OpenVPN</h2>
<p>Для того, чтобы после перезапуска системы у нас запускался демон <strong>OpenVPN</strong> самостоятельно, надо переименовать его init-скрипт из <tt>K11openvpn</tt> в <tt>S11openvpn</tt>:</p>
<pre><code># cd /media/DISK_A1/system/etc/init.d/
# mv K11openvpn S11openvpn
</code></pre>
<p>Добавлять вызов <tt>/media/DISK_A1/system/etc/firewall.d/fw.sh</tt> никуда не надо, он запустится автоматически.</p>
<blockquote class="info">Я столкнулся с проблемой запуска модуля <tt>tun.ko</tt> в init-скрипте. Тот что был там указан выдавал следующее: <code>insmod: cannot insert '/media/DISK_A1/system/lib/modules/2.6.23-rt/tun.ko': invalid module format (-1): Exec format error</code>в результате я нашел альтернативный и он корретно заработал:
<code># find / -name "*tun*.ko"
/lib/modules/2.6.22-tc/tun.ko
/media/DISK_A1/system/lib/modules/2.6.23-rt/tun.ko
# insmod /lib/modules/2.6.22-tc/tun.ko</code> после чего я поменял в файле <b>S11openvpn</b> на альтернативный модуль:
<code>#insmod $MOUNT/lib/modules/2.6.23-rt/tun.ko
insmod /lib/modules/2.6.22-tc/tun.ko
...
#rmmod $MOUNT/lib/modules/2.6.23-rt/tun.ko
rmmod /lib/modules/2.6.22-tc/tun.ko
</code>
</blockquote>
<p>Впринципе всё, перезапустите роутер и должно все заработать как надо!</p>
<h2>Обновление с zyxware на entware</h2>
<p>Не особо рекомендую обновляться до <strong>entware</strong>, т.к. при запуске <strong>OpenVPN</strong> через init-скрипт я получал <tt>Segmentation Fault</tt> (правда если просто запускать <tt>openvpn openvpn.conf</tt>, то проблемы не наблюдалось), разбираться с этим мне не хотелось, т.к. для себя особого смысла обновляться я не видел. Главное отличие <strong>entware</strong> это большее количество пакетов чем в <strong>zyxware</strong>, так что если хочется, то попробовать всетаки можно. Ниже расскажу как это сделать:</p>
<p>Для этого вынем нашу флешку из роутера и запишем туда архив, который можно взять <a rel="nofollow" href="http://forum.zyxmon.org/post17409.html#p17409">отсюда</a>. Снова подключите флешку в первый USB-слот. В <a rel="nofollow" href="http://www.mr-allen.net/2013/09/zyxel-keenetic-giga-ii/">сети рекомендуют</a> обновлятся через telnet, но я обновлялся по ssh:</p>
<pre><code># cd /media/DISK_A1
# wget http://keenetic.zyxmon.org/entware/entware_keenetic.tgz
# tar -xzf entware_keenetic.tgz
# chmod +x ./entware_keenetic_install.sh
# ./entware_keenetic_install.sh
</code></pre>
<p>После чего перезапустите роутер из веб-панели <strong>Zyxel</strong>. На вашей флешке, помимо каталога <tt>/system</tt> появится еще <tt>/opt</tt>. Все, теперь вы счастливый обладатель <strong>entware</strong>!</p>