-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b03e205
Showing
2 changed files
with
109 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
FROM python | ||
RUN pip install motor sanic sanic-prometheus sanic_wtf | ||
ADD lease.py / | ||
CMD /lease.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#!/usr/bin/env python | ||
|
||
import os | ||
import socket | ||
from datetime import datetime | ||
from motor.motor_asyncio import AsyncIOMotorClient | ||
from prometheus_client import Counter | ||
from sanic import Sanic, response | ||
from sanic.exceptions import InvalidUsage, NotFound | ||
from sanic_prometheus import monitor | ||
from sanic_wtf import SanicForm | ||
from wtforms import IntegerField, StringField | ||
from wtforms.validators import DataRequired, IPAddress, NumberRange | ||
|
||
submit_count = Counter("pinecrypt_lease_submit", "IP updates", ["service"]) | ||
flush_count = Counter("pinecrypt_lease_flush", "IP assignment flushes", ["service"]) | ||
not_found_count = Counter("pinecrypt_lease_not_found", "Certificate not found", ["service"]) | ||
|
||
class LeaseUpdateForm(SanicForm): | ||
service = StringField("Service name") | ||
internal_addr = StringField("Internal IP address", validators=[DataRequired(), IPAddress(ipv4=True, ipv6=True)]) | ||
remote_addr = StringField("Remote IP address", validators=[DataRequired(), IPAddress(ipv4=True, ipv6=True)]) | ||
remote_port = IntegerField(validators=[NumberRange(min=0, max=65534)]) | ||
|
||
app = Sanic("lease") | ||
app.config["WTF_CSRF_ENABLED"] = False | ||
|
||
MONGO_URI = os.getenv("MONGO_URI", "mongodb://127.0.0.1:27017/default") | ||
FQDN = socket.getfqdn() | ||
|
||
|
||
@app.listener('before_server_start') | ||
async def setup_db(app, loop): | ||
app.ctx.db = AsyncIOMotorClient(MONGO_URI).get_default_database() | ||
|
||
|
||
async def submit(request, q): | ||
q["status"] = "signed" | ||
# TODO: add expiration check | ||
|
||
form = LeaseUpdateForm(request) | ||
if not form.validate(): | ||
raise InvalidUsage("Invalid form input") | ||
|
||
result = await app.ctx.db.certidude_certificates.update_one(q, { | ||
"$set": { | ||
"last_seen": datetime.utcnow(), | ||
"instance": "%s-%s" % (FQDN, form.service.data), | ||
"remote.port": form.remote_port.data, | ||
"remote.addr": form.remote_addr.data, | ||
}, | ||
"$addToSet": { | ||
"ip": form.internal_addr.data | ||
} | ||
}) | ||
if result.modified_count == 1: | ||
submit_count.labels(form.service.data).inc() | ||
return response.text('Lease updated') | ||
else: | ||
assert result.modified_count == 0 | ||
not_found_count.labels(form.service.data).inc() | ||
raise NotFound("Node not found") | ||
|
||
|
||
@app.route("/api/by-serial/<serial_number:int>", methods=["GET"]) | ||
async def get_by_serial(request, serial_number): | ||
obj = await app.ctx.db.certidude_certificates.find_one({ | ||
"serial_number": "%x" % serial_number, | ||
"status": "signed"}) | ||
# TODO: Add expiration check | ||
if obj: | ||
return response.text("Certificate valid") | ||
else: | ||
raise NotFound("Certificate not found or not valid") | ||
|
||
|
||
@app.route("/api/by-dn/<distinguished_name:string>", methods=["POST"]) | ||
async def submit_by_dn(request, distinguished_name): | ||
return await submit(request, {"distinguished_name": distinguished_name.replace("%20", " ")}) | ||
|
||
|
||
@app.route("/api/by-serial/<serial_number:int>", methods=["POST"]) | ||
async def submit_by_serial(request, serial_number): | ||
return await submit(request, {"serial_number": "%x" % serial_number}) | ||
|
||
|
||
@app.route("/api/by-service/<service:string>", methods=["DELETE"]) | ||
async def flush(request, service): | ||
""" | ||
Flush IP addresses assigned by this instance as it was restarted | ||
""" | ||
await app.ctx.db.certidude_certificates.update_many({ | ||
"instance": "%s-%s" % (FQDN, service), | ||
}, { | ||
"$unset": { | ||
"ip": "", | ||
"instance": "", | ||
} | ||
}) | ||
flush_count.labels(service).inc() | ||
return response.text('Leases flushed') | ||
|
||
|
||
monitor(app).expose_endpoint() | ||
app.run(port=2001) |