Skip to content

Commit f899339

Browse files
committed
Add PXE installer functionality
1 parent ce5936d commit f899339

24 files changed

+415
-6
lines changed

Taskfile.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
version: '3'
1010

1111
vars:
12-
PYTHON_VERSION: '3.8.5'
12+
PYTHON_VERSION: '3.10.7'
1313
PYTHON_VENV_NAME:
1414
sh: basename "{{.PWD}}"
1515
PYTHON_VENV_ROOT:

ansible/Taskfile.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@ tasks:
3939
desc: Run Ansible playbooks on file servers
4040
cmds:
4141
- '{{.ANSIBLE_PROD}} file-server.yml'
42+
43+
pxe-start:
44+
desc: Start PXE Server
45+
cmds:
46+
- '{{.ANSIBLE_PROD}} pxe-boot.yml -K -t start'
47+
48+
pxe-stop:
49+
desc: Stop PXE Server
50+
cmds:
51+
- '{{.ANSIBLE_PROD}} pxe-boot.yml -K -t stop'

ansible/group_vars/all.sops.yml

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
---
21
email: ENC[AES256_GCM,data:Ge33wDgmRXynuD/5Wj3BRg9MAaX8EI4iNuh5HV+b,iv:eYA03KsPrB1b6DpjlSL7RsNhFDCIanTh+JQkQ5cEAd4=,tag:KwMVyTgPbWXmasgd73nkwQ==,type:str]
32
admin_users:
43
- ENC[AES256_GCM,data:WFyXmPcq,iv:uvI8bjEv0YeT+4W94duJWaJx3MxMC7mgumMpRf+KVMQ=,tag:QpovnpmCQt1MB5+kuZk87Q==,type:str]
54
ansible_user: ENC[AES256_GCM,data:1FB48orO,iv:0SbHHuq30p3nTBrh5YVs/pik4jRR00lviOtuZ4wac/I=,tag:4pPe3V1VeXybnnB6PeZmcQ==,type:str]
5+
ansible_ssh_private_key_file: ENC[AES256_GCM,data:mZLwSnN21ZCSwJqKfKs9n11kpx+5CJMVDu8n0K+vUHU=,iv:Vp/ceLjBAj+uqrJngbiIlKoZoRg5al8A9HrFfyLk9y0=,tag:pN1p9BHsOJP6YavgXwaGsw==,type:str]
6+
#ENC[AES256_GCM,data:/JHS9s4wTwY=,iv:N6FC5XyZQwF6fsYLpZSIdSkpLnqZ1kf1PH0qW6xeew0=,tag:AzADUOoyC2PKwP/puB3FAg==,type:comment]
7+
preseed_user_fullname: ENC[AES256_GCM,data:8CXibEgGDp1F3ksy,iv:znAfDKWih3zX/WnJa1KJbDjXdonIe8/YslgEWi7Fnlk=,tag:Z18aTaN2/+W8ghT/TpE7/w==,type:str]
8+
preseed_user_password: ENC[AES256_GCM,data:ZU5GaJtIItuuJZ5oXsx8,iv:pz0UkHQ5uKMId/kVVdRhIj1IdQjSqZC640bQTs0sGAY=,tag:b3iRxHYkypQ+ReqRbC+VwQ==,type:str]
69
sops:
710
kms: []
811
gcp_kms: []
@@ -18,8 +21,8 @@ sops:
1821
cDdjUkhCRXkyZi9vRitDNDd6R08rcFkK/UGF7Bisk9Uh/sBh3EeeNC24VIsYsYnM
1922
ZI4f7nB6h/YpQGhQiicvAf+LQMeggY7SmJ07xtEK49DkGJVZAFDf3w==
2023
-----END AGE ENCRYPTED FILE-----
21-
lastmodified: "2022-06-18T08:02:55Z"
22-
mac: ENC[AES256_GCM,data:6KmV1udN6BWrwzpMOjv5QN7GV6fbDAcKNlpYG3/3Rt0g9GnZ0KTg5JILOFxt9YQoyp+88ILt329QYTqdDu7lVIp9ZrSH0+3AmNIIoaK9auH/9sYVEkdrIiDd1PxRAD91CA4rdhJbfEUrO8BTGcgdqdmXZ7x3MmgecgdPQpnhgoQ=,iv:7Oi8yiavXLwnouYxsrQn87aYRFqbCkT4Efaq7DBOYWw=,tag:/f1Xfs84sMM75JNhUBniHg==,type:str]
24+
lastmodified: "2022-09-24T06:13:09Z"
25+
mac: ENC[AES256_GCM,data:DxWxcCzIJzU283EmZJW1576O+QWwOeZa2KskENBzpk0/jX5Db8TGFR8kKd6dS/6YSftm0SONyc7+WzjQt5DNDC7qnijyJDyfjc84qGK9S0pNScPCi6nfh8yE4u3KxhJ0aLhk4IkVRN5vGSUW99Vl0t+4SzsvN1EPiG1lqibjCno=,iv:d0/E3I4zuohY8J3wv6zK+9OazuD1e/VmWjXi9PcGtpY=,tag:I5UH6ee2lNBpfUX6cUfiVA==,type:str]
2326
pgp: []
2427
unencrypted_suffix: _unencrypted
2528
version: 3.7.1

ansible/group_vars/all.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ firewall_servers_subnet: 10.1.1.0/24
1515
firewall_wireless_subnet: 10.1.2.0/24
1616
firewall_clients_subnet: 10.1.3.0/24
1717

18+
ssh_public_key: "{{ lookup('file', (ansible_ssh_private_key_file + '.pub')) }}"
19+
1820
timezone: Australia/Sydney
19-
debian_version: buster
2021

2122
ansible_python_interpreter: /usr/bin/python3.7
23+
debian_version: buster

ansible/host_vars/kvm1.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
---
22
ansible_host: '10.1.1.21'
3+
mac: '00:23:24:a3:e1:58'

ansible/host_vars/kvm2.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
---
22
ansible_host: '10.1.1.22'
3+
mac: '00:23:24:aa:7b:0e'

ansible/host_vars/kvm3.yml

+4
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
---
22
ansible_host: '10.1.1.23'
3+
mac: '00:23:24:aa:7d:0f'
4+
5+
ansible_python_interpreter: /usr/bin/python3.9
6+
debian_version: bullseye

ansible/host_vars/localhost.sops.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
routeros_api_username: ENC[AES256_GCM,data:AImZyJK5,iv:QcAWsh0W5s3Fs+QOX6tKSM+aHpSyvOxdM85SfnaFXqw=,tag:M3ZnQt1P1Jwu3g0XwLg0EA==,type:str]
3+
routeros_api_password: ENC[AES256_GCM,data:MR28SFoFD3Qdja+zPRBz5Y6xJ3JZATmAtT5t6p3C,iv:QxArLbdpoRx+y5v9822Onu7bTk4RXV2CNFxyMqO5n2U=,tag:5yGIjQyrTMADpZO1/brVkg==,type:str]
4+
sops:
5+
kms: []
6+
gcp_kms: []
7+
azure_kv: []
8+
hc_vault: []
9+
age:
10+
- recipient: age1ety65vhtrhlevgmm899eyvdmv4avfm0replxg5tz6a2jyq84n4us7ryfc6
11+
enc: |
12+
-----BEGIN AGE ENCRYPTED FILE-----
13+
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1K2FmbjBDSTB3Q2pCYjNJ
14+
SGt1NUlUS24yRnJveFpBemtJOEFCQjZWaEFJCjNoRFFSU0xjQVJIZ204cWJIelRB
15+
N1JBc1J0YXhBZVF5ekhYNTBEYUVXejAKLS0tIDJRakJRYmxXbHIzUXZVV3p6YTI4
16+
bFNFWSsvTlRlZFFTcFNPTGhhc0pZbXMKwDuDzVkMWVq6eceFcAJACcN2IjBk3Hea
17+
rhc/qAqXqOm32RZFKi9GD1RtKdx0EHYxQVzpTKYvnmp+ImPAyXf5Mw==
18+
-----END AGE ENCRYPTED FILE-----
19+
lastmodified: "2022-10-01T13:11:04Z"
20+
mac: ENC[AES256_GCM,data:RhrNfF7OUjOWHzc02L5fe4OuiJVn+a5xoK+Gftp5jx1vJttRjvXkgigDBoyhB0o9VVDjJoeIyuIRU+n4aV4MIsewXOYUiE9LvzdxMCdDsKl/d6mWmFp25xpSt6+4HuVxfbaXBCSkB5RABk16T2BMlh83Fq99YlcISUnVkC4CQ9c=,iv:FwIkuRAFYO24x8V4E0ZMI0r7uldja+BOfg3lFy4209E=,tag:frRN8e6gOSEK3IB9IbQyBA==,type:str]
21+
pgp: []
22+
unencrypted_suffix: _unencrypted
23+
version: 3.7.1

ansible/production

+11-1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,15 @@ k8s-worker-03
2222
k8s_controllers
2323
k8s_workers
2424

25-
[file_server]
25+
[file_servers]
2626
storage
27+
28+
[metal_storage:children]
29+
file_servers
30+
31+
[metal_kvm:children]
32+
kvm_hypervisors
33+
34+
[metal:children]
35+
metal_storage
36+
metal_kvm

ansible/pxe-boot.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
- name: pxe_boot
3+
hosts: localhost
4+
gather_facts: true
5+
roles:
6+
- pxe_boot

ansible/requirements.yml

+8
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,11 @@ collections:
66
version: 1.4.1
77
- name: community.mysql
88
version: 3.5.1
9+
- name: ansible.utils
10+
version: 2.6.1
11+
- name: community.docker
12+
version: 2.7.1
13+
- name: ansible.posix
14+
version: 1.4.0
15+
- name: community.routeros
16+
version: 2.3.0
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
installer_img_url: https://deb.debian.org/debian/dists/bullseye/main/installer-amd64/20210731+deb11u5/images/netboot/netboot.tar.gz
2+
installer_img_checksum: sha256:e8edf26ac9837d7dbbcfd96f47f51530260a6c68568938978e1b63ea698d5663
3+
4+
subnet: "{{ firewall_servers_subnet | ansible.utils.ipaddr('network') }}"
5+
netmask: "{{ firewall_servers_subnet | ansible.utils.ipaddr('netmask') }}"
6+
gateway: '{{ firewall_servers_subnet | ansible.utils.nthhost(1) }}'
7+
nameserver: '{{ firewall_servers_subnet | ansible.utils.nthhost(1) }}'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
version: "3"
2+
3+
services:
4+
tftp:
5+
build: ./tftp
6+
network_mode: host
7+
volumes:
8+
- ./data/os/debian-installer:/var/lib/tftpboot/debian-installer
9+
- ./data/pxe-config/grub.cfg:/var/lib/tftpboot/debian-installer/amd64/grub/grub.cfg
10+
- ./data/os/debian-installer/amd64/grubx64.efi:/var/lib/tftpboot/grubx64.efi
11+
- ./data/preseed:/var/lib/tftpboot/preseed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM alpine:3.16
2+
3+
RUN apk add busybox tftp-hpa
4+
5+
ENTRYPOINT [ "/bin/sh", "-c" ]
6+
7+
CMD [ "busybox syslogd -n -O /dev/stdout & in.tftpd -vvv --foreground --secure /var/lib/tftpboot" ]

ansible/roles/pxe_boot/tasks/main.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
- name: PXE | Start
3+
ansible.builtin.import_tasks: start.yml
4+
tags:
5+
- pxe_boot
6+
- start
7+
when: ansible_facts['distribution'] == 'Fedora'
8+
9+
- name: PXE | Stop
10+
ansible.builtin.import_tasks: stop.yml
11+
tags:
12+
- pxe_boot
13+
- stop
14+
when: ansible_facts['distribution'] == 'Fedora'
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
- name: PXE | download installer image
3+
ansible.builtin.get_url:
4+
url: '{{ installer_img_url }}'
5+
dest: '{{ role_path }}/files/data/source/{{ installer_img_url | basename }}'
6+
checksum: '{{ installer_img_checksum }}'
7+
mode: 0644
8+
register: installer_img
9+
tags:
10+
- pxe_boot
11+
- start
12+
when: ansible_facts['distribution'] == 'Fedora'
13+
14+
- name: PXE | extract installer image
15+
ansible.builtin.unarchive:
16+
src: '{{ installer_img.dest }}'
17+
dest: '{{ role_path }}/files/data/os'
18+
tags:
19+
- pxe_boot
20+
- start
21+
when: ansible_facts['distribution'] == 'Fedora'
22+
23+
- name: PXE | generate grub config
24+
ansible.builtin.template:
25+
src: data/pxe-config/grub.cfg.j2
26+
dest: '{{ role_path }}/files/data/pxe-config/grub.cfg'
27+
mode: 0644
28+
tags:
29+
- pxe_boot
30+
- start
31+
when: ansible_facts['distribution'] == 'Fedora'
32+
33+
- name: PXE | generate preseed file for each kvm machine
34+
ansible.builtin.template:
35+
src: data/preseed/preseed-kvm.cfg.j2
36+
dest: "{{ role_path }}/files/data/preseed/{{ hostvars[item]['mac'] }}.cfg"
37+
mode: 0644
38+
loop: "{{ groups['metal_kvm'] }}"
39+
tags:
40+
- pxe_boot
41+
- start
42+
when: ansible_facts['distribution'] == 'Fedora'
43+
44+
- name: PXE | start the pxe container
45+
community.docker.docker_compose:
46+
project_src: '{{ role_path }}/files'
47+
project_name: pxe
48+
state: present
49+
restarted: true
50+
build: true
51+
tags:
52+
- pxe_boot
53+
- start
54+
when: ansible_facts['distribution'] == 'Fedora'
55+
56+
- name: PXE | allow tftp inbound
57+
ansible.posix.firewalld:
58+
zone: public
59+
service: tftp
60+
permanent: false
61+
state: enabled
62+
become: true
63+
vars:
64+
ansible_python_interpreter: /usr/bin/python3.10 # https://github.com/ansible-collections/ansible.posix/issues/283
65+
tags:
66+
- pxe_boot
67+
- start
68+
when: ansible_facts['distribution'] == 'Fedora'
69+
70+
- name: PXE | add dhcp options
71+
community.routeros.api_find_and_modify:
72+
hostname: '{{ gateway }}'
73+
username: '{{ routeros_api_username }}'
74+
password: '{{ routeros_api_password }}'
75+
tls: true
76+
path: ip dhcp-server network
77+
find:
78+
address: '{{ firewall_servers_subnet }}'
79+
values:
80+
next-server: "{{ ansible_facts['default_ipv4']['address'] }}"
81+
boot-file-name: grubx64.efi
82+
require_matches_min: 1
83+
require_matches_max: 1
84+
when: ansible_facts['distribution'] == 'Fedora'

ansible/roles/pxe_boot/tasks/stop.yml

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
- name: PXE | add dhcp options
3+
community.routeros.api_find_and_modify:
4+
hostname: '{{ gateway }}'
5+
username: '{{ routeros_api_username }}'
6+
password: '{{ routeros_api_password }}'
7+
tls: true
8+
path: ip dhcp-server network
9+
find:
10+
address: '{{ firewall_servers_subnet }}'
11+
values:
12+
next-server: '127.0.0.1' # Appears that this part of the api does not support unsetting values
13+
require_matches_min: 1
14+
require_matches_max: 1
15+
when: ansible_facts['distribution'] == 'Fedora'
16+
17+
# FIXME: Could not process rule: No such file or directory
18+
# - name: PXE | Block TFTP inbound
19+
# ansible.posix.firewalld:
20+
# zone: public
21+
# service: tftp
22+
# permanent: false
23+
# state: disabled
24+
# become: true
25+
# vars:
26+
# ansible_python_interpreter: /usr/bin/python3.10 # https://github.com/ansible-collections/ansible.posix/issues/283
27+
# tags:
28+
# - pxe_boot
29+
# - stop
30+
# when: ansible_facts['distribution'] == 'Fedora'
31+
32+
# FIXME: Workaround for the above failing
33+
- name: PXE | block tftp inbound
34+
ansible.builtin.service:
35+
name: firewalld
36+
state: restarted
37+
become: true
38+
tags:
39+
- pxe_boot
40+
- stop
41+
when: ansible_facts['distribution'] == 'Fedora'
42+
43+
- name: PXE | remove the pxe container
44+
community.docker.docker_compose:
45+
project_src: '{{ role_path }}/files'
46+
project_name: pxe
47+
state: absent
48+
tags:
49+
- pxe_boot
50+
- stop
51+
when: ansible_facts['distribution'] == 'Fedora'
52+
53+
- name: PXE | remove preseed files
54+
ansible.builtin.file:
55+
path: "{{ role_path }}/files/data/preseed/{{ hostvars[item]['mac'] }}.cfg"
56+
state: absent
57+
loop: "{{ groups['metal_kvm'] }}"
58+
tags:
59+
- pxe_boot
60+
- stop
61+
when: ansible_facts['distribution'] == 'Fedora'
62+
63+
- name: PXE | remove grub config
64+
ansible.builtin.file:
65+
path: '{{ role_path }}/files/data/pxe-config/grub.cfg'
66+
state: absent
67+
tags:
68+
- pxe_boot
69+
- stop
70+
when: ansible_facts['distribution'] == 'Fedora'
71+
72+
- name: PXE | remove os files
73+
ansible.builtin.file:
74+
path: '{{ role_path }}/files/data/os/{{ item }}'
75+
state: absent
76+
loop:
77+
- debian-installer
78+
- pxelinux.cfg
79+
- ldlinux.c32
80+
- pxelinux.0
81+
- version.info
82+
tags:
83+
- pxe_boot
84+
- stop
85+
when: ansible_facts['distribution'] == 'Fedora'
86+
87+
- name: PXE | remove installer image
88+
ansible.builtin.file:
89+
path: '{{ role_path }}/files/data/source/{{ installer_img_url | basename }}'
90+
state: absent
91+
tags:
92+
- pxe_boot
93+
- stop
94+
when: ansible_facts['distribution'] == 'Fedora'

0 commit comments

Comments
 (0)