--- - name: Packages ansible.builtin.apt: name: - xserver-xorg - xinit - x11-xserver-utils # Without a window manager, a browser's request to go full-screen (which is all # --kiosk does) has nobody to answer it, and it settles for its default window # size in the corner of the screen. Openbox answers it, and nothing else. - openbox - unclutter cache_valid_time: 7200 - name: Browser # Chromium comes from archive.raspberrypi.com, not Debian: that build carries the # "+rpt" suffix and Raspberry Pi's own patches for the V3D GPU on this board. Debian # also ships a much older `chromium`, so pinning the wrong archive here would quietly # undo the reason for choosing it. ansible.builtin.apt: name: "{{ 'chromium' if pi_kiosk_browser == 'chromium' else 'firefox-esr' }}" cache_valid_time: 7200 notify: Reload systemd and restart getty - name: Debugging tools for the attached screen # Only pulled in for pi_kiosk_mode=debug; apt does not take them away again when the # host goes back to kiosk mode, which is fine - they are inert and small. ansible.builtin.apt: name: - xterm # a terminal that is ours, whatever x-terminal-emulator points at - x11-utils # xwininfo, xprop: is the window actually the size of the screen? - mesa-utils # glxinfo, glxgears: is anything accelerated, or is this llvmpipe? - xdotool # drive the UI from ssh, so a measurement can be taken on a real # screen rather than an idle one - scrot # and see what that screen actually shows, over ssh cache_valid_time: 7200 when: pi_kiosk_mode == 'debug' - name: Create kiosk user ansible.builtin.user: name: "{{ pi_kiosk_user }}" groups: [video, input, tty] shell: /bin/bash create_home: true - name: Own the directories X and Firefox write into # Xorg runs as root here (Debian's wrapper, allowed_users=console) with HOME set to # the kiosk user's, so it creates ~/.cache itself - root-owned and 0700 - the first # time it writes a mesa shader cache. Firefox then cannot create ~/.cache/mozilla and # gives up with "Your Firefox profile cannot be loaded. It may be missing or # inaccessible". Creating these up front, owned by the user, means root only ever # adds subdirectories to a directory the user already owns. ansible.builtin.file: path: "/home/{{ pi_kiosk_user }}/{{ item }}" state: directory owner: "{{ pi_kiosk_user }}" group: "{{ pi_kiosk_user }}" mode: "0700" loop: - .cache - .mozilla - name: Autologin the kiosk user on tty1 ansible.builtin.file: path: /etc/systemd/system/getty@tty1.service.d state: directory mode: "0755" - name: Install getty autologin override ansible.builtin.template: src: autologin.conf.j2 dest: /etc/systemd/system/getty@tty1.service.d/autologin.conf mode: "0644" notify: Reload systemd and restart getty - name: Enable the autologin getty on tty1 # Enabling it is the part that survives a reboot. These images do not enable # getty@tty1 themselves - getty.target pulls in getty-static.service and nothing # else - so without this the drop-in above is installed, the handler starts the unit # once, and the kiosk then silently stops coming up after the next reboot. ansible.builtin.systemd: name: "getty@tty1" enabled: true state: started daemon_reload: true - name: Install .bash_profile (starts X on tty1 login) ansible.builtin.template: src: bash_profile.j2 dest: "/home/{{ pi_kiosk_user }}/.bash_profile" owner: "{{ pi_kiosk_user }}" group: "{{ pi_kiosk_user }}" mode: "0644" notify: Reload systemd and restart getty - name: Install .xinitrc (starts Openbox, then the browser) ansible.builtin.template: src: xinitrc.j2 dest: "/home/{{ pi_kiosk_user }}/.xinitrc" owner: "{{ pi_kiosk_user }}" group: "{{ pi_kiosk_user }}" mode: "0755" notify: Reload systemd and restart getty - name: Ensure the browser policy directory exists ansible.builtin.file: path: "{{ '/etc/chromium/policies/managed' if pi_kiosk_browser == 'chromium' else '/etc/firefox-esr' }}" state: directory mode: "0755" # The packages normally create these; a backstop in case a layout changes - name: Install the browser's enterprise policy # Everything that is a setting rather than a startup flag: no sign-in, no sync, no # metrics, no password manager, no translate bar. The flags themselves are in # .xinitrc, where they have to be. ansible.builtin.copy: src: "{{ 'chromium-policies.json' if pi_kiosk_browser == 'chromium' else 'policies.json' }}" dest: >- {{ '/etc/chromium/policies/managed/kiosk.json' if pi_kiosk_browser == 'chromium' else '/etc/firefox-esr/policies.json' }} mode: "0644" notify: Reload systemd and restart getty - name: Ensure the Openbox config directories exist # Both levels explicitly, and owned by the user: a file: task creates missing parents # as root, and a root-owned ~/.config is the same trap that broke the Firefox profile # above. ansible.builtin.file: path: "/home/{{ pi_kiosk_user }}/{{ item }}" state: directory owner: "{{ pi_kiosk_user }}" group: "{{ pi_kiosk_user }}" mode: "0755" loop: - .config - .config/openbox - name: Install the Openbox root menu ansible.builtin.template: src: openbox-menu.xml.j2 dest: "/home/{{ pi_kiosk_user }}/.config/openbox/menu.xml" owner: "{{ pi_kiosk_user }}" group: "{{ pi_kiosk_user }}" mode: "0644" notify: Reload systemd and restart getty