owner: роль причёсана под конвенции eget и secrets

- валидация аргументов через assert вместо пары fail с одинаковым именем
- with_items/with_dict заменены на loop, имена задач без точки на конце
- добавлены meta/main.yml и README с таблицей переменных
- задача ansible-owner-role-cleanup удалена из беклога
This commit is contained in:
av
2026-08-22 19:27:33 +03:00
parent 70372ee028
commit 856bf72560
5 changed files with 100 additions and 41 deletions
-1
View File
@@ -42,7 +42,6 @@ _(sec-ревью)_ — три независимых security-ревью на м
- [Мелкий хардненинг контейнеров](container-hardening-nits.md) — `no-new-privileges`, non-root `user:`, homepage off `:latest`, соглашение о `127.0.0.1:` портах _(sec-ревью)_ - [Мелкий хардненинг контейнеров](container-hardening-nits.md) — `no-new-privileges`, non-root `user:`, homepage off `:latest`, соглашение о `127.0.0.1:` портах _(sec-ревью)_
- [Унифицировать ACL техпанелей Authelia](authelia-acl-unify.md) — rssbridge (SSRF) и dashboard за `one_factor` без subject; свести к `group:admins` + `two_factor` _(sec-ревью)_ - [Унифицировать ACL техпанелей Authelia](authelia-acl-unify.md) — rssbridge (SSRF) и dashboard за `one_factor` без subject; свести к `group:admins` + `two_factor` _(sec-ревью)_
- [Причесать роль `owner` под конвенции](ansible-owner-role-cleanup.md) — `assert` вместо `fail`+`when`, `loop` вместо `with_*`, добавить `meta`/README _(ансибл-ревью)_
- [Инвентарь: `host_vars`, именованные группы, точечный `become`](ansible-inventory-hostvars.md) — хост-специфику в `host_vars/server.yml`, хост в именованную группу, глобальный root → точечный become _(ансибл-ревью)_ - [Инвентарь: `host_vars`, именованные группы, точечный `become`](ansible-inventory-hostvars.md) — хост-специфику в `host_vars/server.yml`, хост в именованную группу, глобальный root → точечный become _(ансибл-ревью)_
- [Фоновая зачистка стиля и конфигурации Ansible](ansible-style-nits.md) — sudoers.d, профиль ansible-lint, `ansible.cfg`, кавычки, `cache_valid_time` _(ансибл-ревью)_ - [Фоновая зачистка стиля и конфигурации Ansible](ansible-style-nits.md) — sudoers.d, профиль ansible-lint, `ansible.cfg`, кавычки, `cache_valid_time` _(ансибл-ревью)_
- [Logrotate для логов бэкапов](backup-logs-logrotate.md) — логи `backup-all.py`/restic копятся без ротации _(tududi)_ - [Logrotate для логов бэкапов](backup-logs-logrotate.md) — логи `backup-all.py`/restic копятся без ротации _(tududi)_
@@ -1,20 +0,0 @@
# Причесать роль `owner` под конвенции
**Приоритет:** низкий
Роль `owner` разошлась по стилю с `eget`/`secrets`:
- **`roles/owner/tasks/main.yml:2-10`** — валидация аргументов через `fail` +
`when`, причём две задачи с **идентичным именем**. `eget` для того же делает
`assert` (`roles/eget/tasks/main.yml:15`). Привести к одному стилю — `assert`
либо декларативный `meta/argument_specs.yml`.
- **`roles/owner/tasks/main.yml:32,53`** — устаревшие `with_items`/`with_dict`;
конвенция — `loop` (`loop: "{{ owner_ssh_keys }}"`,
`loop: "{{ owner_env_dict | dict2items }}"`).
- У `owner` нет `meta/main.yml` и README, тогда как у `eget` и `secrets` есть.
- Имена задач с точкой на конце (`"Prepare env variables."`) — ansible-lint в
строгом профиле это ловит.
Контекст: [docs/drafts/ansible-review.md](../drafts/ansible-review.md) §5.
Связано: roles/owner, roles/eget (образец), roles/secrets.
+52
View File
@@ -0,0 +1,52 @@
# owner
Заводит системного пользователя и группу под одно приложение: фиксированные
uid/gid, ssh-ключи и переменные окружения в `~/.env`.
Один сервис — один пользователь: контейнеры приложения работают от его uid/gid,
и файлы в `base_dir` принадлежат ему же. Роль вызывается первой задачей почти
каждого `playbook-<app>.yml`.
## Что делает
1. **Группа и пользователь.** Создаёт группу `owner_group` с `owner_gid` и
пользователя `owner_name` с `owner_uid` и шеллом `/bin/bash`. Номера задаёт
плейбук приложения (соглашение по нумерации — в `AGENTS.md`).
2. **SSH-ключи.** Прописывает `owner_ssh_keys` в `authorized_keys`.
3. **Переменные окружения.** Рендерит `~/.env` из `owner_env` плюс `USER_UID` и
`USER_GID` — их подставляют docker-compose шаблоны приложений. В `.bashrc`
добавляет строку, подгружающую `~/.env` целиком, и вычищает старые построчные
`export`-ы, оставшиеся от прежней схемы.
## Результат для плейбука
Роль регистрирует `owner_create_result` — результат модуля `user`. Плейбуки и
compose-шаблоны берут оттуда `owner_create_result.uid` и
`owner_create_result.group` (это gid) для `user:` в docker-compose.
## Переменные
Полный список и значения по умолчанию — в `defaults/main.yml`.
| Переменная | Обязательная | Описание |
| --------------------- | ------------ | --------------------------------------------------- |
| `owner_name` | да | Имя пользователя; по умолчанию оно же имя группы |
| `owner_uid` | да | Uid пользователя |
| `owner_group` | нет | Имя группы, по умолчанию `owner_name` |
| `owner_gid` | нет | Gid группы, по умолчанию `owner_uid` |
| `owner_extra_groups` | нет | Дополнительные группы (обычно `["docker"]`) |
| `owner_ssh_keys` | нет | Список публичных ключей для `authorized_keys` |
| `owner_env` | нет | Словарь переменных окружения, попадающих в `~/.env` |
## Пример использования
```yaml
- name: "Create user and environment"
ansible.builtin.import_role:
name: owner
vars:
owner_name: "{{ app_user }}"
owner_uid: "{{ app_owner_uid }}"
owner_gid: "{{ app_owner_gid }}"
owner_extra_groups: ["docker"]
```
+20
View File
@@ -0,0 +1,20 @@
---
galaxy_info:
role_name: owner
author: "Anton Vakhrushev"
description: "Системный пользователь приложения: группа, uid/gid, ssh-ключи и переменные окружения"
license: "MIT"
min_ansible_version: "2.14"
platforms:
- name: Ubuntu
versions:
- all
- name: Debian
versions:
- all
galaxy_tags:
- users
- system
- environment
dependencies: []
+28 -20
View File
@@ -1,21 +1,24 @@
--- ---
- name: 'Check app requirements for user "{{ owner_name }}".' # tasks file for owner
ansible.builtin.fail:
msg: You must set owner name.
when: not owner_name
- name: 'Check app requirements for user "{{ owner_name }}".' - name: "Validate owner role arguments"
ansible.builtin.fail: ansible.builtin.assert:
msg: You must set owner uid. that:
when: not owner_uid - owner_name is string and owner_name | length > 0
- owner_uid | int > 0
- owner_gid | int > 0
fail_msg: >-
Роль owner требует непустой owner_name и положительные owner_uid/owner_gid
(owner_name={{ owner_name }}, owner_uid={{ owner_uid }}, owner_gid={{ owner_gid }})
quiet: true
- name: 'Create group "{{ owner_group }}".' - name: "Create group {{ owner_group }}"
ansible.builtin.group: ansible.builtin.group:
name: "{{ owner_group }}" name: "{{ owner_group }}"
gid: "{{ owner_gid }}" gid: "{{ owner_gid }}"
state: present state: present
- name: 'Create user "{{ owner_name }}".' - name: "Create user {{ owner_name }}"
ansible.builtin.user: ansible.builtin.user:
name: "{{ owner_name }}" name: "{{ owner_name }}"
group: "{{ owner_group }}" group: "{{ owner_group }}"
@@ -24,20 +27,21 @@
shell: /bin/bash shell: /bin/bash
register: owner_create_result register: owner_create_result
- name: 'Set up user ssh keys for user "{{ owner_name }}".' - name: "Set up ssh keys for user {{ owner_name }}"
ansible.posix.authorized_key: ansible.posix.authorized_key:
user: "{{ owner_name }}" user: "{{ owner_name }}"
key: "{{ item }}" key: "{{ item }}"
state: present state: present
with_items: "{{ owner_ssh_keys }}" loop: "{{ owner_ssh_keys }}"
when: owner_ssh_keys | length > 0
- name: "Prepare env variables." # USER_UID/USER_GID кладём в окружение всегда: их подставляют docker-compose
# шаблоны приложений, чтобы контейнер работал от того же пользователя.
- name: "Prepare env variables"
ansible.builtin.set_fact: ansible.builtin.set_fact:
# yamllint disable-line rule:line-length owner_env_dict: >-
owner_env_dict: '{{ owner_env | combine({"USER_UID": owner_create_result.uid, "USER_GID": owner_create_result.group}) }}' {{ owner_env | combine({"USER_UID": owner_create_result.uid, "USER_GID": owner_create_result.group}) }}
- name: 'Set up environment variables for user "{{ owner_name }}".' - name: "Set up environment variables for user {{ owner_name }}"
ansible.builtin.template: ansible.builtin.template:
src: env.template src: env.template
dest: "/home/{{ owner_name }}/.env" dest: "/home/{{ owner_name }}/.env"
@@ -45,14 +49,18 @@
group: "{{ owner_group }}" group: "{{ owner_group }}"
mode: "0640" mode: "0640"
- name: 'Remove from bashrc absent environment variables for user "{{ owner_name }}".' # Раньше переменные писались в .bashrc построчно; теперь источник истины —
# ~/.env, поэтому старые строки вычищаем.
- name: "Remove from bashrc absent environment variables for user {{ owner_name }}"
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: "/home/{{ owner_name }}/.bashrc" path: "/home/{{ owner_name }}/.bashrc"
regexp: "^export {{ item.key }}=" regexp: "^export {{ item.key }}="
state: absent state: absent
with_dict: "{{ owner_env_dict }}" loop: "{{ owner_env_dict | dict2items }}"
loop_control:
label: "{{ item.key }}"
- name: 'Include in bashrc environment variables for user "{{ owner_name }}".' - name: "Include in bashrc environment variables for user {{ owner_name }}"
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: "/home/{{ owner_name }}/.bashrc" path: "/home/{{ owner_name }}/.bashrc"
regexp: "^export \\$\\(grep -v" regexp: "^export \\$\\(grep -v"