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.
When installing python3-pip
package on Ubuntu 18.04 I noticed that python3-keyring
and 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
and
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.
Solution
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.
Configuration file
Edit ~/.local/share/python_keyring/keyringrc.cfg:
[backend]
default-keyring=keyrings.alt.file.PlaintextKeyring
Python API
Add this to your code:
import keyring.backend
from keyrings.alt.file import PlaintextKeyring
keyring.set_keyring(PlaintextKeyring())
Security
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.