diff --git a/.env.test b/.env.test index 46a92ef..d44b349 100644 --- a/.env.test +++ b/.env.test @@ -7,5 +7,3 @@ NO_NVM_INSTALL=1 NO_PIP_INSTALL=1 VERBOSE_NVM_INSTALL=1 COMPOSE_FILE=docker-compose.yml:docker-compose-dev.yml:docker-compose-build.yml:docker-compose-test.yml -BITWARDEN_IMAGE=vaultwarden/server:1.25.2 -# BITWARDEN_IMAGE=bitwardenrs/server-postgresql:1.18.0 diff --git a/.gitignore b/.gitignore index f7e5db7..15ae211 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ .bash_history src/.coverage /dist +/.tox diff --git a/CHANGES.md b/CHANGES.md index 652eb69..15ea7f8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,15 @@ ## CHANGES +### 1.0.57 +- QA & CI/CD fixes [kiorky] +- Fix newer vaultwarden patch [kiorky] +- Fix newer vaultwarden adduser [kiorky] +- Fix new vaultwarden create_orga [kiorky] +- Fix newer vaultwarden set_org_acces [kiorky] + +### 1.0.56 +- Customizable auth payload support (2Factor, api auth) [Markus Kötter ]) + ### 1.0.55 - ensure requests is in requirements [kiorky] diff --git a/Dockerfile b/Dockerfile index 122de6b..c455c50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ # syntax=docker/dockerfile:1.3 -FROM corpusops/ubuntu-bare:20.04 +FROM corpusops/ubuntu-bare:22.04 WORKDIR /tmp/install ARG DEV_DEPENDENCIES_PATTERN='^#\s*dev dependencies' \ PY_VER=3.8 \ USER_NAME=app USER_UID=1000 USER_GROUP= USER_GID= \ USER_HOME=/w ARG PIP_SRC=$USER_HOME/lib -ENV USER_NAME=$USER_NAME USER_GROUP=${USER_GROUP:-$USER_NAME} USER_UID=$USER_UID USER_GID=${USER_GID:-${USER_UID}} USER_HOME=$USER_HOME PY_VER=${PY_VER:-} PIP_SRC=$PIP_SRC +ENV USER_NAME=$USER_NAME USER_GROUP=${USER_GROUP:-$USER_NAME} USER_UID=$USER_UID USER_GID=${USER_GID:-${USER_UID}} USER_HOME=$USER_HOME PY_VER=${PY_VER} PIP_SRC=$PIP_SRC # system dependendencies (pkgs, users, etc) ADD apt*.txt ./ @@ -16,6 +16,7 @@ RUN set -e \ useradd -s /bin/bash -d $USER_HOME -m -u $USER_UID -g $USER_UID $USER_NAME;fi \ && sed -i -re "s/(python-?)[0-9]\.[0-9]+/\1$PY_VER/g" apt.txt \ && apt update && apt install -y $(egrep -v "^#" apt.txt) \ + && git config --global --add safe.directory '*' \ && mkdir -pv "$PIP_SRC" && chown $USER_NAME "$PIP_SRC" \ && printf "$USER_NAME ALL=(ALL) NOPASSWD:ALL\n">/etc/sudoers.d/app \ && : end @@ -24,7 +25,7 @@ RUN set -e \ WORKDIR $USER_HOME # See https://github.com/pypa/setuptools/issues/3301 # ARG PIP_REQ=>=22 SETUPTOOLS_REQ=<60 \ -ARG PIP_REQ=>=22 SETUPTOOLS_REQ>=60 \ +ARG PIP_REQ=>=22 SETUPTOOLS_REQ=>=60 \ REQUIREMENTS=requirements/requirements.txt requirements/requirements-dev.txt ENV REQUIREMENTS=$REQUIREMENTS PIP_REQ=$PIP_REQ SETUPTOOLS_REQ=$SETUPTOOLS_REQ ADD --chown=app:app lib/ lib/ diff --git a/README.md b/README.md index 61d447b..a8c7640 100644 --- a/README.md +++ b/README.md @@ -41,17 +41,17 @@ docker-compose run --rm app bash ``` ```bash -sed "/COMPOSE_FILE/d" .env -echo COMPOSE_FILE=docker-compose.yml:docker-compose-dev.yml" +sed -i -e "/COMPOSE_FILE/d" .env +echo "COMPOSE_FILE=docker-compose.yml:docker-compose-dev.yml" >> .env docker-compose up -d --force-recreate -docker-compose exec -U app bash +docker-compose exec -u app app bash ``` ### run tests ```bash -sed "/COMPOSE_FILE/d" .env -echo COMPOSE_FILE=docker-compose.yml:docker-compose-dev.yml:docker-compose-test.yml" -docker-compose exec -U app app tox -e linting,coverage +sed -i -e "/COMPOSE_FILE/d" .env +echo "COMPOSE_FILE=docker-compose.yml:docker-compose-dev.yml:docker-compose-test.yml" >> .env +docker-compose exec -u app app tox -e linting,coverage ``` ## Credits and bibliography diff --git a/USAGE.md b/USAGE.md index e24f935..b7cf392 100644 --- a/USAGE.md +++ b/USAGE.md @@ -151,6 +151,44 @@ c.remove_user_from_collection(userOrEmail, colc) c.remove_user_from_organization(userOrEmail, orga) ``` +### Manipulating the login data structure via callback (2Factor) +This allows other login mechanisms such as totp or api key: + +example 1: + +```python + +def mfa2fa(loginpayload): + totp = pyotp.TOTP(otpseed) + loginpayload.update( + { + "twoFactorToken": str(totp.now()), + "twoFactorProvider": "0", + "twoFactorRemember": "0" + } + ) + return loginpayload + +client = Client(server, email, password, authentication_cb=mfa2fa) +``` + +example 1: + +```python +def api_key(loginpayload): + loginpayload.update( + { + "client_id": CLIENT_ID, + "client_secret": CLIENT_SECRET, + "scope": "api", + "grant_type": "client_credentials" + } + ) + return loginpayload + +client = Client(server, email, password, authentication_cb=api_key) +``` + ### encode the vaultwarden/bitwarden_rs key for autovalidating user ```sh base64 $BITWARDEN_RS_SERVER_DATA/rsa_key.der|tr -d '\n' diff --git a/apt.txt b/apt.txt index 341ae7f..fc780e5 100644 --- a/apt.txt +++ b/apt.txt @@ -3,25 +3,25 @@ openssh-client rsync # runtime dependencies python3 -python3.8 -python3.8-distutils -python3.8-lib2to3 -libpython3.8 +python3.10 +python3.10-distutils +python3.10-lib2to3 +libpython3.10 binutils ca-certificates curl gettext git less -libllvm10 +libllvm11 libsoup2.4-1 -llvm-10 +llvm-11 lsb-release sudo tzdata vim wget -python3.8-venv +python3.10-venv python3-virtualenv python3-distlib python3-pkg-resources @@ -32,7 +32,6 @@ build-essential gpg libgcc-9-dev libstdc++-9-dev -llvm-10-dev -python3.8-dev -python3-dev +llvm-11-dev +python3.10-dev software-properties-common diff --git a/docker-compose-build.yml b/docker-compose-build.yml index f7a210e..0b30556 100644 --- a/docker-compose-build.yml +++ b/docker-compose-build.yml @@ -4,5 +4,5 @@ services: build: context: "." args: - PY_VER: 3.8 + PY_VER: "3.10" REQUIREMENTS: requirements/requirements.txt requirements/requirements-dev.txt diff --git a/docker-compose-test.yml b/docker-compose-test.yml index bb89a99..b59f159 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -8,7 +8,7 @@ x-bases: BITWARDEN_DB_IMAGE: corpusops/postgres:13 BITWARDEN_DOMAIN: bitwarden DATABASE_URL: postgresql://db:db@db/db - BITWARDEN_IMAGE: vaultwarden/server:1.21.0 + BITWARDEN_IMAGE: vaultwarden/server:1.29.1 DATA_FOLDER: /data DISABLE_ADMIN_TOKEN: "true" DOMAIN: http://bitwarden @@ -44,7 +44,7 @@ x-bases: SMTP_SSL: "false" SMTP_TIMEOUT: '15' WEBSOCKET_ENABLED: "true" -networks: {app_net: {driver: bridge, ipam: {config: [{subnet: "${BITWARDEN_NETWORK:-172.38.0}.0/24"}], driver: default}}} +networks: {bitwarden_net: {driver: bridge, ipam: {config: [{subnet: "${BITWARDEN_NETWORK:-172.38.0}.0/24"}], driver: default}}} services: setup: <<: [ *base ] @@ -72,13 +72,13 @@ services: rf while true;do printf "HTTP/1.1 200 OK\nContent-Length: 7\n\nstarted\n"|( nc -l -p 80 || /bin/true);done image: corpusops/postgres:13 - networks: {app_net: {ipv4_address: "${BITWARDEN_NETWORK:-172.38.0}.6"}} + networks: {bitwarden_net: {ipv4_address: "${BITWARDEN_NETWORK:-172.38.0}.6"}} volumes: - helpers:/helpers:rw db: <<: [ *base ] image: corpusops/postgres:13 - networks: {app_net: {ipv4_address: "${BITWARDEN_NETWORK:-172.38.0}.4"}} + networks: {bitwarden_net: {ipv4_address: "${BITWARDEN_NETWORK:-172.38.0}.4"}} security_opt: [seccomp=unconfined] volumes: - db:/var/lib/postgresql/data:rw @@ -92,13 +92,13 @@ services: -p -c MailHog' hostname: mailcatcher image: corpusops/mailhog - networks: {app_net: {ipv4_address: "${BITWARDEN_NETWORK:-172.38.0}.12"}} + networks: {bitwarden_net: {ipv4_address: "${BITWARDEN_NETWORK:-172.38.0}.12"}} user: root volumes: - mails:/mails:rw bitwarden: <<: [ *base ] - image: $BITWARDEN_IMAGE + image: ${BITWARDEN_IMAGE:-vaultwarden/server:1.29.1} hostname: bitwarden depends_on: [db, setup, mailcatcher] entrypoint: @@ -106,7 +106,7 @@ services: - -exc - 'while !(curl -s setup);do echo "not ready">&2;sleep 0.5;done && exec /start.sh "$$@"' - networks: {app_net: {ipv4_address: "${BITWARDEN_NETWORK:-172.38.0}.14"}} + networks: {bitwarden_net: {ipv4_address: "${BITWARDEN_NETWORK:-172.38.0}.14"}} volumes: - bitwarden:/data:rw - ./bitwarden:/conf:rw @@ -114,7 +114,7 @@ services: - logs:/logs:rw app: <<: [ *base ] - networks: {app_net: {}} + networks: {bitwarden_net: {}} depends_on: [bitwarden] entrypoint: - bash @@ -136,6 +136,22 @@ services: volumes: - "bitwarden:/bitwarden" - helpers:/helpers:rw + traefik: + image: "traefik:v2.10" + networks: {bitwarden_net: {}} + entrypoint: + - "sh" + - "-xec" + - |- + /entrypoint.sh --configFile=/app/traefik.toml + ports: + - "${BITWARDEN_PORT:-3010}:80" + - "${BITWARDEN_PORT:-3013}:8080" + - "${BITWARDEN_PORT:-3011}:443" + volumes: + - "./:/app:ro" + - "./traefik.toml:/traefik.toml" + - "./traefik.r.toml:/traefik.r.toml" volumes: bitwarden: {} mails: {} diff --git a/gencert.sh b/gencert.sh new file mode 100755 index 0000000..392f0a8 --- /dev/null +++ b/gencert.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env sh +set -ex +cd $(dirname $(readlink -f $0)) +apk update && apk add openssl +openssl req -nodes -x509 -sha256 -newkey rsa:4096 \ + -keyout local/cert.key \ + -out local/cert.crt \ + -days 356 \ + -subj "/C=NL/ST=Zuid Holland/L=Rotterdam/O=ACME Corp/OU=IT Dept/CN=example.org" \ + -addext "subjectAltName = DNS:localhost,DNS:wkbox2,DNS:localhost:3012,DNS:wkbox2:3012,DNS:vaultwarden:3011,DNS:vaultwarden:3012" +# vim:set et sts=4 ts=4 tw=80: diff --git a/requirements/requirements.txt b/requirements/requirements.txt index a334300..be95814 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,5 +1,5 @@ -e . --e git+https://github.com/corpusops/vaultier-cli.git#egg=vaultcli +-e git+https://github.com/corpusops/vaultier-cli.git##egg=vaultcli click hkdf diff --git a/setup.py b/setup.py index cee87eb..5e4cc09 100644 --- a/setup.py +++ b/setup.py @@ -28,11 +28,13 @@ def read(*rnames): long_description = "\n\n".join([read(a) for a in READMES]) classifiers = ["Programming Language :: Python", "Topic :: Software Development"] name = "bitwardentools" -version = "1.0.55" +version = "1.0.57" src_dir = "src" req = re.compile("^(?!(-e|#))", flags=re.I | re.M) install_requires = [ - a.strip() for a in open("requirements/requirements.txt").read().splitlines() if req.search(a) and a.strip() + a.strip() + for a in open("requirements/requirements.txt").read().splitlines() + if req.search(a) and a.strip() ] extra_requires = {} candidates = {} @@ -51,7 +53,7 @@ def read(*rnames): author="kiorky", author_email="kiorky@cryptelium.net", url="https://github.com/corpusops/bitwardentools", - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", license="GPL", packages=find_packages(src_dir), package_dir={"": src_dir}, diff --git a/src/bitwardentools/client.py b/src/bitwardentools/client.py index aa03e87..d5c4da7 100644 --- a/src/bitwardentools/client.py +++ b/src/bitwardentools/client.py @@ -21,6 +21,7 @@ import requests from jwt import encode as jwt_encode +from packaging import version as _version from bitwardentools import crypto as bwcrypto from bitwardentools.common import L, caseinsentive_key_search @@ -96,6 +97,12 @@ "managePolicies": False, "manageUsers": False, } +IS_BITWARDEN_RE = re.compile( + "[0-9][0-9][0-9][0-9][.][0-9][0-9]?[.][0-9][0-9]?", flags=re.I | re.U | re.M +) +API_CHANGES = { + "1.27.0": _version.parse("1.27.0"), +} def uncapitzalize(s): @@ -138,17 +145,17 @@ def rewrite_acls_collection(i, skip=None): skip = re.compile(skip) if isinstance(i, dict): for v, k in { - "Data": "data", - "Id": "id", - "AccessAll": "accessAll", - "Email": "email", - "Name": "name", - "Status": "status", - "Collections": "collections", - "UserId": "userId", - "Type": "type", - "HidePasswords": "hidePasswords", - "ReadOnly": "readOnly", + "data": "data", + "id": "id", + "accessAll": "accessAll", + "email": "email", + "name": "name", + "status": "status", + "collections": "collections", + "userId": "userId", + "type": "type", + "hidePasswords": "hidePasswords", + "readOnly": "readOnly", }.items(): if skip and (skip.search(v) or skip.search(k)): i.pop(v, None) @@ -360,9 +367,9 @@ def get_types(t): def get_type(obj, default=""): if isinstance(obj, BWFactory): - objtyp = getattr(obj, "Object", getattr(obj, "object", "")) + objtyp = getattr(obj, "object", getattr(obj, "object", "")) elif isinstance(obj, dict): - objtyp = obj.get("Object", obj.get("object", "")) + objtyp = obj.get("object", obj.get("object", "")) else: objtyp = default return objtyp.lower() @@ -477,13 +484,13 @@ def construct( try: object_class_name = jsond["object"] except KeyError: - object_class_name = jsond["Object"] + object_class_name = jsond["object"] if object_class_name.lower().startswith("cipher"): try: - typ = jsond["Type"] + typ = jsond["type"] object_class = SECRETS_CLASSES[typ] except KeyError: - L.error(f'Unkown cipher {jsond.get("Id", "")}') + L.error(f'Unkown cipher {jsond.get("id", "")}') else: object_class = get_obj(object_class_name) a = object_class( @@ -685,7 +692,7 @@ def __init__( login=True, cache=None, vaultier=False, - authentication_cb=None + authentication_cb=None, ): # goal is to allow shared cache amongst client instances # but also if we want totally isolated caches @@ -719,6 +726,8 @@ def __init__( self.authentication_cb = authentication_cb if login: self.login() + self._is_vaultwarden = False + self._version = None @property def token(self): @@ -999,9 +1008,9 @@ def get_organizations(self, sync=None, cache=None, token=None): assert _CACHE.get("sync") except AssertionError: sdata = self.api_sync(sync=sync) - for orga in sdata.get("Profile", {}).get("Organizations", []): + for orga in sdata.get("profile", {}).get("organizations", []): orga = deepcopy(orga) - orga["Object"] = "organization" + orga["object"] = "organization" obj = BWFactory.construct(orga, client=self, unmarshall=True) self.cache(obj) _CACHE["sync"] = True @@ -1042,7 +1051,6 @@ def get_organization(self, orga, sync=None, cache=None, token=None, complete=Non return self.finish_orga(v, token=token, cache=cache, complete=complete) except KeyError: pass - import pdb;pdb.set_trace() exc = OrganizationNotFound(f"No such organization found {orga}") exc.criteria = [orga] raise exc @@ -1386,12 +1394,12 @@ def get_organization_key(self, orga, token=None, sync=None): enc_okey = ( dict( [ - (a["Id"], a) - for a in sdata.get("Profile", {}).get("Organizations", []) + (a["id"], a) + for a in sdata.get("profile", {}).get("organizations", []) ] ) .get(orga.id, {}) - .get("Key", None) + .get("key", None) ) if enc_okey: break @@ -1425,6 +1433,9 @@ def edit_orgcollection(self, collection, token=None, **jsond): if not bwcrypto.SYM_ENCRYPTED_STRING_RE.match(data["name"]): _, k = self.get_organization_key(obj._orga, token=token) data["name"] = bwcrypto.encrypt(bwcrypto.CIPHERS.sym, data["name"], k) + v, i = self.version() + if i and (v > API_CHANGES["1.27.0"]): + data.setdefault("users", []) obj = self._upload_object( f"/api/organizations/{obj._orga.id}/collections/{obj.id}", data, @@ -1435,6 +1446,20 @@ def edit_orgcollection(self, collection, token=None, **jsond): self.cache(obj) return obj + def version(self, force=False): + # bitwarden scheme is yyyy.mm.xx + # vaultwarden scheme is SEMVER + if force or not self._version: + try: + v = self.r("/api/version", method="get").json() + except Exception: + trace = traceback.format_exc() + L.error(trace) + v = "1.0.0" + self._is_vaultwarden = not bool(IS_BITWARDEN_RE.search(v)) + self._version = _version.parse(v) + return self._version, self._is_vaultwarden + def create_orgcollection( self, name, organizationId=None, orga=None, externalId=None, token=None, **jsond ): @@ -1442,7 +1467,11 @@ def create_orgcollection( token = self.get_token(token) _, k = self.get_organization_key(orga, token=token) encoded_name = bwcrypto.encrypt(bwcrypto.CIPHERS.sym, name, k) - data = {"externalId": [], "groups": [], "name": encoded_name} + v, i = self.version() + if i and (v > API_CHANGES["1.27.0"]): + data = {"externalId": "", "groups": [], "users": [], "name": encoded_name} + else: + data = {"externalId": "", "groups": [], "name": encoded_name} log = "Creating :" if orga: log += f" in orga: {orga.name}/{orga.id}:" @@ -1485,7 +1514,7 @@ def get_collections(self, orga=None, sync=None, cache=None, token=None): assert _CACHE["sync"] except AssertionError: for enccol in ( - self.r("/api/collections", method="get").json().get("Data", []) + self.r("/api/collections", method="get").json().get("data", []) ): col = BWFactory.construct(enccol, client=self, unmarshall=True) _, colk = self.get_organization_key(col.organizationId, token=token) @@ -1673,12 +1702,12 @@ def get_ciphers( exc.response = resp raise exc dciphers = [] - for cipher in ciphers.get("Data", []): + for cipher in ciphers.get("data", []): try: dciphers.append(self.decrypt(cipher, token=token)) except bwcrypto.DecryptError: - self._broken_ciphers[cipher["Id"]] = cipher - L.info(f'Cant decrypt cipher {cipher["Id"]}, broken ?') + self._broken_ciphers[cipher["id"]] = cipher + L.info(f'Cant decrypt cipher {cipher["id"]}, broken ?') for cipher in dciphers: obj = BWFactory.construct(cipher, client=self, unmarshall=True) self.cache(obj, vaultier=vaultier) @@ -2458,6 +2487,9 @@ def get_accesses(self, objs, sync=None, token=None): ) raise exc u = f"/api/organizations/{orga.id}/users/{ouid}" + v, i = self.version() + if i and (v > API_CHANGES["1.27.0"]): + u += "?includeGroups=True&includeCollections=true" resp = self.r(u, token=token, method="get", json={}) try: self.assert_bw_response(resp) @@ -2677,6 +2709,9 @@ def add_user_to_organization( dcollections, readonly=readonly, hidepasswords=hidepasswords )["payloads"] u = f"/api/organizations/{orga.id}/users/invite" + v, i = self.version() + if i and (v > API_CHANGES["1.27.0"]): + params.setdefault("groups", []) response = self.r(u, json=params, token=token) self.assert_bw_response(response) return response @@ -2783,13 +2818,19 @@ def set_organization_access( # set access on subsequent collections cremove = cdata.get("remove", remove) if cremove: + found = True try: uacl["daccess"][email][cid] except KeyError: - L.info( - f"{email} has no access to collection {collection.name}/{collection.id}, no removal" - ) - else: + try: + cacl = self.get_accesses(collection) + cacl["daccess"][email] + except KeyError: + found = False + L.info( + f"{email} has no access to collection {collection.name}/{collection.id}, no removal" + ) + if found: L.info(f"Removing {cid} from {email} collections") todo = True payload["removed"].add(cid) @@ -3146,9 +3187,9 @@ def confirm_invitation(self, orga, email, name=None, sync=None, token=None): user_id = acl["userId"] resp = self.r(f"/api/users/{user_id}/public-key", method="get") self.assert_bw_response(resp) - userorgkey = b64decode(resp.json()["PublicKey"]) + userorgkey = b64decode(resp.json()["publicKey"]) encoded_key = bwcrypto.encrypt_asym(orgkey[1], userorgkey) - payload = {"Key": encoded_key} + payload = {"key": encoded_key} try: u = f"/api/organizations/{orga.id}/users/{acl['id']}/confirm" resp = self.r(u, json=payload, token=token) diff --git a/src/bitwardentools/create_and_notify.py b/src/bitwardentools/create_and_notify.py index 9ae6988..abd5baf 100755 --- a/src/bitwardentools/create_and_notify.py +++ b/src/bitwardentools/create_and_notify.py @@ -20,9 +20,7 @@ @click.command() @click.option("--login") @click.option("--password", default="") -@click.option( - "--register-to", default=os.environ.get("BW_ORGAS_REGISTER_TO", "") -) +@click.option("--register-to", default=os.environ.get("BW_ORGAS_REGISTER_TO", "")) @click.option("--server", default=bitwardentools.SERVER) @click.option("--mail-lang", default=MAIL_LANG) @click.option("--tls", default=os.environ.get("BW_MAIL_TLS", "1")) @@ -90,7 +88,7 @@ def main( client.create_user( login, name=login.split("@")[0], password=password, auto_validate=True ) - for i in register_to.split(':'): + for i in register_to.split(":"): client.set_organization_access( login, i, access_level=bwclient.CollectionAccess.admin, accessAll=True ) diff --git a/src/tests/test_client.py b/src/tests/test_client.py index 379765c..19076b6 100755 --- a/src/tests/test_client.py +++ b/src/tests/test_client.py @@ -58,7 +58,7 @@ def wipe_users(self): for attr, email, password in self.get_users(): try: self.client.delete_user(email) - except (bwclient.UserNotFoundError): + except bwclient.UserNotFoundError: pass @classmethod @@ -616,7 +616,10 @@ def test_set_organization_access(self): self.assertFalse(a1a["daccess"][self.user1[0].email]["hidePasswords"]) self.assertTrue(a1b["daccess"][self.user1[0].email]["hidePasswords"]) self.assertEqual( - strip_dict_data(ao1["daccess"][self.user1[0].email]), + strip_dict_data( + ao1["daccess"][self.user1[0].email], + skip=f"{DSKIP}|(g|G)roups|TwoFactorEnabled|ResetPasswordEnrolled|ExternalId|collections", + ), { "accessAll": False, "email": "foo@org.comsimple1", diff --git a/testreset.sh b/testreset.sh index 5ce5011..3179564 100755 --- a/testreset.sh +++ b/testreset.sh @@ -5,5 +5,5 @@ docker-compose exec -T db bash -c \ docker-compose stop -t0 docker-compose rm -f docker-compose run --rm --entrypoint bash app -exc 'rm -rf /bitwarden/*' -docker-compose up -d --force-recreate --no-deps db setup bitwarden +docker-compose up -d --force-recreate --no-deps db setup bitwarden traefik docker-compose up -d --force-recreate --no-deps app diff --git a/traefik.r.toml b/traefik.r.toml new file mode 100644 index 0000000..791d04c --- /dev/null +++ b/traefik.r.toml @@ -0,0 +1,10 @@ +[http.routers.r] +entryPoints = ["web", "websecure"] +service = "s@file" +rule = "PathPrefix(`/`)" +tls=true + +[http.services.s.loadBalancer] +passHostHeader = true +[[http.services.s.loadBalancer.servers]] +url = "http://bitwarden/" diff --git a/traefik.toml b/traefik.toml new file mode 100644 index 0000000..1cc625f --- /dev/null +++ b/traefik.toml @@ -0,0 +1,10 @@ +[global] +checkNewVersion=false +sendAnonymousUsage=false + +[entryPoints] +web.address=":80" +websecure.address=":443" + +[providers.file] +filename="/app/traefik.r.toml"