summaryrefslogtreecommitdiff
path: root/doc/guide
diff options
context:
space:
mode:
authorMicah Anderson <micah@leap.se>2014-12-23 16:47:35 -0500
committerMicah Anderson <micah@leap.se>2014-12-23 16:47:35 -0500
commit574a0554a95ba74867ebd0ca4a93195bfa104c14 (patch)
treefd07b5b2ec8b32e82aa665dad117ee6e51791884 /doc/guide
parent126faf8606f4911ccc3c1f55a9e0f381a46d536a (diff)
parentfc9a8af17d927085486052a53233401c42b0caab (diff)
Merge branch 'develop'
Conflicts: platform.rb Change-Id: Ic2e08e594d29a585691341c8667ac0b64933a505
Diffstat (limited to 'doc/guide')
-rw-r--r--doc/guide/commands.md419
-rw-r--r--doc/guide/config.md263
-rw-r--r--doc/guide/en.haml6
-rw-r--r--doc/guide/keys-and-certificates.md176
-rw-r--r--doc/guide/miscellaneous.md14
-rw-r--r--doc/guide/nodes.md169
6 files changed, 1047 insertions, 0 deletions
diff --git a/doc/guide/commands.md b/doc/guide/commands.md
new file mode 100644
index 00000000..0cee709a
--- /dev/null
+++ b/doc/guide/commands.md
@@ -0,0 +1,419 @@
+@title = 'Command Line Reference'
+@summary = "A copy of leap --help"
+
+The command "leap" can be used to manage a bevy of servers running the LEAP platform from the comfort of your own home.
+
+
+# Global Options
+
+* `--log FILE`
+Override default log file
+Default Value: None
+
+* `-v|--verbose LEVEL`
+Verbosity level 0..5
+Default Value: 1
+
+* `--[no-]color`
+Disable colors in output
+
+* `--debug`
+Enable debugging library (leap_cli development only)
+
+* `--help`
+Show this message
+
+* `--version`
+Display version number and exit
+
+* `--yes`
+Skip prompts and assume "yes"
+
+
+# leap add-user USERNAME
+
+Adds a new trusted sysadmin by adding public keys to the "users" directory.
+
+
+
+**Options**
+
+* `--pgp-pub-key arg`
+OpenPGP public key file for this new user
+Default Value: None
+
+* `--ssh-pub-key arg`
+SSH public key file for this new user
+Default Value: None
+
+* `--self`
+Add yourself as a trusted sysadin by choosing among the public keys available for the current user.
+
+
+# leap cert
+
+Manage X.509 certificates
+
+
+
+## leap cert ca
+
+Creates two Certificate Authorities (one for validating servers and one for validating clients).
+
+See see what values are used in the generation of the certificates (like name and key size), run `leap inspect provider` and look for the "ca" property. To see the details of the created certs, run `leap inspect <file>`.
+
+## leap cert csr
+
+Creates a CSR for use in buying a commercial X.509 certificate.
+
+Unless specified, the CSR is created for the provider's primary domain. The properties used for this CSR come from `provider.ca.server_certificates`.
+
+**Options**
+
+* `--domain DOMAIN`
+Specify what domain to create the CSR for.
+Unless specified, the CSR is created for the provider's primary domain. The properties used for this CSR come from `provider.ca.server_certificates`.
+Default Value: None
+
+
+## leap cert dh
+
+Creates a Diffie-Hellman parameter file.
+
+
+
+## leap cert update FILTER
+
+Creates or renews a X.509 certificate/key pair for a single node or all nodes, but only if needed.
+
+This command will a generate new certificate for a node if some value in the node has changed that is included in the certificate (like hostname or IP address), or if the old certificate will be expiring soon. Sometimes, you might want to force the generation of a new certificate, such as in the cases where you have changed a CA parameter for server certificates, like bit size or digest hash. In this case, use --force. If <node-filter> is empty, this command will apply to all nodes.
+
+**Options**
+
+* `--force`
+Always generate new certificates
+
+
+# leap clean
+
+Removes all files generated with the "compile" command.
+
+
+
+# leap compile
+
+Compile generated files.
+
+
+
+## leap compile all [ENVIRONMENT]
+
+Compiles node configuration files into hiera files used for deployment.
+
+
+
+## leap compile zone
+
+Compile a DNS zone file for your provider.
+
+
+Default Command: all
+
+# leap db
+
+Database commands.
+
+
+
+## leap db destroy [FILTER]
+
+Destroy all the databases. If present, limit to FILTER nodes.
+
+
+
+# leap deploy FILTER
+
+Apply recipes to a node or set of nodes.
+
+The FILTER can be the name of a node, service, or tag.
+
+**Options**
+
+* `--ip IPADDRESS`
+Override the default SSH IP address.
+Default Value: None
+
+* `--port PORT`
+Override the default SSH port.
+Default Value: None
+
+* `--tags TAG[,TAG]`
+Specify tags to pass through to puppet (overriding the default).
+Default Value: leap_base,leap_service
+
+* `--dev`
+Development mode: don't run 'git submodule update' before deploy.
+
+* `--fast`
+Makes the deploy command faster by skipping some slow steps. A "fast" deploy can be used safely if you recently completed a normal deploy.
+
+* `--force`
+Deploy even if there is a lockfile.
+
+* `--[no-]sync`
+Sync files, but don't actually apply recipes.
+
+
+# leap env
+
+Manipulate and query environment information.
+
+The 'environment' node property can be used to isolate sets of nodes into entirely separate environments. A node in one environment will never interact with a node from another environment. Environment pinning works by modifying your ~/.leaprc file and is dependent on the absolute file path of your provider directory (pins don't apply if you move the directory)
+
+## leap env ls
+
+List the available environments. The pinned environment, if any, will be marked with '*'.
+
+
+
+## leap env pin ENVIRONMENT
+
+Pin the environment to ENVIRONMENT. All subsequent commands will only apply to nodes in this environment.
+
+
+
+## leap env unpin
+
+Unpin the environment. All subsequent commands will apply to all nodes.
+
+
+Default Command: ls
+
+# leap facts
+
+Gather information on nodes.
+
+
+
+## leap facts update FILTER
+
+Query servers to update facts.json.
+
+Queries every node included in FILTER and saves the important information to facts.json
+
+# leap help command
+
+Shows a list of commands or help for one command
+
+Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
+
+**Options**
+
+* `-c`
+List commands one per line, to assist with shell completion
+
+
+# leap inspect FILE
+
+Prints details about a file. Alternately, the argument FILE can be the name of a node, service or tag.
+
+
+
+**Options**
+
+* `--base`
+Inspect the FILE from the provider_base (i.e. without local inheritance).
+
+
+# leap list [FILTER]
+
+List nodes and their classifications
+
+Prints out a listing of nodes, services, or tags. If present, the FILTER can be a list of names of nodes, services, or tags. If the name is prefixed with +, this acts like an AND condition. For example:
+
+`leap list node1 node2` matches all nodes named "node1" OR "node2"
+
+`leap list openvpn +local` matches all nodes with service "openvpn" AND tag "local"
+
+**Options**
+
+* `--print arg`
+What attributes to print (optional)
+Default Value: None
+
+* `--disabled`
+Include disabled nodes in the list.
+
+
+# leap local
+
+Manage local virtual machines.
+
+This command provides a convient way to manage Vagrant-based virtual machines. If FILTER argument is missing, the command runs on all local virtual machines. The Vagrantfile is automatically generated in 'test/Vagrantfile'. If you want to run vagrant commands manually, cd to 'test'.
+
+## leap local destroy [FILTER]
+
+Destroys the virtual machine(s), reclaiming the disk space
+
+
+
+## leap local reset [FILTER]
+
+Resets virtual machine(s) to the last saved snapshot
+
+
+
+## leap local save [FILTER]
+
+Saves the current state of the virtual machine as a new snapshot
+
+
+
+## leap local start [FILTER]
+
+Starts up the virtual machine(s)
+
+
+
+## leap local status [FILTER]
+
+Print the status of local virtual machine(s)
+
+
+
+## leap local stop [FILTER]
+
+Shuts down the virtual machine(s)
+
+
+
+# leap mosh NAME
+
+Log in to the specified node with an interactive shell using mosh (requires node to have mosh.enabled set to true).
+
+
+
+# leap new DIRECTORY
+
+Creates a new provider instance in the specified directory, creating it if necessary.
+
+
+
+**Options**
+
+* `--contacts arg`
+Default email address contacts.
+Default Value: None
+
+* `--domain arg`
+The primary domain of the provider.
+Default Value: None
+
+* `--name arg`
+The name of the provider.
+Default Value: None
+
+* `--platform arg`
+File path of the leap_platform directory.
+Default Value: None
+
+
+# leap node
+
+Node management
+
+
+
+## leap node add NAME [SEED]
+
+Create a new configuration file for a node named NAME.
+
+If specified, the optional argument SEED can be used to seed values in the node configuration file.
+
+The format is property_name:value.
+
+For example: `leap node add web1 ip_address:1.2.3.4 services:webapp`.
+
+To set nested properties, property name can contain '.', like so: `leap node add web1 ssh.port:44`
+
+Separeate multiple values for a single property with a comma, like so: `leap node add mynode services:webapp,dns`
+
+**Options**
+
+* `--local`
+Make a local testing node (by automatically assigning the next available local IP address). Local nodes are run as virtual machines on your computer.
+
+
+## leap node init FILTER
+
+Bootstraps a node or nodes, setting up SSH keys and installing prerequisite packages
+
+This command prepares a server to be used with the LEAP Platform by saving the server's SSH host key, copying the authorized_keys file, installing packages that are required for deploying, and registering important facts. Node init must be run before deploying to a server, and the server must be running and available via the network. This command only needs to be run once, but there is no harm in running it multiple times.
+
+**Options**
+
+* `--ip IPADDRESS`
+Override the default SSH IP address.
+Default Value: None
+
+* `--port PORT`
+Override the default SSH port.
+Default Value: None
+
+* `--echo`
+If set, passwords are visible as you type them (default is hidden)
+
+
+## leap node mv OLD_NAME NEW_NAME
+
+Renames a node file, and all its related files.
+
+
+
+## leap node rm NAME
+
+Removes all the files related to the node named NAME.
+
+
+
+# leap ssh NAME
+
+Log in to the specified node with an interactive shell.
+
+
+
+**Options**
+
+* `--port arg`
+Override ssh port for remote host
+Default Value: None
+
+* `--ssh arg`
+Pass through raw options to ssh (e.g. --ssh '-F ~/sshconfig')
+Default Value: None
+
+
+# leap test
+
+Run tests.
+
+
+
+## leap test init
+
+Creates files needed to run tests.
+
+
+
+## leap test run
+
+Run tests.
+
+
+
+**Options**
+
+* `--[no-]continue`
+Continue over errors and failures (default is --no-continue).
+
+Default Command: run
diff --git a/doc/guide/config.md b/doc/guide/config.md
new file mode 100644
index 00000000..be67e6bd
--- /dev/null
+++ b/doc/guide/config.md
@@ -0,0 +1,263 @@
+@title = "Configuration Files"
+@summary = "How to edit configuration files."
+
+Files
+-------------------------------------------
+
+Here are a list of some of the common files that make up a provider. Except for Leapfile and provider.json, the files are optional. Unless otherwise specified, all file names are relative to the 'provider directory' root (where the Leapfile is).
+
+`Leapfile` -- If present, this file tells `leap` that the directory is a provider directory. This file is usually empty, but can contain global options.
+
+`~/.leaprc` -- Evaluated the same as Leapfile, but not committed to source control.
+
+`provider.json` -- Global options related to this provider.
+
+`provider.ENVIRONMENT.json` -- Global options for the provider that are applied to only a single environment.
+
+`common.json` -- All nodes inherit from this file.
+
+`secrets.json` -- An automatically generated file that contains any randomly generated strings needed in order to deploy. These strings are often secret and should be protected, although any need for a random string or number that is remembered will produce another entry in this file. This file is automatically generated and refreshed each time you run `leap compile` or `leap deploy`. If an entry is no longer needed, it will get removed. If you want to change a secret, you can remove this file and have it regenerated, or remove the particular line item and just those items will be created anew.
+
+`facts.json` -- If some of your servers are running on AWS or OpenStack, you will need to discover certain properties about how networking is configured on these machines in order for a full deploy to work. In these cases, make sure to run `leap facts update` to periodically regenerate the facts.json file.
+
+`nodes/NAME.json` -- The configuration file for node called NAME.
+
+`services/SERVICE.json` -- The properties in this configuration file are applied to any node that includes SERVICE in its `services` property.
+
+`services/SERVICE.ENVIRONMENT.json` -- The properties in this configuration file are applied to any node that includes SERVICE in its services and has environment equal to ENVIRONMENT.
+
+`services/TAG.json` -- The properties in this configuration file are applied to any node that has includes TAG in its `tags` property.
+
+`services/TAG.ENVIRONMENT.json` -- The properties in this configuration file are applied to any node that has includes TAG in its `tags` property and has `environment` property equal to ENVIRONMENT.
+
+`files/*` -- Various static files used by the platform (e.g. keys, certificates, webapp customization, etc).
+
+`users/USER/` -- A directory that stores the public keys of the sysadmin with name USER. This person will have root access to all the servers.
+
+
+Leapfile
+-------------------------------------------
+
+A `Leapfile` defines options for the `leap` command and lives at the root of your provider directory. `Leapfile` is evaluated as ruby, so you can include whatever weird logic you want in this file. In particular, there are several variables you can set that modify the behavior of leap. For example:
+
+ @platform_directory_path = '../leap_platform'
+ @log = '/var/log/leap.log'
+
+Additionally, you can create a `~/.leaprc` file that is loaded after `Leapfile` and is evaluated the same way.
+
+Platform options:
+
+* `@platform_directory_path` (required). This must be set to the path where `leap_platform` lives. The path may be relative.
+
+Vagrant options:
+
+* `@vagrant_network`. Allows you to override the default network used for local nodes. It should include a netmask like `@vagrant_network = '10.0.0.0/24'`.
+* `@custom_vagrant_vm_line`. Insert arbitrary text into the auto-generated Vagrantfile. For example, `@custom_vagrant_vm_line = "config.vm.boot_mode = :gui"`.
+
+Logging options:
+
+* `@log`. If set, all command invocation and results are logged to the specified file. This is the same as the switch `--log FILE`, except that the command line switch will override the value in the Leapfile.
+
+
+JSON format
+-------------------------------------------
+
+All configuration files, other than `Leapfile`, are in the JSON format. For example:
+
+ {
+ "key1": "value1",
+ "key2": "value2"
+ }
+
+Keys should match `/[a-z0-9_]/`
+
+Unlike traditional JSON, comments are allowed. If the first non-whitespace characters are `//` then the line is treated as a comment.
+
+ // this is a comment
+ {
+ // this is a comment
+ "key": "value" // this is an error
+ }
+
+Options in the configuration files might be nested hashes, arrays, numbers, strings, or boolean. Numbers and boolean values should **not** be quoted. For example:
+
+ {
+ "openvpn": {
+ "ip_address": "1.1.1.1",
+ "protocols": ["tcp", "udp"],
+ "ports": [80, 53],
+ "options": {
+ "public_ip": false,
+ "adblock": true
+ }
+ }
+ }
+
+If the value string is prefixed with an '=' character, the result is evaluated as ruby. For example:
+
+ {
+ "domain": {
+ "public": "domain.org"
+ }
+ "api_domain": "= 'api.' + domain.public"
+ }
+
+In this case, the property "api_domain" will be set to "api.domain.org". So long as you do not create unresolvable circular dependencies, you can reference other properties in evaluated ruby that are themselves evaluated ruby.
+
+See "Macros" below for information on the special macros available to the evaluated ruby.
+
+TIP: In rare cases, you might want to force the evaluation of a value to happen in a later pass after most of the other properties have been evaluated. To do this, prefix the value string with "=>" instead of "=".
+
+Node inheritance
+----------------------------------------
+
+Every node inherits from common.json and also any of the services or tags attached to the node. Additionally, the `leap_platform` contains a directory `provider_base` that defines the default values for tags, services and common.json.
+
+Suppose you have a node configuration for `bitmask/nodes/willamette.json` like so:
+
+ {
+ "services": "webapp",
+ "tags": ["production", "northwest-us"],
+ "ip_address": "1.1.1.1"
+ }
+
+This node will have hostname "willamette" and it will inherit from the following files (in this order):
+
+1. common.json
+ - load defaults: `provider_base/common.json`
+ - load provider: `bitmask/common.json`
+2. service "webapp"
+ - load defaults: `provider_base/services/webapp.json`
+ - load provider: `bitmask/services/webapp.json`
+3. tag "production"
+ - load defaults: `provider_base/tags/production.json`
+ - load provider: `bitmask/tags/production.json`
+4. tag "northwest-us"
+ - load: `bitmask/tags/northwest-us.json`
+5. finally, load node "willamette"
+ - load: `bitmask/nodes/willamette.json`
+
+The `provider_base` directory is under the `leap_platform` specified in the file `Leapfile`.
+
+To see all the variables a node has inherited, you could run `leap inspect willamette`.
+
+Common configuration options
+----------------------------------------
+
+You can use the command `leap inspect` to see what options are available for a provider, node, service, or tag configuration. For example:
+
+* `leap inspect common` -- show the options inherited by all nodes.
+* `leap inspect --base common` -- show the common.json from `provider_base` without the local `common.json` inheritance applied.
+* `leap inspect webapp` -- show all the options available for the service `webapp`.
+
+Here are some of the more important options you should be aware of:
+
+* `ip_address` -- Required for all nodes, no default.
+* `ssh.port` -- The SSH port you want the node's OpenSSH server to bind to. This is also the default when trying to connect to a node, but if the node currently has OpenSSH running on a different port then run deploy with `--port` to override the `ssh.port` configuration value.
+* `mosh.enabled` -- If set to `true`, then mosh will be installed on the server. The default is `false`.
+
+Macros
+----------------------------------------
+
+When using evaluated ruby in a JSON configuration file, there are several special macros that are available. These are evaluated in the context of a node (available as the variable `self`).
+
+The following methods are available to the evaluated ruby:
+
+`variable.variable`
+
+ > Any variable defined or inherited by a particular node configuration is available by just referencing it using either hash notation or object field notation (e.g. `['domain']['public']` or `domain.public`). Circular references are not allowed, but otherwise it is OK to nest evaluated values in other evaluated values. If a value has not been defined, the hash notation will return nil but the field notation will raise an exception. Properties of services, tags, and the global provider can all be referenced the same way. For example, `global.services['openvpn'].x509.dh`.
+
+`nodes`
+
+ > A hash of all nodes. This list can be filtered.
+
+`nodes_like_me`
+
+ > A hash of nodes that have the same deployment tags as the current node (e.g. 'production' or 'local').
+
+`global.services`
+
+ > A hash of all services, e.g. `global.services['openvpn']` would return the "openvpn" service.
+
+`global.tags`
+
+ > A hash of all tags, e.g. `global.tags['production']` would return the "production" tag.
+
+ `global.provider`
+
+ > Can be used to access variables defined in `provider.json`, e.g. `global.provider.contacts.default`.
+
+`file(filename)`
+
+ > Inserts the full contents of the file. If the file is an erb template, it is rendered. The filename can either be one of the pre-defined file symbols, or it can be a path relative to the "files" directory in your provider instance. E.g, `file :ca_cert` or `files 'ca/ca.crt'`.
+
+`file_path(filename)`
+
+ > Ensures that the file will get rsynced to the node as an individual file. The value returned by `file_path` is the full path where this file will ultimately live when deploy to the node. e.g. `file_path :ca_cert` or `file_path 'branding/images/logo.png'`.
+
+`secret(:symbol)`
+
+ > Returns the value of a secret in secrets.json (or creates it if necessary). E.g. `secret :couch_admin_password`
+
+`hosts_file`
+
+ > Returns a data structure that puppet will use to generate /etc/hosts. Care is taken to use the local IP of other hosts when needed.
+
+`known_hosts_file`
+
+ > Returns the lines needed in a SSH `known_hosts` file.
+
+`stunnel_client(node_list, port, options={})`
+
+ > Returns a stunnel configuration data structure for the client side. Argument `node_list` is an `ObjectList` of nodes running stunnel servers. Argument `port` is the real port of the ultimate service running on the servers that the client wants to connect to.
+
+`stunnel_server(port)`
+
+ > Generates a stunnel server entry. The `port` is the real port targeted service.
+
+Hash tables
+-----------------------------------------
+
+The macros `nodes`, `nodes_like_me`, `global.services`, and `global.tags` all return a hash table of configuration objects (either nodes, services, or tags). There are several ways to filter and process these hash tables:
+
+Access an element by name:
+
+ nodes['vpn1'] # returns node named 'vpn1'
+ global.services['openvpn'] # returns service named 'openvpn'
+
+Create a new hash table by applying filters:
+
+ nodes[:public_dns => true] # all nodes where public_dns == true
+ nodes[:services => 'openvpn', 'location.country_code' => 'US'] # openvpn service OR in the US.
+ nodes[[:services, 'openvpn'], [:services, 'tor']] # services equal to openvpn OR tor
+ nodes[:services => 'openvpn'][:tags => 'production'] # openvpn AND production
+ nodes[:name => "!bob"] # all nodes that are NOT named "bob"
+
+Create an array of values by selecting a single field:
+
+ nodes.field('location.name')
+ ==> ['seattle', 'istanbul']
+
+Create an array of hashes by selecting multiple fields:
+
+ nodes.fields('domain.full', 'ip_address')
+ ==> [
+ {'domain_full' => 'red.bitmask.net', 'ip_address' => '1.1.1.1'},
+ {'domain_full' => 'blue.bitmask.net', 'ip_address' => '1.1.1.2'},
+ ]
+
+Create a new hash table of hashes, with only certain fields:
+
+ nodes.pick_fields('domain.full', 'ip_address')
+ ==> {
+ "red" => {'domain_full' => 'red.bitmask.net', 'ip_address' => '1.1.1.1'},
+ "blue => {'domain_full' => 'blue.bitmask.net', 'ip_address' => '1.1.1.2'},
+ }
+
+With `pick_fields`, if there is only one field, it will generate a simple hash table:
+
+ nodes.pick_fields('ip_address')
+ ==> {
+ "red" => '1.1.1.1',
+ "blue => '1.1.1.2',
+ }
diff --git a/doc/guide/en.haml b/doc/guide/en.haml
new file mode 100644
index 00000000..4c9bd69f
--- /dev/null
+++ b/doc/guide/en.haml
@@ -0,0 +1,6 @@
+- @nav_title = "Guide"
+- @title = "Platform Guide"
+
+%h1.first Platform Guide
+
+= child_summaries \ No newline at end of file
diff --git a/doc/guide/keys-and-certificates.md b/doc/guide/keys-and-certificates.md
new file mode 100644
index 00000000..bd7f3495
--- /dev/null
+++ b/doc/guide/keys-and-certificates.md
@@ -0,0 +1,176 @@
+@title = "Keys and Certificates"
+@summary = "Working with SSH keys, secrets, and X.509 certificates."
+
+Working with SSH
+================================
+
+Whenever the `leap` command nees to push changes to a node or gather information from a node, it tunnels this command over SSH. Another way to put this: the security of your servers rests entirely on SSH. Because of this, it is important that you understand how `leap` uses SSH.
+
+SSH related files
+-------------------------------
+
+Assuming your provider directory is called 'provider':
+
+* `provider/nodes/crow/crow_ssh.pub` -- The public SSH host key for node 'crow'.
+* `provider/users/alice/alice_ssh.pub` -- The public SSH user key for user 'alice'. Anyone with the private key that corresponds to this public key will have root access to all nodes.
+* `provider/files/ssh/known_hosts` -- An autogenerated known_hosts, built from combining `provider/nodes/*/*_ssh.pub`. You must not edit this file directly. If you need to change it, remove or change one of the files that is used to generate `known_hosts` and then run `leap compile`.
+* `provider/files/ssh/authorized_keys` -- An autogenerated list of all the user SSH keys with root access to the notes. It is created from `provider/users/*/*_ssh.pub`. You must not edit this file directly. If you need to change it, remove or change one of the files that is used to generate `authorized_keys` and then run `leap compile`.
+
+All of these files should be committed to source control.
+
+If you rename, remove, or add a node with `leap node [mv|add|rm]` the SSH key files and the `known_hosts` file will get properly updated.
+
+SSH and local nodes
+-----------------------------
+
+Local nodes are run as Vagrant virtual machines. The `leap` command handles SSH slightly differently for these nodes.
+
+Basically, all the SSH security is turned off for local nodes. Since local nodes only exist for a short time on your computer and can't be reached from the internet, this is not a problem.
+
+Specifically, for local nodes:
+
+1. `known_hosts` is never updated with local node keys, since the SSH public key of a local node is different for each user.
+2. `leap` entirely skips the checking of host keys when connecting with a local node.
+3. `leap` adds the public Vagrant SSH key to the list of SSH keys for a user. The public Vagrant SSH key is a shared and insecure key that has root access to most Vagrant virtual machines.
+
+When SSH host key changes
+-------------------------------
+
+If the host key for a node has changed, you will get an error "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED".
+
+To fix this, you need to remove the file `files/nodes/stompy/stompy_ssh.pub` and run `leap node init stompy`, where the node's name is 'stompy'. **Only do this if you are ABSOLUTELY CERTAIN that the node's SSH host key has changed**.
+
+Changing the SSH port
+--------------------------------
+
+Suppose you have a node `blinky` that has SSH listening on port 22 and you want to make it port 2200.
+
+First, modify the configuration for `blinky` to specify the variable `ssh.port` as 2200. Usually, this is done in `common.json` or in a tag file.
+
+For example, you could put this in `tags/production.json`:
+
+ {
+ "ssh": {
+ "port": 2200
+ }
+ }
+
+Run `leap compile` and open `hiera/blinky.yaml` to confirm that `ssh.port` is set to 2200. The port number must be specified as a number, not a string (no quotes).
+
+Then, you need to deploy this change so that SSH will bind to 2200. You cannot simply run `leap deploy blinky` because this command will default to using the variable `ssh.port` which is now `2200` but SSH on the node is still bound to 22.
+
+So, you manually override the port in the deploy command, using the old port:
+
+ leap deploy --port 22 blinky
+
+Afterwards, SSH on `blinky` should be listening on port 2200 and you can just run `leap deploy blinky` from then on.
+
+X.509 Certificates
+================================
+
+Configuration options
+-------------------------------------------
+
+The `ca` option in provider.json provides settings used when generating CAs and certificates. The defaults are as follows:
+
+ {
+ "ca": {
+ "name": "= global.provider.ca.organization + ' Root CA'",
+ "organization": "= global.provider.name[global.provider.default_language]",
+ "organizational_unit": "= 'https://' + global.provider.domain",
+ "bit_size": 4096,
+ "digest": "SHA256",
+ "life_span": "10y",
+ "server_certificates": {
+ "bit_size": 2048,
+ "digest": "SHA256",
+ "life_span": "1y"
+ },
+ "client_certificates": {
+ "bit_size": 2048,
+ "digest": "SHA256",
+ "life_span": "2m",
+ "limited_prefix": "LIMITED",
+ "unlimited_prefix": "UNLIMITED"
+ }
+ }
+ }
+
+You should not need to override these defaults in your own provider.json, but you can if you want to. To see what values are used for your provider, run `leap inspect provider.json`.
+
+NOTE: A certificate `bit_size` greater than 2048 will probably not be recognized by most commercial CAs.
+
+Certificate Authorities
+-----------------------------------------
+
+There are three x.509 certificate authorities (CA) associated with your provider:
+
+1. **Commercial CA:** It is strongly recommended that you purchase a commercial cert for your primary domain. The goal of platform is to not depend on the commercial CA system, but it does increase security and usability if you purchase a certificate. The cert for the commercial CA must live at `files/cert/commercial_ca.crt`.
+2. **Server CA:** This is a self-signed CA responsible for signing all the **server** certificates. The private key lives at `files/ca/ca.key` and the public cert lives at `files/ca/ca.crt`. The key is very sensitive information and must be kept private. The public cert is distributed publicly.
+3. **Client CA:** This is a self-signed CA responsible for signing all the **client** certificates. The private key lives at `files/ca/client_ca.key` and the public cert lives at `files/ca/client_ca.crt`. Neither file is distribute publicly. It is not a big deal if the private key for the client CA is compromised, you can just generate a new one and re-deploy.
+
+To generate both the Server CA and the Client CA, run the command:
+
+ leap cert ca
+
+Server certificates
+-----------------------------------
+
+Most every server in your service provider will have a x.509 certificate, generated by the `leap` command using the Server CA. Whenever you modify any settings of a node that might affect it's certificate (like changing the IP address, hostname, or settings in provider.json), you can magically regenerate all the certs that need to be regenerated with this command:
+
+ leap cert update
+
+Run `leap help cert update` for notes on usage options.
+
+Because the server certificates are generated locally on your personal machine, the private key for the Server CA need never be put on any server. It is up to you to keep this file secure.
+
+Client certificates
+--------------------------------
+
+Every leap client gets its own time-limited client certificate. This cert is use to connect to the OpenVPN gateway (and probably other things in the future). It is generated on the fly by the webapp using the Client CA.
+
+To make this work, the private key of the Client CA is made available to the webapp. This might seem bad, but compromise of the Client CA simply allows the attacker to use the OpenVPN gateways without paying. In the future, we plan to add a command to automatically regenerate the Client CA periodically.
+
+There are two types of client certificates: limited and unlimited. A client using a limited cert will have its bandwidth limited to the rate specified by `provider.service.bandwidth_limit` (in Bytes per second). An unlimited cert is given to the user if they authenticate and the user's service level matches one configured in `provider.service.levels` without bandwidth limits. Otherwise, the user is given a limited client cert.
+
+Commercial certificates
+-----------------------------------
+
+We strongly recommend that you use a commercial signed server certificate for your primary domain (in other words, a certificate with a common name matching whatever you have configured for `provider.domain`). This provides several benefits:
+
+1. When users visit your website, they don't get a scary notice that something is wrong.
+2. When a user runs the LEAP client, selecting your service provider will not cause a warning message.
+3. When other providers first discover your provider, they are more likely to trust your provider key if it is fetched over a commercially verified link.
+
+The LEAP platform is designed so that it assumes you are using a commercial cert for the primary domain of your provider, but all other servers are assumed to use non-commercial certs signed by the Server CA you create.
+
+To generate a CSR, run:
+
+ leap cert csr
+
+This command will generate the CSR and private key matching `provider.domain` (you can change the domain with `--domain=DOMAIN` switch). It also generates a server certificate signed with the Server CA. You should delete this certificate and replace it with a real one once it is created by your commercial CA.
+
+The related commercial cert files are:
+
+ files/
+ certs/
+ domain.org.crt # Server certificate for domain.org, obtained by commercial CA.
+ domain.org.csr # Certificate signing request
+ domain.org.key # Private key for you certificate
+ commercial_ca.crt # The CA cert obtained from the commercial CA.
+
+The private key file is extremely sensitive and care should be taken with its provenance.
+
+If your commercial CA has a chained CA cert, you should be OK if you just put the **last** cert in the chain into the `commercial_ca.crt` file. This only works if the other CAs in the chain have certs in the debian package `ca-certificates`, which is the case for almost all CAs.
+
+If you want to add additional fields to the CSR, like country, city, or locality, you can configure these values in provider.json like so:
+
+ "ca": {
+ "server_certificates": {
+ "country": "US",
+ "state": "Washington",
+ "locality": "Seattle"
+ }
+ }
+
+If they are not present, the CSR will be created without them. \ No newline at end of file
diff --git a/doc/guide/miscellaneous.md b/doc/guide/miscellaneous.md
new file mode 100644
index 00000000..c38c007c
--- /dev/null
+++ b/doc/guide/miscellaneous.md
@@ -0,0 +1,14 @@
+@title = "Miscellaneous"
+@summary = "Miscellaneous commands you may need to know."
+
+Facts
+==============================
+
+There are a few cases when we must gather internal data from a node before we can successfully deploy to other nodes. This is what `facts.json` is for. It stores a snapshot of certain facts about each node, as needed. Entries in `facts.json` are updated automatically when you initialize, rename, or remove a node. To manually force a full update of `facts.json`, run:
+
+ leap facts update FILTER
+
+Run `leap help facts update` for more information.
+
+The file `facts.json` should be committed to source control. You might not have a `facts.json` if one is not required for your provider.
+
diff --git a/doc/guide/nodes.md b/doc/guide/nodes.md
new file mode 100644
index 00000000..bc48ff32
--- /dev/null
+++ b/doc/guide/nodes.md
@@ -0,0 +1,169 @@
+@title = "Nodes"
+@summary = "Working with nodes, services, tags, and locations."
+
+Node types
+================================
+
+Every node has one or more services that determines the node's function within your provider's infrastructure.
+
+When adding a new node to your provider, you should ask yourself four questions:
+
+* **many or few?** Some services benefit from having many nodes, while some services are best run on only one or two nodes.
+* **required or optional?** Some services are required, while others can be left out.
+* **who does the node communicate with?** Some services communicate very heavily with other particular services. Nodes running these services should be close together.
+* **public or private?** Some services communicate with the public internet, while others only need to communicate with other nodes in the infrastructure.
+
+Brief overview of the services:
+
+* **webapp**: The web application. Runs both webapp control panel for users and admins as well as the REST API that the client uses. Needs to communicate heavily with `couchdb` nodes. You need at least one, good to have two for redundancy. The webapp does not get a lot of traffic, so you will not need many.
+* **couchdb**: The database for users and user data. You can get away with just one, but for proper redundancy you should have at least three. Communicates heavily with `webapp`, `mx`, and `soledad` nodes.
+* **soledad**: Handles the data syncing with clients. Typically combined with `couchdb` service, since it communicates heavily with couchdb.
+* **mx**: Incoming and outgoing MX servers. Communicates with the public internet, clients, and `couchdb` nodes.
+* **openvpn**: OpenVPN gateway for clients. You need at least one, but want as many as needed to support the bandwidth your users are doing. The `openvpn` nodes are autonomous and don't need to communicate with any other nodes. Often combined with `tor` service.
+* **monitor**: Internal service to monitor all the other nodes. Currently, you can have zero or one `monitor` nodes.
+* **tor**: Sets up a tor exit node, unconnected to any other service.
+* **dns**: Not yet implemented.
+
+Webapp
+-----------------------------------
+
+The webapp node is responsible for both the user face web application and the API that the client interacts with.
+
+Some users can be "admins" with special powers to answer tickets and close accounts. To make an account into an administrator, you need to configure the `webapp.admins` property with an array of user names.
+
+For example, to make users `alice` and `bob` into admins, create a file `services/webapp.json` with the following content:
+
+ {
+ "webapp": {
+ "admins": ["bob", "alice"]
+ }
+ }
+
+And then redeploy to all webapp nodes:
+
+ leap deploy webapp
+
+By putting this in `services/webapp.json`, you will ensure that all webapp nodes inherit the value for `webapp.admins`.
+
+Services
+================================
+
+What nodes do you need for a provider that offers particular services?
+
+<table class="table table-striped">
+<tr>
+<th>Node Type</th>
+<th>VPN Service</th>
+<th>Email Service</th>
+</tr>
+<tr>
+<td>webapp</td>
+<td>required</td>
+<td>required</td>
+</tr>
+<tr>
+<td>couchdb</td>
+<td>required</td>
+<td>required</td>
+</tr>
+<tr>
+<td>soledad</td>
+<td>not used</td>
+<td>required</td>
+</tr>
+<tr>
+<td>mx</td>
+<td>not used</td>
+<td>required</td>
+</tr>
+<tr>
+<td>openvpn</td>
+<td>required</td>
+<td>not used</td>
+</tr>
+<tr>
+<td>monitor</td>
+<td>optional</td>
+<td>optional</td>
+</tr>
+<tr>
+<td>tor</td>
+<td>optional</td>
+<td>optional</td>
+</tr>
+<table>
+
+Locations
+================================
+
+All nodes should have a `location.name` specified, and optionally additional information about the location, like the time zone. This location information is used for two things:
+
+* Determine which nodes can, or must, communicate with one another via a local network. The way some virtualization environments work, like OpenStack, requires that nodes communicate via the local network if they are on the same network.
+* Allows the client to prefer connections to nodes that are closer in physical proximity to the user. This is particularly important for OpenVPN nodes.
+
+The location stanza in a node's config file looks like this:
+
+ {
+ "location": {
+ "id": "ankara",
+ "name": "Ankara",
+ "country_code": "TR",
+ "timezone": "+2",
+ "hemisphere": "N"
+ }
+ }
+
+The fields:
+
+* `id`: An internal handle to use for this location. If two nodes have match `location.id`, then they are treated as being on a local network with one another. This value defaults to downcase and underscore of `location.name`.
+* `name`: Can be anything, might be displayed to the user in the client if they choose to manually select a gateway.
+* `country_code`: The [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) two letter country code.
+* `timezone`: The timezone expressed as an offset from UTC (in standard time, not daylight savings). You can look up the timezone using this [handy map](http://www.timeanddate.com/time/map/).
+* `hemisphere`: This should be "S" for all servers in South America, Africa, or Australia. Otherwise, this should be "N".
+
+These location options are very imprecise, but good enough for most usage. The client often does not know its own location precisely either. Instead, the client makes an educated guess at location based on the OS's timezone and locale.
+
+If you have multiple nodes in a single location, it is best to use a tag for the location. For example:
+
+`tags/ankara.json`:
+
+ {
+ "location": {
+ "name": "Ankara",
+ "country_code": "TR",
+ "timezone": "+2",
+ "hemisphere": "N"
+ }
+ }
+
+`nodes/vpngateway.json`:
+
+ {
+ "services": "openvpn",
+ "tags": ["production", "ankara"],
+ "ip_address": "1.1.1.1",
+ "openvpn": {
+ "gateway_address": "1.1.1.2"
+ }
+ }
+
+Unless you are using OpenStack or AWS, setting `location` for nodes is not required. It is, however, highly recommended.
+
+Disabling Nodes
+=====================================
+
+There are two ways to temporarily disable a node:
+
+**Option 1: enabled == false**
+
+If a node has a property `enabled` set to false, then the `leap` command will skip over the node and pretend that it does not exist. For example:
+
+ {
+ "ip_address": "1.1.1.1",
+ "services": ["openvpn"],
+ "enabled": false
+ }
+
+**Options 2: no-deploy**
+
+If the file `/etc/leap/no-deploy` exists on a node, then when you run the commmand `leap deploy` it will halt and prevent a deploy from going through (if the node was going to be included in the deploy).