Ваш
Системный Администратор

Качественная IT инфраструктура - залог успеха компании.

Me.Doc откат на 188-ю версию

schedule folder_open Any, Security label_outline Backup, MeDoc

UPD: Выпустили обновление 10.01.190.
Так, что откатываемся и обновляемся.

В связи с недавними событиями, 189-я версия Me.Doc является небезопасной и не рекомендуется к использованию.
Сама компания заявляет что у неё уже есть обновление ПО, гарантированно (Ха-Ха), не содержащее бэкдоров, но КиберПолиция не даёт им добра на его развёртывания.
В данный момент единственным, способом хоть как-то обезопасить себя, является откат на 188-ю версию.

1. Загружаем с офф. сайта 188-ю версию
2. Загружаем скрипт, правящий версию в БД
3. Останавливаем службы Firebird server - ZvitGrp и ZvitGrp
4. Копируем каталог C:\ProgramData\Medoc_ (оттуда нам понадобится БД, да и вообще пусть будет)
5. Производим деинсталляцию 189-й версии с компьютера/сервера (убедитесь что не забыли про п.4.)
6. Инсталлируем 188-ю версию
7. Проверяем что в службах не запущены службы из п.3.
8. Копируем каталог db из копии сделаной в п.4. в ново установленную 188-ю версию, с заменой файлов (
C:\ProgramData\Medoc\MedocSRV_ если сетевая)
9. Запускаем ScriptRun.exe из каталога с установленным Me.Doc и в открывшемся окне выбираем скрипт загруженный в п.2. (слева в меню кнопка “створити”) и после выбора “виконаты”
10. После отработки скрипта запускаем службы из п.3.
11. Проверяем работоспособность.

Что конкретно делает скрипт - не известно, по теории он должен просто править версию ПО указанную в БД с 189-й на 188-ю ибо без этого 188-я версия не будет работать с БД.
Не удаляйте резервную копию до тех пор пока не выяснится как жить дальше :)

Установка 32-х битного Wine в CentOS7

schedule folder_open Unix label_outline CentOS, Fix
1
2
sudo rpm -Uvh http://rpm.playonlinux.com/playonlinux-yum-4-1.noarch.rpm
sudo yum install playonlinux glibc.i686 alsa-lib.i686 libXv.i686 libXScrnSaver.i686 libSM.i686 libXi.i686 libXrender.i686 libXrandr.i686 freetype.i686 fontconfig.i686 zlib.i686 glib2.i686 libstdc++.i686

Дальше заходим в PlayOnLinux, устанавливаем x86 версию Wine и радуемся жизни.

Q: Почему PlayOnLinux?
A: Просто удобная обёртка над Wine и не засирает систему.

Не работает TouchPad в CentOS7 [Dell Vostro 15]

schedule folder_open Any, Hardware, Unix label_outline CentOS, Fix, Notebook

Возникла проблема при установке CentOS7 на мой Dell Vostro 15, после установки (и во время) не работал TouchPad.
Проблема в общем решалась доволи просто, больше ушло времени на Google-инг.

Добавляем опцию i8042.nopnp в конфиг grub /etc/default/grub:

1
GRUB_CMDLINE_LINUX_DEFAULT="... quiet splash i8042.nopnp"

Обновляем grub:

UEFI

1
sudo grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg

Legacy

1
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Добавляем модуль i2c_hid в бдэклист (от root-а):

1
2
sudo su
echo "blacklist i2c_hid" > /etc/modprobe.d/i2c-hid.conf

Перегенерируем initramfs (от root-а):

1
2
cp -v /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak
dracut -f

Дальше перезагрузка и радость :)

Gitlab-CI deploy OctoPress blog

schedule folder_open Any, Development, Unix label_outline Docker, Fix, Git, Project, Ruby, SSH

А зачем? Веяние моды?
Нет. Просто я замучался бороться в глюками Ruby под Win10 :)

Для работы используются две ветки master и development.
development ветка используется для деплоя на тестовый сервер и проверки что всё собралось верно, после “мержа” в master ветку, уже из неё деплоится на основной сервер.

Так как OctoPress генерирует чистую статику то в качестве development сервера можно использовать просто подкаталог на основном.

1
2
/var/www/blog
/var/www/blog/dev

При деплое master ветки, rsync удалит этот каталог. Также, никто не мешает заместь rsync, деплоить в тот-же github-page и вообще куда душе угодно.

$SSH_DEPLOY_KEY - “Secret Variables” из настроек проекта в Gitlab (Проект -> Settings -> CI/CD Pipelines -> Secret Variables).
Необходимо создать новую переменную и в качестве value вставить SSH Private Key, public ключ, соответственно, должен быть прописан на сервере - у пользователя из-под которого будем осуществлять деплой и иметься права на запись в соответствующие каталоги.
my_theme - заменить на имя используемой вами темы.

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
image: ruby:2.1
# Подготовка окружения
before_script:
  # Ставим Node.JS, есть зависимость
  - curl -sL https://deb.nodesource.com/setup_4.x | bash -
  - apt-get install nodejs rsync -y
  - mkdir "${HOME}/.ssh"
  - echo "${SSH_DEPLOY_KEY}" > id_rsa # копируем SSH private key
  - chmod 600 id_rsa
  - ssh-keyscan -H sysalex.com >> "${HOME}/.ssh/known_hosts" # Добавляем наш хост в известные
  - bundle install # ставим гемы

# Master ветка
PROD generate and deploy:
  script:
    - cp -rf .themes/my_theme/* ./ # Копируем тему (указать свою)
    - rake generate # производим сборку
    # синхронизируем каталог public с каталогом на сервере
    - rsync -hrvz --delete  -e 'ssh -i id_rsa' public/ user@example.com:/path/to/dir/
  only:
    - master

# Development ветка  
DEV generate and deploy:
  script:
    - cp -rf .themes/my_theme/* ./
    - rake generate
    - rsync -hrvz --delete  -e 'ssh -i id_rsa' public/ user@example.com:/path/to/dir/dev/
  only:
    - develop

Среднее время сборки, составляет 3 мин. из них 90% это установка необходимых Gem, можно значительно сократить это время - если использовать, заранее, подготовленный образ с уже установленными, необходимыми Gem (может, когда-нибудь, заморочусь этим повросом).

VirtualBox USB device connect

schedule folder_open Hardware, Unix label_outline Fix, Ubuntu

При установке VirtualBox (например в Ubuntu) локальный пользователь не добавляется в группу vboxusers,при установке плагина Oracle VM VirtualBox Extension Pack необходимого для работы с USB, пользователь так-же не добавляется в группу.

К счастью это легко исправить всего одной командой, до тех пор VB просто не будет “видеть” USB устройств
(как вариант можно запустить VB из под root но это сильно не рекомендуется делать)

1
sudo usermod -a -G vboxusers YOUR_USER

Chef, auto registration node on server

schedule folder_open Development, Unix label_outline Chef, Chef-client, Chef-server

Скрипт для установки Chef-Client и автоматической регистрации ноды на Chef-Server - без Knife и лишних телодвижений.

node_name соответсвует текущему имени хоста указанному в /etc/hostname ($HOSTNAME переменная окружения), в run_list="" необходимо указать рецепт(ы)/роль(и) или оставить пустым.
Другие параметры Chef-Client смотрите в офф. документации client.rb , chef-client

Если используются подготовленные инсталяционные образы (у меня так) то Chef-Client лучше интергрировать сразу в образ вместе с postinstall скриптом.

В случае если заданное имя хоста не резолвиться через DNS то на Chef-Server параметр FQDN останется незаполненным для этой ноды, в большинстве случаев это не мешает работе но при возможности необходимо обеспечить резолвинг имени через DNS сервер или /etc/hosts и при следующей синхронизации FQDN будет заполнена.

Имя ноды необязательно должно соответсвовать имени хоста но в этом случае необходимо явно указать node_name в конфигурационном файле /etc/chef/client.rb до первого запуска Chef-Client, также это необходимо сделать если по какой либо причине пришлось изменить имя хоста (необходимо в node_name прописать старое имя) иначе клиент не сможет пройти авторизацию на сервере.
Для задания имени ноды можно использовать переменную окружения $NODE_NAME

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
#!/bin/bash

# settings
chef_package="chef_12.9.41-1_amd64.deb" # Set Chef package version
chef_package_url="https://packages.chef.io/stable/ubuntu/14.04/${chef_package}" # Download URL
org_name="ORG_NAME" # set organizations name  
validation_name="validator" # Validator username
chef_url="https://chef.server/organizations/${org_name}"

config_dir="/etc/chef" # config directory
log="/var/log/chef-client.log" # log file

#####################################
# Roles and Recipes, assigned for Node
# "role[ROLE_NAME], recipe[RECIPE_NAME], ..."
run_list=""

wget ${chef_url}
dpkg -i ${chef_package}

# Create client config file
# See other parameters in the officiall documentation
# https://docs.chef.io/config_rb_client.html
cat <<EOF > ${config_dir}/client.rb
log_level        :info
log_location     '${log}'
chef_server_url  '${chef_url}'
validation_client_name '${validation_name}'
ssl_verify_mode  :none
node_name ENV[NODE_NAME] || ENV[HOSTNAME]
EOF

# Create "validation" key file
# Insert content your validate private key
cat <<EOF > ${config_dir}/validation.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBA
......
dPQ7sje3GKXqsGfT58hBCCPc=
-----END RSA PRIVATE KEY-----
EOF

# Run client
chef-client -r "${run_list}"

Ubuntu: восстановление системных файлов

schedule folder_open Unix label_outline Fix, Ubuntu

Сегодня человек попросил переустановить Ubuntu только потому что зачем-то удалил каталог /usr/share/icons, но данная проблема решается намного проще и главное быстрее чем переустановка и настройка новой системы.

1. Загружаем ISO образ Ubuntu с офф. сайта. (в моём случае это Ubuntu 14.04.4 x64)

2. Монтируем ISO образ

1
2
mkdir /tmp/iso
sudo mount -t iso9660 -o loop /path_to_iso/ubuntu-14.04.4-desktop-amd64.iso /tmp/iso

3. Монтируем filesystem.squashfs - образ файловой системы со всеми необходимыми нам файлами

1
2
mkdir /tmp/squashfs
sudo mount -t squashfs -o loop /tmp/iso/casper/filesystem.squashfs /tmp/squashfs

если в наличии есть “флэшка” с Ubuntu то п.1-2 можно пропустить и взять filesystem.squashfs оттуда, он расположен в каталоге /casper/filesystem.squashfs относительно корня накопителя.

к примеру у меня есть “флэшка” с Ubuntu и тогда команда из п.3 примет такой вид

1
2
mkdir /tmp/squashfs
sudo mount -t squashfs -o loop /media/sysalex/casper/filesystem.squashfs /tmp/squashfs

где sysalex это LABEL флэш накопителя (Ubuntu как правило так именует).
После того как смонтировали filesystem.squashfs можно непосредственно занятся копирыванием недостающих файлов.

4. Копируем недостающие файлы

1
sudo cp -rf /tmp/squashfs/usr/share/icons/* /usr/share/icons/

5. Размонтируем смонитрованные файловые системы

1
sudo umount /tmp/iso /tmp/squashfs

На этом можно закончить и с чувством выполненного долга выпить чаю :)

Mikrotik - узнаём IP без использования динамического DNS

schedule folder_open Hardware, Security label_outline DNS, MikroTik

Меленький лайфхак для микторика.
Часто забывают про Tools-ы в ПО MikroTik а там есть немало полезных утилит и в том числе fetch.
Фактически это позволяет получать текущий IP роутера (в случае динамики) - без рекламы и смс без использования сервисов на подобии NoIP и скриптов для них.

Данная команда сделает HTTP запрос на адрес sysalex.com, 8888 порт

1
/tool fetch keep-result=no  address=sysalex.com src-path=/ mode=http  port=8888

Нам остаётся только обработать запрос на нашей стороне. В простейшем варианте это NetCat nc -l -p 8888 -v В “Connection from” будет собственно сам адрес роутера.

Остаётся добавить скрипт и задачу в планировщик - для автоматизации процесса сливания IP ;)

Docker CentOS 7

schedule folder_open Unix label_outline CentOS, Docker, Fix

При установке Docker из офф. репозитория (статья на docs.docker.com), устанавливается systemd скрипт не поддерживающий добавления дополнительных опций инициализации демона через файлы /etc/sysconfig/docker и /etc/default/docker, использование которых описано во многих статьях документации и гайдах (настройка DNS и т.д.).
cat /usr/lib/systemd/system/docker.service

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target docker.socket
Requires=docker.socket

[Service]
Type=notify
ExecStart=/usr/bin/docker daemon -H fd://
/* skip */

[Install]
WantedBy=multi-user.target

Для исправления этого неудобства необходимо инит скрипт привести к такому виду
cat /etc/systemd/system/docker.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target docker.socket
Requires=docker.socket

[Service]
Type=notify
# Указываем путь к файлу
EnvironmentFile=-/etc/default/docker
ExecStart=/usr/bin/docker daemon $DOCKER_OPTS -H fd://
/* skip */

[Install]
WantedBy=multi-user.target

После в файле /etc/default/docker можно указать доп. опции инициализации сервиса, к примеру
cat /etc/default/docker

1
DOCKER_OPTS="--dns=172.17.0.1 --dns=8.8.8.8 --dns=8.8.4.4"

В принципе не знаю это нормальная ситуация и оно так везде или только проблема в CentOS офф. репозитории.
Статья на офф. сайте по конфигурации Docker - Control and configure Docker with systemd.

Telegram бот для хуков GitLab

schedule folder_open Development, Monitoring label_outline Git, Go, Project

Сразу откажусь от ответственности и реальных или мнимых убытков нанесённых данным постом - вообще какие претензии к человеку который до этого на GO только HelloWord написал ;)

На этого бата возложена только одна задача - принять WebHook от Gitlab и отрапортовать об этом в канал Telegram. Соответственно Бот должен быть приглашен в этот канал.

Бот принимает только одну команду “/start_hook”, она должна быть подана в канале в который планируется вести публикацию событий - при этом бот запомнит ID канала. (мультиканальность не предусмотрена)

Web сервис для принятия хуков запущен на localhost порт 8000 (по умолчанию) что в свою очередь предполагает что данный бот будет запущен непосредствено на сервере Gitlab Community. Так как не предусмотренно никаких средств авторизации (пока) то не стоит использовать его в других вариантах.

Как вариант можно организовать “авторизацию” заменив “/hook” на параметр подлиннее - подобие API токена

1
r.POST("/a673f1b90e7d82dae08eedddce14f9910df467a868569e059e66ac4431acfc72", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {

и в проекте, в хуках, задавать адрес не http://localhost:8000/hook а http://localhost:8000/a6..0e7d82…….431acfc72.

В результате на каждый Push в репозиторий, в Telegram канал будет падать уведомление

Необходимо задать API ключ и имя администратора (только от этого пользователя будут приниматься команды) - константы TelegramAPI и AdminName

Проект на Github telegram_gitlab_bot для как говорится “обратной связи” :)

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
package main

import (
  "encoding/json"
  "fmt"
  "log"
  "net/http"
  "strconv"
  "strings"

  "github.com/Syfaro/telegram-bot-api"
  "github.com/julienschmidt/httprouter"
)

type (
  // Структура Gitlab JSON
  GitLabHook struct {
      ObjectKind   string     `json:"object_kind"`
      Ref          string     `json:"ref"`
      UserName     string     `json:"user_name"`
      Repository   Repository `json:"repository"`
      Commit       []Commit   `json:"commits"`
      TotalCommits int        `json:"total_commits_count"`
  }
  Repository struct {
      Homepage string `json:"homepage"`
      Name     string `json:"name"`
  }
  Commit struct {
      Id      string `json:"id"`
      Message string `json:"message"`
      Url     string `json:"url"`
      Author  Author `json:"author"`
  }
  Author struct {
      Name  string `json:"name"`
      Email string `json:"email"`
  }
  // -------------
  HookMessage struct {
      Text string
  }
)

// API ключ для бота - выданный @BotFather
const TelegramAPI string = "YOU BOT API"
const AdminName string = "Admin username without @"

var (
  webhook_response chan HookMessage
  ChatID           int
  Text             string
  UserName         string
  FromUserID       int
  Message          tgbotapi.Chattable
  GitLabChatID     int
)

func main() {
  // Канал для хуков
  webhook_response = make(chan HookMessage, 5)
  // Подключаем бота
  tgbot, err := tgbotapi.NewBotAPI(TelegramAPI)
  if err != nil {
      log.Panic(err)
  }

  ucfg := tgbotapi.NewUpdate(0)
  ucfg.Timeout = 60
  updates, err := tgbot.GetUpdatesChan(ucfg)
  // Запускаем поток с WEB сервисом
  go WebHook()
  // Магия
  for {
      select {
      // Ловим обновления с канала бота
      case update := <-updates:
          // Текст сообщения в чате.
          // Если это в привате бота то любой текст
          // Если из публичного чата то всё начинающееся с "/"
          Text = update.Message.Text
          // ID диалога, если приват то ID равен ID пользователя
          // Если сообщение потупило из публичного чата то ID этого чата
          ChatID = update.Message.Chat.ID
          // Имя пользователя от которого поступило сообщение
          UserName = update.Message.From.UserName
          // ID этого пользователя
          FromUserID = update.Message.From.ID
          // Отлавливаем команду "/start_hook" в диалогах
          if Text == "/start_hook" && UserName == AdminName {
              GitLabChatID = ChatID
              Message := tgbotapi.NewMessage(ChatID, fmt.Sprintf("Пользователь %s запустил публикацию событий Git репозиториев в этот канал", UserName))
              tgbot.Send(Message)
          }
          // Обрабатываем сообщения от WebHook
      case hook := <-webhook_response:
          if GitLabChatID != 0 { // Обрабатываем только PUSH хуки
              Message := tgbotapi.NewMessage(GitLabChatID, hook.Text)
              tgbot.Send(Message)
          }
      }
  }
}

// WEB сервис
func WebHook() {
  r := httprouter.New()
  // Обрабатываем запрос к корню, как-бы и не нужен но пусть будет
  r.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
      fmt.Fprint(w, "Sorry!\n")
  })
  // Обрабатываем POST запрос от GitLab
  r.POST("/hook", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
      u := GitLabHook{}

      json.NewDecoder(r.Body).Decode(&u)

      webhook_response <- MakeMessage(u)

      w.Header().Set("Content-Type", "application/json")
      w.WriteHeader(201)
  })

  http.ListenAndServe("localhost:3000", r)
}

func MakeMessage(hook GitLabHook) HookMessage {
  message := "New commits in \"" + hook.Repository.Name + "\"\n" +
      "Branch: " + strings.Replace(hook.Ref, "refs/heads/", "", -1) + "\n" +
      "User: " + hook.UserName + "\n\n"
  for _, commit := range hook.Commit {
      message += fmt.Sprintf("🔹 %s \n", strings.TrimRight(commit.Message, "\n"))
  }
  message += "\nTotal commits: " + strconv.Itoa(hook.TotalCommits) + "\n"
  return HookMessage{message}
}