Skip to content

Commit

Permalink
Finished docs; added password functions
Browse files Browse the repository at this point in the history
Signed-off-by: Andreas Maier <andreas.r.maier@gmx.de>
  • Loading branch information
andy-maier committed Mar 29, 2021
1 parent 493feef commit c86b41b
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 65 deletions.
18 changes: 13 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ can stay encrypted in the file system but can still be used by the program in
clear text.

At first use on a particular vault file, the encryption command prompts for a
vault password and stores that in the keyring of your local system using the
`keyring package`_. Subsequent encryption and decryption of the vault file will
then use the key from the keyring, avoiding any further password prompts.
Programmatic access can also be done with the password from the keyring.
The use of the keyring can be disabled if that is desired.
vault password and stores that in the keyring facility of your local system
using the `keyring package`_. Subsequent encryption and decryption of the vault
file will then use the password from the keyring, avoiding any further password
prompts. Programmatic access can also be done with the password from the
keyring. The use of the keyring facility can be disabled if that is desired.

The encryption of the vault files is implemented using the 'fernet'
functionality of the `cryptography package`_.
Expand All @@ -59,6 +59,14 @@ This package allows putting at rest the habit of having clear text files that
contain passwords, API keys and other secrets, and allows transitioning to a
secure but still easy to use approach for managing such secrets.

Why a new vault implementation: The ansible-vault command provided the
functionality we needed and was originally used (except for the keyring storage
which we added). However, Ansible does not support native Windows and that
was a requirement. Also, the ansible-vault command requires installing the
entire Ansible which is quite large. Searching Pypi for suitable vaults
that a) have commands for encrypting and decrypting and b) provide programmatic
access to the encrypted file, did not reveal anything suitable.


.. _`Documentation and change log`:

Expand Down
17 changes: 16 additions & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
API Reference
=============

This section describes the API of the **easy-vault** package.
This section describes the API of the **easy-vault** package. The API is
kept stable using the compatibility rules defined for
:term:`semantic versioning`.

Any functions not described in this section are considered internal and may
change incompatibly without warning.


.. _`EasyVault class`:
Expand All @@ -43,6 +48,16 @@ KeyRingLib class
:special-members: __str__


.. _`Password functions`:

Password functions
------------------

.. autofunction:: easy_vault.get_password

.. autofunction:: easy_vault.set_password


.. _`Exception classes`:

Exception classes
Expand Down
3 changes: 3 additions & 0 deletions docs/appendix.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ References

.. glossary::

Semantic versioning
`Semantic versioning 2.0 <https://semver.org/>`_

Python glossary
* `Python 2.7 Glossary <https://docs.python.org/2.7/glossary.html>`_
* `Python 3.4 Glossary <https://docs.python.org/3.4/glossary.html>`_
18 changes: 13 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ can stay encrypted in the file system but can still be used by the program in
clear text.

At first use on a particular vault file, the encryption command prompts for a
vault password and stores that in the keyring of your local system using the
`keyring package`_. Subsequent encryption and decryption of the vault file will
then use the key from the keyring, avoiding any further password prompts.
Programmatic access can also be done with the password from the keyring.
The use of the keyring can be disabled if that is desired.
vault password and stores that in the keyring facility of your local system
using the `keyring package`_. Subsequent encryption and decryption of the vault
file will then use the password from the keyring, avoiding any further password
prompts. Programmatic access can also be done with the password from the
keyring. The use of the keyring facility can be disabled if that is desired.

The encryption of the vault files is implemented using the 'fernet'
functionality of the `cryptography package`_.
Expand All @@ -52,6 +52,14 @@ This package allows putting at rest the habit of having clear text files that
contain passwords, API keys and other secrets, and allows transitioning to a
secure but still easy to use approach for managing such secrets.

Why a new vault implementation: The ansible-vault command provided the
functionality we needed and was originally used (except for the keyring storage
which we added). However, Ansible does not support native Windows and that
was a requirement. Also, the ansible-vault command requires installing the
entire Ansible which is quite large. Searching Pypi for suitable vaults
that a) have commands for encrypting and decrypting and b) provide programmatic
access to the encrypted file, did not reveal anything suitable.

.. toctree::
:maxdepth: 2
:numbered:
Expand Down
77 changes: 53 additions & 24 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Supported environments

The **easy-vault** package is supported in these environments:

* Operating Systems: Linux, Windows (native, and with UNIX-like environments),
macOS/OS-X
* Operating Systems: Linux, macOS / OS-X, native Windows, Linux subsystem in
Windows, UNIX-like environments in Windows.

* Python: 2.7, 3.4, and higher

Expand All @@ -48,50 +48,79 @@ prerequisite packages into the active Python environment:
Managing the vault file
-----------------------

**TODO: Write this section: encryption, decryption, keyring, password**
The **easy-vault** package comes with a command named "easy-vault" that is
used to encrypt or decrypt the vault file in place:

.. code-block:: bash
$ easy-vault encrypt VAULTFILE
$ easy-vault decrypt VAULTFILE
This command displays self-explanatory help, e.g.:

.. code-block:: bash
$ easy-vault --help
$ easy-vault encrypt --help
$ easy-vault decrypt --help
.. _`Accessing the secrets in a program`:

Accessing the secrets in a program
----------------------------------

The following Python code demonstrates the use case of a command line utility
that accesses the secrets in the vault:
The **easy-vault** package provides programmatic access to the vault file,
regardless of whether the vault file is currently encrypted or decrypted.
See the :ref:`API Reference` for details.

The following Python code demonstrates how to access the secrets in a vault file
in YAML format:

.. code-block:: python
import getpass
from easy_vault import EasyVault, EasyVaultException, KeyRingLib
import easy_vault
vault_file = 'examples/vault.yml' # Path name of Ansible vault file
keyringlib = KeyRingLib()
password = keyringlib.get_password(vault_file)
if password is None:
password = getpass.getpass("Enter password for vault file {fn}:".
format(fn=vault_file))
print("Setting password for vault file {fn} in keyring".
format(fn=vault_file))
keyringlib.set_password(vault_file, password)
else:
print("Using password for vault file {fn} from keyring".
format(fn=vault_file))
vault = EasyVault(vault_file, password)
password = easy_vault.get_password(vault_file)
vault = easy_vault.EasyVault(vault_file, password)
try:
vault_obj = vault.get_yaml()
except EasyVaultException as exc:
except easy_vault.EasyVaultException as exc:
. . . # handle error
easy_vault.set_password(vault_file, password)
myserver_nick = 'myserver1' # Nickname of a secret in the vault file
myserver_secrets = vault_obj['secrets'][myserver_nick]
session = MySession( # A fictitious session class
host=myserver_secrets['host'],
username=myserver_secrets['username'],
password=myserver_secrets['password'])
host=myserver_secrets['host'], # 10.11.12.13
username=myserver_secrets['username'], # myuser1
password=myserver_secrets['password']) # mypass1
# Do something in the server session
. . .
Here is the vault file 'examples/vault.yml' that is used in the example
code:

.. code-block:: yaml
# Example Ansible vault file
secrets:
myserver1:
host: 10.11.12.13
username: myuser1
password: mypass1
myserver2:
host: 10.11.12.14
username: myuser2
password: mypass2
The vault file does not need to be in YAML format; there are access functions
for accessing its raw content as a Byte string and as a Unicode string, too.
1 change: 1 addition & 0 deletions easy_vault/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from ._easy_vault import * # noqa: F403,F401
from ._key_ring_lib import * # noqa: F403,F401
from ._password import * # noqa: F403,F401
from . import _version

#: The full version of this package including any development levels, as a
Expand Down
4 changes: 3 additions & 1 deletion easy_vault/_key_ring_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""

from __future__ import absolute_import, print_function
import os
import keyring

__all__ = ['KeyRingLib']
Expand Down Expand Up @@ -93,4 +94,5 @@ def keyring_username(filepath):
Returns:
:term:`unicode string`: keyring user name.
"""
return u'file:{fn}'.format(fn=filepath)
normpath = os.path.normpath(filepath)
return u'file:{fn}'.format(fn=normpath)
112 changes: 112 additions & 0 deletions easy_vault/_password.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Convenience functions for getting and setting the password in the keyring.
"""

from __future__ import absolute_import, print_function

import getpass
from ._key_ring_lib import KeyRingLib

__all__ = ['get_password', 'set_password']


def get_password(filepath, use_keyring=True, verbose=False, echo=print):
"""
Get the vault password from the keyring or by prompting for it.
This is a convenience function that uses the :class:`~easy_vault.KeyRingLib`
class.
In non-keyring mode, the keyring is not used, and the password is always
prompted for.
In keyring mode, the password is attempted to be obtained from the keyring
and is prompted for if not available there.
Parameters:
filepath (:term:`unicode string`):
Path name of the vault file.
use_keyring (bool):
Use keyring mode (`True`) or non-keyring mode (`False`).
verbose (bool):
Print additional messages. Note that the password prompt is always
displayed.
echo (function):
Print function to be used for the additional messages in verbose mode.
Returns:
:term:`unicode string`: Password for the vault file.
"""
keyringlib = KeyRingLib()

if not use_keyring:
return getpass.getpass("Enter password for vault file {fn}:".
format(fn=filepath))

password = keyringlib.get_password(filepath)
if password is None:
return getpass.getpass("Enter password for vault file {fn}:".
format(fn=filepath))

if verbose:
echo("Using password from keyring for vault file: {fn}".
format(fn=filepath))
return password


def set_password(
filepath, password, use_keyring=True, verbose=False, echo=print):
"""
Set the password in the keyring.
This is a convenience function that uses the :class:`~easy_vault.KeyRingLib`
class.
In non-keyring mode, this function does nothing.
In keyring mode, the password is stored in the keyring. This is done by
first getting it, and setting it only if it was not set or was different.
This approach has been chosen in order to print the verbose message
about setting the password only if it was really changed.
Parameters:
filepath (:term:`unicode string`):
Path name of the vault file.
password (:term:`unicode string`):
Password for the vault file.
use_keyring (bool):
Use keyring mode (`True`) or non-keyring mode (`False`).
verbose (bool):
Print additional messages.
echo (function):
Print function to be used for the additional messages in verbose mode.
"""
if use_keyring:
keyringlib = KeyRingLib()
current_password = keyringlib.get_password(filepath)
if current_password is None or password != current_password:
if verbose:
echo("Setting password in keyring for vault file: {fn}".
format(fn=filepath))
keyringlib.set_password(filepath, password)

0 comments on commit c86b41b

Please sign in to comment.