mirror of
https://github.com/gaschz/qubes-pass.git
synced 2025-06-07 01:38:31 +02:00
Initial commit.
This commit is contained in:
commit
4314d40696
31
Makefile
Normal file
31
Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
BINDIR=/usr/bin
|
||||
SYSCONFDIR=/etc
|
||||
DESTDIR=
|
||||
PROGNAME=qubes-pass
|
||||
|
||||
clean:
|
||||
find -name '*~' -print0 | xargs -0 rm -fv
|
||||
rm -fv *.tar.gz *.rpm
|
||||
|
||||
dist: clean
|
||||
excludefrom= ; test -f .gitignore && excludefrom=--excludefrom=.gitignore ; DIR=$(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec` && FILENAME=$$DIR.tar.gz && tar cvzf "$$FILENAME" --exclude="$$FILENAME" --exclude=.git --exclude=.gitignore $$excludefrom --transform="s|^|$$DIR/|" --show-transformed *
|
||||
|
||||
rpm: dist
|
||||
T=`mktemp -d` && rpmbuild --define "_topdir $$T" -ta $(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec`.tar.gz || { rm -rf "$$T"; exit 1; } && mv "$$T"/RPMS/*/* "$$T"/SRPMS/* . || { rm -rf "$$T"; exit 1; } && rm -rf "$$T"
|
||||
|
||||
srpm: dist
|
||||
T=`mktemp -d` && rpmbuild --define "_topdir $$T" -ts $(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec`.tar.gz || { rm -rf "$$T"; exit 1; } && mv "$$T"/SRPMS/* . || { rm -rf "$$T"; exit 1; } && rm -rf "$$T"
|
||||
|
||||
install-client:
|
||||
install -Dm 755 bin/qvm-pass -t $(DESTDIR)/$(BINDIR)/
|
||||
install -Dm 755 bin/qubes-pass-client -t $(DESTDIR)/$(BINDIR)/
|
||||
|
||||
install-service:
|
||||
install -Dm 644 etc/qubes-rpc/ruddo.PassRead -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/
|
||||
install -Dm 644 etc/qubes-rpc/ruddo.PassManage -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/
|
||||
|
||||
install-dom0:
|
||||
install -Dm 664 etc/qubes-rpc/policy/ruddo.PassRead -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/policy/
|
||||
getent group qubes && chgrp qubes $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/policy/ || true
|
||||
install -Dm 664 etc/qubes-rpc/policy/ruddo.PassManage -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/policy/
|
||||
getent group qubes && chgrp qubes $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/policy/ || true
|
63
README.md
Normal file
63
README.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Inter-VM Pass password manager for Qubes OS
|
||||
|
||||
This is a very simple bridge between Qubes OS VMs. With it, you can
|
||||
store and retrieve passwords between VMs without having to grant
|
||||
any of the VMs any special policy privileges other than access to the
|
||||
Qubes services implemented here.
|
||||
|
||||
## Using the software
|
||||
|
||||
These instructions assume you have installed the software. See the
|
||||
*Installing the software* heading below for more information.
|
||||
|
||||
Run `qvm-pass -?` on a terminal to get usage information.
|
||||
|
||||
## Installing the software
|
||||
|
||||
There are three components for this software:
|
||||
|
||||
* The client `qvm-pass-client` you install in the VMs (or their templates)
|
||||
where you want to manage your passwords.
|
||||
* The service `qvm-pass-service` you install in the VMs (or their templates)
|
||||
where you want to store your passwords.
|
||||
* The policy `qvm-pass-dom0` is the dom0 side of things, necessary to
|
||||
enable the services and control access from the clients to the service.
|
||||
|
||||
First, build the software, After cloning this repository on a suitable VM,
|
||||
run the command:
|
||||
|
||||
```
|
||||
make rpm
|
||||
```
|
||||
|
||||
This will generate three installable packages on the local directory:
|
||||
|
||||
* `qvm-pass-client-<version>.noarch.rpm`
|
||||
* `qvm-pass-service-<version>.noarch.rpm`
|
||||
* `qvm-pass-dom0-<version>.noarch.rpm`
|
||||
|
||||
Copy the `qvm-pass-client-<version>.noarch.rpm` file to the template VM
|
||||
or standalone VM where you plan to manage passwords. Install the RPM with
|
||||
`dnf install <name of the RPM>`. At this point, this VM, as well as
|
||||
any VMs using this as a template, have gained the ability to list
|
||||
and store passwords stored in other VMs.
|
||||
|
||||
Now copy the `qvm-pass-service-<version>.noarch.rpm` file to the template
|
||||
VM or standalone VM where you plan to store passwords. Install the RPM with
|
||||
`dnf install <name of the RPM>`. At this point, this VM, as well as
|
||||
any VMs using this as a template, have gained the ability to securely store
|
||||
passwords in `/home/user/.password-store`.
|
||||
|
||||
Now copy the `qvm-pass-policy-dom0-<version>.noarch.rpm` file to
|
||||
your dom0. At this point, the default policy (`ask`) is active on
|
||||
your Qubes OS system, and you can begin using the client.
|
||||
|
||||
Those clever among you will have discovered that there is a `Makefile`
|
||||
included, and that you can use the `Makefile` to install the software on
|
||||
other non-RPM templates. I welcome pull requests to add support for
|
||||
other distro packages and Qubes OS templates.
|
||||
|
||||
## Troubleshooting and debugging
|
||||
|
||||
As always, you can file new issues on the repo of this project for help
|
||||
with fixing bugs that the programs may have. Pull requests also welcome.
|
52
bin/qubes-pass-client
Executable file
52
bin/qubes-pass-client
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [ -s /rw/config/pass-split-domain -a -z "$QUBES_PASS_DOMAIN" ] ; then
|
||||
export QUBES_PASS_DOMAIN=$( cat /rw/config/pass-split-domain )
|
||||
fi
|
||||
|
||||
if [ -z "$QUBES_PASS_DOMAIN" ] ; then
|
||||
title="Qubes pass error"
|
||||
msg="The QUBES_PASS_DOMAIN variable is not defined. Either create /rw/config/pass-split-domain with the VM containing your pass setup, set the environment variable yourself, or pass -d on the command line."
|
||||
echo "$title: $msg" >&2
|
||||
zenity --error --text "$msg" --title "$title" &
|
||||
exit 124
|
||||
fi
|
||||
|
||||
if [ "$1" == "list" ] ; then
|
||||
|
||||
cmd=$(echo "$1" | base64)
|
||||
echo "$cmd" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassRead
|
||||
|
||||
elif [ "$1" == "get" ] ; then
|
||||
|
||||
cmd=$(echo "$1" | base64)
|
||||
key=$(echo "$2" | base64)
|
||||
echo "$cmd
|
||||
$key" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassRead
|
||||
|
||||
elif [ "$1" == "get-or-generate" ] ; then
|
||||
cmd=$(echo "$1" | base64)
|
||||
key=$(echo "$2" | base64)
|
||||
autogen=$(echo 1 | base64)
|
||||
echo "$cmd
|
||||
$key
|
||||
$autogen" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassManage
|
||||
|
||||
elif [ "$1" == "insert" ] ; then
|
||||
|
||||
cmd=$(echo "$1" | base64)
|
||||
key=$(echo "$2" | base64)
|
||||
multiline=$(echo "$3" | base64)
|
||||
contents=$(echo "$4" | base64)
|
||||
echo "$cmd
|
||||
$key
|
||||
$multiline
|
||||
$contents" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassManage
|
||||
|
||||
elif [ "$1" == "init" ] ; then
|
||||
|
||||
cmd=$(echo "$1" | base64)
|
||||
echo "$cmd" | /usr/lib/qubes/qrexec-client-vm "$QUBES_PASS_DOMAIN" ruddo.PassManage
|
||||
|
||||
fi
|
124
bin/qvm-pass
Executable file
124
bin/qvm-pass
Executable file
@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
|
||||
TEMP=`getopt -o ?dmfe: -- "$@"`
|
||||
force=0
|
||||
multiline=0
|
||||
echo=0
|
||||
eval set -- "$TEMP"
|
||||
set -e
|
||||
|
||||
usage() {
|
||||
echo "qvm-pass usage:"
|
||||
echo ""
|
||||
echo " qvm-pass [-d <passvm>] <subcommand> [arguments...]"
|
||||
echo ""
|
||||
echo "subcommands:"
|
||||
echo ""
|
||||
echo " list"
|
||||
echo " Retrieves the list of keys from the pass store."
|
||||
echo " No subcommand accomplishes the same results"
|
||||
echo " get <key>"
|
||||
echo " Retrieves a key from the pass store."
|
||||
echo " If your key is not named after a subcommand, you can also"
|
||||
echo " get its contents by passing it as the first argument of"
|
||||
echo " this command, omitting the get subcommand."
|
||||
echo " get-or-generate <key>"
|
||||
echo " Retrieves a key from the pass store; creates the key"
|
||||
echo " with 32 characters length if it does not exist yet,"
|
||||
echo " and returns the generated key on standard output."
|
||||
echo " insert [--echo,-e | --multiline,-m] [--force,-f] <key>"
|
||||
echo " Creates a key in the pass store."
|
||||
exit 0
|
||||
}
|
||||
|
||||
while true ; do
|
||||
case "$1" in
|
||||
-d)
|
||||
case "$2" in
|
||||
"") shift 2 ;;
|
||||
*) export QUBES_PASS_DOMAIN="$2" ; shift 2 ;;
|
||||
esac ;;
|
||||
-m)
|
||||
multiline=1 ; shift ;;
|
||||
-f)
|
||||
force=1 ; shift ;;
|
||||
-e)
|
||||
echo=1 ; shift ;;
|
||||
"-?")
|
||||
usage ;;
|
||||
--) shift ; break ;;
|
||||
*) echo "error processing options; run with -? for more information" ; exit 64 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$1" in
|
||||
get|get-or-generate)
|
||||
if [ "$force$multiline$echo" != "000" ] ; then
|
||||
echo "the $1 subcommand does not accept that option; run with -? for more information" >&2 ; exit 64
|
||||
fi
|
||||
if [ -z "$2" ] ; then
|
||||
echo "the $1 subcommand requires a key; run with -? for more information" >&2 ; exit 64
|
||||
fi
|
||||
if [ -n "$3" ] ; then
|
||||
echo "the $1 subcommand only accepts one argument; run with -? for more information" >&2 ; exit 64
|
||||
fi
|
||||
exec qubes-pass-client "$1" "$2"
|
||||
;;
|
||||
init)
|
||||
if [ "$force$multiline$echo" != "000" ] ; then
|
||||
echo "the $1 subcommand does not accept that option; run with -? for more information" >&2 ; exit 64
|
||||
fi
|
||||
if [ -n "$2" ] ; then
|
||||
echo "the $1 subcommand does not accept any arguments; run with -? for more information" >&2 ; exit 64
|
||||
fi
|
||||
exec qubes-pass-client "$1"
|
||||
;;
|
||||
insert)
|
||||
shift
|
||||
|
||||
if [ "$force" != "1" ] ; then
|
||||
ret=0 ; errs=$(qubes-pass-client get "$1" >/dev/null 2>&1) || ret=$?
|
||||
if [ "$ret" == "0" ] ; then
|
||||
read -p "An entry already exists for $1. Overwrite it? [y/N] " response
|
||||
if [ "$response" != "y" ] ; then exit 0 ; fi
|
||||
elif [ "$ret" == "8" ] ; then
|
||||
true
|
||||
else
|
||||
echo "$errs" >&2
|
||||
exit $ret
|
||||
fi
|
||||
fi
|
||||
|
||||
contents=
|
||||
if [ "$multiline" == "1" ] ; then
|
||||
echo "Enter contents of $1 and press Ctrl+D when finished:"
|
||||
echo ""
|
||||
contents=$(cat)
|
||||
elif [ "$echo" == "1" ] ; then
|
||||
read -p "Enter password for b: " contents >&2
|
||||
else
|
||||
read -s -p "Enter password for b: " contents >&2
|
||||
echo
|
||||
read -s -p "Retype password for b: " retypedcontents >&2
|
||||
echo
|
||||
if [ "$retypedcontents" != "$contents" ] ; then
|
||||
echo "Error: the entered passwords do not match."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
exec qubes-pass-client insert "$1" "$multiline" "$contents"
|
||||
;;
|
||||
list)
|
||||
if [ "$force$multiline$echo" != "000" ] ; then
|
||||
echo "the $1 subcommand does not accept that option; run with -? for more information" >&2 ; exit 64
|
||||
fi
|
||||
exec qubes-pass-client list
|
||||
;;
|
||||
*)
|
||||
if [ "$force$multiline$echo" != "000" ] ; then
|
||||
echo "the get subcommand does not accept that option; run with -? for more information" >&2 ; exit 64
|
||||
fi
|
||||
exec qubes-pass-client get "$1"
|
||||
;;
|
||||
esac
|
6
etc/qubes-rpc/policy/ruddo.PassManage
Normal file
6
etc/qubes-rpc/policy/ruddo.PassManage
Normal file
@ -0,0 +1,6 @@
|
||||
## Note that policy parsing stops at the first match,
|
||||
## so anything below the last line will have no effect.
|
||||
|
||||
## Please use a single # to start your custom comments.
|
||||
|
||||
$anyvm $anyvm ask
|
6
etc/qubes-rpc/policy/ruddo.PassRead
Normal file
6
etc/qubes-rpc/policy/ruddo.PassRead
Normal file
@ -0,0 +1,6 @@
|
||||
## Note that policy parsing stops at the first match,
|
||||
## so anything below the last line will have no effect.
|
||||
|
||||
## Please use a single # to start your custom comments.
|
||||
|
||||
$anyvm $anyvm ask
|
80
etc/qubes-rpc/ruddo.PassManage
Normal file
80
etc/qubes-rpc/ruddo.PassManage
Normal file
@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
read -n 4096 cmd
|
||||
cmd=$(echo "$cmd" | base64 -d)
|
||||
|
||||
if [ "$cmd" == "init" ] ; then
|
||||
|
||||
if test -f "$HOME"/.password-store/.gpg-id ; then
|
||||
key=$(cat "$HOME"/.password-store/.gpg-id)
|
||||
echo "Not creating password store already exists and uses GPG key $key." >&2
|
||||
exit 8
|
||||
fi
|
||||
|
||||
tmp=$(mktemp)
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
cat > "$tmp" <<EOF
|
||||
Key-Type: RSA
|
||||
Key-Length: 4096
|
||||
Name-Real: Pass store
|
||||
Name-Email: noreply@passwordstore.org
|
||||
Expire-Date: 0
|
||||
EOF
|
||||
ret=0 ; out=$(gpg2 --batch --gen-key "$tmp" 2>&1) || ret=$?
|
||||
if [ "$ret" != "0" ] ; then
|
||||
echo "$out" >&2
|
||||
exit "$ret"
|
||||
fi
|
||||
|
||||
key=$(echo "$out" | awk '/gpg: key .* marked as ultimately trusted/ { print $3 }')
|
||||
pass init "$key"
|
||||
echo "Do not forget to back up your password store regularly." >&2
|
||||
echo "To back up your password store, back up the entire $HOSTNAME VM using Qubes backup." >&2
|
||||
echo "Key files to backup: $HOME/.password-store and $HOME/.gnupg2" >&2
|
||||
|
||||
elif [ "$cmd" == "get-or-generate" ] ; then
|
||||
|
||||
read -n 4096 entry
|
||||
read -n 4096 autogen
|
||||
entry=$(echo "$entry" | base64 -d)
|
||||
autogen=$(echo "$autogen" | base64 -d)
|
||||
|
||||
if [ "$autogen" == "1" ] ; then
|
||||
ret=0 ; out=$(pass -- "$entry") || ret=$?
|
||||
if [ "$ret" == "1" ] && echo "$out" | grep -q "not in the password store" ; then
|
||||
logger -t ruddo.PassManage "creating password entry $entry"
|
||||
ret=0 ; out=$(pass generate -- "$entry" 32) || ret=$?
|
||||
if [ "$ret" == "1" ] ; then
|
||||
echo "Password generation failed: $out"
|
||||
exit "$ret"
|
||||
fi
|
||||
elif [ "$ret" != "0" ] ; then
|
||||
echo "$out" >&2
|
||||
exit "$ret"
|
||||
fi
|
||||
logger -t ruddo.PassManage "requested password entry $entry"
|
||||
exec pass -- "$entry"
|
||||
else
|
||||
exit 23
|
||||
fi
|
||||
|
||||
elif [ "$cmd" == "insert" ] ; then
|
||||
|
||||
read -n 4096 entry
|
||||
read -n 4096 multiline
|
||||
read -n 1048576 contents
|
||||
entry=$(echo "$entry" | base64 -d)
|
||||
multiline=$(echo "$multiline" | base64 -d)
|
||||
contents=$(echo "$contents" | base64 -d)
|
||||
|
||||
logger -t ruddo.PassManage "creating password entry $entry"
|
||||
|
||||
if [ "$multiline" == "1" ] ; then
|
||||
echo "$contents" | pass insert --multiline --force -- "$entry"
|
||||
else
|
||||
echo "$contents" | pass insert -e --force -- "$entry"
|
||||
fi
|
||||
|
||||
fi
|
29
etc/qubes-rpc/ruddo.PassRead
Normal file
29
etc/qubes-rpc/ruddo.PassRead
Normal file
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
read -n 4096 cmd
|
||||
cmd=$(echo "$cmd" | base64 -d)
|
||||
|
||||
if [ "$cmd" == "list" ] ; then
|
||||
|
||||
logger -t ruddo.PassRead "requested password list".
|
||||
exec pass
|
||||
|
||||
elif [ "$cmd" == "get" ] ; then
|
||||
|
||||
read -n 4096 entry
|
||||
entry=$(echo "$entry" | base64 -d)
|
||||
logger -t ruddo.PassRead "requested password entry $entry"
|
||||
|
||||
tmp=$(mktemp)
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
ret=0 ; pass -- "$entry" 2> "$tmp" || ret=$?
|
||||
if grep -qF "$entry is not in the password store." "$tmp" ; then
|
||||
cat "$tmp" >&2
|
||||
exit 8
|
||||
fi
|
||||
cat "$tmp" >&2
|
||||
exit $?
|
||||
|
||||
fi
|
78
qubes-pass.spec
Normal file
78
qubes-pass.spec
Normal file
@ -0,0 +1,78 @@
|
||||
%define debug_package %{nil}
|
||||
|
||||
%define mybuildnumber %{?build_number}%{?!build_number:1}
|
||||
|
||||
Name: qubes-pass
|
||||
Version: 0.0.1
|
||||
Release: %{mybuildnumber}%{?dist}
|
||||
Summary: Inter-VM pass password management for Qubes OS AppVMs and StandaloneVMs
|
||||
BuildArch: noarch
|
||||
|
||||
License: GPLv3+
|
||||
URL: https://github.com/Rudd-O/%{name}
|
||||
Source0: https://github.com/Rudd-O/%{name}/archive/{%version}.tar.gz#/%{name}-%{version}.tar.gz
|
||||
|
||||
BuildRequires: make
|
||||
|
||||
%package service
|
||||
Summary: Service package for Qubes OS dom0s that services %{name}
|
||||
|
||||
Requires: pass
|
||||
|
||||
%package dom0
|
||||
Summary: Policy package for Qubes OS dom0s that arbitrates %{name}
|
||||
|
||||
Requires: qubes-core-dom0-linux
|
||||
|
||||
%description
|
||||
This package lets you setup a safe password management VM and then
|
||||
manage the password store remotely from other VMs. You are meant to
|
||||
install this package on the VM or template where you want to access
|
||||
and manage your password store.
|
||||
|
||||
%description service
|
||||
This package lets you serve a safe password store to other VMs.
|
||||
You are meant to install this package on the VM or template where
|
||||
you want to keep your password store safe.
|
||||
|
||||
%description dom0
|
||||
This package contains the Qubes OS execution policy for the %{name} package.
|
||||
You are meant to install this package on the dom0 of the machine where you
|
||||
have VMs that have the %{name} package installed.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
# variables must be kept in sync with install
|
||||
make DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} SYSCONFDIR=%{_sysconfdir}
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
# variables must be kept in sync with build
|
||||
for target in install-client install-service install-dom0; do
|
||||
make $target DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} SYSCONFDIR=%{_sysconfdir}
|
||||
done
|
||||
|
||||
%check
|
||||
if grep -r '@.*@' $RPM_BUILD_ROOT ; then
|
||||
echo "Check failed: files with AT identifiers appeared" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
%files
|
||||
%attr(0755, root, root) %{_bindir}/qvm-pass
|
||||
%attr(0755, root, root) %{_bindir}/qubes-pass-client
|
||||
%doc README.md
|
||||
|
||||
%files service
|
||||
%attr(0644, root, root) %{_sysconfdir}/qubes-rpc/ruddo.PassRead
|
||||
%attr(0644, root, root) %{_sysconfdir}/qubes-rpc/ruddo.PassManage
|
||||
|
||||
%files dom0
|
||||
%config(noreplace) %attr(0644, root, qubes) %{_sysconfdir}/qubes-rpc/policy/ruddo.PassRead
|
||||
%config(noreplace) %attr(0644, root, qubes) %{_sysconfdir}/qubes-rpc/policy/ruddo.PassManage
|
||||
|
||||
%changelog
|
||||
* Mon Oct 24 2016 Manuel Amador (Rudd-O) <rudd-o@rudd-o.com>
|
||||
- Initial release.
|
Loading…
x
Reference in New Issue
Block a user