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

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

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;

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

А вы разве так не делаете?

schedule folder_open Any, Hardware label_outline Hell

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

Далее последовал примерно такой монолог:

“Он уже не раз так выключался, мы делали так - включали, заворачивали его плотно в ткань и клали под подушку. Через время доставали и он продолжал нормально включаться. А вы разве так не ремонтируете?”.

Теперь вот сидим и думаем, где мы в этой жизни что-то пропустили. :)

Сказ о том как у мужика колёса воровали

schedule folder_open Any, Security label_outline Hell, Wireless

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

Сегодня упал на хвост к товарищу, он ехал в соседний город, а мне так-же не мешало бы заехать к клиенту и в результате мы оказались у одного клиента с не работающим видео наблюдением. (Товарищ по профилю занимается видео наблюдением)

FL.RU сливает а мы парсим

schedule folder_open Any, Security label_outline Hell


Сегодня на Хабре появился пост о том что в индексе Yandex имеется большое количество файлов и как оказывается не какие нибудь публичные файлы из профилей и т.д. а что нинаесть самые приватные - из приватных переписок заказчика и исполнителя.

Собственно эта тема получила большой резонанс на Хабре но как-то вообще не особо осветилась в более глобальных маштабах (вообще рекомендую к прочтению комментариев к посту на Хабре).

Разработчики (Админы?) среагировали доволи быстро на проблему, но как-то не понятно они на неё среагировали - в комментариях предположили что срочным образом, в конфиг Nginx был прописан такой строчко location ~ *.(doc|docx|xls|xlsx|ppt|pptx|rar|zip|pdf|txt)$ { return 403; }.
Я даже по началу растроился но после быстрых тестов, оказалось что 403 ошибку дают только перешедим без рефера с https://st.fl.ru, у меня немного отлягло, не зря пока скрипт писал (правда потом контору спалил ValdikSS но на момент написания поста, ситуация не поменялась).

Мне от нечего делать стало интересно слить часть файлов засветившихся в индексе, чем и занялся (пишу пост а оно всё качается ;))

Gett SSD params

schedule folder_open Any, Hardware, Unix label_outline SSD

Пользователям Windows намного больше повезло в плане различных утилит для получения различной информации о “железе”, в Linux хоть и не всё так красиво выглядит но утилиток тоже хватает, правда в большенстве случаев консольных :)

Захотелось посмотреть чем живёт мой SSD диск KINGSTON SVP200S37A120G под CentOS7, включен ли TRIM и другие плюшки.