Compare commits
26 Commits
main
...
daa5826573
| Author | SHA1 | Date | |
|---|---|---|---|
| daa5826573 | |||
| 0696218eb6 | |||
| 4789bbd850 | |||
| e5e4aa1832 | |||
| 74f567c8eb | |||
| 996e242180 | |||
| 9627a4a5e0 | |||
| e20776a571 | |||
| 130ad1dba5 | |||
| 6649426ca7 | |||
| c7418ad40f | |||
| b3771097c2 | |||
| 2e1f8d4146 | |||
| 14e09c7ee7 | |||
| e59f07fb6d | |||
| 6bef8b28e5 | |||
| eb7fd456e4 | |||
| 5f4f84ea15 | |||
| 8f4fee8b63 | |||
| 1d3bc59a8b | |||
| 49bd2f630b | |||
| 07b33028d1 | |||
| cc68a1f863 | |||
| 4897cdadbd | |||
| e0571783ba | |||
| c633ead7cc |
@@ -1,3 +1,20 @@
|
|||||||
# ansible_home
|
# ansible_home
|
||||||
|
|
||||||
My own home services automation repository
|
## How to use it?
|
||||||
|
|
||||||
|
It's better to use a virtual environment to avoid incompatibility issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.vmn.su/max/vmn-ansible
|
||||||
|
cd vmn-ansible
|
||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
ansible-galaxy collection install -r requirements.yaml
|
||||||
|
ansible-galaxy role install -r requirements.yaml
|
||||||
|
ansible-playbook playbooks/deploy_and_set_up_lxc_on_proxmox.yaml --vault-pass-file $VAULT_PASS_PATH -i inventory/localhost.yaml --private-key $SSH_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
**deploy** playbooks must be used with `inventory/localhost.yaml`, they need access to proxmox host only
|
||||||
|
|
||||||
|
There are also a systemd service and timer under `systemd` directory
|
||||||
|
|||||||
+19
-4
@@ -1,11 +1,18 @@
|
|||||||
---
|
---
|
||||||
all:
|
all:
|
||||||
children:
|
children:
|
||||||
|
this_host:
|
||||||
|
hosts:
|
||||||
|
localhost:
|
||||||
|
ansible_become_password: "{{ ansible_become_passwd }}"
|
||||||
|
ansible_connection: local
|
||||||
|
ansible_python_interpreter: "{{ ansible_playbook_python }}"
|
||||||
physical:
|
physical:
|
||||||
proxmox_1:
|
hosts:
|
||||||
ansible_host: "{{ proxmox_1_host }}"
|
proxmox_1:
|
||||||
ansible_become_password: "{{ ansible_become_passwd }}"
|
ansible_host: "{{ proxmox_1_host }}"
|
||||||
services_to_run: []
|
ansible_become_password: "{{ ansible_become_passwd }}"
|
||||||
|
services_to_run: []
|
||||||
virtual:
|
virtual:
|
||||||
hosts:
|
hosts:
|
||||||
localhost:
|
localhost:
|
||||||
@@ -39,3 +46,11 @@ all:
|
|||||||
ansible_become_password: "{{ ansible_become_passwd }}"
|
ansible_become_password: "{{ ansible_become_passwd }}"
|
||||||
services_to_run:
|
services_to_run:
|
||||||
- coder_ai
|
- coder_ai
|
||||||
|
web-servers:
|
||||||
|
hosts:
|
||||||
|
cert-proxy:
|
||||||
|
ansible_host: 192.168.0.47
|
||||||
|
ansible_become_password: "{{ ansible_become_passwd }}"
|
||||||
|
monitoring:
|
||||||
|
ansible_host: 192.168.0.49
|
||||||
|
ansible_become_password: "{{ ansible_become_passwd }}"
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
physical:
|
|
||||||
hosts:
|
|
||||||
localhost:
|
|
||||||
ansible_become_password: "{{ ansible_become_passwd }}"
|
|
||||||
ansible_connection: local
|
|
||||||
ansible_python_interpreter: "{{ ansible_playbook_python }}"
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy LXC
|
- name: Deploy LXC
|
||||||
hosts: all
|
hosts: localhost
|
||||||
remote_user: ansible
|
remote_user: ansible
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
@@ -10,50 +10,30 @@
|
|||||||
ansible_user_passwd_hash: "{{ ansible_password | password_hash('sha512', 's3edscrj45e6r') }}"
|
ansible_user_passwd_hash: "{{ ansible_password | password_hash('sha512', 's3edscrj45e6r') }}"
|
||||||
user_passwd_hash: "{{ user_password | password_hash('sha512', 's3ed6123jhgcr') }}"
|
user_passwd_hash: "{{ user_password | password_hash('sha512', 's3ed6123jhgcr') }}"
|
||||||
|
|
||||||
vars_prompt:
|
pre_tasks:
|
||||||
- name: lxc_hostname
|
- name: Include input and validation for hostname
|
||||||
prompt: "Hostname for the container (may only contain letters (a-z, A-Z), numbers (0-9), and hyphens)"
|
ansible.builtin.include_tasks: ../tasks/hostname_validation.yaml
|
||||||
private: false
|
|
||||||
|
- name: Include input and validation for password
|
||||||
|
ansible.builtin.include_tasks: ../tasks/password_validation.yaml
|
||||||
|
|
||||||
- name: lxc_root_password
|
- name: Include input and validation for IP
|
||||||
prompt: "Root password"
|
ansible.builtin.include_tasks: ../tasks/ip_validation.yaml
|
||||||
private: true
|
|
||||||
confirm: true
|
|
||||||
encrypt: sha512_crypt
|
|
||||||
|
|
||||||
- name: lxc_ip_address
|
- name: Include input and validation for LXC privilege
|
||||||
prompt: "IP address for the container (in x.x.x.x/x formant)"
|
ansible.builtin.include_tasks: ../tasks/lxc_privileged_validation.yaml
|
||||||
private: false
|
|
||||||
|
|
||||||
- name: lxc_is_privileged
|
- name: Include input and validation for human admin user creation
|
||||||
prompt: "Unprivileged LXC (true/false)?"
|
ansible.builtin.include_tasks: ../tasks/human_admin_user_validation.yaml
|
||||||
private: false
|
|
||||||
|
|
||||||
pre_tasks:
|
|
||||||
- name: Validate hostname
|
|
||||||
ansible.builtin.fail:
|
|
||||||
msg: |
|
|
||||||
Invalid hostname: "{{ lxc_hostname }}"
|
|
||||||
Hostname may only contain letters (a-z, A-Z), numbers (0-9), and hyphens (-).
|
|
||||||
when: not lxc_hostname is regex('^[a-zA-Z0-9-]+$')
|
|
||||||
run_once: true
|
|
||||||
|
|
||||||
roles:
|
roles:
|
||||||
- ../roles/deploy_lxc_on_proxmox
|
- ../roles/deploy_lxc_on_proxmox
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Make the prompted hostname available to the whole playbook
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
fact_lxc_hostname: "{{ lxc_hostname }}"
|
|
||||||
|
|
||||||
- name: Make the prompted IP available to the whole playbook
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
#fact_lxc_ip_address: "{{ lxc_ip_address | ansible.utils.ipaddr('address') }}"
|
|
||||||
fact_lxc_ip_address: "{{ lxc_ip_address.split('/') | first }}"
|
|
||||||
|
|
||||||
- name: Add the target host to the inventory
|
- name: Add the target host to the inventory
|
||||||
ansible.builtin.add_host:
|
ansible.builtin.add_host:
|
||||||
name: "{{ fact_lxc_ip_address }}"
|
name: "{{ lxc_ip_address }}"
|
||||||
groups: new_host
|
groups: new_host
|
||||||
ansible_user: ansible
|
ansible_user: ansible
|
||||||
ansible_become_password: "{{ ansible_become_passwd }}"
|
ansible_become_password: "{{ ansible_become_passwd }}"
|
||||||
@@ -73,7 +53,15 @@
|
|||||||
- ../roles/configure_ansible_user
|
- ../roles/configure_ansible_user
|
||||||
- ../roles/harden_ssh
|
- ../roles/harden_ssh
|
||||||
- ../roles/base_system
|
- ../roles/base_system
|
||||||
- ../roles/human_admin_user
|
|
||||||
# Update configs - omz, nvim, ranger and so on. Distribute the last version of those configs
|
tasks:
|
||||||
- ../roles/update_configs
|
- name: Include human admin user role
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: ../roles/human_admin_user
|
||||||
|
when: "hostvars['localhost']['lxc_human_admin_user'] | bool"
|
||||||
|
|
||||||
|
- name: Include update configs role
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: ../roles/update_configs
|
||||||
|
when: "hostvars['localhost']['lxc_human_admin_user'] | bool"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy VM
|
- name: Deploy VM
|
||||||
hosts: all
|
hosts: localhost
|
||||||
remote_user: ansible
|
remote_user: ansible
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
|
|
||||||
@@ -13,39 +13,95 @@
|
|||||||
vars_prompt:
|
vars_prompt:
|
||||||
- name: vm_hostname
|
- name: vm_hostname
|
||||||
prompt: "Hostname for the VM (may only contain letters (a-z, A-Z), numbers (0-9), and hyphens)"
|
prompt: "Hostname for the VM (may only contain letters (a-z, A-Z), numbers (0-9), and hyphens)"
|
||||||
|
default: "new-vm-test"
|
||||||
|
private: false
|
||||||
|
|
||||||
|
- name: vm_os_template
|
||||||
|
prompt: "Select OS template. Available: debian-13 | alpine"
|
||||||
|
default: "debian-13"
|
||||||
|
private: false
|
||||||
|
|
||||||
|
- name: vm_become_method
|
||||||
|
prompt: "Select become method. Available: sudo | su | pbrun | pfexec | doas | dzdo | ksu | runas | machinectl"
|
||||||
|
default: "sudo"
|
||||||
|
private: false
|
||||||
|
|
||||||
|
- name: vm_create_human_admin_user
|
||||||
|
prompt: "Create human admin user? (true/false)?"
|
||||||
|
default: true
|
||||||
private: false
|
private: false
|
||||||
|
|
||||||
- name: vm_ip_address
|
- name: vm_ip_address
|
||||||
prompt: "IP address for the VM (in x.x.x.x/x formant)"
|
prompt: "IP address for the VM (in x.x.x.x/x formant)"
|
||||||
|
default: "192.168.0.89/24"
|
||||||
private: false
|
private: false
|
||||||
|
|
||||||
pre_tasks:
|
pre_tasks:
|
||||||
- name: Validate hostname
|
- name: Validate hostname
|
||||||
ansible.builtin.fail:
|
ansible.builtin.fail:
|
||||||
msg: |
|
msg: |
|
||||||
Invalid hostname: "{{ vm_hostname }}"
|
Invalid hostname: "{{ lxc_hostname }}"
|
||||||
Hostname may only contain letters (a-z, A-Z), numbers (0-9), and hyphens (-).
|
Hostname must:
|
||||||
when: not vm_hostname is regex('^[a-zA-Z0-9-]+$')
|
- contain only letters (a-z, A-Z), digits (0-9), hyphens (-), and dots (.)
|
||||||
|
- not start or end with a hyphen or dot
|
||||||
|
- have each label between 1 and 63 characters
|
||||||
|
- not exceed 253 characters in total
|
||||||
|
when: >
|
||||||
|
lxc_hostname | length > 253 or
|
||||||
|
lxc_hostname is not regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$')
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Validate IP address
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: |
|
||||||
|
Invalid IP address: "{{ vm_ip_address }}"
|
||||||
|
IP address must be in x.x.x.x/x format (e.g., 192.168.0.89/24).
|
||||||
|
when: not vm_ip_address is regex('^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(\\/(3[0-2]|[12]?\\d))?$')
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Validate OS template
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: |
|
||||||
|
Invalid OS template: "{{ vm_os_template }}"
|
||||||
|
Select from available options: debian-13, alpine
|
||||||
|
when: vm_os_template not in ['debian-13', 'alpine']
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Validate become method
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: |
|
||||||
|
Invalid become method: "{{ vm_become_method }}"
|
||||||
|
Select from available options: sudo | su | pbrun | pfexec | doas | dzdo | ksu | runas | machinectl
|
||||||
|
when: vm_become_method not in ['sudo', 'su', 'pbrun', 'pfexec', 'doas', 'dzdo', 'ksu', 'runas', 'machinectl']
|
||||||
run_once: true
|
run_once: true
|
||||||
|
|
||||||
roles:
|
roles:
|
||||||
- ../roles/deploy_vm_on_proxmox
|
- ../roles/deploy_vm_on_proxmox
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Make the prompted hostname available to the whole playbook
|
- name: Make the hostname available to the whole playbook
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
fact_vm_hostname: "{{ vm_hostname }}"
|
fact_vm_hostname: "{{ vm_hostname }}"
|
||||||
|
|
||||||
- name: Make the prompted IP available to the whole playbook
|
- name: Make the OS template available to the whole playbook
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
fact_vm_os_template: "{{ vm_os_template }}"
|
||||||
|
|
||||||
|
- name: Make the IP available to the whole playbook
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
fact_vm_ip_address: "{{ vm_ip_address.split('/') | first }}"
|
fact_vm_ip_address: "{{ vm_ip_address.split('/') | first }}"
|
||||||
|
|
||||||
|
- name: Make the "human admin user" selection available to the whole playbook
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
fact_vm_create_human_admin_user: "{{ vm_create_human_admin_user }}"
|
||||||
|
|
||||||
- name: Add the target host to the inventory
|
- name: Add the target host to the inventory
|
||||||
ansible.builtin.add_host:
|
ansible.builtin.add_host:
|
||||||
name: "{{ fact_vm_ip_address }}"
|
name: "{{ fact_vm_ip_address }}"
|
||||||
groups: new_host
|
groups: new_host
|
||||||
ansible_user: ansible
|
ansible_user: ansible
|
||||||
ansible_become_password: "{{ ansible_become_passwd }}"
|
ansible_become_password: "{{ ansible_become_passwd }}"
|
||||||
|
ansible_become_method: "{{ vm_become_method }}"
|
||||||
|
|
||||||
|
|
||||||
- name: Configure VM
|
- name: Configure VM
|
||||||
@@ -62,7 +118,15 @@
|
|||||||
- ../roles/configure_ansible_user
|
- ../roles/configure_ansible_user
|
||||||
- ../roles/harden_ssh
|
- ../roles/harden_ssh
|
||||||
- ../roles/base_system
|
- ../roles/base_system
|
||||||
- ../roles/human_admin_user
|
|
||||||
# Update configs - omz, nvim, ranger and so on. Distribute the last version of those configs
|
tasks:
|
||||||
- ../roles/update_configs
|
- name: Include human admin user role
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: ../roles/human_admin_user
|
||||||
|
when: "hostvars['localhost']['fact_vm_create_human_admin_user'] | bool"
|
||||||
|
|
||||||
|
- name: Include update configs role
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: ../roles/update_configs
|
||||||
|
when: "hostvars['localhost']['fact_vm_create_human_admin_user'] | bool"
|
||||||
|
|
||||||
|
|||||||
+28
-1
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy and set up an LXC container in Proxmox
|
- name: Perform regular maintenance
|
||||||
hosts: virtual
|
hosts: virtual
|
||||||
remote_user: ansible
|
remote_user: ansible
|
||||||
|
|
||||||
@@ -20,3 +20,30 @@
|
|||||||
- ../roles/update_configs
|
- ../roles/update_configs
|
||||||
# Make sure that the services that are supposed to run are running
|
# Make sure that the services that are supposed to run are running
|
||||||
- ../roles/check_services
|
- ../roles/check_services
|
||||||
|
|
||||||
|
- name: Ensure all web-servers have fresh TLS certificates
|
||||||
|
hosts: web-servers
|
||||||
|
remote_user: ansible
|
||||||
|
|
||||||
|
vars_files:
|
||||||
|
../inventory/group_vars/all/secrets.yaml
|
||||||
|
|
||||||
|
roles:
|
||||||
|
# Pull certificates from cert_proxy and distribute them among the servers
|
||||||
|
- ../roles/update_certificates
|
||||||
|
|
||||||
|
post_tasks:
|
||||||
|
- name: Delete certificates from cert-proxy
|
||||||
|
ansible.builtin.file:
|
||||||
|
name: /tmp/letsencrypt
|
||||||
|
state: absent
|
||||||
|
when: "inventory_hostname == 'cert-proxy'"
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Delete certificates from ansible-server
|
||||||
|
ansible.builtin.file:
|
||||||
|
name: /tmp/letsencrypt
|
||||||
|
state: absent
|
||||||
|
run_once: true
|
||||||
|
delegate_to: 127.0.0.1
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
become: true
|
become: true
|
||||||
remote_user: ansible
|
remote_user: ansible
|
||||||
|
|
||||||
- name: Install qemu-guest-agent on VM
|
- name: Install qemu-guest-agent on VM with apt-based systems
|
||||||
when:
|
when:
|
||||||
- ansible_facts['os_family'] == "Debian"
|
- ansible_facts['os_family'] == "Debian"
|
||||||
- ansible_facts['virtualization_type'] == "kvm"
|
- ansible_facts['virtualization_type'] == "kvm"
|
||||||
@@ -29,3 +29,25 @@
|
|||||||
name: "{{ alpine_packages }}"
|
name: "{{ alpine_packages }}"
|
||||||
update_cache: true
|
update_cache: true
|
||||||
remote_user: ansible
|
remote_user: ansible
|
||||||
|
|
||||||
|
- name: Install qemu-guest-agent on VM with Alpine
|
||||||
|
when:
|
||||||
|
- ansible_facts['distribution'] == "Alpine"
|
||||||
|
- ansible_facts['virtualization_type'] == "kvm"
|
||||||
|
community.general.apk:
|
||||||
|
name: qemu-guest-agent
|
||||||
|
update_cache: true
|
||||||
|
become: true
|
||||||
|
remote_user: ansible
|
||||||
|
tags:
|
||||||
|
- kvm-guests
|
||||||
|
- packages
|
||||||
|
|
||||||
|
|
||||||
|
- name: Enable qemu-guest-agent
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: qemu-guest-agent
|
||||||
|
enabled: yes
|
||||||
|
state: started
|
||||||
|
when: ansible_facts['virtualization_type'] == "kvm"
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,63 @@
|
|||||||
---
|
---
|
||||||
- name: Generate locales
|
- name: Generate locales for Debian-based systems
|
||||||
community.general.locale_gen:
|
community.general.locale_gen:
|
||||||
name:
|
name:
|
||||||
- en_US.UTF-8
|
- en_US.UTF-8
|
||||||
- ru_RU.UTF-8
|
- ru_RU.UTF-8
|
||||||
state: present
|
state: present
|
||||||
|
when: (ansible_facts['distribution'] == "Debian") or
|
||||||
|
(ansible_facts['distribution'] == "Ubuntu")
|
||||||
|
|
||||||
- name: Set locale
|
- name: Set locale for Debian-based systems
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: /etc/locale.conf
|
dest: /etc/locale.conf
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
content: |
|
content: |
|
||||||
LANG=en_US.UTF-8
|
LANG=en_US.UTF-8
|
||||||
LC_ALL=en_US.UTF-8
|
LC_ALL=en_US.UTF-8
|
||||||
|
when: (ansible_facts['distribution'] == "Debian") or
|
||||||
|
(ansible_facts['distribution'] == "Ubuntu")
|
||||||
|
|
||||||
|
# The same for Alpine
|
||||||
|
- name: Install musl-locales package for Alpine
|
||||||
|
community.general.apk:
|
||||||
|
name: musl-locales
|
||||||
|
state: present
|
||||||
|
when: ansible_facts['distribution'] == "Alpine"
|
||||||
|
|
||||||
|
- name: Copy default locale profile to a custom override file for Alpine
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: /etc/profile.d/20locale.sh
|
||||||
|
dest: /etc/profile.d/locale.sh
|
||||||
|
remote_src: true
|
||||||
|
mode: '0644'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
force: false # Do not overwrite if the file already exists
|
||||||
|
when: ansible_facts['distribution'] == "Alpine"
|
||||||
|
|
||||||
|
- name: Set LANG and LC_ALL in the custom locale file for Alpine
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/profile.d/locale.sh
|
||||||
|
regexp: "{{ item.regexp }}"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
state: present
|
||||||
|
loop:
|
||||||
|
- { regexp: '^export LANG=', line: 'export LANG=en_US.UTF-8' }
|
||||||
|
- { regexp: '^export LC_ALL=', line: 'export LC_ALL=en_US.UTF-8' }
|
||||||
|
when: ansible_facts['distribution'] == "Alpine"
|
||||||
|
|
||||||
|
- name: Ensure Unicode is enabled in /etc/rc.conf for Alpine
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/rc.conf
|
||||||
|
regexp: '^#?unicode='
|
||||||
|
line: 'unicode="YES"'
|
||||||
|
state: present
|
||||||
|
when: ansible_facts['distribution'] == "Alpine"
|
||||||
|
|
||||||
|
|
||||||
|
# Works fine for all
|
||||||
- name: Set time
|
- name: Set time
|
||||||
community.general.timezone:
|
community.general.timezone:
|
||||||
name: Europe/Samara
|
name: Europe/Samara
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ alpine_packages:
|
|||||||
- git
|
- git
|
||||||
- kitty
|
- kitty
|
||||||
- neovim
|
- neovim
|
||||||
|
- openssh
|
||||||
- openssh-server
|
- openssh-server
|
||||||
- ranger
|
- ranger
|
||||||
- rsync
|
- rsync
|
||||||
- ssh
|
- tzdata
|
||||||
- zsh
|
- zsh
|
||||||
|
|||||||
@@ -8,14 +8,35 @@
|
|||||||
- sudo
|
- sudo
|
||||||
update-cache: true
|
update-cache: true
|
||||||
|
|
||||||
|
- name: Ensure /etc/resolv.conf exists with DNS servers
|
||||||
|
when: (ansible_facts['distribution'] == "Alpine")
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/resolv.conf
|
||||||
|
content: |
|
||||||
|
nameserver 192.168.0.1
|
||||||
|
nameserver 8.8.8.8
|
||||||
|
mode: '0644'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
become: true
|
||||||
|
|
||||||
# The same commands for Alpine
|
# The same commands for Alpine
|
||||||
- name: Update and install packages on Alpine
|
- name: Install sudo on Alpine
|
||||||
when: (ansible_facts['distribution'] == "Alpine")
|
when: (ansible_facts['distribution'] == "Alpine")
|
||||||
community.general.apk:
|
community.general.apk:
|
||||||
name: sudo
|
name: sudo
|
||||||
update_cache: true
|
update_cache: true
|
||||||
remote_user: ansible
|
remote_user: ansible
|
||||||
|
|
||||||
|
- name: Enable sudo for root and human_admin_user
|
||||||
|
when: (ansible_facts['distribution'] == "Alpine")
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/sudoers.d/80-ansible-created-users
|
||||||
|
content: |
|
||||||
|
root ALL=(ALL) ALL:ALL
|
||||||
|
{{ human_admin_user }} ALL=(ALL) ALL:ALL
|
||||||
|
remote_user: ansible
|
||||||
|
|
||||||
|
|
||||||
## Creating and setting up the ansible user
|
## Creating and setting up the ansible user
|
||||||
## First, create sshusers group to grant ssh access
|
## First, create sshusers group to grant ssh access
|
||||||
@@ -24,6 +45,12 @@
|
|||||||
name: sshusers
|
name: sshusers
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: Ensure group "sudo" exists
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: sudo
|
||||||
|
state: present
|
||||||
|
|
||||||
|
|
||||||
## Add the user to sshusers (for ssh access) and sudo (gain root access)
|
## Add the user to sshusers (for ssh access) and sudo (gain root access)
|
||||||
- name: Create a new user with a password for Ansible
|
- name: Create a new user with a password for Ansible
|
||||||
ansible.builtin.user:
|
ansible.builtin.user:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
password: "{{ lxc_root_password }}"
|
password: "{{ lxc_root_password }}"
|
||||||
hostname: "{{ lxc_hostname }}"
|
hostname: "{{ lxc_hostname }}"
|
||||||
ostemplate: 'main:vztmpl/debian-13-golden-image.tar.gz'
|
ostemplate: 'main:vztmpl/debian-13-golden-image.tar.gz'
|
||||||
unprivileged: "{{ lxc_is_privileged }}"
|
unprivileged: "{{ lxc_privileged }}"
|
||||||
memory: 2048
|
memory: 2048
|
||||||
cores: 2
|
cores: 2
|
||||||
state: present
|
state: present
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
storage: local
|
storage: local
|
||||||
pubkey: "{{ lookup('file', lookup('env', 'HOME') + '/.ssh/ansible_key.pub') }}"
|
pubkey: "{{ lookup('file', lookup('env', 'HOME') + '/.ssh/ansible_key.pub') }}"
|
||||||
netif:
|
netif:
|
||||||
net0: "name=eth0,gw=192.168.0.1,ip={{ lxc_ip_address }},bridge=vmbr0"
|
net0: "name=eth0,gw=192.168.0.1,ip={{ lxc_ip_address }}/{{ lxc_netmask }},bridge=vmbr0"
|
||||||
|
|
||||||
- name: Make sure the container has started
|
- name: Make sure the container has started
|
||||||
become: false
|
become: false
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
api_token_id: ansible
|
api_token_id: ansible
|
||||||
api_token_secret: "{{ proxmox_token_secret }}"
|
api_token_secret: "{{ proxmox_token_secret }}"
|
||||||
|
|
||||||
clone: debian-13-cloud-init-template
|
clone: "{{ vm_os_template }}-cloud-init-template"
|
||||||
name: "{{ vm_hostname }}"
|
name: "{{ vm_hostname }}"
|
||||||
storage: main
|
storage: main
|
||||||
format: qcow2
|
format: qcow2
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
FILE=/home/$USER/.oh-my-zsh/oh-my-zsh.sh
|
TARGET_USER=$1
|
||||||
|
|
||||||
|
FILE=/home/$TARGET_USER/.oh-my-zsh/oh-my-zsh.sh
|
||||||
if [ -f "$FILE" ]; then
|
if [ -f "$FILE" ]; then
|
||||||
echo "$FILE exists and we not installing ohmyzsh"
|
echo "$FILE exists and we not installing ohmyzsh"
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
echo "$FILE does not exist and we install ohmyzsh"
|
echo "$FILE does not exist and we install ohmyzsh"
|
||||||
cd /home/max
|
cd /home/$TARGET_USER
|
||||||
wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
|
sudo -u $TARGET_USER wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
|
||||||
chmod +x /home/max/install.sh
|
chmod +x /home/$TARGET_USER/install.sh
|
||||||
timeout -s 15 -k 30s 20s /home/max/install.sh --unattendend --keep-zshrc || exit 0
|
sudo -u $TARGET_USER timeout -s 15 -k 30s 20s /home/$TARGET_USER/install.sh --unattendend --keep-zshrc || exit 0
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# PUT NEW BLOCKS STRICTLY BEFORE ANY MATCH BLOCK
|
||||||
|
|
||||||
|
|
||||||
|
# Private keys are NOT distributed via Ansible
|
||||||
|
# They must to be copied manually ONLY to those hosts that need access to others
|
||||||
|
|
||||||
|
# Most of the home major LAN hosts should be accessible with this key
|
||||||
|
Match Host 192.168.0.?
|
||||||
|
User max
|
||||||
|
IdentityFile ~/.ssh/max_regular_key
|
||||||
|
|
||||||
|
# This virtual network has the same hosts range
|
||||||
|
Match Host 10.9.2.?
|
||||||
|
User max
|
||||||
|
IdentityFile ~/.ssh/max_regular_key
|
||||||
@@ -25,10 +25,9 @@
|
|||||||
|
|
||||||
# WARNING: UNPRIVILEGED USER (not ansible) COMMANDS
|
# WARNING: UNPRIVILEGED USER (not ansible) COMMANDS
|
||||||
- name: Install oh my zsh
|
- name: Install oh my zsh
|
||||||
remote_user: "{{ human_admin_user }}"
|
remote_user: ansible
|
||||||
ansible.builtin.command: "/home/{{ human_admin_user }}/install_omz.sh"
|
ansible.builtin.command: "/home/{{ human_admin_user }}/install_omz.sh {{ human_admin_user }}"
|
||||||
changed_when: true
|
changed_when: true
|
||||||
vars:
|
|
||||||
|
|
||||||
- name: Configure oh my zsh, by pushing the config file
|
- name: Configure oh my zsh, by pushing the config file
|
||||||
remote_user: ansible
|
remote_user: ansible
|
||||||
@@ -48,6 +47,15 @@
|
|||||||
group: "{{ human_admin_user }}"
|
group: "{{ human_admin_user }}"
|
||||||
mode: u=rw,g=r,o-rwx
|
mode: u=rw,g=r,o-rwx
|
||||||
|
|
||||||
|
- name: Configure ssh client, by pushing the config
|
||||||
|
remote_user: ansible
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ role_path }}/files/ssh_config"
|
||||||
|
dest: "/home/{{ human_admin_user }}/.ssh/config"
|
||||||
|
owner: "{{ human_admin_user }}"
|
||||||
|
group: "{{ human_admin_user }}"
|
||||||
|
mode: u=rw,g=r,o-rwx
|
||||||
|
|
||||||
|
|
||||||
# WARNING: we've finished with the initial setup, drop ansible key
|
# WARNING: we've finished with the initial setup, drop ansible key
|
||||||
# Push regular user key
|
# Push regular user key
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
- name: Pull directory from remote host to control node
|
||||||
|
ansible.posix.synchronize:
|
||||||
|
mode: pull
|
||||||
|
src: /tmp/letsencrypt
|
||||||
|
dest: /tmp
|
||||||
|
recursive: true
|
||||||
|
delete: true
|
||||||
|
when: "inventory_hostname == 'cert-proxy'"
|
||||||
|
|
||||||
|
- name: Push loki.vmn.su certificate to the target
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: /tmp/letsencrypt/live/loki.vmn.su/fullchain.pem
|
||||||
|
dest: /etc/nginx/certs/loki_fullchain.pem
|
||||||
|
local_follow: true
|
||||||
|
when: "inventory_hostname == 'monitoring'"
|
||||||
|
|
||||||
|
- name: Push loki.vmn.su key to the target
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: /tmp/letsencrypt/live/loki.vmn.su/privkey.pem
|
||||||
|
dest: /etc/nginx/certs/loki_privkey.pem
|
||||||
|
local_follow: true
|
||||||
|
when: "inventory_hostname == 'monitoring'"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Set default hostname
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_hostname: "new-lxc-test"
|
||||||
|
|
||||||
|
- name: Prompt for hostname with immediate validation
|
||||||
|
ansible.builtin.pause:
|
||||||
|
prompt: "Enter hostname (letters, digits, hyphens, dots; max 253 chars) [new-lxc-test]"
|
||||||
|
register: hostname_result
|
||||||
|
until: >
|
||||||
|
hostname_result.user_input | length == 0 or
|
||||||
|
(
|
||||||
|
hostname_result.user_input | length <= 253 and
|
||||||
|
hostname_result.user_input is regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$')
|
||||||
|
)
|
||||||
|
retries: 2
|
||||||
|
delay: 0
|
||||||
|
|
||||||
|
- name: Use user input only if provided, otherwise keep the default
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_hostname: "{{ hostname_result.user_input }}"
|
||||||
|
when: hostname_result.user_input | length > 0
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Set default human admin user
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_human_admin_user: true
|
||||||
|
|
||||||
|
- name: Prompt for human admin user creation with immediate validation
|
||||||
|
ansible.builtin.pause:
|
||||||
|
prompt: "Create human admin user? (true/false) [true]"
|
||||||
|
register: human_admin_user_result
|
||||||
|
until: >
|
||||||
|
human_admin_user_result.user_input | length == 0 or
|
||||||
|
(
|
||||||
|
human_admin_user_result.user_input in ['true', 'false']
|
||||||
|
)
|
||||||
|
retries: 2
|
||||||
|
delay: 0
|
||||||
|
|
||||||
|
- name: Use user input only if provided, otherwise keep the default
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_human_admin_user: "{{ human_admin_user_result.user_input }}"
|
||||||
|
when: human_admin_user_result.user_input | length > 0
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Set default ip
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_ip_address: "192.168.0.90"
|
||||||
|
|
||||||
|
- name: Set default network mask
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_netmask: "24"
|
||||||
|
|
||||||
|
- name: Prompt for IP with immediate validation
|
||||||
|
ansible.builtin.pause:
|
||||||
|
prompt: "Enter ip in x.x.x.x/x format [192.168.0.90/24]"
|
||||||
|
register: ip_result
|
||||||
|
until: >
|
||||||
|
ip_result.user_input | length == 0 or
|
||||||
|
ip_result.user_input is regex('^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)/(3[0-2]|[12]?\\d)$')
|
||||||
|
retries: 2
|
||||||
|
delay: 0
|
||||||
|
|
||||||
|
- name: Use user input only if provided, otherwise keep the default (IP)
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_ip_address: "{{ ip_result.user_input.split('/') | first }}"
|
||||||
|
when: ip_result.user_input | length > 0
|
||||||
|
|
||||||
|
- name: Use user input only if provided, otherwise keep the default (netmask)
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_netmask: "{{ ip_result.user_input.split('/') | second }}"
|
||||||
|
when: ip_result.user_input | length > 0
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Set default privileged
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_privileged: false
|
||||||
|
|
||||||
|
- name: Prompt for privileged with immediate validation
|
||||||
|
ansible.builtin.pause:
|
||||||
|
prompt: "Privileged LXC? (true/false) [false]"
|
||||||
|
register: privileged_result
|
||||||
|
until: >
|
||||||
|
privileged_result.user_input | length == 0 or
|
||||||
|
(
|
||||||
|
privileged_result.user_input in ['true', 'false']
|
||||||
|
)
|
||||||
|
retries: 2
|
||||||
|
delay: 0
|
||||||
|
|
||||||
|
- name: Use user input only if provided, otherwise keep the default
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_privileged: "{{ privileged_result.user_input }}"
|
||||||
|
when: privileged_result.user_input | length > 0
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Set default password
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_root_password: "new-lxc-test"
|
||||||
|
|
||||||
|
- name: Prompt for password
|
||||||
|
ansible.builtin.pause:
|
||||||
|
prompt: "Enter password (letters, digits, hyphens, dots; max 253 chars) [new-lxc-test]"
|
||||||
|
echo: false
|
||||||
|
register: password_result_1
|
||||||
|
until: >
|
||||||
|
password_result_1.user_input | length == 0 or
|
||||||
|
(
|
||||||
|
password_result_1.user_input | length <= 253 and
|
||||||
|
password_result_1.user_input is regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$')
|
||||||
|
)
|
||||||
|
retries: 3
|
||||||
|
delay: 0
|
||||||
|
|
||||||
|
- name: Prompt for password (confirmation)
|
||||||
|
ansible.builtin.pause:
|
||||||
|
prompt: "Confirm your new password"
|
||||||
|
echo: no
|
||||||
|
register: password_result_2
|
||||||
|
|
||||||
|
- name: Ensure passwords match
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- password_result_1.user_input == password_result_2.user_input
|
||||||
|
fail_msg: "Passwords do not match!"
|
||||||
|
success_msg: "Password confirmed."
|
||||||
|
|
||||||
|
- name: Use user input only if provided, otherwise keep the default
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
lxc_root_password: "{{ password_result_1.user_input }}"
|
||||||
|
when: password_result_1.user_input | length > 0
|
||||||
|
|
||||||
Reference in New Issue
Block a user