Implement SSL certificate expiration check

- Added pyOpenSSL to Pipfile and Pipfile.lock for SSL certificate expiration check implementation
- Implemented SSLCertificateExpiration check in checks.py
- Updated config.yaml with new test cases
- Minor changes and clean up in base.py and config.py
This commit is contained in:
Alexis Métaireau 2023-10-07 00:19:36 +02:00
parent ff4588bc39
commit d3c4f1e87b
7 changed files with 222 additions and 18 deletions

View file

@ -12,6 +12,7 @@ httpx = "*"
click = "*"
aiosqlite = "*"
sqlalchemy = {extras = ["asyncio"], version = "*"}
pyopenssl = "*"
[dev-packages]

108
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "01a59c7304004f92b993a672a37e535ee3b3816cdb77093d5431db2124afb567"
"sha256": "e6eaf14f53ea7b88c8245712c5639fa870ba5c7418f3f12697422d510386e6fc"
},
"pipfile-spec": 6,
"requires": {
@ -49,6 +49,64 @@
"markers": "python_version >= '3.6'",
"version": "==2023.7.22"
},
"cffi": {
"hashes": [
"sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc",
"sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a",
"sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417",
"sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab",
"sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520",
"sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36",
"sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743",
"sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8",
"sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed",
"sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684",
"sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56",
"sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324",
"sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d",
"sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235",
"sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e",
"sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088",
"sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000",
"sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7",
"sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e",
"sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673",
"sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c",
"sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe",
"sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2",
"sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098",
"sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8",
"sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a",
"sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0",
"sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b",
"sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896",
"sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e",
"sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9",
"sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2",
"sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b",
"sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6",
"sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404",
"sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f",
"sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0",
"sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4",
"sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc",
"sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936",
"sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba",
"sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872",
"sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb",
"sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614",
"sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1",
"sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d",
"sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969",
"sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b",
"sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4",
"sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627",
"sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956",
"sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"
],
"markers": "python_version >= '3.8'",
"version": "==1.16.0"
},
"click": {
"hashes": [
"sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
@ -58,6 +116,35 @@
"markers": "python_version >= '3.7'",
"version": "==8.1.7"
},
"cryptography": {
"hashes": [
"sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67",
"sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311",
"sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8",
"sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13",
"sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143",
"sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f",
"sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829",
"sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd",
"sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397",
"sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac",
"sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d",
"sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a",
"sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839",
"sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e",
"sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6",
"sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9",
"sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860",
"sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca",
"sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91",
"sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d",
"sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714",
"sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb",
"sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f"
],
"markers": "python_version >= '3.7'",
"version": "==41.0.4"
},
"fastapi": {
"hashes": [
"sha256:3270de872f0fe9ec809d4bd3d4d890c6d5cc7b9611d721d6438f9dacc8c4ef2e",
@ -74,6 +161,7 @@
"sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9",
"sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d",
"sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14",
"sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383",
"sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b",
"sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99",
"sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7",
@ -88,7 +176,6 @@
"sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c",
"sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4",
"sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362",
"sha256:553d6fb2324e7f4f0899e5ad2c427a4579ed4873f42124beba763f16032959af",
"sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692",
"sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365",
"sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9",
@ -119,7 +206,6 @@
"sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705",
"sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c",
"sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f",
"sha256:c3692ecf3fe754c8c0f2c95ff19626584459eab110eaab66413b1e7425cd84e9",
"sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c",
"sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870",
"sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353",
@ -168,6 +254,13 @@
"markers": "python_version >= '3.5'",
"version": "==3.4"
},
"pycparser": {
"hashes": [
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
"version": "==2.21"
},
"pydantic": {
"hashes": [
"sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7",
@ -288,6 +381,15 @@
"markers": "python_version >= '3.7'",
"version": "==2.10.1"
},
"pyopenssl": {
"hashes": [
"sha256:24f0dc5227396b3e831f4c7f602b950a5e9833d292c8e4a2e06b709292806ae2",
"sha256:276f931f55a452e7dea69c7173e984eb2a4407ce413c918aa34b55f82f9b8bac"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==23.2.0"
},
"pyyaml": {
"hashes": [
"sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5",

View file

@ -20,7 +20,7 @@ Features :
Implemented checks :
- [x] Returned status code matches what you expect ;
- [ ] Returned body matches what you expect ;
- [x] Returned body matches what you expect ;
- [ ] SSL certificate expires in more than X days ;
## How to run ?

View file

@ -6,7 +6,8 @@ import httpx
from argos.schemas import Task
# XXX We could name this Result, but is it could overlap with schemas.Result.
# Need to better define the naming around this.
@dataclass
class Response:
status: str

View file

@ -1,5 +1,10 @@
from argos.logging import logger
from argos.checks.base import BaseCheck, Response, ExpectedIntValue, ExpectedStringValue
import ssl
import time
from datetime import datetime
from OpenSSL import crypto
class HTTPStatus(BaseCheck):
@ -14,7 +19,7 @@ class HTTPStatus(BaseCheck):
return self.response(
response.status_code == self.expected,
expected=self.expected,
retrieved=response.status_code
retrieved=response.status_code,
)
@ -24,9 +29,7 @@ class HTTPBodyContains(BaseCheck):
async def run(self) -> dict:
response = await self.client.request(method="get", url=self.task.url)
return self.response(
self.expected in response.text
)
return self.response(self.expected in response.text)
class SSLCertificateExpiration(BaseCheck):
@ -34,4 +37,23 @@ class SSLCertificateExpiration(BaseCheck):
expected_cls = ExpectedStringValue
async def run(self):
return True
response = await self.client.get(self.task.url)
if response.is_error:
raise
conn = self.client.transport.get_connection_info(self.task.url)
cert = ssl.DER_cert_to_PEM_cert(conn.raw_certificates[0])
x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
not_after = x509.get_notAfter().decode("utf-8")
not_after = datetime.strptime(not_after, "%Y%m%d%H%M%SZ")
now = time.time()
if time.mktime(not_after.timetuple()) < now:
expired = True
else:
expired = False
return self.response(
expired == False, expected=now, retrieved=not_after.timetuple()
)

View file

@ -3,6 +3,8 @@ from pydantic import BaseModel
from enum import StrEnum
from typing import List, Optional, Tuple
from typing import Dict, Union, List
import yaml
from pydantic import BaseModel, Field, HttpUrl, validator
@ -22,8 +24,27 @@ class SSL(BaseModel):
thresholds: Thresholds
WebsiteCheck = dict[str, str | int]
# StrEnum("Check", get_check_names()) ?
class WebsiteCheck(BaseModel):
key: str
value: str | List[str] | Dict[str, str]
class Config:
arbitrary_types_allowed = True
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, value):
if isinstance(value, str):
return {"expected": value}
elif isinstance(value, dict):
return value
elif isinstance(value, list):
return {"expected": value}
else:
raise ValueError("Invalid type")
class WebsitePath(BaseModel):

View file

@ -20,13 +20,70 @@ ssl:
warning: "10d"
websites:
- domain: "https://blog.notmyidea.org"
- domain: "https://mypads.framapad.org"
paths:
- path: "/mypads/"
checks:
- status-is: 200
- body-contains: '<div id= "mypads"></div>'
# le check du certificat devrait plutôt être au niveau
# de domain et paths, AMHA
- ssl-certificate-expiration: "on-check"
- path: "/admin/"
checks:
- status-is: 401
- domain: "https://munin.framasoft.org"
paths:
- path: "/"
checks:
- status-is: 200
- body-contains: "Alexis"
- ssl-certificate-expiration: "on-check"
- path: "/foo"
- status-is: 301
- path: "/munin/"
checks:
- status-is: 400
- status-is: 401
- domain: "https://framagenda.org"
paths:
- path: "/status.php"
checks:
- status-is: 200
# Là, idéalement, il faudrait un json-contains,
# qui serait une table de hachage
- body-contains: '"maintenance":false'
- ssl-certificate-expiration: "on-check"
- path: "/"
checks:
- status-is: 302
- path: "/login"
checks:
- status-is: 200
- domain: "https://framadrive.org"
paths:
- path: "/status.php"
checks:
- status-is: 200
- body-contains: '"maintenance":false'
- ssl-certificate-expiration: "on-check"
- path: "/"
checks:
- status-is: 302
- path: "/login"
checks:
- status-is: 200
- domain: "https://cloud.framabook.org"
paths:
- path: "/status.php"
checks:
- status-is: 200
- body-contains: '"maintenance":false'
- ssl-certificate-expiration: "on-check"
- path: "/"
checks:
- status-is: 302
- path: "/login"
checks:
- status-is: 200
- domain: "https://framasoft.org"
path:
- path: "/"
checks:
- status-is: 200
- ssl-certificate-expiration: "on-check"