feat(cloudtrail): cloudtrail service and checks (#1449)

Co-authored-by: sergargar <sergio@verica.io>
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
This commit is contained in:
Nacho Rivera
2022-11-03 15:39:41 +01:00
committed by GitHub
parent c6adf3a6d8
commit 8ae989cce8
38 changed files with 1716 additions and 534 deletions

3
.gitignore vendored
View File

@@ -43,3 +43,6 @@ junit-reports/
# Text
*.txt
# .env
.env

View File

@@ -9,7 +9,7 @@ boto3 = "1.24.8"
arnparse = "0.0.2"
botocore = "1.27.8"
pydantic = "1.9.1"
moto = "4.0.8"
moto = "4.0.9"
sure = "2.0.0"
bandit = "1.7.4"
safety = "1.10.3"

239
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "02042e0bff2fe10e70314636dd3fe288654c6b0deb47a1270b0d1dbcf3660cd5"
"sha256": "17d457477683f58bd28bc11042edcc0374d620d037243490cc3c7803cb5a4536"
},
"pipfile-spec": 6,
"requires": {
@@ -42,25 +42,26 @@
},
"boto3": {
"hashes": [
"sha256:2284a107d43f73b6007c7c8b946a8fd6f9baa6c97b5c956edc67d9be864def58"
"sha256:0a19d07a39d69b8e84e24d75474bbf4e737b1749d0c665503dfc2446f321e1f0",
"sha256:8f0e4eb5c26f927c09bc03302d38156af578b816f1e4f8ca4f0f734d134b9d4f"
],
"index": "pypi",
"version": "==1.25.3"
"version": "==1.26.0"
},
"botocore": {
"hashes": [
"sha256:2c2604262e5ab35ea83e9d5cf8be267e7fcdab6c815a432cfe15f23d92ce723d",
"sha256:4ea45626d8c5875c12e5767aa637388ce81871162f494eaf7b3888e875de84b7"
"sha256:c706640f8cf9297d2af87fc711394631afc14aaa225c7554e220964b5047b47d",
"sha256:f25dc0827005f81abf4b890a20c71f64ee40ea9e9795df38a242fdeed79e0a89"
],
"index": "pypi",
"version": "==1.28.3"
"version": "==1.29.0"
},
"certifi": {
"hashes": [
"sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14",
"sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==2022.9.24"
},
"cffi": {
@@ -221,51 +222,35 @@
},
"cryptography": {
"hashes": [
"sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a",
"sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f",
"sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0",
"sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407",
"sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7",
"sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6",
"sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153",
"sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750",
"sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad",
"sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6",
"sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b",
"sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5",
"sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a",
"sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d",
"sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d",
"sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294",
"sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0",
"sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a",
"sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac",
"sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61",
"sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013",
"sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e",
"sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb",
"sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9",
"sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd",
"sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"
"sha256:068147f32fa662c81aebab95c74679b401b12b57494872886eb5c1139250ec5d",
"sha256:06fc3cc7b6f6cca87bd56ec80a580c88f1da5306f505876a71c8cfa7050257dd",
"sha256:25c1d1f19729fb09d42e06b4bf9895212292cb27bb50229f5aa64d039ab29146",
"sha256:402852a0aea73833d982cabb6d0c3bb582c15483d29fb7085ef2c42bfa7e38d7",
"sha256:4e269dcd9b102c5a3d72be3c45d8ce20377b8076a43cbed6f660a1afe365e436",
"sha256:5419a127426084933076132d317911e3c6eb77568a1ce23c3ac1e12d111e61e0",
"sha256:554bec92ee7d1e9d10ded2f7e92a5d70c1f74ba9524947c0ba0c850c7b011828",
"sha256:5e89468fbd2fcd733b5899333bc54d0d06c80e04cd23d8c6f3e0542358c6060b",
"sha256:65535bc550b70bd6271984d9863a37741352b4aad6fb1b3344a54e6950249b55",
"sha256:6ab9516b85bebe7aa83f309bacc5f44a61eeb90d0b4ec125d2d003ce41932d36",
"sha256:6addc3b6d593cd980989261dc1cce38263c76954d758c3c94de51f1e010c9a50",
"sha256:728f2694fa743a996d7784a6194da430f197d5c58e2f4e278612b359f455e4a2",
"sha256:785e4056b5a8b28f05a533fab69febf5004458e20dad7e2e13a3120d8ecec75a",
"sha256:78cf5eefac2b52c10398a42765bfa981ce2372cbc0457e6bf9658f41ec3c41d8",
"sha256:7f836217000342d448e1c9a342e9163149e45d5b5eca76a30e84503a5a96cab0",
"sha256:8d41a46251bf0634e21fac50ffd643216ccecfaf3701a063257fe0b2be1b6548",
"sha256:984fe150f350a3c91e84de405fe49e688aa6092b3525f407a18b9646f6612320",
"sha256:9b24bcff7853ed18a63cfb0c2b008936a9554af24af2fb146e16d8e1aed75748",
"sha256:b1b35d9d3a65542ed2e9d90115dfd16bbc027b3f07ee3304fc83580f26e43249",
"sha256:b1b52c9e5f8aa2b802d48bd693190341fae201ea51c7a167d69fc48b60e8a959",
"sha256:bbf203f1a814007ce24bd4d51362991d5cb90ba0c177a9c08825f2cc304d871f",
"sha256:be243c7e2bfcf6cc4cb350c0d5cdf15ca6383bbcb2a8ef51d3c9411a9d4386f0",
"sha256:bfbe6ee19615b07a98b1d2287d6a6073f734735b49ee45b11324d85efc4d5cbd",
"sha256:c46837ea467ed1efea562bbeb543994c2d1f6e800785bd5a2c98bc096f5cb220",
"sha256:dfb4f4dd568de1b6af9f4cda334adf7d72cf5bc052516e1b2608b683375dd95c",
"sha256:ed7b00096790213e09eb11c97cc6e2b757f15f3d2f85833cd2d3ec3fe37c1722"
],
"markers": "python_version >= '3.6'",
"version": "==38.0.1"
},
"detect-secrets": {
"hashes": [
"sha256:d08ecabeee8b68c0acb0e8a354fb98d822a653f6ed05e520cead4c6fc1fc02cd",
"sha256:d56787e339758cef48c9ccd6692f7a094b9963c979c9813580b0169e41132833"
],
"index": "pypi",
"version": "==1.4.0"
},
"docker": {
"hashes": [
"sha256:19e330470af40167d293b0352578c1fa22d74b34d3edf5d4ff90ebc203bbb2f1",
"sha256:6e06ee8eca46cd88733df09b6b80c24a1a556bc5cb1e1ae54b2c239886d245cf"
],
"index": "pypi",
"version": "==6.0.0"
"markers": "python_full_version >= '3.6.0'",
"version": "==38.0.3"
},
"dparse": {
"hashes": [
@@ -296,7 +281,7 @@
"sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd",
"sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==4.0.9"
},
"gitpython": {
@@ -315,14 +300,6 @@
"markers": "python_version >= '3.5'",
"version": "==3.4"
},
"importlib-resources": {
"hashes": [
"sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668",
"sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437"
],
"markers": "python_version >= '3.7'",
"version": "==5.10.0"
},
"iniconfig": {
"hashes": [
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
@@ -346,47 +323,6 @@
"markers": "python_version >= '3.7'",
"version": "==1.0.1"
},
"jsonschema": {
"hashes": [
"sha256:165059f076eff6971bae5b742fc029a7b4ef3f9bcf04c14e4776a7605de14b23",
"sha256:9e74b8f9738d6a946d70705dc692b74b5429cd0960d58e79ffecfc43b2221eb9"
],
"markers": "python_version >= '3.7'",
"version": "==4.16.0"
},
"jsonschema-spec": {
"hashes": [
"sha256:1e525177574c23ae0f55cd62382632a083a0339928f0ca846a975a4da9851cec",
"sha256:780a22d517cdc857d9714a80d8349c546945063f20853ea32ba7f85bc643ec7d"
],
"markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'",
"version": "==0.1.2"
},
"lazy-object-proxy": {
"hashes": [
"sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada",
"sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d",
"sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7",
"sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe",
"sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd",
"sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c",
"sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858",
"sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288",
"sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec",
"sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f",
"sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891",
"sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c",
"sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25",
"sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156",
"sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8",
"sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f",
"sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e",
"sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0",
"sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"
],
"markers": "python_version >= '3.7'",
"version": "==1.8.0"
},
"markupsafe": {
"hashes": [
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
@@ -438,52 +374,26 @@
"sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62",
"sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==4.0.3"
},
"moto": {
"extras": [
"iam"
],
"extras": [],
"hashes": [
"sha256:3bd8a72dc385819c84ed3f9d18c386985634041c6ae544d957bd8ab88c6c15f1",
"sha256:8761880f5f3c3af27daac3882a56aae7e21fa2121f430cf917e55e977bddaf6b"
"sha256:2fb909d2ea1b732f89604e4268e2c2207c253e590a635a410c3c2aaebb34e113",
"sha256:ba03b638cf3b1cec64cbe9ac0d184ca898b69020c8e3c5b9b4961c1670629010"
],
"index": "pypi",
"version": "==4.0.8"
},
"openapi-schema-validator": {
"hashes": [
"sha256:34fbd14b7501abe25e64d7b4624a9db02cde1a578d285b3da6f34b290cdf0b3a",
"sha256:7cf27585dd7970b7257cefe48e1a3a10d4e34421831bdb472d96967433bc27bd"
],
"markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'",
"version": "==0.3.4"
},
"openapi-spec-validator": {
"hashes": [
"sha256:4a8aee1e45b1ac868e07ab25e18828fe9837baddd29a8e20fdb3d3c61c8eea3d",
"sha256:8248634bad1f23cac5d5a34e193ab36e23914057ca69e91a1ede5af75552c465"
],
"index": "pypi",
"version": "==0.5.1"
"version": "==4.0.9"
},
"packaging": {
"hashes": [
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==21.3"
},
"pathable": {
"hashes": [
"sha256:5c869d315be50776cc8a993f3af43e0c60dc01506b399643f919034ebf4cdcab",
"sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14"
],
"markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'",
"version": "==0.4.3"
},
"pbr": {
"hashes": [
"sha256:b97bc6695b2aff02144133c2e7399d5885223d42b7912ffaec2ca3898e673bfe",
@@ -497,7 +407,7 @@
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==1.0.0"
},
"pycparser": {
@@ -557,33 +467,6 @@
"markers": "python_full_version >= '3.6.8'",
"version": "==3.0.9"
},
"pyrsistent": {
"hashes": [
"sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c",
"sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc",
"sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e",
"sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26",
"sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec",
"sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286",
"sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045",
"sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec",
"sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8",
"sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c",
"sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca",
"sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22",
"sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a",
"sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96",
"sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc",
"sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1",
"sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07",
"sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6",
"sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b",
"sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5",
"sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"
],
"markers": "python_version >= '3.7'",
"version": "==0.18.1"
},
"pytest": {
"hashes": [
"sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71",
@@ -605,15 +488,15 @@
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.2"
},
"pytz": {
"hashes": [
"sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22",
"sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"
"sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427",
"sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"
],
"version": "==2022.5"
"version": "==2022.6"
},
"pyyaml": {
"hashes": [
@@ -658,7 +541,7 @@
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==6.0"
},
"requests": {
@@ -721,7 +604,7 @@
"sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0",
"sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646"
],
"markers": "platform_python_implementation == 'CPython' and python_version < '3.11'",
"markers": "python_version < '3.11' and platform_python_implementation == 'CPython'",
"version": "==0.2.7"
},
"s3transfer": {
@@ -760,7 +643,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"smmap": {
@@ -768,7 +651,7 @@
"sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94",
"sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"
],
"markers": "python_version >= '3.6'",
"markers": "python_full_version >= '3.6.0'",
"version": "==5.0.0"
},
"stevedore": {
@@ -791,7 +674,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
},
"tomli": {
@@ -833,14 +716,6 @@
"index": "pypi",
"version": "==2.6"
},
"websocket-client": {
"hashes": [
"sha256:398909eb7e261f44b8f4bd474785b6ec5f5b499d4953342fe9755e01ef624090",
"sha256:f9611eb65c8241a67fb373bef040b3cf8ad377a9f6546a12b620b6511e8ea9ef"
],
"markers": "python_version >= '3.7'",
"version": "==1.4.1"
},
"werkzeug": {
"hashes": [
"sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f",
@@ -864,14 +739,6 @@
],
"markers": "python_version >= '3.4'",
"version": "==0.13.0"
},
"zipp": {
"hashes": [
"sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1",
"sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"
],
"markers": "python_version < '3.10'",
"version": "==3.10.0"
}
},
"develop": {}

View File

@@ -1,68 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_check21="2.1"
CHECK_TITLE_check21="[check21] Ensure CloudTrail is enabled in all regions"
CHECK_SCORED_check21="SCORED"
CHECK_CIS_LEVEL_check21="LEVEL1"
CHECK_SEVERITY_check21="High"
CHECK_ASFF_TYPE_check21="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check21="AwsCloudTrailTrail"
CHECK_ALTERNATE_check201="check21"
CHECK_ASFF_COMPLIANCE_TYPE_check21="ens-op.acc.7.aws.iam.1 ens-op.mon.1.aws.trail.1"
CHECK_SERVICENAME_check21="cloudtrail"
CHECK_RISK_check21='AWS CloudTrail is a web service that records AWS API calls for your account and delivers log files to you. The recorded information includes the identity of the API caller; the time of the API call; the source IP address of the API caller; the request parameters; and the response elements returned by the AWS service.'
CHECK_REMEDIATION_check21='Ensure Logging is set to ON on all regions (even if they are not being used at the moment.'
CHECK_DOC_check21='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrailconcepts.html#cloudtrail-concepts-management-events'
CHECK_CAF_EPIC_check21='Logging and Monitoring'
check21(){
trail_count=0
# "Ensure CloudTrail is enabled in all regions (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail)
if [[ "$MULTIREGION_TRAIL_STATUS" == 'False' ]];then
textFail "$regx: Trail $trail is not enabled for all regions" "$regx" "$trail"
else
TRAIL_ON_OFF_STATUS=$($AWSCLI cloudtrail get-trail-status $PROFILE_OPT --region $TRAIL_REGION --name $trail --query IsLogging --output text)
if [[ "$TRAIL_ON_OFF_STATUS" == 'False' ]];then
textFail "$regx: Trail $trail is configured for all regions but it is OFF" "$regx" "$trail"
else
textPass "$regx: Trail $trail is enabled for all regions" "$regx" "$trail"
fi
fi
done
fi
done
if [[ $trail_count == 0 ]]; then
if [[ $FILTERREGION ]]; then
textFail "$regx: No CloudTrail trails were found in the filtered region" "$regx" "$trail"
else
textFail "$regx: No CloudTrail trails were found in the account" "$regx" "$trail"
fi
fi
}

View File

@@ -1,60 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_check22="2.2"
CHECK_TITLE_check22="[check22] Ensure CloudTrail log file validation is enabled"
CHECK_SCORED_check22="SCORED"
CHECK_CIS_LEVEL_check22="LEVEL2"
CHECK_SEVERITY_check22="Medium"
CHECK_ASFF_TYPE_check22="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check22="AwsCloudTrailTrail"
CHECK_ALTERNATE_check202="check22"
CHECK_ASFF_COMPLIANCE_TYPE_check22="ens-op.exp.10.aws.trail.1"
CHECK_SERVICENAME_check22="cloudtrail"
CHECK_RISK_check22='Enabling log file validation will provide additional integrity checking of CloudTrail logs. '
CHECK_REMEDIATION_check22='Ensure LogFileValidationEnabled is set to true for each trail.'
CHECK_DOC_check22='http://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-log-filevalidation-enabling.html'
CHECK_CAF_EPIC_check22='Logging and Monitoring'
check22(){
trail_count=0
# "Ensure CloudTrail log file validation is enabled (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
LOGFILEVALIDATION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].LogFileValidationEnabled' --output text --trail-name-list $trail)
if [[ "$LOGFILEVALIDATION_TRAIL_STATUS" == 'False' ]];then
textFail "$regx: Trail $trail log file validation disabled" "$regx" "$trail"
else
textPass "$regx: Trail $trail log file validation enabled" "$regx" "$trail"
fi
done
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -1,94 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_check23="2.3"
CHECK_TITLE_check23="[check23] Ensure the S3 bucket CloudTrail logs to is not publicly accessible"
CHECK_SCORED_check23="SCORED"
CHECK_CIS_LEVEL_check23="LEVEL1"
CHECK_SEVERITY_check23="Critical"
CHECK_ASFF_TYPE_check23="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check23="AwsS3Bucket"
CHECK_ALTERNATE_check203="check23"
CHECK_ASFF_COMPLIANCE_TYPE_check23="ens-op.exp.10.aws.trail.3 ens-op.exp.10.aws.trail.4"
CHECK_SERVICENAME_check23="cloudtrail"
CHECK_RISK_check23='Allowing public access to CloudTrail log content may aid an adversary in identifying weaknesses in the affected accounts use or configuration.'
CHECK_REMEDIATION_check23='Analyze Bucket policy to validate appropriate permissions. Ensure the AllUsers principal is not granted privileges. Ensure the AuthenticatedUsers principal is not granted privileges.'
CHECK_DOC_check23='https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html'
CHECK_CAF_EPIC_check23='Logging and Monitoring'
check23(){
trail_count=0
# "Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].[S3BucketName]' --output text --trail-name-list $trail)
if [[ -z $CLOUDTRAILBUCKET ]]; then
textFail "Trail $trail in $TRAIL_REGION does not publish to S3"
continue
fi
CLOUDTRAIL_ACCOUNT_ID=$(echo $trail | awk -F: '{ print $5 }')
if [ "$CLOUDTRAIL_ACCOUNT_ID" != "$ACCOUNT_NUM" ]; then
textInfo "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not in current account"
continue
fi
#
# LOCATION - requests referencing buckets created after March 20, 2019
# must be made to S3 endpoints in the same region as the bucket was
# created.
#
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $CLOUDTRAILBUCKET --output text 2>&1)
if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
textInfo "Trail $trail in $TRAIL_REGION Access Denied getting bucket location for $CLOUDTRAILBUCKET"
continue
fi
if [[ $BUCKET_LOCATION == "None" ]]; then
BUCKET_LOCATION="us-east-1"
fi
if [[ $BUCKET_LOCATION == "EU" ]]; then
BUCKET_LOCATION="eu-west-1"
fi
CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $CLOUDTRAILBUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --output text 2>&1)
if [[ $(echo "$CLOUDTRAILBUCKET_HASALLPERMISIONS" | grep AccessDenied) ]]; then
textInfo "Trail $trail in $TRAIL_REGION Access Denied getting bucket acl for $CLOUDTRAILBUCKET"
continue
fi
if [[ -z $CLOUDTRAILBUCKET_HASALLPERMISIONS ]]; then
textPass "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not publicly accessible"
else
textFail "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is publicly accessible"
fi
done
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -1,66 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_check24="2.4"
CHECK_TITLE_check24="[check24] Ensure CloudTrail trails are integrated with CloudWatch Logs"
CHECK_SCORED_check24="SCORED"
CHECK_CIS_LEVEL_check24="LEVEL1"
CHECK_SEVERITY_check24="Low"
CHECK_ASFF_TYPE_check24="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check24="AwsCloudTrailTrail"
CHECK_ALTERNATE_check204="check24"
CHECK_ASFF_COMPLIANCE_TYPE_check24="ens-op.exp.8.aws.cw.1"
CHECK_SERVICENAME_check24="cloudtrail"
CHECK_RISK_check24='Sending CloudTrail logs to CloudWatch Logs will facilitate real-time and historic activity logging based on user; API; resource; and IP address; and provides opportunity to establish alarms and notifications for anomalous or sensitivity account activity.'
CHECK_REMEDIATION_check24='Validate that the trails in CloudTrail has an arn set in the CloudWatchLogsLogGroupArn property.'
CHECK_DOC_check24='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/send-cloudtrail-events-to-cloudwatch-logs.html'
CHECK_CAF_EPIC_check24='Logging and Monitoring'
check24(){
trail_count=0
# "Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
LATESTDELIVERY_TIMESTAMP=$($AWSCLI cloudtrail get-trail-status --name $trail $PROFILE_OPT --region $TRAIL_REGION --query 'LatestCloudWatchLogsDeliveryTime' --output text|grep -v None)
if [[ ! $LATESTDELIVERY_TIMESTAMP ]];then
textFail "$TRAIL_REGION: $trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" "$TRAIL_REGION" "$trail"
else
LATESTDELIVERY_DATE=$(timestamp_to_date $LATESTDELIVERY_TIMESTAMP)
HOWOLDER=$(how_older_from_today $LATESTDELIVERY_DATE)
if [ $HOWOLDER -gt "1" ];then
textFail "$TRAIL_REGION: $trail trail is not logging in the last 24h or not configured" "$TRAIL_REGION" "$trail"
else
textPass "$TRAIL_REGION: $trail trail has been logging during the last 24h" "$TRAIL_REGION" "$trail"
fi
fi
done
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -1,59 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_check27="2.7"
CHECK_TITLE_check27="[check27] Ensure CloudTrail logs are encrypted at rest using KMS CMKs"
CHECK_SCORED_check27="SCORED"
CHECK_CIS_LEVEL_check27="LEVEL2"
CHECK_SEVERITY_check27="Medium"
CHECK_ASFF_TYPE_check27="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check27="AwsCloudTrailTrail"
CHECK_ALTERNATE_check207="check27"
CHECK_ASFF_COMPLIANCE_TYPE_check27="ens-op.exp.10.aws.trail.5"
CHECK_SERVICENAME_check27="cloudtrail"
CHECK_RISK_check27='By default; the log files delivered by CloudTrail to your bucket are encrypted by Amazon server-side encryption with Amazon S3-managed encryption keys (SSE-S3). To provide a security layer that is directly manageable; you can instead use server-side encryption with AWS KMSmanaged keys (SSE-KMS) for your CloudTrail log files.'
CHECK_REMEDIATION_check27='This approach has the following advantages: You can create and manage the CMK encryption keys yourself. You can use a single CMK to encrypt and decrypt log files for multiple accounts across all regions. You have control over who can use your key for encrypting and decrypting CloudTrail log files. You can assign permissions for the key to the users. You have enhanced security.'
CHECK_DOC_check27='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/encrypting-cloudtrail-log-files-with-aws-kms.html'
CHECK_CAF_EPIC_check27='Logging and Monitoring'
check27(){
trail_count=0
# "Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
KMSKEYID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].KmsKeyId' --output text --trail-name-list $trail)
if [[ "$KMSKEYID" ]];then
textPass "$regx: Trail $trail has encryption enabled" "$regx" "$trail"
else
textFail "$regx: Trail $trail has encryption disabled" "$regx" "$trail"
fi
done
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
cloudtrail_client = Cloudtrail(current_audit_info)

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "cloudtrail_cloudwatch_logging_enabled",
"CheckTitle": "Ensure CloudTrail trails are integrated with CloudWatch Logs",
"CheckType": ["Software and Configuration Checks", "Industry and Regulatory Standards" ,"CIS AWS Foundations Benchmark"],
"ServiceName": "cloudtrail",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "low",
"ResourceType": "AwsCloudTrailTrail",
"Description": "Ensure CloudTrail trails are integrated with CloudWatch Logs",
"Risk": "Sending CloudTrail logs to CloudWatch Logs will facilitate real-time and historic activity logging based on user; API; resource; and IP address; and provides opportunity to establish alarms and notifications for anomalous or sensitivity account activity.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws cloudtrail update-trail --name <trail_name> --cloudwatch-logs-log-group- arn <cloudtrail_log_group_arn> --cloudwatch-logs-role-arn <cloudtrail_cloudwatchLogs_role_arn>",
"NativeIaC": "",
"Other": "https://docs.bridgecrew.io/docs/logging_4#aws-console",
"Terraform": ""
},
"Recommendation": {
"Text": "Validate that the trails in CloudTrail has an arn set in the CloudWatchLogsLogGroupArn property.",
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/send-cloudtrail-events-to-cloudwatch-logs.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,44 @@
from datetime import datetime, timedelta, timezone
from lib.check.models import Check, Check_Report
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
maximum_time_without_logging = 1
class cloudtrail_cloudwatch_logging_enabled(Check):
def execute(self):
findings = []
for trail in cloudtrail_client.trails:
if trail.name:
report = Check_Report(self.metadata)
report.region = trail.region
report.resource_id = trail.name
report.resource_arn = trail.trail_arn
report.status = "PASS"
if trail.is_multiregion:
report.status_extended = (
f"Multiregion trail {trail.name} has been logging the last 24h"
)
else:
report.status_extended = f"Single region trail {trail.name} has been logging the last 24h"
if trail.latest_cloudwatch_delivery_time:
last_log_delivery = (
datetime.now().replace(tzinfo=timezone.utc)
- trail.latest_cloudwatch_delivery_time
)
if last_log_delivery > timedelta(days=maximum_time_without_logging):
report.status = "FAIL"
if trail.is_multiregion:
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h"
else:
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h"
else:
report.status = "FAIL"
if trail.is_multiregion:
report.status_extended = f"Multiregion trail {trail.name} is not configured to deliver logs"
else:
report.status_extended = f"Single region trail {trail.name} is not configured to deliver logs"
findings.append(report)
return findings

View File

@@ -0,0 +1,225 @@
from datetime import datetime, timedelta, timezone
from re import search
from unittest import mock
from boto3 import client
from moto import mock_cloudtrail, mock_s3
class Test_cloudtrail_cloudwatch_logging_enabled:
@mock_cloudtrail
@mock_s3
def test_trails_sending_logs_during_and_not_last_day(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
cloudtrail_client_eu_west_1 = client("cloudtrail", region_name="eu-west-1")
s3_client_eu_west_1 = client("s3", region_name="eu-west-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
trail_name_eu = "trail_test_eu"
bucket_name_eu = "bucket_test_eu"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
s3_client_eu_west_1.create_bucket(
Bucket=bucket_name_eu,
CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
trail_eu = cloudtrail_client_eu_west_1.create_trail(
Name=trail_name_eu, S3BucketName=bucket_name_eu, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled import (
cloudtrail_cloudwatch_logging_enabled,
)
for trail in service_client.trails:
if trail.name == trail_name_us:
trail.latest_cloudwatch_delivery_time = datetime.now().replace(
tzinfo=timezone.utc
)
elif trail.name == trail_name_eu:
trail.latest_cloudwatch_delivery_time = (
datetime.now() - timedelta(days=2)
).replace(tzinfo=timezone.utc)
regions = []
for region in service_client.regional_clients.keys():
regions.append(region)
check = cloudtrail_cloudwatch_logging_enabled()
result = check.execute()
# len of result if has to be 2 since we only have 2 single region trails
assert len(result) == 2
for report in result:
if report.resource_id == trail_name_us:
assert report.resource_id == trail_name_us
assert report.resource_arn == trail_us["TrailARN"]
assert report.status == "PASS"
assert search(
report.status_extended,
f"Single region trail {trail_name_us} has been logging the last 24h",
)
if report.resource_id == trail_name_eu:
assert report.resource_id == trail_name_eu
assert report.resource_arn == trail_eu["TrailARN"]
assert report.status == "FAIL"
assert search(
report.status_extended,
f"Single region trail {trail_name_eu} is not logging in the last 24h",
)
@mock_cloudtrail
@mock_s3
def test_multi_region_and_single_region_logging_and_not(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
cloudtrail_client_eu_west_1 = client("cloudtrail", region_name="eu-west-1")
s3_client_eu_west_1 = client("s3", region_name="eu-west-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
trail_name_eu = "trail_test_eu"
bucket_name_eu = "bucket_test_eu"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
s3_client_eu_west_1.create_bucket(
Bucket=bucket_name_eu,
CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=True
)
trail_eu = cloudtrail_client_eu_west_1.create_trail(
Name=trail_name_eu, S3BucketName=bucket_name_eu, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled import (
cloudtrail_cloudwatch_logging_enabled,
)
for trail in service_client.trails:
if trail.name == trail_name_us:
trail.latest_cloudwatch_delivery_time = datetime.now().replace(
tzinfo=timezone.utc
)
elif trail.name == trail_name_eu:
trail.latest_cloudwatch_delivery_time = (
datetime.now() - timedelta(days=2)
).replace(tzinfo=timezone.utc)
regions = []
for region in service_client.regional_clients.keys():
regions.append(region)
check = cloudtrail_cloudwatch_logging_enabled()
result = check.execute()
# len of result should be 24 -> (1 multiregion entry per region + 1 entry because of single region trail)
assert len(result) == 24
for report in result:
if report.resource_id == trail_name_us:
assert report.resource_id == trail_name_us
assert report.resource_arn == trail_us["TrailARN"]
assert report.status == "PASS"
assert search(
report.status_extended,
f"Multiregion trail {trail_name_us} has been logging the last 24h",
)
if report.resource_id == trail_name_eu and report.region == "eu-west-1":
assert report.resource_id == trail_name_eu
assert report.resource_arn == trail_eu["TrailARN"]
assert report.status == "FAIL"
assert search(
report.status_extended,
f"Single region trail {trail_name_eu} is not logging in the last 24h",
)
@mock_cloudtrail
@mock_s3
def test_trails_sending_and_not_sending_logs(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
cloudtrail_client_eu_west_1 = client("cloudtrail", region_name="eu-west-1")
s3_client_eu_west_1 = client("s3", region_name="eu-west-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
trail_name_eu = "trail_test_eu"
bucket_name_eu = "bucket_test_eu"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
s3_client_eu_west_1.create_bucket(
Bucket=bucket_name_eu,
CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
trail_eu = cloudtrail_client_eu_west_1.create_trail(
Name=trail_name_eu, S3BucketName=bucket_name_eu, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled import (
cloudtrail_cloudwatch_logging_enabled,
)
for trail in service_client.trails:
if trail.name == trail_name_us:
trail.latest_cloudwatch_delivery_time = datetime.now().replace(
tzinfo=timezone.utc
)
elif trail.name == trail_name_us:
trail.latest_cloudwatch_delivery_time = None
regions = []
for region in service_client.regional_clients.keys():
regions.append(region)
check = cloudtrail_cloudwatch_logging_enabled()
result = check.execute()
# len of result if has to be 2 since we only have 2 single region trails
assert len(result) == 2
for report in result:
if report.resource_id == trail_name_us:
assert report.resource_id == trail_name_us
assert report.resource_arn == trail_us["TrailARN"]
assert report.status == "PASS"
assert search(
report.status_extended,
f"Single region trail {trail_name_us} has been logging the last 24h",
)
if report.resource_id == trail_name_eu:
assert report.resource_id == trail_name_eu
assert report.resource_arn == trail_eu["TrailARN"]
assert report.status == "FAIL"
assert search(
report.status_extended,
f"Single region trail {trail_name_eu} is not configured to deliver logs",
)

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "cloudtrail_kms_encryption_enabled",
"CheckTitle": "Ensure CloudTrail logs are encrypted at rest using KMS CMKs",
"CheckType": ["Software and Configuration Checks", "Industry and Regulatory Standards" ,"CIS AWS Foundations Benchmark"],
"ServiceName": "cloudtrail",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsCloudTrailTrail",
"Description": "Ensure CloudTrail logs are encrypted at rest using KMS CMKs",
"Risk": "By default; the log files delivered by CloudTrail to your bucket are encrypted by Amazon server-side encryption with Amazon S3-managed encryption keys (SSE-S3). To provide a security layer that is directly manageable; you can instead use server-side encryption with AWS KMSmanaged keys (SSE-KMS) for your CloudTrail log files.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws cloudtrail update-trail --name <trail_name> --kms-id <cloudtrail_kms_key> aws kms put-key-policy --key-id <cloudtrail_kms_key> --policy <cloudtrail_kms_key_policy>",
"NativeIaC": "https://docs.bridgecrew.io/docs/logging_7#fix---buildtime",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "This approach has the following advantages: You can create and manage the CMK encryption keys yourself. You can use a single CMK to encrypt and decrypt log files for multiple accounts across all regions. You have control over who can use your key for encrypting and decrypting CloudTrail log files. You can assign permissions for the key to the users. You have enhanced security.",
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/encrypting-cloudtrail-log-files-with-aws-kms.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,35 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
class cloudtrail_kms_encryption_enabled(Check):
def execute(self):
findings = []
for trail in cloudtrail_client.trails:
if trail.name:
report = Check_Report(self.metadata)
report.region = trail.region
report.resource_id = trail.name
report.resource_arn = trail.trail_arn
report.status = "FAIL"
if trail.is_multiregion:
report.status_extended = (
f"Multiregion trail {trail.name} has encryption disabled"
)
else:
report.status_extended = (
f"Single region trail {trail.name} has encryption disabled"
)
if trail.kms_key:
report.status = "PASS"
if trail.is_multiregion:
report.status_extended = (
f"Multiregion trail {trail.name} has encryption enabled"
)
else:
report.status_extended = (
f"Single region trail {trail.name} has encryption enabled"
)
findings.append(report)
return findings

View File

@@ -0,0 +1,89 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_cloudtrail, mock_kms, mock_s3
class Test_cloudtrail_kms_encryption_enabled:
@mock_cloudtrail
@mock_s3
def test_trail_no_kms(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
):
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled import (
cloudtrail_kms_encryption_enabled,
)
check = cloudtrail_kms_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"has encryption disabled",
result[0].status_extended,
)
assert result[0].resource_id == trail_name_us
assert result[0].resource_arn == trail_us["TrailARN"]
@mock_cloudtrail
@mock_s3
@mock_kms
def test_trail_kms(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
kms_client = client("kms", region_name="us-east-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
key_arn = kms_client.create_key()["KeyMetadata"]["Arn"]
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us,
S3BucketName=bucket_name_us,
IsMultiRegionTrail=False,
KmsKeyId=key_arn,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
):
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled import (
cloudtrail_kms_encryption_enabled,
)
check = cloudtrail_kms_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"has encryption enabled",
result[0].status_extended,
)
assert result[0].resource_id == trail_name_us
assert result[0].resource_arn == trail_us["TrailARN"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "cloudtrail_log_file_validation_enabled",
"CheckTitle": "Ensure CloudTrail log file validation is enabled",
"CheckType": ["Software and Configuration Checks", "Industry and Regulatory Standards" ,"CIS AWS Foundations Benchmark"],
"ServiceName": "cloudtrail",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsCloudTrailTrail",
"Description": "Ensure CloudTrail log file validation is enabled",
"Risk": "Enabling log file validation will provide additional integrity checking of CloudTrail logs. ",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws cloudtrail update-trail --name <trail_name> --enable-log-file-validation",
"NativeIaC": "https://docs.bridgecrew.io/docs/logging_2#cloudformation",
"Other": "",
"Terraform": "https://docs.bridgecrew.io/docs/logging_2#terraform"
},
"Recommendation": {
"Text": "Ensure LogFileValidationEnabled is set to true for each trail.",
"Url": "http://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-log-filevalidation-enabling.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,31 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
class cloudtrail_log_file_validation_enabled(Check):
def execute(self):
findings = []
for trail in cloudtrail_client.trails:
if trail.name:
report = Check_Report(self.metadata)
report.region = trail.region
report.resource_id = trail.name
report.resource_arn = trail.trail_arn
report.status = "FAIL"
if trail.is_multiregion:
report.status_extended = (
f"Multiregion trail {trail.name} log file validation disabled"
)
else:
report.status_extended = (
f"Single region trail {trail.name} log file validation disabled"
)
if trail.log_file_validation_enabled:
report.status = "PASS"
if trail.is_multiregion:
report.status_extended = f"Multiregion trail {trail.name} log file validation enabled"
else:
report.status_extended = f"Single region trail {trail.name} log file validation enabled"
findings.append(report)
return findings

View File

@@ -0,0 +1,102 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_cloudtrail, mock_s3
class Test_cloudtrail_log_file_validation_enabled:
@mock_cloudtrail
@mock_s3
def test_no_logging_validation(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
):
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled import (
cloudtrail_log_file_validation_enabled,
)
check = cloudtrail_log_file_validation_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search("log file validation disabled", result[0].status_extended)
assert result[0].resource_id == trail_name_us
assert result[0].resource_arn == trail_us["TrailARN"]
@mock_cloudtrail
@mock_s3
def test_various_trails_with_and_without_logging_validation(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
cloudtrail_client_eu_west_1 = client("cloudtrail", region_name="eu-west-1")
s3_client_eu_west_1 = client("s3", region_name="eu-west-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
trail_name_eu = "trail_test_eu"
bucket_name_eu = "bucket_test_eu"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
s3_client_eu_west_1.create_bucket(
Bucket=bucket_name_eu,
CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us,
S3BucketName=bucket_name_us,
IsMultiRegionTrail=False,
EnableLogFileValidation=True,
)
trail_eu = cloudtrail_client_eu_west_1.create_trail(
Name=trail_name_eu, S3BucketName=bucket_name_eu, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled import (
cloudtrail_log_file_validation_enabled,
)
regions = []
for region in service_client.regional_clients.keys():
regions.append(region)
check = cloudtrail_log_file_validation_enabled()
result = check.execute()
assert len(result) == 2
for report in result:
if report.resource_id == trail_name_us:
assert report.status == "PASS"
assert search("log file validation enabled", report.status_extended)
assert report.resource_id == trail_name_us
assert report.resource_arn == trail_us["TrailARN"]
elif report.resource_id == trail_name_eu:
assert report.status == "FAIL"
assert search(
"log file validation disabled", report.status_extended
)
assert report.resource_id == trail_name_eu
assert report.resource_arn == trail_eu["TrailARN"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "cloudtrail_logs_s3_bucket_access_logging_enabled",
"CheckTitle": "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket",
"CheckType": ["Software and Configuration Checks", "Industry and Regulatory Standards" ,"CIS AWS Foundations Benchmark"],
"ServiceName": "cloudtrail",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsCloudTrailTrail",
"Description": "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket",
"Risk": "Server access logs can assist you in security and access audits; help you learn about your customer base; and understand your Amazon S3 bill.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "https://docs.bridgecrew.io/docs/logging_6#aws-console",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure that S3 buckets have Logging enabled. CloudTrail data events can be used in place of S3 bucket logging. If that is the case; this finding can be considered a false positive.",
"Url": "https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,31 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
from providers.aws.services.s3.s3_client import s3_client
class cloudtrail_logs_s3_bucket_access_logging_enabled(Check):
def execute(self):
findings = []
for trail in cloudtrail_client.trails:
if trail.name:
trail_bucket = trail.s3_bucket
report = Check_Report(self.metadata)
report.region = trail.region
report.resource_id = trail.name
report.resource_arn = trail.trail_arn
report.status = "FAIL"
if trail.is_multiregion:
report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}"
else:
report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}"
for bucket in s3_client.buckets:
if trail_bucket == bucket.name and bucket.logging:
report.status = "PASS"
if trail.is_multiregion:
report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}"
else:
report.status_extended = f"Single region trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}"
findings.append(report)
return findings

View File

@@ -0,0 +1,113 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_cloudtrail, mock_s3
class Test_cloudtrail_logs_s3_bucket_access_logging_enabled:
@mock_cloudtrail
@mock_s3
def test_bucket_not_logging(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
from providers.aws.services.s3.s3_service import S3
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
):
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.s3_client",
new=S3(current_audit_info),
):
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled import (
cloudtrail_logs_s3_bucket_access_logging_enabled,
)
check = cloudtrail_logs_s3_bucket_access_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"S3 bucket access logging is not enabled for bucket",
result[0].status_extended,
)
assert result[0].resource_id == trail_name_us
assert result[0].resource_arn == trail_us["TrailARN"]
@mock_cloudtrail
@mock_s3
def test_bucket_logging(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
logging_bucket = "logging"
s3_client_us_east_1.create_bucket(
Bucket=bucket_name_us,
)
s3_client_us_east_1.create_bucket(
Bucket=logging_bucket,
)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
s3_client_us_east_1.put_bucket_acl(
Bucket=logging_bucket,
GrantWrite="uri=http://acs.amazonaws.com/groups/s3/LogDelivery",
GrantReadACP="uri=http://acs.amazonaws.com/groups/s3/LogDelivery",
)
s3_client_us_east_1.put_bucket_logging(
Bucket=bucket_name_us,
BucketLoggingStatus={
"LoggingEnabled": {
"TargetBucket": logging_bucket,
"TargetPrefix": logging_bucket,
}
},
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
from providers.aws.services.s3.s3_service import S3
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
):
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.s3_client",
new=S3(current_audit_info),
):
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled import (
cloudtrail_logs_s3_bucket_access_logging_enabled,
)
check = cloudtrail_logs_s3_bucket_access_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"S3 bucket access logging is enabled for bucket",
result[0].status_extended,
)
assert result[0].resource_id == trail_name_us
assert result[0].resource_arn == trail_us["TrailARN"]

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "cloudtrail_logs_s3_bucket_is_not_publicly_accessible",
"CheckTitle": "Ensure the S3 bucket CloudTrail logs is not publicly accessible",
"CheckType": ["Software and Configuration Checks", "Industry and Regulatory Standards" ,"CIS AWS Foundations Benchmark"],
"ServiceName": "cloudtrail",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "critical",
"ResourceType": "AwsCloudTrailTrail",
"Description": "Ensure the S3 bucket CloudTrail logs to is not publicly accessible",
"Risk": "Allowing public access to CloudTrail log content may aid an adversary in identifying weaknesses in the affected accounts use or configuration.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "https://docs.bridgecrew.io/docs/logging_3#aws-console",
"Terraform": ""
},
"Recommendation": {
"Text": "Analyze Bucket policy to validate appropriate permissions. Ensure the AllUsers principal is not granted privileges. Ensure the AuthenticatedUsers principal is not granted privileges.",
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,39 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
from providers.aws.services.s3.s3_client import s3_client
class cloudtrail_logs_s3_bucket_is_not_publicly_accessible(Check):
def execute(self):
findings = []
for trail in cloudtrail_client.trails:
if trail.name:
trail_bucket = trail.s3_bucket
report = Check_Report(self.metadata)
report.region = trail.region
report.resource_id = trail.name
report.resource_arn = trail.trail_arn
report.status = "PASS"
if trail.is_multiregion:
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible"
else:
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible"
for bucket in s3_client.buckets:
# Here we need to ensure that acl_grantee is filled since if we don't have permissions to query the api for a concrete region
# (for example due to a SCP) we are going to try access an attribute from a None type
if trail_bucket == bucket.name and bucket.acl_grantee:
for grant in bucket.acl_grantee:
if (
grant.URI
== "http://acs.amazonaws.com/groups/global/AllUsers"
):
report.status = "FAIL"
if trail.is_multiregion:
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible"
else:
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible"
break
findings.append(report)
return findings

View File

@@ -0,0 +1,166 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_cloudtrail, mock_s3
class Test_cloudtrail_logs_s3_bucket_is_not_publicly_accessible:
@mock_cloudtrail
@mock_s3
def test_trail_bucket_no_acl(self):
cloudtrail_client = client("cloudtrail", region_name="us-east-1")
s3_client = client("s3", region_name="us-east-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
s3_client.create_bucket(Bucket=bucket_name_us)
trail_us = cloudtrail_client.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
from providers.aws.services.s3.s3_service import S3
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_client",
new=Cloudtrail(current_audit_info),
):
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.s3_client",
new=S3(current_audit_info),
):
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible import (
cloudtrail_logs_s3_bucket_is_not_publicly_accessible,
)
check = cloudtrail_logs_s3_bucket_is_not_publicly_accessible()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].resource_id == trail_name_us
assert result[0].resource_arn == trail_us["TrailARN"]
assert search(
result[0].status_extended,
f"S3 Bucket {bucket_name_us} from single region trail {trail_name_us} is not publicly accessible",
)
@mock_cloudtrail
@mock_s3
def test_trail_bucket_not_valid_acl(self):
cloudtrail_client = client("cloudtrail", region_name="us-east-1")
s3_client = client("s3", region_name="us-east-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
s3_client.create_bucket(Bucket=bucket_name_us)
trail_us = cloudtrail_client.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
s3_client.put_bucket_acl(
AccessControlPolicy={
"Grants": [
{
"Grantee": {
"DisplayName": "test",
"EmailAddress": "",
"ID": "test_ID",
"Type": "Group",
"URI": "http://acs.amazonaws.com/groups/global/AllUsers",
},
"Permission": "READ",
},
],
"Owner": {"DisplayName": "test", "ID": "test_id"},
},
Bucket=bucket_name_us,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
from providers.aws.services.s3.s3_service import S3
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_client",
new=Cloudtrail(current_audit_info),
):
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.s3_client",
new=S3(current_audit_info),
):
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible import (
cloudtrail_logs_s3_bucket_is_not_publicly_accessible,
)
check = cloudtrail_logs_s3_bucket_is_not_publicly_accessible()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == trail_name_us
assert result[0].resource_arn == trail_us["TrailARN"]
assert search(
result[0].status_extended,
f"S3 Bucket {bucket_name_us} from single region trail {trail_name_us} is publicly accessible",
)
@mock_cloudtrail
@mock_s3
def test_trail_bucket_not_valid_acl(self):
cloudtrail_client = client("cloudtrail", region_name="us-east-1")
s3_client = client("s3", region_name="us-east-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
s3_client.create_bucket(Bucket=bucket_name_us)
trail_us = cloudtrail_client.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
s3_client.put_bucket_acl(
AccessControlPolicy={
"Grants": [
{
"Grantee": {
"DisplayName": "test",
"EmailAddress": "",
"ID": "test_ID",
"Type": "CanonicalUser",
"URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers",
},
"Permission": "READ",
},
],
"Owner": {"DisplayName": "test", "ID": "test_id"},
},
Bucket=bucket_name_us,
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_client",
new=Cloudtrail(current_audit_info),
):
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible import (
cloudtrail_logs_s3_bucket_is_not_publicly_accessible,
)
check = cloudtrail_logs_s3_bucket_is_not_publicly_accessible()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].resource_id == trail_name_us
assert result[0].resource_arn == trail_us["TrailARN"]
assert search(
result[0].status_extended,
f"S3 Bucket {bucket_name_us} from single region trail {trail_name_us} is not publicly accessible",
)

View File

@@ -0,0 +1,46 @@
{
"Provider": "aws",
"CheckID": "cloudtrail_multi_region_enabled",
"CheckTitle": "Ensure CloudTrail is enabled in all regions",
"CheckType": ["Software and Configuration Checks", "Industry and Regulatory Standards" ,"CIS AWS Foundations Benchmark"],
"ServiceName": "cloudtrail",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "high",
"ResourceType": "AwsCloudTrailTrail",
"Description": "Ensure CloudTrail is enabled in all regions",
"Risk": "AWS CloudTrail is a web service that records AWS API calls for your account and delivers log files to you. The recorded information includes the identity of the API caller; the time of the API call; the source IP address of the API caller; the request parameters; and the response elements returned by the AWS service.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws cloudtrail create-trail --name <trail_name> --bucket-name <s3_bucket_for_cloudtrail> --is-multi-region-trail aws cloudtrail update-trail --name <trail_name> --is-multi-region-trail ",
"NativeIaC": "https://docs.bridgecrew.io/docs/logging_1#cloudformation",
"Other": "https://docs.bridgecrew.io/docs/logging_1#aws-console",
"Terraform": "https://docs.bridgecrew.io/docs/logging_1#terraform"
},
"Recommendation": {
"Text": "Ensure Logging is set to ON on all regions (even if they are not being used at the moment.",
"Url": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrailconcepts.html#cloudtrail-concepts-management-events"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": [
{
"Control": [
"2.1"
],
"Framework": "CIS-AWS",
"Group": [
"level1"
],
"Version": "1.4"
}
]
}

View File

@@ -0,0 +1,47 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client
class cloudtrail_multi_region_enabled(Check):
def execute(self):
findings = []
actual_region = None
for trail in cloudtrail_client.trails:
report = Check_Report(self.metadata)
report.region = trail.region
if trail.name: # Check if there are trails in region
# Check if region has changed and add report of previous region
if actual_region != trail.region:
if report: # Check if it not the beginning
findings.append(report)
trail_in_region = False
if not trail_in_region:
if trail.is_logging:
report.status = "PASS"
if trail.is_multiregion:
report.status_extended = (
f"Trail {trail.name} is multiregion and it is logging"
)
else:
report.status_extended = f"Trail {trail.name} is not multiregion and it is logging"
report.resource_id = trail.name
report.resource_arn = trail.trail_arn
trail_in_region = True # Trail enabled in region
else:
report.status = "FAIL"
report.status_extended = (
f"No CloudTrail trails enabled and logging were found"
)
report.resource_arn = "No trails"
report.resource_id = "No trails"
actual_region = trail.region
else:
report.status = "FAIL"
report.status_extended = (
"No CloudTrail trails enabled and logging were found"
)
report.resource_arn = "No trails"
report.resource_id = "No trails"
findings.append(report)
return findings

View File

@@ -0,0 +1,155 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_cloudtrail, mock_s3
class Test_cloudtrail_multi_region_enabled:
@mock_cloudtrail
def test_no_trails(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled import (
cloudtrail_multi_region_enabled,
)
regions = []
for region in service_client.regional_clients.keys():
regions.append(region)
check = cloudtrail_multi_region_enabled()
result = check.execute()
assert len(result) == len(regions)
for report in result:
assert report.status == "FAIL"
assert search(
"No CloudTrail trails enabled and logging were found",
report.status_extended,
)
assert report.resource_id == "No trails"
assert report.resource_arn == "No trails"
@mock_cloudtrail
@mock_s3
def test_various_trails_no_login(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
cloudtrail_client_eu_west_1 = client("cloudtrail", region_name="eu-west-1")
s3_client_eu_west_1 = client("s3", region_name="eu-west-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
trail_name_eu = "trail_test_eu"
bucket_name_eu = "bucket_test_eu"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
s3_client_eu_west_1.create_bucket(
Bucket=bucket_name_eu,
CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
trail_eu = cloudtrail_client_eu_west_1.create_trail(
Name=trail_name_eu, S3BucketName=bucket_name_eu, IsMultiRegionTrail=False
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled import (
cloudtrail_multi_region_enabled,
)
regions = []
for region in service_client.regional_clients.keys():
regions.append(region)
check = cloudtrail_multi_region_enabled()
result = check.execute()
assert len(result) == len(regions)
for report in result:
assert report.status == "FAIL"
assert search(
"No CloudTrail trails enabled and logging were found",
report.status_extended,
)
assert report.resource_id == "No trails"
assert report.resource_arn == "No trails"
@mock_cloudtrail
@mock_s3
def test_various_trails_with_and_without_login(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
cloudtrail_client_eu_west_1 = client("cloudtrail", region_name="eu-west-1")
s3_client_eu_west_1 = client("s3", region_name="eu-west-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
trail_name_eu = "trail_test_eu"
bucket_name_eu = "bucket_test_eu"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
s3_client_eu_west_1.create_bucket(
Bucket=bucket_name_eu,
CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
)
trail_us = cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
cloudtrail_client_eu_west_1.create_trail(
Name=trail_name_eu, S3BucketName=bucket_name_eu, IsMultiRegionTrail=False
)
response = cloudtrail_client_us_east_1.start_logging(Name=trail_name_us)
status = cloudtrail_client_us_east_1.get_trail_status(Name=trail_name_us)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled.cloudtrail_client",
new=Cloudtrail(current_audit_info),
) as service_client:
# Test Check
from providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled import (
cloudtrail_multi_region_enabled,
)
regions = []
for region in service_client.regional_clients.keys():
regions.append(region)
check = cloudtrail_multi_region_enabled()
result = check.execute()
assert len(result) == len(regions)
for report in result:
if report.resource_id == trail_name_us:
assert report.status == "PASS"
assert search(
"is not multiregion and it is logging", report.status_extended
)
assert report.resource_id == trail_name_us
assert report.resource_arn == trail_us["TrailARN"]
else:
assert report.status == "FAIL"
assert search(
"No CloudTrail trails enabled and logging were found",
report.status_extended,
)
assert report.resource_id == "No trails"
assert report.resource_arn == "No trails"

View File

@@ -0,0 +1,131 @@
import datetime
import threading
from dataclasses import dataclass
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################### CLOUDTRAIL
class Cloudtrail:
def __init__(self, audit_info):
self.service = "cloudtrail"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.trails = []
self.__threading_call__(self.__get_trails__)
self.__get_trail_status__()
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __get_trails__(self, regional_client):
logger.info("Cloudtrail - Getting trails...")
try:
describe_trails = regional_client.describe_trails()["trailList"]
if describe_trails:
for trail in describe_trails:
if "KmsKeyId" in trail:
kms_key_id = trail["KmsKeyId"]
else:
kms_key_id = None
self.trails.append(
Trail(
name=trail["Name"],
is_multiregion=trail["IsMultiRegionTrail"],
home_region=trail["HomeRegion"],
trail_arn=trail["TrailARN"],
region=regional_client.region,
is_logging=False,
log_file_validation_enabled=trail[
"LogFileValidationEnabled"
],
latest_cloudwatch_delivery_time=None,
s3_bucket=trail["S3BucketName"],
kms_key=kms_key_id,
)
)
else:
self.trails.append(
Trail(
name=None,
is_multiregion=None,
home_region=None,
trail_arn=None,
region=regional_client.region,
is_logging=None,
log_file_validation_enabled=None,
latest_cloudwatch_delivery_time=None,
s3_bucket=None,
kms_key=None,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}: {error}"
)
def __get_trail_status__(self):
logger.info("Cloudtrail - Getting trail status")
try:
for trail in self.trails:
for region, client in self.regional_clients.items():
if trail.region == region and trail.name:
status = client.get_trail_status(Name=trail.trail_arn)
trail.is_logging = status["IsLogging"]
if "LatestCloudWatchLogsDeliveryTime" in status:
trail.latest_cloudwatch_delivery_time = status[
"LatestCloudWatchLogsDeliveryTime"
]
except Exception as error:
logger.error(f"{client.region} -- {error.__class__.__name__}: {error}")
@dataclass
class Trail:
name: str
is_multiregion: bool
home_region: str
trail_arn: str
region: str
is_logging: bool
log_file_validation_enabled: bool
latest_cloudwatch_delivery_time: datetime
s3_bucket: str
kms_key: str
def __init__(
self,
name,
is_multiregion,
home_region,
trail_arn,
region,
is_logging,
log_file_validation_enabled,
latest_cloudwatch_delivery_time,
s3_bucket,
kms_key,
):
self.name = name
self.is_multiregion = is_multiregion
self.home_region = home_region
self.trail_arn = trail_arn
self.region = region
self.is_logging = is_logging
self.log_file_validation_enabled = log_file_validation_enabled
self.latest_cloudwatch_delivery_time = latest_cloudwatch_delivery_time
self.s3_bucket = s3_bucket
self.kms_key = kms_key

View File

@@ -0,0 +1,145 @@
from boto3 import client, session
from moto import mock_cloudtrail, mock_s3
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
AWS_ACCOUNT_NUMBER = 123456789012
class Test_Cloudtrail_Service:
# Mocked Audit Info
def set_mocked_audit_info(self):
audit_info = AWS_Audit_Info(
original_session=None,
audit_session=session.Session(
profile_name=None,
botocore_session=None,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
)
return audit_info
# Test Cloudtrail Service
@mock_cloudtrail
def test_service(self):
audit_info = self.set_mocked_audit_info()
cloudtrail = Cloudtrail(audit_info)
assert cloudtrail.service == "cloudtrail"
# Test Cloudtrail client
@mock_cloudtrail
def test_client(self):
audit_info = self.set_mocked_audit_info()
cloudtrail = Cloudtrail(audit_info)
for client in cloudtrail.regional_clients.values():
assert client.__class__.__name__ == "CloudTrail"
# Test Cloudtrail session
@mock_cloudtrail
def test__get_session__(self):
audit_info = self.set_mocked_audit_info()
cloudtrail = Cloudtrail(audit_info)
assert cloudtrail.session.__class__.__name__ == "Session"
# Test Cloudtrail Session
@mock_cloudtrail
def test_audited_account(self):
audit_info = self.set_mocked_audit_info()
cloudtrail = Cloudtrail(audit_info)
assert cloudtrail.audited_account == AWS_ACCOUNT_NUMBER
# WAITING FOR MOTO PR TO BE APPROVED (https://github.com/spulec/moto/pull/5607)
@mock_cloudtrail
@mock_s3
def test_describe_trails(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
cloudtrail_client_eu_west_1 = client("cloudtrail", region_name="eu-west-1")
s3_client_eu_west_1 = client("s3", region_name="eu-west-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
trail_name_eu = "trail_test_eu"
bucket_name_eu = "bucket_test_eu"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
s3_client_eu_west_1.create_bucket(
Bucket=bucket_name_eu,
CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
)
cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us, S3BucketName=bucket_name_us, IsMultiRegionTrail=False
)
cloudtrail_client_eu_west_1.create_trail(
Name=trail_name_eu, S3BucketName=bucket_name_eu, IsMultiRegionTrail=False
)
audit_info = self.set_mocked_audit_info()
cloudtrail = Cloudtrail(audit_info)
# Here we are expecting 2, but moto does something weird and return 46 records
assert len(cloudtrail.trails) == 23
for trail in cloudtrail.trails:
if trail.name:
assert trail.name == trail_name_us or trail.name == trail_name_eu
assert not trail.is_multiregion
assert (
trail.home_region == "us-east-1" or trail.home_region == "eu-west-1"
)
assert trail.region == "us-east-1" or trail.region == "eu-west-1"
assert not trail.is_logging
assert not trail.log_file_validation_enabled
assert not trail.latest_cloudwatch_delivery_time
assert (
trail.s3_bucket == bucket_name_eu
or trail.s3_bucket == bucket_name_us
)
@mock_cloudtrail
@mock_s3
def test_status_trails(self):
cloudtrail_client_us_east_1 = client("cloudtrail", region_name="us-east-1")
s3_client_us_east_1 = client("s3", region_name="us-east-1")
cloudtrail_client_eu_west_1 = client("cloudtrail", region_name="eu-west-1")
s3_client_eu_west_1 = client("s3", region_name="eu-west-1")
trail_name_us = "trail_test_us"
bucket_name_us = "bucket_test_us"
trail_name_eu = "trail_test_eu"
bucket_name_eu = "bucket_test_eu"
s3_client_us_east_1.create_bucket(Bucket=bucket_name_us)
s3_client_eu_west_1.create_bucket(
Bucket=bucket_name_eu,
CreateBucketConfiguration={"LocationConstraint": "eu-west-1"},
)
cloudtrail_client_us_east_1.create_trail(
Name=trail_name_us,
S3BucketName=bucket_name_us,
IsMultiRegionTrail=False,
EnableLogFileValidation=True,
)
cloudtrail_client_us_east_1.start_logging(Name=trail_name_us)
cloudtrail_client_eu_west_1.create_trail(
Name=trail_name_eu, S3BucketName=bucket_name_eu, IsMultiRegionTrail=False
)
audit_info = self.set_mocked_audit_info()
cloudtrail = Cloudtrail(audit_info)
# Here we are expecting 2, but moto does something weird and return 46 records
assert len(cloudtrail.trails) == 23
for trail in cloudtrail.trails:
if trail.name:
if trail.name == trail_name_us:
assert not trail.is_multiregion
assert trail.home_region == "us-east-1"
assert trail.region == "us-east-1"
assert trail.is_logging
assert trail.log_file_validation_enabled
assert not trail.latest_cloudwatch_delivery_time
assert trail.s3_bucket == bucket_name_us

View File

@@ -16,6 +16,7 @@ class S3:
self.buckets = self.__list_buckets__()
self.__threading_call__(self.__get_bucket_versioning__)
self.__threading_call__(self.__get_bucket_logging__)
self.__threading_call__(self.__get_bucket_acl__)
def __get_session__(self):
return self.session
@@ -74,11 +75,52 @@ class S3:
bucket_logging = regional_client.get_bucket_logging(Bucket=bucket.name)
if "LoggingEnabled" in bucket_logging:
bucket.logging = True
bucket.logging_target_bucket = bucket_logging["LoggingEnabled"][
"TargetBucket"
]
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __get_bucket_acl__(self, bucket):
logger.info("S3 - Get buckets acl...")
try:
grantees = []
regional_client = self.regional_clients[bucket.region]
acl_grants = regional_client.get_bucket_acl(Bucket=bucket.name)["Grants"]
for grant in acl_grants:
grantee = ACL_Grantee(grantee_type=grant["Grantee"])
if "DisplayName" in grant["Grantee"]:
grantee.display_name = grant["Grantee"]["DisplayName"]
if "Type" in grant["Grantee"]:
grantee.grantee_type = grant["Grantee"]["Type"]
if "ID" in grant["Grantee"]:
grantee.ID = grant["Grantee"]["ID"]
if "URI" in grant["Grantee"]:
grantee.URI = grant["Grantee"]["URI"]
grantees.append(grantee)
bucket.acl_grantee = grantees
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
@dataclass
class ACL_Grantee:
display_name: str
ID: str
grantee_type: str
URI: str
def __init__(self, grantee_type):
self.display_name = None
self.ID = None
self.grantee_type = grantee_type
self.URI = None
@dataclass
class Bucket:
@@ -86,9 +128,13 @@ class Bucket:
versioning: bool
logging: bool
region: str
acl_grantee: list[ACL_Grantee]
logging_target_bucket: str
def __init__(self, name, region):
self.name = name
self.versioning = False
self.logging = False
self.region = region
self.acl_grantee = None
self.logging_target_bucket = None

View File

@@ -105,6 +105,41 @@ class Test_S3_Service:
assert s3.buckets[0].name == bucket_name
assert s3.buckets[0].versioning == True
# Test S3 Get Bucket Versioning
@mock_s3
def test__get_bucket_acl__(self):
s3_client = client("s3")
bucket_name = "test-bucket"
s3_client.create_bucket(Bucket=bucket_name)
s3_client.put_bucket_acl(
AccessControlPolicy={
"Grants": [
{
"Grantee": {
"DisplayName": "test",
"ID": "test_ID",
"Type": "Group",
"URI": "http://acs.amazonaws.com/groups/global/AllUsers",
},
"Permission": "READ",
},
],
"Owner": {"DisplayName": "test", "ID": "test_id"},
},
Bucket=bucket_name,
)
audit_info = self.set_mocked_audit_info()
s3 = S3(audit_info)
assert len(s3.buckets) == 1
assert s3.buckets[0].name == bucket_name
assert s3.buckets[0].acl_grantee[0].display_name == "test"
assert s3.buckets[0].acl_grantee[0].ID == "test_ID"
assert s3.buckets[0].acl_grantee[0].grantee_type == "Group"
assert (
s3.buckets[0].acl_grantee[0].URI
== "http://acs.amazonaws.com/groups/global/AllUsers"
)
# Test S3 Get Bucket Versioning
# @mock_s3
# def test__get_bucket_logging__(self):