idk
This commit is contained in:
1 parent
b745376a65
commit
c49055d4dd
3 files changed
+126
-5
No files matched your search
+22
-1
@@ -28,10 +28,31 @@ cd ansible
|
||||
ansible-galaxy collection install -r requirements.yml
|
||||
```
|
||||
|
||||
## Bootstrap (once per fresh VPS)
|
||||
|
||||
`bootstrap.yml` connects as **root** (it overrides whatever `ansible_user`
|
||||
the inventory has), creates the `ansible` automation user — SSH key only,
|
||||
locked password, passwordless sudo — *proves the new user can log in and
|
||||
sudo* in a second play, and only then locks sshd down: key-only auth
|
||||
(`PasswordAuthentication no`) and **root SSH login disabled**
|
||||
(`PermitRootLogin no`). Break-glass access after that is the provider's
|
||||
console, not SSH:
|
||||
|
||||
```bash
|
||||
cp inventory.example.yml inventory.yml
|
||||
ansible-playbook bootstrap.yml # root already has your key
|
||||
ansible-playbook bootstrap.yml -k # root password login (needs sshpass)
|
||||
# custom key or username:
|
||||
ansible-playbook bootstrap.yml -e bootstrap_ssh_public_key_file=~/.ssh/vpnhub.pub
|
||||
```
|
||||
|
||||
Everything after this runs as `ansible` (already the inventory example's
|
||||
default).
|
||||
|
||||
## First-time setup
|
||||
|
||||
```bash
|
||||
cp inventory.example.yml inventory.yml # set host/user
|
||||
cp inventory.example.yml inventory.yml # set host/user (if not done above)
|
||||
cp group_vars/vpnhub.example.yml group_vars/vpnhub.yml
|
||||
$EDITOR group_vars/vpnhub.yml # DC IPs, Duo keys, secrets
|
||||
ansible-vault encrypt group_vars/vpnhub.yml # both files are gitignored anyway
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
# One-job playbook: connect as root, create the automation user that every
|
||||
# later run uses — key-only auth, passwordless sudo — verify it actually
|
||||
# works, then lock sshd down to keys. Run once per fresh VPS:
|
||||
#
|
||||
# ansible-playbook bootstrap.yml # root already has your key
|
||||
# ansible-playbook bootstrap.yml -k # root still uses a password
|
||||
# # (needs sshpass installed)
|
||||
#
|
||||
# Afterwards set `ansible_user: ansible` in inventory.yml and never
|
||||
# connect as root again. Overridable with -e:
|
||||
# bootstrap_user=ansible
|
||||
# bootstrap_ssh_public_key_file=~/.ssh/id_ed25519.pub
|
||||
# bootstrap_harden_ssh=true
|
||||
#
|
||||
# Safe by construction: play 2 logs in AS the new user and proves key +
|
||||
# sudo work; sshd is only hardened in play 3, which never runs if play 2
|
||||
# fails. Play 3 disables root SSH login entirely — from then on the only
|
||||
# way in is the automation user's key (console access via the RackNerd
|
||||
# panel remains the break-glass path).
|
||||
|
||||
- name: Create the automation user (connects as root)
|
||||
hosts: vpnhub
|
||||
gather_facts: false
|
||||
vars:
|
||||
ansible_user: root # play var — outranks inventory on purpose
|
||||
bootstrap_user: ansible
|
||||
bootstrap_ssh_public_key_file: "~/.ssh/id_ed25519.pub"
|
||||
tasks:
|
||||
- name: Create the user (no password hash set, account password locked)
|
||||
ansible.builtin.user:
|
||||
name: "{{ bootstrap_user }}"
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
password_lock: true
|
||||
|
||||
- name: Install the SSH public key
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ bootstrap_user }}"
|
||||
key: "{{ lookup('file', bootstrap_ssh_public_key_file) }}"
|
||||
|
||||
- name: Grant passwordless sudo
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/sudoers.d/{{ bootstrap_user }}"
|
||||
content: "{{ bootstrap_user }} ALL=(ALL) NOPASSWD:ALL\n"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
validate: /usr/sbin/visudo -cf %s
|
||||
|
||||
- name: Prove the new user works before touching sshd
|
||||
hosts: vpnhub
|
||||
gather_facts: false
|
||||
become: true
|
||||
vars:
|
||||
bootstrap_user: ansible
|
||||
ansible_user: "{{ bootstrap_user }}"
|
||||
tasks:
|
||||
- name: Key login and passwordless sudo both work
|
||||
ansible.builtin.command: whoami
|
||||
register: bootstrap_whoami
|
||||
changed_when: false
|
||||
failed_when: bootstrap_whoami.stdout != "root"
|
||||
|
||||
- name: Lock sshd down to key-only auth, no root login
|
||||
hosts: vpnhub
|
||||
gather_facts: false
|
||||
become: true
|
||||
vars:
|
||||
bootstrap_user: ansible
|
||||
ansible_user: "{{ bootstrap_user }}"
|
||||
bootstrap_harden_ssh: true
|
||||
handlers:
|
||||
- name: restart sshd
|
||||
ansible.builtin.service:
|
||||
name: ssh
|
||||
state: restarted
|
||||
tasks:
|
||||
- name: Key-only sshd drop-in
|
||||
# Named 01-* on purpose: sshd honors the FIRST value it reads per
|
||||
# keyword, and cloud-init images ship 50-cloud-init.conf that may
|
||||
# set PasswordAuthentication yes — we must sort before it.
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/ssh/sshd_config.d/01-vpnhub.conf
|
||||
content: |
|
||||
PasswordAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
PermitRootLogin no
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: bootstrap_harden_ssh | bool
|
||||
notify: restart sshd
|
||||
|
||||
- name: Validate full sshd config before the restart fires
|
||||
ansible.builtin.command: /usr/sbin/sshd -t
|
||||
when: bootstrap_harden_ssh | bool
|
||||
changed_when: false
|
||||
@@ -5,7 +5,9 @@ all:
|
||||
hosts:
|
||||
racknerd:
|
||||
ansible_host: vpn.atlashorizon.net
|
||||
ansible_user: root
|
||||
# Non-root instead? Use:
|
||||
# ansible_user: nick
|
||||
# ansible_become: true
|
||||
# Normal state — the user bootstrap.yml creates:
|
||||
ansible_user: ansible
|
||||
# ansible_ssh_private_key_file: ~/.ssh/id_ed25519
|
||||
#
|
||||
# bootstrap.yml ignores ansible_user and connects as root
|
||||
# itself, so this file can point at "ansible" from day one.
|
||||
Reference in new issue
Block a user