From e620c286482b128da4de90054a790c44f7c40333 Mon Sep 17 00:00:00 2001 From: kalzu rekku Date: Tue, 22 Oct 2024 22:06:19 +0300 Subject: [PATCH] Keep It Simple Stupid. Started using the CherryPy server and made classes from the parts. --- kiss/toml-manager/Pipfile | 14 + kiss/toml-manager/Pipfile.lock | 258 ++++++++++++++++++ kiss/toml-manager/app.py | 18 ++ kiss/toml-manager/auth/token_auth.py | 36 +++ kiss/toml-manager/config.toml | 18 ++ kiss/toml-manager/config/config_loader.py | 28 ++ .../controllers/toml_controller.py | 35 +++ .../controllers/wireguard_controller.py | 43 +++ kiss/toml-manager/services/toml_service.py | 29 ++ .../services/wireguard_service.py | 36 +++ kiss/toml-manager/topics.toml | 24 ++ kiss/toml-manager/utils/crypto_utils.py | 61 +++++ 12 files changed, 600 insertions(+) create mode 100644 kiss/toml-manager/Pipfile create mode 100644 kiss/toml-manager/Pipfile.lock create mode 100644 kiss/toml-manager/app.py create mode 100644 kiss/toml-manager/auth/token_auth.py create mode 100644 kiss/toml-manager/config.toml create mode 100644 kiss/toml-manager/config/config_loader.py create mode 100644 kiss/toml-manager/controllers/toml_controller.py create mode 100644 kiss/toml-manager/controllers/wireguard_controller.py create mode 100644 kiss/toml-manager/services/toml_service.py create mode 100644 kiss/toml-manager/services/wireguard_service.py create mode 100644 kiss/toml-manager/topics.toml create mode 100644 kiss/toml-manager/utils/crypto_utils.py diff --git a/kiss/toml-manager/Pipfile b/kiss/toml-manager/Pipfile new file mode 100644 index 0000000..34c5e12 --- /dev/null +++ b/kiss/toml-manager/Pipfile @@ -0,0 +1,14 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +cherrypy = "*" +cryptography = "*" +toml = "*" + +[dev-packages] + +[requires] +python_version = "3.12" diff --git a/kiss/toml-manager/Pipfile.lock b/kiss/toml-manager/Pipfile.lock new file mode 100644 index 0000000..2d9cea6 --- /dev/null +++ b/kiss/toml-manager/Pipfile.lock @@ -0,0 +1,258 @@ +{ + "_meta": { + "hash": { + "sha256": "3904278bc410034de66233bf5207dbd20ea354ea27293021e91dd2ec82110050" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.12" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "autocommand": { + "hashes": [ + "sha256:710afe251075e038e19e815e25f8155cabe02196cfb545b2185e0d9c8b2b0459", + "sha256:878de9423c5596491167225c2a455043c3130fb5b7286ac83443d45e74955f34" + ], + "markers": "python_version >= '3.7'", + "version": "==2.2.2" + }, + "cffi": { + "hashes": [ + "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", + "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", + "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1", + "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", + "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", + "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", + "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", + "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", + "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", + "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", + "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc", + "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", + "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", + "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", + "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", + "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", + "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", + "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", + "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", + "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b", + "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", + "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", + "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c", + "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", + "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", + "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", + "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8", + "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1", + "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", + "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", + "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", + "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", + "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", + "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", + "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", + "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", + "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", + "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", + "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", + "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", + "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", + "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", + "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", + "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964", + "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", + "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", + "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", + "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", + "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", + "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", + "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", + "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", + "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", + "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", + "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", + "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", + "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", + "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9", + "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", + "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", + "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", + "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", + "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", + "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", + "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", + "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", + "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.17.1" + }, + "cheroot": { + "hashes": [ + "sha256:6ea332f20bfcede14e66174d112b30e9807492320d737ca628badc924d997595", + "sha256:e0b82f797658d26b8613ec8eb563c3b08e6bd6a7921e9d5089bd1175ad1b1740" + ], + "markers": "python_version >= '3.6'", + "version": "==10.0.1" + }, + "cherrypy": { + "hashes": [ + "sha256:129e444b9a63cea4e765481b156376f1cfe319e64caaaec2485636532373b298", + "sha256:6c70e78ee11300e8b21c0767c542ae6b102a49cac5cfd4e3e313d7bb907c5891" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==18.10.0" + }, + "cryptography": { + "hashes": [ + "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", + "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", + "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", + "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", + "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", + "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", + "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", + "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", + "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", + "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", + "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", + "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", + "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", + "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", + "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", + "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", + "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", + "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", + "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", + "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", + "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", + "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", + "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", + "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", + "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", + "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", + "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==43.0.3" + }, + "jaraco.collections": { + "hashes": [ + "sha256:0e4829409d39ad18a40aa6754fee2767f4d9730c4ba66dc9df89f1d2756994c2", + "sha256:a9480be7fe741d34639b3c32049066d7634b520746552d1a5d0fcda07ada1020" + ], + "markers": "python_version >= '3.8'", + "version": "==5.1.0" + }, + "jaraco.context": { + "hashes": [ + "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", + "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4" + ], + "markers": "python_version >= '3.8'", + "version": "==6.0.1" + }, + "jaraco.functools": { + "hashes": [ + "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", + "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649" + ], + "markers": "python_version >= '3.8'", + "version": "==4.1.0" + }, + "jaraco.text": { + "hashes": [ + "sha256:08de508939b5e681b14cdac2f1f73036cd97f6f8d7b25e96b8911a9a428ca0d1", + "sha256:5b71fecea69ab6f939d4c906c04fee1eda76500d1641117df6ec45b865f10db0" + ], + "markers": "python_version >= '3.8'", + "version": "==4.0.0" + }, + "more-itertools": { + "hashes": [ + "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", + "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6" + ], + "markers": "python_version >= '3.8'", + "version": "==10.5.0" + }, + "portend": { + "hashes": [ + "sha256:5250a352c19c959d767cac878b829d93e5dc7625a5143399a2a00dc6628ffb72", + "sha256:8b3fe3f78779df906559a21d9eaa6e21c8fa5a7a8cc76362cbbe1e16777399cf" + ], + "markers": "python_version >= '3.8'", + "version": "==3.2.0" + }, + "pycparser": { + "hashes": [ + "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" + ], + "markers": "python_version >= '3.8'", + "version": "==2.22" + }, + "python-dateutil": { + "hashes": [ + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==2.9.0.post0" + }, + "setuptools": { + "hashes": [ + "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec", + "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8" + ], + "markers": "python_version >= '3.8'", + "version": "==75.2.0" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==1.16.0" + }, + "tempora": { + "hashes": [ + "sha256:888190a2dbe3255ff26dfa9fcecb25f4d38434c0f1943cd61de98bb41c410c50", + "sha256:93dac0d33825e66c8314d7bd206b9ecb959075c8728bb05b9b050b2726d0442a" + ], + "markers": "python_version >= '3.8'", + "version": "==5.7.0" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "index": "pypi", + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", + "version": "==0.10.2" + }, + "zc.lockfile": { + "hashes": [ + "sha256:adb2ee6d9e6a2333c91178dcb2c9b96a5744c78edb7712dc784a7d75648e81ec", + "sha256:ddb2d71088c061dc8a5edbaa346b637d742ca1e1564be75cb98e7dcae715de19" + ], + "markers": "python_version >= '3.7'", + "version": "==3.0.post1" + } + }, + "develop": {} +} diff --git a/kiss/toml-manager/app.py b/kiss/toml-manager/app.py new file mode 100644 index 0000000..a36c030 --- /dev/null +++ b/kiss/toml-manager/app.py @@ -0,0 +1,18 @@ +import cherrypy +from config.config_loader import load_config +from controllers.wireguard_controller import WireGuardController + +def start_server(): + config = load_config() + cherrypy.config.update({ + 'server.socket_host': config['server']['host'], + 'server.socket_port': config['server']['port'], + }) + + cherrypy.tree.mount(WireGuardController(), '/api/wireguard') + + cherrypy.engine.start() + cherrypy.engine.block() + +if __name__ == "__main__": + start_server() diff --git a/kiss/toml-manager/auth/token_auth.py b/kiss/toml-manager/auth/token_auth.py new file mode 100644 index 0000000..e3fe4c2 --- /dev/null +++ b/kiss/toml-manager/auth/token_auth.py @@ -0,0 +1,36 @@ +import json +import base64 +from utils.crypto_utils import decrypt_symmetric_key, decrypt_data, encrypt_data, CLIENT_PUBLIC_KEY +from cryptography.exceptions import InvalidSignature +import logging + +def validate_token(auth_header, encrypted_data): + if not auth_header or not auth_header.startswith("Bearer "): + raise ValueError("Invalid Authorization header") + + # Extract JWE token + jwe_token = auth_header.split(" ")[1] + payload = json.loads(base64.b64decode(jwe_token.split('.')[1])) + + # Extract the encrypted symmetric key from the token payload + encrypted_symmetric_key = base64.b64decode(payload['enc_sym_key']) + + # Decrypt the symmetric key + symmetric_key = decrypt_symmetric_key(encrypted_symmetric_key) + + # Decrypt the data using the symmetric key + decrypted_data = decrypt_data(encrypted_data, symmetric_key) + + # Verify client's signature + signature = base64.b64decode(payload['signature']) + try: + CLIENT_PUBLIC_KEY.verify( + signature, + decrypted_data.encode(), + ec.ECDSA(hashes.SHA256()) + ) + except InvalidSignature: + raise ValueError("Invalid client signature") + + # Return both decrypted data and the symmetric key for response encryption + return json.loads(decrypted_data), symmetric_key diff --git a/kiss/toml-manager/config.toml b/kiss/toml-manager/config.toml new file mode 100644 index 0000000..acfa898 --- /dev/null +++ b/kiss/toml-manager/config.toml @@ -0,0 +1,18 @@ +[server] +host = "127.0.0.1" # IP address to bind to +port = 8080 # Port to bind to + +[wireguard] +config_file = "../wireguard_example_server_config.conf" + +[logging] +log_file = "../wpm.log" # Optional: Log file location +debug = true # Enable debug logging + +[client_keys] +public_key = """ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0t+2/KJ7+YOHu4DmR20YtisenovD +tvJKywNdAWX5uqnA7UsYWPVKN827afMkgZuGKgZ5wtVM4DvQCq8MyRDHgw== +-----END PUBLIC KEY----- +""" # example public key diff --git a/kiss/toml-manager/config/config_loader.py b/kiss/toml-manager/config/config_loader.py new file mode 100644 index 0000000..8da01e3 --- /dev/null +++ b/kiss/toml-manager/config/config_loader.py @@ -0,0 +1,28 @@ +import tomllib +import logging + +DEFAULT_CONFIG = { + "server": { + "host": "0.0.0.0", + "port": 8000, + }, + "wireguard": { + "config_file": "/etc/wireguard/wg0.conf", + }, + "logging": { + "log_file": None, + "debug": False, + } +} + +def load_config(config_file: str = "config.toml") -> dict: + try: + with open(config_file, "rb") as f: + config = tomllib.load(f) + return {**DEFAULT_CONFIG, **config} + except FileNotFoundError: + logging.warning(f"Config file {config_file} not found. Using default configuration.") + return DEFAULT_CONFIG + except tomllib.TOMLDecodeError as e: + logging.error(f"Error parsing config file: {e}") + exit(1) diff --git a/kiss/toml-manager/controllers/toml_controller.py b/kiss/toml-manager/controllers/toml_controller.py new file mode 100644 index 0000000..e758fe4 --- /dev/null +++ b/kiss/toml-manager/controllers/toml_controller.py @@ -0,0 +1,35 @@ +import cherrypy +from services.topic_service import TopicService +from auth.token_auth import validate_token + +class GeneralTOMLController: + @cherrypy.expose + @cherrypy.tools.json_in() + @cherrypy.tools.json_out() + def index(self): + if cherrypy.request.method != "POST": + raise cherrypy.HTTPError(405, "Method Not Allowed. Use POST.") + + auth_header = cherrypy.request.headers.get('Authorization') + encrypted_data = cherrypy.request.body.read() + + try: + decrypted_data = validate_token(auth_header, encrypted_data) + action = decrypted_data.get('action') + file_path = decrypted_data.get('file_path') + + # Handle any TOML file actions + if action == 'read_file': + config = read_toml_file(file_path) + return {"content": config} + elif action == 'write_file': + data_to_write = decrypted_data.get('data') + write_toml_file(file_path, data_to_write) + return {"message": f"File {file_path} updated successfully"} + else: + return {"error": "Invalid action"} + + except Exception as e: + cherrypy.log(f"Error processing request: {str(e)}") + return {"error": "Invalid request"}, 400 + diff --git a/kiss/toml-manager/controllers/wireguard_controller.py b/kiss/toml-manager/controllers/wireguard_controller.py new file mode 100644 index 0000000..39977ea --- /dev/null +++ b/kiss/toml-manager/controllers/wireguard_controller.py @@ -0,0 +1,43 @@ +import cherrypy +from services.wireguard_service import read_config, write_config, create_backup, reload_wireguard_service +from auth.token_auth import validate_token +from utils.crypto_utils import encrypt_data + +class WireGuardController: + @cherrypy.expose + @cherrypy.tools.json_in() + @cherrypy.tools.json_out() + def index(self): + if cherrypy.request.method != "POST": + raise cherrypy.HTTPError(405, "Method Not Allowed. Use POST.") + + auth_header = cherrypy.request.headers.get('Authorization') + encrypted_data = cherrypy.request.body.read() + + try: + # Decrypt the request and get the symmetric key + decrypted_data, symmetric_key = validate_token(auth_header, encrypted_data) + action = decrypted_data.get('action') + + response_data = {} + + # Handle different actions (as an example, adding a peer) + if action == 'add_peer': + create_backup() + new_peer = decrypted_data.get('peer') + config = read_config("/etc/wireguard/wg0.conf") + config += "\n\n" + peer_to_string(new_peer) + write_config("/etc/wireguard/wg0.conf", config) + reload_wireguard_service("/etc/wireguard/wg0.conf") + response_data = {"message": "Peer added successfully"} + else: + response_data = {"error": "Invalid action"} + + # Encrypt the response data before sending + encrypted_response = encrypt_data(response_data, symmetric_key) + return {"data": encrypted_response} + + except Exception as e: + cherrypy.log(f"Error processing request: {str(e)}") + return {"error": "Invalid request"}, 400 + diff --git a/kiss/toml-manager/services/toml_service.py b/kiss/toml-manager/services/toml_service.py new file mode 100644 index 0000000..00fb9d5 --- /dev/null +++ b/kiss/toml-manager/services/toml_service.py @@ -0,0 +1,29 @@ +import tomllib +from pathlib import Path +import logging + +def read_toml_file(file_path: str) -> dict: + try: + with open(file_path, "rb") as file: + return tomllib.load(file) + except FileNotFoundError: + logging.error(f"File {file_path} not found.") + return {} + except tomllib.TOMLDecodeError as e: + logging.error(f"Error parsing {file_path}: {e}") + return {} + +def write_toml_file(file_path: str, data: dict) -> None: + with open(file_path, "w") as file: + toml.dump(data, file) + +def create_backup(file_path: str) -> str: + config_path = Path(file_path) + backup_dir = config_path.parent / "backups" + backup_dir.mkdir(exist_ok=True) + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + backup_file = backup_dir / f"{config_path.stem}_{timestamp}.toml" + shutil.copy2(file_path, backup_file) + logging.info(f"Backup created: {backup_file}") + return backup_file diff --git a/kiss/toml-manager/services/wireguard_service.py b/kiss/toml-manager/services/wireguard_service.py new file mode 100644 index 0000000..17c6948 --- /dev/null +++ b/kiss/toml-manager/services/wireguard_service.py @@ -0,0 +1,36 @@ +from pathlib import Path +import shutil +import logging +import subprocess +from datetime import datetime + +def read_config(config_file: str) -> str: + with open(config_file, 'r') as file: + return file.read() + +def write_config(config_file: str, content: str) -> None: + with open(config_file, 'w') as file: + file.write(content) + +def create_backup(config_file: str) -> str: + config_path = Path(config_file) + backup_dir = config_path.parent / "backups" + backup_dir.mkdir(exist_ok=True) + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + backup_file = backup_dir / f"{config_path.stem}_{timestamp}.conf" + shutil.copy2(config_file, backup_file) + logging.info(f"Backup created: {backup_file}") + return backup_file + +def reload_wireguard_service(config_file: str): + interface = Path(config_file).stem + try: + subprocess.run(["wg-quick", "down", interface], check=True) + subprocess.run(["wg-quick", "up", interface], check=True) + logging.info(f"WireGuard service for interface {interface} restarted successfully") + return True + except subprocess.CalledProcessError as e: + logging.error(f"Failed to reload WireGuard service: {str(e)}") + return False + diff --git a/kiss/toml-manager/topics.toml b/kiss/toml-manager/topics.toml new file mode 100644 index 0000000..4dad4b9 --- /dev/null +++ b/kiss/toml-manager/topics.toml @@ -0,0 +1,24 @@ +[Interface] +PrivateKey = PrivateKey123= +Address = 10.37.1.1/24 +ListenPort = 51820 + +[Peer] +PublicKey = PublicKey123= +AllowedIPs = 10.37.1.2/32 + +[Peer] +PublicKey = PublicKey1234= +AllowedIPs = 10.37.2.2/32 + +[Peer] +PublicKey = PublicKey12345 +AllowedIPs = 10.37.3.2/32 + +[Peer] +PublicKey = PublicKey123456 +AllowedIPs = 10.37.4.2/32 + +[Peer] +PublicKey = PublicKey321 +AllowedIPs = 0.0.0.0 diff --git a/kiss/toml-manager/utils/crypto_utils.py b/kiss/toml-manager/utils/crypto_utils.py new file mode 100644 index 0000000..2262c44 --- /dev/null +++ b/kiss/toml-manager/utils/crypto_utils.py @@ -0,0 +1,61 @@ +import os +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.kdf.hkdf import HKDF +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.backends import default_backend +import json +import base64 +from config.config_loader import load_config + +def encrypt_data(data, symmetric_key): + # Convert the data to a JSON string + json_data = json.dumps(data).encode('utf-8') + + # Generate a random IV for encryption + iv = os.urandom(16) + + # Create AES Cipher and encrypt the data + cipher = Cipher(algorithms.AES(symmetric_key), modes.CFB(iv), backend=default_backend()) + encryptor = cipher.encryptor() + encrypted_data = encryptor.update(json_data) + encryptor.finalize() + + # Combine IV and encrypted data + encrypted_payload = iv + encrypted_data + + # Encode the result in base64 to make it JSON-compatible + return base64.b64encode(encrypted_payload).decode('utf-8') + +# AES Decryption +def decrypt_data(encrypted_data, symmetric_key): + # Decode base64 + encrypted_data = base64.b64decode(encrypted_data) + + iv = encrypted_data[:16] + cipher = Cipher(algorithms.AES(symmetric_key), modes.CFB(iv), backend=default_backend()) + decryptor = cipher.decryptor() + decrypted_data = decryptor.update(encrypted_data[16:]) + decryptor.finalize() + return decrypted_data.decode('utf-8') + +def generate_ecc_key_pair(): + private_key = ec.generate_private_key(ec.SECP256R1(), default_backend()) + public_key = private_key.public_key() + return private_key, public_key + +def load_client_public_key(config): + client_public_key_pem = config.get('client_keys', {}).get('public_key') + if not client_public_key_pem: + raise ValueError("Client public key not found") + return serialization.load_pem_public_key(client_public_key_pem.encode(), backend=default_backend()) + +def decrypt_symmetric_key(encrypted_symmetric_key, private_key): + return private_key.decrypt( + encrypted_symmetric_key, + ec.ECIES(hashes.SHA256()) + ) + +# Load server/client public and private keys +SERVER_PRIVATE_KEY, SERVER_PUBLIC_KEY = generate_ecc_key_pair() +CONFIG = load_config("config.toml") +CLIENT_PUBLIC_KEY = load_client_public_key(CONFIG)