Встраивание видео с камеры Hikvision на веб-страницу

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

Поскольку камера отдает видео по RTSP, по RTSP мы его и заберем. Быстрое гугление показало что у большинства Hikvision камер rtsp-видео доступно по такому урлу: rtsp://логин:пароль@IP_адрес:порт/ISAPI/Streaming/Channels/101

101, в данном случае указывает на камеру и поток. Хочешь знать больше? Читай мануал!

VLC, или иной медиаплеер какой-нибудь, умеет с rtsp работать, конечно же, но видео надо встроить на веб-страницу. А чтобы встроить и воспроизвести — придется соорудить приблуду, которая rtsp конвертонет в rtmp, который будет схаван плеером (у меня vgaplayer) и отображен на странице.

Почему vgaplayer? Что попалось и заработало, тем и воспользовался.

1. Конвертация RTSP в RTMP

Поскольку веб-страница сервится nginx’ом, то nginx’у и отдуваться.

Скачать нужно исходники, т.к. надо будет собрать nginx с rtmp-модулем. Счастливому пользователю FreeBSD, который лупонул make config в /usr/ports/www/nginx и увидел в опциях RTMP…

…обломаю радость. Это не тот RTMP который нужен.

Тот, который нужен, можно тянуть отсюда. Исходники nginx’а, думаю, всем понятно откуда брать.

В общем, ложим исходники модуля по соседству с исходниками самого nginx’а. Заходим в каталог с исходниками самого и конфигурим сборку:

1
./configure [....] --add-module=../nginx-rtmp-module/

Если все прошло хорошо, make install clean (главное, не прозевать [….] — я некисло так с префиксом наглупил)

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

1
sudo pkg install ffmpeg

В nginx.conf, в самый низ (или в conf.d/твой-вхост.conf если надо) следует добавить кусок типа такого:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
rtmp {
        server {
                listen 1935;
                chunk_size 4096;
                application live {
                        live on;
                        record off;
                        hls on;
                        hls_nested on;
                        hls_path /usr/local/nginx/html/hls/;
                        hls_fragment 3;
                        hls_playlist_length 60;
                        exec_pull /usr/local/bin/ffmpeg -i rtsp://логин:пароль@IP-адрес-камеры:rtsp-порт/ISAPI/Streaming/Channels/$name -vcodec copy -acodec copy -f flv rtmp://IP-адрес-сервака:1935/live/$name;
                }
        }
}

Не забывай: твой префикс может отличаться.

После этих изменений, ясен пень, следует рестартнуть nginx.

Видос будет доступен по rtmp://IP-адрес-сервака:1935/live/101

2. А как на счет встроить в веб-страницу?

Вариантов куча. Я остановился на самом простом: vgaplayer

1
2
3
4
5
6
7
8
9
10
11
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="99%">
  <param name="movie" value="vgaplayer.swf" />
  <param name="FlashVars" value="url=rtmp://IP-адрес-сервака:1935/live/101" />
  <embed src="vgaplayer.swf"
         width="100%" height="420"
         allowScriptAccess="sameDomain"
         allowFullScreen="true"
         type="application/x-shockwave-flash"
         FlashVars="url=rtmp://IP-адрес-сервака:1935/live/101"
         pluginspage="http://www.adobe.com/go/getflashplayer" />
</object>

Флеш, понятное дело, в 2019 моветон. Но html5 вроде как в rtmp-поток так и не умеет до сих пор.

Рубрика: it | Метки: , , , , , , , , | Оставить комментарий

Установка MikroTik RouterOS на Soekris net4511

Для решения одной задачи из забвения был вытянут Soekris net4511.

Забавная железяка. Подробнее, если интересно, можно о ней почитать тут.

Некоторое время назад, на такую же, имел опыт ставить FreeBSD. Но на этот раз нужна была RouterOS от MikroTik.

На вопрос почему не воспользоваться netinstall’ом от MikroTik отвечу: железяка не умеет в PXE.

Решение задачи, как это часто бывает, пришло во время перекура-с-кофейком. Но не во время первого.

Был включен давно покрывающийся пылью системник (уже и не помню что на нем крутилось), с подключенным переходником из CompactFlash в IDE.

Понятное дело, нарезал исоху инсталлятора на флешку, втулил в старый компьютер, да в BIOS’е выбрал грузиться с флехи. Но ничего не вышло. Инсталлятор запустился, но заметил что нет CD-ROM’а и отказался продолжать сотрудничество. Но и я не лыком шит, достал USB CD-ROM, занял им последние два свободных порта на материнке (первые два — клавиатура и флешка, всего четыре) и ушел в ребут. Это, как не трудно догадаться, ни разу не помогло. Отчасти из-за того, что RouterOS x86 не умеет в USB CD-ROM. Отчасти, полагаю, и из-за того, что установочная медиа не в сидироме крутится. IDE-шного привода, к сожалению, под рукой не оказалось. Да и идти за болванками — не вариант. Во-первых — последний опыт показывает что половину купленных можно смело выбрасывать в окно и смотреть как они летят, во-вторых — это еще идти искать надо…

Здесь я вернулся к варианту про PXE. Благо старый системник с двумя сетевками — встроенной и навесной. Машина с четвертым пнем на борту. Но тут тоже не свезло. Бортовая сетевка мертвая, а навесная не умеет в PXE.

Но я не отчаялся — ведь у меня есть две флешки! А значит, на одну из них я могу нарезать какой-нибудь LiveCD, у которого есть dd; этим dd слить дамп CompactFlash’а на вторую флешку. Что я и сделал.

По полю SIZE, думаю, понятно где и что.

Далее, вытянув из старого системника флешку с дампом CompactFlash’ки, засел за рабочую тачку и запустил VirtualBox. В нем создал стандартнейшую ВМ, но с «изъянами»:

  • В разделе настроек «Система» отрубил флоппик и подрубил «Сеть»
  • В разделе «COM-порты» включил первый (можно любой из, но, хотя бы, один). Иначе, если не включить, (у меня) не работал выхлоп в RS232 на реальном железе.

Вставив флеху с дампом, сконвертировал этот самый дамп в понятный VirtualBox’у формат:

1
VBoxManage convertdd compactflash.raw mikrotik.vdi --format VDI

В разделе «Носители» в качестве жесткого диска указал этот сконвертированный дамп, а в качестве CD-носителя — валявшийся неподалеку образ 5.24-ой RouterOS. Можно, конечно, было качнуть и что-то поновее, но для решаемой задачи и такой сгодится.

Запустил ВМ. Процесс инсталляции RouterOS на образ занял меньше минуты. Реальная железяка дольше грузится, лол.

Потушив ВМ, сконвертировал VDI-образ в сырой, который, при помощи того же dd, будет залит обратно на CompactFlash:

1
VBoxManage clonehd mikrotik.vdi mikrotik.raw --format RAW

Потушил старый компьютер (спасибо тебе! :3), вытянул CompactFlash и втулил в target-железяку, к которой уже были заботливо подключены RS232 для выхлопа, и шнурок чтобы WinBox’ом увидеть (хорошо под wine’ом, замечу, работает)

Дальше можно настраивать под нужные задачи.

Единственное «но» — эстетам вроде меня будет крайне необходимо переименовать интерфейсы.

P.S.: Во время первой загрузки на реальном железе RouterOS ребутнется, тут не стоит пугаться.

Рубрика: it | Метки: , , , , , , , | Оставить комментарий

Flask-приложение (python3) и Apache2 на debian-машине

Первым делом, если еще не установлен, ставим apache2 и, поскольку нужен python3, соответствующий модуль:

1
2
sudo apt-get install apache2 libapache2-mod-wsgi-py3
sudo a2enmod wsgi

 

По-умолчанию, в debian’е не установлен python3. А еще нам нужен pip для него, и venv для виртуального окружения, поэтому:

1
sudo apt-get install python3 python3-pip python3-venv

 

В /var/www создаем каталог проекта. Пускай будет обзываться Example. Внутри него также создадим каталог для логов, приложения, темплейтов и статики:

1
2
3
sudo mkdir /var/www/Example
sudo mkdir /var/www/Example/logs
sudo mkdir /var/www/Example/app /var/www/Example/app/templates /var/www/Example/app/static

 

Создадим скрипт app.wsgi, который позже скормим апачу:

1
sudo nano /var/www/Example/app.wsgi
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
activate_this = "/var/www/Example/app/venv/bin/activate_this.py"
with open(activate_this) as file_:
    exec(file_.read(), dict(__file__=activate_this))
import sys
import logging
logging.basicConfig(stream=sys.stderr)
sys.path.insert(0, "/var/www/Example/")
from app import app as application

 

Создадим еще два файла для простейшего Flask-приложения, __init__.py и routes.py:

1
sudo nano /var/www/Example/app/__init__.py
1
2
3
4
5
from flask import Flask
app = Flask(__name__)
from app import routes
1
sudo nano /var/www/Example/app/routes.py
1
2
3
4
5
6
7
8
from flask import render_template
from app import app
@app.route('/')
def index():
    return render_template(
        'index.html'
    )

 

И простенький index.html:

1
sudo nano /var/www/Example/app/templates/index.html
1
2
3
4
5
6
7
<!DOCTYPE html>
<html>
<head><title>Test</title></head>
<body>
  <h1>Hello, Flask!</h1>
</body>
</html>

 

Установим владельца и группу для /var/www/Example:

1
sudo chown -R username:www-data /var/www/Example

 

Установим виртуальное окружение для нашего проекта, войдем в него, установим Flask, покинем виртуальное окружение:

1
2
3
4
python -m venv /var/www/Example/app/venv
source /var/www/Example/app/venv/bin/activate
pip install Flask
deactivate

 

Положим в каталог /var/www/Example/app/venv/bin/ скрипт, необходимый для работы mod_wsgi с виртуальным окружением:

1
nano /var/www/Example/app/venv/bin/activate_this.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
try:
    __file__
except NameError:
    raise AssertionError(
        "You must run this like execfile('path/to/activate_this.py', dict(__file__='path/to/activate_this.py'))")
import sys
import os
old_os_path = os.environ.get('PATH', '')
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + os.pathsep + old_os_path
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if sys.platform == 'win32':
    site_packages = os.path.join(base, 'Lib', 'site-packages')
else:
    site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages')
prev_sys_path = list(sys.path)
import site
site.addsitedir(site_packages)
sys.real_prefix = sys.prefix
sys.prefix = base
new_sys_path = []
for item in list(sys.path):
    if item not in prev_sys_path:
        new_sys_path.append(item)
        sys.path.remove(item)
sys.path[:0] = new_sys_path

 

Создадим конфиг vhost’а для Apache2:

1
sudo nano /etc/apache2/sites-available/example-flask.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    ErrorLog /var/www/Example/logs/error.log
    CustomLog /var/www/Example/logs/access.log combined
    
    WSGIDaemonProcess Example user=www-data group=www-data threads=5 home=/var/www/Example
    WSGIScriptAlias / /var/www/Example/app.wsgi
    <Directory /var/www/Example>
        
        WSGIProcessGroup Example
        WSGIApplicationGroup %{GLOBAL}
        WSGIScriptReloading On
        Order deny,allow
        Allow from all
    </Directory>
    
    Alias /static /var/www/Example/app/static
    <Directory /var/www/Example/app/static>
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

 

Активируем его и перезапустим Apache2:

1
2
sudo a2ensite example-flask
sudo systemctl reload apache2

 

Готово.

Рубрика: it | Метки: , , , , , , | Оставить комментарий

Прикручиваем Node.js к nginx в Arch Linux

Возникла необходимость прикрутить приложение на Node.js к Django-проекту, над которым сейчас работаю. Сам проект работает через gunicorn + nginx. Открывать в мир порт, который будет слушать Node.js неохота, да и не хочу чтобы на любом виртуалхосте он был доступен, поэтому решил прикрутить его (Node.js) к nginx’у. Ну и создать для него (Node.js) юнит в systemd, чтоб запускть-перезапускать.

Сам юнит:

1
sudo nano /etc/systemd/system/node_имя_вхоста.service
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=node_имя_вхоста.js -- Node JS App for имя_вхоста
After=network.target
[Service]
Environment=NODE_PORT=номер_порта
Type=simple
User=имя_пользователя
ExecStart=/usr/bin/node /путь/к/nodejs/приложению/имя_приложения.js
Restart=on-failure
[Install]
WantedBy=multi-user.target

Енаблим и запускаем:

1
2
sudo systemctl enable node_имя_вхоста
sudo systemctl start node_имя_вхоста

Далее добавляем кусок в конфиг виртуалхоста nginx’а:

1
sudo nano /etc/nginx/conf.d/имя_вхоста.conf
1
2
3
4
5
6
7
8
9
10
...
    location /nodejs/ {
        proxy_pass http://localhost:номер_порта/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
    }
....

Перезапускаем nginx:

1
sudo systemctl restart nginx

И готово:

1
2
3
4
5
6
7
var http = require('http');
var server = http.createServer(function(req, res) {
    res.writeHead(200);
    res.end('<h1>Hello, node.js!</h1>');
});
server.listen(номер_порта);
Рубрика: it | Метки: , , , , , , , | Оставить комментарий

Прикручиваем AWStats к nginx в Arch Linux

Возникла необходимость собирать статистику посещений одного из django-проектиков. Так как когда-то, давным-давно, имел кое-какой опыт работы с awstats, его же и решил прикрутить. Ранее, правда, работал с apache2, а тут nginx. Можно, конечно, поставить apache2, чтоб слушал какой-нибудь 8080-порт, или типа того. Но зачем?

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

Подразумевая что nginx уже поставлен и настроен, доставляем недостающие для решения задачи пакеты:

1
sudo pacman -S awstats fcgiwrap spawn-cgi

awstats положит все ему необходимое в /usr/share/webapps/awstats (за другие дистрибутивы не ручаюсь). Копию всего этого добра, с которой будем работать, ложим в /srv/http/awstats, чтобы получилась такая структура:

1
tree -d /srv/http/awstats
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/srv/http/awstats/
├── cgi-bin
│   ├── lang
│   │   ├── tooltips_f
│   │   ├── tooltips_m
│   │   └── tooltips_w
│   ├── lib
│   └── plugins
│       └── example
├── classes
│   └── src
├── css
├── icon
│   ├── browser
│   ├── clock
│   ├── cpu
│   ├── flags
│   ├── mime
│   ├── os
│   └── other
└── js

Ставим права:

1
chown -R http:http /srv/http/awstats

Далее правим конфиг vhost’а, для которого прикручиваем awstats (конечно, можно и в отдельный vhost вынести, но я не стал):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server {
    ...
    # тут основная часть конфига vhost'а
    ...  
    location /awstats {
        auth_basic      "Restricted";
        auth_basic_user_file    /etc/nginx/auth/htpasswd;
        root /srv/http/awstats/cgi-bin;
        fastcgi_pass    unix:/var/run/fcgiwrap.sock;
        fastcgi_index   index.php;
        fastcgi_param   SCRIPT_NAME     /awstats.pl;
        fastcgi_param   QUERY_STRING    $query_string;
        fastcgi_param   REQUEST_METHOD  $request_method;
        fastcgi_param   CONTENT_TYPE    $content_type;
        fastcgi_param   CONTENT_LENGTH  $content_length;
        include         fastcgi_params;
    }
    location /awstatsclasses/ {
        alias /srv/http/awstats/classes/;
    }
    location /awstatscss/ {
        alias /srv/http/awstats/css/;
    }
    location /icon/ {
        alias /srv/http/awstats/icon/;
    }
}

Создаем пользователя, которому разрешено смотреть статистику (ограничивать по IP неохота, т. к. с разных мест нужно будет смотреть):

1
htpasswd -cmb /etc/nginx/auth/htpasswd имя_пользователя пароль

Клепаем конфиг awstats (/etc/awstats/awstats.доменное_имя_сайта.conf):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
LogFile="/var/log/nginx/access.log"
LogType=W
LogFormat=1
LogSeparator=" "
SiteDomain="доменное_имя_сайта"
HostAliases="localhost 127.0.0.1"
DNSLookup=1
DirData="/srv/http/awstats"
DirCgi="/cgi-bin"
DirIcons="/icon"
AllowToUpdateStatsFromBrowser=1
AllowFullYearView=2
EnableLockForUpdate=1
DNSStaticCacheFile="dnscache.txt"
DNSLastUpdateCacheFile="dnscachelastupdate.txt"
SkipDNSLookupFor=""
AllowAccessFromWebToAuthenticatedUsersOnly=0
AllowAccessFromWebToFollowingAuthenticatedUsers=""
AllowAccessFromWebToFollowingIPAddresses=""
CreateDirDataIfNotExists=1
BuildHistoryFormat=text
BuildReportFormat=html
SaveDatabaseFilesWithPermissionsForEveryone=0
PurgeLogFile=0
ArchiveLogRecords=0
KeepBackupOfHistoricFiles=0
DefaultFile="index.php index.html"
SkipHosts=""
SkipUserAgents=""
SkipFiles=""
SkipReferrersBlackList=""
OnlyHosts=""
OnlyUserAgents=""
OnlyUsers=""
OnlyFiles=""
NotPageList="css js class gif jpg jpeg png bmp ico rss xml swf"
ValidHTTPCodes="200 304"
ValidSMTPCodes="1 250"
AuthenticatedUsersNotCaseSensitive=0
URLNotCaseSensitive=0
URLWithAnchor=0
URLQuerySeparators="?;"
URLWithQuery=0
URLWithQueryWithOnlyFollowingParameters=""
URLWithQueryWithoutFollowingParameters=""
URLReferrerWithQuery=0
WarningMessages=1
ErrorMessages=""
DebugMessages=0
NbOfLinesForCorruptedLog=50
WrapperScript=""
DecodeUA=0
MiscTrackerUrl="/js/awstats_misc_tracker.js"
LevelForBrowsersDetection=2
LevelForOSDetection=2
LevelForRefererAnalyze=2
LevelForRobotsDetection=2
LevelForSearchEnginesDetection=2
LevelForKeywordsDetection=2
LevelForFileTypesDetection=2
LevelForWormsDetection=0
UseFramesWhenCGI=1
DetailedReportsOnNewWindows=1
Expires=0
MaxRowsInHTMLOutput=1000
Lang="auto"
DirLang="/srv/http/awstats/lang"
ShowMenu=1
ShowSummary=UVPHB
ShowMonthStats=UVPHB
ShowDaysOfMonthStats=VPHB
ShowDaysOfWeekStats=PHB
ShowHoursStats=PHB
ShowDomainsStats=PHB
ShowHostsStats=PHBL
ShowAuthenticatedUsers=0
ShowRobotsStats=HBL
ShowWormsStats=0
ShowEMailSenders=0
ShowEMailReceivers=0
ShowSessionsStats=1
ShowPagesStats=PBEX
ShowFileTypesStats=HB
ShowFileSizesStats=0
ShowDownloadsStats=HB
ShowOSStats=1
ShowBrowsersStats=1
ShowScreenSizeStats=0
ShowOriginStats=PH
ShowKeyphrasesStats=1
ShowKeywordsStats=1
ShowMiscStats=a
ShowHTTPErrorsStats=1
ShowSMTPErrorsStats=0
ShowClusterStats=0
AddDataArrayMonthStats=1
AddDataArrayShowDaysOfMonthStats=1
AddDataArrayShowDaysOfWeekStats=1
AddDataArrayShowHoursStats=1
IncludeInternalLinksInOriginSection=0
MaxNbOfDomain = 10
MinHitDomain  = 1
MaxNbOfHostsShown = 10
MinHitHost    = 1
MaxNbOfLoginShown = 10
MinHitLogin   = 1
MaxNbOfRobotShown = 10
MinHitRobot   = 1
MaxNbOfDownloadsShown = 10
MinHitDownloads = 1
MaxNbOfPageShown = 10
MinHitFile    = 1
MaxNbOfOsShown = 10
MinHitOs      = 1
MaxNbOfBrowsersShown = 10
MinHitBrowser = 1
MaxNbOfScreenSizesShown = 5
MinHitScreenSize = 1
MaxNbOfWindowSizesShown = 5
MinHitWindowSize = 1
MaxNbOfRefererShown = 10
MinHitRefer   = 1
MaxNbOfKeyphrasesShown = 10
MinHitKeyphrase = 1
MaxNbOfKeywordsShown = 10
MinHitKeyword = 1
MaxNbOfEMailsShown = 20
MinHitEMail   = 1
FirstDayOfWeek=1
ShowFlagLinks=""
ShowLinksOnUrl=1
UseHTTPSLinkForUrl=""
MaxLengthOfShownURL=64
HTMLHeadSection=""
HTMLEndSection=""
MetaRobot=0
Logo="awstats_logo6.png"
BarWidth   = 260
BarHeight  = 90
StyleSheet=""
color_Background="FFFFFF"
color_TableBGTitle="CCCCDD"
color_TableTitle="000000"
color_TableBG="CCCCDD"
color_TableRowTitle="FFFFFF"
color_TableBGRowTitle="ECECEC"
color_TableBorder="ECECEC"
color_text="000000"
color_textpercent="606060"
color_titletext="000000"
color_weekend="EAEAEA"
color_link="0011BB"
color_hover="605040"
color_u="FFAA66"
color_v="F4F090"
color_p="4477DD"
color_h="66DDEE"
color_k="2EA495"
color_s="8888DD"
color_e="CEC2E8"
color_x="C1B2E2"
LoadPlugin="hashfiles"
ExtraTrackedRowsLimit=500

Далее запускаем первоначальное накопление капитала данных для статистики:

1
/usr/share/webapps/awstats/cgi-bin/awstats.pl -config=доменное_имя_сайта -update

Создаем systemd-таймер, чтоб раз в n-минут (у меня 10) awstats обновлял данные:

1
sudo nano /etc/systemd/system/awstats-доменное_имя_сайта.service
1
2
3
4
5
6
7
8
[Unit]
Description=awstats for доменное_имя_сайта
[Service]
Type=simple
ExecStart=/usr/share/webapps/awstats/cgi-bin/awstats.pl -config=доменное_имя_сайта -update > /dev/null
User=root
Group=users
1
sudo nano /etc/systemd/system/awstats-доменное_имя_сайта.timer
1
2
3
4
5
6
7
8
9
[Unit]
Description=awstats for доменное_имя_сайта
[Timer]
OnBootSec=10min
OnUnitActiveSec=10min
[Install]
WantedBy=timers.target

Врубаем таймер:

1
2
sudo systemctl enable awstats-доменное_имя_сайта.timer
sudo systemctl start awstats-доменное_имя_сайта.timer

Врубаем fcgiwrap:

1
2
sudo systemctl enable fcgiwrap.socket
sudo systemctl start fcgiwrap.socket

Перезапускаем nginx:

1
sudo systemctl restart nginx

Ну а далее переходим на http(s)://доменное_имя_сайта/awstats?config=доменное_имя_сайта

 

Рубрика: it | Метки: , , , , , , | Оставить комментарий

PostgreSQL после обновления ArchLinux

Очередной pacman -Syyu на этот раз прошел не столь незаметно как прежде. Отвалились все django-проектики, которые работают с postgresql. Причина проста — несовместимость формата БД версии 9.6 (стояла прежде) с версией 10.0-1 (которая стала после обновления), о чем радостно сообщил journalctl -xe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ноя 27 11:35:44 archlinux systemd[1]: Starting PostgreSQL database server...
-- Subject: Начинается запуск юнита postgresql.service
-- Defined-By: systemd
--
-- Начат процесс запуска юнита postgresql.service.
ноя 27 11:35:44 archlinux postgres[17505]: An old version of the database format was found.
ноя 27 11:35:44 archlinux postgres[17505]: You need to dump and reload before using PostgreSQL 9.6.
ноя 27 11:35:44 archlinux postgres[17505]: See http://www.postgresql.org/docs/9.6/static/upgrading.html
ноя 27 11:35:44 archlinux sudo[17502]: pam_unix(sudo:session): session closed for user root
ноя 27 11:35:44 archlinux systemd[1]: postgresql.service: Control process exited, code=exited status=1
ноя 27 11:35:44 archlinux systemd[1]: postgresql.service: Failed with result 'exit-code'.
ноя 27 11:35:44 archlinux systemd[1]: Failed to start PostgreSQL database server.
-- Subject: Ошибка юнита postgresql.service
-- Defined-By: systemd
--
-- Произошел сбой юнита postgresql.service.
--
-- Результат: RESULT.

Ну и ладно. Благо, решение уже написано в самом логе. Хорошо и то, что в AUR’е доступны и предыдущие версии postgresql. Поэтому:

1
yaourt -S postgresql-9.6

Пропустил предложения поправить PKGBUILD да postgresql.install — старая версия нужна ведь только чтобы сделать дамп БД (правильно, конечно, было бы его сделать перед обновлением системы, но то такое).

После недолгой сборки и установки сделал этот самый дамп:

1
2
3
sudo -u postgres pg_ctl -D /var/lib/postgres/data start
sudo -u postgres pg_dumpall -f /tmp/pg_dump.sql
sudo -u postgres pg_ctl -D /var/lib/postgres/data stop

Далее грохнул теперь уже ненужный каталог:

1
sudo rm -rf /var/lib/postgres/data

И переустановил свежую версию postgresql:

1
sudo pacman -S postgresql

И, наконец, запустил новый postgresql и залил дамп обратно:

1
2
sudo systemctl start postgresql.service
sudo -u postgres psql -f /tmp/pg_dump.sql

Voila!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
systemctl status postgresql.service
● postgresql.service - PostgreSQL database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2017-11-27 11:41:31 EET; 19min ago
  Process: 18393 ExecStart=/usr/bin/pg_ctl -s -D ${PGROOT}/data start -w -t 120 (code=exited, status=0/SUCCESS)
  Process: 18391 ExecStartPre=/usr/bin/postgresql-check-db-dir ${PGROOT}/data (code=exited, status=0/SUCCESS)
 Main PID: 18396 (postgres)
    Tasks: 7 (limit: 4915)
   CGroup: /system.slice/postgresql.service
           ├─18396 /usr/bin/postgres -D /var/lib/postgres/data
           ├─18398 postgres: checkpointer process
           ├─18399 postgres: writer process
           ├─18400 postgres: wal writer process
           ├─18401 postgres: autovacuum launcher process
           ├─18402 postgres: stats collector process
           └─18403 postgres: bgworker: logical replication launcher
ноя 27 11:41:31 archlinux systemd[1]: Starting PostgreSQL database server...
ноя 27 11:41:31 archlinux postgres[18393]: 2017-11-27 11:41:31.418 EET [18396] СООБЩЕНИЕ:  для приёма подключений по адресу IPv6 "::1" открыт порт 5432
ноя 27 11:41:31 archlinux postgres[18393]: 2017-11-27 11:41:31.419 EET [18396] СООБЩЕНИЕ:  для приёма подключений по адресу IPv4 "127.0.0.1" открыт порт 5432
ноя 27 11:41:31 archlinux postgres[18393]: 2017-11-27 11:41:31.479 EET [18396] СООБЩЕНИЕ:  для приёма подключений открыт сокет Unix "/run/postgresql/.s.PGSQL.5432"
ноя 27 11:41:31 archlinux postgres[18393]: 2017-11-27 11:41:31.598 EET [18397] СООБЩЕНИЕ:  система БД была выключена: 2017-11-27 11:08:57 EET
ноя 27 11:41:31 archlinux postgres[18393]: 2017-11-27 11:41:31.634 EET [18396] СООБЩЕНИЕ:  система БД готова принимать подключения
ноя 27 11:41:31 archlinux systemd[1]: Started PostgreSQL database server.

 

Рубрика: it | Метки: , , , , | Оставить комментарий

Перенос БД Django-проекта с MySQL на PostgreSQL

Для экспериментов и наведения порядка в коде проекта пришлось поднять его копию в песочнице. Отличия, правда, некоторые имеются: не apache2 в ней, но nginx, и не MySQL/MariaDB, но PostgreSQL.

С первым отличием справляться долго не пришлось. Поднял очередной virtualenv, да почти копипастой с других проектов в песочнице создал два конфига (один для nginx, второй для gunicorn).

А вот перенести БД не так просто и быстро. Вернее, и просто, и быстро, если уже опыт имеется. А мне раньше не приходилось.

Итак, на основном хосте создал пользователя с правом доступа к БД с адреса песочницы:

1
GRANT SELECT ON имя_бд.* TO 'имя_пользователя'@'IP-адрес_песочницы' IDENTIFIED BY 'пароль';

В песочнице же, где PostgreSQL, при помощи manage.py migrate воссоздал необходимую структуру БД.

Далее поставил в песочнице необходимый мегаинструмент:

1
sudo pip2 install py-mysql2pgsql

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

1
py-mysql2pgsql

Его выхлоп подсказал имя конфига:

1
2
3
No configuration file found.
A new file has been initialized at: mysql2pgsql.yml
Please review the configuration and retry...

В который я, не медля, прописал логины с паролями да прочую информацию.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql:
 hostname: IP-адрес-mysql-БД
 port: 3306
 socket:
 username: имя_пользователя
 password: пароль
 database: имя_БД
 compress: false
destination:
 # if file is given, output goes to file, else postgres
 file:
 postgres:
  hostname: localhost
  port: 5432
  username: имя_пользователя
  password: пароль
  database: имя_БД

Далее пришлось чутка подправить lib/python2.7/site-packages/mysql2pgsql/lib/postgres_writer.py, поскольку получил ошибку Exception: unknown datetime(6)

А именно заменить строку

1
elif column['type'] == 'datetime':

на

1
elif column['type'] == 'datetime' or column['type'].startswith('datetime('):

После этого запустил мегаинструмент еще раз и, не успев даже треть сигареты скурить, получил желаемое.

Рубрика: it | Метки: , , , | Оставить комментарий

Запуск X11 приложений по сети

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

Благо, OpenVPN настроен, IP своей рабочей станции знаю, sshd на ней запущен. Осталось всего ничего.

Качаю PuTTY, Xming и необходимые для него шрифты и устанавливаю все это добро.

PuTTY. Указываю IP удаленной рабочей станции и порт на котором висит sshd:

Перехожу в раздел Connection → SSH → X11, включаю «Enable X11 forwarding»:

И сохраняю сессию, чтоб каждый раз не вводить IP/порт и включать форвардинг:

Теперь Xming. Запускаю XLaunch, выбираю многооконный режим и оставляю номер дисплея 0:

Выбираю старт без запуска клиента:

Ничего не трогаю и жму «далее»:

И сохраняю конфиг на рабочий стол (можно куда угодно):

Далее завершаю настройку и Xming автоматом стартует. Загружаю сохраненную ранее в PuTTY сессию, ввожу логин/пароль и запускаю, например, terminator:

Или еще что-нибудь:

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

Рубрика: it | Метки: , , , , , | Оставить комментарий

OpenVPN на Arch Linux

Когда надо заиметь доступ в локальную сеть извне, скажем, с сети другого провайдера, например, на помощь приходит OpenVPN. Установка нехитрая и много времени не займет.

Ставим, собственно, сам openvpn и тулкит для генерации сертификатов и ключей безопасности:

1
sudo pacman -S openvpn easyrsa

Приступаем к генерации ключей и сертификатов. Создаем рабочий каталог и тянем в него необходимые для генерации файлы:

1
2
3
4
mkdir ~/easyrsa
cp -r /etc/easyrsa/x509-types ~/easyrsa/
cp /etc/easyrsa/openssl-1.0.cnf ~/easyrsa/
cd ~/easyrsa

Создаем инфраструктуру публичных ключей PKI:

1
easyrsa init-pki

И удостоверяющий центр CA:

1
easyrsa build-ca

Процесс создания удостоверяющего центра запросит пароль. Пароль желательно придумать сложный и запомнить/записать. Он понадобится когда будем подписывать сертификаты. Также надо будет придумать т. н. Common Name. Туда можно писать что угодно.

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

1
easyrsa gen-dh

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

1
easyrsa gen-crl

Отзыв же сертификата выполняется этой командой:

1
easyrsa revoke username

Далее выполняем запрос на приватный ключ и сертификат для нашего сервера (nopass указываем чтоб не пришлось каждый раз при перезагрузке сервера или рестарте OpenVPN вводить пароль):

1
easyrsa gen-req openvpn-server nopass

Понадобится придумать Common Name для сервера (можно вписать что угодно).

Подписываем сертификат для сервера (запросит придуманный ранее пароль для CA):

1
easyrsa sign-req server openvpn-server

Набор непосредственно для сервера почти готов, закидываем все необходимое в /etc/openvpn/keys/:

1
2
3
4
5
6
sudo mkdir -p /etc/openvpn/keys
sudo cp pki/ca.crt /etc/openvpn/keys/
sudo cp pki/crl.pem /etc/openvpn/keys/
sudo cp pki/issued/openvpn-server.crt /etc/openvpn/keys/
sudo cp pki/private/openvpn-server.key /etc/openvpn/keys/
sudo cp pki/dh.pem /etc/openvpn/keys/

И там генерим еще TLS-ключ:

1
2
cd /etc/openvpn/keys
sudo openvpn --genkey --secret ta.key

Теперь пишем конфиг для OpenVPN-сервера:

1
sudo nano /etc/openvpn/server/server.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
port 1194
proto udp
dev tap0
dh /etc/openvpn/keys/dh.pem
ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/openvpn-server.crt
key /etc/openvpn/keys/openvpn-server.key
tls-auth /etc/openvpn/keys/ta.key 0
crl-verify /etc/openvpn/keys/crl.pem
script-security 2
cipher BF-CBC
tls-server
comp-lzo
mute 10
persist-key
persist-tun
max-clients 50
keepalive 10 900
client-config-dir /etc/openvpn/ccd
ifconfig-pool-persist /etc/openvpn/ccd/ipp.txt
server-bridge 10.0.1.1 255.255.255.0 10.0.1.200 10.0.1.250
push "route 10.0.1.0 255.255.255.0"
status /var/log/openvpn/openvpn-status.log 1
status-version 3
log-append /var/log/openvpn/openvpn-server.log
verb 5

Строкой server-bridge 10.0.1.1 255.255.255.0 10.0.1.200 10.0.1.250 подразумевается, что сетевка, через которую бегает локальный трафик во внутренней сетке (к которой, собственно, надо подключиться извне) будет объединена в сетевой мост с интерфейсом tap0, с которым будет построен тоннель. Так же подразумевается что локалка имеет адреса из сети 10.0.1.0/24 и что подключаемые извне клиенты получат адреса из области 10.0.1.200-10.0.1.250 (следует убедится что они не заняты и DHCP-сервер из этой области пользователям ничего не выдаст).

Если надо добавить маршрут на какой-либо ресурс, на который пускает только из той сетки к которой будет построено подключение — добавляем в конфиг строку типа

1
push "route 172.16.0.123 255.255.255.255"

, где 172.16.0.123 — адрес того ресура, доступ к которому необходимо предоставить.

Не забываем создать каталог для логов OpenVPN-сервера:

1
sudo mkdir /var/log/openvpn/

Создаем юнит для systemd:

1
sudo nano /etc/systemd/system/openvpn.service
1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=OpenVPN server
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/etc/openvpn
ExecStart=/usr/bin/openvpn /etc/openvpn/server/server.conf
[Install]
WantedBy=multi-user.target

И не забываем за netctl-юниты для tap-интерфейса:

1
sudo nano /etc/netctl/openvpn-tap
1
2
3
4
5
6
Description="tap0 interface"
Interface=tap0
Connection=tuntap
Mode='tap'
User='nobody'
Group='nobody'

и моста:

1
sudo nano /etc/netctl/openvpn-bridge
1
2
3
4
5
6
Description="OpenVPN bridge"
Interface=br0
Connection=bridge
BindsToInterfaces=(enp5s0 tap0)
IP=static
Address=('10.0.1.1/24')

, где enp5s0 (у меня) сетевка, через которую работает LAN. И за саму сетевку не забываем:

1
sudo nano /etc/netctl/enp5s0
1
2
3
Description="LAN interface"
Interface=enp5s0
Connection=ethernet

Активируем и запускаем netctl-юниты:

1
2
3
4
5
6
sudo netctl enable openvpn-tap
sudo netctl enable enp5s0
sudo netctl enable openvpn-bridge
sudo netctl start openvpn-tap
sudo netctl start enp5s0
sudo netctl start openvpn-bridge

Активируем и запускаем systemd-юнит OpenVPN:

1
2
sudo systemctl enable openvpn
sudo systemctl start openvpn

Теперь приступаем к генерации клиентских ключей и сертификатов. И конфига для OpenVPN-клиента.

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

1
2
cd ~/easyrsa
easyrsa gen-req username

Придется придумать пароль для username (клиенту его нужно будет вводить при запуске VPN-соединения с сервером) и Common Name (как всегда, можно вписать что угодно). Если есть желание чтоб клиенту не надо было вводить пароль, то после username можно дописать nopass. Но так неинтересно.

Подписываем (затребует CA пароль):

1
easyrsa sign-req client username

Теперь создадим, для удобства, какой-нибудь каталог, в котором соберем все что необходимо будет отдать клиенту для подключения:

1
2
3
4
5
6
mkdir username.openvpn
cp pki/issued/username.crt username.openvpn/
cp pki/private/username.key username.openvpn/
cp pki/ca.crt username.openvpn/
sudo chmod +r /etc/openvpn/keys/ta.key
cp /etc/openvpn/keys/ta.key username.openvpn/

И создадим конфиг для OpenVPN-клиента:

1
nano username.openvpn/username.ovpn
1
2
3
4
5
6
7
8
9
10
11
12
client
dev tap
proto udp
remote 1.2.3.4 1194
tls-client
ca "ca.crt"
tls-auth "ta.key" 1
cert "username.crt"
key "username.key"
remote-cert-tls server
comp-lzo
tun-mtu 1500

, где вместо 1.2.3.4 пишем IP-адрес (или доменное имя) сервера, к которому клиенту надо подключиться извне.

Архивируем все это добро:

1
tar -cvzf username.openvpn.tar.gz username.openvpn/

И отдаем клиенту любым удобным способом (почта, флешка etc). Не забываем поделиться ссылкой на OpenVPN-клиент.

Рубрика: it | Метки: , , , , | Оставить комментарий

Сливание прошивки с древней железяки

Недавно довелось сливать прошивку с одной не шибко новой железки. К сожалению, ни одним из поисковиков в интернете такую же прошивку, чтоб не мучиться, не нашел.

RS-232 кабелем подсоединился к железке и начал подбирать нужный baud rate (прошивка, как и загрузчик, к слову, не оригинальные, которые идут в поставке). Подобрав, получил приветствие:

1
2
3
4
5
6
7
8
9
10
11
RedBoot(tm) bootstrap and debug environment [ROM]
Red Hat certified release, version 1.92 - built 10:44:29, Apr  9 2004
Platform: IXDP425 Development Platform (XScale)
Copyright (C) 2000, 2001, 2002, Red Hat, Inc.
RAM: 0x00000000-0x10000000, 0x0001f9b8-0x0ffd1000 available
FLASH: 0x50000000 - 0x50800000, 64 blocks of 0x00020000 bytes each.
Bootcode Version 1.9 2004/04/08
== Executing boot script in 2.000 seconds - enter ^C to abort

Естественно, сразу зажал ^C и получил приглашение:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
RedBoot> help
Load RAM-Statup Redboot!
   ramstart
Upgrade Redboot itself!
   upgradeBTLD
Manage aliases kept in FLASH memory
   alias name [value]
Set/Query the system console baud rate
   baudrate [-b <rate>]
Manage machine caches
   cache [ON | OFF]
Display/switch console channel
   channel [-1|<channel number>]
Compute a 32bit checksum [POSIX algorithm] for a range of memory
   cksum -b <location> -l <length>
Display (hex dump) a range of memory
   dump -b <location> [-l <length>] [-s] [-1|2|4]
Execute an image - with MMU off
   exec [-w timeout] [-b <load addr> [-l <length>]]
        [-r <ramdisk addr> [-s <ramdisk length>]]
        [-c "kernel command line"] [<entry_point>]
Manage FLASH images
   fis {cmds}
Manage configuration kept in FLASH memory
   fconfig [-i] [-l] [-n] [-f] [-d] | [-d] nickname [value]
Execute code at a location
   go [-w <timeout>] [entry]
Help about help?
   help [<topic>]
Set/change IP addresses
   ip_address [-l <local_ip_address>] [-h <server_address>]
Load a file
   load [-r] [-v] [-d] [-h <host>] [-m <varies>] [-c <channel_number>]
        [-b <base_address>] <file_name>
Compare two blocks of memory
   mcmp -s <location> -d <location> -l <length> [-1|-2|-4]
Fill a block of memory with a pattern
   mfill -b <location> -l <length> -p <pattern> [-1|-2|-4]
Network connectivity test
   ping [-v] [-n <count>] [-l <length>] [-t <timeout>] [-r <rate>]
        [-i <IP_addr>] -h <IP_addr>
Reset the system
   reset
Set/Read MAC address for NPE ethernet ports
   set_npe_mac [-p <portnum>] [xx:xx:xx:xx:xx:xx]
Display RedBoot version information
   version
Display (hex dump) a range of memory
   x -b <location> [-l <length>] [-s] [-1|2|4]
Boot in zcom's way!
   ixp425boot
RedBoot>

Посмотрел где, что и по каким адресам лежит:

1
2
3
4
5
6
7
8
RedBoot> fis list
Name              FLASH addr  Mem addr    Length      Entry point
RedBoot           0x50000000  0x50000000  0x00060000  0x00000000
RedBoot config    0x507C0000  0x507C0000  0x00001000  0x00000000
Config backup     0x507C1000  0x507C1000  0x00001000  0x00000000
FIS directory     0x507E0000  0x507E0000  0x00020000  0x00000000
ixp425            0x50060000  0x50060000  0x00600000  0x01600000
RedBoot>

Отлично, ixp425, а это и есть прошивка, мне как раз и надо! Включил в PuTTY запись всего выхлопа в файл и запустил дамп:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
RedBoot> dump -b 0x50060000 -l 0x00600000
50060000: 28 CD 3D 45 00 3C 80 00  00 00 04 03 00 00 00 00  |(.=E.<..........|
50060010: 43 6F 6D 70 72 65 73 73  65 64 20 52 4F 4D 46 53  |Compressed ROMFS|
50060020: 47 3C 20 1F 00 00 00 00  00 00 03 8A 00 00 00 20  |G< ............ |
50060030: 5A 57 4C 39 33 30 30 00  00 00 00 00 00 00 00 00  |ZWL9300.........|
50060040: 41 FF 00 00 00 02 88 00  00 08 4D 88 E1 0F 20 00  |A.........M... .|
50060050: E3 82 20 C0 E1 21 F0 02  E3 CF 20 1F E2 82 38 01  |.. ..!.... ...8.|
...
5065FFC0: BB 17 BA F7 3A 4B AA 16  A7 C9 CF 7F 88 C9 8A 45  |....:K.........E|
5065FFD0: BA 1D F8 F0 A7 7C 68 0D  F2 74 27 05 4B 55 9C 55  |.....|h..t'.KU.U|
5065FFE0: BA 5F EA 37 7C 57 22 C7  BB 74 A5 7D CB 03 A0 1F  |._.7|W"..t.}....|
5065FFF0: C8 D8 A2 55 2F 54 AA D5  0B 7B EA F5 62 83 AA 25  |...U/T...{..b..%|

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
import string
fd = open('firmware.dump', 'r')
raw_data = fd.read()
fd.close()
byte = 0
last_byte = 3964936
fd = open('firmware.rmt', 'w')
raw_data_arr = string.split(raw_data, '\r')
for line in raw_data_arr:
    item_arr = string.split(line, ' ')
    for i in range(1, 18):
        if i != 9:
            if byte < last_byte:
                fd.write(chr(int(item_arr[i], 16)))
                byte += 1
            else:
                break
fd.close()

3964936 — последовательный номер последнего байта — я получил отследив HEX-последовательность окончания прошивки «30 38 34 36» + еще 4 байта (контрольная сумма, насколько я понял).

Рубрика: it | Метки: , , , , | Оставить комментарий