--- # workstation_kerberos.yml — Configure Kerberos client + browser SPNEGO on managed endpoints. # # Run this playbook once during laptop provisioning (or re-run idempotently). # It replaces the per-user "defaults write" workaround with a managed, auditable # policy that is identical across every machine. # # WHAT IT DOES # ------------ # 1. Writes /etc/krb5.conf (or /Library/Preferences/edu.mit.Kerberos on macOS) # so that kinit resolves PROLE.ORG principals against myrddin.prole.org. # 2. Deploys Chrome managed preferences (AuthServerAllowlist) so Chrome/Edge # automatically negotiates Kerberos for *.prole.org without any per-user # browser configuration. # 3. On Windows: writes the equivalent HKLM registry keys (no GPO required). # # USAGE # ----- # # All managed workstations (macOS + Windows): # ansible-playbook infrastructure/playbooks/workstation_kerberos.yml # # # macOS / Linux only: # ansible-playbook infrastructure/playbooks/workstation_kerberos.yml \ # --limit workstations # # # Windows only: # ansible-playbook infrastructure/playbooks/workstation_kerberos.yml \ # --limit workstations_windows # # # Single machine: # ansible-playbook infrastructure/playbooks/workstation_kerberos.yml \ # --limit # # # Dry-run: # ansible-playbook infrastructure/playbooks/workstation_kerberos.yml \ # --check --diff # # HOSTS # ----- # [workstations] — macOS / Linux endpoints (become: sudo) # [workstations_windows] — Windows endpoints (become: runas) # Add new machines to the appropriate group; no playbook changes required. # # ADDING A NEW REALM # ------------------ # Extend the krb5_realms list in vars below. The template generates the # [realms] and [domain_realm] sections automatically. # ============================================================================ # Play 1 — macOS / Linux workstations # Uses sudo (the default become plugin for Unix targets). # morgana runs ansible_connection=local so DNS self-lookup is not required. # ============================================================================ - name: Configure Kerberos client and browser SPNEGO on macOS/Linux workstations hosts: "workstations,!workstations_windows" gather_facts: true become: true vars: krb5_default_realm: "PROLE.ORG" krb5_realms: - realm: "PROLE.ORG" kdc: "myrddin.prole.org" admin_server: "myrddin.prole.org" domain_suffixes: - ".prole.org" - "prole.org" # Chrome / Chromium / Edge — negotiate Kerberos for these domains. # *.prole.org covers git.prole.org, svc.prole.org, db.prole.org, etc. chrome_negotiate_domains: "*.prole.org" tasks: # ----------------------------------------------------------------------- # 1. krb5.conf — shared by kinit, curl --negotiate, Python krb5, etc. # ----------------------------------------------------------------------- - name: Write /etc/krb5.conf ansible.builtin.copy: dest: /etc/krb5.conf owner: root group: "{{ 'wheel' if ansible_os_family == 'Darwin' else 'root' }}" mode: '0644' content: | [libdefaults] default_realm = {{ krb5_default_realm }} dns_canonicalize_hostname = false rdns = false # Ticket forwarding is disabled by default; enable per-service via # [appdefaults] if needed (e.g. SSH GSSAPIDelegateCredentials). forwardable = false [realms] {% for r in krb5_realms %} {{ r.realm }} = { kdc = {{ r.kdc }} admin_server = {{ r.admin_server }} } {% endfor %} [domain_realm] {% for r in krb5_realms %} {% for d in r.domain_suffixes %} {{ d }} = {{ r.realm }} {% endfor %} {% endfor %} # macOS also checks this legacy path; symlink for compatibility. - name: Symlink macOS legacy Kerberos config ansible.builtin.file: src: /etc/krb5.conf dest: /Library/Preferences/edu.mit.Kerberos state: link force: true when: ansible_os_family == "Darwin" # ----------------------------------------------------------------------- # 2. Chrome managed policy — macOS # Placed in /Library/Managed Preferences/ so it applies to all users on # the machine. No per-user Chrome configuration required. # ----------------------------------------------------------------------- - name: Ensure Chrome managed preferences directory exists (macOS) ansible.builtin.file: path: /Library/Managed Preferences state: directory owner: root group: wheel mode: '0755' when: ansible_os_family == "Darwin" - name: Deploy Chrome SPNEGO managed policy (macOS) ansible.builtin.copy: dest: /Library/Managed Preferences/com.google.Chrome.plist owner: root group: wheel mode: '0644' content: | AuthServerAllowlist {{ chrome_negotiate_domains }} AuthNegotiateDelegateAllowlist {{ chrome_negotiate_domains }} when: ansible_os_family == "Darwin" notify: Restart Chrome (macOS) # Microsoft Edge on macOS uses the same managed-preferences directory with # a different bundle ID. - name: Deploy Edge SPNEGO managed policy (macOS) ansible.builtin.copy: dest: /Library/Managed Preferences/com.microsoft.Edge.plist owner: root group: wheel mode: '0644' content: | AuthServerAllowlist {{ chrome_negotiate_domains }} AuthNegotiateDelegateAllowlist {{ chrome_negotiate_domains }} when: ansible_os_family == "Darwin" # ----------------------------------------------------------------------- # 2b. Chrome / Chromium managed policy — Linux # Google Chrome looks in /etc/opt/chrome/policies/managed/ # Chromium looks in /etc/chromium/policies/managed/ # ----------------------------------------------------------------------- - name: Ensure Chrome policy directory exists (Linux) ansible.builtin.file: path: "{{ item }}" state: directory owner: root group: root mode: '0755' loop: - /etc/opt/chrome/policies/managed - /etc/chromium/policies/managed when: ansible_os_family != "Darwin" - name: Deploy Chrome/Chromium SPNEGO managed policy (Linux) ansible.builtin.copy: dest: "{{ item }}/kerberos.json" owner: root group: root mode: '0644' content: | { "AuthServerAllowlist": "{{ chrome_negotiate_domains }}", "AuthNegotiateDelegateAllowlist": "{{ chrome_negotiate_domains }}" } loop: - /etc/opt/chrome/policies/managed - /etc/chromium/policies/managed when: ansible_os_family != "Darwin" # ----------------------------------------------------------------------- # 3. Verify — print where to check policy was applied # ----------------------------------------------------------------------- - name: Show policy verification URL ansible.builtin.debug: msg: > Chrome/Edge policy applied on {{ inventory_hostname }}. Verify at chrome://policy (or edge://policy) in a browser. Expected: AuthServerAllowlist = {{ chrome_negotiate_domains }} handlers: # Chrome must be fully quit and restarted (not just reloaded) for managed # preferences to take effect on macOS. - name: Restart Chrome (macOS) ansible.builtin.debug: msg: > Chrome policy updated on {{ inventory_hostname }}. Ask the user to quit Chrome completely (Cmd-Q) and relaunch. Managed preferences take effect on next launch — no reinstall needed. # ============================================================================ # Play 2 — Windows workstations # Uses runas (the correct become plugin for Windows targets). # No krb5.conf needed — Windows uses built-in SSPI/Kerberos via AD domain join. # Browser policy is written to HKLM registry keys directly (no GPO required). # If the machine later joins the PROLE.ORG Samba AD domain, these keys can be # replaced or supplemented by a proper GPO; win_regedit is idempotent. # ============================================================================ - name: Configure browser SPNEGO policy on Windows workstations hosts: workstations_windows gather_facts: true # No become needed — connect directly as an Administrator account. # win_regedit writes to HKLM without privilege escalation when the # ansible_user is already a local or domain Administrator. vars: chrome_negotiate_domains: "*.prole.org" tasks: # ----------------------------------------------------------------------- # Chrome + Edge managed policy via registry # HKLM\SOFTWARE\Policies\Google\Chrome\AuthServerAllowlist # HKLM\SOFTWARE\Policies\Microsoft\Edge\AuthServerAllowlist # ----------------------------------------------------------------------- - name: Set Chrome SPNEGO policy keys ansible.windows.win_regedit: path: "{{ item.path }}" name: "{{ item.name }}" data: "{{ chrome_negotiate_domains }}" type: String state: present loop: - path: HKLM:\SOFTWARE\Policies\Google\Chrome name: AuthServerAllowlist - path: HKLM:\SOFTWARE\Policies\Google\Chrome name: AuthNegotiateDelegateAllowlist - path: HKLM:\SOFTWARE\Policies\Microsoft\Edge name: AuthServerAllowlist - path: HKLM:\SOFTWARE\Policies\Microsoft\Edge name: AuthNegotiateDelegateAllowlist notify: Restart browsers (Windows) - name: Show policy verification URL ansible.builtin.debug: msg: > Chrome/Edge registry policy applied on {{ inventory_hostname }}. Verify at chrome://policy or edge://policy. Expected: AuthServerAllowlist = {{ chrome_negotiate_domains }} handlers: - name: Restart browsers (Windows) ansible.builtin.debug: msg: > Chrome/Edge policy updated on {{ inventory_hostname }}. Ask the user to close all Chrome and Edge windows and relaunch. Registry policy takes effect on next browser start.