• Должен ли разработчик заниматься ручным тестированием?

    Tyranron
    @Tyranron
    Поставьте себя на место заказчика и смените область, к примеру, на создание автомобиля. Вы заказываете у мастера автомобиль ручной сборки. Он его собирает, проверяет заводится он или нет, и отдаём Вам в пользование. Вы садитесь в автомобиль, выезжаете на любимый автобан и даёте жару в 100 км/час. На 101 км/час у Вас отваливаются передние колёса. Приятная ситуация? А ведь мастер "просто собрал машину".

    Если смотреть на бизнес по-взрослому, то всё просто:
    • Есть Заказчик. У него стоит задача создать Продукт, с помощью которого он бы зарабатывал деньги (ну или другая цель, не суть).
    • Есть Исполнитель (разработчик или команда разработчиков). Исполнитель обязуется за обусловленный Гонорар создать и поддерживать Продукт, который бы отвечал критериям качества Заказчика.
    • Если Исполнитель делает Продукт не соответствующий критериям качества Заказчика, значит он не выполняет условия Договора.
    • Если Заказчик не выплачивает Гонорар в должном объеме и вовремя, или требует чего-то не обусловленного в рамках Договора, значит он не выполняет условия Договора, а то и нарушает.
    • И Исполнитель и Заказчик должны понимать, что они создают Продукт, и должным образом очертить в рамках Договора критерии к созданию/поддержке этого Продукта. Дабы однозначность этого вопроса была явная и юридическая.


    Всё остальное - лирика.

    Если Ваша команда (как Исполнитель) не обладает штатом тестировщиков, то соответствующие обязанности по обеспечению качества Продукта Вы должны осознанно брать на себя. Нет никаких "разработчик просто пишет код". Есть "разработчик создаёт Продукт". Команда разработки обязуется согласно Договору создать Продукт ожидаемого качества и всё. Как это качество обеспечивается - вопрос уже второстепенный и зависит от состава команды, существующих технических средств, да и самого Продукта как такового (много каверзных кейсов не поддаются вменяемой автоматизации). Соответственно, бюджет на обеспечение качество Продукта должен закладывать в любом случае, независимо от того есть ли тестировщик или нет.
    Ответ написан
    5 комментариев
  • Как получить значения высшего по уровню range в template golang?

    Tyranron
    @Tyranron
    Создать переменные итерации для внешнего цикла:

    <form method="POST" action="/saveLucr">
            {{ range $i, $p := .Proflist }}
            <b> <input name="name_lucru" type="text" value="{{ $p.NameProf }}" />&nbsp;&nbsp;</b>
            <b> <input name="orar_prof" type="text" value="{{ $p.OrarProf }}" /> </b>
           <label  id="addProfesie">add profesie</label>
            <table id="{{ $p.NameProf }}">
                <tbody>
                    {{ range $p.PersList }}
                    <tr name = "tr" id = "tr_{{ $p.NameProf }}">
                        <td> Numele: <input name="name" type="text" value="{{ .NameLucr }}" /></td>
                        ...
                    </tr>
                    {{ end }} 
                 </tbody>
            </table>
            <P><label  id="addLucrator">Add_Lucrator</label></P>
            {{ end }}
            <input type="submit" value="GO!">
        </form>
    Ответ написан
  • Как правильно обработать rusqlite mappedrows?

    Tyranron
    @Tyranron
    Туториал устарел. Лучше всегда читать официальную доку. Она в Rust очень удобная и в сообществе принято её хорошо сдабривать примерами.

    Первая ошибка говорит о том, что Вы возвращаете Candle {...}, в то время как ожидается Result (это как раз устаревшая часть туториала). Просто сделайте Ok(Candle {...}) (пример с документации как раз подобное и показывает).

    Вторая ошибка говорит о том, что Вы пытаетесь возвращаемый тип rusqlite::row::MappedRows подставить туда, где явно ожидаете Vec. Rust статически и строго типизированный язык, так не получится. По сути, Вам там просто нужно добавить .collect() после ?. Так как MappedRowsреализует Iterator, то .collect() как раз пробежится по всем значениям итератора и соберёт их в Vec, который Вам и нужен.
    Ответ написан
  • Почему минимальный размер приложения такой большой?

    Tyranron
    @Tyranron
    Избитая тема, которая уже подымалась и изучалась не один раз. Достаточно сделать: https://www.google.com/search?q=rust+binary+size и сразу попадаем на статью Rustlog: Why is a Rust executable large?. Настоятельно рекомендую к прочтению, хоть она и слегка устарела (Rust больше не использует jemalloc по умолчанию), но вопрос там разобран крайне детально.

    Если коротко, то в Ваш бинарь попадают:
    1. Если собирали не в release mode, то debug-символы.
    2. Аллокатор, либо код-клей для системного аллокатора.
    3. Код для panic unwinding (в том числе и libbacktrace).
    4. Части libstd Rust'а, даже если Вы их не используете.

    Если стоит задача минимизировать бинарь Rust (под всякий embedded, к примеру), то обычно делают #![no_std], юзают мини-аллокаторы, делают panic = abort и идут на другие лишения.
    Ответ написан
    3 комментария
  • Как строить и деплоить Go-приложения?

    Tyranron
    @Tyranron
    1. Нормально будет как внутри единого процесса крутить (с помощью какого-то robfig/cron, к примеру), так и отдельными процессами (cron-задачами, или даже демонами). Здесь всё зависит от конкретной ситуации и того что Вам нужно. У каждого подхода свои очевидные трейд-офф'ы. Просто выбираете то, что Вам больше подходит. На кодовую базу это мало влияет, ибо всегда можно переиспользовать пакеты, да и вообще можно держать всё в едином бинарнике, просто прокинув разные CLI-интерфейсы.

    2. Горутины не могут быть пошарены между несколькими серверами. Если Ваши амбиции влезут в один сервер, то на внешние очереди можно не грузиться.

    3. Если речь идёт строго об одном процессе, который нельзя размножить, то смотрите в сторону graceful updates/restarts. Если же Вы можете множить приложение (в конце-концов живём в век победившых Docker/Kubernetes), то есть более общие техники, подходящие абсолютно для любого приложения: blue/green deployment либо rolling update (в том же Kubernetes из-коробки).
    Ответ написан
    3 комментария
  • Автоматическое копирование вольюмов Docker?

    Tyranron
    @Tyranron
    Нужно разделять stateless-сервисы и stateful-сервисы. Первые Вы можете раскладывать по нескольким хостам и не париться, а вот другие надо думать как именно их масштабировать, и какие гарантии нужны. По хорошему, само Ваше приложение должно быть stateless, и не монтировать никаких директорий, куда бы складывало файлы на длительное хранение, а заливало бы эти файлы для длительного хранения на другой специальный файловый сервер хоть по S3 bucket интерфейсу, хоть по, прости господи, FTP.

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

    Каковы есть/приходят на ум варианты:
    1. Поднять на всех хостах распределенную файловую систему (CephFS, GlusterFS, и т.п.). Монтируем в контейнер приложения volume под этой системой и тупо пишем туда файлы как обычно. Распределенная ФС самостоятельно размажет файлы по хостам в зависимости от желаемых настроек. Читаем файлы из той же директории.
      Плюсы: не нужно менять код приложения, легко использовать, простая концепция в понимании.
      Минусы: при интенсивной работе с файлами может не хватать производительности (подобные ФС считаются медленными), при отвале части хостов может не работать запись (так как требуется кворум n/2 + 1), эксплуатация/поддержка подобных систем может быть не самой тривиальной задачей (веселой починки сломавшегося Ceph-кластера).
    2. Поднять на всех хостах Minio (свой poor man's S3 bucket), либо другой свой отдельный файловый сервер. Работает в двух режимах: single (одна нода работает только со своими файлами, запись в несколько надо надо делать на стороне приложения, и фоновую синхронизацию тоже самостоятельно) и distributed (ноды обьединены в кворумный кластер с размазыванием файлов).
      Плюсы: S3 bucket интерфейс, легкая эксплуатация, можно монтировать как ФС.
      Минусы: возможно нужно менять код приложения (чтобы уметь работать с S3), производительность на чтение в distributed режиме медленная (сравнима с распределенными ФС из пункта 1).
    3. Просто монтировать на каждом хосте локальную директорию, для которой настроить постоянную фоновую синхронизацию через BitTorrent Sync какой-то (а может даже просто rsync).
      Плюсы: производительность обычной ФС, никаких кворумов (а значит блокировок на запись), легко использовать (монтируешь и вперёд).
      Минусы: файлы доступны на других хостах только через какое-то время, а значит приложение должно уметь это учитывать, возможна потеря данных (нода приняла файлы и сгорела не успев их отсинхронизировать на другие), если файлы не влезают на один хост - то вариант не подходит, либо шардить (размазывать) придётся руками самостоятельно в приложении.
    4. Использовать готовое отказоустойчивое облако: AWS Bucket, DigitalOcean Space и т.п.
    Ответ написан
    2 комментария
  • Нужна ли математика в информационной безопасности?

    Tyranron
    @Tyranron
    genby_8 смотря что Вы понимаете под "информационной безопасностью". Если это firewall настроить или внедрить подготовленные выражения SQL, то математика тут не нужна, а нужна документация к firewall/библиотеке.

    А если брать в широком смысле, то:
    • Криптография - это математика и нетривиальная: теория чисел, комбинаторика, эллиптические кривые, теория информации.
    • Криптоанализ - это математика: теория вероятности, стохастические процессы, теория игр.
    • Стеганография - тоже математика: теория кодирования и т.д.
    • Квантовая криптография - без квантовой физики и тензорного анализа туда соваться нечего.
    • Анализ/предсказание угроз - тоже математика: всякие нейронные сети, математические модели, те же стохастические процессы, и, прости господи, черт знает что ещё.

    И да, в основе всего это лежит так всеми нелюбимый матан. Без вменяемого матана, доступ во все эти замечательные разделы математики крайне ограничен и безрезультатен.
    Ответ написан
    Комментировать
  • Есть ли разница между горутинами и await в C#?

    Tyranron
    @Tyranron
    Несмотря на то, что эти инструменты созданы для решения одних и тех же проблем, делают они это по-разному, как под капотом, так и в плане предлагаемых абстракций. Потому нельзя сказать что async/await в C# - это "просто обертка над горутинами".

    Горутины в Go - это концепция stackful coroutines (под капотом) + CSP (в абстракциях). Каждый раз, когда мы создаём горутину, под неё выделяется отдельный стек вызовов для её собственных нужд. При этом, когда происходит паника, то stack unwinding (размотка стека вызовов) происходит только в пределах этой горутины и не покидает границ её стека. Стек горутины полностью отвязан от стека её создания/вызова, потому горутина не может возвращать результат. Любое общение между горутинами выполняется либо посредством каналов, либо какой-то общей памяти.

    async/await в C# (то есть, Task'и) - это концепция stackless coroutines (под капотом) + futures (в абстракциях). Код с async/await'ами компилятор превращает в определенную стэйт-машину с yield point'ами. У них нет отдельного стека, они выполняются в том же стеке что и вызывающий их код. Соотвественно, есть возможность словить exception'ы (аналог panic'и) возникающие внутри асинхронного Task'а прямо в запускающем его коде. Так как выполнение идёт на том же стеке - Task нормально может возвращать результат и мы его можем считать в вызывающем коде без дополнительных примитивов/инструментов.

    При этом, если мы запустим Go с GOMAXPROCS=1, то мы получим однопоточный асинхронный код в Go (по умолчанию он многопоточный). Также и в C# мы можем выполнять Task'и как на одном потоке, так и на thread pool, получая аналогичные Go гарантии рантайма.

    С точки зрения абстракций/использования - это уже вкусовщина. Кому как больше нравится. У futures лучше дизайн в плане composability (их эргономичнее join'ить и select'ить), но они вынуждают писать везде async и await. У горутин надо постоянно городить чехарду с синхронизацией (попробуйте сделать аналог await для произвольной горутины), но если эту чехарду прятать под капот (как обычно и делают), то код вообще выглядит полностью синхронным и программисты радуются.
    Ответ написан
    4 комментария
  • Математически доказанная безопасность Rust - это как?

    Tyranron
    @Tyranron
    Процитирую humbug из комментов к недавней статье:

    Конкретно в https://people.mpi-sws.org/~dreyer/papers/rustbelt... формально доказывается, что система типов Раста, владение, заимствование и прочее — корректны. Доказывается, что программа безопасна, если написана на безопасном подмножестве Раст. Доказывается, что программа безопасна, если в ней есть вкрапления unsafe, в которых программист не допустил ошибки, UB.

    Кроме того, проект RustBelt на текущем этапе занимается формальной верификацией библиотеки std, но полная проверка требует времени. Поэтому библиотеку проверяют по кускам. Да, были найдены и исправлены 2 ошибки в unsafe коде (что показывает, что ребята делом занимались), тем не менее все эти thread, mutex, Arc/Rc формально безопасны.


    Собственно, сами математические доказательства смотрите в научных публикациях:
    RustBelt: Securing the Foundations of the Rust Pro...
    KRust: A Formal Executable Semantics of Rust
    K-Rust: An Executable Formal Semantics for Rust

    Сайт проекта RustBelt: plv.mpi-sws.org/rustbelt
    Ответ написан
    Комментировать
  • Как донастроить Gitlab-omnibus с внешним nginx и https?

    Tyranron
    @Tyranron
    Вы на Nginx'е, получается, TLS-терминацию делаете, а GitLab при этом у Вас тоже пытается слушать по HTTPS. Вам нужно сказать GitLab'у, чтобы слушал обычный HTTP, и что он позади reverse proxy. И обращаться к нему не по unix-сокету Workhorse (именно на это у Вас ругается Nginx), а напрямую на нужный порт.

    Что-то вроде этого:
    external_url 'https://gitlab.mydomain.com'
    nginx['listen_port'] = 80
    nginx['listen_https'] = false
    nginx['proxy_set_headers'] = {
      "Host" => "$http_host_with_default",
      "X-Real-IP" => "$remote_addr",
      "X-Forwarded-For" => "$proxy_add_x_forwarded_for",
      "X-Forwarded-Proto" => "https",
      "X-Forwarded-Ssl" => "on",
      "Upgrade" => "$http_upgrade",
      "Connection" => "$connection_upgrade"
    }

    ports:
        - '8080:80'
        - '2022:22'

    proxy_pass http://gitlab:8080;

    Но конкретно в данной ситуации не совсем понятно, зачем Вам нужен ещё один фронтовый Nginx рядом. Вы ведь можете просто потюнить на предмет сертификатов/шифров тот Nginx, который уже идёт внутри omnibus образа.
    Ответ написан
    8 комментариев
  • Стоит ли избегать знака присваивния := в Go при нагруженном бекенде?

    Tyranron
    @Tyranron
    Это неважно.
    Не следует "экономить на спичках" и соблюдать пустые ритуалы. Вы пишите приложение нормально, чтобы его понимали и нормально читали другие разработчики. Когда же дело доходит до оптимизации, Вы профилируете и смотрите где конкретно у Вас проблемы и затыки. Их и убираете.

    Что же касается конкретно аллокаций, то всё не совсем так (вернее, совсем не так).
    Аллокация - это выделение куска памяти в куче (heap), который потом должен быть убран сборщиком мусора. Именно потому это и считается ударом по производительности, так как добавляет работы как аллокатору, так и сборщику мусора.
    Но использование := само по себе не включает/выключает использование аллокации. Оно лишь декларирует новую переменную.
    Будет ли произведена аллокация зависит от другого. В первую очередь, от того какие Вы данные создаёте. Если это примитивный тип фиксированного размера, размещаемый на стэке (например int), то работы с кучей у Вас не будет вовсе, соответственно и аллокаций. Во вторую очередь, от такой магической штуки как escape analysis, которая может запросто разместить данные из кучи на стэке, если ей так того захочется, тогда аллокации у Вас происходить тоже не будет.

    Давайте посмотрим примеры:
    1. var i int
      i = 1
      i = 2

      0 аллокаций, так как i размещается строго на стэке.

    2. i := 1
      i := 2

      0 аллокаций, так как i размещается строго на стэке.

    3. i := new(Struct{1,2,3})
      i := new(Struct{3,2,1})

      2 аллокации, так как new разместит данные в куче и вернёт указатель на них. Но только если escape analysis не переместит это всё в стэк (что конкретно в данном случае достаточно вероятно).

    4. var i *Struct
      i = new(Struct{1,2,3})
      i = new(Struct{3,2,1})

      2 аллокации, так как new все ещё разместит данные в куче и вернёт указатель на них.



    Как Вы видите, := сам по себе никак к аллокациям не относится, и является лишь синтаксическим сахаром.

    P.S. Давеча на Хабре была отличнейшая статья про стандартную библиотеку Go, показывающая что занятие преждевременными микрооптимизациями без профилирования - вполне себе бессмысленная затея.
    Ответ написан
    Комментировать
  • Как правильно настроить контейнеры nginx + fpm?

    Tyranron
    @Tyranron
    В конфиге Nginx недостаёт передачи SCRIPT_FILENAME Fast-CGI параметра. Нужно добавить следующее:
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    Ответ написан
    Комментировать
  • В чём отличие functional reactive и imperative ractive подходов?

    Tyranron
    @Tyranron
    В первом примере (императивном), у Вас объект класса Calculator после создания хранит в себе состояние: this.VAT = 22. И все последующие методы этого класса работают с этим состоянием (контекстом класса).

    Во втором же примере (функциональном) - напротив, никакого состояния у класса нет. Он, по сути, являет собой отдельный неймспейс для чистых функций. Функции в своей работе абсолютно не затрагивают состояние объекта.

    Вообще, ИМХО, пример просто плохой. Добавьте в оба примера логику мутации (изменения) VAT, и Вам сразу же станет понятно в чём различие. Ибо в первом примере Вы будете напрямую изменять this.VAT, и использовать его потом. А во втором примере, Вам придётся прокидывать значение VAT параметром, нигде его не храня.

    А отличие functional reactive и imperative reactive подходов в том, что:

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

    2. В императивном же подходе у Вас данные размазаны по отдельным живущим сущностям, у которых есть своё внутреннее состояние, и которые общаются между собой. Реактивность тут только в том, что у этих сущностей свойства реактивные, то есть при изменении свойства, все зависящие от него сущности перевычисляют зависящие свойства/значения.

    Разница, как таковая, не в реактивности, а всего лишь в способе декомпозиции задачи. Реактивность что там, что сям, по сути одна и та же (в примере: Rx.Observable.from(items)).
    Ответ написан
    Комментировать
  • Как правильно пользоваться stun?

    Tyranron
    @Tyranron
    Ну, использовать сам STUN достаточно просто - обратиться к серверу и получить свой внешний IP + порт, о чём Вы и написали.

    Вопрос, получается, больше заключается в том, как организовать пробивку NAT используя STUN, то есть, сигналинг (signaling).

    Вообще, исчерпывающий ответ дан в RFC 5245 - Interactive Connectivity Establishment (ICE) (используется в WebRTC).
    Более понятным языком у Mozilla.
    Заметьте, что на диаграммах в первую очередь создаётся RTCPeerConnection, что у Caller, что у Callee. Я не совсем в курсе, что там под капотом при этом происходит, но могу предположить что сначала создается сокет на прослушку, потому уже идёт запрос на STUN-сервер, который и "пробивает" таблицу роутинга и возвращает "пробитый" внешний порт + адрес, который и отправляется другому узлу, и уже потом другой узел может подключиться на "пробитый" внешний порт.
    Ответ написан
    Комментировать
  • Статья на хабре про хеширование паролей. Непонятны некоторые моменты. Поможете разобраться?

    Tyranron
    @Tyranron
    Отвечали уже не раз. Советую внимательно ознакомиться.

    Также, на том же Хабре, есть более свежие вменяемы статьи:
    Про хранение паролей в БД (обязательно читать комменты)
    Правильные ответы по криптографии: 2018 год

    Непосредственно по Вашим вопросам:

    1. При создании хэша пароля генерировать случайную соль. Сохранять в БД хэш + соль. При проверке пароля выбирать из БД хэш + соль, хэшировать полученный пароль с солью, и сопоставлять результат с хэшом из БД.

    3. Для каждого хранимого хэша пароля своя уникальная соль.

    4. Соль генерируется достаточно случайной, потому что она должна быть сложно угадываемой. Это означает, что энтропия для этого дела берется достаточно большая, и Вы можете не переживать за то, что чисто теоретически у Вас может сгенерироваться 2 одинаковые соли. Вероятность подобного случая примерно сравнимая с вероятностью того, что все молекулы воздуха в Вашей комнате возьмут и соберутся в одном углу, то есть, крайне мала(с). И даже если Вы спустите весь запас удачи своей жизни на такой воистину уникальный случай, то ничего страшного и непоправимого при этом не случится - ну поймёт злоумышленник, что конкретно у этих 2х пользователей одинаковый пароль, ну и всё на этом, - пароли то всё ещё надо взламывать.

    5. Да, можно. Хотя задача хэширования паролей и задача восстановления доступа по токену - это 2 разные задачи, с разными дано и целями, но первая задача является частью второй, ибо Вам нужно хранить в БД отпечаток секрета пользователя (коим временный токен восстановления и является). Алгоритм простой: генерируем случайный временный токен восстановления -> пишем в БД его отпечаток (хэш + соль) и время жизни -> отправляем токен пользователю -> пользователь переходит по ссылке с токеном -> проверяем валидность токена по его отпечатку и времени жизни -> заставляем пользователя ввести новый пароль. По сути, токен восстановления и являет собой одноразовый временный пароль, который предоставляет доступ для установки постоянного пароля.

    Ну и традиционный совет: используйте для получения отпечатков секретов пользователей рекомендуемые алгоритмы (Argon2, Bcrypt, scrypt, и т.п.), а не собственные реализации. Там всё сделано под капотом как надо, ибо нюансов своих в этом деле тоже хватает (стоимость по времени, стоимость по памяти, constant time сравнения, и т.д.).
    Ответ написан
    3 комментария
  • Как протестировать вызовы методов у websocket.Conn из gorilla/websocket?

    Tyranron
    @Tyranron
    Чтобы мокать в модульных тестах, есть вот такое:
    https://github.com/posener/wstest

    Также есть вариант делать интеграционки/E2E-тесты через
    https://github.com/gavv/httpexpect
    Запилено тут, юзать через форк (ибо у автора 2 месяц руки не доходят поревьюить).
    Там можно тесты натравливать как напрямую на HTTP-сервер с WS, так и подменить клиент чтобы он сразу к хэндлерам присасывался, не подымая полноценный HTTP-сервер (как раз идею и слизал с posener/wstest).

    Оба решения используются мной на реальном проекте без проблем и вполне удачно.
    Ответ написан
    2 комментария
  • End to end шифрование сообщений, какой алгоритм/библиотеку выбрать?

    Tyranron
    @Tyranron
    Есть некий эталон в виде опенсорсного Signal: https://www.signal.org/

    У них всё в этом плане открыто и расписано.
    Дизайн доки: https://www.signal.org/docs/
    Код: https://github.com/signalapp

    Там же в дизайн доках ссылка на ихнюю готовую JS'ную либу для работы с их протоколом:
    https://github.com/signalapp/libsignal-protocol-ja...
    Ответ написан
    2 комментария
  • GOLANG приложение и свои сертификаты?

    Tyranron
    @Tyranron
    Если всё делать по стандартному фэн-шую, то никаких отличий от обыкновенных OpenSSL туториалов то и нет:

    0. Если нет уже готового CA (Certificate Authority), то генерируем новый: ecdsa.GenerateKey() + x509.CreateCertificate() (self-signed).

    1. Генерируем приватный ключ PK (Private Key) для своего сертификата: ecdsa.GenerateKey().

    2. Генерим запрос на создание сертификата CSR (Certificate Signing Request): x509.CreateCertificateRequest(). В качесте CN (Common Name) указываем тот адрес, по которому будем стучаться к приложению. Если таких адресов предполагается несколько, то используем SAN расширение в шаблоне сертификата.

    3. Берем CA и выпускаем себе сертификат по сгенерированному CSR: x509.CreateCertificate().

    4. Используем сертификат для TLS: http.ServeTLS().

    Приватный ключ и сертификат (и свой, и CA) сохраняем в любую желаемую директорию в файловой системе. Права к приватным ключам при этом выставляем 0600. Если это дело всё разовое, то можно и во временную директорию (os.TempDir()), чтобы не мусорить.

    Если это только чтобы поиграться, и не предполагается выстраивание своей PKI (Public Key Infrastructure), то можно и не париться с CA/CSR, а сразу выпускать self-signed сертификат с нужными Вам CN/SAN. То есть, остается только нулевой шаг.

    Если у нас приложение пырится во внешний мир не "голым", а прикрыто каким-то проксирующим сервером (например, Nginx), что, в принципе, есть даже рекомендуемой практикой, то сертификат можно подключать для нашего хоста прямо там, а приложение оставить себе без TLS. В этом случае Nginx будет расшифровывать трафик у себя и в приложение пробрасывать уже незашифрованный трафик. Подобное называется терминацией TLS.
    Если же мы хотим, чтобы между прокси-сервером и нашим приложением продолжал ходить зашифрованный трафик, то прокси-серверу нужно в настройках скормить наш CA сертификат, либо отключить проверку CA.
    Ответ написан
    2 комментария
  • Стоит ли использовать Dart для API сервера?

    Tyranron
    @Tyranron
    Пробовали, но не на сервере. Язык приятный, экосистема сыровата, людей мало.

    Если пишете карманный проекта для себя - используйте чего душа пожелает, хоть Idris с зависимыми типами. Разброс в стабильности/производительности здесь вопрос второстепенный, и реальная производительность будет зависеть в первую очередь от того, как именно Вы напишете проект. Потому можете спокойно использовать Dart на сервере, если он Вам по душе (но, честно, он тот ещё "середнячок" и есть языки поприятнее).

    Если проект не для себя, а для заказчика, либо вообще в будущем планируется поддержка его другими людьми (то есть планируется рост проекта), то Dart - плохой выбор. Найти обученного специалиста - тяжело. Экосистема всё ещё сыровата (хотя потенциал хорош). Исключение - если у Вас есть желание и ресурсы на обучение команды Dart специалистов (как, например, у Wrike).
    Ответ написан
    1 комментарий