During the custom Linux build agent deployment for Visual Studio Team Services (Ubuntu Server 18.04-LTS) I noticed that there is an issue with something I did not noticed previously on Ubuntu Server and I'm not noticing on my Mac OS X. When I'm trying to run Python (3.6) script, which is using Azure SDK, I'm receiving interactive prompt "Please enter password for encrypted keyring". It's not a problem to create keyring and provide a password when running script interactively but when we are talking about automation or in a context of VSTS it is not acceptable to have any interactive steps in the process. I have started some investigation on that.
Who is asking
When I have terminated this interactive prompt I have got a traceback pointing where the prompt starts in a code:
Please enter password for encrypted keyring: File "/usr/local/lib/python3.6/dist-packages/msrestazure/azure_active_directory.py", line 448, in __init__ self.set_token() File "/usr/local/lib/python3.6/dist-packages/msrestazure/azure_active_directory.py", line 485, in set_token self._default_token_cache(self.token) File "/usr/local/lib/python3.6/dist-packages/msrestazure/azure_active_directory.py", line 207, in _default_token_cache keyring.set_password(self.cred_store, self.store_key, str(token)) File "/usr/lib/python3/dist-packages/keyring/core.py", line 47, in set_password _keyring_backend.set_password(service_name, username, password) File "/usr/lib/python3/dist-packages/keyrings/alt/file_base.py", line 135, in set_password password_encrypted = self.encrypt(password.encode('utf-8'), assoc) File "/usr/lib/python3/dist-packages/keyrings/alt/file.py", line 206, in encrypt cipher = self._create_cipher(self.keyring_key, salt, IV) File "/usr/lib/python3/dist-packages/keyring/util/properties.py", line 56, in __get__ return self.fget(obj) File "/usr/lib/python3/dist-packages/keyrings/alt/file.py", line 96, in keyring_key self._unlock() File "/usr/lib/python3/dist-packages/keyrings/alt/file.py", line 186, in _unlock 'Please enter password for encrypted keyring: ') File "/usr/lib/python3.6/getpass.py", line 77, in unix_getpass passwd = _raw_input(prompt, stream, input=input) File "/usr/lib/python3.6/getpass.py", line 146, in _raw_input line = input.readline()
I have focused on
File "/usr/local/lib/python3.6/dist-packages/msrestazure/azure_active_directory.py", line 207, in _default_token_cache.
What I hound there is:
def _default_token_cache(self, token): """Store token for future sessions. :param dict token: An authentication token. :rtype: None """ self.token = token if keyring: try: keyring.set_password(self.cred_store, self.store_key, str(token)) except Exception as err: _LOGGER.warning("Keyring cache token has failed: %s", str(err))
So, it is nothing more, than using system keyring to store oAuth2 token for future use.
msrestazure/azure_active_directory.py - which is used by Azure SDK for Python, is checking if
keyring Python module is used in our system and if yes, it is using it to store oAuth token. Clear.
Why it started to happen?
I'm using Ubuntu Server 16.04-LTS for a long time, and there is no issue like that there. Why it is on 18.04? I made some more investigation on that and now I know.
python3-pip package on Ubuntu 18.04 I noticed that
python3-keyrings.alt are also installed. Knowing that it's clear that
_default_token_cache() function in
msrestazure/azure_active_directory.py will decide to use keyring.
I have checked where it started looking at
python3-pip dependencies and recommendations at packages.ubuntu.com. One of the package recommendations, and in fact used modules, is
python3-wheel - and this module has:
python3-keyring - store and access your passwords safely - Python 3 version of the package
python3-keyrings.alt - alternate backend implementations for python3-keyring
as recommended dependencies. So, when installing
python3-pip package in default mode, we will always get
python3-keyring also installed.
There is no such recommendation in 16.04-LTS.
Of course we can try to not install
python3-keyring or remove it. But it can be needed or even required by other packages or modules. If we do not need encryption, we can use the PlaintextKeyring backend which does not need any password. We have at least two options to do it and get back to the state where keyring is not used.
Add this to your code:
import keyring.backend from keyrings.alt.file import PlaintextKeyring keyring.set_keyring(PlaintextKeyring())
The safety of using
PlaintextKeyring() as token storage is another story and it will be probably discussed on GitHub soon. I will inform you about other solutions (using keyring if possible) or security conclusions.