chore: update
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
POSTGRES_USER=xaviaota
|
||||
POSTGRES_ROOT_PASS=Ipv0ph9tqVew5zuHdHZAVef
|
||||
|
||||
PRIVATE_KEY_PROD=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQy9rRXNwd0lGT1pzTi8KbVNId2pDdTI2RnVHTE8yUWNYVUt4SHdKR0hoMks2aGRQeDkyWENaRHJGL3kxZ1V5UVhoRFhLMGRjZG5WVjh1eQpWVGg5WlZNU3NsYjM5N1RKUEhXdy9pd2hQT202T1E1Tko0V1c4M0VuWmpvd29XaXJERlViLzMzMmhLMTQxTXNCCkI0RTF1ZkNBZWpIc0c3VllrWlhkOENPL3ZMcFcyMGYrNzFsUzBDZnAzQ3NkeXdZVm5tK3F2alBaa3d6NTFUVXYKK3lNWGpYQkFCb0l0S2xqbXVOeUhZVjRQV0NOVVdsVXlZeXJFbHAyanNIaFE5eGhsL1ZnSisyT21uYzZOdlJrTApMWFc1ZWp1c2c1aEhrUUdEVzlNU0NRd2luc0t3TGdWUjFrbzJXMGNPUTJLYVh1RVF1bmIwTDVxNE9Od2RHUmhtCjlKazA5cnE5QWdNQkFBRUNnZ0VBS2ZIZ1kxNEJjamRTSzBoT2hDdE5EeG5Gb0pXSjF3emd3ZURQLy9YWnVwWkkKb1ZKSEExV2VFMGRWSXdqMXpsRkRrZ3hpNDRXZ1d2U0VaNkg5WW9lakx2N1p5Q1gyOGZGSkc5NHJyNE4wOGcrNApML0N0Y2FZUWFyNFprN2hrQU1BTWlycmd5KysxUkRYajFXL2RQUFJmMHVIdXVISkF2UW56dE1yMktPK2NKWlF3CllMby9aMmJ5QnBEY3ErWlgzbXFldGE5MVpRWmdIM3B3KzYyL2FrRC9LM3lOMXJoQ1plWENCMU9HbjdLVlgrNVcKUVlmRWV5ZzE5QlhxSVQ1VkE2Rm1zci9BdGdUZXphd0dYeUV5clMwdUxSeUV5UmFUbW5EaENJeWdmMCtMOWlLbwpvSFZWKy9JVllpcGJkcTh6ZnhhTXFvclNXNStmM3hyMDBCcXFONlhDU1FLQmdRRDc0a3lQNm03RGVzZGJPMVNICmhtUE0xdVRINUhKQ0o2UmkvRkpzWk4xRXA3Wm1keERqeWx0T0diRmtJMkpxL1d4ZllaVFk4ajNKenhKWjJ5TDkKUk9XQkNYcjVBeVZoeEtSY09RK2ZrZy92M2RFTmdYMWdwQnNDWk1xSzY1bm91b3lwSExIWUlOQnhiRTNHc2twYwpkQnVMOHRDbUl6REtsL0srTEtSVEUxVHNid0tCZ1FEQ3NhaGkxZWo4QWtkaUtDQndqMEdKcXdkdHplS0MyN1VCClZRcTl3ek5OY2paUzlNQTBCMWowZk80enZId1IyYURqcUR6cytaNGgvN2lFbHdDTzFwOXFkbURpdERZblR0Z1YKN1d0RHJ1U0NmN0RVME1xNUZJeTI5a2s1d0FiNEw0Ym9lcTZvNzY5cSszdlhlUCtKQitoMUxCL0FSS1ZqV3hHbwo2dStsOWZYNWt3S0JnUURBWkdmcWZUeEJYNlNxVUxVT0YxNWZkYnlsMmg1VDNZZTRlOVpSS1BIQm9RQzNYemF0ClowRTZkSEdFZFdxVklpRUc4YmdKeFczZ21hUUFzbHNWYk95SUZUK3l0KzV6c1FtQno2dlpURzJUMzYvd1N2b0QKT1JYUVRFQlF3QWg5TlJCL2s0VmIraVI4bmljQ0graWdtYWI0ekcwR21WYUpTR3FIUy8zdjNwSTlUUUtCZ0dkMAowajZhM3JBelljMUY5MTc0c09HLzBUWEhCc3QwaG0yTHpDWmtPTG01ckNaT3ltZWJVK2l2RGtETnNyd2xnZm9YClp6Q2h3Q2JlNVNVSHZKTXFkeEd5ais5SHhkTURjWTNOV0lkaHd5bVpRdFVEa2dYaFpwaStyTkNVT3QvZEdYb2wKM3ZRRVVabXV3dk1QNS9IZ0V1VllxaEtZSjY1eE1TUXFtMmY4R0ZZakFvR0JBS1F5Q3Z6YStXY2RlS2xQSDhTagp4QzE0bUgvZFpBVzRweStSMlZPTHlrQUlqVEVCcUllSFVHVkowaGwxZ3E5WExCY2t2TXlOMXdpTFJZSlF1WVl6CjN6L3Rwd1F1UEtGZVZYL1ltRXd6N1ZqWW5mVUt3YjB4RU53SDdkMUNFVjNyakpMMFp6VTE5OFZnZHFqdXhBNXcKRkdWTHdWYllNWm9BSFdQM2kwZmZPeHV1Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
|
||||
ADMIN_PASSWORD_PROD=7jgEeJsRTwLkOXsisb6pODHN
|
||||
UPLOAD_KEY_PROD=9b5b36eec185ba242ce5afcc6e344dce03a4c855
|
||||
POSTGRES_PASS_PROD=5AiArInxJTbXVlhmpr9g2rQ
|
||||
|
||||
PRIVATE_KEY_PREPROD=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRRE9lZjV6dmd3ekNLZ0gKS0drVEh2eUJidWhrei9vbC9qT0ZLYnlWekdCL1orS0FxZTNCM29NYTZ1U3BGdVVQQjROVUluUDlqZUZ1T3h2Uwo2Qk1vNE9mSlRtU0VuZ2J1clpiWjM0aFNER3FzcnNReWI2dk9kWXhYOW9PTGwwZGc2UnVKMXVPbmMzZCtqRmdHCnlObE1oSVZDajFSQ1JvV1hPYlBYai9naEFtSmtnNDlLTWptN0hqSElucUphZ2xlNHJVczhKbkpObmxWcTJ6aVIKZVU0RU51aHZHN2dQSUYxR3lWRlJ6TW1wL0N3MWc1Zi9pVTNKQ2RSRFhYS3dUOG93UDNIdDhHMVFWWnQ5MDN1KwpmQ1ZtK2NNUUQ5cHpJdHE2c3Z4bmI0RUF4K3B2TnJ2d1FSQmJKcFBqWit6eVFXSlJGSGxEOUszWCtVYWp5UjlHCjg3ajFNOG9KQWdNQkFBRUNnZ0VBSjI5clp1WEVaTGJoaU1SeUZTSFU3aytsZVNPenppL1gzVFlVZTdQV05sODgKQm9EdUZSb1Boa09ZdXFiUnh1WVRjaTFHWkFZR0tVeUd5dWhpU0d0ME9hR01WenZyYlFpZ2lDcEdXZzFmRG00dQpzTWRkVkFobGJxTjVpbDZ4VmxRUFcyczhmYzN5NDZ1Z1lHTmFCV0JHSjJsQ21Zbm9sL0tBWThwdWhMZDAzV2RmClViMzF0S1pOOXkyVkFnSUZIUnh0Z3NBY1ljem5HOXBEQ0JhTUlxcjhkZXc2THArQWFVbVptbXM4TklZNkQ1dlkKbHBBSmhVejdnMXVMcXJyTnV3T2J3aG9kanZHVURuSG9VWHlaL3dyeEJXb3F0YTNibnBTNkt1NHRoeTdZajV5Rwp6RytVWVgwL0lCeFNrOHQvS2dXU2MyclExVUhUaXlHMlM2YzlZNFdONVFLQmdRRG9na3Q5bFpNMEFqRzl4dHcyCi8wVE1hUHJWTWt3ejVQd004NEU3U2krV0pYeC9UNzIzQ2xER3lkMGVKU0JJMENBUDZHbnU2a1ozRWk4Vy9QamUKK2NXSXB3dWJSSTBvb2hKbG45VEV0OW9jTytyTitYUHcvbnFRNFk0bnVGSEJaTUxRa1RvbEhIZzNqRVA5YlYwNApaY1B0UkFJSngxbjFibXlmak1ESlBZOE85d0tCZ1FEalZtTFBKNXUvNEgyZWRHQ0JrQXIwY1Jia2xzT3U3ekZqCitlUEhydVp1ZzNKWThVRVdMT0xWV0Q2NXNuL1hhaGcrT3FmWXAvRGs5NXRqWjhlUFhhS2RvVllDaERrZXFBTE4KMnFibHU1bktjdmo0d3plN1p1MTlCRDlMOWhmTWxtei9pYTY3bkR5c3hZTDd1Z1pIWVZSQ2J2b3V2K3pMVHhDQwpwUC9qQ3JpdS93S0JnQnFLYlFXYjRsN0lkVmNkY1J2U0RjOXJtRjVuekcwTUVYMkUrbGFEUkdWV1NCemNSR0sxCmliejNaQWRCNDZxTUgyelJjZTQwemZCZm9nSjB4QS9uNFJXVlErem9hVmlmRDR5TUx5UjVmQ3RHNTZtMWRQZW8KaE9NdTdDUTZsUktIWnZtWjZsSThLcmpraVFwUHhwbGVmSHdFREZ1RFp4QnVGM1lyQWdLMnhUTEhBb0dBSWVodApublpLN2NpZkUvWkp1cWx3YU50QlRlRUsyTm5HWlNJTmptZ1E2S1RrZGsydHBwQVZXVHJyU0NsT2xpTzE3L294CnRNZi96emI3VDAxWVg1TFl3V2doK3dnYnVPM1lWVG5xc05OVmNmUEtITmlKUklOcW9yM3lidk1LdzdJZ0dkRkEKWmEwOUE0d0dLR09yc2hQYlRIbGZDWUFJNHA0dFdUOGhCNDArMUtNQ2dZQWY5RkVVTDBZRDZKblNiK2R0YjRQMQpnVVUwNDZnc0Ira3l5anNmb3hXMEVQM3hQZmVCMCtmc0kzeUhuOHZHQWRNMFdFVW9FeENqZzVaY0Y4bXUrcUpVCnpSQTB5ajM1SnkvU0sxY2FBVjZiMDRiUFBLRmYxT3pWV3laR3NzMVlVVy9xVlJsRlQ4RXFDYTc5NGlaMHA0NVgKR2ZPUEkvbzMydzhKUEY2WUdrUXEzZz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
|
||||
ADMIN_PASSWORD_PREPROD=kc2To8hAm1AN00umhtfZ91rt
|
||||
UPLOAD_KEY_PREPROD=133a37f8498e9a828a8050e7e7e60b3553fef2d2
|
||||
POSTGRES_PASS_PREPROD=uc1zmnNPWu9WWA6eCAxAiJ
|
||||
|
||||
PRIVATE_KEY_OMNEX_CLI=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRQ1l4L1Z2c3pxb1BtaHUKUjRWVnZEY2drVE81TDRwZ2VwdHR4ODZCT045ZDBaZ01HNmFpWFFJZXZhT1hLUjlIMW1qeURyRk95NDB2VlNiKwpoK1JwRDJ2Z2JCTmdaTFJmeVE0V0E1U3g0QXk1UVU2V0Y0UGpQdC8wK3pQcjIrcXBDMDVrZGJSSXhMVjdXSnkrCnl4cCtFTjAxbFlXOU5FdWxqWEJyN2ZNRldKYmlXTmRWQjYzY3N5d2YzWVd0WXlvNHdCZkFTT3cxemVacWdCclMKYStsT0FOOWthYjBsWnpqcXVZbDFEbEE3bzJnZVlZcDNOelNpTU1GbXpGUm1NNmhDRzM1VzE1azlaS0E5dmZ6TQpGSE9kaEZZQXRFR0RnUDhveU56SCtrOFdkd3NCM1MzRnhtK3V3aTA5OFV4dDd5UEQ1LzNGb2lIYytWejR1N2FCCmszYlZDZ0w1QWdNQkFBRUNnZ0VBQk5NdmQ3NGxsSXNOcmRUb1VRbGdoWHRuSFNVelFJSFlGSVJEd1Q0NjcwOHkKRng0aVJJZFMxMXZHYXdkNkQzWmJTbHRjZzJMbmlWYXVEeDlheWV3YUlhOFJhMnV4YkJoN3IzRGdiUlVvc0dBNgpVNEJpM1REVVE3djhCZW41aHFvK3lQLyt5QnV6NlFxRzFHVGM2QjFrejdTZEJ5NkdmZVZzMGJCdmlvd2pqZy96CkZicy83LzJpbVRtVUN6V29KOW5za1BLOUdmaU5ONWxiYU5NSnpJVGRYQVkvVC9uM0N2WXBLRTlMMlZJbVMzNWgKNkh5LzgzdE5GWWhvYk5xVmtlRjFFc1R1OURqVVhRUzl0MVFCZVd0a0IvbkRWQS9uLzFiQU56UE5SdEFNZUpTQgpQNkZLTkRwSzJ1cWJpT3dQQnZMaDdtQ0I0VVI1elpSUzFJN0RHRjBjc1FLQmdRRFdZeG55NUEzeS9oUFl5MFNmCllYSzBXaTlsbGc4dTgvYjBGUFJoTytQcHoxYVFLdGJVYThWL0FCRXdHd3FXVS9naGs1L21LNkJsYWg3VWRBVEEKdDVEM1AvTU1hS3JZNHZ6NlV2NUVadG1zWU5mTjBFLzNPTWtHZ3pzWEIrR2ZnaHdjNlcwbnFpcVcwK0tMYmJYZgpDemM0c25nWW1YZE1ESE1QNUZXUm9VRVNLUUtCZ1FDMmI2Y3lTOUtIVmc0dnRlYkdlb3ZkMWZMdHhTV294WlJKCmxSMWVIM0RrWWZaMEk2UU5NNE9STHdNb0JNUmlpUTVwbnhyQ1plV0VJN1BjQis1QmFTR21lOGs1TEJqenMwZ2MKWEo3K1RIVXUvT3FIeFA2TVdpMGNWc1cvQkxIWDlWTE8zY2JZQnBlYnRRTTRoWjJrcEN0WVRnTDhWeDE2U0o0TQpjTHUyNVdta1VRS0JnSHdQeTBOZ3I2WFBYR0xYZGpXWktjeXVaOEVzcmtxb2lWOUVCUXVEb2VkNndZZ2FFa25JCnQvZy9iTExlNHlpTkhRa3BLWjREM3dUdy9UaEtqem1Ld013ajZKMWtzSUZrRWFzMmZjZk5rT2VNL3pVdCtDaUQKWnVscTViWDFMSjZtcDJnZnVMZjhaRHIyRUtKSThiZEtBVUhCSFhoMnpxOW1nT3RRWWlndEhiL0pBb0dBR2JpMApRZ3NlcTZZZWFldWpsZWhCUUEyaUtwUFpNMnBuOWRmb1JCVDZ2Ymh5SHYzSGEzVVAzN0JaS1pOQ2ZRSUYzaTVvCmYzQU9aQXNFbU1rbGRtTjBNeHpReWhiRmpkYUJIRDZTekJmTGpjeHE1S21KbWhBOHUvb2Y5MGFhTEhDU05FaGcKZUtTMVMyYkNCWDRSTTkxUm55bWdraU5XTEorQWFSQ2I1cldCcDRFQ2dZQklDeVBkeTVCNCswaVFhVnVkdG1wcgpPenR3R2VxaGI4WHNLcU5iNjRUQW52TWZNWnE3Y3lWYTNYZ0EvcVZtZVBhQmg5Z3JWeWxlUG94V3RIN09xL05MCllnakxmQnUvVk5LQWlIaXJkTVAwTEhQYzFaUVNYS09EYU5RdmdXazBTYnMwN1d1SGN5RG5oM0E3UzFlMmdvTXYKRW94NkdPVlRhRnB0cDYxYVdHN3dWdz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
|
||||
ADMIN_PASSWORD_OMNEX_CLI=XamoflYJO76jU3WkX074EMfe
|
||||
UPLOAD_KEY_OMNEX_CLI=653f61211812ea0c4ce7b763db4821a417549e91
|
||||
POSTGRES_PASS_OMNEX_CLI=h7z47IGFzsNhXWJtvwOXxWjD
|
||||
|
||||
PRIVATE_KEY_OMNEX_ADMIN=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2xBd1laeWtVSXRSNFYKQllQN1MyM2VXNFk1L3UxdFlpa3l0b2xZaithd3FUUlBCK09mS2EzV2xCRkJyS1I0VWNYUkRTR1VrQk9HT3g2UgphWklyd1o4eVhCbG1OeEl1TlIvVDE1cW9DMzI3Uy9jUmcyL0grQjJVOWtqTFhSQUM1WVVscnZ4cXhBUFFRdytOCjFYNW8wUWhSVi83Nlc3T2lhaURocktjRWNWUjJldkU3Q3BMblRTbU8zS1FQSDM4NEg3alYxN0o4YVRKczR5QnEKVUtPSGpGT05rUWNMbHFLOHE5YWdLQTl6c2RKVTFCTWNCK2oyVXVBcGtEc0pRWG9nVFRaV1poQzhqTnlCVFhINQpiZWxpVlN0M1FPdm5zTDFnRFd2R3ZyTXRiUzQyRDNGMEwzNWNoM0I0VG5ZaDJ5Y3VsSXFtZnlMVXRBanc5dENjCkpESk1LK1dCQWdNQkFBRUNnZ0VBR0lqOUVMdWZEUjdsQktvbnlDY2NoSG5tV053N1VFN09vbUE2eHNUS3Y4ekEKM3RsazhtYi9sbzY0akNQUDVxWDkvRW53Q3Nkclo4T0ZsWWgxZC9VWHB5VERrQXV6TTlTcFVWWFhCSktFMU9NRQpMd0gwam02cGMxczFVcXVaTkYwTk1aKzFuMmhheXM0VjAzaUJqYk03QWVGTUk4eWJ4cFB5Skt3SHYyVGNjcXN5CjY3QXNZazU2cm1PQjhsbGV0Ni9kOHp6cXFPbHZBV0xxWmZHSnViM3hRbCs2U3ZoNkM3SDhlTW9XdnhpaVZIdDMKUTkxamF4L0xUYzU2WkFBd3IzczMrRGp1Rit1cEs3NVRML2hyeWlYN0g0cHRFblE0UllIWFVVNHhZR3lnQzFkWQowZUllcVZCcmFlSFNzTjR0QURHc0FXaHpkajZ2SmtJOVpYcWhaUHFqbVFLQmdRRFRGcGFaMUcyRVMzUVpReUVaCm9DUVJCQTQxNy9sVCtWVENuZ0x2V1Q0V2pRM1BWUDZNWVFaQXZIOSswVEEwaDFKUzFFTDk0cVR3aVFrR1VnZXEKRXBKTG40aTFxTkpWNHkxU1U3VENpL1N5OWVYL3E4RVFMZDBNUFl1eGpvRTFHdGgwODRCTEFBUkd2bWVGclB4MgpwdmpvSDJjWENhb2NoOHlsWHRHYjl6azZUUUtCZ1FESUhzYWxpQ0F1WUZkV3ZvSWVlZGtEYVU5VDlzc3A1TnBWCjEzRzNuWFJzYWN0OFEwaFhNTjBBRWdNdngvSXFEOUlmRmFQQnJFOUlYbDZxQ3lrSW5IQWZ1ekJkVThJUU05OGgKWXZwMGhRVlhnaEF0blB6VkgxTEE4aUJTbktZMTdOS1ppR3cvU2sza1lCWnk2aGg2QWRmc0R3eDJ3RlJUalpzWAplbExkek1IS0JRS0JnQUw5THVTK3ZVNUNRMEtZVUwrc0pOYVkyQnFyYXp4bEsyNFdEclNVN3V3aTB5ODhaTjVZCkFWNnBUS1RmRVcrcyt0MXhaQUYwVkNHVHp5em52SFFNRDBaQzNBeTJkMFFBRTRRMjZ6aFdBd0VGdWx3RWNwZG8KRHRrOWhkemR4N2FZeXJmZE90UFZERHAwTjhNTTBBWmVGdWd5aWFNcDM5N0hqWDVKK1czYjhsT0JBb0dBZU5hRQprcTJvNlBnSnBQcHlMWjdiMklCOWd6RFJxcUZpNlZTbmVQMGRzTjBOSHlLdGl1Q1pnaEplVkRyQzBuMHRIdEg5ClNOUVlKNVc4U2FkbGhxcVgxY0U5U2cxcWNFZ3lJc0hXaEFkU20yMlJBVW43TFdHMW9ZWlMyQzk0K2JUZjBnK2IKZFhnVG9xUVd5aHJxM3JsQVBXMDAxSHY4TDF1aFc4TGtTTVA3L0ZVQ2dZRUFxRFo5eEI3d3h5Nzc2VWs2SUFBdgpEVkV4TTZhQjYwMVJkd09pUlk4VTFkU3ZoRko5TFJHM3g5VVl2NHdKa1IvZFZMTGJlVnVvTHJJMEUvWERqQmhMClp6dzZOWTYydUY4K2tmTUFqOEpSNkx1azF0WWtYM1lIN2tvM0loMDFSVWMyYXNpU1dkbHAyUk1BbnNodGk2MHYKWC9OdmU0ZFhOOHZDbC9maE5kbEJuVFE9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
|
||||
ADMIN_PASSWORD_OMNEX_ADMIN=v8dkMb6uAeiGMHEczI3IFphl
|
||||
UPLOAD_KEY_OMNEX_ADMIN=144225999c44d936caf42adeecb74a419b7d91c8
|
||||
POSTGRES_PASS_OMNEX_ADMIN=We9ji9I75kIfZJy4QjTqJ4Rp
|
||||
|
||||
PRIVATE_KEY_MOBILE_PROD=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRQzJmb0IrVzA0TWtYSXkKQU5yVWVmZkR5MVZvc29aNlVnRC9GV3Z2ellZMFBmMjlrV3NhdEJUZnV3Zk1RUGc0VlZsMk9YY2k3b0wwMVN0ZQptbDJaT01iK2tGMGZuM2RhNWRSRkZQVkVwN0tJK3kxYlJTQmN5M003OVk4b29uMlExVGNvb0NlYUtWWFZ4N1lrCmc2L0dQRExMLyt0STBIVlhkdFF4TURyMUVjQ1p1VG8yZys5MW84NE1MWEpYaXBIa3VTNjRVWUFqbFBySkZjcTkKalI0VEo3ellzTDAxUDlmUG1va0FtMlZjMkI5ZEcrQmpsU0JYcDdveUxJT3RNd0MxekFDa1dpVSs4MWQ1OU5NWgpBdjA0RkpFTkIvUDZ4ZUczV3FNL24vOUlOR3kyWHZkOXdrVXVaRU9iWkppTmg0Q0w2Wld6aEpQQ1BxMmNIY3FyCmNIeFhlUnR4QWdNQkFBRUNnZ0VBQmRYS3B2K2xZYnArMDhGNlBjUCttNWlUVFFzMFNKSDVyUzVTcXJMQUlVaUEKSmdFUjB5TkI3WnR0MXVhVzZUbzFaNVhUek42aWVsQ2NsK1JJck44SFlkbGMyT2VBNmZ1aVMya3RwSG5XT0JJTAo4N2lrbmNMMWxzdTF2WjlnYzNLVjFtczBpUFhjWkQ4RUFHdmpnV3VvVmlTRE1ldFViUzBFQU9TaFpmZWcyR2xwCmJhbllxa3h4U1RVbUpocmYyc1dETDRRRUtMb3NxU1BqWWxRSmFMdFYwR3ZSckFwTzFNbGxyWFB6V05SUGRrUHEKSWdjQWxGU3drZzZ3OUNHSmowSG1YQm1DcUFMM0dlczRTWVBrakkvSThESEFxRHZIUGpuRVdtTGYxeXVySVFHawo2aGYySUgwK2xTcDVYd29wZ1hnQlYvRUVxN3VRM0xuNkIrcUF2MTVCU1FLQmdRRGxqQmR1bTd4VXdweVFRSmkrCkFzb0daZHk5UW5xaHltV3VBWUw3Rlh5SXlER0UyWkZ6djc5Q2RVV0t0Z1VmNWxtL0htNDN1T0xxWjBQYWRnT2MKUENFTFowOGdRSmFJTXBLc0ZMeXdycmpaQUJGZzZRRGlwTllpN1AvSmRJaldWaTcwQWNoekJkNjVWd1hzSWlKNApBdGNJRmxuYUNXeDVwb3VNR0MwR0EyWHM5UUtCZ1FETGhrcEJyR0pOU2dCZ3J5bEwxUS9Yb0szYVRqZk5yOE1xCjBYMVBqSU0vMG5zdFlWMUp1VFpxaW1kSmkzQ09va1BDMTZ5YlhzZ2ZTdmZ1TTBiaTRiS1VQUDl4aVRDaCtCNkMKRUNueXo1QXdnbWNyU21iUEQzWlNBTDhYTi9mSVlhNW5raHBwbHZuVmVMWVVRbWRaVlNaOVFBZ2pmTTcwTnJTcAorUVlzKzYvbkRRS0JnQmdmUTZzYzlJcVh0T28zejVWUUo3aktaWXNCL0c4YUhOdFBUMXRoWTZmZ0djd1RMUVRUCjVwNXVRbk9VTXkrcC9rNW1QVURmemdEQi8xQnF6SEJUcFVsVWZ1MHR2R1B0cEpvdXg3RnRld0ZhejNpMm82QXQKb0U4bFJxLzk3ZzlRR1RrTkl1cHJlSDlUbE15K3ZZNk5CZG5VdDNhQlBRUC9xM1I2YWVqUzNKZ3RBb0dBREJJdgo3MHM1SFZwcFFWTENRVVFteDJRNVMzeGVKTzNxeG14Y1lVNTFMbURlZTJlTjcrUnpUTWhlcmViK1N3ZG11M1N0ClFSM1RBc0FnaWxreU9CeW95ZEFBV295SGZPUEpjMjlmL1kzQUFKQ0xhKzdpd0I4cnBsdHkrZjZRektxMmZHQVcKL3ZJOGFTUGFzTUxyZkVubm1ZQWx1MjBZblRWdGE3TmdZV1NnSHpVQ2dZQXNiT3JPbHhEbWpjZER4N3l1YmNSOApTMTBTalJzNkhMQ2ttV3djV2lpbGpXelpaT2pyVytmcThyTzE2cEp0K2IrVjZtVVRScXplTG5xTVVvb3pjQXAvClJSS0xEVC94aFVZeHM1TWhGUlBTa3E3YXdLV3NpdFVheVEvaW4xaEtkUDhlZzJ6Qzg2eE5zNzhERE1MWUxsdWEKbm9NMm0yL0NxcEZySHN6cFhnQmgrZz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
|
||||
ADMIN_PASSWORD_MOBILE_PROD=zjRFjKEnu3dTB4Re2BUEWj0p
|
||||
UPLOAD_KEY_MOBILE_PROD=f7213601d353955c65eb2790fecadc4a41d3e9f9
|
||||
POSTGRES_PASS_MOBILE_PROD=LbYA14vuUG937x9wvOKErN
|
||||
|
||||
PRIVATE_KEY_MOBILE_PREPROD=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQzl6OCtueTRLM1JJZHYKNjBKb3o4K0x2Q2J4QUVwL3FxZEtjV3Y2bDRlWXpmRlptUklTVHUvZzE2Vk9YQnduOEQ0RE4rL1EvTHQ2NGdaMAo1VFhkdGRsRklNL25CdGRVNGttM1Y1KzBteUphVTVQcDZFYXVkazRkMzYxNldkT2VYOTladU1tcTBPbUdtM2R1CmJRTi9GVEtscTBiODBDZERWSVNJc3pKVENJRk9zaGFnc1ZENWRzTHBRdWVncCtGRTZwZVBXSHQ1SDdFSW5ieEgKNHU0SDZRbVR3U0VQd3ZnSm1xVTcvRldUM2Q0M2VnbTVNY2xoUHVOWjkvYTVQN1ZKek5GVysvaG1hZk9kVCtCUQo5Y2x4Q1VoS2VhOW9iLzd3MkR4bjFPRU1vT2VlZDRta1ZjQ0VXcFRyWmFOdjlqMnROdjBWWm1leG9GQzROa2FuCjZjRlVkeFBsQWdNQkFBRUNnZ0VBQkdSRmdyVmVZemZPVzc3a0VpRnFHai9uNEJXZCtQdzRLS3h5ZWlGOWFBSUQKMCtDTEh0U29ENFM0ZE1BRUtzRzZrdXA5YUZGMS9sbUh4V0ZKbTIvMHIwNXd0bFlJaEw5RG1MUUorWmR6OWx1NQpXcnAwRlluejNyOFR6WGFSN2VKNy9DQjBFZzYwQ0lMdnJYb1BrRkRXUGllMkJHMXdnSFNqMHd5S1hUVEtLOG1WCmFrVGl4VWFkNGNMV2NoOUhZeXNEM1MweGRtRnNaZi9tQ2VWb2JpMWlVSlJnZDFSeVRCcmFlaEZSc2RHMlRBN3cKVTdYaVlDSlgrbG52SEhGR0xwS2pUcGtydUhpU3ViaGE0MXhMWU8xR2xzMzAvb2dYbTZtZHR1b2xvQ1lOM2RGWQpBS3UweFJkQ3Z2SThNYVFjaWtjVWdNQ2N1L1BObE5TaHNyOWxoZHA1OVFLQmdRRC90UTZ0WTFqczJ5dXU4UUZUCm5zaWlNamhFajhHSHBwZEtyNTNpYWdpdkdreWhSOTBoK0pZUlR4RG1KZnk3Mjl5VWorTDhsWlJ3dVBJczlNYWMKUXFzeTBDZ2JId25SK1NEdFhSUkVxTkZKdHg2SkxCNGw4THZMa1lhZFBaQnN5SGZUa3RBcXJjZjFMM0Z1WmFnTwpoa1hORW44c2VGWGh5MnMxdE1XdlVNY2tNd0tCZ1FDK0IzRHd4UmtWRTZjeWNqNWJRejV0d3lVUEZYT0hFVVpzClZ1bjk1aXFPQjR4TDFNeStaTW1kWktkRXpncjc1MTEyZzlJRmRtWU5xalhoNXhMMnVhdG5SNmxTY3lvOTlIbk8KWFd5UWtBNm9MYUVVQ0QvdG80NHEya1M0STA2MytURTFUbWZNRVB0d0NJeUYzNmNnU1NwMUZ5U29zck05NmYvdQp5RXg4dWx3UGh3S0JnRXVvaDdvM3ZmVCsrRkxmemxPdWxKQ2hLVkxuOGd3djkyM0t2bjFpZ0VWSEU4dFlsdzVaCkhoWHEvZ1lLMFZmSU91RGVIZnc4U2RwOGJuTnN6V01vdXJ6aktDcTJLVkt1VXRiSWhwbTJGWTZwRnVqaFc5NC8KOTJESkl6cGRIK0dBQXl4ZnNTWWZteDFwdHpkQjd0SzFXZXZPZ0VCcExvZ0xoeUl4NVpmMDM2ZFBBb0dCQUxnRQpyTEg3cTlabE9idjROYnBhMjlHZTcvd3pZYlNpWGZmOEVvdjBuN0Q2VGpvQTBHUnZaVzR0VW0ybXVheEcySk0wCkErV1dTaTFoYnR4YjBGbHNZemVZaGJkNkhxWHdZU1FlQkV2WnROZlhsK3kyTUhJSHVGeDNFYVRmVEJPTUZNTHkKTWtIREVEbmxqVzcycmZqdDRWZVNHY1hqaHYrbDRsbHhUZUJhOTF0eEFvR0JBS3k5WGxUUXFqTW1MRjB3cGpVMApxS1pLRmdFRHJUNUI3b05MaXlzNWlWK2dGbEVaeWV0N3VGVVBWdHV0TDMzTWttSkFIMkxXRjRVMTBTc1ZIWUE0ClQvUEtEMzhNZGhRR0U0RytIL1psSkpXWjhLdkM1MFZBWTEwanBKTnlXa0JicjlBU0lYSVhTUlp2Zi9IYnNraUIKTElHSUJ3WVF1ZjI3RHprRk9WMmRMRFgyCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
|
||||
ADMIN_PASSWORD_MOBILE_PREPROD=thxz419zFmNdh547A3vrQE1v
|
||||
UPLOAD_KEY_MOBILE_PREPROD=8f65a01120509b149f3a781b139391272519041b
|
||||
POSTGRES_PASS_MOBILE_PREPROD=uS6K8V54JeUHOYsbEDdbIt
|
||||
@@ -0,0 +1,212 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: ota-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_ROOT_PASS}
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_PASS_PROD=${POSTGRES_PASS_PROD}
|
||||
- POSTGRES_PASS_PREPROD=${POSTGRES_PASS_PREPROD}
|
||||
- POSTGRES_PASS_OMNEX_CLI=${POSTGRES_PASS_OMNEX_CLI}
|
||||
- POSTGRES_PASS_OMNEX_ADMIN=${POSTGRES_PASS_OMNEX_ADMIN}
|
||||
- POSTGRES_PASS_MOBILE_PROD=${POSTGRES_PASS_MOBILE_PROD}
|
||||
- POSTGRES_PASS_MOBILE_PREPROD=${POSTGRES_PASS_MOBILE_PREPROD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./init-multi-db.sh:/docker-entrypoint-initdb.d/init-multi-db.sh:ro
|
||||
networks:
|
||||
- ota-net
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
ota-prod:
|
||||
image: xaviaio/xavia-ota:latest
|
||||
container_name: ota-prod
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- HOST=https://ota-prod.uber-stup.club
|
||||
- BLOB_STORAGE_TYPE=local
|
||||
- DB_TYPE=postgres
|
||||
- POSTGRES_USER=ota_prod
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASS_PROD}
|
||||
- POSTGRES_DB=ota_prod
|
||||
- POSTGRES_HOST=postgres
|
||||
- POSTGRES_PORT=5432
|
||||
- PRIVATE_KEY_BASE_64=${PRIVATE_KEY_PROD}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD_PROD}
|
||||
- UPLOAD_KEY=${UPLOAD_KEY_PROD}
|
||||
volumes:
|
||||
- ota_prod_releases:/app/local-releases
|
||||
networks:
|
||||
- ota-net
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
ota-preprod:
|
||||
image: xaviaio/xavia-ota:latest
|
||||
container_name: ota-preprod
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- HOST=https://ota-preprod.uber-stup.club
|
||||
- BLOB_STORAGE_TYPE=local
|
||||
- DB_TYPE=postgres
|
||||
- POSTGRES_USER=ota_preprod
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASS_PREPROD}
|
||||
- POSTGRES_DB=ota_preprod
|
||||
- POSTGRES_HOST=postgres
|
||||
- POSTGRES_PORT=5432
|
||||
- PRIVATE_KEY_BASE_64=${PRIVATE_KEY_PREPROD}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD_PREPROD}
|
||||
- UPLOAD_KEY=${UPLOAD_KEY_PREPROD}
|
||||
volumes:
|
||||
- ota_preprod_releases:/app/local-releases
|
||||
networks:
|
||||
- ota-net
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
ota-omnex-cli:
|
||||
image: xaviaio/xavia-ota:latest
|
||||
container_name: ota-omnex-cli
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- HOST=https://ota-omnex-cli.uber-stup.club
|
||||
- BLOB_STORAGE_TYPE=local
|
||||
- DB_TYPE=postgres
|
||||
- POSTGRES_USER=ota_omnex_cli
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASS_OMNEX_CLI}
|
||||
- POSTGRES_DB=ota_omnex_cli
|
||||
- POSTGRES_HOST=postgres
|
||||
- POSTGRES_PORT=5432
|
||||
- PRIVATE_KEY_BASE_64=${PRIVATE_KEY_OMNEX_CLI}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD_OMNEX_CLI}
|
||||
- UPLOAD_KEY=${UPLOAD_KEY_OMNEX_CLI}
|
||||
volumes:
|
||||
- ota_omnex_cli_releases:/app/local-releases
|
||||
networks:
|
||||
- ota-net
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
ota-omnex-admin:
|
||||
image: xaviaio/xavia-ota:latest
|
||||
container_name: ota-omnex-admin
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- HOST=https://ota-omnex-admin.uber-stup.club
|
||||
- BLOB_STORAGE_TYPE=local
|
||||
- DB_TYPE=postgres
|
||||
- POSTGRES_USER=ota_omnex_admin
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASS_OMNEX_ADMIN}
|
||||
- POSTGRES_DB=ota_omnex_admin
|
||||
- POSTGRES_HOST=postgres
|
||||
- POSTGRES_PORT=5432
|
||||
- PRIVATE_KEY_BASE_64=${PRIVATE_KEY_OMNEX_ADMIN}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD_OMNEX_ADMIN}
|
||||
- UPLOAD_KEY=${UPLOAD_KEY_OMNEX_ADMIN}
|
||||
volumes:
|
||||
- ota_omnex_admin_releases:/app/local-releases
|
||||
networks:
|
||||
- ota-net
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
ota-mobile-prod:
|
||||
image: xaviaio/xavia-ota:latest
|
||||
container_name: ota-mobile-prod
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- HOST=https://ota-mobile-prod.uber-stup.club
|
||||
- BLOB_STORAGE_TYPE=local
|
||||
- DB_TYPE=postgres
|
||||
- POSTGRES_USER=ota_mobile_prod
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASS_MOBILE_PROD}
|
||||
- POSTGRES_DB=ota_mobile_prod
|
||||
- POSTGRES_HOST=postgres
|
||||
- POSTGRES_PORT=5432
|
||||
- PRIVATE_KEY_BASE_64=${PRIVATE_KEY_MOBILE_PROD}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD_MOBILE_PROD}
|
||||
- UPLOAD_KEY=${UPLOAD_KEY_MOBILE_PROD}
|
||||
volumes:
|
||||
- ota_mobile_prod_releases:/app/local-releases
|
||||
networks:
|
||||
- ota-net
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
ota-mobile-preprod:
|
||||
image: xaviaio/xavia-ota:latest
|
||||
container_name: ota-mobile-preprod
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- HOST=https://ota-mobile-preprod.uber-stup.club
|
||||
- BLOB_STORAGE_TYPE=local
|
||||
- DB_TYPE=postgres
|
||||
- POSTGRES_USER=ota_mobile_preprod
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASS_MOBILE_PREPROD}
|
||||
- POSTGRES_DB=ota_mobile_preprod
|
||||
- POSTGRES_HOST=postgres
|
||||
- POSTGRES_PORT=5432
|
||||
- PRIVATE_KEY_BASE_64=${PRIVATE_KEY_MOBILE_PREPROD}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD_MOBILE_PREPROD}
|
||||
- UPLOAD_KEY=${UPLOAD_KEY_MOBILE_PREPROD}
|
||||
volumes:
|
||||
- ota_mobile_preprod_releases:/app/local-releases
|
||||
networks:
|
||||
- ota-net
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: ota-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./certs:/etc/nginx/certs:ro
|
||||
- certbot_www:/var/www/certbot:ro
|
||||
networks:
|
||||
- ota-net
|
||||
depends_on:
|
||||
- ota-prod
|
||||
- ota-preprod
|
||||
- ota-omnex-cli
|
||||
- ota-omnex-admin
|
||||
- ota-mobile-prod
|
||||
- ota-mobile-preprod
|
||||
|
||||
dozzle-agent:
|
||||
image: amir20/dozzle:latest
|
||||
command: agent
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
ports:
|
||||
- "0.0.0.0:7007:7007"
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
ota-net:
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
ota_prod_releases:
|
||||
ota_preprod_releases:
|
||||
ota_omnex_cli_releases:
|
||||
ota_omnex_admin_releases:
|
||||
ota_mobile_prod_releases:
|
||||
ota_mobile_preprod_releases:
|
||||
certbot_www:
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
for pair in "ota_prod:${POSTGRES_PASS_PROD}" "ota_preprod:${POSTGRES_PASS_PREPROD}" "ota_omnex_cli:${POSTGRES_PASS_OMNEX_CLI}" "ota_omnex_admin:${POSTGRES_PASS_OMNEX_ADMIN}" "ota_mobile_prod:${POSTGRES_PASS_MOBILE_PROD}" "ota_mobile_preprod:${POSTGRES_PASS_MOBILE_PREPROD}"; do
|
||||
db="${pair%%:*}"
|
||||
pass="${pair##*:}"
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
|
||||
CREATE USER ${db} WITH PASSWORD '${pass}';
|
||||
CREATE DATABASE ${db} OWNER ${db};
|
||||
EOSQL
|
||||
done
|
||||
@@ -0,0 +1,168 @@
|
||||
# =========================================================
|
||||
# Redirection HTTP -> HTTPS pour les 4 sous-domaines
|
||||
# (garde /.well-known/acme-challenge/ en clair pour certbot)
|
||||
# =========================================================
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name ota-prod.uber-stup.club ota-preprod.uber-stup.club ota-omnex-cli.uber-stup.club ota-omnex-admin.uber-stup.club ota-mobile-prod.uber-stup.club ota-mobile-preprod.uber-stup.club;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# =========================================================
|
||||
# ota-prod.uber-stup.club -> conteneur ota-prod:3000
|
||||
# =========================================================
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name ota-prod.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/ota-prod.uber-stup.club.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/ota-prod.uber-stup.club.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
client_max_body_size 500m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://ota-prod:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
# =========================================================
|
||||
# ota-preprod.uber-stup.club -> conteneur ota-preprod:3000
|
||||
# =========================================================
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name ota-preprod.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/ota-preprod.uber-stup.club.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/ota-preprod.uber-stup.club.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
client_max_body_size 500m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://ota-preprod:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
# =========================================================
|
||||
# ota-omnex-cli.uber-stup.club -> conteneur ota-omnex-cli:3000
|
||||
# =========================================================
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name ota-omnex-cli.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/ota-omnex-cli.uber-stup.club.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/ota-omnex-cli.uber-stup.club.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
client_max_body_size 500m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://ota-omnex-cli:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
# =========================================================
|
||||
# ota-omnex-admin.uber-stup.club -> conteneur ota-omnex-admin:3000
|
||||
# =========================================================
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name ota-omnex-admin.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/ota-omnex-admin.uber-stup.club.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/ota-omnex-admin.uber-stup.club.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
client_max_body_size 500m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://ota-omnex-admin:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
# =========================================================
|
||||
# ota-mobile-prod.uber-stup.club -> conteneur ota-mobile-prod:3000
|
||||
# =========================================================
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name ota-mobile-prod.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/ota-mobile-prod.uber-stup.club.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/ota-mobile-prod.uber-stup.club.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
client_max_body_size 500m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://ota-mobile-prod:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
# =========================================================
|
||||
# ota-mobile-preprod.uber-stup.club -> conteneur ota-mobile-preprod:3000
|
||||
# =========================================================
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name ota-mobile-preprod.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/ota-mobile-preprod.uber-stup.club.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/ota-mobile-preprod.uber-stup.club.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
client_max_body_size 500m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://ota-mobile-preprod:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
docker run --rm \
|
||||
-v ota_certbot_letsencrypt:/etc/letsencrypt \
|
||||
-v ota_certbot_www:/var/www/certbot \
|
||||
certbot/certbot renew --webroot -w /var/www/certbot --quiet
|
||||
|
||||
changed=0
|
||||
for sub in ota-prod ota-preprod ota-omnex-cli ota-omnex-admin ota-mobile-prod ota-mobile-preprod; do
|
||||
domain="${sub}.uber-stup.club"
|
||||
if ! docker run --rm -v ota_certbot_letsencrypt:/etc/letsencrypt -v /opt/ota/certs:/out alpine \
|
||||
sh -c "cmp -s /etc/letsencrypt/live/${domain}/fullchain.pem /out/${domain}.crt"; then
|
||||
docker run --rm -v ota_certbot_letsencrypt:/etc/letsencrypt -v /opt/ota/certs:/out alpine sh -c \
|
||||
"cp /etc/letsencrypt/live/${domain}/fullchain.pem /out/${domain}.crt && cp /etc/letsencrypt/live/${domain}/privkey.pem /out/${domain}.key"
|
||||
changed=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$changed" = "1" ]; then
|
||||
cd /opt/ota && docker compose up -d --force-recreate nginx
|
||||
fi
|
||||
@@ -0,0 +1,2 @@
|
||||
RUSTFS_ACCESS_KEY=iBacJPHUHSf6SMTnxrr
|
||||
RUSTFS_SECRET_KEY=4jNzZhstv1WkwTwtvR4gHSbrx01m4sJl4LBBleeJ
|
||||
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script de dump PostgreSQL + upload vers MinIO
|
||||
SDK officiel MinIO + contournement certificat auto-signe
|
||||
"""
|
||||
|
||||
import os
|
||||
import ssl
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import urllib3
|
||||
|
||||
# Configuration PostgreSQL
|
||||
PG_VERSION = "16"
|
||||
DB_HOST = "localhost"
|
||||
DB_PORT = 5432
|
||||
DB_NAME = "gestion_commande_db"
|
||||
DB_USER = "admin_gestion_commande_db"
|
||||
DB_PASSWORD = "1SWDxH20rV7K2Uc2PNlwCaCxfVZEtKomF0CK9OMh"
|
||||
|
||||
# Configuration MinIO
|
||||
MINIO_ENDPOINT = "10.0.0.4:9000"
|
||||
MINIO_ACCESS_KEY = "admin"
|
||||
MINIO_SECRET_KEY = "admin@12345"
|
||||
MINIO_BUCKET = "backup-db-prod-mln"
|
||||
MINIO_OBJECT = f"postgresql/{DB_NAME}.dump"
|
||||
MINIO_SECURE = False # HTTPS
|
||||
|
||||
# Fichier local temporaire
|
||||
OUTPUT_DIR = Path("./dumps")
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
DUMP_FILE = OUTPUT_DIR / f"{DB_NAME}.dump"
|
||||
|
||||
|
||||
def find_pg_dump() -> str:
|
||||
candidates = [
|
||||
f"/usr/lib/postgresql/{PG_VERSION}/bin/pg_dump",
|
||||
f"/usr/pgsql-{PG_VERSION}/bin/pg_dump",
|
||||
"/usr/bin/pg_dump",
|
||||
"/usr/local/bin/pg_dump",
|
||||
]
|
||||
for path in candidates:
|
||||
if Path(path).exists():
|
||||
return path
|
||||
result = subprocess.run(["which", "pg_dump"], capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
return result.stdout.strip()
|
||||
sys.exit("pg_dump introuvable.")
|
||||
|
||||
|
||||
def run_dump(pg_dump: str) -> None:
|
||||
env = os.environ.copy()
|
||||
env["PGPASSWORD"] = DB_PASSWORD
|
||||
|
||||
cmd = [
|
||||
pg_dump,
|
||||
"--host",
|
||||
DB_HOST,
|
||||
"--port",
|
||||
str(DB_PORT),
|
||||
"--username",
|
||||
DB_USER,
|
||||
"--dbname",
|
||||
DB_NAME,
|
||||
"--format",
|
||||
"custom",
|
||||
"--compress",
|
||||
"9",
|
||||
"--verbose",
|
||||
"--file",
|
||||
str(DUMP_FILE),
|
||||
]
|
||||
|
||||
print(f"[1/2] Dump -> {DUMP_FILE}")
|
||||
result = subprocess.run(cmd, env=env, capture_output=True, text=True)
|
||||
if result.stderr:
|
||||
print(result.stderr)
|
||||
if result.returncode != 0:
|
||||
sys.exit(f"Echec du dump (code {result.returncode})")
|
||||
|
||||
size_mb = DUMP_FILE.stat().st_size / 1024 / 1024
|
||||
print(f" OK ({size_mb:.2f} Mo)")
|
||||
|
||||
|
||||
def upload_to_minio() -> None:
|
||||
from minio import Minio
|
||||
|
||||
print(f"[2/2] Upload -> {MINIO_ENDPOINT}/{MINIO_BUCKET}/{MINIO_OBJECT}")
|
||||
|
||||
# Contexte SSL qui accepte les certificats auto-signes
|
||||
ssl_ctx = ssl.create_default_context()
|
||||
ssl_ctx.check_hostname = False
|
||||
ssl_ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
http_client = urllib3.PoolManager(
|
||||
ssl_context=ssl_ctx,
|
||||
cert_reqs="CERT_NONE",
|
||||
)
|
||||
|
||||
client = Minio(
|
||||
MINIO_ENDPOINT,
|
||||
access_key=MINIO_ACCESS_KEY,
|
||||
secret_key=MINIO_SECRET_KEY,
|
||||
secure=MINIO_SECURE,
|
||||
http_client=http_client,
|
||||
)
|
||||
|
||||
if not client.bucket_exists(MINIO_BUCKET):
|
||||
client.make_bucket(MINIO_BUCKET)
|
||||
print(f" Bucket '{MINIO_BUCKET}' cree.")
|
||||
|
||||
client.fput_object(
|
||||
bucket_name=MINIO_BUCKET,
|
||||
object_name=MINIO_OBJECT,
|
||||
file_path=str(DUMP_FILE),
|
||||
content_type="application/octet-stream",
|
||||
)
|
||||
|
||||
print(f" OK : s3://{MINIO_BUCKET}/{MINIO_OBJECT}")
|
||||
|
||||
DUMP_FILE.unlink()
|
||||
print(f" Fichier local supprime.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
from minio import Minio # noqa
|
||||
except ImportError:
|
||||
sys.exit("SDK MinIO manquant : pip install minio")
|
||||
|
||||
pg_dump_bin = find_pg_dump()
|
||||
print(f"pg_dump : {pg_dump_bin}\n")
|
||||
|
||||
run_dump(pg_dump_bin)
|
||||
upload_to_minio()
|
||||
|
||||
print("\nBackup termine avec succes !")
|
||||
print(f" s3://{MINIO_BUCKET}/{MINIO_OBJECT}")
|
||||
@@ -0,0 +1,104 @@
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: s3_nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "10.0.0.4:80:80"
|
||||
- "10.0.0.4:8080:8080"
|
||||
- "10.0.0.4:9000:9000"
|
||||
- "10.0.0.4:9001:9001"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./certs:/etc/nginx/certs:ro
|
||||
- nginx_logs:/var/log/nginx
|
||||
depends_on:
|
||||
- minio
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: s3_minio
|
||||
restart: unless-stopped
|
||||
hostname: minio
|
||||
volumes:
|
||||
- /mnt/data:/data
|
||||
environment:
|
||||
- MINIO_ROOT_USER=admin
|
||||
- MINIO_ROOT_PASSWORD=admin@12345
|
||||
- MINIO_ADDRESS=:9000
|
||||
- MINIO_CONSOLE_ADDRESS=:9001
|
||||
command: minio server /data --console-address :9001
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
dozzle-agent:
|
||||
image: amir20/dozzle:latest
|
||||
command: agent
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
ports:
|
||||
- "10.0.0.4:7007:7007"
|
||||
restart: unless-stopped
|
||||
|
||||
beszel-agent:
|
||||
image: henrygd/beszel-agent:latest
|
||||
container_name: beszel_agent
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
PORT: "10001"
|
||||
KEY: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEwRVn66crRDX5iL0bxNOnrp3OhTiXHKs+COx/bo5vmQ beszel-agent"
|
||||
|
||||
# ─── ClamAV Antivirus ─────────────────────────────────────────
|
||||
clamav:
|
||||
image: clamav/clamav:stable
|
||||
container_name: s3_clamav
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- FRESHCLAM_CHECKS=24
|
||||
- CLAMD_SCAN_MAX_FILESIZE=200M
|
||||
- CLAMD_SCAN_MAX_SCANSIZE=200M
|
||||
volumes:
|
||||
- clamav_db:/var/lib/clamav
|
||||
- clamav_logs:/var/log/clamav
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
clamav-scanner:
|
||||
image: clamav/clamav:stable
|
||||
container_name: s3_clamav_scanner
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- clamav
|
||||
volumes:
|
||||
- clamav_db:/var/lib/clamav:ro
|
||||
- clamav_scan_logs:/var/log/clamav
|
||||
- /mnt/data:/scan:ro
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
echo 'Waiting for virus definitions...' &&
|
||||
until [ -f /var/lib/clamav/daily.cvd ] || [ -f /var/lib/clamav/daily.cld ]; do sleep 10; done &&
|
||||
while true; do
|
||||
DATE=$$(date +%Y%m%d_%H%M%S) &&
|
||||
LOG=/var/log/clamav/scan-$$DATE.log &&
|
||||
echo \"[$$DATE] Scan started\" | tee -a $$LOG &&
|
||||
clamscan --recursive --infected --log=$$LOG /scan &&
|
||||
echo \"[$$DATE] Scan complete\" | tee -a $$LOG &&
|
||||
sleep 86400;
|
||||
done"
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
networks:
|
||||
s3_net:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
nginx_logs:
|
||||
clamav_db:
|
||||
clamav_logs:
|
||||
clamav_scan_logs:
|
||||
@@ -0,0 +1,96 @@
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
# ── MinIO Console GUI HTTPS (VPN: 10.0.0.4:8080) ────────
|
||||
server {
|
||||
listen 8080 ssl;
|
||||
server_name 10.0.0.4 _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/cert.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/key.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/minio_console_access.log;
|
||||
error_log /var/log/nginx/minio_console_error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://minio:9001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
}
|
||||
|
||||
# ── HTTP Redirect to HTTPS (VPN: 10.0.0.4:80) ────────────
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
location / {
|
||||
return 301 https://$host:8080$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# ── MinIO Console Direct HTTPS (VPN: 10.0.0.4:9001) ──────
|
||||
server {
|
||||
listen 9001 ssl;
|
||||
server_name 10.0.0.4 _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/cert.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/key.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/minio_console_direct_access.log;
|
||||
error_log /var/log/nginx/minio_console_direct_error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://minio:9001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
}
|
||||
|
||||
# ── MinIO S3 API HTTP (VPN: 10.0.0.4:9000) ────────────────
|
||||
server {
|
||||
listen 9000;
|
||||
server_name 10.0.0.4 _;
|
||||
|
||||
access_log /var/log/nginx/minio_s3_api_access.log;
|
||||
error_log /var/log/nginx/minio_s3_api_error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://minio:9000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto http;
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 2g;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
RUSTFS_ACCESS_KEY=JJg3uFeuKUUrdqq4oefQ8sIp6hnI
|
||||
RUSTFS_SECRET_KEY=7wPrOd1324t411q7VnuzfNkQXCch4U5qqOHx
|
||||
@@ -0,0 +1,62 @@
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: s3_nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/certs:/etc/nginx/certs:ro
|
||||
- nginx_logs:/var/log/nginx
|
||||
depends_on:
|
||||
- rustfs
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
rustfs_perm:
|
||||
image: alpine:latest
|
||||
container_name: rustfs_perm
|
||||
volumes:
|
||||
- /mnt/logs:/logs
|
||||
- /mnt/data:/data
|
||||
command: sh -c "chown -R 10001:10001 /data /logs"
|
||||
|
||||
rustfs:
|
||||
image: rustfs/rustfs:latest
|
||||
depends_on:
|
||||
rustfs_perm:
|
||||
condition: service_completed_successfully
|
||||
hostname: rustfs
|
||||
container_name: rustfs
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/data:/data
|
||||
- /mnt/logs:/logs
|
||||
environment:
|
||||
- RUSTFS_ACCESS_KEY=${RUSTFS_ACCESS_KEY:-JJg3uFeuKUUrdqq4oefQ8sIp6hnI}
|
||||
- RUSTFS_SECRET_KEY=${RUSTFS_SECRET_KEY:-7wPrOd1324t411q7VnuzfNkQXCch4U5qqOHx}
|
||||
- RUSTFS_CONSOLE_ENABLE=true
|
||||
- RUSTFS_ADDRESS=0.0.0.0:9000
|
||||
- RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001
|
||||
- RUSTFS_OBS_LOGGER_LEVEL=debug
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
dozzle-agent:
|
||||
image: amir20/dozzle:latest
|
||||
command: agent
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
ports:
|
||||
- "7007:7007"
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
s3_net:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
nginx_logs:
|
||||
@@ -0,0 +1,53 @@
|
||||
worker_processes auto;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
http {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 http://$host:9001$request_uri;
|
||||
}
|
||||
# ── RustFS Console ──────────────────────────────────────
|
||||
server {
|
||||
listen 9001;
|
||||
server_name _;
|
||||
access_log /var/log/nginx/rustfs_access.log;
|
||||
error_log /var/log/nginx/rustfs_error.log;
|
||||
location / {
|
||||
set $upstream http://rustfs:9001;
|
||||
proxy_pass $upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
}
|
||||
|
||||
# ── RustFS S3 API ───────────────────────────────────────
|
||||
server {
|
||||
listen 9000;
|
||||
server_name _;
|
||||
access_log /var/log/nginx/rustfs_s3_access.log;
|
||||
error_log /var/log/nginx/rustfs_s3_error.log;
|
||||
location / {
|
||||
set $upstream http://rustfs:9000;
|
||||
proxy_pass $upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_cache_convert_head off;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 2g;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
RUSTFS_ACCESS_KEY=iBacJPHUHSf6SMTnxrr
|
||||
RUSTFS_SECRET_KEY=4jNzZhstv1WkwTwtvR4gHSbrx01m4sJl4LBBleeJ
|
||||
|
||||
XAVIA_ADMIN_PASSWORD=2VYfheOweyMiP510GGfjNYmq3vJGJ
|
||||
XAVIA_UPLOAD_KEY=dNW2tiNXRR6w1cLwlmCOyKFIKvoTz8xQMSyd
|
||||
XAVIA_PRIVATE_KEY_BASE_64=MlRvcGlyNHpFRjhYOTlvUjMxbHFCVHRQdjYyelh4Wnc0MDM4TkZaVQ==
|
||||
XAVIA_POSTGRES_USER=xavia
|
||||
XAVIA_POSTGRES_PASSWORD=6DzLcsb5rgWukuGdrHtRBQ222f
|
||||
XAVIA_POSTGRES_DB=releases_db
|
||||
@@ -0,0 +1,115 @@
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: s3_nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/certs:/etc/nginx/certs:ro
|
||||
- nginx_logs:/var/log/nginx
|
||||
depends_on:
|
||||
- rustfs
|
||||
- xavia
|
||||
- gitea
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
rustfs_perm:
|
||||
image: alpine:latest
|
||||
container_name: rustfs_perm
|
||||
volumes:
|
||||
- /mnt/data:/data
|
||||
command: chown -R 10001:10001 /data
|
||||
|
||||
rustfs:
|
||||
image: rustfs/rustfs:latest
|
||||
depends_on:
|
||||
rustfs_perm:
|
||||
condition: service_completed_successfully
|
||||
hostname: rustfs
|
||||
container_name: rustfs
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/data:/data
|
||||
- /mnt/logs:/logs
|
||||
environment:
|
||||
- RUSTFS_ACCESS_KEY=${RUSTFS_ACCESS_KEY:-iBacJPHUHSf6SMTnxrr}
|
||||
- RUSTFS_SECRET_KEY=${RUSTFS_SECRET_KEY:-4jNzZhstv1WkwTwtvR4gHSbrx01m4sJl4LBBleeJ}
|
||||
- RUSTFS_SERVER_DOMAINS=rustfs.uber-stup.club
|
||||
- RUSTFS_CONSOLE_ENABLE=true
|
||||
- RUSTFS_ADDRESS=:9000
|
||||
- RUSTFS_CONSOLE_ADDRESS=:9001
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
xavia_db:
|
||||
image: postgres:16-alpine
|
||||
container_name: xavia_db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=${XAVIA_POSTGRES_USER:-xavia}
|
||||
- POSTGRES_PASSWORD=${XAVIA_POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=${XAVIA_POSTGRES_DB:-releases_db}
|
||||
volumes:
|
||||
- xavia_db_data:/var/lib/postgresql/data
|
||||
- ./init-db:/docker-entrypoint-initdb.d:ro
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
xavia:
|
||||
image: xaviaio/xavia-ota:latest
|
||||
container_name: xavia
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- HOST=https://ota.uber-stup.club
|
||||
- BLOB_STORAGE_TYPE=local
|
||||
- DB_TYPE=postgres
|
||||
- ADMIN_PASSWORD=${XAVIA_ADMIN_PASSWORD}
|
||||
- UPLOAD_KEY=${XAVIA_UPLOAD_KEY}
|
||||
- PRIVATE_KEY_BASE_64=${XAVIA_PRIVATE_KEY_BASE_64}
|
||||
- POSTGRES_USER=${XAVIA_POSTGRES_USER:-xavia}
|
||||
- POSTGRES_PASSWORD=${XAVIA_POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=${XAVIA_POSTGRES_DB:-releases_db}
|
||||
- POSTGRES_HOST=xavia_db
|
||||
- POSTGRES_PORT=5432
|
||||
volumes:
|
||||
- xavia_blobs:/app/blobs
|
||||
depends_on:
|
||||
- xavia_db
|
||||
networks:
|
||||
- s3_net
|
||||
gitea:
|
||||
image: docker.gitea.com/gitea:latest
|
||||
container_name: gitea
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
restart: always
|
||||
volumes:
|
||||
- gitea:/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
networks:
|
||||
- s3_net
|
||||
|
||||
dozzle-agent:
|
||||
image: amir20/dozzle:latest
|
||||
command: agent
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
ports:
|
||||
- "7007:7007"
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
s3_net:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
nginx_logs:
|
||||
xavia_db_data:
|
||||
xavia_blobs:
|
||||
gitea:
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS releases (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
runtime_version VARCHAR(255) NOT NULL,
|
||||
path VARCHAR(255) NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL,
|
||||
commit_hash VARCHAR(255) NOT NULL,
|
||||
commit_message VARCHAR(255) NOT NULL,
|
||||
update_id VARCHAR(255)
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE IF NOT EXISTS releases_tracking (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
release_id UUID NOT NULL REFERENCES releases(id),
|
||||
download_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
platform VARCHAR(50) NOT NULL,
|
||||
CONSTRAINT fk_release
|
||||
FOREIGN KEY(release_id)
|
||||
REFERENCES releases(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Index for faster queries on release_id and timestamp
|
||||
CREATE INDEX idx_tracking_release_id ON releases_tracking(release_id);
|
||||
CREATE INDEX idx_tracking_platform ON releases_tracking(platform);
|
||||
@@ -0,0 +1,125 @@
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
# Redirect HTTP -> HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# ── RustFS Console ──────────────────────────────────────
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name rustfs.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/rustfs_access.log;
|
||||
error_log /var/log/nginx/rustfs_error.log;
|
||||
|
||||
location / {
|
||||
set $upstream http://rustfs:9001;
|
||||
proxy_pass $upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
}
|
||||
|
||||
# ── RustFS S3 API ───────────────────────────────────────
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name s3.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/rustfs_s3_access.log;
|
||||
error_log /var/log/nginx/rustfs_s3_error.log;
|
||||
|
||||
location / {
|
||||
set $upstream http://rustfs:9000;
|
||||
proxy_pass $upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 2g;
|
||||
}
|
||||
}
|
||||
|
||||
# ── Xavia OTA ───────────────────────────────────────────
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name ota.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/xavia_access.log;
|
||||
error_log /var/log/nginx/xavia_error.log;
|
||||
|
||||
location / {
|
||||
set $upstream http://xavia:3000;
|
||||
proxy_pass $upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name gitea.uber-stup.club;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/gitea_access.log;
|
||||
error_log /var/log/nginx/gitea_error.log;
|
||||
|
||||
location / {
|
||||
set $upstream http://gitea:3000;
|
||||
proxy_pass $upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Dozzle
|
||||
DOZZLE_USERNAME=admin_mln_monitoring
|
||||
DOZZLE_PASSWORD=dX753n05Jo4T6B66cR
|
||||
|
||||
# Wazuh — changer TOUS ces mots de passe avant de déployer
|
||||
WAZUH_INDEXER_PASSWORD=9qB523OjLyFSp6vMpH!
|
||||
WAZUH_API_PASSWORD=myAJYqnemF544nsD33!
|
||||
WAZUH_DASHBOARD_PASSWORD=E9Jpr6586kQ3wYrCS2!
|
||||
@@ -0,0 +1,283 @@
|
||||
# ClamAV Integration with Wazuh
|
||||
|
||||
Integration guide for deploying ClamAV antivirus with Wazuh monitoring stack.
|
||||
|
||||
## Overview
|
||||
|
||||
ClamAV scans files and containers for malware, while Wazuh collects and analyzes ClamAV logs for:
|
||||
- Real-time malware detection alerts
|
||||
- Ransomware detection
|
||||
- Signature database update status
|
||||
- Scan statistics and quarantine actions
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ ClamAV Container (clamav) │
|
||||
│ • Scans Docker containers/files │
|
||||
│ • Generates logs: clamav.log │
|
||||
│ • Freshclam updates signatures │
|
||||
└──────────────┬──────────────────────┘
|
||||
│ (logs volume)
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Wazuh Manager (wazuh.manager) │
|
||||
│ • Collects ClamAV logs │
|
||||
│ • Parses with decoders │
|
||||
│ • Matches detection rules │
|
||||
│ • Sends alerts to indexer │
|
||||
└──────────────┬──────────────────────┘
|
||||
│ (syslog format)
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Wazuh Dashboard (UI) │
|
||||
│ • Display malware detections │
|
||||
│ • Show scan results │
|
||||
│ • Alert severity levels │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
### 1. Add ClamAV to docker-compose
|
||||
|
||||
The following service has been added to `docker-compose-security.yml`:
|
||||
|
||||
```yaml
|
||||
clamav:
|
||||
image: clamav/clamav:stable
|
||||
container_name: clamav
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- FRESHCLAM_CHECKS=24
|
||||
- CLAMD_SCAN_MAX_FILESIZE=100M
|
||||
volumes:
|
||||
- clamav_data:/var/lib/clamav # Signature database
|
||||
- clamav_logs:/var/log/clamav # Log output
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
networks:
|
||||
- monitoring_net
|
||||
```
|
||||
|
||||
### 2. Deploy ClamAV
|
||||
|
||||
```bash
|
||||
cd /home/ubuntu/docker
|
||||
docker compose -f docker-compose-security.yml up -d clamav
|
||||
|
||||
# Verify deployment
|
||||
docker ps | grep clamav
|
||||
docker logs clamav
|
||||
```
|
||||
|
||||
### 3. Wazuh Configuration
|
||||
|
||||
#### Log Collection
|
||||
|
||||
File: `wazuh/config/wazuh_manager/localfile_clamav.conf`
|
||||
|
||||
Configures Wazuh to collect:
|
||||
- `clamav.log` — Main scanner logs
|
||||
- `freshclam.log` — Signature update logs
|
||||
- `alert.log` — High-priority detections
|
||||
|
||||
#### Detection Rules
|
||||
|
||||
File: `wazuh/config/wazuh_manager/rules/clamav_rules.xml`
|
||||
|
||||
Key rules (level/priority):
|
||||
|
||||
| Rule ID | Level | Trigger | Example |
|
||||
|---------|-------|---------|---------|
|
||||
| 100501 | 15 | FOUND/Infected | Malware detected |
|
||||
| 100502 | 12 | Trojan/PUA | Potentially unwanted app |
|
||||
| 100503 | 15 | .Virus/Worm | Classic virus detection |
|
||||
| 100504 | 15 | Ransomware | Ransomware families |
|
||||
| 100505 | 3 | Update successful | Signature DB refreshed |
|
||||
| 100506 | 10 | Update failed | Download/connection error |
|
||||
| 100507 | 8 | Daemon error | ClamAV process error |
|
||||
| 100515 | 16 | Multiple hits in 1h | Possible outbreak |
|
||||
|
||||
---
|
||||
|
||||
## Log Format
|
||||
|
||||
### ClamAV Log Example
|
||||
|
||||
```
|
||||
Jun 10 14:35:22 clamav clamd[1234]: /var/data/suspect.exe: Trojan.Win32.Generic!c FOUND
|
||||
Jun 10 14:35:25 clamav freshclam[5678]: ClamAV update completed. 1 databases updated.
|
||||
Jun 10 14:36:01 clamav clamd[1234]: Scanning started
|
||||
Jun 10 14:36:45 clamav clamd[1234]: Scanning finished. 2 files scanned, 0 threats found
|
||||
```
|
||||
|
||||
### Wazuh Alert Example
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-06-10T14:35:22.000Z",
|
||||
"rule": {
|
||||
"id": "100501",
|
||||
"level": 15,
|
||||
"description": "ClamAV: Malware detected"
|
||||
},
|
||||
"data": {
|
||||
"srcfile": "/var/data/suspect.exe",
|
||||
"alert": "Trojan.Win32.Generic!c FOUND"
|
||||
},
|
||||
"groups": ["malware", "clamav", "infection"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Operations
|
||||
|
||||
### Check ClamAV Status
|
||||
|
||||
```bash
|
||||
# Container status
|
||||
docker ps | grep clamav
|
||||
|
||||
# View logs
|
||||
docker logs clamav
|
||||
|
||||
# Manual scan
|
||||
docker exec clamav clamscan -r /var/data
|
||||
```
|
||||
|
||||
### Update Signatures
|
||||
|
||||
ClamAV automatically updates signatures (configured via `FRESHCLAM_CHECKS=24`).
|
||||
|
||||
Manual update:
|
||||
```bash
|
||||
docker exec clamav freshclam
|
||||
```
|
||||
|
||||
### View Wazuh Alerts
|
||||
|
||||
1. Access Wazuh Dashboard: `https://10.0.0.2`
|
||||
2. Go to: **Security Events** → **Search**
|
||||
3. Filter by:
|
||||
- `rule.id: 100501` — Malware detections
|
||||
- `rule.id: 100504` — Ransomware alerts
|
||||
- `rule.groups: malware` — All malware-related events
|
||||
|
||||
### Query via API
|
||||
|
||||
```bash
|
||||
# Get recent malware detections
|
||||
curl -k -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
"https://monitoring-uber:55000/api/v0/search?q=rule.id:100501&pretty"
|
||||
|
||||
# Get ClamAV scan statistics
|
||||
curl -k -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
"https://monitoring-uber:55000/api/v0/search?q=group:clamav&pretty"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tuning & Optimization
|
||||
|
||||
### Scan Performance
|
||||
|
||||
Adjust scan parameters in docker-compose environment:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- CLAMD_SCAN_MAX_FILESIZE=100M # Max file size to scan
|
||||
- CLAMD_MAX_SCAN_SIZE=200M # Max total scan size
|
||||
- CLAMD_SCAN_PE_PLUS=yes # Enhanced PE detection
|
||||
- CLAMD_SCAN_ARCHIVE=yes # Scan inside archives
|
||||
- CLAMD_MAX_FILES=10000 # Max files to scan
|
||||
```
|
||||
|
||||
### Update Frequency
|
||||
|
||||
Default: 24 checks per day (every hour)
|
||||
|
||||
Change via:
|
||||
```yaml
|
||||
environment:
|
||||
- FRESHCLAM_CHECKS=48 # 2-hourly updates
|
||||
```
|
||||
|
||||
### Alert Severity
|
||||
|
||||
Adjust rule levels in `clamav_rules.xml` based on your risk tolerance:
|
||||
- Level 3-6: Info/Low
|
||||
- Level 8-10: Medium
|
||||
- Level 12-15: High
|
||||
- Level 16+: Critical/Outbreak
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### ClamAV Not Scanning
|
||||
|
||||
```bash
|
||||
# Check daemon is running
|
||||
docker exec clamav ps aux | grep clamd
|
||||
|
||||
# Check signatures are loaded
|
||||
docker exec clamav clamscan --version
|
||||
|
||||
# Manually scan
|
||||
docker exec clamav clamscan /var/data
|
||||
```
|
||||
|
||||
### Signature Updates Failing
|
||||
|
||||
```bash
|
||||
# Check freshclam logs
|
||||
docker logs clamav | grep freshclam
|
||||
|
||||
# Manual update with verbose output
|
||||
docker exec clamav freshclam -v
|
||||
|
||||
# Check internet connectivity
|
||||
docker exec clamav wget https://cvd.clamav.net
|
||||
```
|
||||
|
||||
### Wazuh Not Receiving Logs
|
||||
|
||||
```bash
|
||||
# Check logs are being generated
|
||||
docker exec clamav tail -f /var/log/clamav/clamav.log
|
||||
|
||||
# Check Wazuh log collection
|
||||
docker exec wazuh_manager tail -f /var/ossec/logs/ossec.log | grep clamav
|
||||
|
||||
# Verify rules loaded
|
||||
docker exec wazuh_manager cat /var/ossec/etc/rules/clamav_rules.xml | head -10
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files Modified/Created
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `docker-compose-security.yml` | Added ClamAV service + volumes |
|
||||
| `wazuh/config/wazuh_manager/localfile_clamav.conf` | Log collection config |
|
||||
| `wazuh/config/wazuh_manager/rules/clamav_rules.xml` | Malware detection rules |
|
||||
| `CLAMAV_INTEGRATION.md` | This documentation |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Deploy: `docker compose up -d clamav`
|
||||
2. Wait for initial database download (5-10 minutes)
|
||||
3. Monitor logs: `docker logs -f clamav`
|
||||
4. Check Wazuh dashboard for alerts
|
||||
5. Configure scan schedules/locations as needed
|
||||
6. Set up active response (optional) for automatic quarantine
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
# Firewall Configuration Rules
|
||||
|
||||
Updated: 2026-06-10
|
||||
|
||||
## Overview
|
||||
|
||||
Firewall rules for monitoring stack with Wazuh, Dozzle, Beszel, and VPN infrastructure.
|
||||
|
||||
---
|
||||
|
||||
## monitoring-uber (185.103.167.138)
|
||||
|
||||
### Inbound Rules (INPUT)
|
||||
|
||||
| Port | Protocol | Source | Purpose | Status |
|
||||
|------|----------|--------|---------|--------|
|
||||
| 22 | TCP | Admin IPs | SSH administration | ALLOW |
|
||||
| 22 | TCP | 185.103.166.119 (prod) | SSH — BLOCKED | **DROP** |
|
||||
| 22 | TCP | 185.103.166.112 (pre-prod) | SSH — BLOCKED | **DROP** |
|
||||
| 22 | TCP | 80.96.58.164 (s3) | SSH — BLOCKED | **DROP** |
|
||||
| 1514 | TCP | 185.103.166.119 | Wazuh agents (prod) | ALLOW |
|
||||
| 1514 | TCP | 185.103.166.112 | Wazuh agents (pre-prod) | ALLOW |
|
||||
| 51820 | UDP | Any | WireGuard VPN | ALLOW |
|
||||
| 443 | TCP | 10.0.0.0/24 | Wazuh Dashboard (VPN) | ALLOW |
|
||||
| 8080 | TCP | 10.0.0.0/24 | Dozzle logs (VPN) | ALLOW |
|
||||
| 8090 | TCP | 10.0.0.0/24 | Beszel monitoring (VPN) | ALLOW |
|
||||
| 9000-9001 | TCP | 10.0.0.0/24 | S3/RustFS (VPN) | ALLOW |
|
||||
|
||||
### Outbound Rules (OUTPUT)
|
||||
- **Default:** ACCEPT (all traffic allowed)
|
||||
- Allows DNS, updates, internet access
|
||||
|
||||
### Forward Rules (FORWARD)
|
||||
- **Default:** DROP (no transit traffic)
|
||||
|
||||
### Persistence
|
||||
- Rules saved to `/etc/iptables/rules.v4`
|
||||
- Auto-loaded on boot via `iptables-persistent`
|
||||
|
||||
---
|
||||
|
||||
## vpn-uber (45.150.111.158)
|
||||
|
||||
### Inbound Rules (INPUT)
|
||||
|
||||
| Port | Protocol | Source | Purpose | Status |
|
||||
|------|----------|--------|---------|--------|
|
||||
| 22 | TCP | Any | SSH administration | ALLOW |
|
||||
| 51820 | UDP | Any | WireGuard VPN | ALLOW |
|
||||
| 10001 | TCP | 10.0.0.0/24 | Beszel agent (from VPN only) | ALLOW |
|
||||
|
||||
### Outbound Rules (OUTPUT)
|
||||
- **Default:** ACCEPT (all traffic allowed)
|
||||
|
||||
### Forward Rules (FORWARD)
|
||||
- **Default:** ACCEPT
|
||||
- Enables routing: wg0 ↔ eth0 (VPN ↔ Internet)
|
||||
- ESTABLISHED/RELATED always allowed
|
||||
|
||||
### Network Features
|
||||
- NAT masquerade: `eth0` (POSTROUTING)
|
||||
- IP forwarding: enabled (`net.ipv4.ip_forward=1`)
|
||||
- Allows VPN clients to reach internet through VPN server
|
||||
|
||||
### Persistence
|
||||
- Rules saved to `/etc/iptables/rules.v4`
|
||||
- IP forwarding saved to `/etc/sysctl.conf`
|
||||
|
||||
---
|
||||
|
||||
## Security Hardening
|
||||
|
||||
### Principle: Least Privilege
|
||||
1. **SSH restrictions**: Production servers (prod, pre-prod, s3) **cannot** SSH into monitoring-uber
|
||||
- Prevents lateral movement if a prod server is compromised
|
||||
- Admins must SSH directly to monitoring-uber or via VPN
|
||||
|
||||
2. **Service isolation**: All user-facing services (Wazuh, Dozzle, Beszel) accessible **only via VPN**
|
||||
- Not exposed to public internet
|
||||
- Network: 10.0.0.0/24 (private VPN)
|
||||
|
||||
3. **Port whitelist**: Only required ports open
|
||||
- Wazuh agent ingest: 1514/tcp (prod/pre-prod only)
|
||||
- VPN: 51820/udp (all)
|
||||
- SSH: 22/tcp (admin access, blocked from prod servers)
|
||||
- Monitoring agents: 10001/tcp (VPN only)
|
||||
|
||||
### Attack Surface Reduction
|
||||
- Production agents cannot access monitoring infrastructure
|
||||
- Monitoring dashboard only accessible via VPN
|
||||
- No exposed dashboards or logs to public internet
|
||||
- SSH brute-force: limited by rate (ESTABLISHED/RELATED state tracking)
|
||||
|
||||
---
|
||||
|
||||
## Updating Rules
|
||||
|
||||
### monitoring-uber
|
||||
```bash
|
||||
ssh root@185.103.167.138
|
||||
./firewall-monitoring-uber.sh
|
||||
```
|
||||
|
||||
### vpn-uber
|
||||
```bash
|
||||
ssh root@45.150.111.158
|
||||
./firewall-vpn-server.sh
|
||||
```
|
||||
|
||||
### Verify Rules Applied
|
||||
```bash
|
||||
# Check current rules
|
||||
iptables -L INPUT -n
|
||||
iptables -L FORWARD -n
|
||||
|
||||
# Check saved rules
|
||||
cat /etc/iptables/rules.v4
|
||||
```
|
||||
|
||||
### Restore Rules on Boot
|
||||
Rules are automatically restored via `/etc/iptables/rules.v4` if `iptables-persistent` is installed:
|
||||
```bash
|
||||
apt-get install iptables-persistent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beszel Agent Configuration
|
||||
|
||||
Beszel agents installed on all infrastructure servers:
|
||||
|
||||
| Server | Type | Port | Access |
|
||||
|--------|------|------|--------|
|
||||
| prod-uber | Docker | 10001 | Public IP (185.103.166.119:10001) |
|
||||
| pre-prod-uber | Docker | 10001 | Public IP (185.103.166.112:10001) |
|
||||
| s3-uber | Docker | 10001 | Public IP (80.96.58.164:10001) |
|
||||
| vpn-uber | Binarie (systemd) | 10001 | VPN IP (10.0.0.1:10001) |
|
||||
|
||||
**Note:** vpn-uber agent is accessible **only from VPN** due to firewall rules.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Service not reachable
|
||||
```bash
|
||||
# Check if service is listening
|
||||
ss -tlnp | grep <port>
|
||||
|
||||
# Check firewall allows traffic
|
||||
iptables -L INPUT -n | grep <port>
|
||||
|
||||
# Test connectivity
|
||||
nc -zv <ip> <port>
|
||||
```
|
||||
|
||||
### SSH access denied from prod
|
||||
- Expected behavior (security hardening)
|
||||
- Use admin SSH keys or SSH via VPN instead
|
||||
|
||||
### Beszel agent shows offline
|
||||
- Check agent is listening: `ss -tlnp | grep 10001`
|
||||
- Check firewall allows Beszel dashboard to reach agent
|
||||
- Verify network routing between monitoring-uber and agent server
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
# OWASP Top 10 ModSecurity Rules — Wazuh Active Response
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Ce système détecte et répond aux attaques OWASP Top 10 via ModSecurity + Wazuh.
|
||||
|
||||
**Deux couches de détection :**
|
||||
|
||||
| Type | Trigger | Règle | Level | AR |
|
||||
|------|---------|-------|-------|-----|
|
||||
| **Bloqué (403)** | HTTP 403 | 100220-100281 | 12-15 | Fermer 4h-24h |
|
||||
| **Remontée (Warning)** | Anomaly scoring | 100320-100381 | 6-11 | Fermer 30min-6h |
|
||||
|
||||
---
|
||||
|
||||
## Correspondances CRS ModSecurity
|
||||
|
||||
```
|
||||
941xxx — XSS (Cross-Site Scripting)
|
||||
942xxx — SQL Injection, LDAP Injection, etc.
|
||||
943xxx — XXE (XML External Entity)
|
||||
930xxx — Path Traversal, RFI, RCE, LFI
|
||||
932xxx — Remote Command Execution
|
||||
933xxx — PHP Injection
|
||||
934xxx — Java Injection
|
||||
950xxx — Exploit attempts
|
||||
951xxx — Regex DoS, Scanner detection
|
||||
952xxx — Restricted File Access
|
||||
953xxx — Insecure File Upload
|
||||
954xxx — Proxy abuse, Scanner detection
|
||||
955xxx — HTTP Response Splitting, Header Injection
|
||||
970xxx — SSRF (Server-Side Request Forgery)
|
||||
971xxx — SSRF (alternative patterns)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Règles par OWASP Category
|
||||
|
||||
### OWASP #1 : Broken Access Control
|
||||
|
||||
**Détecte :** Path Traversal, File Inclusion, Unauthorized Access
|
||||
|
||||
| Rule ID | Type | Patterns | Level | Description |
|
||||
|---------|------|----------|-------|-------------|
|
||||
| 100250 | Bloqué | 930xx (path\|traversal\|lfi) | 12 | LFI/Path Traversal bloquée |
|
||||
| 100251 | Bloqué | 930xx (rfi\|remote\|include) | 13 | RFI bloquée |
|
||||
| 100252 | Bloqué | 952xx | 11 | File Access bloquée |
|
||||
| 100293 | Bloqué | Freq(100250)×4/180s | 12 | Path Traversal brute-force |
|
||||
| 100350 | Remontée | 930xx (path\|traversal\|lfi) | 8 | LFI attempt (warning) |
|
||||
| 100351 | Remontée | 930xx (rfi\|remote\|include) | 9 | RFI attempt (warning) |
|
||||
| 100352 | Remontée | 952xx | 8 | File Access attempt (warning) |
|
||||
|
||||
**Active Response :**
|
||||
- Bloqué : `firewall-drop 6h`
|
||||
- Brute-force : `firewall-drop 24h`
|
||||
- Remontée : `firewall-drop 2h-4h`
|
||||
|
||||
---
|
||||
|
||||
### OWASP #3 : Injection
|
||||
|
||||
**Détecte :** SQLi, LDAP, Command Injection, PHP/Java Injection
|
||||
|
||||
| Rule ID | Type | Patterns | Level | Description |
|
||||
|---------|------|----------|-------|-------------|
|
||||
| 100220 | Bloqué | 942xx | 13 | SQL Injection bloquée |
|
||||
| 100221 | Bloqué | 942xx (LDAP pattern) | 12 | LDAP Injection bloquée |
|
||||
| 100222 | Bloqué | 932xx | 14 | Command Injection bloquée |
|
||||
| 100223 | Bloqué | 933xx | 13 | PHP Injection bloquée |
|
||||
| 100224 | Bloqué | 934xx | 13 | Java Injection bloquée |
|
||||
| 100290 | Bloqué | Freq(100220)×3/60s | 14 | SQLi brute-force |
|
||||
| 100292 | Bloqué | Freq(100222)×2/60s | 15 | Command Injection CRITICAL |
|
||||
| 100320 | Remontée | 942xx | 9 | SQLi attempt (warning) |
|
||||
| 100321 | Remontée | 932xx | 10 | Command Injection attempt |
|
||||
| 100322 | Remontée | 942xx (LDAP) | 9 | LDAP Injection attempt |
|
||||
| 100323 | Remontée | 933xx\|934xx | 9 | PHP/Java Injection attempt |
|
||||
| 100394 | Remontée | Freq(100320)×5/300s | 10 | SQLi anomaly scoring |
|
||||
| 100396 | Remontée | Freq(100321)×2/60s | 12 | Command Injection CRITICAL |
|
||||
|
||||
**Active Response :**
|
||||
- SQLi Bloquée : `firewall-drop 6h`
|
||||
- SQLi Brute-force : `firewall-drop 24h + host-deny permanent`
|
||||
- Command Injection : `firewall-drop 24h + host-deny`
|
||||
- Command Injection CRITICAL : `firewall-drop permanent + host-deny permanent`
|
||||
- SQLi Remontée : `firewall-drop 2h`
|
||||
- SQLi Anomaly : `firewall-drop 4h`
|
||||
- Command Injection Remontée : `firewall-drop 4h`
|
||||
|
||||
---
|
||||
|
||||
### OWASP #6 : Vulnerable & Outdated Components
|
||||
|
||||
**Détecte :** RCE, Exploit attempts, known vulnerabilities
|
||||
|
||||
| Rule ID | Type | Patterns | Level | Description |
|
||||
|---------|------|----------|-------|-------------|
|
||||
| 100230 | Bloqué | 930xx\|950xx | 14 | RCE/Exploit bloquée |
|
||||
| 100330 | Remontée | 930xx\|950xx | 11 | RCE/Exploit attempt (warning) |
|
||||
|
||||
**Active Response :**
|
||||
- Bloqué : `firewall-drop 24h`
|
||||
- Remontée : `firewall-drop 6h`
|
||||
|
||||
---
|
||||
|
||||
### OWASP #7 : Authentication & Session Management
|
||||
|
||||
**Détecte :** XSS, Header Injection, Session hijacking attempts
|
||||
|
||||
| Rule ID | Type | Patterns | Level | Description |
|
||||
|---------|------|----------|-------|-------------|
|
||||
| 100240 | Bloqué | 941xx | 12 | XSS bloquée |
|
||||
| 100241 | Bloqué | 955xx | 12 | Header Injection bloquée |
|
||||
| 100291 | Bloqué | Freq(100240)×5/120s | 13 | XSS anomaly scoring |
|
||||
| 100340 | Remontée | 941xx | 8 | XSS attempt (warning) |
|
||||
| 100341 | Remontée | 955xx | 8 | Header Injection attempt |
|
||||
| 100395 | Remontée | Freq(100340)×8/300s | 10 | XSS anomaly scoring |
|
||||
|
||||
**Active Response :**
|
||||
- XSS Bloquée : `firewall-drop 4h`
|
||||
- XSS Anomaly : `firewall-drop 6h`
|
||||
- Header Injection : `firewall-drop 6h`
|
||||
- XSS Remontée : `firewall-drop 1h`
|
||||
- Header Remontée : `firewall-drop 2h`
|
||||
|
||||
---
|
||||
|
||||
### OWASP #8 : Software & Data Integrity Failures
|
||||
|
||||
**Détecte :** XXE, Deserialization attacks
|
||||
|
||||
| Rule ID | Type | Patterns | Level | Description |
|
||||
|---------|------|----------|-------|-------------|
|
||||
| 100260 | Bloqué | 943xx | 13 | XXE bloquée |
|
||||
| 100360 | Remontée | 943xx | 9 | XXE attempt (warning) |
|
||||
|
||||
**Active Response :**
|
||||
- Bloqué : `firewall-drop 12h`
|
||||
- Remontée : `firewall-drop 4h`
|
||||
|
||||
---
|
||||
|
||||
### OWASP #9 : Logging & Monitoring Failures
|
||||
|
||||
**Détecte :** Web scanners, bot activity, reconnaissance
|
||||
|
||||
| Rule ID | Type | Patterns | Level | Description |
|
||||
|---------|------|----------|-------|-------------|
|
||||
| 100270 | Bloqué | 951xx\|954xx | 10 | Scanner detection (bloquée) |
|
||||
| 100370 | Remontée | 951xx\|954xx | 6 | Scanner detection (warning) |
|
||||
|
||||
**Active Response :**
|
||||
- Bloqué : `firewall-drop 2h`
|
||||
- Remontée : `firewall-drop 30min`
|
||||
|
||||
---
|
||||
|
||||
### Autres : SSRF & File Upload
|
||||
|
||||
| Rule ID | Type | Patterns | Level | Description |
|
||||
|---------|------|----------|-------|-------------|
|
||||
| 100280 | Bloqué | 970xx\|971xx | 13 | SSRF bloquée |
|
||||
| 100281 | Bloqué | 953xx | 12 | File Upload bloquée |
|
||||
| 100380 | Remontée | 970xx\|971xx | 9 | SSRF attempt |
|
||||
| 100381 | Remontée | 953xx | 8 | File Upload attempt |
|
||||
|
||||
**Active Response :**
|
||||
- SSRF Bloquée : `firewall-drop 12h`
|
||||
- File Upload Bloquée : `firewall-drop 6h`
|
||||
- SSRF Remontée : `firewall-drop 4h`
|
||||
- File Upload Remontée : `firewall-drop 2h`
|
||||
|
||||
---
|
||||
|
||||
## Timeline des Active Responses
|
||||
|
||||
### Bloquées (HTTP 403)
|
||||
|
||||
| Durée | Règles | Motif |
|
||||
|-------|--------|-------|
|
||||
| Permanent | 100292 | Command Injection CRITICAL (2+ tentatives / 60s) |
|
||||
| 24h | 100290 | SQLi brute-force (3+ / 60s) |
|
||||
| 24h | 100230 | RCE/Exploit |
|
||||
| 12h | 100251, 100260, 100280 | RFI, XXE, SSRF |
|
||||
| 6h | 100220, 100221, 100250, 100252, 100281, 100330 | SQLi, LDAP, Path Traversal, File Access, Upload, RCE |
|
||||
| 4h | 100240 | XSS |
|
||||
| 2h | 100270 | Scanner |
|
||||
|
||||
### Remontées (Anomaly Scoring)
|
||||
|
||||
| Durée | Règles | Motif |
|
||||
|-------|--------|-------|
|
||||
| Permanent | 100396 | Command Injection (2+ / 60s) |
|
||||
| 6h | 100395, 100330, 100351 | XSS anomaly, RCE, RFI |
|
||||
| 4h | 100394, 100321, 100323, 100360, 100380 | SQLi anomaly, Cmd Inj, PHP/Java, XXE, SSRF |
|
||||
| 2h | 100320, 100322, 100341, 100350, 100352, 100381 | SQLi, LDAP, Header Inj, Path Traversal, File Access, Upload |
|
||||
| 30min | 100370 | Scanner |
|
||||
|
||||
---
|
||||
|
||||
## Files
|
||||
|
||||
- **`modsecurity_owasp_rules.xml`** — Définition des règles (100220-100396)
|
||||
- **`ossec.conf`** — Active-response configs (firewall-drop, durées)
|
||||
- **`firewall-monitoring-uber.sh`** — iptables pour autoriser trafic ModSec
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
**Dashboard Wazuh :**
|
||||
|
||||
1. **Alerts → Security Events → OWASP**
|
||||
2. **Visualization:**
|
||||
- Blocking rate (HTTP 403)
|
||||
- Anomaly scoring trends
|
||||
- Top attackers (srcip)
|
||||
- Attack distribution by category
|
||||
|
||||
**Logs:**
|
||||
|
||||
```bash
|
||||
# On monitoring-uber
|
||||
tail -f /var/ossec/logs/alerts/alerts.json | grep -i "owasp\|100[23][0-9][0-9]"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tuning
|
||||
|
||||
Les seuils de fréquence peuvent être ajustés :
|
||||
|
||||
- **SQLi anomaly:** `frequency="5" timeframe="300"` → Réduire pour être plus agressif
|
||||
- **XSS anomaly:** `frequency="8" timeframe="300"` → Augmenter pour réduire false positives
|
||||
- **Command Injection:** `frequency="2" timeframe="60"` → Critique, ne pas modifier
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- **First-match semantics:** Wazuh évalue les règles par ID croissant → les règles bloquées (100220+) ont priorité sur les remontées (100320+)
|
||||
- **Field matching:** Les patterns utilisent `<field name="transaction.messages.message">` pour matcher les CRS rule IDs dans les logs ModSecurity
|
||||
- **SCA suppressed:** Les alertes SCA (CIS benchmark) sont à level 0 (voir `local_sca_noise.xml`)
|
||||
- **Persistence:** Tous les AR sont sauvegardés dans `iptables-save` → survient les reboots
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
# VPN Deployment Summary
|
||||
|
||||
## Infrastructure
|
||||
|
||||
| Role | Hostname | IP Public | IP VPN | Status |
|
||||
|------|----------|-----------|--------|--------|
|
||||
| **VPN Server** | vpn-uber | 45.150.111.158 | 10.0.0.1/24 | ✓ Active |
|
||||
| **Monitoring** | monitoring-uber | 185.103.167.138 | 10.0.0.2/24 | ✓ Connected |
|
||||
| **Admins** | (clients) | — | 10.0.0.3+ | Ready |
|
||||
|
||||
---
|
||||
|
||||
## Server Credentials
|
||||
|
||||
### VPN Server (vpn-uber)
|
||||
|
||||
- **IP Public:** 45.150.111.158
|
||||
- **Port:** 51820/udp
|
||||
- **Server Private Key:** (secured at `/etc/wireguard/privatekey`)
|
||||
- **Server Public Key:** `VQa6g1foaXhJgYWbwyJ9R/EAmmXg0nOnhPhbIIPLlmg=`
|
||||
- **Network:** 10.0.0.0/24
|
||||
- **Interface IP:** 10.0.0.1
|
||||
|
||||
**SSH Access:**
|
||||
```bash
|
||||
ssh root@45.150.111.158
|
||||
# Password: yqZ98EJL1h3ISlfT6l (from host.ini)
|
||||
```
|
||||
|
||||
### Monitoring Server (monitoring-uber)
|
||||
|
||||
- **IP Public:** 185.103.167.138
|
||||
- **VPN IP:** 10.0.0.2/24
|
||||
- **Status:** Connected to VPN ✓
|
||||
- **Firewall:** Configured (iptables)
|
||||
- **Interface:** wg0 UP
|
||||
|
||||
**SSH Access:**
|
||||
```bash
|
||||
ssh root@185.103.167.138
|
||||
# Password: rL9lY6YkcDQmfRuZ3Z (from host.ini)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VPN Configuration
|
||||
|
||||
### Firewall Rules
|
||||
|
||||
**VPN Server (vpn-uber):**
|
||||
```
|
||||
INPUT:
|
||||
✓ 22/tcp (SSH)
|
||||
✓ 51820/udp (WireGuard)
|
||||
✗ Everything else → REJECT
|
||||
|
||||
OUTPUT:
|
||||
✓ All
|
||||
|
||||
FORWARD:
|
||||
✓ wg0 interface (VPN traffic)
|
||||
✓ NAT masquerade (10.0.0.0/24 → Internet)
|
||||
```
|
||||
|
||||
**Monitoring Server (monitoring-uber):**
|
||||
```
|
||||
INPUT:
|
||||
✓ 1514/tcp FROM 185.103.166.119 (Prod agents)
|
||||
✓ 1514/tcp FROM 185.103.166.112 (Pre-prod agents)
|
||||
✓ 51820/udp (VPN)
|
||||
✓ 443/tcp FROM 10.0.0.0/24 (Wazuh Dashboard)
|
||||
✓ 8080/tcp FROM 10.0.0.0/24 (Dozzle)
|
||||
✓ 9090/tcp FROM 10.0.0.0/24 (Beszel)
|
||||
✓ 9000-9001/tcp FROM 10.0.0.0/24 (S3/RustFS)
|
||||
✗ Everything else → DROP
|
||||
|
||||
OUTPUT:
|
||||
✓ All (Internet)
|
||||
```
|
||||
|
||||
### WireGuard Routing
|
||||
|
||||
```
|
||||
VPN Server (10.0.0.1)
|
||||
↓
|
||||
Monitoring (10.0.0.2) — Connected ✓
|
||||
↓
|
||||
Admin Clients (10.0.0.3+) — Ready to connect
|
||||
```
|
||||
|
||||
**Current Peers:**
|
||||
```
|
||||
peer: 4WLn2BR9ZGhM195mIvLbZR7tP/mb7ks4+mZ5ppZ21xw= (monitoring-uber)
|
||||
allowed ips: 10.0.0.2/32
|
||||
status: Connected ✓ (45ms latency, 0% loss)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Services Accessible via VPN
|
||||
|
||||
Once connected to VPN from admin client:
|
||||
|
||||
| Service | URL | Port | Status |
|
||||
|---------|-----|------|--------|
|
||||
| **Wazuh Dashboard** | https://10.0.0.2 | 443 | ✓ (VPN only) |
|
||||
| **Dozzle** | http://10.0.0.2:8080 | 8080 | ✓ (VPN only) |
|
||||
| **Beszel** | http://10.0.0.2:9090 | 9090 | ✓ (VPN only) |
|
||||
| **S3/RustFS Console** | http://10.0.0.2:9001 | 9001 | ✓ (VPN only) |
|
||||
| **S3/RustFS API** | http://10.0.0.2:9000 | 9000 | ✓ (VPN only) |
|
||||
|
||||
**Mode:** VPN + Internet normal (no kill switch)
|
||||
- Admins can access services via VPN
|
||||
- Admins keep normal Internet access
|
||||
- If VPN drops → automatic fallback to Internet
|
||||
|
||||
---
|
||||
|
||||
## Next Steps for Admins
|
||||
|
||||
### 1. Generate Client Configs
|
||||
|
||||
```bash
|
||||
./wireguard-admin-client.sh admin1 45.150.111.158 "VQa6g1foaXhJgYWbwyJ9R/EAmmXg0nOnhPhbIIPLlmg="
|
||||
```
|
||||
|
||||
Creates: `admin1.conf`
|
||||
|
||||
### 2. Add Client to VPN Server
|
||||
|
||||
SSH to VPN server:
|
||||
```bash
|
||||
ssh root@45.150.111.158
|
||||
```
|
||||
|
||||
Then:
|
||||
```bash
|
||||
# Get client's public key from admin1.conf (PrivateKey → PublicKey)
|
||||
wg set wg0 peer <CLIENT_PUBKEY> allowed-ips 10.0.0.3/32
|
||||
wg show
|
||||
```
|
||||
|
||||
### 3. Admin Setup
|
||||
|
||||
- Install WireGuard app (wireguard.com/install)
|
||||
- Import admin1.conf
|
||||
- Connect to VPN
|
||||
- Test access to services
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### From Monitoring Server
|
||||
|
||||
```bash
|
||||
# Test VPN connection
|
||||
ping 10.0.0.1
|
||||
# Response: 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=45.0 ms
|
||||
|
||||
# Check VPN status
|
||||
ip addr show wg0
|
||||
ip route show | grep wg0
|
||||
|
||||
# VPN traffic
|
||||
tcpdump -i wg0
|
||||
```
|
||||
|
||||
### From VPN Server
|
||||
|
||||
```bash
|
||||
# Monitor active connections
|
||||
wg show
|
||||
|
||||
# Check interface
|
||||
ip addr show wg0
|
||||
|
||||
# Verify firewall rules
|
||||
iptables -L
|
||||
iptables -L -t nat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Monitoring not connecting to VPN
|
||||
|
||||
```bash
|
||||
# Check WireGuard daemon
|
||||
ssh root@185.103.167.138
|
||||
systemctl status wg-quick@wg0
|
||||
journalctl -u wg-quick@wg0 -n 20
|
||||
|
||||
# Restart
|
||||
systemctl restart wg-quick@wg0
|
||||
```
|
||||
|
||||
### VPN Server not accepting peers
|
||||
|
||||
```bash
|
||||
# On VPN server, verify WireGuard is running
|
||||
wg show
|
||||
|
||||
# Check if peer was added correctly
|
||||
wg show wg0
|
||||
|
||||
# If not showing, re-add:
|
||||
wg set wg0 peer <CLIENT_PUBKEY> allowed-ips 10.0.0.X/32
|
||||
```
|
||||
|
||||
### Admin client can't connect
|
||||
|
||||
- Verify VPN server is listening: `netstat -ulnp | grep 51820`
|
||||
- Verify firewall allows 51820/udp: `ufw status` or `iptables -L`
|
||||
- Check client config has correct server IP and pubkey
|
||||
- Try reconnecting after 2-3 seconds
|
||||
|
||||
---
|
||||
|
||||
## Files & Scripts Used
|
||||
|
||||
- `firewall-vpn-server.sh` — VPN server firewall (iptables)
|
||||
- `wireguard-server-setup.sh` — WireGuard server install + config
|
||||
- `firewall-monitoring-uber.sh` — Monitoring firewall (iptables)
|
||||
- `wireguard-client-setup.sh` — WireGuard client install
|
||||
- `wireguard-admin-client.sh` — Generate admin client configs
|
||||
- `VPN_SETUP.md` — Full setup guide (phases 1-7)
|
||||
|
||||
---
|
||||
|
||||
## Persistence
|
||||
|
||||
All configurations survive reboot:
|
||||
|
||||
- **Firewall:** `/etc/iptables/rules.v4` (loaded via iptables-restore)
|
||||
- **WireGuard:** Enabled at boot via `systemctl enable wg-quick@wg0`
|
||||
- **Routes:** Persistent in wg0 config
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Firewall rules block unauthorized access
|
||||
- WireGuard handshake: 45ms latency (acceptable)
|
||||
- No kill switch enabled (VPN + Internet normal)
|
||||
- Services (Wazuh, Dozzle, Beszel, S3) accessible ONLY via VPN
|
||||
- Agents (prod/pre-prod) still have direct access to Wazuh manager (1514/tcp)
|
||||
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
✓ VPN Server deployed on vpn-uber (45.150.111.158)
|
||||
✓ Monitoring-uber connected to VPN (10.0.0.2)
|
||||
✓ Firewall configured on both servers
|
||||
✓ VPN connection tested (45ms, 0% loss)
|
||||
✓ Ready for admin client setup
|
||||
@@ -0,0 +1,306 @@
|
||||
# VPN Setup for Monitoring Stack
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Internet (public)
|
||||
├─ [Prod/Pre-prod] (accessible)
|
||||
│ └─→(1514/tcp)→ monitoring-uber (port ouvert)
|
||||
│
|
||||
├─ [VPN Server] (nouveau VPS, 1 CPU 2GB RAM)
|
||||
│ └─ WireGuard 0.0.0.0:51820/udp
|
||||
│
|
||||
└─ [monitoring-uber] (185.103.167.138)
|
||||
├─ Client VPN (10.0.0.2)
|
||||
└─ Services: Wazuh, Dozzle, Beszel, S3 (VPN only)
|
||||
|
||||
VPN Network: 10.0.0.0/24
|
||||
├─ VPN Server: 10.0.0.1
|
||||
├─ monitoring-uber: 10.0.0.2
|
||||
└─ Admins: 10.0.0.3+
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
**Mode:** VPN + Internet normal (pas de kill switch)
|
||||
- Admins connectés au VPN → accès à services VPN (10.0.0.0/24)
|
||||
- Admins gardent aussi accès à Internet normal (pas de restriction)
|
||||
- Si VPN tombe → retrouvent Internet automatiquement
|
||||
|
||||
---
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: VPN Server Setup (nouveau VPS)
|
||||
|
||||
```bash
|
||||
# 1. Login to VPS
|
||||
ssh root@<VPN_SERVER_IP>
|
||||
|
||||
# 2. Run firewall setup
|
||||
chmod +x /path/to/firewall-vpn-server.sh
|
||||
./firewall-vpn-server.sh
|
||||
|
||||
# 3. Setup WireGuard server
|
||||
chmod +x /path/to/wireguard-server-setup.sh
|
||||
./wireguard-server-setup.sh
|
||||
|
||||
# Output will show:
|
||||
# - Server Public Key (note this)
|
||||
# - Example: aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE=
|
||||
```
|
||||
|
||||
Save the **Server Public Key** — you'll need it for clients.
|
||||
|
||||
### Phase 2: monitoring-uber VPN Client
|
||||
|
||||
```bash
|
||||
# 1. Login to monitoring-uber
|
||||
ssh root@185.103.167.138
|
||||
|
||||
# 2. Run firewall setup
|
||||
chmod +x /path/to/firewall-monitoring-uber.sh
|
||||
./firewall-monitoring-uber.sh
|
||||
|
||||
# 3. Setup WireGuard client
|
||||
# Syntax: wireguard-client-setup.sh <VPN_SERVER_IP> <VPN_SERVER_PUBKEY>
|
||||
chmod +x /path/to/wireguard-client-setup.sh
|
||||
./wireguard-client-setup.sh <VPN_SERVER_IP> "<SERVER_PUBKEY>"
|
||||
|
||||
# Example:
|
||||
# ./wireguard-client-setup.sh 123.45.67.89 "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE="
|
||||
|
||||
# Output will show:
|
||||
# - Client Public Key (note this)
|
||||
# - Example: XyZ9876543210AbCdEfGhIjKlMnOpQrStUvWxYz=
|
||||
```
|
||||
|
||||
### Phase 3: Add monitoring-uber to VPN Server
|
||||
|
||||
```bash
|
||||
# Back on VPS, add monitoring-uber as a peer
|
||||
ssh root@<VPN_SERVER_IP>
|
||||
|
||||
# Use the Client Public Key from Phase 2
|
||||
wg set wg0 peer <CLIENT_PUBKEY> allowed-ips 10.0.0.2/32
|
||||
|
||||
# Verify
|
||||
wg show
|
||||
|
||||
# Example output:
|
||||
# interface: wg0
|
||||
# public key: aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE=
|
||||
# private key: (hidden)
|
||||
# listening port: 51820
|
||||
#
|
||||
# peer: XyZ9876543210AbCdEfGhIjKlMnOpQrStUvWxYz=
|
||||
# endpoint: <monitoring-uber-ip>:xxxxx
|
||||
# allowed ips: 10.0.0.2/32
|
||||
# latest handshake: X seconds ago
|
||||
# transfer: X B received, X B sent
|
||||
```
|
||||
|
||||
### Phase 4: Verify VPN Connection
|
||||
|
||||
```bash
|
||||
# On monitoring-uber
|
||||
ping 10.0.0.1
|
||||
|
||||
# Should respond
|
||||
# PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
|
||||
# 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=XX.X ms
|
||||
|
||||
# Check interface
|
||||
ip addr show wg0
|
||||
```
|
||||
|
||||
### Phase 5: Rebind Services to VPN IP
|
||||
|
||||
```bash
|
||||
# On monitoring-uber
|
||||
chmod +x /path/to/rebind-services-to-vpn.sh
|
||||
./rebind-services-to-vpn.sh
|
||||
|
||||
# This will update docker-compose-security.yml:
|
||||
# - Wazuh Dashboard: 0.0.0.0:443 → 10.0.0.2:443
|
||||
# - Dozzle: 0.0.0.0:8080 → 10.0.0.2:8080
|
||||
# - Beszel: 0.0.0.0:9090 → 10.0.0.2:9090
|
||||
# - S3/RustFS: 0.0.0.0:9000/9001 → 10.0.0.2:9000/9001
|
||||
# - Wazuh Manager: stays on 0.0.0.0:1514 (for agents)
|
||||
|
||||
# Restart services
|
||||
cd /home/ubuntu/docker
|
||||
docker compose -f docker-compose-security.yml down
|
||||
docker compose -f docker-compose-security.yml up -d
|
||||
|
||||
# Verify
|
||||
docker ps
|
||||
```
|
||||
|
||||
### Phase 6: Ajouter un admin VPN
|
||||
|
||||
La clé privée ne doit **jamais quitter la machine de l'admin**. La procédure se fait en deux temps.
|
||||
|
||||
#### Étape A — Sur la machine de l'admin (à faire par l'admin)
|
||||
|
||||
Installer WireGuard si besoin :
|
||||
- Windows/Mac : https://www.wireguard.com/install/
|
||||
- Linux : `sudo apt install wireguard`
|
||||
|
||||
Générer les clés **localement** :
|
||||
|
||||
```bash
|
||||
# Linux / Mac
|
||||
wg genkey | tee privatekey | wg pubkey > publickey
|
||||
cat privatekey # à garder secret
|
||||
cat publickey # à envoyer à l'administrateur VPN
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows (PowerShell, WireGuard installé)
|
||||
cd "C:\Program Files\WireGuard"
|
||||
.\wireguard.exe /genkey | Out-File -Encoding ascii privatekey.txt
|
||||
Get-Content privatekey.txt | .\wireguard.exe /pubkey | Out-File -Encoding ascii publickey.txt
|
||||
type publickey.txt # à envoyer à l'administrateur VPN
|
||||
```
|
||||
|
||||
Créer le fichier de config `admin_nom.conf` **sur sa machine** :
|
||||
|
||||
```ini
|
||||
[Interface]
|
||||
Address = 10.0.0.X/32 # IP assignée par l'administrateur VPN
|
||||
PrivateKey = <SA_CLE_PRIVEE>
|
||||
DNS = 8.8.8.8
|
||||
|
||||
[Peer]
|
||||
PublicKey = VQa6g1foaXhJgYWbwyJ9R/EAmmXg0nOnhPhbIIPLlmg=
|
||||
AllowedIPs = 10.0.0.0/24
|
||||
Endpoint = 45.150.111.158:51820
|
||||
PersistentKeepalive = 25
|
||||
```
|
||||
|
||||
Importer ce fichier dans l'app WireGuard → **Tunnel prêt, pas encore actif**.
|
||||
|
||||
#### Étape B — Sur le serveur VPN (à faire par l'administrateur)
|
||||
|
||||
L'admin envoie sa **clé publique** et l'administrateur l'ajoute :
|
||||
|
||||
```bash
|
||||
ssh root@45.150.111.158
|
||||
|
||||
# Assigner une IP libre (voir tableau ci-dessous) et ajouter le peer
|
||||
wg set wg0 peer <PUBKEY_ADMIN> allowed-ips 10.0.0.X/32
|
||||
|
||||
# Persister la config (survie au reboot)
|
||||
wg-quick save wg0
|
||||
|
||||
# Vérifier
|
||||
wg show
|
||||
```
|
||||
|
||||
L'admin peut maintenant activer le tunnel dans l'app WireGuard.
|
||||
|
||||
#### IPs assignées
|
||||
|
||||
| Admin | IP VPN | Clé publique |
|
||||
|--------|------------|--------------|
|
||||
| admin1 | 10.0.0.3 | (voir admin1.conf) |
|
||||
| admin2 | 10.0.0.10 | `SL6qEf2K0/3a26wBkvQpOmgILHxxLS1N8M5hJAqfRCc=` |
|
||||
|
||||
Prochaine IP libre : **10.0.0.11**
|
||||
|
||||
### Phase 7: Admin Connection
|
||||
|
||||
Each admin:
|
||||
|
||||
1. Download WireGuard app: https://www.wireguard.com/install/
|
||||
2. Import config file (admin1.conf, admin2.conf, etc.)
|
||||
3. Connect to VPN
|
||||
4. Access services:
|
||||
- **Wazuh Dashboard**: https://10.0.0.2
|
||||
- **Dozzle**: http://10.0.0.2:8080
|
||||
- **Beszel**: http://10.0.0.2:9090
|
||||
- **S3/RustFS Console**: http://10.0.0.2:9001
|
||||
|
||||
## Firewall Rules Summary
|
||||
|
||||
### VPS VPN Server
|
||||
|
||||
| Protocol | Port | Source | Action |
|
||||
|----------|------|--------|--------|
|
||||
| UDP | 51820 | Any | ACCEPT (WireGuard) |
|
||||
| TCP | 22 | Any | ACCEPT (SSH) |
|
||||
| ICMP | echo-request | Any | ACCEPT |
|
||||
| Any | Any | Any | REJECT |
|
||||
|
||||
NAT masquerade enabled for VPN → Internet routing.
|
||||
|
||||
### monitoring-uber (185.103.167.138)
|
||||
|
||||
| Protocol | Port | Source | Action |
|
||||
|----------|------|--------|--------|
|
||||
| TCP | 1514 | 185.103.166.119 | ACCEPT (Prod Wazuh Agent) |
|
||||
| TCP | 1514 | 185.103.166.112 | ACCEPT (Pre-prod Wazuh Agent) |
|
||||
| UDP | 51820 | Any | ACCEPT (VPN) |
|
||||
| TCP | 443 | 10.0.0.0/24 | ACCEPT (Wazuh Dashboard — VPN) |
|
||||
| TCP | 8080 | 10.0.0.0/24 | ACCEPT (Dozzle — VPN) |
|
||||
| TCP | 9090 | 10.0.0.0/24 | ACCEPT (Beszel — VPN) |
|
||||
| TCP | 9000-9001 | 10.0.0.0/24 | ACCEPT (S3 — VPN) |
|
||||
| TCP | 22 | Any | ACCEPT (SSH) |
|
||||
| Any | Any | Any | DROP (Deny all) |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### VPN connection not establishing
|
||||
|
||||
```bash
|
||||
# On monitoring-uber
|
||||
systemctl status wg-quick@wg0
|
||||
journalctl -u wg-quick@wg0 -n 20
|
||||
|
||||
# Restart
|
||||
systemctl restart wg-quick@wg0
|
||||
```
|
||||
|
||||
### Can't access services over VPN
|
||||
|
||||
```bash
|
||||
# On monitoring-uber, check bindings
|
||||
netstat -tlnp | grep -E "(443|8080|9090|9000)"
|
||||
|
||||
# Should show 10.0.0.2 (not 0.0.0.0)
|
||||
```
|
||||
|
||||
### Wazuh agents can't connect
|
||||
|
||||
Make sure firewall allows 1514/tcp from prod/pre-prod:
|
||||
|
||||
```bash
|
||||
# On monitoring-uber
|
||||
iptables -L INPUT -v | grep 1514
|
||||
```
|
||||
|
||||
Should show rules for prod (185.103.166.119) and pre-prod (185.103.166.112).
|
||||
|
||||
## Persistence & Boot
|
||||
|
||||
All rules are saved with:
|
||||
- `iptables-save` → `/etc/iptables/rules.v4`
|
||||
- WireGuard: `systemctl enable wg-quick@wg0`
|
||||
|
||||
Both survive reboots.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [ ] Deploy VPS VPN Server
|
||||
- [ ] Run firewall + WireGuard setup on VPS
|
||||
- [ ] Get Server Public Key
|
||||
- [ ] Deploy firewall + WireGuard client on monitoring-uber
|
||||
- [ ] Add monitoring-uber peer on VPS
|
||||
- [ ] Verify VPN connection (ping 10.0.0.1)
|
||||
- [ ] Rebind services to VPN IP
|
||||
- [ ] Restart docker containers
|
||||
- [ ] Generate admin client configs
|
||||
- [ ] Add admin peers on VPS
|
||||
- [ ] Test admin VPN connection
|
||||
- [ ] Test service access (Wazuh, Dozzle, Beszel, S3)
|
||||
@@ -0,0 +1,10 @@
|
||||
[Interface]
|
||||
Address = 10.0.0.3/32
|
||||
PrivateKey = ePPTC7QXatWQd9qZWmAqlgd+IskVQ9/7rvwsfUtosEs=
|
||||
DNS = 8.8.8.8
|
||||
|
||||
[Peer]
|
||||
PublicKey = VQa6g1foaXhJgYWbwyJ9R/EAmmXg0nOnhPhbIIPLlmg=
|
||||
AllowedIPs = 10.0.0.0/24
|
||||
Endpoint = 45.150.111.158:51820
|
||||
PersistentKeepalive = 25
|
||||
@@ -0,0 +1,52 @@
|
||||
# Values for port, users, and token are optional.
|
||||
# Defaults are port 45876, the first created user, and a generated UUID token.
|
||||
|
||||
systems:
|
||||
- name: backup-prod-mln
|
||||
host: 10.0.0.4
|
||||
port: 10001
|
||||
token: 945de0ec-678a-4e79-9cf8-c9158a427a9d
|
||||
users:
|
||||
- free6nk33@gmail.com
|
||||
- name: bdd-redis-prod
|
||||
host: 10.0.0.5
|
||||
port: 10001
|
||||
token: 6c29ec90-93eb-402b-b7ae-132e510d9d54
|
||||
users:
|
||||
- free6nk33@gmail.com
|
||||
- name: load-balancer
|
||||
host: 93.185.166.221
|
||||
port: 45876
|
||||
token: c9f1bb8d-ef48-4f1b-a8fd-ed834b93f683
|
||||
users:
|
||||
- free6nk33@gmail.com
|
||||
- name: pre-prod-uber
|
||||
host: 185.103.166.112
|
||||
port: 10001
|
||||
token: ff4a-52aec89a1-5357-8329685224
|
||||
users:
|
||||
- free6nk33@gmail.com
|
||||
- name: prod-uber
|
||||
host: 185.103.166.119
|
||||
port: 10001
|
||||
token: 551-5c86bcbaa34-68b-bb9e248e32
|
||||
users:
|
||||
- free6nk33@gmail.com
|
||||
- name: runner
|
||||
host: 132.243.162.161
|
||||
port: 45876
|
||||
token: 4252d294-cb8c-4ed6-a70e-19973186f9b5
|
||||
users:
|
||||
- free6nk33@gmail.com
|
||||
- name: s3-uber
|
||||
host: 80.96.58.164
|
||||
port: 10001
|
||||
token: 5688-ac1b97819c-79f3-104e051884
|
||||
users:
|
||||
- free6nk33@gmail.com
|
||||
- name: vpn-uber
|
||||
host: 10.0.0.1
|
||||
port: 10001
|
||||
token: b5b-257ee3ebf-be6-c92809ee12
|
||||
users:
|
||||
- free6nk33@gmail.com
|
||||
@@ -0,0 +1,16 @@
|
||||
mode: iptables
|
||||
update_frequency: 10s
|
||||
log_mode: stdout
|
||||
log_level: info
|
||||
api_url: http://127.0.0.1:7777
|
||||
api_key: monitoring-bouncer-key-2026
|
||||
insecure_skip_verify: false
|
||||
disable_ipv6: false
|
||||
deny_action: DROP
|
||||
deny_log: false
|
||||
supported_decisions_types:
|
||||
- ban
|
||||
iptables_chains:
|
||||
- INPUT
|
||||
- FORWARD
|
||||
- DOCKER-USER
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
services:
|
||||
dozzle:
|
||||
image: amir20/dozzle:latest
|
||||
container_name: dozzle
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./dozzle/users.yml:/data/users.yml:ro
|
||||
environment:
|
||||
DOZZLE_AUTH_PROVIDER: simple
|
||||
DOZZLE_AUTH_TTL: 48h
|
||||
DOZZLE_REMOTE_AGENT: "185.103.166.112:7007|pre-prod-uber|PRE-PROD,185.103.166.119:7007|prod-uber|PROD,10.0.0.4:7007|backup-mln|BACKUP,80.96.58.164:7007|s3-uber|VPS,10.0.0.5:7007|bdd-redis-prod|BDD-PROD"
|
||||
networks:
|
||||
- monitoring_net
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: monitoring_nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "10.0.0.2:80:80"
|
||||
- "10.0.0.2:443:443"
|
||||
- "10.0.0.2:8080:8080"
|
||||
- "10.0.0.2:8090:8090"
|
||||
- "10.0.0.2:3001:3001"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/certs:/etc/nginx/certs:ro
|
||||
- nginx_logs:/var/log/nginx
|
||||
depends_on:
|
||||
- dozzle
|
||||
- wazuh.dashboard
|
||||
- beszel
|
||||
networks:
|
||||
- monitoring_net
|
||||
|
||||
# ─── ClamAV Antivirus ────────────────────────────────────
|
||||
clamav:
|
||||
image: clamav/clamav:stable
|
||||
container_name: clamav
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- FRESHCLAM_CHECKS=24
|
||||
- CLAMD_SCAN_MAX_FILESIZE=100M
|
||||
volumes:
|
||||
- clamav_data:/var/lib/clamav
|
||||
- clamav_logs:/var/log/clamav
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
networks:
|
||||
- monitoring_net
|
||||
|
||||
# ─── Beszel Hub ───────────────────────────────────────────
|
||||
beszel:
|
||||
image: henrygd/beszel:latest
|
||||
container_name: beszel
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- beszel_data:/beszel_data
|
||||
networks:
|
||||
- monitoring_net
|
||||
|
||||
wazuh.manager:
|
||||
image: wazuh/wazuh-manager:4.14.5
|
||||
hostname: wazuh.manager
|
||||
container_name: wazuh_manager
|
||||
restart: unless-stopped
|
||||
ulimits:
|
||||
memlock:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 655360
|
||||
hard: 655360
|
||||
ports:
|
||||
- "1514:1514" # Communication agents
|
||||
- "1515:1515" # Enrôlement agents
|
||||
- "514:514/udp" # Syslog entrant
|
||||
environment:
|
||||
- INDEXER_URL=https://wazuh.indexer:9200
|
||||
- INDEXER_USERNAME=admin
|
||||
- INDEXER_PASSWORD=${WAZUH_INDEXER_PASSWORD}
|
||||
- FILEBEAT_SSL_VERIFICATION_MODE=full
|
||||
- SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
|
||||
- SSL_CERTIFICATE=/etc/ssl/filebeat.pem
|
||||
- SSL_KEY=/etc/ssl/filebeat.key
|
||||
- API_USERNAME=wazuh-wui
|
||||
- API_PASSWORD=${WAZUH_API_PASSWORD}
|
||||
entrypoint: ["/bin/bash", "/wazuh-custom/init.sh"]
|
||||
volumes:
|
||||
- ./wazuh/config/wazuh_manager/:/wazuh-custom/:ro
|
||||
- wazuh_api_configuration:/var/ossec/api/configuration
|
||||
- wazuh_etc:/var/ossec/etc
|
||||
- wazuh_logs:/var/ossec/logs
|
||||
- wazuh_queue:/var/ossec/queue
|
||||
- wazuh_var_multigroups:/var/ossec/var/multigroups
|
||||
- wazuh_integrations:/var/ossec/integrations
|
||||
- wazuh_active_response:/var/ossec/active-response/bin
|
||||
- wazuh_agentless:/var/ossec/agentless
|
||||
- wazuh_wodles:/var/ossec/wodles
|
||||
- filebeat_etc:/etc/filebeat
|
||||
- filebeat_var:/var/lib/filebeat
|
||||
- ./wazuh/certs/root-ca-manager.pem:/etc/ssl/root-ca.pem
|
||||
- ./wazuh/certs/wazuh.manager.pem:/etc/ssl/filebeat.pem
|
||||
- ./wazuh/certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /usr/bin/docker:/usr/local/bin/docker:ro
|
||||
- ./wazuh/config/wazuh_manager/active-response:/wazuh-custom/active-response:ro
|
||||
- ./wazuh/config/wazuh_manager/agent.conf:/wazuh-custom/agent.conf:ro
|
||||
networks:
|
||||
- monitoring_net
|
||||
|
||||
# ─── Wazuh Indexer (OpenSearch) ───────────────────────────
|
||||
wazuh.indexer:
|
||||
image: wazuh/wazuh-indexer:4.14.5
|
||||
hostname: wazuh.indexer
|
||||
container_name: wazuh_indexer
|
||||
restart: unless-stopped
|
||||
ulimits:
|
||||
memlock:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
environment:
|
||||
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
|
||||
- INDEXER_PASSWORD=${WAZUH_INDEXER_PASSWORD}
|
||||
volumes:
|
||||
- wazuh_indexer_data:/var/lib/wazuh-indexer
|
||||
- ./wazuh/certs/root-ca.pem:/usr/share/wazuh-indexer/config/certs/root-ca.pem
|
||||
- ./wazuh/certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.key
|
||||
- ./wazuh/certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem
|
||||
- ./wazuh/certs/admin.pem:/usr/share/wazuh-indexer/config/certs/admin.pem
|
||||
- ./wazuh/certs/admin-key.pem:/usr/share/wazuh-indexer/config/certs/admin-key.pem
|
||||
- ./wazuh/config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/config/opensearch.yml
|
||||
- ./wazuh/config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/config/opensearch-security/internal_users.yml
|
||||
networks:
|
||||
- monitoring_net
|
||||
|
||||
# ─── Wazuh Dashboard ──────────────────────────────────────
|
||||
wazuh.dashboard:
|
||||
image: wazuh/wazuh-dashboard:4.14.5
|
||||
hostname: wazuh.dashboard
|
||||
container_name: wazuh_dashboard
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- INDEXER_USERNAME=admin
|
||||
- INDEXER_PASSWORD=${WAZUH_INDEXER_PASSWORD}
|
||||
- WAZUH_API_URL=https://wazuh.manager
|
||||
- DASHBOARD_USERNAME=kibanaserver
|
||||
- DASHBOARD_PASSWORD=${WAZUH_DASHBOARD_PASSWORD}
|
||||
- API_USERNAME=wazuh-wui
|
||||
- API_PASSWORD=${WAZUH_API_PASSWORD}
|
||||
volumes:
|
||||
- ./wazuh/certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
|
||||
- ./wazuh/certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
|
||||
- ./wazuh/certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem
|
||||
- ./wazuh/config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
|
||||
depends_on:
|
||||
- wazuh.indexer
|
||||
- wazuh.manager
|
||||
networks:
|
||||
- monitoring_net
|
||||
uptime-kuma:
|
||||
image: louislam/uptime-kuma:1
|
||||
container_name: uptime-kuma
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- uptime_kuma_data:/app/data
|
||||
networks:
|
||||
- monitoring_net
|
||||
|
||||
networks:
|
||||
monitoring_net:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
wazuh_api_configuration:
|
||||
wazuh_etc:
|
||||
wazuh_logs:
|
||||
wazuh_queue:
|
||||
wazuh_var_multigroups:
|
||||
wazuh_integrations:
|
||||
wazuh_active_response:
|
||||
wazuh_agentless:
|
||||
wazuh_wodles:
|
||||
filebeat_etc:
|
||||
filebeat_var:
|
||||
wazuh_indexer_data:
|
||||
nginx_logs:
|
||||
beszel_data:
|
||||
clamav_data:
|
||||
clamav_logs:
|
||||
uptime_kuma_data:
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
users:
|
||||
admin:
|
||||
name: Admin
|
||||
email: admin@uber-stup.club
|
||||
password: $2b$11$AOUC5QG2l8Ee0gUKIyZGKuHdL0iuvDa3C0bVAl2Gyt6Axx7.E/hHm
|
||||
roles: download
|
||||
@@ -0,0 +1,106 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Firewall monitoring-uber (185.103.167.138)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# INBOUND:
|
||||
# • 1514/tcp FROM prod (185.103.166.119) — agents Wazuh
|
||||
# • 1514/tcp FROM pre-prod (185.103.166.112) — agents Wazuh
|
||||
# • 51820/udp — VPN WireGuard
|
||||
# • 10.0.0.0/24 (VPN admins) → 443, 8080, 8090, 9000, 9001
|
||||
# • SSH: BLOCKED from prod/pre-prod/s3 (security hardening)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# OUTBOUND:
|
||||
# • Tout (0.0.0.0/0) — Internet, updates, DNS
|
||||
# • VPN vers VPS
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
echo "[*] Configurant firewall monitoring-uber..."
|
||||
|
||||
# Flush des règles existantes
|
||||
iptables -F
|
||||
iptables -X
|
||||
iptables -t nat -F
|
||||
iptables -t nat -X
|
||||
iptables -t mangle -F
|
||||
iptables -t mangle -X
|
||||
|
||||
# Politique par défaut
|
||||
iptables -P INPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
iptables -P OUTPUT ACCEPT
|
||||
|
||||
# ─── INPUT ────────────────────────────────────────────────────────
|
||||
# Loopback (services internes)
|
||||
iptables -A INPUT -i lo -j ACCEPT
|
||||
|
||||
# Established/Related
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# SSH (administration locale, bloquer depuis prod/pre-prod/s3)
|
||||
iptables -A INPUT -p tcp --dport 22 -s 185.103.166.119 -j DROP # prod
|
||||
iptables -A INPUT -p tcp --dport 22 -s 185.103.166.112 -j DROP # pre-prod
|
||||
iptables -A INPUT -p tcp --dport 22 -s 80.96.58.164 -j DROP # s3
|
||||
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # allow from other sources
|
||||
|
||||
# ─── Agents Wazuh (prod + pre-prod) ────────────────────────────
|
||||
# Prod: 185.103.166.119
|
||||
iptables -A INPUT -p tcp --dport 1514 -s 185.103.166.119 -j ACCEPT
|
||||
|
||||
# Pre-prod: 185.103.166.112
|
||||
iptables -A INPUT -p tcp --dport 1514 -s 185.103.166.112 -j ACCEPT
|
||||
|
||||
# ─── VPN (WireGuard) ──────────────────────────────────────────
|
||||
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
|
||||
|
||||
# ─── Services (accessibles via VPN seulement) ─────────────────
|
||||
# Admins via VPN: 10.0.0.0/24
|
||||
|
||||
# Wazuh Dashboard (443/https)
|
||||
iptables -A INPUT -p tcp --dport 443 -s 10.0.0.0/24 -j ACCEPT
|
||||
|
||||
# Dozzle (8080/http)
|
||||
iptables -A INPUT -p tcp --dport 8080 -s 10.0.0.0/24 -j ACCEPT
|
||||
|
||||
# Beszel (8090/http)
|
||||
iptables -A INPUT -p tcp --dport 8090 -s 10.0.0.0/24 -j ACCEPT
|
||||
|
||||
# S3/RustFS (9000/9001)
|
||||
iptables -A INPUT -p tcp --dport 9000 -s 10.0.0.0/24 -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport 9001 -s 10.0.0.0/24 -j ACCEPT
|
||||
|
||||
# ─── ICMP ─────────────────────────────────────────────────────
|
||||
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
|
||||
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
|
||||
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
|
||||
|
||||
# Reject le reste
|
||||
iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited
|
||||
|
||||
# ─── FORWARD ──────────────────────────────────────────────────
|
||||
# Blocker forward par défaut (services locaux, pas de transit)
|
||||
iptables -A FORWARD -j REJECT --reject-with icmp-host-prohibited
|
||||
|
||||
# ─── Sauvegarder ──────────────────────────────────────────────
|
||||
mkdir -p /etc/iptables
|
||||
iptables-save > /etc/iptables/rules.v4
|
||||
|
||||
echo "[✓] Firewall monitoring-uber configuré"
|
||||
echo ""
|
||||
echo "Règles appliquées (ALLOW) :"
|
||||
echo " • SSH 22/tcp — from admin IPs only (BLOCKED from prod/pre-prod/s3)"
|
||||
echo " • Wazuh 1514/tcp FROM prod (185.103.166.119) — agents Wazuh"
|
||||
echo " • Wazuh 1514/tcp FROM pre-prod (185.103.166.112) — agents Wazuh"
|
||||
echo " • VPN 51820/udp — WireGuard"
|
||||
echo " • Wazuh Dashboard 443/tcp FROM 10.0.0.0/24 (VPN)"
|
||||
echo " • Dozzle 8080/tcp FROM 10.0.0.0/24 (VPN)"
|
||||
echo " • Beszel 8090/tcp FROM 10.0.0.0/24 (VPN)"
|
||||
echo " • S3/RustFS 9000-9001/tcp FROM 10.0.0.0/24 (VPN)"
|
||||
echo " • OUTPUT (Internet/DNS)"
|
||||
echo ""
|
||||
echo "Règles appliquées (DENY) :"
|
||||
echo " • SSH 22/tcp FROM prod (185.103.166.119) — BLOCKED"
|
||||
echo " • SSH 22/tcp FROM pre-prod (185.103.166.112) — BLOCKED"
|
||||
echo " • SSH 22/tcp FROM s3 (80.96.58.164) — BLOCKED"
|
||||
echo " • All other INPUT/FORWARD — default DROP"
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Firewall VPS VPN Server (WireGuard) — vpn-uber (45.150.111.158)
|
||||
# Allow: WireGuard (51820/udp), SSH (22/tcp), Beszel agent (10001/tcp from VPN)
|
||||
# Reject: tout le reste
|
||||
# NAT: masquerade pour routage VPN
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
echo "[*] Configurant firewall VPS VPN Server..."
|
||||
|
||||
# Flush des règles existantes
|
||||
iptables -F
|
||||
iptables -X
|
||||
iptables -t nat -F
|
||||
iptables -t nat -X
|
||||
iptables -t mangle -F
|
||||
iptables -t mangle -X
|
||||
|
||||
# Politique par défaut
|
||||
iptables -P INPUT DROP
|
||||
iptables -P FORWARD ACCEPT
|
||||
iptables -P OUTPUT ACCEPT
|
||||
|
||||
# ─── INPUT ────────────────────────────────────────────────────────
|
||||
# Loopback
|
||||
iptables -A INPUT -i lo -j ACCEPT
|
||||
|
||||
# Established/Related
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# SSH (administration)
|
||||
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
|
||||
|
||||
# WireGuard
|
||||
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
|
||||
|
||||
# Beszel Agent (10001/tcp) — accessible depuis VPN uniquement
|
||||
iptables -A INPUT -p tcp --dport 10001 -s 10.0.0.0/24 -j ACCEPT
|
||||
|
||||
# ICMP (ping, MTU discovery)
|
||||
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
|
||||
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
|
||||
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
|
||||
|
||||
# Reject le reste
|
||||
iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited
|
||||
|
||||
# ─── FORWARD ──────────────────────────────────────────────────────
|
||||
# VPN ↔ Internet
|
||||
iptables -A FORWARD -i wg0 -j ACCEPT
|
||||
iptables -A FORWARD -o wg0 -j ACCEPT
|
||||
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# ─── NAT ──────────────────────────────────────────────────────────
|
||||
# Masquerade pour routage VPN
|
||||
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||
|
||||
# ─── Sauvegarder ──────────────────────────────────────────────────
|
||||
mkdir -p /etc/iptables
|
||||
iptables-save > /etc/iptables/rules.v4
|
||||
|
||||
# ─── IP Forwarding ────────────────────────────────────────────────
|
||||
sysctl -w net.ipv4.ip_forward=1
|
||||
grep -q "net.ipv4.ip_forward" /etc/sysctl.conf || echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
|
||||
|
||||
echo "[✓] Firewall VPS VPN Server configuré"
|
||||
echo ""
|
||||
echo "Règles appliquées (ALLOW) :"
|
||||
echo " • SSH 22/tcp — administration"
|
||||
echo " • WireGuard 51820/udp — VPN peers"
|
||||
echo " • Beszel Agent 10001/tcp FROM 10.0.0.0/24 — monitoring dashboard"
|
||||
echo " • ICMP (ping, MTU discovery)"
|
||||
echo ""
|
||||
echo "Configuration :"
|
||||
echo " • FORWARD ACCEPT (VPN ↔ Internet routing)"
|
||||
echo " • NAT masquerade activé (eth0)"
|
||||
echo " • IP forwarding activé (net.ipv4.ip_forward=1)"
|
||||
echo " • Default INPUT policy: DROP"
|
||||
@@ -0,0 +1,74 @@
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
# Main HTTPS server
|
||||
server {
|
||||
listen 0.0.0.0:80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 0.0.0.0:443 ssl default_server;
|
||||
listen [::]:443 ssl default_server;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
# Wazuh (root)
|
||||
location / {
|
||||
proxy_pass https://wazuh.dashboard:5601/;
|
||||
proxy_ssl_verify off;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host wazuh.dashboard:5601;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
proxy_hide_header Content-Security-Policy;
|
||||
}
|
||||
|
||||
# Dozzle
|
||||
location /dozzle {
|
||||
proxy_pass http://dozzle:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect ~^http://dozzle:8080/(.*)$ /dozzle/$1;
|
||||
proxy_redirect ~^/$/ /dozzle/;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
|
||||
# Beszel
|
||||
location /beszel {
|
||||
proxy_pass http://beszel:8090;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect ~^http://beszel:8090/(.*)$ /beszel/$1;
|
||||
proxy_redirect ~^/$/ /beszel/;
|
||||
sub_filter 'href="/' 'href="/beszel/';
|
||||
sub_filter 'src="/' 'src="/beszel/';
|
||||
sub_filter 'url(/' 'url(/beszel/';
|
||||
sub_filter_once off;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
upstream wazuh_backend {
|
||||
server wazuh.dashboard:5601;
|
||||
}
|
||||
|
||||
upstream dozzle_backend {
|
||||
server dozzle:8080;
|
||||
}
|
||||
|
||||
upstream beszel_backend {
|
||||
server beszel:8090;
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# Main HTTPS server
|
||||
server {
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ssl default_server;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
# Root redirects to /wazuh
|
||||
location = / {
|
||||
return 301 https://$host/wazuh;
|
||||
}
|
||||
|
||||
# Wazuh
|
||||
location /wazuh/ {
|
||||
proxy_pass https://wazuh_backend/;
|
||||
proxy_ssl_verify off;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
# Dozzle
|
||||
location /dozzle/ {
|
||||
proxy_pass http://dozzle_backend/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
|
||||
# Beszel
|
||||
location /beszel/ {
|
||||
proxy_pass http://beszel_backend/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
# ── Dozzle (8080) ───────────────────────────────────────
|
||||
server {
|
||||
listen 8080 ssl;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/dozzle_access.log;
|
||||
error_log /var/log/nginx/dozzle_error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://dozzle:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
}
|
||||
|
||||
# ── Beszel (8090) ───────────────────────────────────────
|
||||
server {
|
||||
listen 8090 ssl;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/beszel_access.log;
|
||||
error_log /var/log/nginx/beszel_error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://beszel:8090;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
}
|
||||
|
||||
# ── Wazuh (443) ──────────────────────────────────────────
|
||||
server {
|
||||
listen 443 ssl default_server;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
access_log /var/log/nginx/wazuh_access.log;
|
||||
error_log /var/log/nginx/wazuh_error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass https://wazuh.dashboard:5601;
|
||||
proxy_ssl_verify off;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
}
|
||||
|
||||
# ── HTTP Redirect to HTTPS ───────────────────────────────
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Rebind services to private VPN IP (10.0.0.2)
|
||||
# Services: Wazuh Dashboard, Dozzle, Beszel, S3/RustFS
|
||||
#
|
||||
# IMPORTANT:
|
||||
# - Wazuh Manager stays on 0.0.0.0:1514 (agents need access)
|
||||
# - Other services bind to 10.0.0.2 (VPN only)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
VPN_IP="10.0.0.2"
|
||||
COMPOSE_FILE="/home/ubuntu/docker/docker-compose-security.yml"
|
||||
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
echo "[!] File not found: $COMPOSE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Rebinding services to VPN IP ($VPN_IP)..."
|
||||
echo " Config: $COMPOSE_FILE"
|
||||
|
||||
# ─── Wazuh Dashboard (443) ────────────────────────────────────
|
||||
echo "[*] Updating Wazuh Dashboard (443)..."
|
||||
sed -i 's|0\.0\.0\.0:443:|'"$VPN_IP"':443:|g' "$COMPOSE_FILE"
|
||||
|
||||
# ─── Dozzle (8080) ────────────────────────────────────────────
|
||||
echo "[*] Updating Dozzle (8080)..."
|
||||
sed -i 's|0\.0\.0\.0:8080:|'"$VPN_IP"':8080:|g' "$COMPOSE_FILE"
|
||||
|
||||
# ─── Beszel (9090) ────────────────────────────────────────────
|
||||
echo "[*] Updating Beszel (9090)..."
|
||||
sed -i 's|0\.0\.0\.0:9090:|'"$VPN_IP"':9090:|g' "$COMPOSE_FILE"
|
||||
|
||||
# ─── RustFS / S3 (9000, 9001) ─────────────────────────────────
|
||||
echo "[*] Updating RustFS/S3 (9000, 9001)..."
|
||||
sed -i 's|0\.0\.0\.0:9000:|'"$VPN_IP"':9000:|g' "$COMPOSE_FILE"
|
||||
sed -i 's|0\.0\.0\.0:9001:|'"$VPN_IP"':9001:|g' "$COMPOSE_FILE"
|
||||
|
||||
# ─── Keep Wazuh Manager on 0.0.0.0:1514 ───────────────────────
|
||||
# (agents need public access)
|
||||
|
||||
echo ""
|
||||
echo "[✓] Services rebound to $VPN_IP"
|
||||
echo ""
|
||||
echo "Verify changes:"
|
||||
grep -n "ports:" -A 2 "$COMPOSE_FILE" | grep -E "(443|8080|9090|9000|9001|1514)" || true
|
||||
|
||||
echo ""
|
||||
echo "Services now listening on:"
|
||||
echo " • 0.0.0.0:1514 — Wazuh Manager (agents)"
|
||||
echo " • $VPN_IP:443 — Wazuh Dashboard (VPN only)"
|
||||
echo " • $VPN_IP:8080 — Dozzle (VPN only)"
|
||||
echo " • $VPN_IP:9090 — Beszel (VPN only)"
|
||||
echo " • $VPN_IP:9000/9001 — RustFS (VPN only)"
|
||||
echo ""
|
||||
echo "Next: Restart services"
|
||||
echo " cd /home/ubuntu/docker"
|
||||
echo " docker compose -f docker-compose-security.yml down"
|
||||
echo " docker compose -f docker-compose-security.yml up -d"
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
nodes:
|
||||
indexer:
|
||||
- name: wazuh.indexer
|
||||
ip: "wazuh.indexer"
|
||||
server:
|
||||
- name: wazuh.manager
|
||||
ip: "wazuh.manager"
|
||||
dashboard:
|
||||
- name: wazuh.dashboard
|
||||
ip: "wazuh.dashboard"
|
||||
@@ -0,0 +1,12 @@
|
||||
server.host: 0.0.0.0
|
||||
server.port: 5601
|
||||
opensearch.hosts: https://wazuh.indexer:9200
|
||||
opensearch.ssl.verificationMode: certificate
|
||||
opensearch.ssl.certificateAuthorities: ["/usr/share/wazuh-dashboard/certs/root-ca.pem"]
|
||||
opensearch.requestHeadersWhitelist: ["securitytenant","Authorization"]
|
||||
opensearch_security.multitenancy.enabled: false
|
||||
opensearch_security.readonly_mode.roles: ["kibana_read_only"]
|
||||
server.ssl.enabled: true
|
||||
server.ssl.key: "/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem"
|
||||
server.ssl.certificate: "/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem"
|
||||
uiSettings.overrides.defaultRoute: /app/wz-home
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
_meta:
|
||||
type: "internalusers"
|
||||
config_version: 2
|
||||
|
||||
admin:
|
||||
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
|
||||
reserved: true
|
||||
backend_roles:
|
||||
- "admin"
|
||||
description: "Admin user"
|
||||
|
||||
kibanaserver:
|
||||
hash: "$2b$12$czi/zi7uht/K5.vcCvsXueFi6ot7GFwFfVBax7rCaNqBq1jhTOZ7u"
|
||||
reserved: true
|
||||
description: "Kibanaserver user"
|
||||
|
||||
kibanaro:
|
||||
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
|
||||
reserved: false
|
||||
backend_roles:
|
||||
- "kibanauser"
|
||||
- "readall"
|
||||
description: "Kibana read-only user"
|
||||
|
||||
logstash:
|
||||
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
|
||||
reserved: false
|
||||
backend_roles:
|
||||
- "logstash"
|
||||
description: "Logstash user"
|
||||
|
||||
readall:
|
||||
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
|
||||
reserved: false
|
||||
backend_roles:
|
||||
- "readall"
|
||||
description: "Readall user"
|
||||
|
||||
snapshotrestore:
|
||||
hash: "$2b$12$lNbkFg7B3IN.BXwZHjztteTDd3hTIvT/kTJhRFqfUY/228Bw45POO"
|
||||
reserved: false
|
||||
backend_roles:
|
||||
- "snapshotrestore"
|
||||
description: "Snapshotrestore user"
|
||||
@@ -0,0 +1,51 @@
|
||||
network.host: "0.0.0.0"
|
||||
node.name: "wazuh.indexer"
|
||||
cluster.name: "wazuh-cluster"
|
||||
|
||||
# Compatibilité Filebeat 7.x (libbeat) avec OpenSearch 2.x
|
||||
# Filebeat envoie _type dans les requêtes bulk — OpenSearch 2.x le rejette
|
||||
# sans ce flag.
|
||||
compatibility.override_main_response_version: true
|
||||
|
||||
path.data: /var/lib/wazuh-indexer
|
||||
path.logs: /var/log/wazuh-indexer
|
||||
|
||||
plugins.security.ssl.http.pemcert_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem
|
||||
plugins.security.ssl.http.pemkey_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.key
|
||||
plugins.security.ssl.http.pemtrustedcas_filepath: /usr/share/wazuh-indexer/config/certs/root-ca.pem
|
||||
plugins.security.ssl.transport.pemcert_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem
|
||||
plugins.security.ssl.transport.pemkey_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.key
|
||||
plugins.security.ssl.transport.pemtrustedcas_filepath: /usr/share/wazuh-indexer/config/certs/root-ca.pem
|
||||
plugins.security.ssl.http.enabled: true
|
||||
plugins.security.ssl.transport.enforce_hostname_verification: false
|
||||
plugins.security.ssl.transport.resolve_hostname: false
|
||||
|
||||
plugins.security.authcz.admin_dn:
|
||||
- "CN=admin,OU=Wazuh,O=Wazuh,L=California,C=US"
|
||||
plugins.security.nodes_dn:
|
||||
- "CN=wazuh.indexer,OU=Wazuh,O=Wazuh,L=California,C=US"
|
||||
|
||||
plugins.security.check_snapshot_restore_write_privileges: true
|
||||
plugins.security.enable_snapshot_restore_privilege: true
|
||||
plugins.security.restapi.roles_enabled:
|
||||
- "all_access"
|
||||
- "security_rest_api_access"
|
||||
|
||||
plugins.security.system_indices.enabled: true
|
||||
plugins.security.system_indices.indices:
|
||||
- ".opendistro-alerting-config"
|
||||
- ".opendistro-alerting-alert*"
|
||||
- ".opendistro-anomaly-results*"
|
||||
- ".opendistro-anomaly-detector*"
|
||||
- ".opendistro-anomaly-checkpoints"
|
||||
- ".opendistro-anomaly-detection-state"
|
||||
- ".opendistro-reports-*"
|
||||
- ".opendistro-notifications-*"
|
||||
- ".opendistro-notebooks"
|
||||
- ".opensearch-observability"
|
||||
- ".opendistro-asynchronous-search-response*"
|
||||
- ".replication-metadata-store"
|
||||
|
||||
discovery.type: single-node
|
||||
plugins.security.allow_default_init_securityindex: true
|
||||
cluster.routing.allocation.disk.threshold_enabled: false
|
||||
@@ -0,0 +1,63 @@
|
||||
<!-- Configuration centralisée distribuée aux agents Wazuh -->
|
||||
<!-- Déposée dans /var/ossec/etc/shared/default/agent.conf sur le manager -->
|
||||
|
||||
<agent_config>
|
||||
|
||||
<!-- ── Logs nginx ──────────────────────────────────────────────────── -->
|
||||
<localfile>
|
||||
<log_format>apache</log_format>
|
||||
<location>/var/log/nginx/access.log</location>
|
||||
</localfile>
|
||||
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/log/nginx/error.log</location>
|
||||
</localfile>
|
||||
|
||||
<!-- ── Auth system ───────────────────────────────────────────────── -->
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/log/auth.log</location>
|
||||
</localfile>
|
||||
|
||||
<!-- ── Syslog ────────────────────────────────────────────────────── -->
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/log/syslog</location>
|
||||
</localfile>
|
||||
|
||||
<!-- ── Unattended-upgrades (patchs de sécurité auto) ───────────────── -->
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/log/unattended-upgrades/unattended-upgrades.log</location>
|
||||
</localfile>
|
||||
|
||||
<!-- ── auditd (regles CIS 6.2.3.x deployees via ansible/hardening) ─── -->
|
||||
<localfile>
|
||||
<log_format>audit</log_format>
|
||||
<location>/var/log/audit/audit.log</location>
|
||||
</localfile>
|
||||
|
||||
</agent_config>
|
||||
|
||||
<!-- ── Logs backend Go (prod-mln) ────────────────────────────────── -->
|
||||
<!-- "command" (pas full_command) = chaque ligne de docker logs → event séparé -->
|
||||
<!-- Chaque ligne [GIN] devient son propre event → decoder peut extraire srcip/url/status -->
|
||||
<agent_config name="prod-mln">
|
||||
<localfile>
|
||||
<log_format>command</log_format>
|
||||
<command>docker logs --since 65s gestion-backend 2>&1</command>
|
||||
<alias>backend</alias>
|
||||
<frequency>60</frequency>
|
||||
</localfile>
|
||||
</agent_config>
|
||||
|
||||
<!-- ── Logs backend Go (pre-prod-mln) ───────────────────────────── -->
|
||||
<agent_config name="pre-prod-mln">
|
||||
<localfile>
|
||||
<log_format>command</log_format>
|
||||
<command>docker logs --since 65s gestion-backend 2>&1</command>
|
||||
<alias>backend</alias>
|
||||
<frequency>60</frequency>
|
||||
</localfile>
|
||||
</agent_config>
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<!-- ModSecurity decoder — non utilisé, remplacé par règles json+location -->
|
||||
<decoder name="waf-modsec-unused">
|
||||
<prematch>UNUSED_MODSEC_PLACEHOLDER_NEVER_MATCHES</prematch>
|
||||
</decoder>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!-- WireGuard Decoders
|
||||
Parent: kernel (built-in, program_name=^kernel)
|
||||
Decoders enfants directs de kernel (Wazuh: 2 niveaux max) -->
|
||||
|
||||
<decoder name="wireguard-handshake-init">
|
||||
<parent>kernel</parent>
|
||||
<prematch>wireguard: wg0: Sending handshake initiation</prematch>
|
||||
<regex type="pcre2">wireguard: wg0: Sending handshake initiation to peer (\d+) \(([\d.]+):\d+\)</regex>
|
||||
<order>id,srcip</order>
|
||||
</decoder>
|
||||
|
||||
<decoder name="wireguard-handshake-resp-sent">
|
||||
<parent>kernel</parent>
|
||||
<prematch>wireguard: wg0: Sending handshake response</prematch>
|
||||
<regex type="pcre2">wireguard: wg0: Sending handshake response to peer (\d+) \(([\d.]+):\d+\)</regex>
|
||||
<order>id,srcip</order>
|
||||
</decoder>
|
||||
|
||||
<decoder name="wireguard-handshake-resp-recv">
|
||||
<parent>kernel</parent>
|
||||
<prematch>wireguard: wg0: Receiving handshake response</prematch>
|
||||
<regex type="pcre2">wireguard: wg0: Receiving handshake response from peer (\d+) \(([\d.]+):\d+\)</regex>
|
||||
<order>id,srcip</order>
|
||||
</decoder>
|
||||
|
||||
<decoder name="wireguard-keepalive">
|
||||
<parent>kernel</parent>
|
||||
<prematch>wireguard: wg0: </prematch>
|
||||
<regex type="pcre2">wireguard: wg0: \S+ keepalive packet \S+ peer (\d+) \(([\d.]+):\d+\)</regex>
|
||||
<order>id,srcip</order>
|
||||
</decoder>
|
||||
|
||||
<decoder name="wireguard-timeout">
|
||||
<parent>kernel</parent>
|
||||
<prematch>wireguard: wg0: Handshake for peer</prematch>
|
||||
<regex type="pcre2">wireguard: wg0: Handshake for peer (\d+)[^(]+\(([\d.]+):\d+\).+retrying \(try (\d+)\)</regex>
|
||||
<order>id,srcip,extra_data</order>
|
||||
</decoder>
|
||||
|
||||
<decoder name="wireguard-roaming">
|
||||
<parent>kernel</parent>
|
||||
<prematch>wireguard: wg0: Peer </prematch>
|
||||
<regex type="pcre2">wireguard: wg0: Peer (\d+).+roaming to ([\d.]+):\d+</regex>
|
||||
<order>id,srcip</order>
|
||||
</decoder>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# ─── Copie de la config custom (montée hors du volume wazuh_etc) ──────────────
|
||||
CUSTOM=/wazuh-custom
|
||||
|
||||
cp "$CUSTOM/ossec.conf" /var/ossec/etc/ossec.conf
|
||||
|
||||
mkdir -p /var/ossec/etc/decoders /var/ossec/etc/rules /var/ossec/etc/lists
|
||||
cp "$CUSTOM/decoders/"*.xml /var/ossec/etc/decoders/ 2>/dev/null || true
|
||||
cp "$CUSTOM/rules/"*.xml /var/ossec/etc/rules/ 2>/dev/null || true
|
||||
cp "$CUSTOM/lists/"* /var/ossec/etc/lists/ 2>/dev/null || true
|
||||
|
||||
# ─── Agent config partagée (distribuée aux agents) ───────────────────────────
|
||||
if [ -f "$CUSTOM/agent.conf" ]; then
|
||||
mkdir -p /var/ossec/etc/shared/default
|
||||
cp "$CUSTOM/agent.conf" /var/ossec/etc/shared/default/agent.conf
|
||||
fi
|
||||
|
||||
# ─── Active-response scripts ──────────────────────────────────────────────────
|
||||
if [ -d "$CUSTOM/active-response" ]; then
|
||||
for script in "$CUSTOM/active-response/"*.sh; do
|
||||
[ -f "$script" ] || continue
|
||||
dest="/var/ossec/active-response/bin/$(basename "$script")"
|
||||
cp "$script" "$dest"
|
||||
chmod 750 "$dest"
|
||||
chown root:wazuh "$dest" 2>/dev/null || true
|
||||
echo "Active-response déployé: $dest"
|
||||
done
|
||||
fi
|
||||
|
||||
# ─── Démarrage Wazuh Manager (entrypoint original) ────────────────────────────
|
||||
exec /init
|
||||
@@ -0,0 +1,23 @@
|
||||
<!-- ClamAV Log Monitoring -->
|
||||
<!-- Collect ClamAV logs for antivirus detection -->
|
||||
|
||||
<!-- ClamAV Main Log -->
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/log/clamav/clamav.log</location>
|
||||
<source_type>log</source_type>
|
||||
</localfile>
|
||||
|
||||
<!-- ClamAV Freshclam (Signature Updates) Log -->
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/log/clamav/freshclam.log</location>
|
||||
<source_type>log</source_type>
|
||||
</localfile>
|
||||
|
||||
<!-- ClamAV Alert Log (High Priority Detections) -->
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/log/clamav/alert.log</location>
|
||||
<source_type>log</source_type>
|
||||
</localfile>
|
||||
+960
@@ -0,0 +1,960 @@
|
||||
<ossec_config>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
GLOBAL
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<global>
|
||||
<jsonout_output>yes</jsonout_output>
|
||||
<alerts_log>yes</alerts_log>
|
||||
<logall>no</logall>
|
||||
<logall_json>no</logall_json>
|
||||
<email_notification>no</email_notification>
|
||||
<agents_disconnection_time>10m</agents_disconnection_time>
|
||||
<agents_disconnection_alert_time>0</agents_disconnection_alert_time>
|
||||
</global>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
ALERTES
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<alerts>
|
||||
<log_alert_level>3</log_alert_level>
|
||||
</alerts>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
LOGGING
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<logging>
|
||||
<log_format>plain</log_format>
|
||||
</logging>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
COMMUNICATION AGENTS (port 1514 TCP)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<remote>
|
||||
<connection>secure</connection>
|
||||
<port>1514</port>
|
||||
<protocol>tcp</protocol>
|
||||
<queue_size>131072</queue_size>
|
||||
</remote>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
POLICY MONITORING
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<rootcheck>
|
||||
<disabled>no</disabled>
|
||||
<check_files>yes</check_files>
|
||||
<check_trojans>yes</check_trojans>
|
||||
<check_dev>yes</check_dev>
|
||||
<check_sys>yes</check_sys>
|
||||
<check_pids>yes</check_pids>
|
||||
<check_ports>yes</check_ports>
|
||||
<check_if>yes</check_if>
|
||||
<frequency>43200</frequency>
|
||||
<rootkit_files>etc/rootcheck/rootkit_files.txt</rootkit_files>
|
||||
<rootkit_trojans>etc/rootcheck/rootkit_trojans.txt</rootkit_trojans>
|
||||
<skip_nfs>yes</skip_nfs>
|
||||
</rootcheck>
|
||||
|
||||
<wodle name="cis-cat">
|
||||
<disabled>yes</disabled>
|
||||
<timeout>1800</timeout>
|
||||
<interval>1d</interval>
|
||||
<scan-on-start>yes</scan-on-start>
|
||||
<java_path>wodles/java</java_path>
|
||||
<ciscat_path>wodles/ciscat</ciscat_path>
|
||||
</wodle>
|
||||
|
||||
<wodle name="osquery">
|
||||
<disabled>yes</disabled>
|
||||
<run_daemon>yes</run_daemon>
|
||||
<log_path>/var/log/osquery/osqueryd.results.log</log_path>
|
||||
<config_path>/etc/osquery/osquery.conf</config_path>
|
||||
<add_labels>yes</add_labels>
|
||||
</wodle>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
SYSTEM INVENTORY
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<wodle name="syscollector">
|
||||
<disabled>no</disabled>
|
||||
<interval>1h</interval>
|
||||
<scan_on_start>yes</scan_on_start>
|
||||
<hardware>yes</hardware>
|
||||
<os>yes</os>
|
||||
<network>yes</network>
|
||||
<packages>yes</packages>
|
||||
<ports all="yes">yes</ports>
|
||||
<processes>yes</processes>
|
||||
<synchronization>
|
||||
<max_eps>10</max_eps>
|
||||
</synchronization>
|
||||
</wodle>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
SECURITY CONFIGURATION ASSESSMENT
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<sca>
|
||||
<enabled>yes</enabled>
|
||||
<scan_on_start>yes</scan_on_start>
|
||||
<interval>12h</interval>
|
||||
<skip_nfs>yes</skip_nfs>
|
||||
</sca>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
VULNERABILITY DETECTION
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<vulnerability-detection>
|
||||
<enabled>yes</enabled>
|
||||
<index-status>yes</index-status>
|
||||
<feed-update-interval>60m</feed-update-interval>
|
||||
</vulnerability-detection>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
INDEXER (Wazuh Indexer / OpenSearch)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<indexer>
|
||||
<enabled>yes</enabled>
|
||||
<hosts>
|
||||
<host>https://wazuh.indexer:9200</host>
|
||||
</hosts>
|
||||
<ssl>
|
||||
<certificate_authorities>
|
||||
<ca>/etc/ssl/root-ca.pem</ca>
|
||||
</certificate_authorities>
|
||||
<certificate>/etc/ssl/filebeat.pem</certificate>
|
||||
<key>/etc/ssl/filebeat.key</key>
|
||||
</ssl>
|
||||
</indexer>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
FILE INTEGRITY MONITORING
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<syscheck>
|
||||
<disabled>no</disabled>
|
||||
<frequency>43200</frequency>
|
||||
<scan_on_start>yes</scan_on_start>
|
||||
<alert_new_files>yes</alert_new_files>
|
||||
<auto_ignore frequency="10" timeframe="3600">no</auto_ignore>
|
||||
<directories>/etc,/usr/bin,/usr/sbin</directories>
|
||||
<directories>/bin,/sbin,/boot</directories>
|
||||
<ignore>/etc/mtab</ignore>
|
||||
<ignore>/etc/hosts.deny</ignore>
|
||||
<ignore>/etc/mail/statistics</ignore>
|
||||
<ignore>/etc/random-seed</ignore>
|
||||
<ignore>/etc/random.seed</ignore>
|
||||
<ignore>/etc/adjtime</ignore>
|
||||
<ignore>/etc/httpd/logs</ignore>
|
||||
<ignore>/etc/utmpx</ignore>
|
||||
<ignore>/etc/wtmpx</ignore>
|
||||
<ignore>/etc/cups/certs</ignore>
|
||||
<ignore>/etc/dumpdates</ignore>
|
||||
<ignore>/etc/svc/volatile</ignore>
|
||||
<ignore type="sregex">.log$|.swp$</ignore>
|
||||
<nodiff>/etc/ssl/private.key</nodiff>
|
||||
<skip_nfs>yes</skip_nfs>
|
||||
<skip_dev>yes</skip_dev>
|
||||
<skip_proc>yes</skip_proc>
|
||||
<skip_sys>yes</skip_sys>
|
||||
<process_priority>10</process_priority>
|
||||
<max_eps>100</max_eps>
|
||||
<synchronization>
|
||||
<enabled>yes</enabled>
|
||||
<interval>5m</interval>
|
||||
<max_interval>1h</max_interval>
|
||||
<max_eps>10</max_eps>
|
||||
</synchronization>
|
||||
</syscheck>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
ACTIVE RESPONSE — WHITELIST
|
||||
90.50.148.138 : IP publique admin — jamais bloquée
|
||||
90.120.69.13 : IP publique admin — jamais bloquée
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<global>
|
||||
<white_list>127.0.0.1</white_list>
|
||||
<white_list>^localhost.localdomain$</white_list>
|
||||
<white_list>90.50.148.138</white_list>
|
||||
<white_list>90.120.69.13</white_list>
|
||||
<white_list>10.0.0.3</white_list>
|
||||
</global>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
ACTIVE RESPONSE — COMMANDES
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<command>
|
||||
<name>disable-account</name>
|
||||
<executable>disable-account</executable>
|
||||
<timeout_allowed>yes</timeout_allowed>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<name>restart-wazuh</name>
|
||||
<executable>restart-wazuh</executable>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<name>firewall-drop</name>
|
||||
<executable>firewall-drop</executable>
|
||||
<timeout_allowed>yes</timeout_allowed>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<name>host-deny</name>
|
||||
<executable>host-deny</executable>
|
||||
<timeout_allowed>yes</timeout_allowed>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<name>route-null</name>
|
||||
<executable>route-null</executable>
|
||||
<timeout_allowed>yes</timeout_allowed>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<name>win_route-null</name>
|
||||
<executable>route-null.exe</executable>
|
||||
<timeout_allowed>yes</timeout_allowed>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<name>netsh</name>
|
||||
<executable>netsh.exe</executable>
|
||||
<timeout_allowed>yes</timeout_allowed>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<name>clamav-quarantine</name>
|
||||
<executable>clamav-quarantine</executable>
|
||||
<timeout_allowed>no</timeout_allowed>
|
||||
</command>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
ACTIVE RESPONSE — RÈGLES
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- SSH mauvais mot de passe → ban PERMANENT (1 tentative suffit) -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>5710,5711,5712,5716,5720,5760,5763</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Brute-force SSH (plusieurs échecs) → ban PERMANENT -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_group>authentication_failures</rules_group>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Échec auth générique → ban PERMANENT -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_group>authentication_failed</rules_group>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- SSH mauvais mot de passe → host-deny PERMANENT (double protection) -->
|
||||
<active-response>
|
||||
<command>host-deny</command>
|
||||
<location>local</location>
|
||||
<rules_id>5710,5711,5712,5716,5720,5760,5763</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Attaques web / injection (niveau ≥ 7) → firewall-drop 5 min -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<level>7</level>
|
||||
<rules_group>web,attack</rules_group>
|
||||
<timeout>300</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Scan de ports → firewall-drop 1 heure -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>1002,2502,2503,2504</rules_id>
|
||||
<timeout>3600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- SSH brute-force → host-deny PERMANENT (double protection) -->
|
||||
<active-response>
|
||||
<command>host-deny</command>
|
||||
<location>local</location>
|
||||
<rules_group>authentication_failures,authentication_failed</rules_group>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- ModSecurity SQLi / XSS / LFI → firewall-drop 6 heures -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100202,100203,100205</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- ModSecurity RCE / anomalie critique → firewall-drop 24 heures -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100204,100206</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- ClamAV virus détecté → quarantaine du fichier infecté -->
|
||||
<active-response>
|
||||
<command>clamav-quarantine</command>
|
||||
<location>local</location>
|
||||
<rules_id>52502</rules_id>
|
||||
</active-response>
|
||||
|
||||
<!-- Attaque soutenue ModSec (5 blocs / 2 min) → firewall-drop 24 heures -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100210</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- SSH brute-force (3 tentatives / 60s) → firewall-drop 1 heure -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100300</rules_id>
|
||||
<timeout>3600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- SSH scan de comptes (3 users inexistants / 60s) → firewall-drop 24 heures -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100301</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Sudo brute-force (3 échecs / 5 min) → disable-account 2h + firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>disable-account</command>
|
||||
<location>local</location>
|
||||
<rules_id>100310</rules_id>
|
||||
<timeout>7200</timeout>
|
||||
</active-response>
|
||||
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100310</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
OWASP Top 10 — Active Response (ModSecurity)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- OWASP#3 SQL Injection (100220) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100220</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 SQL Injection brute-force (100290) → firewall-drop 24h + host-deny -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100290</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<active-response>
|
||||
<command>host-deny</command>
|
||||
<location>local</location>
|
||||
<rules_id>100290</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 LDAP Injection (100221) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100221</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 Command Injection (100222) → firewall-drop 24h + host-deny permanent -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100222</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<active-response>
|
||||
<command>host-deny</command>
|
||||
<location>local</location>
|
||||
<rules_id>100222</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 Command Injection brute-force CRITICAL (100292) → permanent ban -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100292</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<active-response>
|
||||
<command>host-deny</command>
|
||||
<location>local</location>
|
||||
<rules_id>100292</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 PHP/Java Injection (100223, 100224) → firewall-drop 12h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100223,100224</rules_id>
|
||||
<timeout>43200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#6 RCE / Exploit attempts (100230) → firewall-drop 24h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100230</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#7 XSS attacks (100240) → firewall-drop 4h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100240</rules_id>
|
||||
<timeout>14400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#7 XSS brute-force (100291) → firewall-drop 24h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100291</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#7 HTTP Header Injection (100241) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100241</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#1 Path Traversal / LFI (100250) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100250</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#1 Path Traversal brute-force (100293) → firewall-drop 24h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100293</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#1 Remote File Inclusion (100251) → firewall-drop 12h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100251</rules_id>
|
||||
<timeout>43200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#1 Restricted File Access (100252) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100252</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#8 XXE (XML External Entity) (100260) → firewall-drop 12h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100260</rules_id>
|
||||
<timeout>43200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#9 Scanner Detection (100270) → firewall-drop 2h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100270</rules_id>
|
||||
<timeout>7200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP SSRF (100280) → firewall-drop 12h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100280</rules_id>
|
||||
<timeout>43200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP File Upload (100281) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>local</location>
|
||||
<rules_id>100281</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<active-response>
|
||||
<disabled>no</disabled>
|
||||
<command>host-deny</command>
|
||||
<location>all</location>
|
||||
<rules_id>100210</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100204,100206</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>5710,5711,5712,5716,5720,5760,5763</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Brute-force SSH (plusieurs échecs) → ban PERMANENT -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_group>authentication_failures</rules_group>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Échec auth générique → ban PERMANENT -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_group>authentication_failed</rules_group>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- SSH mauvais mot de passe → host-deny PERMANENT (double protection) -->
|
||||
<active-response>
|
||||
<command>host-deny</command>
|
||||
<location>all</location>
|
||||
<rules_id>5710,5711,5712,5716,5720,5760,5763</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Attaques web / injection (niveau ≥ 7) → firewall-drop 5 min -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<level>7</level>
|
||||
<rules_group>web,attack</rules_group>
|
||||
<timeout>300</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- Scan de ports → firewall-drop 1 heure -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>1002,2502,2503,2504</rules_id>
|
||||
<timeout>3600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- SSH brute-force → host-deny PERMANENT (double protection) -->
|
||||
<active-response>
|
||||
<command>host-deny</command>
|
||||
<location>all</location>
|
||||
<rules_group>authentication_failures,authentication_failed</rules_group>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- ModSecurity SQLi / XSS / LFI → firewall-drop 6 heures -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100202,100203,100205</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- SSH brute-force (3 tentatives / 60s) → firewall-drop 1 heure sur tous les agents -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100300</rules_id>
|
||||
<timeout>3600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- SSH scan de comptes → firewall-drop 24 heures sur tous les agents -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100301</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
OWASP Top 10 — Active Response (ModSecurity - location=all)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- OWASP#3 SQLi (100220) → firewall-drop 6h (tous agents) -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100220</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 SQLi brute-force (100290) → firewall-drop 24h (tous agents) -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100290</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 LDAP (100221) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100221</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 Command Injection (100222) → firewall-drop 24h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100222</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 Command Injection CRITICAL (100292) → permanent ban -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100292</rules_id>
|
||||
<timeout>0</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#3 PHP/Java (100223, 100224) → firewall-drop 12h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100223,100224</rules_id>
|
||||
<timeout>43200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#6 RCE/Exploit (100230) → firewall-drop 24h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100230</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#7 XSS (100240) → firewall-drop 4h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100240</rules_id>
|
||||
<timeout>14400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#7 XSS brute-force (100291) → firewall-drop 24h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100291</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#7 Header Injection (100241) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100241</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#1 Path Traversal/LFI (100250) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100250</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#1 Path Traversal brute-force (100293) → firewall-drop 24h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100293</rules_id>
|
||||
<timeout>86400</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#1 RFI (100251) → firewall-drop 12h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100251</rules_id>
|
||||
<timeout>43200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#1 File Access (100252) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100252</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#8 XXE (100260) → firewall-drop 12h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100260</rules_id>
|
||||
<timeout>43200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP#9 Scanner (100270) → firewall-drop 2h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100270</rules_id>
|
||||
<timeout>7200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP SSRF (100280) → firewall-drop 12h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100280</rules_id>
|
||||
<timeout>43200</timeout>
|
||||
</active-response>
|
||||
|
||||
<!-- OWASP File Upload (100281) → firewall-drop 6h -->
|
||||
<active-response>
|
||||
<command>firewall-drop</command>
|
||||
<location>all</location>
|
||||
<rules_id>100281</rules_id>
|
||||
<timeout>21600</timeout>
|
||||
</active-response>
|
||||
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
LOG ANALYSIS (commandes système)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<localfile>
|
||||
<log_format>command</log_format>
|
||||
<command>df -P</command>
|
||||
<frequency>360</frequency>
|
||||
</localfile>
|
||||
|
||||
<localfile>
|
||||
<log_format>full_command</log_format>
|
||||
<command>netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d</command>
|
||||
<alias>netstat listening ports</alias>
|
||||
<frequency>360</frequency>
|
||||
</localfile>
|
||||
|
||||
<localfile>
|
||||
<log_format>full_command</log_format>
|
||||
<command>last -n 20</command>
|
||||
<frequency>360</frequency>
|
||||
</localfile>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
RULESET
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<ruleset>
|
||||
<!-- Règles et decoders natifs Wazuh -->
|
||||
<decoder_dir>ruleset/decoders</decoder_dir>
|
||||
<rule_dir>ruleset/rules</rule_dir>
|
||||
<!-- Règles exclues — non pertinentes pour ce stack -->
|
||||
<rule_exclude>0025-sendmail_rules.xml</rule_exclude>
|
||||
<rule_exclude>0030-postfix_rules.xml</rule_exclude>
|
||||
<rule_exclude>0035-spamd_rules.xml</rule_exclude>
|
||||
<rule_exclude>0040-imapd_rules.xml</rule_exclude>
|
||||
<rule_exclude>0045-mailscanner_rules.xml</rule_exclude>
|
||||
<rule_exclude>0050-ms-exchange_rules.xml</rule_exclude>
|
||||
<rule_exclude>0055-courier_rules.xml</rule_exclude>
|
||||
<rule_exclude>0065-pix_rules.xml</rule_exclude>
|
||||
<rule_exclude>0070-netscreenfw_rules.xml</rule_exclude>
|
||||
<rule_exclude>0075-cisco-ios_rules.xml</rule_exclude>
|
||||
<rule_exclude>0080-sonicwall_rules.xml</rule_exclude>
|
||||
<rule_exclude>0090-telnetd_rules.xml</rule_exclude>
|
||||
<rule_exclude>0100-solaris_bsm_rules.xml</rule_exclude>
|
||||
<rule_exclude>0105-asterisk_rules.xml</rule_exclude>
|
||||
<rule_exclude>0110-ms_dhcp_rules.xml</rule_exclude>
|
||||
<rule_exclude>0115-arpwatch_rules.xml</rule_exclude>
|
||||
<rule_exclude>0130-trend-osce_rules.xml</rule_exclude>
|
||||
<rule_exclude>0135-hordeimp_rules.xml</rule_exclude>
|
||||
<rule_exclude>0155-dovecot_rules.xml</rule_exclude>
|
||||
<rule_exclude>0160-vmpop3d_rules.xml</rule_exclude>
|
||||
<rule_exclude>0165-vpopmail_rules.xml</rule_exclude>
|
||||
<rule_exclude>0170-ftpd_rules.xml</rule_exclude>
|
||||
<rule_exclude>0175-proftpd_rules.xml</rule_exclude>
|
||||
<rule_exclude>0185-vsftpd_rules.xml</rule_exclude>
|
||||
<rule_exclude>0190-ms_ftpd_rules.xml</rule_exclude>
|
||||
<rule_exclude>0195-named_rules.xml</rule_exclude>
|
||||
<rule_exclude>0215-policy_rules.xml</rule_exclude>
|
||||
<rule_exclude>0750-github_rules.xml</rule_exclude>
|
||||
<rule_exclude>0250-apache_rules.xml</rule_exclude>
|
||||
<rule_exclude>0255-zeus_rules.xml</rule_exclude>
|
||||
<rule_exclude>0265-php_rules.xml</rule_exclude>
|
||||
<rule_exclude>0275-squid_rules.xml</rule_exclude>
|
||||
<rule_exclude>0295-mysql_rules.xml</rule_exclude>
|
||||
<rule_exclude>0305-dropbear_rules.xml</rule_exclude>
|
||||
<rule_exclude>0315-apparmor_rules.xml</rule_exclude>
|
||||
<rule_exclude>0350-amazon_rules.xml</rule_exclude>
|
||||
<rule_exclude>0360-serv-u_rules.xml</rule_exclude>
|
||||
<rule_exclude>0385-oscap_rules.xml</rule_exclude>
|
||||
<rule_exclude>0390-fortiddos_rules.xml</rule_exclude>
|
||||
<rule_exclude>0391-fortigate_rules.xml</rule_exclude>
|
||||
<rule_exclude>0392-fortimail_rules.xml</rule_exclude>
|
||||
<rule_exclude>0393-fortiauth_rules.xml</rule_exclude>
|
||||
<rule_exclude>0120-symantec-av_rules.xml</rule_exclude>
|
||||
<rule_exclude>0125-symantec-ws_rules.xml</rule_exclude>
|
||||
<rule_exclude>0395-hp_rules.xml</rule_exclude>
|
||||
<rule_exclude>0405-rsa-auth-manager_rules.xml</rule_exclude>
|
||||
<rule_exclude>0410-imperva_rules.xml</rule_exclude>
|
||||
<rule_exclude>0415-sophos_rules.xml</rule_exclude>
|
||||
<rule_exclude>0990-amazon-security-lake_rules.xml</rule_exclude>
|
||||
<rule_exclude>0435-ms_logs_rules.xml</rule_exclude>
|
||||
<rule_exclude>0445-identity_guard_rules.xml</rule_exclude>
|
||||
<rule_exclude>0450-mongodb_rules.xml</rule_exclude>
|
||||
<rule_exclude>0460-jenkins_rules.xml</rule_exclude>
|
||||
<rule_exclude>0470-vshell_rules.xml</rule_exclude>
|
||||
<rule_exclude>0475-suricata_rules.xml</rule_exclude>
|
||||
<rule_exclude>0480-qualysguard_rules.xml</rule_exclude>
|
||||
<rule_exclude>0485-cylance_rules.xml</rule_exclude>
|
||||
<rule_exclude>0700-paloalto_rules.xml</rule_exclude>
|
||||
<rule_exclude>0500-owncloud_rules.xml</rule_exclude>
|
||||
<rule_exclude>0505-vuls_rules.xml</rule_exclude>
|
||||
<rule_exclude>0525-openvas_rules.xml</rule_exclude>
|
||||
<rule_exclude>0530-mysql_audit_rules.xml</rule_exclude>
|
||||
<rule_exclude>0535-mariadb_rules.xml</rule_exclude>
|
||||
<rule_exclude>0540-pfsense_rules.xml</rule_exclude>
|
||||
<rule_exclude>0545-osquery_rules.xml</rule_exclude>
|
||||
<rule_exclude>0550-kaspersky_rules.xml</rule_exclude>
|
||||
<rule_exclude>0555-azure_rules.xml</rule_exclude>
|
||||
<rule_exclude>0565-ms_ipsec_rules.xml</rule_exclude>
|
||||
<rule_exclude>0575-win-base_rules.xml</rule_exclude>
|
||||
<rule_exclude>0580-win-security_rules.xml</rule_exclude>
|
||||
<rule_exclude>0585-win-application_rules.xml</rule_exclude>
|
||||
<rule_exclude>0590-win-system_rules.xml</rule_exclude>
|
||||
<rule_exclude>0601-win-vipre_rules.xml</rule_exclude>
|
||||
<rule_exclude>0602-win-wfirewall_rules.xml</rule_exclude>
|
||||
<rule_exclude>0610-win-ms_logs_rules.xml</rule_exclude>
|
||||
<rule_exclude>0630-nextcloud_rules.xml</rule_exclude>
|
||||
<rule_exclude>0675-panda-paps_rules.xml</rule_exclude>
|
||||
<rule_exclude>0680-checkpoint-smart1_rules.xml</rule_exclude>
|
||||
<rule_exclude>0770-gitlab_rules.xml</rule_exclude>
|
||||
<rule_exclude>0775-arbor_rules.xml</rule_exclude>
|
||||
<rule_exclude>0780-fireeye_rules.xml</rule_exclude>
|
||||
<rule_exclude>0785-huawei-usg_rules.xml</rule_exclude>
|
||||
<rule_exclude>0800-sysmon_id_1.xml</rule_exclude>
|
||||
<rule_exclude>0810-sysmon_id_3.xml</rule_exclude>
|
||||
<rule_exclude>0820-sysmon_id_7.xml</rule_exclude>
|
||||
<rule_exclude>0830-sysmon_id_11.xml</rule_exclude>
|
||||
<rule_exclude>0840-win_event_channel.xml</rule_exclude>
|
||||
<rule_exclude>0860-sysmon_id_13.xml</rule_exclude>
|
||||
<rule_exclude>0870-sysmon_id_8.xml</rule_exclude>
|
||||
<rule_exclude>0905-cisco-ftd_rules.xml</rule_exclude>
|
||||
<rule_exclude>0690-gcp_rules.xml</rule_exclude>
|
||||
<rule_exclude>0910-ms-exchange-proxylogon_rules.xml</rule_exclude>
|
||||
<rule_exclude>0920-oracledb_rules.xml</rule_exclude>
|
||||
<rule_exclude>0925-eset-remote_rules.xml</rule_exclude>
|
||||
<rule_exclude>0955-WEF-baseline_rules.xml</rule_exclude>
|
||||
<rule_exclude>0960-macos_rules.xml</rule_exclude>
|
||||
<rule_exclude>0995-microsoft-graph_rules.xml</rule_exclude>
|
||||
<rule_exclude>0755-office365_rules.xml</rule_exclude>
|
||||
<rule_exclude>0140-roundcube_rules.xml</rule_exclude>
|
||||
<rule_exclude>0998-aws-security-hub-rules.xml</rule_exclude>
|
||||
<rule_exclude>0400-openvpn_rules.xml</rule_exclude>
|
||||
<list>etc/lists/audit-keys</list>
|
||||
<list>etc/lists/amazon/aws-eventnames</list>
|
||||
<list>etc/lists/security-eventchannel</list>
|
||||
<list>etc/lists/malicious-ioc/malicious-ip</list>
|
||||
<list>etc/lists/malicious-ioc/malicious-domains</list>
|
||||
<list>etc/lists/malicious-ioc/malware-hashes</list>
|
||||
<list>etc/lists/wireguard-trusted-ips</list>
|
||||
<!-- Decoders et règles custom ModSecurity -->
|
||||
<decoder_dir>etc/decoders</decoder_dir>
|
||||
<rule_dir>etc/rules</rule_dir>
|
||||
</ruleset>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
RULE TEST
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<rule_test>
|
||||
<enabled>yes</enabled>
|
||||
<threads>1</threads>
|
||||
<max_sessions>64</max_sessions>
|
||||
<session_timeout>15m</session_timeout>
|
||||
</rule_test>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
AUTHENTIFICATION AGENTS (wazuh-authd)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<auth>
|
||||
<disabled>no</disabled>
|
||||
<port>1515</port>
|
||||
<use_source_ip>no</use_source_ip>
|
||||
<purge>yes</purge>
|
||||
<use_password>no</use_password>
|
||||
<ciphers>HIGH:!ADH:!EXP:!MD5:!RC4:!3DES:!CAMELLIA:@STRENGTH</ciphers>
|
||||
<ssl_verify_host>no</ssl_verify_host>
|
||||
<ssl_manager_cert>etc/sslmanager.cert</ssl_manager_cert>
|
||||
<ssl_manager_key>etc/sslmanager.key</ssl_manager_key>
|
||||
<ssl_auto_negotiate>no</ssl_auto_negotiate>
|
||||
</auth>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
CLUSTER (standalone — désactivé)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
<cluster>
|
||||
<name>wazuh</name>
|
||||
<node_name>node01</node_name>
|
||||
<node_type>master</node_type>
|
||||
<key>aa093264ef885029653eea20dfcf51ae</key>
|
||||
<port>1516</port>
|
||||
<bind_addr>0.0.0.0</bind_addr>
|
||||
<nodes>
|
||||
<node>wazuh.manager</node>
|
||||
</nodes>
|
||||
<hidden>no</hidden>
|
||||
<disabled>yes</disabled>
|
||||
</cluster>
|
||||
|
||||
</ossec_config>
|
||||
|
||||
<ossec_config>
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/ossec/logs/active-responses.log</location>
|
||||
</localfile>
|
||||
</ossec_config>
|
||||
@@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<group name="clamav">
|
||||
<rule id="100500" level="0">
|
||||
<decoded_as>syslog</decoded_as>
|
||||
<program_name>clam</program_name>
|
||||
<description>ClamAV message grouping</description>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Malware Detections (Critical) -->
|
||||
<rule id="100501" level="15">
|
||||
<parent>100500</parent>
|
||||
<regex>FOUND|Infected|detected</regex>
|
||||
<description>ClamAV: Malware detected - $(var malware_name)</description>
|
||||
<group>malware,clamav,infection</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Trojan/PUA Detection -->
|
||||
<rule id="100502" level="12">
|
||||
<parent>100500</parent>
|
||||
<regex>Trojan|PUA|unwanted|grayware</regex>
|
||||
<description>ClamAV: Potentially Unwanted Application detected</description>
|
||||
<group>malware,clamav,pua</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Virus Detection -->
|
||||
<rule id="100503" level="15">
|
||||
<parent>100500</parent>
|
||||
<regex>\.Virus\.|\.Win\.|\.Linux\.|\.Worm\.|\.Backdoor</regex>
|
||||
<description>ClamAV: Virus detected - High severity</description>
|
||||
<group>malware,clamav,virus</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Ransomware Detection -->
|
||||
<rule id="100504" level="15">
|
||||
<parent>100500</parent>
|
||||
<regex>Ransomware|Encrypted|Cryptolocker|BadRabbit|WannaCry</regex>
|
||||
<description>ClamAV: Ransomware detected</description>
|
||||
<group>malware,clamav,ransomware</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Signature Update Success -->
|
||||
<rule id="100505" level="3">
|
||||
<parent>100500</parent>
|
||||
<program_name>freshclam</program_name>
|
||||
<regex>ClamAV update completed successfully|updated</regex>
|
||||
<description>ClamAV: Signature database updated successfully</description>
|
||||
<group>clamav,updates</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Signature Update Failed -->
|
||||
<rule id="100506" level="10">
|
||||
<parent>100500</parent>
|
||||
<program_name>freshclam</program_name>
|
||||
<regex>ERROR|Failed|error downloading|connection failed|timeout</regex>
|
||||
<description>ClamAV: Signature update failed</description>
|
||||
<group>clamav,updates,error</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Daemon Error -->
|
||||
<rule id="100507" level="8">
|
||||
<parent>100500</parent>
|
||||
<program_name>clamd</program_name>
|
||||
<regex>ERROR|error|CRITICAL</regex>
|
||||
<description>ClamAV: Daemon error</description>
|
||||
<group>clamav,error</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Scanner Started -->
|
||||
<rule id="100508" level="3">
|
||||
<parent>100500</parent>
|
||||
<regex>Scanning started|initializing scanner</regex>
|
||||
<description>ClamAV: Scanning started</description>
|
||||
<group>clamav,scanning</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Scanner Completed -->
|
||||
<rule id="100509" level="3">
|
||||
<parent>100500</parent>
|
||||
<regex>Scanning finished|scan completed</regex>
|
||||
<description>ClamAV: Scanning completed</description>
|
||||
<group>clamav,scanning</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Corrupted Files Alert -->
|
||||
<rule id="100510" level="8">
|
||||
<parent>100500</parent>
|
||||
<regex>corrupted|corrupt file|damaged|unreadable</regex>
|
||||
<description>ClamAV: Corrupted file detected</description>
|
||||
<group>clamav,corruption</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Quarantine Actions -->
|
||||
<rule id="100511" level="10">
|
||||
<parent>100500</parent>
|
||||
<regex>quarantine|moved|removed|deleted|archived</regex>
|
||||
<description>ClamAV: File quarantined/removed</description>
|
||||
<group>clamav,quarantine,action</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Scan Statistics -->
|
||||
<rule id="100512" level="3">
|
||||
<parent>100500</parent>
|
||||
<regex>infects|infected files|detected</regex>
|
||||
<description>ClamAV: Scan statistics</description>
|
||||
<group>clamav,statistics</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Permission Denied -->
|
||||
<rule id="100513" level="6">
|
||||
<parent>100500</parent>
|
||||
<regex>Permission denied|access denied|unable to scan</regex>
|
||||
<description>ClamAV: Permission denied when scanning file</description>
|
||||
<group>clamav,access</group>
|
||||
</rule>
|
||||
|
||||
<!-- ClamAV Database Problem -->
|
||||
<rule id="100514" level="10">
|
||||
<parent>100500</parent>
|
||||
<regex>database error|bad database|corrupt database|outdated database</regex>
|
||||
<description>ClamAV: Database problem detected</description>
|
||||
<group>clamav,database,error</group>
|
||||
</rule>
|
||||
|
||||
<!-- Aggregate: Multiple Malware Detections in Short Time -->
|
||||
<rule id="100515" level="16">
|
||||
<parent>100501</parent>
|
||||
<frequency>3</timeframe>3600</frequency>
|
||||
<same_source_ip />
|
||||
<description>ClamAV: Multiple malware detections in short time - possible outbreak</description>
|
||||
<group>malware,clamav,outbreak</group>
|
||||
</rule>
|
||||
|
||||
<!-- Aggregate: Ransomware Alert with Infection -->
|
||||
<rule id="100516" level="16">
|
||||
<if_matched_sid>100501</if_matched_sid>
|
||||
<if_matched_sid>100504</if_matched_sid>
|
||||
<same_source_ip />
|
||||
<timeframe>300</timeframe>
|
||||
<description>ClamAV: Ransomware and virus detected together - critical threat</description>
|
||||
<group>malware,clamav,ransomware,critical</group>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════
|
||||
Active-Response — règles de détection précoce pour l'AR graduée
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Ces règles viennent EN PLUS des bans existants sur les règles natives
|
||||
(5710, 5716, etc.) pour produire des alertes clairement labellisées
|
||||
et déclencher des AR spécifiques par scénario d'attaque.
|
||||
-->
|
||||
|
||||
<!-- ─── SSH ─────────────────────────────────────────────────────────── -->
|
||||
<group name="syslog,sshd,authentication_failures,">
|
||||
|
||||
<!-- 3 mots de passe incorrects en 60s depuis la même IP
|
||||
Note : le ban PERMANENT sur 5716 (1ère tentative) reste actif en parallèle.
|
||||
Cette règle produit une alerte "brute-force" explicite + AR temporaire graduée. -->
|
||||
<rule id="100300" level="10" frequency="3" timeframe="60">
|
||||
<if_matched_sid>5716</if_matched_sid>
|
||||
<same_source_ip />
|
||||
<description>SSH: brute-force — 3 mots de passe faux en 60s depuis $(srcip)</description>
|
||||
<group>authentication_failures,ssh_brute,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 3 users inexistants en 60s depuis la même IP → scan de comptes -->
|
||||
<rule id="100301" level="12" frequency="3" timeframe="60">
|
||||
<if_matched_sid>5710</if_matched_sid>
|
||||
<same_source_ip />
|
||||
<description>SSH: scan de comptes — 3 users inexistants en 60s depuis $(srcip)</description>
|
||||
<group>authentication_failures,ssh_scan,</group>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
|
||||
<!-- ─── SUDO ─────────────────────────────────────────────────────────── -->
|
||||
<group name="syslog,sudo,authentication_failures,">
|
||||
|
||||
<!-- 3 échecs sudo en 5 min par le même utilisateur.
|
||||
Couverture non présente dans les règles natives → AR disable-account. -->
|
||||
<rule id="100310" level="10" frequency="3" timeframe="300">
|
||||
<if_matched_sid>5401</if_matched_sid>
|
||||
<same_user />
|
||||
<description>Sudo: 3 échecs d'auth en 5 min — tentative d'escalade de privilèges</description>
|
||||
<group>authentication_failures,sudo_brute,</group>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
@@ -0,0 +1,105 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════
|
||||
Règles d'audit CIS 6.2.3.x — IDs 100900-100930
|
||||
Regles deployees par ansible/hardening/playbook-audit-rules-*.yml
|
||||
(surveillance sudo, surveillance omnex, et regles CIS restantes).
|
||||
|
||||
Objectif : ne faire remonter dans le dashboard QUE les evenements
|
||||
qui indiquent un probleme de securite potentiel (level >= 4, seuil
|
||||
de <log_alert_level> dans ossec.conf). Le reste (chaque commande
|
||||
sudo, chaque commande omnex, chmod/chown routiniers, montages
|
||||
Docker, deletions de fichiers, sessions login normales) continue
|
||||
d'etre capture dans /var/log/audit/audit.log sur chaque host
|
||||
(consultable via `ausearch -k <cle>`) mais reste sous le seuil
|
||||
d'alerte generique (level 3, rule 80780+) donc invisible du
|
||||
dashboard — c'est le comportement voulu, pas un oubli.
|
||||
|
||||
Cles VOLONTAIREMENT laissees au niveau generique (pas de regle ici) :
|
||||
user_emulation, omnex_actions, perm_mod, mounts, session, logins,
|
||||
delete — activite routiniere d'administration, pas un signal de
|
||||
securite en soi. Consultable via ausearch si besoin d'investiguer.
|
||||
-->
|
||||
|
||||
<group name="audit,">
|
||||
|
||||
<!-- 6.2.3.1 — Modification de /etc/sudoers ou /etc/sudoers.d
|
||||
Signal fort : quelqu'un modifie qui a le droit d'utiliser sudo. -->
|
||||
<rule id="100900" level="12">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^scope$</field>
|
||||
<description>Audit: /etc/sudoers modifie — changement de perimetre administrateur</description>
|
||||
<group>audit_security,gdpr_IV_35.7.d,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.3 — Modification du fichier de log sudo (technique anti-forensique) -->
|
||||
<rule id="100901" level="12">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^sudo_log_file$</field>
|
||||
<description>Audit: /var/log/sudo.log modifie — possible tentative d'effacement de traces</description>
|
||||
<group>audit_security,gdpr_IV_35.7.d,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.8 — Modification des fichiers d'identite (passwd/shadow/group/pam) -->
|
||||
<rule id="100902" level="10">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^identity$</field>
|
||||
<description>Audit: fichier d'identite systeme modifie (passwd/shadow/group/pam)</description>
|
||||
<group>audit_security,gdpr_IV_35.7.d,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.4 — Changement de date/heure systeme (technique anti-forensique classique) -->
|
||||
<rule id="100903" level="8">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^time-change$</field>
|
||||
<description>Audit: horloge systeme modifiee</description>
|
||||
<group>audit_security,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.5 — Changement d'environnement reseau (hostname, /etc/hosts, netplan...) -->
|
||||
<rule id="100904" level="8">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^system-locale$</field>
|
||||
<description>Audit: configuration reseau systeme modifiee (hostname/hosts/netplan)</description>
|
||||
<group>audit_security,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.14 — Modification de la politique AppArmor (desactivation possible d'un control de securite) -->
|
||||
<rule id="100905" level="10">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^MAC-policy$</field>
|
||||
<description>Audit: politique AppArmor modifiee — possible desactivation d'un controle de securite</description>
|
||||
<group>audit_security,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.15-17 — Usage de chcon/setfacl/chacl (commandes rares, manipulation de contexte/ACL) -->
|
||||
<rule id="100906" level="8">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^perm_chng$</field>
|
||||
<description>Audit: commande chcon/setfacl/chacl executee — manipulation de contexte ou d'ACL</description>
|
||||
<group>audit_security,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.18 — Usage de usermod (modification de compte via commande, possible escalade) -->
|
||||
<rule id="100907" level="8">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^usermod$</field>
|
||||
<description>Audit: commande usermod executee — modification de compte utilisateur</description>
|
||||
<group>audit_security,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.19 — Chargement/dechargement de module noyau (technique rootkit classique) -->
|
||||
<rule id="100908" level="12">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^kernel_modules$</field>
|
||||
<description>Audit: module noyau charge/decharge — signal potentiel de rootkit</description>
|
||||
<group>audit_security,</group>
|
||||
</rule>
|
||||
|
||||
<!-- 6.2.3.7 — Tentative d'acces fichier refusee (EACCES/EPERM) : quelqu'un a essaye et echoue -->
|
||||
<rule id="100909" level="6">
|
||||
<if_sid>80700</if_sid>
|
||||
<field name="audit.key">^access$</field>
|
||||
<description>Audit: tentative d'acces fichier refusee (permissions insuffisantes)</description>
|
||||
<group>audit_security,</group>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════
|
||||
Règles ModSecurity — WAF container (gestion-waf)
|
||||
Le decoder json intégré parse les events — on utilise <field> + <location>.
|
||||
═══════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<group name="web,modsecurity,">
|
||||
|
||||
<!-- Base : tout event JSON provenant du log ModSecurity -->
|
||||
<rule id="100100" level="3">
|
||||
<decoded_as>json</decoded_as>
|
||||
<location>modsec_audit.log</location>
|
||||
<field name="transaction.client_ip">\.+</field>
|
||||
<description>ModSecurity: WAF event capturé</description>
|
||||
<group>web,modsecurity,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Accès bloqué (HTTP 403) -->
|
||||
<rule id="100102" level="10">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.response.http_code">^403$</field>
|
||||
<description>ModSecurity: accès bloqué (403) — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>web,modsecurity,attack,blocked,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Trafic passant avec règle déclenchée (warning, anomaly scoring) -->
|
||||
<rule id="100101" level="6">
|
||||
<if_sid>100100</if_sid>
|
||||
<description>ModSecurity: règle WAF déclenchée (non bloqué) — $(transaction.client_ip)</description>
|
||||
<group>web,modsecurity,attack,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Blocages répétés depuis la même IP -->
|
||||
<rule id="100103" level="12" frequency="5" timeframe="60">
|
||||
<if_matched_sid>100102</if_matched_sid>
|
||||
<description>ModSecurity: blocages répétés — possible scan ou attaque soutenue</description>
|
||||
<group>web,modsecurity,attack,blocked,</group>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
@@ -0,0 +1,113 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════
|
||||
Suppression des alertes SCA (Security Configuration Assessment)
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Les scans CIS benchmark génèrent des centaines d'alertes par cycle.
|
||||
Toutes passées à level 0 — les résultats restent consultables dans
|
||||
l'onglet SCA du dashboard sans polluer le flux d'alertes.
|
||||
-->
|
||||
|
||||
<group name="sca,">
|
||||
|
||||
<!-- ─── Résumés de scan (score < 90%, <80%, <50%, <30%) ─────────── -->
|
||||
<rule id="19001" level="0" overwrite="yes">
|
||||
<if_sid>19000</if_sid>
|
||||
<field name="sca.type">^summary</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA summary — supprimé (voir onglet SCA du dashboard).</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19002" level="0" overwrite="yes">
|
||||
<if_sid>19001</if_sid>
|
||||
<field name="sca.score">^8\d</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA summary: score < 90% — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19003" level="0" overwrite="yes">
|
||||
<if_sid>19001</if_sid>
|
||||
<field name="sca.score">^7\d|^6\d|^5\d</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA summary: score < 80% — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19004" level="0" overwrite="yes">
|
||||
<if_sid>19001</if_sid>
|
||||
<field name="sca.score">^4\d|^3\d</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA summary: score < 50% — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19005" level="0" overwrite="yes">
|
||||
<if_sid>19001</if_sid>
|
||||
<field name="sca.score">^2\d|^1\d$|^\d$</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA summary: score < 30% — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<!-- ─── Checks individuels (passed / failed / not applicable) ────── -->
|
||||
<rule id="19007" level="0" overwrite="yes">
|
||||
<if_sid>19006</if_sid>
|
||||
<field name="sca.check.result">^failed</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA check failed — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19008" level="0" overwrite="yes">
|
||||
<if_sid>19006</if_sid>
|
||||
<field name="sca.check.result">^passed</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA check passed — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19009" level="0" overwrite="yes">
|
||||
<if_sid>19006</if_sid>
|
||||
<field name="sca.check.result">^not applicable</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA check not applicable — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<!-- ─── Changements de statut entre scans ───────────────────────── -->
|
||||
<rule id="19010" level="0" overwrite="yes">
|
||||
<if_sid>19008</if_sid>
|
||||
<field name="sca.check.previous_result">^failed</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA: failed → passed — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19011" level="0" overwrite="yes">
|
||||
<if_sid>19007</if_sid>
|
||||
<field name="sca.check.previous_result">^passed</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA: passed → failed — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19012" level="0" overwrite="yes">
|
||||
<if_sid>19009</if_sid>
|
||||
<field name="sca.check.previous_result">^passed</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA: passed → not applicable — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19013" level="0" overwrite="yes">
|
||||
<if_sid>19009</if_sid>
|
||||
<field name="sca.check.previous_result">^failed</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA: failed → not applicable — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19014" level="0" overwrite="yes">
|
||||
<if_sid>19007</if_sid>
|
||||
<field name="sca.check.previous_result">^not applicable</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA: not applicable → failed — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
<rule id="19015" level="0" overwrite="yes">
|
||||
<if_sid>19008</if_sid>
|
||||
<field name="sca.check.previous_result">^not applicable</field>
|
||||
<options>no_full_log</options>
|
||||
<description>SCA: not applicable → passed — supprimé.</description>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════
|
||||
Réduction du bruit SSH/PAM — overrides des règles natives Wazuh
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Supprimés (level 0) : événements sans valeur sécurité générés en masse
|
||||
Conservés intacts : tout ce qui alimente l'active-response et la
|
||||
détection de brute-force (5710, 5716, 5720, 5760, 5763…)
|
||||
-->
|
||||
|
||||
<!-- ─── DPKG ───────────────────────────────────────────────────────────── -->
|
||||
<group name="syslog,dpkg,">
|
||||
<rule id="2901" level="0" overwrite="yes">
|
||||
<if_sid>2900</if_sid>
|
||||
<description>Dpkg: supprimé.</description>
|
||||
</rule>
|
||||
<rule id="2902" level="0" overwrite="yes">
|
||||
<if_sid>2900</if_sid>
|
||||
<description>Dpkg: supprimé.</description>
|
||||
</rule>
|
||||
<rule id="2903" level="0" overwrite="yes">
|
||||
<if_sid>2900</if_sid>
|
||||
<description>Dpkg: supprimé.</description>
|
||||
</rule>
|
||||
<rule id="2904" level="0" overwrite="yes">
|
||||
<if_sid>2900</if_sid>
|
||||
<description>Dpkg: supprimé.</description>
|
||||
</rule>
|
||||
</group>
|
||||
|
||||
<!-- ─── IP ADMIN (90.50.148.138) ─────────────────────────────────────── -->
|
||||
<!-- Auth success supprimé pour l'IP admin : pas de valeur sécurité,
|
||||
les échecs restent visibles si l'IP était usurpée. -->
|
||||
<group name="syslog,sshd,">
|
||||
<rule id="100010" level="0">
|
||||
<if_sid>5715</if_sid>
|
||||
<srcip>90.50.148.138</srcip>
|
||||
<description>sshd: auth success IP admin — supprimé.</description>
|
||||
</rule>
|
||||
</group>
|
||||
|
||||
<!-- ─── PAM ──────────────────────────────────────────────────────────── -->
|
||||
<group name="pam,syslog,">
|
||||
|
||||
<!-- 5501 (level 3) → PAM "session opened" : redondant avec 5715 sshd auth success -->
|
||||
<rule id="5501" level="0" overwrite="yes">
|
||||
<if_sid>5500</if_sid>
|
||||
<match>session opened for user </match>
|
||||
<description>PAM: Login session opened (bruit supprimé).</description>
|
||||
</rule>
|
||||
|
||||
<!-- 5502 (level 3) → PAM "session closed" : purement informatif -->
|
||||
<rule id="5502" level="0" overwrite="yes">
|
||||
<if_sid>5500</if_sid>
|
||||
<match>session closed for user </match>
|
||||
<description>PAM: Login session closed (bruit supprimé).</description>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
|
||||
<!-- ─── SSH ──────────────────────────────────────────────────────────── -->
|
||||
<group name="syslog,sshd,">
|
||||
|
||||
<!-- 5704 (level 4) → timeout individuel : la règle de fréquence 5705
|
||||
(level 10) continue de compter et d'alerter sur les patterns -->
|
||||
<rule id="5704" level="0" overwrite="yes">
|
||||
<if_sid>5700</if_sid>
|
||||
<match>fatal: Timeout before authentication for</match>
|
||||
<description>sshd: Timeout individuel supprimé (fréquence 5705 active).</description>
|
||||
</rule>
|
||||
|
||||
<!-- 5706 (level 6) → "Did not receive identification string" : scanners
|
||||
qui touchent le port 22 sans finir le handshake, très fréquent -->
|
||||
<rule id="5706" level="0" overwrite="yes">
|
||||
<if_sid>5700</if_sid>
|
||||
<match>Did not receive identification string from</match>
|
||||
<description>sshd: Sonde sans identification (bruit de scan supprimé).</description>
|
||||
</rule>
|
||||
|
||||
<!-- 5740 (level 4) → connection reset by peer : bruit réseau/bots -->
|
||||
<rule id="5740" level="0" overwrite="yes">
|
||||
<if_sid>5700</if_sid>
|
||||
<match>Connection reset by peer$</match>
|
||||
<description>sshd: Connection reset by peer (bruit supprimé).</description>
|
||||
</rule>
|
||||
|
||||
<!-- 5741 (level 4) → connection refused : pas d'intérêt sur un agent -->
|
||||
<rule id="5741" level="0" overwrite="yes">
|
||||
<if_sid>5700</if_sid>
|
||||
<match>Connection refused$</match>
|
||||
<description>sshd: Connection refused (bruit supprimé).</description>
|
||||
</rule>
|
||||
|
||||
<!-- 5749 (level 4) → bad packet length : scanners/bots -->
|
||||
<rule id="5749" level="0" overwrite="yes">
|
||||
<if_sid>5700</if_sid>
|
||||
<match>^Bad packet length</match>
|
||||
<description>sshd: Bad packet length (bruit de scan supprimé).</description>
|
||||
</rule>
|
||||
|
||||
<!-- 5762 (level 4) → connection reset : doublon de 5740 -->
|
||||
<rule id="5762" level="0" overwrite="yes">
|
||||
<if_sid>5700</if_sid>
|
||||
<match>Connection reset</match>
|
||||
<description>sshd: Connection reset (bruit supprimé).</description>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
|
||||
<!-- ─── IOC Suricata/Windows — parents exclus (0475, 0580, 0840) ──────────
|
||||
IMPORTANT: <match>PLACEHOLDER_NEVER_MATCHES</match> au lieu de <match>.</match>
|
||||
pour éviter que ces rules catch-all bloquent l'évaluation des rules avec
|
||||
un ID plus élevé (first-match semantics dans Wazuh). -->
|
||||
<group name="ids,">
|
||||
<rule id="99917" level="0" overwrite="yes">
|
||||
<match>WAZUH_PLACEHOLDER_SURICATA_NEVER_MATCHES</match>
|
||||
<description>Suricata: malicious domain — overwrite placeholder (non applicable).</description>
|
||||
</rule>
|
||||
<rule id="99918" level="0" overwrite="yes">
|
||||
<match>WAZUH_PLACEHOLDER_SURICATA_DNS_NEVER_MATCHES</match>
|
||||
<description>Suricata: malicious domain DNS — overwrite placeholder (non applicable).</description>
|
||||
</rule>
|
||||
</group>
|
||||
|
||||
<group name="windows,windows_security,">
|
||||
<rule id="99919" level="0" overwrite="yes">
|
||||
<match>WAZUH_PLACEHOLDER_WINDOWS_FAILED_NEVER_MATCHES</match>
|
||||
<description>Windows: failed logon malicious IP — overwrite placeholder (non applicable).</description>
|
||||
</rule>
|
||||
<rule id="99920" level="0" overwrite="yes">
|
||||
<match>WAZUH_PLACEHOLDER_WINDOWS_SUCCESS_NEVER_MATCHES</match>
|
||||
<description>Windows: successful logon malicious IP — overwrite placeholder (non applicable).</description>
|
||||
</rule>
|
||||
</group>
|
||||
@@ -0,0 +1,352 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════
|
||||
ModSecurity OWASP Top 10 — Règles granulaires
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Détection des attaques OWASP Top 10 via ModSecurity + active-response
|
||||
|
||||
Correspondances CRS (OWASP ModSec Core Rule Set) :
|
||||
• 941xxx : XSS (Cross-Site Scripting)
|
||||
• 942xxx : SQL Injection, LDAP Injection
|
||||
• 943xxx : XXE (XML External Entity)
|
||||
• 930xxx : Path Traversal, RFI (Remote File Inclusion)
|
||||
• 932xxx : Remote Command Execution
|
||||
• 933xxx : PHP Injection
|
||||
• 934xxx : Java Injection
|
||||
• 950xxx : Exploit attempts
|
||||
• 951xxx : Regex DoS
|
||||
• 952xxx : Restricted File Access
|
||||
• 953xxx : Insecure File Upload
|
||||
• 954xxx : Proxy Abuse
|
||||
• 955xxx : Malicious HTTP Headers
|
||||
• 970xxx : SSRF / Open Redirect
|
||||
-->
|
||||
|
||||
<group name="web,modsecurity,owasp,">
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
RÈGLES BLOQUÉES (100102 - HTTP 403)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
OWASP #3 : INJECTION (SQLi, Command Injection, LDAP, etc.)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- SQL Injection BLOQUÉE (942xx) -->
|
||||
<rule id="100220" level="13">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">942[0-9]</field>
|
||||
<description>OWASP#3 SQL Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,sql_injection,</group>
|
||||
</rule>
|
||||
|
||||
<!-- LDAP Injection (942xx - LDAP patterns) -->
|
||||
<rule id="100221" level="12">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">942[0-9].*LDAP\|ldap_filter</field>
|
||||
<description>OWASP#3 LDAP Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,ldap_injection,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Command Injection / OS Command Execution (932xx) -->
|
||||
<rule id="100222" level="14">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">932[0-9]</field>
|
||||
<description>OWASP#3 Remote Command Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,command_injection,rce,</group>
|
||||
</rule>
|
||||
|
||||
<!-- PHP Injection (933xx) -->
|
||||
<rule id="100223" level="13">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">933[0-9]</field>
|
||||
<description>OWASP#3 PHP Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,php_injection,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Java Injection (934xx) -->
|
||||
<rule id="100224" level="13">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">934[0-9]</field>
|
||||
<description>OWASP#3 Java Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,java_injection,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
OWASP #6 : VULNERABLE & OUTDATED COMPONENTS (Exploit attempts)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Remote Code Execution attempts (930xx, 950xx) -->
|
||||
<rule id="100230" level="14">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">930[0-9]\|950[0-9]</field>
|
||||
<description>OWASP#6 RCE / Exploit attempt bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_vulnerable_components,rce,exploit,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
OWASP #7 : AUTHENTICATION & SESSION (XSS/CSRF/Session Theft)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Cross-Site Scripting (941xx) -->
|
||||
<rule id="100240" level="12">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">941[0-9]</field>
|
||||
<description>OWASP#7 XSS attack bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_auth,xss,session_theft,</group>
|
||||
</rule>
|
||||
|
||||
<!-- HTTP Response Splitting / Header Injection (955xx) -->
|
||||
<rule id="100241" level="12">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">955[0-9]</field>
|
||||
<description>OWASP#7 HTTP Header Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_auth,header_injection,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
OWASP #1 : BROKEN ACCESS CONTROL (Path Traversal, Bypass)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Path Traversal / Local File Inclusion (930xx - LFI patterns) -->
|
||||
<rule id="100250" level="12">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">930[0-9].*\(path\|traversal\|lfi\)</field>
|
||||
<description>OWASP#1 Path Traversal / LFI bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_access_control,lfi,path_traversal,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Remote File Inclusion (930xx - RFI patterns) -->
|
||||
<rule id="100251" level="13">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">930[0-9].*\(rfi\|remote\|include\)</field>
|
||||
<description>OWASP#1 Remote File Inclusion bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_access_control,rfi,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Restricted File Access (952xx) -->
|
||||
<rule id="100252" level="11">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">952[0-9]</field>
|
||||
<description>OWASP#1 Unauthorized File Access bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_access_control,file_access,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
OWASP #8 : SOFTWARE & DATA INTEGRITY (XXE, Deserialization)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- XML External Entity (XXE) (943xx) -->
|
||||
<rule id="100260" level="13">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">943[0-9]</field>
|
||||
<description>OWASP#8 XML External Entity (XXE) bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_data_integrity,xxe,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
OWASP #9 : LOGGING & MONITORING FAILURES (Scanner Detection)
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Web Application Scanner Detection (951xx, 954xx) -->
|
||||
<rule id="100270" level="10">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">951[0-9]\|954[0-9]</field>
|
||||
<description>OWASP#9 Web Scanner / Bot detected — $(transaction.client_ip)</description>
|
||||
<group>owasp_monitoring,scanner_detection,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
AUTRES : SSRF, Open Redirect, Malicious File Upload
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- Server-Side Request Forgery (970xx, 971xx) -->
|
||||
<rule id="100280" level="13">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">970[0-9]\|971[0-9]</field>
|
||||
<description>OWASP Server-Side Request Forgery (SSRF) bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_misc,ssrf,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Insecure File Upload (953xx) -->
|
||||
<rule id="100281" level="12">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">953[0-9]</field>
|
||||
<description>OWASP Insecure File Upload bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_misc,file_upload,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
FRÉQUENCE & PATTERNS RÉPÉTÉS
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- SQLi répétées (brute-force injection) -->
|
||||
<rule id="100290" level="14" frequency="3" timeframe="60">
|
||||
<if_matched_sid>100220</if_matched_sid>
|
||||
<description>OWASP#3 SQL Injection brute-force — $(transaction.client_ip) tentatives répétées</description>
|
||||
<group>owasp_injection,sql_injection,brute_force,</group>
|
||||
</rule>
|
||||
|
||||
<!-- XSS répétées (scanner ou attaque soutenue) -->
|
||||
<rule id="100291" level="13" frequency="5" timeframe="120">
|
||||
<if_matched_sid>100240</if_matched_sid>
|
||||
<description>OWASP#7 XSS attack pattern — $(transaction.client_ip) attaque soutenue</description>
|
||||
<group>owasp_auth,xss,</group>
|
||||
</rule>
|
||||
|
||||
<!-- RCE attempts répétées (critiques) -->
|
||||
<rule id="100292" level="15" frequency="2" timeframe="60">
|
||||
<if_matched_sid>100222</if_matched_sid>
|
||||
<description>OWASP#3 Command Injection attempts — $(transaction.client_ip) CRITICAL</description>
|
||||
<group>owasp_injection,command_injection,critical,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Path Traversal brute-force -->
|
||||
<rule id="100293" level="12" frequency="4" timeframe="180">
|
||||
<if_matched_sid>100250</if_matched_sid>
|
||||
<description>OWASP#1 Path Traversal brute-force — $(transaction.client_ip)</description>
|
||||
<group>owasp_access_control,lfi,brute_force,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
RÈGLES REMONTÉES (100100 - tous les events, bloqués ou non)
|
||||
Patterns OWASP détectés sans être bloqués
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- OWASP#3 SQLi remontée (942xx - non bloquée, anomaly scoring) -->
|
||||
<rule id="100320" level="9">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">942[0-9]</field>
|
||||
<description>OWASP#3 SQL Injection attempt (anomaly scoring) — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,sql_injection,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#3 Command Injection remontée (932xx - non bloquée) -->
|
||||
<rule id="100321" level="10">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">932[0-9]</field>
|
||||
<description>OWASP#3 Command Injection attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,command_injection,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#3 LDAP Injection remontée (942xx LDAP) -->
|
||||
<rule id="100322" level="9">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">942[0-9].*LDAP\|ldap_filter</field>
|
||||
<description>OWASP#3 LDAP Injection attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,ldap_injection,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#3 PHP/Java Injection remontée (933xx, 934xx) -->
|
||||
<rule id="100323" level="9">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">933[0-9]\|934[0-9]</field>
|
||||
<description>OWASP#3 Code Injection attempt (PHP/Java) — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_injection,code_injection,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#6 RCE/Exploit remontée (930xx, 950xx - non bloquée) -->
|
||||
<rule id="100330" level="11">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">930[0-9]\|950[0-9]</field>
|
||||
<description>OWASP#6 RCE / Exploit attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_vulnerable_components,rce,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#7 XSS remontée (941xx - non bloquée) -->
|
||||
<rule id="100340" level="8">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">941[0-9]</field>
|
||||
<description>OWASP#7 XSS attempt (anomaly scoring) — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_auth,xss,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#7 Header Injection remontée (955xx) -->
|
||||
<rule id="100341" level="8">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">955[0-9]</field>
|
||||
<description>OWASP#7 HTTP Header Injection attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_auth,header_injection,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#1 Path Traversal remontée (930xx LFI patterns) -->
|
||||
<rule id="100350" level="8">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">930[0-9].*\(path\|traversal\|lfi\)</field>
|
||||
<description>OWASP#1 Path Traversal attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_access_control,lfi,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#1 RFI remontée (930xx RFI patterns) -->
|
||||
<rule id="100351" level="9">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">930[0-9].*\(rfi\|remote\|include\)</field>
|
||||
<description>OWASP#1 Remote File Inclusion attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_access_control,rfi,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#1 File Access remontée (952xx) -->
|
||||
<rule id="100352" level="8">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">952[0-9]</field>
|
||||
<description>OWASP#1 Unauthorized File Access attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_access_control,file_access,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#8 XXE remontée (943xx) -->
|
||||
<rule id="100360" level="9">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">943[0-9]</field>
|
||||
<description>OWASP#8 XML External Entity (XXE) attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_data_integrity,xxe,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP#9 Scanner remontée (951xx, 954xx) -->
|
||||
<rule id="100370" level="6">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">951[0-9]\|954[0-9]</field>
|
||||
<description>OWASP#9 Web Scanner / Bot detected — $(transaction.client_ip)</description>
|
||||
<group>owasp_monitoring,scanner_detection,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP SSRF remontée (970xx, 971xx) -->
|
||||
<rule id="100380" level="9">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">970[0-9]\|971[0-9]</field>
|
||||
<description>OWASP Server-Side Request Forgery (SSRF) attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_misc,ssrf,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- OWASP File Upload remontée (953xx) -->
|
||||
<rule id="100381" level="8">
|
||||
<if_sid>100100</if_sid>
|
||||
<field name="transaction.messages.message">953[0-9]</field>
|
||||
<description>OWASP Insecure File Upload attempt — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>owasp_misc,file_upload,warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════
|
||||
FRÉQUENCE — Attaques par anomaly scoring répétées
|
||||
═══════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- SQLi anomaly scoring répétée -->
|
||||
<rule id="100394" level="10" frequency="5" timeframe="300">
|
||||
<if_matched_sid>100320</if_matched_sid>
|
||||
<description>OWASP#3 SQL Injection anomaly scoring — $(transaction.client_ip) tentatives répétées</description>
|
||||
<group>owasp_injection,sql_injection,anomaly,</group>
|
||||
</rule>
|
||||
|
||||
<!-- XSS anomaly scoring répétée -->
|
||||
<rule id="100395" level="10" frequency="8" timeframe="300">
|
||||
<if_matched_sid>100340</if_matched_sid>
|
||||
<description>OWASP#7 XSS anomaly scoring — $(transaction.client_ip) attaque soutenue</description>
|
||||
<group>owasp_auth,xss,anomaly,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Command Injection tentatives répétées -->
|
||||
<rule id="100396" level="12" frequency="2" timeframe="60">
|
||||
<if_matched_sid>100321</if_matched_sid>
|
||||
<description>OWASP#3 Command Injection attempts — $(transaction.client_ip) CRITICAL</description>
|
||||
<group>owasp_injection,command_injection,anomaly,</group>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
@@ -0,0 +1,44 @@
|
||||
<group name="web,modsecurity,attack,">
|
||||
|
||||
<!-- Règles spécialisées par type d'attaque — basées sur local_modsecurity_rules.xml (rule 100102) -->
|
||||
|
||||
<!-- SQL Injection bloquée -->
|
||||
<rule id="100202" level="12">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">SQL Injection\|SQLi\|942[0-9]</field>
|
||||
<description>ModSecurity: SQL Injection bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>modsecurity_sqli,sql_injection,</group>
|
||||
</rule>
|
||||
|
||||
<!-- XSS bloqué -->
|
||||
<rule id="100203" level="12">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">XSS\|941[0-9]</field>
|
||||
<description>ModSecurity: XSS bloqué — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>modsecurity_xss,xss,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Remote Code Execution bloqué -->
|
||||
<rule id="100204" level="14">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">RCE\|930[0-9]\|932[0-9]</field>
|
||||
<description>ModSecurity: tentative RCE bloquée — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>modsecurity_rce,rce,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Local File Inclusion bloqué -->
|
||||
<rule id="100205" level="12">
|
||||
<if_sid>100102</if_sid>
|
||||
<field name="transaction.messages.message">LFI\|930[0-9]</field>
|
||||
<description>ModSecurity: LFI bloqué — $(transaction.client_ip) → $(transaction.request.uri)</description>
|
||||
<group>modsecurity_lfi,lfi,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Blocages répétés — attaque soutenue -->
|
||||
<rule id="100210" level="14" frequency="5" timeframe="120">
|
||||
<if_matched_sid>100102</if_matched_sid>
|
||||
<description>ModSecurity: blocages répétés en 2 min — attaque soutenue</description>
|
||||
<group>modsecurity_repeated,</group>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
@@ -0,0 +1,129 @@
|
||||
<!-- Règles WireGuard VPN — IDs 100800-100826
|
||||
Les logs kernel passent par rule 5100 (program_name=^kernel).
|
||||
On hérite via if_sid 5100 et on filtre par match/regex.
|
||||
Les rules 1082x sont des enfants pour les peers de confiance
|
||||
(etc/lists/wireguard-trusted-ips) : le negate="yes" sur <list>
|
||||
n'est pas supporté par analysisd ici, donc on matche positivement
|
||||
et on laisse la règle la plus spécifique (l'enfant) l'emporter
|
||||
sur le parent.
|
||||
Niveau 3 (pas 0) : <log_alert_level>3</log_alert_level> dans
|
||||
ossec.conf exclut tout niveau < 3 de l'index OpenSearch. Un niveau
|
||||
0 rendrait les handshakes de confiance invisibles du dashboard VPN
|
||||
(compteurs handshakes/peers actifs à 0). Le groupe vpn_trusted
|
||||
permet de les filtrer d'une vue "alertes à traiter" sans les
|
||||
retirer des stats. -->
|
||||
|
||||
<group name="wireguard,vpn,">
|
||||
|
||||
<!-- Keepalive : level 0 = pas d'index OpenSearch -->
|
||||
<rule id="100800" level="0">
|
||||
<if_sid>5100</if_sid>
|
||||
<match>wireguard: wg0: </match>
|
||||
<regex>keepalive packet</regex>
|
||||
<description>WireGuard: keepalive peer $(srcip)</description>
|
||||
<group>wireguard,vpn_keepalive,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Handshake initiation -->
|
||||
<rule id="100801" level="3">
|
||||
<if_sid>5100</if_sid>
|
||||
<match>wireguard: wg0: Sending handshake initiation</match>
|
||||
<description>WireGuard: handshake initiation vers peer $(srcip)</description>
|
||||
<group>wireguard,vpn_handshake,</group>
|
||||
</rule>
|
||||
|
||||
<rule id="100821" level="3">
|
||||
<if_sid>100801</if_sid>
|
||||
<list field="srcip" lookup="match_key">etc/lists/wireguard-trusted-ips</list>
|
||||
<description>WireGuard: handshake initiation - peer de confiance $(srcip)</description>
|
||||
<group>wireguard,vpn_handshake,vpn_trusted,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Handshake réponse envoyée -->
|
||||
<rule id="100802" level="3">
|
||||
<if_sid>5100</if_sid>
|
||||
<match>wireguard: wg0: Sending handshake response</match>
|
||||
<description>WireGuard: handshake reponse envoyee a peer $(srcip)</description>
|
||||
<group>wireguard,vpn_handshake,</group>
|
||||
</rule>
|
||||
|
||||
<rule id="100822" level="3">
|
||||
<if_sid>100802</if_sid>
|
||||
<list field="srcip" lookup="match_key">etc/lists/wireguard-trusted-ips</list>
|
||||
<description>WireGuard: handshake reponse - peer de confiance $(srcip)</description>
|
||||
<group>wireguard,vpn_handshake,vpn_trusted,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Handshake réponse reçue (session établie) -->
|
||||
<rule id="100803" level="3">
|
||||
<if_sid>5100</if_sid>
|
||||
<match>wireguard: wg0: Receiving handshake response</match>
|
||||
<description>WireGuard: handshake complet avec peer $(srcip)</description>
|
||||
<group>wireguard,vpn_handshake,</group>
|
||||
</rule>
|
||||
|
||||
<rule id="100823" level="3">
|
||||
<if_sid>100803</if_sid>
|
||||
<list field="srcip" lookup="match_key">etc/lists/wireguard-trusted-ips</list>
|
||||
<description>WireGuard: handshake complet - peer de confiance $(srcip)</description>
|
||||
<group>wireguard,vpn_handshake,vpn_trusted,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Handshake timeout (pas d'exclusion : un timeout reste un signal utile,
|
||||
même pour un peer de confiance) -->
|
||||
<rule id="100810" level="7">
|
||||
<if_sid>5100</if_sid>
|
||||
<match>wireguard: wg0: Handshake for peer</match>
|
||||
<regex>did not complete</regex>
|
||||
<description>WireGuard: handshake timeout peer $(srcip)</description>
|
||||
<group>wireguard,vpn_warning,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Timeout répété >= 5 -->
|
||||
<rule id="100811" level="10">
|
||||
<if_sid>100810</if_sid>
|
||||
<match>try 5|try 6|try 7|try 8|try 9</match>
|
||||
<description>WireGuard: peer $(srcip) deconnecte</description>
|
||||
<group>wireguard,vpn_disconnected,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Timeout grave >= 10 -->
|
||||
<rule id="100812" level="12">
|
||||
<if_sid>100810</if_sid>
|
||||
<regex>retrying .try 1\d.</regex>
|
||||
<description>WireGuard: peer $(srcip) hors ligne depuis plus de 60s</description>
|
||||
<group>wireguard,vpn_disconnected,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Peer roaming -->
|
||||
<rule id="100815" level="5">
|
||||
<if_sid>5100</if_sid>
|
||||
<match>wireguard: wg0: Peer </match>
|
||||
<regex>is now roaming</regex>
|
||||
<description>WireGuard: peer $(id) roaming vers $(srcip)</description>
|
||||
<group>wireguard,vpn_roaming,</group>
|
||||
</rule>
|
||||
|
||||
<rule id="100825" level="3">
|
||||
<if_sid>100815</if_sid>
|
||||
<list field="srcip" lookup="match_key">etc/lists/wireguard-trusted-ips</list>
|
||||
<description>WireGuard: roaming - peer de confiance $(id) vers $(srcip)</description>
|
||||
<group>wireguard,vpn_roaming,vpn_trusted,</group>
|
||||
</rule>
|
||||
|
||||
<!-- Reconnexion après déconnexion -->
|
||||
<rule id="100816" level="6">
|
||||
<if_sid>5100</if_sid>
|
||||
<match>wireguard: wg0: Receiving handshake response</match>
|
||||
<description>WireGuard: peer $(srcip) reconnecte</description>
|
||||
<group>wireguard,vpn_reconnected,</group>
|
||||
</rule>
|
||||
|
||||
<rule id="100826" level="3">
|
||||
<if_sid>100816</if_sid>
|
||||
<list field="srcip" lookup="match_key">etc/lists/wireguard-trusted-ips</list>
|
||||
<description>WireGuard: reconnexion - peer de confiance $(srcip)</description>
|
||||
<group>wireguard,vpn_reconnected,vpn_trusted,</group>
|
||||
</rule>
|
||||
|
||||
</group>
|
||||
@@ -0,0 +1,333 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Crée le dashboard "Securite - API Backend & WAF" dans OpenSearch Dashboards (Wazuh).
|
||||
|
||||
Usage:
|
||||
python3 create-dashboard-api.py
|
||||
|
||||
Le script se connecte depuis l'intérieur du container wazuh_dashboard via:
|
||||
docker exec wazuh_dashboard python3 /tmp/create-dashboard-api.py
|
||||
|
||||
Données utilisées :
|
||||
- rules 100101 (WAF alerté) / 100102 (WAF bloqué) → ModSecurity
|
||||
- rules 100600-100613 → Brute-force login API
|
||||
- fields: data.transaction.client_ip, .request.uri,
|
||||
.response.http_code, .is_interrupted,
|
||||
.messages.details.ruleId
|
||||
"""
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import ssl
|
||||
import base64
|
||||
import sys
|
||||
import os
|
||||
|
||||
# ── Configuration ─────────────────────────────────────────────────────────────
|
||||
DASHBOARD_HOST = "https://localhost:5601"
|
||||
DASHBOARD_USER = "kibanaserver"
|
||||
DASHBOARD_PASS = os.environ.get("DASHBOARD_PASSWORD", "E9Jpr6586kQ3wYrCS2!")
|
||||
INDEX_PATTERN = "wazuh-alerts-*"
|
||||
|
||||
# ── Queries ───────────────────────────────────────────────────────────────────
|
||||
Q_WAF = "rule.id: 100101 OR rule.id: 100102"
|
||||
Q_BLOCKED = "rule.id: 100102"
|
||||
Q_ALERTED = "rule.id: 100101"
|
||||
Q_BFORCE = "rule.groups: api_brute_force"
|
||||
Q_ALL = Q_WAF + " OR " + Q_BFORCE
|
||||
|
||||
# ── Client HTTP ───────────────────────────────────────────────────────────────
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
_auth = base64.b64encode(f"{DASHBOARD_USER}:{DASHBOARD_PASS}".encode()).decode()
|
||||
_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"osd-xsrf": "true",
|
||||
"Authorization": f"Basic {_auth}",
|
||||
}
|
||||
|
||||
def api(method, path, body=None):
|
||||
data = json.dumps(body).encode() if body else None
|
||||
req = urllib.request.Request(DASHBOARD_HOST + path, data=data, headers=_headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=ctx, timeout=15) as r:
|
||||
return json.loads(r.read())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"error": e.code, "msg": e.read().decode()[:300]}
|
||||
|
||||
def search_src(query):
|
||||
return json.dumps({
|
||||
"index": INDEX_PATTERN,
|
||||
"filter": [],
|
||||
"query": {"language": "kuery", "query": query},
|
||||
})
|
||||
|
||||
# ── Visualisations ────────────────────────────────────────────────────────────
|
||||
VISUALIZATIONS = [
|
||||
# ── 1. Métrique : total alertes WAF ──────────────────────────────────────
|
||||
{
|
||||
"id": "api-waf-total",
|
||||
"title": "[API] WAF - Total Alertes",
|
||||
"type": "metric",
|
||||
"query": Q_WAF,
|
||||
"visState": {
|
||||
"type": "metric",
|
||||
"params": {
|
||||
"metric": {
|
||||
"percentageMode": False,
|
||||
"useRanges": False,
|
||||
"colorSchema": "Green to Red",
|
||||
"metricColorMode": "Labels",
|
||||
"colorsRange": [{"from": 0, "to": 50}, {"from": 50, "to": 500}, {"from": 500, "to": 99999}],
|
||||
"labels": {"show": True},
|
||||
"invertColors": False,
|
||||
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False, "subText": "alertes WAF", "fontSize": 60},
|
||||
}
|
||||
},
|
||||
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
|
||||
},
|
||||
},
|
||||
# ── 2. Métrique : requêtes bloquées ──────────────────────────────────────
|
||||
{
|
||||
"id": "api-waf-blocked",
|
||||
"title": "[API] WAF - Requetes Bloquees",
|
||||
"type": "metric",
|
||||
"query": Q_BLOCKED,
|
||||
"visState": {
|
||||
"type": "metric",
|
||||
"params": {
|
||||
"metric": {
|
||||
"percentageMode": False,
|
||||
"useRanges": False,
|
||||
"colorSchema": "Green to Red",
|
||||
"metricColorMode": "Labels",
|
||||
"colorsRange": [{"from": 0, "to": 10}, {"from": 10, "to": 100}, {"from": 100, "to": 99999}],
|
||||
"labels": {"show": True},
|
||||
"invertColors": False,
|
||||
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False, "subText": "bloquees par WAF", "fontSize": 60},
|
||||
}
|
||||
},
|
||||
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
|
||||
},
|
||||
},
|
||||
# ── 3. Métrique : brute-force login ──────────────────────────────────────
|
||||
{
|
||||
"id": "api-bf-count",
|
||||
"title": "[API] Brute-Force Login",
|
||||
"type": "metric",
|
||||
"query": Q_BFORCE,
|
||||
"visState": {
|
||||
"type": "metric",
|
||||
"params": {
|
||||
"metric": {
|
||||
"percentageMode": False,
|
||||
"useRanges": False,
|
||||
"colorSchema": "Green to Red",
|
||||
"metricColorMode": "Labels",
|
||||
"colorsRange": [{"from": 0, "to": 5}, {"from": 5, "to": 50}, {"from": 50, "to": 99999}],
|
||||
"labels": {"show": True},
|
||||
"invertColors": False,
|
||||
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False, "subText": "alertes brute-force", "fontSize": 60},
|
||||
}
|
||||
},
|
||||
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
|
||||
},
|
||||
},
|
||||
# ── 4. Timeline : activite WAF ────────────────────────────────────────────
|
||||
{
|
||||
"id": "api-waf-timeline",
|
||||
"title": "[API] Timeline Activite WAF",
|
||||
"type": "histogram",
|
||||
"query": Q_ALL,
|
||||
"visState": {
|
||||
"type": "histogram",
|
||||
"params": {
|
||||
"type": "histogram",
|
||||
"grid": {"categoryLines": False},
|
||||
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "bottom", "show": True, "style": {}, "scale": {"type": "linear"}, "labels": {"show": True, "truncate": 100}, "title": {}}],
|
||||
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value", "position": "left", "show": True, "style": {}, "scale": {"type": "linear", "mode": "normal"}, "labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100}, "title": {"text": "Requetes"}}],
|
||||
"seriesParams": [{"show": True, "type": "histogram", "mode": "stacked", "data": {"label": "Requetes", "id": "1"}, "valueAxis": "ValueAxis-1", "drawLinesBetweenPoints": True, "showCircles": True}],
|
||||
"addTooltip": True, "addLegend": True, "legendPosition": "right", "times": [], "addTimeMarker": False,
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "date_histogram", "schema": "segment", "params": {"field": "@timestamp", "interval": "auto", "customInterval": "2h", "min_doc_count": 1, "extended_bounds": {}}},
|
||||
{"id": "3", "enabled": True, "type": "terms", "schema": "group", "params": {"field": "rule.id", "size": 5, "order": "desc", "orderBy": "1", "otherBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 5. Bar horizontal : top endpoints ciblés ─────────────────────────────
|
||||
{
|
||||
"id": "api-top-endpoints",
|
||||
"title": "[API] Top Endpoints Cibles",
|
||||
"type": "horizontal_bar",
|
||||
"query": Q_WAF,
|
||||
"visState": {
|
||||
"type": "horizontal_bar",
|
||||
"params": {
|
||||
"type": "horizontal_bar",
|
||||
"grid": {"categoryLines": False},
|
||||
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "left", "show": True, "style": {}, "scale": {"type": "linear"}, "labels": {"show": True, "rotate": 0, "filter": True, "truncate": 250}, "title": {}}],
|
||||
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value", "position": "bottom", "show": True, "style": {}, "scale": {"type": "linear", "mode": "normal"}, "labels": {"show": True, "rotate": 75, "filter": False, "truncate": 100}, "title": {"text": "Requetes"}}],
|
||||
"seriesParams": [{"show": True, "type": "horizontal_bar", "mode": "normal", "data": {"label": "Requetes", "id": "1"}, "valueAxis": "ValueAxis-1", "drawLinesBetweenPoints": True, "showCircles": True}],
|
||||
"addTooltip": True, "addLegend": True, "legendPosition": "right", "times": [], "addTimeMarker": False,
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment", "params": {"field": "data.transaction.request.uri", "size": 15, "order": "desc", "orderBy": "1", "otherBucket": True, "otherBucketLabel": "Autres", "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 6. Bar horizontal : top IPs attaquantes ───────────────────────────────
|
||||
{
|
||||
"id": "api-top-ips",
|
||||
"title": "[API] Top IPs Attaquantes",
|
||||
"type": "horizontal_bar",
|
||||
"query": Q_ALL,
|
||||
"visState": {
|
||||
"type": "horizontal_bar",
|
||||
"params": {
|
||||
"type": "horizontal_bar",
|
||||
"grid": {"categoryLines": False},
|
||||
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "left", "show": True, "style": {}, "scale": {"type": "linear"}, "labels": {"show": True, "rotate": 0, "filter": True, "truncate": 200}, "title": {}}],
|
||||
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value", "position": "bottom", "show": True, "style": {}, "scale": {"type": "linear", "mode": "normal"}, "labels": {"show": True, "rotate": 75, "filter": False, "truncate": 100}, "title": {"text": "Alertes"}}],
|
||||
"seriesParams": [{"show": True, "type": "horizontal_bar", "mode": "normal", "data": {"label": "Alertes", "id": "1"}, "valueAxis": "ValueAxis-1", "drawLinesBetweenPoints": True, "showCircles": True}],
|
||||
"addTooltip": True, "addLegend": True, "legendPosition": "right", "times": [], "addTimeMarker": False,
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment", "params": {"field": "data.transaction.client_ip", "size": 15, "order": "desc", "orderBy": "1", "otherBucket": False, "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 7. Pie : distribution codes HTTP ─────────────────────────────────────
|
||||
{
|
||||
"id": "api-http-codes",
|
||||
"title": "[API] Distribution Codes HTTP",
|
||||
"type": "pie",
|
||||
"query": Q_WAF,
|
||||
"visState": {
|
||||
"type": "pie",
|
||||
"params": {
|
||||
"type": "pie", "addTooltip": True, "addLegend": True,
|
||||
"legendPosition": "right", "isDonut": True,
|
||||
"labels": {"show": True, "values": True, "last_level": True, "truncate": 100},
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment", "params": {"field": "data.transaction.response.http_code", "size": 10, "order": "desc", "orderBy": "1", "otherBucket": True, "otherBucketLabel": "Autres", "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 8. Pie : bloqué vs passé ──────────────────────────────────────────────
|
||||
{
|
||||
"id": "api-blocked-ratio",
|
||||
"title": "[API] Bloque vs Alerte WAF",
|
||||
"type": "pie",
|
||||
"query": Q_WAF,
|
||||
"visState": {
|
||||
"type": "pie",
|
||||
"params": {
|
||||
"type": "pie", "addTooltip": True, "addLegend": True,
|
||||
"legendPosition": "right", "isDonut": False,
|
||||
"labels": {"show": True, "values": True, "last_level": True, "truncate": 100},
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment", "params": {"field": "data.transaction.is_interrupted", "size": 5, "order": "desc", "orderBy": "1", "otherBucket": False, "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 9. Table : top règles CRS déclenchées ────────────────────────────────
|
||||
{
|
||||
"id": "api-crs-rules",
|
||||
"title": "[API] Top Regles CRS Declenchees",
|
||||
"type": "table",
|
||||
"query": Q_WAF,
|
||||
"visState": {
|
||||
"type": "table",
|
||||
"params": {
|
||||
"perPage": 15, "showPartialRows": False, "showMetricsAtAllLevels": False,
|
||||
"sort": {"columnIndex": None, "direction": None}, "showTotal": False, "totalFunc": "sum",
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "bucket", "params": {"field": "data.transaction.messages.details.ruleId", "size": 15, "order": "desc", "orderBy": "1", "otherBucket": False, "missingBucket": False}},
|
||||
{"id": "3", "enabled": True, "type": "terms", "schema": "bucket", "params": {"field": "data.transaction.messages.message", "size": 1, "order": "desc", "orderBy": "1", "otherBucket": False, "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
"uiStateJSON": json.dumps({"vis": {"params": {"sort": {"columnIndex": 0, "direction": "desc"}}}}),
|
||||
},
|
||||
]
|
||||
|
||||
# ── Layout dashboard (grille 48 colonnes) ─────────────────────────────────────
|
||||
# Ligne 0 : 3 métriques (total | bloquées | brute-force)
|
||||
# Ligne 6 : timeline pleine largeur
|
||||
# Ligne 18: top endpoints (gauche) + top IPs (droite)
|
||||
# Ligne 32: codes HTTP (gauche) + bloqué/passé (milieu) + règles CRS (droite)
|
||||
PANELS = [
|
||||
# Métriques
|
||||
{"panelIndex": "1", "gridData": {"x": 0, "y": 0, "w": 16, "h": 6, "i": "1"}, "version": "2.19.5", "type": "visualization", "id": "api-waf-total", "embeddableConfig": {}},
|
||||
{"panelIndex": "2", "gridData": {"x": 16, "y": 0, "w": 16, "h": 6, "i": "2"}, "version": "2.19.5", "type": "visualization", "id": "api-waf-blocked", "embeddableConfig": {}},
|
||||
{"panelIndex": "3", "gridData": {"x": 32, "y": 0, "w": 16, "h": 6, "i": "3"}, "version": "2.19.5", "type": "visualization", "id": "api-bf-count", "embeddableConfig": {}},
|
||||
# Timeline
|
||||
{"panelIndex": "4", "gridData": {"x": 0, "y": 6, "w": 48, "h": 12, "i": "4"}, "version": "2.19.5", "type": "visualization", "id": "api-waf-timeline", "embeddableConfig": {}},
|
||||
# Top endpoints + Top IPs
|
||||
{"panelIndex": "5", "gridData": {"x": 0, "y": 18, "w": 28, "h": 16, "i": "5"}, "version": "2.19.5", "type": "visualization", "id": "api-top-endpoints", "embeddableConfig": {}},
|
||||
{"panelIndex": "6", "gridData": {"x": 28, "y": 18, "w": 20, "h": 16, "i": "6"}, "version": "2.19.5", "type": "visualization", "id": "api-top-ips", "embeddableConfig": {}},
|
||||
# Codes HTTP + bloqué/passé + règles CRS
|
||||
{"panelIndex": "7", "gridData": {"x": 0, "y": 34, "w": 16, "h": 14, "i": "7"}, "version": "2.19.5", "type": "visualization", "id": "api-http-codes", "embeddableConfig": {}},
|
||||
{"panelIndex": "8", "gridData": {"x": 16, "y": 34, "w": 16, "h": 14, "i": "8"}, "version": "2.19.5", "type": "visualization", "id": "api-blocked-ratio", "embeddableConfig": {}},
|
||||
{"panelIndex": "9", "gridData": {"x": 32, "y": 34, "w": 16, "h": 14, "i": "9"}, "version": "2.19.5", "type": "visualization", "id": "api-crs-rules", "embeddableConfig": {}},
|
||||
]
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||
def main():
|
||||
print("=== Creation dashboard: Securite - API Backend & WAF ===\n")
|
||||
|
||||
for viz in VISUALIZATIONS:
|
||||
vis_state = dict(viz["visState"])
|
||||
vis_state["title"] = viz["title"]
|
||||
body = {
|
||||
"attributes": {
|
||||
"title": viz["title"],
|
||||
"visState": json.dumps(vis_state),
|
||||
"uiStateJSON": viz.get("uiStateJSON", "{}"),
|
||||
"description": "",
|
||||
"kibanaSavedObjectMeta": {"searchSourceJSON": search_src(viz["query"])},
|
||||
}
|
||||
}
|
||||
r = api("POST", f"/api/saved_objects/visualization/{viz['id']}?overwrite=true", body)
|
||||
if "id" in r:
|
||||
print(f" OK {viz['title']}")
|
||||
else:
|
||||
print(f" ERR {viz['title']}: {r}")
|
||||
sys.exit(1)
|
||||
|
||||
dashboard_body = {
|
||||
"attributes": {
|
||||
"title": "Securite - API Backend et WAF",
|
||||
"description": "ModSecurity WAF alertes et blocages, endpoints cibles, top IPs, codes HTTP, regles CRS",
|
||||
"panelsJSON": json.dumps(PANELS),
|
||||
"optionsJSON": json.dumps({"useMargins": True, "hidePanelTitles": False}),
|
||||
"timeRestore": False,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": json.dumps({"query": {"language": "kuery", "query": ""}, "filter": []})
|
||||
},
|
||||
}
|
||||
}
|
||||
r = api("POST", "/api/saved_objects/dashboard/api-backend-waf-dashboard?overwrite=true", dashboard_body)
|
||||
if "id" in r:
|
||||
print(f"\n OK Dashboard: {r['id']}")
|
||||
print(f"\n URL: https://<monitoring-ip>/#/app/dashboards#/view/{r['id']}")
|
||||
else:
|
||||
print(f"\n ERR Dashboard: {r}")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n=== Termine ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,369 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Crée le dashboard "VPN - Activite WireGuard" dans OpenSearch Dashboards (Wazuh).
|
||||
|
||||
Usage:
|
||||
docker cp create-dashboard-vpn.py wazuh_dashboard:/tmp/
|
||||
docker exec wazuh_dashboard python3 /tmp/create-dashboard-vpn.py
|
||||
|
||||
Données utilisées :
|
||||
- rules 100801-100803 → handshakes WireGuard
|
||||
- rules 100810-100812 → timeouts / déconnexions
|
||||
- rule 100815 → peer roaming
|
||||
- fields: data.srcip (IP peer), data.id (peer number), data.extra_data (retry)
|
||||
- agent.name: vpn-prod
|
||||
"""
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import ssl
|
||||
import base64
|
||||
import sys
|
||||
import os
|
||||
|
||||
# ── Configuration ──────────────────────────────────────────────────────────────
|
||||
DASHBOARD_HOST = "https://localhost:5601"
|
||||
DASHBOARD_USER = "kibanaserver"
|
||||
DASHBOARD_PASS = os.environ.get("DASHBOARD_PASSWORD", "E9Jpr6586kQ3wYrCS2!")
|
||||
INDEX_PATTERN = "wazuh-alerts-*"
|
||||
|
||||
# ── Queries ────────────────────────────────────────────────────────────────────
|
||||
# Note: pour un peer de la liste de confiance (etc/lists/wireguard-trusted-ips),
|
||||
# analysisd indexe l'alerte sous le rule.id de la regle enfant 1082x (voir
|
||||
# wireguard-rules.xml), pas celui du parent 1008xx. Il faut donc inclure les
|
||||
# deux jeux d'IDs partout, sinon les peers de confiance disparaissent des stats.
|
||||
Q_ALL = ("rule.id: 100801 OR rule.id: 100802 OR rule.id: 100803 OR rule.id: 100810 "
|
||||
"OR rule.id: 100811 OR rule.id: 100812 OR rule.id: 100815 OR rule.id: 100816 "
|
||||
"OR rule.id: 100821 OR rule.id: 100822 OR rule.id: 100823 OR rule.id: 100825 OR rule.id: 100826")
|
||||
Q_HANDSHAKES = ("rule.id: 100801 OR rule.id: 100802 OR rule.id: 100803 "
|
||||
"OR rule.id: 100821 OR rule.id: 100822 OR rule.id: 100823")
|
||||
Q_WARNINGS = "rule.id: 100810 OR rule.id: 100811 OR rule.id: 100812"
|
||||
# Peers actifs : cardinalite sur data.srcip, tous rule.id de handshake confondus
|
||||
# (100802/100822 = reponse envoyee, le cas dominant sur un serveur qui recoit les
|
||||
# connexions ; 100803/100823 = reponse recue, cote initiateur).
|
||||
Q_COMPLETE = Q_HANDSHAKES
|
||||
|
||||
# ── Client HTTP ────────────────────────────────────────────────────────────────
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
_auth = base64.b64encode(f"{DASHBOARD_USER}:{DASHBOARD_PASS}".encode()).decode()
|
||||
_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"osd-xsrf": "true",
|
||||
"Authorization": f"Basic {_auth}",
|
||||
}
|
||||
|
||||
def api(method, path, body=None):
|
||||
data = json.dumps(body).encode() if body else None
|
||||
req = urllib.request.Request(DASHBOARD_HOST + path, data=data, headers=_headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=ctx, timeout=15) as r:
|
||||
return json.loads(r.read())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"error": e.code, "msg": e.read().decode()[:300]}
|
||||
|
||||
def search_src(query):
|
||||
return json.dumps({
|
||||
"index": INDEX_PATTERN,
|
||||
"filter": [],
|
||||
"query": {"language": "kuery", "query": query},
|
||||
})
|
||||
|
||||
# ── Visualisations ─────────────────────────────────────────────────────────────
|
||||
VISUALIZATIONS = [
|
||||
# ── 1. Métrique : total handshakes (24h) ──────────────────────────────────
|
||||
{
|
||||
"id": "vpn-total-handshakes",
|
||||
"title": "[VPN] Handshakes (24h)",
|
||||
"type": "metric",
|
||||
"query": Q_HANDSHAKES,
|
||||
"visState": {
|
||||
"type": "metric",
|
||||
"params": {
|
||||
"metric": {
|
||||
"percentageMode": False, "useRanges": False,
|
||||
"colorSchema": "Green to Red",
|
||||
"metricColorMode": "None",
|
||||
"colorsRange": [{"from": 0, "to": 9999999}],
|
||||
"labels": {"show": True},
|
||||
"invertColors": False,
|
||||
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False,
|
||||
"subText": "handshakes VPN", "fontSize": 60},
|
||||
}
|
||||
},
|
||||
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
|
||||
},
|
||||
},
|
||||
# ── 2. Métrique : alertes déconnexion ─────────────────────────────────────
|
||||
{
|
||||
"id": "vpn-disconnects",
|
||||
"title": "[VPN] Alertes Deconnexion",
|
||||
"type": "metric",
|
||||
"query": Q_WARNINGS,
|
||||
"visState": {
|
||||
"type": "metric",
|
||||
"params": {
|
||||
"metric": {
|
||||
"percentageMode": False, "useRanges": False,
|
||||
"colorSchema": "Green to Red",
|
||||
"metricColorMode": "Labels",
|
||||
"colorsRange": [{"from": 0, "to": 1}, {"from": 1, "to": 10}, {"from": 10, "to": 99999}],
|
||||
"labels": {"show": True},
|
||||
"invertColors": False,
|
||||
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False,
|
||||
"subText": "timeouts / deconnexions", "fontSize": 60},
|
||||
}
|
||||
},
|
||||
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
|
||||
},
|
||||
},
|
||||
# ── 3. Métrique : peers actifs distincts ──────────────────────────────────
|
||||
{
|
||||
"id": "vpn-active-peers",
|
||||
"title": "[VPN] Peers Actifs (distincts)",
|
||||
"type": "metric",
|
||||
"query": Q_COMPLETE,
|
||||
"visState": {
|
||||
"type": "metric",
|
||||
"params": {
|
||||
"metric": {
|
||||
"percentageMode": False, "useRanges": False,
|
||||
"colorSchema": "Blues",
|
||||
"metricColorMode": "None",
|
||||
"colorsRange": [{"from": 0, "to": 99999}],
|
||||
"labels": {"show": True},
|
||||
"invertColors": False,
|
||||
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False,
|
||||
"subText": "IPs peers uniques", "fontSize": 60},
|
||||
}
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "cardinality", "schema": "metric",
|
||||
"params": {"field": "data.srcip"}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 4. Timeline : activité WireGuard ──────────────────────────────────────
|
||||
{
|
||||
"id": "vpn-timeline",
|
||||
"title": "[VPN] Timeline Activite WireGuard",
|
||||
"type": "histogram",
|
||||
"query": Q_ALL,
|
||||
"visState": {
|
||||
"type": "histogram",
|
||||
"params": {
|
||||
"type": "histogram",
|
||||
"grid": {"categoryLines": False},
|
||||
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "bottom",
|
||||
"show": True, "style": {}, "scale": {"type": "linear"},
|
||||
"labels": {"show": True, "truncate": 100}, "title": {}}],
|
||||
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value",
|
||||
"position": "left", "show": True, "style": {},
|
||||
"scale": {"type": "linear", "mode": "normal"},
|
||||
"labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100},
|
||||
"title": {"text": "Evenements"}}],
|
||||
"seriesParams": [{"show": True, "type": "histogram", "mode": "stacked",
|
||||
"data": {"label": "Evenements", "id": "1"},
|
||||
"valueAxis": "ValueAxis-1", "drawLinesBetweenPoints": True,
|
||||
"showCircles": True}],
|
||||
"addTooltip": True, "addLegend": True, "legendPosition": "right",
|
||||
"times": [], "addTimeMarker": False,
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "date_histogram", "schema": "segment",
|
||||
"params": {"field": "@timestamp", "interval": "auto", "min_doc_count": 1, "extended_bounds": {}}},
|
||||
{"id": "3", "enabled": True, "type": "terms", "schema": "group",
|
||||
"params": {"field": "rule.description", "size": 5, "order": "desc", "orderBy": "1",
|
||||
"otherBucket": True, "otherBucketLabel": "Autres"}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 5. Bar horizontal : événements par peer (IP) ──────────────────────────
|
||||
{
|
||||
"id": "vpn-events-per-peer",
|
||||
"title": "[VPN] Evenements par Peer",
|
||||
"type": "horizontal_bar",
|
||||
"query": Q_ALL,
|
||||
"visState": {
|
||||
"type": "horizontal_bar",
|
||||
"params": {
|
||||
"type": "horizontal_bar",
|
||||
"grid": {"categoryLines": False},
|
||||
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "left",
|
||||
"show": True, "style": {},
|
||||
"labels": {"show": True, "rotate": 0, "filter": True, "truncate": 200},
|
||||
"title": {}}],
|
||||
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value",
|
||||
"position": "bottom", "show": True, "style": {},
|
||||
"scale": {"type": "linear", "mode": "normal"},
|
||||
"labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100},
|
||||
"title": {"text": "Evenements"}}],
|
||||
"seriesParams": [{"show": True, "type": "horizontal_bar", "mode": "stacked",
|
||||
"data": {"label": "Evenements", "id": "1"},
|
||||
"valueAxis": "ValueAxis-1"}],
|
||||
"addTooltip": True, "addLegend": True, "legendPosition": "right",
|
||||
"times": [], "addTimeMarker": False,
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment",
|
||||
"params": {"field": "data.srcip", "size": 10, "order": "desc", "orderBy": "1",
|
||||
"otherBucket": False, "missingBucket": False}},
|
||||
{"id": "3", "enabled": True, "type": "terms", "schema": "group",
|
||||
"params": {"field": "rule.description", "size": 4, "order": "desc", "orderBy": "1",
|
||||
"otherBucket": True, "otherBucketLabel": "Autres"}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 6. Pie : types d'événements ───────────────────────────────────────────
|
||||
{
|
||||
"id": "vpn-event-types",
|
||||
"title": "[VPN] Types d Evenements",
|
||||
"type": "pie",
|
||||
"query": Q_ALL,
|
||||
"visState": {
|
||||
"type": "pie",
|
||||
"params": {
|
||||
"type": "pie", "addTooltip": True, "addLegend": True,
|
||||
"legendPosition": "right", "isDonut": True,
|
||||
"labels": {"show": True, "values": True, "last_level": True, "truncate": 100},
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment",
|
||||
"params": {"field": "rule.description", "size": 8, "order": "desc", "orderBy": "1",
|
||||
"otherBucket": True, "otherBucketLabel": "Autres", "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 7. Timeline : timeouts et déconnexions ────────────────────────────────
|
||||
{
|
||||
"id": "vpn-timeout-timeline",
|
||||
"title": "[VPN] Timeouts et Deconnexions",
|
||||
"type": "histogram",
|
||||
"query": Q_WARNINGS,
|
||||
"visState": {
|
||||
"type": "histogram",
|
||||
"params": {
|
||||
"type": "histogram",
|
||||
"grid": {"categoryLines": False},
|
||||
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "bottom",
|
||||
"show": True, "style": {},
|
||||
"labels": {"show": True, "truncate": 100}, "title": {}}],
|
||||
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value",
|
||||
"position": "left", "show": True, "style": {},
|
||||
"scale": {"type": "linear", "mode": "normal"},
|
||||
"labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100},
|
||||
"title": {"text": "Alertes"}}],
|
||||
"seriesParams": [{"show": True, "type": "histogram", "mode": "stacked",
|
||||
"data": {"label": "Alertes", "id": "1"},
|
||||
"valueAxis": "ValueAxis-1"}],
|
||||
"addTooltip": True, "addLegend": True, "legendPosition": "right",
|
||||
"times": [], "addTimeMarker": False,
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "date_histogram", "schema": "segment",
|
||||
"params": {"field": "@timestamp", "interval": "auto", "min_doc_count": 1, "extended_bounds": {}}},
|
||||
{"id": "3", "enabled": True, "type": "terms", "schema": "group",
|
||||
"params": {"field": "data.srcip", "size": 6, "order": "desc", "orderBy": "1",
|
||||
"otherBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
# ── 8. Table : log des événements VPN ─────────────────────────────────────
|
||||
{
|
||||
"id": "vpn-events-table",
|
||||
"title": "[VPN] Journal Evenements VPN",
|
||||
"type": "table",
|
||||
"query": Q_ALL,
|
||||
"visState": {
|
||||
"type": "table",
|
||||
"params": {
|
||||
"perPage": 20, "showPartialRows": False, "showMetricsAtAllLevels": False,
|
||||
"sort": {"columnIndex": None, "direction": None},
|
||||
"showTotal": False, "totalFunc": "sum",
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "bucket",
|
||||
"params": {"field": "data.srcip", "size": 20, "order": "desc", "orderBy": "1",
|
||||
"otherBucket": False, "missingBucket": True, "missingBucketLabel": "inconnu"}},
|
||||
{"id": "3", "enabled": True, "type": "terms", "schema": "bucket",
|
||||
"params": {"field": "rule.description", "size": 5, "order": "desc", "orderBy": "1",
|
||||
"otherBucket": False, "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
"uiStateJSON": json.dumps({"vis": {"params": {"sort": {"columnIndex": 0, "direction": "desc"}}}}),
|
||||
},
|
||||
]
|
||||
|
||||
# ── Layout dashboard (grille 48 colonnes) ──────────────────────────────────────
|
||||
# Ligne 0 : 3 métriques
|
||||
# Ligne 6 : timeline activité (pleine largeur)
|
||||
# Ligne 18: events par peer (gauche) + types d'événements (droite)
|
||||
# Ligne 34: timeline timeouts (gauche) + table journal (droite)
|
||||
PANELS = [
|
||||
{"panelIndex": "1", "gridData": {"x": 0, "y": 0, "w": 16, "h": 6, "i": "1"}, "version": "2.19.5", "type": "visualization", "id": "vpn-total-handshakes", "embeddableConfig": {}},
|
||||
{"panelIndex": "2", "gridData": {"x": 16, "y": 0, "w": 16, "h": 6, "i": "2"}, "version": "2.19.5", "type": "visualization", "id": "vpn-disconnects", "embeddableConfig": {}},
|
||||
{"panelIndex": "3", "gridData": {"x": 32, "y": 0, "w": 16, "h": 6, "i": "3"}, "version": "2.19.5", "type": "visualization", "id": "vpn-active-peers", "embeddableConfig": {}},
|
||||
{"panelIndex": "4", "gridData": {"x": 0, "y": 6, "w": 48, "h": 12, "i": "4"}, "version": "2.19.5", "type": "visualization", "id": "vpn-timeline", "embeddableConfig": {}},
|
||||
{"panelIndex": "5", "gridData": {"x": 0, "y": 18, "w": 30, "h": 16, "i": "5"}, "version": "2.19.5", "type": "visualization", "id": "vpn-events-per-peer", "embeddableConfig": {}},
|
||||
{"panelIndex": "6", "gridData": {"x": 30, "y": 18, "w": 18, "h": 16, "i": "6"}, "version": "2.19.5", "type": "visualization", "id": "vpn-event-types", "embeddableConfig": {}},
|
||||
{"panelIndex": "7", "gridData": {"x": 0, "y": 34, "w": 24, "h": 14, "i": "7"}, "version": "2.19.5", "type": "visualization", "id": "vpn-timeout-timeline", "embeddableConfig": {}},
|
||||
{"panelIndex": "8", "gridData": {"x": 24, "y": 34, "w": 24, "h": 14, "i": "8"}, "version": "2.19.5", "type": "visualization", "id": "vpn-events-table", "embeddableConfig": {}},
|
||||
]
|
||||
|
||||
# ── Main ───────────────────────────────────────────────────────────────────────
|
||||
def main():
|
||||
print("=== Creation dashboard: VPN - Activite WireGuard ===\n")
|
||||
|
||||
for viz in VISUALIZATIONS:
|
||||
vis_state = dict(viz["visState"])
|
||||
vis_state["title"] = viz["title"]
|
||||
body = {
|
||||
"attributes": {
|
||||
"title": viz["title"],
|
||||
"visState": json.dumps(vis_state),
|
||||
"uiStateJSON": viz.get("uiStateJSON", "{}"),
|
||||
"description": "",
|
||||
"kibanaSavedObjectMeta": {"searchSourceJSON": search_src(viz["query"])},
|
||||
}
|
||||
}
|
||||
r = api("POST", f"/api/saved_objects/visualization/{viz['id']}?overwrite=true", body)
|
||||
if "id" in r:
|
||||
print(f" OK {viz['title']}")
|
||||
else:
|
||||
print(f" ERR {viz['title']}: {r}")
|
||||
sys.exit(1)
|
||||
|
||||
dashboard_body = {
|
||||
"attributes": {
|
||||
"title": "VPN - Activite WireGuard",
|
||||
"description": "Handshakes peers, deconnexions, timeouts, activite par IP — agent vpn-prod",
|
||||
"panelsJSON": json.dumps(PANELS),
|
||||
"optionsJSON": json.dumps({"useMargins": True, "hidePanelTitles": False}),
|
||||
"timeRestore": True,
|
||||
"timeFrom": "now-24h",
|
||||
"timeTo": "now",
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": json.dumps({"query": {"language": "kuery", "query": ""}, "filter": []})
|
||||
},
|
||||
}
|
||||
}
|
||||
r = api("POST", "/api/saved_objects/dashboard/vpn-wireguard-dashboard?overwrite=true", dashboard_body)
|
||||
if "id" in r:
|
||||
print(f"\n OK Dashboard: {r['id']}")
|
||||
print(f" URL: https://<monitoring-ip>/#/app/dashboards#/view/{r['id']}")
|
||||
else:
|
||||
print(f"\n ERR Dashboard: {r}")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n=== Termine ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Crée le dashboard "Securite - Alertes Web et Firewall" dans OpenSearch Dashboards (Wazuh).
|
||||
|
||||
Usage:
|
||||
python3 create-dashboard.py
|
||||
|
||||
Le script se connecte depuis l'intérieur du container wazuh_dashboard via:
|
||||
docker exec wazuh_dashboard python3 /tmp/create-dashboard.py
|
||||
|
||||
Ou directement si OpenSearch Dashboards est accessible sur localhost:5601.
|
||||
"""
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import ssl
|
||||
import base64
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
# ── Configuration ─────────────────────────────────────────────────────────────
|
||||
DASHBOARD_HOST = "https://localhost:5601"
|
||||
DASHBOARD_USER = "kibanaserver"
|
||||
DASHBOARD_PASS = os.environ.get("DASHBOARD_PASSWORD", "E9Jpr6586kQ3wYrCS2!")
|
||||
INDEX_PATTERN = "wazuh-alerts-*"
|
||||
|
||||
# ── Queries de filtre ─────────────────────────────────────────────────────────
|
||||
Q_WEB = "rule.groups: web OR rule.groups: attack OR rule.groups: modsecurity OR rule.groups: api_brute_force"
|
||||
Q_FW = "rule.id: 651"
|
||||
Q_ALL = Q_WEB + " OR " + Q_FW
|
||||
|
||||
# ── Client HTTP ───────────────────────────────────────────────────────────────
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
_auth = base64.b64encode(f"{DASHBOARD_USER}:{DASHBOARD_PASS}".encode()).decode()
|
||||
_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"osd-xsrf": "true",
|
||||
"Authorization": f"Basic {_auth}",
|
||||
}
|
||||
|
||||
def api(method, path, body=None):
|
||||
data = json.dumps(body).encode() if body else None
|
||||
req = urllib.request.Request(DASHBOARD_HOST + path, data=data, headers=_headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=ctx, timeout=15) as r:
|
||||
return json.loads(r.read())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"error": e.code, "msg": e.read().decode()[:300]}
|
||||
|
||||
def search_src(query):
|
||||
return json.dumps({
|
||||
"index": INDEX_PATTERN,
|
||||
"filter": [],
|
||||
"query": {"language": "kuery", "query": query},
|
||||
})
|
||||
|
||||
# ── Définition des visualisations ─────────────────────────────────────────────
|
||||
VISUALIZATIONS = [
|
||||
{
|
||||
"id": "sec-web-count",
|
||||
"title": "[Securite] Alertes Web - Total",
|
||||
"type": "metric",
|
||||
"query": Q_WEB,
|
||||
"visState": {
|
||||
"type": "metric",
|
||||
"params": {
|
||||
"metric": {
|
||||
"percentageMode": False,
|
||||
"useRanges": False,
|
||||
"colorSchema": "Green to Red",
|
||||
"metricColorMode": "Labels",
|
||||
"colorsRange": [{"from": 0, "to": 10}, {"from": 10, "to": 100}, {"from": 100, "to": 9999}],
|
||||
"labels": {"show": True},
|
||||
"invertColors": False,
|
||||
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False, "subText": "alertes web", "fontSize": 60},
|
||||
}
|
||||
},
|
||||
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "sec-cs-count",
|
||||
"title": "[Securite] Firewall - Total Bans",
|
||||
"type": "metric",
|
||||
"query": Q_FW,
|
||||
"visState": {
|
||||
"type": "metric",
|
||||
"params": {
|
||||
"metric": {
|
||||
"percentageMode": False,
|
||||
"useRanges": False,
|
||||
"colorSchema": "Green to Red",
|
||||
"metricColorMode": "Labels",
|
||||
"colorsRange": [{"from": 0, "to": 5}, {"from": 5, "to": 50}, {"from": 50, "to": 9999}],
|
||||
"labels": {"show": True},
|
||||
"invertColors": False,
|
||||
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False, "subText": "bans firewall", "fontSize": 60},
|
||||
}
|
||||
},
|
||||
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "sec-web-timeline",
|
||||
"title": "[Securite] Alertes Web - Timeline",
|
||||
"type": "histogram",
|
||||
"query": Q_WEB,
|
||||
"visState": {
|
||||
"type": "histogram",
|
||||
"params": {
|
||||
"type": "histogram",
|
||||
"grid": {"categoryLines": False},
|
||||
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "bottom", "show": True, "style": {}, "scale": {"type": "linear"}, "labels": {"show": True, "truncate": 100}, "title": {}}],
|
||||
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value", "position": "left", "show": True, "style": {}, "scale": {"type": "linear", "mode": "normal"}, "labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100}, "title": {"text": "Alertes"}}],
|
||||
"seriesParams": [{"show": True, "type": "histogram", "mode": "stacked", "data": {"label": "Alertes", "id": "1"}, "valueAxis": "ValueAxis-1", "drawLinesBetweenPoints": True, "showCircles": True}],
|
||||
"addTooltip": True, "addLegend": True, "legendPosition": "right", "times": [], "addTimeMarker": False,
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "date_histogram", "schema": "segment", "params": {"field": "@timestamp", "interval": "auto", "customInterval": "2h", "min_doc_count": 1, "extended_bounds": {}}},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "sec-top-ips",
|
||||
"title": "[Securite] Top IPs Attaquantes",
|
||||
"type": "horizontal_bar",
|
||||
"query": Q_ALL,
|
||||
"visState": {
|
||||
"type": "horizontal_bar",
|
||||
"params": {
|
||||
"type": "horizontal_bar",
|
||||
"grid": {"categoryLines": False},
|
||||
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "left", "show": True, "style": {}, "scale": {"type": "linear"}, "labels": {"show": True, "rotate": 0, "filter": True, "truncate": 200}, "title": {}}],
|
||||
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value", "position": "bottom", "show": True, "style": {}, "scale": {"type": "linear", "mode": "normal"}, "labels": {"show": True, "rotate": 75, "filter": False, "truncate": 100}, "title": {"text": "Alertes"}}],
|
||||
"seriesParams": [{"show": True, "type": "horizontal_bar", "mode": "stacked", "data": {"label": "Alertes", "id": "1"}, "valueAxis": "ValueAxis-1", "drawLinesBetweenPoints": True, "showCircles": True}],
|
||||
"addTooltip": True, "addLegend": True, "legendPosition": "right", "times": [], "addTimeMarker": False,
|
||||
},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment", "params": {"field": "data.srcip", "size": 15, "order": "desc", "orderBy": "1", "otherBucket": False, "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "sec-top-rules",
|
||||
"title": "[Securite] Top Regles Declenchees",
|
||||
"type": "pie",
|
||||
"query": Q_WEB,
|
||||
"visState": {
|
||||
"type": "pie",
|
||||
"params": {"type": "pie", "addTooltip": True, "addLegend": True, "legendPosition": "right", "isDonut": True, "labels": {"show": False, "values": True, "last_level": True, "truncate": 100}},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment", "params": {"field": "rule.description", "size": 10, "order": "desc", "orderBy": "1", "otherBucket": True, "otherBucketLabel": "Autres", "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "sec-cs-bans-table",
|
||||
"title": "[Securite] Firewall - IPs Bannies",
|
||||
"type": "table",
|
||||
"query": Q_FW,
|
||||
"visState": {
|
||||
"type": "table",
|
||||
"params": {"perPage": 20, "showPartialRows": False, "showMetricsAtAllLevels": False, "sort": {"columnIndex": None, "direction": None}, "showTotal": False, "totalFunc": "sum"},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "bucket", "params": {"field": "data.srcip", "size": 30, "order": "desc", "orderBy": "1", "otherBucket": False, "missingBucket": False}},
|
||||
{"id": "3", "enabled": True, "type": "terms", "schema": "bucket", "params": {"field": "rule.description", "size": 5, "order": "desc", "orderBy": "1", "otherBucket": False, "missingBucket": False}},
|
||||
],
|
||||
},
|
||||
"uiStateJSON": json.dumps({"vis": {"params": {"sort": {"columnIndex": 0, "direction": "desc"}}}}),
|
||||
},
|
||||
{
|
||||
"id": "sec-severity",
|
||||
"title": "[Securite] Distribution Severite",
|
||||
"type": "pie",
|
||||
"query": Q_WEB,
|
||||
"visState": {
|
||||
"type": "pie",
|
||||
"params": {"type": "pie", "addTooltip": True, "addLegend": True, "legendPosition": "right", "isDonut": False, "labels": {"show": True, "values": True, "last_level": True, "truncate": 100}},
|
||||
"aggs": [
|
||||
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
|
||||
{"id": "2", "enabled": True, "type": "terms", "schema": "segment", "params": {"field": "rule.level", "size": 15, "order": "desc", "orderBy": "1", "otherBucket": False}},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# ── Layout dashboard (grid 48 colonnes) ───────────────────────────────────────
|
||||
PANELS = [
|
||||
{"panelIndex": "1", "gridData": {"x": 0, "y": 0, "w": 24, "h": 6, "i": "1"}, "version": "2.19.5", "type": "visualization", "id": "sec-web-count", "embeddableConfig": {}},
|
||||
{"panelIndex": "2", "gridData": {"x": 24, "y": 0, "w": 24, "h": 6, "i": "2"}, "version": "2.19.5", "type": "visualization", "id": "sec-cs-count", "embeddableConfig": {}},
|
||||
{"panelIndex": "3", "gridData": {"x": 0, "y": 6, "w": 48, "h": 12, "i": "3"}, "version": "2.19.5", "type": "visualization", "id": "sec-web-timeline", "embeddableConfig": {}},
|
||||
{"panelIndex": "4", "gridData": {"x": 0, "y": 18, "w": 28, "h": 14, "i": "4"}, "version": "2.19.5", "type": "visualization", "id": "sec-top-ips", "embeddableConfig": {}},
|
||||
{"panelIndex": "5", "gridData": {"x": 28, "y": 18, "w": 20, "h": 14, "i": "5"}, "version": "2.19.5", "type": "visualization", "id": "sec-top-rules", "embeddableConfig": {}},
|
||||
{"panelIndex": "6", "gridData": {"x": 0, "y": 32, "w": 48, "h": 12, "i": "6"}, "version": "2.19.5", "type": "visualization", "id": "sec-cs-bans-table", "embeddableConfig": {}},
|
||||
{"panelIndex": "7", "gridData": {"x": 0, "y": 44, "w": 24, "h": 10, "i": "7"}, "version": "2.19.5", "type": "visualization", "id": "sec-severity", "embeddableConfig": {}},
|
||||
]
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||
def main():
|
||||
print("=== Création du dashboard Securite - Alertes Web et Firewall ===\n")
|
||||
|
||||
# Visualisations
|
||||
for viz in VISUALIZATIONS:
|
||||
vis_state = dict(viz["visState"])
|
||||
vis_state["title"] = viz["title"]
|
||||
body = {
|
||||
"attributes": {
|
||||
"title": viz["title"],
|
||||
"visState": json.dumps(vis_state),
|
||||
"uiStateJSON": viz.get("uiStateJSON", "{}"),
|
||||
"description": "",
|
||||
"kibanaSavedObjectMeta": {"searchSourceJSON": search_src(viz["query"])},
|
||||
}
|
||||
}
|
||||
r = api("POST", f"/api/saved_objects/visualization/{viz['id']}?overwrite=true", body)
|
||||
if "id" in r:
|
||||
print(f" ✅ {viz['title']}")
|
||||
else:
|
||||
print(f" ❌ {viz['title']}: {r}")
|
||||
sys.exit(1)
|
||||
|
||||
# Dashboard
|
||||
dashboard_body = {
|
||||
"attributes": {
|
||||
"title": "Securite - Alertes Web et Firewall",
|
||||
"description": "Alertes OWASP ModSecurity brute-force API et bans Firewall",
|
||||
"panelsJSON": json.dumps(PANELS),
|
||||
"optionsJSON": json.dumps({"useMargins": True, "hidePanelTitles": False}),
|
||||
"timeRestore": False,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": json.dumps({"query": {"language": "kuery", "query": ""}, "filter": []})
|
||||
},
|
||||
}
|
||||
}
|
||||
r = api("POST", "/api/saved_objects/dashboard/sec-web-crowdsec-dashboard?overwrite=true", dashboard_body)
|
||||
if "id" in r:
|
||||
print(f"\n ✅ Dashboard: {r['id']}")
|
||||
print(f"\n URL: https://<monitoring-ip>/#/app/dashboards#/view/{r['id']}")
|
||||
else:
|
||||
print(f"\n ❌ Dashboard: {r}")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n=== Terminé ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Crée des Saved Queries réutilisables pour la détection d'attaques web
|
||||
(ModSecurity/OWASP, rules 100100-100396 — voir
|
||||
config/wazuh_manager/rules/modsecurity_owasp_rules.xml) dans OpenSearch
|
||||
Dashboards (Wazuh).
|
||||
|
||||
Usage:
|
||||
docker cp create-saved-queries-web.py wazuh_dashboard:/tmp/
|
||||
docker exec wazuh_dashboard python3 /tmp/create-saved-queries-web.py
|
||||
|
||||
Une fois créées, ouvrir n'importe quelle vue (Security Events, Discover...) >
|
||||
barre de recherche > icone dossier (Saved Queries) > sélectionner la query.
|
||||
"""
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import ssl
|
||||
import base64
|
||||
import sys
|
||||
import os
|
||||
|
||||
# ── Configuration ────────────────────────────────────────────────────────────
|
||||
DASHBOARD_HOST = "https://localhost:5601"
|
||||
DASHBOARD_USER = "kibanaserver"
|
||||
DASHBOARD_PASS = os.environ.get("DASHBOARD_PASSWORD", "E9Jpr6586kQ3wYrCS2!")
|
||||
|
||||
# ── Queries ───────────────────────────────────────────────────────────────────
|
||||
QUERIES = [
|
||||
{
|
||||
"id": "web-attacks-all",
|
||||
"title": "Attaques Web - Toutes",
|
||||
"description": "Tous les événements ModSecurity/OWASP (bloqués + remontés en anomaly scoring).",
|
||||
"kuery": "rule.groups: web",
|
||||
},
|
||||
{
|
||||
"id": "web-attacks-blocked",
|
||||
"title": "Attaques Web - Bloquées (WAF)",
|
||||
"description": "Requêtes réellement bloquées par le WAF (exclut les anomaly scoring non bloquants, groupe 'warning').",
|
||||
"kuery": "rule.groups: web AND NOT rule.groups: warning",
|
||||
},
|
||||
{
|
||||
"id": "web-attacks-critical",
|
||||
"title": "Attaques Web - Critiques",
|
||||
"description": "Niveau >= 13 : SQLi/RCE/command injection bloquées, XXE, SSRF, patterns répétés (owasp_injection critical/brute_force).",
|
||||
"kuery": "rule.groups: web AND rule.level >= 13",
|
||||
},
|
||||
{
|
||||
"id": "web-attacks-scanners",
|
||||
"title": "Attaques Web - Scanners / Recon",
|
||||
"description": "Détection de scanners/bots (OWASP#9 — groupe scanner_detection).",
|
||||
"kuery": "rule.groups: scanner_detection",
|
||||
},
|
||||
{
|
||||
"id": "web-attacks-repeated",
|
||||
"title": "Attaques Web - Patterns répétés (brute-force)",
|
||||
"description": "Attaques soutenues détectées par fréquence : SQLi (100290), XSS (100291), RCE (100292), path traversal (100293), anomaly scoring répété (100394-100396).",
|
||||
"kuery": "rule.id: 100290 OR rule.id: 100291 OR rule.id: 100292 OR rule.id: 100293 OR rule.id: 100394 OR rule.id: 100395 OR rule.id: 100396",
|
||||
},
|
||||
]
|
||||
|
||||
# ── Client HTTP ───────────────────────────────────────────────────────────────
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
_auth = base64.b64encode(f"{DASHBOARD_USER}:{DASHBOARD_PASS}".encode()).decode()
|
||||
_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"osd-xsrf": "true",
|
||||
"Authorization": f"Basic {_auth}",
|
||||
}
|
||||
|
||||
def api(method, path, body=None):
|
||||
data = json.dumps(body).encode() if body else None
|
||||
req = urllib.request.Request(DASHBOARD_HOST + path, data=data, headers=_headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=ctx, timeout=15) as r:
|
||||
return json.loads(r.read())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"error": e.code, "msg": e.read().decode()[:300]}
|
||||
|
||||
def main():
|
||||
print("=== Creation Saved Queries: Attaques Web ===\n")
|
||||
failed = False
|
||||
for q in QUERIES:
|
||||
body = {
|
||||
"attributes": {
|
||||
"title": q["title"],
|
||||
"description": q["description"],
|
||||
"query": {"language": "kuery", "query": q["kuery"]},
|
||||
"filters": [],
|
||||
}
|
||||
}
|
||||
r = api("POST", f"/api/saved_objects/query/{q['id']}?overwrite=true", body)
|
||||
if "id" in r:
|
||||
print(f" OK {q['title']}")
|
||||
else:
|
||||
print(f" ERR {q['title']}: {r}")
|
||||
failed = True
|
||||
if failed:
|
||||
sys.exit(1)
|
||||
print("\n=== Termine ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Crée une Saved Query "Sans VPN" réutilisable dans OpenSearch Dashboards (Wazuh).
|
||||
|
||||
Objectif : permettre d'exclure en un clic les alertes WireGuard/VPN (handshakes,
|
||||
timeouts, roaming, reconnexions — rules 100800-100826) de la vue native
|
||||
"Security Events" / "Threat Hunting", qui liste par défaut TOUTES les alertes
|
||||
sans filtre de groupe. Le dashboard dédié "VPN - Activite WireGuard"
|
||||
(create-dashboard-vpn.py) continue lui d'afficher ces événements normalement :
|
||||
cette query ne supprime rien de l'index, elle filtre uniquement l'affichage.
|
||||
|
||||
Usage:
|
||||
docker cp create-saved-query-no-vpn.py wazuh_dashboard:/tmp/
|
||||
docker exec wazuh_dashboard python3 /tmp/create-saved-query-no-vpn.py
|
||||
|
||||
Une fois créée, ouvrir "Security Events" > barre de recherche > icône
|
||||
"Saved Queries" (dossier) > sélectionner "Sans VPN".
|
||||
"""
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import ssl
|
||||
import base64
|
||||
import sys
|
||||
import os
|
||||
|
||||
# ── Configuration ────────────────────────────────────────────────────────────
|
||||
DASHBOARD_HOST = "https://localhost:5601"
|
||||
DASHBOARD_USER = "kibanaserver"
|
||||
DASHBOARD_PASS = os.environ.get("DASHBOARD_PASSWORD", "E9Jpr6586kQ3wYrCS2!")
|
||||
|
||||
QUERY_ID = "no-vpn-alerts"
|
||||
QUERY_TITLE = "Sans VPN"
|
||||
QUERY_DESC = "Exclut les alertes WireGuard/VPN (handshakes, timeouts, roaming) de la vue Security Events."
|
||||
QUERY_KUERY = "NOT rule.groups: wireguard"
|
||||
|
||||
# ── Client HTTP ───────────────────────────────────────────────────────────────
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
_auth = base64.b64encode(f"{DASHBOARD_USER}:{DASHBOARD_PASS}".encode()).decode()
|
||||
_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"osd-xsrf": "true",
|
||||
"Authorization": f"Basic {_auth}",
|
||||
}
|
||||
|
||||
def api(method, path, body=None):
|
||||
data = json.dumps(body).encode() if body else None
|
||||
req = urllib.request.Request(DASHBOARD_HOST + path, data=data, headers=_headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=ctx, timeout=15) as r:
|
||||
return json.loads(r.read())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"error": e.code, "msg": e.read().decode()[:300]}
|
||||
|
||||
def main():
|
||||
print("=== Creation Saved Query: Sans VPN ===\n")
|
||||
body = {
|
||||
"attributes": {
|
||||
"title": QUERY_TITLE,
|
||||
"description": QUERY_DESC,
|
||||
"query": {"language": "kuery", "query": QUERY_KUERY},
|
||||
"filters": [],
|
||||
}
|
||||
}
|
||||
r = api("POST", f"/api/saved_objects/query/{QUERY_ID}?overwrite=true", body)
|
||||
if "id" in r:
|
||||
print(f" OK Saved Query '{QUERY_TITLE}' créée ({r['id']})")
|
||||
print(" Utilisation : Security Events > barre de recherche > icone dossier (Saved Queries) > 'Sans VPN'")
|
||||
else:
|
||||
print(f" ERR {r}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
CERTS_DIR="$SCRIPT_DIR/certs"
|
||||
|
||||
mkdir -p "$CERTS_DIR"
|
||||
cd /tmp
|
||||
|
||||
echo "[*] Téléchargement de l'outil de génération de certificats Wazuh..."
|
||||
curl -sO https://packages.wazuh.com/4.14/wazuh-certs-tool.sh
|
||||
cp "$SCRIPT_DIR/config/certs.yml" config.yml
|
||||
|
||||
echo "[*] Génération des certificats..."
|
||||
bash wazuh-certs-tool.sh -A
|
||||
|
||||
echo "[*] Copie des certificats..."
|
||||
cp /tmp/wazuh-certificates/*.pem "$CERTS_DIR/"
|
||||
cp /tmp/wazuh-certificates/root-ca.pem "$CERTS_DIR/root-ca-manager.pem"
|
||||
|
||||
chmod 640 "$CERTS_DIR"/*.pem
|
||||
rm -rf /tmp/wazuh-certificates /tmp/wazuh-install-files /tmp/wazuh-install-files.tar /tmp/wazuh-certs-tool.sh /tmp/config.yml
|
||||
|
||||
echo "[OK] Certificats générés dans $CERTS_DIR"
|
||||
@@ -0,0 +1,2 @@
|
||||
Soc-1:AqGx3H0O9RJqfRR5Wzw7W!
|
||||
Soc-2:P493A81j3bsSXCupftpVJI!
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Generate WireGuard client config for admin
|
||||
# Usage: wireguard-admin-client.sh <admin_name> <server_ip> <server_pubkey>
|
||||
# Output: admin_name.conf (ready for import in WireGuard app)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
ADMIN_NAME="${1:-admin2}"
|
||||
VPN_SERVER_IP="${2:-}"
|
||||
VPN_SERVER_PUBKEY="${3:-}"
|
||||
|
||||
if [ -z "$VPN_SERVER_IP" ] || [ -z "$VPN_SERVER_PUBKEY" ]; then
|
||||
echo "Usage: $0 <admin_name> <server_ip> <server_pubkey>"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 admin1 123.45.67.89 'aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE='"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Generating WireGuard config for admin: $ADMIN_NAME"
|
||||
|
||||
# ─── Generate unique keys for this admin ───────────────────────
|
||||
ADMIN_PRIVATEKEY=$(wg genkey)
|
||||
ADMIN_PUBKEY=$(echo "$ADMIN_PRIVATEKEY" | wg pubkey)
|
||||
|
||||
# ─── Auto-assign IP from 10.0.0.3+ ─────────────────────────────
|
||||
# Format: [admin_name].conf uses 10.0.0.3, 10.0.0.4, etc.
|
||||
# You should track these manually or use a database
|
||||
ADMIN_IP="10.0.0.3" # ← CHANGE THIS for each admin
|
||||
|
||||
# ─── Create config file ────────────────────────────────────────
|
||||
cat > "${ADMIN_NAME}.conf" << EOF
|
||||
[Interface]
|
||||
Address = $ADMIN_IP/32
|
||||
PrivateKey = $ADMIN_PRIVATEKEY
|
||||
DNS = 8.8.8.8
|
||||
|
||||
[Peer]
|
||||
PublicKey = $VPN_SERVER_PUBKEY
|
||||
AllowedIPs = 10.0.0.0/24
|
||||
Endpoint = $VPN_SERVER_IP:51820
|
||||
PersistentKeepalive = 25
|
||||
EOF
|
||||
|
||||
echo "[✓] Config created: ${ADMIN_NAME}.conf"
|
||||
echo ""
|
||||
echo "Configuration:"
|
||||
echo " Name: $ADMIN_NAME"
|
||||
echo " IP: $ADMIN_IP"
|
||||
echo " Public Key: $ADMIN_PUBKEY"
|
||||
echo ""
|
||||
echo "Steps to add to VPN server:"
|
||||
echo " 1. Copy public key above"
|
||||
echo " 2. SSH to VPN server"
|
||||
echo " 3. wg set wg0 peer $ADMIN_PUBKEY allowed-ips $ADMIN_IP/32"
|
||||
echo " 4. wg show"
|
||||
echo ""
|
||||
echo "To import on your device:"
|
||||
echo " 1. Install WireGuard app (Windows/Mac/Linux/iPhone/Android)"
|
||||
echo " 2. Import ${ADMIN_NAME}.conf"
|
||||
echo " 3. Connect to VPN"
|
||||
echo " 4. Access services:"
|
||||
echo " • Wazuh: https://10.0.0.2"
|
||||
echo " • Dozzle: http://10.0.0.2:8080"
|
||||
echo " • Beszel: http://10.0.0.2:9090"
|
||||
echo " • S3: http://10.0.0.2:9000"
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# WireGuard Client Setup (monitoring-uber)
|
||||
# Install WireGuard, generate client keys, auto-connect to VPN server
|
||||
# Client IP: 10.0.0.2/24
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
# ─── Parameters ────────────────────────────────────────────────────
|
||||
VPN_SERVER_IP="${1:-}" # IP publique du serveur VPN
|
||||
VPN_SERVER_PUBKEY="${2:-}" # Clé publique du serveur VPN
|
||||
|
||||
if [ -z "$VPN_SERVER_IP" ] || [ -z "$VPN_SERVER_PUBKEY" ]; then
|
||||
echo "Usage: $0 <VPN_SERVER_IP> <VPN_SERVER_PUBKEY>"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 123.45.67.89 'aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdE='"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Setting up WireGuard Client on monitoring-uber..."
|
||||
echo " Server IP: $VPN_SERVER_IP"
|
||||
echo " Server Pubkey: $VPN_SERVER_PUBKEY"
|
||||
|
||||
# ─── Install WireGuard ─────────────────────────────────────────
|
||||
echo "[*] Installing WireGuard..."
|
||||
apt-get update -qq
|
||||
apt-get install -y wireguard wireguard-tools
|
||||
|
||||
# ─── Create key directory ──────────────────────────────────────
|
||||
mkdir -p /etc/wireguard
|
||||
cd /etc/wireguard
|
||||
umask 077
|
||||
|
||||
# ─── Generate client keys ──────────────────────────────────────
|
||||
if [ ! -f client_privatekey ]; then
|
||||
echo "[*] Generating client private key..."
|
||||
wg genkey > client_privatekey
|
||||
cat client_privatekey | wg pubkey > client_publickey
|
||||
echo "[✓] Keys generated"
|
||||
echo ""
|
||||
echo "Client Public Key (for server):"
|
||||
cat client_publickey
|
||||
echo ""
|
||||
else
|
||||
echo "[!] Client keys already exist"
|
||||
fi
|
||||
|
||||
# ─── Create wg0 configuration ──────────────────────────────────
|
||||
echo "[*] Creating WireGuard client configuration..."
|
||||
|
||||
PRIVATE_KEY=$(cat client_privatekey)
|
||||
CLIENT_PUBKEY=$(cat client_publickey)
|
||||
|
||||
cat > wg0.conf << EOF
|
||||
[Interface]
|
||||
# monitoring-uber VPN IP
|
||||
Address = 10.0.0.2/24
|
||||
ListenPort = 0
|
||||
PrivateKey = $PRIVATE_KEY
|
||||
|
||||
[Peer]
|
||||
# VPN Server
|
||||
PublicKey = $VPN_SERVER_PUBKEY
|
||||
AllowedIPs = 10.0.0.0/24
|
||||
Endpoint = $VPN_SERVER_IP:51820
|
||||
PersistentKeepalive = 25
|
||||
EOF
|
||||
|
||||
chmod 600 wg0.conf
|
||||
|
||||
echo "[✓] Configuration created at /etc/wireguard/wg0.conf"
|
||||
|
||||
# ─── Enable at boot and start ──────────────────────────────────
|
||||
echo "[*] Enabling WireGuard at boot..."
|
||||
systemctl enable wg-quick@wg0 2>/dev/null || true
|
||||
systemctl start wg-quick@wg0
|
||||
|
||||
sleep 2
|
||||
|
||||
# Vérifier connexion
|
||||
if ip addr show wg0 &>/dev/null; then
|
||||
echo "[✓] WireGuard interface up"
|
||||
ip addr show wg0
|
||||
else
|
||||
echo "[!] WireGuard interface not up, check logs:"
|
||||
journalctl -u wg-quick@wg0 -n 10
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "[✓] WireGuard Client configured"
|
||||
echo ""
|
||||
echo "Configuration Summary:"
|
||||
echo " • Interface: wg0"
|
||||
echo " • Client IP: 10.0.0.2/24"
|
||||
echo " • Server: $VPN_SERVER_IP:51820"
|
||||
echo " • Config: /etc/wireguard/wg0.conf"
|
||||
echo ""
|
||||
echo "IMPORTANT: Add this client public key to VPN server:"
|
||||
echo " wg set wg0 peer $(cat client_publickey) allowed-ips 10.0.0.2/32"
|
||||
echo ""
|
||||
echo "Verify connection:"
|
||||
echo " ping 10.0.0.1"
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# WireGuard Server Setup (VPS VPN)
|
||||
# Install WireGuard, generate server keys, configure interface
|
||||
# Network: 10.0.0.0/24
|
||||
# Server IP: 10.0.0.1
|
||||
# Listen: 0.0.0.0:51820/udp
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
echo "[*] Setting up WireGuard Server..."
|
||||
|
||||
# ─── Install WireGuard ─────────────────────────────────────────
|
||||
echo "[*] Installing WireGuard..."
|
||||
apt-get update -qq
|
||||
apt-get install -y wireguard wireguard-tools
|
||||
|
||||
# ─── Create key directory ──────────────────────────────────────
|
||||
mkdir -p /etc/wireguard
|
||||
cd /etc/wireguard
|
||||
umask 077
|
||||
|
||||
# ─── Generate server keys ──────────────────────────────────────
|
||||
if [ ! -f privatekey ]; then
|
||||
echo "[*] Generating server private key..."
|
||||
wg genkey > privatekey
|
||||
cat privatekey | wg pubkey > publickey
|
||||
echo "[✓] Keys generated"
|
||||
echo ""
|
||||
echo "Server Public Key:"
|
||||
cat publickey
|
||||
echo ""
|
||||
else
|
||||
echo "[!] Server keys already exist"
|
||||
fi
|
||||
|
||||
# ─── Create wg0 configuration ──────────────────────────────────
|
||||
echo "[*] Creating WireGuard interface configuration..."
|
||||
|
||||
cat > wg0.conf << 'EOF'
|
||||
[Interface]
|
||||
# Server IP dans le réseau VPN
|
||||
Address = 10.0.0.1/24
|
||||
ListenPort = 51820
|
||||
|
||||
# Charger la clé privée
|
||||
PrivateKey = PRIVATE_KEY_PLACEHOLDER
|
||||
|
||||
# Accepter VPN traffic
|
||||
PostUp = iptables -I FORWARD 1 -i %i -j ACCEPT; iptables -I FORWARD 1 -o %i -j ACCEPT; iptables -t nat -I POSTROUTING 1 -o eth0 -j MASQUERADE
|
||||
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
||||
|
||||
# Peers: monitoring-uber, admins (ajouté après)
|
||||
EOF
|
||||
|
||||
# Remplacer placeholder par clé réelle
|
||||
PRIVATE_KEY=$(cat privatekey)
|
||||
sed -i "s|PRIVATE_KEY_PLACEHOLDER|$PRIVATE_KEY|" wg0.conf
|
||||
|
||||
# ─── Enable WireGuard interface ────────────────────────────────
|
||||
echo "[*] Bringing up WireGuard interface..."
|
||||
ip link add dev wg0 type wireguard
|
||||
ip addr add 10.0.0.1/24 dev wg0
|
||||
ip link set wg0 up
|
||||
wg set wg0 private-key <(cat privatekey)
|
||||
wg set wg0 listen-port 51820
|
||||
|
||||
# ─── Enable at boot ───────────────────────────────────────────
|
||||
echo "[*] Enabling WireGuard at boot..."
|
||||
systemctl enable wg-quick@wg0 2>/dev/null || true
|
||||
systemctl start wg-quick@wg0 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "[✓] WireGuard Server configured"
|
||||
echo ""
|
||||
echo "Configuration Summary:"
|
||||
echo " • Interface: wg0"
|
||||
echo " • Server IP: 10.0.0.1/24"
|
||||
echo " • Listen: 0.0.0.0:51820/udp"
|
||||
echo " • Config: /etc/wireguard/wg0.conf"
|
||||
echo ""
|
||||
echo "Server Public Key (for clients):"
|
||||
cat publickey
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Create client configs with wg-quick or manually"
|
||||
echo " 2. Add peers to wg0:"
|
||||
echo " wg set wg0 peer <CLIENT_PUBKEY> allowed-ips 10.0.0.X/32"
|
||||
Reference in New Issue
Block a user