Add the prepare-server playbook
Two plays: bootstrap as the provider account (ubuntu, over bootstrap.pem), then everything else as the user that first play just created. The split is forced rather than stylistic -- the second play logs in as that user, so its authorized_keys entry and its NOPASSWD sudoers entry both have to exist before it starts. That is why the key tasks live in the 'user' role and 'wheel' runs in the first play: move either into play 2 and it fails with an auth error or "Missing sudo password". Both accounts and both keys are variables. The inventory sits beside the playbook and is read via ansible.cfg, which Ansible picks up from the current working directory rather than the playbook's, so it has to be run from this directory. bootstrap.pem is gitignored -- it is the provider key for the host.
This commit is contained in:
1 parent
f2d1b606ce
commit
97d6892c83
11 files changed
+445
No files matched your search
@@ -0,0 +1,4 @@
|
||||
Host {{ server }} {{ server }}.tanshu.com
|
||||
HostName {{ server }}.tanshu.com
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
User {{ user }}
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
# Tasks for the common role.
|
||||
#
|
||||
# Runs in the second play, against the account the server was prepared for
|
||||
# ({{ user }}). The .ssh directory and the authorized_keys entries are already
|
||||
# in place by the time this runs: the user role installs them during the
|
||||
# bootstrap play, because they are what makes logging in as {{ user }} --
|
||||
# and therefore this play -- possible in the first place.
|
||||
|
||||
- name: Update the sshd config file to disable password logins
|
||||
ansible.builtin.replace:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: "(\\s+)#PasswordAuthentication yes(\\s+.*)?$"
|
||||
replace: "\\1PasswordAuthentication no\\2"
|
||||
backup: true
|
||||
|
||||
- name: Check if .ssh config file exists
|
||||
ansible.builtin.stat:
|
||||
path: "/home/{{ user }}/.ssh/config"
|
||||
register: config_status
|
||||
|
||||
- name: No need to upload the .ssh config
|
||||
when: config_status.stat.exists
|
||||
ansible.builtin.debug:
|
||||
msg: No need to upload the .ssh config as it already exists.
|
||||
|
||||
- name: Upload the .ssh config file
|
||||
when: not config_status.stat.exists
|
||||
ansible.builtin.template:
|
||||
src: "files/config"
|
||||
dest: "/home/{{ user }}/.ssh/config"
|
||||
group: "{{ user }}"
|
||||
owner: "{{ user }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Check if ed25519 key exists
|
||||
ansible.builtin.stat:
|
||||
path: "/home/{{ user }}/.ssh/id_ed25519"
|
||||
register: key_status
|
||||
|
||||
- name: No need to generate new ed25519 key
|
||||
when: key_status.stat.exists
|
||||
ansible.builtin.debug:
|
||||
msg: No need to generate new ed25519 key as it already exists.
|
||||
|
||||
- name: Generate new id_ed25519 key
|
||||
when: not key_status.stat.exists
|
||||
become: true
|
||||
become_user: "{{ user }}"
|
||||
ansible.builtin.shell: ssh-keygen -t ed25519 -q -f "/home/{{ user }}/.ssh/id_ed25519" -C "{{ user }}@{{ inventory_hostname }} $(date '+%Y.%m.%d')" -N ""
|
||||
changed_when: true
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
# Tasks for the user role.
|
||||
#
|
||||
# Runs in the first (bootstrap) play, against the provider's bootstrap account
|
||||
# ({{ bootstrap_user }}), and does the two things that must happen before
|
||||
# anything else can: create the account this server is being prepared for
|
||||
# ({{ user }}), and install the public keys that make it reachable. Every
|
||||
# later task logs in as {{ user }} rather than as the bootstrap account, so
|
||||
# without those keys the second play cannot connect at all.
|
||||
#
|
||||
# Authentication is key based only: no password is set, which leaves the
|
||||
# account locked for password logins. The shell, group membership and the rest
|
||||
# of the account's configuration belong to the wheel/zsh roles. Setting a
|
||||
# shell here would fight the zsh role and change it back on every run, so this
|
||||
# role deliberately does not touch it.
|
||||
|
||||
- name: Create the desired user
|
||||
ansible.builtin.user:
|
||||
name: "{{ user }}"
|
||||
create_home: true
|
||||
state: present
|
||||
|
||||
- name: Ensure the user's SSH directory exists
|
||||
ansible.builtin.file:
|
||||
path: "/home/{{ user }}/.ssh"
|
||||
state: directory
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
mode: "0700"
|
||||
|
||||
- name: Ensure authorized_keys file exists
|
||||
ansible.builtin.copy:
|
||||
content: ""
|
||||
dest: "/home/{{ user }}/.ssh/authorized_keys"
|
||||
force: false
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
mode: "0644"
|
||||
|
||||
# The second play logs in as {{ user }} with the private half of this key on
|
||||
# the control node, so this entry is what makes that login possible. It is
|
||||
# installed here, during the bootstrap play, rather than being left to depend
|
||||
# on one of the named keys below happening to be the control node's own.
|
||||
#
|
||||
# lineinfile rather than ansible.builtin.authorized_key: the latter redirects
|
||||
# to ansible.posix, whose 2.1.0 module imports a deprecated helper and emits a
|
||||
# deprecation warning on every run. This is the same guarantee without it.
|
||||
- name: Authorize the control node's key for the new user
|
||||
ansible.builtin.lineinfile:
|
||||
dest: "/home/{{ user }}/.ssh/authorized_keys"
|
||||
line: "{{ lookup('file', user_key ~ '.pub') | trim }}"
|
||||
create: true
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Check Terminus public key
|
||||
ansible.builtin.shell: "grep -c \"{{ terminus_key }}\" /home/{{ user }}/.ssh/authorized_keys || true"
|
||||
register: terminuskey_test
|
||||
changed_when: false
|
||||
|
||||
- name: Add Terminus public key
|
||||
ansible.builtin.lineinfile:
|
||||
dest: "/home/{{ user }}/.ssh/authorized_keys"
|
||||
line: "{{ terminus_key }}"
|
||||
when: terminuskey_test.stdout == "0"
|
||||
|
||||
- name: Check clair public key
|
||||
ansible.builtin.shell: "grep -c \"{{ clair_key }}\" /home/{{ user }}/.ssh/authorized_keys || true"
|
||||
register: clairkey_test
|
||||
changed_when: false
|
||||
|
||||
- name: Add clair public key
|
||||
ansible.builtin.lineinfile:
|
||||
dest: "/home/{{ user }}/.ssh/authorized_keys"
|
||||
line: "{{ clair_key }}"
|
||||
when: clairkey_test.stdout == "0"
|
||||
|
||||
- name: Check Anjin public key
|
||||
ansible.builtin.shell: "grep -c \"{{ anjin_key }}\" /home/{{ user }}/.ssh/authorized_keys || true"
|
||||
register: anjin_key_test
|
||||
changed_when: false
|
||||
|
||||
- name: Add Anjin public key
|
||||
ansible.builtin.lineinfile:
|
||||
dest: "/home/{{ user }}/.ssh/authorized_keys"
|
||||
line: "{{ anjin_key }}"
|
||||
when: anjin_key_test.stdout == "0"
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
- name: Ensure group wheel exists
|
||||
ansible.builtin.group:
|
||||
name: wheel
|
||||
state: present
|
||||
|
||||
- name: Allow wheel group to have passwordless sudo
|
||||
ansible.builtin.copy:
|
||||
# A block scalar, so the file ends with a newline. With an inline string
|
||||
# there is no trailing terminator and visudo rejects it with
|
||||
# "syntax error: missing line terminator at end of file".
|
||||
content: |
|
||||
%wheel ALL=(ALL) NOPASSWD: ALL
|
||||
dest: /etc/sudoers.d/wheel
|
||||
mode: "0440"
|
||||
validate: "/usr/sbin/visudo -cf %s"
|
||||
|
||||
- name: Adding user to group wheel
|
||||
ansible.builtin.user:
|
||||
name: "{{ user }}"
|
||||
groups: wheel
|
||||
append: true
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
- name: Install ZSH and required packages
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- zsh
|
||||
- tmux
|
||||
- powerline
|
||||
- git
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Change default shell to ZSH
|
||||
ansible.builtin.user:
|
||||
name: "{{ user }}"
|
||||
shell: /bin/zsh
|
||||
|
||||
- name: Check if oh-my-zsh is already installed
|
||||
ansible.builtin.stat:
|
||||
path: "/home/{{ user }}/.oh-my-zsh"
|
||||
register: oh_my_zsh_stat
|
||||
|
||||
- name: Install oh-my-zsh
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/robbyrussell/oh-my-zsh.git
|
||||
dest: "/home/{{ user }}/.oh-my-zsh"
|
||||
depth: 1
|
||||
when: not oh_my_zsh_stat.stat.exists
|
||||
become: true
|
||||
become_user: "{{ user }}"
|
||||
|
||||
- name: Install zsh-autosuggestions plugin
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/zsh-users/zsh-autosuggestions
|
||||
dest: "/home/{{ user }}/.oh-my-zsh/custom/plugins/zsh-autosuggestions"
|
||||
depth: 1
|
||||
become: true
|
||||
become_user: "{{ user }}"
|
||||
|
||||
- name: Install zsh-syntax-highlighting plugin
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/zsh-users/zsh-syntax-highlighting.git
|
||||
dest: "/home/{{ user }}/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting"
|
||||
depth: 1
|
||||
become: true
|
||||
become_user: "{{ user }}"
|
||||
|
||||
- name: Ensure .zshrc exists (copy template if needed)
|
||||
ansible.builtin.copy:
|
||||
src: "/home/{{ user }}/.oh-my-zsh/templates/zshrc.zsh-template"
|
||||
dest: "/home/{{ user }}/.zshrc"
|
||||
remote_src: true
|
||||
force: false
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Set ZSH theme to agnoster
|
||||
ansible.builtin.replace:
|
||||
path: "/home/{{ user }}/.zshrc"
|
||||
regexp: 'ZSH_THEME="robbyrussell"'
|
||||
replace: 'ZSH_THEME="agnoster"'
|
||||
|
||||
- name: Enable ZSH plugins
|
||||
ansible.builtin.replace:
|
||||
path: "/home/{{ user }}/.zshrc"
|
||||
regexp: 'plugins=\(git\)'
|
||||
replace: "plugins=(git zsh-autosuggestions zsh-syntax-highlighting)"
|
||||
|
||||
- name: Add custom aliases to .zshrc
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/home/{{ user }}/.zshrc"
|
||||
line: "alias ls='ls --color=auto --indicator-style=slash --human-readable --format=long'"
|
||||
create: true
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
Reference in new issue
Block a user