Обновление сервисов, запущенных на Go

в 8:55, , рубрики: aes, aes-128, AES-256, Go, криптография, обновление кода, обновление кода на лету, Программирование

Я люблю программировать на Go, но больше всего сейчас мне нравится программировать в gobot для Raspberry Pi. Каждое изменение в коде требует определенное время на нудные операции, связанные с обновлением кода. Сначала я должен остановить процесс, так как Filezilla отказывается писать в исполняемый файл, когда процесс запущен, загрузить новый исполняемый файл по SFTP и запустить его (это не только нудно, но еще 10-20 секунд простоя, когда процесс остановлен).

Аналогичная ситуация меня преследует и при разработке для обычного веба на Go. Именно в gobot я вынужден очень часто обновлять код, что связанно со стилем разработки, который приносит мне удовольствие в свободное время. С разработкой нового пакета обновлять код, написанный на Go стало проще и быстрее.

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

go get github.com/CossackPyra/updater  

Этой командой вы установили мой updater в ваш Go, также вы можете установить тулзу в командную строку следующей командой:

go get github.com/CossackPyra/updater/pyra-poster

Вот пример веб-сервиса с возможностью обновления:

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "github.com/CossackPyra/updater"
)

func main() {

    http.HandleFunc("/", handle_def)

    os.Mkdir("tmp", 0700)
    u1 := updater.UpdaterServer("tmp", []byte("1234567890123456"), os.Args[0])
    http.Handle("/updater-me", u1)
    http.ListenAndServe(":9999", nil)
}

func handle_def(w http.ResponseWriter, r *http.Request) {
    fmt.Println("def ", r.Method)
    io.WriteString(w, "v 24n")
    io.WriteString(w, r.URL.Path)
    println(r.URL.Path)
}

Именно следующие 3 строчки добавляют возможность обновлять программу:

    "github.com/CossackPyra/updater"
...
    u1 := updater.UpdaterServer("tmp", []byte("1234567890123456"), os.Args[0])
    http.Handle("/updater-me", u1)

В данном случает все шифруется 16-байтовым ключом []byte(«1234567890123456»), а 31323334353637383930313233343536 это его шестнадцатеричное представление. new-service новый исполняемый файл, и вот команда, чтобы обновить старый процесс.

pyra-poster 31323334353637383930313233343536 new-service http://127.0.0.1:9999/updater-me

Как это работает?

Весь код уместился в 280 строках. У меня больше доверия в коду, в котором можно быстро разобраться. Я не до конца уверен в криптографической стойкости моего алгоритма шифрования и как вообще можно быть уверенным в чем-то подобном, поэтому мне интересно его обсудить с вами.

При инициализации хендлера в updater.UpdaterServer, генерится случайная последовательность 16 байт (rand1). Для того, чтобы обновить код, надо выполнить два запроса к хендлеру. Первый GET запрос получает эти случайные 16 байт (rand1). Случайные 16 байт (rand1) и секретный пароль (key1) используются для того, чтобы зашифровать отправляемый исполняемый файл алгоритмом AES и отправить шифрограмму методом POST.

Сначала буфер наполняется данными согласно следующему рецепту:
— Случайные 20 байт (это не rand1, а rand0 — в настоящий момент они никак не используются);
— 20 байт — хеш исполняемого файла sha1;
— «pyra-poster» — последовательность байт, название протокола;
— 6 байт (1 — int16 Little Endian, 0 — int32 Little Endian) — версия протокола;
— сам исполняемый файл.

На этот буфер налагается гамма-функция методом xor, полученная блочным шифром AES(key1, rand1). В результате получается, шифротекст, который отправляется в хендлер методом POST.

На сервере для дешифрования на последовательность накладывается гамма AES(key1, rand1), проверяется заголовок «pyra-poster»(1,0), исполняемый файл сохраняется во временную папку и вычисляется хеш по алгоритму sha1 и сравнивается со sha1 из заголовка. Если хеши равны, то можно полагать с большой вероятностью, что это аутентичный файл созданный хозяином сервиса.

После обновления сервиса rand1 меняется и поэтому, перехватив шифротекст, повторно загрузить его нельзя. Рассчитать key1, имея rand1, и зная особенность заголовка «pyra-poster»(1,0) или, даже имея исполняемый файл, также достаточно сложно.

16-байтовый (128 бит) ключ key1 можно заменить на 32-байтовый (256 бит) без каких-либо дополнительных изменений в коде. Большинство веб броузеров как раз используют шифрование AES 256 бит.

Пользуйтесь на здоровье.

Автор: pyra

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js