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

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

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}
}

[RUBY] Быстрое сканирование портов в сети

schedule folder_open Development, Monitoring, Security label_outline Ruby, Scanning

Пример скрипта для быстрого сканирования диапазона адресов на предмет открытого TCP порта.
Сканирование ведётся в асинхронном режиме.
Гемы: Сelluloid GEM для потоков, Net-Ping GEM что-бы не писать велосипед с сокетами

1
2
3
gem install celluloid net-ping
wget https://gist.githubusercontent.com/POStroi/181d2dd3291ce707c8cc/raw/19931c85c6fe7a088d289a73bbe1282f9b29fd78/scan.rb
ruby scan.rb 192.168.0.1-254 80

Github GIST

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
#!/usr/bin/env ruby
# gem install celluloid net-ping
require 'celluloid/current'
require 'net/ping'
require 'ipaddr'

class ScanPort
  include Celluloid

  def scan host, port
    res = Net::Ping::TCP.new(host, port)
    if res.ping?
      type = detect(res.host)
      puts "[+] #{host} : #{type}\n"
    end
  end

  def detect(ip)
    begin
      data = Net::HTTP.get_response(ip, '/')
      if !data.body.empty?
        response = data.body
      else
        response = data.header['set-cookie']
      end

      case response
        when /LuCI/
          'OpenWRT LuCI'
        when /mikrotik/
          'MikroTik'
        when /AIROS_/
          'Ubiquiti'
        when /DVR Components/
          'eDVR'
        when /ITV/
          'ITV WebServer Client'
        when /It works!/
          'Blank NGINX'
        when /m.jsp/
          'Phantom DWDR'
        when /DVR IE Video/
          'DVR IE Video - model?'
        when />TL-(.*)</
          "TP-Link #{$1}"
        when /doc\/page\/login.asp/, /\/index.asp">location<\/a>./
          'HikVision DVR'
        when />WEB SERVICE</
          'Maybe - Dahua DVR'

        else
          'Not detect'
      end
    rescue
      'Response error'
    end
  end
end

def make_hosts_list(network)
  ip_range = IPAddr.new network
  (ip_range.to_range).map(&:to_s)
end

def main network, port
  hosts = make_hosts_list(network)
  hosts.each do |host|
    sc = ScanPort.new
    sc.async.scan host, port
  end
end

if ARGV.size < 2
  puts "USE: #{File.basename(__FILE__)} network port "
  puts "Sample: #{File.basename(__FILE__)} 192.168.0.1/24 80 "
  exit
end

main ARGV[0], ARGV[1].to_i

Ruby on Rails перевод на Русский имени модели

schedule folder_open Any, Development label_outline Hell, Ruby

Странный вопрос мне задали на Toster, спросили как в “рельсах” получить перевод на русский язык имя модели/класса.

Сам вопрос:

CapeRatel:
POS_troi: Ну да. Не подскажите как из обьекта @article вывести переведенное на русский язык имя класса в нижнем регистре. А то г..кодить не хочется.

Выданное решение:

config/locales/ru.yml

1
2
3
4
ru:
  class_name_translate:
    article: 'Статьи'
    tags: 'Тэги'

в контроллерах

1
2
3
4
5
6
7
8
9
10
11
def get_articles
  @article = Article.all
  @tag = Tag.all
  @article_ru = translate_model_name(@article).mb_chars.downcase
  @tag_ru = translate_model_name(@tag).mb_chars.downcase
end

#в application.rb
def translate_model_name(obj)
  I18n.t(obj.model_name.i18n_key, scope: :class_name_translate)
end

Есть у кого идеи предложения? Прошу под кат в комментарии, интересно мнение “рэльсистов” :)

Notebook Wi-Fi fix

schedule folder_open Hardware label_outline Wireless

Не редко у ноутбуков производства HP/Lenovo и других, встречается ситуация когда Wi-Fi напрочь отказвается работать. В диспетчера зедач отображается как нормально работающее устройство но отказывается включатся по кнопке, не помогает установка оффициальных драйверов и всяких “родных” утилиток.

Мне такие ноутбуки приходят как правило с “убитым” BIOS - люди до потери пульса прошивают в надежде на исправление ситуации (Иногда помогает).

Проблема частая на ноутбуках с новоиспечённой Windows 8х.

Предлагаю быстрое и надёжное решение этой проблемы - необходимо снять Wi-Fi карту и заклеить контакт соединяющийся с 20-м контактом mPCI-е слота.
Если Wi-Fi карта рабочая то всё будет работать, есдинственный минус это то что пропадёт возможность отключить Wi-Fi штатными средствами.
Вид сзади, второй контакт справа от выреза.

Указанный “пин” зарезервирован под функцию управления питанием модулей/энергосбережения. При низком уровне на нём, модуль переходит в режим “энергосбережения” что равносильно отключению.

Отключение Bluetooth в CentOS и аплета в панели

schedule folder_open Any, Unix label_outline Bluetooth, CentOS

Кто вообще нынче изпользует Bluetooth?
Не нашел в настройках “Гнома” большой, зелёной, кнопки “Выключить это неподобство” и при этом почему-то в CentOS 7 его включают по умолчанию. Мне не жалко памяти и батареи но жутко раздражает :)

Отключаем сам сервис:

1
sudo systemctl disable bluetooth.service

но для того чтобы убрать аплет с верхней панели (рядом с часами), необходимо подправить файл /usr/share/gnome-shell/js/misc/config.js:

1
const HAVE_BLUETOOTH = 1;

необходимо сбросить в 0

1
const HAVE_BLUETOOTH = 0;

Перезаходим и радуемся :)