diff options
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c543f4 --- /dev/null +++ b/README.md | |||
| @@ -0,0 +1,289 @@ | |||
| 1 | # etoolkit | ||
| 2 | |||
| 3 | *etoolkit* is a simple toolkit for defining and setting environment variables | ||
| 4 | in a flexible and secure way. | ||
| 5 | |||
| 6 | |||
| 7 | ## General | ||
| 8 | |||
| 9 | *etoolkit* started as a simple idea while I was working at the University | ||
| 10 | of Oslo. Later I felt the need for even more flexible solution. The following | ||
| 11 | goals were set: | ||
| 12 | |||
| 13 | - the ability to define env. var. "instances" with support for inheritance | ||
| 14 | |||
| 15 | - encrypting values using a master password | ||
| 16 | |||
| 17 | - the ability to spawn a child process with the defined variables | ||
| 18 | |||
| 19 | - support for macros | ||
| 20 | |||
| 21 | |||
| 22 | ## Requirements | ||
| 23 | |||
| 24 | Apart from Python >= 3.7, the only requirement is | ||
| 25 | [cryptography](https://pypi.org/project/cryptography/) | ||
| 26 | |||
| 27 | |||
| 28 | ## Overview | ||
| 29 | |||
| 30 | In a typical UNIX environment, env. variables are usually set in one | ||
| 31 | or more of the initialization / startup files like f.i. */etc/profile*, | ||
| 32 | *~/.bash_profile*, *~/.bashrc*, *~/.tcshrc*, *~/.cshrc* ... depending | ||
| 33 | on the OS, shell, distribution... etc. | ||
| 34 | Env. variables represent key-value pairs (env. variable name and its value). | ||
| 35 | |||
| 36 | A potential problem when dealing with env. variables containing sensitive | ||
| 37 | values like passwords, tokens, connection strings... is that they are inherited | ||
| 38 | by all child processes spawned from the login / interactive shell. | ||
| 39 | |||
| 40 | A malicious / exploited process (like f.i. web browser) will then be able to | ||
| 41 | fetch those values by using *getenv*. Such process may in addition be able to | ||
| 42 | simply read *~/.bashrc* or any similar file readable by the process owner and | ||
| 43 | get the values from there if necessary. | ||
| 44 | |||
| 45 | *etoolkit* attempts to solve both of these problems. | ||
| 46 | |||
| 47 | When started, it reads env. variables from its own configuration file. | ||
| 48 | Sensitive data may be encrypted using a master password. Hence reading | ||
| 49 | etoolkit's configuration file will not reveal the real value of the data. | ||
| 50 | |||
| 51 | When *etoolkit* has processed (decryption, macro replacement, etc) its data, | ||
| 52 | it may define new env. variables corresponding to that data and may start a new | ||
| 53 | interactive shell. The processed environment variables will not be available | ||
| 54 | for processes that were not spawned by that same *etoolkit* session. | ||
| 55 | |||
| 56 | |||
| 57 | ## Installation | ||
| 58 | |||
| 59 | ### pip (pypi) | ||
| 60 | |||
| 61 | ```bash | ||
| 62 | pip install etoolkit | ||
| 63 | ``` | ||
| 64 | |||
| 65 | |||
| 66 | ### Gentoo | ||
| 67 | |||
| 68 | ```bash | ||
| 69 | layman -a sgs | ||
| 70 | emerge dev-python/etoolkit | ||
| 71 | ``` | ||
| 72 | |||
| 73 | |||
| 74 | ## Setup and examples | ||
| 75 | |||
| 76 | Most users (including me) will simply use the command line interface (CLI). | ||
| 77 | |||
| 78 | |||
| 79 | ### CLI | ||
| 80 | |||
| 81 | *etoolkit* comes with a simple CLI: | ||
| 82 | |||
| 83 | ```bash | ||
| 84 | python -m etoolkit -h | ||
| 85 | ``` | ||
| 86 | |||
| 87 | ... or even (if *etoolkit* was installed using the methods described above) | ||
| 88 | |||
| 89 | ```bash | ||
| 90 | etoolkit -h | ||
| 91 | ``` | ||
| 92 | |||
| 93 | |||
| 94 | The *etoolkit* CLI loads the configuration located by default in | ||
| 95 | *~/.etoolkit.json*. That file is based on | ||
| 96 | [etoolkit_sample.json](https://github.com/blackm0re/etoolkit/blob/master/etoolkit_sample.json). | ||
| 97 | |||
| 98 | The *"instances"* part of the configuration defines unique instances, each of | ||
| 99 | which represents its own environment with its own defined env. variables. | ||
| 100 | Each instance consists of key-value pairs corresponding to env. name and value. | ||
| 101 | All pairs will result in corresponding env. variables being defined, with the | ||
| 102 | exception of the following *etoolkit* internal keys: | ||
| 103 | |||
| 104 | - **ETOOLKIT_PARENT** - string - reference to another instance | ||
| 105 | |||
| 106 | - **ETOOLKIT_SENSITIVE** - list - env. variables containing sensitive data | ||
| 107 | |||
| 108 | All pairs defined in *"general"* (currently only **MASTER_PASSWORD_HASH**) are | ||
| 109 | *etoolkit* internal as well. | ||
| 110 | |||
| 111 | An instance may inherit (and if desired overwrite) key value pairs from its | ||
| 112 | parent. | ||
| 113 | |||
| 114 | Variables containing sensitive data can be encrypted / decrypted using a master | ||
| 115 | password. Currently *etoolkit* requires that all values in a configuration | ||
| 116 | file are encrypted with the same master password. Setting a master password | ||
| 117 | hash is a recommended but not mandatory. | ||
| 118 | |||
| 119 | ```bash | ||
| 120 | etoolkit -p | ||
| 121 | ``` | ||
| 122 | |||
| 123 | That command will prompt for master password and output a hash that can then | ||
| 124 | be stored | ||
| 125 | in ["general"]["MASTER_PASSWORD_HASH"]. | ||
| 126 | The hash is only used for verifying that a correct master password has been | ||
| 127 | provided at a later time. Issuing: | ||
| 128 | |||
| 129 | ```bash | ||
| 130 | etoolkit -e | ||
| 131 | ``` | ||
| 132 | |||
| 133 | will prompt for the master password, then for the value to be encrypted and | ||
| 134 | finally display the encrypted string of the value. Unless invoked with the | ||
| 135 | *-E* / *--echo* parameter *etoolkit* will not display the value that is about | ||
| 136 | to be encrypted. | ||
| 137 | |||
| 138 | More than one value can be encrypted / decrypted with a single master password | ||
| 139 | prompt if *-m* / *--multiple-values* parameter is provided. Manual decryption | ||
| 140 | of single value(s): | ||
| 141 | |||
| 142 | ```bash | ||
| 143 | etoolkit -d -m | ||
| 144 | ``` | ||
| 145 | |||
| 146 | Listing all available instances defined in the configuration file and then | ||
| 147 | loading a specific instance can be achieved by: | ||
| 148 | |||
| 149 | ```bash | ||
| 150 | etoolkit -l | ||
| 151 | etoolkit <instance-name> | ||
| 152 | ``` | ||
| 153 | |||
| 154 | *etoolkit* will prompt for the master password the first time it encounters | ||
| 155 | and encrypted value. Once provided the master password will be used to decrypt | ||
| 156 | the rest of the encrypted values. | ||
| 157 | |||
| 158 | When all values are fetched from a given instance (and its parents) and then | ||
| 159 | decrypted, they are further processed by replacing macros with their | ||
| 160 | corresponding values. Currently the following macros are supported: | ||
| 161 | |||
| 162 | - **%h** - the home directory of the user running *etoolkit* (~/) | ||
| 163 | |||
| 164 | - **%i** - the names of the instance that is about to be loaded | ||
| 165 | |||
| 166 | - **%u** - the username of the user running *etoolkit* (*getpass.getuser()*) | ||
| 167 | |||
| 168 | In addition, value starting with *":"* is appended to the existing value | ||
| 169 | (if any) of the env. variable about to be set | ||
| 170 | (like *MYVAR=$MYVAR\<new value\>)*). | ||
| 171 | The opposite is true for value ending with *":"* (*MYVAR=\<new value\>$MYVAR*). | ||
| 172 | |||
| 173 | When the variables are finally processed, *etoolkit* sets / changes them as env. | ||
| 174 | variables (using *setenv* / *os.environ.update*) and spawns an interactive | ||
| 175 | child process (by invoking *system($SHELL)*). | ||
| 176 | |||
| 177 | One can also spawn a different process than an interactive shell by using the | ||
| 178 | *-s* / *--spawn* parameter. | ||
| 179 | |||
| 180 | ```bash | ||
| 181 | etoolkit -s /bin/othershell <instance-name> | ||
| 182 | ``` | ||
| 183 | |||
| 184 | Contact the author for questions and suggestions! :) | ||
| 185 | |||
| 186 | |||
| 187 | ### Using the *EtoolkitInstance* class | ||
| 188 | |||
| 189 | *etoolkit* comes with its own *etoolkit* package that contains the | ||
| 190 | *EtoolkitInstance* class. | ||
| 191 | |||
| 192 | The class encapsulates the function of creating and processing instances | ||
| 193 | from a given structure (dict). | ||
| 194 | |||
| 195 | It may be useful if one should prefer for example making her own CLI interface. | ||
| 196 | or the *instances* structure being loaded from a diferent configuration file | ||
| 197 | (f.i. .yml). | ||
| 198 | |||
| 199 | ```python | ||
| 200 | import os | ||
| 201 | |||
| 202 | import etoolkit | ||
| 203 | |||
| 204 | |||
| 205 | # using some static methods in order to create encrypted values | ||
| 206 | etoolkit.EtoolkitInstance.encrypt('the very secret passwd', 'secret1') | ||
| 207 | # Out: 'enc-val$1$Y/TBb1F3siHTw6qZg9ERzZfA8PLPf2CwGSQLpu9jYWw=$FT5tS9o+ABvsxogIXpJim16Gz5SVtV8=' | ||
| 208 | |||
| 209 | etoolkit.EtoolkitInstance.encrypt('the very secret passwd', 'secret2') | ||
| 210 | # Out: 'enc-val$1$vIBcoCNiYrsDLtF41uLuSEnppBjhliD0B8jwcBJcj/c=$KwOGe/y1dlxktDaCnJPIVNuaQ4Q7yNo=' | ||
| 211 | |||
| 212 | |||
| 213 | # The encrypted values will be used in our configuration structure | ||
| 214 | # The following structure defines the 3 instances: default, dev and secret | ||
| 215 | instances = { | ||
| 216 | "general": { | ||
| 217 | }, | ||
| 218 | "instances": { | ||
| 219 | "default": { | ||
| 220 | "ETOOLKIT_PROMPT": "(%i)" | ||
| 221 | }, | ||
| 222 | "dev": { | ||
| 223 | "ETOOLKIT_PARENT": "default", | ||
| 224 | "PYTHONPATH": ":/home/user/.pythonpath", | ||
| 225 | "DB_CONNECTION": "enc-val$1$Y/TBb1F3siHTw6qZg9ERzZfA8PLPf2CwGSQLpu9jYWw=$FT5tS9o+ABvsxogIXpJim16Gz5SVtV8=" | ||
| 226 | }, | ||
| 227 | "secret": { | ||
| 228 | "ETOOLKIT_PARENT": "default", | ||
| 229 | "ETOOLKIT_SENSITIVE": ["PASSWORD"], | ||
| 230 | "GNUPGHOME": "%h/private/.gnupg", | ||
| 231 | "PASSWORD": "enc-val$1$vIBcoCNiYrsDLtF41uLuSEnppBjhliD0B8jwcBJcj/c=$KwOGe/y1dlxktDaCnJPIVNuaQ4Q7yNo=" | ||
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | |||
| 237 | dev_instance = etoolkit.EtoolkitInstance('dev', instances) | ||
| 238 | |||
| 239 | # fetch the variables before the processing stage (calling get_environ()) | ||
| 240 | # since raw_env_variables is a dict, it can be modified (f.i. .update()) | ||
| 241 | dev_instance.raw_env_variables | ||
| 242 | |||
| 243 | dev_instance.master_password = 'the very secret passwd' # or perhaps using getpass | ||
| 244 | env_vars = dev_instance.get_env() | ||
| 245 | print(env_vars['PASSWORD']) # outputs: 'secret2' | ||
| 246 | |||
| 247 | inst.dump_env(env_vars) # prints all values, with the exception of 'PASSWORD' | ||
| 248 | |||
| 249 | # set the env. variables. | ||
| 250 | os.environ.update(env_vars) | ||
| 251 | ``` | ||
| 252 | |||
| 253 | |||
| 254 | ### Tips | ||
| 255 | |||
| 256 | When starting a new interactive process (f.i. bash), the process will | ||
| 257 | in turn invoke its startup script (f.i. *~/.bashrc*). | ||
| 258 | Avoid redefining the env. variables that have just been set by *etoolkit*! | ||
| 259 | |||
| 260 | If you want your shell prompt to display the name of the loaded instance, you | ||
| 261 | can set a new env. variable (f.i. "ETOOLKIT_PROMPT" as shown in the sample | ||
| 262 | configuration above) and then add the following at the bottom of your startup | ||
| 263 | file (f.i. ~/.bashrc): | ||
| 264 | |||
| 265 | ```bash | ||
| 266 | if [ -n "$ETOOLKIT_PROMPT" ]; then | ||
| 267 | export PS1="$ETOOLKIT_PROMPT$PS1" | ||
| 268 | fi | ||
| 269 | ``` | ||
| 270 | |||
| 271 | |||
| 272 | ## Support and contributing | ||
| 273 | |||
| 274 | *etoolkit* is hosted on GitHub: https://github.com/blackm0re/etoolkit | ||
| 275 | |||
| 276 | |||
| 277 | ## Author | ||
| 278 | |||
| 279 | Simeon Simeonov - sgs @ LiberaChat | ||
| 280 | |||
| 281 | |||
| 282 | ## [License](https://github.com/blackm0re/etoolkit/blob/master/LICENSE) | ||
| 283 | |||
| 284 | Copyright (c) 2021, Simeon Simeonov | ||
| 285 | All rights reserved. | ||
| 286 | |||
| 287 | [Licensed](https://github.com/blackm0re/etoolkit/blob/master/LICENSE) under the | ||
| 288 | GNU General Public License v3.0 or later. | ||
| 289 | SPDX-License-Identifier: GPL-3.0-or-later | ||
