proxmox ansible bootstrapping
This commit is contained in:
parent
2d1a363a50
commit
bdf3bc6b02
20 changed files with 481 additions and 4 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -47,4 +47,3 @@ result-*
|
|||
|
||||
# Ignore automatically generated direnv output
|
||||
.direnv
|
||||
|
||||
|
|
|
|||
82
README.md
82
README.md
|
|
@ -1,2 +1,82 @@
|
|||
# proxmox
|
||||
# Proxmox Home Server Ansible Bootstrapping
|
||||
|
||||
This repository contains Ansible playbooks and roles for bootstrapping a fresh Proxmox VE installation.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Ansible installed on your control machine.
|
||||
* Your Proxmox VE server has an initial root password set.
|
||||
* Network connectivity from your Ansible control machine to the Proxmox server.
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Clone this repository:**
|
||||
```bash
|
||||
git clone https://gitprocopius.com/plasmagoat/proxmox.git
|
||||
cd proxmox
|
||||
```
|
||||
|
||||
2. **Configure `inventory.ini`:**
|
||||
Update `proxmox_host` and `ansible_host` with your Proxmox server's details.
|
||||
|
||||
```ini
|
||||
# inventory.ini
|
||||
[proxmox]
|
||||
proxmox_01 ansible_host=192.168.1.200 ansible_user=root
|
||||
```
|
||||
|
||||
3. **Create and encrypt `group_vars/proxmox/vault.yml`:**
|
||||
This file will store your initial Proxmox root password.
|
||||
|
||||
```bash
|
||||
ansible-vault create group_vars/proxmox/vault.yml
|
||||
```
|
||||
Enter a strong vault password when prompted. Then add the following content:
|
||||
|
||||
```yaml
|
||||
# group_vars/all/vault.yml
|
||||
initial_root_password: "YourActualProxmoxRootPassword"
|
||||
```
|
||||
Save and exit.
|
||||
|
||||
4. **Configure `group_vars/proxmox/main.yml`:**
|
||||
Update `name` and `ssh_keys` with your desired non-root user and your public SSH key(s).
|
||||
|
||||
```yaml
|
||||
# group_vars/all/main.yml
|
||||
admin:
|
||||
name: "your_ansible_user"
|
||||
ssh_keys:
|
||||
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB... your_public_key"
|
||||
```
|
||||
|
||||
5. **Create a vault password file (recommended):**
|
||||
Create a file (e.g., `~/.ansible_vault_pass`) containing only your vault password.
|
||||
```bash
|
||||
echo "YourVaultPassword" > ~/.ansible_vault_pass
|
||||
chmod 600 ~/.ansible_vault_pass
|
||||
```
|
||||
Ensure `ansible.cfg` points to this file.
|
||||
|
||||
## Running the Playbook
|
||||
|
||||
Execute the bootstrapping playbook:
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/bootstrap.yml
|
||||
```
|
||||
|
||||
If you didn't set `vault_password_file` in `ansible.cfg`, you'll be prompted for the vault password.
|
||||
|
||||
## Post-Bootstrapping
|
||||
|
||||
After the playbook completes:
|
||||
|
||||
1. **Test SSH login with the new user:**
|
||||
```bash
|
||||
ssh your_ansible_user@<your_proxmox_ip_address>
|
||||
```
|
||||
You should be able to log in without a password using your SSH key.
|
||||
|
||||
2. **Consider removing root SSH login:**
|
||||
The `common` role already includes a task to disable `PermitRootLogin`. Verify it's set to `no` in `/etc/ssh/sshd_config` on the Proxmox host.
|
||||
|
|
|
|||
5
ansible.cfg
Normal file
5
ansible.cfg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[defaults]
|
||||
inventory = ./inventory.ini
|
||||
roles_path = ./roles
|
||||
host_key_checking = False
|
||||
vault_password_file = ~/.ansible_vault_pass
|
||||
23
files/cloud-init.yml
Normal file
23
files/cloud-init.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# files/cloud-init.yaml
|
||||
# Used to seed new NixOS VMs via Proxmox's cloud-init mechanism
|
||||
# Replace username, password hash, and SSH key with your values or use secrets
|
||||
# This file itself is not secret-sensitive unless it embeds a password
|
||||
|
||||
#cloud-config
|
||||
users:
|
||||
- name: nixadmin
|
||||
groups: [wheel, sudo]
|
||||
shell: /run/current-system/sw/bin/bash
|
||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||
ssh_authorized_keys:
|
||||
- ${CI_USER_SSH_PUBKEY} # Will be templated in via Ansible
|
||||
|
||||
hostname: ${VM_HOSTNAME}
|
||||
|
||||
package_update: false
|
||||
package_upgrade: false
|
||||
chpasswd:
|
||||
expire: false
|
||||
runcmd:
|
||||
- [nixos-generate-config, "--root", "/"]
|
||||
- [systemctl, "restart", "sshd"]
|
||||
35
group_vars/README.md
Normal file
35
group_vars/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
### Ansible Vault usage
|
||||
|
||||
````markdown
|
||||
# Using Ansible Vault for secrets
|
||||
|
||||
- Create vault file (only once):
|
||||
```bash
|
||||
ansible-vault create group_vars/proxmox/vault.yml
|
||||
````
|
||||
|
||||
* Edit vault file:
|
||||
|
||||
```bash
|
||||
ansible-vault edit group_vars/proxmox/vault.yml
|
||||
```
|
||||
|
||||
* Vault file supports nested YAML structures.
|
||||
|
||||
* Run playbooks with vault password prompt:
|
||||
|
||||
```bash
|
||||
ansible-playbook bootstrap.yml --ask-vault-pass
|
||||
```
|
||||
|
||||
* Or provide a password file:
|
||||
|
||||
```bash
|
||||
ansible-playbook bootstrap.yml --vault-password-file ~/.vault_pass.txt
|
||||
```
|
||||
|
||||
* Access secrets in playbooks as normal variables, e.g.:
|
||||
|
||||
```yaml
|
||||
{{ proxmox.root_password }}
|
||||
```
|
||||
25
group_vars/proxmox/main.yml
Normal file
25
group_vars/proxmox/main.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Proxmox specific variables
|
||||
proxmox_enterprise_repo_enabled: false # Set to true if you have a Proxmox subscription
|
||||
proxmox_no_subscription_repo_enabled: true
|
||||
proxmox_pve_version: "8.4.1" # Adjust as needed
|
||||
|
||||
# Proxmox Network Configuration
|
||||
proxmox_network_ip: "192.168.1.100"
|
||||
proxmox_network_cidr: "24"
|
||||
proxmox_network_gateway: "192.168.1.1"
|
||||
proxmox_physical_nic: "eno1" # Main NIC for vmbr0
|
||||
|
||||
# General system-wide variables
|
||||
admin:
|
||||
name: "plasmagoat"
|
||||
groups: ["sudo"]
|
||||
shell: /bin/bash
|
||||
ssh_keys:
|
||||
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUP7m8jZJiclZGfSje8CeBYFhX10SrdtjYziuChmj1X plasmagoat@macbook-air"
|
||||
|
||||
ci_user:
|
||||
name: forgejo-runner
|
||||
groups: ["sudo"]
|
||||
shell: /bin/bash
|
||||
ssh_keys:
|
||||
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGlzZWik5bbH6/xjiCpwo1SQSJ/J/Cv7y4ZQ45P68GLB forgejo-runner"
|
||||
7
group_vars/proxmox/vault.yml
Normal file
7
group_vars/proxmox/vault.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
$ANSIBLE_VAULT;1.1;AES256
|
||||
61313964636437313765633263626265306663373866616265393463383838616130373639373037
|
||||
6261666639613636363666626635353636343439663263320a303137653761646664633463376466
|
||||
62616630306332373862653838376563623465393130386536383666616133656538306336666165
|
||||
3430373162633736610a633864623662366536353436343235353764386664376662363138376435
|
||||
66633337393735633539303565663634333635366462386465313739613762613932643231656437
|
||||
3464393961663935373964623432383834643263353230313333
|
||||
2
inventory.ini
Normal file
2
inventory.ini
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[proxmox]
|
||||
proxmox-01 ansible_host=192.168.1.205 ansible_user=root
|
||||
24
playbooks/bootstrap.yml
Normal file
24
playbooks/bootstrap.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
- name: Proxmox Home Server Bootstrapping
|
||||
hosts: proxmox
|
||||
gather_facts: true # We'll gather facts manually later if needed
|
||||
become: true # Use sudo for all tasks
|
||||
|
||||
vars:
|
||||
ansible_become_pass: "{{ initial_root_password }}" # Use the vaulted root password for initial connection
|
||||
|
||||
pre_tasks:
|
||||
- name: Wait for SSH to be available
|
||||
ansible.builtin.wait_for_connection:
|
||||
timeout: 300 # Adjust as needed
|
||||
|
||||
- name: Check hostname to ensure it's not localhost
|
||||
ansible.builtin.fail:
|
||||
msg: "Hostname is still 'localhost'. Please set a proper hostname on Proxmox first before running this playbook."
|
||||
when: ansible_hostname == 'localhost'
|
||||
|
||||
roles:
|
||||
- role: common
|
||||
- role: proxmox_setup
|
||||
- role: ci_user
|
||||
# - role: cloudinit
|
||||
# - role: networking # Uncomment if you've populated this role
|
||||
20
roles/ci_user/tasks/main.yml
Normal file
20
roles/ci_user/tasks/main.yml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
- name: Create CI user
|
||||
ansible.builtin.user:
|
||||
name: "{{ ci_user.name }}"
|
||||
groups: "{{ ci_user.groups }}"
|
||||
shell: "{{ ci_user.shell }}"
|
||||
state: present
|
||||
create_home: yes
|
||||
when: ci_user.name is defined and ci_user.name | length > 0
|
||||
|
||||
- name: Add SSH keys for CI user
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ ci_user.name }}"
|
||||
state: present
|
||||
key: "{{ item }}"
|
||||
loop: "{{ ci_user.ssh_keys }}"
|
||||
when:
|
||||
- ci_user.name is defined
|
||||
- ci_user.name | length > 0
|
||||
- ci_user.ssh_keys is defined
|
||||
- ci_user.ssh_keys | length > 0
|
||||
12
roles/cloudinit/tasks/main.yml
Normal file
12
roles/cloudinit/tasks/main.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
- name: Install cloud-init
|
||||
ansible.builtin.package:
|
||||
name: cloud-init
|
||||
state: present
|
||||
|
||||
- name: Ensure default Cloud-Init configuration is in place
|
||||
ansible.builtin.copy:
|
||||
src: cloud.cfg
|
||||
dest: /etc/cloud/cloud.cfg
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
4
roles/common/handles/main.yml
Normal file
4
roles/common/handles/main.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
- name: Restart sshd
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
49
roles/common/tasks/main.yml
Normal file
49
roles/common/tasks/main.yml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
- name: Ensure latest apt cache
|
||||
ansible.builtin.apt:
|
||||
update_cache: yes
|
||||
cache_valid_time: 3600 # 1 hour
|
||||
|
||||
- name: Upgrade all packages
|
||||
ansible.builtin.apt:
|
||||
upgrade: dist
|
||||
|
||||
- name: Install common packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- curl
|
||||
- wget
|
||||
- htop
|
||||
- git
|
||||
- rsync
|
||||
- nfs-common # If you plan to mount NFS shares
|
||||
state: present
|
||||
|
||||
- name: Create new admin user
|
||||
ansible.builtin.user:
|
||||
name: "{{ admin.name }}"
|
||||
groups: "{{ admin.groups }}"
|
||||
shell: "{{ admin.shell }}"
|
||||
state: present
|
||||
create_home: yes
|
||||
append: yes # Ensures other groups don't get removed
|
||||
when: admin.name is defined and admin.name | length > 0
|
||||
|
||||
- name: Add SSH keys for new admin user
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ admin.name }}"
|
||||
state: present
|
||||
key: "{{ item }}"
|
||||
loop: "{{ admin.ssh_keys }}"
|
||||
when:
|
||||
- admin.name is defined
|
||||
- admin.name | length > 0
|
||||
- admin.ssh_keys is defined
|
||||
- admin.ssh_keys | length > 0
|
||||
# - name: Disable root SSH login (optional, but recommended)
|
||||
# ansible.builtin.lineinfile:
|
||||
# path: /etc/ssh/sshd_config
|
||||
# regexp: '^PermitRootLogin'
|
||||
# line: 'PermitRootLogin no'
|
||||
# state: present
|
||||
# notify: Restart sshd
|
||||
# when: new_admin_user is defined and new_admin_user | length > 0
|
||||
2
roles/networking/README.md
Normal file
2
roles/networking/README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# On your Proxmox server (via SSH or console)
|
||||
sudo cp /etc/network/interfaces /etc/network/interfaces.bak_ansible_pre_change_$(date +%Y%m%d%H%M)
|
||||
4
roles/networking/handlers/main.yml
Normal file
4
roles/networking/handlers/main.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
- name: Restart networking
|
||||
ansible.builtin.service:
|
||||
name: networking
|
||||
state: restarted
|
||||
57
roles/networking/tasks/main.yml
Normal file
57
roles/networking/tasks/main.yml
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
- name: Backup current /etc/network/interfaces before making changes
|
||||
ansible.builtin.copy:
|
||||
src: /etc/network/interfaces
|
||||
dest: "/etc/network/interfaces.bak_ansible_{{ ansible_date_time.iso8601_basic }}"
|
||||
remote_src: yes
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
delegate_to: "{{ inventory_hostname }}" # Ensure this runs on the remote host
|
||||
|
||||
- name: Render and deploy /etc/network/interfaces from template
|
||||
ansible.builtin.template:
|
||||
src: interfaces.j2
|
||||
dest: /etc/network/interfaces
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart networking
|
||||
# - name: Ensure network interfaces file exists
|
||||
# ansible.builtin.copy:
|
||||
# content: |
|
||||
# source /etc/network/interfaces.d/*
|
||||
# dest: /etc/network/interfaces
|
||||
# owner: root
|
||||
# group: root
|
||||
# mode: '0644'
|
||||
|
||||
# - name: Configure bond0
|
||||
# ansible.builtin.copy:
|
||||
# content: |
|
||||
# auto bond0
|
||||
# iface bond0 inet manual
|
||||
# bond-slaves eno1 eno2 # Replace with your actual interfaces
|
||||
# bond-mode active-backup
|
||||
# bond-miimon 100
|
||||
# bond-primary eno1
|
||||
# dest: /etc/network/interfaces.d/bond0
|
||||
# owner: root
|
||||
# group: root
|
||||
# mode: '0644'
|
||||
# notify: Restart networking
|
||||
|
||||
# - name: Configure vmbr0 using bond0
|
||||
# ansible.builtin.copy:
|
||||
# content: |
|
||||
# auto vmbr0
|
||||
# iface vmbr0 inet static
|
||||
# address 192.168.1.10/24
|
||||
# gateway 192.168.1.1
|
||||
# bridge-ports bond0
|
||||
# bridge-stp off
|
||||
# bridge-fd 0
|
||||
# dest: /etc/network/interfaces.d/vmbr0_bond
|
||||
# owner: root
|
||||
# group: root
|
||||
# mode: '0644'
|
||||
# notify: Restart networking
|
||||
22
roles/networking/templates/interface.j2
Normal file
22
roles/networking/templates/interface.j2
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# /etc/network/interfaces -- used by ifup(8) and ifdown(8)
|
||||
#
|
||||
# Include files from /etc/network/interfaces.d:
|
||||
source /etc/network/interfaces.d/*
|
||||
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
auto vmbr0
|
||||
iface vmbr0 inet static
|
||||
address {{ proxmox_network_ip }}/{{ proxmox_network_cidr }}
|
||||
gateway {{ proxmox_network_gateway }}
|
||||
bridge-ports {{ proxmox_physical_nic }}
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
# Your ethtool post-up line
|
||||
post-up ethtool -K {{ proxmox_physical_nic }} tso off gso off
|
||||
|
||||
auto eno2
|
||||
iface eno2 inet manual
|
||||
# Your ethtool post-up line for eno2
|
||||
post-up ethtool -K eno2 tso off gso off
|
||||
14
roles/proxmox_setup/handlers/main.yml
Normal file
14
roles/proxmox_setup/handlers/main.yml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
- name: Restart networking
|
||||
ansible.builtin.service:
|
||||
name: networking
|
||||
state: restarted
|
||||
|
||||
- name: Restart systemd-resolved
|
||||
ansible.builtin.service:
|
||||
name: systemd-resolved
|
||||
state: restarted
|
||||
|
||||
- name: Restart pveproxy
|
||||
ansible.builtin.service:
|
||||
name: pveproxy
|
||||
state: restarted
|
||||
82
roles/proxmox_setup/tasks/main.yml
Normal file
82
roles/proxmox_setup/tasks/main.yml
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
- name: Remove enterprise repository
|
||||
ansible.builtin.apt_repository:
|
||||
update_cache: false
|
||||
repo: deb https://enterprise.proxmox.com/debian/pve bookworm pve-enterprise
|
||||
state: absent
|
||||
when: not proxmox_enterprise_repo_enabled
|
||||
|
||||
- name: Remove enterprise pbs repository
|
||||
ansible.builtin.apt_repository:
|
||||
update_cache: false
|
||||
repo: deb https://enterprise.proxmox.com/debian/pbs bookworm InRelease
|
||||
state: absent
|
||||
when: not proxmox_enterprise_repo_enabled
|
||||
|
||||
- name: Remove enterprise ceph repository
|
||||
ansible.builtin.apt_repository:
|
||||
update_cache: false
|
||||
repo: deb https://enterprise.proxmox.com/debian/ceph-quincy bookworm enterprise
|
||||
state: absent
|
||||
when: not proxmox_enterprise_repo_enabled
|
||||
|
||||
- name: Add community repository
|
||||
ansible.builtin.apt_repository:
|
||||
update_cache: true
|
||||
repo: deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription
|
||||
state: present
|
||||
when: proxmox_no_subscription_repo_enabled
|
||||
|
||||
- name: Update apt cache after repo changes
|
||||
ansible.builtin.apt:
|
||||
update_cache: yes
|
||||
cache_valid_time: 3600
|
||||
|
||||
- name: Ensure Proxmox VE packages are up-to-date
|
||||
ansible.builtin.apt:
|
||||
name: proxmox-ve
|
||||
state: latest
|
||||
update_cache: yes
|
||||
|
||||
- name: Install common Proxmox tools
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- proxmox-backup-client
|
||||
- smartmontools
|
||||
- zfsutils-linux
|
||||
state: present
|
||||
# Example: Configure network bridge if not already done (adjust for your network)
|
||||
# - name: Configure network bridge vmbr0
|
||||
# ansible.builtin.copy:
|
||||
# content: |
|
||||
# auto vmbr0
|
||||
# iface vmbr0 inet static
|
||||
# address 192.168.1.10/24
|
||||
# gateway 192.168.1.1
|
||||
# bridge-ports eno1 # Replace eno1 with your actual physical interface
|
||||
# bridge-stp off
|
||||
# bridge-fd 0
|
||||
# dest: /etc/network/interfaces.d/vmbr0
|
||||
# owner: root
|
||||
# group: root
|
||||
# mode: '0644'
|
||||
# notify: Restart networking
|
||||
|
||||
# Example: Configure DNS (optional, Proxmox sets up systemd-resolved by default)
|
||||
# - name: Configure /etc/resolv.conf
|
||||
# ansible.builtin.copy:
|
||||
# content: |
|
||||
# nameserver 1.1.1.1
|
||||
# nameserver 8.8.8.8
|
||||
# dest: /etc/resolv.conf
|
||||
# owner: root
|
||||
# group: root
|
||||
# mode: '0644'
|
||||
# notify: Restart systemd-resolved # if using systemd-resolved
|
||||
|
||||
# - name: Disable subscription nag (optional, for no-subscription users)
|
||||
# ansible.builtin.replace:
|
||||
# path: /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
|
||||
# regexp: '^(.*)Ext\.Msg\.show\(\{(.*)$\n^(.*)No valid subscription(.*)$'
|
||||
# replace: '\1void({\2\n\3No valid subscription\4'
|
||||
# when: not proxmox_enterprise_repo_enabled
|
||||
# notify: Restart pveproxy
|
||||
11
roles/user/tasks/main.yml
Normal file
11
roles/user/tasks/main.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
- name: Create CI user
|
||||
ansible.builtin.user:
|
||||
name: "{{ ci_user }}"
|
||||
shell: /bin/bash
|
||||
groups: sudo
|
||||
password: "{{ ci_password | password_hash('sha512') }}"
|
||||
|
||||
- name: Add authorized key
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ ci_user }}"
|
||||
key: "{{ lookup('file', '../files/ci_user.pub') }}"
|
||||
Loading…
Add table
Add a link
Reference in a new issue