Compare commits
30 Commits
v0.5.4
...
Lerentis/i
Author | SHA1 | Date | |
---|---|---|---|
6f099c4bf2
|
|||
aa015cc7ba | |||
2de9bbb0bf | |||
4505f3985c | |||
82b684e460 | |||
8ec698f50e | |||
9b8fe1d8ef | |||
516f2a34cf | |||
361d0866e9
|
|||
9d4ade904e
|
|||
8c3714f7e0
|
|||
36ae5cc602
|
|||
d908419b78
|
|||
2d399ff8ce
|
|||
c753737497
|
|||
886fe3783d
|
|||
18a47f8ad2 | |||
e405734e72 | |||
8bf4292991
|
|||
b149b26485 | |||
5263a811e1 | |||
4b59ff1aac | |||
ad1cc9f646 | |||
0f518ab28d
|
|||
1bf2a24cf2
|
|||
a73e8ff982
|
|||
54a4ffa212 | |||
16040bf87a
|
|||
9c1c7417e1 | |||
219c9d0413 |
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@ -8,7 +8,9 @@ on:
|
||||
jobs:
|
||||
release:
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
packages: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -34,15 +36,36 @@ jobs:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Get app version from chart
|
||||
uses: mikefarah/yq@v4.33.1
|
||||
uses: mikefarah/yq@v4.34.1
|
||||
id: app_version
|
||||
with:
|
||||
cmd: yq '.appVersion' charts/bitwarden-crd-operator/Chart.yaml
|
||||
|
||||
- name: "GHCR Login"
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: lerentis
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: "GHCR Build and Push"
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ghcr.io/lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }}
|
||||
|
||||
- name: Create SBOM
|
||||
uses: anchore/sbom-action@v0
|
||||
with:
|
||||
image: lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }}
|
||||
image: ghcr.io/lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }}
|
||||
|
||||
- name: Publish SBOM
|
||||
uses: anchore/sbom-action/publish-sbom@v0
|
||||
|
55
.github/workflows/test-and-lint.yml
vendored
Normal file
55
.github/workflows/test-and-lint.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
name: Lint and Test
|
||||
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
lint-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v3
|
||||
with:
|
||||
version: v3.11.2
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
check-latest: true
|
||||
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@v2.4.0
|
||||
|
||||
- name: Run chart-testing (list-changed)
|
||||
id: list-changed
|
||||
run: |
|
||||
changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }})
|
||||
if [[ -n "$changed" ]]; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Run chart-testing (lint)
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
run: ct lint --target-branch ${{ github.event.repository.default_branch }}
|
||||
|
||||
pr-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: "GHCR Build"
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
push: false
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ghcr.io/lerentis/bitwarden-crd-operator:dev
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -165,4 +165,6 @@ include
|
||||
lib
|
||||
lib64
|
||||
|
||||
myvalues.yaml
|
||||
myvalues.yaml
|
||||
|
||||
.vscode
|
50
Dockerfile
50
Dockerfile
@ -1,29 +1,45 @@
|
||||
FROM alpine:latest as builder
|
||||
FROM alpine:3.18.0
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/Lerentis/bitwarden-crd-operator
|
||||
LABEL org.opencontainers.image.description="Kubernetes Operator to create k8s secrets from bitwarden"
|
||||
LABEL org.opencontainers.image.licenses=MIT
|
||||
|
||||
ARG PYTHON_VERSION=3.11.4-r0
|
||||
ARG PIP_VERSION=23.1.2-r0
|
||||
ARG GCOMPAT_VERSION=1.1.0-r1
|
||||
ARG LIBCRYPTO_VERSION=3.1.0-r4
|
||||
ARG BW_VERSION=2023.1.0
|
||||
|
||||
RUN apk add wget unzip
|
||||
|
||||
RUN cd /tmp && wget https://github.com/bitwarden/clients/releases/download/cli-v${BW_VERSION}/bw-linux-${BW_VERSION}.zip && \
|
||||
unzip /tmp/bw-linux-${BW_VERSION}.zip
|
||||
|
||||
FROM alpine:3.17.3
|
||||
|
||||
ARG PYTHON_VERSION=3.10.11-r0
|
||||
ARG PIP_VERSION=22.3.1-r1
|
||||
ARG GCOMPAT_VERSION=1.1.0-r0
|
||||
|
||||
COPY --from=builder /tmp/bw /usr/local/bin/bw
|
||||
COPY requirements.txt requirements.txt
|
||||
COPY requirements.txt /requirements.txt
|
||||
|
||||
RUN set -eux; \
|
||||
apk add --virtual build-dependencies wget unzip; \
|
||||
ARCH="$(apk --print-arch)"; \
|
||||
case "${ARCH}" in \
|
||||
aarch64|arm64) \
|
||||
apk add npm; \
|
||||
npm install -g @bitwarden/cli@${BW_VERSION}; \
|
||||
;; \
|
||||
amd64|x86_64) \
|
||||
cd /tmp; \
|
||||
wget https://github.com/bitwarden/clients/releases/download/cli-v${BW_VERSION}/bw-linux-${BW_VERSION}.zip; \
|
||||
unzip /tmp/bw-linux-${BW_VERSION}.zip; \
|
||||
mv /tmp/bw /usr/local/bin/bw; \
|
||||
chmod +x /usr/local/bin/bw; \
|
||||
;; \
|
||||
*) \
|
||||
echo "Unsupported arch: ${ARCH}"; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac; \
|
||||
apk del --purge build-dependencies; \
|
||||
addgroup -S -g 1000 bw-operator; \
|
||||
adduser -S -D -u 1000 -G bw-operator bw-operator; \
|
||||
mkdir -p /home/bw-operator; \
|
||||
chown -R bw-operator /home/bw-operator; \
|
||||
chmod +x /usr/local/bin/bw; \
|
||||
apk add gcc musl-dev libstdc++ gcompat=${GCOMPAT_VERSION} python3=${PYTHON_VERSION} py3-pip=${PIP_VERSION}; \
|
||||
pip install -r requirements.txt --no-warn-script-location; \
|
||||
apk add gcc musl-dev libstdc++ gcompat=${GCOMPAT_VERSION} python3=${PYTHON_VERSION} py3-pip=${PIP_VERSION} libcrypto3=${LIBCRYPTO_VERSION}; \
|
||||
pip install -r /requirements.txt --no-warn-script-location; \
|
||||
rm /requirements.txt; \
|
||||
apk del --purge gcc musl-dev libstdc++;
|
||||
|
||||
COPY --chown=bw-operator:bw-operator src /home/bw-operator
|
||||
|
@ -4,9 +4,9 @@ description: Deploy the Bitwarden CRD Operator
|
||||
|
||||
type: application
|
||||
|
||||
version: "v0.5.4"
|
||||
version: "v0.7.4"
|
||||
|
||||
appVersion: "0.5.4"
|
||||
appVersion: "0.6.4"
|
||||
|
||||
keywords:
|
||||
- operator
|
||||
@ -20,7 +20,7 @@ home: https://lerentis.github.io/bitwarden-crd-operator/
|
||||
sources:
|
||||
- https://github.com/Lerentis/bitwarden-crd-operator
|
||||
|
||||
kubeVersion: '>= 1.23.0-0'
|
||||
kubeVersion: ">= 1.23.0-0"
|
||||
|
||||
maintainers:
|
||||
- name: lerentis
|
||||
@ -55,10 +55,10 @@ annotations:
|
||||
content:
|
||||
- element:
|
||||
secretName: username
|
||||
secretRef: nameofUser
|
||||
secretRef: nameofUser
|
||||
- element:
|
||||
secretName: password
|
||||
secretRef: passwordOfUser
|
||||
secretRef: passwordOfUser
|
||||
id: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
||||
name: "test-secret"
|
||||
namespace: "default"
|
||||
@ -90,12 +90,13 @@ annotations:
|
||||
apps:
|
||||
"some.app.identifier:some_version":
|
||||
pubkey: {{ bitwarden_lookup("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "fields", "public_key") }}
|
||||
enabled: true
|
||||
enabled: true
|
||||
artifacthub.io/license: MIT
|
||||
artifacthub.io/operator: "true"
|
||||
artifacthub.io/operator: "true"
|
||||
artifacthub.io/containsSecurityUpdates: "false"
|
||||
artifacthub.io/changes: |
|
||||
- kind: changed
|
||||
description: "Bump Alpine base image from 3.17.2 to 3.17.3"
|
||||
- kind: fixed
|
||||
description: "Fixed bitwarden installation"
|
||||
artifacthub.io/images: |
|
||||
- name: bitwarden-crd-operator
|
||||
image: lerentis/bitwarden-crd-operator:0.5.4
|
||||
image: ghcr.io/lerentis/bitwarden-crd-operator:0.6.4
|
||||
|
@ -5,7 +5,7 @@
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: lerentis/bitwarden-crd-operator
|
||||
repository: ghcr.io/lerentis/bitwarden-crd-operator
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
# tag: "0.1.0"
|
||||
@ -14,15 +14,15 @@ imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
#env:
|
||||
# - name: BW_HOST
|
||||
# value: "define_it"
|
||||
# - name: BW_CLIENTID
|
||||
# value: "define_it"
|
||||
# - name: BW_CLIENTSECRET
|
||||
# value: "define_it"
|
||||
# - name: BW_PASSWORD
|
||||
# value: "define_id"
|
||||
# env:
|
||||
# - name: BW_HOST
|
||||
# value: "define_it"
|
||||
# - name: BW_CLIENTID
|
||||
# value: "define_it"
|
||||
# - name: BW_CLIENTSECRET
|
||||
# value: "define_it"
|
||||
# - name: BW_PASSWORD
|
||||
# value: "define_id"
|
||||
|
||||
externalConfigSecret:
|
||||
enabled: false
|
||||
|
14
example.yaml
14
example.yaml
@ -15,4 +15,18 @@ spec:
|
||||
secretScope: login
|
||||
id: "88781348-c81c-4367-9801-550360c21295"
|
||||
name: "test-secret"
|
||||
namespace: "default"
|
||||
---
|
||||
apiVersion: "lerentis.uploadfilter24.eu/v1beta4"
|
||||
kind: BitwardenSecret
|
||||
metadata:
|
||||
name: test-scope
|
||||
spec:
|
||||
content:
|
||||
- element:
|
||||
secretName: public_key
|
||||
secretRef: pubKey
|
||||
secretScope: fields
|
||||
id: "466fc4b0-ffca-4444-8d88-b59d4de3d928"
|
||||
name: "test-scope"
|
||||
namespace: "default"
|
@ -1,3 +1,3 @@
|
||||
kopf==1.36.0
|
||||
kopf==1.36.1
|
||||
kubernetes==26.1.0
|
||||
Jinja2==3.1.2
|
||||
|
@ -9,12 +9,12 @@ from utils.utils import command_wrapper, unlock_bw
|
||||
def bitwarden_signin(logger, **kwargs):
|
||||
if 'BW_HOST' in os.environ:
|
||||
try:
|
||||
command_wrapper(f"config server {os.getenv('BW_HOST')}")
|
||||
command_wrapper(logger, f"config server {os.getenv('BW_HOST')}")
|
||||
except BaseException:
|
||||
logger.warn("Revieved none zero exit code from server config")
|
||||
logger.warn("Received non-zero exit code from server config")
|
||||
logger.warn("This is expected from startup")
|
||||
pass
|
||||
else:
|
||||
logger.info("BW_HOST not set. Assuming SaaS installation")
|
||||
command_wrapper("login --apikey")
|
||||
command_wrapper(logger, "login --apikey")
|
||||
unlock_bw(logger)
|
||||
|
@ -46,7 +46,7 @@ def create_managed_registry_secret(spec, name, namespace, logger, **kwargs):
|
||||
|
||||
unlock_bw(logger)
|
||||
logger.info(f"Locking up secret with ID: {id}")
|
||||
secret_json_object = json.loads(get_secret_from_bitwarden(id))
|
||||
secret_json_object = get_secret_from_bitwarden(logger, id)
|
||||
|
||||
api = kubernetes.client.CoreV1Api()
|
||||
|
||||
@ -60,7 +60,7 @@ def create_managed_registry_secret(spec, name, namespace, logger, **kwargs):
|
||||
secret = create_dockerlogin(
|
||||
logger,
|
||||
secret,
|
||||
secret_json_object,
|
||||
secret_json_object["data"],
|
||||
username_ref,
|
||||
password_ref,
|
||||
registry)
|
||||
@ -118,7 +118,7 @@ def update_managed_registry_secret(
|
||||
|
||||
unlock_bw(logger)
|
||||
logger.info(f"Locking up secret with ID: {id}")
|
||||
secret_json_object = json.loads(get_secret_from_bitwarden(id))
|
||||
secret_json_object = get_secret_from_bitwarden(logger, id)
|
||||
|
||||
api = kubernetes.client.CoreV1Api()
|
||||
|
||||
@ -132,7 +132,7 @@ def update_managed_registry_secret(
|
||||
secret = create_dockerlogin(
|
||||
logger,
|
||||
secret,
|
||||
secret_json_object,
|
||||
secret_json_object["data"],
|
||||
username_ref,
|
||||
password_ref,
|
||||
registry)
|
||||
|
@ -45,7 +45,7 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs):
|
||||
|
||||
unlock_bw(logger)
|
||||
logger.info(f"Locking up secret with ID: {id}")
|
||||
secret_json_object = json.loads(get_secret_from_bitwarden(id))
|
||||
secret_json_object = get_secret_from_bitwarden(logger, id)
|
||||
|
||||
api = kubernetes.client.CoreV1Api()
|
||||
|
||||
@ -106,7 +106,7 @@ def update_managed_secret(
|
||||
|
||||
unlock_bw(logger)
|
||||
logger.info(f"Locking up secret with ID: {id}")
|
||||
secret_json_object = json.loads(get_secret_from_bitwarden(id))
|
||||
secret_json_object = get_secret_from_bitwarden(logger, id)
|
||||
|
||||
api = kubernetes.client.CoreV1Api()
|
||||
|
||||
|
@ -4,7 +4,7 @@ from utils.utils import get_secret_from_bitwarden, parse_fields_scope, parse_log
|
||||
|
||||
|
||||
def bitwarden_lookup(id, scope, field):
|
||||
_secret_json = json.loads(get_secret_from_bitwarden(id))
|
||||
_secret_json = get_secret_from_bitwarden(None, id)
|
||||
if scope == "login":
|
||||
return parse_login_scope(_secret_json, field)
|
||||
if scope == "fields":
|
||||
|
@ -7,44 +7,47 @@ class BitwardenCommandException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_secret_from_bitwarden(id):
|
||||
return command_wrapper(command=f"get item {id}")
|
||||
def get_secret_from_bitwarden(logger, id):
|
||||
return command_wrapper(logger, command=f"get item {id}")
|
||||
|
||||
|
||||
def unlock_bw(logger):
|
||||
status_output = command_wrapper("status")
|
||||
status = json.loads(status_output)['status']
|
||||
status_output = command_wrapper(logger, "status", False)
|
||||
status = status_output['data']['template']['status']
|
||||
if status == 'unlocked':
|
||||
logger.info("Already unlocked")
|
||||
return
|
||||
token_output = command_wrapper("unlock --passwordenv BW_PASSWORD")
|
||||
tokens = token_output.split('"')[1::2]
|
||||
os.environ["BW_SESSION"] = tokens[1]
|
||||
token_output = command_wrapper(logger, "unlock --passwordenv BW_PASSWORD")
|
||||
os.environ["BW_SESSION"] = token_output["data"]["raw"]
|
||||
logger.info("Signin successful. Session exported")
|
||||
|
||||
|
||||
def command_wrapper(command):
|
||||
def command_wrapper(logger, command, use_success: bool = True):
|
||||
system_env = dict(os.environ)
|
||||
sp = subprocess.Popen(
|
||||
[f"bw {command}"],
|
||||
[f"bw --response {command}"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True,
|
||||
shell=True,
|
||||
env=system_env)
|
||||
out, err = sp.communicate()
|
||||
if err:
|
||||
raise BitwardenCommandException(err)
|
||||
return out.decode(encoding='UTF-8')
|
||||
if "DEBUG" in system_env:
|
||||
logger.info(out.decode(encoding='UTF-8'))
|
||||
resp = json.loads(out.decode(encoding='UTF-8'))
|
||||
if resp["success"] != None and (not use_success or (use_success and resp["success"] == True)):
|
||||
return resp
|
||||
logger.warn(resp)
|
||||
return None
|
||||
|
||||
|
||||
def parse_login_scope(secret_json, key):
|
||||
return secret_json["login"][key]
|
||||
return secret_json["data"]["login"][key]
|
||||
|
||||
|
||||
def parse_fields_scope(secret_json, key):
|
||||
if "fields" not in secret_json:
|
||||
if "fields" not in secret_json["data"]:
|
||||
return None
|
||||
for entry in secret_json["fields"]:
|
||||
for entry in secret_json["data"]["fields"]:
|
||||
if entry['name'] == key:
|
||||
return entry['value']
|
||||
|
Reference in New Issue
Block a user