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 = "*" click = "*"
aiosqlite = "*" aiosqlite = "*"
sqlalchemy = {extras = ["asyncio"], version = "*"} sqlalchemy = {extras = ["asyncio"], version = "*"}
pyopenssl = "*"
[dev-packages] [dev-packages]

108
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "01a59c7304004f92b993a672a37e535ee3b3816cdb77093d5431db2124afb567" "sha256": "e6eaf14f53ea7b88c8245712c5639fa870ba5c7418f3f12697422d510386e6fc"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -49,6 +49,64 @@
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2023.7.22" "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": { "click": {
"hashes": [ "hashes": [
"sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
@ -58,6 +116,35 @@
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==8.1.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": { "fastapi": {
"hashes": [ "hashes": [
"sha256:3270de872f0fe9ec809d4bd3d4d890c6d5cc7b9611d721d6438f9dacc8c4ef2e", "sha256:3270de872f0fe9ec809d4bd3d4d890c6d5cc7b9611d721d6438f9dacc8c4ef2e",
@ -74,6 +161,7 @@
"sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9", "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9",
"sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d", "sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d",
"sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14", "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14",
"sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383",
"sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b", "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b",
"sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99", "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99",
"sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7", "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7",
@ -88,7 +176,6 @@
"sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c", "sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c",
"sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4", "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4",
"sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362", "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362",
"sha256:553d6fb2324e7f4f0899e5ad2c427a4579ed4873f42124beba763f16032959af",
"sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692", "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692",
"sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365", "sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365",
"sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9", "sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9",
@ -119,7 +206,6 @@
"sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705", "sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705",
"sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c", "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c",
"sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f", "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f",
"sha256:c3692ecf3fe754c8c0f2c95ff19626584459eab110eaab66413b1e7425cd84e9",
"sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c", "sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c",
"sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870", "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870",
"sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353", "sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353",
@ -168,6 +254,13 @@
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.5'",
"version": "==3.4" "version": "==3.4"
}, },
"pycparser": {
"hashes": [
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
"version": "==2.21"
},
"pydantic": { "pydantic": {
"hashes": [ "hashes": [
"sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7", "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7",
@ -288,6 +381,15 @@
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==2.10.1" "version": "==2.10.1"
}, },
"pyopenssl": {
"hashes": [
"sha256:24f0dc5227396b3e831f4c7f602b950a5e9833d292c8e4a2e06b709292806ae2",
"sha256:276f931f55a452e7dea69c7173e984eb2a4407ce413c918aa34b55f82f9b8bac"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==23.2.0"
},
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
"sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5",

View file

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

View file

@ -6,7 +6,8 @@ import httpx
from argos.schemas import Task 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 @dataclass
class Response: class Response:
status: str status: str

View file

@ -1,5 +1,10 @@
from argos.logging import logger from argos.logging import logger
from argos.checks.base import BaseCheck, Response, ExpectedIntValue, ExpectedStringValue from argos.checks.base import BaseCheck, Response, ExpectedIntValue, ExpectedStringValue
import ssl
import time
from datetime import datetime
from OpenSSL import crypto
class HTTPStatus(BaseCheck): class HTTPStatus(BaseCheck):
@ -14,7 +19,7 @@ class HTTPStatus(BaseCheck):
return self.response( return self.response(
response.status_code == self.expected, response.status_code == self.expected,
expected=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: async def run(self) -> dict:
response = await self.client.request(method="get", url=self.task.url) response = await self.client.request(method="get", url=self.task.url)
return self.response( return self.response(self.expected in response.text)
self.expected in response.text
)
class SSLCertificateExpiration(BaseCheck): class SSLCertificateExpiration(BaseCheck):
@ -34,4 +37,23 @@ class SSLCertificateExpiration(BaseCheck):
expected_cls = ExpectedStringValue expected_cls = ExpectedStringValue
async def run(self): 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 enum import StrEnum
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from typing import Dict, Union, List
import yaml import yaml
from pydantic import BaseModel, Field, HttpUrl, validator from pydantic import BaseModel, Field, HttpUrl, validator
@ -22,8 +24,27 @@ class SSL(BaseModel):
thresholds: Thresholds thresholds: Thresholds
WebsiteCheck = dict[str, str | int] class WebsiteCheck(BaseModel):
# StrEnum("Check", get_check_names()) ? 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): class WebsitePath(BaseModel):

View file

@ -20,13 +20,70 @@ ssl:
warning: "10d" warning: "10d"
websites: 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: paths:
- path: "/" - path: "/"
checks: checks:
- status-is: 200 - status-is: 301
- body-contains: "Alexis" - path: "/munin/"
- ssl-certificate-expiration: "on-check"
- path: "/foo"
checks: 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"