Archive Legacy Setup And Add Ansible Control Plane
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
bash_config_file: /etc/profile.d/homelab.sh
|
||||
bash_config_bashrc: /etc/bash.bashrc
|
||||
bash_config_legacy_user: ada
|
||||
bash_config_legacy_home: "/home/{{ bash_config_legacy_user }}"
|
||||
bash_config_legacy_profile_files:
|
||||
- /etc/profile.d/custom_bash.sh
|
||||
@@ -0,0 +1,63 @@
|
||||
- name: Install unified HomeLab bash config
|
||||
ansible.builtin.template:
|
||||
src: homelab.sh.j2
|
||||
dest: "{{ bash_config_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Ensure system bashrc sources managed config
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ bash_config_bashrc }}"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
marker: "# {mark} ANSIBLE MANAGED HOMELAB BASH CONFIG"
|
||||
block: |
|
||||
if [ -f /etc/profile.d/homelab.sh ]; then
|
||||
. /etc/profile.d/homelab.sh
|
||||
fi
|
||||
|
||||
- name: List bash users with local home directories
|
||||
ansible.builtin.shell: >-
|
||||
getent passwd | awk -F: '$7 ~ /\/bash$/ && $6 ~ /^\// { print $1 ":" $6 }'
|
||||
register: bash_config_bash_users_raw
|
||||
changed_when: false
|
||||
|
||||
- name: Ensure bash users source managed system config
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ item.split(':')[1] }}/.bashrc"
|
||||
create: true
|
||||
owner: "{{ item.split(':')[0] }}"
|
||||
group: "{{ item.split(':')[0] }}"
|
||||
mode: "0644"
|
||||
marker: "# {mark} ANSIBLE MANAGED HOMELAB SYSTEM BASH CONFIG"
|
||||
block: |
|
||||
if [ -f /etc/profile.d/homelab.sh ]; then
|
||||
. /etc/profile.d/homelab.sh
|
||||
fi
|
||||
loop: "{{ bash_config_bash_users_raw.stdout_lines }}"
|
||||
|
||||
- name: Check legacy per-user bashrc
|
||||
ansible.builtin.stat:
|
||||
path: "{{ bash_config_legacy_home }}/.bashrc"
|
||||
register: bash_config_legacy_bashrc
|
||||
|
||||
- name: Remove legacy per-user bashrc source block
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ bash_config_legacy_home }}/.bashrc"
|
||||
marker: "# {mark} ANSIBLE MANAGED HOMELAB BASH CONFIG"
|
||||
state: absent
|
||||
when: bash_config_legacy_bashrc.stat.exists
|
||||
|
||||
- name: Remove legacy per-user bash config file
|
||||
ansible.builtin.file:
|
||||
path: "{{ bash_config_legacy_home }}/.bashrc.d/homelab.sh"
|
||||
state: absent
|
||||
|
||||
- name: Remove legacy profile scripts that conflict with unified config
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ bash_config_legacy_profile_files }}"
|
||||
@@ -0,0 +1,44 @@
|
||||
# Managed by Ansible: HomeLab unified bash config.
|
||||
|
||||
export EDITOR="${EDITOR:-vim}"
|
||||
export PAGER="${PAGER:-less}"
|
||||
export LESS="${LESS:--FRSX}"
|
||||
export HISTCONTROL="ignoreboth:erasedups"
|
||||
export HISTSIZE=50000
|
||||
export HISTFILESIZE=100000
|
||||
shopt -s histappend 2>/dev/null || true
|
||||
shopt -s checkwinsize 2>/dev/null || true
|
||||
|
||||
case ":$PATH:" in
|
||||
*":$HOME/.local/bin:"*) ;;
|
||||
*) export PATH="$HOME/.local/bin:$PATH" ;;
|
||||
esac
|
||||
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias l='ls -CF'
|
||||
alias grep='grep --color=auto'
|
||||
alias ip='ip --color=auto'
|
||||
alias df='df -h'
|
||||
alias du='du -h'
|
||||
alias free='free -h'
|
||||
alias ..='cd ..'
|
||||
alias ...='cd ../..'
|
||||
alias please='sudo $(history -p !!)'
|
||||
|
||||
alias gs='git status --short --branch'
|
||||
alias gd='git diff'
|
||||
alias gl='git log --oneline --decorate -10'
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
alias sctl='systemctl'
|
||||
alias jctl='journalctl'
|
||||
fi
|
||||
|
||||
if [ -n "$PS1" ]; then
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
PS1='\[\e[31m\]\u@\h\[\e[0m\]:\[\e[34m\]\w\[\e[0m\]# '
|
||||
else
|
||||
PS1='\[\e[32m\]\u@\h\[\e[0m\]:\[\e[34m\]\w\[\e[0m\]\$ '
|
||||
fi
|
||||
fi
|
||||
@@ -1,5 +0,0 @@
|
||||
bootstrap_user: ada
|
||||
bootstrap_ssh_public_keys: [ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIWNsUQmppB6cQccXX1ZaBbFIcmM6RmghsTVbG9TgoZB ada@ada-x1]
|
||||
docker_registry_mirrors:
|
||||
- https://mirror.gcr.io
|
||||
- https://dockerhub.timeweb.cloud
|
||||
@@ -1,4 +0,0 @@
|
||||
- name: restart sshd
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
@@ -1,85 +0,0 @@
|
||||
- name: Ensure user exists
|
||||
ansible.builtin.user:
|
||||
name: "{{ bootstrap_user }}"
|
||||
shell: /bin/bash
|
||||
groups: sudo
|
||||
append: true
|
||||
create_home: true
|
||||
state: present
|
||||
|
||||
# docker group создаётся только после установки Docker.
|
||||
# Добавление происходит в роли docker, не здесь.
|
||||
|
||||
- name: Add SSH authorized keys
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ bootstrap_user }}"
|
||||
key: "{{ item }}"
|
||||
state: present
|
||||
loop: "{{ bootstrap_ssh_public_keys }}"
|
||||
when: bootstrap_ssh_public_keys | length > 0
|
||||
|
||||
- name: Gather service facts
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Ensure Docker config directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/docker
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure Docker registry mirrors
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/docker/daemon.json
|
||||
content: "{{ {'registry-mirrors': docker_registry_mirrors} | to_nice_json }}\n"
|
||||
mode: "0644"
|
||||
register: docker_daemon_config
|
||||
when: docker_registry_mirrors | length > 0
|
||||
|
||||
- name: Restart Docker after daemon config change
|
||||
ansible.builtin.service:
|
||||
name: docker
|
||||
state: restarted
|
||||
when:
|
||||
- docker_daemon_config is changed
|
||||
- "'docker.service' in ansible_facts.services"
|
||||
|
||||
# Команды, которые реально нужны без пароля:
|
||||
# apt / apt-get — обновление пакетов
|
||||
# systemctl — управление сервисами
|
||||
# ufw — фаервол
|
||||
# resticprofile — установка systemd-таймеров бэкапа
|
||||
#
|
||||
# Ansible become НЕ входит в этот список намеренно:
|
||||
# для плейбуков запускать с -K. Это сознательный компромисс
|
||||
# между удобством и тем, чтобы не давать python3 NOPASSWD (= root).
|
||||
- name: Configure sudoers — NOPASSWD for specific commands
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/sudoers.d/{{ bootstrap_user }}
|
||||
content: |
|
||||
{{ bootstrap_user }} ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get
|
||||
{{ bootstrap_user }} ALL=(ALL) NOPASSWD: /usr/bin/systemctl
|
||||
{{ bootstrap_user }} ALL=(ALL) NOPASSWD: /usr/bin/ufw
|
||||
{{ bootstrap_user }} ALL=(ALL) NOPASSWD: /usr/local/bin/resticprofile
|
||||
validate: /usr/sbin/visudo -cf %s
|
||||
mode: "0440"
|
||||
|
||||
# Drop-in конфиг — не трогаем основной sshd_config.
|
||||
# Требует Debian 12+ (OpenSSH 8.9+).
|
||||
- name: Harden SSH
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/ssh/sshd_config.d/99-hardening.conf
|
||||
content: |
|
||||
PasswordAuthentication no
|
||||
PermitRootLogin no
|
||||
PubkeyAuthentication yes
|
||||
mode: "0644"
|
||||
notify: restart sshd
|
||||
|
||||
- name: Ensure sshd_config.d is included
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
line: Include /etc/ssh/sshd_config.d/*.conf
|
||||
state: present
|
||||
insertbefore: BOF
|
||||
validate: /usr/sbin/sshd -t -f %s
|
||||
notify: restart sshd
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
openvpn_interface: tun0
|
||||
openvpn_listen_port: 8443
|
||||
openvpn_proto_server: tcp-server
|
||||
openvpn_proto_client: tcp-client
|
||||
openvpn_config_dir: /etc/openvpn/homelab
|
||||
openvpn_config_path: /etc/openvpn/homelab/homelab.conf
|
||||
openvpn_static_key_path: /etc/openvpn/homelab/static.key
|
||||
openvpn_service_name: homelab-openvpn
|
||||
openvpn_home_routes:
|
||||
- network: 192.168.1.5
|
||||
netmask: 255.255.255.255
|
||||
- network: 192.168.1.6
|
||||
netmask: 255.255.255.254
|
||||
- network: 192.168.1.8
|
||||
netmask: 255.255.255.248
|
||||
- network: 192.168.1.16
|
||||
netmask: 255.255.255.240
|
||||
- network: 192.168.1.32
|
||||
netmask: 255.255.255.248
|
||||
- network: 192.168.1.40
|
||||
netmask: 255.255.255.255
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: restart openvpn
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ openvpn_service_name }}"
|
||||
state: restarted
|
||||
enabled: true
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
- name: Install OpenVPN packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- openvpn
|
||||
- iptables
|
||||
- iproute2
|
||||
- netcat-openbsd
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Ensure OpenVPN config directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ openvpn_config_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: Generate static key on server if missing
|
||||
ansible.builtin.command: "openvpn --genkey secret {{ openvpn_static_key_path }}"
|
||||
args:
|
||||
creates: "{{ openvpn_static_key_path }}"
|
||||
when: openvpn_role == 'server'
|
||||
no_log: true
|
||||
|
||||
- name: Read static key from server
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ openvpn_static_key_path }}"
|
||||
register: openvpn_static_key_raw
|
||||
when: openvpn_role == 'server'
|
||||
no_log: true
|
||||
|
||||
- name: Copy static key to all peers
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ openvpn_static_key_path }}"
|
||||
content: "{{ hostvars['ru-vps'].openvpn_static_key_raw.content | b64decode }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
notify: restart openvpn
|
||||
|
||||
- name: Enable IPv4 forwarding on gateway
|
||||
ansible.posix.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: "1"
|
||||
state: present
|
||||
reload: true
|
||||
when: openvpn_role == 'gateway'
|
||||
|
||||
- name: Allow OpenVPN TCP in UFW on server
|
||||
ansible.builtin.command: "ufw allow {{ openvpn_listen_port }}/tcp"
|
||||
register: ufw_allow_openvpn
|
||||
changed_when: "'Rule added' in ufw_allow_openvpn.stdout or 'Rules updated' in ufw_allow_openvpn.stdout"
|
||||
failed_when: false
|
||||
when: openvpn_role == 'server'
|
||||
|
||||
- name: Render OpenVPN config
|
||||
ansible.builtin.template:
|
||||
src: homelab.conf.j2
|
||||
dest: "{{ openvpn_config_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
notify: restart openvpn
|
||||
|
||||
- name: Render OpenVPN gateway up script
|
||||
ansible.builtin.template:
|
||||
src: up.sh.j2
|
||||
dest: "{{ openvpn_config_dir }}/up.sh"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
notify: restart openvpn
|
||||
when: openvpn_role == 'gateway'
|
||||
|
||||
- name: Render OpenVPN gateway down script
|
||||
ansible.builtin.template:
|
||||
src: down.sh.j2
|
||||
dest: "{{ openvpn_config_dir }}/down.sh"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
notify: restart openvpn
|
||||
when: openvpn_role == 'gateway'
|
||||
|
||||
- name: Render OpenVPN systemd service
|
||||
ansible.builtin.template:
|
||||
src: homelab-openvpn.service.j2
|
||||
dest: "/etc/systemd/system/{{ openvpn_service_name }}.service"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: restart openvpn
|
||||
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Enable and start OpenVPN
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ openvpn_service_name }}"
|
||||
state: started
|
||||
enabled: true
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
iptables -t nat -D POSTROUTING -s {{ openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o eth0 -j MASQUERADE 2>/dev/null || true
|
||||
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=HomeLab OpenVPN {{ openvpn_role }}
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/sbin/openvpn --config {{ openvpn_config_path }}
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,26 @@
|
||||
dev {{ openvpn_interface }}
|
||||
ifconfig {{ openvpn_local_ip }} {{ openvpn_peer_ip }}
|
||||
secret {{ openvpn_static_key_path }}
|
||||
cipher AES-256-CBC
|
||||
auth SHA256
|
||||
persist-key
|
||||
persist-tun
|
||||
verb 3
|
||||
keepalive 10 60
|
||||
{% if openvpn_role == 'server' %}
|
||||
proto {{ openvpn_proto_server }}
|
||||
port {{ openvpn_listen_port }}
|
||||
{% for route in openvpn_home_routes %}
|
||||
route {{ route.network }} {{ route.netmask }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if openvpn_role == 'gateway' %}
|
||||
proto {{ openvpn_proto_client }}
|
||||
remote 157.22.231.198 {{ openvpn_listen_port }}
|
||||
resolv-retry infinite
|
||||
nobind
|
||||
route-nopull
|
||||
script-security 2
|
||||
up /etc/openvpn/homelab/up.sh
|
||||
down /etc/openvpn/homelab/down.sh
|
||||
{% endif %}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
iptables -t nat -C POSTROUTING -s {{ openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o eth0 -j MASQUERADE 2>/dev/null || \
|
||||
iptables -t nat -A POSTROUTING -s {{ openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o eth0 -j MASQUERADE
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
pve_lxc_vmid: 132
|
||||
pve_lxc_node: mini-pc
|
||||
pve_lxc_hostname: wg-mini
|
||||
pve_lxc_ip: 192.168.1.23/24
|
||||
pve_lxc_gateway: 192.168.1.1
|
||||
pve_lxc_bridge: vmbr0
|
||||
pve_lxc_storage: local-lvm
|
||||
pve_lxc_disk: local-lvm:8
|
||||
pve_lxc_cores: 1
|
||||
pve_lxc_memory: 256
|
||||
pve_lxc_swap: 128
|
||||
pve_lxc_onboot: true
|
||||
pve_lxc_startup: order=30
|
||||
pve_lxc_unprivileged: true
|
||||
pve_lxc_features:
|
||||
- nesting=1
|
||||
pve_lxc_nameserver: 1.1.1.1
|
||||
pve_lxc_pubkey_file: ~/.ssh/id_ed25519_homelab.pub
|
||||
pve_lxc_cmode: shell
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
- name: Validate Proxmox API environment
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- lookup('env', 'PROXMOX_HOST') | length > 0
|
||||
- lookup('env', 'PROXMOX_USER') | length > 0
|
||||
- lookup('env', 'PROXMOX_TOKEN_ID') | length > 0
|
||||
- lookup('env', 'PROXMOX_TOKEN_SECRET') | length > 0
|
||||
fail_msg: Load ansible/.env with PROXMOX_HOST, PROXMOX_USER, PROXMOX_TOKEN_ID and PROXMOX_TOKEN_SECRET.
|
||||
|
||||
- name: Create or update LXC container
|
||||
community.proxmox.proxmox:
|
||||
api_host: "{{ lookup('env', 'PROXMOX_HOST') }}"
|
||||
api_user: "{{ lookup('env', 'PROXMOX_USER') }}"
|
||||
api_token_id: "{{ lookup('env', 'PROXMOX_TOKEN_ID') }}"
|
||||
api_token_secret: "{{ lookup('env', 'PROXMOX_TOKEN_SECRET') }}"
|
||||
validate_certs: "{{ lookup('env', 'PROXMOX_VALIDATE_CERTS') | default('false', true) | bool }}"
|
||||
vmid: "{{ pve_lxc_vmid }}"
|
||||
node: "{{ pve_lxc_node }}"
|
||||
hostname: "{{ pve_lxc_hostname }}"
|
||||
ostemplate: "{{ pve_lxc_ostemplate }}"
|
||||
disk: "{{ pve_lxc_disk }}"
|
||||
cores: "{{ pve_lxc_cores }}"
|
||||
memory: "{{ pve_lxc_memory }}"
|
||||
swap: "{{ pve_lxc_swap }}"
|
||||
nameserver: "{{ pve_lxc_nameserver }}"
|
||||
cmode: "{{ pve_lxc_cmode }}"
|
||||
netif:
|
||||
net0: "name=eth0,gw={{ pve_lxc_gateway }},ip={{ pve_lxc_ip }},bridge={{ pve_lxc_bridge }},firewall=1"
|
||||
pubkey: "{{ lookup('file', pve_lxc_pubkey_file) }}"
|
||||
features: "{{ pve_lxc_features }}"
|
||||
unprivileged: "{{ pve_lxc_unprivileged }}"
|
||||
onboot: "{{ pve_lxc_onboot }}"
|
||||
startup: "{{ pve_lxc_startup }}"
|
||||
update: true
|
||||
state: present
|
||||
|
||||
- name: Start LXC container
|
||||
community.proxmox.proxmox:
|
||||
api_host: "{{ lookup('env', 'PROXMOX_HOST') }}"
|
||||
api_user: "{{ lookup('env', 'PROXMOX_USER') }}"
|
||||
api_token_id: "{{ lookup('env', 'PROXMOX_TOKEN_ID') }}"
|
||||
api_token_secret: "{{ lookup('env', 'PROXMOX_TOKEN_SECRET') }}"
|
||||
validate_certs: "{{ lookup('env', 'PROXMOX_VALIDATE_CERTS') | default('false', true) | bool }}"
|
||||
vmid: "{{ pve_lxc_vmid }}"
|
||||
state: started
|
||||
Reference in New Issue
Block a user