Examples · Cloud & Cybersecurity
Harden SSH with Ansible
Qwen3-Coder writes an Ansible playbook that turns off password logins over SSH. Then you check it, and run it on a lab VM without locking yourself out.
Advanced · 60 min
Why
On a server reachable from the internet, SSH should accept keys only: no passwords, no root. With password logins, such a server receives thousands of guessing attempts a day. Doing it by hand on one server is easy; on fifty, you use Ansible.
What you need
A Linux virtual machine for the lab (Ubuntu 24.04), your SSH key already working on it, and Ansible on your computer: pip install ansible.
The model
Qwen3-Coder 30B (qwen3-coder-30b). You can use the chat: no code is needed to ask.
The request
Write an Ansible playbook for Ubuntu 24.04 that hardens SSH: no password login, no root login, only the members of the group
ssh-usersmay log in, at most 3 authentication attempts. Add the user{{ ansible_user }}to the group first. Put the settings in a drop-in file in/etc/ssh/sshd_config.d/, validate it withsshd -tbefore it is applied, and restart ssh with a handler. It must be idempotent.
Read the answer line by line before you run anything: you are about to change how you log in to that machine.
Check it against this one
A correct playbook looks like this:
- name: Harden SSH
hosts: lab
become: true
tasks:
- name: Group allowed to log in over SSH
ansible.builtin.group:
name: ssh-users
state: present
- name: Add the current user to it
ansible.builtin.user:
name: "{{ ansible_user }}"
groups: ssh-users
append: true
- name: Hardening settings, checked by sshd before they are put in place
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/10-hardening.conf
owner: root
group: root
mode: "0644"
content: |
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
AllowGroups ssh-users
MaxAuthTries 3
validate: /usr/sbin/sshd -t -f %s
notify: Restart ssh
handlers:
- name: Restart ssh
ansible.builtin.service:
name: ssh
state: restarted
Why 10-: in sshd_config the first value wins, and on many cloud images a file called 50-cloud-init.conf turns password logins back on. Files are read in alphabetical order, so 10- comes first.
Run it
inventory.ini:
[lab]
lab-vm ansible_host=192.168.56.10 ansible_user=student
First a dry run, which changes nothing and shows the differences:
ansible-playbook -i inventory.ini harden-ssh.yml --check --diff
Then, with a second SSH session open on the VM in case something goes wrong:
ansible-playbook -i inventory.ini harden-ssh.yml
Prove it
ssh -o PubkeyAuthentication=no student@192.168.56.10
must answer Permission denied (publickey). On the VM, sudo sshd -T | grep -Ei 'passwordauth|permitroot|allowgroups|maxauthtries' shows the values in force.
Exercise
- Run the playbook twice. The second time Ansible must report
changed=0: that is what idempotent means. - Ask the model to add fail2ban, and to explain what it protects from that the keys do not.
- Ask the model what happens if you forget the step with
ansible_user, then explain why.
