«Идеальная настройка вашего Golang проекта» и «FTP-сервер с виртуальными пользователями на базе vsftpd»: разница между страницами

Материал из support.qbpro.ru
(Различия между страницами)
imported>Vix
Нет описания правки
 
imported>Vix
Нет описания правки
 
Строка 1: Строка 1:
* '''Часть 1'''
''Автор: Уваров А.С. 29.01.2016''<br>
<hr>
[[Файл:Vsftpd-virtual-user-000.jpg]]<br>
Идеальная настройка вашего Golangпроекта
Протокол FTP, несмотря на преклонный возраст и серьезные недостатки продолжает широко применяться, во многом благодаря простоте и поддержке со стороны самого широкого спектра устройств и ПО. FTP-сервер является неотъемлемой частью любого веб-сервера, применяется в системах автоматизированного обмена данными. Наиболее часто встающей перед администраторами таких систем задачей является создание сервера с виртуальными пользователями. Сегодня мы расскажем, как это сделать.
 
[https://yandex.ru/turbo/nuancesprog.ru/s/p/6062/?parent-reqid=1604670340041351-275217593417438871900275-prestable-app-host-sas-web-yp-138&utm_source=turbo_turbo Часть 2], [https://nuancesprog.ru/?p=6340 Часть 3]
 
При запуске нового проекта самые большие трудности у меня всегда вызывала его настройка. Всегда стараешься сделать её «идеальной»:
* используешь лучшую структуру каталогов, чтобы всё было легко найти и импортирование происходило без проблем;
* настраиваешь все команды так, чтобы нужные действия выполнялись в один клик или с вводом одной команды;
* находишь лучший инструмент контроля качества кода, средство форматирования, среду тестирования для используемого в проекте языка и библиотеки…
 
Этот список можно продолжать и продолжать, и всё равно до идеальной настройки будет ещё далеко… Но, по моему скромному мнению, эта настройка для Golang просто лучшая!
 
Она так хорошо себя проявляет отчасти и потому, что основана на существующих проектах, которые вы можете найти здесь и тут.
 
Краткое изложение доступно в моём репозитории —  https://github.com/MartinHeinz/go-project-blueprint
Структура каталогов
 
Первым делом обратимся к структуре каталогов нашего проекта. Здесь у нас несколько файлов верхнего уровня и четыре каталога:
 
    '''pkg''' — это пакет Go, который содержит только строку версии global. Меняется на версию из хэша текущей фиксации при проведении сборки;
    '''config''' — конфигурационный каталог, который содержит файлы со всеми необходимыми переменными среды. Вы можете использовать любой тип файла, но я бы рекомендовал файлы YAML: их проще читать;
    '''build''' — в этой директории у нас все скрипты оболочки, необходимые для сборки и тестирования приложения, а также создания отчётов для инструментов анализа кода;
    '''cmd '''— фактический исходный код. По правилам именования исходный каталог называется cmd. Внутри есть ещё один каталог с именем проекта (в нашем случае blueprint). В свою очередь, внутри этого каталога находится main.go, запускающий всё приложение. Также здесь можно найти все остальные исходные файлы, разделённые на модули (подробнее об этом далее).
 
Оказывается, многие предпочитают помещать исходный код в каталоги internal и pkg. Я думаю, что это лишнее: достаточно использовать для этого cmd, где для всего есть своё место.
 
Помимо каталогов, есть ещё большое количество файлов, о которых мы поговорим в статье.
Модули Go для идеального управления зависимостями
 
В проектах Go используются самые разные стратегии управления зависимостями. Однако с версии 1.11 Go обзавёлся официальным решением. Все наши зависимости приводятся в файле go.mod, в корневом каталоге. Вот как он может выглядеть:
 
module github.com/MartinHeinz/go-project-blueprint
go 1.12
require (
  github.com/spf13/viper v1.4.0
  github.com/stretchr/testify v1.4.0
)
 
Вы можете спросить: «А как в этот файл включить зависимости?». Очень просто, всего одной командой:
 
go mod vendor
 
Эта команда переустанавливает vendor каталог основного модуля для включения всех пакетов, необходимых для сборки и тестирования каждого пакета модуля исходя из состояния файлов go.mod и исходного кода Go.
Фактический исходный код и конфигурация
 
И вот наконец мы добрались до исходного кода. Как уже говорилось, исходный код разделён на модули. Модуль представляет собой каталог внутри исходного корневого каталога. В каждом модуле находятся исходные файлы вместе с соответствующими файлами тестов. Например:
 
./cmd/
└── blueprint
    ├── apis <- Module
    │  ├── apis_test.go
    │  ├── user.go
    │  └── user_test.go
    ├── daos <- Module
    │  ├── user.go
    │  └── user_test.go
    ├── services <- Module
    │  ├── user.go
    │  └── user_test.go
    ├── config <- Module
    │      └── config.go
    └── main.go
 
Такая структура способствует лучшей читаемости и лёгкости сопровождения кода: он идеально разделён на части, которые проще просматривать. Что касается конфигурации, в этой настройке используем библиотеку конфигураций Go Viper, которая может иметь дело с разными форматами, параметрами командной строки, переменными среды и т.д.
 
Посмотрим, как мы используем этот Viper здесь. Вот пакет config:
 
var Config appConfig
type appConfig struct {
  // Пример переменной, загружаемой в функции LoadConfig
  ConfigVar string
}
// LoadConfig загружает конфигурацию из файлов
func LoadConfig(configPaths ...string) error {
  v := viper.New()
  v.SetConfigName("example")  // <- имя конфигурационного файла
  v.SetConfigType("yaml")
  v.SetEnvPrefix("blueprint")
  v.AutomaticEnv()
  for _, path := range configPaths {
  v.AddConfigPath(path)  // <- // путь для поиска конфигурационного файла в
  }
  if err := v.ReadInConfig(); err != nil {
  return fmt.Errorf("failed to read the configuration file: %s", err)
  }
  return v.Unmarshal(&Config)
}
 
Он состоит из единственного файла. Объявляет один struct, который содержит все переменные конфигурации и имеет одну функцию LoadConfig, которая загружает конфигурацию. Требуется путь до конфигурационных файлов, в нашем случае используем путь до каталога config, который находится в корневом каталоге проекта и содержит наши YAML файлы. И как их будем использовать? Запустим первым делом в main.go:
 
if err := config.LoadConfig("./config"); err != nil {
    panic(fmt.Errorf("invalid application configuration: %s", err))
}
 
Простое и быстрое тестирование
 
Что важнее всего после кода? Тесты. Чтобы писать много хороших тестов, нужна настройка, с которой это будет делать легко. Для этого мы используем цель Makefile под названием test, которая собирает и выполняет все тесты в подкаталогах cmd (все файлы с расширением _test.go). Эти тесты кэшируются, так что их запуск происходит только при наличии изменений в соответствующей части кода. Это очень важно: если тесты будут слишком медленными, рано или поздно вы перестанете их запускать и сопровождать. Помимо модульного тестирования, make test помогает следить за общим качеством кода, запуская с каждым тестовым прогоном gofmt и go vet. gofmt способствует правильному форматированию кода, а go vet помогает с помощью эвристических алгоритмов выявлять в коде любые подозрительные конструкции. Вот пример того, что может получиться в результате выполнения:
 
foo@bar:~$ make test
Running tests:
ok  github.com/MartinHeinz/go-project-blueprint/cmd/blueprint (cached)
?    github.com/MartinHeinz/go-project-blueprint/cmd/blueprint/config [no test files]
?    github.com/MartinHeinz/go-project-blueprint/pkg [no test files]
Checking gofmt: FAIL - the following files need to be gofmt'ed:
    cmd/blueprint/main.go
 
Checking go vet: FAIL
# github.com/MartinHeinz/go-project-blueprint/cmd/blueprint
cmd/blueprint/main.go:19:7: assignment copies lock value to l: sync.Mutex
Makefile:157: recipe for target 'test' failed
make: *** [test] Error 1
Запуск всегда в Docker
Многие говорят, что у них запуск невозможен в облаке, а только на компьютере. Здесь есть простое решение: всегда запускаться в контейнере docker.  Делаете ли вы сборку, запускаете ли или тестируете — делайте всё это в контейнере. Кстати, что касается тестирования, make test выполняется тоже только в docker.
 
Посмотрим, как это происходит. Начнём с файлов Dockerfile из корневого каталога проекта: один из них для тестирования (test.Dockerfile), а другой — для запуска приложения (in.Dockerfile):
 
    test.Dockerfile — в идеале нам было бы достаточно одного файла Dockerfile для запуска приложения и тестирования. Но во время тестовых прогонов нам может потребоваться внести небольшие изменения в среде выполнения, поэтому у нас здесь есть образ для установки дополнительных инструментов и библиотек. Предположим, например, что мы подключаемся к базе данных. Нам не нужно поднимать весь PostgreSQL-сервер при каждом тестовом прогоне или зависеть от какой-нибудь базы данных на хост-машине. Мы просто используем для тестовых прогонов базу данных в памяти SQLite. И если дополнительные установки не понадобятся нашим тестам, то двоичным данным в SQLite они будут очень даже кстати: устанавливаем gcc и g++, переключаем флажок на CGO_ENABLED, и готово.
    in.Dockerfile — если посмотреть на этот Dockerfile в репозитории, что мы увидим: просто несколько аргументов и копирование конфигурации в образ. Но что здесь происходит? in.Dockerfile используется только из Makefile (заполненного аргументами), когда мы запускаем make container. Давайте теперь обратимся в сам Makefile, Всё, что связано с docker, делает для нас именно он. 👇
 
Связываем всё вместе с помощью Makefile
 
Долгое время Make-файлы казались мне страшными (до этого я сталкивался с ними лишь при работе с кодом C), но на самом деле ничего страшного здесь нет, и их много где можно использовать, в том числе для этого проекта! Посмотрим, какие цели у нас здесь есть в Makefile:
 
  make test — первая в рабочем потоке — собранное приложение — создаёт исполняемый двоичный код в каталоге bin:
@echo "making $(OUTBIN)"
  @docker run                                              \ # <- Это `докерный запуск`
    -i                                                    \ #    скрытая команда
    --rm                                                  \ # <- Удаляем контейнер по завершении
    -u $$(id -u):$$(id -g)                                \ # <- Используем текущего пользователя
    -v $$(pwd):/src                                        \ # <- Подключаем исходную папку
    -w /src                                                \ # <- Устанавливаем рабочий каталог
    -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin              \ # <- Подключаем каталоги
    -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin/$(OS)_$(ARCH) \ #    с выводом двоичных данных
    -v $$(pwd)/.go/cache:/.cache                          \
    --env HTTP_PROXY=$(HTTP_PROXY)                        \
    --env HTTPS_PROXY=$(HTTPS_PROXY)                      \
    $(BUILD_IMAGE)                                        \
    /bin/sh -c "                                          \ # <- Запускаем скрипт сборки
        ARCH=$(ARCH)                                      \ #    (Проверяет на наличие
        OS=$(OS)                                          \ #    аргументов, устанавливает
        VERSION=$(VERSION)                                \ #    переменные среды и запускает
        ./build/build.sh                                  \ #    `go install`)
    "
  @if ! cmp -s .go/$(OUTBIN) $(OUTBIN); then \ # <- Если двоичные данные изменились
      mv .go/$(OUTBIN) $(OUTBIN);            \ #    перемещаем их из `.go` в `bin`
      date >$@;                              \
  fi
 
    make test — тестовая — она снова использует почти тот же docker run, только здесь ещё есть скрипт test.sh (покажем только то, что нас интересует):
TARGETS=$(for d in "$@"; do echo ./$d/...; done)
go test -installsuffix "static" ${TARGETS} 2>&1
ERRS=$(find "$@" -type f -name \*.go | xargs gofmt -l 2>&1 || true)
ERRS=$(go vet ${TARGETS} 2>&1 || true)
Эти строчки — важная часть файла. Первая из них собирает тестовые цели, где в качестве параметра указан путь. Вторая строчка запускает тесты и выводит документацию по тестированию ПО. Оставшиеся две строчки запускают gofmt и go vet. Они собирают и выводят ошибки (если таковые имеются).
 
    '''make container''' — и, наконец, важнейшая часть — создание развёртываемого контейнера:
 
.container-$(DOTFILE_IMAGE): bin/$(OS)_$(ARCH)/$(BIN) in.Dockerfile
  @sed                                \
      -e 's|{ARG_BIN}|$(BIN)|g'        \
      -e 's|{ARG_ARCH}|$(ARCH)|g'      \
      -e 's|{ARG_OS}|$(OS)|g'          \
      -e 's|{ARG_FROM}|$(BASEIMAGE)|g' \
      in.Dockerfile > .dockerfile-$(OS)_$(ARCH)
  @docker build -t $(IMAGE):$(TAG) -t $(IMAGE):latest -f .dockerfile-$(OS)_$(ARCH) .
  @docker images -q $(IMAGE):$(TAG) > $@
 
Код для этой цели довольно прост: сначала он подставляет переменные в in.Dockerfile, а затем запускает docker build для получения образа с «изменёнными» и «последними» тегами. И дальше передаёт имя контейнера на стандартный вывод.
 
    Теперь, когда у нас есть образ, нужно где-то его хранить. С этим нам помогает make push, который помещает образ в хранилище образов Docker registry.
    '''make ci''' — ещё один способ использовать Makefile —  задействовать его в процессах непрерывной интеграции и развёртывания приложений (об этом речь пойдёт далее). Эта цель очень похожа на make test: тоже запускает все тесты и плюс к этому генерирует отчёты о покрытии, которые потом используются как вводная информация при проведении анализа кода.
    make clean — и, наконец, если нам нужно провести очистку проекта, запускаем make clean, который удалит все файлы, сгенерированные предыдущими целями.
 
Остальные цели можно объединить в две группы: первые не так важны для нормального рабочего процесса, а вторые являются лишь частью других целей, поэтому о них можно не упоминать.
Интеграция и развёртывание ПО для идеальной разработки
 
Завершаем статью важной частью — процессом непрерывной интеграции и развёртывания приложений. Не буду подробно расписывать, что в нем такого — вы и сами прекрасно сможете разобраться (практически в каждой строке есть комментарий, так что всё должно быть понятно):
 
# Matrix build запускает 4 параллельные сборки                       
matrix:                       
  include:                         
    - language: go  # Сборка и тестирование                                            sudo: required                           
services:                             
    - docker                           
script:                             
- export GO111MODULE=on                             
- go mod vendor  # Загружаем зависимости                              - make build  # Собираем приложение                             
- test -f bin/linux_amd64/blueprint  # Тест на наличие двоичных данных, полученных на предыдущем этапе                             
- make all-container  # Создаём все докерные контейнеры                              - docker images | grep "^docker.pkg.github.com/martinheinz/go-project-blueprint /blueprint.*__linux_amd64"  # Проверяем наличие созданных образов                             
- make test  # Запускает тесты внутри тестового образа
                                                   
- language: go  # SonarCloud                           
addons:                             
sonarcloud:                               
organization: martinheinz-github                                token:                                 
secure:
"tYsUxue9kLZWb+Y8kwU28j2sa0pq20z2ZvZrbKCN7Sw0WGtODQLaK9tZ94u1Sy02qL5QcabukbENmbvfouzXf4EfaKjDmYH9+Ja22X26MfTLVpaCDTQEGmNyREOFCHpjNXPgDMv1C70By5U+aPWSYF/lehB5rFijwCf7rmTFRNUDeotCTCuWb2dIkrX2i6raVu34SvqqGxKQmmH+NPLe7uKO/wXqH+cWQH1P9oJYeVksNGruw4M0MznUeQHeJQYpTLooxhEEzYiBbkerWGDMwBdZdPQwVrO2b8FEDRw/GWTFoL+FkdVMl4n4lrbO/cQLbPMTGcfupNCuVHh1n8cGp8spMkrfQGtKqvDRuz2tBs0n1PWXCRS6pgZQw/ClLPgi/vVryVRwOabIHSQQLRVhcdp8pkYdyX3aH1EdlIHiJLT6sacS0vJPqZMF/HNsPEoHe4YdiYvx/tcYMU63KQVZzgF4HfQMWy69s1d0RZUqd+wrtHU1DHwnkq1TSe+8nMlbvbmMsm6FVqGistrnVjx4C9TjDWQcjprYU40zCvc1uvoSPimVcaD8ITalCDHlEfoV7wZuisV8+gJzOh9pDZ/joohW7/P3zklGgI2sH7qt62GE4o5UyRArzJC7eIj7Oxx6GdbeEqw09M4rCfR1g5tHWIqVHz5CajvkXkPqrRGu2oI="                           
before_script:                             
- ./reports.sh  # Создаёт каталоги и файлы для отчётов                              - export GO111MODULE=on                             
- go mod vendor  # Загружаем зависимости                              - make ci  # Запускаем тесты и генерируем отчёты (см. этап `ci` в Makefile)                           
script:                             
- sonar-scanner  # Запускаем анализ с помощью плагина SonarCloud scanner plugin
                                                   
- language: go  # CodeClimate                            before_script:                             
- ./reports.sh  # Создаём каталоги и файлы для отчётов                              - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64  > ./cc-test-reporter  # Загружаем генератор тестовых отчётов CodeClimate                             
- chmod +x ./cc-test-reporter  # Делаем его исполняемым                              - ./cc-test-reporter before-build  # Уведомляем CodeClimate о готовом отчёте                           
script:                             
- export GO111MODULE=on                             
-  go mod vendor  # Загружаем зависимости                              - make ci  # Запускаем тесты и генерируем отчёты (см. этап `ci` в Makefile)                           
after_script:                             
- ./cc-test-reporter after-build -t gocov --exit-code $TRAVIS_TEST_RESULT  # Отправляем отчёт в CodeClimate или уведомляем о неудавшейся сборке кодом  завершения 
                                                 
- language: go  # Помещаем контейнер в хранилище                            services:                             
- docker                           
if: branch = master                           
script:                             
- export GO111MODULE=on                             
- go mod vendor  # Загружаем зависимости                              - echo "$DOCKER_PASSWORD" | docker login docker.pkg.github.com -u "$DOCKER_USERNAME"  --password-stdin  # Подключаемся ко хранилищу GitHub Registry, используя переменные среды Travis                             
- make container  # Создаём изменённые и последние образы                              - make push  # Помещаем образ в хранилище
                                               
notifications:                       
email: false
 
Но кое-что можно прояснить.
В этой сборке Travis использована сборка Matrix Build с 4 параллельными заданиями для ускорения всего процесса:
 
    Сборка и тестирование: здесь мы проверяем, что приложение работает как надо;
    SonarCloud: здесь мы генерируем отчёты о покрытии и отправляем их на сервер SonarCloud;
    CodeClimate: здесь — как и в предыдущем задании — мы генерируем отчёты о покрытии и отправляем их, только на этот раз в CodeClimate с помощью их генератора тестовых отчётов;
    Push to Registry: и, наконец, помещаем наш контейнер в хранилище GitHub Registry.
 
Заключение
 
Надеюсь, эта статья поможет вам в ваших будущих разработках кода на Go. Все подробности изложены в репозитории.
 
В следующей части узнаем, как на базе этого макета проекта, который мы сегодня выстроили, с лёгкостью создавать интерфейсы RESTful API, тестировать с базой данных в памяти, а также настраивать крутую документацию (а пока можно подсмотреть в ветке rest-api репозитория). 🙂
 
Перевод статьи Martin Heinz: Ultimate Setup for Your Next Golang Project (впервые опубликована на martinheinz.dev).
Войдите, чтобы отправлять и оценивать комментарии
Расскажите, что вы об этом думаете?
Магические методы в Python
7 марта 2020
Создание объекта dict, принимающего только целые и плавающие числа в качестве значений
 
В этом сценарии мы реализуем класс, который создает объекты-словари, принимающие только целые и плавающие значения.
 
При добавлении других типов данных, таких как строки, списки и кортежи, будет появляться исключение, указывающее пользователю, что пользовательский объект dict может принимать только целые и плавающие числа в качестве значений.
 
Для реализации этого процесса используются следующие методы:
__int__, __setitem__ и __str __
 
Для начала создаем пользовательский класс CustomIntFloat и передаем dict в список наследования аргументов. Это означает, что созданный объект будет вести себя как словарь, за исключением тех мест, в которых это поведение будет выборочно изменено.
 
Затем создаем метод __init__, чтобы сконструировать объект dict CustomIntFloat, который принимает ключ и значение в список аргументов, установленный в тип None по умолчанию. Таким образом, если пользователь создает объект класса CustomIntFloat без передачи ключа или значения, будет сгенерирован пустой dict. Данное условие гласит: если ключ не передан, то параметру ключа по умолчанию присваивается аргумент None, а пустой dict создается путем ссылки на объект CustomIntFloat с атрибутом класса empty_dict.
 
Если пользователь указывает ключ length и соответствующее значение, которое является экземпляром класса int или float, то ключ и значение будут установлены в объекте.


Наконец, если пользователь указывает несколько ключей и значений в качестве итерируемых в операторе else, то они будут заархивированы функцией zip и им будет присвоено имя переменной zipped. Выполняем цикл на zipped, чтобы проверить, имеет ли значение тип int или float. Если нет, то будет сгенерировано пользовательское исключение CustomIntFloatError.
Про настройку FTP-сервера на базе vsftpd мы уже рассказывали на страницах нашего сайта, недавно существенно дополнив и обновив статью Простой FTP-сервер на базе Ubuntu (vsftpd). В ней мы рассказали каким образом можно ограничить список пользователей, имеющих возможность подключаться к серверу, и их права. Однако локальные пользователи остаются при этом локальными пользователями и несмотря на ограниченные права могут оказаться объектом атаки, направленной на повышение прав, например, через уязвимости в каком-либо ПО.
Класс исключения CustomIntFloatError и метод __str__


При генерации исключения CustomIntFloatError создается экземпляр класса CustomIntFloatError.
Кроме того, ручное редактирование списков и настроек пользователей может оказаться весьма трудоемким делом если их не один-два, а существенно больше. Также иногда возникает ситуация, что светить данные реальных пользователей просто нежелательно, например, FTP используется движком сайта (CMS), связка пользователь-пароль явно прописана в конфиге и теоретически доступна любому работающему с сайтом сотруднику.


Таким образом, этот пользовательский класс исключений нуждается в помощи magic-методов __init__ и __str__. Созданный экземпляр принимает переданное значение и устанавливает его в качестве значения атрибута в классе CustomIntFloatError.
В таких ситуациях следует использовать виртуального пользователя, который будет иметь доступ исключительно к данному FTP-ресурсу и учетные данные которого можно смело сообщить всем принимающих участие в работе реальным сотрудникам.


Это означает, что при появлении сообщения об ошибке значение, переданное в __init__ объекта CustomIntFloat, может быть установлено как атрибут (self.value) в классе CustomIntFloatError и с легкостью проверено.
В нашей статье мы настроим FTP-сервер для реализации следующей схемы:


Если ввод неверный, то появляется исключение CustomIntFloatError, а объект не создается. Сообщение об ошибке информирует пользователя о том, что допустимыми являются только целые и плавающие значения.
[[vsftpd-virtual-user-001.jpg]]<br>
Пусть в нашем распоряжении имеется некий веб-сервер под управлением которого находятся три сайта: blue.lab, green.lab и red.lab, физически расположенные в одноименных директориях. Наша задача создать FTP-сервер с виртуальными пользователями, каждый из которых имеет доступ только к своему сайту.


Аналогичным образом при попытке создать экземпляр объекта z (который был закомментирован) с несколькими ключами и значениями, возникает то же исключение, информирующее пользователя о том, что ‘three’ не является допустимым вводом.
Важное замечание, в данной схеме все сайты должны работать от имени веб-сервера (www-data), если вы используете запуск каждого сайта от имени своего (реального) пользователя, то вам лучше настроить FTP-сервер для работы с реальными пользователями или дополнительно обеспечить нужные права доступа для виртуальных пользователей, что может представлять из себя нетривиальную задачу.


# z = CustomIntFloat(key=['a', 'b', 'c'], value=[1, 2, 'three'])
Прежде всего установим соглашение: имя виртуального пользователя должно совпадать и именем корневой FTP-папки, в нашем случае папки сайта, а все такие папки должны располагаться в одном месте, т.е. подпадать под шаблон /var/www/$USER. Это позволит в дальнейшем с легкостью манипулировать сайтами и виртуальными пользователями, а также избежать путаницы в именах и настройках.
z = CustomIntFloat(key=['a', 'b', 'c'], value=[1, 2, 3])


Метод __setitem__
Определившись с моделью размещения данных можно переходить к настройкам ПО. Кроме vsftpd нам понадобятся пакеты libpam-pwdfile и apache2-utils. Последний входит в состав одноименного веб-сервера и если у вас уже работает Apache, то устанавливать его не надо.
__setitem__ — это magic-метод, который вызывается при установке ключа и значения в словаре. Если после создания объекта CustomIntFloat пользователь попытается добавить значение, которое не относится к типу int или float, появится то же исключение CustomIntFloatError. Ниже показано, как установить ключ и значение:


x = CustomIntFloat('a', 1)
Сама установка предельно проста:
print(type(x))
x['b'] = 2.1
print(x)
# x['c'] = 'Three'


В результате недопустимого ввода возникает исключение CustomIntFloatError:
apt-get install vsftpd libpam-pwdfile apache2-utils
После чего перейдем к настройкам FTP-сервера, которые расположены в файле /etc/vsftpd.conf, мы перечислим опции в порядке их нахождения в файле.


Исходный код:
Данная опция запускает FTP-сервер как службу


  class CustomIntFloatError(Exception):
  listen=YES
    def __init__(self, value):
ниже имеется аналогичная взаимоисключающая опция:
        self.value = value


    def __str__(self):
listen_ipv6=NO
        return self.value + ' is not valid\nOnly Integers and floats are valid values \nin CustomIntFloat(dict) '
Первая из них обеспечивает поддержку IPv4, вторая сразу IPv6 и IPv4, поэтому должна быть включена только одна из них.
class CustomIntFloat(dict):


    empty_dict = {}
Запрещаем анонимных пользователей:


    def __init__(self, key=None, value=None):
anonymous_enable=NO
        if key is None:
И разрешаем локальных (и виртуальных) пользователей:
            CustomIntFloat.empty_dict = {}


        elif len(key) == 1 and isinstance(value, (int, float)):
local_enable=YES
              dict.__setitem__(self, key, value)
Также разрешаем запись:
        else:
            zipped = zip(key, value)
            for tup in zipped:
                if isinstance(tup[1], (int, float)):
                    dict.__setitem__(self, tup[0], tup[1])
                else:
                    raise CustomIntFloatError(tup[1])


    def __setitem__(self, key, value):
write_enable=YES
        if not isinstance(value, (int, float)):
и задаем маску для вновь создаваемых файлов и папок:
            raise CustomIntFloatError(value)
        return dict.__setitem__(self, key, value)


Обзор класса CustomIntFloat
local_umask=022
Это обеспечит установку прав 755 на папки и 644 на файлы, т.е. полный доступ только владельцу и чтение для группы и остальных.


С помощью наследования через такие встроенные классы, как dict, можно настраивать поведение через повторную реализацию magic-методов. У этого подхода есть множество преимуществ.
Чтобы FTP использовал локальное время сервера, а не GMT установим:


Стоит отметить, что пользователю не нужно изучать новый синтаксис. Он может добавить ключ и значение к объекту dict CustomIntFloat привычным образом. Единственным отличием является выбор допустимых значений типа int и float. Если пользователь указывает любой другой тип, то сообщение об ошибке информирует его об этом и указывает допустимые типы значений.
use_localtime=YES
Сразу несколько методов
И включим лог загружаемых и скачиваемых файлов:
Примеры с использованием математических операторов
__sub__, __add__ и __mul__ (с пользовательским __repr__)


С помощью magic-методов можно также воспользоваться математическими операторами в Python. Рассмотрим на примере таких методов, как __add__, __sub__ и __mul__ в созданном нами пользовательском объекте.
xferlog_enable=YES
Разрешаем передачу данных через порт 20, требуется для лучшей совместимости с некоторыми клиентами:


Такие операторы, как +, -, / и *, являются полиморфными методами. Как показано ниже, знак плюса (+) является полиморфным и может использоваться для объединения строк, суммирования целых чисел и комбинирования списков. Это возможно благодаря тому, что такие типы, как str, list и int, обладают методом add в соответствующих классах. Python просто преобразует знак + в вызов метода __add__ для объекта, который его вызвал (см. примеры ниже).
connect_from_port_20=YES
Задаем путь к логу и его формат:


Это означает, что при включении метода __add__ в класс можно воспользоваться знаком + в объектах.
xferlog_file=/var/log/vsftpd.log
Применение magic-методов оператора в классе
xferlog_std_format=YES
Укажем таймауты:


Создаем класс NumOperations, который генерирует объекты NumOperations. Когда пользователь этого класса передает список в список аргументов __init__, он устанавливается в качестве атрибута в объекте NumOperations и получает название .math_list.
idle_session_timeout=600
data_connection_timeout=120
Первый задает время отключения бездействующего пользователя, а второй время ожидания возобновления неоконченной передачи.


После создания объекта(ов) NumOperations можно с легкостью использовать magic-методы для работы с ними и передачи математической операции.
Для корректной работы с текстовыми данными, в частности с символами переноса строки в разных ОС (CR+LF в Windows, LF в Linux) включим поддержку ASCII, это полезно, если вы скачиваете скрипты или файлы с Linux сервера, правите их в среде Windows и заливаете обратно. В этом случае FTP автоматически будет менять символы переноса строки для соответствия стандартам системы.


Например, magic-метод __sub__ принимает 2 объекта NumOperations, объединяет их списки и просматривает другие соответствующие им списки кортежей. Второй элемент в кортеже вычитается из первого, и это значение добавляется в новый список minuslst и передается в качестве аргумента в конструктор NumOperations.
ascii_upload_enable=YES
ascii_download_enable=YES
Обязательно изолируем пользователя в корне его домашней директории и рядом впишем опцию, разрешающую запись в корень:


Теперь он возвращает новый объект NumOperations.
chroot_local_user=YES
allow_writeable_chroot=YES
Остальные опции оставляем без изменения.


Эта передача выполняется по методу __sub__. Это означает, что можно воспользоваться оператором минус (-).
Ниже добавим необходимые нам настройки. Разрешим пассивный режим и явно укажем диапазон портов:


Magic-метод __repr__ реализуется повторно, чтобы возвращать представление строки списка, установленное в новом объекте. Он был изменен, поэтому когда пользователь печатает выходные данные двух объектов NumOperations, результат будет соответствовать ожиданиям.
pasv_enable=YES
pasv_min_port=62000
pasv_max_port=62999
Включим виртуальных пользователей:


Ниже представлен список, где элементы были вычтены друг из друга:
guest_enable=YES
Укажем от имени какого реального пользователя они будут работать, так как в нашем случае нам надо работать с содержимым веб-сервера, то указываем соответствующего пользователя www-data.


  [90, 81, 72, 63, 54].
  guest_username=www-data
Применяем к виртуальным пользователям настройки локальных, иначе к ним будут применены ограничения анонимных.


Методы __add__ и __mul__ реализуются аналогично __sub__, однако используют списковое включение для сокращения количества строк кода.
virtual_use_local_privs=YES
Magic-методы вычитания, сложения и умножения были определены для работы с пользовательскими объектами NumOperation.
Укажем шаблон для автоматической генерации домашнего каталога виртуального пользователя:


Такое поведение при передаче аналогично таким пакетам анализа данных, как Pandas и Numpy.
user_sub_token=$USER
Теперь можно задать сами домашние директории


Методы __add__ и __mul__ также предназначены для работы с двумя объектами NumOperations. Это означает, что пользователь может воспользоваться оператором плюс + и умножением *. Как видно из приведенного ниже примера, q является результатом x * y, который возвращает новый объект NumOperations. При вводе q мы получаем представление строки операции передачи в виде списка.
local_root=/var/www/$USER
Также иногда неплохо будет скрыть реальных владельцев содержимого, для этого добавьте:


Исходный код доступен по ссылке на GitHub gist:
hide_ids=YES
Если вы используете Ubuntu, то добавьте следующую недокументированную опцию:


  class NumOperations(object):
  seccomp_sandbox=NO
    def __init__(self, math_list):
Это позволит избежать ошибки 500 OOPS: prctl PR_SET_SECCOMP failed, которая возникает из некорректной совместной работы vsftpd и системы безопасности seccomp, в Debian данная ошибка не проявляется.
        self.math_list = math_list
    def __sub__(self, other):
        minuslst = []
        zipped = zip(self.math_list, other.math_list)
        for tup in zipped:
            minuslst.append(tup[0] - tup[1])
        return NumOperations(minuslst)


    def __add__(self, other):
На этом настройка FTP-сервера закончена, сохраняем файл и переходим к настройке правил аутентификации. Откроем /etc/pam.d/vsftpd, закомментируем в нем все строки и добавим две свои:
        addlst = [x + y for x, y in zip(self.math_list, other.math_list)]
        return NumOperations(addlst)


    def __mul__(self, other):
auth required pam_pwdfile.so pwdfile /etc/vsftpwd
        mullst = [x * y for x, y in zip(self.math_list, other.math_list)]
account required pam_permit.so
        return NumOperations(mullst)
После этого аутентификация локальных пользователей на FTP-сервере будет невозможна, работать будут только виртуальные. Опция pwdfile /etc/vsftpwd указывает путь к файлу паролей, его нам необходимо будет создать на следующем шаге.


    def __repr__(self):
Для создания пользователей будем использовать утилиту htpasswd из состава apache2-utils, для создания файла паролей и первого пользователя введите:
        return str(self.math_list)


htpasswd -c -d /etc/vsftpwd blue.lab
Ключ -c предписывает создать файл паролей, если он существует, то все данные в нем будут очищены, а файл перезаписан, -d задает необходимый для vsftpd алгоритм шифрования хэша пароля.


x = NumOperations([100, 90, 80, 70, 60])
Следующие пользователи создаются командой (в нашем примере это пользователь green.lab):
y = NumOperations([10, 9, 8, 7, 6])
p = x - y
z = x + y
q = x * y
print('Subtraction: ' + str(p))
print('Addition: ' + str(z))
print('Multiplication: ' + str(q))


Читайте также:
htpasswd -d /etc/vsftpwd green.lab
Для удаления пользователя используйте (удаляем red.lab):


    Анализ аудиоданных с помощью глубокого обучения и Python (часть 1)
htpasswd -D /etc/vsftpwd red.lab
    Максимальная производительность Pandas Python
После чего можно перезапустить службу FTP-сервера и проверить его работу:
    Продвинутые методы и техники списков в Python


Перевод статьи Stephen Fordham: Using Magic Methods in Python
service vsftpd restart
Войдите, чтобы отправлять и оценивать комментарии
Как видим, настроить FTP-сервер с виртуальными пользователями по нашей инструкции (в отличие от многих иных "инструкций" в сети) абсолютно несложно и недолго. Если вы будете внимательно следовать всем нашим рекомендациям, то сложностей у вас возникнуть не должно.
Расскажите, что вы об этом думаете?


'''ИСТОЧНИК:'''
'''ИСТОЧНИК:'''
<hr>
<hr>
* [https://yandex.ru/turbo/nuancesprog.ru/s/p/5966/ Идеальная настройка вашего Golangпроекта]
* [https://interface31.ru/tech_it/2016/01/nastraivaem-ftp-server-s-virtual-nymi-pol-zovatelyami-na-baze-vsftpd.html Записки IT специалиста]

Версия от 02:20, 13 декабря 2020

Автор: Уваров А.С. 29.01.2016
Vsftpd-virtual-user-000.jpg
Протокол FTP, несмотря на преклонный возраст и серьезные недостатки продолжает широко применяться, во многом благодаря простоте и поддержке со стороны самого широкого спектра устройств и ПО. FTP-сервер является неотъемлемой частью любого веб-сервера, применяется в системах автоматизированного обмена данными. Наиболее часто встающей перед администраторами таких систем задачей является создание сервера с виртуальными пользователями. Сегодня мы расскажем, как это сделать.

Про настройку FTP-сервера на базе vsftpd мы уже рассказывали на страницах нашего сайта, недавно существенно дополнив и обновив статью Простой FTP-сервер на базе Ubuntu (vsftpd). В ней мы рассказали каким образом можно ограничить список пользователей, имеющих возможность подключаться к серверу, и их права. Однако локальные пользователи остаются при этом локальными пользователями и несмотря на ограниченные права могут оказаться объектом атаки, направленной на повышение прав, например, через уязвимости в каком-либо ПО.

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

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

В нашей статье мы настроим FTP-сервер для реализации следующей схемы:

vsftpd-virtual-user-001.jpg
Пусть в нашем распоряжении имеется некий веб-сервер под управлением которого находятся три сайта: blue.lab, green.lab и red.lab, физически расположенные в одноименных директориях. Наша задача создать FTP-сервер с виртуальными пользователями, каждый из которых имеет доступ только к своему сайту.

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

Прежде всего установим соглашение: имя виртуального пользователя должно совпадать и именем корневой FTP-папки, в нашем случае папки сайта, а все такие папки должны располагаться в одном месте, т.е. подпадать под шаблон /var/www/$USER. Это позволит в дальнейшем с легкостью манипулировать сайтами и виртуальными пользователями, а также избежать путаницы в именах и настройках.

Определившись с моделью размещения данных можно переходить к настройкам ПО. Кроме vsftpd нам понадобятся пакеты libpam-pwdfile и apache2-utils. Последний входит в состав одноименного веб-сервера и если у вас уже работает Apache, то устанавливать его не надо.

Сама установка предельно проста:

apt-get install vsftpd libpam-pwdfile apache2-utils

После чего перейдем к настройкам FTP-сервера, которые расположены в файле /etc/vsftpd.conf, мы перечислим опции в порядке их нахождения в файле.

Данная опция запускает FTP-сервер как службу

listen=YES

ниже имеется аналогичная взаимоисключающая опция:

listen_ipv6=NO

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

Запрещаем анонимных пользователей:

anonymous_enable=NO

И разрешаем локальных (и виртуальных) пользователей:

local_enable=YES

Также разрешаем запись:

write_enable=YES

и задаем маску для вновь создаваемых файлов и папок:

local_umask=022

Это обеспечит установку прав 755 на папки и 644 на файлы, т.е. полный доступ только владельцу и чтение для группы и остальных.

Чтобы FTP использовал локальное время сервера, а не GMT установим:

use_localtime=YES

И включим лог загружаемых и скачиваемых файлов:

xferlog_enable=YES

Разрешаем передачу данных через порт 20, требуется для лучшей совместимости с некоторыми клиентами:

connect_from_port_20=YES

Задаем путь к логу и его формат:

xferlog_file=/var/log/vsftpd.log
xferlog_std_format=YES

Укажем таймауты:

idle_session_timeout=600
data_connection_timeout=120

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

Для корректной работы с текстовыми данными, в частности с символами переноса строки в разных ОС (CR+LF в Windows, LF в Linux) включим поддержку ASCII, это полезно, если вы скачиваете скрипты или файлы с Linux сервера, правите их в среде Windows и заливаете обратно. В этом случае FTP автоматически будет менять символы переноса строки для соответствия стандартам системы.

ascii_upload_enable=YES
ascii_download_enable=YES

Обязательно изолируем пользователя в корне его домашней директории и рядом впишем опцию, разрешающую запись в корень:

chroot_local_user=YES
allow_writeable_chroot=YES

Остальные опции оставляем без изменения.

Ниже добавим необходимые нам настройки. Разрешим пассивный режим и явно укажем диапазон портов:

pasv_enable=YES
pasv_min_port=62000
pasv_max_port=62999

Включим виртуальных пользователей:

guest_enable=YES

Укажем от имени какого реального пользователя они будут работать, так как в нашем случае нам надо работать с содержимым веб-сервера, то указываем соответствующего пользователя www-data.

guest_username=www-data

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

virtual_use_local_privs=YES

Укажем шаблон для автоматической генерации домашнего каталога виртуального пользователя:

user_sub_token=$USER

Теперь можно задать сами домашние директории

local_root=/var/www/$USER

Также иногда неплохо будет скрыть реальных владельцев содержимого, для этого добавьте:

hide_ids=YES

Если вы используете Ubuntu, то добавьте следующую недокументированную опцию:

seccomp_sandbox=NO

Это позволит избежать ошибки 500 OOPS: prctl PR_SET_SECCOMP failed, которая возникает из некорректной совместной работы vsftpd и системы безопасности seccomp, в Debian данная ошибка не проявляется.

На этом настройка FTP-сервера закончена, сохраняем файл и переходим к настройке правил аутентификации. Откроем /etc/pam.d/vsftpd, закомментируем в нем все строки и добавим две свои:

auth required pam_pwdfile.so pwdfile /etc/vsftpwd
account required pam_permit.so

После этого аутентификация локальных пользователей на FTP-сервере будет невозможна, работать будут только виртуальные. Опция pwdfile /etc/vsftpwd указывает путь к файлу паролей, его нам необходимо будет создать на следующем шаге.

Для создания пользователей будем использовать утилиту htpasswd из состава apache2-utils, для создания файла паролей и первого пользователя введите:

htpasswd -c -d /etc/vsftpwd blue.lab

Ключ -c предписывает создать файл паролей, если он существует, то все данные в нем будут очищены, а файл перезаписан, -d задает необходимый для vsftpd алгоритм шифрования хэша пароля.

Следующие пользователи создаются командой (в нашем примере это пользователь green.lab):

htpasswd -d /etc/vsftpwd green.lab

Для удаления пользователя используйте (удаляем red.lab):

htpasswd -D /etc/vsftpwd red.lab

После чего можно перезапустить службу FTP-сервера и проверить его работу:

service vsftpd restart

Как видим, настроить FTP-сервер с виртуальными пользователями по нашей инструкции (в отличие от многих иных "инструкций" в сети) абсолютно несложно и недолго. Если вы будете внимательно следовать всем нашим рекомендациям, то сложностей у вас возникнуть не должно.

ИСТОЧНИК: