feat(role): Secure SSH config
This commit is contained in:
		
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,7 @@ | ||||
| # ---> Ansible | ||||
| *.retry | ||||
|  | ||||
| ansible/hosts.yml | ||||
| ansible/group_vars/all/* | ||||
| ansible/roles/20-common-20-ssh/defaults/* | ||||
| ansible/roles/20-common-20-ssh/files/root/.ssh/* | ||||
| !*.example | ||||
|   | ||||
							
								
								
									
										40
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,3 +1,41 @@ | ||||
| # ansible-mail-infra | ||||
|  | ||||
| Set up infrastructure for all things e-mail | ||||
| Set up infrastructure for all things e-mail | ||||
|  | ||||
| ## Prep | ||||
|  | ||||
| On your Ansible controller make sure the `sshpass` binary exists if Ansible has to connect to target machines via SSH username-password authentication instead of SSH key authentication. The binary usually comes with a package of the same name. | ||||
|  | ||||
| We're assuming that you're running Ansible as a Python package inside a virtual environment. Install Ansible like so: | ||||
| ``` | ||||
| pip install ansible | ||||
| ``` | ||||
|  | ||||
| We're also assuming that secrets are stored in a HashiCorp Vault instance to which you have access. These role access Vault via `hvac`, the HashiCorp Vault API client for Python 3.x, see [github.com/hvac/hvac](https://github.com/hvac/hvac) for reference. Install it like so: | ||||
| ``` | ||||
| pip install hvac | ||||
| ``` | ||||
|  | ||||
| ## Vars | ||||
|  | ||||
| * For Vault access copy [ansible/roles/20-common-20-ssh/defaults/main.yml.example](ansible/roles/20-common-20-ssh/defaults/main.yml.example) to a proper `ansible/roles/20-common-20-ssh/defaults/main.yml` and set Vault credentials and locations as needed. | ||||
|  | ||||
| * Create your inventory, copy [ansible/hosts.yml.example](ansible/hosts.yml.example) into a proper `ansible/hosts.yml` file with at least one host in host group `all`. | ||||
|  | ||||
| * Replace [ansible/group_vars/all/vars.yml.example](ansible/group_vars/all/vars.yml.example) with a proper `ansible/group_vars/all/vars.yml` file and set at least `ansible_user`. It defaults to `ansible_user: 'root'`. | ||||
|  | ||||
| * In [ansible/roles/20-common-20-ssh/files/root/.ssh](ansible/roles/20-common-20-ssh/files/root/.ssh) copy both [authorized_keys.example](ansible/roles/20-common-20-ssh/files/root/.ssh/authorized_keys.example) and [known_hosts.example](ansible/roles/20-common-20-ssh/files/root/.ssh/known_hosts.example) to proper files. They contain SSH authorized_keys and public SSH host keys you want installed on target machines. | ||||
|  | ||||
| ## Run it | ||||
|  | ||||
| On first run execute it like so: | ||||
| ``` | ||||
| ansible-playbook --tags 'first_run' --inventory hosts.yml playbook.yml | ||||
| ``` | ||||
|  | ||||
| The `first_run` tag ensures that `ansible_password` variable gets set which in turn causes Ansible to log in to target machines via SSH username-password authentication. | ||||
|  | ||||
| On subsequent runs like so: | ||||
| ``` | ||||
| ansible-playbook --inventory hosts.yml playbook.yml | ||||
| ``` | ||||
|   | ||||
							
								
								
									
										1
									
								
								ansible/group_vars/all/vars.yml.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ansible/group_vars/all/vars.yml.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| ansible_user: 'root' | ||||
							
								
								
									
										3
									
								
								ansible/hosts.yml.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								ansible/hosts.yml.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| all: | ||||
|     hosts: | ||||
|         fully.qualified.domain.name: | ||||
							
								
								
									
										4
									
								
								ansible/playbook.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								ansible/playbook.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| - name: 'Set up SSH' | ||||
|   hosts: all | ||||
|   roles: | ||||
|     - role: 20-common-20-ssh | ||||
							
								
								
									
										6
									
								
								ansible/roles/20-common-20-ssh/defaults/main.yml.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ansible/roles/20-common-20-ssh/defaults/main.yml.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| root_home_dir_abs: '/root' | ||||
| ansible_hashi_vault_auth_method: 'token' | ||||
| ansible_hashi_vault_token: 'hvs.xxxxxxxxxx' | ||||
| ansible_hashi_vault_engine_mount_point: 'kv' | ||||
| ansible_hashi_vault_token_validate: 'false' | ||||
| ansible_hashi_vault_url: 'http://localhost:8200/' | ||||
							
								
								
									
										6
									
								
								ansible/roles/20-common-20-ssh/files/etc/ssh/sshd_config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ansible/roles/20-common-20-ssh/files/etc/ssh/sshd_config
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| PubkeyAuthentication yes | ||||
| PasswordAuthentication no | ||||
| PermitEmptyPasswords no | ||||
| UsePAM no | ||||
| PermitRootLogin without-password | ||||
| PrintMotd yes | ||||
| @@ -0,0 +1 @@ | ||||
| ssh-ed25519 AAAAC3NzaC1lZ... comment | ||||
| @@ -0,0 +1 @@ | ||||
| host.name ssh-rsa AAAAB3NzaC1... | ||||
							
								
								
									
										5
									
								
								ansible/roles/20-common-20-ssh/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ansible/roles/20-common-20-ssh/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| - name: 'Restart sshd' | ||||
|   listen: 'Restart sshd.service' | ||||
|   ansible.builtin.service: | ||||
|     name: 'sshd.service' | ||||
|     state: 'restarted' | ||||
							
								
								
									
										85
									
								
								ansible/roles/20-common-20-ssh/tasks/20-ssh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								ansible/roles/20-common-20-ssh/tasks/20-ssh.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| - name: 'Get secrets' | ||||
|   no_log: 'true' | ||||
|   loop_control: | ||||
|     loop_var: 'server' | ||||
|   with_community.hashi_vault.vault_kv2_get: | ||||
|     - '{{ inventory_hostname | split(".") | reverse | join("/") }}/os/root/creds' | ||||
|   ansible.builtin.set_fact: | ||||
|     vault_data: '{{ server.secret }}' | ||||
|  | ||||
|  | ||||
|  | ||||
| - name: 'If first run: set SSH password' | ||||
|   tags: | ||||
|     - 'first_run' | ||||
|     - 'never' | ||||
|   no_log: 'true' | ||||
|   ansible.builtin.set_fact: | ||||
|     ansible_password: '{{ vault_data.initial_password }}' | ||||
|  | ||||
|  | ||||
|  | ||||
| - name: 'Make sure ''{{ root_home_dir_abs }}/.ssh'' exists with correct permissions' | ||||
|   file: | ||||
|     path: '{{ root_home_dir_abs }}/.ssh' | ||||
|     state: 'directory' | ||||
|     mode: 'u=rwX,go=' | ||||
|     owner: '{{ ansible_user }}' | ||||
|     group: '{{ ansible_user }}' | ||||
|     recurse: 'yes' | ||||
|  | ||||
|  | ||||
|  | ||||
| - name: 'Copy ''authorized_keys'' file to server' | ||||
|   copy: | ||||
|     src: 'root/.ssh/authorized_keys' | ||||
|     dest: '{{ root_home_dir_abs }}/.ssh/authorized_keys' | ||||
|     mode: '0600' | ||||
|     owner: '{{ ansible_user }}' | ||||
|     group: '{{ ansible_user }}' | ||||
|  | ||||
|  | ||||
|  | ||||
| - name: 'Copy ''known_hosts'' file to server' | ||||
|   copy: | ||||
|     src: 'root/.ssh/known_hosts' | ||||
|     dest: '{{ root_home_dir_abs }}/.ssh/known_hosts' | ||||
|     mode: '0600' | ||||
|     owner: '{{ ansible_user }}' | ||||
|     group: '{{ ansible_user }}' | ||||
|  | ||||
|  | ||||
|  | ||||
| - name: 'If on Red Hat or derivative OS: secure sshd' | ||||
|   register: 'rv_secure_sshd' | ||||
|   when: '(ansible_facts[''os_family''] | lower == ''redhat'')' | ||||
|   blockinfile: | ||||
|     block: "{{ lookup('file', 'etc/ssh/sshd_config') }}" | ||||
|     dest: "/etc/ssh/sshd_config" | ||||
|     state: 'present' | ||||
|     insertbefore: 'BOF' | ||||
|     marker: '{mark}' | ||||
|     marker_begin: '####### Managed remotely via config management ####### quico-ops start' | ||||
|     marker_end: '####### Managed remotely via config management ####### quico-ops end' | ||||
|     validate: '/usr/sbin/sshd -T -f %s' | ||||
|   notify: | ||||
|     - 'Restart sshd.service' | ||||
|  | ||||
|  | ||||
|  | ||||
| - name: 'Flush handlers' | ||||
|   meta: flush_handlers | ||||
|  | ||||
|  | ||||
|  | ||||
| - name: 'Reset connection' | ||||
|   ansible.builtin.meta: 'reset_connection' | ||||
|  | ||||
|  | ||||
|  | ||||
| - name: 'Wait for SSH connection to return' | ||||
|   when: '(rv_secure_sshd.changed)' | ||||
|   ansible.builtin.wait_for_connection: | ||||
|     connect_timeout: '1' | ||||
|     delay: '1' | ||||
|     sleep: '2' | ||||
							
								
								
									
										3
									
								
								ansible/roles/20-common-20-ssh/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								ansible/roles/20-common-20-ssh/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| - import_tasks: '20-ssh.yml' | ||||
|   tags: | ||||
|     - 'first_run' | ||||
		Reference in New Issue
	
	Block a user