This commit is contained in:
		
							
								
								
									
										5
									
								
								.ansible-lint.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.ansible-lint.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | --- | ||||||
|  | skip_list: | ||||||
|  |   - fqcn-builtins | ||||||
|  |   - no-jinja-nesting | ||||||
|  |   - experimental | ||||||
							
								
								
									
										16
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | ---- | ||||||
|  | kind: pipeline | ||||||
|  | name: default | ||||||
|  | steps: | ||||||
|  |   - name: integration test | ||||||
|  |     image: python:3.9-buster | ||||||
|  |     environment: | ||||||
|  |       REMOTE_USER: molecule | ||||||
|  |       HCLOUD_TOKEN: | ||||||
|  |         from_secret: HCLOUD_TOKEN | ||||||
|  |     commands: | ||||||
|  |       - apt update && apt install -y pwgen | ||||||
|  |       - mkdir -p /root/.ansible/roles && ln -sr . /root/.ansible/roles/autonomic.new-hetzner | ||||||
|  |       - export INSTANCE_UUID=$(pwgen 8 1) | ||||||
|  |       - pip install -r requirements.txt | ||||||
|  |       - molecule test | ||||||
							
								
								
									
										18
									
								
								.envrc.sample
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.envrc.sample
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | # Your username that you use for accounts on our machines. | ||||||
|  | export REMOTE_USER= | ||||||
|  | export ANSIBLE_USER=$REMOTE_USER | ||||||
|  |  | ||||||
|  | # The path to our pass credentials store | ||||||
|  | export PASSWORD_STORE_DIR= | ||||||
|  |  | ||||||
|  | # The Hetzner Cloud API token for managing our instances | ||||||
|  | # Uncomment the prod/test line below depending on what you're doing | ||||||
|  | # export HCLOUD_TOKEN=$(pass show logins/hetzner/prod/api_key) | ||||||
|  | # export HCLOUD_TOKEN=$(pass show logins/hetzner/test/api_key) | ||||||
|  | export HCLOUD_TOKEN=$(pass show logins/hetzner/cicd/api_key) | ||||||
|  |  | ||||||
|  | # For molecule role testing | ||||||
|  | export INSTANCE_UUID=$RANDOM | ||||||
|  |  | ||||||
|  | # So molecule will show credentials in the logs | ||||||
|  | export MOLECULE_NO_LOG=False | ||||||
							
								
								
									
										16
									
								
								.yamllint.yml
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								.yamllint.yml
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | --- | ||||||
|  | extends: default | ||||||
|  |  | ||||||
|  | yaml-files: | ||||||
|  |   - "*.yaml" | ||||||
|  |   - "*.yml" | ||||||
|  |  | ||||||
|  | ignore: | | ||||||
|  |   .venv | ||||||
|  |   .drone.yml | ||||||
|  |  | ||||||
|  | rules: | ||||||
|  |   line-length: disable | ||||||
|  |   braces: | ||||||
|  |     max-spaces-inside: 1 | ||||||
|  |     level: error | ||||||
							
								
								
									
										15
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | autonomic.new-hetzner: Creates and runs "bootstrapping" roles against a new Hetzner Cloud server | ||||||
|  | Copyright (C) 2022  Autonomic Co-operative <helo@autonomic.zone> | ||||||
|  |  | ||||||
|  | This program is free software: you can redistribute it and/or modify | ||||||
|  | it under the terms of the GNU General Public License as published by | ||||||
|  | the Free Software Foundation, either version 3 of the License, or | ||||||
|  | (at your option) any later version. | ||||||
|  |  | ||||||
|  | This program is distributed in the hope that it will be useful, | ||||||
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | GNU General Public License for more details. | ||||||
|  |  | ||||||
|  | You should have received a copy of the GNU General Public License | ||||||
|  | along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||||
							
								
								
									
										3
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | # autonomic.new-hetzner | ||||||
|  |  | ||||||
|  | [](https://drone.autonomic.zone/autonomic-cooperative/autonomic.new-hetzner) | ||||||
							
								
								
									
										4
									
								
								defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | --- | ||||||
|  | new_hetzner_backups_enabled: true | ||||||
|  | new_hetzner_delete_protection: true | ||||||
|  | new_hetzner_rebuild_protection: true | ||||||
							
								
								
									
										20
									
								
								meta/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								meta/main.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | --- | ||||||
|  | dependencies: [] | ||||||
|  | galaxy_info: | ||||||
|  |   role_name: new_hetzner | ||||||
|  |   namespace: autonomic | ||||||
|  |   author: autonomic | ||||||
|  |   description: | | ||||||
|  |     Creates and runs "bootstrapping" roles against a new Hetzner Cloud server. | ||||||
|  |     The roles that are run are: autonomic.add-users, autonomic.sshd, | ||||||
|  |     autonomic.ufw, autonomic.packages, autonomic.name and autonomic.motd. The | ||||||
|  |     new user passwords are generated and stored in the password store. If | ||||||
|  |     generating a new server, don't forget to add the details to the inventory | ||||||
|  |     listing. | ||||||
|  |   company: Autonomic | ||||||
|  |   license: GPLv3 | ||||||
|  |   min_ansible_version: 2.9 | ||||||
|  |   platforms: | ||||||
|  |     - name: Debian | ||||||
|  |       versions: | ||||||
|  |         - buster | ||||||
							
								
								
									
										17
									
								
								molecule/default/Dockerfile.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								molecule/default/Dockerfile.j2
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | # Molecule managed | ||||||
|  |  | ||||||
|  | {% if item.registry is defined %} | ||||||
|  | FROM {{ item.registry.url }}/{{ item.image }} | ||||||
|  | {% else %} | ||||||
|  | FROM {{ item.image }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% if item.env is defined %} | ||||||
|  | {% for var, value in item.env.items() %} | ||||||
|  | {% if value %} | ||||||
|  | ENV {{ var }} {{ value }} | ||||||
|  | {% endif %} | ||||||
|  | {% endfor %} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | RUN apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; | ||||||
							
								
								
									
										25
									
								
								molecule/default/converge.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								molecule/default/converge.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | --- | ||||||
|  | - name: Converge | ||||||
|  |   hosts: all | ||||||
|  |   vars: | ||||||
|  |     - new_hetzner_server_name: autonomic.new-hetzner-molecule | ||||||
|  |     - add_users_inventory_hostname: autonomic.new-hetzner-molecule | ||||||
|  |     - new_hetzner_server_type: cx11 | ||||||
|  |     - new_hetzner_server_image: debian-10 | ||||||
|  |     - new_hetzner_delete_protection: false | ||||||
|  |     - new_hetzner_rebuild_protection: false | ||||||
|  |   tasks: | ||||||
|  |     - name: Run the role under test | ||||||
|  |       block: | ||||||
|  |         - import_role: | ||||||
|  |             name: autonomic.new-hetzner | ||||||
|  |       always: | ||||||
|  |         - name: Flush all handlers | ||||||
|  |           meta: flush_handlers | ||||||
|  |           ignore_errors: true | ||||||
|  |  | ||||||
|  |         - name: Ensure the server is deleted | ||||||
|  |           hcloud_server: | ||||||
|  |             name: "{{ new_hetzner_server_name }}" | ||||||
|  |             api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" | ||||||
|  |             state: absent | ||||||
							
								
								
									
										37
									
								
								molecule/default/molecule.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								molecule/default/molecule.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | --- | ||||||
|  | dependency: | ||||||
|  |   name: galaxy | ||||||
|  |  | ||||||
|  | driver: | ||||||
|  |   name: docker | ||||||
|  |  | ||||||
|  | platforms: | ||||||
|  |   - name: "autonomic.new-hetzner-${INSTANCE_UUID}" | ||||||
|  |     image: debian:buster | ||||||
|  |  | ||||||
|  | provisioner: | ||||||
|  |   name: ansible | ||||||
|  |  | ||||||
|  | lint: | | ||||||
|  |   set -e | ||||||
|  |   yamllint -c .yamllint.yml . | ||||||
|  |   ansible-lint --exclude .drone.yml -c .ansible-lint.yml . | ||||||
|  |  | ||||||
|  | scenario: | ||||||
|  |   test_sequence: | ||||||
|  |     - lint | ||||||
|  |     - dependency | ||||||
|  |     - cleanup | ||||||
|  |     - destroy | ||||||
|  |     - syntax | ||||||
|  |     - create | ||||||
|  |     - prepare | ||||||
|  |     - converge | ||||||
|  |     # TODO(decentral1se): Disabled for now since there are so many tasks which | ||||||
|  |     # simply always report changed and I'd rather not patch this issue which is | ||||||
|  |     # really something to do with Ansible and not Molecule. | ||||||
|  |     # - idempotence | ||||||
|  |     - side_effect | ||||||
|  |     - verify | ||||||
|  |     - cleanup | ||||||
|  |     - destroy | ||||||
							
								
								
									
										16
									
								
								molecule/default/prepare.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								molecule/default/prepare.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | --- | ||||||
|  | - name: Converge | ||||||
|  |   hosts: all | ||||||
|  |   tasks: | ||||||
|  |     - name: Install python-pip | ||||||
|  |       package: | ||||||
|  |         name: | ||||||
|  |           - python-apt | ||||||
|  |           - python-pip | ||||||
|  |           - openssh-client | ||||||
|  |           - pass | ||||||
|  |         state: present | ||||||
|  |     - name: Install module dependencies | ||||||
|  |       pip: | ||||||
|  |         name: hcloud | ||||||
|  |         state: present | ||||||
							
								
								
									
										36
									
								
								molecule/default/requirements.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								molecule/default/requirements.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | --- | ||||||
|  |  | ||||||
|  | roles: | ||||||
|  |   - name: autonomic.add-users | ||||||
|  |     src: https://git.autonomic.zone/autonomic-cooperative/autonomic.add-users | ||||||
|  |     version: 0.1.0 | ||||||
|  |     scm: git | ||||||
|  |  | ||||||
|  |   - name: autonomic.sshd | ||||||
|  |     src: https://git.autonomic.zone/autonomic-cooperative/autonomic.sshd | ||||||
|  |     version: 0.1.0 | ||||||
|  |     scm: git | ||||||
|  |  | ||||||
|  |   - name: autonomic.ufw | ||||||
|  |     src: https://git.autonomic.zone/autonomic-cooperative/autonomic.ufw | ||||||
|  |     version: 0.1.0 | ||||||
|  |     scm: git | ||||||
|  |  | ||||||
|  |   - name: autonomic.packages | ||||||
|  |     src: https://git.autonomic.zone/autonomic-cooperative/autonomic.packages | ||||||
|  |     version: 0.1.0 | ||||||
|  |     scm: git | ||||||
|  |  | ||||||
|  |   - name: autonomic.name | ||||||
|  |     src: https://git.autonomic.zone/autonomic-cooperative/autonomic.name | ||||||
|  |     version: 0.1.0 | ||||||
|  |     scm: git | ||||||
|  |  | ||||||
|  |   - name: autonomic.motd | ||||||
|  |     src: https://git.autonomic.zone/autonomic-cooperative/autonomic.motd | ||||||
|  |     version: 0.1.0 | ||||||
|  |     scm: git | ||||||
|  |  | ||||||
|  | collections: | ||||||
|  |   - name: hetzner.hcloud | ||||||
|  |     version: 1.6.0 | ||||||
							
								
								
									
										5
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | ansible-lint==6.0.0 | ||||||
|  | ansible==5.4.0 | ||||||
|  | molecule-docker=1.1.0 | ||||||
|  | molecule-hetznercloud==1.3.0 | ||||||
|  | molecule==3.6.1 | ||||||
							
								
								
									
										143
									
								
								tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | |||||||
|  | --- | ||||||
|  | - name: Ensure mandatory variables are configured | ||||||
|  |   assert: | ||||||
|  |     that: "{{ item }} is defined" | ||||||
|  |     fail_msg: "You must define the '{{ item }}' variable" | ||||||
|  |   with_items: | ||||||
|  |     - new_hetzner_server_name | ||||||
|  |     - new_hetzner_server_type | ||||||
|  |     - new_hetzner_server_image | ||||||
|  |  | ||||||
|  | - name: Include resource variables | ||||||
|  |   include_vars: "{{ role_path }}/../../resources/{{ lookup('env', 'MEMBERS_FILE') | default('members.yml', True) }}" | ||||||
|  |  | ||||||
|  | # Note(decentral1se): gives root SSH access for all autonomic members | ||||||
|  | - name: Ensure all Autonomic member SSH keys are registered | ||||||
|  |   hcloud_ssh_key: | ||||||
|  |     name: "{{ item.email }}" | ||||||
|  |     api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" | ||||||
|  |     public_key: "{{ item.ssh_key }}" | ||||||
|  |     state: present | ||||||
|  |   with_items: "{{ members }}" | ||||||
|  |  | ||||||
|  | - name: Create new hetzner cloud instance | ||||||
|  |   hcloud_server: | ||||||
|  |     api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" | ||||||
|  |     image: "{{ new_hetzner_server_image }}" | ||||||
|  |     name: "{{ new_hetzner_server_name }}" | ||||||
|  |     server_type: "{{ new_hetzner_server_type }}" | ||||||
|  |     ssh_keys: "{{ members | map(attribute='email') | list }}" | ||||||
|  |     labels: | ||||||
|  |       managed: ansible | ||||||
|  |     backups: "{{ new_hetzner_backups_enabled }}" | ||||||
|  |     delete_protection: "{{ new_hetzner_delete_protection }}" | ||||||
|  |     rebuild_protection: "{{ new_hetzner_rebuild_protection }}" | ||||||
|  |     state: present | ||||||
|  |   register: new_instance | ||||||
|  |   async: 7200 | ||||||
|  |   poll: 0 | ||||||
|  |  | ||||||
|  | - name: Wait for instance creation to complete | ||||||
|  |   async_status: | ||||||
|  |     jid: "{{ new_instance.ansible_job_id }}" | ||||||
|  |   register: hetzner_job | ||||||
|  |   until: hetzner_job.finished | ||||||
|  |   retries: 300 | ||||||
|  |  | ||||||
|  | # Note(decentral1se): before user accounts are created we connect as root. This | ||||||
|  | # is possible because we registered the SSH keys with Hetzner Cloud and when | ||||||
|  | # the new instance is created, those keys are in /root/authorized_keys | ||||||
|  | - name: Dynamically create root connection details for the new instance | ||||||
|  |   add_host: | ||||||
|  |     hostname: root-new-instance | ||||||
|  |     ansible_host: "{{ hetzner_job.hcloud_server.ipv4_address }}" | ||||||
|  |     ansible_ssh_extra_args: "-o StrictHostKeyChecking=no" | ||||||
|  |     ansible_user: root | ||||||
|  |  | ||||||
|  | - name: "Wait for SSH on {{ new_hetzner_server_name }} to come up on port 22" | ||||||
|  |   wait_for: | ||||||
|  |     port: 22 | ||||||
|  |     host: "{{ hetzner_job.hcloud_server.ipv4_address }}" | ||||||
|  |     search_regex: SSH | ||||||
|  |     delay: 10 | ||||||
|  |  | ||||||
|  | - name: Run the add-users role on the new instance | ||||||
|  |   vars: | ||||||
|  |     members: "../../../resources/members.yml" | ||||||
|  |   delegate_to: root-new-instance | ||||||
|  |   import_role: | ||||||
|  |     name: autonomic.add-users | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
|  |  | ||||||
|  | - name: Run the sshd role on the new instance | ||||||
|  |   delegate_to: root-new-instance | ||||||
|  |   import_role: | ||||||
|  |     name: autonomic.sshd | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
|  |  | ||||||
|  | - name: Run all service restart handlers | ||||||
|  |   delegate_to: root-new-instance | ||||||
|  |   meta: flush_handlers | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
|  |  | ||||||
|  | - name: "Wait for SSH to come up again on {{ sshd_port }}" | ||||||
|  |   wait_for: | ||||||
|  |     port: "{{ sshd_port }}" | ||||||
|  |     host: "{{ hetzner_job.hcloud_server.ipv4_address }}" | ||||||
|  |     search_regex: SSH | ||||||
|  |     delay: 10 | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
|  |  | ||||||
|  | # Note(decentral1se): At this point we're connecting with our own new user | ||||||
|  | # account and using sudo based privilege escalation with the password generated | ||||||
|  | # by pass from the passwords repository. Dog fooding our own connection setup ensures it | ||||||
|  | # works | ||||||
|  | - name: "Dynamically create {{ lookup('env', 'REMOTE_USER') }} connection details for the new instance" | ||||||
|  |   add_host: | ||||||
|  |     hostname: user-new-instance | ||||||
|  |     ansible_host: "{{ hetzner_job.hcloud_server.ipv4_address }}" | ||||||
|  |     ansible_ssh_extra_args: "-o StrictHostKeyChecking=no" | ||||||
|  |     ansible_user: "{{ ansible_user }}" | ||||||
|  |     ansible_port: "{{ sshd_port }}" | ||||||
|  |  | ||||||
|  |     # Note(decentral1se): Ansible refuses to use the correct password without specifying both of these | ||||||
|  |     ansible_sudo_pass: "{{ lookup('passwordstore', 'users/{{ ansible_user }}/sudo/{{ new_hetzner_server_name }}') }}" | ||||||
|  |     ansible_become_password: "{{ lookup('passwordstore', 'users/{{ ansible_user }}/sudo/{{ new_hetzner_server_name }}') }}" | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
|  |  | ||||||
|  | - name: Run the ufw role on the new instance | ||||||
|  |   delegate_to: user-new-instance | ||||||
|  |   become: true | ||||||
|  |   import_role: | ||||||
|  |     name: autonomic.ufw | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
|  |  | ||||||
|  | - name: Run the packages role on the new instance | ||||||
|  |   delegate_to: user-new-instance | ||||||
|  |   become: true | ||||||
|  |   import_role: | ||||||
|  |     name: autonomic.packages | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
|  |  | ||||||
|  | - name: Run the name role on the new instance | ||||||
|  |   delegate_to: user-new-instance | ||||||
|  |   become: true | ||||||
|  |   import_role: | ||||||
|  |     name: autonomic.name | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
|  |  | ||||||
|  | - name: Run the motd role on the new instance | ||||||
|  |   delegate_to: user-new-instance | ||||||
|  |   become: true | ||||||
|  |   import_role: | ||||||
|  |     name: autonomic.motd | ||||||
|  |   tags: | ||||||
|  |     - molecule-notest | ||||||
		Reference in New Issue
	
	Block a user