diff --git a/roles/ansible/tasks/main.yml b/roles/ansible/tasks/main.yml index e5970b8..ed97d53 100644 --- a/roles/ansible/tasks/main.yml +++ b/roles/ansible/tasks/main.yml @@ -1,3 +1 @@ --- -- name: Load role related variables - ansible.builtin.include_vars: main.yml \ No newline at end of file diff --git a/roles/ansible/vars/main.yml b/roles/ansible/vars/main.yml index 6e5e958..e986c65 100644 --- a/roles/ansible/vars/main.yml +++ b/roles/ansible/vars/main.yml @@ -1,5 +1,35 @@ __ansible__config: modules: + ansible: + builtin: + systemd_service: + required: ['name'] + uniques: ['name'] + aliases: + daemon_reexec: ['daemon-reexec'] + daemon_reload: ['daemon-reload'] + name: ['service', 'unit'] + service: + required: ['name'] + uniques: ['name'] + aliases: + arguments: ['args'] + apt: + uniques: ['name'] + aliases: + allow_downgrade: ['allow-downgrade', 'allow_downgrades', 'allow-downgrades'] + allow_unauthenticated: ['allow-unauthenticated'] + default_release: ['default-release'] + install_recommends: ['install-recommends'] + name: ['package', 'pkg'] + update_cache: ['update-cache'] + apt_repository: + uniques: ['repo'] + aliases: + update_cache: ['update-cache'] + apt_key: + uniques: ['keyserver', 'url', 'id', 'file' ,'data'] + aliases: {} ansible_builtin_apt: uniques: ['name'] aliases: @@ -40,4 +70,13 @@ __ansible__config: uniques: ['name'] aliases: groups: ['group', 'groupname'] - name: ['host','hostname'] \ No newline at end of file + name: ['host','hostname'] + ansible_builtin_user: + uniques: ['name'] + aliases: + create_home: ['createhome'] + name: ['user'] + ansible_builtin_group: + uniques: ['name'] + ansible_posix_authorized_key: + required: ['user', 'key'] diff --git a/roles/auth/defaults/main.yml b/roles/auth/defaults/main.yml new file mode 100644 index 0000000..f9cc390 --- /dev/null +++ b/roles/auth/defaults/main.yml @@ -0,0 +1,24 @@ +auth__role_enabled: false + +auth__manage_groups: false +auth__manage_users: false +auth__manage_authorized_keys: false +auth__manage_ssh_config: false + +auth__ssh_config_dir: /etc/ssh/ssh_config.d +auth__ssh_config_file: "{{ auth__ssh_config_dir }}/50-ansible.conf" +auth__ssh_config_template: etc/ssh/ssh_config.d/custom.conf.j2 +auth__ssh_config_backup: true +# module can be systemd_service or service +# module can be left empty to skip the service restart +# when can be immediate or at the end of the play +auth__ssh_config_change_strategy: + module: systemd_service + when: immediate +# auth__sshd_validate: "/usr/sbin/sshd -t -f %s" + +auth__default: [] +auth__group: [] +auth__host: [] + +auth__all: "{{ auth__host + auth__group + auth__default }}" \ No newline at end of file diff --git a/roles/auth/handlers/main.yml b/roles/auth/handlers/main.yml new file mode 100644 index 0000000..5e51599 --- /dev/null +++ b/roles/auth/handlers/main.yml @@ -0,0 +1,45 @@ +--- +- name: Effect systemd service for ssh config changes + become: true + ansible.builtin.systemd_service: + daemon_reexec: "{{ change_strategy.daemon_reexec | default(omit) | bool }}" + daemon_reload: "{{ change_strategy.daemon_reload | default(omit) | bool }}" + enabled: "{{ change_strategy.enabled | default(omit) | bool }}" + force: "{{ change_strategy.force | default(omit) | bool }}" + masked: "{{ change_strategy.masked | default(omit) | bool }}" + name: "{{ change_strategy.name | default('ssh.service') }}" + no_block : "{{ change_strategy.no_block | default(omit) | bool }}" + scope: "{{ change_strategy.scope | default(omit) }}" + state: "{{ change_strategy.state | default(omit) }}" + vars: + change_strategy: "{{ (__ansible__config is defined) | + ternary(auth__ssh_config_change_strategy | aybarsm.helper.replace_aliases(__ansible__config.modules.ansible.builtin.systemd_service.aliases), + auth__ssh_config_change_strategy) + ) }}" + register: auth__ssh_config_apply_changes_systemd_service + listen: "auth__ssh_config_apply_changes" + when: + - change_strategy.module is defined + - change_strategy.module == 'systemd_service' + +- name: Effect service for ssh config changes + become: true + ansible.builtin.service: + arguments: "{{ change_strategy.arguments | default(omit) }}" + enabled: "{{ change_strategy.enabled | default(omit) }}" + name: "{{ change_strategy.name }}" + pattern: "{{ change_strategy.pattern | default(omit) }}" + runlevel: "{{ change_strategy.runlevel | default(omit) }}" + sleep: "{{ change_strategy.sleep | default(omit) }}" + state: "{{ change_strategy.state | default(omit) }}" + use: "{{ change_strategy.use | default(omit) }}" + vars: + change_strategy: "{{ (__ansible__config is defined) | + ternary(auth__ssh_config_change_strategy | aybarsm.helper.replace_aliases(__ansible__config.modules.ansible.builtin.service.aliases), + auth__ssh_config_change_strategy) + ) }}" + register: auth__ssh_config_apply_changes_service + listen: "auth__ssh_config_apply_changes" + when: + - change_strategy.module is defined + - change_strategy.module == 'service' \ No newline at end of file diff --git a/roles/auth/meta/main.yml b/roles/auth/meta/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/auth/tasks/authorized_keys.yml b/roles/auth/tasks/authorized_keys.yml new file mode 100644 index 0000000..84f9ae6 --- /dev/null +++ b/roles/auth/tasks/authorized_keys.yml @@ -0,0 +1,19 @@ +--- +- name: Apply authorized keys configuration + become: true + ansible.posix.authorized_key: + comment: "{{ item.comment | default(omit) }}" + exclusive: "{{ item.exclusive | default(omit) | bool }}" + follow: "{{ item.follow | default(omit) | bool }}" + key: "{{ item.key }}" + key_options: "{{ item.key_options | default(omit) }}" + manage_dir: "{{ item.manage_dir | default(omit) | bool }}" + path: "{{ item.path | default(omit) }}" + state: "{{ item.state | default(omit) }}" + user: "{{ item.user }}" + validate_certs: "{{ item.validate_certs | default(omit) | bool }}" + loop: "{{ auth__authorized_keys_all }}" + register: auth__authorized_keys_apply + when: + - auth__authorized_keys_all | type_debug == 'list' + - auth__authorized_keys_all | length > 0 diff --git a/roles/auth/tasks/groups.yml b/roles/auth/tasks/groups.yml new file mode 100644 index 0000000..27929b5 --- /dev/null +++ b/roles/auth/tasks/groups.yml @@ -0,0 +1,16 @@ +--- +- name: Apply groups configuration + become: true + ansible.builtin.group: + force: "{{ item.force | default(omit) | bool }}" + gid: "{{ item.gid | default(omit) }}" + local: "{{ item.local | default(omit) | bool }}" + name: "{{ item.name }}" + non_unique: "{{ item.non_unique | default(omit) | bool }}" + state: "{{ item.state | default(omit) }}" + system: "{{ item.system | default(omit) | bool }}" + loop: "{{ auth__groups_all }}" + register: auth__groups_apply + when: + - auth__groups_all | type_debug == 'list' + - auth__groups_all | length > 0 \ No newline at end of file diff --git a/roles/auth/tasks/main.yml b/roles/auth/tasks/main.yml new file mode 100644 index 0000000..f4d5594 --- /dev/null +++ b/roles/auth/tasks/main.yml @@ -0,0 +1,32 @@ +--- +# Require for configuration management +- name: Load aybarsm ansible role main variables + ansible.builtin.include_vars: "../ansible/vars/main.yml" + +- name: Include groups tasks + ansible.builtin.include_tasks: + file: groups.yml + when: + - auth__role_enabled | default(false) | bool + - auth__manage_groups | default(false) | bool + +- name: Include users tasks + ansible.builtin.include_tasks: + file: users.yml + when: + - auth__role_enabled | default(false) | bool + - auth__manage_users | default(false) | bool + +- name: Include authroized keys tasks + ansible.builtin.include_tasks: + file: authorized_keys.yml + when: + - auth__role_enabled | default(false) | bool + - auth__manage_authorized_keys | default(false) | bool + +- name: Include ssh config tasks + ansible.builtin.include_tasks: + file: ssh_config.yml + when: + - auth__role_enabled | default(false) | bool + - auth__manage_ssh_config | default(false) | bool \ No newline at end of file diff --git a/roles/auth/tasks/ssh_config.yml b/roles/auth/tasks/ssh_config.yml new file mode 100644 index 0000000..dd5cebd --- /dev/null +++ b/roles/auth/tasks/ssh_config.yml @@ -0,0 +1,21 @@ +--- +- name: Apply ssh configuration + become: true + ansible.builtin.template: + src: "{{ auth__ssh_config_template }}" + dest: "{{ auth__ssh_config_file }}" + backup: "{{ auth__ssh_config_backup | default(omit) | bool }}" + validate: "{{ auth__sshd_validate | default(omit) }}" + register: auth__ssh_config_apply + notify: "auth__ssh_config_apply_changes" + when: + - auth__ssh_config_all | type_debug == 'list' + - auth__ssh_config_all | length > 0 + +- name: Effect ssh config changes + ansible.builtin.meta: 'flush_handlers' + when: + - auth__ssh_config_change_strategy.module is defined + - auth__ssh_config_change_strategy.when is defined + - auth__ssh_config_change_strategy.when == 'immediate' + \ No newline at end of file diff --git a/roles/auth/tasks/users.yml b/roles/auth/tasks/users.yml new file mode 100644 index 0000000..2caab97 --- /dev/null +++ b/roles/auth/tasks/users.yml @@ -0,0 +1,47 @@ +--- +- name: Apply users configuration + become: true + ansible.builtin.user: + append: "{{ item.append | default(omit) | bool }}" + authorization: "{{ item.authorization | default(omit) }}" + comment: "{{ item.comment | default(omit) }}" + create_home: "{{ item.create_home | default(omit) | bool }}" + expires: "{{ item.expires | default(omit) }}" + force: "{{ item.force | default(omit) | bool }}" + generate_ssh_key: "{{ item.generate_ssh_key | default(omit) | bool }}" + group: "{{ item.group | default(omit) }}" + groups: "{{ item.groups | default(omit) }}" + hidden: "{{ item.hidden | default(omit) | bool }}" + home: "{{ item.home | default(omit) }}" + local: "{{ item.local | default(omit) | bool }}" + login_class: "{{ item.login_class | default(omit) }}" + move_home: "{{ item.move_home | default(omit) | bool }}" + name: "{{ item.name }}" + non_unique: "{{ item.non_unique | default(omit) | bool }}" + password: "{{ item.password | default(omit) }}" + password_expire_max: "{{ item.password_expire_max | default(omit) }}" + password_expire_min: "{{ item.password_expire_min | default(omit) }}" + password_expire_warn: "{{ item.password_expire_warn | default(omit) }}" + password_lock: "{{ item.password_lock | default(omit) | bool }}" + profile: "{{ item.profile | default(omit) }}" + remove: "{{ item.remove | default(omit) | bool }}" + role: "{{ item.role | default(omit) }}" + seuser: "{{ item.seuser | default(omit) }}" + shell: "{{ item.shell | default(omit) }}" + skeleton: "{{ item.skeleton | default(omit) }}" + ssh_key_bits: "{{ item.ssh_key_bits | default(omit) }}" + ssh_key_comment: "{{ item.ssh_key_comment | default(omit) }}" + ssh_key_file: "{{ item.ssh_key_file | default(omit) }}" + ssh_key_passphrase: "{{ item.ssh_key_passphrase | default(omit) }}" + ssh_key_type: "{{ item.ssh_key_type | default(omit) }}" + state: "{{ item.state | default(omit) }}" + system: "{{ item.system | default(omit) | bool }}" + uid: "{{ item.uid | default(omit) }}" + umask: "{{ item.umask | default(omit) }}" + update_password: "{{ item.update_password | default(omit) }}" + loop: "{{ auth__users_all }}" + register: auth__users_apply + when: + - auth__users_all | type_debug == 'list' + - auth__users_all | length > 0 + \ No newline at end of file diff --git a/roles/auth/templates/etc/ssh/ssh_config.d/custom.conf.j2 b/roles/auth/templates/etc/ssh/ssh_config.d/custom.conf.j2 new file mode 100644 index 0000000..40d632f --- /dev/null +++ b/roles/auth/templates/etc/ssh/ssh_config.d/custom.conf.j2 @@ -0,0 +1,2 @@ +{{ ansible_managed | comment }} +{{ auth__ssh_config_all | aybarsm.helper.to_querystring('name', 'value', ' ', '\n', 'children', 4, ' ', true) }} \ No newline at end of file diff --git a/roles/auth/vars/main.yml b/roles/auth/vars/main.yml new file mode 100644 index 0000000..0e36c02 --- /dev/null +++ b/roles/auth/vars/main.yml @@ -0,0 +1,52 @@ +--- +__auth__config: + authorized_keys: + selectattr: + - when: + - ['type', 'defined'] + - ['type', 'equalto', 'authorized_key'] + - ['user', 'defined'] + - ['key', 'defined'] + groups: + selectattr: + - when: + - ['type', 'defined'] + - ['type', 'equalto', 'group'] + - when: "{{ __ansible__config.modules.ansible_builtin_group.uniques | product(['defined']) | list }}" + logic: or + users: + selectattr: + - when: + - ['type', 'defined'] + - ['type', 'equalto', 'user'] + - when: "{{ __ansible__config.modules.ansible_builtin_user.uniques | product(['defined']) | list }}" + logic: or + ssh_config: + selectattr: + - when: + - ['type', 'defined'] + - ['type', 'equalto', 'ssh_config'] + - ['name', 'defined'] + - ['value', 'defined'] + +auth__authorized_keys_all: "{{ auth__all | + aybarsm.helper.selectattr(__auth__config.authorized_keys.selectattr) | + aybarsm.helper.unique_combinations([['user', 'key']]) | + default([]) }}" + +auth__groups_all: "{{ auth__all | + aybarsm.helper.selectattr(__auth__config.packages.selectattr) | + aybarsm.helper.unique_recursive(__ansible__config.modules.ansible_builtin_group.uniques) | + default([]) }}" + +auth__users_all: "{{ auth__all | + aybarsm.helper.selectattr(__auth__config.packages.selectattr) | + aybarsm.helper.replace_aliases(__ansible__config.modules.ansible_builtin_user.aliases) | + aybarsm.helper.unique_recursive(__ansible__config.modules.ansible_builtin_user.uniques) | + default([]) }}" + +auth__ssh_config_all: "{{ auth__all | + aybarsm.helper.selectattr(__auth__config.ssh_config.selectattr) | + aybarsm.helper.replace_aliases(__ansible__config.modules.ansible_builtin_user.aliases) | + aybarsm.helper.unique_recursive('name', 'children') | + default([]) }}" \ No newline at end of file diff --git a/roles/proxmox/defaults/main.yml b/roles/proxmox/defaults/main.yml index ca14103..0ef8dd1 100644 --- a/roles/proxmox/defaults/main.yml +++ b/roles/proxmox/defaults/main.yml @@ -5,14 +5,18 @@ proxmox__repo_url_no_subscription: http://download.proxmox.com/debian proxmox__purpose_names: ['pve', 'pbs', 'pmg', 'ceph-pacific', 'ceph-quincy', 'ceph-reef'] proxmox__purpose_types: ['enterprise', 'no-subscription'] +proxmox__purpose_packages: + pve: pve-manager + pbs: proxmox-backup-server + pmg: proxmox-mailgateway # If enabled, the role will manage the purpose package setup for repos and package versioning. proxmox__manage_purpose_package_setup: true proxmox__purpose_package_setup_template: purpose_package_setup.j2 -# proxmox__default: {} -# proxmox__group: {} -# proxmox__host: {} +proxmox__default: {} +proxmox__group: {} +proxmox__host: {} # Example configuration: # Purposes will be overwritten by the host configuration. @@ -40,4 +44,4 @@ proxmox__purpose_package_setup_template: purpose_package_setup.j2 # The combination strategy below is highly nested hierarchy compliant and recommended (Example provided below) # proxmox__host > proxmox__group > proxmox__default -proxmox__all: "{{ proxmox__default | combine(proxmox__group, proxmox__host, recursive=true, list_merge=replace) }}" \ No newline at end of file +proxmox__all: "{{ proxmox__default | combine(proxmox__group, proxmox__host, recursive=true, list_merge='replace') }}" \ No newline at end of file diff --git a/roles/proxmox/meta/main.yml b/roles/proxmox/meta/main.yml index a704663..a7eebd5 100644 --- a/roles/proxmox/meta/main.yml +++ b/roles/proxmox/meta/main.yml @@ -1,2 +1,2 @@ -dependencies: - - role: aybarsm.linux.package_manager \ No newline at end of file +# dependencies: +# - role: aybarsm.linux.package_manager \ No newline at end of file diff --git a/roles/proxmox/tasks/main.yml b/roles/proxmox/tasks/main.yml index 3927e0e..8b5dbb0 100644 --- a/roles/proxmox/tasks/main.yml +++ b/roles/proxmox/tasks/main.yml @@ -2,8 +2,8 @@ - name: Debug ansible.builtin.debug: msg: - role_path: "{{ role_path }}" - role_repos: "{{ lookup('template', proxmox__purpose_repo_template) }}" + # role_path: "{{ role_path }}" + purpose_package_setup: "{{ lookup('template', proxmox__purpose_package_setup_template) }}" delegate_to: localhost # - name: Import aybarsm linux ansible role # ansible.builtin.import_role: diff --git a/roles/proxmox/templates/purpose_package_setup.j2 b/roles/proxmox/templates/purpose_package_setup.j2 index 2ad3f6f..e6da31e 100644 --- a/roles/proxmox/templates/purpose_package_setup.j2 +++ b/roles/proxmox/templates/purpose_package_setup.j2 @@ -1,30 +1,42 @@ -{%- set proxmox_package_setup = [] -%} +{%- if ansible_distribution_release is undefined -%} +{%- set ansible_distribution_release = 'bookworm' -%} +{%- endif -%} +{%- set proxmox_repos = [] -%} +{%- set proxmox_packages = [] -%} {%- if proxmox__all.purposes is defined -%} -{%- set proxmox_host_purposes = proxmox__all.purposes | map('regex_replace', proxmox__purpose_separator_regex, '\\1') -%} -{%- for proxmox_purpose in __proxmox__purposes -%} -{%- set proxmox_purpose_segments = proxmox_purpose | split(proxmox__purpose_separator) -%} -{%- set purpose_name = proxmox_purpose_segments[0] -%} -{%- set purpose_type = proxmox_purpose_segments[1] -%} -{%- if ansible_distribution_release is undefined -%} -{%- set ansible_distribution_release = 'bookworm' -%} -{%- endif -%} -{%- set purpose_repo_state = 'present' if proxmox_purpose in proxmox_host_purposes else 'absent' -%} -{%- set purpose_name_url = proxmox__repo_url_enterprise if purpose_type == 'enterprise' else proxmox__repo_url_no_subscription -%} -{%- if purpose_name in ['pve', 'pbs', 'pmg'] -%} -{%- set proxmox_package_setup = proxmox_package_setup.append({ +{%- for purpose in proxmox__purpose_names -%} +{%- for purpose_type in proxmox__purpose_types -%} +{%- set host_config = proxmox__all.purposes[purpose] if purpose in proxmox__all.purposes else none -%} +{%- set is_host_purpose = true if host_config else false -%} +{%- set is_host_purpose_type = true if host_config and host_config.type is defined and host_config.type == purpose_type else false -%} +{%- set host_purpose_version = host_config.version if host_config and host_config.version is defined else none -%} +{%- set purpose_repo_state = 'present' if is_host_purpose and is_host_purpose_type else 'absent' -%} +{%- set purpose_repo = proxmox__repo_url_enterprise if purpose_type == 'enterprise' else proxmox__repo_url_no_subscription -%} +{%- if purpose in ['pve', 'pbs', 'pmg'] -%} +{%- set proxmox_repos = proxmox_repos.append({ 'type': 'repo', - 'repo': 'deb ' + purpose_name_url + '/' + purpose_name + ' ' + ansible_distribution_release + ' ' + purpose_name + '-' + purpose_type, - 'filename': purpose_name + '-' + purpose_type, + 'repo': 'deb ' + purpose_repo + '/' + purpose + ' ' + ansible_distribution_release + ' ' + purpose + '-' + purpose_type, + 'filename': purpose + '-' + purpose_type, 'state': purpose_repo_state }) -%} -{%- elif purpose_name.startswith('ceph-') -%} -{%- set proxmox_package_setup = proxmox_package_setup.append({ +{%- if host_purpose_version and proxmox__purpose_packages[purpose] is defined -%} +{%- set package_name = proxmox__purpose_packages[purpose] if host_purpose_version == 'latest' else proxmox__purpose_packages[purpose] + '=' + host_purpose_version -%} +{%- set package_state = 'latest' if host_purpose_version == 'latest' else 'present' -%} +{%- set proxmox_packages = proxmox_packages.append({ + 'type': 'package', + 'name': package_name, + 'state': package_state +}) -%} +{%- endif -%} +{%- elif purpose.startswith('ceph-') -%} +{%- set proxmox_repos = proxmox_repos.append({ 'type': 'repo', - 'repo': 'deb ' + purpose_name_url + '/' + purpose_name + ' ' + ansible_distribution_release + ' ' + purpose_type, + 'repo': 'deb ' + purpose_repo + '/' + purpose + ' ' + ansible_distribution_release + ' ' + purpose_type, 'filename': 'ceph', 'state': purpose_repo_state }) -%} -{%- endif -%} +{%- endif -%} +{%- endfor -%} {%- endfor -%} {%- endif -%} -{{ proxmox_package_setup }} +{{ proxmox_repos + proxmox_packages }} diff --git a/roles/proxmox/vars/main.yml b/roles/proxmox/vars/main.yml deleted file mode 100644 index e137444..0000000 --- a/roles/proxmox/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -__proxmox__purposes: "{{ proxmox__purpose_names | product(proxmox__purpose_types) | map('join', proxmox__purpose_separator) | list }}"