├── .gitignore ├── README.md ├── ansible.cfg ├── droplet_create.sh ├── inventory └── hosts.example └── setup.yml /.gitignore: -------------------------------------------------------------------------------- 1 | keys/* 2 | inventory/hosts 3 | .DS_Store 4 | .python-version 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Urbit Automation 2 | 3 | Trivially start a DigitalOcean droplet and start an Urbit planet. 4 | 5 | WARNING: This script could result in the need for a breach if misused or if you hit an unknown bug. Use carefully. 6 | 7 | This project is intended to simplify initial boot of an Urbit planet. After initial boot the user will need to manage the planet manually as normal. The playbook avoids restarting a running planet, so running multiple times should not cause an issue. 8 | 9 | [![awesome urbit badge](https://img.shields.io/badge/~-awesome%20urbit-lightgrey)](https://github.com/urbit/awesome-urbit) 10 | 11 | ## Prerequisite 12 | 13 | - Urbit [planet name](https://urbit.live/) 14 | - Urbit [network key](https://bridge.urbit.org/) for the planet 15 | - [DigitalOcean token](https://www.digitalocean.com/docs/apis-clis/api/create-personal-access-token/) 16 | 17 | ## Dependencies 18 | 19 | - [Ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-rhel-centos-or-fedora) 20 | - [jq](https://stedolan.github.io/jq/download/) 21 | 22 | ## Configuration 23 | 24 | The droplet that is created uses a hard coded configuration that can be found and modified [here](https://github.com/nisfeb/urbit-boot-automation/blob/master/droplet_create.sh#L78). There is [an issue](https://github.com/nisfeb/urbit-boot-automation/issues/4) created to make this configurable with defaults. In the mean time, modification of this file before running the script will allow you to specify your exact preferences. Note that the syntax for droplet sizing is specific. You can find the DigitalOcean table specifying limits and size types [here](https://www.digitalocean.com/docs/droplets/). 25 | 26 | ## How it Works 27 | 28 | 1. `./droplet_create.sh -n PLANET_NAME -t DIGITAL_OCEAN_TOKEN -k URBIT_NETWORK_KEY` takes the planet name, network key, and DigitalOcean token and creates a properly sized and named Droplet with a properly configured `authorized_keys` entry for Ansible to use. It then adds the planet to inventory with the correct variables. If you want to SSH into the host, you can use the keys that are generated and placed in the `keys` directory. _This script currently starts a default droplet and does not harden the server with additional security settings._ 29 | 2. `ansible-playbook -i inventory/hosts setup.yml` uses the settings in `inventory/hosts` to boot each planet if it has not yet been booted. If you get an error like `Failed to connect to the host via ssh` wait a moment and run the playbook again. The Droplet may just not be fully initiailized. 30 | 31 | ## Contributing 32 | 33 | WARNING: The `droplet_create.sh` script adds server details to the `inventory/hosts` file. This data should _not_ be pushed into a public repository. Ensure that you remove this information before creating a PR or pushing to a public fork. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | retry_files_enabled = False 3 | host_key_checking = False 4 | 5 | [connection] 6 | pipelining = True 7 | -------------------------------------------------------------------------------- /droplet_create.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ######################### 4 | # The command line help # 5 | ######################### 6 | show_help() { 7 | echo "Usage: $0 [option...]" >&2 8 | echo 9 | echo " -n, --name Planet name (without sig ~)" 10 | echo " -t, --token DigitalOcean token" 11 | echo " -k, --key Urbit network key" 12 | echo 13 | exit 1 14 | } 15 | 16 | POSITIONAL=() 17 | while [[ $# -gt 0 ]] 18 | do 19 | key="$1" 20 | 21 | case $key in 22 | -n|--name) 23 | PLANET_NAME="$2" 24 | shift # past argument 25 | shift # past value 26 | ;; 27 | -t|--token) 28 | DO_TOKEN="$2" 29 | shift # past argument 30 | shift # past value 31 | ;; 32 | -k|--key) 33 | URBIT_KEY="$2" 34 | shift # past argument 35 | shift # past value 36 | ;; 37 | *) # unknown option 38 | POSITIONAL+=("$1") # save it in an array for later 39 | shift # past argument 40 | ;; 41 | esac 42 | done 43 | set -- "${POSITIONAL[@]}" # restore positional parameters 44 | 45 | echo "PLANET NAME = ${PLANET_NAME}" 46 | echo "DO TOKEN = ${DO_TOKEN}" 47 | echo "URBIT KEY = ${URBIT_KEY}" 48 | if [[ -n $1 ]]; then 49 | echo "Last line of file specified as non-opt/last argument:" 50 | tail -1 "$1" 51 | fi 52 | 53 | create_droplet() { 54 | # Generate SSH Key 55 | mkdir -p ./keys 56 | ssh-keygen -t rsa -b 2048 -f ./keys/$PLANET_NAME -C $PLANET_NAME -q -N "" 57 | # Add SSH key to your DO account and capture the reponse JSON 58 | key_response=$(curl -X POST https://api.digitalocean.com/v2/account/keys \ 59 | -H "Content-Type: application/json" \ 60 | -H "Authorization: Bearer $DO_TOKEN" \ 61 | --data-binary @- <> ./inventory/hosts 105 | } 106 | 107 | create_droplet 108 | -------------------------------------------------------------------------------- /inventory/hosts.example: -------------------------------------------------------------------------------- 1 | # To connect using a non-root user (and elevate to root with sudo later), 2 | # replace `ansible_ssh_user=root` with something like this: `ansible_ssh_user=username become=true become_user=root` 3 | # 4 | # For improved Ansible performance, SSH pipelining is enabled by default in `ansible.cfg`. 5 | # If this causes SSH connection troubles, disable it by adding `ansible_ssh_pipelining=False` 6 | # to the host line below or by adding `ansible_ssh_pipelining: False` to your variables file. 7 | # 8 | # If you're running this Ansible playbook on the same server as the one you're installing to, 9 | # consider adding an additional `ansible_connection=local` argument below. 10 | 11 | [planet_servers] 12 | -------------------------------------------------------------------------------- /setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Set up an Urbit planet" 3 | hosts: "{{ target if target is defined else 'planet_servers' }}" 4 | become: true 5 | 6 | tasks: 7 | # Add Swap to the node 8 | - name: Create swap space 9 | command: dd if=/dev/zero of=/extraswap bs=1M count=2048 10 | when: ansible_swaptotal_mb < 1000 11 | 12 | - name: Make swap 13 | command: mkswap /extraswap 14 | when: ansible_swaptotal_mb < 1000 15 | 16 | - name: Add to fstab 17 | action: lineinfile dest=/etc/fstab regexp="extraswap" line="/extraswap none swap sw 0 0" state=present 18 | 19 | - name: Turn swap on 20 | command: swapon -a 21 | 22 | # Configure UFW 23 | # Deny everything and enable UFW 24 | - ufw: 25 | state: enabled 26 | policy: allow 27 | # Allow ssh 28 | - ufw: 29 | rule: allow 30 | port: ssh 31 | proto: tcp 32 | # Limit ssh (block IPs that try more 6 times in 30 seconds) 33 | - ufw: 34 | rule: limit 35 | port: ssh 36 | proto: tcp 37 | # Allow 80 38 | - ufw: 39 | rule: allow 40 | port: 80 41 | proto: tcp 42 | # Allow 443 43 | - ufw: 44 | rule: allow 45 | port: 443 46 | proto: tcp 47 | # Allow 8506 for ames (ames port set on urbit boot) 48 | - ufw: 49 | rule: allow 50 | port: 8506 51 | proto: udp 52 | 53 | # Disable password SSH 54 | - name: Disallow password authentication 55 | lineinfile: dest=/etc/ssh/sshd_config 56 | regexp="^PasswordAuthentication" 57 | line="PasswordAuthentication no" 58 | state=present 59 | notify: Restart ssh 60 | 61 | # Install and start Urbit 62 | - name: Set swapiness 63 | shell: echo 0 | sudo tee /proc/sys/vm/swappiness 64 | 65 | - name: Test if the planet is already running. Used to prevent double runs. 66 | shell: ps -aux | grep {{ planet_name }} | wc | awk '$1 < 2 {err = 1} END {exit err}' 67 | register: planet_running 68 | 69 | - name: Test if the planet has run by checking for its data directory. Used to prevent accidental restart of stopped planet. 70 | stat: 71 | path: "{{ planet_name }}" 72 | register: planet_has_run 73 | 74 | - name: Download and unarchive latest Urbit binary 75 | unarchive: 76 | src: https://urbit.org/install/linux64/latest 77 | dest: /usr/local/bin 78 | remote_src: yes 79 | list_files: yes 80 | register: urbit_dir 81 | 82 | - name: Start the planet if it has never run and is not currently running 83 | shell: /usr/local/bin/{{ urbit_dir.files[0] }} -p 8506 -d -w {{ planet_name }} -G {{ planet_key }} > {{ planet_name }}.out 84 | tags: urbit 85 | when: not (planet_has_run.stat.exists) and planet_running != 1 86 | --------------------------------------------------------------------------------