-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
passwordstore lookup plugin gopass compatibility #4766
Comments
Files identified in the description:
If these files are incorrect, please update the |
@stephan-devop Please see #4185 for a detailed description of the problem this fixes, short version: One is of course free to open an issue there and request a change in behavior, but this will take a while to trickle down into the field reliably (I would assume for it to take years), also because it should somehow be backwards compatible, so probably adding a command line switch, which could then be added to the plugin combined with a version check. Now, nowhere does the plugin state that it supports anything besides "real" passwordstore, that's why I optimized the behavior for it to stop horrible things to happen by accident (e.g., using the tree node name as a cryptographic key is terribly insecure) . Therefore this wasn't foreseen, but having a compatibility shim is not something outlandish either, so I can feel your pain. That said, I see multiple practical solutions:
The last option is probably the most realistic short term solution to your specific problem. |
There have been fixes in the past (I quickly found #1493, #1589) to make the lookup compatible with gopass, but we do not explicitly declare that it is compatible (for that we should have tests for gopass as well). 2 and 3 sound like good solutions to me, but someone would have to implement them, preferably with tests. |
Why don't we check if the |
Because
Adding what I proposed above is a few lines of code changed plus another five lines of documentation (which then should also mention gopass and the wrapper script). Is your gopass wrapper available somewhere (if yes, it should be linked to from documentation so it’s actually a thing). |
I agree with your reasoning and it's fine for me. I've opted for 2 as I think it is more user-friendly. This is my first pull request for this project so please point out any code style violations I might have committed. I've created a draft pull request introducing a variable
I've linked the wrapper script above. But the logical next step would be to incorporate the script into the lookup plugin by adding "exceptions" for gopass, right? Then I had a look at the integration tests. How do I run them locally? Is there some kind of docker environment available? |
The wrapper script does two things which I've added to the plugin:
|
Hi @stephan-devop, I also created a patch (should have probably coordinated with you). I also went for option 2, but in a lighter way:
Now, I don't want to take away your steam/spirit, but I think this is an easier implementation (also to review). It lacks the extra "--password" flag of course, this would still have to be done in the wrapper. I'll open a draft review, so we can compare. |
I added #4780, which has minimal logic changes to it (and works with wrappers beyond gopass). @stephan-devop Would that work for you, or do you think it needs to be specific? Again, my bad for not coordinating our efforts, one way or another we spent time working on the same thing. |
I have a suggestion how we could get rid of the wrapper script and achieve gopass compatibility without the penalty of a system call for pass users: If the password contains "You haven't checked for updates in a while." or "Parsing is enabled. Use -n to disable." then run |
But anyway, even if I still have to use a wrapper script, I won't have to patch this plugin in the future. So your work helps a lot, thank you! |
Let's see what Felix says, but I think for now this is a relatively low profile solution (the change was quite quick, but figuring out integration tests took forever). There is still room for a change to fully support multiple pass-like managers in the future through explicit configuration or by simply wrapping the existing plugin into another one - but that would be quite a lot of work to yield clean results. |
I can understand your point and I appreciate your work. Actually, I found it quite interesting to get a glimpse of the ansible code which I use so often. |
Looks OK to me. |
How about combining both approaches? I.e. have a |
The current approach has the advantage that it’s basically zero cost for users of If we add real As all of this seems like a lot of work, maybe it makes more sense for someone to develop a pass/gopass shim that is smart enough to understand the arguments passed in and mimic pass’s behavior as a separate effort outside of ansible. This would also have the advantage that it could be used in other projects too (think: pkg/apt-get install gopass-shim). |
@stephan-devop See below for a cleaner version of your wrapper:
#!/bin/sh
export GOPASS_NO_REMINDER=yes
export GOPASS_NO_NOTIFY=yes
cmd="$1"
shift
cmdopt=""
# if command is "show", add option to
# return bare password
if [ "$1" = "show" ]; then
cmdopt="--password"
fi
exec gopass "$cmd" $cmdopt "$@" |
Thanks a lot! But shouldn’t we initialize |
My suggestion has zero cost for users of |
Assuming it could be part of the environment, true. I'll edit the comment. |
How would this be zero cost, if we need to call Also, users would still need to install This would also allow users to have both packages - gopass and pass - installed on the same host. |
@felixfontein Could be as simple as: diff --git a/plugins/lookup/passwordstore.py b/plugins/lookup/passwordstore.py
index 359190c3..559403d7 100644
--- a/plugins/lookup/passwordstore.py
+++ b/plugins/lookup/passwordstore.py
@@ -106,6 +106,20 @@ DOCUMENTATION = '''
type: str
default: 15m
version_added: 4.5.0
+ backend:
+ description:
+ - Specify which backend to use.
+ - Defaults to C(pass), passwordstore.org's original pass utility.
+ - C(gopass) support is incomplete.
+ ini:
+ - section: passwordstore_lookup
+ key: backend
+ type: str
+ default: pass
+ choices:
+ - pass
+ - gopass
+ version_added: 4.5.0
'''
EXAMPLES = """
ansible.cfg: |
@@ -240,7 +254,7 @@ class LookupModule(LookupBase):
if self.realpass is None:
try:
self.passoutput = to_text(
- check_output2(["pass", "--version"], env=self.env),
+ check_output2([self.pass_cmd, "--version"], env=self.env),
errors='surrogate_or_strict'
)
self.realpass = 'pass: the standard unix password manager' in self.passoutput
@@ -288,6 +302,9 @@ class LookupModule(LookupBase):
self.env = os.environ.copy()
self.env['LANGUAGE'] = 'C' # make sure to get errors in English as required by check_output2
+ if self.get_option('backend') == 'gopass':
+ self.env['GOPASS_NO_REMINDER'] = "YES"
+
# Set PASSWORD_STORE_DIR
if os.path.isdir(self.paramvals['directory']):
self.env['PASSWORD_STORE_DIR'] = self.paramvals['directory']
@@ -306,7 +323,9 @@ class LookupModule(LookupBase):
def check_pass(self):
try:
self.passoutput = to_text(
- check_output2(["pass", "show", self.passname], env=self.env),
+ check_output2([self.pass_cmd, 'show'] +
+ (['--password'] if self.get_option('backend') == 'gopass' else []) +
+ [self.passname], env=self.env),
errors='surrogate_or_strict'
).splitlines()
self.password = self.passoutput[0]
@@ -357,7 +376,7 @@ class LookupModule(LookupBase):
if self.paramvals['backup']:
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
try:
- check_output2(['pass', 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
+ check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
except (subprocess.CalledProcessError) as e:
raise AnsibleError(e)
return newpass
@@ -369,7 +388,7 @@ class LookupModule(LookupBase):
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
msg = newpass + '\n' + "lookup_pass: First generated by ansible on {0}\n".format(datetime)
try:
- check_output2(['pass', 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
+ check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
except (subprocess.CalledProcessError) as e:
raise AnsibleError(e)
return newpass
@@ -398,6 +417,7 @@ class LookupModule(LookupBase):
yield
def setup(self, variables):
+ self.pass_cmd = self.get_option('backend') # pass and gopass are commands as well
self.locked = None
timeout = self.get_option('locktimeout')
if not re.match('^[0-9]+[smh]$', timeout): |
Ah, I forgot that your solution only calls it in certain cases. In that case your solution isn't zero cost as well ;-) But in the 'happy path' it is...
So far I was assuming that it automatically does that.
That would be even better. Your diff looks good. (I would also allow to configure it by inventory though, i.e. |
@felixfontein I integrated this with the previous change and updated #4780. |
Summary
The passwordstore lookup plugin uses the binary
pass
. The plugin used to work fine with the gopass drop-in replacement using a small wrapper script. But sadly, that does not work anymore.The reason is that the passwordstore plugin checks the internal pass configuration. It checks for the
~/.password-store
directory and the.gpg
file at a location wherepass
would store it. If either fails the plugin throws an error.For me, the main advantage of
gopass
overpass
is the mounts feature which allows multiple password stores at arbitrary paths. They happen to be stored somewhere completely different thanpass
would store them but the interface to retrieve them is the same. I think that it's not a good idea if the passwordstore lookup plugin verifies the backend's configuration. It should rely on thepass
interface instead.I've tried to understand why the passwordstore plugin checks for the directory and the gpg file and I have two questions:
Why is there an
else
which kicks in if the passwordstore directory does not exist? Isn't this handled bypass
?Then there is a comment I do not understand:
What kind of tree node is this? This looks suspiciously like a workaround for some
pass
bug which should be fixed upstream.For the time being, I've implemented a workaround for our (virtual) development environment by applying a simple patch which removes both checks. Of course, this is hardly a general solution, as those checks probably exist for a reason. Could someone please tell me for which reason?
Issue Type
Bug Report
Component Name
passwordstore lookup plugin
Ansible Version
Community.general Version
Configuration
$ ansible-config dump --only-changed
OS / Environment
Debian GNU/Linux 11 (bullseye), cf. takelage-dev
Steps to Reproduce
Use
gopass
as backend instead ofpass
.Expected Results
gopass
should work as a passwordstore lookup plugin backend as its interface is the same as thepass
interface.Actual Results
The passwordstore lookup plugin complains about the missing
pass
configuration directory and missing gpg files.Code of Conduct
The text was updated successfully, but these errors were encountered: