- name: Fail when student password hash is not configured fail: msg: >- Set COURSEWARE_STUDENT_PASSWORD_HASH in the environment before running ./labctl up. Example: export COURSEWARE_STUDENT_PASSWORD_HASH="$(openssl passwd -6 'student-password')" when: courseware_student_password_hash | trim | length == 0 - name: Install terminal prerequisites become: true apt: name: - openssh-server state: present update_cache: true - name: Ensure sshd drop-in directory exists become: true file: path: /etc/ssh/sshd_config.d state: directory mode: "0755" - name: Configure courseware loopback-only sshd policy become: true template: src: sshd-courseware-terminal.conf.j2 dest: /etc/ssh/sshd_config.d/50-courseware-terminal.conf mode: "0644" register: courseware_terminal_sshd_config - name: Validate sshd configuration become: true command: argv: - /usr/sbin/sshd - -t - -f - /etc/ssh/sshd_config changed_when: false - name: Ensure sshd runtime directory exists become: true file: path: /run/sshd state: directory mode: "0755" - name: Start and enable sshd with systemd when available become: true systemd: name: ssh state: started enabled: true when: ansible_service_mgr == "systemd" - name: Check for running sshd when systemd is unavailable become: true command: pgrep -x sshd register: courseware_terminal_sshd_pid changed_when: false failed_when: false when: ansible_service_mgr != "systemd" - name: Reload running sshd when config changed outside systemd become: true command: pkill -HUP -x sshd when: - ansible_service_mgr != "systemd" - courseware_terminal_sshd_pid.rc == 0 - courseware_terminal_sshd_config.changed - name: Start sshd when it is not already running outside systemd become: true command: argv: - /usr/sbin/sshd when: - ansible_service_mgr != "systemd" - courseware_terminal_sshd_pid.rc != 0 - name: Ensure managed terminal user exists become: true user: name: "{{ courseware_student_username }}" password: "{{ courseware_student_password_hash }}" shell: /bin/bash create_home: true state: present - name: Ensure lab 3 workspace root exists become: true file: path: "{{ courseware_lab3_dir }}" state: directory owner: "{{ courseware_student_username }}" group: "{{ courseware_student_username }}" mode: "0755" - name: Ensure lab 3 WhiteRabbitNeo workspace exists become: true file: path: "{{ courseware_lab3_dir }}/WhiteRabbitNeo" state: directory owner: "{{ courseware_student_username }}" group: "{{ courseware_student_username }}" mode: "0755" - name: Write lab 3 workspace README become: true template: src: lab3-workspace-readme.txt.j2 dest: "{{ courseware_lab3_dir }}/README.txt" owner: "{{ courseware_student_username }}" group: "{{ courseware_student_username }}" mode: "0644" - name: Check for repo-local WhiteRabbitNeo base repo stat: path: "{{ courseware_root }}/assets/lab2/WhiteRabbitNeo-V3-7B" register: courseware_lab3_base_repo_stat - name: Check for repo-local WhiteRabbitNeo GGUF directory stat: path: "{{ courseware_root }}/assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF" register: courseware_lab3_gguf_repo_stat - name: Link WhiteRabbitNeo base repo into the student workspace when repo-local assets exist become: true file: src: "{{ courseware_root }}/assets/lab2/WhiteRabbitNeo-V3-7B" dest: "{{ courseware_lab3_dir }}/WhiteRabbitNeo/WhiteRabbitNeo-V3-7B" state: link force: true owner: "{{ courseware_student_username }}" group: "{{ courseware_student_username }}" when: courseware_lab3_base_repo_stat.stat.exists - name: Link WhiteRabbitNeo GGUF directory into the student workspace when repo-local assets exist become: true file: src: "{{ courseware_root }}/assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF" dest: "{{ courseware_lab3_dir }}/WhiteRabbitNeo/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF" state: link force: true owner: "{{ courseware_student_username }}" group: "{{ courseware_student_username }}" when: courseware_lab3_gguf_repo_stat.stat.exists - name: Link WhiteRabbitNeo download helper into the student workspace become: true file: src: "{{ courseware_lab2_dir }}/download_whiterabbitneo-gguf.sh" dest: "{{ courseware_lab3_dir }}/download_whiterabbitneo-gguf.sh" state: link force: true owner: "{{ courseware_student_username }}" group: "{{ courseware_student_username }}" - name: Create contained WeTTY directory file: path: "{{ courseware_wetty_dir }}" state: directory mode: "0755" - name: Install contained WeTTY runtime command: argv: - npm - install - "{{ courseware_wetty_spec }}" args: chdir: "{{ courseware_wetty_dir }}" creates: "{{ courseware_wetty_dir }}/node_modules/.bin/wetty" environment: PATH: "{{ courseware_node_runtime_bin_dir }}:{{ ansible_env.PATH }}" - name: Check loopback sshd listener become: true command: ss -ltn register: courseware_terminal_ss_listeners changed_when: false - name: Assert sshd is loopback-only assert: that: - "'127.0.0.1:22' in courseware_terminal_ss_listeners.stdout" - "'0.0.0.0:22' not in courseware_terminal_ss_listeners.stdout" - "'[::]:22' not in courseware_terminal_ss_listeners.stdout" fail_msg: "sshd must listen only on 127.0.0.1:22 for the browser terminal deployment." - name: Assert WeTTY binary exists stat: path: "{{ courseware_wetty_dir }}/node_modules/.bin/wetty" register: courseware_wetty_bin_stat - name: Fail when WeTTY installation is incomplete fail: msg: "WeTTY was not installed under {{ courseware_wetty_dir }}." when: not courseware_wetty_bin_stat.stat.exists