Fix application specific password handling
All checks were successful
continuous-integration/drone Build is passing

This commit is contained in:
Lauri Võsandi 2023-08-17 10:40:19 +03:00
parent 8a39056599
commit 817acc3ecf
3 changed files with 87 additions and 8 deletions

44
deployment.yaml Normal file
View File

@ -0,0 +1,44 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wildduck-exporter
namespace: wildduck
spec:
replicas: 1
selector:
matchLabels:
app: wildduck-exporter
template:
metadata:
labels:
app: wildduck-exporter
spec:
containers:
- name: wildduck-exporter
image: harbor.k-space.ee/k-space/wildduck-exporter
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
command:
- /wildduck_exporter.py
args:
- info
- accounting
ports:
- containerPort: 3001
name: metrics
env:
- name: MONGODB_HOST
valueFrom:
secretKeyRef:
name: wildduck
key: MONGO_URI
- name: PROMETHEUS_BEARER_TOKEN
valueFrom:
secretKeyRef:
name: wildduck-exporter
key: PROMETHEUS_BEARER_TOKEN
imagePullSecrets:
- name: regcred

18
skaffold.yaml Normal file
View File

@ -0,0 +1,18 @@
---
apiVersion: skaffold/v3
kind: Config
metadata:
name: wildduck-exporter
build:
artifacts:
- docker:
dockerfile: Dockerfile
image: harbor.k-space.ee/k-space/wildduck-exporter
deploy:
kubectl:
flags:
global:
- --namespace=wildduck
manifests:
rawYaml:
- deployment.yaml

View File

@ -1,7 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import sys
from motor.motor_asyncio import AsyncIOMotorClient from motor.motor_asyncio import AsyncIOMotorClient
from sanic import Sanic, response, exceptions from sanic import Sanic, exceptions
from sanic.log import logger
MONGODB_HOST = os.getenv("MONGODB_HOST") MONGODB_HOST = os.getenv("MONGODB_HOST")
if not MONGODB_HOST: if not MONGODB_HOST:
@ -12,6 +14,7 @@ if not PROMETHEUS_BEARER_TOKEN:
raise ValueError("No PROMETHEUS_BEARER_TOKEN specified") raise ValueError("No PROMETHEUS_BEARER_TOKEN specified")
app = Sanic("exporter") app = Sanic("exporter")
usernames = list(set(sys.argv[1:]))
@app.listener("before_server_start") @app.listener("before_server_start")
@ -32,30 +35,44 @@ async def wrap(i, prefix="wildduck_"):
async def fetch(): async def fetch():
async for u in app.ctx.db.users.find(): last_login = {}
user_labels = {}
args = []
if usernames:
args.append({"username": {"$in": usernames}})
async for u in app.ctx.db.users.find(*args):
labels = { labels = {
"username": u["username"], "username": u["username"],
"email": u["address"], "email": u["address"],
} }
user_labels[u["_id"]] = labels
if u["lastLogin"]["time"]: if u["lastLogin"]["time"]:
yield "last_login", "gauge", u["lastLogin"]["time"].timestamp(), labels last_login[u["_id"]] = u["lastLogin"]["time"]
if u["storageUsed"]: if u["storageUsed"]:
yield "storage_used", "gauge", u["storageUsed"], labels yield "storage_used", "gauge", u["storageUsed"], labels
if u["targets"]: if u["targets"]:
yield "forwarding_addresses", "gauge", len(u["targets"]), labels yield "forwarding_addresses", "gauge", len(u["targets"]), labels
yield "account_enabled", "gauge", not u["disabled"], labels yield "account_enabled", "gauge", not u["disabled"], labels
# Merge application specific passwords last used timestamps
async for a in app.ctx.db.asps.find({"user": {"$in": list(last_login.keys())}}):
if a["used"] > last_login[a["user"]]:
last_login[a["user"]] = a["used"]
for user, dt in last_login.items():
yield "last_login", "gauge", dt.timestamp(), user_labels[user]
@app.route("/metrics") @app.route("/metrics")
async def view_export(request): async def view_export(request):
if request.token != PROMETHEUS_BEARER_TOKEN: if request.token != PROMETHEUS_BEARER_TOKEN:
raise exceptions.Forbidden("Invalid bearer token") raise exceptions.Forbidden("Invalid bearer token")
response = await request.respond(content_type="text/plain")
async def streaming_fn(response):
async for line in wrap(fetch()): async for line in wrap(fetch()):
await response.write(line + "\n") await response.send(line + "\n")
return response.stream(streaming_fn, content_type="text/plain")
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0", port=3001, single_process=True) app.run(host="0.0.0.0", port=3001, single_process=True)