From feb032b84f78045e559314fefd7ce59bb9c55191 Mon Sep 17 00:00:00 2001 From: brent s Date: Sun, 29 Mar 2020 01:45:12 -0400 Subject: [PATCH] some more auth stuff and documentation --- docs/README.adoc | 341 ++++++++++++++++++++++++++++++++++++++++++ docs/gendocs.sh | 3 + vaultpass/__init__.py | 2 +- vaultpass/auth.py | 3 + vaultpass/gpgauth.py | 16 ++ 5 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 docs/README.adoc create mode 100755 docs/gendocs.sh create mode 100644 vaultpass/gpgauth.py diff --git a/docs/README.adoc b/docs/README.adoc new file mode 100644 index 0000000..5c77ea6 --- /dev/null +++ b/docs/README.adoc @@ -0,0 +1,341 @@ += VaultPass User Manual +Brent Saner +v1.0, 2020-03-28 +:doctype: book +:data-uri: +:imagesdir: images +:sectlinks: +:toc: preamble +:toc2: left +:idprefix: +:toclevels: 7 +:source-highlighter: highlightjs + +== Preface +=== What is Vault? +https://www.vaultproject.io/[Vault by HashiCorp^] is a "secrets manager" - it securely protects various secrets with a +very robust system of authentication and authorization. + +It also provides an https://en.wikipedia.org/wiki/X.509[X.509^] https://en.wikipedia.org/wiki/Public_key_infrastructure[PKI^] +system for certificates generation and a token/OTP generator. + +=== What is Pass? +https://www.passwordstore.org/[Pass^] ("The standard Unix password manager") is a password manager written entirely in +bash and backed by GPG. It's fairly barebones in terms of technology but does a decent enough job. + +=== What is VaultPass? +VaultPass attempts to bridge the gap between the two. It aims to be a drop-in replacement for the pass CLI utility via +subcommands and other operations, but obviously with Vault as a backend instead of GPG-encrypted flatfile hierarchy. + +Obviously since the backends are vastly different, total parity is going to be impossible. But I try to get it pretty close. + + +== Configuration +Unlike Pass, PassVault requires a persistent configuration. At the very **least**, the authentication method needs to be +specified. + +The default location for the configuration file is `~/.config/vaultpass.xml`. It's an XML document formatted with the +following structure: + +. The https://www.w3.org/TR/xml/#sec-prolog-dtd[XML prolog^], specifying the character encoding of the document and +XML version.footnote:confheader[These aren't **strictly** necessary, but will make cross-parsing and validation MUCH +easier. It's *highly* recommended to use them.] +. The root element (`vaultpass`). +This element contains attributes describing parsing/validation specifics as well, such as the +https://www.w3.org/TR/xml-names/[namespace definitions^] and https://www.w3.org/TR/xmlschema11-1/#xsi_schemaLocation[schema location^].footnote:confheader[] +.. The `server` element.footnote:optelem[This element/attribute/text content is *optional*. See the item's description +for how default values/behaviour are determined.] This element is a container for connection and management of the +Vault server. This consists of: +... A single `uri` element.footnote:optelem[] It should be the same as the **base** URL for your Vault server. +The default (if not specified) is to first check for a **`VAULT_SERVER`** environment variable and, if not found, to use +`http://localhost:8000/`. +... An unseal directive, which can be used to (attempt to) automatically unseal the server if it is sealed. +This isn't required, but can assist in automatic operation. +One of either:footnote:optelem[] +.... `unseal`, the unseal key shard (a Base64 string), or +.... `unsealGpg`, the unseal key shard encrypted with GPG. See the section on <>. +... A required authentication directive which specifies how we should authenticate to Vault. It should be comprised of +one of either: +.... `auth` (see <> section below), or +.... `authGpg`, an <> config snippet encrypted with GPG. See the section on <>. + +Let's look at an example configuration. + +=== Example Configuration + +.`~/.config/vaultpass.xml` example: +[source,xml] +---- + + + + + http://localhost:8000/ + YOUR_UNSEAL_SHARD + + + + + + +---- + +In the above, we can see that it would use the vault server at `http://localhost:8000/` using whatever token is either +in the **`VAULT_TOKEN`** environment variable or, if empty, the `~/.vault-token` file. Because an unseal shard was +provided, it will be able to attempt to automatically unseal the Vault (assuming its shard will complete the threshold +needed). + +=== Auth +Vault itself supports a https://www.vaultproject.io/docs/auth/[large number of authentication methods^]. However, in +the interest if maintainability, this project has limited support to only the most common authentication methods. More +authentication methods may be added in the future upon request. + +NOTE: All of these (except for <>) **require** configuration in Vault first. Configuration of those +authentication methods is out of scope for this document and project. Please ensure that your authentication works as +expected in the https://www.vaultproject.io/downloads/[Vault CLI utility^] or via the +https://www.vaultproject.io/api-docs/auth/[Vault API^] first before submitting a bug report in VaultPass. + +==== AppRole +AppRole takes two required children elements: + +. `appRole` (the container element) +.. `role`, the AppRole's RoleID, and +.. `secret`, the AppRole's SecretID. + +===== Example Snippet +[source,xml] +---- + + + + my-role + 37b74931-c4cd-d49a-9246-ccc62d682a25 + + + +---- + +==== LDAP +LDAP takes two required children elements and one optional child element: + +. `ldap` (the container element) +.. `username`, the username (as according to the *`userdn`* and *`userattr`* settings +https://www.vaultproject.io/docs/auth/ldap/#binding-parameters[in the configuration^]) +.. `password`, the password for the account object. +.. `mountPoint` footnote:optelem[], the https://www.vaultproject.io/api-docs/system/mounts/[mount point^] for the LDAP authentication in +Vault. The default, if not provided, is `ldap`. + +===== Example Snippet +[source,xml] +---- + + + + mitchellh + MyPassword1 + ldap + + + +---- + +==== Token +Token auth is the most basic supported authentication in Vault and can be used without any further configuration. + +It consists of, at its most basic (and "automagic") configuration, a single element -- but this can be configured more +in-depth/explicitly. + +. `token` (the container element) +.. The token itself or content/source of the token.footnote:optelem[] + +It has one optional attribute: `source`.footnote:optelem[]. It can be one of the following: + +* `env:MY_TOKEN_VAR`, in which environmental token **`MY_TOKEN_VAR`** will be sourced. +* A filesystem path, in which the file is assumed to contain the token (and ONLY the token). + +To determine the behaviour of how this behaves, please refer to the below table. + +.Determining `token` behaviour +[cols="^1,5,10"] +|=== +|No. |If... |Then... + +| 1 |self-enclosed, no `source` |The **`VAULT_TOKEN`** environment variable is checked. If not defined, the file +`~/.vault-token` will be checked. If that file doesn't exist, a `RuntimeError` will be raised. +| 2 |self-enclosed, `source` given| The `source` is assumed to be the *only* source and no automatic detection will occur. +| 3 |token contained in tags, no `source`| The specified token will be used and no automatic detection will occur. +| 4 |token contained in tags, `source` given |Same as **3**; `source` is ignored. +|=== + +===== Example Snippet +[source,xml] +---- + + + + + + + + + + + + + + + + + + +---- + +==== User/Password +Vault's https://www.vaultproject.io/docs/auth/userpass/[userpass authentication method^] must be +https://www.vaultproject.io/docs/auth/userpass/#configuration[configured^] beforehand, but it's a relatively simple +configuration. + +VaultPass user/password authentication takes two required children elements and one optional element. + +. `userpass` (the container element) +.. `username`, the username of the account. +.. `password`, the password for the account. +.. `mountPoint` footnote:optelem[], the https://www.vaultproject.io/api-docs/system/mounts/[mount point^] for the auth. +If not specified, the default is `userpass`. + +===== Example Snippet +[source,xml] +---- + + + + mitchellh + foo + userpass + + + +---- + +=== GPG-Encrypted Elements +Understandably, in order to have a persistent configuration, that means storing on disk. That also means that they need +to be able to be accessed with no or minimal user interruption. Pass used GPG natively, so it didn't have an issue with +this; since https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html[gpg-agent^] is typically +spawned on first use of a https://www.gnupg.org/gph/en/manual/r1616.html[GPG homedir^] (usually `~/.gnupg/` by default) +and keeps an authenticated session open for 10 minutes +(https://superuser.com/questions/624343/keep-gnupg-credentials-cached-for-entire-user-session[by default^]). + +To get around needing to store plaintext credentials on-disk in any form, VaultPass has `unsealGpg` and `authGpg` +elements. These elements are of the same composition (described <>) and allow you to use GPG to +encrypt that sensitive information. + +Note that while this does increase security, it breaks compatibility with other XML parsers - they won't be able to +decrypt and parse the encrypted snippet unless explicitly coded to do so. + +==== `*Gpg` elements +`*Gpg` elements (`authGpg`, `unsealGpg`) have the same structure: + +. `unsealGpg`/`authGpg`, the container element. +.. The path to the encrypted file as the contained text. + +It has some optional attributes as well: + +.`*Gpg` element attributes +|=== +|Attribute |Content + +|`keyFPR` footnote:optelem[] | The GPG key to use to decrypt the file. It accepts multiple key ID formats, but it's *highly* recommended to +use the full 40-character (without spaces) key fingerprint. If not specified, VaultPass will use the first private key +it finds in the keyring. +|`gpgHome` footnote:optelem[] | The GPG home directory. If not specified, VaultPass will first check the **`GNUPGHOME`** environment +variable. If that's empty, we'll default to `~/.gnupg/`. +|=== + +The contents of the encrypted file should match the **unencrypted** XML content it's replacing. + +CAUTION: Note that if you use namespaces in your `vaultpass.xml` config file, you **MUST** use matching declarations in +your encrypted file. + +Let's look at an example of GPG-encrypted elements. + +==== GPG-Encrypted Elements Example + +.`~/.config/vaultpass.xml` snippet: +[source,xml] +---- + + + + + http://localhost:8000/ + ~/.private/vaultpass/unseal.asc + + ~/.private/vaultpass/auth.gpg + +---- + +As shown, it supports both <> and <> encryption formats. + +==== ASCII-Armored +===== Encrypted +.`~/.private/vaultpass/unseal.asc` contents: +[source] +---- +-----BEGIN PGP MESSAGE----- + +hQIMA7QuYg9nGdZdAQ//eHvEZ7vpLvygM2ofIiT2uW7cWYQaYm/09li7s0+0ZqTu +hNki7oIQ1Ip+k6ds45eEXPG6hXwZ7+mtIDG8VcYpo0PdwpvcJ9qqAgvnFAynvjgH +pRkeIw4VUfGxxhs8oZMvdrXuYtwzaXIhn0UuZv+cIS1Jj6IfG0xSpRvd+M0MW+Wk +IWSIyUcY6fkP7MFEiId7sQwm6htHXJDqiVAmwn4lqk2CnIhtsTd5HUyRzGg5gZs+ +sFAssa7QjoBKJMkTDVH4EIC4GcgNtTB/rg7XBoX1k36CHZAwB/boZ5arMYswwkYp +VFv9At13vkkRMf23bb7siq7U0Vbvs0PGsFJS/1ivS1IyzFGFZGHaTz7ndk2q2iyY +tMjMe+z+i2VAGvtfdE7H4K4TrqrM9OZ81vyJkEjRBrkSfR9sWOgv5yBFDvoeVkZl +k1gRXLkrF/7eZn8vD17oOew/zr+um7s/rTtLp5GEknOsKzb1NOMBHP44dXdxNreT +HdRlNDLgOp2KffXgNSm/A026tMSA0nf0kpJmR1yLjucKPoy6wVrTMh+sLNubgxmZ +BCz64myu8dfWtHQfPSis1kjrs15mfQoOu9Cl9st8gTs50sKWTa+dGdajZEcz8rcX +OMBLwiTQodP/0uRHf8YofIFk86QXbYALd4WsC/KvDQBiaz8HRcfkccDQCHQvdLrS +wEkBuhCZj1OqUnTXg0qggMD0Hp2pO0CqD4uZ3RHvIt49W+7oUr22Y4VarRNeP06x +JhYC3Sr0RXv/Vi21DMiUUUAXYeYKP82HpP0zSZhCcwVZZje1dXwq85SH04u9pT+n +f2JqgATxmAaepQZCANxAluknfSluuCBi0hmhagYY2IsgKmJcSsksm0AWfGyzgoeV +ZypDlE3MuERVLJSDBjZtfnScy3CeTWWj5vw7Nfm5XEqOuIIbZaTV/qb6i6y4rc6k +Yx5xYKHeuXJGbrQdVJemcXyDIV5tDw5RtLpO57EwL+uEYgSbN9rO/N2B83QjB7D5 +lCmbJtQcjxG/eJ/SrB2oS47YdEKRy+cH0Xx+ +=scGv +-----END PGP MESSAGE----- +---- + +===== Decrypted +[source,xml] +---- +1fs1tV46ebb6awF6edtuzsoEawZlBARFp5rSaED+EJI= +---- + +==== Binary +===== Encrypted +.`~/.private/vaultpass/auth.gpg` contents: +[source] +---- + +---- + + +===== Decrypted +[source,xml] +---- + + + s.Lp4ix1CKBtJOfA46Ks4b4cs6 + + +---- diff --git a/docs/gendocs.sh b/docs/gendocs.sh new file mode 100755 index 0000000..8c82745 --- /dev/null +++ b/docs/gendocs.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +asciidoctor README.adoc -o ./README.html diff --git a/vaultpass/__init__.py b/vaultpass/__init__.py index 6ecba3b..c88180e 100644 --- a/vaultpass/__init__.py +++ b/vaultpass/__init__.py @@ -29,7 +29,7 @@ class PassMan(object): def getClient(self): # This may need to be re-tooled in the future. auth_xml = self.cfg.xml.find('auth') - authmethod_xml = auth_xml.getchildren()[0]\ + authmethod_xml = auth_xml.getchildren()[0] for a in dir(auth): if a.startswith('_'): continue diff --git a/vaultpass/auth.py b/vaultpass/auth.py index d522985..2924607 100644 --- a/vaultpass/auth.py +++ b/vaultpass/auth.py @@ -128,3 +128,6 @@ class Token(_AuthBase): self.token = self._getEnv(e) else: self.token = self._getFile(a) + self.client.token = self.token + self.authCheck() + return(None) diff --git a/vaultpass/gpgauth.py b/vaultpass/gpgauth.py new file mode 100644 index 0000000..9c1282e --- /dev/null +++ b/vaultpass/gpgauth.py @@ -0,0 +1,16 @@ +import os +import logging +## +import gpg + + +# Special shoutout to Jthan for ruining my life. + + +_logger = logging.getLogger() + + +class GPGAuth(object): + def __init__(self, gpgauth_xml): + pass +