Commit 8dfb8df4 authored by bbguimaraes's avatar bbguimaraes
Browse files

ansible: add Arch Linux installation playbook

parent 89e3cc25
install
=======
Arch Linux installation
-----------------------
Make sure to start the system in UEFI mode, which may need changes in the BIOS
setup. Check with `dmesg | grep 'EFI v'`. On the live installation
environment, on the target machine, set up root password for SSH access and
create a file with the LUKS encryption key (_with no trailing space_):
# yes root | passwd
# cat > /tmp/luks.key
<password>
To make things easier, install an SSH public key on the target from the machine
which will execute Ansible commands:
$ ip=…
$ ssh-copy-id root@$ip
Execute the installation playbook:
$ ansible-playbook \
--inventory $ip, \
--user root \
--extra-vars hostname=… \
--extra-vars disk_device=nvme0n1 \
--extra-vars lvm_vg=vg0 \
ansible/install/arch.yaml
Basic configuration
-------------------
When the new system is restarted into the new installation, copy the SSH key
again, and:
$ ansible-playbook \
--user root --extra-vars sudo_wheel_nopasswd=true \
ansible/install/base.yaml
For servers:
$ ansible-playbook --user root ansible/install/swap.yaml
User base setup:
$ ansible-playbook ansible/install/base_user.yaml
Desktop configuration
---------------------
For desktop systems:
$ ansible-playbook --user root ansible/install/desktop.yaml
$ ansible-playbook --user root ansible/install/backlight.yaml
$ ansible-playbook ansible/install/keys.yaml
$ ansible-playbook ansible/install/desktop_user.yaml
Start XOrg, configure Nextcloud, synchronize files, then:
$ ansible-playbook ansible/install/user.yaml
$ ansible-playbook \
--extra-vars nixpkgs=https://nixos.org/channels/nix-22.05 \
ansible/install/nix.yaml
$ ansible-playbook --user root ansible/install/gpg_pam.yaml
For personal systems:
$ ansible-playbook ansible/install/personal.yaml
Conclusion
----------
Set a user password:
# passwd bbguimaraes
Require password for `sudo`:
$ ansible-playbook \
--become --extra-vars sudo_wheel_nopasswd=false \
ansible/install/base.yaml
Disable root's password and remove the authorized SSH key:
# passwd --lock root
# rm ~/.ssh/authorized_keys
- hosts: arch
vars:
efi_part: 1
lvm_part: 2
efi_dev: "{{ disk_dev }}p{{ efi_part }}"
lvm_dev: "{{ disk_dev }}p{{ lvm_part }}"
swap_part: /dev/{{ lvm_vg }}/swap
root_part: /dev/{{ lvm_vg }}/root
home_part: /dev/{{ lvm_vg }}/home
root_mount: /mnt
efi_mount: /mnt/efi
home_mount: /mnt/home
initramfs_keyfile: /boot/initramfs.key
tasks:
- name: check required variables
assert:
that: "{{ item }} is defined"
loop:
- hostname
- disk_dev
- lvm_vg
- name: create EFI system partition
parted:
state: present
device: /dev/{{ disk_dev }}
label: gpt
number: "{{ efi_part }}"
flags: [esp]
part_start: "0MB"
part_end: "512MB"
- name: create LVM partition
parted:
state: present
device: /dev/{{ disk_dev }}
label: gpt
number: "{{ lvm_part }}"
part_start: "512MB"
part_end: "100%"
- name: format EFI system partition
filesystem:
dev: /dev/{{ efi_dev }}
fstype: vfat
opts: -F 32
- name: check for LUKS device
command:
argv:
- cryptsetup
- isLuks
- /dev/{{ lvm_dev }}
changed_when: false
failed_when: false
register: is_luks
- name: format LUKS device
command:
argv:
- cryptsetup
- luksFormat
- --batch-mode
- --type
- luks1
- --key-file
- /tmp/luks.key
- /dev/{{ lvm_dev }}
when: is_luks is failed
- name: open LUKS device
luks_device:
state: opened
device: /dev/{{ lvm_dev }}
name: lvm
keyfile: /tmp/luks.key
- name: create physical volumes / volume groups
lvg:
state: present
pvs: "/dev/mapper/lvm"
vg: "{{ lvm_vg }}"
- name: create swap logical volume
lvol:
state: present
vg: "{{ lvm_vg }}"
lv: swap
size: 8G
- name: create root logical volume
lvol:
state: present
vg: "{{ lvm_vg }}"
lv: root
size: 64G
- name: create home logical volume
lvol:
state: present
vg: "{{ lvm_vg }}"
lv: home
size: 100%FREE
shrink: false
- name: format swap partition
filesystem:
dev: "{{ swap_part }}"
fstype: swap
- name: format root partition
filesystem:
dev: "{{ root_part }}"
fstype: ext4
- name: format home partition
filesystem:
dev: "{{ home_part }}"
fstype: ext4
- name: mount root partition
command:
argv:
- mount
- "{{ root_part }}"
- "{{ root_mount }}"
warn: false
when: root_mount not in (ansible_mounts|map(attribute='mount'))
- name: create mount points
file:
state: directory
path: "{{ item }}"
loop:
- "{{ efi_mount }}"
- "{{ home_mount }}"
- name: activate swap
command:
argv:
- swapon
- "{{ swap_part }}"
when: ansible_swaptotal_mb == 0
- name: mount EFI partition
command:
argv:
- mount
- /dev/{{ efi_dev }}
- "{{ efi_mount }}"
warn: false
when: efi_mount not in (ansible_mounts|map(attribute='mount'))
- name: mount home partition
command:
argv:
- mount
- "{{ home_part }}"
- "{{ home_mount }}"
warn: false
when: "home_mount not in (ansible_mounts|map(attribute='mount'))"
- name: install base packages
command:
argv:
- pacstrap
- /mnt
- --needed
- base
- cryptsetup
- dhcpcd
- efibootmgr
- linux
- linux-firmware
- grub
- lvm2
- netctl
- openssh
- os-prober
- python
- vim
- wpa_supplicant
- name: create initramfs key file
command:
argv:
- dd
- bs=512
- count=4
- if=/dev/random
- of={{ root_mount}}/{{ initramfs_keyfile }}
- iflag=fullblock
creates: "{{ root_mount}}/{{ initramfs_keyfile }}"
- name: set initramfs key file permissions
file:
path: "{{ root_mount}}/{{ initramfs_keyfile }}"
owner: root
group: root
mode: 0
- name: add initramfs key file to LUKS device
command:
argv:
- cryptsetup
- --key-file
- /tmp/luks.key
- luksAddKey
- /dev/{{ lvm_dev }}
- "{{ root_mount}}/{{ initramfs_keyfile }}"
- name: generate fstab
shell: |
{ head -n 4 /etc/fstab && genfstab -U /mnt; } > /mnt/etc/fstab
- name: create local time symlink
file:
state: link
src: /usr/share/zoneinfo/UTC
path: "{{ root_mount }}/etc/localtime"
- name: enable locale
lineinfile:
line: en_US.UTF-8 UTF-8
path: "{{ root_mount }}/etc/locale.gen"
- name: create locale.conf
copy:
dest: "{{ root_mount }}//etc/locale.conf"
content: LANG=en_US.UTF-8
- name: enable mkinitcpio hooks
lineinfile:
line: >-
HOOKS=(base udev autodetect keyboard keymap consolefont modconf block
encrypt lvm2 filesystems fsck)
path: "{{ root_mount }}/etc/mkinitcpio.conf"
- name: add initramfs key file to mkinitcpio configuration
lineinfile:
line: FILES+=({{ initramfs_keyfile }})
path: "{{ root_mount }}/etc/mkinitcpio.conf"
- name: allow booting from LUKS1-encrypted parition
lineinfile:
path: "{{ root_mount }}/etc/default/grub"
line: GRUB_ENABLE_CRYPTODISK=y
- name: preload GRUB modules
lineinfile:
path: "{{ root_mount }}/etc/default/grub"
line: GRUB_PRELOAD_MODULES="part_gpt part_msdos lvm"
- set_fact:
lvm_uuid: "{{ ansible_device_links.uuids[lvm_dev][0] }}"
- name: set kernel parameter to unlock encrypted device
lineinfile:
path: "{{ root_mount }}/etc/default/grub"
line:
'GRUB_CMDLINE_LINUX="cryptdevice=UUID={{ lvm_uuid }}:lvm
cryptkey=rootfs:{{ initramfs_keyfile }}
root={{ root_part }}"'
- name: create hostname file
copy:
dest: "{{ root_mount}}/etc/hostname"
content: "{{ hostname }}"
- name: enable services
file:
state: link
src: /usr/lib/systemd/system/{{ item }}.service
path: /etc/systemd/system/multi-user.target.wants/{{ item }}.service
loop:
- dhcpcd
- sshd
- name: execute arch-chroot setup
shell: |
set -eu
arch-chroot /mnt <<'EOF'
set -eu
hwclock --systohc
locale-gen
mkinitcpio --allpresets
grub-install \
--target=x86_64-efi \
--efi-directory=/efi \
--bootloader-id=GRUB \
--recheck
grub-mkconfig -o /boot/grub/grub.cfg
yes root | passwd
EOF
- name: unmount directories
command:
argv:
- umount
- --recursive
- "{{ root_mount }}"
- name: import keys
command:
argv: ["gpg2", "--recv-keys", "{{ item }}"]
loop: "{{ keys|default([]) }}"
- name: check if package already installed
command: pacman -Qqi {{ pkg }}
ignore_errors: true
changed_when: false
register: installed
- name: build packages
command: aur sync -c {{ pkg }}
command: aur sync --no-view --no-confirm {{ pkg }}
register: rc
when: installed is failed
- name: update pacman cache
......
- name: check target
stat:
path: "{{ dir }}/{{ target_bin }}"
register: rc
- name: create temporary build directory
file:
state: directory
path: "{{ tmp_dir }}/{{ target }}"
- name: create configure script
command:
chdir: "{{ dir }}"
argv: ["autoreconf", "-i"]
creates: "{{ dir }}/configure"
- name: execute configure script
command:
chdir: "{{ tmp_dir }}/{{ target }}"
argv: ["{{ dir }}/configure"]
creates: "{{ tmp_dir }}/{{ target }}/Makefile"
- name: execute make
make:
chdir: "{{ tmp_dir }}/{{ target }}"
- name: copy executable to directory
copy:
remote_src: true
src: "{{ tmp_dir }}/{{ target }}/{{ target_bin }}"
dest: "{{ dir }}/{{ target_bin }}"
mode: preserve
......@@ -11,6 +11,7 @@
- rxvt-unicode-terminfo
- sudo
- tmux
- vi
- vim
- name: install base system packages
package:
......@@ -20,8 +21,8 @@
tags: base
tasks:
- name: check required variable
fail: {}
when: sudo_wheel_nopasswd is undefined
assert:
that: sudo_wheel_nopasswd is defined
- name: add users
user:
state: present
......@@ -31,6 +32,11 @@
groups:
- video
- wheel
- name: add SSH authorized key
authorized_key:
user: bbguimaraes
state: present
key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"
- name: enable sudoers.d
lineinfile:
state: present
......@@ -46,7 +52,7 @@
validate: visudo -cf %s
content: |
%wheel ALL=(ALL) ALL
when: not sudo_wheel_nopasswd
when: not sudo_wheel_nopasswd|bool
- name: allow wheel to sudo without a password
copy:
dest: /etc/sudoers.d/0_wheel
......@@ -56,7 +62,7 @@
validate: visudo -cf %s
content: |
%wheel ALL=(ALL) NOPASSWD: ALL
when: sudo_wheel_nopasswd
when: sudo_wheel_nopasswd|bool
- name: update pkgfile
command:
argv: ['pkgfile', '--update']
......
......@@ -10,7 +10,7 @@
path: '{{ src_dir }}'
- name: clone dotfiles repository
git:
repo: https://bbguimaraes@github.com/bbguimaraes/dotfiles.git
repo: https://git.bbguimaraes.com/dotfiles.git
dest: '{{ src_dir }}/dotfiles'
update: no
- name: create home directories
......@@ -23,6 +23,7 @@
- name: setup dotfile symlinks
file:
state: link
force: true
src: '{{ src_dir }}/dotfiles/{{ item.0 }}'
dest: '{{ home }}/{{ item.1 }}'
loop:
......
......@@ -3,35 +3,35 @@
vars:
pkgs:
- >- # dev
ansible busybox clang cloc cmake colordiff ctags dash distcc emacs gdb go
graphviz hub ipython musl podman python-pip rust tidy tig vim-ctrlp
vim-fugitive vim-gitgutter arm-none-eabi-gcc base-devel ccache cjson dash
devtools doxygen encfs libc++ llvm mit-scheme mypy opengl-man-pages pocl
qemu renderdoc valgrind vim-tagbar
ansible arm-none-eabi-gcc base-devel busybox ccache cjson clang cloc cmake
colordiff ctags dash dash devtools distcc doxygen emacs encfs gdb go
graphviz hub ipython libc++ llvm musl mypy opengl-man-pages pocl podman
python-pip qemu rust tidy tig valgrind vim-ctrlp vim-fugitive
vim-gitgutter vim-tagbar
- >- # xorg
mesa xorg xorg-xinit
- >- # graphical
calibre chromium dmenu feh gimp i3lock i3status qt5ct redshift
rxvt-unicode simplescreenrecorder scrot xclip xdotool blender breeze
celluloid dunst firefox i3-wm imagemagick inkscape kdenlive
libreoffice-fresh mednafen picom stellarium ttf-dejavu
blender breeze calibre celluloid chromium dmenu dunst feh firefox gimp
i3lock i3status i3-wm imagemagick inkscape kdenlive libreoffice-fresh
mednafen picom qt5ct redshift rxvt-unicode scrot simplescreenrecorder
ttf-dejavu xclip xdotool
- >- #audio
audacity alsa-utils hydrogen mpv musescore pavucontrol pulseaudio
alsa-utils audacity hydrogen mpv musescore pavucontrol pulseaudio
pulseaudio-alsa pulseaudio-bluetooth
- >- # tools
acpi discount ffmpeg fish gnuplot inotify-tools ltrace lsof moreutils
msmtp mutt nextcloud-client offlineimap p7zip pass perf poppler pv
python-matplotlib ripgrep unrar strace units vdirsyncer weechat
android-tools bc bind-tools bluez bluez-utils jq khal lynx openbsd-netcat
pacman-contrib pigz pixz rlwrap sshfs unzip weechat-matrix words
acpi android-tools bc bind-tools bluez bluez-utils discount ffmpeg fish
gnuplot inetutils inotify-tools jq khal lsof ltrace lynx moreutils msmtp
mutt nextcloud-client nix offlineimap openbsd-netcat p7zip pacman-contrib
pass perf pigz pixz poppler pv python-matplotlib ripgrep rlwrap sshfs
strace units unrar unzip vdirsyncer weechat weechat-matrix words
youtube-dl
- >- # aurutils
expac diffstat pacutils parallel wget
diffstat expac pacutils parallel wget
- >- # wine
lib32-mesa mingw-w64 wine wine-mono
- >- # nngn
boost clinfo emscripten glew glfw-x11 glm intel-compute-runtime
opencl-headers ocl-icd qt5-charts vulkan-intel vulkan-devel
boost clinfo emscripten glew glfw-x11 glm intel-compute-runtime ocl-icd
opencl-headers qt5-charts vulkan-devel vulkan-intel
- >- # nngn tests
freeglut glu
tasks:
......@@ -43,6 +43,13 @@
block: |
[multilib]
Include = /etc/pacman.d/mirrorlist
register: multilib
- name: update package list
command:
argv:
- pacman
- -Sy
when: multilib.changed
- name: install base packages
package:
name: '{{ (pkgs|join(" ")).split(" ") }}'
......@@ -80,11 +87,8 @@
- name: check if aurutils gpg key is imported
command: gpg2 --list-keys '{{ aurutils_gpg_key }}'
register: rc
ignore_errors: true
failed_when: false
changed_when: false
- name: import aurutils gpg key
command: gpg2 --recv-keys '{{ aurutils_gpg_key }}'
when: rc is failed
- name: check if already installed
command: pacman -Qqi aurutils
register: aurutils_installed
......@@ -97,24 +101,42 @@
when: aurutils_installed is failed
- name: extract files
unarchive:
remote_src: true
src: /tmp/aurutils.tar.gz
dest: /tmp
when: aurutils_installed is failed
- name: check if package already built
find:
paths: /tmp/aurutils
file_type: file
patterns:
- aurutils-*.pkg.tar.zst
register: aurutils_built
- name: makepkg
command:
argv: makepkg
argv:
- makepkg
- --noconfirm
- --syncdeps
- --rmdeps
chdir: /tmp/aurutils
creates: /tmp/aurutils/aurutils-*.pkg.tar.xz
when: aurutils_installed is failed
when: aurutils_installed is failed and not aurutils_built.files
- name: create local repository
command:
argv:
- repo-add
- '{{ aurutils_repo }}/aurutils.db.tar'
creates: '{{ aurutils_repo }}/aurutils.db.tar'
- name: list package file
find:
paths: /tmp/aurutils
file_type: file
patterns:
- aurutils-*.pkg.tar.zst
register: aurutils_built
when: aurutils_installed is failed
- name: install package
command: pacman --noconfirm -U '{{ item }}'
with_fileglob: /tmp/aurutils/aurutils-*.pkg.tar.xz
command: pacman --noconfirm -U '{{ aurutils_built.files[0].path }}'
when: aurutils_installed is failed
- name: add local repository to pacman.conf
blockinfile:
......@@ -127,11 +149,3 @@
Server = file://{{ aurutils_repo }}
- name: update pacman cache
command: pacman -Sy
- hosts: desktops:&arch
tags: misc
tasks:
- name: disable pulseaudio esound module
replace:
path: /etc/pulse/default.pa
regexp: '^(load-module module-esound-protocol-unix)$'
replace: '#\1'
- hosts: desktops:&arch
tasks:
- block:
- import_tasks: ../aur.yaml
vars: {"pkg": "pam-gnupg"}
- name: enable for console logins
blockinfile:
state: present
dest: /etc/pam.d/system-local-login
block: |
auth optional pam_gnupg.so
session optional pam_gnupg.so
- name: enable for i3lock
lineinfile:
state: present
dest: /etc/pam.d/i3lock
line: |
auth optional pam_gnupg.so
- hosts: desktops:&arch
</