before cleanup
This commit is contained in:
@@ -50,7 +50,7 @@ all:
|
|||||||
musicmouse:
|
musicmouse:
|
||||||
squeezelite_name: MusicMouse
|
squeezelite_name: MusicMouse
|
||||||
shairport_name: MusicMouse
|
shairport_name: MusicMouse
|
||||||
alsa_card_name: 1
|
alsa_card_name: sndrpihifiberry
|
||||||
hifiberry_overlay: hifiberry-dacplus
|
hifiberry_overlay: hifiberry-dacplus
|
||||||
sensor_room_name: Kinderzimmer
|
sensor_room_name: Kinderzimmer
|
||||||
sensor_room_name_ascii: kinderzimmer
|
sensor_room_name_ascii: kinderzimmer
|
||||||
|
|||||||
@@ -1,63 +1,191 @@
|
|||||||
# Copy this to ".ansible/plugins/lookup"
|
# Copy this file to ~/.ansible/plugins/lookup/keepass.py
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# pip install keepassxc-proxy-client
|
||||||
|
#
|
||||||
|
# KeePassXC setup:
|
||||||
|
# Tools > Settings > Browser Integration > Enable browser integration
|
||||||
|
# The plugin will prompt you to approve the association in KeePassXC on first run.
|
||||||
|
# Association credentials are saved to ~/.keepassxc_ansible_assoc (override with
|
||||||
|
# env var KEEPASSXC_ASSOC_FILE).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# {{ lookup('keepass', 'https://example.com', 'password') }}
|
||||||
|
# {{ lookup('keepass', 'https://example.com', 'username') }}
|
||||||
|
# {{ lookup('keepass', 'https://example.com') }} # defaults to password
|
||||||
|
#
|
||||||
|
# Terms:
|
||||||
|
# terms[0] URL (or title) to search for — matched against KeePassXC entries
|
||||||
|
# terms[1] Attribute to return: username / user / password / pass / passwd
|
||||||
|
# Defaults to 'password' if omitted.
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
from ansible.plugins.lookup import LookupBase
|
from ansible.plugins.lookup import LookupBase
|
||||||
from keepasshttplib import keepasshttplib, encrypter
|
|
||||||
import requests
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
DOCUMENTATION = r"""
|
||||||
lookup: keepass
|
lookup: keepass
|
||||||
author: Martin Bauer <bauer_martin@gmx.de>
|
short_description: Fetch credentials from KeePassXC via the browser proxy protocol
|
||||||
version_added: '0.2'
|
|
||||||
short_description: fetch data from KeePass over KeePassHTTP
|
|
||||||
description:
|
description:
|
||||||
- This lookup returns a username or password queried by the URL of the keepass entry
|
- Connects to a running KeePassXC instance over its browser-integration Unix socket
|
||||||
|
(or Windows named pipe) and retrieves credentials for a given URL.
|
||||||
|
- On first use the plugin associates itself with KeePassXC; you will see an
|
||||||
|
approval dialog in KeePassXC. The association is saved to disk so subsequent
|
||||||
|
runs are silent.
|
||||||
options:
|
options:
|
||||||
_terms:
|
_terms:
|
||||||
description:
|
description:
|
||||||
- first is the URL to search for
|
- First term is the URL (or entry title URL) to look up.
|
||||||
- second is a property name of the entry, e.g. username or password
|
- Second term (optional) is the attribute to return.
|
||||||
required: True
|
Accepted values are username, user, password, pass, passwd.
|
||||||
|
Defaults to password.
|
||||||
|
required: true
|
||||||
notes:
|
notes:
|
||||||
- https://github.com/viczem/ansible-keepass
|
- KeePassXC must be running with Browser Integration enabled.
|
||||||
|
- Requires the Python package keepassxc-proxy-client.
|
||||||
example:
|
|
||||||
- "{{ lookup('keepass', 'urlOfEntry', 'password') }}"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = r"""
|
||||||
|
- name: Get password for an entry
|
||||||
|
debug:
|
||||||
|
msg: "{{ lookup('keepass', 'https://example.com') }}"
|
||||||
|
|
||||||
|
- name: Get username for an entry
|
||||||
|
debug:
|
||||||
|
msg: "{{ lookup('keepass', 'https://example.com', 'username') }}"
|
||||||
|
|
||||||
|
- name: Set pi account password from KeePassXC
|
||||||
|
user:
|
||||||
|
name: pi
|
||||||
|
password: "{{ lookup('keepass', 'default_rpi_password') | password_hash('sha512') }}"
|
||||||
|
"""
|
||||||
|
|
||||||
|
ASSOC_FILE_ENV = "KEEPASSXC_ASSOC_FILE"
|
||||||
|
ASSOC_FILE_DEFAULT = os.path.expanduser("~/.keepassxc_ansible_assoc")
|
||||||
|
ASSOC_NAME = "ansible-keepassxc"
|
||||||
|
|
||||||
|
USERNAME_ATTRS = {"username", "user"}
|
||||||
|
PASSWORD_ATTRS = {"password", "pass", "passwd"}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_assoc_file():
|
||||||
|
return os.environ.get(ASSOC_FILE_ENV, ASSOC_FILE_DEFAULT)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_assoc(connection):
|
||||||
|
"""Load a previously saved association from disk into *connection*. Returns True on success."""
|
||||||
|
path = _get_assoc_file()
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
with open(path, "r") as fh:
|
||||||
|
data = json.load(fh)
|
||||||
|
connection.load_associate(data["name"], bytes(data["public_key"]))
|
||||||
|
return True
|
||||||
|
except (KeyError, ValueError, OSError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _save_assoc(connection):
|
||||||
|
"""Persist the current association to disk."""
|
||||||
|
path = _get_assoc_file()
|
||||||
|
assoc_id, public_key = connection.dump_associate()
|
||||||
|
data = {"name": assoc_id, "public_key": list(public_key)}
|
||||||
|
with open(path, "w") as fh:
|
||||||
|
json.dump(data, fh)
|
||||||
|
os.chmod(path, 0o600)
|
||||||
|
|
||||||
|
|
||||||
|
def _connect():
|
||||||
|
"""Return an authenticated, test-verified Connection to KeePassXC."""
|
||||||
|
try:
|
||||||
|
from keepassxc_proxy_client.protocol import Connection, ResponseUnsuccesfulException
|
||||||
|
except ImportError:
|
||||||
|
raise AnsibleError(
|
||||||
|
"keepassxc-proxy-client is not installed. "
|
||||||
|
"Run: pip install keepassxc-proxy-client"
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = Connection()
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn.connect()
|
||||||
|
except Exception as exc:
|
||||||
|
raise AnsibleError(
|
||||||
|
"Cannot connect to KeePassXC. Is it running with Browser Integration enabled? "
|
||||||
|
"Error: {}".format(exc)
|
||||||
|
)
|
||||||
|
|
||||||
|
conn.change_public_keys()
|
||||||
|
|
||||||
|
# Try to reuse an existing association
|
||||||
|
if _load_assoc(conn):
|
||||||
|
try:
|
||||||
|
conn.test_associate(trigger_unlock=True)
|
||||||
|
return conn # existing association is still valid
|
||||||
|
except Exception:
|
||||||
|
pass # fall through and re-associate
|
||||||
|
|
||||||
|
# First run (or stale association) — ask KeePassXC to authorise us
|
||||||
|
try:
|
||||||
|
conn.associate()
|
||||||
|
except Exception as exc:
|
||||||
|
raise AnsibleError(
|
||||||
|
"KeePassXC association failed. "
|
||||||
|
"Please approve the request in the KeePassXC dialog. "
|
||||||
|
"Error: {}".format(exc)
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn.test_associate(trigger_unlock=True)
|
||||||
|
except Exception as exc:
|
||||||
|
raise AnsibleError("KeePassXC association test failed: {}".format(exc))
|
||||||
|
|
||||||
|
_save_assoc(conn)
|
||||||
|
return conn
|
||||||
|
|
||||||
|
|
||||||
class LookupModule(LookupBase):
|
class LookupModule(LookupBase):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(LookupModule, self).__init__(*args, **kwargs)
|
|
||||||
self.k = keepasshttplib.Keepasshttplib()
|
|
||||||
|
|
||||||
def run(self, terms, variables=None, **kwargs):
|
def run(self, terms, variables=None, **kwargs):
|
||||||
if not terms or len(terms) > 2:
|
if not terms or len(terms) > 2:
|
||||||
raise AnsibleError('Keepass wrong request format')
|
raise AnsibleError(
|
||||||
if len(terms) == 1:
|
"keepass lookup expects 1 or 2 arguments: "
|
||||||
entry_path, entry_attr = terms[0], 'password'
|
"lookup('keepass', '<url>', '<attribute>')"
|
||||||
else:
|
)
|
||||||
entry_path, entry_attr = terms[0], terms[1]
|
|
||||||
|
url = terms[0]
|
||||||
#if not self._test_connection():
|
attr = terms[1].lower() if len(terms) == 2 else "password"
|
||||||
# raise AnsibleError('Keepass is closed!')
|
|
||||||
try:
|
if attr not in USERNAME_ATTRS | PASSWORD_ATTRS:
|
||||||
auth = self.k.get_credentials(entry_path)
|
raise AnsibleError(
|
||||||
except Exception as e:
|
"keepass: unsupported attribute '{}'. "
|
||||||
raise AnsibleError('Keepass error obtaining entry {}: {}'.format(entry_path, e))
|
"Use one of: username, user, password, pass, passwd".format(attr)
|
||||||
if auth:
|
)
|
||||||
if entry_attr not in ('username', 'user', 'pass', 'passwd', 'password'):
|
|
||||||
raise AnsibleError("Keepass wrong entry")
|
conn = _connect()
|
||||||
|
|
||||||
ret = auth[0] if entry_attr.startswith('user') else auth[1]
|
|
||||||
return [ret]
|
|
||||||
|
|
||||||
def _test_connection(self):
|
|
||||||
key = self.k.get_key_from_keyring()
|
|
||||||
if key is None:
|
|
||||||
key = encrypter.generate_key()
|
|
||||||
id_ = self.k.get_id_from_keyring()
|
|
||||||
try:
|
try:
|
||||||
return self.k.test_associate(key, id_)
|
entries = conn.get_logins(url)
|
||||||
except requests.exceptions.ConnectionError as e:
|
except Exception as exc:
|
||||||
raise AnsibleError('Keepass Connection Error: {}'.format(e))
|
raise AnsibleError(
|
||||||
|
"keepass: failed to retrieve logins for '{}': {}".format(url, exc)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not entries:
|
||||||
|
raise AnsibleError(
|
||||||
|
"keepass: no entries found in KeePassXC for URL '{}'".format(url)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return the first matching entry (KeePassXC already filters by URL)
|
||||||
|
entry = entries[0]
|
||||||
|
|
||||||
|
if attr in USERNAME_ATTRS:
|
||||||
|
return [entry.get("login", "")]
|
||||||
|
else:
|
||||||
|
return [entry.get("password", "")]
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import json
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import subprocess
|
||||||
|
|
||||||
# ------------------- Config ----------------------------------------------------------------
|
# ------------------- Config ----------------------------------------------------------------
|
||||||
|
|
||||||
@@ -78,6 +79,12 @@ async def ble_scan():
|
|||||||
await stop_event.wait()
|
await stop_event.wait()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error", e)
|
print("Error", e)
|
||||||
|
try:
|
||||||
|
subprocess.run(["hciconfig", "hci0", "reset"], check=True)
|
||||||
|
except Exception as reset_err:
|
||||||
|
print(f"Reset failed: {reset_err}")
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
|
||||||
print("Starting again")
|
print("Starting again")
|
||||||
|
|
||||||
|
|
||||||
@@ -94,4 +101,4 @@ if __name__ == "__main__":
|
|||||||
os.system(f"hciconfig {restart_interface} up")
|
os.system(f"hciconfig {restart_interface} up")
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
print("Done")
|
print("Done")
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
user:
|
user:
|
||||||
name: pi
|
name: pi
|
||||||
update_password: always
|
update_password: always
|
||||||
password: "{{ lookup('keepass', 'default_rpi_password') | password_hash('sha512') }}"
|
password: "{{ lookup('keepass', 'ansible://default_rpi_password') | password_hash('sha512') }}"
|
||||||
- name: Install Packages (vim, git, basic python stuff)
|
- name: Install Packages (vim, git, basic python stuff)
|
||||||
apt:
|
apt:
|
||||||
name:
|
name:
|
||||||
|
|||||||
Reference in New Issue
Block a user