Initial commit
This commit is contained in:
		
							
								
								
									
										4
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| FROM python | ||||
| RUN pip install motor sanic sanic-prometheus sanic_wtf | ||||
| ADD lease.py / | ||||
| CMD /lease.py | ||||
							
								
								
									
										105
									
								
								lease.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										105
									
								
								lease.py
									
									
									
									
									
										Executable file
									
								
							| @@ -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) | ||||
		Reference in New Issue
	
	Block a user