diff --git a/roles/server_iso/.gitignore b/roles/server_iso/.gitignore new file mode 100644 index 0000000..d2adb52 --- /dev/null +++ b/roles/server_iso/.gitignore @@ -0,0 +1 @@ +files/*.iso diff --git a/roles/server_iso/README.md b/roles/server_iso/README.md new file mode 100644 index 0000000..b06504c --- /dev/null +++ b/roles/server_iso/README.md @@ -0,0 +1,31 @@ +# server_iso Role +Create an Oracle Linux 9 ISO for automatic server installation. + +## Variables + +|Name |Type |Requiered |Description | +|------|------|-----------|-------------| +|src_iso_download_url |str | no | Download link to source installation medium | +|src_iso_path |str | no | Storage location of downloaded source installation medium | +|src_iso_checksum |str | no | Checksum of downloaded source installation medium | +|src_iso_label |str | no | Volume ID of installation medium. Could be displayed by `xorriso -indev {{ role_path }}/files/OracleLinux-R9-U0-x86_64-boot-uek.iso -toc` | +|reboot_after_installation |bool | no | Automatic reboot after installation. | +|gateway |str | no | IPv4 default gateway address. This will only be used when ansible_host is a IP address. | +|nameserver |str | no | IPv4 nameserver address. This will only be used when ansible_host is a IP address. | +|netmask |str | no | IPv4 subnet mask. This will only be used when ansible_host is a IP address. | +|ssh_keys |list[str] | no | List of ssh public keys that will be added to .ssh/authorized_keys | +|iso_path |str | yes | Storage location for the created ISO | + + +## Example Playbook +```yaml +- name: Create server ISO + hosts: server + gather_facts: false + vars: + iso_path: "~/Downloads/OEL_{{ inventory_hostname }}.iso" + ssh_keys: + - "ssh-rsa 8J+OtU5ldmVyIGdvbm5hIGdpdmUgeW91IHVw8J+Otg== UmljayDwn5W6@IPCfp7sg" + roles: + - server_iso +``` diff --git a/roles/server_iso/defaults/main.yml b/roles/server_iso/defaults/main.yml new file mode 100644 index 0000000..9af7905 --- /dev/null +++ b/roles/server_iso/defaults/main.yml @@ -0,0 +1,6 @@ +--- +src_iso_download_url: https://yum.oracle.com/ISOS/OracleLinux/OL9/u0/x86_64/OracleLinux-R9-U0-x86_64-boot-uek.iso +src_iso_path: "{{ role_path }}/files/OracleLinux-R9-U0-x86_64-boot-uek.iso" +src_iso_checksum: sha256:a782e8c78a629ab1c19fcb32e76d3b81c7ef1b04060a0465253c103686339f3a +src_iso_label: OL-9-0-0-BaseOS-x86_64 # xorriso -indev ~/sync/kestrel-pronghorn/ansible/roles/server_iso/files/OracleLinux-R9-U0-x86_64-boot-uek.iso -toc +reboot_after_installation: false diff --git a/roles/server_iso/files/.gitkeep b/roles/server_iso/files/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/roles/server_iso/handlers/main.yml b/roles/server_iso/handlers/main.yml new file mode 100644 index 0000000..c02c695 --- /dev/null +++ b/roles/server_iso/handlers/main.yml @@ -0,0 +1,15 @@ +--- +- name: Set mode + ansible.builtin.file: + path: "{{ tmp_iso_path['path'] }}" + state: directory + mode: 0744 + recurse: true + listen: Server ISO cleanup + delegate_to: localhost + +- name: Server ISO cleanup + ansible.builtin.file: + path: "{{ tmp_iso_path['path'] }}" + state: absent + delegate_to: localhost diff --git a/roles/server_iso/meta/argument_specs.yml b/roles/server_iso/meta/argument_specs.yml new file mode 100644 index 0000000..6e65d72 --- /dev/null +++ b/roles/server_iso/meta/argument_specs.yml @@ -0,0 +1,60 @@ +--- +argument_specs: + main: + short_description: Create an ISO for server installation + description: Create an Oracle Linux 9 ISO for automatic server installation. + options: + # line 2 of defaults/main.yml + src_iso_download_url: + default: "https://yum.oracle.com/ISOS/OracleLinux/OL9/u0/x86_64/OracleLinux-R9-U0-x86_64-boot-uek.iso" + description: "Download link to source installation medium" + type: "str" + + # line 3 of defaults/main.yml + src_iso_path: + default: "{{ role_path }}/files/OracleLinux-R9-U0-x86_64-boot-uek.iso" + description: "Storage location of downloaded source installation medium" + type: "str" + + # line 4 of defaults/main.yml + src_iso_checksum: + default: "sha256:a782e8c78a629ab1c19fcb32e76d3b81c7ef1b04060a0465253c103686339f3a" + description: "Checksum of downloaded source installation medium" + type: "str" + + # line 5 of defaults/main.yml + src_iso_label: + default: "OL-9-0-0-BaseOS-x86_64" + description: "Volume ID of installation medium. Could be displayed by `xorriso -indev {{ role_path }}/files/OracleLinux-R9-U0-x86_64-boot-uek.iso -toc`" + type: "str" + + # line 6 of defaults/main.yml + reboot_after_installation: + default: false + description: "Automatic reboot after installation." + type: "bool" + + gateway: + default: "{{ (((ansible_host + '/255.255.255.0') | ansible.utils.ipaddr('network') | ansible.utils.ipaddr('int'))+1) | string | ansible.utils.ipaddr}}" + description: "IPv4 default gateway address. This will only be used when ansible_host is a IP address." + type: "str" + + nameserver: + default: "{{ (((ansible_host + '/255.255.255.0') | ansible.utils.ipaddr('network') | ansible.utils.ipaddr('int'))+1) | string | ansible.utils.ipaddr}}" + description: "IPv4 nameserver address. This will only be used when ansible_host is a IP address." + type: "str" + + netmask: + default: "255.255.255.0" + description: "IPv4 subnet mask. This will only be used when ansible_host is a IP address." + type: "str" + + ssh_keys: + description: "List of ssh public keys that will be added to .ssh/authorized_keys" + type: 'list' + elements: 'str' + + iso_path: + required: true + description: "Storage location for the created ISO" + type: "str" diff --git a/roles/server_iso/meta/main.yml b/roles/server_iso/meta/main.yml new file mode 100644 index 0000000..ebcb7e7 --- /dev/null +++ b/roles/server_iso/meta/main.yml @@ -0,0 +1,45 @@ +--- +galaxy_info: + author: Frank Adaemmer + description: Create an ISO for server installation + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: GPL-3.0-or-later + + min_ansible_version: '2.9' + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + # + # Provide a list of supported platforms, and for each platform a list of versions. + # If you don't wish to enumerate all versions for a particular platform, use 'all'. + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + platforms: + - name: EL + version: + - '9' + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. diff --git a/roles/server_iso/tasks/create_iso.yml b/roles/server_iso/tasks/create_iso.yml new file mode 100644 index 0000000..c378548 --- /dev/null +++ b/roles/server_iso/tasks/create_iso.yml @@ -0,0 +1,67 @@ +--- +- name: Create ISO block + delegate_to: localhost + block: + - name: Create custom kickstart file + ansible.builtin.template: + src: kickstart.ks.j2 + dest: "{{ tmp_iso_path['path'] }}/kickstart.ks" + mode: 0644 + + - name: Add kickstart file to GRUB BIOS + ansible.builtin.replace: + path: "{{ tmp_iso_path['path'] }}/isolinux/isolinux.cfg" + regexp: ^(\s*append .*) + replace: \g<1> inst.ks=hd:LABEL={{ src_iso_label }}:/kickstart.ks + + - name: Add kickstart file to GRUB EFI + ansible.builtin.replace: + path: "{{ tmp_iso_path['path'] }}/EFI/BOOT/grub.cfg" + regexp: ^(\s*linuxefi .*) + replace: \g<1> inst.ks=hd:LABEL={{ src_iso_label }}:/kickstart.ks + + - name: Decrease timeout BIOS + ansible.builtin.lineinfile: + path: "{{ tmp_iso_path['path'] }}/isolinux/isolinux.cfg" + line: timeout 30 + regexp: ^timeout + - name: Remove default BIOS + ansible.builtin.lineinfile: + path: "{{ tmp_iso_path['path'] }}/isolinux/isolinux.cfg" + regexp: ^ menu default + state: absent + - name: Set default BIOS + ansible.builtin.lineinfile: + path: "{{ tmp_iso_path['path'] }}/isolinux/isolinux.cfg" + line: ' menu default' + regexp: ^ menu default + firstmatch: true + insertafter: ^ menu label + + - name: Decrease timeout EFI + ansible.builtin.lineinfile: + path: "{{ tmp_iso_path['path'] }}/EFI/BOOT/grub.cfg" + line: set timeout=3 + regexp: ^set timeout= + + - name: Set default EFI + ansible.builtin.lineinfile: + path: "{{ tmp_iso_path['path'] }}/EFI/BOOT/grub.cfg" + line: set default="0" + regexp: ^set default= + + - name: Pack ISO + ansible.builtin.command: + cmd: > + xorrisofs -U -r -v -J -joliet-long -V "{{ src_iso_label }}" + -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 + -boot-info-table -eltorito-alt-boot -e images/efiboot.img -no-emul-boot + -o "{{ iso_path }}" . + chdir: "{{ tmp_iso_path['path'] }}" + changed_when: + - result['rc'] is defined + - result['rc'] == 0 + register: result + - name: Pause + ansible.builtin.pause: + prompt: "Pause" diff --git a/roles/server_iso/tasks/download_iso.yml b/roles/server_iso/tasks/download_iso.yml new file mode 100644 index 0000000..fa06e71 --- /dev/null +++ b/roles/server_iso/tasks/download_iso.yml @@ -0,0 +1,8 @@ +--- +- name: Download ISO + ansible.builtin.get_url: + url: "{{ src_iso_download_url }}" + dest: "{{ src_iso_path }}" + checksum: "{{ src_iso_checksum }}" + mode: 0644 + delegate_to: localhost diff --git a/roles/server_iso/tasks/extract_iso.yml b/roles/server_iso/tasks/extract_iso.yml new file mode 100644 index 0000000..49a5833 --- /dev/null +++ b/roles/server_iso/tasks/extract_iso.yml @@ -0,0 +1,20 @@ +--- +- name: Extract Block + delegate_to: localhost + block: + - name: Create temp dir + ansible.builtin.tempfile: + state: directory + register: tmp_iso_path + notify: Server ISO cleanup + + - name: Extract ISO + ansible.builtin.command: + cmd: > + xorriso -osirrox on + -indev {{ src_iso_path }} + -extract / {{ tmp_iso_path['path'] }} + changed_when: + - result['rc'] is defined + - result['rc'] == 0 + register: result diff --git a/roles/server_iso/tasks/main.yml b/roles/server_iso/tasks/main.yml new file mode 100644 index 0000000..aad6831 --- /dev/null +++ b/roles/server_iso/tasks/main.yml @@ -0,0 +1,14 @@ +--- +- name: Assert iso_path + ansible.builtin.assert: + that: + - iso_path is defined + +- name: Download ISO + ansible.builtin.import_tasks: download_iso.yml + +- name: Extract ISO + ansible.builtin.import_tasks: extract_iso.yml + +- name: Create ISO + ansible.builtin.import_tasks: create_iso.yml diff --git a/roles/server_iso/templates/kickstart.ks.j2 b/roles/server_iso/templates/kickstart.ks.j2 new file mode 100644 index 0000000..9098b56 --- /dev/null +++ b/roles/server_iso/templates/kickstart.ks.j2 @@ -0,0 +1,91 @@ +skipx +firewall --enabled --ssh + +repo --name="AppStream" --baseurl=http://yum.oracle.com/repo/OracleLinux/OL9/appstream/x86_64 +repo --name="UEK" --baseurl=http://yum.oracle.com/repo/OracleLinux/OL9/UEKR7/x86_64 + +%addon com_redhat_kdump --enable --reserve-mb='auto' + +%end + +%addon com_redhat_oscap + content-type = scap-security-guide + datastream-id = scap_org.open-scap_datastream_from_xccdf_ssg-ol9-xccdf-1.2.xml + xccdf-id = scap_org.open-scap_cref_ssg-ol9-xccdf-1.2.xml + profile = xccdf_org.ssgproject.content_profile_standard +%end + +{% if ansible_host is defined and ansible_host | ipaddr %} +network --bootproto=static --gateway={{ + gateway | default((((ansible_host + '/255.255.255.0') | ansible.utils.ipaddr('network') | ansible.utils.ipaddr('int'))+1)|string|ansible.utils.ipaddr) + }} --ip={{ ansible_host }} --nameserver={{ + nameserver | default((((ansible_host + '/255.255.255.0') | ansible.utils.ipaddr('network') | ansible.utils.ipaddr('int'))+1)|string|ansible.utils.ipaddr) + }} --netmask={{ netmask | default('255.255.255.0') }} +{%- else %} +network --bootproto=dhcp --hostname={{ inventory_hostname }} +{%- endif %} --device=enp0s3 --onboot=on --hostname={{ inventory_hostname }} +user --name={{ ansible_user | default(lookup('pipe', 'whoami')) }} --groups=wheel --password={{ ansible_password | password_hash }} --iscrypted + +# Keyboard layouts +keyboard --xlayouts='de' +# System language +lang en_US.UTF-8 + +# Use network installation +url --url="https://yum.oracle.com/repo/OracleLinux/OL9/baseos/latest/x86_64" + +%packages +@^server-product-environment +openscap +openscap-scanner +rsyslog +scap-security-guide + +%end + +%post --interpreter=/bin/bash + +#---- Install our SSH key ---- +mkdir -p -m0700 /home/{{ ansible_user | default(lookup('pipe', 'whoami')) }}/.ssh/ + +cat </home/{{ ansible_user | default(lookup('pipe', 'whoami')) }}/.ssh/authorized_keys +{% for ssh_key in ssh_keys %} +{{ ssh_key }} +{% endfor %} +EOF + +### set permissions +chmod 0600 /home/{{ ansible_user | default(lookup('pipe', 'whoami')) }}/.ssh/authorized_keys + +### change owner +chown -R {{ ansible_user | default(lookup('pipe', 'whoami')) }}:{{ ansible_user | default(lookup('pipe', 'whoami')) }} /home/{{ ansible_user | default(lookup('pipe', 'whoami')) }}/.ssh + +### fix up selinux context +restorecon -R /home/{{ ansible_user | default(lookup('pipe', 'whoami')) }}/.ssh/ + +%end + +# Run the Setup Agent on first boot +firstboot --enable + +# Generated using Blivet version 3.4.0 +ignoredisk --only-use=sda +# Partition clearing information +clearpart --all --initlabel +# Disk partitioning information +part pv.116 --fstype="lvmpv" --ondisk=sda --grow --size=25600 +part /boot --fstype="xfs" --ondisk=sda --size=1024 +volgroup ol --pesize=4096 pv.116 +logvol /var/log --fstype="xfs" --size=2048 --name=var_log --vgname=ol +logvol /var/log/audit --fstype="xfs" --size=1024 --name=var_log_audit --vgname=ol +logvol / --fstype="xfs" --grow --name=root --vgname=ol --size 20480 + +# System timezone +timezone Europe/Berlin --utc + +# Disable root password +rootpw --lock +{% if reboot_after_installation %} +# Reboot after installation +reboot +{% endif %}