From b43a617b39e3f9efcf1457a181586b62a3334600 Mon Sep 17 00:00:00 2001 From: Seth Grover Date: Mon, 17 Oct 2022 11:56:23 -0600 Subject: [PATCH] v6.4.0 development Squashed commit of the following: commit 9f03bda8b13dc838dce7c26205cb44512e28c20e Author: Seth Grover Date: Mon Oct 17 10:56:57 2022 -0600 Fixes for creating Views in Arkime v4.0 commit 3ce99188eb05efac12d0079f79153b65101b4753 Author: Seth Grover Date: Thu Oct 13 13:12:28 2022 -0600 fixes to ISO build commit b40df91b64a4abb3de4432a43379ae3978946c79 Merge: 6edeff3f 2e5f653f Author: Seth Grover Date: Thu Oct 13 10:16:41 2022 -0600 now that arkime v4.0.0 is out, bump internal arkime version commit 6edeff3ff44eaac16a5a59945c5a4d3018f36867 Author: Seth Grover Date: Tue Oct 11 16:10:15 2022 -0600 fix idaholab/Malcolm#120, capa hits parsing (replaced att&ck with attack in parsing) commit 594cb2faba08e9dc49f164e9ca710aef2e2b3185 Author: Seth Grover Date: Tue Oct 11 14:30:35 2022 -0600 set bacnet instance_number to long instead of integer commit 106bf61fecf450a076de977c1f4db9a127d126d9 Author: SG Date: Mon Oct 10 13:38:20 2022 -0600 add s7comm upload/download log support commit 515c1702b76e40689e068db107f7e528253eb528 Author: SG Date: Mon Oct 10 13:32:24 2022 -0600 add s7comm upload/download log support commit 143bfcb2c8ec33a20ca1a378647e0819643b3422 Author: SG Date: Mon Oct 10 10:27:13 2022 -0600 i don't think we need to actually run auth_setup prior to packaging commit 4da36a9ef1c0e56864fd518fbb8dd36bf9ff5c78 Author: SG Date: Mon Oct 10 08:14:39 2022 -0600 exclusions for documentation commit dfcccc9214d3adb8381329d02690afc3db95dc73 Author: SG Date: Mon Oct 10 08:03:39 2022 -0600 minor documentation tweaks commit da6fad5f1344c37e442ddceb159646376c57245c Author: Seth Grover Date: Thu Oct 6 15:23:39 2022 -0600 fix missing link of license.txt commit dccf3f4a5962b85b9fed1a7588e1528700e6eb27 Author: Seth Grover Date: Thu Oct 6 14:51:50 2022 -0600 workflow changes for nginx for docs commit 02fd8a254f2d2e6510e57d909c169a34015bcb61 Author: Seth Grover Date: Thu Oct 6 14:49:41 2022 -0600 tweak tgz download commit a093739b3abd8471a3fbe9378aed82849ba28c47 Author: Seth Grover Date: Thu Oct 6 14:39:16 2022 -0600 fix nginx docs commit f55a592a8487a790338ec2ddc09c00779b8039e4 Author: Seth Grover Date: Thu Oct 6 14:37:00 2022 -0600 fix nginx docs commit 86582a2100e10ff383547e53d08d9bba106ed4c4 Author: Seth Grover Date: Thu Oct 6 14:30:52 2022 -0600 fix nginx docs commit 5b4ada799265fcccb6270e9134fb4d6a676ff1c1 Author: Seth Grover Date: Thu Oct 6 11:17:17 2022 -0600 add seo tags commit 0dcec0e84160f8b73fb402ca7b34b55e1b9c6161 Author: SG Date: Thu Oct 6 10:31:51 2022 -0600 fix sensor build workflow commit 99daec245852ea9fee999ec963f37d2fd816da93 Author: SG Date: Thu Oct 6 10:28:10 2022 -0600 fix sensor build workflow commit bcaa9a960540c77297b58502045935815537d456 Author: SG Date: Thu Oct 6 10:10:40 2022 -0600 tweak ignore paths for documenation build commit e273d76edc639286d9d29f46bb12955faa1f8b65 Author: SG Date: Thu Oct 6 10:08:04 2022 -0600 Tweaks to building documentation commit 19b7ff92e023f6b5f0b9290a092b5099143ad932 Author: SG Date: Thu Oct 6 07:09:57 2022 -0600 Tweaks to building documentation commit 9a0661ca079ba5e3d3020a4175a85401453d059b Author: SG Date: Thu Oct 6 06:57:29 2022 -0600 don't include the kitchen sink when building documentation commit 964669bdcdfd423de2aaab2562a0c2876a8cb8ad Author: SG Date: Wed Oct 5 15:46:47 2022 -0600 more work on development of documentation to split out into github pages commit d6613365b20c60c7da2e2af231e963e00527caef Author: SG Date: Wed Oct 5 15:27:24 2022 -0600 more work on development of documentation to split out into github pages commit 48ac4f439080bbdee8a5874e52da4c33bee16e8b Author: SG Date: Wed Oct 5 15:23:19 2022 -0600 more work on development of documentation to split out into github pages commit f558cd2f89d1a9ef8b36360ee7dad8e0776cc5c2 Author: SG Date: Wed Oct 5 15:06:12 2022 -0600 more work on development of documentation to split out into github pages commit 7203a0bfa74aada843eb88eba099a12c4b09d0cd Author: SG Date: Wed Oct 5 13:46:48 2022 -0600 more work on development of documentation to split out into github pages commit 0cc0c381a49727208f806ef8a1af6f8dadffd553 Author: SG Date: Wed Oct 5 13:45:22 2022 -0600 more work on development of documentation to split out into github pages commit ac2d3f012bdd9df54d47779ea1b840e9b3ad9ddf Author: SG Date: Wed Oct 5 13:36:06 2022 -0600 more work on development of documentation to split out into github pages commit 6573c6dba70ef0b256a1e6162b307b49e1701d25 Author: SG Date: Wed Oct 5 12:37:56 2022 -0600 more work on development of documentation to split out into github pages commit 554e605a19805bd211731de50c112494b29b8781 Author: SG Date: Wed Oct 5 12:33:38 2022 -0600 more work on development of documentation to split out into github pages commit 8b4b2c918fdd48ffc1fd267ea450e1bc18b50ffd Author: SG Date: Wed Oct 5 12:07:11 2022 -0600 more work on development of documentation to split out into github pages commit ba203f193f37461fa3e893919bcbad136c15c031 Author: Seth Grover Date: Wed Oct 5 07:36:12 2022 -0600 Add package java-1.8.0-openjdk Seems to be a change in the base Docker image and the current version of logstash fails to build with a reference to missing javac. Add java-1.8.0-openjdk according to error message. commit f73b663f81d2246520f0d17d14d995fc4544ac73 Author: Seth Grover Date: Mon Oct 3 12:28:32 2022 -0600 bump fluent-bit version in ps1 commit 7bc9ad219f2156546aedbbc5e05c6a0f8732639e Author: Seth Grover Date: Fri Sep 23 17:45:59 2022 -0600 for web development commit 6cae2e6f5314d682761a17c14e5c9c5ed5b3ee28 Author: Seth Grover Date: Fri Sep 23 17:33:41 2022 -0600 minor tweaks for documentation commit 568da6c1e495751267067b503d2ba8a57221dd9c Author: Seth Grover Date: Fri Sep 23 17:29:29 2022 -0600 Reworked development for using GitHub pages instead of one monolithic README file Squashed commit of the following: commit 76f450814cafdfd4dbbf214639f345598e1a6724 Author: Seth Grover Date: Thu Sep 22 22:56:54 2022 -0600 fix hedgehog images commit 5758e6f7c2b58f8bea8c689a6cd552d4e49a9bab Author: Seth Grover Date: Thu Sep 22 22:54:28 2022 -0600 fix hedgehog images commit c576497703a89e4eeb73eea251855ff12978097d Author: Seth Grover Date: Thu Sep 22 22:45:13 2022 -0600 experimenting with github pages commit 502966933492e82dcd56bb33e92244a4a7d4c79c Author: Seth Grover Date: Thu Sep 22 22:40:07 2022 -0600 experimenting with github pages commit b85fec2ed2df21cd5b79fff6371c934981f843e1 Author: Seth Grover Date: Thu Sep 22 22:33:07 2022 -0600 experimenting with github pages commit 061d2ac7311e6c2929ee7a3f2016f7b59664788b Author: Seth Grover Date: Thu Sep 22 22:29:05 2022 -0600 experimenting with github pages commit 3b5e26af81b850f6787fe6375f6320cb366fe0cc Author: Seth Grover Date: Thu Sep 22 22:18:37 2022 -0600 experimenting with github pages commit 3f20469d192f224c79b7b83894781df69808854c Author: Seth Grover Date: Thu Sep 22 22:07:39 2022 -0600 experimenting with github pages commit ce521e781fafc09ab77427bcf24ca27757ea6452 Author: Seth Grover Date: Thu Sep 22 21:52:45 2022 -0600 experimenting with github pages commit 811a35df08707f36b75a698ce9c8ff440ab337a0 Author: Seth Grover Date: Thu Sep 22 21:37:30 2022 -0600 experimenting with github pages commit e6f4471b33389ddaff500840f69f0d60cc3fa443 Author: Seth Grover Date: Thu Sep 22 21:32:15 2022 -0600 experimenting with github pages commit f70fd9577ca8a817eff4ad1752558b913de8726a Author: Seth Grover Date: Thu Sep 22 21:23:37 2022 -0600 experimenting with github pages commit 48752eb17a0c8670d37abc491236a92621204773 Author: Seth Grover Date: Thu Sep 22 16:01:51 2022 -0600 experimenting with github pages commit 62307831b5160dca0150be26aeb8f4efac82b025 Author: Seth Grover Date: Thu Sep 22 16:00:35 2022 -0600 experimenting with github pages commit 6321f685a298c77670d667228bcb990494602a27 Author: Seth Grover Date: Thu Sep 22 15:55:58 2022 -0600 experimenting with github pages commit 74a8e8e6ccbf1909f1f57ce42d544b8b5945a4bd Author: Seth Grover Date: Thu Sep 22 15:51:52 2022 -0600 experimenting with github pages commit 216aed26c1395d2d2c5e26f7d9422ffe8af52af2 Author: Seth Grover Date: Thu Sep 22 15:50:52 2022 -0600 experimenting with github pages commit 7fa1e76639e39546dbd0cb78ac4c7d37c9a967a8 Author: Seth Grover Date: Thu Sep 22 15:49:01 2022 -0600 experimenting with github pages commit 1c72362952005eb21a99d0ed1dbc4ae2027231ce Author: Seth Grover Date: Thu Sep 22 15:47:31 2022 -0600 experimenting with github pages commit 6ccf841e0543bb3dbfff20cf42c9230de7186674 Author: Seth Grover Date: Thu Sep 22 15:45:06 2022 -0600 experimenting with github pages commit adc63604ca7d6388f820022615a1174af8316a4e Author: Seth Grover Date: Thu Sep 22 15:42:42 2022 -0600 experimenting with github pages commit 25964a83b0216ff577a79681f6e463b21c1d0929 Author: Seth Grover Date: Thu Sep 22 15:35:02 2022 -0600 experimenting with github pages commit c43e2acb7518fdb5057dbbcfc44f08d0dc7f82b5 Author: Seth Grover Date: Thu Sep 22 15:21:01 2022 -0600 experimenting with github pages commit 9871deb1efe8ed738fc64e0fab847f2d7716edec Author: Seth Grover Date: Thu Sep 22 15:19:24 2022 -0600 experimenting with github pages commit 760a1f9b1bebd9ff72e032415a8022af24954d1a Author: Seth Grover Date: Thu Sep 22 15:16:40 2022 -0600 experimenting with github pages commit 6ae5032b55f14ab1301f0d0a3b5d83ddb2b756dc Author: Seth Grover Date: Thu Sep 22 15:11:46 2022 -0600 experimenting with github pages commit 0ea9c94f314d3568be4bffeb86f8e5d7a3afc403 Author: Seth Grover Date: Thu Sep 22 15:08:57 2022 -0600 experimenting with github pages commit b95b060633ac25083d499aa2a06e9f0948be5a1d Author: Seth Grover Date: Thu Sep 22 15:05:54 2022 -0600 experimenting with github pages commit 3195c4ebcf17a91eb72e31077e315ae98b072526 Author: Seth Grover Date: Thu Sep 22 15:04:48 2022 -0600 experimenting with github pages commit a07bc5ee863ea874f1e98fde933e98c398753821 Author: Seth Grover Date: Thu Sep 22 15:02:25 2022 -0600 experimenting with github pages commit d77099ffdf1cc4ba927fae18c9b1fbb9a0452f4e Author: Seth Grover Date: Thu Sep 22 14:59:06 2022 -0600 experimenting with github pages commit 18f4647fcd9cdaf4893e323fe90ae35313fffa27 Author: Seth Grover Date: Thu Sep 22 14:57:31 2022 -0600 experimenting with github pages commit 7a0847656da19594662ff75b4cc489de19fb9f44 Author: Seth Grover Date: Thu Sep 22 14:52:19 2022 -0600 experimenting with github pages commit acf2a6d6e6de9944abacbf47381b4d499fae1d27 Author: Seth Grover Date: Thu Sep 22 14:47:56 2022 -0600 experimenting with github pages commit 26029bcddd783f8633a89d380e59a06885f9fc74 Author: Seth Grover Date: Thu Sep 22 14:46:35 2022 -0600 experimenting with github pages commit 60cdab0e7ab69f1ce9b8eab4d84911cb1cf76875 Author: Seth Grover Date: Thu Sep 22 14:42:13 2022 -0600 experimenting with github pages commit 39e88b6287d22fffd143b75e0a5229fcb0827892 Author: Seth Grover Date: Thu Sep 22 14:41:13 2022 -0600 experimenting with github pages commit 651acd35545dd0a88d9c65cb312f5bba2cc75d75 Author: Seth Grover Date: Thu Sep 22 14:39:38 2022 -0600 experimenting with github pages commit df96e0e6762e6413c5250648d158814c953cc18b Author: Seth Grover Date: Thu Sep 22 14:36:26 2022 -0600 experimenting with github pages commit 5016081066706e6e8fcdf9ba4eb11a7683a98bad Author: Seth Grover Date: Thu Sep 22 14:22:46 2022 -0600 experimenting with github pages commit f1bff3643531994e3d198a44a4b17ae2b480781d Author: Seth Grover Date: Thu Sep 22 14:21:24 2022 -0600 experimenting with github pages commit 0e0d9f0f6cede7d84012eea6723f9ab1e7d52653 Author: Seth Grover Date: Thu Sep 22 14:18:34 2022 -0600 experimenting with github pages commit e17042255492de675179b97ea8f75ef472feee4a Author: Seth Grover Date: Thu Sep 22 14:03:54 2022 -0600 experimenting with github pages commit 63de7bbfb18b50636bb408a443940b662d9ddbca Author: Seth Grover Date: Thu Sep 22 14:00:54 2022 -0600 Revert "experimenting with github pages" This reverts commit f43a4aac38d3cbed4a3b6df88c1e79890399260c. commit f43a4aac38d3cbed4a3b6df88c1e79890399260c Author: Seth Grover Date: Thu Sep 22 13:59:10 2022 -0600 experimenting with github pages commit b9925dc58cea88f827c962e61de65d39ecff748c Author: Seth Grover Date: Thu Sep 22 13:38:46 2022 -0600 experimenting with github pages commit 41528fb576a6b6fddbd3e0f86321d06736c5090c Author: Seth Grover Date: Thu Sep 22 13:34:51 2022 -0600 experimenting with github pages commit efd3c88e5b7d77bcec29495bf257d2e973799136 Author: Seth Grover Date: Thu Sep 22 13:32:01 2022 -0600 experimenting with github pages commit e0f4466d676f602fc0342b3609bb831e027c3f46 Author: Seth Grover Date: Thu Sep 22 13:30:37 2022 -0600 experimenting with github pages commit 8b8d469cd032e7bdac8c59c742298101a088a399 Author: Seth Grover Date: Thu Sep 22 13:28:06 2022 -0600 experimenting with github pages commit 9c00ea29b2d7cc52601e5fb942ce1513f7a464ec Author: Seth Grover Date: Thu Sep 22 13:21:43 2022 -0600 experimenting with github pages commit 1a0df245fe5e9f824bc077944044131ff2692fe0 Author: Seth Grover Date: Thu Sep 22 13:16:25 2022 -0600 experimenting with github pages commit b7ae1d265dc0db26254015a2004c3e13bb2f48f2 Author: Seth Grover Date: Thu Sep 22 12:54:49 2022 -0600 basic config commit 208ef010c889ab26df9349beac3e0e2f45fbebfd Author: Seth Grover Date: Thu Sep 22 12:53:30 2022 -0600 experimenting with jekyll commit 8aea3e2df6453e2010705ef9967d2fc5092fc89b Author: Seth Grover Date: Thu Sep 22 12:43:41 2022 -0600 links work in progress commit 1605844a5a43aba083a4c4c40bd3fe055fddaef2 Author: Seth Grover Date: Thu Sep 22 12:37:25 2022 -0600 Added github pages config commit 599eb8363304fd130175f0d91d75b5e60de34922 Author: Seth Grover Date: Thu Sep 22 12:25:14 2022 -0600 Added github pages config commit 73754a45f63c871c310cce7ea2356f0ba7db5941 Author: Seth Grover Date: Thu Sep 22 12:18:58 2022 -0600 documentation links work in progress commit 03012afea06771d9b19742153fbce555cd0e5104 Author: Seth Grover Date: Thu Sep 22 12:18:19 2022 -0600 documentation links work in progress commit 3b8cd74a2bc8e6135485b057f79419fc4e0a22ed Author: Seth Grover Date: Thu Sep 22 12:00:57 2022 -0600 documentation links work in progress commit 7b13fa7222afb0bf5668e442727982e4a2b35caf Author: Seth Grover Date: Thu Sep 22 11:59:20 2022 -0600 documentation links work in progress commit 52df01b537f655d55e978febe476bca67fc51962 Author: Seth Grover Date: Thu Sep 22 11:54:45 2022 -0600 documentation links work in progress commit b7ac174c08ee90771efef2e62cf09982d2f2a991 Author: Seth Grover Date: Thu Sep 22 11:02:37 2022 -0600 testing relative links commit 952936d12b514672fa3eface72710558a26ab141 Author: Seth Grover Date: Thu Sep 22 10:47:47 2022 -0600 split all the .md documentation into different files commit 6a7003a35a5b39ec12b3c250056fa2006c7c0db9 Author: Seth Grover Date: Wed Sep 21 13:54:31 2022 -0600 simplify adjustment of netbox unit file commit 1896e24081f6e9cd8f03a9463e90d7095c28ba6b Author: Seth Grover Date: Wed Sep 21 13:29:05 2022 -0600 Using /assets/ for netbox conflicts with Arkime's /assets/, so use /netbox/ for the NetBox path commit 5ca383da06f8da3aaa766165503b66c5242b6719 Author: Seth Grover Date: Wed Sep 21 13:26:09 2022 -0600 Using /assets/ for netbox conflicts with Arkime's /assets/, so use /netbox/ for the NetBox path commit 2cf383babd4a002c7692b05a404fd5bb96ea7512 Author: Seth Grover Date: Wed Sep 21 12:03:26 2022 -0600 Fix depends commit 06a1369f0e327b3c1d6a3f8560283f66b94ed354 Author: Seth Grover Date: Tue Sep 20 19:15:28 2022 -0600 tweaks for ISO commit 32caf8867d7e55b9fa2cdac848a4a42d2597f584 Author: Seth Grover Date: Tue Sep 20 15:10:50 2022 -0600 fix packaging commit 28969eaf366fb773587fe3da38e48f3badfccc68 Author: Seth Grover Date: Tue Sep 20 14:36:40 2022 -0600 readme update commit bc6c9ea48726f79994f6e9a779ef9f6603a2fd6e Author: Seth Grover Date: Tue Sep 20 14:32:38 2022 -0600 update poster commit ff402b313ad92c93256d3a3634a9395ad015bfff Author: Seth Grover Date: Tue Sep 20 14:10:41 2022 -0600 slide update commit 8cd74d7248c35c4f0a4df35236e163da585869d7 Author: Seth Grover Date: Tue Sep 20 13:30:52 2022 -0600 build fixes commit 87d8b0d68363d4659c467f6d4d04c44718042be4 Merge: 90f40dc5 85c764e4 Author: Seth Grover Date: Tue Sep 20 13:14:36 2022 -0600 Merge branch 'netbox' of https://github.com/mmguero-dev/Malcolm into development commit 85c764e473adaedc5ce45ab3db5ca30a94df235d Author: Seth Grover Date: Tue Sep 20 13:13:56 2022 -0600 readme commit 12d99f7978a6da2fbf8ab2b7cbe5df277c788f21 Author: Seth Grover Date: Tue Sep 20 13:12:21 2022 -0600 readme commit 94948fb2110710aa6f00f43bb34cd576bfb24d8f Author: Seth Grover Date: Tue Sep 20 12:26:30 2022 -0600 readme commit 357fde61cea37c2ff4fafd7e17b44b14439f4986 Author: Seth Grover Date: Tue Sep 20 12:16:21 2022 -0600 netbox wip commit f6ecce55838d9710b22e2ff79279fc1c5a31e332 Author: Seth Grover Date: Tue Sep 20 11:58:15 2022 -0600 netbox wip commit bcd0ee9ee951ce91a7fc29b802a00d9a816b6c4b Author: Seth Grover Date: Tue Sep 20 11:20:52 2022 -0600 netbox wip commit 41a1706ad6aeb0dd8a11f2399e86b1caa9a3b2ac Author: Seth Grover Date: Tue Sep 20 11:08:21 2022 -0600 initialize netbox on startup commit 7c0d37a41749b8d5ddf1892096894fb2dfa12875 Author: Seth Grover Date: Tue Sep 20 10:21:21 2022 -0600 cleaner disabling of netbox by default commit 58c21aaf47dc7c160d2f6ab89ada7c5eb3b40174 Author: Seth Grover Date: Tue Sep 20 09:27:51 2022 -0600 use tini for init on docker containers; allow netbox to be toggled commit d04883550c034118b819fc04e49ad9166f6377e9 Author: Seth Grover Date: Tue Sep 20 06:56:09 2022 -0600 Bump zeek to v5.0.2 commit 90f40dc52fce8447b970a79b94e3ca6d1d292e14 Author: Seth Grover Date: Tue Sep 20 06:56:09 2022 -0600 Bump zeek to v5.0.2 commit 9e6fbee3f2d9636b586b5d8c5b8d29b76b503e3a Author: Seth Grover Date: Mon Sep 19 21:52:31 2022 -0600 netbox wip commit b2a043f0e8b5801bcb89550b5086a4b8aebf74f2 Author: Seth Grover Date: Mon Sep 19 21:44:29 2022 -0600 netbox wip commit 481e01f1b5826382dc638bbdc03c0506d4e53caa Author: Seth Grover Date: Mon Sep 19 21:34:19 2022 -0600 netbox WIP commit d348641fb40a30f6a164b142bff4f2c21a50ac43 Author: Seth Grover Date: Mon Sep 19 20:35:56 2022 -0600 build docker images specifically for malcolm commit cc907c21df335b612e3e5d6928a39f45407aa925 Author: SG Date: Mon Sep 19 15:40:30 2022 -0600 Fix envs for netbox commit 382d8eafdbc09113d3c8cdc28ee9d709cc10b589 Author: SG Date: Mon Sep 19 15:17:21 2022 -0600 initial swag at netbox integration commit 639d25135735d618ed93c863aa7c78d84fc07f59 Author: SG Date: Mon Sep 19 14:27:26 2022 -0600 bump to version 6.4.0 for idaholab/Malcolm#17 commit 29ed8ddf4f7ce596815a236aa9b910e0b7628510 Author: Seth Grover Date: Thu Sep 15 07:10:15 2022 -0600 fix broken visualization commit ccd1e255b573b65e92ab688f03ce95ff63b686a0 Author: Seth Grover Date: Wed Sep 14 15:08:16 2022 -0600 bump opensearch and dashboards to v2.3.0 commit 18f9cbfd26721d5cffc5374a2e1fb9cdd447deae Author: SG Date: Tue Sep 13 08:55:47 2022 -0600 update audit rules commit 60b3ff06efde539347a7a4cc5aeced9b198a0bf0 Author: SG Date: Tue Sep 13 08:44:15 2022 -0600 set boot grub permission at the end of preseed commit 7c1a0116f9c15d510bc00e82ddc7a943bfb8ad85 Author: Seth Grover Date: Mon Sep 12 16:17:30 2022 -0600 documentation tweaks for hardening (see idaholab/Malcolm#111) commit 332e7d143cea3678d152f0c4f67293fc3a5d40f3 Author: Seth Grover Date: Mon Sep 12 16:16:57 2022 -0600 documentation tweaks for hardening (see idaholab/Malcolm#111) commit 2a26bbac2e9e54aa1267995f61a54915f91fb38c Author: Seth Grover Date: Mon Sep 12 16:16:32 2022 -0600 documentation tweaks for hardening (see idaholab/Malcolm#111) commit a48b12ecca5c44f00bbe4e5afc6fe6849089d41a Author: Seth Grover Date: Mon Sep 12 16:13:19 2022 -0600 documentation tweaks for hardening (see idaholab/Malcolm#111) commit f3cb433bfa8c4fb7fc26a74c38bc7c6237c57b7b Author: Seth Grover Date: Mon Sep 12 15:35:14 2022 -0600 work in progress for updates to documentation for hardening, see idaholab/Malcolm#111 commit 6ac88e0835d593154988ce0110bd80c88a782e9f Author: Seth Grover Date: Mon Sep 12 14:35:20 2022 -0600 documentation and script changes for switching to just using hte harbian-audit project commit 5eee8926a9ad7264f09efe4f5277d4e033e56e91 Author: Seth Grover Date: Thu Sep 8 13:10:36 2022 -0600 bump version to v6.3.1 for development commit 59e24b1a933972fa5c3d686631bedaf40b3d0e3b Author: Seth Grover Date: Thu Sep 8 08:21:25 2022 -0600 Added template slide commit 2e5f653f7cfc7cfa7fdd892a74583a8acbeab07d Author: Seth Grover Date: Mon Aug 15 09:41:34 2022 -0600 set userAuthIps to work with docker commit 8185a9190b3aa40a2b697bf973cc363f201624b5 Author: Seth Grover Date: Mon Aug 15 09:12:18 2022 -0600 fix build commit 7481a86c8f9e566fc268bd325e14e641b1f402d9 Author: Seth Grover Date: Mon Aug 15 08:54:33 2022 -0600 trying build of main branch --- .dockerignore | 7 + ...alcolm-iso-build-docker-wrap-push-ghcr.yml | 1 - .../workflows/netbox-build-and-push-ghcr.yml | 62 + .../workflows/nginx-build-and-push-ghcr.yml | 6 + .../postgresql-build-and-push-ghcr.yml | 61 + .../workflows/redis-build-and-push-ghcr.yml | 61 + ...sensor-iso-build-docker-wrap-push-ghcr.yml | 7 +- .gitignore | 9 + Dockerfiles/api.Dockerfile | 4 +- Dockerfiles/arkime.Dockerfile | 24 +- Dockerfiles/dashboards-helper.Dockerfile | 4 +- Dockerfiles/dashboards.Dockerfile | 13 +- Dockerfiles/file-monitor.Dockerfile | 3 +- Dockerfiles/file-upload.Dockerfile | 5 +- Dockerfiles/filebeat.Dockerfile | 9 +- Dockerfiles/freq.Dockerfile | 5 +- Dockerfiles/htadmin.Dockerfile | 5 +- Dockerfiles/logstash.Dockerfile | 6 +- Dockerfiles/name-map-ui.Dockerfile | 4 +- Dockerfiles/netbox.Dockerfile | 90 + Dockerfiles/nginx.Dockerfile | 29 +- Dockerfiles/opensearch.Dockerfile | 12 +- Dockerfiles/pcap-capture.Dockerfile | 9 +- Dockerfiles/pcap-monitor.Dockerfile | 3 +- Dockerfiles/postgresql.Dockerfile | 58 + Dockerfiles/redis.Dockerfile | 47 + Dockerfiles/suricata.Dockerfile | 3 +- Dockerfiles/zeek.Dockerfile | 3 +- Gemfile | 5 + README.md | 4109 +---------------- _config.yml | 98 + _includes/head-custom.html | 1 + _layouts/default.html | 98 + arkime/etc/config.ini | 18 +- arkime/etc/user_settings.json | 25 +- arkime/etc/views/arkime_sessions.json | 6 + arkime/etc/views/public_ip_addresses.json | 6 + arkime/etc/views/suricata_alerts.json | 6 + arkime/etc/views/suricata_logs.json | 6 + arkime/etc/views/zeek_conn.json | 6 + arkime/etc/views/zeek_exclude_conn.json | 6 + arkime/etc/views/zeek_logs.json | 6 + arkime/patch/capture_event_dataset.patch | 34 - arkime/patch/db_pl_quiet_backup_warning.patch | 4 +- arkime/patch/field_best_priority.patch | 13 - arkime/patch/fields_db_max_5000.patch | 12 +- arkime/patch/footer_links.patch | 6 +- .../hide_pcap_download_without_file.patch | 55 - .../packetpos_arkime_issues_1952_1953.patch | 15 - arkime/patch/remove_upload.patch | 6 +- ...sort_zeek.patch => spi_sort_malcolm.patch} | 8 +- .../viewer_db_opensearchv2_keyword_hack.patch | 4 +- arkime/patch/viewer_wider_field_detail.patch | 6 +- arkime/scripts/arkime-needs-upgrade.sh | 3 + arkime/scripts/initarkime.sh | 16 +- arkime/supervisord.conf | 9 - arkime/wise/source.zeeklogs.js | 9 + .../5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json | 2 +- .../e76d05c0-eb9f-11e9-a384-0fcf32210194.json | 130 +- .../composable/component/zeek_ot.json | 33 +- docker-compose-standalone.yml | 169 +- docker-compose.yml | 181 +- docs/README.md | 102 + docs/alerting.md | 37 + docs/anomaly-detection.md | 12 + docs/api-aggregations.md | 24 + docs/api-document-lookup.md | 88 + docs/api-event-logging.md | 67 + docs/api-examples.md | 1479 ++++++ docs/api-fields.md | 26 + docs/api-indices.md | 28 + docs/api-ping.md | 11 + docs/api-version.md | 49 + docs/api.md | 12 + docs/arkime.md | 188 + docs/authsetup.md | 105 + docs/components.md | 59 + docs/contributing-dashboards.md | 41 + docs/contributing-file-scanners.md | 14 + docs/contributing-guide.md | 26 + docs/contributing-local-modifications.md | 126 + docs/contributing-logstash.md | 51 + docs/contributing-new-image.md | 18 + docs/contributing-new-log-fields.md | 11 + docs/contributing-pcap.md | 12 + docs/contributing-style.md | 5 + docs/contributing-zeek.md | 15 + docs/contributing/README.md | 339 -- docs/cyberchef.md | 5 + docs/dashboards.md | 99 + docs/development.md | 162 + docs/download.md | 39 + docs/file-scanning.md | 28 + docs/hardening.md | 62 + docs/hedgehog-boot.md | 17 + docs/hedgehog-config-root.md | 47 + docs/hedgehog-config-user.md | 189 + docs/hedgehog-config-zeek-intel.md | 7 + docs/hedgehog-config.md | 17 + docs/hedgehog-hardening.md | 59 + docs/hedgehog-installation.md | 41 + docs/hedgehog-iso-build.md | 36 + docs/hedgehog-ssh.md | 19 + docs/hedgehog-troubleshooting.md | 9 + docs/hedgehog-upgrade.md | 330 ++ docs/hedgehog.md | 41 + docs/host-and-subnet-mapping.md | 94 + docs/host-config-linux.md | 91 + docs/host-config-macos.md | 36 + docs/host-config-windows.md | 16 + docs/host-config.md | 5 + docs/ics-best-guess.md | 11 + .../images/arkime-capture-ip-port.png | Bin .../hedgehog}/images/arkime_confirm.png | Bin .../images/hedgehog}/images/autostarts.png | Bin .../hedgehog}/images/autostarts_confirm.png | Bin .../images/hedgehog}/images/boot_options.png | Bin .../hedgehog}/images/capture_config_main.png | Bin .../hedgehog}/images/capture_filter.png | Bin .../hedgehog}/images/capture_iface_select.png | Bin .../images/hedgehog}/images/capture_paths.png | Bin .../images/hedgehog}/images/desktop.png | Bin .../hedgehog}/images/file_quarantine.png | Bin .../hedgehog}/images/filebeat_certs.png | Bin .../hedgehog}/images/filebeat_confirm.png | Bin .../hedgehog}/images/filebeat_ip_port.png | Bin .../hedgehog}/images/filebeat_log_path.png | Bin .../images/hedgehog}/images/filebeat_ssl.png | Bin .../hedgehog}/images/filebeat_ssl_verify.png | Bin .../hedgehog}/images/forwarder_config.png | Bin .../images/hedgehog-color-w-text.png | 0 .../hedgehog}/images/hostname_setting.png | Bin .../images/hedgehog}/images/htpdate_freq.png | Bin .../images/hedgehog}/images/htpdate_host.png | Bin .../images/hedgehog}/images/htpdate_setup.png | Bin .../images/hedgehog}/images/htpdate_test.png | Bin .../images/hedgehog}/images/iface_mode.png | Bin .../images/hedgehog}/images/iface_static.png | Bin .../hedgehog}/images/installer_progress.png | Bin .../images/kiosk_mode_sensor_menu.png | Bin .../images/kiosk_mode_services_menu.png | Bin .../hedgehog}/images/kiosk_mode_status.png | Bin .../images/kiosk_mode_wipe_prompt.png | Bin .../images/malcolm_arkime_reachback_acl.png | Bin .../images/hedgehog}/images/ntp_host.png | Bin .../images/opensearch_connection_protocol.png | Bin .../images/opensearch_connection_success.png | Bin .../hedgehog}/images/opensearch_password.png | Bin .../images/opensearch_ssl_verification.png | Bin .../hedgehog}/images/opensearch_username.png | Bin .../hedgehog}/images/root_config_mode.png | Bin .../images/hedgehog}/images/select_iface.png | Bin .../hedgehog}/images/time_sync_mode.png | Bin .../hedgehog}/images/time_sync_success.png | Bin .../hedgehog}/images/users_and_passwords.png | Bin .../hedgehog}/images/zeek_file_carve_mode.png | Bin .../images/zeek_file_carve_scanners.png | Bin .../images/hedgehog}/logo/Attribution.txt | 0 .../images/hedgehog}/logo/favicon.ico | Bin .../logo/font/ubuntu/CONTRIBUTING.txt | 0 .../hedgehog}/logo/font/ubuntu/COPYRIGHT.txt | 0 .../logo/font/ubuntu/DESCRIPTION.en_us.html | 0 .../hedgehog}/logo/font/ubuntu/FONTLOG.txt | 0 .../logo/font/ubuntu/LICENCE-FAQ.txt | 0 .../hedgehog}/logo/font/ubuntu/LICENCE.txt | 0 .../hedgehog}/logo/font/ubuntu/METADATA.pb | 0 .../hedgehog}/logo/font/ubuntu/README.txt | 0 .../hedgehog}/logo/font/ubuntu/TRADEMARKS.txt | 0 .../images/hedgehog}/logo/font/ubuntu/UFL.txt | 0 .../logo/font/ubuntu/Ubuntu-Bold.ttf | Bin .../logo/font/ubuntu/Ubuntu-BoldItalic.ttf | Bin .../logo/font/ubuntu/Ubuntu-Italic.ttf | Bin .../logo/font/ubuntu/Ubuntu-Light.ttf | Bin .../logo/font/ubuntu/Ubuntu-LightItalic.ttf | Bin .../logo/font/ubuntu/Ubuntu-Medium.ttf | Bin .../logo/font/ubuntu/Ubuntu-MediumItalic.ttf | Bin .../logo/font/ubuntu/Ubuntu-Regular.ttf | Bin .../font/ubuntucondensed/CONTRIBUTING.txt | 0 .../logo/font/ubuntucondensed/COPYRIGHT.txt | 0 .../ubuntucondensed/DESCRIPTION.en_us.html | 0 .../logo/font/ubuntucondensed/FONTLOG.txt | 0 .../logo/font/ubuntucondensed/LICENCE-FAQ.txt | 0 .../logo/font/ubuntucondensed/LICENCE.txt | 0 .../logo/font/ubuntucondensed/METADATA.pb | 0 .../logo/font/ubuntucondensed/README.txt | 0 .../logo/font/ubuntucondensed/TRADEMARKS.txt | 0 .../logo/font/ubuntucondensed/UFL.txt | 0 .../UbuntuCondensed-Regular.ttf | Bin .../logo/font/ubuntumono/CONTRIBUTING.txt | 0 .../logo/font/ubuntumono/COPYRIGHT.txt | 0 .../font/ubuntumono/DESCRIPTION.en_us.html | 0 .../logo/font/ubuntumono/FONTLOG.txt | 0 .../logo/font/ubuntumono/LICENCE-FAQ.txt | 0 .../logo/font/ubuntumono/LICENCE.txt | 0 .../logo/font/ubuntumono/METADATA.pb | 0 .../hedgehog}/logo/font/ubuntumono/README.txt | 0 .../logo/font/ubuntumono/TRADEMARKS.txt | 0 .../hedgehog}/logo/font/ubuntumono/UFL.txt | 0 .../logo/font/ubuntumono/UbuntuMono-Bold.ttf | Bin .../font/ubuntumono/UbuntuMono-BoldItalic.ttf | Bin .../font/ubuntumono/UbuntuMono-Italic.ttf | Bin .../font/ubuntumono/UbuntuMono-Regular.ttf | Bin .../hedgehog}/logo/hedgehog-bw-large.png | Bin .../hedgehog}/logo/hedgehog-bw-small.png | Bin .../logo/hedgehog-bw-w-text-large.png | Bin .../logo/hedgehog-bw-w-text-small.png | Bin .../hedgehog}/logo/hedgehog-bw-w-text.ai | 0 .../images/hedgehog}/logo/hedgehog-bw.ai | 0 .../hedgehog}/logo/hedgehog-color-large.png | Bin .../hedgehog}/logo/hedgehog-color-small.png | Bin .../logo/hedgehog-color-w-text-large.png | Bin .../logo/hedgehog-color-w-text-small.png | Bin .../hedgehog}/logo/hedgehog-color-w-text.ai | 0 .../hedgehog}/logo/hedgehog-color-w-text.png | Bin .../images/hedgehog}/logo/hedgehog-color.ai | 0 .../images/hedgehog}/logo/hedgehog-color.eps | Bin .../images/hedgehog}/logo/hedgehog-color.png | Bin .../logo/hedgehog-wallpaper-plain.png | Bin .../hedgehog}/logo/hedgehog-wallpaper.png | Bin .../hedgehog}/logo/hedgehog-wallpaper.xcf | Bin docs/images/malcolm_components.png | Bin 0 -> 353486 bytes docs/images/malcolm_poster.odg | Bin 439077 -> 507267 bytes docs/images/malcolm_poster.pdf | Bin 399012 -> 522913 bytes docs/index-management.md | 7 + docs/live-analysis.md | 62 + docs/malcolm-config.md | 77 + docs/malcolm-iso.md | 87 + docs/malcolm-preparation.md | 15 + docs/malcolm-upgrade.md | 73 + docs/netbox.md | 7 + docs/opensearch-instances.md | 81 + docs/protocols.md | 64 + docs/queries-cheat-sheet.md | 74 + ...ork Traffic Analysis Quick Start Guide.odt | Bin ...ork Traffic Analysis Quick Start Guide.pdf | Bin docs/quickstart.md | 97 + docs/running.md | 51 + docs/severity.md | 47 + .../Network Traffic Analysis with Malcolm.odp | Bin 7381772 -> 7459016 bytes .../Network Traffic Analysis with Malcolm.pdf | Bin 5438784 -> 5489841 bytes docs/slides/template.odp | Bin 0 -> 177830 bytes docs/system-requirements.md | 7 + .../README.md => docs/third-party-logs.md | 25 +- docs/time-sync.md | 9 + docs/ubuntu-install-example.md | 324 ++ docs/upload.md | 30 + docs/web/.gitignore | 6 - docs/zeek-intel.md | 62 + logstash/maps/zeek_log_ecs_categories.yaml | 1 + logstash/pipelines/enrichment/11_lookups.conf | 3 +- logstash/pipelines/zeek/11_zeek_logs.conf | 41 + .../pipelines/zeek/12_zeek_normalize.conf | 20 +- malcolm-iso/build.sh | 10 +- malcolm-iso/build_via_vagrant.sh | 2 + .../hooks/normal/0910-agg-build.hook.chroot | 2 - .../normal/0911-get-stig-scripts.hook.chroot | 10 +- .../includes.binary/install/preseed_base.cfg | 3 +- .../etc/audit/rules.d/audit.rules | 15 + .../panel/launcher-17/16637019741.desktop | 12 + .../panel/launcher-20/16343116973.desktop | 2 +- .../panel/launcher-23/16343117599.desktop | 2 +- .../xfce-perchannel-xml/xfce4-panel.xml | 6 + .../applications/malcolm-cyberchef.desktop | 2 +- .../share/applications/malcolm-netbox.desktop | 11 + .../share/applications/malcolm-readme.desktop | 2 +- malcolm-iso/vagrant/Vagrantfile | 1 - netbox/.gitignore | 3 + netbox/config/configuration/configuration.py | 256 + netbox/config/configuration/extra.py | 55 + netbox/config/configuration/logging.py | 55 + netbox/config/configuration/plugins.py | 13 + netbox/config/reports/devices.py.example | 46 + netbox/config/scripts/__init__.py | 0 netbox/env/.gitignore | 4 + netbox/env/netbox.env.example | 56 + netbox/scripts/netbox_init.py | 233 + netbox/supervisord.conf | 88 + nginx/nginx.conf | 28 +- nginx/nginx_readonly.conf | 14 + scripts/build.sh | 7 +- scripts/control.py | 102 +- scripts/documentation_build.sh | 120 + scripts/malcolm_appliance_packager.sh | 16 +- scripts/malcolm_common.py | 4 + scripts/third-party-logs/fluent-bit-setup.ps1 | 2 +- sensor-iso/README.md | 907 ---- sensor-iso/arkime/Dockerfile | 2 +- sensor-iso/arkime/build-arkime-deb.sh | 4 +- sensor-iso/build.sh | 31 +- sensor-iso/build_via_vagrant.sh | 27 +- .../normal/0910-sensor-build.hook.chroot | 2 - .../normal/0911-get-stig-scripts.hook.chroot | 10 +- .../includes.binary/install/preseed_base.cfg | 3 +- .../etc/audit/rules.d/audit.rules | 15 + .../panel/launcher-18/16346759724.desktop | 2 +- .../applications/hedgehog-readme.desktop | 2 +- sensor-iso/doc.css | 324 -- .../interface/sensor_ctl/arkime/config.ini | 2 +- .../interface/sensor_ctl/control_vars.conf | 5 +- .../supervisor.d/documentation.conf | 9 + sensor-iso/vagrant/Vagrantfile | 1 - shared/bin/service_check_passthrough.sh | 15 +- shared/bin/zeek_carve_utils.py | 2 +- 303 files changed, 7804 insertions(+), 6077 deletions(-) create mode 100644 .github/workflows/netbox-build-and-push-ghcr.yml create mode 100644 .github/workflows/postgresql-build-and-push-ghcr.yml create mode 100644 .github/workflows/redis-build-and-push-ghcr.yml create mode 100644 Dockerfiles/netbox.Dockerfile create mode 100644 Dockerfiles/postgresql.Dockerfile create mode 100644 Dockerfiles/redis.Dockerfile create mode 100644 Gemfile create mode 100644 _config.yml create mode 100644 _includes/head-custom.html create mode 100644 _layouts/default.html create mode 100644 arkime/etc/views/arkime_sessions.json create mode 100644 arkime/etc/views/public_ip_addresses.json create mode 100644 arkime/etc/views/suricata_alerts.json create mode 100644 arkime/etc/views/suricata_logs.json create mode 100644 arkime/etc/views/zeek_conn.json create mode 100644 arkime/etc/views/zeek_exclude_conn.json create mode 100644 arkime/etc/views/zeek_logs.json delete mode 100644 arkime/patch/capture_event_dataset.patch delete mode 100644 arkime/patch/field_best_priority.patch delete mode 100644 arkime/patch/hide_pcap_download_without_file.patch delete mode 100644 arkime/patch/packetpos_arkime_issues_1952_1953.patch rename arkime/patch/{spi_sort_zeek.patch => spi_sort_malcolm.patch} (88%) create mode 100644 docs/README.md create mode 100644 docs/alerting.md create mode 100644 docs/anomaly-detection.md create mode 100644 docs/api-aggregations.md create mode 100644 docs/api-document-lookup.md create mode 100644 docs/api-event-logging.md create mode 100644 docs/api-examples.md create mode 100644 docs/api-fields.md create mode 100644 docs/api-indices.md create mode 100644 docs/api-ping.md create mode 100644 docs/api-version.md create mode 100644 docs/api.md create mode 100644 docs/arkime.md create mode 100644 docs/authsetup.md create mode 100644 docs/components.md create mode 100644 docs/contributing-dashboards.md create mode 100644 docs/contributing-file-scanners.md create mode 100644 docs/contributing-guide.md create mode 100644 docs/contributing-local-modifications.md create mode 100644 docs/contributing-logstash.md create mode 100644 docs/contributing-new-image.md create mode 100644 docs/contributing-new-log-fields.md create mode 100644 docs/contributing-pcap.md create mode 100644 docs/contributing-style.md create mode 100644 docs/contributing-zeek.md delete mode 100644 docs/contributing/README.md create mode 100644 docs/cyberchef.md create mode 100644 docs/dashboards.md create mode 100644 docs/development.md create mode 100644 docs/download.md create mode 100644 docs/file-scanning.md create mode 100644 docs/hardening.md create mode 100644 docs/hedgehog-boot.md create mode 100644 docs/hedgehog-config-root.md create mode 100644 docs/hedgehog-config-user.md create mode 100644 docs/hedgehog-config-zeek-intel.md create mode 100644 docs/hedgehog-config.md create mode 100644 docs/hedgehog-hardening.md create mode 100644 docs/hedgehog-installation.md create mode 100644 docs/hedgehog-iso-build.md create mode 100644 docs/hedgehog-ssh.md create mode 100644 docs/hedgehog-troubleshooting.md create mode 100644 docs/hedgehog-upgrade.md create mode 100644 docs/hedgehog.md create mode 100644 docs/host-and-subnet-mapping.md create mode 100644 docs/host-config-linux.md create mode 100644 docs/host-config-macos.md create mode 100644 docs/host-config-windows.md create mode 100644 docs/host-config.md create mode 100644 docs/ics-best-guess.md rename {sensor-iso/docs => docs/images/hedgehog}/images/arkime-capture-ip-port.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/arkime_confirm.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/autostarts.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/autostarts_confirm.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/boot_options.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/capture_config_main.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/capture_filter.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/capture_iface_select.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/capture_paths.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/desktop.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/file_quarantine.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/filebeat_certs.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/filebeat_confirm.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/filebeat_ip_port.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/filebeat_log_path.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/filebeat_ssl.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/filebeat_ssl_verify.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/forwarder_config.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/hedgehog-color-w-text.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/hostname_setting.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/htpdate_freq.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/htpdate_host.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/htpdate_setup.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/htpdate_test.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/iface_mode.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/iface_static.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/installer_progress.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/kiosk_mode_sensor_menu.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/kiosk_mode_services_menu.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/kiosk_mode_status.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/kiosk_mode_wipe_prompt.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/malcolm_arkime_reachback_acl.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/ntp_host.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/opensearch_connection_protocol.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/opensearch_connection_success.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/opensearch_password.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/opensearch_ssl_verification.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/opensearch_username.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/root_config_mode.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/select_iface.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/time_sync_mode.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/time_sync_success.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/users_and_passwords.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/zeek_file_carve_mode.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/images/zeek_file_carve_scanners.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/Attribution.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/favicon.ico (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/CONTRIBUTING.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/COPYRIGHT.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/DESCRIPTION.en_us.html (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/FONTLOG.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/LICENCE-FAQ.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/LICENCE.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/METADATA.pb (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/README.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/TRADEMARKS.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/UFL.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/Ubuntu-Bold.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/Ubuntu-BoldItalic.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/Ubuntu-Italic.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/Ubuntu-Light.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/Ubuntu-LightItalic.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/Ubuntu-Medium.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/Ubuntu-MediumItalic.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntu/Ubuntu-Regular.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/CONTRIBUTING.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/COPYRIGHT.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/DESCRIPTION.en_us.html (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/FONTLOG.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/LICENCE-FAQ.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/LICENCE.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/METADATA.pb (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/README.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/TRADEMARKS.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/UFL.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntucondensed/UbuntuCondensed-Regular.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/CONTRIBUTING.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/COPYRIGHT.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/DESCRIPTION.en_us.html (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/FONTLOG.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/LICENCE-FAQ.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/LICENCE.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/METADATA.pb (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/README.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/TRADEMARKS.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/UFL.txt (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/UbuntuMono-Bold.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/UbuntuMono-BoldItalic.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/UbuntuMono-Italic.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/font/ubuntumono/UbuntuMono-Regular.ttf (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-bw-large.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-bw-small.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-bw-w-text-large.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-bw-w-text-small.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-bw-w-text.ai (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-bw.ai (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color-large.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color-small.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color-w-text-large.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color-w-text-small.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color-w-text.ai (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color-w-text.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color.ai (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color.eps (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-color.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-wallpaper-plain.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-wallpaper.png (100%) rename {sensor-iso/docs => docs/images/hedgehog}/logo/hedgehog-wallpaper.xcf (100%) create mode 100644 docs/images/malcolm_components.png create mode 100644 docs/index-management.md create mode 100644 docs/live-analysis.md create mode 100644 docs/malcolm-config.md create mode 100644 docs/malcolm-iso.md create mode 100644 docs/malcolm-preparation.md create mode 100644 docs/malcolm-upgrade.md create mode 100644 docs/netbox.md create mode 100644 docs/opensearch-instances.md create mode 100644 docs/protocols.md create mode 100644 docs/queries-cheat-sheet.md rename docs/{ => quick-start}/Malcolm Network Traffic Analysis Quick Start Guide.odt (100%) rename docs/{ => quick-start}/Malcolm Network Traffic Analysis Quick Start Guide.pdf (100%) create mode 100644 docs/quickstart.md create mode 100644 docs/running.md create mode 100644 docs/severity.md create mode 100644 docs/slides/template.odp create mode 100644 docs/system-requirements.md rename scripts/third-party-logs/README.md => docs/third-party-logs.md (85%) create mode 100644 docs/time-sync.md create mode 100644 docs/ubuntu-install-example.md create mode 100644 docs/upload.md delete mode 100644 docs/web/.gitignore create mode 100644 docs/zeek-intel.md create mode 100644 malcolm-iso/config/includes.chroot/etc/skel/.config/xfce4/panel/launcher-17/16637019741.desktop create mode 100644 malcolm-iso/config/includes.chroot/usr/share/applications/malcolm-netbox.desktop create mode 100644 netbox/.gitignore create mode 100644 netbox/config/configuration/configuration.py create mode 100644 netbox/config/configuration/extra.py create mode 100644 netbox/config/configuration/logging.py create mode 100644 netbox/config/configuration/plugins.py create mode 100644 netbox/config/reports/devices.py.example create mode 100644 netbox/config/scripts/__init__.py create mode 100644 netbox/env/.gitignore create mode 100644 netbox/env/netbox.env.example create mode 100755 netbox/scripts/netbox_init.py create mode 100644 netbox/supervisord.conf create mode 100755 scripts/documentation_build.sh delete mode 100644 sensor-iso/README.md delete mode 100644 sensor-iso/doc.css create mode 100644 sensor-iso/interface/sensor_ctl/supervisor.d/documentation.conf diff --git a/.dockerignore b/.dockerignore index 7d719ba41..622ebf891 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,13 +17,20 @@ .tmp docker-compose*yml Dockerfiles +Gemfile.lock opensearch opensearch-backup arkime-logs arkime-raw +malcolm-iso +sensor-iso nginx/nginx_ldap.conf pcap +_site scripts !scripts/malcolm_common.py zeek-logs suricata-logs +netbox/netbox/media +netbox/netbox/postgres +netbox/netbox/redis \ No newline at end of file diff --git a/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml b/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml index b6dc86fbb..07196d567 100644 --- a/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml +++ b/.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml @@ -59,7 +59,6 @@ jobs: gnupg2 \ imagemagick \ jq \ - pandoc \ po4a \ rsync \ software-properties-common \ diff --git a/.github/workflows/netbox-build-and-push-ghcr.yml b/.github/workflows/netbox-build-and-push-ghcr.yml new file mode 100644 index 000000000..bfcfc8651 --- /dev/null +++ b/.github/workflows/netbox-build-and-push-ghcr.yml @@ -0,0 +1,62 @@ +name: netbox-build-and-push-ghcr + +on: + push: + branches: + - main + - development + paths: + - 'netbox/**' + - 'Dockerfiles/netbox.Dockerfile' + - 'shared/bin/*' + - '.trigger_workflow_build' + workflow_dispatch: + repository_dispatch: + +jobs: + docker: + runs-on: ubuntu-latest + permissions: + actions: write + packages: write + contents: read + steps: + - + name: Cancel previous run in progress + uses: styfle/cancel-workflow-action@0.9.1 + with: + ignore_sha: true + all_but_latest: true + access_token: ${{ secrets.GITHUB_TOKEN }} + - + name: Checkout + uses: actions/checkout@v2 + - + name: Extract branch name + shell: bash + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + id: extract_branch + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + driver-opts: | + image=moby/buildkit:master + - + name: Log in to registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfiles/netbox.Dockerfile + push: true + tags: ghcr.io/${{ github.repository_owner }}/malcolmnetsec/netbox:${{ steps.extract_branch.outputs.branch }} diff --git a/.github/workflows/nginx-build-and-push-ghcr.yml b/.github/workflows/nginx-build-and-push-ghcr.yml index 61d1e8c40..a0b00496d 100644 --- a/.github/workflows/nginx-build-and-push-ghcr.yml +++ b/.github/workflows/nginx-build-and-push-ghcr.yml @@ -10,6 +10,12 @@ on: - 'Dockerfiles/nginx.Dockerfile' - 'shared/bin/*' - '.trigger_workflow_build' + - '_config.yml' + - '_includes/**' + - '_layouts/**' + - 'docs/**' + - 'Gemfile' + - 'README.md' workflow_dispatch: repository_dispatch: diff --git a/.github/workflows/postgresql-build-and-push-ghcr.yml b/.github/workflows/postgresql-build-and-push-ghcr.yml new file mode 100644 index 000000000..7a70f8274 --- /dev/null +++ b/.github/workflows/postgresql-build-and-push-ghcr.yml @@ -0,0 +1,61 @@ +name: postgresql-build-and-push-ghcr + +on: + push: + branches: + - main + - development + paths: + - 'Dockerfiles/postgresql.Dockerfile' + - 'shared/bin/*' + - '.trigger_workflow_build' + workflow_dispatch: + repository_dispatch: + +jobs: + docker: + runs-on: ubuntu-latest + permissions: + actions: write + packages: write + contents: read + steps: + - + name: Cancel previous run in progress + uses: styfle/cancel-workflow-action@0.9.1 + with: + ignore_sha: true + all_but_latest: true + access_token: ${{ secrets.GITHUB_TOKEN }} + - + name: Checkout + uses: actions/checkout@v2 + - + name: Extract branch name + shell: bash + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + id: extract_branch + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + driver-opts: | + image=moby/buildkit:master + - + name: Log in to registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfiles/postgresql.Dockerfile + push: true + tags: ghcr.io/${{ github.repository_owner }}/malcolmnetsec/postgresql:${{ steps.extract_branch.outputs.branch }} diff --git a/.github/workflows/redis-build-and-push-ghcr.yml b/.github/workflows/redis-build-and-push-ghcr.yml new file mode 100644 index 000000000..f40d61baf --- /dev/null +++ b/.github/workflows/redis-build-and-push-ghcr.yml @@ -0,0 +1,61 @@ +name: redis-build-and-push-ghcr + +on: + push: + branches: + - main + - development + paths: + - 'Dockerfiles/redis.Dockerfile' + - 'shared/bin/*' + - '.trigger_workflow_build' + workflow_dispatch: + repository_dispatch: + +jobs: + docker: + runs-on: ubuntu-latest + permissions: + actions: write + packages: write + contents: read + steps: + - + name: Cancel previous run in progress + uses: styfle/cancel-workflow-action@0.9.1 + with: + ignore_sha: true + all_but_latest: true + access_token: ${{ secrets.GITHUB_TOKEN }} + - + name: Checkout + uses: actions/checkout@v2 + - + name: Extract branch name + shell: bash + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + id: extract_branch + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + driver-opts: | + image=moby/buildkit:master + - + name: Log in to registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfiles/redis.Dockerfile + push: true + tags: ghcr.io/${{ github.repository_owner }}/malcolmnetsec/redis:${{ steps.extract_branch.outputs.branch }} diff --git a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml index 93117d742..482139766 100644 --- a/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml +++ b/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml @@ -59,7 +59,6 @@ jobs: gnupg2 \ imagemagick \ jq \ - pandoc \ po4a \ rsync \ software-properties-common \ @@ -86,16 +85,18 @@ jobs: - name: Build image run: | - cp -r ./shared ./sensor-iso + cp -r ./shared ./docs ./_config.yml ./_includes ./_layouts ./Gemfile ./README.md ./sensor-iso + cp ./scripts/documentation_build.sh ./sensor-iso/docs/ cp -r ./arkime/patch ./sensor-iso/shared/arkime_patch pushd ./sensor-iso echo "${{ steps.extract_malcolm_version.outputs.mversion }}" > ./shared/version.txt echo "${{ secrets.MAXMIND_GEOIP_DB_LICENSE_KEY }}" > ./shared/maxmind_license.txt echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" > ./shared/environment.chroot sudo /usr/bin/env bash ./build.sh - rm -rf ./shared/ + rm -rf ./shared/ ./docs/ ./_config.yml ./_includes ./_layouts /Gemfile ./README.md sudo chmod 644 ./hedgehog-*.* popd + - name: ghcr.io login uses: docker/login-action@v1 diff --git a/.gitignore b/.gitignore index 4387c2e8b..e20bada11 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,15 @@ malcolm_*images.tar.gz *.iso *-build.log +Gemfile.lock +_site +sensor-iso/_config.yml +sensor-iso/_includes +sensor-iso/_layouts +sensor-iso/_site +sensor-iso/docs +sensor-iso/Gemfile +sensor-iso/README.md # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/Dockerfiles/api.Dockerfile b/Dockerfiles/api.Dockerfile index 2857eed47..0700f7d48 100644 --- a/Dockerfiles/api.Dockerfile +++ b/Dockerfiles/api.Dockerfile @@ -76,7 +76,7 @@ COPY shared/bin/opensearch_status.sh "${APP_HOME}"/ ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ RUN apt-get -q update \ && apt-get -y -q --no-install-recommends upgrade \ - && apt-get -y -q --no-install-recommends install curl netcat \ + && apt-get -y -q --no-install-recommends install curl netcat tini \ && python3 -m pip install --upgrade pip \ && python3 -m pip install --no-cache /wheels/* \ && chmod 755 /usr/local/bin/docker-uid-gid-setup.sh \ @@ -89,7 +89,7 @@ RUN apt-get -q update \ EXPOSE 5000 -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "${APP_HOME}/entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "${APP_HOME}/entrypoint.sh"] # to be populated at build-time: ARG BUILD_DATE diff --git a/Dockerfiles/arkime.Dockerfile b/Dockerfiles/arkime.Dockerfile index e8efe7f85..09931e2b8 100644 --- a/Dockerfiles/arkime.Dockerfile +++ b/Dockerfiles/arkime.Dockerfile @@ -4,7 +4,7 @@ FROM debian:11-slim AS build ENV DEBIAN_FRONTEND noninteractive -ENV ARKIME_VERSION "3.4.2" +ENV ARKIME_VERSION "v4.0.0" ENV ARKIME_DIR "/opt/arkime" ENV ARKIME_URL "https://github.com/arkime/arkime.git" ENV ARKIME_LOCALELASTICSEARCH no @@ -12,9 +12,6 @@ ENV ARKIME_INET yes ADD arkime/scripts/bs4_remove_div.py /opt/ ADD arkime/patch/* /opt/patches/ -ADD README.md $ARKIME_DIR/doc/ -ADD docs/doc.css $ARKIME_DIR/doc/ -ADD docs/images $ARKIME_DIR/doc/images/ RUN apt-get -q update && \ apt-get -y -q --no-install-recommends upgrade && \ @@ -31,7 +28,6 @@ RUN apt-get -q update && \ git-core \ groff \ groff-base \ - imagemagick \ libcap-dev \ libjson-perl \ libkrb5-dev \ @@ -44,32 +40,20 @@ RUN apt-get -q update && \ make \ meson \ ninja-build \ - pandoc \ patch \ python3-dev \ python3-pip \ python3-setuptools \ python3-wheel \ - rename \ sudo \ swig \ wget \ zlib1g-dev && \ pip3 install --no-cache-dir beautifulsoup4 && \ - cd $ARKIME_DIR/doc/images && \ - find . -name "*.png" -exec bash -c 'convert "{}" -fuzz 2% -transparent white -background white -alpha remove -strip -interlace Plane -quality 85% "{}.jpg" && rename "s/\.png//" "{}.jpg"' \; && \ - cd $ARKIME_DIR/doc && \ - sed -i "s/^# Malcolm$//" README.md && \ - sed -i '/./,$!d' README.md && \ - sed -i "s/.png/.jpg/g" README.md && \ - sed -i "s@docs/images@images@g" README.md && \ - sed -i 's/\!\[.*\](.*\/badge.svg)//g' README.md && \ - pandoc -s --self-contained --metadata title="Malcolm README" --css $ARKIME_DIR/doc/doc.css -o $ARKIME_DIR/doc/README.html $ARKIME_DIR/doc/README.md && \ cd /opt && \ - git clone --depth=1 --single-branch --recurse-submodules --shallow-submodules --no-tags --branch="v$ARKIME_VERSION" "$ARKIME_URL" "./arkime-"$ARKIME_VERSION && \ + git clone --recurse-submodules --branch="$ARKIME_VERSION" "$ARKIME_URL" "./arkime-"$ARKIME_VERSION && \ cd "./arkime-"$ARKIME_VERSION && \ bash -c 'for i in /opt/patches/*; do patch -p 1 -r - --no-backup-if-mismatch < $i || true; done' && \ - find $ARKIME_DIR/doc/images/screenshots -name "*.png" -delete && \ export PATH="$ARKIME_DIR/bin:${PATH}" && \ ln -sfr $ARKIME_DIR/bin/npm /usr/local/bin/npm && \ ln -sfr $ARKIME_DIR/bin/node /usr/local/bin/node && \ @@ -156,6 +140,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l file \ geoip-bin \ gettext \ + jq \ libcap2-bin \ libjson-perl \ libkrb5-3 \ @@ -178,6 +163,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l supervisor \ vim-tiny \ wget \ + tini \ tar gzip unzip cpio bzip2 lzma xz-utils p7zip-full unrar zlib1g && \ pip3 install --no-cache-dir beautifulsoup4 pyzmq && \ ln -sfr $ARKIME_DIR/bin/npm /usr/local/bin/npm && \ @@ -231,7 +217,7 @@ ENV PATH="/opt:$ARKIME_DIR/bin:${PATH}" EXPOSE 8000 8005 8081 WORKDIR $ARKIME_DIR -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "/opt/docker_entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/opt/docker_entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/dashboards-helper.Dockerfile b/Dockerfiles/dashboards-helper.Dockerfile index 976ba3c13..b80cdc93a 100644 --- a/Dockerfiles/dashboards-helper.Dockerfile +++ b/Dockerfiles/dashboards-helper.Dockerfile @@ -69,7 +69,7 @@ ADD scripts/malcolm_common.py /data/ RUN apk update --no-cache && \ apk upgrade --no-cache && \ - apk --no-cache add bash python3 py3-pip curl openssl procps psmisc npm shadow jq && \ + apk --no-cache add bash python3 py3-pip curl openssl procps psmisc npm shadow jq tini && \ npm install -g http-server && \ pip3 install supervisor humanfriendly requests && \ curl -fsSLO "$SUPERCRONIC_URL" && \ @@ -97,7 +97,7 @@ RUN apk update --no-cache && \ EXPOSE $OFFLINE_REGION_MAPS_PORT -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/dashboards.Dockerfile b/Dockerfiles/dashboards.Dockerfile index 23bf3e115..994c2e543 100644 --- a/Dockerfiles/dashboards.Dockerfile +++ b/Dockerfiles/dashboards.Dockerfile @@ -14,10 +14,10 @@ ENV PGROUP "dashboarder" ENV TERM xterm -ARG OPENSEARCH_VERSION="2.2.1" +ARG OPENSEARCH_VERSION="2.3.0" ENV OPENSEARCH_VERSION $OPENSEARCH_VERSION -ARG OPENSEARCH_DASHBOARDS_VERSION="2.2.1" +ARG OPENSEARCH_DASHBOARDS_VERSION="2.3.0" ENV OPENSEARCH_DASHBOARDS_VERSION $OPENSEARCH_DASHBOARDS_VERSION # base system dependencies for checking out and building plugins @@ -68,7 +68,7 @@ RUN eval "$(nodenv init -)" && \ # runtime ################################################################## -FROM opensearchproject/opensearch-dashboards:2.2.1 +FROM opensearchproject/opensearch-dashboards:2.3.0 LABEL maintainer="malcolm@inl.gov" LABEL org.opencontainers.image.authors='malcolm@inl.gov' @@ -89,6 +89,8 @@ ENV PUSER_PRIV_DROP true ENV TERM xterm +ENV TINI_VERSION v0.19.0 + ARG OPENSEARCH_URL="http://opensearch:9200" ARG OPENSEARCH_LOCAL="true" ARG CREATE_OS_ARKIME_SESSION_INDEX="true" @@ -97,7 +99,6 @@ ARG ARKIME_INDEX_PATTERN_ID="arkime_sessions3-*" ARG ARKIME_INDEX_TIME_FIELD="firstPacket" ARG NODE_OPTIONS="--max_old_space_size=4096" - ENV CREATE_OS_ARKIME_SESSION_INDEX $CREATE_OS_ARKIME_SESSION_INDEX ENV ARKIME_INDEX_PATTERN $ARKIME_INDEX_PATTERN ENV ARKIME_INDEX_PATTERN_ID $ARKIME_INDEX_PATTERN_ID @@ -111,6 +112,7 @@ ENV NODE_OPTIONS $NODE_OPTIONS USER root COPY --from=build /usr/share/opensearch-dashboards/plugins/sankey_vis/build/kbnSankeyVis.zip /tmp/kbnSankeyVis.zip +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini RUN yum upgrade -y && \ yum install -y curl psmisc util-linux openssl python3 zip unzip && \ @@ -120,6 +122,7 @@ RUN yum upgrade -y && \ cd /usr/share/opensearch-dashboards/plugins && \ /usr/share/opensearch-dashboards/bin/opensearch-dashboards-plugin install file:///tmp/kbnSankeyVis.zip --allow-root && \ chown -R ${DEFAULT_UID}:${DEFAULT_GID} /usr/share/opensearch-dashboards/plugins/* && \ + chmod +x /usr/bin/tini && \ yum clean all && \ rm -rf /var/cache/yum @@ -142,7 +145,7 @@ ADD docs/images/favicon/favicon32.png /usr/share/opensearch-dashboards/src/core/ ADD docs/images/favicon/apple-touch-icon-precomposed.png /usr/share/opensearch-dashboards/src/core/server/core_app/assets/favicons/apple-touch-icon.png -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] CMD ["/usr/share/opensearch-dashboards/opensearch-dashboards-docker-entrypoint.sh"] diff --git a/Dockerfiles/file-monitor.Dockerfile b/Dockerfiles/file-monitor.Dockerfile index 625e6e543..2578d6371 100644 --- a/Dockerfiles/file-monitor.Dockerfile +++ b/Dockerfiles/file-monitor.Dockerfile @@ -119,6 +119,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l libtool \ make \ pkg-config \ + tini \ unzip && \ apt-get -y -q install \ inotify-tools \ @@ -224,7 +225,7 @@ VOLUME ["$YARA_RULES_SRC_DIR"] EXPOSE 3310 EXPOSE $EXTRACTED_FILE_HTTP_SERVER_PORT -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "/docker-entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/docker-entrypoint.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/file-upload.Dockerfile b/Dockerfiles/file-upload.Dockerfile index 6d05e99e0..e840b164c 100644 --- a/Dockerfiles/file-upload.Dockerfile +++ b/Dockerfiles/file-upload.Dockerfile @@ -66,7 +66,8 @@ RUN apt-get -q update && \ php$PHP_VERSION \ php$PHP_VERSION-fpm \ php$PHP_VERSION-apcu \ - nginx-light && \ + nginx-light \ + tini && \ apt-get clean -y -q && \ rm -rf /var/lib/apt/lists/* @@ -99,7 +100,7 @@ RUN mkdir -p /var/run/sshd /var/www/upload/server/php/chroot /run/php && \ VOLUME [ "/var/www/upload/server/php/chroot/files" ] EXPOSE 22 80 -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "/docker-entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/docker-entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/filebeat.Dockerfile b/Dockerfiles/filebeat.Dockerfile index 3114bcc3e..06efaed28 100644 --- a/Dockerfiles/filebeat.Dockerfile +++ b/Dockerfiles/filebeat.Dockerfile @@ -62,8 +62,12 @@ ENV SUPERCRONIC "supercronic-linux-amd64" ENV SUPERCRONIC_SHA1SUM "d7f4c0886eb85249ad05ed592902fa6865bb9d70" ENV SUPERCRONIC_CRONTAB "/etc/crontab" +ENV TINI_VERSION v0.19.0 + USER root +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini + RUN yum install -y epel-release && \ yum upgrade -y && \ yum install -y curl inotify-tools file psmisc tar gzip unzip cpio bzip2 lzma xz openssl p7zip p7zip-plugins unar python3-setuptools python3-pip && \ @@ -74,7 +78,8 @@ RUN yum install -y epel-release && \ echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - && \ chmod +x "$SUPERCRONIC" && \ mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" && \ - ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic + ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic && \ + chmod +x /usr/bin/tini ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ ADD filebeat/filebeat.yml /usr/share/filebeat/filebeat.yml @@ -131,7 +136,7 @@ ENV FILEBEAT_ZEEK_DIR "/zeek/" VOLUME ["/usr/share/filebeat/data", "/usr/share/filebeat-nginx/data", "/usr/share/filebeat-tcp/data"] -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/freq.Dockerfile b/Dockerfiles/freq.Dockerfile index f7bd96e7c..c79a7f05e 100644 --- a/Dockerfiles/freq.Dockerfile +++ b/Dockerfiles/freq.Dockerfile @@ -37,7 +37,8 @@ RUN apt-get -q update && \ psmisc \ python3 \ python3-dev \ - python3-pip && \ + python3-pip \ + tini && \ pip3 install supervisor six && \ cd /opt && \ mkdir -p ./freq_server && \ @@ -60,7 +61,7 @@ WORKDIR /opt/freq_server EXPOSE $FREQ_PORT -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/htadmin.Dockerfile b/Dockerfiles/htadmin.Dockerfile index 20dbd3d98..8a62a9cf0 100644 --- a/Dockerfiles/htadmin.Dockerfile +++ b/Dockerfiles/htadmin.Dockerfile @@ -51,7 +51,8 @@ RUN apt-get -q update && \ php$PHP_VERSION-fpm \ php$PHP_VERSION-gd \ procps \ - supervisor && \ + supervisor \ + tini && \ ( yes '' | pecl channel-update pecl.php.net ) && \ ( yes '' | pecl install mcrypt-$MCRYPT_VERSION ) && \ ln -s -r /usr/lib/php/20??????/*.so /usr/lib/php/$PHP_VERSION/ && \ @@ -84,7 +85,7 @@ ADD htadmin/nginx/sites-available/default /etc/nginx/sites-available/default EXPOSE 80 -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/logstash.Dockerfile b/Dockerfiles/logstash.Dockerfile index 9438a267a..b541d0aa4 100644 --- a/Dockerfiles/logstash.Dockerfile +++ b/Dockerfiles/logstash.Dockerfile @@ -65,6 +65,8 @@ ENV PUSER_PRIV_DROP true ENV TERM xterm +ENV TINI_VERSION v0.19.0 + ARG LOGSTASH_ENRICHMENT_PIPELINE=enrichment ARG LOGSTASH_PARSE_PIPELINE_ADDRESSES=zeek-parse,suricata-parse,beats-parse ARG LOGSTASH_OPENSEARCH_PIPELINE_ADDRESS_INTERNAL=internal-os @@ -81,6 +83,7 @@ ENV LS_JAVA_HOME=/usr/share/logstash/jdk USER root COPY --from=build /opt/logstash-filter-fingerprint /opt/logstash-filter-fingerprint +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini RUN yum install -y epel-release && \ yum upgrade -y && \ @@ -94,6 +97,7 @@ RUN yum install -y epel-release && \ logstash-filter-useragent \ logstash-input-beats logstash-output-elasticsearch && \ logstash-plugin install /opt/logstash-filter-fingerprint/logstash-filter-fingerprint-*.gem && \ + chmod +x /usr/bin/tini && \ rm -rf /opt/logstash-filter-fingerprint /root/.cache /root/.gem /root/.bundle ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ @@ -138,7 +142,7 @@ EXPOSE 5044 EXPOSE 9001 EXPOSE 9600 -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/name-map-ui.Dockerfile b/Dockerfiles/name-map-ui.Dockerfile index 6201caf8d..c08017579 100644 --- a/Dockerfiles/name-map-ui.Dockerfile +++ b/Dockerfiles/name-map-ui.Dockerfile @@ -28,7 +28,7 @@ RUN apk update --no-cache && \ apk upgrade --no-cache && \ apk --no-cache add bash php8 php8-fpm php8-mysqli php8-json php8-openssl php8-curl php8-fileinfo \ php8-zlib php8-xml php8-phar php8-intl php8-dom php8-xmlreader php8-ctype php8-session \ - php8-mbstring php8-gd nginx supervisor curl inotify-tools file psmisc shadow openssl + php8-mbstring php8-gd nginx supervisor curl inotify-tools file psmisc shadow openssl tini COPY name-map-ui/config/nginx.conf /etc/nginx/nginx.conf COPY name-map-ui/config/fpm-pool.conf /etc/php8/php-fpm.d/www.conf @@ -67,7 +67,7 @@ COPY docs/images/favicon/favicon.ico /var/www/html/ EXPOSE 8080 -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/netbox.Dockerfile b/Dockerfiles/netbox.Dockerfile new file mode 100644 index 000000000..bb4d48d9d --- /dev/null +++ b/Dockerfiles/netbox.Dockerfile @@ -0,0 +1,90 @@ +FROM netboxcommunity/netbox:latest + +# Copyright (c) 2022 Battelle Energy Alliance, LLC. All rights reserved. +LABEL maintainer="malcolm@inl.gov" +LABEL org.opencontainers.image.authors='malcolm@inl.gov' +LABEL org.opencontainers.image.url='https://github.com/idaholab/Malcolm' +LABEL org.opencontainers.image.documentation='https://github.com/idaholab/Malcolm/blob/main/README.md' +LABEL org.opencontainers.image.source='https://github.com/idaholab/Malcolm' +LABEL org.opencontainers.image.vendor='Idaho National Laboratory' +LABEL org.opencontainers.image.title='malcolmnetsec/netbox' +LABEL org.opencontainers.image.description='Malcolm container providing the NetBox asset management system' + +ENV DEBIAN_FRONTEND noninteractive +ENV TERM xterm +ENV LANG C.UTF-8 + +ARG DEFAULT_UID=1000 +ARG DEFAULT_GID=1000 +ENV DEFAULT_UID $DEFAULT_UID +ENV DEFAULT_GID $DEFAULT_GID +ENV PUSER "boxer" +ENV PGROUP "boxer" +ENV PUSER_PRIV_DROP true + +ENV SUPERCRONIC_VERSION "0.2.1" +ENV SUPERCRONIC_URL "https://github.com/aptible/supercronic/releases/download/v$SUPERCRONIC_VERSION/supercronic-linux-amd64" +ENV SUPERCRONIC "supercronic-linux-amd64" +ENV SUPERCRONIC_SHA1SUM "d7f4c0886eb85249ad05ed592902fa6865bb9d70" +ENV SUPERCRONIC_CRONTAB "/etc/crontab" + +ARG NETBOX_DEFAULT_SITE=Malcolm +ARG NETBOX_CRON=false + +ENV BASE_PATH netbox +ENV NETBOX_DEFAULT_SITE $NETBOX_DEFAULT_SITE +ENV NETBOX_CRON $NETBOX_CRON + +RUN apt-get -q update && \ + apt-get -y -q --no-install-recommends upgrade && \ + apt-get install -q -y --no-install-recommends \ + jq \ + procps \ + psmisc \ + python3-psycopg2 \ + python3-pynetbox \ + python3-slugify \ + supervisor \ + tini && \ + curl -fsSLO "$SUPERCRONIC_URL" && \ + echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - && \ + chmod +x "$SUPERCRONIC" && \ + mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" && \ + ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic && \ + touch "${SUPERCRONIC_CRONTAB}" && \ + apt-get -q -y autoremove && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ + groupadd --gid ${DEFAULT_GID} ${PUSER} && \ + useradd -m --uid ${DEFAULT_UID} --gid ${DEFAULT_GID} ${PUSER} && \ + usermod -a -G tty ${PUSER} && \ + mkdir -p /opt/unit && \ + chown -R $PUSER:$PGROUP /etc/netbox /opt/unit /opt/netbox && \ + mkdir -p /opt/netbox/netbox/$BASE_PATH && \ + mv /opt/netbox/netbox/static /opt/netbox/netbox/$BASE_PATH/static && \ + jq '. += { "settings": { "http": { "discard_unsafe_fields": false } } }' /etc/unit/nginx-unit.json | jq ".routes[0].match.uri = \"/${BASE_PATH}/static/*\"" > /etc/unit/nginx-unit-new.json && \ + mv /etc/unit/nginx-unit-new.json /etc/unit/nginx-unit.json && \ + chmod 644 /etc/unit/nginx-unit.json + +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --chmod=755 netbox/scripts/* /usr/local/bin/ +COPY --chmod=644 netbox/supervisord.conf /etc/supervisord.conf +COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic + +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/service_check_passthrough.sh"] + +CMD ["/opt/netbox/docker-entrypoint.sh", "/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] + +# to be populated at build-time: +ARG BUILD_DATE +ARG MALCOLM_VERSION +ARG VCS_REVISION + +ENV BUILD_DATE $BUILD_DATE +ENV MALCOLM_VERSION $MALCOLM_VERSION +ENV VCS_REVISION $VCS_REVISION + +LABEL org.opencontainers.image.created=$BUILD_DATE +LABEL org.opencontainers.image.version=$MALCOLM_VERSION +LABEL org.opencontainers.image.revision=$VCS_REVISION diff --git a/Dockerfiles/nginx.Dockerfile b/Dockerfiles/nginx.Dockerfile index f2d964b55..aa7c484af 100644 --- a/Dockerfiles/nginx.Dockerfile +++ b/Dockerfiles/nginx.Dockerfile @@ -7,6 +7,30 @@ # jwilder/nginx-proxy - https://github.com/jwilder/nginx-proxy/blob/master/Dockerfile.alpine #################################################################################### + +# first build documentation with jekyll +FROM ghcr.io/mmguero-dev/jekyll:latest as docbuild + +ARG GITHUB_TOKEN +ARG VCS_REVISION +ENV VCS_REVISION $VCS_REVISION + +ADD README.md _config.yml Gemfile /site/ +ADD _includes/ /site/_includes/ +ADD _layouts/ /site/_layouts/ +ADD docs/ /site/docs/ + +WORKDIR /site + +# build documentation, remove unnecessary files, then massage a bit to work nicely with NGINX (which will be serving it) +RUN find /site -type f -name "*.md" -exec sed -i "s/{{[[:space:]]*site.github.build_revision[[:space:]]*}}/$VCS_REVISION/g" "{}" \; && \ + ( [ -n "${GITHUB_TOKEN}" ] && export JEKYLL_GITHUB_TOKEN="${GITHUB_TOKEN}" || true ) && \ + docker-entrypoint.sh bundle exec jekyll build && \ + find /site/_site -type f -name "*.md" -delete && \ + find /site/_site -type f -name "*.html" -exec sed -i "s@/\(docs\|assets\)@/readme/\1@g" "{}" \; && \ + find /site/_site -type f -name "*.html" -exec sed -i 's@\(href=\)"/"@\1"/readme/"@g' "{}" \; + +# build NGINX image FROM alpine:3.16 LABEL maintainer="malcolm@inl.gov" @@ -185,7 +209,7 @@ RUN set -x ; \ | xargs -r apk info --installed \ | sort -u \ )" ; \ - apk add --no-cache --virtual .nginx-rundeps $runDeps ca-certificates bash wget openssl apache2-utils openldap stunnel supervisor tzdata; \ + apk add --no-cache --virtual .nginx-rundeps $runDeps ca-certificates bash wget openssl apache2-utils openldap stunnel supervisor tini tzdata; \ update-ca-certificates; \ apk del .nginx-build-deps ; \ apk del .gettext ; \ @@ -196,6 +220,7 @@ RUN set -x ; \ COPY --from=jwilder/nginx-proxy:alpine /app/nginx.tmpl /etc/nginx/ COPY --from=jwilder/nginx-proxy:alpine /etc/nginx/network_internal.conf /etc/nginx/ COPY --from=jwilder/nginx-proxy:alpine /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/ +COPY --from=docbuild /site/_site /usr/share/nginx/html/readme ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ ADD nginx/scripts /usr/local/bin/ @@ -206,7 +231,7 @@ ADD docs/images/icon/favicon.ico /usr/share/nginx/html/favicon.ico VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"] -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] +ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] CMD ["supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/opensearch.Dockerfile b/Dockerfiles/opensearch.Dockerfile index b46298d6a..ecb23a882 100644 --- a/Dockerfiles/opensearch.Dockerfile +++ b/Dockerfiles/opensearch.Dockerfile @@ -1,4 +1,4 @@ -FROM opensearchproject/opensearch:2.2.1 +FROM opensearchproject/opensearch:2.3.0 # Copyright (c) 2022 Battelle Energy Alliance, LLC. All rights reserved. LABEL maintainer="malcolm@inl.gov" @@ -21,6 +21,8 @@ ENV PUSER_PRIV_DROP true ENV TERM xterm +ENV TINI_VERSION v0.19.0 + ARG OPENSEARCH_LOCAL=true ENV OPENSEARCH_LOCAL $OPENSEARCH_LOCAL @@ -33,6 +35,8 @@ ENV OPENSEARCH_JAVA_HOME=/usr/share/opensearch/jdk USER root +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini + # Remove the opensearch-security plugin - Malcolm manages authentication and encryption via NGINX reverse proxy # Remove the performance-analyzer plugin - Reduce resources in docker image # override_main_response_version - https://opensearch.org/docs/latest/clients/agents-and-ingestion-tools/index/#compatibility-matrices @@ -45,17 +49,19 @@ RUN yum install -y openssl util-linux procps && \ sed -i "s/^[0-9][0-9]*\(-:-XX:\(+UseG1GC\|G1ReservePercent\|InitiatingHeapOccupancyPercent\)\)/$($OPENSEARCH_JAVA_HOME/bin/java -version 2>&1 | grep version | awk '{print $3}' | tr -d '\"' | cut -d. -f1)\1/" /usr/share/opensearch/config/jvm.options && \ mkdir -p /var/local/ca-trust && \ chown -R $PUSER:$PGROUP /usr/share/opensearch/config/opensearch.yml /var/local/ca-trust && \ + chmod +x /usr/bin/tini && \ sed -i "s/^\([[:space:]]*\)\([^#].*performance-analyzer-agent-cli\)/\1# \2/" /usr/share/opensearch/opensearch-docker-entrypoint.sh && \ sed -i '/^[[:space:]]*[^#].*runOpensearch.*/i /usr/local/bin/jdk-cacerts-auto-import.sh || true' /usr/share/opensearch/opensearch-docker-entrypoint.sh ADD shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ ADD shared/bin/jdk-cacerts-auto-import.sh /usr/local/bin/ -ADD shared/bin/service_check_passthrough.sh /usr/local/bin/docker-entrypoint.sh +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/docker-entrypoint.sh +COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic VOLUME ["/var/local/ca-trust"] -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker-entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker-entrypoint.sh"] CMD ["/usr/share/opensearch/opensearch-docker-entrypoint.sh"] diff --git a/Dockerfiles/pcap-capture.Dockerfile b/Dockerfiles/pcap-capture.Dockerfile index bbd43170f..399900328 100644 --- a/Dockerfiles/pcap-capture.Dockerfile +++ b/Dockerfiles/pcap-capture.Dockerfile @@ -34,8 +34,8 @@ ARG PCAP_IFACE=lo ARG PCAP_IFACE_TWEAK=false ARG PCAP_NETSNIFF_MAGIC=0xa1b2c3d4 ARG PCAP_TCPDUMP_FILENAME_PATTERN=%Y%m%d%H%M%S.pcap -ARG PCAP_ROTATE_MINUTES=30 -ARG PCAP_ROTATE_MEGABYTES=500 +ARG PCAP_ROTATE_MINUTES=10 +ARG PCAP_ROTATE_MEGABYTES=4096 ARG PCAP_PATH=/pcap ARG PCAP_FILTER= ARG PCAP_SNAPLEN=0 @@ -69,7 +69,8 @@ RUN apt-get -q update && \ procps \ psmisc \ supervisor \ - tcpdump && \ + tcpdump \ + tini && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ groupadd --gid ${DEFAULT_GID} ${PGROUP} && \ @@ -87,7 +88,7 @@ RUN apt-get -q update && \ WORKDIR "$PCAP_PATH" -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] CMD ["/usr/local/bin/supervisor.sh"] diff --git a/Dockerfiles/pcap-monitor.Dockerfile b/Dockerfiles/pcap-monitor.Dockerfile index e4c2306e8..890f6331a 100644 --- a/Dockerfiles/pcap-monitor.Dockerfile +++ b/Dockerfiles/pcap-monitor.Dockerfile @@ -55,6 +55,7 @@ RUN apt-get -q update && \ python3-setuptools \ python3-wheel \ supervisor \ + tini \ vim-tiny && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ @@ -71,7 +72,7 @@ ADD scripts/malcolm_common.py /usr/local/bin/ EXPOSE 30441 -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-u", "root", "-n"] diff --git a/Dockerfiles/postgresql.Dockerfile b/Dockerfiles/postgresql.Dockerfile new file mode 100644 index 000000000..bd4b6739e --- /dev/null +++ b/Dockerfiles/postgresql.Dockerfile @@ -0,0 +1,58 @@ +FROM postgres:14-alpine + +# Copyright (c) 2022 Battelle Energy Alliance, LLC. All rights reserved. +LABEL maintainer="malcolm@inl.gov" +LABEL org.opencontainers.image.authors='malcolm@inl.gov' +LABEL org.opencontainers.image.url='https://github.com/idaholab/Malcolm' +LABEL org.opencontainers.image.documentation='https://github.com/idaholab/Malcolm/blob/main/README.md' +LABEL org.opencontainers.image.source='https://github.com/idaholab/Malcolm' +LABEL org.opencontainers.image.vendor='Idaho National Laboratory' +LABEL org.opencontainers.image.title='malcolmnetsec/postgresql' +LABEL org.opencontainers.image.description='Malcolm container providing the PostgreSQL object-relational database' + +ARG DEFAULT_UID=1000 +ARG DEFAULT_GID=1000 +ENV DEFAULT_UID $DEFAULT_UID +ENV DEFAULT_GID $DEFAULT_GID +ENV PUSER "postgres" +ENV PGROUP "postgres" +ENV PUSER_PRIV_DROP true +ENV PUSER_CHOWN "/run/postgresql;/var/lib/postgresql" + +ENV TERM xterm + +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic + +RUN apk update --no-cache && \ + apk upgrade --no-cache && \ + apk add --no-cache bash procps psmisc shadow tini && \ + apk add --no-cache --virtual .build-deps rsync && \ + rsync -a /usr/local/bin/ /usr/bin/ && \ + rsync -a /usr/local/share/ /usr/share/ && \ + rsync -a /usr/local/lib/ /usr/lib/ && \ + rm -rf /usr/local/bin /usr/local/share /usr/local/lib && \ + ln -s /usr/bin /usr/local/bin && \ + ln -s /usr/share /usr/local/share && \ + ln -s /usr/lib /usr/local/lib && \ + apk del .build-deps + +USER root + +ENTRYPOINT ["/sbin/tini", "--", "/usr/bin/docker-uid-gid-setup.sh", "/usr/local/bin/service_check_passthrough.sh"] + +CMD ["/usr/bin/docker-entrypoint.sh", "postgres"] + +# to be populated at build-time: +ARG BUILD_DATE +ARG MALCOLM_VERSION +ARG VCS_REVISION + +ENV BUILD_DATE $BUILD_DATE +ENV MALCOLM_VERSION $MALCOLM_VERSION +ENV VCS_REVISION $VCS_REVISION + +LABEL org.opencontainers.image.created=$BUILD_DATE +LABEL org.opencontainers.image.version=$MALCOLM_VERSION +LABEL org.opencontainers.image.revision=$VCS_REVISION diff --git a/Dockerfiles/redis.Dockerfile b/Dockerfiles/redis.Dockerfile new file mode 100644 index 000000000..c109f2ed7 --- /dev/null +++ b/Dockerfiles/redis.Dockerfile @@ -0,0 +1,47 @@ +FROM redis:7-alpine + +# Copyright (c) 2022 Battelle Energy Alliance, LLC. All rights reserved. +LABEL maintainer="malcolm@inl.gov" +LABEL org.opencontainers.image.authors='malcolm@inl.gov' +LABEL org.opencontainers.image.url='https://github.com/idaholab/Malcolm' +LABEL org.opencontainers.image.documentation='https://github.com/idaholab/Malcolm/blob/main/README.md' +LABEL org.opencontainers.image.source='https://github.com/idaholab/Malcolm' +LABEL org.opencontainers.image.vendor='Idaho National Laboratory' +LABEL org.opencontainers.image.title='malcolmnetsec/redis' +LABEL org.opencontainers.image.description='Malcolm container providing Redis, an in-memory data structure store' + +ARG DEFAULT_UID=999 +ARG DEFAULT_GID=1000 +ENV DEFAULT_UID $DEFAULT_UID +ENV DEFAULT_GID $DEFAULT_GID +ENV PUSER "redis" +ENV PGROUP "redis" +ENV PUSER_PRIV_DROP true + +ENV TERM xterm + +COPY --chmod=755 shared/bin/docker-uid-gid-setup.sh /usr/local/bin/ +COPY --chmod=755 shared/bin/service_check_passthrough.sh /usr/local/bin/ +COPY --from=pierrezemb/gostatic --chmod=755 /goStatic /usr/bin/goStatic + +RUN apk update --no-cache && \ + apk upgrade --no-cache && \ + apk --no-cache add bash psmisc shadow tini && \ + addgroup ${PUSER} tty + +WORKDIR /home/${PUSER} + +ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/service_check_passthrough.sh"] + +# to be populated at build-time: +ARG BUILD_DATE +ARG MALCOLM_VERSION +ARG VCS_REVISION + +ENV BUILD_DATE $BUILD_DATE +ENV MALCOLM_VERSION $MALCOLM_VERSION +ENV VCS_REVISION $VCS_REVISION + +LABEL org.opencontainers.image.created=$BUILD_DATE +LABEL org.opencontainers.image.version=$MALCOLM_VERSION +LABEL org.opencontainers.image.revision=$VCS_REVISION diff --git a/Dockerfiles/suricata.Dockerfile b/Dockerfiles/suricata.Dockerfile index 62cd981e1..67c88890a 100644 --- a/Dockerfiles/suricata.Dockerfile +++ b/Dockerfiles/suricata.Dockerfile @@ -94,6 +94,7 @@ RUN sed -i "s/bullseye main/bullseye main contrib non-free/g" /etc/apt/sources.l python3-zmq \ supervisor \ vim-tiny \ + tini \ zlib1g && \ curl -fsSLO "$SUPERCRONIC_URL" && \ echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - && \ @@ -178,6 +179,6 @@ VOLUME ["$SURICATA_RUN_DIR"] WORKDIR $SURICATA_RUN_DIR -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Dockerfiles/zeek.Dockerfile b/Dockerfiles/zeek.Dockerfile index b2d915bbb..aedf190c0 100644 --- a/Dockerfiles/zeek.Dockerfile +++ b/Dockerfiles/zeek.Dockerfile @@ -105,6 +105,7 @@ RUN export DEBARCH=$(dpkg --print-architecture) && \ python3-zmq \ supervisor \ swig \ + tini \ vim-tiny \ zlib1g-dev && \ pip3 install --no-cache-dir pymisp stix2 taxii2-client dateparser && \ @@ -273,7 +274,7 @@ ENV PUSER_CHOWN "$ZEEK_DIR" VOLUME ["${ZEEK_DIR}/share/zeek/site/intel"] -ENTRYPOINT ["/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-uid-gid-setup.sh", "/usr/local/bin/docker_entrypoint.sh"] CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"] diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..602eb993d --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +source "https://rubygems.org" + +gem "github-pages", group: :jekyll_plugins +gem "jekyll-include-cache", group: :jekyll_plugins +gem 'jekyll-seo-tag', group: :jekyll_plugins \ No newline at end of file diff --git a/README.md b/README.md index c2a3cb78a..e8b6e3daa 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Malcolm -![](./docs/images/logo/Malcolm_banner.png) +![](./docs/images/logo/Malcolm_outline_banner_dark.png) -[Malcolm](https://github.com/idaholab/Malcolm) is a powerful network traffic analysis tool suite designed with the following goals in mind: +[Malcolm]({{ site.github.repository_url }}) is a powerful network traffic analysis tool suite designed with the following goals in mind: * **Easy to use** – Malcolm accepts network traffic data in the form of full packet capture (PCAP) files and Zeek (formerly Bro) logs. These artifacts can be uploaded via a simple browser-based interface or captured live and forwarded to Malcolm using lightweight forwarders. In either case, the data is automatically normalized, enriched, and correlated for analysis. * **Powerful traffic analysis** – Visibility into network communications is provided through two intuitive interfaces: OpenSearch Dashboards, a flexible data visualization plugin with dozens of prebuilt dashboards providing an at-a-glance overview of network protocols; and Arkime (formerly Moloch), a powerful tool for finding and identifying the network sessions comprising suspected security incidents. @@ -15,4088 +15,37 @@ Although all of the open source tools which make up Malcolm are already availabl In short, Malcolm provides an easily deployable network analysis tool suite for full packet capture artifacts (PCAP files) and Zeek logs. While Internet access is required to build it, it is not required at runtime. -## Share your feedback +## Documentation -You can help steer Malcolm's development by sharing your ideas and feedback. Please take a few minutes to complete [this survey ↪](https://forms.gle/JYt9QwA5C4SYX8My6) (hosted on Google Forms) so we can understand the members of the Malcolm community and their use cases for this tool. +See the [**Malcolm documentation**](docs/README.md). -## Table of Contents +## Share your feedback -* [Automated Build Workflows Status](#BuildBadges) -* [Quick start](#QuickStart) - * [Getting Malcolm](#GetMalcolm) - * [User interface](#UserInterfaceURLs) -* [Overview](#Overview) -* [Components](#Components) -* [Supported Protocols](#Protocols) -* [Development](#Development) - * [Building from source](#Build) -* [Pre-Packaged installation files](#Packager) -* [Preparing your system](#Preparing) - * [Recommended system requirements](#SystemRequirements) - * [System configuration and tuning](#ConfigAndTuning) - * [`docker-compose.yml` parameters](#DockerComposeYml) - * [Linux host system configuration](#HostSystemConfigLinux) - * [macOS host system configuration](#HostSystemConfigMac) - * [Windows host system configuration](#HostSystemConfigWindows) -* [Running Malcolm](#Running) - * [OpenSearch instances](#OpenSearchInstance) - * [Authentication and authorization for remote OpenSearch clusters](#OpenSearchAuth) - * [Configure authentication](#AuthSetup) - * [Local account management](#AuthBasicAccountManagement) - * [Lightweight Directory Access Protocol (LDAP) authentication](#AuthLDAP) - - [LDAP connection security](#AuthLDAPSecurity) - * [TLS certificates](#TLSCerts) - * [Starting Malcolm](#Starting) - * [Stopping and restarting Malcolm](#StopAndRestart) - * [Clearing Malcolm's data](#Wipe) - * [Temporary read-only interface](#ReadOnlyUI) -* [Capture file and log archive upload](#Upload) - - [Tagging](#Tagging) - - [Processing uploaded PCAPs with Zeek and Suricata](#UploadPCAPProcessors) -* [Live analysis](#LiveAnalysis) - * [Using a network sensor appliance](#Hedgehog) - * [Monitoring local network interfaces](#LocalPCAP) - * [Manually forwarding logs from an external source](#ExternalForward) -* [Arkime](#Arkime) - * [Zeek log integration](#ArkimeZeek) - - [Correlating Zeek logs and Arkime sessions](#ZeekArkimeFlowCorrelation) - * [Help](#ArkimeHelp) - * [Sessions](#ArkimeSessions) - * [PCAP Export](#ArkimePCAPExport) - * [SPIView](#ArkimeSPIView) - * [SPIGraph](#ArkimeSPIGraph) - * [Connections](#ArkimeConnections) - * [Hunt](#ArkimeHunt) - * [Statistics](#ArkimeStats) - * [Settings](#ArkimeSettings) -* [OpenSearch Dashboards](#Dashboards) - * [Discover](#Discover) - - [Screenshots](#DiscoverGallery) - * [Visualizations and dashboards](#DashboardsVisualizations) - - [Prebuilt visualizations and dashboards](#PrebuiltVisualizations) - - [Screenshots](#PrebuiltVisualizationsGallery) - - [Building your own visualizations and dashboards](#BuildDashboard) - + [Screenshots](#NewVisualizationsGallery) -* [Search Queries in Arkime and OpenSearch](#SearchCheatSheet) -* [Other Malcolm features](#MalcolmFeatures) - - [Automatic file extraction and scanning](#ZeekFileExtraction) - - [Automatic host and subnet name assignment](#HostAndSubnetNaming) - + [IP/MAC address to hostname mapping via `host-map.txt`](#HostNaming) - + [CIDR subnet to network segment name mapping via `cidr-map.txt`](#SegmentNaming) - + [Defining hostname and CIDR subnet names interface](#NameMapUI) - + [Applying mapping changes](#ApplyMapping) - - [OpenSearch index management](#IndexManagement) - - [Event severity scoring](#Severity) - + [Customizing event severity scoring](#SeverityConfig) - - [Zeek Intelligence Framework](#ZeekIntel) - + [STIX™ and TAXII™](#ZeekIntelSTIX) - + [MISP](#ZeekIntelMISP) - - [Anomaly Detection](#AnomalyDetection) - - [Alerting](#Alerting) - + [Email Sender Accounts](#AlertingEmail) - - ["Best Guess" Fingerprinting for ICS Protocols](#ICSBestGuess) - - [API](#API) - + [Examples](#APIExamples) -* [Ingesting Third-party Logs](#ThirdPartyLogs) -* [Malcolm installer ISO](#ISO) - * [Installation](#ISOInstallation) - * [Generating the ISO](#ISOBuild) - * [Setup](#ISOSetup) - * [Time synchronization](#ConfigTime) - * [Hardening](#Hardening) - * [STIG compliance exceptions](#STIGExceptions) - * [CIS benchmark compliance exceptions](#CISExceptions) -* [Installation example using Ubuntu 22.04 LTS](#InstallationExample) -* [Upgrading Malcolm](#UpgradePlan) -* [Modifying or Contributing to Malcolm](#Contributing) -* [Forks](#Forks) -* [Copyright](#Footer) -* [Contact](#Contact) +You can help steer Malcolm's development by sharing your ideas and feedback. Please take a few minutes to complete [this survey ↪](https://forms.gle/JYt9QwA5C4SYX8My6) (hosted on Google Forms) so we can understand the members of the Malcolm community and their use cases for this tool. ## Automated Builds Status -See [**Building from source**](#Build) to read how you can use GitHub [workflow files](./.github/workflows/) to build Malcolm. - -![api-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/api-build-and-push-ghcr/badge.svg) -![arkime-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/arkime-build-and-push-ghcr/badge.svg) -![dashboards-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/dashboards-build-and-push-ghcr/badge.svg) -![dashboards-helper-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/dashboards-helper-build-and-push-ghcr/badge.svg) -![file-monitor-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/file-monitor-build-and-push-ghcr/badge.svg) -![file-upload-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/file-upload-build-and-push-ghcr/badge.svg) -![filebeat-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/filebeat-build-and-push-ghcr/badge.svg) -![freq-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/freq-build-and-push-ghcr/badge.svg) -![htadmin-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/htadmin-build-and-push-ghcr/badge.svg) -![logstash-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/logstash-build-and-push-ghcr/badge.svg) -![name-map-ui-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/name-map-ui-build-and-push-ghcr/badge.svg) -![nginx-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/nginx-build-and-push-ghcr/badge.svg) -![opensearch-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/opensearch-build-and-push-ghcr/badge.svg) -![pcap-capture-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/pcap-capture-build-and-push-ghcr/badge.svg) -![pcap-monitor-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/pcap-monitor-build-and-push-ghcr/badge.svg) -![suricata-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/suricata-build-and-push-ghcr/badge.svg) -![zeek-build-and-push-ghcr](https://github.com/idaholab/Malcolm/workflows/zeek-build-and-push-ghcr/badge.svg) -![malcolm-iso-build-docker-wrap-push-ghcr](https://github.com/idaholab/Malcolm/workflows/malcolm-iso-build-docker-wrap-push-ghcr/badge.svg) -![sensor-iso-build-docker-wrap-push-ghcr](https://github.com/idaholab/Malcolm/workflows/sensor-iso-build-docker-wrap-push-ghcr/badge.svg) - -## Quick start - -### Getting Malcolm - -For a `TL;DR` example of downloading, configuring, and running Malcolm on a Linux platform, see [Installation example using Ubuntu 22.04 LTS](#InstallationExample). - -The scripts to control Malcolm require Python 3. The [`install.py`](#ConfigAndTuning) script requires the [requests](https://docs.python-requests.org/en/latest/) module for Python 3, and will make use of the [pythondialog](https://pythondialog.sourceforge.io/) module for user interaction (on Linux) if it is available. - -#### Source code - -The files required to build and run Malcolm are available on its [GitHub page](https://github.com/idaholab/Malcolm/tree/main). Malcolm's source code is released under the terms of a permissive open source software license (see see `License.txt` for the terms of its release). - -#### Building Malcolm from scratch - -The `build.sh` script can build Malcolm's Docker images from scratch. See [Building from source](#Build) for more information. - -#### Initial configuration - -You must run [`auth_setup`](#AuthSetup) prior to pulling Malcolm's Docker images. You should also ensure your system configuration and `docker-compose.yml` settings are tuned by running `./scripts/install.py` or `./scripts/install.py --configure` (see [System configuration and tuning](#ConfigAndTuning)). - -#### Pull Malcolm's Docker images - -Malcolm's Docker images are periodically built and hosted on [Docker Hub](https://hub.docker.com/u/malcolmnetsec). If you already have [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/), these prebuilt images can be pulled by navigating into the Malcolm directory (containing the `docker-compose.yml` file) and running `docker-compose pull` like this: -``` -$ docker-compose pull -Pulling api ... done -Pulling arkime ... done -Pulling dashboards ... done -Pulling dashboards-helper ... done -Pulling file-monitor ... done -Pulling filebeat ... done -Pulling freq ... done -Pulling htadmin ... done -Pulling logstash ... done -Pulling name-map-ui ... done -Pulling nginx-proxy ... done -Pulling opensearch ... done -Pulling pcap-capture ... done -Pulling pcap-monitor ... done -Pulling suricata ... done -Pulling upload ... done -Pulling zeek ... done -``` - -You can then observe that the images have been retrieved by running `docker images`: -``` -$ docker images -REPOSITORY TAG IMAGE ID CREATED SIZE -malcolmnetsec/api 6.3.0 xxxxxxxxxxxx 3 days ago 158MB -malcolmnetsec/arkime 6.3.0 xxxxxxxxxxxx 3 days ago 816MB -malcolmnetsec/dashboards 6.3.0 xxxxxxxxxxxx 3 days ago 1.02GB -malcolmnetsec/dashboards-helper 6.3.0 xxxxxxxxxxxx 3 days ago 184MB -malcolmnetsec/filebeat-oss 6.3.0 xxxxxxxxxxxx 3 days ago 624MB -malcolmnetsec/file-monitor 6.3.0 xxxxxxxxxxxx 3 days ago 588MB -malcolmnetsec/file-upload 6.3.0 xxxxxxxxxxxx 3 days ago 259MB -malcolmnetsec/freq 6.3.0 xxxxxxxxxxxx 3 days ago 132MB -malcolmnetsec/htadmin 6.3.0 xxxxxxxxxxxx 3 days ago 242MB -malcolmnetsec/logstash-oss 6.3.0 xxxxxxxxxxxx 3 days ago 1.35GB -malcolmnetsec/name-map-ui 6.3.0 xxxxxxxxxxxx 3 days ago 143MB -malcolmnetsec/nginx-proxy 6.3.0 xxxxxxxxxxxx 3 days ago 121MB -malcolmnetsec/opensearch 6.3.0 xxxxxxxxxxxx 3 days ago 1.17GB -malcolmnetsec/pcap-capture 6.3.0 xxxxxxxxxxxx 3 days ago 121MB -malcolmnetsec/pcap-monitor 6.3.0 xxxxxxxxxxxx 3 days ago 213MB -malcolmnetsec/suricata 6.3.0 xxxxxxxxxxxx 3 days ago 278MB -malcolmnetsec/zeek 6.3.0 xxxxxxxxxxxx 3 days ago 1GB -``` - -#### Import from pre-packaged tarballs - -Once built, the `malcolm_appliance_packager.sh` script can be used to create pre-packaged Malcolm tarballs for import on another machine. See [Pre-Packaged Installation Files](#Packager) for more information. - -### Starting and stopping Malcolm - -Use the scripts in the `scripts/` directory to start and stop Malcolm, view debug logs of a currently running -instance, wipe the database and restore Malcolm to a fresh state, etc. - -### User interface - -A few minutes after starting Malcolm (probably 5 to 10 minutes for Logstash to be completely up, depending on the system), the following services will be accessible: - -* Arkime: [https://localhost:443](https://localhost:443) -* OpenSearch Dashboards: [https://localhost/dashboards/](https://localhost/dashboards/) or [https://localhost:5601](https://localhost:5601) -* Capture File and Log Archive Upload (Web): [https://localhost/upload/](https://localhost/upload/) -* Capture File and Log Archive Upload (SFTP): `sftp://@127.0.0.1:8022/files` -* [Host and Subnet Name Mapping](#HostAndSubnetNaming) Editor: [https://localhost/name-map-ui/](https://localhost/name-map-ui/) -* Account Management: [https://localhost:488](https://localhost:488) - -## Overview - -![Malcolm Network Diagram](./docs/images/malcolm_network_diagram.png) - -Malcolm processes network traffic data in the form of packet capture (PCAP) files or Zeek logs. A [sensor](#Hedgehog) (packet capture appliance) monitors network traffic mirrored to it over a SPAN port on a network switch or router, or using a network TAP device. [Zeek](https://www.zeek.org/index.html) logs and [Arkime](https://molo.ch/) sessions are generated containing important session metadata from the traffic observed, which are then securely forwarded to a Malcolm instance. Full PCAP files are optionally stored locally on the sensor device for examination later. - -Malcolm parses the network session data and enriches it with additional lookups and mappings including GeoIP mapping, hardware manufacturer lookups from [organizationally unique identifiers (OUI)](http://standards-oui.ieee.org/oui/oui.txt) in MAC addresses, assigning names to [network segments](#SegmentNaming) and [hosts](#HostNaming) based on user-defined IP address and MAC mappings, performing [TLS fingerprinting](#https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967), and many others. - -The enriched data is stored in an [OpenSearch](https://opensearch.org/) document store in a format suitable for analysis through two intuitive interfaces: OpenSearch Dashboards, a flexible data visualization plugin with dozens of prebuilt dashboards providing an at-a-glance overview of network protocols; and Arkime, a powerful tool for finding and identifying the network sessions comprising suspected security incidents. These tools can be accessed through a web browser from analyst workstations or for display in a security operations center (SOC). Logs can also optionally be forwarded on to another instance of Malcolm. - -![Malcolm Data Pipeline](./docs/images/malcolm_data_pipeline.png) - -For smaller networks, use at home by network security enthusiasts, or in the field for incident response engagements, Malcolm can also easily be deployed locally on an ordinary consumer workstation or laptop. Malcolm can process local artifacts such as locally-generated Zeek logs, locally-captured PCAP files, and PCAP files collected offline without the use of a dedicated sensor appliance. - -## Components - -Malcolm leverages the following excellent open source tools, among others. - -* [Arkime](https://arkime.com/) (formerly Moloch) - for PCAP file processing, browsing, searching, analysis, and carving/exporting; Arkime itself consists of two parts: - * [capture](https://github.com/arkime/arkime/tree/master/capture) - a tool for traffic capture, as well as offline PCAP parsing and metadata insertion into OpenSearch - * [viewer](https://github.com/arkime/arkime/tree/master/viewer) - a browser-based interface for data visualization -* [OpenSearch](https://opensearch.org/) - a search and analytics engine for indexing and querying network traffic session metadata -* [Logstash](https://www.elastic.co/products/logstash) and [Filebeat](https://www.elastic.co/products/beats/filebeat) - for ingesting and parsing [Zeek](https://www.zeek.org/index.html) [Log Files](https://docs.zeek.org/en/stable/script-reference/log-files.html) and ingesting them into OpenSearch in a format that Arkime understands in the same way it natively understands PCAP data -* [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) - for creating additional ad-hoc visualizations and dashboards beyond that which is provided by Arkime viewer -* [Zeek](https://www.zeek.org/index.html) - a network analysis framework and IDS -* [Suricata](https://suricata.io/) - an IDS and threat detection engine -* [Yara](https://github.com/VirusTotal/yara) - a tool used to identify and classify malware samples -* [Capa](https://github.com/fireeye/capa) - a tool for detecting capabilities in executable files -* [ClamAV](https://www.clamav.net/) - an antivirus engine for scanning files extracted by Zeek -* [CyberChef](https://github.com/gchq/CyberChef) - a "swiss-army knife" data conversion tool -* [jQuery File Upload](https://github.com/blueimp/jQuery-File-Upload) - for uploading PCAP files and Zeek logs for processing -* [List.js](https://github.com/javve/list.js) - for the [host and subnet name mapping](#HostAndSubnetNaming) interface -* [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) - for simple, reproducible deployment of the Malcolm appliance across environments and to coordinate communication between its various components -* [Nginx](https://nginx.org/) - for HTTPS and reverse proxying Malcolm components -* [nginx-auth-ldap](https://github.com/kvspb/nginx-auth-ldap) - an LDAP authentication module for nginx -* [Fluent Bit](https://fluentbit.io/) - for forwarding metrics to Malcolm from [network sensors](#Hedgehog) (packet capture appliances) -* [Mark Baggett](https://github.com/MarkBaggett)'s [freq](https://github.com/MarkBaggett/freq) - a tool for calculating entropy of strings -* [Florian Roth](https://github.com/Neo23x0)'s [Signature-Base](https://github.com/Neo23x0/signature-base) Yara ruleset -* These Zeek plugins: - * some of Amazon.com, Inc.'s [ICS protocol](https://github.com/amzn?q=zeek) analyzers - * Andrew Klaus's [Sniffpass](https://github.com/cybera/zeek-sniffpass) plugin for detecting cleartext passwords in HTTP POST requests - * Andrew Klaus's [zeek-httpattacks](https://github.com/precurse/zeek-httpattacks) plugin for detecting noncompliant HTTP requests - * ICS protocol analyzers for Zeek published by [DHS CISA](https://github.com/cisagov/ICSNPP) and [Idaho National Lab](https://github.com/idaholab/ICSNPP) - * Corelight's ["bad neighbor" (CVE-2020-16898)](https://github.com/corelight/CVE-2020-16898) plugin - * Corelight's ["Log4Shell" (CVE-2021-44228)](https://github.com/corelight/cve-2021-44228) plugin - * Corelight's ["OMIGOD" (CVE-2021-38647)](https://github.com/corelight/CVE-2021-38647) plugin - * Corelight's [Apache HTTP server 2.4.49-2.4.50 path traversal/RCE vulnerability (CVE-2021-41773)](https://github.com/corelight/CVE-2021-41773) plugin - * Corelight's [bro-xor-exe](https://github.com/corelight/bro-xor-exe-plugin) plugin - * Corelight's [callstranger-detector](https://github.com/corelight/callstranger-detector) plugin - * Corelight's [community ID](https://github.com/corelight/zeek-community-id) flow hashing plugin - * Corelight's [DCE/RPC remote code execution vulnerability (CVE-2022-26809)](https://github.com/corelight/cve-2022-26809) plugin - * Corelight's [HTTP More Filenames](https://github.com/corelight/http-more-files-names) plugin - * Corelight's [HTTP protocol stack vulnerability (CVE-2021-31166)](https://github.com/corelight/CVE-2021-31166) plugin - * Corelight's [pingback](https://github.com/corelight/pingback) plugin - * Corelight's [ripple20](https://github.com/corelight/ripple20) plugin - * Corelight's [SIGred](https://github.com/corelight/SIGred) plugin - * Corelight's [VMware Workspace ONE Access and Identity Manager RCE vulnerability (CVE-2022-22954)](https://github.com/corelight/cve-2022-22954) plugin - * Corelight's [Zerologon](https://github.com/corelight/zerologon) plugin - * Corelight's [Microsoft Excel privilege escalation detection (CVE-2021-42292)](https://github.com/corelight/CVE-2021-42292) plugin - * J-Gras' [Zeek::AF_Packet](https://github.com/J-Gras/zeek-af_packet-plugin) plugin - * Johanna Amann's [CVE-2020-0601](https://github.com/0xxon/cve-2020-0601) ECC certificate validation plugin and [CVE-2020-13777](https://github.com/0xxon/cve-2020-13777) GnuTLS unencrypted session ticket detection plugin - * Lexi Brent's [EternalSafety](https://github.com/0xl3x1/zeek-EternalSafety) plugin - * MITRE Cyber Analytics Repository's [Bro/Zeek ATT&CK®-Based Analytics (BZAR)](https://github.com/mitre-attack/car/tree/master/implementations) script - * Salesforce's [gQUIC](https://github.com/salesforce/GQUIC_Protocol_Analyzer) analyzer - * Salesforce's [HASSH](https://github.com/salesforce/hassh) SSH fingerprinting plugin - * Salesforce's [JA3](https://github.com/salesforce/ja3) TLS fingerprinting plugin - * Zeek's [Spicy](https://github.com/zeek/spicy) plugin framework -* [GeoLite2](https://dev.maxmind.com/geoip/geoip2/geolite2/) - Malcolm includes GeoLite2 data created by [MaxMind](https://www.maxmind.com) - -## Supported Protocols - -Malcolm uses [Zeek](https://docs.zeek.org/en/stable/script-reference/proto-analyzers.html) and [Arkime](https://github.com/arkime/arkime/tree/master/capture/parsers) to analyze network traffic. These tools provide varying degrees of visibility into traffic transmitted over the following network protocols: - -| Traffic | Wiki | Organization/Specification | Arkime | Zeek | -|---|:---:|:---:|:---:|:---:| -|Internet layer|[🔗](https://en.wikipedia.org/wiki/Internet_layer)|[🔗](https://tools.ietf.org/html/rfc791)|[✓](https://github.com/arkime/arkime/blob/master/capture/packet.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/conn/main.zeek.html#type-Conn::Info)| -|Border Gateway Protocol (BGP)|[🔗](https://en.wikipedia.org/wiki/Border_Gateway_Protocol)|[🔗](https://tools.ietf.org/html/rfc2283)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/bgp.c)|| -|Building Automation and Control (BACnet)|[🔗](https://en.wikipedia.org/wiki/BACnet)|[🔗](http://www.bacnet.org/)||[✓](https://github.com/cisagov/icsnpp-bacnet)| -|Bristol Standard Asynchronous Protocol (BSAP)|[🔗](https://en.wikipedia.org/wiki/Bristol_Standard_Asynchronous_Protocol)|[🔗](http://www.documentation.emersonprocess.com/groups/public/documents/specification_sheets/d301321x012.pdf)[🔗](http://www.documentation.emersonprocess.com/groups/public/documents/instruction_manuals/d301401x012.pdf)||[✓](https://github.com/cisagov/icsnpp-bsap)| -|Distributed Computing Environment / Remote Procedure Calls (DCE/RPC)|[🔗](https://en.wikipedia.org/wiki/DCE/RPC)|[🔗](https://pubs.opengroup.org/onlinepubs/009629399/toc.pdf)||[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/dce-rpc/main.zeek.html#type-DCE_RPC::Info)| -|Dynamic Host Configuration Protocol (DHCP)|[🔗](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol)|[🔗](https://tools.ietf.org/html/rfc2131)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/dhcp.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/dhcp/main.zeek.html#type-DHCP::Info)| -|Distributed Network Protocol 3 (DNP3)|[🔗](https://en.wikipedia.org/wiki/DNP3)|[🔗](https://www.dnp.org)||[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/dnp3/main.zeek.html#type-DNP3::Info)[✓](https://github.com/cisagov/icsnpp-dnp3)| -|Domain Name System (DNS)|[🔗](https://en.wikipedia.org/wiki/Domain_Name_System)|[🔗](https://tools.ietf.org/html/rfc1035)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/dns.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/dns/main.zeek.html#type-DNS::Info)| -|EtherCAT|[🔗](https://en.wikipedia.org/wiki/EtherCAT)|[🔗](https://www.ethercat.org/en/downloads/downloads_A02E436C7A97479F9261FDFA8A6D71E5.htm)||[✓](https://github.com/cisagov/icsnpp-ethercat)| -|EtherNet/IP / Common Industrial Protocol (CIP)|[🔗](https://en.wikipedia.org/wiki/EtherNet/IP) [🔗](https://en.wikipedia.org/wiki/Common_Industrial_Protocol)|[🔗](https://www.odva.org/Technology-Standards/EtherNet-IP/Overview)||[✓](https://github.com/cisagov/icsnpp-enip)| -|FTP (File Transfer Protocol)|[🔗](https://en.wikipedia.org/wiki/File_Transfer_Protocol)|[🔗](https://tools.ietf.org/html/rfc959)||[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/ftp/info.zeek.html#type-FTP::Info)| -|GENISYS||[🔗](https://manualzz.com/doc/6363274/genisys-2000---ansaldo-sts---product-support#93)[🔗](https://gitlab.com/wireshark/wireshark/-/issues/3422)||[✓](https://github.com/cisagov/icsnpp-genisys)| -|Google Quick UDP Internet Connections (gQUIC)|[🔗](https://en.wikipedia.org/wiki/QUIC#Google_QUIC_(gQUIC))|[🔗](https://www.chromium.org/quic)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/quic.c)|[✓](https://github.com/salesforce/GQUIC_Protocol_Analyzer/blob/master/scripts/Salesforce/GQUIC/main.bro)| -|Hypertext Transfer Protocol (HTTP)|[🔗](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol)|[🔗](https://tools.ietf.org/html/rfc7230)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/http.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/http/main.zeek.html#type-HTTP::Info)| -|IPsec|[🔗](https://en.wikipedia.org/wiki/IPsec)|[🔗](https://zeek.org/2021/04/20/zeeks-ipsec-protocol-analyzer/)||[✓](https://github.com/corelight/zeek-spicy-ipsec)| -|Internet Relay Chat (IRC)|[🔗](https://en.wikipedia.org/wiki/Internet_Relay_Chat)|[🔗](https://tools.ietf.org/html/rfc1459)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/irc.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/irc/main.zeek.html#type-IRC::Info)| -|Lightweight Directory Access Protocol (LDAP)|[🔗](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol)|[🔗](https://tools.ietf.org/html/rfc4511)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/ldap.c)|[✓](https://github.com/zeek/spicy-ldap)| -|Kerberos|[🔗](https://en.wikipedia.org/wiki/Kerberos_(protocol))|[🔗](https://tools.ietf.org/html/rfc4120)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/krb5.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/krb/main.zeek.html#type-KRB::Info)| -|Modbus|[🔗](https://en.wikipedia.org/wiki/Modbus)|[🔗](http://www.modbus.org/)||[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/modbus/main.zeek.html#type-Modbus::Info)[✓](https://github.com/cisagov/icsnpp-modbus)| -|MQ Telemetry Transport (MQTT)|[🔗](https://en.wikipedia.org/wiki/MQTT)|[🔗](https://mqtt.org/)||[✓](https://docs.zeek.org/en/stable/scripts/policy/protocols/mqtt/main.zeek.html)| -|MySQL|[🔗](https://en.wikipedia.org/wiki/MySQL)|[🔗](https://dev.mysql.com/doc/internals/en/client-server-protocol.html)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/mysql.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/mysql/main.zeek.html#type-MySQL::Info)| -|NT Lan Manager (NTLM)|[🔗](https://en.wikipedia.org/wiki/NT_LAN_Manager)|[🔗](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4?redirectedfrom=MSDN)||[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/ntlm/main.zeek.html#type-NTLM::Info)| -|Network Time Protocol (NTP)|[🔗](https://en.wikipedia.org/wiki/Network_Time_Protocol)|[🔗](http://www.ntp.org)||[✓](https://docs.zeek.org/en/latest/scripts/base/protocols/ntp/main.zeek.html#type-NTP::Info)| -|Oracle|[🔗](https://en.wikipedia.org/wiki/Oracle_Net_Services)|[🔗](https://docs.oracle.com/cd/E11882_01/network.112/e41945/layers.htm#NETAG004)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/oracle.c)|| -|Open Platform Communications Unified Architecture (OPC UA) Binary|[🔗](https://en.wikipedia.org/wiki/OPC_Unified_Architecture)|[🔗](https://opcfoundation.org/developer-tools/specifications-unified-architecture)||[✓](https://github.com/cisagov/icsnpp-opcua-binary)| -|Open Shortest Path First (OSPF)|[🔗](https://en.wikipedia.org/wiki/Open_Shortest_Path_First)|[🔗](https://datatracker.ietf.org/wg/ospf/charter/)[🔗](https://datatracker.ietf.org/doc/html/rfc2328)[🔗](https://datatracker.ietf.org/doc/html/rfc5340)||[✓](https://github.com/corelight/zeek-spicy-ospf)| -|OpenVPN|[🔗](https://en.wikipedia.org/wiki/OpenVPN)|[🔗](https://openvpn.net/community-resources/openvpn-protocol/)[🔗](https://zeek.org/2021/03/16/a-zeek-openvpn-protocol-analyzer/)||[✓](https://github.com/corelight/zeek-spicy-openvpn)| -|PostgreSQL|[🔗](https://en.wikipedia.org/wiki/PostgreSQL)|[🔗](https://www.postgresql.org/)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/postgresql.c)|| -|Process Field Net (PROFINET)|[🔗](https://en.wikipedia.org/wiki/PROFINET)|[🔗](https://us.profinet.com/technology/profinet/)||[✓](https://github.com/amzn/zeek-plugin-profinet/blob/master/scripts/main.zeek)| -|Remote Authentication Dial-In User Service (RADIUS)|[🔗](https://en.wikipedia.org/wiki/RADIUS)|[🔗](https://tools.ietf.org/html/rfc2865)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/radius.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/radius/main.zeek.html#type-RADIUS::Info)| -|Remote Desktop Protocol (RDP)|[🔗](https://en.wikipedia.org/wiki/Remote_Desktop_Protocol)|[🔗](https://docs.microsoft.com/en-us/windows/win32/termserv/remote-desktop-protocol?redirectedfrom=MSDN)||[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/rdp/main.zeek.html#type-RDP::Info)| -|Remote Framebuffer (RFB)|[🔗](https://en.wikipedia.org/wiki/RFB_protocol)|[🔗](https://tools.ietf.org/html/rfc6143)||[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/rfb/main.zeek.html#type-RFB::Info)| -|S7comm / Connection Oriented Transport Protocol (COTP)|[🔗](https://wiki.wireshark.org/S7comm) [🔗](https://wiki.wireshark.org/COTP)|[🔗](https://support.industry.siemens.com/cs/document/26483647/what-properties-advantages-and-special-features-does-the-s7-protocol-offer-?dti=0&lc=en-WW) [🔗](https://www.ietf.org/rfc/rfc0905.txt)||[✓](https://github.com/cisagov/icsnpp-s7comm)| -|Secure Shell (SSH)|[🔗](https://en.wikipedia.org/wiki/Secure_Shell)|[🔗](https://tools.ietf.org/html/rfc4253)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/ssh.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/ssh/main.zeek.html#type-SSH::Info)| -|Secure Sockets Layer (SSL) / Transport Layer Security (TLS)|[🔗](https://en.wikipedia.org/wiki/Transport_Layer_Security)|[🔗](https://tools.ietf.org/html/rfc5246)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/socks.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/ssl/main.zeek.html#type-SSL::Info)| -|Session Initiation Protocol (SIP)|[🔗](https://en.wikipedia.org/wiki/Session_Initiation_Protocol)|[🔗](https://tools.ietf.org/html/rfc3261)||[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/sip/main.zeek.html#type-SIP::Info)| -|Server Message Block (SMB) / Common Internet File System (CIFS)|[🔗](https://en.wikipedia.org/wiki/Server_Message_Block)|[🔗](https://docs.microsoft.com/en-us/windows/win32/fileio/microsoft-smb-protocol-and-cifs-protocol-overview)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/smb.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/smb/main.zeek.html)| -|Simple Mail Transfer Protocol (SMTP)|[🔗](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol)|[🔗](https://tools.ietf.org/html/rfc5321)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/smtp.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/smtp/main.zeek.html#type-SMTP::Info)| -|Simple Network Management Protocol (SNMP)|[🔗](https://en.wikipedia.org/wiki/Simple_Network_Management_Protocol)|[🔗](https://tools.ietf.org/html/rfc2578)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/smtp.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/snmp/main.zeek.html#type-SNMP::Info)| -|SOCKS|[🔗](https://en.wikipedia.org/wiki/SOCKS)|[🔗](https://tools.ietf.org/html/rfc1928)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/socks.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/socks/main.zeek.html#type-SOCKS::Info)| -|STUN (Session Traversal Utilities for NAT)|[🔗](https://en.wikipedia.org/wiki/STUN)|[🔗](https://datatracker.ietf.org/doc/html/rfc3489)|[✓](https://github.com/arkime/arkime/blob/main/capture/parsers/misc.c#L147)|[✓](https://github.com/corelight/zeek-spicy-stun)| -|Syslog|[🔗](https://en.wikipedia.org/wiki/Syslog)|[🔗](https://tools.ietf.org/html/rfc5424)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/tls.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/protocols/syslog/main.zeek.html#type-Syslog::Info)| -|Tabular Data Stream (TDS)|[🔗](https://en.wikipedia.org/wiki/Tabular_Data_Stream)|[🔗](https://www.freetds.org/tds.html) [🔗](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/b46a581a-39de-4745-b076-ec4dbb7d13ec)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/tds.c)|[✓](https://github.com/amzn/zeek-plugin-tds/blob/master/scripts/main.zeek)| -|Telnet / remote shell (rsh) / remote login (rlogin)|[🔗](https://en.wikipedia.org/wiki/Telnet)[🔗](https://en.wikipedia.org/wiki/Berkeley_r-commands)|[🔗](https://tools.ietf.org/html/rfc854)[🔗](https://tools.ietf.org/html/rfc1282)|[✓](https://github.com/arkime/arkime/blob/master/capture/parsers/misc.c#L336)|[✓](https://docs.zeek.org/en/current/scripts/base/bif/plugins/Zeek_Login.events.bif.zeek.html)[❋](https://github.com/idaholab/Malcolm/blob/main/zeek/config/login.zeek)| -|TFTP (Trivial File Transfer Protocol)|[🔗](https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol)|[🔗](https://tools.ietf.org/html/rfc1350)||[✓](https://github.com/zeek/spicy-analyzers/blob/main/analyzer/protocol/tftp/tftp.zeek)| -|WireGuard|[🔗](https://en.wikipedia.org/wiki/WireGuard)|[🔗](https://www.wireguard.com/protocol/)[🔗](https://www.wireguard.com/papers/wireguard.pdf)||[✓](https://github.com/corelight/zeek-spicy-wireguard)| -|various tunnel protocols (e.g., GTP, GRE, Teredo, AYIYA, IP-in-IP, etc.)|[🔗](https://en.wikipedia.org/wiki/Tunneling_protocol)||[✓](https://github.com/arkime/arkime/blob/master/capture/packet.c)|[✓](https://docs.zeek.org/en/stable/scripts/base/frameworks/tunnels/main.zeek.html#type-Tunnel::Info)| - -Additionally, Zeek is able to detect and, where possible, log the type, vendor and version of [various](https://docs.zeek.org/en/stable/scripts/base/frameworks/software/main.zeek.html#type-Software::Type) other [software protocols](https://en.wikipedia.org/wiki/Application_layer). - -As part of its network traffic analysis, Zeek can extract and analyze files transferred across the protocols it understands. In addition to generating logs for transferred files, deeper analysis is done into the following file types: - -* [Portable executable](https://docs.zeek.org/en/stable/scripts/base/files/pe/main.zeek.html#type-PE::Info) files -* [X.509](https://docs.zeek.org/en/stable/scripts/base/files/x509/main.zeek.html#type-X509::Info) certificates - -See [automatic file extraction and scanning](#ZeekFileExtraction) for additional features related to file scanning. - -See [Zeek log integration](#ArkimeZeek) for more information on how Malcolm integrates [Arkime sessions and Zeek logs](#ZeekArkimeFlowCorrelation) for analysis. - -## Development - -Checking out the [Malcolm source code](https://github.com/idaholab/Malcolm/tree/main) results in the following subdirectories in your `malcolm/` working copy: - -* `api` - code and configuration for the `api` container which provides a REST API to query Malcolm -* `arkime` - code and configuration for the `arkime` container which processes PCAP files using `capture` and which serves the Viewer application -* `arkime-logs` - an initially empty directory to which the `arkime` container will write some debug log files -* `arkime-raw` - an initially empty directory to which the `arkime` container will write captured PCAP files; as Arkime as employed by Malcolm is currently used for processing previously-captured PCAP files, this directory is currently unused -* `Dockerfiles` - a directory containing build instructions for Malcolm's docker images -* `docs` - a directory containing instructions and documentation -* `opensearch` - an initially empty directory where the OpenSearch database instance will reside -* `opensearch-backup` - an initially empty directory for storing OpenSearch [index snapshots](#IndexManagement) -* `filebeat` - code and configuration for the `filebeat` container which ingests Zeek logs and forwards them to the `logstash` container -* `file-monitor` - code and configuration for the `file-monitor` container which can scan files extracted by Zeek -* `file-upload` - code and configuration for the `upload` container which serves a web browser-based upload form for uploading PCAP files and Zeek logs, and which serves an SFTP share as an alternate method for upload -* `freq-server` - code and configuration for the `freq` container used for calculating entropy of strings -* `htadmin` - configuration for the `htadmin` user account management container -* `dashboards` - code and configuration for the `dashboards` container for creating additional ad-hoc visualizations and dashboards beyond that which is provided by Arkime Viewer -* `logstash` - code and configuration for the `logstash` container which parses Zeek logs and forwards them to the `opensearch` container -* `malcolm-iso` - code and configuration for building an [installer ISO](#ISO) for a minimal Debian-based Linux installation for running Malcolm -* `name-map-ui` - code and configuration for the `name-map-ui` container which provides the [host and subnet name mapping](#HostAndSubnetNaming) interface -* `nginx` - configuration for the `nginx` reverse proxy container -* `pcap` - an initially empty directory for PCAP files to be uploaded, processed, and stored -* `pcap-capture` - code and configuration for the `pcap-capture` container which can capture network traffic -* `pcap-monitor` - code and configuration for the `pcap-monitor` container which watches for new or uploaded PCAP files notifies the other services to process them -* `scripts` - control scripts for starting, stopping, restarting, etc. Malcolm -* `sensor-iso` - code and configuration for building a [Hedgehog Linux](#Hedgehog) ISO -* `shared` - miscellaneous code used by various Malcolm components -* `suricata` - code and configuration for the `suricata` container which handles PCAP processing using Suricata -* `suricata-logs` - an initially empty directory for Suricata logs to be uploaded, processed, and stored -* `zeek` - code and configuration for the `zeek` container which handles PCAP processing using Zeek -* `zeek-logs` - an initially empty directory for Zeek logs to be uploaded, processed, and stored - -and the following files of special note: - -* `auth.env` - the script `./scripts/auth_setup` prompts the user for the administrator credentials used by the Malcolm appliance, and `auth.env` is the environment file where those values are stored -* `cidr-map.txt` - specify custom IP address to network segment mapping -* `host-map.txt` - specify custom IP and/or MAC address to host mapping -* `net-map.json` - an alternative to `cidr-map.txt` and `host-map.txt`, mapping hosts and network segments to their names in a JSON-formatted file -* `docker-compose.yml` - the configuration file used by `docker-compose` to build, start, and stop an instance of the Malcolm appliance -* `docker-compose-standalone.yml` - similar to `docker-compose.yml`, only used for the ["packaged"](#Packager) installation of Malcolm - -### Building from source - -Building the Malcolm docker images from scratch requires internet access to pull source files for its components. Once internet access is available, execute the following command to build all of the Docker images used by the Malcolm appliance: - -``` -$ ./scripts/build.sh -``` - -Then, go take a walk or something since it will be a while. When you're done, you can run `docker images` and see you have fresh images for: - -* `malcolmnetsec/api` (based on `python:3-slim`) -* `malcolmnetsec/arkime` (based on `debian:11-slim`) -* `malcolmnetsec/dashboards-helper` (based on `alpine:3.16`) -* `malcolmnetsec/dashboards` (based on `opensearchproject/opensearch-dashboards`) -* `malcolmnetsec/file-monitor` (based on `debian:11-slim`) -* `malcolmnetsec/file-upload` (based on `debian:11-slim`) -* `malcolmnetsec/filebeat-oss` (based on `docker.elastic.co/beats/filebeat-oss`) -* `malcolmnetsec/freq` (based on `debian:11-slim`) -* `malcolmnetsec/htadmin` (based on `debian:11-slim`) -* `malcolmnetsec/logstash-oss` (based on `opensearchproject/logstash-oss-with-opensearch-output-plugin`) -* `malcolmnetsec/name-map-ui` (based on `alpine:3.16`) -* `malcolmnetsec/nginx-proxy` (based on `alpine:3.16`) -* `malcolmnetsec/opensearch` (based on `opensearchproject/opensearch`) -* `malcolmnetsec/pcap-capture` (based on `debian:11-slim`) -* `malcolmnetsec/pcap-monitor` (based on `debian:11-slim`) -* `malcolmnetsec/suricata` (based on `debian:11-slim`) -* `malcolmnetsec/zeek` (based on `debian:11-slim`) - -Alternately, if you have forked Malcolm on GitHub, [workflow files](./.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](#Hedgehog) and [Malcolm](#ISO) installer ISOs. The resulting images are named according to the pattern `ghcr.io/owner/malcolmnetsec/image:branch` (e.g., if you've forked Malcolm with the github user `romeogdetlevjr`, the `arkime` container built for the `main` would be named `ghcr.io/romeogdetlevjr/malcolmnetsec/arkime:main`). To run your local instance of Malcolm using these images instead of the official ones, you'll need to edit your `docker-compose.yml` file(s) and replace the `image:` tags according to this new pattern, or use the bash helper script `./shared/bin/github_image_helper.sh` to pull and re-tag the images. - -## Pre-Packaged installation files - -### Creating pre-packaged installation files - -`scripts/malcolm_appliance_packager.sh` can be run to package up the configuration files (and, if necessary, the Docker images) which can be copied to a network share or USB drive for distribution to non-networked machines. For example: - -``` -$ ./scripts/malcolm_appliance_packager.sh -You must set a username and password for Malcolm, and self-signed X.509 certificates will be generated - -Store administrator username/password for local Malcolm access? (Y/n): y - -Administrator username: analyst -analyst password: -analyst password (again): - -(Re)generate self-signed certificates for HTTPS access (Y/n): y - -(Re)generate self-signed certificates for a remote log forwarder (Y/n): y - -Store username/password for primary remote OpenSearch instance? (y/N): n - -Store username/password for secondary remote OpenSearch instance? (y/N): n - -Store username/password for email alert sender account? (y/N): n - -Packaged Malcolm to "/home/user/tmp/malcolm_20190513_101117_f0d052c.tar.gz" - -Do you need to package docker images also [y/N]? y -This might take a few minutes... - -Packaged Malcolm docker images to "/home/user/tmp/malcolm_20190513_101117_f0d052c_images.tar.gz" - - -To install Malcolm: - 1. Run install.py - 2. Follow the prompts - -To start, stop, restart, etc. Malcolm: - Use the control scripts in the "scripts/" directory: - - start (start Malcolm) - - stop (stop Malcolm) - - restart (restart Malcolm) - - logs (monitor Malcolm logs) - - wipe (stop Malcolm and clear its database) - - auth_setup (change authentication-related settings) - -A minute or so after starting Malcolm, the following services will be accessible: - - Arkime: https://localhost/ - - OpenSearch Dashboards: https://localhost/dashboards/ - - PCAP upload (web): https://localhost/upload/ - - PCAP upload (sftp): sftp://USERNAME@127.0.0.1:8022/files/ - - Host and subnet name mapping editor: https://localhost/name-map-ui/ - - Account management: https://localhost:488/ -``` - -The above example will result in the following artifacts for distribution as explained in the script's output: - -``` -$ ls -lh -total 2.0G --rwxr-xr-x 1 user user 61k May 13 11:32 install.py --rw-r--r-- 1 user user 2.0G May 13 11:37 malcolm_20190513_101117_f0d052c_images.tar.gz --rw-r--r-- 1 user user 683 May 13 11:37 malcolm_20190513_101117_f0d052c.README.txt --rw-r--r-- 1 user user 183k May 13 11:32 malcolm_20190513_101117_f0d052c.tar.gz -``` - -### Installing from pre-packaged installation files - -If you have obtained pre-packaged installation files to install Malcolm on a non-networked machine via an internal network share or on a USB key, you likely have the following files: - -* `malcolm_YYYYMMDD_HHNNSS_xxxxxxx.README.txt` - This readme file contains a minimal set up instructions for extracting the contents of the other tarballs and running the Malcolm appliance. -* `malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` - This tarball contains the configuration files and directory configuration used by an instance of Malcolm. It can be extracted via `tar -xf malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` upon which a directory will be created (named similarly to the tarball) containing the directories and configuration files. Alternatively, `install.py` can accept this filename as an argument and handle its extraction and initial configuration for you. -* `malcolm_YYYYMMDD_HHNNSS_xxxxxxx_images.tar.gz` - This tarball contains the Docker images used by Malcolm. It can be imported manually via `docker load -i malcolm_YYYYMMDD_HHNNSS_xxxxxxx_images.tar.gz` -* `install.py` - This install script can load the Docker images and extract Malcolm configuration files from the aforementioned tarballs and do some initial configuration for you. - -Run `install.py malcolm_XXXXXXXX_XXXXXX_XXXXXXX.tar.gz` and follow the prompts. If you do not already have Docker and Docker Compose installed, the `install.py` script will help you install them. - -## Preparing your system - -### Recommended system requirements - -Malcolm runs on top of [Docker](https://www.docker.com/) which runs on recent releases of Linux, Apple macOS and Microsoft Windows 10. - -To quote the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/guide/current/hardware.html), "If there is one resource that you will run out of first, it will likely be memory." The same is true for Malcolm: you will want at least 16 gigabytes of RAM to run Malcolm comfortably. For processing large volumes of traffic, I'd recommend at a bare minimum a dedicated server with 16 cores and 16 gigabytes of RAM. Malcolm can run on less, but more is better. You're going to want as much hard drive space as possible, of course, as the amount of PCAP data you're able to analyze and store will be limited by your hard drive. - -Arkime's wiki has a couple of documents ([here](https://github.com/arkime/arkime#hardware-requirements) and [here](https://github.com/arkime/arkime/wiki/FAQ#what-kind-of-capture-machines-should-we-buy) and [here](https://github.com/arkime/arkime/wiki/FAQ#how-many-elasticsearch-nodes-or-machines-do-i-need) and a [calculator here](https://molo.ch/#estimators)) which may be helpful, although not everything in those documents will apply to a Docker-based setup like Malcolm. - -### System configuration and tuning - -If you already have Docker and Docker Compose installed, the `install.py` script can still help you tune system configuration and `docker-compose.yml` parameters for Malcolm. To run it in "configuration only" mode, bypassing the steps to install Docker and Docker Compose, run it like this: -``` -./scripts/install.py --configure -``` - -Although `install.py` will attempt to automate many of the following configuration and tuning parameters, they are nonetheless listed in the following sections for reference: - -#### `docker-compose.yml` parameters - -Edit `docker-compose.yml` and search for the `OPENSEARCH_JAVA_OPTS` key. Edit the `-Xms4g -Xmx4g` values, replacing `4g` with a number that is half of your total system memory, or just under 32 gigabytes, whichever is less. So, for example, if I had 64 gigabytes of memory I would edit those values to be `-Xms31g -Xmx31g`. This indicates how much memory can be allocated to the OpenSearch heaps. For a pleasant experience, I would suggest not using a value under 10 gigabytes. Similar values can be modified for Logstash with `LS_JAVA_OPTS`, where using 3 or 4 gigabytes is recommended. - -Various other environment variables inside of `docker-compose.yml` can be tweaked to control aspects of how Malcolm behaves, particularly with regards to processing PCAP files and Zeek logs. The environment variables of particular interest are located near the top of that file under **Commonly tweaked configuration options**, which include: - -* `ARKIME_ANALYZE_PCAP_THREADS` – the number of threads available to Arkime for analyzing PCAP files (default `1`) -* `AUTO_TAG` – if set to `true`, Malcolm will automatically create Arkime sessions and Zeek logs with tags based on the filename, as described in [Tagging](#Tagging) (default `true`) -* `BEATS_SSL` – if set to `true`, Logstash will use require encrypted communications for any external [Beats](https://www.elastic.co/guide/en/logstash/current/plugins-inputs-beats.html)-based forwarders from which it will accept logs (default `true`) -* `CONNECTION_SECONDS_SEVERITY_THRESHOLD` - when [severity scoring](#Severity) is enabled, this variable indicates the duration threshold (in seconds) for assigning severity to long connections (default `3600`) -* `EXTRACTED_FILE_CAPA_VERBOSE` – if set to `true`, all Capa rule hits will be logged; otherwise (`false`) only [MITRE ATT&CK® technique](https://attack.mitre.org/techniques) classifications will be logged -* `EXTRACTED_FILE_ENABLE_CAPA` – if set to `true`, [Zeek-extracted files](#ZeekFileExtraction) that are determined to be PE (portable executable) files will be scanned with [Capa](https://github.com/fireeye/capa) -* `EXTRACTED_FILE_ENABLE_CLAMAV` – if set to `true`, [Zeek-extracted files](#ZeekFileExtraction) will be scanned with [ClamAV](https://www.clamav.net/) -* `EXTRACTED_FILE_ENABLE_YARA` – if set to `true`, [Zeek-extracted files](#ZeekFileExtraction) will be scanned with [Yara](https://github.com/VirusTotal/yara) -* `EXTRACTED_FILE_HTTP_SERVER_ENABLE` – if set to `true`, the directory containing [Zeek-extracted files](#ZeekFileExtraction) will be served over HTTP at `./extracted-files/` (e.g., [https://localhost/extracted-files/](https://localhost/extracted-files/) if you are connecting locally) -* `EXTRACTED_FILE_HTTP_SERVER_ENCRYPT` – if set to `true`, those Zeek-extracted files will be AES-256-CBC-encrypted in an `openssl enc`-compatible format (e.g., `openssl enc -aes-256-cbc -d -in example.exe.encrypted -out example.exe`) -* `EXTRACTED_FILE_HTTP_SERVER_KEY` – specifies the AES-256-CBC decryption password for encrypted Zeek-extracted files; used in conjunction with `EXTRACTED_FILE_HTTP_SERVER_ENCRYPT` -* `EXTRACTED_FILE_IGNORE_EXISTING` – if set to `true`, files extant in `./zeek-logs/extract_files/` directory will be ignored on startup rather than scanned -* `EXTRACTED_FILE_PRESERVATION` – determines behavior for preservation of [Zeek-extracted files](#ZeekFileExtraction) -* `EXTRACTED_FILE_UPDATE_RULES` – if set to `true`, file scanner engines (e.g., ClamAV, Capa, Yara) will periodically update their rule definitions -* `EXTRACTED_FILE_YARA_CUSTOM_ONLY` – if set to `true`, Malcolm will bypass the default [Yara ruleset](https://github.com/Neo23x0/signature-base) and use only user-defined rules in `./yara/rules` -* `FREQ_LOOKUP` - if set to `true`, domain names (from DNS queries and SSL server names) will be assigned entropy scores as calculated by [`freq`](https://github.com/MarkBaggett/freq) (default `false`) -* `FREQ_SEVERITY_THRESHOLD` - when [severity scoring](#Severity) is enabled, this variable indicates the entropy threshold for assigning severity to events with entropy scores calculated by [`freq`](https://github.com/MarkBaggett/freq); a lower value will only assign severity scores to fewer domain names with higher entropy (e.g., `2.0` for `NQZHTFHRMYMTVBQJE.COM`), while a higher value will assign severity scores to more domain names with lower entropy (e.g., `7.5` for `naturallanguagedomain.example.org`) (default `2.0`) -* `LOGSTASH_OUI_LOOKUP` – if set to `true`, Logstash will map MAC addresses to vendors for all source and destination MAC addresses when analyzing Zeek logs (default `true`) -* `LOGSTASH_REVERSE_DNS` – if set to `true`, Logstash will perform a reverse DNS lookup for all external source and destination IP address values when analyzing Zeek logs (default `false`) -* `LOGSTASH_SEVERITY_SCORING` - if set to `true`, Logstash will perform [severity scoring](#Severity) when analyzing Zeek logs (default `true`) -* `MANAGE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will be marked as available for deletion by Arkime if available storage space becomes too low (default `false`) -* `MAXMIND_GEOIP_DB_LICENSE_KEY` - Malcolm uses MaxMind's free GeoLite2 databases for GeoIP lookups. As of December 30, 2019, these databases are [no longer available](https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases/) for download via a public URL. Instead, they must be downloaded using a MaxMind license key (available without charge [from MaxMind](https://www.maxmind.com/en/geolite2/signup)). The license key can be specified here for GeoIP database downloads during build- and run-time. -* `OPENSEARCH_LOCAL` - if set to `true`, Malcolm will use its own internal [OpenSearch instance](#OpenSearchInstance) (default `true`) -* `OPENSEARCH_URL` - when using Malcolm's internal OpenSearch instance (i.e., `OPENSEARCH_LOCAL` is `true`) this should be `http://opensearch:9200`, otherwise this value specifies the primary remote instance URL in the format `protocol://host:port` (default `http://opensearch:9200`) -* `OPENSEARCH_SSL_CERTIFICATE_VERIFICATION` - if set to `true`, connections to the primary remote OpenSearch instance will require full TLS certificate validation (this may fail if using self-signed certificates) (default `false`) -* `OPENSEARCH_SECONDARY` - if set to `true`, Malcolm will forward logs to a secondary remote OpenSearch instance in addition to the primary (local or remote) OpenSearch instance (default `false`) -* `OPENSEARCH_SECONDARY_URL` - when forwarding to a secondary remote OpenSearch instance (i.e., `OPENSEARCH_SECONDARY` is `true`) this value specifies the secondary remote instance URL in the format `protocol://host:port` -* `OPENSEARCH_SECONDARY_SSL_CERTIFICATE_VERIFICATION` - if set to `true`, connections to the secondary remote OpenSearch instance will require full TLS certificate validation (this may fail if using self-signed certificates) (default `false`) -* `NGINX_BASIC_AUTH` - if set to `true`, use [TLS-encrypted HTTP basic](#AuthBasicAccountManagement) authentication (default); if set to `false`, use [Lightweight Directory Access Protocol (LDAP)](#AuthLDAP) authentication -* `NGINX_LOG_ACCESS_AND_ERRORS` - if set to `true`, all access to Malcolm via its [web interfaces](#UserInterfaceURLs) will be logged to OpenSearch (default `false`) -* `NGINX_SSL` - if set to `true`, require HTTPS connections to Malcolm's `nginx-proxy` container (default); if set to `false`, use unencrypted HTTP connections (using unsecured HTTP connections is **NOT** recommended unless you are running Malcolm behind another reverse proxy like Traefik, Caddy, etc.) -* `PCAP_ENABLE_NETSNIFF` – if set to `true`, Malcolm will capture network traffic on the local network interface(s) indicated in `PCAP_IFACE` using [netsniff-ng](http://netsniff-ng.org/) -* `PCAP_ENABLE_TCPDUMP` – if set to `true`, Malcolm will capture network traffic on the local network interface(s) indicated in `PCAP_IFACE` using [tcpdump](https://www.tcpdump.org/); there is no reason to enable *both* `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP` -* `PCAP_FILTER` – specifies a tcpdump-style filter expression for local packet capture; leave blank to capture all traffic -* `PCAP_IFACE` – used to specify the network interface(s) for local packet capture if `PCAP_ENABLE_NETSNIFF`, `PCAP_ENABLE_TCPDUMP`, `ZEEK_LIVE_CAPTURE` or `SURICATA_LIVE_CAPTURE` are enabled; for multiple interfaces, separate the interface names with a comma (e.g., `'enp0s25'` or `'enp10s0,enp11s0'`) -* `PCAP_IFACE_TWEAK` - if set to `true`, Malcolm will [use `ethtool`](shared/bin/nic-capture-setup.sh) to disable NIC hardware offloading features and adjust ring buffer sizes for capture interface(s); this should be `true` if the interface(s) are being used for capture only, `false` if they are being used for management/communication -* `PCAP_ROTATE_MEGABYTES` – used to specify how large a locally-captured PCAP file can become (in megabytes) before it is closed for processing and a new PCAP file created -* `PCAP_ROTATE_MINUTES` – used to specify a time interval (in minutes) after which a locally-captured PCAP file will be closed for processing and a new PCAP file created -* `pipeline.workers`, `pipeline.batch.size` and `pipeline.batch.delay` - these settings are used to tune the performance and resource utilization of the the `logstash` container; see [Tuning and Profiling Logstash Performance](https://www.elastic.co/guide/en/logstash/current/tuning-logstash.html), [`logstash.yml`](https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html) and [Multiple Pipelines](https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html) -* `PUID` and `PGID` - Docker runs all of its containers as the privileged `root` user by default. For better security, Malcolm immediately drops to non-privileged user accounts for executing internal processes wherever possible. The `PUID` (**p**rocess **u**ser **ID**) and `PGID` (**p**rocess **g**roup **ID**) environment variables allow Malcolm to map internal non-privileged user accounts to a corresponding [user account](https://en.wikipedia.org/wiki/User_identifier) on the host. -* `SENSITIVE_COUNTRY_CODES` - when [severity scoring](#Severity) is enabled, this variable defines a comma-separated list of sensitive countries (using [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes)) (default `'AM,AZ,BY,CN,CU,DZ,GE,HK,IL,IN,IQ,IR,KG,KP,KZ,LY,MD,MO,PK,RU,SD,SS,SY,TJ,TM,TW,UA,UZ'`, taken from the U.S. Department of Energy Sensitive Country List) -* `SURICATA_AUTO_ANALYZE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will automatically be analyzed by Suricata, and the resulting logs will also be imported (default `false`) -* `SURICATA_AUTO_ANALYZE_PCAP_THREADS` – the number of threads available to Malcolm for analyzing Suricata logs (default `1`) -* `SURICATA_CUSTOM_RULES_ONLY` – if set to `true`, Malcolm will bypass the default [Suricata ruleset](https://github.com/OISF/suricata/tree/master/rules) and use only user-defined rules (`./suricata/rules/*.rules`). -* `SURICATA_UPDATE_RULES` – if set to `true`, Suricata signatures will periodically be updated (default `false`) -* `SURICATA_LIVE_CAPTURE` - if set to `true`, Suricata will monitor live traffic on the local interface(s) defined by `PCAP_FILTER` -* `SURICATA_ROTATED_PCAP` - if set to `true`, Suricata can analyze captured PCAP files captured by `netsniff-ng` or `tcpdump` (see `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP`, as well as `SURICATA_AUTO_ANALYZE_PCAP_FILES`); if `SURICATA_LIVE_CAPTURE` is `true`, this should be false, otherwise Suricata will see duplicate traffic -* `SURICATA_…` - the [`suricata` container entrypoint script](shared/bin/suricata_config_populate.py) can use **many** more environment variables to tweak [suricata.yaml](https://github.com/OISF/suricata/blob/master/suricata.yaml.in); in that script, `DEFAULT_VARS` defines those variables (albeit without the `SURICATA_` prefix you must add to each for use) -* `TOTAL_MEGABYTES_SEVERITY_THRESHOLD` - when [severity scoring](#Severity) is enabled, this variable indicates the size threshold (in megabytes) for assigning severity to large connections or file transfers (default `1000`) -* `VTOT_API2_KEY` – used to specify a [VirusTotal Public API v.20](https://www.virustotal.com/en/documentation/public-api/) key, which, if specified, will be used to submit hashes of [Zeek-extracted files](#ZeekFileExtraction) to VirusTotal -* `ZEEK_AUTO_ANALYZE_PCAP_FILES` – if set to `true`, all PCAP files imported into Malcolm will automatically be analyzed by Zeek, and the resulting logs will also be imported (default `false`) -* `ZEEK_AUTO_ANALYZE_PCAP_THREADS` – the number of threads available to Malcolm for analyzing Zeek logs (default `1`) -* `ZEEK_DISABLE_…` - if set to any non-blank value, each of these variables can be used to disable a certain Zeek function when it analyzes PCAP files (for example, setting `ZEEK_DISABLE_LOG_PASSWORDS` to `true` to disable logging of cleartext passwords) -* `ZEEK_DISABLE_BEST_GUESS_ICS` - see ["Best Guess" Fingerprinting for ICS Protocols](#ICSBestGuess) -* `ZEEK_EXTRACTOR_MODE` – determines the file extraction behavior for file transfers detected by Zeek; see [Automatic file extraction and scanning](#ZeekFileExtraction) for more details -* `ZEEK_INTEL_FEED_SINCE` - when querying a [TAXII](#ZeekIntelSTIX) or [MISP](#ZeekIntelMISP) feed, only process threat indicators that have been created or modified since the time represented by this value; it may be either a fixed date/time (`01/01/2021`) or relative interval (`30 days ago`) -* `ZEEK_INTEL_ITEM_EXPIRATION` - specifies the value for Zeek's [`Intel::item_expiration`](https://docs.zeek.org/en/current/scripts/base/frameworks/intel/main.zeek.html#id-Intel::item_expiration) timeout as used by the [Zeek Intelligence Framework](#ZeekIntel) (default `-1min`, which disables item expiration) -* `ZEEK_INTEL_REFRESH_CRON_EXPRESSION` - specifies a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) indicating the refresh interval for generating the [Zeek Intelligence Framework](#ZeekIntel) files (defaults to empty, which disables automatic refresh) -* `ZEEK_LIVE_CAPTURE` - if set to `true`, Zeek will monitor live traffic on the local interface(s) defined by `PCAP_FILTER` -* `ZEEK_ROTATED_PCAP` - if set to `true`, Zeek can analyze captured PCAP files captured by `netsniff-ng` or `tcpdump` (see `PCAP_ENABLE_NETSNIFF` and `PCAP_ENABLE_TCPDUMP`, as well as `ZEEK_AUTO_ANALYZE_PCAP_FILES`); if `ZEEK_LIVE_CAPTURE` is `true`, this should be false, otherwise Zeek will see duplicate traffic - -#### Linux host system configuration - -##### Installing Docker - -Docker installation instructions vary slightly by distribution. Please follow the links below to docker.com to find the instructions specific to your distribution: - -* [Ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/) -* [Debian](https://docs.docker.com/install/linux/docker-ce/debian/) -* [Fedora](https://docs.docker.com/install/linux/docker-ce/fedora/) -* [CentOS](https://docs.docker.com/install/linux/docker-ce/centos/) -* [Binaries](https://docs.docker.com/install/linux/docker-ce/binaries/) - -After installing Docker, because Malcolm should be run as a non-root user, add your user to the `docker` group with something like: -``` -$ sudo usermod -aG docker yourusername -``` - -Following this, either reboot or log out then log back in. - -Docker starts automatically on DEB-based distributions. On RPM-based distributions, you need to start it manually or enable it using the appropriate `systemctl` or `service` command(s). - -You can test docker by running `docker info`, or (assuming you have internet access), `docker run --rm hello-world`. - -##### Installing docker-compose - -Please follow [this link](https://docs.docker.com/compose/install/) on docker.com for instructions on installing docker-compose. - -##### Operating system configuration - -The host system (ie., the one running Docker) will need to be configured for the [best possible OpenSearch performance](https://www.elastic.co/guide/en/elasticsearch/reference/master/system-config.html). Here are a few suggestions for Linux hosts (these may vary from distribution to distribution): - -* Append the following lines to `/etc/sysctl.conf`: - -``` -# the maximum number of open file handles -fs.file-max=2097152 - -# increase maximums for inotify watches -fs.inotify.max_user_watches=131072 -fs.inotify.max_queued_events=131072 -fs.inotify.max_user_instances=512 - -# the maximum number of memory map areas a process may have -vm.max_map_count=262144 - -# decrease "swappiness" (swapping out runtime memory vs. dropping pages) -vm.swappiness=1 - -# the maximum number of incoming connections -net.core.somaxconn=65535 - -# the % of system memory fillable with "dirty" pages before flushing -vm.dirty_background_ratio=40 - -# maximum % of dirty system memory before committing everything -vm.dirty_ratio=80 -``` - -* Depending on your distribution, create **either** the file `/etc/security/limits.d/limits.conf` containing: - -``` -# the maximum number of open file handles -* soft nofile 65535 -* hard nofile 65535 -# do not limit the size of memory that can be locked -* soft memlock unlimited -* hard memlock unlimited -``` - -**OR** the file `/etc/systemd/system.conf.d/limits.conf` containing: - -``` -[Manager] -# the maximum number of open file handles -DefaultLimitNOFILE=65535:65535 -# do not limit the size of memory that can be locked -DefaultLimitMEMLOCK=infinity -``` - -* Change the readahead value for the disk where the OpenSearch data will be stored. There are a few ways to do this. For example, you could add this line to `/etc/rc.local` (replacing `/dev/sda` with your disk block descriptor): - -``` -# change disk read-adhead value (# of blocks) -blockdev --setra 512 /dev/sda -``` - -* Change the I/O scheduler to `deadline` or `noop`. Again, this can be done in a variety of ways. The simplest is to add `elevator=deadline` to the arguments in `GRUB_CMDLINE_LINUX` in `/etc/default/grub`, then running `sudo update-grub2` - -* If you are planning on using very large data sets, consider formatting the drive containing the `opensearch` volume as XFS. - -After making all of these changes, do a reboot for good measure! - -#### macOS host system configuration - -##### Automatic installation using `install.py` - -The `install.py` script will attempt to guide you through the installation of Docker and Docker Compose if they are not present. If that works for you, you can skip ahead to **Configure docker daemon option** in this section. - -##### Install Homebrew - -The easiest way to install and maintain docker on Mac is using the [Homebrew cask](https://brew.sh). Execute the following in a terminal. - -``` -$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" -$ brew install cask -$ brew tap homebrew/cask-versions -``` - -##### Install docker-edge - -``` -$ brew cask install docker-edge -``` -This will install the latest version of docker and docker-compose. It can be upgraded later using `brew` as well: -``` -$ brew cask upgrade --no-quarantine docker-edge -``` -You can now run docker from the Applications folder. - -##### Configure docker daemon option - -Some changes should be made for performance ([this link](http://markshust.com/2018/01/30/performance-tuning-docker-mac) gives a good succinct overview). - -* **Resource allocation** - For a good experience, you likely need at least a quad-core MacBook Pro with 16GB RAM and an SSD. I have run Malcolm on an older 2013 MacBook Pro with 8GB of RAM, but the more the better. Go in your system tray and select **Docker** → **Preferences** → **Advanced**. Set the resources available to docker to at least 4 CPUs and 8GB of RAM (>= 16GB is preferable). - -* **Volume mount performance** - You can speed up performance of volume mounts by removing unused paths from **Docker** → **Preferences** → **File Sharing**. For example, if you're only going to be mounting volumes under your home directory, you could share `/Users` but remove other paths. - -After making these changes, right click on the Docker 🐋 icon in the system tray and select **Restart**. - -#### Windows host system configuration - -#### Installing and configuring Docker Desktop for Windows - -Installing and configuring [Docker to run under Windows](https://docs.docker.com/desktop/windows/wsl/) must be done manually, rather than through the `install.py` script as is done for Linux and macOS. - -1. Be running Windows 10, version 1903 or higher -1. Prepare your system and [install WSL](https://docs.microsoft.com/en-us/windows/wsl/install) and a Linux distribution by running `wsl --install -d Debian` in PowerShell as Administrator (these instructions are tested with Debian, but may work with other distributions) -1. Install Docker Desktop for Windows either by downloading the installer from the [official Docker site](https://hub.docker.com/editions/community/docker-ce-desktop-windows) or installing it through [chocolatey](https://chocolatey.org/packages/docker-desktop). -1. Follow the [Docker Desktop WSL 2 backend](https://docs.docker.com/desktop/windows/wsl/) instructions to finish configuration and review best practices -1. Reboot -1. Open the WSL distribution's terminal and run run `docker info` to make sure Docker is running - -#### Finish Malcolm's configuration - -Once Docker is installed, configured and running as described in the previous section, run [`./scripts/install.py --configure`](#ConfigAndTuning) to finish configuration of the local Malcolm installation. Malcolm will be controlled and run from within your WSL distribution's terminal environment. - -## Running Malcolm - -### OpenSearch instances - -Malcolm's default standalone configuration is to use a local [OpenSearch](https://opensearch.org/) instance in a Docker container to index and search network traffic metadata. OpenSearch can also run as a [cluster](https://opensearch.org/docs/latest/opensearch/cluster/) with instances distributed across multiple nodes with dedicated [roles](https://opensearch.org/docs/latest/opensearch/cluster/#nodes) like cluster manager, data node, ingest node, etc. - -As the permutations of OpenSearch cluster configurations are numerous, it is beyond Malcolm's scope to set up multi-node clusters. However, Malcolm can be configured to use a remote OpenSearch cluster rather than its own internal instance. - -The `OPENSEARCH_…` [environment variables in `docker-compose.yml`](#DockerComposeYml) control whether Malcolm uses its own local OpenSearch instance or a remote OpenSearch instance as its primary data store. The configuration portion of Malcolm install script ([`./scripts/install.py --configure`](#ConfigAndTuning)) can help you configure these options. - -For example, to use the default standalone configuration, answer `Y` when prompted `Should Malcolm use and maintain its own OpenSearch instance?`. - -Or, to use a remote OpenSearch cluster: - -``` -… -Should Malcolm use and maintain its own OpenSearch instance? (Y/n): n - -Enter primary remote OpenSearch connection URL (e.g., https://192.168.1.123:9200): https://192.168.1.123:9200 - -Require SSL certificate validation for communication with primary OpenSearch instance? (y/N): n - -You must run auth_setup after install.py to store OpenSearch connection credentials. -… -``` - -Whether the primary OpenSearch instance is a locally maintained single-node instance or is a remote cluster, Malcolm can be configured additionally forward logs to a secondary remote OpenSearch instance. The `OPENSEARCH_SECONDARY_…` [environment variables in `docker-compose.yml`](#DockerComposeYml) control this behavior. Configuration of a remote secondary OpenSearch instance is similar to that of a remote primary OpenSearch instance: - - -``` -… -Forward Logstash logs to a secondary remote OpenSearch instance? (y/N): y - -Enter secondary remote OpenSearch connection URL (e.g., https://192.168.1.123:9200): https://192.168.1.124:9200 - -Require SSL certificate validation for communication with secondary OpenSearch instance? (y/N): n - -You must run auth_setup after install.py to store OpenSearch connection credentials. -… -``` - -#### Authentication and authorization for remote OpenSearch clusters - -In addition to setting the environment variables in [`docker-compose.yml`](#DockerComposeYml) as described above, you must provide Malcolm with credentials for it to be able to communicate with remote OpenSearch instances. These credentials are stored in the Malcolm installation directory as `.opensearch.primary.curlrc` and `.opensearch.secondary.curlrc` for the primary and secondary OpenSearch connections, respectively, and are bind mounted into the Docker containers which need to communicate with OpenSearch. These [cURL-formatted](https://everything.curl.dev/cmdline/configfile) config files can be generated for you by the [`auth_setup`](#AuthSetup) script as illustrated: - -``` -$ ./scripts/auth_setup - -… - -Store username/password for primary remote OpenSearch instance? (y/N): y - -OpenSearch username: servicedb -servicedb password: -servicedb password (again): - -Require SSL certificate validation for OpenSearch communication? (Y/n): n - -Store username/password for secondary remote OpenSearch instance? (y/N): y - -OpenSearch username: remotedb -remotedb password: -remotedb password (again): - -Require SSL certificate validation for OpenSearch communication? (Y/n): n - -… -``` - -These files are created with permissions such that only the user account running Malcolm can access them: - -``` -$ ls -la .opensearch.*.curlrc --rw------- 1 user user 36 Aug 22 14:17 .opensearch.primary.curlrc --rw------- 1 user user 35 Aug 22 14:18 .opensearch.secondary.curlrc -``` - -One caveat with Malcolm using a remote OpenSearch cluster as its primary document store is that the accounts used to access Malcolm's [web interfaces](#UserInterfaceURLs), particularly [OpenSearch Dashboards](#Dashboards), are in some instance passed directly through to OpenSearch itself. For this reason, both Malcolm and the remote primary OpenSearch instance must have the same account information. The easiest way to accomplish this is to use an Active Directory/LDAP server that both [Malcolm](#AuthLDAP) and [OpenSearch](https://opensearch.org/docs/latest/security-plugin/configuration/ldap/) use as a common authentication backend. - -See the OpenSearch documentation on [access control](https://opensearch.org/docs/latest/security-plugin/access-control/index/) for more information. - -### Configure authentication - -Malcolm requires authentication to access the [user interface](#UserInterfaceURLs). [Nginx](https://nginx.org/) can authenticate users with either local TLS-encrypted HTTP basic authentication or using a remote Lightweight Directory Access Protocol (LDAP) authentication server. - -With the local basic authentication method, user accounts are managed by Malcolm and can be created, modified, and deleted using a [user management web interface](#AccountManagement). This method is suitable in instances where accounts and credentials do not need to be synced across many Malcolm installations. - -LDAP authentication are managed on a remote directory service, such as a [Microsoft Active Directory Domain Services](https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/get-started/virtual-dc/active-directory-domain-services-overview) or [OpenLDAP](https://www.openldap.org/). - -Malcolm's authentication method is defined in the `x-auth-variables` section near the top of the [`docker-compose.yml`](#DockerComposeYml) file with the `NGINX_BASIC_AUTH` environment variable: `true` for local TLS-encrypted HTTP basic authentication, `false` for LDAP authentication. - -In either case, you **must** run `./scripts/auth_setup` before starting Malcolm for the first time in order to: - -* define the local Malcolm administrator account username and password (although these credentials will only be used for basic authentication, not LDAP authentication) -* specify whether or not to (re)generate the self-signed certificates used for HTTPS access - * key and certificate files are located in the `nginx/certs/` directory -* specify whether or not to (re)generate the self-signed certificates used by a remote log forwarder (see the `BEATS_SSL` environment variable above) - * certificate authority, certificate, and key files for Malcolm's Logstash instance are located in the `logstash/certs/` directory - * certificate authority, certificate, and key files to be copied to and used by the remote log forwarder are located in the `filebeat/certs/` directory; if using [Hedgehog Linux](#Hedgehog), these certificates should be copied to the `/opt/sensor/sensor_ctl/logstash-client-certificates` directory on the sensor -* specify whether or not to [store the username/password](https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#authenticate-sender-account) for [email alert senders](https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#create-destinations) - * these parameters are stored securely in the OpenSearch keystore file `opensearch/opensearch.keystore` - -##### Local account management - -[`auth_setup`](#AuthSetup) is used to define the username and password for the administrator account. Once Malcolm is running, the administrator account can be used to manage other user accounts via a **Malcolm User Management** page served over HTTPS on port 488 (e.g., [https://localhost:488](https://localhost:488) if you are connecting locally). - -Malcolm user accounts can be used to access the [interfaces](#UserInterfaceURLs) of all of its [components](#Components), including Arkime. Arkime uses its own internal database of user accounts, so when a Malcolm user account logs in to Arkime for the first time Malcolm creates a corresponding Arkime user account automatically. This being the case, it is *not* recommended to use the Arkime **Users** settings page or change the password via the **Password** form under the Arkime **Settings** page, as those settings would not be consistently used across Malcolm. - -Users may change their passwords via the **Malcolm User Management** page by clicking **User Self Service**. A forgotten password can also be reset via an emailed link, though this requires SMTP server settings to be specified in `htadmin/config.ini` in the Malcolm installation directory. - -#### Lightweight Directory Access Protocol (LDAP) authentication - -The [nginx-auth-ldap](https://github.com/kvspb/nginx-auth-ldap) module serves as the interface between Malcolm's [Nginx](https://nginx.org/) web server and a remote LDAP server. When you run [`auth_setup`](#AuthSetup) for the first time, a sample LDAP configuration file is created at `nginx/nginx_ldap.conf`. - -``` -# This is a sample configuration for the ldap_server section of nginx.conf. -# Yours will vary depending on how your Active Directory/LDAP server is configured. -# See https://github.com/kvspb/nginx-auth-ldap#available-config-parameters for options. - -ldap_server ad_server { - url "ldap://ds.example.com:3268/DC=ds,DC=example,DC=com?sAMAccountName?sub?(objectClass=person)"; - - binddn "bind_dn"; - binddn_passwd "bind_dn_password"; - - group_attribute member; - group_attribute_is_dn on; - require group "CN=Malcolm,CN=Users,DC=ds,DC=example,DC=com"; - require valid_user; - satisfy all; -} - -auth_ldap_cache_enabled on; -auth_ldap_cache_expiration_time 10000; -auth_ldap_cache_size 1000; -``` - -This file is mounted into the `nginx` container when Malcolm is started to provide connection information for the LDAP server. - -The contents of `nginx_ldap.conf` will vary depending on how the LDAP server is configured. Some of the [avaiable parameters](https://github.com/kvspb/nginx-auth-ldap#available-config-parameters) in that file include: - -* **`url`** - the `ldap://` or `ldaps://` connection URL for the remote LDAP server, which has the [following syntax](https://www.ietf.org/rfc/rfc2255.txt): `ldap[s]://:/???` -* **`binddn`** and **`binddn_password`** - the account credentials used to query the LDAP directory -* **`group_attribute`** - the group attribute name which contains the member object (e.g., `member` or `memberUid`) -* **`group_attribute_is_dn`** - whether or not to search for the user's full distinguished name as the value in the group's member attribute -* **`require`** and **`satisfy`** - `require user`, `require group` and `require valid_user` can be used in conjunction with `satisfy any` or `satisfy all` to limit the users that are allowed to access the Malcolm instance - -Before starting Malcolm, edit `nginx/nginx_ldap.conf` according to the specifics of your LDAP server and directory tree structure. Using a LDAP search tool such as [`ldapsearch`](https://www.openldap.org/software/man.cgi?query=ldapsearch) in Linux or [`dsquery`](https://social.technet.microsoft.com/wiki/contents/articles/2195.active-directory-dsquery-commands.aspx) in Windows may be of help as you formulate the configuration. Your changes should be made within the curly braces of the `ldap_server ad_server { … }` section. You can troubleshoot configuration file syntax errors and LDAP connection or credentials issues by running `./scripts/logs` (or `docker-compose logs nginx`) and examining the output of the `nginx` container. - -The **Malcolm User Management** page described above is not available when using LDAP authentication. - -##### LDAP connection security - -Authentication over LDAP can be done using one of three ways, [two of which](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/8e73932f-70cf-46d6-88b1-8d9f86235e81) offer data confidentiality protection: - -* **StartTLS** - the [standard extension](https://tools.ietf.org/html/rfc2830) to the LDAP protocol to establish an encrypted SSL/TLS connection within an already established LDAP connection -* **LDAPS** - a commonly used (though unofficial and considered deprecated) method in which SSL negotiation takes place before any commands are sent from the client to the server -* **Unencrypted** (cleartext) (***not recommended***) - -In addition to the `NGINX_BASIC_AUTH` environment variable being set to `false` in the `x-auth-variables` section near the top of the [`docker-compose.yml`](#DockerComposeYml) file, the `NGINX_LDAP_TLS_STUNNEL` and `NGINX_LDAP_TLS_STUNNEL` environment variables are used in conjunction with the values in `nginx/nginx_ldap.conf` to define the LDAP connection security level. Use the following combinations of values to achieve the connection security methods above, respectively: - -* **StartTLS** - - `NGINX_LDAP_TLS_STUNNEL` set to `true` in [`docker-compose.yml`](#DockerComposeYml) - - `url` should begin with `ldap://` and its port should be either the default LDAP port (389) or the default Global Catalog port (3268) in `nginx/nginx_ldap.conf` -* **LDAPS** - - `NGINX_LDAP_TLS_STUNNEL` set to `false` in [`docker-compose.yml`](#DockerComposeYml) - - `url` should begin with `ldaps://` and its port should be either the default LDAPS port (636) or the default LDAPS Global Catalog port (3269) in `nginx/nginx_ldap.conf` -* **Unencrypted** (clear text) (***not recommended***) - - `NGINX_LDAP_TLS_STUNNEL` set to `false` in [`docker-compose.yml`](#DockerComposeYml) - - `url` should begin with `ldap://` and its port should be either the default LDAP port (389) or the default Global Catalog port (3268) in `nginx/nginx_ldap.conf` - -For encrypted connections (whether using **StartTLS** or **LDAPS**), Malcolm will require and verify certificates when one or more trusted CA certificate files are placed in the `nginx/ca-trust/` directory. Otherwise, any certificate presented by the domain server will be accepted. - -### TLS certificates - -When you [set up authentication](#AuthSetup) for Malcolm a set of unique [self-signed](https://en.wikipedia.org/wiki/Self-signed_certificate) TLS certificates are created which are used to secure the connection between clients (e.g., your web browser) and Malcolm's browser-based interface. This is adequate for most Malcolm instances as they are often run locally or on internal networks, although your browser will most likely require you to add a security exception for the certificate the first time you connect to Malcolm. - -Another option is to generate your own certificates (or have them issued to you) and have them placed in the `nginx/certs/` directory. The certificate and key file should be named `cert.pem` and `key.pem`, respectively. - -A third possibility is to use a third-party reverse proxy (e.g., [Traefik](https://doc.traefik.io/traefik/) or [Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy)) to handle the issuance of the certificates for you and to broker the connections between clients and Malcolm. Reverse proxies such as these often implement the [ACME](https://datatracker.ietf.org/doc/html/rfc8555) protocol for domain name authentication and can be used to request certificates from certificate authorities like [Let's Encrypt](https://letsencrypt.org/how-it-works/). In this configuration, the reverse proxy will be encrypting the connections instead of Malcolm, so you'll need to set the `NGINX_SSL` environment variable to `false` in [`docker-compose.yml`](#DockerComposeYml) (or answer `no` to the "Require encrypted HTTPS connections?" question posed by `install.py`). If you are setting `NGINX_SSL` to `false`, **make sure** you understand what you are doing and ensure that external connections cannot reach ports over which Malcolm will be communicating without encryption, including verifying your local firewall configuration. - -### Starting Malcolm - -[Docker compose](https://docs.docker.com/compose/) is used to coordinate running the Docker containers. To start Malcolm, navigate to the directory containing `docker-compose.yml` and run: -``` -$ ./scripts/start -``` -This will create the containers' virtual network and instantiate them, then leave them running in the background. The Malcolm containers may take a several minutes to start up completely. To follow the debug output for an already-running Malcolm instance, run: -``` -$ ./scripts/logs -``` -You can also use `docker stats` to monitor the resource utilization of running containers. - -### Stopping and restarting Malcolm - -You can run `./scripts/stop` to stop the docker containers and remove their virtual network. Alternatively, `./scripts/restart` will restart an instance of Malcolm. Because the data on disk is stored on the host in docker volumes, doing these operations will not result in loss of data. - -Malcolm can be configured to be automatically restarted when the Docker system daemon restart (for example, on system reboot). This behavior depends on the [value](https://docs.docker.com/config/containers/start-containers-automatically/) of the [`restart:`](https://docs.docker.com/compose/compose-file/#restart) setting for each service in the `docker-compose.yml` file. This value can be set by running [`./scripts/install.py --configure`](#ConfigAndTuning) and answering "yes" to "`Restart Malcolm upon system or Docker daemon restart?`." - -### Clearing Malcolm's data - -Run `./scripts/wipe` to stop the Malcolm instance and wipe its OpenSearch database (**including** [index snapshots and management policies](#IndexManagement) and [alerting configuration](#Alerting)). - -### Temporary read-only interface - -To temporarily set the Malcolm user interaces into a read-only configuration, run the following commands from the Malcolm installation directory. - -First, to configure [Nginx] to disable access to the upload and other interfaces for changing Malcolm settings, and to deny HTTP methods other than `GET` and `POST`: - -``` -docker-compose exec nginx-proxy bash -c "cp /etc/nginx/nginx_readonly.conf /etc/nginx/nginx.conf && nginx -s reload" -``` - -Second, to set the existing OpenSearch data store to read-only: - -``` -docker-compose exec dashboards-helper /data/opensearch_read_only.py -i _cluster -``` - -These commands must be re-run every time you restart Malcolm. - -Note that after you run these commands you may see an increase of error messages in the Malcolm containers' output as various background processes will fail due to the read-only nature of the indices. Additionally, some features such as Arkime's [Hunt](#ArkimeHunt) and [building your own visualizations and dashboards](#BuildDashboard) in OpenSearch Dashboards will not function correctly in read-only mode. - -## Capture file and log archive upload - -Malcolm serves a web browser-based upload form for uploading PCAP files and Zeek logs at [https://localhost/upload/](https://localhost/upload/) if you are connecting locally. - -![Capture File and Log Archive Upload](./docs/images/screenshots/malcolm_upload.png) - -Additionally, there is a writable `files` directory on an SFTP server served on port 8022 (e.g., `sftp://USERNAME@localhost:8022/files/` if you are connecting locally). - -The types of files supported are: - -* PCAP files (of mime type `application/vnd.tcpdump.pcap` or `application/x-pcapng`) - - PCAPNG files are *partially* supported: Zeek is able to process PCAPNG files, but not all of Arkime's packet examination features work correctly -* Zeek logs in archive files (`application/gzip`, `application/x-gzip`, `application/x-7z-compressed`, `application/x-bzip2`, `application/x-cpio`, `application/x-lzip`, `application/x-lzma`, `application/x-rar-compressed`, `application/x-tar`, `application/x-xz`, or `application/zip`) - - where the Zeek logs are found in the internal directory structure in the archive file does not matter - -Files uploaded via these methods are monitored and moved automatically to other directories for processing to begin, generally within one minute of completion of the upload. - -### Tagging - -In addition to be processed for uploading, Malcolm events will be tagged according to the components of the filenames of the PCAP files or Zeek log archives files from which the events were parsed. For example, records created from a PCAP file named `ACME_Scada_VLAN10.pcap` would be tagged with `ACME`, `Scada`, and `VLAN10`. Tags are extracted from filenames by splitting on the characters "," (comma), "-" (dash), and "_" (underscore). These tags are viewable and searchable (via the `tags` field) in Arkime and OpenSearch Dashboards. This behavior can be changed by modifying the `AUTO_TAG` [environment variable in `docker-compose.yml`](#DockerComposeYml). - -Tags may also be specified manually with the [browser-based upload form](#Upload). - -### Processing uploaded PCAPs with Zeek and Suricata - -The **Analyze with Zeek** and **Analyze with Suricata** checkboxes may be used when uploading PCAP files to cause them to be analyzed by Zeek and Suricata, respectively. This is functionally equivalent to the `ZEEK_AUTO_ANALYZE_PCAP_FILES` and `SURICATA_AUTO_ANALYZE_PCAP_FILES` environment variables [described above](#DockerComposeYml), only on a per-upload basis. Zeek can also automatically carve out files from file transfers; see [Automatic file extraction and scanning](#ZeekFileExtraction) for more details. - -## Live analysis - -### Using a network sensor appliance - -A dedicated network sensor appliance is the recommended method for capturing and analyzing live network traffic when performance and throughput is of utmost importance. [Hedgehog Linux](./sensor-iso/README.md) is a custom Debian-based operating system built to: - -* monitor network interfaces -* capture packets to PCAP files -* detect file transfers in network traffic and extract and scan those files for threats -* generate and forward Zeek and Suricata logs, Arkime sessions, and other information to [Malcolm](https://github.com/idaholab/Malcolm) - -Please see the [Hedgehog Linux README](./sensor-iso/README.md) for more information. - -### Monitoring local network interfaces - -Malcolm's `pcap-capture`, `suricata-live` and `zeek-live` containers can monitor one or more local network interfaces, specified by the `PCAP_IFACE` environment variable in [`docker-compose.yml`](#DockerComposeYml). These containers are started with additional privileges (`IPC_LOCK`, `NET_ADMIN`, `NET_RAW`, and `SYS_ADMIN`) to allow opening network interfaces in promiscuous mode for capture. - -The instances of Zeek and Suricata (in the `suricata-live` and `zeek-live` containers when the `SURICATA_LIVE_CAPTURE` and `ZEEK_LIVE_CAPTURE` environment variables in [`docker-compose.yml`](#DockerComposeYml) are set to `true`, respectively) analyze traffic on-the-fly and generate log files containing network session metadata. These log files are in turn scanned by Filebeat and forwarded to Logstash for enrichment and indexing into the OpenSearch document store. - -In contrast, the `pcap-capture` container buffers traffic to PCAP files and periodically rotates these files for processing (by Arkime's `capture` utlity in the `arkime` container) according to the thresholds defined by the `PCAP_ROTATE_MEGABYTES` and `PCAP_ROTATE_MINUTES` environment variables in [`docker-compose.yml`](#DockerComposeYml). If for some reason (e.g., a low resources environment) you also want Zeek and Suricata to process these intermediate PCAP files rather than monitoring the network interfaces directly, you can set `SURICATA_ROTATED_PCAP`/`ZEEK_ROTATED_PCAP` to `true` and `SURICATA_LIVE_CAPTURE`/`ZEEK_LIVE_CAPTURE` to false. - -These various options for monitoring traffic on local network interfaces can also be configured by running [`./scripts/install.py --configure`](#ConfigAndTuning). - -Note that currently Microsoft Windows and Apple macOS platforms run Docker inside of a virtualized environment. Live traffic capture and analysis on those platforms would require additional configuration of virtual interfaces and port forwarding in Docker which is outside of the scope of this document. - -### Manually forwarding logs from an external source - -Malcolm's Logstash instance can also be configured to accept logs from a [remote forwarder](https://www.elastic.co/products/beats/filebeat) by running [`./scripts/install.py --configure`](#ConfigAndTuning) and answering "yes" to "`Expose Logstash port to external hosts?`." Enabling encrypted transport of these logs files is discussed in [Configure authentication](#AuthSetup) and the description of the `BEATS_SSL` environment variable in the [`docker-compose.yml`](#DockerComposeYml) file. - -Configuring Filebeat to forward Zeek logs to Malcolm might look something like this example [`filebeat.yml`](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-reference-yml.html): -``` -filebeat.inputs: -- type: log - paths: - - /var/zeek/*.log - fields_under_root: true - compression_level: 0 - exclude_lines: ['^\s*#'] - scan_frequency: 10s - clean_inactive: 180m - ignore_older: 120m - close_inactive: 90m - close_renamed: true - close_removed: true - close_eof: false - clean_renamed: true - clean_removed: true - -output.logstash: - hosts: ["192.0.2.123:5044"] - ssl.enabled: true - ssl.certificate_authorities: ["/foo/bar/ca.crt"] - ssl.certificate: "/foo/bar/client.crt" - ssl.key: "/foo/bar/client.key" - ssl.supported_protocols: "TLSv1.2" - ssl.verification_mode: "none" -``` - -## Arkime - -The Arkime interface will be accessible over HTTPS on port 443 at the docker hosts IP address (e.g., [https://localhost](https://localhost) if you are connecting locally). - -### Zeek log integration - -A stock installation of Arkime extracts all of its network connection ("session") metadata ("SPI" or "Session Profile Information") from full packet capture artifacts (PCAP files). Zeek (formerly Bro) generates similar session metadata, linking network events to sessions via a connection UID. Malcolm aims to facilitate analysis of Zeek logs by mapping values from Zeek logs to the Arkime session database schema for equivalent fields, and by creating new "native" Arkime database fields for all the other Zeek log values for which there is not currently an equivalent in Arkime: - -![Zeek log session record](./docs/images/screenshots/arkime_session_zeek.png) - -In this way, when full packet capture is an option, analysis of PCAP files can be enhanced by the additional information Zeek provides. When full packet capture is not an option, similar analysis can still be performed using the same interfaces and processes using the Zeek logs alone. - -A few values of particular mention include **Data Source** (`event.provider` in OpenSearch), which can be used to distinguish from among the sources of the network traffic metadata record (e.g., `zeek` for Zeek logs and `arkime` for Arkime sessions); and, **Log Type** (`event.dataset` in OpenSearch), which corresponds to the kind of Zeek `.log` file from which the record was created. In other words, a search could be restricted to records from `conn.log` by searching `event.provider == zeek && event.dataset == conn`, or restricted to records from `weird.log` by searching `event.provider == zeek && event.dataset == weird`. - -Click the icon of the owl **🦉** in the upper-left hand corner of to access the Arkime usage documentation (accessible at [https://localhost/help](https://localhost/help) if you are connecting locally), click the **Fields** label in the navigation pane, then search for `zeek` to see a list of the other Zeek log types and fields available to Malcolm. - -![Zeek fields](./docs/images/screenshots/arkime_help_fields.png) - -The values of records created from Zeek logs can be expanded and viewed like any native Arkime session by clicking the plus **➕** icon to the left of the record in the Sessions view. However, note that when dealing with these Zeek records the full packet contents are not available, so buttons dealing with viewing and exporting PCAP information will not behave as they would for records from PCAP files. Other than that, Zeek records and their values are usable in Malcolm just like native PCAP session records. - -#### Correlating Zeek logs and Arkime sessions - -The Arkime interface displays both Zeek logs and Arkime sessions alongside each other. Using fields common to both data sources, one can [craft queries](#SearchCheatSheet) to filter results matching desired criteria. - -A few fields of particular mention that help limit returned results to those Zeek logs and Arkime session records generated from the same network connection are [Community ID](https://github.com/corelight/community-id-spec) (`network.community_id`) and Zeek's [connection UID](https://docs.zeek.org/en/stable/examples/logs/#using-uids) (`zeek.uid`), which Malcolm maps to both Arkime's `rootId` field and the [ECS](https://www.elastic.co/guide/en/ecs/current/ecs-event.html#field-event-id) `event.id` field. - -Community ID is specification for standard flow hashing [published by Corelight](https://github.com/corelight/community-id-spec) with the intent of making it easier to pivot from one dataset (e.g., Arkime sessions) to another (e.g., Zeek `conn.log` entries). In Malcolm both Arkime and [Zeek](https://github.com/corelight/zeek-community-id) populate this value, which makes it possible to filter for a specific network connection and see both data sources' results for that connection. - -The `rootId` field is used by Arkime to link session records together when a particular session has too many packets to be represented by a single session. When normalizing Zeek logs to Arkime's schema, Malcolm piggybacks on `rootId` to store Zeek's [connection UID](https://docs.zeek.org/en/stable/examples/logs/#using-uids) to crossreference entries across Zeek log types. The connection UID is also stored in `zeek.uid`. - -Filtering on community ID OR'ed with zeek UID (e.g., `network.community_id == "1:r7tGG//fXP1P0+BXH3zXETCtEFI=" || rootId == "CQcoro2z6adgtGlk42"`) is an effective way to see both the Arkime sessions and Zeek logs generated by a particular network connection. - -![Correlating Arkime sessions and Zeek logs](./docs/images/screenshots/arkime_correlate_communityid_uid.png) - -### Help - -Click the icon of the owl 🦉 in the upper-left hand corner of to access the Arkime usage documentation (accessible at [https://localhost/help](https://localhost/help) if you are connecting locally), which includes such topics as [search syntax](https://localhost/help#search), the [Sessions view](https://localhost/help#sessions), [SPIView](https://localhost/help#spiview), [SPIGraph](https://localhost/help#spigraph), and the [Connections](https://localhost/help#connections) graph. - -### Sessions - -The **Sessions** view provides low-level details of the sessions being investigated, whether they be Arkime sessions created from PCAP files or [Zeek logs mapped](#ArkimeZeek) to the Arkime session database schema. - -![Arkime's Sessions view](./docs/images/screenshots/arkime_sessions.png) - -The **Sessions** view contains many controls for filtering the sessions displayed from all sessions down to sessions of interest: - -* [search bar](https://localhost/help#search): Indicated by the magnifying glass **🔍** icon, the search bar allows defining filters on session/log metadata -* [time bounding](https://localhost/help#timebounding) controls: The **🕘**, **Start**, **End**, **Bounding**, and **Interval** fields, and the **date histogram** can be used to visually zoom and pan the time range being examined. -* search button: The **Search** button re-runs the sessions query with the filters currently specified. -* views button: Indicated by the eyeball **👁** icon, views allow overlaying additional previously-specified filters onto the current sessions filters. For convenience, Malcolm provides several Arkime preconfigured views including filtering on the `event.dataset` field. - -![Malcolm views](./docs/images/screenshots/arkime_apply_view.png) - -* map: A global map can be expanded by clicking the globe **🌎** icon. This allows filtering sessions by IP-based geolocation when possible. - -Some of these filter controls are also available on other Arkime pages (such as SPIView, SPIGraph, Connections, and Hunt). - -The number of sessions displayed per page, as well as the page currently displayed, can be specified using the paging controls underneath the time bounding controls. - -The sessions table is displayed below the filter controls. This table contains the sessions/logs matching the specified filters. - -To the left of the column headers are two buttons. The **Toggle visible columns** button, indicated by a grid **⊞** icon, allows toggling which columns are displayed in the sessions table. The **Save or load custom column configuration** button, indicated by a columns **◫** icon, allows saving the current displayed columns or loading previously-saved configurations. This is useful for customizing which columns are displayed when investigating different types of traffic. Column headers can also be clicked to sort the results in the table, and column widths may be adjusted by dragging the separators between column headers. - -Details for individual sessions/logs can be expanded by clicking the plus **➕** icon on the left of each row. Each row may contain multiple sections and controls, depending on whether the row represents a Arkime session or a [Zeek log](#ArkimeZeek). Clicking the field names and values in the details sections allows additional filters to be specified or summary lists of unique values to be exported. - -When viewing Arkime session details (ie., a session generated from a PCAP file), an additional packets section will be visible underneath the metadata sections. When the details of a session of this type are expanded, Arkime will read the packet(s) comprising the session for display here. Various controls can be used to adjust how the packet is displayed (enabling **natural** decoding and enabling **Show Images & Files** may produce visually pleasing results), and other options (including PCAP download, carving images and files, applying decoding filters, and examining payloads in [CyberChef](https://github.com/gchq/CyberChef)) are available. - -See also Arkime's usage documentation for more information on the [Sessions view](https://localhost/help#sessions). - -#### PCAP Export - -Clicking the down arrow **▼** icon to the far right of the search bar presents a list of actions including **PCAP Export** (see Arkime's [sessions help](https://localhost/help#sessions) for information on the other actions). When full PCAP sessions are displayed, the **PCAP Export** feature allows you to create a new PCAP file from the matching Arkime sessions, including controls for which sessions are included (open items, visible items, or all matching items) and whether or not to include linked segments. Click **Export PCAP** button to generate the PCAP, after which you'll be presented with a browser download dialog to save or open the file. Note that depending on the scope of the filters specified this might take a long time (or, possibly even time out). - -![Export PCAP](./docs/images/screenshots/arkime_export_pcap.png) - -See the [issues](#Issues) section of this document for an error that can occur using this feature when Zeek log sessions are displayed.View - -### SPIView - -Arkime's **SPI** (**S**ession **P**rofile **I**nformation) **View** provides a quick and easy-to-use interface for exploring session/log metrics. The SPIView page lists categories for general session metrics (e.g., protocol, source and destination IP addresses, sort and destination ports, etc.) as well as for all of various types of network traffic understood by Malcolm. These categories can be expanded and the top *n* values displayed, along with each value's cardinality, for the fields of interest they contain. - -![Arkime's SPIView](./docs/images/screenshots/arkime_spiview.png) - -Click the the plus **➕** icon to the right of a category to expand it. The values for specific fields are displayed by clicking the field description in the field list underneath the category name. The list of field names can be filtered by typing part of the field name in the *Search for fields to display in this category* text input. The **Load All** and **Unload All** buttons can be used to toggle display of all of the fields belonging to that category. Once displayed, a field's name or one of its values may be clicked to provide further actions for filtering or displaying that field or its values. Of particular interest may be the **Open [fieldname] SPI Graph** option when clicking on a field's name. This will open a new tab with the SPI Graph ([see below](#ArkimeSPIGraph)) populated with the field's top values. - -Note that because the SPIView page can potentially run many queries, SPIView limits the search domain to seven days (in other words, seven indices, as each index represents one day's worth of data). When using SPIView, you will have best results if you limit your search time frame to less than or equal to seven days. This limit can be adjusted by editing the `spiDataMaxIndices` setting in [config.ini](./etc/arkime/config.ini) and rebuilding the `malcolmnetsec/arkime` docker container. - -See also Arkime's usage documentation for more information on [SPIView](https://localhost/help#spiview). - -### SPIGraph - -Arkime's **SPI** (**S**ession **P**rofile **I**nformation) **Graph** visualizes the occurrence of some field's top *n* values over time, and (optionally) geographically. This is particularly useful for identifying trends in a particular type of communication over time: traffic using a particular protocol when seen sparsely at regular intervals on that protocol's date histogram in the SPIGraph may indicate a connection check, polling, or beaconing (for example, see the `llmnr` protocol in the screenshot below). - -![Arkime's SPIGraph](./docs/images/screenshots/arkime_spigraph.png) - -Controls can be found underneath the time bounding controls for selecting the field of interest, the number of elements to be displayed, the sort order, and a periodic refresh of the data. - -See also Arkime's usage documentation for more information on [SPIGraph](https://localhost/help#spigraph). - -### Connections - -The **Connections** page presents network communications via a force-directed graph, making it easy to visualize logical relationships between network hosts. - -![Arkime's Connections graph](./docs/images/screenshots/arkime_connections.png) - -Controls are available for specifying the query size (where smaller values will execute more quickly but may only contain an incomplete representation of the top *n* sessions, and larger values may take longer to execute but will be more complete), which fields to use as the source and destination for node values, a minimum connections threshold, and the method for determining the "weight" of the link between two nodes. As is the case with most other visualizations in Arkime, the graph is interactive: clicking on a node or the link between two nodes can be used to modify query filters, and the nodes themselves may be repositioned by dragging and dropping them. A node's color indicates whether it communicated as a source/originator, a destination/responder, or both. - -While the default source and destination fields are *Src IP* and *Dst IP:Dst Port*, the Connections view is able to use any combination of fields. For example: - -* *Src OUI* and *Dst OUI* (hardware manufacturers) -* *Src IP* and *Protocols* -* *Originating Network Segment* and *Responding Network Segment* (see [CIDR subnet to network segment name mapping](#SegmentNaming)) -* *Originating GeoIP City* and *Responding GeoIP City* - -or any other combination of these or other fields. - -See also Arkime's usage documentation for more information on the [Connections graph](https://localhost/help#connections). - -### Hunt - -Arkime's **Hunt** feature allows an analyst to search within the packets themselves (including payload data) rather than simply searching the session metadata. The search string may be specified using ASCII (with or without case sensitivity), hex codes, or regular expressions. Once a hunt job is complete, matching sessions can be viewed in the [Sessions](#ArkimeSessions) view. - -Clicking the **Create a packet search job** on the Hunt page will allow you to specify the following parameters for a new hunt job: - -* a packet search job **name** -* a **maximum number of packets** to examine per session -* the **search string** and its format (*ascii*, *ascii (case sensitive)*, *hex*, *regex*, or *hex regex*) -* whether to search **source packets**, **destination packets**, or both -* whether to search **raw** or **reassembled** packets - -Click the **➕ Create** button to begin the search. Arkime will scan the source PCAP files from which the sessions were created according to the search criteria. Note that whatever filters were specified when the hunt job is executed will apply to the hunt job as well; the number of sessions matching the current filters will be displayed above the hunt job parameters with text like "ⓘ Creating a new packet search job will search the packets of # sessions." - -![Hunt creation](./docs/images/screenshots/arkime_hunt_creation.png) - -Once a hunt job is submitted, it will be assigned a unique hunt ID (a long unique string of characters like `yuBHAGsBdljYmwGkbEMm`) and its progress will be updated periodically in the **Hunt Job Queue** with the execution percent complete, the number of matches found so far, and the other parameters with which the job was submitted. More details for the hunt job can be viewed by expanding its row with the plus **➕** icon on the left. - -![Hunt completed](./docs/images/screenshots/arkime_hunt_finished.png) - -Once the hunt job is complete (and a minute or so has passed, as the `huntId` must be added to the matching session records in the database), click the folder **📂** icon on the right side of the hunt job row to open a new [Sessions](#ArkimeSessions) tab with the search bar prepopulated to filter to sessions with packets matching the search criteria. - -![Hunt result sessions](./docs/images/screenshots/arkime_hunt_sessions.png) - -From this list of filtered sessions you can expand session details and explore packet payloads which matched the hunt search criteria. - -The hunt feature is available only for sessions created from full packet capture data, not Zeek logs. This being the case, it is a good idea to click the eyeball **👁** icon and select the **Arkime Sessions** view to exclude Zeek logs from candidate sessions prior to using the hunt feature. - -See also Arkime's usage documentation for more information on the [hunt feature](https://localhost/help#hunt). - -### Statistics - -Arkime provides several other reports which show information about the state of Arkime and the underlying OpenSearch database. - -The **Files** list displays a list of PCAP files processed by Arkime, the date and time of the earliest packet in each file, and the file size: - -![Arkime's Files list](./docs/images/screenshots/arkime_files.png) - -The **ES Indices** list (available under the **Stats** page) lists the OpenSearch indices within which log data is contained: - -![Arkime's ES indices list](./docs/images/screenshots/arkime_es_stats.png) - -The **History** view provides a historical list of queries issues to Arkime and the details of those queries: - -![Arkime's History view](./docs/images/screenshots/arkime_history.png) - -See also Arkime's usage documentation for more information on the [Files list](https://localhost/help#files), [statistics](https://localhost/help#files), and [history](https://localhost/help#history). - -### Settings - -#### General settings - -The **Settings** page can be used to tweak Arkime preferences, defined additional custom views and column configurations, tweak the color theme, and more. - -See Arkime's usage documentation for more information on [settings](https://localhost/help#settings). - -![Arkime general settings](./docs/images/screenshots/arkime_general_settings.png) - -![Arkime custom view management](./docs/images/screenshots/arkime_view_settings.png) - -## OpenSearch Dashboards - -While Arkime provides very nice visualizations, especially for network traffic, [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) (an open source general-purpose data visualization tool for OpenSearch) can be used to create custom visualizations (tables, charts, graphs, dashboards, etc.) using the same data. - -The OpenSearch Dashboards container can be accessed at [https://localhost/dashboards/](https://localhost/dashboards/) if you are connecting locally. Several preconfigured dashboards for Zeek logs are included in Malcolm's OpenSearch Dashboards configuration. - -OpenSearch Dashboards has several components for data searching and visualization: - -### Discover - -The **Discover** view enables you to view events on a record-by-record basis (similar to a *session* record in Arkime or an individual line from a Zeek log). See the official [Kibana User Guide](https://www.elastic.co/guide/en/kibana/7.10/index.html) (OpenSearch Dashboards is an open-source fork of Kibana, which is no longer open-source software) for information on using the Discover view: - -* [Discover](https://www.elastic.co/guide/en/kibana/7.10/discover.html) -* [Searching Your Data](https://www.elastic.co/guide/en/kibana/7.10/search.html) - -#### Screenshots - -![Discover view](./docs/images/screenshots/dashboards_discover.png) - -![Viewing the details of a session in Discover](./docs/images/screenshots/dashboards_discover_table.png) - -![Filtering by tags to display only sessions with public IP addresses](./docs/images/screenshots/dashboards_add_filter.png) - -![Changing the fields displayed in Discover](./docs/images/screenshots/dashboards_fields_list.png) - -![Opening a previously-saved search](./docs/images/screenshots/dashboards_open_search.png) - -### Visualizations and dashboards - -#### Prebuilt visualizations and dashboards - -Malcolm comes with dozens of prebuilt visualizations and dashboards for the network traffic represented by each of the Zeek log types. Click **Dashboard** to see a list of these dashboards. As is the case with all OpenSearch Dashboards visualizations, all of the charts, graphs, maps, and tables are interactive and can be clicked on to narrow or expand the scope of the data you are investigating. Similarly, click **Visualize** to explore the prebuilt visualizations used to build the dashboards. - -Many of Malcolm's prebuilt visualizations for Zeek logs were originally inspired by the excellent [Kibana Dashboards](https://github.com/Security-Onion-Solutions/securityonion-elastic/tree/master/kibana/dashboards) that are part of [Security Onion](https://securityonion.net/). - -##### Screenshots - -![The Security Overview highlights security-related network events](./docs/images/screenshots/dashboards_security_overview.png) - -![The ICS/IoT Security Overview dashboard displays information about ICS and IoT network traffic](./docs/images/screenshots/dashboards_ics_iot_security_overview.png) - -![The Connections dashboard displays information about the "top talkers" across all types of sessions](./docs/images/screenshots/dashboards_connections.png) - -![The HTTP dashboard displays important details about HTTP traffic](./docs/images/screenshots/dashboards_http.png) - -![There are several Connections visualizations using locations from GeoIP lookups](./docs/images/screenshots/dashboards_latlon_map.png) - -![OpenSearch Dashboards includes both coordinate and region map types](./docs/images/screenshots/dashboards_region_map.png) - -![The Suricata Alerts dashboard highlights traffic which matched Suricata signatures](./docs/images/screenshots/dashboards_suricata_alerts.png) - -![The Zeek Notices dashboard highlights things which Zeek determine are potentially bad](./docs/images/screenshots/dashboards_notices.png) - -![The Zeek Signatures dashboard displays signature hits, such as antivirus hits on files extracted from network traffic](./docs/images/screenshots/dashboards_signatures.png) - -![The Software dashboard displays the type, name, and version of software seen communicating on the network](./docs/images/screenshots/dashboards_software.png) - -![The PE (portable executables) dashboard displays information about executable files transferred over the network](./docs/images/screenshots/dashboards_portable_executables.png) - -![The SMTP dashboard highlights details about SMTP traffic](./docs/images/screenshots/dashboards_smtp.png) - -![The SSL dashboard displays information about SSL versions, certificates, and TLS JA3 fingerprints](./docs/images/screenshots/dashboards_ssl.png) - -![The files dashboard displays metrics about the files transferred over the network](./docs/images/screenshots/dashboards_files_source.png) - -![This dashboard provides insight into DNP3 (Distributed Network Protocol), a protocol used commonly in electric and water utilities](./docs/images/screenshots/dashboards_dnp3.png) - -![Modbus is a standard protocol found in many industrial control systems (ICS)](./docs/images/screenshots/dashboards_modbus.png) - -![BACnet is a communications protocol for Building Automation and Control (BAC) networks](./docs/images/screenshots/dashboards_bacnet.png) - -![EtherCAT is an Ethernet-based fieldbus system](./docs/images/screenshots/dashboards_ecat.png) - -![EtherNet/IP is an industrial network protocol that adapts the Common Industrial Protocol to standard Ethernet](./docs/images/screenshots/dashboards_ethernetip.png) - -![PROFINET is an industry technical standard for data communication over Industrial Ethernet](./docs/images/screenshots/dashboards_profinet.png) - -![S7comm is a Siemens proprietary protocol that runs between programmable logic controllers (PLCs) of the Siemens family](./docs/images/screenshots/dashboards_s7comm.png) - -#### Building your own visualizations and dashboards - -See the official [Kibana User Guide](https://www.elastic.co/guide/en/kibana/7.10/index.html) and [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) (OpenSearch Dashboards is an open-source fork of Kibana, which is no longer open-source software) documentation for information on creating your own visualizations and dashboards: - -* [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) -* [Kibana Dashboards](https://www.elastic.co/guide/en/kibana/7.10/dashboard.html) -* [TimeLine](https://www.elastic.co/guide/en/kibana/7.12/timelion.html) - -##### Screenshots - -![OpenSearch dashboards boasts many types of visualizations for displaying your data](./docs/images/screenshots/dashboards_new_visualization.png) - -## Search Queries in Arkime and OpenSearch Dashboards - -OpenSearch Dashboards supports two query syntaxes: the legacy [Lucene](https://www.elastic.co/guide/en/kibana/current/lucene-query.html) syntax and [Dashboards Query Language (DQL)](https://opensearch.org/docs/1.2/dashboards/dql/), both of which are somewhat different than Arkime's query syntax (see the help at [https://localhost/help#search](https://localhost/help#search) if you are connecting locally). The Arkime interface is for searching and visualizing both Arkime sessions and Zeek logs. The prebuilt dashboards in the OpenSearch Dashboards interface are for searching and visualizing Zeek logs, but will not include Arkime sessions. Here are some common patterns used in building search query strings for Arkime and OpenSearch Dashboards, respectively. See the links provided for further documentation. - -| | [Arkime Search String](https://localhost/help#search) | [OpenSearch Dashboards Search String (Lucene)](https://www.elastic.co/guide/en/kibana/current/lucene-query.html) | [OpenSearch Dashboards Search String (DQL)](https://www.elastic.co/guide/en/kibana/current/kuery-query.html)| -|---|:---:|:---:|:---:| -| Field exists |`event.dataset == EXISTS!`|`_exists_:event.dataset`|`event.dataset:*`| -| Field does not exist |`event.dataset != EXISTS!`|`NOT _exists_:event.dataset`|`NOT event.dataset:*`| -| Field matches a value |`port.dst == 22`|`destination.port:22`|`destination.port:22`| -| Field does not match a value |`port.dst != 22`|`NOT destination.port:22`|`NOT destination.port:22`| -| Field matches at least one of a list of values |`tags == [foo, bar]`|`tags:(foo OR bar)`|`tags:(foo or bar)`| -| Field range (inclusive) |`http.statuscode >= 200 && http.statuscode <= 300`|`http.statuscode:[200 TO 300]`|`http.statuscode >= 200 and http.statuscode <= 300`| -| Field range (exclusive) |`http.statuscode > 200 && http.statuscode < 300`|`http.statuscode:{200 TO 300}`|`http.statuscode > 200 and http.statuscode < 300`| -| Field range (mixed exclusivity) |`http.statuscode >= 200 && http.statuscode < 300`|`http.statuscode:[200 TO 300}`|`http.statuscode >= 200 and http.statuscode < 300`| -| Match all search terms (AND) |`(tags == [foo, bar]) && (http.statuscode == 401)`|`tags:(foo OR bar) AND http.statuscode:401`|`tags:(foo or bar) and http.statuscode:401`| -| Match any search terms (OR) |`(zeek.ftp.password == EXISTS!) || (zeek.http.password == EXISTS!) || (related.user == "anonymous")`|`_exists_:zeek.ftp.password OR _exists_:zeek.http.password OR related.user:"anonymous"`|`zeek.ftp.password:* or zeek.http.password:* or related.user:"anonymous"`| -| Global string search (anywhere in the document) |all Arkime search expressions are field-based|`microsoft`|`microsoft`| -| Wildcards|`host.dns == "*micro?oft*"` (`?` for single character, `*` for any characters)|`dns.host:*micro?oft*` (`?` for single character, `*` for any characters)|`dns.host:*micro*ft*` (`*` for any characters)| -| Regex |`host.http == /.*www\.f.*k\.com.*/`|`zeek.http.host:/.*www\.f.*k\.com.*/`|DQL does not support regex| -| IPv4 values |`ip == 0.0.0.0/0`|`source.ip:"0.0.0.0/0" OR destination.ip:"0.0.0.0/0"`|`source.ip:"0.0.0.0/0" OR destination.ip:"0.0.0.0/0"`| -| IPv6 values |`(ip.src == EXISTS! || ip.dst == EXISTS!) && (ip != 0.0.0.0/0)`|`(_exists_:source.ip AND NOT source.ip:"0.0.0.0/0") OR (_exists_:destination.ip AND NOT destination.ip:"0.0.0.0/0")`|`(source.ip:* and not source.ip:"0.0.0.0/0") or (destination.ip:* and not destination.ip:"0.0.0.0/0")`| -| GeoIP information available |`country == EXISTS!`|`_exists_:destination.geo OR _exists_:source.geo`|`destination.geo:* or source.geo:*`| -| Zeek log type |`event.dataset == notice`|`event.dataset:notice`|`event.dataset:notice`| -| IP CIDR Subnets |`ip.src == 172.16.0.0/12`|`source.ip:"172.16.0.0/12"`|`source.ip:"172.16.0.0/12"`| -| Search time frame |Use Arkime time bounding controls under the search bar|Use OpenSearch Dashboards time range controls in the upper right-hand corner|Use OpenSearch Dashboards time range controls in the upper right-hand corner| - -When building complex queries, it is **strongly recommended** that you enclose search terms and expressions in parentheses to control order of operations. - -As Zeek logs are ingested, Malcolm parses and normalizes the logs' fields to match Arkime's underlying OpenSearch schema. A complete list of these fields can be found in the Arkime help (accessible at [https://localhost/help#fields](https://localhost/help#fields) if you are connecting locally). - -Whenever possible, Zeek fields are mapped to existing corresponding Arkime fields: for example, the `orig_h` field in Zeek is mapped to Arkime's `source.ip` field. The original Zeek fields are also left intact. To complicate the issue, the Arkime interface uses its own aliases to reference those fields: the source IP field is referenced as `ip.src` (Arkime's alias) in Arkime and `source.ip` or `source.ip` in OpenSearch Dashboards. - -The table below shows the mapping of some of these fields. - -| Field Description |Arkime Field Alias(es)|Arkime-mapped Zeek Field(s)|Zeek Field(s)| -|---|:---:|:---:|:---:| -| [Community ID](https://github.com/corelight/community-id-spec) Flow Hash ||`network.community_id`|`network.community_id`| -| Destination IP |`ip.dst`|`destination.ip`|`destination.ip`| -| Destination MAC |`mac.dst`|`destination.mac`|`destination.mac`| -| Destination Port |`port.dst`|`destination.port`|`destination.port`| -| Duration |`session.length`|`length`|`zeek.conn.duration`| -| First Packet Time |`starttime`|`firstPacket`|`zeek.ts`, `@timestamp`| -| IP Protocol |`ip.protocol`|`ipProtocol`|`network.transport`| -| Last Packet Time |`stoptime`|`lastPacket`|| -| MIME Type |`email.bodymagic`, `http.bodymagic`|`http.bodyMagic`|`file.mime_type`, `zeek.files.mime_type`, `zeek.ftp.mime_type`, `zeek.http.orig_mime_types`, `zeek.http.resp_mime_types`, `zeek.irc.dcc_mime_type`| -| Protocol/Service |`protocols`|`protocol`|`network.transport`, `network.protocol`| -| Request Bytes |`databytes.src`, `bytes.src`|`source.bytes`, `client.bytes`|`zeek.conn.orig_bytes`, `zeek.conn.orig_ip_bytes`| -| Request Packets |`packets.src`|`source.packets`|`zeek.conn.orig_pkts`| -| Response Bytes |`databytes.dst`, `bytes.dst`|`destination.bytes`, `server.bytes`|`zeek.conn.resp_bytes`, `zeek.conn.resp_ip_bytes`| -| Response Packets |`packets.dst`|`destination.packets`|`zeek.con.resp_pkts`| -| Source IP |`ip.src`|`source.ip`|`source.ip`| -| Source MAC |`mac.src`|`source.mac`|`source.mac`| -| Source Port |`port.src`|`source.port`|`source.port`| -| Total Bytes |`databytes`, `bytes`|`totDataBytes`, `network.bytes`|| -| Total Packets |`packets`|`network.packets`|| -| Username |`user`|`user`|`related.user`| -| Zeek Connection UID|||`zeek.uid`, `event.id`| -| Zeek File UID |||`zeek.fuid`, `event.id`| -| Zeek Log Type |||`event.dataset`| - -In addition to the fields listed above, Arkime provides several special field aliases for matching any field of a particular type. While these aliases do not exist in OpenSearch Dashboards *per se*, they can be approximated as illustrated below. - -| Matches Any | Arkime Special Field Example | OpenSearch Dashboards/Zeek Equivalent Example | -|---|:---:|:---:| -| IP Address | `ip == 192.168.0.1` | `source.ip:192.168.0.1 OR destination.ip:192.168.0.1` | -| Port | `port == [80, 443, 8080, 8443]` | `source.port:(80 OR 443 OR 8080 OR 8443) OR destination.port:(80 OR 443 OR 8080 OR 8443)` | -| Country (code) | `country == [RU,CN]` | `destination.geo.country_code2:(RU OR CN) OR source.geo.country_code2:(RU OR CN) OR dns.GEO:(RU OR CN)` | -| Country (name) | | `destination.geo.country_name:(Russia OR China) OR source.geo.country_name:(Russia OR China)` | -| ASN | `asn == "*Mozilla*"` | `source.as.full:*Mozilla* OR destination.as.full:*Mozilla* OR dns.ASN:*Mozilla*` | -| Host | `host == www.microsoft.com` | `zeek.http.host:www.microsoft.com (or zeek.dhcp.host_name, zeek.dns.host, zeek.ntlm.host, smb.host, etc.)` | -| Protocol (layers >= 4) | `protocols == tls` | `protocol:tls` | -| User | `user == EXISTS! && user != anonymous` | `_exists_:user AND (NOT user:anonymous)` | - -For details on how to filter both Zeek logs and Arkime session records for a particular connection, see [Correlating Zeek logs and Arkime sessions](#ZeekArkimeFlowCorrelation). - -## Other Malcolm features - -### Automatic file extraction and scanning - -Malcolm can leverage Zeek's knowledge of network protocols to automatically detect file transfers and extract those files from PCAPs as Zeek processes them. This behavior can be enabled globally by modifying the `ZEEK_EXTRACTOR_MODE` [environment variable in `docker-compose.yml`](#DockerComposeYml), or on a per-upload basis for PCAP files uploaded via the [browser-based upload form](#Upload) when **Analyze with Zeek** is selected. - -To specify which files should be extracted, the following values are acceptable in `ZEEK_EXTRACTOR_MODE`: - -* `none`: no file extraction -* `interesting`: extraction of files with mime types of common attack vectors -* `mapped`: extraction of files with recognized mime types -* `known`: extraction of files for which any mime type can be determined -* `all`: extract all files - -Extracted files can be examined through any of the following methods: - -* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, specify the `VTOT_API2_KEY` [environment variable in `docker-compose.yml`](#DockerComposeYml) -* scanning files with [**ClamAV**](https://www.clamav.net/); to enable this method, set the `EXTRACTED_FILE_ENABLE_CLAMAV` [environment variable in `docker-compose.yml`](#DockerComposeYml) to `true` -* scanning files with [**Yara**](https://github.com/VirusTotal/yara); to enable this method, set the `EXTRACTED_FILE_ENABLE_YARA` [environment variable in `docker-compose.yml`](#DockerComposeYml) to `true` -* scanning PE (portable executable) files with [**Capa**](https://github.com/fireeye/capa); to enable this method, set the `EXTRACTED_FILE_ENABLE_CAPA` [environment variable in `docker-compose.yml`](#DockerComposeYml) to `true` - -Files which are flagged via any of these methods will be logged as Zeek `signatures.log` entries, and can be viewed in the **Signatures** dashboard in OpenSearch Dashboards. - -The `EXTRACTED_FILE_PRESERVATION` [environment variable in `docker-compose.yml`](#DockerComposeYml) determines the behavior for preservation of Zeek-extracted files: - -* `quarantined`: preserve only flagged files in `./zeek-logs/extract_files/quarantine` -* `all`: preserve flagged files in `./zeek-logs/extract_files/quarantine` and all other extracted files in `./zeek-logs/extract_files/preserved` -* `none`: preserve no extracted files - -The `EXTRACTED_FILE_HTTP_SERVER_…` [environment variables in `docker-compose.yml`](#DockerComposeYml) configure access to the Zeek-extracted files path through the means of a simple HTTPS directory server. Beware that Zeek-extracted files may contain malware. As such, the files may be optionally encrypted upon download. - -### Automatic host and subnet name assignment - -#### IP/MAC address to hostname mapping via `host-map.txt` - -The `host-map.txt` file in the Malcolm installation directory can be used to define names for network hosts based on IP and/or MAC addresses in Zeek logs. The default empty configuration looks like this: -``` -# IP or MAC address to host name map: -# address|host name|required tag -# -# where: -# address: comma-separated list of IPv4, IPv6, or MAC addresses -# e.g., 172.16.10.41, 02:42:45:dc:a2:96, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 -# -# host name: host name to be assigned when event address(es) match -# -# required tag (optional): only check match and apply host name if the event -# contains this tag -# -``` -Each non-comment line (not beginning with a `#`), defines an address-to-name mapping for a network host. For example: -``` -127.0.0.1,127.0.1.1,::1|localhost| -192.168.10.10|office-laptop.intranet.lan| -06:46:0b:a6:16:bf|serial-host.intranet.lan|testbed -``` -Each line consists of three `|`-separated fields: address(es), hostname, and, optionally, a tag which, if specified, must belong to a log for the matching to occur. - -As Zeek logs are processed into Malcolm's OpenSearch instance, the log's source and destination IP and MAC address fields (`source.ip`, `destination.ip`, `source.mac`, and `destination.mac`, respectively) are compared against the lists of addresses in `host-map.txt`. When a match is found, a new field is added to the log: `source.hostname` or `destination.hostname`, depending on whether the matching address belongs to the originating or responding host. If the third field (the "required tag" field) is specified, a log must also contain that value in its `tags` field in addition to matching the IP or MAC address specified in order for the corresponding `_hostname` field to be added. - -`source.hostname` and `destination.hostname` may each contain multiple values. For example, if both a host's source IP address and source MAC address were matched by two different lines, `source.hostname` would contain the hostname values from both matching lines. - -#### CIDR subnet to network segment name mapping via `cidr-map.txt` - -The `cidr-map.txt` file in the Malcolm installation directory can be used to define names for network segments based on IP addresses in Zeek logs. The default empty configuration looks like this: -``` -# CIDR to network segment format: -# IP(s)|segment name|required tag -# -# where: -# IP(s): comma-separated list of CIDR-formatted network IP addresses -# e.g., 10.0.0.0/8, 169.254.0.0/16, 172.16.10.41 -# -# segment name: segment name to be assigned when event IP address(es) match -# -# required tag (optional): only check match and apply segment name if the event -# contains this tag -# -``` -Each non-comment line (not beginning with a `#`), defines an subnet-to-name mapping for a network host. For example: -``` -192.168.50.0/24,192.168.40.0/24,10.0.0.0/8|corporate| -192.168.100.0/24|control| -192.168.200.0/24|dmz| -172.16.0.0/12|virtualized|testbed -``` -Each line consists of three `|`-separated fields: CIDR-formatted subnet IP range(s), subnet name, and, optionally, a tag which, if specified, must belong to a log for the matching to occur. - -As Zeek logs are processed into Malcolm's OpenSearch instance, the log's source and destination IP address fields (`source.ip` and `destination.ip`, respectively) are compared against the lists of addresses in `cidr-map.txt`. When a match is found, a new field is added to the log: `source.segment` or `destination.segment`, depending on whether the matching address belongs to the originating or responding host. If the third field (the "required tag" field) is specified, a log must also contain that value in its `tags` field in addition to its IP address falling within the subnet specified in order for the corresponding `_segment` field to be added. - -`source.segment` and `destination.segment` may each contain multiple values. For example, if `cidr-map.txt` specifies multiple overlapping subnets on different lines, `source.segment` would contain the hostname values from both matching lines if `source.ip` belonged to both subnets. - -If both `source.segment` and `destination.segment` are added to a log, and if they contain different values, the tag `cross_segment` will be added to the log's `tags` field for convenient identification of cross-segment traffic. This traffic could be easily visualized using Arkime's **Connections** graph, by setting the **Src:** value to **Originating Network Segment** and the **Dst:** value to **Responding Network Segment**: - -![Cross-segment traffic in Connections](./docs/images/screenshots/arkime_connections_segments.png) - -#### Defining hostname and CIDR subnet names interface - -As an alternative to manually editing `cidr-map.txt` and `host-map.txt`, a **Host and Subnet Name Mapping** editor is available at [https://localhost/name-map-ui/](https://localhost/name-map-ui/) if you are connecting locally. Upon loading, the editor is populated from `cidr-map.txt`, `host-map.txt` and `net-map.json`. - -This editor provides the following controls: - -* 🔎 **Search mappings** - narrow the list of visible items using a search filter -* **Type**, **Address**, **Name** and **Tag** *(column headings)* - sort the list of items by clicking a column header -* 📝 *(per item)* - modify the selected item -* 🚫 *(per item)* - remove the selected item -* 🖳 **host** / 🖧 **segment**, **Address**, **Name**, **Tag (optional)** and 💾 - save the item with these values (either adding a new item or updating the item being modified) -* 📥 **Import** - clear the list and replace it with the contents of an uploaded `net-map.json` file -* 📤 **Export** - format and download the list as a `net-map.json` file -* 💾 **Save Mappings** - format and store `net-map.json` in the Malcolm directory (replacing the existing `net-map.json` file) -* 🔁 **Restart Logstash** - restart log ingestion, parsing and enrichment - -![Host and Subnet Name Mapping Editor](./docs/images/screenshots/malcolm_name_map_ui.png) - -#### Applying mapping changes - -When changes are made to either `cidr-map.txt`, `host-map.txt` or `net-map.json`, Malcolm's Logstash container must be restarted. The easiest way to do this is to restart malcolm via `restart` (see [Stopping and restarting Malcolm](#StopAndRestart)) or by clicking the 🔁 **Restart Logstash** button in the [name mapping interface](#NameMapUI) interface. - -Restarting Logstash may take several minutes, after which log ingestion will be resumed. - -### OpenSearch index management - -Malcolm releases prior to v6.2.0 used environment variables to configure OpenSearch [Index State Management](https://opensearch.org/docs/latest/im-plugin/ism/index/) [policies](https://opensearch.org/docs/latest/im-plugin/ism/policies/). - -Since then, OpenSearch Dashboards has developed and released plugins with UIs for [Index State Management](https://opensearch.org/docs/latest/im-plugin/ism/index/) and [Snapshot Management](https://opensearch.org/docs/latest/opensearch/snapshots/sm-dashboards/). Because these plugins provide a more comprehensive and user-friendly interfaces for these features, the old environment variable-based configuration code has been removed from Malcolm, with the exception of the code that uses `OPENSEARCH_INDEX_SIZE_PRUNE_LIMIT` and `OPENSEARCH_INDEX_SIZE_PRUNE_NAME_SORT` which deals with deleting the oldest network session metadata indices when the database exceeds a certain size. - -Note that OpenSearch index state management and snapshot management only deals with disk space consumed by OpenSearch indices: it does not have anything to do with PCAP file storage. The `MANAGE_PCAP_FILES` environment variable in the [`docker-compose.yml`](#DockerComposeYml) file can be used to allow Arkime to prune old PCAP files based on available disk space. - -### Event severity scoring - -As Zeek logs are parsed and enriched prior to indexing, a severity score up to `100` (a higher score indicating a more severe event) can be assigned when one or more of the following conditions are met: - -* cross-segment network traffic (if [network subnets were defined](#HostAndSubnetNaming)) -* connection origination and destination (e.g., inbound, outbound, external, internal) -* traffic to or from sensitive countries - - The comma-separated list of countries (by [ISO 3166-1 alpha-2 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes)) can be customized by setting the `SENSITIVE_COUNTRY_CODES` environment variable in [`docker-compose.yml`](#DockerComposeYml). -* domain names (from DNS queries and SSL server names) with high entropy as calculated by [freq](https://github.com/MarkBaggett/freq) - - The entropy threshold for this condition to trigger can be adjusted by setting the `FREQ_SEVERITY_THRESHOLD` environment variable in [`docker-compose.yml`](#DockerComposeYml). A lower value will only assign severity scores to fewer domain names with higher entropy (e.g., `2.0` for `NQZHTFHRMYMTVBQJE.COM`), while a higher value will assign severity scores to more domain names with lower entropy (e.g., `7.5` for `naturallanguagedomain.example.org`). -* file transfers (categorized by mime type) -* `notice.log`, [`intel.log`](#ZeekIntel) and `weird.log` entries, including those generated by Zeek plugins detecting vulnerabilities (see the list of Zeek plugins under [Components](#Components)) -* detection of cleartext passwords -* use of insecure or outdated protocols -* tunneled traffic or use of VPN protocols -* rejected or aborted connections -* common network services communicating over non-standard ports -* file scanning engine hits on [extracted files](#ZeekFileExtraction) -* large connection or file transfer - - The size (in megabytes) threshold for this condition to trigger can be adjusted by setting the `TOTAL_MEGABYTES_SEVERITY_THRESHOLD` environment variable in [`docker-compose.yml`](#DockerComposeYml). -* long connection duration - - The duration (in seconds) threshold for this condition to trigger can be adjusted by setting the `CONNECTION_SECONDS_SEVERITY_THRESHOLD` environment variable in [`docker-compose.yml`](#DockerComposeYml). - -As this [feature](https://github.com/idaholab/Malcolm/issues/19) is improved it's expected that additional categories will be identified and implemented for severity scoring. - -When a Zeek log satisfies more than one of these conditions its severity scores will be summed, with a maximum score of `100`. A Zeek log's severity score is indexed in the `event.severity` field and the conditions which contributed to its score are indexed in `event.severity_tags`. - -![The Severity dashboard](./docs/images/screenshots/dashboards_severity.png) - -#### Customizing event severity scoring - -These categories' severity scores can be customized by editing `logstash/maps/malcolm_severity.yaml`: - -* Each category can be assigned a number between `1` and `100` for severity scoring. -* Any category may be disabled by assigning it a score of `0`. -* A severity score can be assigned for any [supported protocol](#Protocols) by adding an entry with the key formatted like `"PROTOCOL_XYZ"`, where `XYZ` is the uppercased value of the protocol as stored in the `network.protocol` field. For example, to assign a score of `40` to Zeek logs generated for SSH traffic, you could add the following line to `malcolm_severity.yaml`: - -``` -"PROTOCOL_SSH": 40 -``` - -Restart Logstash after modifying `malcolm_severity.yaml` for the changes to take effect. The [hostname and CIDR subnet names interface](#NameMapUI) provides a convenient button for restarting Logstash. - -Severity scoring can be disabled globally by setting the `LOGSTASH_SEVERITY_SCORING` environment variable to `false` in the [`docker-compose.yml`](#DockerComposeYml) file and [restarting Malcolm](#StopAndRestart). - -### Zeek Intelligence Framework - -To quote Zeek's [Intelligence Framework](https://docs.zeek.org/en/master/frameworks/intel.html) documentation, "The goals of Zeek’s Intelligence Framework are to consume intelligence data, make it available for matching, and provide infrastructure to improve performance and memory utilization. Data in the Intelligence Framework is an atomic piece of intelligence such as an IP address or an e-mail address. This atomic data will be packed with metadata such as a freeform source field, a freeform descriptive field, and a URL which might lead to more information about the specific item." Zeek [intelligence](https://docs.zeek.org/en/master/scripts/base/frameworks/intel/main.zeek.html) [indicator types](https://docs.zeek.org/en/master/scripts/base/frameworks/intel/main.zeek.html#type-Intel::Type) include IP addresses, URLs, file names, hashes, email addresses, and more. - -Malcolm doesn't come bundled with intelligence files from any particular feed, but they can be easily included into your local instance. On [startup](shared/bin/zeek_intel_setup.sh), Malcolm's `malcolmnetsec/zeek` docker container enumerates the subdirectories under `./zeek/intel` (which is [bind mounted](https://docs.docker.com/storage/bind-mounts/) into the container's runtime) and configures Zeek so that those intelligence files will be automatically included in its local policy. Subdirectories under `./zeek/intel` which contain their own `__load__.zeek` file will be `@load`-ed as-is, while subdirectories containing "loose" intelligence files will be [loaded](https://docs.zeek.org/en/master/frameworks/intel.html#loading-intelligence) automatically with a `redef Intel::read_files` directive. - -Note that Malcolm does not manage updates for these intelligence files. You should use the update mechanism suggested by your feeds' maintainers to keep them up to date, or use a [TAXII](#ZeekIntelSTIX) or [MISP](#ZeekIntelMISP) feed as described below. - -Adding and deleting intelligence files under this directory will take effect upon [restarting Malcolm](#StopAndRestart). Alternately, you can use the `ZEEK_INTEL_REFRESH_CRON_EXPRESSION` environment variable containing a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) to specify the interval at which the intel files should be refreshed. It can also be done manually without restarting Malcolm by running the following command from the Malcolm installation directory: - -``` -docker-compose exec --user $(id -u) zeek /usr/local/bin/entrypoint.sh true -``` - -For a public example of Zeek intelligence files, see Critical Path Security's [repository](https://github.com/CriticalPathSecurity/Zeek-Intelligence-Feeds) which aggregates data from various other threat feeds into Zeek's format. - -#### STIX™ and TAXII™ - -In addition to loading Zeek intelligence files, on startup Malcolm will [automatically generate](shared/bin/zeek_intel_from_threat_feed.py) a Zeek intelligence file for all [Structured Threat Information Expression (STIX™)](https://oasis-open.github.io/cti-documentation/stix/intro.html) [v2.0](https://docs.oasis-open.org/cti/stix/v2.0/stix-v2.0-part1-stix-core.html)/[v2.1](https://docs.oasis-open.org/cti/stix/v2.1/stix-v2.1.html) JSON files found under `./zeek/intel/STIX`. - -Additionally, if a special text file named `.stix_input.txt` is found in `./zeek/intel/STIX`, that file will be read and processed as a list of [TAXII™](https://oasis-open.github.io/cti-documentation/taxii/intro.html) [2.0](http://docs.oasis-open.org/cti/taxii/v2.0/cs01/taxii-v2.0-cs01.html)/[2.1](https://docs.oasis-open.org/cti/taxii/v2.1/csprd02/taxii-v2.1-csprd02.html) feeds, one per line, according to the following format (the username and password are optional): - -``` -taxii|version|discovery_url|collection_name|username|password -``` - -For example: - -``` -taxii|2.0|http://example.org/taxii/|IP Blocklist|guest|guest -taxii|2.1|https://example.com/taxii/api2/|URL Blocklist -… -``` - -Malcolm will attempt to query the TAXII feed(s) for `indicator` STIX objects and convert them to the Zeek intelligence format as described above. There are publicly available TAXII 2.x-compatible services provided by a number of organizations including [Anomali Labs](https://www.anomali.com/resources/limo) and [MITRE](https://www.mitre.org/capabilities/cybersecurity/overview/cybersecurity-blog/attck%E2%84%A2-content-available-in-stix%E2%84%A2-20-via), or you may choose from several open-source offerings to roll your own TAXII 2 server (e.g., [oasis-open/cti-taxii-server](https://github.com/oasis-open/cti-taxii-server), [freetaxii/server](https://github.com/freetaxii/server), [StephenOTT/TAXII-Server](https://github.com/StephenOTT/TAXII-Server), etc.). - -Note that only **indicators** of [**cyber-observable objects**](https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_mlbmudhl16lr) matched with the **equals (`=`)** [comparison operator](https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_t11hn314cr7w) against a **single value** can be expressed as Zeek intelligence items. More complex STIX indicators will be silently ignored. - -#### MISP - -In addition to loading Zeek intelligence files, on startup Malcolm will [automatically generate](shared/bin/zeek_intel_from_threat_feed.py) a Zeek intelligence file for all [Malware Information Sharing Platform (MISP)](https://www.misp-project.org/datamodels/) JSON files found under `./zeek/intel/MISP`. - -Additionally, if a special text file named `.misp_input.txt` is found in `./zeek/intel/MISP`, that file will be read and processed as a list of [MISP feed](https://misp.gitbooks.io/misp-book/content/managing-feeds/#feeds) URLs, one per line, according to the following format (the authentication key is optional): - -``` -misp|manifest_url|auth_key -``` - -For example: - -``` -misp|https://example.com/data/feed-osint/manifest.json|df97338db644c64fbfd90f3e03ba8870 -… -``` - -Malcolm will attempt to connect to the MISP feed(s) and retrieve [`Attribute`](https://www.misp-standard.org/rfc/misp-standard-core.html#name-attribute) objects of MISP events and convert them to the Zeek intelligence format as described above. There are publicly available [MISP feeds](https://www.misp-project.org/feeds/) and [communities](https://www.misp-project.org/communities/), or you may [run your own MISP instance](https://www.misp-project.org/2019/09/25/hostev-vs-own-misp.html/). - -Note that only a subset of MISP [attribute types](https://www.misp-project.org/datamodels/#attribute-categories-vs-types) can be expressed with the Zeek intelligence [indicator types](https://docs.zeek.org/en/master/scripts/base/frameworks/intel/main.zeek.html#type-Intel::Type). MISP attributes with other types will be silently ignored. - -### Anomaly Detection - -Malcolm uses the Anomaly Detection plugins for [OpenSearch](https://github.com/opensearch-project/anomaly-detection) and [OpenSearch Dashboards](https://github.com/opensearch-project/anomaly-detection-dashboards-plugin) to identify anomalous log data in near real-time using the [Random Cut Forest](https://api.semanticscholar.org/CorpusID:927435) (RCF) algorithm. This can be paired with [Alerting](#Alerting) to automatically notify when anomalies are found. See [Anomaly detection](https://opensearch.org/docs/latest/monitoring-plugins/ad/index/) in the OpenSearch documentation for usage instructions on how to create detectors for any of the many fields Malcolm supports. - -A fresh installation of Malcolm configures [several detectors](dashboards/anomaly_detectors) for detecting anomalous network traffic: - -* **network_protocol** - Detects anomalies based on application protocol (`network.protocol`) -* **action_result_user** - Detects anomalies in action (`event.action`), result (`event.result`) and user (`related.user`) within application protocols (`network.protocol`) -* **file_mime_type** - Detects anomalies based on transferred file type (`file.mime_type`) -* **total_bytes** - Detects anomalies based on traffic size (sum of `network.bytes`) - -These detectors are disabled by default, but may be enabled for anomaly detection over streaming or [historical data](https://aws.amazon.com/about-aws/whats-new/2022/01/amazon-opensearch-service-elasticsearch-anomaly-detection/). - -### Alerting - -Malcolm uses the Alerting plugins for [OpenSearch](https://github.com/opensearch-project/alerting) and [OpenSearch Dashboards](https://github.com/opensearch-project/alerting-dashboards-plugin). See [Alerting](https://opensearch.org/docs/latest/monitoring-plugins/alerting/index/) in the OpenSearch documentation for usage instructions. - -A fresh installation of Malcolm configures an example [custom webhook destination](https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#create-destinations) named **Malcolm API Loopback Webhook** that directs the triggered alerts back into the [Malcolm API](#API) to be reindexed as a session record with `event.dataset` set to `alerting`. The corresponding monitor **Malcolm API Loopback Monitor** is disabled by default, as you'll likely want to configure the trigger conditions to suit your needs. These examples are provided to illustrate how triggers and monitors can interact with a custom webhook to process alerts. - -#### Email Sender Accounts - -When using an email account to send alerts, you must [authenticate each sender account](https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#authenticate-sender-account) before you can send an email. The [`auth_setup`](#AuthSetup) script can be used to securely store the email account credentials: - -``` -./scripts/auth_setup - -Store administrator username/password for local Malcolm access? (Y/n): n - -(Re)generate self-signed certificates for HTTPS access (Y/n): n - -(Re)generate self-signed certificates for a remote log forwarder (Y/n): n - -Store username/password for primary remote OpenSearch instance? (y/N): n - -Store username/password for secondary remote OpenSearch instance? (y/N): n - -Store username/password for email alert sender account? (y/N): y - -Email account username: analyst@example.org -analyst@example.org password: -analyst@example.org password (again): -Email alert sender account variables stored: opensearch.alerting.destination.email.destination_alpha.password, opensearch.alerting.destination.email.destination_alpha.username -``` - -This action should only be performed while Malcolm is [stopped](#StopAndRestart): otherwise the credentials will not be stored correctly. - -### "Best Guess" Fingerprinting for ICS Protocols - -There are many ICS (industrial control systems) protocols. While Malcolm's collection of [protocol parsers](#Protocols) includes a number of them, many, particularly those that are proprietary or less common, are unlikely to be supported with a full parser in the foreseeable future. - -In an effort to help identify more ICS traffic, Malcolm can use "best guess" method based on transport protocol (e.g., TCP or UDP) and port(s) to categorize potential traffic communicating over some ICS protocols without full parser support. This feature involves a [mapping table](https://github.com/idaholab/Malcolm/blob/master/zeek/config/guess_ics_map.txt) and a [Zeek script](https://github.com/idaholab/Malcolm/blob/master/zeek/config/guess.zeek) to look up the transport protocol and destination and/or source port to make a best guess at whether a connection belongs to one of those protocols. These potential ICS communications are categorized by vendor where possible. - -Naturally, these lookups could produce false positives, so these connections are displayed in their own dashboard (the **Best Guess** dashboard found under the **ICS** section of Malcolm's [OpenSearch Dashboards](#DashboardsVisualizations) navigation pane). Values such as IP addresses, ports, or UID can be used to [pivot to other dashboards](#ZeekArkimeFlowCorrelation) to investigate further. - -![](./docs/images/screenshots/dashboards_bestguess.png) - -This feature is disabled by default, but it can be enabled by clearing (setting to `''`) the value of the `ZEEK_DISABLE_BEST_GUESS_ICS` environment variable in [`docker-compose.yml`](#DockerComposeYml). - -### API - -Malcolm provides a [REST API](./api/project/__init__.py) that can be used to programatically query some aspects of Malcolm's status and data. Malcolm's API is not to be confused with the [Viewer API](https://arkime.com/apiv3) provided by Arkime, although there may be some overlap in functionality. - -#### Ping - -`GET` - /mapi/ping - -Returns `pong` (for a simple "up" check). - -Example output: - -```json -{"ping":"pong"} -``` - -#### Version Information - -`GET` - /mapi/version - -Returns version information about Malcolm and version/[health](https://opensearch.org/docs/latest/opensearch/rest-api/cluster-health/) information about the underlying OpenSearch instance. - -
-Example output: - -```json -{ - "built": "2022-01-18T16:10:39Z", - "opensearch": { - "cluster_name": "docker-cluster", - "cluster_uuid": "TcSiEaOgTdO_l1IivYz2gA", - "name": "opensearch", - "tagline": "The OpenSearch Project: https://opensearch.org/", - "version": { - "build_date": "2021-12-21T01:36:21.407473Z", - "build_hash": "8a529d77c7432bc45b005ac1c4ba3b2741b57d4a", - "build_snapshot": false, - "build_type": "tar", - "lucene_version": "8.10.1", - "minimum_index_compatibility_version": "6.0.0-beta1", - "minimum_wire_compatibility_version": "6.8.0", - "number": "7.10.2" - } - }, - "opensearch_health": { - "active_primary_shards": 29, - "active_shards": 29, - "active_shards_percent_as_number": 82.85714285714286, - "cluster_name": "docker-cluster", - "delayed_unassigned_shards": 0, - "discovered_master": true, - "initializing_shards": 0, - "number_of_data_nodes": 1, - "number_of_in_flight_fetch": 0, - "number_of_nodes": 1, - "number_of_pending_tasks": 0, - "relocating_shards": 0, - "status": "yellow", - "task_max_waiting_in_queue_millis": 0, - "timed_out": false, - "unassigned_shards": 6 - }, - "sha": "8ddbbf4", - "version": "5.2.0" -} -``` -
- -#### Fields - -`GET` - /mapi/fields - -Returns the (very long) list of fields known to Malcolm, comprised of data from Arkime's [`fields` table](https://arkime.com/apiv3#fields-api), the Malcolm [OpenSearch template](./dashboards/templates/malcolm_template.json) and the OpenSearch Dashboards index pattern API. - -
-Example output: - -```json -{ - "fields": { - "@timestamp": { - "type": "date" - }, -… - "zeek.x509.san_uri": { - "description": "Subject Alternative Name URI", - "type": "string" - }, - "zeek.x509.san_uri.text": { - "type": "string" - } - }, - "total": 2005 -} -``` -
- - -#### Indices - -`GET` - /mapi/indices - -Lists [information related to the underlying OpenSearch indices](https://opensearch.org/docs/latest/opensearch/rest-api/cat/cat-indices/), similar to Arkime's [esindices](https://arkime.com/apiv3#esindices-api) API. - -
-Example output: - -```json - -{ - "indices": [ -… - { - "docs.count": "2268613", - "docs.deleted": "0", - "health": "green", - "index": "arkime_sessions3-210301", - "pri": "1", - "pri.store.size": "1.8gb", - "rep": "0", - "status": "open", - "store.size": "1.8gb", - "uuid": "w-4Q0ofBTdWO9KqeIIAAWg" - }, -… - ] -} -``` -
- -#### Field Aggregations - -`GET` or `POST` - /mapi/agg/`` - -Executes an OpenSearch [bucket aggregation](https://opensearch.org/docs/latest/opensearch/bucket-agg/) query for the requested fields across all of Malcolm's indexed network traffic metadata. - -Parameters: - -* `fieldname` (URL parameter) - the name(s) of the field(s) to be queried (comma-separated if multiple fields) (default: `event.provider`) -* `limit` (query parameter) - the maximum number of records to return at each level of aggregation (default: 500) -* `from` (query parameter) - the time frame ([`gte`](https://opensearch.org/docs/latest/opensearch/query-dsl/term/#range)) for the beginning of the search based on the session's `firstPacket` field value in a format supported by the [dateparser](https://github.com/scrapinghub/dateparser) library (default: "1 day ago") -* `to` (query parameter) - the time frame ([`lte`](https://opensearch.org/docs/latest/opensearch/query-dsl/term/#range)) for the beginning of the search based on the session's `firstPacket` field value in a format supported by the [dateparser](https://github.com/scrapinghub/dateparser) library (default: "now") -* `filter` (query parameter) - field filters formatted as a JSON dictionary - -The `from`, `to`, and `filter` parameters can be used to further restrict the range of documents returned. The `filter` dictionary should be formatted such that its keys are field names and its values are the values for which to filter. A field name may be prepended with a `!` to negate the filter (e.g., `{"event.provider":"zeek"}` vs. `{"!event.provider":"zeek"}`). Filtering for value `null` implies "is not set" or "does not exist" (e.g., `{"event.dataset":null}` means "the field `event.dataset` is `null`/is not set" while `{"!event.dataset":null}` means "the field `event.dataset` is not `null`/is set"). - -Examples of `filter` parameter: - -* `{"!network.transport":"icmp"}` - `network.transport` is not `icmp` -* `{"network.direction":["inbound","outbound"]}` - `network.direction` is either `inbound` or `outbound` -* `{"event.provider":"zeek","event.dataset":["conn","dns"]}` - "`event.provider` is `zeek` and `event.dataset` is either `conn` or `dns`" -* `{"!event.dataset":null}` - "`event.dataset` is set (is not `null`)" - -See [Examples](#APIExamples) for more examples of `filter` and corresponding output. - -#### Document Lookup - -`GET` or `POST` - /mapi/document - -Executes an OpenSearch [query](https://opensearch.org/docs/latest/opensearch/bucket-agg/) query for the matching documents across all of Malcolm's indexed network traffic metadata. - -Parameters: - -* `limit` (query parameter) - the maximum number of documents to return (default: 500) -* `from` (query parameter) - the time frame ([`gte`](https://opensearch.org/docs/latest/opensearch/query-dsl/term/#range)) for the beginning of the search based on the session's `firstPacket` field value in a format supported by the [dateparser](https://github.com/scrapinghub/dateparser) library (default: the UNIX epoch) -* `to` (query parameter) - the time frame ([`lte`](https://opensearch.org/docs/latest/opensearch/query-dsl/term/#range)) for the beginning of the search based on the session's `firstPacket` field value in a format supported by the [dateparser](https://github.com/scrapinghub/dateparser) library (default: "now") -* `filter` (query parameter) - field filters formatted as a JSON dictionary (see **Field Aggregations** for examples) - -
-Example cURL command and output: - -``` -$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ - 'https://localhost/mapi/document' \ - -d '{"limit": 10, filter":{"zeek.uid":"CYeji2z7CKmPRGyga"}}' -``` - -```json -{ - "filter": { - "zeek.uid": "CYeji2z7CKmPRGyga" - }, - "range": [ - 0, - 1643056677 - ], - "results": [ - { - "_id": "220124-CYeji2z7CKmPRGyga-http-7677", - "_index": "arkime_sessions3-220124", - "_score": 0.0, - "_source": { - "@timestamp": "2022-01-24T20:31:01.846Z", - "@version": "1", - "agent": { - "hostname": "filebeat", - "id": "bc25716b-8fe7-4de6-a357-65c7d3c15c33", - "name": "filebeat", - "type": "filebeat", - "version": "7.10.2" - }, - "client": { - "bytes": 0 - }, - "destination": { - "as": { - "full": "AS54113 Fastly" - }, - "geo": { - "city_name": "Seattle", - "continent_code": "NA", - "country_code2": "US", - "country_code3": "US", - "country_iso_code": "US", - "country_name": "United States", - "dma_code": 819, - "ip": "151.101.54.132", - "latitude": 47.6092, - "location": { - "lat": 47.6092, - "lon": -122.3314 - }, - "longitude": -122.3314, - "postal_code": "98111", - "region_code": "WA", - "region_name": "Washington", - "timezone": "America/Los_Angeles" - }, - "ip": "151.101.54.132", - "port": 80 - }, - "ecs": { - "version": "1.6.0" - }, - "event": { - "action": [ - "GET" - ], - "category": [ - "web", - "network" - ], -… -``` -
- -#### Examples - -Some security-related API examples: - -
-Protocols - -``` -/mapi/agg/network.type,network.transport,network.protocol,network.protocol_version -``` - -```json -{ - "fields": [ - "network.type", - "network.transport", - "network.protocol", - "network.protocol_version" - ], - "filter": null, - "range": [ - 1970, - 1643067256 - ], - "urls": [ - "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" - ], - "values": { - "buckets": [ - { - "doc_count": 442240, - "key": "ipv4", - "values": { - "buckets": [ - { - "doc_count": 279538, - "key": "udp", - "values": { - "buckets": [ - { - "doc_count": 266527, - "key": "bacnet", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 12365, - "key": "dns", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 78, - "key": "dhcp", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 44, - "key": "ntp", - "values": { - "buckets": [ - { - "doc_count": 22, - "key": "4" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 3, - "key": "enip", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "krb", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "syslog", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 30824, - "key": "tcp", - "values": { - "buckets": [ - { - "doc_count": 7097, - "key": "smb", - "values": { - "buckets": [ - { - "doc_count": 4244, - "key": "1" - }, - { - "doc_count": 1438, - "key": "2" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1792, - "key": "http", - "values": { - "buckets": [ - { - "doc_count": 829, - "key": "1.0" - }, - { - "doc_count": 230, - "key": "1.1" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1280, - "key": "dce_rpc", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 857, - "key": "s7comm", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 426, - "key": "ntlm", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 378, - "key": "gssapi", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 146, - "key": "tds", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 125, - "key": "ssl", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 91, - "key": "tls", - "values": { - "buckets": [ - { - "doc_count": 48, - "key": "TLSv13" - }, - { - "doc_count": 28, - "key": "TLSv12" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 29, - "key": "ssh", - "values": { - "buckets": [ - { - "doc_count": 18, - "key": "2" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 26, - "key": "modbus", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 17, - "key": "iso_cotp", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 8, - "key": "enip", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 6, - "key": "rdp", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 4, - "key": "ftp", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 4, - "key": "krb", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 4, - "key": "rfb", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 3, - "key": "ldap", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "telnet", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 848, - "key": "icmp", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1573, - "key": "ipv6", - "values": { - "buckets": [ - { - "doc_count": 1486, - "key": "udp", - "values": { - "buckets": [ - { - "doc_count": 1433, - "key": "dns", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 80, - "key": "icmp", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
-
-Software - -``` -/mapi/agg/zeek.software.name,zeek.software.unparsed_version -``` - -```json -{ - "fields": [ - "zeek.software.name", - "zeek.software.unparsed_version" - ], - "filter": null, - "range": [ - 1970, - 1643067759 - ], - "urls": [ - "/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" - ], - "values": { - "buckets": [ - { - "doc_count": 6, - "key": "Chrome", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" - }, - { - "doc_count": 1, - "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" - }, - { - "doc_count": 1, - "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" - }, - { - "doc_count": 1, - "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" - }, - { - "doc_count": 1, - "key": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.36 Safari/525.19" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 6, - "key": "Nmap-SSH", - "values": { - "buckets": [ - { - "doc_count": 3, - "key": "Nmap-SSH1-Hostkey" - }, - { - "doc_count": 3, - "key": "Nmap-SSH2-Hostkey" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 5, - "key": "MSIE", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" - }, - { - "doc_count": 1, - "key": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E)" - }, - { - "doc_count": 1, - "key": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" - }, - { - "doc_count": 1, - "key": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 4, - "key": "Firefox", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" - }, - { - "doc_count": 1, - "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:34.0) Gecko/20100101 Firefox/34.0" - }, - { - "doc_count": 1, - "key": "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 3, - "key": "ECS (sec", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "ECS (sec/96EE)" - }, - { - "doc_count": 1, - "key": "ECS (sec/97A6)" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 3, - "key": "NmapNSE", - "values": { - "buckets": [ - { - "doc_count": 3, - "key": "NmapNSE_1.0" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "Microsoft-Windows", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "Microsoft-Windows/6.1 UPnP/1.0 Windows-Media-Player-DMS/12.0.7601.17514 DLNADOC/1.50" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "Microsoft-Windows-NT", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0 Microsoft-HTTPAPI/2.0" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "SimpleHTTP", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "SimpleHTTP/0.6 Python/2.7.17" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "Windows-Media-Player-DMS", - "values": { - "buckets": [ - { - "doc_count": 2, - "key": "Windows-Media-Player-DMS/12.0.7601.17514" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "A-B WWW", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "A-B WWW/0.1" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "CONF-CTR-NAE1", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "CONF-CTR-NAE1" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "ClearSCADA", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "ClearSCADA/6.72.4644.1" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "GoAhead-Webs", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "GoAhead-Webs" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "MSFT", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "MSFT 5.0" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "Microsoft-IIS", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "Microsoft-IIS/7.5" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "Microsoft-WebDAV-MiniRedir", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "Microsoft-WebDAV-MiniRedir/6.1.7601" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "Python-urllib", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "Python-urllib/2.7" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "Schneider-WEB/V", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "Schneider-WEB/V2.1.4" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "Version", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "Version_1.0" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "nginx", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "nginx" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "sublime-license-check", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "sublime-license-check/3.0" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
-
-User agent - -``` -/mapi/agg/user_agent.original -``` - -```json -{ - "fields": [ - "user_agent.original" - ], - "filter": null, - "range": [ - 1970, - 1643067845 - ], - "values": { - "buckets": [ - { - "doc_count": 230, - "key": "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" - }, - { - "doc_count": 142, - "key": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" - }, - { - "doc_count": 114, - "key": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" - }, - { - "doc_count": 50, - "key": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)" - }, - { - "doc_count": 48, - "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" - }, - { - "doc_count": 43, - "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" - }, - { - "doc_count": 33, - "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:34.0) Gecko/20100101 Firefox/34.0" - }, - { - "doc_count": 17, - "key": "Python-urllib/2.7" - }, - { - "doc_count": 12, - "key": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" - }, - { - "doc_count": 9, - "key": "Microsoft-Windows/6.1 UPnP/1.0 Windows-Media-Player-DMS/12.0.7601.17514 DLNADOC/1.50" - }, - { - "doc_count": 9, - "key": "Windows-Media-Player-DMS/12.0.7601.17514" - }, - { - "doc_count": 8, - "key": "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko" - }, - { - "doc_count": 5, - "key": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" - }, - { - "doc_count": 5, - "key": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.36 Safari/525.19" - }, - { - "doc_count": 3, - "key": "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0" - }, - { - "doc_count": 2, - "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" - }, - { - "doc_count": 1, - "key": "Microsoft-WebDAV-MiniRedir/6.1.7601" - }, - { - "doc_count": 1, - "key": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E)" - }, - { - "doc_count": 1, - "key": "sublime-license-check/3.0" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
-
-External traffic (outbound/inbound) - -``` -$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ - 'https://localhost/mapi/agg/network.protocol' \ - -d '{"filter":{"network.direction":["inbound","outbound"]}}' -``` - -```json -{ - "fields": [ - "network.protocol" - ], - "filter": { - "network.direction": [ - "inbound", - "outbound" - ] - }, - "range": [ - 1970, - 1643068000 - ], - "urls": [ - "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" - ], - "values": { - "buckets": [ - { - "doc_count": 202597, - "key": "bacnet" - }, - { - "doc_count": 129, - "key": "tls" - }, - { - "doc_count": 128, - "key": "ssl" - }, - { - "doc_count": 33, - "key": "http" - }, - { - "doc_count": 33, - "key": "ntp" - }, - { - "doc_count": 20, - "key": "dns" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
-
-Cross-segment traffic - -``` -$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ - 'https://localhost/mapi/agg/source.segment,destination.segment,network.protocol' \ - -d '{"filter":{"tags":"cross_segment"}}' -``` - -```json -{ - "fields": [ - "source.segment", - "destination.segment", - "network.protocol" - ], - "filter": { - "tags": "cross_segment" - }, - "range": [ - 1970, - 1643068080 - ], - "urls": [ - "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" - ], - "values": { - "buckets": [ - { - "doc_count": 6893, - "key": "Corporate", - "values": { - "buckets": [ - { - "doc_count": 6893, - "key": "OT", - "values": { - "buckets": [ - { - "doc_count": 891, - "key": "enip" - }, - { - "doc_count": 889, - "key": "cip" - }, - { - "doc_count": 202, - "key": "http" - }, - { - "doc_count": 146, - "key": "modbus" - }, - { - "doc_count": 1, - "key": "ftp" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 189, - "key": "OT", - "values": { - "buckets": [ - { - "doc_count": 138, - "key": "Corporate", - "values": { - "buckets": [ - { - "doc_count": 128, - "key": "http" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 51, - "key": "DMZ", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 28, - "key": "Battery Network", - "values": { - "buckets": [ - { - "doc_count": 25, - "key": "Combined Cycle BOP", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 3, - "key": "Solar Panel Network", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 20, - "key": "Combined Cycle BOP", - "values": { - "buckets": [ - { - "doc_count": 11, - "key": "Battery Network", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 9, - "key": "Solar Panel Network", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "Solar Panel Network", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "Combined Cycle BOP", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
-
-Plaintext password - -``` -$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ - 'https://localhost/mapi/agg/network.protocol' \ - -d '{"filter":{"!related.password":null}}' -``` - -```json -{ - "fields": [ - "network.protocol" - ], - "filter": { - "!related.password": null - }, - "range": [ - 1970, - 1643068162 - ], - "urls": [ - "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" - ], - "values": { - "buckets": [ - { - "doc_count": 20, - "key": "http" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
-
-Insecure/outdated protocols - -``` -$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ - 'https://localhost/mapi/agg/network.protocol,network.protocol_version' \ - -d '{"filter":{"event.severity_tags":"Insecure or outdated protocol"}}' -``` - -```json -{ - "fields": [ - "network.protocol", - "network.protocol_version" - ], - "filter": { - "event.severity_tags": "Insecure or outdated protocol" - }, - "range": [ - 1970, - 1643068248 - ], - "urls": [ - "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" - ], - "values": { - "buckets": [ - { - "doc_count": 4244, - "key": "smb", - "values": { - "buckets": [ - { - "doc_count": 4244, - "key": "1" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "ftp", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "rdp", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "5.1" - }, - { - "doc_count": 1, - "key": "5.2" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 2, - "key": "telnet", - "values": { - "buckets": [], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
-
-Notice categories - -``` -/mapi/agg/zeek.notice.category,zeek.notice.sub_category -``` - -```json -{ - "fields": [ - "zeek.notice.category", - "zeek.notice.sub_category" - ], - "filter": null, - "range": [ - 1970, - 1643068300 - ], - "urls": [ - "/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))", - "/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" - ], - "values": { - "buckets": [ - { - "doc_count": 100, - "key": "ATTACK", - "values": { - "buckets": [ - { - "doc_count": 42, - "key": "Lateral_Movement_Extracted_File" - }, - { - "doc_count": 30, - "key": "Lateral_Movement" - }, - { - "doc_count": 17, - "key": "Discovery" - }, - { - "doc_count": 5, - "key": "Execution" - }, - { - "doc_count": 5, - "key": "Lateral_Movement_Multiple_Attempts" - }, - { - "doc_count": 1, - "key": "Lateral_Movement_and_Execution" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 14, - "key": "EternalSafety", - "values": { - "buckets": [ - { - "doc_count": 11, - "key": "EternalSynergy" - }, - { - "doc_count": 3, - "key": "ViolationPidMid" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 6, - "key": "Scan", - "values": { - "buckets": [ - { - "doc_count": 6, - "key": "Port_Scan" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - }, - { - "doc_count": 1, - "key": "Ripple20", - "values": { - "buckets": [ - { - "doc_count": 1, - "key": "Treck_TCP_observed" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
-
-Severity tags - -``` -/mapi/agg/event.severity_tags -``` - -```json -{ - "fields": [ - "event.severity_tags" - ], - "filter": null, - "range": [ - 1970, - 1643068363 - ], - "urls": [ - "/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))", - "/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" - ], - "values": { - "buckets": [ - { - "doc_count": 160180, - "key": "Outbound traffic" - }, - { - "doc_count": 43059, - "key": "Inbound traffic" - }, - { - "doc_count": 11091, - "key": "Connection attempt rejected" - }, - { - "doc_count": 8967, - "key": "Connection attempt, no reply" - }, - { - "doc_count": 7131, - "key": "Cross-segment traffic" - }, - { - "doc_count": 4250, - "key": "Insecure or outdated protocol" - }, - { - "doc_count": 2219, - "key": "External traffic" - }, - { - "doc_count": 1985, - "key": "Sensitive country" - }, - { - "doc_count": 760, - "key": "Weird" - }, - { - "doc_count": 537, - "key": "Connection aborted (originator)" - }, - { - "doc_count": 474, - "key": "Connection aborted (responder)" - }, - { - "doc_count": 206, - "key": "File transfer (high concern)" - }, - { - "doc_count": 100, - "key": "MITRE ATT&CK framework technique" - }, - { - "doc_count": 66, - "key": "Service on non-standard port" - }, - { - "doc_count": 64, - "key": "Signature (capa)" - }, - { - "doc_count": 30, - "key": "Signature (YARA)" - }, - { - "doc_count": 25, - "key": "Signature (ClamAV)" - }, - { - "doc_count": 20, - "key": "Cleartext password" - }, - { - "doc_count": 19, - "key": "Long connection" - }, - { - "doc_count": 15, - "key": "Notice (vulnerability)" - }, - { - "doc_count": 13, - "key": "File transfer (medium concern)" - }, - { - "doc_count": 6, - "key": "Notice (scan)" - }, - { - "doc_count": 1, - "key": "High volume connection" - } - ], - "doc_count_error_upper_bound": 0, - "sum_other_doc_count": 0 - } -} -``` -
- -#### Event Logging - -`POST` - /mapi/event - -A webhook that accepts alert data to be reindexed into OpenSearch as session records for viewing in Malcolm's [dashboards](#Dashboards). See [Alerting](#Alerting) for more details and an example of how this API is used. - -
-Example input: - -```json - -{ - "alert": { - "monitor": { - "name": "Malcolm API Loopback Monitor" - }, - "trigger": { - "name": "Malcolm API Loopback Trigger", - "severity": 4 - }, - "period": { - "start": "2022-03-08T18:03:30.576Z", - "end": "2022-03-08T18:04:30.576Z" - }, - "results": [ - { - "_shards": { - "total": 5, - "failed": 0, - "successful": 5, - "skipped": 0 - }, - "hits": { - "hits": [], - "total": { - "value": 697, - "relation": "eq" - }, - "max_score": null - }, - "took": 1, - "timed_out": false - } - ], - "body": "", - "alert": "PLauan8BaL6eY1yCu9Xj", - "error": "" - } -} -``` -
- -
-Example output: - -```json - -{ - "_index": "arkime_sessions3-220308", - "_type": "_doc", - "_id": "220308-PLauan8BaL6eY1yCu9Xj", - "_version": 4, - "result": "updated", - "_shards": { - "total": 1, - "successful": 1, - "failed": 0 - }, - "_seq_no": 9045, - "_primary_term": 1 -} -``` -
- -## Ingesting Third-Party Logs - -Malcolm uses [OpenSearch](https://opensearch.org/) and [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) for data storage, search and visualization, and [Logstash](https://www.elastic.co/logstash/) for log processing. Because these tools are data agnostic, Malcolm can be configured to accept various host logs and other third-party logs sent from log forwaders such as [Fluent Bit](https://fluentbit.io/) and [Beats](https://www.elastic.co/beats/). Some examples of the types of logs these forwarders might send include: - -* System resource utilization metrics (CPU, memory, disk, network, etc.) -* System temperatures -* Linux system logs -* Windows event logs -* Process or service health status -* Logs appended to textual log files (e.g., `tail`-ing a log file) -* The output of an external script or program -* Messages in the form of MQTT control packets -* many more… - -Refer to [**Forwarding Third-Party Logs to Malcolm**](./scripts/third-party-logs/README.md) for more information. - -## Malcolm installer ISO - -Malcolm's Docker-based deployment model makes Malcolm able to run on a variety of platforms. However, in some circumstances (for example, as a long-running appliance as part of a security operations center, or inside of a virtual machine) it may be desirable to install Malcolm as a dedicated standalone installation. - -Malcolm can be packaged into an installer ISO based on the current [stable release](https://wiki.debian.org/DebianStable) of [Debian](https://www.debian.org/). This [customized Debian installation](https://wiki.debian.org/DebianLive) is preconfigured with the bare minimum software needed to run Malcolm. - -### Generating the ISO - -Official downloads of the Malcolm installer ISO are not provided: however, it can be built easily on an internet-connected Linux host with Vagrant: - -* [Vagrant](https://www.vagrantup.com/) - - [`vagrant-reload`](https://github.com/aidanns/vagrant-reload) plugin - - [`vagrant-sshfs`](https://github.com/dustymabe/vagrant-sshfs) plugin - - [`bento/debian-11`](https://app.vagrantup.com/bento/boxes/debian-11) Vagrant box - -The build should work with either the [VirtualBox](https://www.virtualbox.org/) provider or the [libvirt](https://libvirt.org/) provider: - -* [VirtualBox](https://www.virtualbox.org/) [provider](https://www.vagrantup.com/docs/providers/virtualbox) - - [`vagrant-vbguest`](https://github.com/dotless-de/vagrant-vbguest) plugin -* [libvirt](https://libvirt.org/) - - [`vagrant-libvirt`](https://github.com/vagrant-libvirt/vagrant-libvirt) provider plugin - - [`vagrant-mutate`](https://github.com/sciurus/vagrant-mutate) plugin to convert [`bento/debian-11`](https://app.vagrantup.com/bento/boxes/debian-11) Vagrant box to `libvirt` format - -To perform a clean build the Malcolm installer ISO, navigate to your local Malcolm working copy and run: - -``` -$ ./malcolm-iso/build_via_vagrant.sh -f -… -Starting build machine... -Bringing machine 'default' up with 'virtualbox' provider... -… -``` - -Building the ISO may take 30 minutes or more depending on your system. As the build finishes, you will see the following message indicating success: - -``` -… -Finished, created "/malcolm-build/malcolm-iso/malcolm-6.3.0.iso" -… -``` - -By default, Malcolm's Docker images are not packaged with the installer ISO, assuming instead that you will pull the [latest images](https://hub.docker.com/u/malcolmnetsec) with a `docker-compose pull` command as described in the [Quick start](#QuickStart) section. If you wish to build an ISO with the latest Malcolm images included, follow the directions to create [pre-packaged installation files](#Packager), which include a tarball with a name like `malcolm_YYYYMMDD_HHNNSS_xxxxxxx_images.tar.gz`. Then, pass that images tarball to the ISO build script with a `-d`, like this: - -``` -$ ./malcolm-iso/build_via_vagrant.sh -f -d malcolm_YYYYMMDD_HHNNSS_xxxxxxx_images.tar.gz -… -``` - -A system installed from the resulting ISO will load the Malcolm Docker images upon first boot. This method is desirable when the ISO is to be installed in an "air gapped" environment or for distribution to non-networked machines. - -Alternately, if you have forked Malcolm on GitHub, [workflow files](./.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](#Hedgehog) and [Malcolm](#ISO) installer ISOs, specifically [`malcolm-iso-build-docker-wrap-push-ghcr.yml`](./.github/workflows/malcolm-iso-build-docker-wrap-push-ghcr.yml) for the Malcolm ISO. You'll need to run the workflows to build and push your fork's Malcolm docker images before building the ISO. The resulting ISO file is wrapped in a Docker image that provides an HTTP server from which the ISO may be downloaded. - -### Installation - -The installer is designed to require as little user input as possible. For this reason, there are NO user prompts and confirmations about partitioning and reformatting hard disks for use by the operating system. The installer assumes that all non-removable storage media (eg., SSD, HDD, NVMe, etc.) are available for use and ⛔🆘😭💀 ***will partition and format them without warning*** 💀😭🆘⛔. - -The installer will ask for several pieces of information prior to installing the Malcolm base operating system: - -* Hostname -* Domain name -* Root password – (optional) a password for the privileged root account which is rarely needed -* User name: the name for the non-privileged service account user account under which the Malcolm runs -* User password – a password for the non-privileged sensor account -* Encryption password (optional) – if the encrypted installation option was selected at boot time, the encryption password must be entered every time the system boots - -At the end of the installation process, you will be prompted with a few self-explanatory yes/no questions: - -* **Disable IPv6?** -* **Automatically login to the GUI session?** -* **Should the GUI session be locked due to inactivity?** -* **Display the [Standard Mandatory DoD Notice and Consent Banner](https://www.stigviewer.com/stig/application_security_and_development/2018-12-24/finding/V-69349)?** *(only applies when installed on U.S. government information systems)* - -Following these prompts, the installer will reboot and the Malcolm base operating system will boot. - -### Setup - -When the system boots for the first time, the Malcolm Docker images will load if the installer was built with pre-packaged installation files as described above. Wait for this operation to continue (the progress dialog will disappear when they have finished loading) before continuing the setup. - -Open a terminal (click the red terminal 🗔 icon next to the Debian swirl logo 🍥 menu button in the menu bar). At this point, setup is similar to the steps described in the [Quick start](#QuickStart) section. Navigate to the Malcolm directory (`cd ~/Malcolm`) and run [`auth_setup`](#AuthSetup) to configure authentication. If the ISO didn't have pre-packaged Malcolm images, or if you'd like to retrieve the latest updates, run `docker-compose pull`. Finalize your configuration by running `scripts/install.py --configure` and follow the prompts as illustrated in the [installation example](#InstallationExample). - -Once Malcolm is configured, you can [start Malcolm](#Starting) via the command line or by clicking the circular yellow Malcolm icon in the menu bar. - -### Time synchronization - -If you wish to set up time synchronization via [NTP](http://www.ntp.org/) or `htpdate`, open a terminal and run `sudo configure-interfaces.py`. Select **Continue**, then choose **Time Sync**. Here you can configure the operating system to keep its time synchronized with either an NTP server (using the NTP protocol), another Malcolm instance, or another HTTP/HTTPS server. On the next dialog, choose the time synchronization method you wish to configure. - -If **htpdate** is selected, you will be prompted to enter the IP address or hostname and port of an HTTP/HTTPS server (for a Malcolm instance, port `9200` may be used) and the time synchronization check frequency in minutes. A test connection will be made to determine if the time can be retrieved from the server. - -If *ntpdate* is selected, you will be prompted to enter the IP address or hostname of the NTP server. - -Upon configuring time synchronization, a "Time synchronization configured successfully!" message will be displayed. - -### Hardening - -The Malcolm aggregator base operating system targets the following guidelines for establishing a secure configuration posture: - -* DISA STIG (Security Technical Implementation Guides) [ported](https://github.com/hardenedlinux/STIG-4-Debian) from [DISA RHEL 7 STIG](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/) v1r1 to a Debian 9 base platform -* [CIS Debian Linux 9 Benchmark](https://www.cisecurity.org/cis-benchmarks/cis-benchmarks-faq/) with additional recommendations by the [hardenedlinux/harbian-audit](https://github.com/hardenedlinux/harbian-audit) project - -#### STIG compliance exceptions - -[Currently](https://github.com/hardenedlinux/STIG-4-Debian/blob/master/stig-debian.txt) there are 158 compliance checks that can be verified automatically and 23 compliance checks that must be verified manually. - -The Malcolm aggregator base operating system claims the following exceptions to STIG compliance: - -| # | ID | Title | Justification | -| --- | --- | --- | --- | -| 1 | [SV-86535r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-71911) | When passwords are changed a minimum of eight of the total number of characters must be changed. | Account/password policy exception: As an aggregator running Malcolm is intended to be used as an appliance rather than a general user-facing software platform, some exceptions to password enforcement policies are claimed. | -| 2 | [SV-86537r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-71913) | When passwords are changed a minimum of four character classes must be changed. | Account/password policy exception | -| 3 | [SV-86549r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71925) | Passwords for new users must be restricted to a 24 hours/1 day minimum lifetime. | Account/password policy exception | -| 4 | [SV-86551r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71927) | Passwords must be restricted to a 24 hours/1 day minimum lifetime. | Account/password policy exception | -| 5 | [SV-86553r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-71929) | Passwords for new users must be restricted to a 60-day maximum lifetime. | Account/password policy exception | -| 6 | [SV-86555r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-71931) | Existing passwords must be restricted to a 60-day maximum lifetime. | Account/password policy exception | -| 7 | [SV-86557r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71933) | Passwords must be prohibited from reuse for a minimum of five generations. | Account/password policy exception | -| 8 | [SV-86565r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71941) | The operating system must disable account identifiers (individuals, groups, roles, and devices) if the password expires. | Account/password policy exception | -| 9 | [SV-86567r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-71943) | Accounts subject to three unsuccessful logon attempts within 15 minutes must be locked for the maximum configurable period. | Account/password policy exception | -| 10 | [SV-86569r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71945) | If three unsuccessful root logon attempts within 15 minutes occur the associated account must be locked. | Account/password policy exception | -| 11 | [SV-86603r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2018-11-28/finding/V-71979) | The … operating system must prevent the installation of software, patches, service packs, device drivers, or operating system components of local packages without verification they have been digitally signed using a certificate that is issued by a Certificate Authority (CA) that is recognized and approved by the organization. | As the base distribution is not using embedded signatures, `debsig-verify` would reject all packages (see comment in `/etc/dpkg/dpkg.cfg`). Enabling it after installation would disallow any future updates. | -| 12 | [SV-86607r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71983) | USB mass storage must be disabled. | The ability to ingest data (such as PCAP files) from a mounted USB mass storage device is a requirement of the system. | -| 13 | [SV-86609r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71985) | File system automounter must be disabled unless required. | The ability to ingest data (such as PCAP files) from a mounted USB mass storage device is a requirement of the system. | -| 14 | [SV-86705r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72081) | The operating system must shut down upon audit processing failure, unless availability is an overriding concern. If availability is a concern, the system must alert the designated staff (System Administrator [SA] and Information System Security Officer [ISSO] at a minimum) in the event of an audit processing failure. | As maximizing availability is a system requirement, audit processing failures will be logged on the device rather than halting the system. | -| 15 | [SV-86713r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72089) | The operating system must immediately notify the System Administrator (SA) and Information System Security Officer ISSO (at a minimum) when allocated audit record storage volume reaches 75% of the repository maximum audit record storage capacity. | same as above | -| 16 | [SV-86715r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72093) | The operating system must immediately notify the System Administrator (SA) and Information System Security Officer (ISSO) (at a minimum) when the threshold for the repository maximum audit record storage capacity is reached. | same as above | -| 17 | [SV-86597r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71973) | A file integrity tool must verify the baseline operating system configuration at least weekly. | This functionality is not configured by default, but it can be configured post-install using the `aide` tool | -| 18 | [SV-86697r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72073) | The file integrity tool must use FIPS 140-2 approved cryptographic hashes for validating file contents and directories. | same as above | -| 19 | [SV-86707r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72083) | The operating system must off-load audit records onto a different system or media from the system being audited. | same as above | -| 20 | [SV-86709r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72085) | The operating system must encrypt the transfer of audit records off-loaded onto a different system or media from the system being audited. | same as above | -| 21 | [SV-86833r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72209) | The system must send rsyslog output to a log aggregation server. | same as above | -| 22 | [SV-87815r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-73163) | The audit system must take appropriate action when there is an error sending audit records to a remote system. | same as above | -| 23 | [SV-86693r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72069) | The file integrity tool must be configured to verify Access Control Lists (ACLs). | As this is not a multi-user system, the ACL check would be irrelevant. | -| 24 | [SV-86837r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_6/2016-12-16/finding/V-38666) | The system must use and update a DoD-approved virus scan program. | As this is a network traffic analysis appliance rather than an end-user device, regular user files will not be created. A virus scan program would impact device performance and would be unnecessary. | -| 25 | [SV-86839r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72215) | The system must update the virus scan program every seven days or more frequently. | As this is a network traffic analysis appliance rather than an end-user device, regular user files will not be created. A virus scan program would impact device performance and would be unnecessary. | -| 26 | [SV-86847r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72223) | All network connections associated with a communication session must be terminated at the end of the session or after 10 minutes of inactivity from the user at a command prompt, except to fulfill documented and validated mission requirements. | Malcolm be controlled from the command line in a manual capture scenario, so timing out a session based on command prompt inactivity would be inadvisable. | -| 27 | [SV-86893r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72269) | The operating system must, for networked systems, synchronize clocks with a server that is synchronized to one of the redundant United States Naval Observatory (USNO) time servers, a time server designated for the appropriate DoD network (NIPRNet/SIPRNet), and/or the Global Positioning System (GPS). | While [time synchronization](#ConfigTime) is supported on the Malcolm aggregator base operating system, an exception is claimed for this rule as the device may be configured to sync to servers other than the ones listed in the STIG. | -| 28 | [SV-86905r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72281) | For systems using DNS resolution, at least two name servers must be configured. | STIG recommendations for DNS servers are not enforced on the Malcolm aggregator base operating system to allow for use in a variety of network scenarios. | -| 29 | [SV-86919r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72295) | Network interfaces must not be in promiscuous mode. | One purpose of the Malcolm aggregator base operating system is to sniff and capture network traffic. | -| 30 | [SV-86931r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72307) | An X Windows display manager must not be installed unless approved. | A locked-down X Windows session is required for the sensor's kiosk display. | -| 31 | [SV-86519r3](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71895) | The operating system must set the idle delay setting for all connection types. | As this is a network traffic aggregation and analysis appliance rather than an end-user device, timing out displays or connections would not be desirable. | -| 32 | [SV-86523r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71899) | The operating system must initiate a session lock for the screensaver after a period of inactivity for graphical user interfaces. | This option is configurable during install time. Some installations of the Malcolm aggregator base operating system may be on appliance hardware not equipped with a keyboard by default, in which case it may not be desirable to lock the session.| -| 33 | [SV-86525r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71901) | The operating system must initiate a session lock for graphical user interfaces when the screensaver is activated. | This option is configurable during install time. Some installations of the Malcolm aggregator base operating system may be on appliance hardware not equipped with a keyboard by default, in which case it may not be desirable to lock the session. | -| 34 | [SV-86589r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-71965) | The operating system must uniquely identify and must authenticate organizational users (or processes acting on behalf of organizational users) using multifactor authentication. | As this is a network traffic capture appliance rather than an end-user device or a multiuser network host, this requirement is not applicable. | -| 35 | [SV-86921r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72297) | The system must be configured to prevent unrestricted mail relaying. | Does not apply as the Malcolm aggregator base operating system not does run a mail service. | -| 36 | [SV-86929r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72305) | If the Trivial File Transfer Protocol (TFTP) server is required, the TFTP daemon must be configured to operate in secure mode. | Does not apply as the Malcolm aggregator base operating system does not run a TFTP server. | -| 37 | [SV-86935r3](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72311) | The Network File System (NFS) must be configured to use RPCSEC_GSS. | Does not apply as the Malcolm aggregator base operating system does not run an NFS server. | -| 38 | [SV-87041r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72417) | The operating system must have the required packages for multifactor authentication installed. | As this is a network traffic capture appliance rather than an end-user device or a multiuser network host, this requirement is not applicable. | -| 39 | [SV-87051r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72427) | The operating system must implement multifactor authentication for access to privileged accounts via pluggable authentication modules (PAM). | As this is a network traffic capture appliance rather than an end-user device or a multiuser network host, this requirement is not applicable. | -| 40 | [SV-87059r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72435) | The operating system must implement smart card logons for multifactor authentication for access to privileged accounts. | As this is a network traffic capture appliance rather than an end-user device or a multiuser network host, this requirement is not applicable. | -| 41 | [SV-87829r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-73177) | Wireless network adapters must be disabled. | As an appliance intended to capture network traffic in a variety of network environments, wireless adapters may be needed to capture and/or report wireless traffic. | -| 42 | [SV-86699r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72075) | The system must not allow removable media to be used as the boot loader unless approved. | the Malcolm aggregator base operating system supports a live boot mode that can be booted from removable media. | - -Please review the notes for these additional rules. While not claiming an exception, they may be implemented or checked in a different way than outlined by the RHEL STIG as the Malcolm aggregator base operating system is not built on RHEL or for other reasons. - -| # | ID | Title | Note | -| --- | --- | --- | --- | -| 1 | [SV-86585r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-71961) | Systems with a Basic Input/Output System (BIOS) must require authentication upon booting into single-user and maintenance modes. | Although the [compliance check script](https://github.com/hardenedlinux/STIG-4-Debian) does not detect it, booting into recovery mode *does* in fact require the root password. | -| 2 | [SV-86587r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-71963) | Systems using Unified Extensible Firmware Interface (UEFI) must require authentication upon booting into single-user and maintenance modes. | Although the [compliance check script](https://github.com/hardenedlinux/STIG-4-Debian) does not detect it, booting into recovery mode *does* in fact require the root password. | -| 3 | [SV-86651r1](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-72027) | All files and directories contained in local interactive user home directories must have mode 0750 or less permissive. | Depending on when the [compliance check script](https://github.com/hardenedlinux/STIG-4-Debian) is run, some ephemeral files may exist in the service account's home directory which will cause this check to fail. For practical purposes the Malcolm aggregator base operating system's configuration does, however, comply. -| 4 | [SV-86623r3](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-12-14/finding/V-71999) | Vendor packaged system security patches and updates must be installed and up to date. | When the the Malcolm aggregator base operating system sensor appliance software is built, all of the latest applicable security patches and updates are included in it. How future updates are to be handled is still in design. | -| 6 | [SV-86691r2](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/2017-07-08/finding/V-72067) | The operating system must implement NIST FIPS-validated cryptography for the following: to provision digital signatures, to generate cryptographic hashes, and to protect data requiring data-at-rest protections in accordance with applicable federal laws, Executive Orders, directives, policies, regulations, and standards. | the Malcolm aggregator base operating system does use FIPS-compatible libraries for cryptographic functions. However, the kernel parameter being checked by the [compliance check script](https://github.com/hardenedlinux/STIG-4-Debian) is incompatible with some of the systems initialization scripts.| - -In addition, DISA STIG rules SV-86663r1, SV-86695r2, SV-86759r3, SV-86761r3, SV-86763r3, SV-86765r3, SV-86595r1, and SV-86615r2 relate to the SELinux kernel which is not used in the Malcolm aggregator base operating system, and are thus skipped. - -#### CIS benchmark compliance exceptions - -[Currently](https://github.com/hardenedlinux/harbian-audit/tree/master/bin/hardening) there are 271 checks to determine compliance with the CIS Debian Linux 9 Benchmark. - -The Malcolm aggregator base operating system claims exceptions from the recommendations in this benchmark in the following categories: - -**1.1 Install Updates, Patches and Additional Security Software** - When the the Malcolm aggregator appliance software is built, all of the latest applicable security patches and updates are included in it. How future updates are to be handled is still in design. - -**1.3 Enable verify the signature of local packages** - As the base distribution is not using embedded signatures, `debsig-verify` would reject all packages (see comment in `/etc/dpkg/dpkg.cfg`). Enabling it after installation would disallow any future updates. - -**2.14 Add nodev option to /run/shm Partition**, **2.15 Add nosuid Option to /run/shm Partition**, **2.16 Add noexec Option to /run/shm Partition** - The Malcolm aggregator base operating system does not mount `/run/shm` as a separate partition, so these recommendations do not apply. - -**2.18 Disable Mounting of cramfs Filesystems**, **2.19 Disable Mounting of freevxfs Filesystems**, **2.20 Disable Mounting of jffs2 Filesystems**, **2.21 Disable Mounting of hfs Filesystems**, **2.22 Disable Mounting of hfsplus Filesystems**, **2.23 Disable Mounting of squashfs Filesystems**, **2.24 Disable Mounting of udf Filesystems** - The Malcolm aggregator base operating system is not compiling a custom Linux kernel, so these filesystems are inherently supported as they are part Debian Linux's default kernel. - -**4.6 Disable USB Devices** - The ability to ingest data (such as PCAP files) from a mounted USB mass storage device is a requirement of the system. - -**6.1 Ensure the X Window system is not installed**, **6.2 Ensure Avahi Server is not enabled**, **6.3 Ensure print server is not enabled** - An X Windows session is provided for displaying dashboards. The library packages `libavahi-common-data`, `libavahi-common3`, and `libcups2` are dependencies of some of the X components used by the Malcolm aggregator base operating system, but the `avahi` and `cups` services themselves are disabled. - -**6.17 Ensure virus scan Server is enabled**, **6.18 Ensure virus scan Server update is enabled** - As this is a network traffic analysis appliance rather than an end-user device, regular user files will not be created. A virus scan program would impact device performance and would be unnecessary. - -**7.2.4 Log Suspicious Packets**, **7.2.7 Enable RFC-recommended Source Route Validation**, **7.4.1 Install TCP Wrappers** - As Malcolm may operate as a network traffic capture appliance sniffing packets on a network interface configured in promiscuous mode, these recommendations do not apply. - -**8.4.1 Install aide package** and **8.4.2 Implement Periodic Execution of File Integrity** - This functionality is not configured by default, but it could be configured post-install using `aide`. - -**8.1.1.2 Disable System on Audit Log Full**, **8.1.1.3 Keep All Auditing Information**, **8.1.1.5 Ensure set remote_server for audit service**, **8.1.1.6 Ensure enable_krb5 set to yes for remote audit service**, **8.1.1.7 Ensure set action for audit storage volume is fulled**, **8.1.1.9 Set space left for auditd service**, a few other audit-related items under section **8.1**, **8.2.5 Configure rsyslog to Send Logs to a Remote Log Host** - As maximizing availability is a system requirement, audit processing failures will be logged on the device rather than halting the system. `auditd` is set up to syslog when its local storage capacity is reached. - -Password-related recommendations under **9.2** and **10.1** - The library package `libpam-pwquality` is used in favor of `libpam-cracklib` which is what the [compliance scripts](https://github.com/hardenedlinux/harbian-audit/tree/master/bin/hardening) are looking for. Also, as an appliance running Malcolm is intended to be used as an appliance rather than a general user-facing software platform, some exceptions to password enforcement policies are claimed. - -**9.3.13 Limit Access via SSH** - The Malcolm aggregator base operating system does not create multiple regular user accounts: only `root` and an aggregator service account are used. SSH access for `root` is disabled. SSH login with a password is also disallowed: only key-based authentication is accepted. The service account accepts no keys by default. As such, the `AllowUsers`, `AllowGroups`, `DenyUsers`, and `DenyGroups` values in `sshd_config` do not apply. - -**9.5 Restrict Access to the su Command** - The Malcolm aggregator base operating system does not create multiple regular user accounts: only `root` and an aggregator service account are used. - -**10.1.10 Set maxlogins for all accounts** and **10.5 Set Timeout on ttys** - The Malcolm aggregator base operating system does not create multiple regular user accounts: only `root` and an aggregator service account are used. - -**12.10 Find SUID System Executables**, **12.11 Find SGID System Executables** - The few files found by [these](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/12.10_find_suid_files.sh) [scripts](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/12.11_find_sgid_files.sh) are valid exceptions required by the Malcolm aggregator base operating system's core requirements. - -Please review the notes for these additional guidelines. While not claiming an exception, the Malcolm aggregator base operating system may implement them in a manner different than is described by the [CIS Debian Linux 9 Benchmark](https://www.cisecurity.org/cis-benchmarks/cis-benchmarks-faq/) or the [hardenedlinux/harbian-audit](https://github.com/hardenedlinux/harbian-audit) audit scripts. - -**4.1 Restrict Core Dumps** - The Malcolm aggregator base operating system disables core dumps using a configuration file for `ulimit` named `/etc/security/limits.d/limits.conf`. The [audit script](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/4.1_restrict_core_dumps.sh) checking for this does not check the `limits.d` subdirectory, which is why this is incorrectly flagged as noncompliant. - -**5.4 Ensure ctrl-alt-del is disabled** - The Malcolm aggregator base operating system disables the `ctrl+alt+delete` key sequence by executing `systemctl disable ctrl-alt-del.target` during installation and the command `systemctl mask ctrl-alt-del.target` at boot time. - -**6.19 Configure Network Time Protocol (NTP)** - While [time synchronization](#ConfigTime) is supported on the Malcolm aggregator base operating system, an exception is claimed for this rule as the network sensor device may be configured to sync to servers in a different way than specified in the benchmark. - -**7.4.4 Create /etc/hosts.deny**, **7.7.1 Ensure Firewall is active**, **7.7.4.1 Ensure default deny firewall policy**, **7.7.4.3 Ensure default deny firewall policy**, **7.7.4.4 Ensure outbound and established connections are configured** - The Malcolm aggregator base operating system **is** configured with an appropriately locked-down software firewall (managed by "Uncomplicated Firewall" `ufw`). However, the methods outlined in the CIS benchmark recommendations do not account for this configuration. - -**8.7 Verifies integrity all packages** - The [script](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/8.7_verify_integrity_packages.sh) which verifies package integrity only "fails" because of missing (status `??5??????` displayed by the utility) language ("locale") files, which are removed as part of the Malcolm aggregator base operating system's trimming-down process. All non-locale-related system files pass intergrity checks. - -## Installation example using Ubuntu 22.04 LTS - -Here's a step-by-step example of getting [Malcolm from GitHub](https://github.com/idaholab/Malcolm/tree/main), configuring your system and your Malcolm instance, and running it on a system running Ubuntu Linux. Your mileage may vary depending on your individual system configuration, but this should be a good starting point. - -The commands in this example should be executed as a non-root user. - -You can use `git` to clone Malcolm into a local working copy, or you can download and extract the artifacts from the [latest release](https://github.com/idaholab/Malcolm/releases). - -To install Malcolm from the latest Malcolm release, browse to the [Malcolm releases page on GitHub](https://github.com/idaholab/Malcolm/releases) and download at a minimum `install.py` and the `malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` file, then navigate to your downloads directory: -``` -user@host:~$ cd Downloads/ -user@host:~/Downloads$ ls -malcolm_common.py install.py malcolm_20190611_095410_ce2d8de.tar.gz -``` - -If you are obtaining Malcolm using `git` instead, run the following command to clone Malcolm into a local working copy: -``` -user@host:~$ git clone https://github.com/idaholab/Malcolm -Cloning into 'Malcolm'... -remote: Enumerating objects: 443, done. -remote: Counting objects: 100% (443/443), done. -remote: Compressing objects: 100% (310/310), done. -remote: Total 443 (delta 81), reused 441 (delta 79), pack-reused 0 -Receiving objects: 100% (443/443), 6.87 MiB | 18.86 MiB/s, done. -Resolving deltas: 100% (81/81), done. - -user@host:~$ cd Malcolm/ -``` - -Next, run the `install.py` script to configure your system. Replace `user` in this example with your local account username, and follow the prompts. Most questions have an acceptable default you can accept by pressing the `Enter` key. Depending on whether you are installing Malcolm from the release tarball or inside of a git working copy, the questions below will be slightly different, but for the most part are the same. -``` -user@host:~/Malcolm$ sudo ./scripts/install.py -Installing required packages: ['apache2-utils', 'make', 'openssl', 'python3-dialog'] - -"docker info" failed, attempt to install Docker? (Y/n): y - -Attempt to install Docker using official repositories? (Y/n): y -Installing required packages: ['apt-transport-https', 'ca-certificates', 'curl', 'gnupg-agent', 'software-properties-common'] -Installing docker packages: ['docker-ce', 'docker-ce-cli', 'containerd.io'] -Installation of docker packages apparently succeeded - -Add a non-root user to the "docker" group?: y - -Enter user account: user - -Add another non-root user to the "docker" group?: n - -"docker-compose version" failed, attempt to install docker-compose? (Y/n): y - -Install docker-compose directly from docker github? (Y/n): y -Download and installation of docker-compose apparently succeeded - -fs.file-max increases allowed maximum for file handles -fs.file-max= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -fs.inotify.max_user_watches increases allowed maximum for monitored files -fs.inotify.max_user_watches= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -fs.inotify.max_queued_events increases queue size for monitored files -fs.inotify.max_queued_events= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -fs.inotify.max_user_instances increases allowed maximum monitor file watchers -fs.inotify.max_user_instances= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -vm.max_map_count increases allowed maximum for memory segments -vm.max_map_count= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -net.core.somaxconn increases allowed maximum for socket connections -net.core.somaxconn= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -vm.swappiness adjusts the preference of the system to swap vs. drop runtime memory pages -vm.swappiness= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -vm.dirty_background_ratio defines the percentage of system memory fillable with "dirty" pages before flushing -vm.dirty_background_ratio= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -vm.dirty_ratio defines the maximum percentage of dirty system memory before committing everything -vm.dirty_ratio= appears to be missing from /etc/sysctl.conf, append it? (Y/n): y - -/etc/security/limits.d/limits.conf increases the allowed maximums for file handles and memlocked segments -/etc/security/limits.d/limits.conf does not exist, create it? (Y/n): y -``` - -If you are configuring Malcolm from within a git working copy, `install.py` will now exit. Run `install.py` again like you did at the beginning of the example, only remove the `sudo` and add `--configure` to run `install.py` in "configuration only" mode. -``` -user@host:~/Malcolm$ ./scripts/install.py --configure -``` - -Alternately, if you are configuring Malcolm from the release tarball you will be asked if you would like to extract the contents of the tarball and to specify the installation directory and `install.py` will continue: -``` -Extract Malcolm runtime files from /home/user/Downloads/malcolm_20190611_095410_ce2d8de.tar.gz (Y/n): y - -Enter installation path for Malcolm [/home/user/Downloads/malcolm]: /home/user/Malcolm -Malcolm runtime files extracted to /home/user/Malcolm -``` - -Now that any necessary system configuration changes have been made, the local Malcolm instance will be configured: -``` -Malcolm processes will run as UID 1000 and GID 1000. Is this OK? (Y/n): y - -Should Malcolm use and maintain its own OpenSearch instance? (Y/n): y - -Forward Logstash logs to a secondary remote OpenSearch instance? (y/N): n - -Setting 10g for OpenSearch and 3g for Logstash. Is this OK? (Y/n): y - -Setting 3 workers for Logstash pipelines. Is this OK? (Y/n): y - -Restart Malcolm upon system or Docker daemon restart? (y/N): y -1: no -2: on-failure -3: always -4: unless-stopped -Select Malcolm restart behavior (unless-stopped): 4 - -Require encrypted HTTPS connections? (Y/n): y - -Will Malcolm be running behind another reverse proxy (Traefik, Caddy, etc.)? (y/N): n - -Specify external Docker network name (or leave blank for default networking) (): - -Authenticate against Lightweight Directory Access Protocol (LDAP) server? (y/N): n - -Store OpenSearch index snapshots locally in /home/user/Malcolm/opensearch-backup? (Y/n): y - -Compress OpenSearch index snapshots? (y/N): n - -Delete the oldest indices when the database exceeds a certain size? (y/N): n - -Automatically analyze all PCAP files with Suricata? (Y/n): y - -Download updated Suricata signatures periodically? (Y/n): y - -Automatically analyze all PCAP files with Zeek? (Y/n): y - -Perform reverse DNS lookup locally for source and destination IP addresses in logs? (y/N): n - -Perform hardware vendor OUI lookups for MAC addresses? (Y/n): y - -Perform string randomness scoring on some fields? (Y/n): y - -Expose OpenSearch port to external hosts? (y/N): n - -Expose Logstash port to external hosts? (y/N): n - -Expose Filebeat TCP port to external hosts? (y/N): y -1: json -2: raw -Select log format for messages sent to Filebeat TCP listener (json): 1 - -Source field to parse for messages sent to Filebeat TCP listener (message): message - -Target field under which to store decoded JSON fields for messages sent to Filebeat TCP listener (miscbeat): miscbeat - -Field to drop from events sent to Filebeat TCP listener (message): message - -Tag to apply to messages sent to Filebeat TCP listener (_malcolm_beats): _malcolm_beats - -Expose SFTP server (for PCAP upload) to external hosts? (y/N): n - -Enable file extraction with Zeek? (y/N): y -1: none -2: known -3: mapped -4: all -5: interesting -Select file extraction behavior (none): 5 -1: quarantined -2: all -3: none -Select file preservation behavior (quarantined): 1 - -Scan extracted files with ClamAV? (y/N): y - -Scan extracted files with Yara? (y/N): y - -Scan extracted PE files with Capa? (y/N): y - -Lookup extracted file hashes with VirusTotal? (y/N): n - -Download updated file scanner signatures periodically? (Y/n): y - -Should Malcolm capture live network traffic to PCAP files for analysis with Arkime? (y/N): y - -Capture packets using netsniff-ng? (Y/n): y - -Capture packets using tcpdump? (y/N): n - -Should Malcolm analyze live network traffic with Suricata? (y/N): y - -Should Malcolm analyze live network traffic with Zeek? (y/N): y - -Specify capture interface(s) (comma-separated): eth0 - -Capture filter (tcpdump-like filter expression; leave blank to capture all traffic) (): not port 5044 and not port 8005 and not port 9200 - -Disable capture interface hardware offloading and adjust ring buffer sizes? (y/N): n - -Malcolm has been installed to /home/user/Malcolm. See README.md for more information. -Scripts for starting and stopping Malcolm and changing authentication-related settings can be found in /home/user/Malcolm/scripts. -``` - -At this point you should **reboot your computer** so that the new system settings can be applied. After rebooting, log back in and return to the directory to which Malcolm was installed (or to which the git working copy was cloned). - -Now we need to [set up authentication](#AuthSetup) and generate some unique self-signed TLS certificates. You can replace `analyst` in this example with whatever username you wish to use to log in to the Malcolm web interface. -``` -user@host:~/Malcolm$ ./scripts/auth_setup - -Store administrator username/password for local Malcolm access? (Y/n): y - -Administrator username: analyst -analyst password: -analyst password (again): - -(Re)generate self-signed certificates for HTTPS access (Y/n): y - -(Re)generate self-signed certificates for a remote log forwarder (Y/n): y - -Store username/password for primary remote OpenSearch instance? (y/N): n - -Store username/password for secondary remote OpenSearch instance? (y/N): n - -Store username/password for email alert sender account? (y/N): n -``` - -For now, rather than [build Malcolm from scratch](#Build), we'll pull images from [Docker Hub](https://hub.docker.com/u/malcolmnetsec): -``` -user@host:~/Malcolm$ docker-compose pull -Pulling api ... done -Pulling arkime ... done -Pulling dashboards ... done -Pulling dashboards-helper ... done -Pulling file-monitor ... done -Pulling filebeat ... done -Pulling freq ... done -Pulling htadmin ... done -Pulling logstash ... done -Pulling name-map-ui ... done -Pulling nginx-proxy ... done -Pulling opensearch ... done -Pulling pcap-capture ... done -Pulling pcap-monitor ... done -Pulling suricata ... done -Pulling upload ... done -Pulling zeek ... done - -user@host:~/Malcolm$ docker images -REPOSITORY TAG IMAGE ID CREATED SIZE -malcolmnetsec/api 6.3.0 xxxxxxxxxxxx 3 days ago 158MB -malcolmnetsec/arkime 6.3.0 xxxxxxxxxxxx 3 days ago 816MB -malcolmnetsec/dashboards 6.3.0 xxxxxxxxxxxx 3 days ago 1.02GB -malcolmnetsec/dashboards-helper 6.3.0 xxxxxxxxxxxx 3 days ago 184MB -malcolmnetsec/filebeat-oss 6.3.0 xxxxxxxxxxxx 3 days ago 624MB -malcolmnetsec/file-monitor 6.3.0 xxxxxxxxxxxx 3 days ago 588MB -malcolmnetsec/file-upload 6.3.0 xxxxxxxxxxxx 3 days ago 259MB -malcolmnetsec/freq 6.3.0 xxxxxxxxxxxx 3 days ago 132MB -malcolmnetsec/htadmin 6.3.0 xxxxxxxxxxxx 3 days ago 242MB -malcolmnetsec/logstash-oss 6.3.0 xxxxxxxxxxxx 3 days ago 1.35GB -malcolmnetsec/name-map-ui 6.3.0 xxxxxxxxxxxx 3 days ago 143MB -malcolmnetsec/nginx-proxy 6.3.0 xxxxxxxxxxxx 3 days ago 121MB -malcolmnetsec/opensearch 6.3.0 xxxxxxxxxxxx 3 days ago 1.17GB -malcolmnetsec/pcap-capture 6.3.0 xxxxxxxxxxxx 3 days ago 121MB -malcolmnetsec/pcap-monitor 6.3.0 xxxxxxxxxxxx 3 days ago 213MB -malcolmnetsec/suricata 6.3.0 xxxxxxxxxxxx 3 days ago 278MB -malcolmnetsec/zeek 6.3.0 xxxxxxxxxxxx 3 days ago 1GB -``` - -Finally, we can start Malcolm. When Malcolm starts it will stream informational and debug messages to the console. If you wish, you can safely close the console or use `Ctrl+C` to stop these messages; Malcolm will continue running in the background. -``` -user@host:~/Malcolm$ ./scripts/start -In a few minutes, Malcolm services will be accessible via the following URLs: ------------------------------------------------------------------------------- - - Arkime: https://localhost/ - - OpenSearch Dashboards: https://localhost/dashboards/ - - PCAP upload (web): https://localhost/upload/ - - PCAP upload (sftp): sftp://username@127.0.0.1:8022/files/ - - Host and subnet name mapping editor: https://localhost/name-map-ui/ - - Account management: https://localhost:488/ - -NAME COMMAND SERVICE STATUS PORTS -malcolm-api-1 "/usr/local/bin/dock…" api running (starting) … -malcolm-arkime-1 "/usr/local/bin/dock…" arkime running (starting) … -malcolm-dashboards-1 "/usr/local/bin/dock…" dashboards running (starting) … -malcolm-dashboards-helper-1 "/usr/local/bin/dock…" dashboards-helper running (starting) … -malcolm-file-monitor-1 "/usr/local/bin/dock…" file-monitor running (starting) … -malcolm-filebeat-1 "/usr/local/bin/dock…" filebeat running (starting) … -malcolm-freq-1 "/usr/local/bin/dock…" freq running (starting) … -malcolm-htadmin-1 "/usr/local/bin/dock…" htadmin running (starting) … -malcolm-logstash-1 "/usr/local/bin/dock…" logstash running (starting) … -malcolm-name-map-ui-1 "/usr/local/bin/dock…" name-map-ui running (starting) … -malcolm-nginx-proxy-1 "/usr/local/bin/dock…" nginx-proxy running (starting) … -malcolm-opensearch-1 "/usr/local/bin/dock…" opensearch running (starting) … -malcolm-pcap-capture-1 "/usr/local/bin/dock…" pcap-capture running … -malcolm-pcap-monitor-1 "/usr/local/bin/dock…" pcap-monitor running (starting) … -malcolm-suricata-1 "/usr/local/bin/dock…" suricata running (starting) … -malcolm-suricata-live-1 "/usr/local/bin/dock…" suricata-live running … -malcolm-upload-1 "/usr/local/bin/dock…" upload running (starting) … -malcolm-zeek-1 "/usr/local/bin/dock…" zeek running (starting) … -malcolm-zeek-live-1 "/usr/local/bin/dock…" zeek-live running … -… -``` - -It will take several minutes for all of Malcolm's components to start up. Logstash will take the longest, probably 3 to 5 minutes. You'll know Logstash is fully ready when you see Logstash spit out a bunch of starting up messages, ending with this: -``` -… -malcolm-logstash-1 | [2022-07-27T20:27:52,056][INFO ][logstash.agent ] Pipelines running {:count=>6, :running_pipelines=>[:"malcolm-input", :"malcolm-output", :"malcolm-beats", :"malcolm-suricata", :"malcolm-enrichment", :"malcolm-zeek"], :non_running_pipelines=>[]} -… -``` - -You can now open a web browser and navigate to one of the [Malcolm user interfaces](#UserInterfaceURLs). - -## Upgrading Malcolm - -At this time there is not an "official" upgrade procedure to get from one version of Malcolm to the next, as it may vary from platform to platform. However, the process is fairly simple can be done by following these steps: - -### Update the underlying system - -You may wish to get the official updates for the underlying system's software packages before you proceed. Consult the documentation of your operating system for how to do this. - -If you are upgrading an Malcolm instance installed from [Malcolm installation ISO](#ISOInstallation), follow scenario 2 below. Due to the Malcolm base operating system's [hardened](#Hardening) configuration, when updating the underlying system, temporarily set the umask value to Debian default (`umask 0022` in the root shell in which updates are being performed) instead of the more restrictive Malcolm default. This will allow updates to be applied with the right permissions. - -### Scenario 1: Malcolm is a GitHub clone - -If you checked out a working copy of the Malcolm repository from GitHub with a `git clone` command, here are the basic steps to performing an upgrade: - -1. stop Malcolm - * `./scripts/stop` -2. stash changes to `docker-compose.yml` and other files - * `git stash save "pre-upgrade Malcolm configuration changes"` -3. pull changes from GitHub repository - * `git pull --rebase` -4. pull new Docker images (this will take a while) - * `docker-compose pull` -5. apply saved configuration change stashed earlier - * `git stash pop` -6. if you see `Merge conflict` messages, resolve the [conflicts](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging#_basic_merge_conflicts) with your favorite text editor -7. you may wish to re-run `install.py --configure` as described in [System configuration and tuning](#ConfigAndTuning) in case there are any new `docker-compose.yml` parameters for Malcolm that need to be set up -8. start Malcolm - * `./scripts/start` -9. you may be prompted to [configure authentication](#AuthSetup) if there are new authentication-related files that need to be generated - * you probably do not need to re-generate self-signed certificates - -### Scenario 2: Malcolm was installed from a packaged tarball - -If you installed Malcolm from [pre-packaged installation files](https://github.com/idaholab/Malcolm#Packager), here are the basic steps to perform an upgrade: - -1. stop Malcolm - * `./scripts/stop` -2. uncompress the new pre-packaged installation files (using `malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` as an example, the file and/or directory names will be different depending on the release) - * `tar xf malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` -3. backup current Malcolm scripts, configuration files and certificates - * `mkdir -p ./upgrade_backup_$(date +%Y-%m-%d)` - * `cp -r filebeat/ htadmin/ logstash/ nginx/ auth.env cidr-map.txt docker-compose.yml host-map.txt net-map.json ./scripts ./README.md ./upgrade_backup_$(date +%Y-%m-%d)/` -3. replace scripts and local documentation in your existing installation with the new ones - * `rm -rf ./scripts ./README.md` - * `cp -r ./malcolm_YYYYMMDD_HHNNSS_xxxxxxx/scripts ./malcolm_YYYYMMDD_HHNNSS_xxxxxxx/README.md ./` -4. replace (overwrite) `docker-compose.yml` file with new version - * `cp ./malcolm_YYYYMMDD_HHNNSS_xxxxxxx/docker-compose.yml ./docker-compose.yml` -5. re-run `./scripts/install.py --configure` as described in [System configuration and tuning](#ConfigAndTuning) -6. using a file comparison tool (e.g., `diff`, `meld`, `Beyond Compare`, etc.), compare `docker-compose.yml` and the `docker-compare.yml` file you backed up in step 3, and manually migrate over any customizations you wish to preserve from that file (e.g., `PCAP_FILTER`, `MAXMIND_GEOIP_DB_LICENSE_KEY`, `MANAGE_PCAP_FILES`; [anything else](#DockerComposeYml) you may have edited by hand in `docker-compose.yml` that's not prompted for in `install.py --configure`) -7. pull the new docker images (this will take a while) - * `docker-compose pull` to pull them from Docker Hub or `docker-compose load -i malcolm_YYYYMMDD_HHNNSS_xxxxxxx_images.tar.gz` if you have an offline tarball of the Malcolm docker images -8. start Malcolm - * `./scripts/start` -9. you may be prompted to [configure authentication](#AuthSetup) if there are new authentication-related files that need to be generated - * you probably do not need to re-generate self-signed certificates - -### Post-upgrade - -#### Monitoring Malcolm - -If you are technically-minded, you may wish to follow the debug output provided by `./scripts/start` (or `./scripts/logs` if you need to re-open the log stream after you've closed it), although there is a lot there and it may be hard to distinguish whether or not something is okay. - -Running `docker-compose ps -a` should give you a good idea if all of Malcolm's Docker containers started up and, in some cases, may be able to indicate if the containers are "healthy" or not. - -After upgrading following one of the previous outlines, give Malcolm several minutes to get started. Once things are up and running, open one of Malcolm's [web interfaces](#UserInterfaceURLs) to verify that things are working. - -#### Loading new OpenSearch Dashboards visualizations - -Once the upgraded instance Malcolm has started up, you'll probably want to import the new dashboards and visualizations for OpenSearch Dashboards. You can signal Malcolm to load the new visualizations by opening OpenSearch Dashboards, clicking **Management** → **Index Patterns**, then selecting the `arkime_sessions3-*` index pattern and clicking the delete **🗑** button near the upper-right of the window. Confirm the **Delete index pattern?** prompt by clicking **Delete**. Close the OpenSearch Dashboards browser window. After a few minutes the missing index pattern will be detected and OpenSearch Dashboards will be signalled to load its new dashboards and visualizations. - -### Major releases - -The Malcolm project uses [semantic versioning](https://semver.org/) when choosing version numbers. If you are moving between major releases (e.g., from v4.0.1 to v5.0.0), you're likely to find that there are enough major backwards compatibility-breaking changes that upgrading may not be worth the time and trouble. A fresh install is strongly recommended between major releases. - -## Modifying or Contributing to Malcolm - -If you are interested in contributing to the Malcolm project, please read the [Malcolm Contributor Guide](./docs/contributing/README.md). - +See [**Building from source**](docs/development.md#Build) to read how you can use GitHub [workflow files]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/) to build Malcolm. + +![api-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/api-build-and-push-ghcr/badge.svg) +![arkime-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/arkime-build-and-push-ghcr/badge.svg) +![dashboards-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/dashboards-build-and-push-ghcr/badge.svg) +![dashboards-helper-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/dashboards-helper-build-and-push-ghcr/badge.svg) +![file-monitor-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/file-monitor-build-and-push-ghcr/badge.svg) +![file-upload-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/file-upload-build-and-push-ghcr/badge.svg) +![filebeat-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/filebeat-build-and-push-ghcr/badge.svg) +![freq-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/freq-build-and-push-ghcr/badge.svg) +![htadmin-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/htadmin-build-and-push-ghcr/badge.svg) +![logstash-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/logstash-build-and-push-ghcr/badge.svg) +![name-map-ui-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/name-map-ui-build-and-push-ghcr/badge.svg) +![nginx-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/nginx-build-and-push-ghcr/badge.svg) +![opensearch-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/opensearch-build-and-push-ghcr/badge.svg) +![pcap-capture-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/pcap-capture-build-and-push-ghcr/badge.svg) +![pcap-monitor-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/pcap-monitor-build-and-push-ghcr/badge.svg) +![suricata-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/suricata-build-and-push-ghcr/badge.svg) +![zeek-build-and-push-ghcr]({{ site.github.repository_url }}/workflows/zeek-build-and-push-ghcr/badge.svg) +![malcolm-iso-build-docker-wrap-push-ghcr]({{ site.github.repository_url }}/workflows/malcolm-iso-build-docker-wrap-push-ghcr/badge.svg) +![sensor-iso-build-docker-wrap-push-ghcr]({{ site.github.repository_url }}/workflows/sensor-iso-build-docker-wrap-push-ghcr/badge.svg) ## Forks @@ -4104,9 +53,9 @@ If you are interested in contributing to the Malcolm project, please read the [M ## Copyright -[Malcolm](https://github.com/idaholab/Malcolm) is Copyright 2022 Battelle Energy Alliance, LLC, and is developed and released through the cooperation of the [Cybersecurity and Infrastructure Security Agency](https://www.cisa.gov/) of the [U.S. Department of Homeland Security](https://www.dhs.gov/). +[Malcolm]({{ site.github.repository_url }}) is Copyright 2022 Battelle Energy Alliance, LLC, and is developed and released through the cooperation of the [Cybersecurity and Infrastructure Security Agency](https://www.cisa.gov/) of the [U.S. Department of Homeland Security](https://www.dhs.gov/). -See [`License.txt`](./License.txt) for the terms of its release. +See [`License.txt`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/License.txt) for the terms of its release. ## Other Software diff --git a/_config.yml b/_config.yml new file mode 100644 index 000000000..7888a0593 --- /dev/null +++ b/_config.yml @@ -0,0 +1,98 @@ +repository: idaholab/Malcolm +title: Malcolm +description: A powerful, easily deployable network traffic analysis tool suite +logo: docs/images/logo/Malcolm_outline_banner_dark.png +remote_theme: pages-themes/minimal@v0.2.0 +external_download_url: https://malcolm.fyi/download/ +youtube_url: https://www.youtube.com/c/MalcolmNetworkTrafficAnalysisToolSuite +docs_uri: docs/ +alerting_docs_uri: docs/alerting.html +anomaly_detection_docs_uri: docs/anomaly-detection.html +api_docs_uri: docs/api.html +arkime_docs_uri: docs/arkime.html +components_docs_uri: docs/components.html +configuring_docs_uri: docs/malcolm-preparation.html +contributing_docs_uri: docs/contributing-guide.html +dashboards_docs_uri: docs/dashboards.html +hardening_docs_uri: docs/hardening.html +hedgehog_docs_uri: docs/hedgehog.html +live_analysis_docs_uri: docs/live-analysis.html +protocols_docs_uri: docs/protocols.html +queries_docs_uri: docs/queries-cheat-sheet.html +quickstart_docs_uri: docs/quickstart.html +severity_docs_uri: docs/severity.html +thirdparty_logs_docs_uri: docs/third-party-logs.html +upload_docs_uri: docs/upload.html +github: + owner_name: Idaho National Laboratory + default_branch: main +plugins: + - jekyll-relative-links + - jekyll-remote-theme + - jekyll-seo-tag +show_downloads: true +relative_links: + enabled: true + collections: true +exclude: + - .dockerignore + - .envrc + - .git + - .github + - .gitignore + - .ldap_config_defaults + - .opensearch.primary.curlrc + - .opensearch.secondary.curlrc + - .tmp + - .travis.yml + - .trigger_workflow_build + - api + - arkime + - arkime-logs + - arkime-raw + - auth.env + - cidr-map.txt + - dashboards + - docker-compose-standalone.yml + - docker-compose.yml + - Dockerfiles + - docs/images/font + - docs/images/hedgehog/logo/font + - docs/quick-start + - docs/slides + - docs/stix + - docs/web + - file-monitor + - file-upload + - filebeat + - freq-server + - Gemfile + - Gemfile.lock + - host-map.txt + - htadmin + - logstash + - malcolm-iso + - name-map-ui + - net-map.json + - netbox + - nginx + - opensearch + - opensearch-backup + - pcap + - pcap-capture + - pcap-monitor + - scripts + - sensor-iso + - shared + - suricata + - suricata-logs + - yara + - zeek + - zeek-logs + - "**/*.ai" + - "**/*.eps" + - "**/*.odg" + - "**/*.ppt*" + - "**/*.pdf" + - "**/*.svg" + - "**/*.xcf" diff --git a/_includes/head-custom.html b/_includes/head-custom.html new file mode 100644 index 000000000..30e2a3471 --- /dev/null +++ b/_includes/head-custom.html @@ -0,0 +1 @@ + diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 000000000..3eabc143b --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,98 @@ + + + + + + + +{% seo %} + + + + {% include head-custom.html %} + + +
+
+ {% if site.logo %} + Logo + {% else %} +

{{ site.title | default: site.github.repository_name }}

+ {% endif %} + +

{{ site.description | default: site.github.project_tagline }}

+ +

Quick Start

+ +

Documentation

+ +

Components

+ +

Supported Protocols

+ +

Configuring

+ +

Arkime

+ +

Dashboards

+ +

API

+ +

Hardening

+ +

Hedgehog Linux

+ +

Contribution Guide

+ + + +
+
+ {{ content }} + +
+ +
+ + + diff --git a/arkime/etc/config.ini b/arkime/etc/config.ini index 674264697..c40df9aa2 100644 --- a/arkime/etc/config.ini +++ b/arkime/etc/config.ini @@ -9,12 +9,13 @@ cronQueries=true rotateIndex=daily passwordSecret=Malcolm httpRealm=Arkime +userAuthIps=::,0.0.0.0/0 interface=eth0 wiseHost=127.0.0.1 wisePort=8081 pcapDir=/data/pcap/processed readTruncatedPackets=true -maxFileSizeG=1 +maxFileSizeG=4 tcpTimeout=600 tcpSaveTimeout=720 udpTimeout=30 @@ -33,7 +34,7 @@ dropGroup=arkime # The userAutoCreateTmpl should more or less match what's in /etc/user_settings.json # which is what's used when creating the default admin user. userNameHeader=http_auth_http_user -userAutoCreateTmpl={"userId": "${this.http_auth_http_user}", "userName": "${this.http_auth_http_user}", "enabled": true, "createEnabled": false, "webEnabled": true, "headerAuthEnabled": true, "emailSearch": true, "removeEnabled": false, "packetSearch": true, "hideStats": false, "hideFiles": false, "hidePcap": false, "disablePcapDownload": false, "settings": { "timezone": "local", "detailFormat": "last", "showTimestamps": "last", "sortColumn": "start", "sortDirection": "desc", "spiGraph": "protocol", "connSrcField": "source.ip", "connDstField": "destination.ip", "numPackets": "last", "theme" : "custom1: #222222,#E2E2E2,#FFFFFF,#00789E,#004A79,#017D73,#092B40,#42b7c5,#2A7580,#ecb30a,#333333,#89ADCC,#6D6D6D,#FFE7E7,#ECFEFF", "manualQuery": false }, "views": { "Public IP Addresses": { "expression": "(country.dst == EXISTS!) || (country.src == EXISTS!) || (ip.dst == EXISTS! && ip.dst != 0.0.0.0/8 && ip.dst != 10.0.0.0/8 && ip.dst != 100.64.0.0/10 && ip.dst != 127.0.0.0/8 && ip.dst != 169.254.0.0/16 && ip.dst != 172.16.0.0/12 && ip.dst != 192.0.0.0/24 && ip.dst != 192.0.2.0/24 && ip.dst != 192.88.99.0/24 && ip.dst != 192.168.0.0/16 && ip.dst != 198.18.0.0/15 && ip.dst != 198.51.100.0/24 && ip.dst != 203.0.113.0/24 && ip.dst != 224.0.0.0/4 && ip.dst != 232.0.0.0/8 && ip.dst != 233.0.0.0/8 && ip.dst != 234.0.0.0/8 && ip.dst != 239.0.0.0/8 && ip.dst != 240.0.0.0/4 && ip.dst != 255.255.255.255 && ip.dst != :: && ip.dst != ::1 && ip.dst != ff00::/8 && ip.dst != fe80::/10 && ip.dst != fc00::/7 && ip.dst != fd00::/8) || (ip.src == EXISTS! && ip.src != 0.0.0.0/8 && ip.src != 10.0.0.0/8 && ip.src != 100.64.0.0/10 && ip.src != 127.0.0.0/8 && ip.src != 169.254.0.0/16 && ip.src != 172.16.0.0/12 && ip.src != 192.0.0.0/24 && ip.src != 192.0.2.0/24 && ip.src != 192.88.99.0/24 && ip.src != 192.168.0.0/16 && ip.src != 198.18.0.0/15 && ip.src != 198.51.100.0/24 && ip.src != 203.0.113.0/24 && ip.src != 224.0.0.0/4 && ip.src != 232.0.0.0/8 && ip.src != 233.0.0.0/8 && ip.src != 234.0.0.0/8 && ip.src != 239.0.0.0/8 && ip.src != 240.0.0.0/4 && ip.src != 255.255.255.255 && ip.src != :: && ip.src != ::1 && ip.src != ff00::/8 && ip.src != fe80::/10 && ip.src != fc00::/7 && ip.src != fd00::/8)" }, "Arkime Sessions": { "expression": "event.provider == arkime" }, "Suricata Logs": { "expression": "event.provider == suricata" }, "Suricata Alerts": { "expression": "event.provider == suricata && event.dataset == alert" }, "Zeek Logs": { "expression": "event.provider == zeek" }, "Zeek conn.log": { "expression": "event.provider == zeek && event.dataset == conn" }, "Zeek Exclude conn.log": { "expression": "event.provider == zeek && event.dataset != conn" } }, "tableStates": { "sessionsNew": { "order": [ [ "firstPacket", "desc" ] ], "visibleHeaders": [ "protocol", "event.provider", "event.dataset", "firstPacket", "lastPacket", "src", "source.port", "dst", "destination.port", "network.packets", "dbby", "tags", "info" ] } } } +userAutoCreateTmpl={"userId": "${this.http_auth_http_user}", "userName": "${this.http_auth_http_user}", "enabled": true, "createEnabled": false, "webEnabled": true, "headerAuthEnabled": true, "emailSearch": true, "removeEnabled": false, "packetSearch": true, "hideStats": false, "hideFiles": false, "hidePcap": false, "disablePcapDownload": false, "settings": { "timezone": "local", "detailFormat": "last", "showTimestamps": "last", "sortColumn": "firstPacket", "sortDirection": "desc", "spiGraph": "protocol", "connSrcField": "source.ip", "connDstField": "destination.ip", "numPackets": "last", "theme" : "custom1: #222222,#E2E2E2,#FFFFFF,#00789E,#004A79,#017D73,#092B40,#42b7c5,#2A7580,#ecb30a,#333333,#89ADCC,#6D6D6D,#FFE7E7,#ECFEFF", "manualQuery": false }, "tableStates": { "sessionsNew": { "order": [ [ "firstPacket", "desc" ] ], "visibleHeaders": [ "protocol", "event.provider", "event.dataset", "firstPacket", "lastPacket", "src", "source.port", "dst", "destination.port", "network.packets", "dbby", "tags", "info" ] } } } parseSMTP=true parseSMB=true parseQSValue=false @@ -1181,6 +1182,18 @@ zeek.s7comm_read_szl.szl_index=db:zeek.s7comm_read_szl.szl_index;group:zeek_s7co zeek.s7comm_read_szl.return_code=db:zeek.s7comm_read_szl.return_code;group:zeek_s7comm_read_szl;kind:termfield;friendly:Return Code;help:Return Code zeek.s7comm_read_szl.return_code_name=db:zeek.s7comm_read_szl.return_code_name;group:zeek_s7comm_read_szl;kind:termfield;friendly:Return Message;help:Return Message +# s7comm_upload_download.log +# https://github.com/cisagov/icsnpp-s7comm +zeek.s7comm_upload_download.rosctr_name=db:zeek.s7comm_upload_download.rosctr_name;group:zeek_s7comm_upload_download;kind:termfield;friendly:Remote Operating Service Control Name;help:Remote Operating Service Control Name +zeek.s7comm_upload_download.function_name=db:zeek.s7comm_upload_download.function_name;group:zeek_s7comm_upload_download;kind:termfield;friendly:Function Name;help:Function Name +zeek.s7comm_upload_download.function_status=db:zeek.s7comm_upload_download.function_status;group:zeek_s7comm_upload_download;kind:termfield;friendly:Function Result;help:Function Result +zeek.s7comm_upload_download.session_id=db:zeek.s7comm_upload_download.session_id;group:zeek_s7comm_upload_download;kind:integer;friendly:Session ID;help:Session ID +zeek.s7comm_upload_download.blocklength=db:zeek.s7comm_upload_download.blocklength;group:zeek_s7comm_upload_download;kind:integer;friendly:Block Length;help:Block Length +zeek.s7comm_upload_download.filename=db:zeek.s7comm_upload_download.filename;group:zeek_s7comm_upload_download;kind:termfield;friendly:File Name;help:File Name +zeek.s7comm_upload_download.block_type=db:zeek.s7comm_upload_download.block_type;group:zeek_s7comm_upload_download;kind:termfield;friendly:Block Type;help:Block Type +zeek.s7comm_upload_download.block_number=db:zeek.s7comm_upload_download.block_number;group:zeek_s7comm_upload_download;kind:termfield;friendly:Block Number;help::Block Number +zeek.s7comm_upload_download.destination_filesystem=db:zeek.s7comm_upload_download.destination_filesystem;group:zeek_s7comm_upload_download;kind:termfield;friendly:Destination File System;help:Destination File System + # signatures.log zeek.signatures.note=db:zeek.signatures.note;group:zeek_signatures;kind:termfield;friendly:Note;help:Note zeek.signatures.signature_id=db:zeek.signatures.signature_id;group:zeek_signatures;kind:termfield;friendly:Signature ID;help:Signature ID @@ -2153,6 +2166,7 @@ zeek_rfb=require:zeek.rfb;title:Zeek rfb.log;fields:zeek.rfb.client_major_versio zeek_s7comm=require:zeek.s7comm;title:Zeek s7comm.log;fields:zeek.s7comm.rosctr_code,zeek.s7comm.rosctr_name,zeek.s7comm.pdu_reference,zeek.s7comm.function_code,zeek.s7comm.function_name,zeek.s7comm.subfunction_code,zeek.s7comm.subfunction_name,zeek.s7comm.error_class,zeek.s7comm.error_code zeek_s7comm_plus=require:zeek.s7comm_plus;title:Zeek s7comm_plus.log;fields:zeek.s7comm_plus.version,zeek.s7comm_plus.opcode,zeek.s7comm_plus.opcode_name,zeek.s7comm_plus.function_code,zeek.s7comm_plus.function_name zeek_s7comm_read_szl=require:zeek.s7comm_read_szl;title:Zeek s7comm_read_szl.log;fields:zeek.s7comm.pdu_reference,zeek.s7comm_read_szl.method,zeek.s7comm_read_szl.szl_id,zeek.s7comm_read_szl.szl_id_name,zeek.s7comm_read_szl.szl_index,zeek.s7comm_read_szl.return_code,zeek.s7comm_read_szl.return_code_name +zeek_s7comm_upload_download=require:zeek.s7comm_upload_download;title:Zeek s7comm_upload_download.log;fields:zeek.s7comm_upload_download.rosctr_name,zeek.s7comm.pdu_reference,zeek.s7comm_upload_download.function_name,zeek.s7comm_upload_download.function_status,zeek.s7comm_upload_download.session_id,zeek.s7comm_upload_download.blocklength,zeek.s7comm_upload_download.filename,zeek.s7comm_upload_download.block_type,zeek.s7comm_upload_download.block_number,zeek.s7comm_upload_download.destination_filesystem zeek_signatures=require:zeek.signatures;title:Zeek signatures.log;fields:event.module,rule.category,rule.name,vulnerability.category,vulnerability.enumeration,vulnerability.id,zeek.signatures.sub_message,zeek.signatures.signature_count,zeek.signatures.host_count zeek_sip=require:zeek.sip;title:Zeek sip.log;fields:zeek.sip.trans_depth,zeek.sip.method,zeek.sip.uri,zeek.sip.date,zeek.sip.request_from,zeek.sip.request_to,zeek.sip.response_from,zeek.sip.response_to,zeek.sip.reply_to,zeek.sip.call_id,zeek.sip.seq,zeek.sip.subject,zeek.sip.request_path,zeek.sip.response_path,zeek.sip.user_agent,zeek.sip.status_code,zeek.sip.status_msg,zeek.sip.warning,zeek.sip.request_body_len,zeek.sip.response_body_len,zeek.sip.content_type,zeek.sip.version zeek_smb_cmd=require:zeek.smb_cmd;title:Zeek smb_cmd.log;fields:zeek.smb_cmd.command,zeek.smb_cmd.sub_command,zeek.smb_cmd.argument,zeek.smb_cmd.status,zeek.smb_cmd.rtt,zeek.smb_cmd.version,zeek.smb_cmd.user,zeek.smb_cmd.tree,zeek.smb_cmd.tree_service diff --git a/arkime/etc/user_settings.json b/arkime/etc/user_settings.json index efab8c8cd..d4f43b3c2 100644 --- a/arkime/etc/user_settings.json +++ b/arkime/etc/user_settings.json @@ -15,7 +15,7 @@ "timezone": "local", "detailFormat": "last", "showTimestamps": "last", - "sortColumn": "start", + "sortColumn": "firstPacket", "sortDirection": "desc", "spiGraph": "protocol", "connSrcField": "source.ip", @@ -24,29 +24,6 @@ "theme" : "custom1: #222222,#E2E2E2,#FFFFFF,#00789E,#004A79,#017D73,#092B40,#42b7c5,#2A7580,#ecb30a,#333333,#89ADCC,#6D6D6D,#FFE7E7,#ECFEFF", "manualQuery": false }, - "views": { - "Public IP Addresses": { - "expression": "(country.dst == EXISTS!) || (country.src == EXISTS!) || (ip.dst == EXISTS! && ip.dst != 0.0.0.0/8 && ip.dst != 10.0.0.0/8 && ip.dst != 100.64.0.0/10 && ip.dst != 127.0.0.0/8 && ip.dst != 169.254.0.0/16 && ip.dst != 172.16.0.0/12 && ip.dst != 192.0.0.0/24 && ip.dst != 192.0.2.0/24 && ip.dst != 192.88.99.0/24 && ip.dst != 192.168.0.0/16 && ip.dst != 198.18.0.0/15 && ip.dst != 198.51.100.0/24 && ip.dst != 203.0.113.0/24 && ip.dst != 224.0.0.0/4 && ip.dst != 232.0.0.0/8 && ip.dst != 233.0.0.0/8 && ip.dst != 234.0.0.0/8 && ip.dst != 239.0.0.0/8 && ip.dst != 240.0.0.0/4 && ip.dst != 255.255.255.255 && ip.dst != :: && ip.dst != ::1 && ip.dst != ff00::/8 && ip.dst != fe80::/10 && ip.dst != fc00::/7 && ip.dst != fd00::/8) || (ip.src == EXISTS! && ip.src != 0.0.0.0/8 && ip.src != 10.0.0.0/8 && ip.src != 100.64.0.0/10 && ip.src != 127.0.0.0/8 && ip.src != 169.254.0.0/16 && ip.src != 172.16.0.0/12 && ip.src != 192.0.0.0/24 && ip.src != 192.0.2.0/24 && ip.src != 192.88.99.0/24 && ip.src != 192.168.0.0/16 && ip.src != 198.18.0.0/15 && ip.src != 198.51.100.0/24 && ip.src != 203.0.113.0/24 && ip.src != 224.0.0.0/4 && ip.src != 232.0.0.0/8 && ip.src != 233.0.0.0/8 && ip.src != 234.0.0.0/8 && ip.src != 239.0.0.0/8 && ip.src != 240.0.0.0/4 && ip.src != 255.255.255.255 && ip.src != :: && ip.src != ::1 && ip.src != ff00::/8 && ip.src != fe80::/10 && ip.src != fc00::/7 && ip.src != fd00::/8)" - }, - "Arkime Sessions": { - "expression": "event.provider == arkime" - }, - "Suricata Logs": { - "expression": "event.provider == suricata" - }, - "Suricata Alerts": { - "expression": "event.provider == suricata && event.dataset == alert" - }, - "Zeek Logs": { - "expression": "event.provider == zeek" - }, - "Zeek conn.log": { - "expression": "event.provider == zeek && event.dataset == conn" - }, - "Zeek Exclude conn.log": { - "expression": "event.provider == zeek && event.dataset != conn" - } - }, "tableStates": { "sessionsNew": { "order": [ diff --git a/arkime/etc/views/arkime_sessions.json b/arkime/etc/views/arkime_sessions.json new file mode 100644 index 000000000..f7ea047b7 --- /dev/null +++ b/arkime/etc/views/arkime_sessions.json @@ -0,0 +1,6 @@ +{ + "name": "Arkime Sessions", + "expression": "event.provider == arkime", + "roles": ["arkimeUser"], + "users" : [ ] +} \ No newline at end of file diff --git a/arkime/etc/views/public_ip_addresses.json b/arkime/etc/views/public_ip_addresses.json new file mode 100644 index 000000000..ac4d6261b --- /dev/null +++ b/arkime/etc/views/public_ip_addresses.json @@ -0,0 +1,6 @@ +{ + "name": "Public IP Addresses", + "expression": "(country.dst == EXISTS!) || (country.src == EXISTS!) || (ip.dst == EXISTS! && ip.dst != 0.0.0.0/8 && ip.dst != 10.0.0.0/8 && ip.dst != 100.64.0.0/10 && ip.dst != 127.0.0.0/8 && ip.dst != 169.254.0.0/16 && ip.dst != 172.16.0.0/12 && ip.dst != 192.0.0.0/24 && ip.dst != 192.0.2.0/24 && ip.dst != 192.88.99.0/24 && ip.dst != 192.168.0.0/16 && ip.dst != 198.18.0.0/15 && ip.dst != 198.51.100.0/24 && ip.dst != 203.0.113.0/24 && ip.dst != 224.0.0.0/4 && ip.dst != 232.0.0.0/8 && ip.dst != 233.0.0.0/8 && ip.dst != 234.0.0.0/8 && ip.dst != 239.0.0.0/8 && ip.dst != 240.0.0.0/4 && ip.dst != 255.255.255.255 && ip.dst != :: && ip.dst != ::1 && ip.dst != ff00::/8 && ip.dst != fe80::/10 && ip.dst != fc00::/7 && ip.dst != fd00::/8) || (ip.src == EXISTS! && ip.src != 0.0.0.0/8 && ip.src != 10.0.0.0/8 && ip.src != 100.64.0.0/10 && ip.src != 127.0.0.0/8 && ip.src != 169.254.0.0/16 && ip.src != 172.16.0.0/12 && ip.src != 192.0.0.0/24 && ip.src != 192.0.2.0/24 && ip.src != 192.88.99.0/24 && ip.src != 192.168.0.0/16 && ip.src != 198.18.0.0/15 && ip.src != 198.51.100.0/24 && ip.src != 203.0.113.0/24 && ip.src != 224.0.0.0/4 && ip.src != 232.0.0.0/8 && ip.src != 233.0.0.0/8 && ip.src != 234.0.0.0/8 && ip.src != 239.0.0.0/8 && ip.src != 240.0.0.0/4 && ip.src != 255.255.255.255 && ip.src != :: && ip.src != ::1 && ip.src != ff00::/8 && ip.src != fe80::/10 && ip.src != fc00::/7 && ip.src != fd00::/8)", + "roles": ["arkimeUser"], + "users" : [ ] +} \ No newline at end of file diff --git a/arkime/etc/views/suricata_alerts.json b/arkime/etc/views/suricata_alerts.json new file mode 100644 index 000000000..3b396c263 --- /dev/null +++ b/arkime/etc/views/suricata_alerts.json @@ -0,0 +1,6 @@ +{ + "name": "Suricata Alerts", + "expression": "event.provider == suricata && event.dataset == alert", + "roles": ["arkimeUser"], + "users" : [ ] +} \ No newline at end of file diff --git a/arkime/etc/views/suricata_logs.json b/arkime/etc/views/suricata_logs.json new file mode 100644 index 000000000..68fd363fb --- /dev/null +++ b/arkime/etc/views/suricata_logs.json @@ -0,0 +1,6 @@ +{ + "name": "Suricata Logs", + "expression": "event.provider == suricata", + "roles": ["arkimeUser"], + "users" : [ ] +} \ No newline at end of file diff --git a/arkime/etc/views/zeek_conn.json b/arkime/etc/views/zeek_conn.json new file mode 100644 index 000000000..f838517fa --- /dev/null +++ b/arkime/etc/views/zeek_conn.json @@ -0,0 +1,6 @@ +{ + "name": "Zeek conn.log", + "expression": "event.provider == zeek && event.dataset == conn", + "roles": ["arkimeUser"], + "users" : [ ] +} \ No newline at end of file diff --git a/arkime/etc/views/zeek_exclude_conn.json b/arkime/etc/views/zeek_exclude_conn.json new file mode 100644 index 000000000..ea81b1552 --- /dev/null +++ b/arkime/etc/views/zeek_exclude_conn.json @@ -0,0 +1,6 @@ +{ + "name": "Zeek Exclude conn.log", + "expression": "event.provider == zeek && event.dataset != conn", + "roles": ["arkimeUser"], + "users" : [ ] +} \ No newline at end of file diff --git a/arkime/etc/views/zeek_logs.json b/arkime/etc/views/zeek_logs.json new file mode 100644 index 000000000..d91aede59 --- /dev/null +++ b/arkime/etc/views/zeek_logs.json @@ -0,0 +1,6 @@ +{ + "name": "Zeek Logs", + "expression": "event.provider == zeek", + "roles": ["arkimeUser"], + "users" : [ ] +} diff --git a/arkime/patch/capture_event_dataset.patch b/arkime/patch/capture_event_dataset.patch deleted file mode 100644 index a8862c7d6..000000000 --- a/arkime/patch/capture_event_dataset.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/capture/db.c b/capture/db.c -index 2515b177..0a6d06bd 100644 ---- a/capture/db.c -+++ b/capture/db.c -@@ -71,6 +71,7 @@ LOCAL int dbExit; - LOCAL char *esBulkQuery; - LOCAL int esBulkQueryLen; - LOCAL char *ecsEventProvider; -+LOCAL char *ecsEventDataset; - - extern uint64_t packetStats[MOLOCH_PACKET_MAX]; - -@@ -906,8 +907,12 @@ void moloch_db_save_session(MolochSession_t *session, int final) - } - BSB_EXPORT_cstr(jbsb, "],"); - -- if (ecsEventProvider) { -+ if (ecsEventProvider && ecsEventDataset) { -+ BSB_EXPORT_sprintf(jbsb, "\"event\":{\"provider\":\"%s\", \"dataset\":\"%s\"},", ecsEventProvider, ecsEventDataset); -+ } else if (ecsEventProvider) { - BSB_EXPORT_sprintf(jbsb, "\"event\":{\"provider\":\"%s\"},", ecsEventProvider); -+ } else if (ecsEventDataset) { -+ BSB_EXPORT_sprintf(jbsb, "\"event\":{\"dataset\":\"%s\"},", ecsEventDataset); - } - - int inGroupNum = 0; -@@ -2661,6 +2666,7 @@ void moloch_db_init() - } - - ecsEventProvider = moloch_config_str(NULL, "ecsEventProvider", NULL); -+ ecsEventDataset = moloch_config_str(NULL, "ecsEventDataset", NULL); - - int thread; - for (thread = 0; thread < config.packetThreads; thread++) { diff --git a/arkime/patch/db_pl_quiet_backup_warning.patch b/arkime/patch/db_pl_quiet_backup_warning.patch index 4e43ebb2f..49248c6be 100644 --- a/arkime/patch/db_pl_quiet_backup_warning.patch +++ b/arkime/patch/db_pl_quiet_backup_warning.patch @@ -1,8 +1,8 @@ diff --git a/db/db.pl b/db/db.pl -index 1b24b6a1..c8034ec4 100755 +index bcb3948a..995c7e4e 100755 --- a/db/db.pl +++ b/db/db.pl -@@ -7111,7 +7111,7 @@ my $health = dbCheckHealth(); +@@ -7383,7 +7383,7 @@ my $health = dbCheckHealth(); my $nodes = esGet("/_nodes"); $main::numberOfNodes = dataNodes($nodes->{nodes}); diff --git a/arkime/patch/field_best_priority.patch b/arkime/patch/field_best_priority.patch deleted file mode 100644 index 799be8d9e..000000000 --- a/arkime/patch/field_best_priority.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/capture/db.c b/capture/db.c -index c1350507..b29a41ac 100644 ---- a/capture/db.c -+++ b/capture/db.c -@@ -2411,7 +2411,7 @@ void moloch_db_add_field(char *group, char *kind, char *expression, char *friend - } - - BSB_EXPORT_u08(bsb, '}'); -- moloch_http_schedule(esServer, "POST", key, key_len, json, BSB_LENGTH(bsb), NULL, MOLOCH_HTTP_PRIORITY_NORMAL, NULL, NULL); -+ moloch_http_schedule(esServer, "POST", key, key_len, json, BSB_LENGTH(bsb), NULL, MOLOCH_HTTP_PRIORITY_BEST, NULL, NULL); - } - /******************************************************************************/ - void moloch_db_update_field(char *expression, char *name, char *value) diff --git a/arkime/patch/fields_db_max_5000.patch b/arkime/patch/fields_db_max_5000.patch index b4799e580..cf5b5e0cb 100644 --- a/arkime/patch/fields_db_max_5000.patch +++ b/arkime/patch/fields_db_max_5000.patch @@ -1,8 +1,8 @@ diff --git a/capture/db.c b/capture/db.c -index c1350507..1ae57143 100644 +index 70be2c19..dd3bfad9 100644 --- a/capture/db.c +++ b/capture/db.c -@@ -2325,7 +2325,7 @@ LOCAL void moloch_db_load_fields() +@@ -2321,7 +2321,7 @@ LOCAL void moloch_db_load_fields() char key[100]; int key_len; @@ -12,7 +12,7 @@ index c1350507..1ae57143 100644 if (!data) { diff --git a/capture/moloch.h b/capture/moloch.h -index 2a63d913..4b7dc4f0 100644 +index b5d866bf..b7bb2f09 100644 --- a/capture/moloch.h +++ b/capture/moloch.h @@ -357,7 +357,7 @@ enum MolochRotate { @@ -25,7 +25,7 @@ index 2a63d913..4b7dc4f0 100644 #define MOLOCH_FIELDS_CNT_MAX (MOLOCH_FIELDS_DB_MAX*2) #define MOLOCH_FIELD_EXSPECIAL_START (MOLOCH_FIELDS_CNT_MAX) diff --git a/capture/plugins/wise.c b/capture/plugins/wise.c -index 2f98ebe1..0e7b4002 100644 +index 252683a0..e26d3ddb 100644 --- a/capture/plugins/wise.c +++ b/capture/plugins/wise.c @@ -111,7 +111,7 @@ typedef struct wiseitem_head { @@ -38,10 +38,10 @@ index 2f98ebe1..0e7b4002 100644 BSB bsb; WiseItem_t *items[WISE_MAX_REQUEST_ITEMS]; diff --git a/viewer/db.js b/viewer/db.js -index 1eebc559..2046f17b 100644 +index 299d2f31..e5b853db 100644 --- a/viewer/db.js +++ b/viewer/db.js -@@ -1835,7 +1835,7 @@ exports.sid2Index = function (id, options) { +@@ -1729,7 +1729,7 @@ exports.sid2Index = function (id, options) { }; exports.loadFields = async () => { diff --git a/arkime/patch/footer_links.patch b/arkime/patch/footer_links.patch index b89cf4e4d..3c92d51c7 100644 --- a/arkime/patch/footer_links.patch +++ b/arkime/patch/footer_links.patch @@ -1,14 +1,14 @@ diff --git a/viewer/vueapp/src/components/utils/Footer.vue b/viewer/vueapp/src/components/utils/Footer.vue -index 50e30c99..d67dfb36 100644 +index 84b28a45..320dd70c 100644 --- a/viewer/vueapp/src/components/utils/Footer.vue +++ b/viewer/vueapp/src/components/utils/Footer.vue @@ -4,9 +4,11 @@

- Arkime v{{molochVersion}} | + Arkime v{{ version }} | - arkime.com + arkime.com 🦉 -+ | Malc⦿lm 📄 ++ | Malc⦿lm 📄 + | Dashboards 📊 - | {{ responseTime | commaString }}ms diff --git a/arkime/patch/hide_pcap_download_without_file.patch b/arkime/patch/hide_pcap_download_without_file.patch deleted file mode 100644 index 9dd5a8468..000000000 --- a/arkime/patch/hide_pcap_download_without_file.patch +++ /dev/null @@ -1,55 +0,0 @@ -diff --git a/viewer/views/sessionDetail.pug b/viewer/views/sessionDetail.pug -index 7a97fde7..89d56aee 100644 ---- a/viewer/views/sessionDetail.pug -+++ b/viewer/views/sessionDetail.pug -@@ -2,30 +2,33 @@ ul.nav.nav-pills.mb-3 - if (session.rootId) - li.nav-item - a.nav-link.cursor-pointer(@click='allSessions(' + '"' + session.rootId + '"' + ', ' + session.firstPacket + ')') All Sessions -+ if (session.packetPos && session.packetPos.length > 0) -+ if (session.rootId) -+ li.nav-item -+ a.nav-link(href=session.node + '/pcap/' + session.id + '.pcap', target="_blank", download=session.id + '-segment.pcap') Download Segment Pcap -+ li.nav-item -+ a.nav-link(href=session.node + '/entirePcap/' + session.rootId + '.pcap', target="_blank", download=session.id + '.pcap') Download Entire Pcap -+ else -+ li.nav-item -+ a.nav-link(href=session.node + '/pcap/' + session.id + '.pcap', target="_blank", v-has-permission="'!disablePcapDownload'", v-b-tooltip.hover.bottom.d300="'Download the PCAP file for this session.'", download=session.id + '.pcap') -+ span.fa.fa-download -+ |  Download PCAP - li.nav-item -- a.nav-link(href=session.node + '/pcap/' + session.id + '.pcap', target="_blank", download=session.id + '-segment.pcap') Download Segment Pcap -+ a.nav-link(href=session.node + '/raw/' + session.id + '?type=src', target="_blank", v-b-tooltip.hover.bottom.d300="'Download the raw source packets for this session.'", download=session.id + '-src-raw') -+ span.fa.fa-arrow-circle-up -+ |  Source Raw - li.nav-item -- a.nav-link(href=session.node + '/entirePcap/' + session.rootId + '.pcap', target="_blank", download=session.id + '.pcap') Download Entire Pcap -- else -- li.nav-item -- a.nav-link(href=session.node + '/pcap/' + session.id + '.pcap', target="_blank", v-has-permission="'!disablePcapDownload'", v-b-tooltip.hover.bottom.d300="'Download the PCAP file for this session.'", download=session.id + '.pcap') -- span.fa.fa-download -- |  Download PCAP -- li.nav-item -- a.nav-link(href=session.node + '/raw/' + session.id + '?type=src', target="_blank", v-b-tooltip.hover.bottom.d300="'Download the raw source packets for this session.'", download=session.id + '-src-raw') -- span.fa.fa-arrow-circle-up -- |  Source Raw -- li.nav-item -- a.nav-link(href=session.node + '/raw/' + session.id + '?type=dst', target="_blank", v-b-tooltip.hover.bottom.d300="'Download the raw destination packets for this session.'", download=session.id + '-dst-raw') -- span.fa.fa-arrow-circle-down -- |  Destination Raw -+ a.nav-link(href=session.node + '/raw/' + session.id + '?type=dst', target="_blank", v-b-tooltip.hover.bottom.d300="'Download the raw destination packets for this session.'", download=session.id + '-dst-raw') -+ span.fa.fa-arrow-circle-down -+ |  Destination Raw - li.nav-item - a.nav-link.cursor-pointer(@click="openPermalink", v-b-tooltip.hover.bottom.d300="'Navigate to the sessions page containing just this session. You can use this link to share this session with other users.'") - span.fa.fa-link - |  Link - b-dropdown.nav-item(text="Actions", size="sm") -- b-dropdown-item(@click="exportPCAP", v-has-permission="'!disablePcapDownload'") -- | Export PCAP -+ if (session.packetPos && session.packetPos.length > 0) -+ b-dropdown-item(@click="exportPCAP", v-has-permission="'!disablePcapDownload'") -+ | Export PCAP - b-dropdown-item(@click="addTags") - | Add Tags - b-dropdown-item(@click="removeTags", v-has-permission="'removeEnabled'") diff --git a/arkime/patch/packetpos_arkime_issues_1952_1953.patch b/arkime/patch/packetpos_arkime_issues_1952_1953.patch deleted file mode 100644 index 9f96e3b04..000000000 --- a/arkime/patch/packetpos_arkime_issues_1952_1953.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/viewer/apiSessions.js b/viewer/apiSessions.js -index 367ef7d9..4ec216b7 100644 ---- a/viewer/apiSessions.js -+++ b/viewer/apiSessions.js -@@ -1124,6 +1124,10 @@ module.exports = (Config, Db, internals, ViewerUtils) => { - - const fields = session.fields; - -+ if (!fields.packetPos) { -+ return endCb(null); -+ } -+ - if (maxPackets && fields.packetPos.length > maxPackets) { - fields.packetPos.length = maxPackets; - } diff --git a/arkime/patch/remove_upload.patch b/arkime/patch/remove_upload.patch index 8eb19d01d..4c17cc35c 100644 --- a/arkime/patch/remove_upload.patch +++ b/arkime/patch/remove_upload.patch @@ -1,8 +1,8 @@ diff --git a/viewer/vueapp/src/router/index.js b/viewer/vueapp/src/router/index.js -index 02494926..a3c974f9 100644 +index 104506cf..0850f2cd 100644 --- a/viewer/vueapp/src/router/index.js +++ b/viewer/vueapp/src/router/index.js -@@ -11,7 +11,6 @@ import Spiview from '@/components/spiview/Spiview'; +@@ -12,7 +12,6 @@ import Spiview from '@/components/spiview/Spiview'; import Spigraph from '@/components/spigraph/Spigraph'; import Connections from '@/components/connections/Connections'; import Settings from '@/components/settings/Settings'; @@ -10,7 +10,7 @@ index 02494926..a3c974f9 100644 import Hunt from '@/components/hunt/Hunt'; import Moloch404 from '@/components/utils/404'; -@@ -86,11 +85,6 @@ const router = new Router({ +@@ -92,11 +91,6 @@ const router = new Router({ name: 'Settings', component: Settings }, diff --git a/arkime/patch/spi_sort_zeek.patch b/arkime/patch/spi_sort_malcolm.patch similarity index 88% rename from arkime/patch/spi_sort_zeek.patch rename to arkime/patch/spi_sort_malcolm.patch index 73afa84bc..1a16f13cf 100644 --- a/arkime/patch/spi_sort_zeek.patch +++ b/arkime/patch/spi_sort_malcolm.patch @@ -1,13 +1,13 @@ diff --git a/viewer/vueapp/src/components/spiview/Spiview.vue b/viewer/vueapp/src/components/spiview/Spiview.vue -index 66e08ecf..f7156b80 100644 +index 76dd613b..b8117034 100644 --- a/viewer/vueapp/src/components/spiview/Spiview.vue +++ b/viewer/vueapp/src/components/spiview/Spiview.vue -@@ -915,6 +915,8 @@ export default { - +@@ -960,6 +960,8 @@ export default { + // sorted list of categories for the view this.categoryList = Object.keys(this.categoryObjects).sort(); + this.categoryList.splice(this.categoryList.indexOf('malcolm'), 1); + this.categoryList.unshift('malcolm'); this.categoryList.splice(this.categoryList.indexOf('general'), 1); this.categoryList.unshift('general'); - + diff --git a/arkime/patch/viewer_db_opensearchv2_keyword_hack.patch b/arkime/patch/viewer_db_opensearchv2_keyword_hack.patch index 4f93580cf..9cf920a01 100644 --- a/arkime/patch/viewer_db_opensearchv2_keyword_hack.patch +++ b/arkime/patch/viewer_db_opensearchv2_keyword_hack.patch @@ -1,8 +1,8 @@ diff --git a/viewer/db.js b/viewer/db.js -index 1eebc559..45645fd3 100644 +index 299d2f31..1b31cace 100644 --- a/viewer/db.js +++ b/viewer/db.js -@@ -382,7 +382,13 @@ function fixSessionFields (fields, unflatten) { +@@ -386,7 +386,13 @@ function fixSessionFields (fields, unflatten) { delete fields[f]; for (let i = 0; i < path.length; i++) { if (i === path.length - 1) { diff --git a/arkime/patch/viewer_wider_field_detail.patch b/arkime/patch/viewer_wider_field_detail.patch index 3543771f2..ec0cc5376 100644 --- a/arkime/patch/viewer_wider_field_detail.patch +++ b/arkime/patch/viewer_wider_field_detail.patch @@ -1,8 +1,8 @@ diff --git a/viewer/vueapp/src/components/sessions/SessionDetail.vue b/viewer/vueapp/src/components/sessions/SessionDetail.vue -index a15f3b52..e7b0cd7b 100644 +index 572c41f3..60143d82 100644 --- a/viewer/vueapp/src/components/sessions/SessionDetail.vue +++ b/viewer/vueapp/src/components/sessions/SessionDetail.vue -@@ -844,7 +844,7 @@ export default { +@@ -846,7 +846,7 @@ export default { .session-detail dt { float: left; clear: left; @@ -11,7 +11,7 @@ index a15f3b52..e7b0cd7b 100644 text-align: right; margin-right: 6px; line-height: 1.7; -@@ -852,7 +852,7 @@ export default { +@@ -854,7 +854,7 @@ export default { } .session-detail dd { diff --git a/arkime/scripts/arkime-needs-upgrade.sh b/arkime/scripts/arkime-needs-upgrade.sh index 6bd5e474d..12fe4cfbf 100755 --- a/arkime/scripts/arkime-needs-upgrade.sh +++ b/arkime/scripts/arkime-needs-upgrade.sh @@ -28,9 +28,12 @@ set -e [arkime_files_v]=arkime_files_v30 [arkime_hunts_v]=arkime_hunts_v30 [arkime_lookups_v]=arkime_lookups_v30 + [arkime_notifiers_v]=arkime_notifiers_v40 [arkime_queries_v]=arkime_queries_v30 + [arkime_sequence_v]=arkime_sequence_v30 [arkime_stats_v]=arkime_stats_v30 [arkime_users_v]=arkime_users_v30 + [arkime_views_v]=arkime_views_v40 ) # get a list of all current indices and loop over them diff --git a/arkime/scripts/initarkime.sh b/arkime/scripts/initarkime.sh index a0a0fea64..d93ef66fa 100755 --- a/arkime/scripts/initarkime.sh +++ b/arkime/scripts/initarkime.sh @@ -58,9 +58,20 @@ if [[ $(curl "${CURL_CONFIG_PARAMS[@]}" -fs -XGET -H'Content-Type: application/j $ARKIME_DIR/bin/capture $DB_SSL_FLAG --packetcnt 0 -r /tmp/not_a_packet.pcap >/dev/null 2>&1 rm -f /tmp/not_a_packet.pcap + echo "Initializing views..." + + for VIEW_FILE in "$ARKIME_DIR"/etc/views/*.json; do + TEMP_JSON=$(mktemp --suffix=.json) + RANDOM_ID="$(openssl rand -base64 14 | sed -E 's/[^[:alnum:][:space:]]+/_/g')" + echo "Creating view $(jq '.name' < "${VIEW_FILE}")" + jq ". += {\"user\": \"${MALCOLM_USERNAME}\"}" < "${VIEW_FILE}" >"${TEMP_JSON}" + curl "${CURL_CONFIG_PARAMS[@]}" -sS --output /dev/null -H'Content-Type: application/json' -XPOST "${OPENSEARCH_URL}/arkime_views/_doc/${RANDOM_ID}" -d "@${TEMP_JSON}" + rm -f "${TEMP_JSON}" + done + echo "Setting defaults..." - curl "${CURL_CONFIG_PARAMS[@]}" -sS -H'Content-Type: application/json' -XPOST "${OPENSEARCH_URL}/arkime_users/_update/$MALCOLM_USERNAME" -d "@$ARKIME_DIR/etc/user_settings.json" + curl "${CURL_CONFIG_PARAMS[@]}" -sS --output /dev/null -H'Content-Type: application/json' -XPOST "${OPENSEARCH_URL}/arkime_users/_update/$MALCOLM_USERNAME" -d "@$ARKIME_DIR/etc/user_settings.json" echo -e "\nOpenSearch database initialized!\n" @@ -84,7 +95,7 @@ fi # if/else OpenSearch database initialized # increase OpenSearch max shards per node from default if desired if [[ -n $OPENSEARCH_MAX_SHARDS_PER_NODE ]]; then # see https://github.com/elastic/elasticsearch/issues/40803 - curl "${CURL_CONFIG_PARAMS[@]}" -sS -H'Content-Type: application/json' -XPUT "${OPENSEARCH_URL}/_cluster/settings" -d "{ \"persistent\": { \"cluster.max_shards_per_node\": \"$OPENSEARCH_MAX_SHARDS_PER_NODE\" } }" + curl "${CURL_CONFIG_PARAMS[@]}" -sS --output /dev/null -H'Content-Type: application/json' -XPUT "${OPENSEARCH_URL}/_cluster/settings" -d "{ \"persistent\": { \"cluster.max_shards_per_node\": \"$OPENSEARCH_MAX_SHARDS_PER_NODE\" } }" fi # before running viewer, call _refresh to make sure everything is available for search first @@ -93,3 +104,4 @@ curl "${CURL_CONFIG_PARAMS[@]}" -sS -XPOST "${OPENSEARCH_URL}/_refresh" touch /var/run/arkime/initialized # the (viewer|wise)_service.sh scripts will start/restart those processes + diff --git a/arkime/supervisord.conf b/arkime/supervisord.conf index 2bb98ec8b..863e55349 100644 --- a/arkime/supervisord.conf +++ b/arkime/supervisord.conf @@ -69,12 +69,3 @@ directory=%(ENV_ARKIME_DIR)s stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 redirect_stderr=true - -[program:readme] -command=python3 -m http.server 8000 -directory=%(ENV_ARKIME_DIR)s/doc -stopasgroup=true -killasgroup=true -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 -redirect_stderr=true diff --git a/arkime/wise/source.zeeklogs.js b/arkime/wise/source.zeeklogs.js index 0d4be5c13..b495c2ee0 100644 --- a/arkime/wise/source.zeeklogs.js +++ b/arkime/wise/source.zeeklogs.js @@ -1564,6 +1564,15 @@ class MalcolmSource extends WISESource { "zeek.s7comm_read_szl.szl_id", "zeek.s7comm_read_szl.szl_id_name", "zeek.s7comm_read_szl.szl_index", + "zeek.s7comm_upload_download.block_number", + "zeek.s7comm_upload_download.block_type", + "zeek.s7comm_upload_download.blocklength", + "zeek.s7comm_upload_download.destination_filesystem", + "zeek.s7comm_upload_download.filename", + "zeek.s7comm_upload_download.function_name", + "zeek.s7comm_upload_download.function_status", + "zeek.s7comm_upload_download.rosctr_name", + "zeek.s7comm_upload_download.session_id", "zeek.signatures.event_message", "zeek.signatures.hits.Capa", "zeek.signatures.hits.ClamAV", diff --git a/dashboards/dashboards/5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json b/dashboards/dashboards/5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json index 7e58dc941..72e72c734 100644 --- a/dashboards/dashboards/5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json +++ b/dashboards/dashboards/5694ca60-cbdf-11ec-a50a-5fedd672f5c5.json @@ -180,7 +180,7 @@ "version": "WzIzMjgsMV0=", "attributes": { "title": "Alerts - Tags", - "visState": "{\"title\":\"Alerts - Tags\",\"type\":\"tagcloud\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"tags\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":100,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"schema\":\"segment\"}],\"params\":{\"scale\":\"square root\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":48,\"showLabel\":false}}", + "visState": "{\"title\":\"Alerts - Tags\",\"type\":\"tagcloud\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"tags\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":100,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"schema\":\"segment\"}}],\"params\":{\"scale\":\"square root\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":48,\"showLabel\":false}}", "uiStateJSON": "{}", "description": "", "version": 1, diff --git a/dashboards/dashboards/e76d05c0-eb9f-11e9-a384-0fcf32210194.json b/dashboards/dashboards/e76d05c0-eb9f-11e9-a384-0fcf32210194.json index 1df551a6c..0b2177eea 100644 --- a/dashboards/dashboards/e76d05c0-eb9f-11e9-a384-0fcf32210194.json +++ b/dashboards/dashboards/e76d05c0-eb9f-11e9-a384-0fcf32210194.json @@ -1,5 +1,5 @@ { - "version": "2.1.0", + "version": "2.3.0", "objects": [ { "id": "e76d05c0-eb9f-11e9-a384-0fcf32210194", @@ -7,18 +7,18 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:34:25.353Z", - "version": "Wzg5OCwxXQ==", + "updated_at": "2022-10-10T19:24:43.925Z", + "version": "WzkwNiwxXQ==", "attributes": { "title": "S7comm / S7comm Plus", "hits": 0, "description": "", - "panelsJSON": "[{\"version\":\"2.1.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":8,\"h\":31,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"},{\"version\":\"2.1.0\",\"gridData\":{\"x\":8,\"y\":0,\"w\":8,\"h\":20,\"i\":\"5716abc8-3472-485a-9fd9-492f775cc371\"},\"panelIndex\":\"5716abc8-3472-485a-9fd9-492f775cc371\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"},{\"version\":\"2.1.0\",\"gridData\":{\"x\":16,\"y\":0,\"w\":32,\"h\":20,\"i\":\"2a9754ed-092c-4afd-9712-203f13d1c369\"},\"panelIndex\":\"2a9754ed-092c-4afd-9712-203f13d1c369\",\"embeddableConfig\":{},\"panelRefName\":\"panel_2\"},{\"version\":\"2.1.0\",\"gridData\":{\"x\":8,\"y\":20,\"w\":18,\"h\":36,\"i\":\"13aac6f7-d251-4845-b5b6-3c1515132504\"},\"panelIndex\":\"13aac6f7-d251-4845-b5b6-3c1515132504\",\"embeddableConfig\":{},\"panelRefName\":\"panel_3\"},{\"version\":\"2.1.0\",\"gridData\":{\"x\":26,\"y\":20,\"w\":10,\"h\":18,\"i\":\"82ee0b2b-60d0-4271-9d3e-acbd5366e660\"},\"panelIndex\":\"82ee0b2b-60d0-4271-9d3e-acbd5366e660\",\"embeddableConfig\":{},\"panelRefName\":\"panel_4\"},{\"version\":\"2.1.0\",\"gridData\":{\"x\":36,\"y\":20,\"w\":12,\"h\":18,\"i\":\"4b9b201e-4f7c-4e17-a3a8-308fe4ec25e9\"},\"panelIndex\":\"4b9b201e-4f7c-4e17-a3a8-308fe4ec25e9\",\"embeddableConfig\":{},\"panelRefName\":\"panel_5\"},{\"version\":\"2.1.0\",\"gridData\":{\"x\":0,\"y\":31,\"w\":8,\"h\":16,\"i\":\"a8447ab6-5810-43a6-a42c-97b6776203c0\"},\"panelIndex\":\"a8447ab6-5810-43a6-a42c-97b6776203c0\",\"embeddableConfig\":{},\"panelRefName\":\"panel_6\"},{\"version\":\"2.1.0\",\"gridData\":{\"x\":26,\"y\":38,\"w\":22,\"h\":18,\"i\":\"4ed75bae-60f2-478f-b1d7-3954019d6340\"},\"panelIndex\":\"4ed75bae-60f2-478f-b1d7-3954019d6340\",\"embeddableConfig\":{},\"panelRefName\":\"panel_7\"},{\"version\":\"2.1.0\",\"gridData\":{\"x\":0,\"y\":56,\"w\":48,\"h\":32,\"i\":\"edae9dd1-a37e-420d-9154-7841a8c62098\"},\"panelIndex\":\"edae9dd1-a37e-420d-9154-7841a8c62098\",\"embeddableConfig\":{},\"panelRefName\":\"panel_8\"}]", + "panelsJSON": "[{\"version\":\"2.3.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":8,\"h\":37,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":8,\"y\":0,\"w\":8,\"h\":20,\"i\":\"5716abc8-3472-485a-9fd9-492f775cc371\"},\"panelIndex\":\"5716abc8-3472-485a-9fd9-492f775cc371\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":16,\"y\":0,\"w\":32,\"h\":20,\"i\":\"2a9754ed-092c-4afd-9712-203f13d1c369\"},\"panelIndex\":\"2a9754ed-092c-4afd-9712-203f13d1c369\",\"embeddableConfig\":{},\"panelRefName\":\"panel_2\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":8,\"y\":20,\"w\":13,\"h\":34,\"i\":\"13aac6f7-d251-4845-b5b6-3c1515132504\"},\"panelIndex\":\"13aac6f7-d251-4845-b5b6-3c1515132504\",\"embeddableConfig\":{},\"panelRefName\":\"panel_3\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":21,\"y\":20,\"w\":13,\"h\":17,\"i\":\"82ee0b2b-60d0-4271-9d3e-acbd5366e660\"},\"panelIndex\":\"82ee0b2b-60d0-4271-9d3e-acbd5366e660\",\"embeddableConfig\":{},\"panelRefName\":\"panel_4\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":34,\"y\":20,\"w\":14,\"h\":17,\"i\":\"4b9b201e-4f7c-4e17-a3a8-308fe4ec25e9\"},\"panelIndex\":\"4b9b201e-4f7c-4e17-a3a8-308fe4ec25e9\",\"embeddableConfig\":{},\"panelRefName\":\"panel_5\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":0,\"y\":37,\"w\":8,\"h\":17,\"i\":\"a8447ab6-5810-43a6-a42c-97b6776203c0\"},\"panelIndex\":\"a8447ab6-5810-43a6-a42c-97b6776203c0\",\"embeddableConfig\":{},\"panelRefName\":\"panel_6\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":21,\"y\":37,\"w\":13,\"h\":17,\"i\":\"7355f72d-16de-45d9-846d-4e1e9f35f897\"},\"panelIndex\":\"7355f72d-16de-45d9-846d-4e1e9f35f897\",\"embeddableConfig\":{},\"panelRefName\":\"panel_7\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":34,\"y\":37,\"w\":14,\"h\":17,\"i\":\"4ed75bae-60f2-478f-b1d7-3954019d6340\"},\"panelIndex\":\"4ed75bae-60f2-478f-b1d7-3954019d6340\",\"embeddableConfig\":{},\"panelRefName\":\"panel_8\"},{\"version\":\"2.3.0\",\"gridData\":{\"x\":0,\"y\":54,\"w\":48,\"h\":32,\"i\":\"edae9dd1-a37e-420d-9154-7841a8c62098\"},\"panelIndex\":\"edae9dd1-a37e-420d-9154-7841a8c62098\",\"embeddableConfig\":{},\"panelRefName\":\"panel_9\"}]", "optionsJSON": "{\"useMargins\":true}", "version": 1, "timeRestore": false, "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"highlightAll\":false,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"*\"},\"filter\":[]}" + "searchSourceJSON": "{\"highlightAll\":false,\"version\":true,\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" } }, "references": [ @@ -60,10 +60,15 @@ { "name": "panel_7", "type": "visualization", - "id": "39091f40-18c0-11ed-9abd-97fb0b4c6d6c" + "id": "c1a225a0-48d0-11ed-ac36-efb71d661952" }, { "name": "panel_8", + "type": "visualization", + "id": "39091f40-18c0-11ed-9abd-97fb0b4c6d6c" + }, + { + "name": "panel_9", "type": "search", "id": "a827b610-18b7-11ed-9815-dd8187ffaa35" } @@ -78,8 +83,8 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:11:15.749Z", - "version": "WzgwMiwxXQ==", + "updated_at": "2022-10-10T18:59:19.274Z", + "version": "WzgwMSwxXQ==", "attributes": { "title": "Network Logs", "visState": "{\"title\":\"Network Logs\",\"type\":\"markdown\",\"params\":{\"markdown\":\"### General\\n[Overview](/dashboards/app/dashboards#/view/0ad3d7c2-3441-485e-9dfe-dbb22e84e576) \\n[Security Overview](/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405) \\n[ICS/IoT Security Overview](/dashboards/app/dashboards#/view/4a4bde20-4760-11ea-949c-bbb5a9feecbf) \\n[Severity](/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330) \\n[Connections](/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4) \\n[Actions and Results](/dashboards/app/dashboards#/view/a33e0a50-afcd-11ea-993f-b7d8522a8bed) \\n[Files](/dashboards/app/dashboards#/view/9ee51f94-3316-4fc5-bd89-93a52af69714) \\n[Executables](/dashboards/app/dashboards#/view/0a490422-0ce9-44bf-9a2d-19329ddde8c3) \\n[Software](/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85) \\n[Zeek Intelligence](/dashboards/app/dashboards#/view/36ed695f-edcc-47c1-b0ec-50d20c93ce0f) \\n[Zeek Notices](/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b) \\n[Zeek Weird](/dashboards/app/dashboards#/view/1fff49f6-0199-4a0f-820b-721aff9ff1f1) \\n[Signatures](/dashboards/app/dashboards#/view/665d1610-523d-11e9-a30e-e3576242f3ed) \\n[Suricata Alerts](/dashboards/app/dashboards#/view/5694ca60-cbdf-11ec-a50a-5fedd672f5c5) \\n[↪ Arkime](/sessions) \\n\\n### Common Protocols\\n[DCE/RPC](/dashboards/app/dashboards#/view/432af556-c5c0-4cc3-8166-b274b4e3a406) ● [DHCP](/dashboards/app/dashboards#/view/2d98bb8e-214c-4374-837b-20e1bcd63a5e) ● [DNS](/dashboards/app/dashboards#/view/2cf94cd0-ecab-40a5-95a7-8419f3a39cd9) ● [FTP](/dashboards/app/dashboards#/view/078b9aa5-9bd4-4f02-ae5e-cf80fa6f887b) / [TFTP](/dashboards/app/dashboards#/view/bf5efbb0-60f1-11eb-9d60-dbf0411cfc48) ● [HTTP](/dashboards/app/dashboards#/view/37041ee1-79c0-4684-a436-3173b0e89876) ● [IRC](/dashboards/app/dashboards#/view/76f2f912-80da-44cd-ab66-6a73c8344cc3) ● [Kerberos](/dashboards/app/dashboards#/view/82da3101-2a9c-4ae2-bb61-d447a3fbe673) ● [LDAP](/dashboards/app/dashboards#/view/05e3e000-f118-11e9-acda-83a8e29e1a24) ● [MQTT](/dashboards/app/dashboards#/view/87a32f90-ef58-11e9-974e-9d600036d105) ● [MySQL](/dashboards/app/dashboards#/view/50ced171-1b10-4c3f-8b67-2db9635661a6) ● [NTLM](/dashboards/app/dashboards#/view/543118a9-02d7-43fe-b669-b8652177fc37) ● [NTP](/dashboards/app/dashboards#/view/af5df620-eeb6-11e9-bdef-65a192b7f586) ● [OSPF](/dashboards/app/dashboards#/view/1cc01ff0-5205-11ec-a62c-7bc80e88f3f0) ● [QUIC](/dashboards/app/dashboards#/view/11ddd980-e388-11e9-b568-cf17de8e860c) ● [RADIUS](/dashboards/app/dashboards#/view/ae79b7d1-4281-4095-b2f6-fa7eafda9970) ● [RDP](/dashboards/app/dashboards#/view/7f41913f-cba8-43f5-82a8-241b7ead03e0) ● [RFB](/dashboards/app/dashboards#/view/f77bf097-18a8-465c-b634-eb2acc7a4f26) ● [SIP](/dashboards/app/dashboards#/view/0b2354ae-0fe9-4fd9-b156-1c3870e5c7aa) ● [SMB](/dashboards/app/dashboards#/view/42e831b9-41a9-4f35-8b7d-e1566d368773) ● [SMTP](/dashboards/app/dashboards#/view/bb827f8e-639e-468c-93c8-9f5bc132eb8f) ● [SNMP](/dashboards/app/dashboards#/view/4e5f106e-c60a-4226-8f64-d534abb912ab) ● [SSH](/dashboards/app/dashboards#/view/caef3ade-d289-4d05-a511-149f3e97f238) ● [SSL](/dashboards/app/dashboards#/view/7f77b58a-df3e-4cc2-b782-fd7f8bad8ffb) / [X.509 Certificates](/dashboards/app/dashboards#/view/024062a6-48d6-498f-a91a-3bf2da3a3cd3) ● [STUN](/dashboards/app/dashboards#/view/fa477130-2b8a-11ec-a9f2-3911c8571bfd) ● [Syslog](/dashboards/app/dashboards#/view/92985909-dc29-4533-9e80-d3182a0ecf1d) ● [TDS](/dashboards/app/dashboards#/view/bed185a0-ef82-11e9-b38a-2db3ee640e88) / [TDS RPC](/dashboards/app/dashboards#/view/32587740-ef88-11e9-b38a-2db3ee640e88) / [TDS SQL](/dashboards/app/dashboards#/view/fa141950-ef89-11e9-b38a-2db3ee640e88) ● [Telnet / rlogin / rsh](/dashboards/app/dashboards#/view/c2549e10-7f2e-11ea-9f8a-1fe1327e2cd2) ● [Tunnels](/dashboards/app/dashboards#/view/11be6381-beef-40a7-bdce-88c5398392fc)\\n\\n### ICS/IoT Protocols\\n[BACnet](/dashboards/app/dashboards#/view/2bec1490-eb94-11e9-a384-0fcf32210194) ● [BSAP](/dashboards/app/dashboards#/view/ca5799a0-56b5-11eb-b749-576de068f8ad) ● [DNP3](/dashboards/app/dashboards#/view/870a5862-6c26-4a08-99fd-0c06cda85ba3) ● [EtherCAT](/dashboards/app/dashboards#/view/4a073440-b286-11eb-a4d4-09fa12a6ebd4) ● [EtherNet/IP](/dashboards/app/dashboards#/view/29a1b290-eb98-11e9-a384-0fcf32210194) ● [GENISYS](/dashboards/app/dashboards#/view/03207c00-d07e-11ec-b4a7-d1b4003706b7) ● [Modbus](/dashboards/app/dashboards#/view/152f29dc-51a2-4f53-93e9-6e92765567b8) ● [OPCUA Binary](/dashboards/app/dashboards#/view/dd87edd0-796a-11ec-9ce6-b395c1ff58f4) ● [PROFINET](/dashboards/app/dashboards#/view/a7514350-eba6-11e9-a384-0fcf32210194) ● [S7comm](/dashboards/app/dashboards#/view/e76d05c0-eb9f-11e9-a384-0fcf32210194) ● [Best Guess](/dashboards/app/dashboards#/view/12e3a130-d83b-11eb-a0b0-f328ce09b0b7)\",\"type\":\"markdown\",\"fontSize\":10,\"openLinksInNewTab\":false},\"aggs\":[]}", @@ -101,8 +106,8 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:11:09.705Z", - "version": "Wzc0NCwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc0MiwxXQ==", "attributes": { "title": "S7comm - Log Count", "visState": "{\"title\":\"S7comm - Log Count\",\"type\":\"metric\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"network.protocol\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Protocol\"},\"schema\":\"group\"}],\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":36}}}}", @@ -131,8 +136,8 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:11:09.705Z", - "version": "Wzc0NSwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc0MywxXQ==", "attributes": { "title": "S7comm - Logs Over Time", "visState": "{\"title\":\"S7comm - Logs Over Time\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"firstPacket\",\"timeRange\":{\"from\":\"now-25y\",\"to\":\"now\"},\"useNormalizedOpenSearchInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"network.protocol\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Protocol\"},\"schema\":\"group\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"square root\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"bottom\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}", @@ -161,8 +166,8 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:23:15.472Z", - "version": "Wzg5MSwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc0NCwxXQ==", "attributes": { "title": "S7comm Operations", "visState": "{\"title\":\"S7comm Operations\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"network.protocol\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Protocol\"},\"schema\":\"bucket\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"event.action\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":100,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"-\",\"customLabel\":\"Action\"},\"schema\":\"bucket\"},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"event.result\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":100,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"-\",\"customLabel\":\"Result\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":25,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", @@ -191,8 +196,8 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:28:09.832Z", - "version": "Wzg5NCwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc0NSwxXQ==", "attributes": { "title": "S7comm Source IP", "visState": "{\"title\":\"S7comm Source IP\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"network.protocol\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"-\",\"customLabel\":\"Protocol\"},\"schema\":\"bucket\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"source.ip\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":200,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Source IP\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", @@ -221,8 +226,8 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:31:25.316Z", - "version": "Wzg5NSwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc0NiwxXQ==", "attributes": { "title": "S7comm Destination IP", "visState": "{\"title\":\"S7comm Destination IP\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"network.protocol\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"-\",\"customLabel\":\"Protocol\"},\"schema\":\"bucket\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"destination.ip\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":200,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Destination IP\"},\"schema\":\"bucket\"},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"destination.port\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Destination Port\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", @@ -251,8 +256,8 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:32:42.356Z", - "version": "Wzg5NiwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc0NywxXQ==", "attributes": { "title": "S7comm Plus Version", "visState": "{\"title\":\"S7comm Plus Version\",\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"network.protocol_version\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Version\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100}}}", @@ -275,14 +280,44 @@ "visualization": "7.10.0" } }, + { + "id": "c1a225a0-48d0-11ed-ac36-efb71d661952", + "type": "visualization", + "namespaces": [ + "default" + ], + "updated_at": "2022-10-10T19:21:35.736Z", + "version": "WzkwNSwxXQ==", + "attributes": { + "title": "S7comm - Upload/Download File Names", + "visState": "{\"title\":\"S7comm - Upload/Download File Names\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"file.path\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":200,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"\",\"customLabel\":\"File Name\"},\"schema\":\"bucket\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"zeek.s7comm_upload_download.destination_filesystem\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"-\",\"customLabel\":\"Destination Filesystem\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"desc\"}}}}", + "description": "", + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "savedSearchRefName": "search_0" + }, + "references": [ + { + "name": "search_0", + "type": "search", + "id": "cb95dda0-48cf-11ed-ac36-efb71d661952" + } + ], + "migrationVersion": { + "visualization": "7.10.0" + } + }, { "id": "39091f40-18c0-11ed-9abd-97fb0b4c6d6c", "type": "visualization", "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:22:18.804Z", - "version": "Wzg5MCwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc0OCwxXQ==", "attributes": { "title": "S7comm Read-SZL", "visState": "{\"title\":\"S7comm Read-SZL\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"zeek.s7comm_read_szl.szl_index\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":200,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"-\",\"customLabel\":\"SZL Index\"},\"schema\":\"bucket\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"event.action\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":100,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"-\",\"customLabel\":\"Action\"},\"schema\":\"bucket\"},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"event.result\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":100,\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":true,\"missingBucketLabel\":\"-\",\"customLabel\":\"Result\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\"}}", @@ -311,8 +346,8 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:11:09.705Z", - "version": "Wzc0OCwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc0OSwxXQ==", "attributes": { "title": "S7comm and Related - Logs", "description": "", @@ -353,7 +388,7 @@ "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:11:09.705Z", + "updated_at": "2022-10-10T18:59:13.093Z", "version": "Wzc1MCwxXQ==", "attributes": { "title": "S7comm Plus - Logs", @@ -387,14 +422,55 @@ "search": "7.9.3" } }, + { + "id": "cb95dda0-48cf-11ed-ac36-efb71d661952", + "type": "search", + "namespaces": [ + "default" + ], + "updated_at": "2022-10-10T19:14:42.936Z", + "version": "WzkwMywxXQ==", + "attributes": { + "title": "S7comm Upload/Download - Logs", + "description": "", + "hits": 0, + "columns": [ + "source.ip", + "source.port", + "destination.ip", + "destination.port", + "event.action", + "event.result", + "zeek.s7comm_upload_download.block_type", + "file.path", + "zeek.s7comm_upload_download.destination_filesystem", + "zeek.uid" + ], + "sort": [], + "version": 1, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"event.dataset:s7comm_upload_download\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + } + }, + "references": [ + { + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + "id": "arkime_sessions3-*" + } + ], + "migrationVersion": { + "search": "7.9.3" + } + }, { "id": "aa66bb80-18b5-11ed-9815-dd8187ffaa35", "type": "search", "namespaces": [ "default" ], - "updated_at": "2022-08-10T15:11:09.705Z", - "version": "Wzc0OSwxXQ==", + "updated_at": "2022-10-10T18:59:13.093Z", + "version": "Wzc1MSwxXQ==", "attributes": { "title": "S7comm Read-SZL - Logs", "description": "", diff --git a/dashboards/templates/composable/component/zeek_ot.json b/dashboards/templates/composable/component/zeek_ot.json index 920c020e1..49f3570f3 100644 --- a/dashboards/templates/composable/component/zeek_ot.json +++ b/dashboards/templates/composable/component/zeek_ot.json @@ -7,7 +7,7 @@ "zeek.bacnet.pdu_service": { "type": "keyword" }, "zeek.bacnet.pdu_type": { "type": "keyword" }, "zeek.bacnet.result_code": { "type": "keyword" }, - "zeek.bacnet_discovery.instance_number": { "type": "integer" }, + "zeek.bacnet_discovery.instance_number": { "type": "long" }, "zeek.bacnet_discovery.object_name": { "type": "keyword" }, "zeek.bacnet_discovery.object_type": { "type": "keyword" }, "zeek.bacnet_discovery.pdu_service": { "type": "keyword" }, @@ -16,7 +16,7 @@ "zeek.bacnet_discovery.range_low": { "type": "integer" }, "zeek.bacnet_discovery.vendor": { "type": "keyword" }, "zeek.bacnet_property.array_index": { "type": "integer" }, - "zeek.bacnet_property.instance_number": { "type": "integer" }, + "zeek.bacnet_property.instance_number": { "type": "long" }, "zeek.bacnet_property.object_type": { "type": "keyword" }, "zeek.bacnet_property.pdu_service": { "type": "keyword" }, "zeek.bacnet_property.property": { "type": "keyword" }, @@ -512,26 +512,35 @@ "zeek.profinet_dce_rpc.packet_type": { "type": "keyword" }, "zeek.profinet_dce_rpc.server_boot_time": { "type": "integer" }, "zeek.profinet_dce_rpc.version": { "type": "integer" }, - "zeek.s7comm.rosctr_code": { "type": "integer" }, - "zeek.s7comm.rosctr_name": { "type": "keyword" }, - "zeek.s7comm.pdu_reference": { "type": "integer" }, + "zeek.s7comm.error_class": { "type": "keyword" }, + "zeek.s7comm.error_code": { "type": "keyword" }, "zeek.s7comm.function_code": { "type": "keyword" }, "zeek.s7comm.function_name": { "type": "keyword" }, + "zeek.s7comm.pdu_reference": { "type": "integer" }, + "zeek.s7comm.rosctr_code": { "type": "integer" }, + "zeek.s7comm.rosctr_name": { "type": "keyword" }, "zeek.s7comm.subfunction_code": { "type": "keyword" }, "zeek.s7comm.subfunction_name": { "type": "keyword" }, - "zeek.s7comm.error_class": { "type": "keyword" }, - "zeek.s7comm.error_code": { "type": "keyword" }, - "zeek.s7comm_plus.version": { "type": "integer" }, - "zeek.s7comm_plus.opcode": { "type": "keyword" }, - "zeek.s7comm_plus.opcode_name": { "type": "keyword" }, "zeek.s7comm_plus.function_code": { "type": "keyword" }, "zeek.s7comm_plus.function_name": { "type": "keyword" }, + "zeek.s7comm_plus.opcode": { "type": "keyword" }, + "zeek.s7comm_plus.opcode_name": { "type": "keyword" }, + "zeek.s7comm_plus.version": { "type": "integer" }, "zeek.s7comm_read_szl.method": { "type": "keyword"}, + "zeek.s7comm_read_szl.return_code": { "type": "keyword"}, + "zeek.s7comm_read_szl.return_code_name": { "type": "keyword"}, "zeek.s7comm_read_szl.szl_id": { "type": "keyword"}, "zeek.s7comm_read_szl.szl_id_name": { "type": "keyword"}, "zeek.s7comm_read_szl.szl_index": { "type": "keyword"}, - "zeek.s7comm_read_szl.return_code": { "type": "keyword"}, - "zeek.s7comm_read_szl.return_code_name": { "type": "keyword"} + "zeek.s7comm_upload_download.block_number": { "type": "keyword"}, + "zeek.s7comm_upload_download.block_type": { "type": "keyword"}, + "zeek.s7comm_upload_download.blocklength": { "type": "integer"}, + "zeek.s7comm_upload_download.destination_filesystem": { "type": "keyword"}, + "zeek.s7comm_upload_download.filename": { "type": "keyword"}, + "zeek.s7comm_upload_download.function_name": { "type": "keyword"}, + "zeek.s7comm_upload_download.function_status": { "type": "keyword"}, + "zeek.s7comm_upload_download.rosctr_name": { "type": "keyword"}, + "zeek.s7comm_upload_download.session_id": { "type": "long"} } } } diff --git a/docker-compose-standalone.yml b/docker-compose-standalone.yml index 6d041dbb2..4c91d1495 100644 --- a/docker-compose-standalone.yml +++ b/docker-compose-standalone.yml @@ -9,6 +9,8 @@ x-process-variables: &process-variables # docker containers will run processes as unprivileged user with UID:GID PUID : 1000 PGID : 1000 + # for debugging container init via tini (https://github.com/krallin/tini) + TINI_VERBOSITY : 1 x-auth-variables: &auth-variables # authentication method: encrypted HTTP basic authentication ('true') vs LDAP ('false') @@ -260,6 +262,22 @@ x-filebeat-variables: &filebeat-variables # Tag to append to events sent to the filebeat TCP input listener FILEBEAT_TCP_TAG : '_malcolm_beats' +x-netbox-variables: &netbox-variables + # Parameters related to NetBox (and supporting tools). Note that other more specific parameters + # can also be configured in the env_file files for netbox* services + # The name of the default "site" to be created upon NetBox initialization + NETBOX_DEFAULT_SITE : 'Malcolm' + # Whether to disable Malcolm's NetBox instance ('true') or not ('false') + NETBOX_DISABLED : &netboxdisabled 'true' + NETBOX_POSTGRES_DISABLED : *netboxdisabled + NETBOX_REDIS_DISABLED : *netboxdisabled + NETBOX_REDIS_CACHE_DISABLED : *netboxdisabled + # Whether or not to periodically query network traffic metadata and use it to populate NetBox + NETBOX_CRON : 'false' + # If using the NetBox interface to create API tokens, set this + # (see https://docs.djangoproject.com/en/4.1/ref/settings/#csrf-trusted-origins) + # CSRF_TRUSTED_ORIGINS : 'https://malcolm.example.org' + x-common-upload-variables: &common-upload-variables # Whether or not to automatically apply tags based (on the PCAP filename) to network traffic metadata # parsed from uploaded PCAP files @@ -324,7 +342,7 @@ x-pcap-capture-variables: &pcap-capture-variables PCAP_IFACE_TWEAK : 'false' # Specifies how large a locally-captured PCAP file can become (in megabytes) before # it is closed for processing and a new PCAP file created - PCAP_ROTATE_MEGABYTES : 1024 + PCAP_ROTATE_MEGABYTES : 4096 # Specifies a time interval (in minutes) after which a locally-captured PCAP file # will be closed for processing and a new PCAP file created PCAP_ROTATE_MINUTES : 10 @@ -334,7 +352,7 @@ x-pcap-capture-variables: &pcap-capture-variables services: opensearch: - image: malcolmnetsec/opensearch:6.3.0 + image: malcolmnetsec/opensearch:6.4.0 restart: "no" stdin_open: false tty: true @@ -377,7 +395,7 @@ services: retries: 3 start_period: 180s dashboards-helper: - image: malcolmnetsec/dashboards-helper:6.3.0 + image: malcolmnetsec/dashboards-helper:6.4.0 restart: "no" stdin_open: false tty: true @@ -410,7 +428,7 @@ services: retries: 3 start_period: 30s dashboards: - image: malcolmnetsec/dashboards:6.3.0 + image: malcolmnetsec/dashboards:6.4.0 restart: "no" stdin_open: false tty: true @@ -437,7 +455,7 @@ services: retries: 3 start_period: 210s logstash: - image: malcolmnetsec/logstash-oss:6.3.0 + image: malcolmnetsec/logstash-oss:6.4.0 restart: "no" stdin_open: false tty: true @@ -484,7 +502,7 @@ services: retries: 3 start_period: 600s filebeat: - image: malcolmnetsec/filebeat-oss:6.3.0 + image: malcolmnetsec/filebeat-oss:6.4.0 restart: "no" stdin_open: false tty: true @@ -525,7 +543,7 @@ services: retries: 3 start_period: 60s arkime: - image: malcolmnetsec/arkime:6.3.0 + image: malcolmnetsec/arkime:6.4.0 restart: "no" stdin_open: false tty: true @@ -567,7 +585,7 @@ services: retries: 3 start_period: 210s zeek: - image: malcolmnetsec/zeek:6.3.0 + image: malcolmnetsec/zeek:6.4.0 restart: "no" stdin_open: false tty: true @@ -606,7 +624,7 @@ services: retries: 3 start_period: 60s zeek-live: - image: malcolmnetsec/zeek:6.3.0 + image: malcolmnetsec/zeek:6.4.0 restart: "no" stdin_open: false tty: true @@ -638,7 +656,7 @@ services: - ./zeek-logs/extract_files:/zeek/extract_files - ./zeek/intel:/opt/zeek/share/zeek/site/intel suricata: - image: malcolmnetsec/suricata:6.3.0 + image: malcolmnetsec/suricata:6.4.0 restart: "no" stdin_open: false tty: true @@ -675,7 +693,7 @@ services: retries: 3 start_period: 120s suricata-live: - image: malcolmnetsec/suricata:6.3.0 + image: malcolmnetsec/suricata:6.4.0 restart: "no" stdin_open: false tty: true @@ -702,7 +720,7 @@ services: - ./suricata-logs:/var/log/suricata - ./suricata/rules:/opt/suricata/rules:ro file-monitor: - image: malcolmnetsec/file-monitor:6.3.0 + image: malcolmnetsec/file-monitor:6.4.0 restart: "no" stdin_open: false tty: true @@ -729,7 +747,7 @@ services: retries: 3 start_period: 60s pcap-capture: - image: malcolmnetsec/pcap-capture:6.3.0 + image: malcolmnetsec/pcap-capture:6.4.0 restart: "no" stdin_open: false tty: true @@ -751,7 +769,7 @@ services: - ./nginx/ca-trust:/var/local/ca-trust:ro - ./pcap/upload:/pcap pcap-monitor: - image: malcolmnetsec/pcap-monitor:6.3.0 + image: malcolmnetsec/pcap-monitor:6.4.0 restart: "no" stdin_open: false tty: true @@ -779,7 +797,7 @@ services: retries: 3 start_period: 90s upload: - image: malcolmnetsec/file-upload:6.3.0 + image: malcolmnetsec/file-upload:6.4.0 restart: "no" stdin_open: false tty: true @@ -809,7 +827,7 @@ services: retries: 3 start_period: 60s htadmin: - image: malcolmnetsec/htadmin:6.3.0 + image: malcolmnetsec/htadmin:6.4.0 restart: "no" stdin_open: false tty: true @@ -835,7 +853,7 @@ services: retries: 3 start_period: 60s freq: - image: malcolmnetsec/freq:6.3.0 + image: malcolmnetsec/freq:6.4.0 restart: "no" stdin_open: false tty: true @@ -858,7 +876,7 @@ services: retries: 3 start_period: 60s name-map-ui: - image: malcolmnetsec/name-map-ui:6.3.0 + image: malcolmnetsec/name-map-ui:6.4.0 restart: "no" stdin_open: false tty: true @@ -882,8 +900,114 @@ services: timeout: 15s retries: 3 start_period: 60s + netbox: + image: malcolmnetsec/netbox:6.4.0 + restart: "no" + stdin_open: false + tty: true + hostname: netbox + networks: + - default + env_file: ./netbox/env/netbox.env + environment: + << : *process-variables + << : *ssl-variables + << : *netbox-variables + VIRTUAL_HOST : 'netbox.malcolm.local' + depends_on: + - netbox-postgres + - netbox-redis + - netbox-redis-cache + volumes: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/config/configuration:/etc/netbox/config:ro + - ./netbox/config/reports:/etc/netbox/reports:ro + - ./netbox/config/scripts:/etc/netbox/scripts:ro + - ./netbox/media:/opt/netbox/netbox/media:rw + healthcheck: + test: ["CMD", "curl", "--silent", "http://localhost:8080/netbox/api/" ] + interval: 60s + timeout: 15s + retries: 3 + start_period: 120s + netbox-postgres: + image: malcolmnetsec/postgresql:6.4.0 + restart: "no" + stdin_open: false + tty: true + hostname: netbox-postgres + networks: + - default + env_file: ./netbox/env/postgres.env + environment: + << : *process-variables + << : *ssl-variables + << : *netbox-variables + VIRTUAL_HOST : 'netbox-postgres.malcolm.local' + volumes: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/postgres:/var/lib/postgresql/data:rw + healthcheck: + test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ] + interval: 60s + timeout: 15s + retries: 3 + start_period: 45s + netbox-redis: + image: malcolmnetsec/redis:6.4.0 + restart: "no" + stdin_open: false + tty: true + hostname: netbox-redis + networks: + - default + env_file: ./netbox/env/redis.env + environment: + << : *process-variables + << : *ssl-variables + << : *netbox-variables + VIRTUAL_HOST : 'netbox-redis.malcolm.local' + command: + - sh + - -c + - redis-server --appendonly yes --requirepass $$REDIS_PASSWORD + volumes: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/redis:/data + healthcheck: + test: ["CMD-SHELL", "pidof redis-server || exit 1" ] + interval: 60s + timeout: 15s + retries: 3 + start_period: 45s + netbox-redis-cache: + image: malcolmnetsec/redis:6.4.0 + restart: "no" + stdin_open: false + tty: true + hostname: netbox-redis-cache + networks: + - default + env_file: ./netbox/env/redis-cache.env + environment: + << : *process-variables + << : *ssl-variables + << : *netbox-variables + VIRTUAL_HOST : 'netbox-redis-cache.malcolm.local' + command: + - sh + - -c + - redis-server --requirepass $$REDIS_PASSWORD + volumes: + - ./nginx/ca-trust:/var/local/ca-trust:ro + healthcheck: + test: ["CMD-SHELL", "pidof redis-server || exit 1" ] + interval: 60s + timeout: 15s + retries: 3 + start_period: 45s api: - image: malcolmnetsec/api:6.3.0 + image: malcolmnetsec/api:6.4.0 command: gunicorn --bind 0:5000 manage:app restart: "no" stdin_open: false @@ -908,7 +1032,7 @@ services: retries: 3 start_period: 60s nginx-proxy: - image: malcolmnetsec/nginx-proxy:6.3.0 + image: malcolmnetsec/nginx-proxy:6.4.0 restart: "no" stdin_open: false tty: true @@ -924,10 +1048,11 @@ services: - api - arkime - dashboards - - upload + - file-monitor - htadmin - name-map-ui - - file-monitor + - netbox + - upload ports: - "0.0.0.0:443:443" - "0.0.0.0:488:488" diff --git a/docker-compose.yml b/docker-compose.yml index 3dd519abb..81a0a104f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,8 @@ x-process-variables: &process-variables # docker containers will run processes as unprivileged user with UID:GID PUID : 1000 PGID : 1000 + # for debugging container init via tini (https://github.com/krallin/tini) + TINI_VERBOSITY : 1 x-auth-variables: &auth-variables # authentication method: encrypted HTTP basic authentication ('true') vs LDAP ('false') @@ -260,6 +262,22 @@ x-filebeat-variables: &filebeat-variables # Tag to append to events sent to the filebeat TCP input listener FILEBEAT_TCP_TAG : '_malcolm_beats' +x-netbox-variables: &netbox-variables + # Parameters related to NetBox (and supporting tools). Note that other more specific parameters + # can also be configured in the env_file files for netbox* services + # The name of the default "site" to be created upon NetBox initialization + NETBOX_DEFAULT_SITE : 'Malcolm' + # Whether to disable Malcolm's NetBox instance ('true') or not ('false') + NETBOX_DISABLED : &netboxdisabled 'true' + NETBOX_POSTGRES_DISABLED : *netboxdisabled + NETBOX_REDIS_DISABLED : *netboxdisabled + NETBOX_REDIS_CACHE_DISABLED : *netboxdisabled + # Whether or not to periodically query network traffic metadata and use it to populate NetBox + NETBOX_CRON : 'false' + # If using the NetBox interface to create API tokens, set this + # (see https://docs.djangoproject.com/en/4.1/ref/settings/#csrf-trusted-origins) + # CSRF_TRUSTED_ORIGINS : 'https://malcolm.example.org' + x-common-upload-variables: &common-upload-variables # Whether or not to automatically apply tags based (on the PCAP filename) to network traffic metadata # parsed from uploaded PCAP files @@ -324,7 +342,7 @@ x-pcap-capture-variables: &pcap-capture-variables PCAP_IFACE_TWEAK : 'false' # Specifies how large a locally-captured PCAP file can become (in megabytes) before # it is closed for processing and a new PCAP file created - PCAP_ROTATE_MEGABYTES : 1024 + PCAP_ROTATE_MEGABYTES : 4096 # Specifies a time interval (in minutes) after which a locally-captured PCAP file # will be closed for processing and a new PCAP file created PCAP_ROTATE_MINUTES : 10 @@ -337,7 +355,7 @@ services: build: context: . dockerfile: Dockerfiles/opensearch.Dockerfile - image: malcolmnetsec/opensearch:6.3.0 + image: malcolmnetsec/opensearch:6.4.0 restart: "no" stdin_open: false tty: true @@ -383,7 +401,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards-helper.Dockerfile - image: malcolmnetsec/dashboards-helper:6.3.0 + image: malcolmnetsec/dashboards-helper:6.4.0 restart: "no" stdin_open: false tty: true @@ -419,7 +437,7 @@ services: build: context: . dockerfile: Dockerfiles/dashboards.Dockerfile - image: malcolmnetsec/dashboards:6.3.0 + image: malcolmnetsec/dashboards:6.4.0 restart: "no" stdin_open: false tty: true @@ -449,7 +467,7 @@ services: build: context: . dockerfile: Dockerfiles/logstash.Dockerfile - image: malcolmnetsec/logstash-oss:6.3.0 + image: malcolmnetsec/logstash-oss:6.4.0 restart: "no" stdin_open: false tty: true @@ -503,7 +521,7 @@ services: build: context: . dockerfile: Dockerfiles/filebeat.Dockerfile - image: malcolmnetsec/filebeat-oss:6.3.0 + image: malcolmnetsec/filebeat-oss:6.4.0 restart: "no" stdin_open: false tty: true @@ -547,7 +565,7 @@ services: build: context: . dockerfile: Dockerfiles/arkime.Dockerfile - image: malcolmnetsec/arkime:6.3.0 + image: malcolmnetsec/arkime:6.4.0 restart: "no" stdin_open: false tty: true @@ -595,7 +613,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: malcolmnetsec/zeek:6.3.0 + image: malcolmnetsec/zeek:6.4.0 restart: "no" stdin_open: false tty: true @@ -638,7 +656,7 @@ services: build: context: . dockerfile: Dockerfiles/zeek.Dockerfile - image: malcolmnetsec/zeek:6.3.0 + image: malcolmnetsec/zeek:6.4.0 restart: "no" stdin_open: false tty: true @@ -674,7 +692,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: malcolmnetsec/suricata:6.3.0 + image: malcolmnetsec/suricata:6.4.0 restart: "no" stdin_open: false tty: true @@ -714,7 +732,7 @@ services: build: context: . dockerfile: Dockerfiles/suricata.Dockerfile - image: malcolmnetsec/suricata:6.3.0 + image: malcolmnetsec/suricata:6.4.0 restart: "no" stdin_open: false tty: true @@ -744,7 +762,7 @@ services: build: context: . dockerfile: Dockerfiles/file-monitor.Dockerfile - image: malcolmnetsec/file-monitor:6.3.0 + image: malcolmnetsec/file-monitor:6.4.0 restart: "no" stdin_open: false tty: true @@ -774,7 +792,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-capture.Dockerfile - image: malcolmnetsec/pcap-capture:6.3.0 + image: malcolmnetsec/pcap-capture:6.4.0 restart: "no" stdin_open: false tty: true @@ -799,7 +817,7 @@ services: build: context: . dockerfile: Dockerfiles/pcap-monitor.Dockerfile - image: malcolmnetsec/pcap-monitor:6.3.0 + image: malcolmnetsec/pcap-monitor:6.4.0 restart: "no" stdin_open: false tty: true @@ -830,7 +848,7 @@ services: build: context: . dockerfile: Dockerfiles/file-upload.Dockerfile - image: malcolmnetsec/file-upload:6.3.0 + image: malcolmnetsec/file-upload:6.4.0 restart: "no" stdin_open: false tty: true @@ -860,7 +878,7 @@ services: retries: 3 start_period: 60s htadmin: - image: malcolmnetsec/htadmin:6.3.0 + image: malcolmnetsec/htadmin:6.4.0 build: context: . dockerfile: Dockerfiles/htadmin.Dockerfile @@ -889,7 +907,7 @@ services: retries: 3 start_period: 60s freq: - image: malcolmnetsec/freq:6.3.0 + image: malcolmnetsec/freq:6.4.0 build: context: . dockerfile: Dockerfiles/freq.Dockerfile @@ -915,7 +933,7 @@ services: retries: 3 start_period: 60s name-map-ui: - image: malcolmnetsec/name-map-ui:6.3.0 + image: malcolmnetsec/name-map-ui:6.4.0 build: context: . dockerfile: Dockerfiles/name-map-ui.Dockerfile @@ -942,8 +960,126 @@ services: timeout: 15s retries: 3 start_period: 60s + netbox: + image: malcolmnetsec/netbox:6.4.0 + build: + context: . + dockerfile: Dockerfiles/netbox.Dockerfile + restart: "no" + stdin_open: false + tty: true + hostname: netbox + networks: + - default + env_file: ./netbox/env/netbox.env + environment: + << : *process-variables + << : *ssl-variables + << : *netbox-variables + VIRTUAL_HOST : 'netbox.malcolm.local' + depends_on: + - netbox-postgres + - netbox-redis + - netbox-redis-cache + volumes: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/config/configuration:/etc/netbox/config:ro + - ./netbox/config/reports:/etc/netbox/reports:ro + - ./netbox/config/scripts:/etc/netbox/scripts:ro + - ./netbox/media:/opt/netbox/netbox/media:rw + healthcheck: + test: ["CMD", "curl", "--silent", "http://localhost:8080/netbox/api/" ] + interval: 60s + timeout: 15s + retries: 3 + start_period: 120s + netbox-postgres: + image: malcolmnetsec/postgresql:6.4.0 + build: + context: . + dockerfile: Dockerfiles/postgresql.Dockerfile + restart: "no" + stdin_open: false + tty: true + hostname: netbox-postgres + networks: + - default + env_file: ./netbox/env/postgres.env + environment: + << : *process-variables + << : *ssl-variables + << : *netbox-variables + VIRTUAL_HOST : 'netbox-postgres.malcolm.local' + volumes: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/postgres:/var/lib/postgresql/data:rw + healthcheck: + test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ] + interval: 60s + timeout: 15s + retries: 3 + start_period: 45s + netbox-redis: + image: malcolmnetsec/redis:6.4.0 + build: + context: . + dockerfile: Dockerfiles/redis.Dockerfile + restart: "no" + stdin_open: false + tty: true + hostname: netbox-redis + networks: + - default + env_file: ./netbox/env/redis.env + environment: + << : *process-variables + << : *ssl-variables + << : *netbox-variables + VIRTUAL_HOST : 'netbox-redis.malcolm.local' + command: + - sh + - -c + - redis-server --appendonly yes --requirepass $$REDIS_PASSWORD + volumes: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./netbox/redis:/data + healthcheck: + test: ["CMD-SHELL", "pidof redis-server || exit 1" ] + interval: 60s + timeout: 15s + retries: 3 + start_period: 45s + netbox-redis-cache: + image: malcolmnetsec/redis:6.4.0 + build: + context: . + dockerfile: Dockerfiles/redis.Dockerfile + restart: "no" + stdin_open: false + tty: true + hostname: netbox-redis-cache + networks: + - default + env_file: ./netbox/env/redis-cache.env + environment: + << : *process-variables + << : *ssl-variables + << : *netbox-variables + VIRTUAL_HOST : 'netbox-redis-cache.malcolm.local' + command: + - sh + - -c + - redis-server --requirepass $$REDIS_PASSWORD + volumes: + - ./nginx/ca-trust:/var/local/ca-trust:ro + healthcheck: + test: ["CMD-SHELL", "pidof redis-server || exit 1" ] + interval: 60s + timeout: 15s + retries: 3 + start_period: 45s api: - image: malcolmnetsec/api:6.3.0 + image: malcolmnetsec/api:6.4.0 build: context: . dockerfile: Dockerfiles/api.Dockerfile @@ -974,7 +1110,7 @@ services: build: context: . dockerfile: Dockerfiles/nginx.Dockerfile - image: malcolmnetsec/nginx-proxy:6.3.0 + image: malcolmnetsec/nginx-proxy:6.4.0 restart: "no" stdin_open: false tty: true @@ -990,10 +1126,11 @@ services: - api - arkime - dashboards - - upload + - file-monitor - htadmin - name-map-ui - - file-monitor + - netbox + - upload ports: - "0.0.0.0:443:443" - "0.0.0.0:488:488" diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..0119dccf8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,102 @@ +# Overview + +![Malcolm Network Diagram](./images/malcolm_network_diagram.png) + +Malcolm processes network traffic data in the form of packet capture (docs/PCAP) files or Zeek logs. A [sensor](live-analysis.md#Hedgehog) (packet capture appliance) monitors network traffic mirrored to it over a SPAN port on a network switch or router, or using a network TAP device. [Zeek](https://www.zeek.org/index.html) logs and [Arkime](https://arkime.com/) sessions are generated containing important session metadata from the traffic observed, which are then securely forwarded to a Malcolm instance. Full PCAP files are optionally stored locally on the sensor device for examination later. + +Malcolm parses the network session data and enriches it with additional lookups and mappings including GeoIP mapping, hardware manufacturer lookups from [organizationally unique identifiers (docs/OUI)](http://standards-oui.ieee.org/oui/oui.txt) in MAC addresses, assigning names to [network segments](host-and-subnet-mapping.md#SegmentNaming) and [hosts](host-and-subnet-mapping.md#HostNaming) based on user-defined IP address and MAC mappings, performing [TLS fingerprinting](https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967), and many others. + +The enriched data is stored in an [OpenSearch](https://opensearch.org/) document store in a format suitable for analysis through two intuitive interfaces: OpenSearch Dashboards, a flexible data visualization plugin with dozens of prebuilt dashboards providing an at-a-glance overview of network protocols; and Arkime, a powerful tool for finding and identifying the network sessions comprising suspected security incidents. These tools can be accessed through a web browser from analyst workstations or for display in a security operations center (SOC). Logs can also optionally be forwarded on to another instance of Malcolm. + +![Malcolm Data Pipeline](./images/malcolm_data_pipeline.png) + +For smaller networks, use at home by network security enthusiasts, or in the field for incident response engagements, Malcolm can also easily be deployed locally on an ordinary consumer workstation or laptop. Malcolm can process local artifacts such as locally-generated Zeek logs, locally-captured PCAP files, and PCAP files collected offline without the use of a dedicated sensor appliance. + + +* [Quick start](quickstart.md#QuickStart) + - [Getting Malcolm](quickstart.md#GetMalcolm) + - [User interface](quickstart.md#UserInterfaceURLs) +* [Components](components.md#Components) +* [Supported Protocols](protocols.md#Protocols) +* [Development](development.md#Development) + - [Building from source](development.md#Build) + - [Pre-Packaged installation files](development.md#Packager) +* [Configuration](malcolm-preparation.md#Configuration) + - [Recommended system requirements](system-requirements.md#SystemRequirements) + - [Malcolm Configuration](malcolm-config.md#ConfigAndTuning) + + [`docker-compose.yml` parameters](malcolm-config.md#DockerComposeYml) + - [Configure authentication](authsetup.md#AuthSetup) + + [Local account management](authsetup.md#AuthBasicAccountManagement) + + [Lightweight Directory Access Protocol (LDAP) authentication](authsetup.md#AuthLDAP) + * [LDAP connection security](authsetup.md#AuthLDAPSecurity) + + [TLS certificates](authsetup.md#TLSCerts) + - [Platform-specific Configuration](host-config.md#HostSystemConfig) + + [Linux host system configuration](host-config-linux.md#HostSystemConfigLinux) + + [macOS host system configuration](host-config-macos.md#HostSystemConfigMac) + + [Windows host system configuration](host-config-windows.md#HostSystemConfigWindows) +* [Running Malcolm](running.md#Running) + - [OpenSearch instances](opensearch-instances.md#OpenSearchInstance) + + [Authentication and authorization for remote OpenSearch clusters](opensearch-instances.md#OpenSearchAuth) + - [Starting Malcolm](running.md#Starting) + - [Stopping and restarting Malcolm](running.md#StopAndRestart) + - [Clearing Malcolm's data](running.md#Wipe) + - [Temporary read-only interface](running.md#ReadOnlyUI) +* [Capture file and log archive upload](upload.md#Upload) + - [Tagging](upload.md#Tagging) + - [Processing uploaded PCAPs with Zeek and Suricata](upload.md#UploadPCAPProcessors) +* [Live analysis](live-analysis.md#LiveAnalysis) + - [Using a network sensor appliance](live-analysis.md#Hedgehog) + - [Monitoring local network interfaces](live-analysis.md#LocalPCAP) + - [Manually forwarding logs from an external source](live-analysis.md#ExternalForward) +* [Arkime](arkime.md#Arkime) + - [Zeek log integration](arkime.md#ArkimeZeek) + + [Correlating Zeek logs and Arkime sessions](arkime.md#ZeekArkimeFlowCorrelation) + - [Help](arkime.md#ArkimeHelp) + - [Sessions](arkime.md#ArkimeSessions) + + [PCAP Export](arkime.md#ArkimePCAPExport) + - [SPIView](arkime.md#ArkimeSPIView) + - [SPIGraph](arkime.md#ArkimeSPIGraph) + - [Connections](arkime.md#ArkimeConnections) + - [Hunt](arkime.md#ArkimeHunt) + - [Statistics](arkime.md#ArkimeStats) + - [Settings](arkime.md#ArkimeSettings) +* [OpenSearch Dashboards](dashboards.md#Dashboards) + - [Discover](dashboards.md#Discover) + + [Screenshots](dashboards.md#DiscoverGallery) + - [Visualizations and dashboards](dashboards.md#DashboardsVisualizations) + + [Prebuilt visualizations and dashboards](dashboards.md#PrebuiltVisualizations) + * [Screenshots](dashboards.md#PrebuiltVisualizationsGallery) + + [Building your own visualizations and dashboards](dashboards.md#BuildDashboard) + * [Screenshots](dashboards.md#NewVisualizationsGallery) +* [Search Queries in Arkime and OpenSearch](queries-cheat-sheet.md#SearchCheatSheet) +* Other Malcolm features + - [Automatic file extraction and scanning](file-scanning.md#ZeekFileExtraction) + - [Automatic host and subnet name assignment](host-and-subnet-mapping.md#HostAndSubnetNaming) + + [IP/MAC address to hostname mapping via `host-map.txt`](host-and-subnet-mapping.md#HostNaming) + + [CIDR subnet to network segment name mapping via `cidr-map.txt`](host-and-subnet-mapping.md#SegmentNaming) + + [Defining hostname and CIDR subnet names interface](host-and-subnet-mapping.md#NameMapUI) + + [Applying mapping changes](host-and-subnet-mapping.md#ApplyMapping) + - [OpenSearch index management](index-management.md#IndexManagement) + - [Event severity scoring](severity.md#Severity) + + [Customizing event severity scoring](severity.md#SeverityConfig) + - [Zeek Intelligence Framework](zeek-intel.md#ZeekIntel) + + [STIX™ and TAXII™](zeek-intel.md#ZeekIntelSTIX) + + [MISP](zeek-intel.md#ZeekIntelMISP) + - [Anomaly Detection](anomaly-detection.md#AnomalyDetection) + - [Alerting](alerting.md#Alerting) + + [Email Sender Accounts](alerting.md#AlertingEmail) + - ["Best Guess" Fingerprinting for ICS Protocols](ics-best-guess.md#ICSBestGuess) + - [Asset Management with NetBox](netbox.md#NetBox) + - [CyberChef](cyberchef.md#CyberChef) + - [API](api.md#API) +* [Forwarding Third-Party Logs to Malcolm](third-party-logs.md#ThirdPartyLogs) +* [Malcolm installer ISO](malcolm-iso.md#ISO) + - [Installation](malcolm-iso.md#ISOInstallation) + - [Generating the ISO](malcolm-iso.md#ISOBuild) + - [Setup](malcolm-iso.md#ISOSetup) + - [Time synchronization](time-sync.md#ConfigTime) +* [Hardening](hardening.md#Hardening) + - [Compliance Exceptions](hardening.md#ComplianceExceptions) +* [Installation example using Ubuntu 22.04 LTS](ubuntu-install-example.md#InstallationExample) +* [Upgrading Malcolm](malcolm-upgrade.md#UpgradePlan) +* [Modifying or Contributing to Malcolm](contributing-guide.md#Contributing) \ No newline at end of file diff --git a/docs/alerting.md b/docs/alerting.md new file mode 100644 index 000000000..4aa5f83ab --- /dev/null +++ b/docs/alerting.md @@ -0,0 +1,37 @@ +# Alerting + +* [Alerting](#Alerting) + - [Email Sender Accounts](#AlertingEmail) + +Malcolm uses the Alerting plugins for [OpenSearch](https://github.com/opensearch-project/alerting) and [OpenSearch Dashboards](https://github.com/opensearch-project/alerting-dashboards-plugin). See [Alerting](https://opensearch.org/docs/latest/monitoring-plugins/alerting/index/) in the OpenSearch documentation for usage instructions. + +A fresh installation of Malcolm configures an example [custom webhook destination](https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#create-destinations) named **Malcolm API Loopback Webhook** that directs the triggered alerts back into the [Malcolm API](api.md#API) to be reindexed as a session record with `event.dataset` set to `alerting`. The corresponding monitor **Malcolm API Loopback Monitor** is disabled by default, as you'll likely want to configure the trigger conditions to suit your needs. These examples are provided to illustrate how triggers and monitors can interact with a custom webhook to process alerts. + +## Email Sender Accounts + +When using an email account to send alerts, you must [authenticate each sender account](https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#authenticate-sender-account) before you can send an email. The [`auth_setup`](authsetup.md#AuthSetup) script can be used to securely store the email account credentials: + +``` +./scripts/auth_setup + +Store administrator username/password for local Malcolm access? (Y/n): n + +(Re)generate self-signed certificates for HTTPS access (Y/n): n + +(Re)generate self-signed certificates for a remote log forwarder (Y/n): n + +Store username/password for primary remote OpenSearch instance? (y/N): n + +Store username/password for secondary remote OpenSearch instance? (y/N): n + +Store username/password for email alert sender account? (y/N): y + +Email account username: analyst@example.org +analyst@example.org password: +analyst@example.org password (again): +Email alert sender account variables stored: opensearch.alerting.destination.email.destination_alpha.password, opensearch.alerting.destination.email.destination_alpha.username + +(Re)generate internal passwords for NetBox (Y/n): n +``` + +This action should only be performed while Malcolm is [stopped](running.md#StopAndRestart): otherwise the credentials will not be stored correctly. \ No newline at end of file diff --git a/docs/anomaly-detection.md b/docs/anomaly-detection.md new file mode 100644 index 000000000..5992a4516 --- /dev/null +++ b/docs/anomaly-detection.md @@ -0,0 +1,12 @@ +# Anomaly Detection + +Malcolm uses the Anomaly Detection plugins for [OpenSearch](https://github.com/opensearch-project/anomaly-detection) and [OpenSearch Dashboards](https://github.com/opensearch-project/anomaly-detection-dashboards-plugin) to identify anomalous log data in near real-time using the [Random Cut Forest](https://api.semanticscholar.org/CorpusID:927435) (RCF) algorithm. This can be paired with [Alerting](alerting.md#Alerting) to automatically notify when anomalies are found. See [Anomaly detection](https://opensearch.org/docs/latest/monitoring-plugins/ad/index/) in the OpenSearch documentation for usage instructions on how to create detectors for any of the many fields Malcolm supports. + +A fresh installation of Malcolm configures [several detectors]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/dashboards/anomaly_detectors) for detecting anomalous network traffic: + +* **network_protocol** - Detects anomalies based on application protocol (`network.protocol`) +* **action_result_user** - Detects anomalies in action (`event.action`), result (`event.result`) and user (`related.user`) within application protocols (`network.protocol`) +* **file_mime_type** - Detects anomalies based on transferred file type (`file.mime_type`) +* **total_bytes** - Detects anomalies based on traffic size (sum of `network.bytes`) + +These detectors are disabled by default, but may be enabled for anomaly detection over streaming or [historical data](https://aws.amazon.com/about-aws/whats-new/2022/01/amazon-opensearch-service-elasticsearch-anomaly-detection/). \ No newline at end of file diff --git a/docs/api-aggregations.md b/docs/api-aggregations.md new file mode 100644 index 000000000..a201aa76e --- /dev/null +++ b/docs/api-aggregations.md @@ -0,0 +1,24 @@ +# Field Aggregations + +`GET` or `POST` - /mapi/agg/`` + +Executes an OpenSearch [bucket aggregation](https://opensearch.org/docs/latest/opensearch/bucket-agg/) query for the requested fields across all of Malcolm's indexed network traffic metadata. + +Parameters: + +* `fieldname` (URL parameter) - the name(s) of the field(s) to be queried (comma-separated if multiple fields) (default: `event.provider`) +* `limit` (query parameter) - the maximum number of records to return at each level of aggregation (default: 500) +* `from` (query parameter) - the time frame ([`gte`](https://opensearch.org/docs/latest/opensearch/query-dsl/term/#range)) for the beginning of the search based on the session's `firstPacket` field value in a format supported by the [dateparser](https://github.com/scrapinghub/dateparser) library (default: "1 day ago") +* `to` (query parameter) - the time frame ([`lte`](https://opensearch.org/docs/latest/opensearch/query-dsl/term/#range)) for the beginning of the search based on the session's `firstPacket` field value in a format supported by the [dateparser](https://github.com/scrapinghub/dateparser) library (default: "now") +* `filter` (query parameter) - field filters formatted as a JSON dictionary + +The `from`, `to`, and `filter` parameters can be used to further restrict the range of documents returned. The `filter` dictionary should be formatted such that its keys are field names and its values are the values for which to filter. A field name may be prepended with a `!` to negate the filter (e.g., `{"event.provider":"zeek"}` vs. `{"!event.provider":"zeek"}`). Filtering for value `null` implies "is not set" or "does not exist" (e.g., `{"event.dataset":null}` means "the field `event.dataset` is `null`/is not set" while `{"!event.dataset":null}` means "the field `event.dataset` is not `null`/is set"). + +Examples of `filter` parameter: + +* `{"!network.transport":"icmp"}` - `network.transport` is not `icmp` +* `{"network.direction":["inbound","outbound"]}` - `network.direction` is either `inbound` or `outbound` +* `{"event.provider":"zeek","event.dataset":["conn","dns"]}` - "`event.provider` is `zeek` and `event.dataset` is either `conn` or `dns`" +* `{"!event.dataset":null}` - "`event.dataset` is set (is not `null`)" + +See [Examples](api-examples.md#APIExamples) for more examples of `filter` and corresponding output. \ No newline at end of file diff --git a/docs/api-document-lookup.md b/docs/api-document-lookup.md new file mode 100644 index 000000000..07d1ddbce --- /dev/null +++ b/docs/api-document-lookup.md @@ -0,0 +1,88 @@ +# Document Lookup + +`GET` or `POST` - /mapi/document + +Executes an OpenSearch [query](https://opensearch.org/docs/latest/opensearch/bucket-agg/) query for the matching documents across all of Malcolm's indexed network traffic metadata. + +Parameters: + +* `limit` (query parameter) - the maximum number of documents to return (default: 500) +* `from` (query parameter) - the time frame ([`gte`](https://opensearch.org/docs/latest/opensearch/query-dsl/term/#range)) for the beginning of the search based on the session's `firstPacket` field value in a format supported by the [dateparser](https://github.com/scrapinghub/dateparser) library (default: the UNIX epoch) +* `to` (query parameter) - the time frame ([`lte`](https://opensearch.org/docs/latest/opensearch/query-dsl/term/#range)) for the beginning of the search based on the session's `firstPacket` field value in a format supported by the [dateparser](https://github.com/scrapinghub/dateparser) library (default: "now") +* `filter` (query parameter) - field filters formatted as a JSON dictionary (see **Field Aggregations** for examples) + +**Example cURL command and output:** + +``` +$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ + 'https://localhost/mapi/document' \ + -d '{"limit": 10, filter":{"zeek.uid":"CYeji2z7CKmPRGyga"}}' +``` + +```json +{ + "filter": { + "zeek.uid": "CYeji2z7CKmPRGyga" + }, + "range": [ + 0, + 1643056677 + ], + "results": [ + { + "_id": "220124-CYeji2z7CKmPRGyga-http-7677", + "_index": "arkime_sessions3-220124", + "_score": 0.0, + "_source": { + "@timestamp": "2022-01-24T20:31:01.846Z", + "@version": "1", + "agent": { + "hostname": "filebeat", + "id": "bc25716b-8fe7-4de6-a357-65c7d3c15c33", + "name": "filebeat", + "type": "filebeat", + "version": "7.10.2" + }, + "client": { + "bytes": 0 + }, + "destination": { + "as": { + "full": "AS54113 Fastly" + }, + "geo": { + "city_name": "Seattle", + "continent_code": "NA", + "country_code2": "US", + "country_code3": "US", + "country_iso_code": "US", + "country_name": "United States", + "dma_code": 819, + "ip": "151.101.54.132", + "latitude": 47.6092, + "location": { + "lat": 47.6092, + "lon": -122.3314 + }, + "longitude": -122.3314, + "postal_code": "98111", + "region_code": "WA", + "region_name": "Washington", + "timezone": "America/Los_Angeles" + }, + "ip": "151.101.54.132", + "port": 80 + }, + "ecs": { + "version": "1.6.0" + }, + "event": { + "action": [ + "GET" + ], + "category": [ + "web", + "network" + ], +… +``` \ No newline at end of file diff --git a/docs/api-event-logging.md b/docs/api-event-logging.md new file mode 100644 index 000000000..e4391e3d5 --- /dev/null +++ b/docs/api-event-logging.md @@ -0,0 +1,67 @@ +# Event Logging + +`POST` - /mapi/event + +A webhook that accepts alert data to be reindexed into OpenSearch as session records for viewing in Malcolm's [dashboards](dashboards.md#Dashboards). See [Alerting](alerting.md#Alerting) for more details and an example of how this API is used. + +**Example input:** + +```json +{ + "alert": { + "monitor": { + "name": "Malcolm API Loopback Monitor" + }, + "trigger": { + "name": "Malcolm API Loopback Trigger", + "severity": 4 + }, + "period": { + "start": "2022-03-08T18:03:30.576Z", + "end": "2022-03-08T18:04:30.576Z" + }, + "results": [ + { + "_shards": { + "total": 5, + "failed": 0, + "successful": 5, + "skipped": 0 + }, + "hits": { + "hits": [], + "total": { + "value": 697, + "relation": "eq" + }, + "max_score": null + }, + "took": 1, + "timed_out": false + } + ], + "body": "", + "alert": "PLauan8BaL6eY1yCu9Xj", + "error": "" + } +} +``` + +**Example output:** + +```json +{ + "_index": "arkime_sessions3-220308", + "_type": "_doc", + "_id": "220308-PLauan8BaL6eY1yCu9Xj", + "_version": 4, + "result": "updated", + "_shards": { + "total": 1, + "successful": 1, + "failed": 0 + }, + "_seq_no": 9045, + "_primary_term": 1 +} +``` \ No newline at end of file diff --git a/docs/api-examples.md b/docs/api-examples.md new file mode 100644 index 000000000..f060d8c11 --- /dev/null +++ b/docs/api-examples.md @@ -0,0 +1,1479 @@ +# Examples + +Some security-related API examples: + +* [Protocols](#Protocols) +* [Software](#Software) +* [User agent](#UserAgent) +* [External traffic (outbound/inbound)](#ExternalTraffic) +* [Cross-segment traffic](#CrossSegmentTraffic) +* [Plaintext password](#PlaintextPassword) +* [Insecure/outdated protocols](#InsecureProtocol) +* [Notice categories](#NoticeCategories) +* [Severity tags](#SeverityTags) + +## Protocols + +``` +/mapi/agg/network.type,network.transport,network.protocol,network.protocol_version +``` + +```json +{ + "fields": [ + "network.type", + "network.transport", + "network.protocol", + "network.protocol_version" + ], + "filter": null, + "range": [ + 1970, + 1643067256 + ], + "urls": [ + "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" + ], + "values": { + "buckets": [ + { + "doc_count": 442240, + "key": "ipv4", + "values": { + "buckets": [ + { + "doc_count": 279538, + "key": "udp", + "values": { + "buckets": [ + { + "doc_count": 266527, + "key": "bacnet", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 12365, + "key": "dns", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 78, + "key": "dhcp", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 44, + "key": "ntp", + "values": { + "buckets": [ + { + "doc_count": 22, + "key": "4" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 3, + "key": "enip", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "krb", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "syslog", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 30824, + "key": "tcp", + "values": { + "buckets": [ + { + "doc_count": 7097, + "key": "smb", + "values": { + "buckets": [ + { + "doc_count": 4244, + "key": "1" + }, + { + "doc_count": 1438, + "key": "2" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1792, + "key": "http", + "values": { + "buckets": [ + { + "doc_count": 829, + "key": "1.0" + }, + { + "doc_count": 230, + "key": "1.1" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1280, + "key": "dce_rpc", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 857, + "key": "s7comm", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 426, + "key": "ntlm", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 378, + "key": "gssapi", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 146, + "key": "tds", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 125, + "key": "ssl", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 91, + "key": "tls", + "values": { + "buckets": [ + { + "doc_count": 48, + "key": "TLSv13" + }, + { + "doc_count": 28, + "key": "TLSv12" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 29, + "key": "ssh", + "values": { + "buckets": [ + { + "doc_count": 18, + "key": "2" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 26, + "key": "modbus", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 17, + "key": "iso_cotp", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 8, + "key": "enip", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 6, + "key": "rdp", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 4, + "key": "ftp", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 4, + "key": "krb", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 4, + "key": "rfb", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 3, + "key": "ldap", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "telnet", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 848, + "key": "icmp", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1573, + "key": "ipv6", + "values": { + "buckets": [ + { + "doc_count": 1486, + "key": "udp", + "values": { + "buckets": [ + { + "doc_count": 1433, + "key": "dns", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 80, + "key": "icmp", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` + +## Software + +``` +/mapi/agg/zeek.software.name,zeek.software.unparsed_version +``` + +```json +{ + "fields": [ + "zeek.software.name", + "zeek.software.unparsed_version" + ], + "filter": null, + "range": [ + 1970, + 1643067759 + ], + "urls": [ + "/dashboards/app/dashboards#/view/87d990cc-9e0b-41e5-b8fe-b10ae1da0c85?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" + ], + "values": { + "buckets": [ + { + "doc_count": 6, + "key": "Chrome", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" + }, + { + "doc_count": 1, + "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" + }, + { + "doc_count": 1, + "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" + }, + { + "doc_count": 1, + "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" + }, + { + "doc_count": 1, + "key": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.36 Safari/525.19" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 6, + "key": "Nmap-SSH", + "values": { + "buckets": [ + { + "doc_count": 3, + "key": "Nmap-SSH1-Hostkey" + }, + { + "doc_count": 3, + "key": "Nmap-SSH2-Hostkey" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 5, + "key": "MSIE", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" + }, + { + "doc_count": 1, + "key": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E)" + }, + { + "doc_count": 1, + "key": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" + }, + { + "doc_count": 1, + "key": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 4, + "key": "Firefox", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" + }, + { + "doc_count": 1, + "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:34.0) Gecko/20100101 Firefox/34.0" + }, + { + "doc_count": 1, + "key": "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 3, + "key": "ECS (sec", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "ECS (sec/96EE)" + }, + { + "doc_count": 1, + "key": "ECS (sec/97A6)" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 3, + "key": "NmapNSE", + "values": { + "buckets": [ + { + "doc_count": 3, + "key": "NmapNSE_1.0" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "Microsoft-Windows", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "Microsoft-Windows/6.1 UPnP/1.0 Windows-Media-Player-DMS/12.0.7601.17514 DLNADOC/1.50" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "Microsoft-Windows-NT", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0 Microsoft-HTTPAPI/2.0" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "SimpleHTTP", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "SimpleHTTP/0.6 Python/2.7.17" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "Windows-Media-Player-DMS", + "values": { + "buckets": [ + { + "doc_count": 2, + "key": "Windows-Media-Player-DMS/12.0.7601.17514" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "A-B WWW", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "A-B WWW/0.1" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "CONF-CTR-NAE1", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "CONF-CTR-NAE1" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "ClearSCADA", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "ClearSCADA/6.72.4644.1" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "GoAhead-Webs", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "GoAhead-Webs" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "MSFT", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "MSFT 5.0" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "Microsoft-IIS", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "Microsoft-IIS/7.5" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "Microsoft-WebDAV-MiniRedir", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "Microsoft-WebDAV-MiniRedir/6.1.7601" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "Python-urllib", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "Python-urllib/2.7" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "Schneider-WEB/V", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "Schneider-WEB/V2.1.4" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "Version", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "Version_1.0" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "nginx", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "nginx" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "sublime-license-check", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "sublime-license-check/3.0" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` + +## User agent + +``` +/mapi/agg/user_agent.original +``` + +```json +{ + "fields": [ + "user_agent.original" + ], + "filter": null, + "range": [ + 1970, + 1643067845 + ], + "values": { + "buckets": [ + { + "doc_count": 230, + "key": "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" + }, + { + "doc_count": 142, + "key": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" + }, + { + "doc_count": 114, + "key": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" + }, + { + "doc_count": 50, + "key": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)" + }, + { + "doc_count": 48, + "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" + }, + { + "doc_count": 43, + "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" + }, + { + "doc_count": 33, + "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:34.0) Gecko/20100101 Firefox/34.0" + }, + { + "doc_count": 17, + "key": "Python-urllib/2.7" + }, + { + "doc_count": 12, + "key": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" + }, + { + "doc_count": 9, + "key": "Microsoft-Windows/6.1 UPnP/1.0 Windows-Media-Player-DMS/12.0.7601.17514 DLNADOC/1.50" + }, + { + "doc_count": 9, + "key": "Windows-Media-Player-DMS/12.0.7601.17514" + }, + { + "doc_count": 8, + "key": "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko" + }, + { + "doc_count": 5, + "key": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" + }, + { + "doc_count": 5, + "key": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.36 Safari/525.19" + }, + { + "doc_count": 3, + "key": "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0" + }, + { + "doc_count": 2, + "key": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" + }, + { + "doc_count": 1, + "key": "Microsoft-WebDAV-MiniRedir/6.1.7601" + }, + { + "doc_count": 1, + "key": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E)" + }, + { + "doc_count": 1, + "key": "sublime-license-check/3.0" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` + +## External traffic (outbound/inbound) + +``` +$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ + 'https://localhost/mapi/agg/network.protocol' \ + -d '{"filter":{"network.direction":["inbound","outbound"]}}' +``` + +```json +{ + "fields": [ + "network.protocol" + ], + "filter": { + "network.direction": [ + "inbound", + "outbound" + ] + }, + "range": [ + 1970, + 1643068000 + ], + "urls": [ + "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" + ], + "values": { + "buckets": [ + { + "doc_count": 202597, + "key": "bacnet" + }, + { + "doc_count": 129, + "key": "tls" + }, + { + "doc_count": 128, + "key": "ssl" + }, + { + "doc_count": 33, + "key": "http" + }, + { + "doc_count": 33, + "key": "ntp" + }, + { + "doc_count": 20, + "key": "dns" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` + +## Cross-segment traffic + +``` +$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ + 'https://localhost/mapi/agg/source.segment,destination.segment,network.protocol' \ + -d '{"filter":{"tags":"cross_segment"}}' +``` + +```json +{ + "fields": [ + "source.segment", + "destination.segment", + "network.protocol" + ], + "filter": { + "tags": "cross_segment" + }, + "range": [ + 1970, + 1643068080 + ], + "urls": [ + "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" + ], + "values": { + "buckets": [ + { + "doc_count": 6893, + "key": "Corporate", + "values": { + "buckets": [ + { + "doc_count": 6893, + "key": "OT", + "values": { + "buckets": [ + { + "doc_count": 891, + "key": "enip" + }, + { + "doc_count": 889, + "key": "cip" + }, + { + "doc_count": 202, + "key": "http" + }, + { + "doc_count": 146, + "key": "modbus" + }, + { + "doc_count": 1, + "key": "ftp" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 189, + "key": "OT", + "values": { + "buckets": [ + { + "doc_count": 138, + "key": "Corporate", + "values": { + "buckets": [ + { + "doc_count": 128, + "key": "http" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 51, + "key": "DMZ", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 28, + "key": "Battery Network", + "values": { + "buckets": [ + { + "doc_count": 25, + "key": "Combined Cycle BOP", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 3, + "key": "Solar Panel Network", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 20, + "key": "Combined Cycle BOP", + "values": { + "buckets": [ + { + "doc_count": 11, + "key": "Battery Network", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 9, + "key": "Solar Panel Network", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "Solar Panel Network", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "Combined Cycle BOP", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` + +## Plaintext password + +``` +$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ + 'https://localhost/mapi/agg/network.protocol' \ + -d '{"filter":{"!related.password":null}}' +``` + +```json +{ + "fields": [ + "network.protocol" + ], + "filter": { + "!related.password": null + }, + "range": [ + 1970, + 1643068162 + ], + "urls": [ + "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" + ], + "values": { + "buckets": [ + { + "doc_count": 20, + "key": "http" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` + +## Insecure/outdated protocols + +``` +$ curl -k -u username -L -XPOST -H 'Content-Type: application/json' \ + 'https://localhost/mapi/agg/network.protocol,network.protocol_version' \ + -d '{"filter":{"event.severity_tags":"Insecure or outdated protocol"}}' +``` + +```json +{ + "fields": [ + "network.protocol", + "network.protocol_version" + ], + "filter": { + "event.severity_tags": "Insecure or outdated protocol" + }, + "range": [ + 1970, + 1643068248 + ], + "urls": [ + "/dashboards/app/dashboards#/view/abdd7550-2c7c-40dc-947e-f6d186a158c4?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" + ], + "values": { + "buckets": [ + { + "doc_count": 4244, + "key": "smb", + "values": { + "buckets": [ + { + "doc_count": 4244, + "key": "1" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "ftp", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "rdp", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "5.1" + }, + { + "doc_count": 1, + "key": "5.2" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 2, + "key": "telnet", + "values": { + "buckets": [], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` + +## Notice categories + +``` +/mapi/agg/zeek.notice.category,zeek.notice.sub_category +``` + +```json +{ + "fields": [ + "zeek.notice.category", + "zeek.notice.sub_category" + ], + "filter": null, + "range": [ + 1970, + 1643068300 + ], + "urls": [ + "/dashboards/app/dashboards#/view/f1f09567-fc7f-450b-a341-19d2f2bb468b?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))", + "/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" + ], + "values": { + "buckets": [ + { + "doc_count": 100, + "key": "ATTACK", + "values": { + "buckets": [ + { + "doc_count": 42, + "key": "Lateral_Movement_Extracted_File" + }, + { + "doc_count": 30, + "key": "Lateral_Movement" + }, + { + "doc_count": 17, + "key": "Discovery" + }, + { + "doc_count": 5, + "key": "Execution" + }, + { + "doc_count": 5, + "key": "Lateral_Movement_Multiple_Attempts" + }, + { + "doc_count": 1, + "key": "Lateral_Movement_and_Execution" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 14, + "key": "EternalSafety", + "values": { + "buckets": [ + { + "doc_count": 11, + "key": "EternalSynergy" + }, + { + "doc_count": 3, + "key": "ViolationPidMid" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 6, + "key": "Scan", + "values": { + "buckets": [ + { + "doc_count": 6, + "key": "Port_Scan" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + }, + { + "doc_count": 1, + "key": "Ripple20", + "values": { + "buckets": [ + { + "doc_count": 1, + "key": "Treck_TCP_observed" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` + +## Severity tags + +``` +/mapi/agg/event.severity_tags +``` + +```json +{ + "fields": [ + "event.severity_tags" + ], + "filter": null, + "range": [ + 1970, + 1643068363 + ], + "urls": [ + "/dashboards/app/dashboards#/view/d2dd0180-06b1-11ec-8c6b-353266ade330?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))", + "/dashboards/app/dashboards#/view/95479950-41f2-11ea-88fa-7151df485405?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:32:50Z',to:now))" + ], + "values": { + "buckets": [ + { + "doc_count": 160180, + "key": "Outbound traffic" + }, + { + "doc_count": 43059, + "key": "Inbound traffic" + }, + { + "doc_count": 11091, + "key": "Connection attempt rejected" + }, + { + "doc_count": 8967, + "key": "Connection attempt, no reply" + }, + { + "doc_count": 7131, + "key": "Cross-segment traffic" + }, + { + "doc_count": 4250, + "key": "Insecure or outdated protocol" + }, + { + "doc_count": 2219, + "key": "External traffic" + }, + { + "doc_count": 1985, + "key": "Sensitive country" + }, + { + "doc_count": 760, + "key": "Weird" + }, + { + "doc_count": 537, + "key": "Connection aborted (originator)" + }, + { + "doc_count": 474, + "key": "Connection aborted (responder)" + }, + { + "doc_count": 206, + "key": "File transfer (high concern)" + }, + { + "doc_count": 100, + "key": "MITRE ATT&CK framework technique" + }, + { + "doc_count": 66, + "key": "Service on non-standard port" + }, + { + "doc_count": 64, + "key": "Signature (capa)" + }, + { + "doc_count": 30, + "key": "Signature (YARA)" + }, + { + "doc_count": 25, + "key": "Signature (ClamAV)" + }, + { + "doc_count": 20, + "key": "Cleartext password" + }, + { + "doc_count": 19, + "key": "Long connection" + }, + { + "doc_count": 15, + "key": "Notice (vulnerability)" + }, + { + "doc_count": 13, + "key": "File transfer (medium concern)" + }, + { + "doc_count": 6, + "key": "Notice (scan)" + }, + { + "doc_count": 1, + "key": "High volume connection" + } + ], + "doc_count_error_upper_bound": 0, + "sum_other_doc_count": 0 + } +} +``` diff --git a/docs/api-fields.md b/docs/api-fields.md new file mode 100644 index 000000000..97803ff95 --- /dev/null +++ b/docs/api-fields.md @@ -0,0 +1,26 @@ +# Fields + +`GET` - /mapi/fields + +Returns the (very long) list of fields known to Malcolm, comprised of data from Arkime's [`fields` table](https://arkime.com/apiv3#fields-api), the Malcolm [OpenSearch template]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/dashboards/templates/malcolm_template.json) and the OpenSearch Dashboards index pattern API. + +**Example output:** + +```json +{ + "fields": { + "@timestamp": { + "type": "date" + }, +… + "zeek.x509.san_uri": { + "description": "Subject Alternative Name URI", + "type": "string" + }, + "zeek.x509.san_uri.text": { + "type": "string" + } + }, + "total": 2005 +} +``` diff --git a/docs/api-indices.md b/docs/api-indices.md new file mode 100644 index 000000000..2003b7880 --- /dev/null +++ b/docs/api-indices.md @@ -0,0 +1,28 @@ +# Indices + +`GET` - /mapi/indices + +Lists [information related to the underlying OpenSearch indices](https://opensearch.org/docs/latest/opensearch/rest-api/cat/cat-indices/), similar to Arkime's [esindices](https://arkime.com/apiv3#esindices-api) API. + +**Example output:** + +```json +{ + "indices": [ +… + { + "docs.count": "2268613", + "docs.deleted": "0", + "health": "green", + "index": "arkime_sessions3-210301", + "pri": "1", + "pri.store.size": "1.8gb", + "rep": "0", + "status": "open", + "store.size": "1.8gb", + "uuid": "w-4Q0ofBTdWO9KqeIIAAWg" + }, +… + ] +} +``` diff --git a/docs/api-ping.md b/docs/api-ping.md new file mode 100644 index 000000000..75dc67070 --- /dev/null +++ b/docs/api-ping.md @@ -0,0 +1,11 @@ +# Ping + +`GET` - /mapi/ping + +Returns `pong` (for a simple "up" check). + +Example output: + +``` +{"ping":"pong"} +``` \ No newline at end of file diff --git a/docs/api-version.md b/docs/api-version.md new file mode 100644 index 000000000..123648891 --- /dev/null +++ b/docs/api-version.md @@ -0,0 +1,49 @@ +# Version Information + +`GET` - /mapi/version + +Returns version information about Malcolm and version/[health](https://opensearch.org/docs/latest/opensearch/rest-api/cluster-health/) information about the underlying OpenSearch instance. + +**Example output:** + +```json +{ + "built": "2022-01-18T16:10:39Z", + "opensearch": { + "cluster_name": "docker-cluster", + "cluster_uuid": "TcSiEaOgTdO_l1IivYz2gA", + "name": "opensearch", + "tagline": "The OpenSearch Project: https://opensearch.org/", + "version": { + "build_date": "2021-12-21T01:36:21.407473Z", + "build_hash": "8a529d77c7432bc45b005ac1c4ba3b2741b57d4a", + "build_snapshot": false, + "build_type": "tar", + "lucene_version": "8.10.1", + "minimum_index_compatibility_version": "6.0.0-beta1", + "minimum_wire_compatibility_version": "6.8.0", + "number": "7.10.2" + } + }, + "opensearch_health": { + "active_primary_shards": 29, + "active_shards": 29, + "active_shards_percent_as_number": 82.85714285714286, + "cluster_name": "docker-cluster", + "delayed_unassigned_shards": 0, + "discovered_master": true, + "initializing_shards": 0, + "number_of_data_nodes": 1, + "number_of_in_flight_fetch": 0, + "number_of_nodes": 1, + "number_of_pending_tasks": 0, + "relocating_shards": 0, + "status": "yellow", + "task_max_waiting_in_queue_millis": 0, + "timed_out": false, + "unassigned_shards": 6 + }, + "sha": "8ddbbf4", + "version": "5.2.0" +} +``` diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 000000000..ff33de19b --- /dev/null +++ b/docs/api.md @@ -0,0 +1,12 @@ +# API + +* [Field Aggregations](api-aggregations.md) +* [Document Lookup](api-document-lookup.md) +* [Event Logging](api-event-logging.md) +* [Fields](api-fields.md) +* [Indices](api-indices.md) +* [Ping](api-ping.md) +* [Version](api-version.md) +* [Examples](api-examples.md) + +Malcolm provides a [REST API]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/api/project/__init__.py) that can be used to programatically query some aspects of Malcolm's status and data. Malcolm's API is not to be confused with the [Viewer API](https://arkime.com/apiv3) provided by Arkime, although there may be some overlap in functionality. diff --git a/docs/arkime.md b/docs/arkime.md new file mode 100644 index 000000000..755798a76 --- /dev/null +++ b/docs/arkime.md @@ -0,0 +1,188 @@ +# Arkime + +* [Arkime](#Arkime) + - [Zeek log integration](#ArkimeZeek) + + [Correlating Zeek logs and Arkime sessions](#ZeekArkimeFlowCorrelation) + - [Help](#ArkimeHelp) + - [Sessions](#ArkimeSessions) + + [PCAP Export](#ArkimePCAPExport) + - [SPIView](#ArkimeSPIView) + - [SPIGraph](#ArkimeSPIGraph) + - [Connections](#ArkimeConnections) + - [Hunt](#ArkimeHunt) + - [Statistics](#ArkimeStats) + - [Settings](#ArkimeSettings) + +The Arkime interface will be accessible over HTTPS on port 443 at the docker hosts IP address (e.g., [https://localhost](https://localhost) if you are connecting locally). + +## Zeek log integration + +A stock installation of Arkime extracts all of its network connection ("session") metadata ("SPI" or "Session Profile Information") from full packet capture artifacts (PCAP files). Zeek (formerly Bro) generates similar session metadata, linking network events to sessions via a connection UID. Malcolm aims to facilitate analysis of Zeek logs by mapping values from Zeek logs to the Arkime session database schema for equivalent fields, and by creating new "native" Arkime database fields for all the other Zeek log values for which there is not currently an equivalent in Arkime: + +![Zeek log session record](./images/screenshots/arkime_session_zeek.png) + +In this way, when full packet capture is an option, analysis of PCAP files can be enhanced by the additional information Zeek provides. When full packet capture is not an option, similar analysis can still be performed using the same interfaces and processes using the Zeek logs alone. + +A few values of particular mention include **Data Source** (`event.provider` in OpenSearch), which can be used to distinguish from among the sources of the network traffic metadata record (e.g., `zeek` for Zeek logs and `arkime` for Arkime sessions); and, **Log Type** (`event.dataset` in OpenSearch), which corresponds to the kind of Zeek `.log` file from which the record was created. In other words, a search could be restricted to records from `conn.log` by searching `event.provider == zeek && event.dataset == conn`, or restricted to records from `weird.log` by searching `event.provider == zeek && event.dataset == weird`. + +Click the icon of the owl **🦉** in the upper-left hand corner of to access the Arkime usage documentation (accessible at [https://localhost/help](https://localhost/help) if you are connecting locally), click the **Fields** label in the navigation pane, then search for `zeek` to see a list of the other Zeek log types and fields available to Malcolm. + +![Zeek fields](./images/screenshots/arkime_help_fields.png) + +The values of records created from Zeek logs can be expanded and viewed like any native Arkime session by clicking the plus **➕** icon to the left of the record in the Sessions view. However, note that when dealing with these Zeek records the full packet contents are not available, so buttons dealing with viewing and exporting PCAP information will not behave as they would for records from PCAP files. Other than that, Zeek records and their values are usable in Malcolm just like native PCAP session records. + +### Correlating Zeek logs and Arkime sessions + +The Arkime interface displays both Zeek logs and Arkime sessions alongside each other. Using fields common to both data sources, one can [craft queries](queries-cheat-sheet.md#SearchCheatSheet) to filter results matching desired criteria. + +A few fields of particular mention that help limit returned results to those Zeek logs and Arkime session records generated from the same network connection are [Community ID](https://github.com/corelight/community-id-spec) (`network.community_id`) and Zeek's [connection UID](https://docs.zeek.org/en/stable/examples/logs/#using-uids) (`zeek.uid`), which Malcolm maps to both Arkime's `rootId` field and the [ECS](https://www.elastic.co/guide/en/ecs/current/ecs-event.html#field-event-id) `event.id` field. + +Community ID is specification for standard flow hashing [published by Corelight](https://github.com/corelight/community-id-spec) with the intent of making it easier to pivot from one dataset (e.g., Arkime sessions) to another (e.g., Zeek `conn.log` entries). In Malcolm both Arkime and [Zeek](https://github.com/corelight/zeek-community-id) populate this value, which makes it possible to filter for a specific network connection and see both data sources' results for that connection. + +The `rootId` field is used by Arkime to link session records together when a particular session has too many packets to be represented by a single session. When normalizing Zeek logs to Arkime's schema, Malcolm piggybacks on `rootId` to store Zeek's [connection UID](https://docs.zeek.org/en/stable/examples/logs/#using-uids) to crossreference entries across Zeek log types. The connection UID is also stored in `zeek.uid`. + +Filtering on community ID OR'ed with zeek UID (e.g., `network.community_id == "1:r7tGG//fXP1P0+BXH3zXETCtEFI=" || rootId == "CQcoro2z6adgtGlk42"`) is an effective way to see both the Arkime sessions and Zeek logs generated by a particular network connection. + +![Correlating Arkime sessions and Zeek logs](./images/screenshots/arkime_correlate_communityid_uid.png) + +## Help + +Click the icon of the owl 🦉 in the upper-left hand corner of to access the Arkime usage documentation (accessible at [https://localhost/help](https://localhost/help) if you are connecting locally), which includes such topics as [search syntax](https://localhost/help#search), the [Sessions view](https://localhost/help#sessions), [SPIView](https://localhost/help#spiview), [SPIGraph](https://localhost/help#spigraph), and the [Connections](https://localhost/help#connections) graph. + +## Sessions + +The **Sessions** view provides low-level details of the sessions being investigated, whether they be Arkime sessions created from PCAP files or [Zeek logs mapped](#ArkimeZeek) to the Arkime session database schema. + +![Arkime's Sessions view](./images/screenshots/arkime_sessions.png) + +The **Sessions** view contains many controls for filtering the sessions displayed from all sessions down to sessions of interest: + +* [search bar](https://localhost/help#search): Indicated by the magnifying glass **🔍** icon, the search bar allows defining filters on session/log metadata +* [time bounding](https://localhost/help#timebounding) controls: The **🕘**, **Start**, **End**, **Bounding**, and **Interval** fields, and the **date histogram** can be used to visually zoom and pan the time range being examined. +* search button: The **Search** button re-runs the sessions query with the filters currently specified. +* views button: Indicated by the eyeball **👁** icon, views allow overlaying additional previously-specified filters onto the current sessions filters. For convenience, Malcolm provides several Arkime preconfigured views including filtering on the `event.dataset` field. + +![Malcolm views](./images/screenshots/arkime_apply_view.png) + +* map: A global map can be expanded by clicking the globe **🌎** icon. This allows filtering sessions by IP-based geolocation when possible. + +Some of these filter controls are also available on other Arkime pages (such as SPIView, SPIGraph, Connections, and Hunt). + +The number of sessions displayed per page, as well as the page currently displayed, can be specified using the paging controls underneath the time bounding controls. + +The sessions table is displayed below the filter controls. This table contains the sessions/logs matching the specified filters. + +To the left of the column headers are two buttons. The **Toggle visible columns** button, indicated by a grid **⊞** icon, allows toggling which columns are displayed in the sessions table. The **Save or load custom column configuration** button, indicated by a columns **◫** icon, allows saving the current displayed columns or loading previously-saved configurations. This is useful for customizing which columns are displayed when investigating different types of traffic. Column headers can also be clicked to sort the results in the table, and column widths may be adjusted by dragging the separators between column headers. + +Details for individual sessions/logs can be expanded by clicking the plus **➕** icon on the left of each row. Each row may contain multiple sections and controls, depending on whether the row represents a Arkime session or a [Zeek log](#ArkimeZeek). Clicking the field names and values in the details sections allows additional filters to be specified or summary lists of unique values to be exported. + +When viewing Arkime session details (ie., a session generated from a PCAP file), an additional packets section will be visible underneath the metadata sections. When the details of a session of this type are expanded, Arkime will read the packet(s) comprising the session for display here. Various controls can be used to adjust how the packet is displayed (enabling **natural** decoding and enabling **Show Images & Files** may produce visually pleasing results), and other options (including PCAP download, carving images and files, applying decoding filters, and examining payloads in [CyberChef](https://github.com/gchq/CyberChef)) are available. + +See also Arkime's usage documentation for more information on the [Sessions view](https://localhost/help#sessions). + +### PCAP Export + +Clicking the down arrow **▼** icon to the far right of the search bar presents a list of actions including **PCAP Export** (see Arkime's [sessions help](https://localhost/help#sessions) for information on the other actions). When full PCAP sessions are displayed, the **PCAP Export** feature allows you to create a new PCAP file from the matching Arkime sessions, including controls for which sessions are included (open items, visible items, or all matching items) and whether or not to include linked segments. Click **Export PCAP** button to generate the PCAP, after which you'll be presented with a browser download dialog to save or open the file. Note that depending on the scope of the filters specified this might take a long time (or, possibly even time out). + +![Export PCAP](./images/screenshots/arkime_export_pcap.png) + +## SPIView + +Arkime's **SPI** (**S**ession **P**rofile **I**nformation) **View** provides a quick and easy-to-use interface for exploring session/log metrics. The SPIView page lists categories for general session metrics (e.g., protocol, source and destination IP addresses, sort and destination ports, etc.) as well as for all of various types of network traffic understood by Malcolm. These categories can be expanded and the top *n* values displayed, along with each value's cardinality, for the fields of interest they contain. + +![Arkime's SPIView](./images/screenshots/arkime_spiview.png) + +Click the the plus **➕** icon to the right of a category to expand it. The values for specific fields are displayed by clicking the field description in the field list underneath the category name. The list of field names can be filtered by typing part of the field name in the *Search for fields to display in this category* text input. The **Load All** and **Unload All** buttons can be used to toggle display of all of the fields belonging to that category. Once displayed, a field's name or one of its values may be clicked to provide further actions for filtering or displaying that field or its values. Of particular interest may be the **Open [fieldname] SPI Graph** option when clicking on a field's name. This will open a new tab with the SPI Graph ([see below](#ArkimeSPIGraph)) populated with the field's top values. + +Note that because the SPIView page can potentially run many queries, SPIView limits the search domain to seven days (in other words, seven indices, as each index represents one day's worth of data). When using SPIView, you will have best results if you limit your search time frame to less than or equal to seven days. This limit can be adjusted by editing the `spiDataMaxIndices` setting in [config.ini]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/arkime/etc/config.ini) and rebuilding the `malcolmnetsec/arkime` docker container. + +See also Arkime's usage documentation for more information on [SPIView](https://localhost/help#spiview). + +## SPIGraph + +Arkime's **SPI** (**S**ession **P**rofile **I**nformation) **Graph** visualizes the occurrence of some field's top *n* values over time, and (optionally) geographically. This is particularly useful for identifying trends in a particular type of communication over time: traffic using a particular protocol when seen sparsely at regular intervals on that protocol's date histogram in the SPIGraph may indicate a connection check, polling, or beaconing (for example, see the `llmnr` protocol in the screenshot below). + +![Arkime's SPIGraph](./images/screenshots/arkime_spigraph.png) + +Controls can be found underneath the time bounding controls for selecting the field of interest, the number of elements to be displayed, the sort order, and a periodic refresh of the data. + +See also Arkime's usage documentation for more information on [SPIGraph](https://localhost/help#spigraph). + +## Connections + +The **Connections** page presents network communications via a force-directed graph, making it easy to visualize logical relationships between network hosts. + +![Arkime's Connections graph](./images/screenshots/arkime_connections.png) + +Controls are available for specifying the query size (where smaller values will execute more quickly but may only contain an incomplete representation of the top *n* sessions, and larger values may take longer to execute but will be more complete), which fields to use as the source and destination for node values, a minimum connections threshold, and the method for determining the "weight" of the link between two nodes. As is the case with most other visualizations in Arkime, the graph is interactive: clicking on a node or the link between two nodes can be used to modify query filters, and the nodes themselves may be repositioned by dragging and dropping them. A node's color indicates whether it communicated as a source/originator, a destination/responder, or both. + +While the default source and destination fields are *Src IP* and *Dst IP:Dst Port*, the Connections view is able to use any combination of fields. For example: + +* *Src OUI* and *Dst OUI* (hardware manufacturers) +* *Src IP* and *Protocols* +* *Originating Network Segment* and *Responding Network Segment* (see [CIDR subnet to network segment name mapping](host-and-subnet-mapping.md#SegmentNaming)) +* *Originating GeoIP City* and *Responding GeoIP City* + +or any other combination of these or other fields. + +See also Arkime's usage documentation for more information on the [Connections graph](https://localhost/help#connections). + +## Hunt + +Arkime's **Hunt** feature allows an analyst to search within the packets themselves (including payload data) rather than simply searching the session metadata. The search string may be specified using ASCII (with or without case sensitivity), hex codes, or regular expressions. Once a hunt job is complete, matching sessions can be viewed in the [Sessions](#ArkimeSessions) view. + +Clicking the **Create a packet search job** on the Hunt page will allow you to specify the following parameters for a new hunt job: + +* a packet search job **name** +* a **maximum number of packets** to examine per session +* the **search string** and its format (*ascii*, *ascii (case sensitive)*, *hex*, *regex*, or *hex regex*) +* whether to search **source packets**, **destination packets**, or both +* whether to search **raw** or **reassembled** packets + +Click the **➕ Create** button to begin the search. Arkime will scan the source PCAP files from which the sessions were created according to the search criteria. Note that whatever filters were specified when the hunt job is executed will apply to the hunt job as well; the number of sessions matching the current filters will be displayed above the hunt job parameters with text like "ⓘ Creating a new packet search job will search the packets of # sessions." + +![Hunt creation](./images/screenshots/arkime_hunt_creation.png) + +Once a hunt job is submitted, it will be assigned a unique hunt ID (a long unique string of characters like `yuBHAGsBdljYmwGkbEMm`) and its progress will be updated periodically in the **Hunt Job Queue** with the execution percent complete, the number of matches found so far, and the other parameters with which the job was submitted. More details for the hunt job can be viewed by expanding its row with the plus **➕** icon on the left. + +![Hunt completed](./images/screenshots/arkime_hunt_finished.png) + +Once the hunt job is complete (and a minute or so has passed, as the `huntId` must be added to the matching session records in the database), click the folder **📂** icon on the right side of the hunt job row to open a new [Sessions](#ArkimeSessions) tab with the search bar prepopulated to filter to sessions with packets matching the search criteria. + +![Hunt result sessions](./images/screenshots/arkime_hunt_sessions.png) + +From this list of filtered sessions you can expand session details and explore packet payloads which matched the hunt search criteria. + +The hunt feature is available only for sessions created from full packet capture data, not Zeek logs. This being the case, it is a good idea to click the eyeball **👁** icon and select the **Arkime Sessions** view to exclude Zeek logs from candidate sessions prior to using the hunt feature. + +See also Arkime's usage documentation for more information on the [hunt feature](https://localhost/help#hunt). + +## Statistics + +Arkime provides several other reports which show information about the state of Arkime and the underlying OpenSearch database. + +The **Files** list displays a list of PCAP files processed by Arkime, the date and time of the earliest packet in each file, and the file size: + +![Arkime's Files list](./images/screenshots/arkime_files.png) + +The **ES Indices** list (available under the **Stats** page) lists the OpenSearch indices within which log data is contained: + +![Arkime's ES indices list](./images/screenshots/arkime_es_stats.png) + +The **History** view provides a historical list of queries issues to Arkime and the details of those queries: + +![Arkime's History view](./images/screenshots/arkime_history.png) + +See also Arkime's usage documentation for more information on the [Files list](https://localhost/help#files), [statistics](https://localhost/help#files), and [history](https://localhost/help#history). + +## Settings + +### General settings + +The **Settings** page can be used to tweak Arkime preferences, defined additional custom views and column configurations, tweak the color theme, and more. + +See Arkime's usage documentation for more information on [settings](https://localhost/help#settings). + +![Arkime general settings](./images/screenshots/arkime_general_settings.png) + +![Arkime custom view management](./images/screenshots/arkime_view_settings.png) \ No newline at end of file diff --git a/docs/authsetup.md b/docs/authsetup.md new file mode 100644 index 000000000..4a0fe6a10 --- /dev/null +++ b/docs/authsetup.md @@ -0,0 +1,105 @@ +# Configure authentication + +* [Configure authentication](#AuthSetup) + - [Local account management](#AuthBasicAccountManagement) + - [Lightweight Directory Access Protocol (LDAP) authentication](#AuthLDAP) + + [LDAP connection security](#AuthLDAPSecurity) + - [TLS certificates](#TLSCerts) + +Malcolm requires authentication to access the [user interface](quickstart.md#UserInterfaceURLs). [Nginx](https://nginx.org/) can authenticate users with either local TLS-encrypted HTTP basic authentication or using a remote Lightweight Directory Access Protocol (LDAP) authentication server. + +With the local basic authentication method, user accounts are managed by Malcolm and can be created, modified, and deleted using a [user management web interface](#AuthBasicAccountManagement). This method is suitable in instances where accounts and credentials do not need to be synced across many Malcolm installations. + +LDAP authentication are managed on a remote directory service, such as a [Microsoft Active Directory Domain Services](https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/get-started/virtual-dc/active-directory-domain-services-overview) or [OpenLDAP](https://www.openldap.org/). + +Malcolm's authentication method is defined in the `x-auth-variables` section near the top of the [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) file with the `NGINX_BASIC_AUTH` environment variable: `true` for local TLS-encrypted HTTP basic authentication, `false` for LDAP authentication. + +In either case, you **must** run `./scripts/auth_setup` before starting Malcolm for the first time in order to: + +* define the local Malcolm administrator account username and password (although these credentials will only be used for basic authentication, not LDAP authentication) +* specify whether or not to (re)generate the self-signed certificates used for HTTPS access + * key and certificate files are located in the `nginx/certs/` directory +* specify whether or not to (re)generate the self-signed certificates used by a remote log forwarder (see the `BEATS_SSL` environment variable above) + * certificate authority, certificate, and key files for Malcolm's Logstash instance are located in the `logstash/certs/` directory + * certificate authority, certificate, and key files to be copied to and used by the remote log forwarder are located in the `filebeat/certs/` directory; if using [Hedgehog Linux](live-analysis.md#Hedgehog), these certificates should be copied to the `/opt/sensor/sensor_ctl/logstash-client-certificates` directory on the sensor +* specify whether or not to [store the username/password](https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#authenticate-sender-account) for [email alert senders](https://opensearch.org/docs/latest/monitoring-plugins/alerting/monitors/#create-destinations) + * these parameters are stored securely in the OpenSearch keystore file `opensearch/opensearch.keystore` + +# Local account management + +[`auth_setup`](#AuthSetup) is used to define the username and password for the administrator account. Once Malcolm is running, the administrator account can be used to manage other user accounts via a **Malcolm User Management** page served over HTTPS on port 488 (e.g., [https://localhost:488](https://localhost:488) if you are connecting locally). + +Malcolm user accounts can be used to access the [interfaces](quickstart.md#UserInterfaceURLs) of all of its [components](components.md#Components), including Arkime. Arkime uses its own internal database of user accounts, so when a Malcolm user account logs in to Arkime for the first time Malcolm creates a corresponding Arkime user account automatically. This being the case, it is *not* recommended to use the Arkime **Users** settings page or change the password via the **Password** form under the Arkime **Settings** page, as those settings would not be consistently used across Malcolm. + +Users may change their passwords via the **Malcolm User Management** page by clicking **User Self Service**. A forgotten password can also be reset via an emailed link, though this requires SMTP server settings to be specified in `htadmin/config.ini` in the Malcolm installation directory. + +## Lightweight Directory Access Protocol (LDAP) authentication + +The [nginx-auth-ldap](https://github.com/kvspb/nginx-auth-ldap) module serves as the interface between Malcolm's [Nginx](https://nginx.org/) web server and a remote LDAP server. When you run [`auth_setup`](#AuthSetup) for the first time, a sample LDAP configuration file is created at `nginx/nginx_ldap.conf`. + +``` +# This is a sample configuration for the ldap_server section of nginx.conf. +# Yours will vary depending on how your Active Directory/LDAP server is configured. +# See https://github.com/kvspb/nginx-auth-ldap#available-config-parameters for options. + +ldap_server ad_server { + url "ldap://ds.example.com:3268/DC=ds,DC=example,DC=com?sAMAccountName?sub?(objectClass=person)"; + + binddn "bind_dn"; + binddn_passwd "bind_dn_password"; + + group_attribute member; + group_attribute_is_dn on; + require group "CN=Malcolm,CN=Users,DC=ds,DC=example,DC=com"; + require valid_user; + satisfy all; +} + +auth_ldap_cache_enabled on; +auth_ldap_cache_expiration_time 10000; +auth_ldap_cache_size 1000; +``` + +This file is mounted into the `nginx` container when Malcolm is started to provide connection information for the LDAP server. + +The contents of `nginx_ldap.conf` will vary depending on how the LDAP server is configured. Some of the [avaiable parameters](https://github.com/kvspb/nginx-auth-ldap#available-config-parameters) in that file include: + +* **`url`** - the `ldap://` or `ldaps://` connection URL for the remote LDAP server, which has the [following syntax](https://www.ietf.org/rfc/rfc2255.txt): `ldap[s]://:/???` +* **`binddn`** and **`binddn_password`** - the account credentials used to query the LDAP directory +* **`group_attribute`** - the group attribute name which contains the member object (e.g., `member` or `memberUid`) +* **`group_attribute_is_dn`** - whether or not to search for the user's full distinguished name as the value in the group's member attribute +* **`require`** and **`satisfy`** - `require user`, `require group` and `require valid_user` can be used in conjunction with `satisfy any` or `satisfy all` to limit the users that are allowed to access the Malcolm instance + +Before starting Malcolm, edit `nginx/nginx_ldap.conf` according to the specifics of your LDAP server and directory tree structure. Using a LDAP search tool such as [`ldapsearch`](https://www.openldap.org/software/man.cgi?query=ldapsearch) in Linux or [`dsquery`](https://social.technet.microsoft.com/wiki/contents/articles/2195.active-directory-dsquery-commands.aspx) in Windows may be of help as you formulate the configuration. Your changes should be made within the curly braces of the `ldap_server ad_server { … }` section. You can troubleshoot configuration file syntax errors and LDAP connection or credentials issues by running `./scripts/logs` (or `docker-compose logs nginx`) and examining the output of the `nginx` container. + +The **Malcolm User Management** page described above is not available when using LDAP authentication. + +# LDAP connection security + +Authentication over LDAP can be done using one of three ways, [two of which](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/8e73932f-70cf-46d6-88b1-8d9f86235e81) offer data confidentiality protection: + +* **StartTLS** - the [standard extension](https://tools.ietf.org/html/rfc2830) to the LDAP protocol to establish an encrypted SSL/TLS connection within an already established LDAP connection +* **LDAPS** - a commonly used (though unofficial and considered deprecated) method in which SSL negotiation takes place before any commands are sent from the client to the server +* **Unencrypted** (cleartext) (***not recommended***) + +In addition to the `NGINX_BASIC_AUTH` environment variable being set to `false` in the `x-auth-variables` section near the top of the [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) file, the `NGINX_LDAP_TLS_STUNNEL` and `NGINX_LDAP_TLS_STUNNEL` environment variables are used in conjunction with the values in `nginx/nginx_ldap.conf` to define the LDAP connection security level. Use the following combinations of values to achieve the connection security methods above, respectively: + +* **StartTLS** + - `NGINX_LDAP_TLS_STUNNEL` set to `true` in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) + - `url` should begin with `ldap://` and its port should be either the default LDAP port (389) or the default Global Catalog port (3268) in `nginx/nginx_ldap.conf` +* **LDAPS** + - `NGINX_LDAP_TLS_STUNNEL` set to `false` in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) + - `url` should begin with `ldaps://` and its port should be either the default LDAPS port (636) or the default LDAPS Global Catalog port (3269) in `nginx/nginx_ldap.conf` +* **Unencrypted** (clear text) (***not recommended***) + - `NGINX_LDAP_TLS_STUNNEL` set to `false` in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) + - `url` should begin with `ldap://` and its port should be either the default LDAP port (389) or the default Global Catalog port (3268) in `nginx/nginx_ldap.conf` + +For encrypted connections (whether using **StartTLS** or **LDAPS**), Malcolm will require and verify certificates when one or more trusted CA certificate files are placed in the `nginx/ca-trust/` directory. Otherwise, any certificate presented by the domain server will be accepted. + +# TLS certificates + +When you [set up authentication](#AuthSetup) for Malcolm a set of unique [self-signed](https://en.wikipedia.org/wiki/Self-signed_certificate) TLS certificates are created which are used to secure the connection between clients (e.g., your web browser) and Malcolm's browser-based interface. This is adequate for most Malcolm instances as they are often run locally or on internal networks, although your browser will most likely require you to add a security exception for the certificate the first time you connect to Malcolm. + +Another option is to generate your own certificates (or have them issued to you) and have them placed in the `nginx/certs/` directory. The certificate and key file should be named `cert.pem` and `key.pem`, respectively. + +A third possibility is to use a third-party reverse proxy (e.g., [Traefik](https://doc.traefik.io/traefik/) or [Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy)) to handle the issuance of the certificates for you and to broker the connections between clients and Malcolm. Reverse proxies such as these often implement the [ACME](https://datatracker.ietf.org/doc/html/rfc8555) protocol for domain name authentication and can be used to request certificates from certificate authorities like [Let's Encrypt](https://letsencrypt.org/how-it-works/). In this configuration, the reverse proxy will be encrypting the connections instead of Malcolm, so you'll need to set the `NGINX_SSL` environment variable to `false` in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml) (or answer `no` to the "Require encrypted HTTPS connections?" question posed by `install.py`). If you are setting `NGINX_SSL` to `false`, **make sure** you understand what you are doing and ensure that external connections cannot reach ports over which Malcolm will be communicating without encryption, including verifying your local firewall configuration. \ No newline at end of file diff --git a/docs/components.md b/docs/components.md new file mode 100644 index 000000000..c885f477d --- /dev/null +++ b/docs/components.md @@ -0,0 +1,59 @@ +# Components + +Malcolm leverages the following excellent open source tools, among others. + +* [Arkime](https://arkime.com/) (formerly Moloch) - for PCAP file processing, browsing, searching, analysis, and carving/exporting; Arkime itself consists of two parts: + * [capture](https://github.com/arkime/arkime/tree/master/capture) - a tool for traffic capture, as well as offline PCAP parsing and metadata insertion into OpenSearch + * [viewer](https://github.com/arkime/arkime/tree/master/viewer) - a browser-based interface for data visualization +* [OpenSearch](https://opensearch.org/) - a search and analytics engine for indexing and querying network traffic session metadata +* [Logstash](https://www.elastic.co/products/logstash) and [Filebeat](https://www.elastic.co/products/beats/filebeat) - for ingesting and parsing [Zeek](https://www.zeek.org/index.html) [Log Files](https://docs.zeek.org/en/stable/script-reference/log-files.html) and ingesting them into OpenSearch in a format that Arkime understands in the same way it natively understands PCAP data +* [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) - for creating additional ad-hoc visualizations and dashboards beyond that which is provided by Arkime viewer +* [Zeek](https://www.zeek.org/index.html) - a network analysis framework and IDS +* [Suricata](https://suricata.io/) - an IDS and threat detection engine +* [Yara](https://github.com/VirusTotal/yara) - a tool used to identify and classify malware samples +* [Capa](https://github.com/fireeye/capa) - a tool for detecting capabilities in executable files +* [ClamAV](https://www.clamav.net/) - an antivirus engine for scanning files extracted by Zeek +* [CyberChef](https://github.com/gchq/CyberChef) - a "swiss-army knife" data conversion tool +* [jQuery File Upload](https://github.com/blueimp/jQuery-File-Upload) - for uploading PCAP files and Zeek logs for processing +* [List.js](https://github.com/javve/list.js) - for the [host and subnet name mapping](host-and-subnet-mapping.md#HostAndSubnetNaming) interface +* [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) - for simple, reproducible deployment of the Malcolm appliance across environments and to coordinate communication between its various components +* [NetBox](https://netbox.dev/) - a suite for modeling and documenting modern networks +* [PostgreSQL](https://www.postgresql.org/) - a relational database for persisting NetBox's data +* [Redis](https://redis.io/) - an in-memory data store for caching NetBox session information +* [Nginx](https://nginx.org/) - for HTTPS and reverse proxying Malcolm components +* [nginx-auth-ldap](https://github.com/kvspb/nginx-auth-ldap) - an LDAP authentication module for nginx +* [Fluent Bit](https://fluentbit.io/) - for forwarding metrics to Malcolm from [network sensors](live-analysis.md#Hedgehog) (packet capture appliances) +* [Mark Baggett](https://github.com/MarkBaggett)'s [freq](https://github.com/MarkBaggett/freq) - a tool for calculating entropy of strings +* [Florian Roth](https://github.com/Neo23x0)'s [Signature-Base](https://github.com/Neo23x0/signature-base) Yara ruleset +* These Zeek plugins: + * some of Amazon.com, Inc.'s [ICS protocol](https://github.com/amzn?q=zeek) analyzers + * Andrew Klaus's [Sniffpass](https://github.com/cybera/zeek-sniffpass) plugin for detecting cleartext passwords in HTTP POST requests + * Andrew Klaus's [zeek-httpattacks](https://github.com/precurse/zeek-httpattacks) plugin for detecting noncompliant HTTP requests + * ICS protocol analyzers for Zeek published by [DHS CISA](https://github.com/cisagov/ICSNPP) and [Idaho National Lab](https://github.com/idaholab/ICSNPP) + * Corelight's ["bad neighbor" (CVE-2020-16898)](https://github.com/corelight/CVE-2020-16898) plugin + * Corelight's ["Log4Shell" (CVE-2021-44228)](https://github.com/corelight/cve-2021-44228) plugin + * Corelight's ["OMIGOD" (CVE-2021-38647)](https://github.com/corelight/CVE-2021-38647) plugin + * Corelight's [Apache HTTP server 2.4.49-2.4.50 path traversal/RCE vulnerability (CVE-2021-41773)](https://github.com/corelight/CVE-2021-41773) plugin + * Corelight's [bro-xor-exe](https://github.com/corelight/bro-xor-exe-plugin) plugin + * Corelight's [callstranger-detector](https://github.com/corelight/callstranger-detector) plugin + * Corelight's [community ID](https://github.com/corelight/zeek-community-id) flow hashing plugin + * Corelight's [DCE/RPC remote code execution vulnerability (CVE-2022-26809)](https://github.com/corelight/cve-2022-26809) plugin + * Corelight's [HTTP More Filenames](https://github.com/corelight/http-more-files-names) plugin + * Corelight's [HTTP protocol stack vulnerability (CVE-2021-31166)](https://github.com/corelight/CVE-2021-31166) plugin + * Corelight's [pingback](https://github.com/corelight/pingback) plugin + * Corelight's [ripple20](https://github.com/corelight/ripple20) plugin + * Corelight's [SIGred](https://github.com/corelight/SIGred) plugin + * Corelight's [VMware Workspace ONE Access and Identity Manager RCE vulnerability (CVE-2022-22954)](https://github.com/corelight/cve-2022-22954) plugin + * Corelight's [Zerologon](https://github.com/corelight/zerologon) plugin + * Corelight's [Microsoft Excel privilege escalation detection (CVE-2021-42292)](https://github.com/corelight/CVE-2021-42292) plugin + * J-Gras' [Zeek::AF_Packet](https://github.com/J-Gras/zeek-af_packet-plugin) plugin + * Johanna Amann's [CVE-2020-0601](https://github.com/0xxon/cve-2020-0601) ECC certificate validation plugin and [CVE-2020-13777](https://github.com/0xxon/cve-2020-13777) GnuTLS unencrypted session ticket detection plugin + * Lexi Brent's [EternalSafety](https://github.com/0xl3x1/zeek-EternalSafety) plugin + * MITRE Cyber Analytics Repository's [Bro/Zeek ATT&CK®-Based Analytics (BZAR)](https://github.com/mitre-attack/car/tree/master/implementations) script + * Salesforce's [gQUIC](https://github.com/salesforce/GQUIC_Protocol_Analyzer) analyzer + * Salesforce's [HASSH](https://github.com/salesforce/hassh) SSH fingerprinting plugin + * Salesforce's [JA3](https://github.com/salesforce/ja3) TLS fingerprinting plugin + * Zeek's [Spicy](https://github.com/zeek/spicy) plugin framework +* [GeoLite2](https://dev.maxmind.com/geoip/geoip2/geolite2/) - Malcolm includes GeoLite2 data created by [MaxMind](https://www.maxmind.com) + +![Malcolm Components](./images/malcolm_components.png) \ No newline at end of file diff --git a/docs/contributing-dashboards.md b/docs/contributing-dashboards.md new file mode 100644 index 000000000..6f53fb810 --- /dev/null +++ b/docs/contributing-dashboards.md @@ -0,0 +1,41 @@ +# OpenSearch Dashboards + +[OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) is an open-source fork of [Kibana](https://www.elastic.co/kibana/), which is [no longer open-source software]({{ site.github.repository_url }}/releases/tag/v5.0.0). + +## Adding new visualizations and dashboards + +Visualizations and dashboards can be [easily created](dashboards.md#BuildDashboard) in OpenSearch Dashboards using its drag-and-drop WYSIWIG tools. Assuming you've created a new dashboard you wish to package with Malcolm, the dashboard and its visualization components can be exported using the following steps: + +1. Identify the ID of the dashboard (found in the URL: e.g., for `/dashboards/app/dashboards#/view/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` the ID would be `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) +1. Export the dashboard with that ID and save it in the `./dashboards./dashboards/` directory with the following command: + ``` + export DASHID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx && \ + docker-compose exec dashboards curl -XGET \ + "http://localhost:5601/dashboards/api/opensearch-dashboards/dashboards/export?dashboard=$DASHID" > \ + ./dashboards/dashboards/$DASHID.json + ``` +1. It's preferrable for Malcolm to dynamically create the `arkime_sessions3-*` index template rather than including it in imported dashboards, so edit the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json` that was generated, and carefully locate and remove the section with the `id` of `arkime_sessions3-*` and the `type` of `index-pattern` (including the comma preceding it): + ``` + , + { + "id": "arkime_sessions3-*", + "type": "index-pattern", + "namespaces": [ + "default" + ], + "updated_at": "2021-12-13T18:21:42.973Z", + "version": "Wzk3MSwxXQ==", + … + "references": [], + "migrationVersion": { + "index-pattern": "7.6.0" + } + } + ``` +1. Include the new dashboard either by using a [bind mount](contributing-local-modifications.md#Bind) for the `./dashboards./dashboards/` directory or by [rebuilding](development.md#Build) the `dashboards-helper` Docker image. Dashboards are imported the first time Malcolm starts up. + +## OpenSearch Dashboards plugins + +The [dashboards.Dockerfile]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/Dockerfiles/dashboards.Dockerfile) installs the OpenSearch Dashboards plugins used by Malcolm (search for `opensearch-dashboards-plugin install` in that file). Additional Dashboards plugins could be installed by modifying this Dockerfile and [rebuilding](development.md#Build) the `dashboards` Docker image. + +Third-party or community plugisn developed for Kibana will not install into OpenSearch dashboards without source code modification. Depending on the plugin, this could range from very smiple to very complex. As an illustrative example, the changes that were required to port the Sankey diagram visualization plugin from Kibana to OpenSearch Dashboards compatibility can be [viewed on GitHub](https://github.com/mmguero-dev/osd_sankey_vis/compare/edacf6b...main). diff --git a/docs/contributing-file-scanners.md b/docs/contributing-file-scanners.md new file mode 100644 index 000000000..0dc0bd572 --- /dev/null +++ b/docs/contributing-file-scanners.md @@ -0,0 +1,14 @@ +# Carved file scanners + +Similar to the [PCAP processing pipeline](contributing-pcap.md#PCAP) described above, new tools can plug into Malcolm's [automatic file extraction and scanning](file-scanning.md#ZeekFileExtraction) to examine file transfers carved from network traffic. + +When Zeek extracts a file it observes being transfered in network traffic, the `file-monitor` container picks up those extracted files and publishes to a [ZeroMQ](https://zeromq.org/) topic that can be subscribed to by any other process that wants to analyze that extracted file. In Malcolm at the time of this writing (as of the [v5.0.0 release]({{ site.github.repository_url }}/releases/tag/v5.0.0)), currently implemented file scanners include ClamAV, YARA, capa and VirusTotal, all of which are managed by the `file-monitor` container. The scripts involved in this code are: + +* [shared/bin/zeek_carve_watcher.py]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/zeek_carve_watcher.py) - watches the directory to which Zeek extracts files and publishes information about those files to the ZeroMQ ventilator on port 5987 +* [shared/bin/zeek_carve_scanner.py]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/zeek_carve_scanner.py) - subscribes to `zeek_carve_watcher.py`'s topic and performs file scanning for the ClamAV, YARA, capa and VirusTotal engines and sends "hits" to another ZeroMQ sync on port 5988 +* [shared/bin/zeek_carve_logger.py]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/zeek_carve_logger.py) - subscribes to `zeek_carve_scanner.py`'s topic and logs hits to a "fake" Zeek signatures.log file which is parsed and ingested by Logstash +* [shared/bin/zeek_carve_utils.py]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/zeek_carve_utils.py) - various variables and classes related to carved file scanning + +Additional file scanners could either be added to the `file-monitor` service, or to avoid coupling with Malcolm's code you could simply define a new service as instructed in the [Adding a new service](contributing-new-image.md#NewImage) section and write your own scripts to subscribe and publish to the topics as described above. While that might be a bit of hand-waving, these general steps take care of the plumbing around extracting the file and notifying your tool, as well as handling the logging of "hits": you shouldn't have to really edit any *existing* code to add a new carved file scanner. + +The `EXTRACTED_FILE_PIPELINE_DEBUG` and `EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA` environment variables in the `docker-compose` files can be set to `true` to enable verbose debug logging from the output of the Docker containers involved in the carved file processing pipeline. \ No newline at end of file diff --git a/docs/contributing-guide.md b/docs/contributing-guide.md new file mode 100644 index 000000000..47b984eeb --- /dev/null +++ b/docs/contributing-guide.md @@ -0,0 +1,26 @@ +# Malcolm Contributor Guide + +The purpose of this document is to provide some direction for those willing to modify Malcolm, whether for local customization or for contribution to the Malcolm project. + + +* [Local modifications](contributing-local-modifications.md#LocalMods) + + [Docker bind mounts](contributing-local-modifications.md#Bind) + + [Building Malcolm's Docker images](contributing-local-modifications.md#ContribBuild) +* [Adding a new service (Docker image)](contributing-new-image.md#NewImage) + + [Networking and firewall](contributing-new-image.md#NewImageFirewall) +* [Adding new log fields](contributing-new-log-fields.md#NewFields) +- [Zeek](contributing-zeek.md#Zeek) + + [`local.zeek`](contributing-zeek.md#LocalZeek) + + [Adding a new Zeek package](contributing-zeek.md#ZeekPackage) + + [Zeek Intelligence Framework](contributing-zeek.md#ContributingZeekIntel) +* [PCAP processors](contributing-pcap.md#PCAP) +* [Logstash](contributing-logstash.md#Logstash) + + [Parsing a new log data source](contributing-logstash.md#LogstashNewSource) + + [Parsing new Zeek logs](contributing-logstash.md#LogstashZeek) + + [Enrichments](contributing-logstash.md#LogstashEnrichments) + + [Logstash plugins](contributing-logstash.md#LogstashPlugins) +* [OpenSearch Dashboards](contributing-dashboards.md#dashboards) + + [Adding new visualizations and dashboards](contributing-dashboards.md#DashboardsNewViz) + + [OpenSearch Dashboards plugins](contributing-dashboards.md#DashboardsPlugins) +* [Carved file scanners](contributing-file-scanners.md#Scanners) +* [Style](contributing-style.md#Style) diff --git a/docs/contributing-local-modifications.md b/docs/contributing-local-modifications.md new file mode 100644 index 000000000..1b906b675 --- /dev/null +++ b/docs/contributing-local-modifications.md @@ -0,0 +1,126 @@ +# Local modifications + +There are several ways to customize Malcolm's runtime behavior via local changes to configuration files. Many commonly-tweaked settings are discussed in the project [README](README.md) (see [`docker-compose.yml` parameters](malcolm-config.md#DockerComposeYml) and [Customizing event severity scoring](severity.md#SeverityConfig) for some examples). + +## Docker bind mounts + +Some configuration changes can be put in place by modifying local copies of configuration files and then use a [Docker bind mount](https://docs.docker.com/storage/bind-mounts/) to overlay the modified file onto the running Malcolm container. This is already done for many files and directories used to persist Malcolm configuration and data. For example, the default list of bind mounted files and directories for each Malcolm service is as follows: + +``` +$ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml + opensearch: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/opensearch.keystore:rw + - ./opensearch:/usr/share/opensearch/data:delegated + - ./opensearch-backup:/opt/opensearch/backup:delegated + dashboards-helper: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + dashboards: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + logstash: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro + - ./logstash/maps/malcolm_severity.yaml:/etc/malcolm_severity.yaml:ro + - ./logstash/certs/ca.crt:/certs/ca.crt:ro + - ./logstash/certs/server.crt:/certs/server.crt:ro + - ./logstash/certs/server.key:/certs/server.key:ro + - ./cidr-map.txt:/usr/share/logstash/config/cidr-map.txt:ro + - ./host-map.txt:/usr/share/logstash/config/host-map.txt:ro + - ./net-map.json:/usr/share/logstash/config/net-map.json:ro + filebeat: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./zeek-logs:/zeek + - ./suricata-logs:/suricata + - ./filebeat/certs/ca.crt:/certs/ca.crt:ro + - ./filebeat/certs/client.crt:/certs/client.crt:ro + - ./filebeat/certs/client.key:/certs/client.key:ro + arkime: + - ./auth.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./pcap:/data/pcap + - ./arkime-logs:/opt/arkime/logs + - ./arkime-raw:/opt/arkime/raw + zeek: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./pcap:/pcap + - ./zeek-logs/upload:/zeek/upload + - ./zeek-logs/extract_files:/zeek/extract_files + - ./zeek/intel:/opt/zeek/share/zeek/site/intel + zeek-live: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./zeek-logs/live:/zeek/live + - ./zeek-logs/extract_files:/zeek/extract_files + - ./zeek/intel:/opt/zeek/share/zeek/site/intel + suricata: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./suricata-logs:/var/log/suricata + - ./pcap:/data/pcap + - ./suricata/rules:/opt/suricata/rules:ro + suricata-live: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./suricata-logs:/var/log/suricata + - ./suricata/rules:/opt/suricata/rules:ro + file-monitor: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./zeek-logs/extract_files:/zeek/extract_files + - ./zeek-logs/current:/zeek/logs + - ./yara/rules:/yara-rules/custom:ro + pcap-capture: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./pcap/upload:/pcap + pcap-monitor: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + - ./zeek-logs:/zeek + - ./pcap:/pcap + upload: + - ./auth.env + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./pcap/upload:/var/www/upload/server/php/chroot/files + htadmin: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw + - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw + - ./nginx/htpasswd:/var/www/htadmin/config/htpasswd:rw + freq: + - ./nginx/ca-trust:/var/local/ca-trust:ro + name-map-ui: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./cidr-map.txt:/var/www/html/maps/cidr-map.txt:ro + - ./host-map.txt:/var/www/html/maps/host-map.txt:ro + - ./net-map.json:/var/www/html/maps/net-map.json:rw + api: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro + nginx-proxy: + - ./nginx/ca-trust:/var/local/ca-trust:ro + - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro + - ./nginx/htpasswd:/etc/nginx/.htpasswd:ro + - ./nginx/certs:/etc/nginx/certs:ro + - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro +``` + +So, for example, if you wanted to make a change to the `nginx-proxy` container's `nginx.conf` file, you could add the following line to the `volumes:` section of the `nginx-proxy` service in your `docker-compose.yml` file: + +``` +- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro +``` + +The change would take effect after stopping and starting Malcolm. + +See the documentation on [Docker bind mount](https://docs.docker.com/storage/bind-mounts/) for more information on this technique. + +## Building Malcolm's Docker images + +Another method for modifying your local copies of Malcolm's services' containers is to [build your own](development.md#Build) containers with the modifications baked-in. + +For example, say you wanted to create a Malcolm container which includes a new dashboard for OpenSearch Dashboards and a new enrichment filter `.conf` file for Logstash. After placing these files under `./dashboards/dashboards` and `./logstash/pipelines/enrichment`, respectively, in your Malcolm working copy, run `./build.sh dashboards-helper logstash` to build just those containers. After the build completes, you can run `docker images` and see you have fresh images for `malcolmnetsec/dashboards-helper` and `malcolmnetsec/logstash-oss`. You may need to review the contents of the [Dockerfiles]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/Dockerfiles) to determine the correct service and filesystem location within that service's Docker image depending on what you're trying to accomplish. + +Alternately, if you have forked Malcolm on GitHub, [workflow files]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](live-analysis.md#Hedgehog) and [Malcolm](malcolm-iso.md#ISO) installer ISOs. The resulting images are named according to the pattern `ghcr.io/owner/malcolmnetsec/image:branch` (e.g., if you've forked Malcolm with the github user `romeogdetlevjr`, the `arkime` container built for the `main` would be named `ghcr.io/romeogdetlevjr/malcolmnetsec/arkime:main`). To run your local instance of Malcolm using these images instead of the official ones, you'll need to edit your `docker-compose.yml` file(s) and replace the `image:` tags according to this new pattern, or use the bash helper script `./shared/bin/github_image_helper.sh` to pull and re-tag the images. \ No newline at end of file diff --git a/docs/contributing-logstash.md b/docs/contributing-logstash.md new file mode 100644 index 000000000..40644bc6a --- /dev/null +++ b/docs/contributing-logstash.md @@ -0,0 +1,51 @@ +# Logstash + +## Parsing a new log data source + +Let's continue with the example of the `cooltool` service we added in the [PCAP processors](contributing-pcap.md#PCAP) section above, assuming that `cooltool` generates some textual log files we want to parse and index into Malcolm. + +You'd have configured `cooltool` in your `cooltool.Dockerfile` and its section in the `docker-compose` files to write logs into a subdirectory or subdirectories in a shared folder [bind mounted](contributing-local-modifications.md#Bind) in such a way that both the `cooltool` and `filebeat` containers can access. Referring to the `zeek` container as an example, this is how the `./zeek-logs` folder is handled; both the `filebeat` and `zeek` services have `./zeek-logs` in their `volumes:` section: + +``` +$ grep -P "^( - ./zeek-logs| [\w-]+:)" docker-compose.yml | grep -B1 "zeek-logs" + filebeat: + - ./zeek-logs:/data/zeek +-- + zeek: + - ./zeek-logs/upload:/zeek/upload +… +``` + +You'll need to provide access to your `cooltool` logs in a similar fashion. + +Next, tweak [`filebeat.yml`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/filebeat/filebeat.yml) by adding a new log input path pointing to the `cooltool` logs to send them along to the `logstash` container. This modified `filebeat.yml` will need to be reflected in the `filebeat` container via [bind mount](contributing-local-modifications.md#Bind) or by [rebuilding](development.md#Build) it. + +Logstash can then be easily extended to add more [`logstash/pipelines`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines). At the time of this writing (as of the [v5.0.0 release]({{ site.github.repository_url }}/releases/tag/v5.0.0)), the Logstash pipelines basically look like this: + +* input (from `filebeat`) sends logs to 1..*n* **parse pipelines** +* each **parse pipeline** does what it needs to do to parse its logs then sends them to the [**enrichment pipeline**](#LogstashEnrichments) +* the [**enrichment pipeline**]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines/enrichment) performs common lookups to the fields that have been normalized and indexes the logs into the OpenSearch data store + +So, in order to add a new **parse pipeline** for `cooltool` after tweaking [`filebeat.yml`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/filebeat/filebeat.yml) as described above, create a `cooltool` directory under [`logstash/pipelines`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines) which follows the same pattern as the `zeek` parse pipeline. This directory will have an input file (tiny), a filter file (possibly large), and an output file (tiny). In your filter file, be sure to set the field [`event.hash`](https://www.elastic.co/guide/en/ecs/master/ecs-event.html#field-event-hash) to a unique value to identify indexed documents in OpenSearch; the [fingerprint filter](https://www.elastic.co/guide/en/logstash/current/plugins-filters-fingerprint.html) may be useful for this. + +Finally, in your `docker-compose` files, set a new `LOGSTASH_PARSE_PIPELINE_ADDRESSES` environment variable under `logstash-variables` to `cooltool-parse,zeek-parse,suricata-parse,beats-parse` (assuming you named the pipeline address from the previous step `cooltool-parse`) so that logs sent from `filebeat` to `logstash` are forwarded to all parse pipelines. + +## Parsing new Zeek logs + +The following modifications must be made in order for Malcolm to be able to parse new Zeek log files: + +1. Add a parsing section to [`logstash/pipelines/zeek/11_zeek_logs.conf`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines/zeek/11_zeek_logs.conf) + * Follow patterns for existing log files as an example + * For common Zeek fields like the `id` four-tuple, timestamp, etc., use the same convention used by existing Zeek logs in that file (e.g., `ts`, `uid`, `orig_h`, `orig_p`, `resp_h`, `resp_p`) + * Take care, especially when copy-pasting filter code, that the Zeek delimiter isn't modified from a tab character to a space character (see "*zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP*" warnings in that file) +1. If necessary, perform log normalization in [`logstash/pipelines/zeek/12_zeek_normalize.conf`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines/zeek/12_zeek_normalize.conf) for values like action (`event.action`), result (`event.result`), application protocol version (`network.protocol_version`), etc. +1. If necessary, define conversions for floating point or integer values in [`logstash/pipelines/zeek/11_zeek_logs.conf`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines/zeek/13_zeek_convert.conf) +1. Identify the new fields and add them as described in [Adding new log fields](contributing-new-log-fields.md#NewFields) + +## Enrichments + +Malcolm's Logstash instance will do a lot of enrichments for you automatically: see the [enrichment pipeline]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines/enrichment), including MAC address to vendor by OUI, GeoIP, ASN, and a few others. In order to take advantage of these enrichments that are already in place, normalize new fields to use the same standardized field names Malcolm uses for things like IP addresses, MAC addresses, etc. You can add your own additional enrichments by creating new `.conf` files containing [Logstash filters](https://www.elastic.co/guide/en/logstash/7.10/filter-plugins.html) in the [enrichment pipeline]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/logstash/pipelines/enrichment) directory and using either of the techniques in the [Local modifications](contributing-local-modifications.md#LocalMods) section to implement your changes in the `logstash` container + +## Logstash plugins + +The [logstash.Dockerfile]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/Dockerfiles/logstash.Dockerfile) installs the Logstash plugins used by Malcolm (search for `logstash-plugin install` in that file). Additional Logstash plugins could be installed by modifying this Dockerfile and [rebuilding](development.md#Build) the `logstash` Docker image. \ No newline at end of file diff --git a/docs/contributing-new-image.md b/docs/contributing-new-image.md new file mode 100644 index 000000000..910264d06 --- /dev/null +++ b/docs/contributing-new-image.md @@ -0,0 +1,18 @@ +# Adding a new service (Docker image) + +A new service can be added to Malcolm by following the following steps: + +1. Create a new subdirectory for the service (under the Malcolm working copy base directory) containing whatever source or configuration files are necessary to build and run the service +1. Create the service's Dockerfile in the [Dockerfiles]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/Dockerfiles) directory of your Malcolm working copy +1. Add a new section for your service under `services:` in the `docker-compose.yml` and `docker-compose-standalone.yml` files +1. If you want to enable automatic builds for your service on GitHub, create a new [workflow]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/), using an existing workflow as an example + +## Networking and firewall + +If your service needs to expose a web interface to the user, you'll need to adjust the following files: + +* Ensure your service's section in the `docker-compose` files uses the `expose` directive to indicate which ports its providing +* Add the service to the `depends_on` section of the `nginx-proxy` service in the `docker-compose` files +* Modify the configuration of the `nginx-proxy` container (in [`nginx/nginx.conf`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/nginx/nginx.conf)) to define `upstream` and `location` directives to point to your service + +Avoid publishing ports directly from your container to the host machine's network interface if at all possible. The `nginx-proxy` container handles encryption and authentication and should sit in front of any user-facing interface provided by Malcolm. \ No newline at end of file diff --git a/docs/contributing-new-log-fields.md b/docs/contributing-new-log-fields.md new file mode 100644 index 000000000..58d248573 --- /dev/null +++ b/docs/contributing-new-log-fields.md @@ -0,0 +1,11 @@ +# Adding new log fields + +As several of the sections in this document will reference adding new data source fields, we'll cover that here at the beginning. + +Although OpenSearch is a NoSQL database and as-such is "unstructured" and "schemaless," in order to add a new data source field you'll need to define that field in a few places in order for it to show up and be usable throughout Malcolm. Minimally, you'll probably want to do it in these three files + +* [`arkime/etc/config.ini`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/arkime/etc/config.ini) - follow existing examples in the `[custom-fields]` and `[custom-views]` sections in order for [Arkime](https://arkime.com) to be aware of your new fields +* [`arkime/wise/source.zeeklogs.js`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/arkime/wise/source.zeeklogs.js) - add new fields to the `allFields` array for Malcolm to create Arkime [value actions](https://arkime.com/settings#right-click) for your fields +* [`dashboards/templates/composable/component/__(name)__.json`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/dashboards/templates/composable/component/) - add new fields to a new [composable index template](https://opensearch.org/docs/latest/opensearch/index-templates/#composable-index-templates) file in this directory and add its name (prefixed with `custom_`) to the `composed_of` section of [`dashboards/templates/malcolm_template.json`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/dashboards/templates/malcolm_template.json) in order for it to be included as part of the `arkime_sessions3-*` [index template](https://opensearch.org/docs/latest/opensearch/index-templates/) used by Arkime and OpenSearch Dashboards in Malcolm + +When possible, I recommend you to use (or at least take inspiration from) the [Elastic Common Schema (ECS) Reference](https://www.elastic.co/guide/en/ecs/current/index.html) when deciding how to define new field names. \ No newline at end of file diff --git a/docs/contributing-pcap.md b/docs/contributing-pcap.md new file mode 100644 index 000000000..04cdb7ae1 --- /dev/null +++ b/docs/contributing-pcap.md @@ -0,0 +1,12 @@ +# PCAP processors + +When a PCAP is uploaded (either through Malcolm's [upload web interface](upload.md#Upload) or just copied manually into the `./pcap/upload/` directory), the `pcap-monitor` container has a script that picks up those PCAP files and publishes to a [ZeroMQ](https://zeromq.org/) topic that can be subscribed to by any other process that wants to analyze that PCAP. In Malcolm at the time of this writing (as of the [v5.0.0 release]({{ site.github.repository_url }}/releases/tag/v5.0.0)), there are two of those: the `zeek` container and the `arkime` container. In Malcolm, they actually both share the [same script]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/pcap_processor.py) to read from that topic and run the PCAP through Zeek and Arkime, respectively. If you're looking for an example to follow, the `zeek` container is the less complicated of the two. So, if you were looking to integrate a new PCAP processing tool into Malcolm (named `cooltool` for this example), the process would be something like: + +1. Define your service as instructed in the [Adding a new service](contributing-new-image.md#NewImage) section + * Note how the existing `zeek` and `arkime` services use [bind mounts](contributing-local-modifications.md#Bind) to access the local `./pcap` directory +1. Write a script (modelled after [the one]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/pcap_processor.py) `zeek` and `arkime` use, if you like) which subscribes to the PCAP topic port (`30441` as defined in [pcap_utils.py]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/pcap_utils.py)) and handles the PCAP files published there, each PCAP file represented by a JSON dictionary with `name`, `tags`, `size`, `type` and `mime` keys (search for `FILE_INFO_` in [pcap_utils.py]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/pcap_utils.py)). This script should be added to and run by your `cooltool.Dockerfile`-generated container. +1. Add whatever other logic needed to get your tool's data into Malcolm, whether by writing it directly info OpenSearch or by sending log files for parsing and enrichment by [Logstash](contributing-logstash.md#Logstash) (especially see the section on [Parsing a new log data source](contributing-logstash.md#LogstashNewSource)) + +While that might be a bit of hand-waving, these general steps take care of the PCAP processing piece: you shouldn't have to really edit any *existing* code to add a new PCAP processor. You're just creating a new container for the Malcolm appliance to the ZeroMQ topic and handle the PCAPs your tool receives. + +The `PCAP_PIPELINE_DEBUG` and `PCAP_PIPELINE_DEBUG_EXTRA` environment variables in the `docker-compose` files can be set to `true` to enable verbose debug logging from the output of the Docker containers involved in the PCAP processing pipeline. diff --git a/docs/contributing-style.md b/docs/contributing-style.md new file mode 100644 index 000000000..9211b23e1 --- /dev/null +++ b/docs/contributing-style.md @@ -0,0 +1,5 @@ +# Style + +## Python + +For Python code found in Malcolm, the author uses [Black: The uncompromising Python code formatter](https://github.com/psf/black) with the options `--line-length 120 --skip-string-normalization`. \ No newline at end of file diff --git a/docs/contributing-zeek.md b/docs/contributing-zeek.md new file mode 100644 index 000000000..db8bfc935 --- /dev/null +++ b/docs/contributing-zeek.md @@ -0,0 +1,15 @@ +# Zeek + +## `local.zeek` + +Some Zeek behavior can be tweaked without having to manually edit configuration files through the use of environment variables: search for `ZEEK` in the [`docker-compose.yml` parameters](malcolm-config.md#DockerComposeYml) section of the documentation. + +Other changes to Zeek's behavior could be made by modifying [local.zeek]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/zeek/config/local.zeek) and either using a [bind mount](contributing-local-modifications.md#Bind) or [rebuilding](development.md#Build) the `zeek` Docker image with the modification. See the [Zeek documentation](https://docs.zeek.org/en/master/quickstart.html#local-site-customization) for more information on customizing a Zeek instance. Note that changing Zeek's behavior could result in changes to the format of the logs Zeek generates, which could break Malcolm's parsing of those logs, so exercise caution. + +## Adding a new Zeek package + +The easiest way to add a new Zeek package to Malcolm is to add the git URL of that package to the `ZKG_GITHUB_URLS` array in [zeek_install_plugins.sh]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/shared/bin/zeek_install_plugins.sh) script and then [rebuilding](development.md#Build) the `zeek` Docker image. This will cause your package to be installed (via the [`zkg`](https://docs.zeek.org/projects/package-manager/en/stable/zkg.html) command-line tool). See [Parsing new Zeek logs](contributing-logstash.md#LogstashZeek) on how to process any new `.log` files if your package generates them. + +## Zeek Intelligence Framework + +See [Zeek Intelligence Framework](zeek-intel.md#ZeekIntel) in the Malcolm README for information on how to use Zeek's [Intelligence Framework](https://docs.zeek.org/en/master/frameworks/intel.html) with Malcolm. \ No newline at end of file diff --git a/docs/contributing/README.md b/docs/contributing/README.md deleted file mode 100644 index 8f9a1b86d..000000000 --- a/docs/contributing/README.md +++ /dev/null @@ -1,339 +0,0 @@ -# Malcolm Contributor Guide - -The purpose of this document is to provide some direction for those willing to modify Malcolm, whether for local customization or for contribution to the Malcolm project. - -## Table of Contents - -* [Local modifications](#LocalMods) - + [Docker bind mounts](#Bind) - + [Building Malcolm's Docker images](#Build) -* [Adding a new service (Docker image)](#NewImage) - + [Networking and firewall](#NewImageFirewall) -* [Adding new log fields](#NewFields) -- [Zeek](#Zeek) - + [`local.zeek`](#LocalZeek) - + [Adding a new Zeek package](#ZeekPackage) - + [Zeek Intelligence Framework](#ZeekIntel) -* [PCAP processors](#PCAP) -* [Logstash](#Logstash) - + [Parsing a new log data source](#LogstashNewSource) - + [Parsing new Zeek logs](#LogstashZeek) - + [Enrichments](#LogstashEnrichments) - + [Logstash plugins](#LogstashPlugins) -* [OpenSearch Dashboards](#dashboards) - + [Adding new visualizations and dashboards](#DashboardsNewViz) - + [OpenSearch Dashboards plugins](#DashboardsPlugins) -* [Carved file scanners](#Scanners) -* [Style](#Style) - -## Local modifications - -There are several ways to customize Malcolm's runtime behavior via local changes to configuration files. Many commonly-tweaked settings are discussed in the project [README](../../README.md) (see [`docker-compose.yml` parameters](../../README.md#DockerComposeYml) and [Customizing event severity scoring](../../README.md#SeverityConfig) for some examples). - -### Docker bind mounts - -Some configuration changes can be put in place by modifying local copies of configuration files and then use a [Docker bind mount](https://docs.docker.com/storage/bind-mounts/) to overlay the modified file onto the running Malcolm container. This is already done for many files and directories used to persist Malcolm configuration and data. For example, the default list of bind mounted files and directories for each Malcolm service is as follows: - -``` -$ grep -P "^( - ./| [\w-]+:)" docker-compose-standalone.yml - opensearch: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro - - ./opensearch/opensearch.keystore:/usr/share/opensearch/config/opensearch.keystore:rw - - ./opensearch:/usr/share/opensearch/data:delegated - - ./opensearch-backup:/opt/opensearch/backup:delegated - dashboards-helper: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - dashboards: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - logstash: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./.opensearch.secondary.curlrc:/var/local/opensearch.secondary.curlrc:ro - - ./logstash/maps/malcolm_severity.yaml:/etc/malcolm_severity.yaml:ro - - ./logstash/certs/ca.crt:/certs/ca.crt:ro - - ./logstash/certs/server.crt:/certs/server.crt:ro - - ./logstash/certs/server.key:/certs/server.key:ro - - ./cidr-map.txt:/usr/share/logstash/config/cidr-map.txt:ro - - ./host-map.txt:/usr/share/logstash/config/host-map.txt:ro - - ./net-map.json:/usr/share/logstash/config/net-map.json:ro - filebeat: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./zeek-logs:/zeek - - ./suricata-logs:/suricata - - ./filebeat/certs/ca.crt:/certs/ca.crt:ro - - ./filebeat/certs/client.crt:/certs/client.crt:ro - - ./filebeat/certs/client.key:/certs/client.key:ro - arkime: - - ./auth.env - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./pcap:/data/pcap - - ./arkime-logs:/opt/arkime/logs - - ./arkime-raw:/opt/arkime/raw - zeek: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./pcap:/pcap - - ./zeek-logs/upload:/zeek/upload - - ./zeek-logs/extract_files:/zeek/extract_files - - ./zeek/intel:/opt/zeek/share/zeek/site/intel - zeek-live: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./zeek-logs/live:/zeek/live - - ./zeek-logs/extract_files:/zeek/extract_files - - ./zeek/intel:/opt/zeek/share/zeek/site/intel - suricata: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./suricata-logs:/var/log/suricata - - ./pcap:/data/pcap - - ./suricata/rules:/opt/suricata/rules:ro - suricata-live: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./suricata-logs:/var/log/suricata - - ./suricata/rules:/opt/suricata/rules:ro - file-monitor: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./zeek-logs/extract_files:/zeek/extract_files - - ./zeek-logs/current:/zeek/logs - - ./yara/rules:/yara-rules/custom:ro - pcap-capture: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./pcap/upload:/pcap - pcap-monitor: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - - ./zeek-logs:/zeek - - ./pcap:/pcap - upload: - - ./auth.env - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./pcap/upload:/var/www/upload/server/php/chroot/files - htadmin: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./htadmin/config.ini:/var/www/htadmin/config/config.ini:rw - - ./htadmin/metadata:/var/www/htadmin/config/metadata:rw - - ./nginx/htpasswd:/var/www/htadmin/config/htpasswd:rw - freq: - - ./nginx/ca-trust:/var/local/ca-trust:ro - name-map-ui: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./cidr-map.txt:/var/www/html/maps/cidr-map.txt:ro - - ./host-map.txt:/var/www/html/maps/host-map.txt:ro - - ./net-map.json:/var/www/html/maps/net-map.json:rw - api: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./.opensearch.primary.curlrc:/var/local/opensearch.primary.curlrc:ro - nginx-proxy: - - ./nginx/ca-trust:/var/local/ca-trust:ro - - ./nginx/nginx_ldap.conf:/etc/nginx/nginx_ldap.conf:ro - - ./nginx/htpasswd:/etc/nginx/.htpasswd:ro - - ./nginx/certs:/etc/nginx/certs:ro - - ./nginx/certs/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro -``` - -So, for example, if you wanted to make a change to the `nginx-proxy` container's `nginx.conf` file, you could add the following line to the `volumes:` section of the `nginx-proxy` service in your `docker-compose.yml` file: - -``` -- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro -``` - -The change would take effect after stopping and starting Malcolm. - -See the documentation on [Docker bind mount](https://docs.docker.com/storage/bind-mounts/) for more information on this technique. - -### Building Malcolm's Docker images - -Another method for modifying your local copies of Malcolm's services' containers is to [build your own](../../README.md#Build) containers with the modifications baked-in. - -For example, say you wanted to create a Malcolm container which includes a new dashboard for OpenSearch Dashboards and a new enrichment filter `.conf` file for Logstash. After placing these files under `./dashboards/dashboards` and `./logstash/pipelines/enrichment`, respectively, in your Malcolm working copy, run `./build.sh dashboards-helper logstash` to build just those containers. After the build completes, you can run `docker images` and see you have fresh images for `malcolmnetsec/dashboards-helper` and `malcolmnetsec/logstash-oss`. You may need to review the contents of the [Dockerfiles](../../Dockerfiles) to determine the correct service and filesystem location within that service's Docker image depending on what you're trying to accomplish. - -Alternately, if you have forked Malcolm on GitHub, [workflow files](../../.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](#Hedgehog) and [Malcolm](#ISO) installer ISOs. The resulting images are named according to the pattern `ghcr.io/owner/malcolmnetsec/image:branch` (e.g., if you've forked Malcolm with the github user `romeogdetlevjr`, the `arkime` container built for the `main` would be named `ghcr.io/romeogdetlevjr/malcolmnetsec/arkime:main`). To run your local instance of Malcolm using these images instead of the official ones, you'll need to edit your `docker-compose.yml` file(s) and replace the `image:` tags according to this new pattern, or use the bash helper script `./shared/bin/github_image_helper.sh` to pull and re-tag the images. - -## Adding a new service (Docker image) - -A new service can be added to Malcolm by following the following steps: - -1. Create a new subdirectory for the service (under the Malcolm working copy base directory) containing whatever source or configuration files are necessary to build and run the service -1. Create the service's Dockerfile in the [Dockerfiles](../../Dockerfiles) directory of your Malcolm working copy -1. Add a new section for your service under `services:` in the `docker-compose.yml` and `docker-compose-standalone.yml` files -1. If you want to enable automatic builds for your service on GitHub, create a new [workflow](../../.github/workflows/), using an existing workflow as an example - -### Networking and firewall - -If your service needs to expose a web interface to the user, you'll need to adjust the following files: - -* Ensure your service's section in the `docker-compose` files uses the `expose` directive to indicate which ports its providing -* Add the service to the `depends_on` section of the `nginx-proxy` service in the `docker-compose` files -* Modify the configuration of the `nginx-proxy` container (in [`nginx/nginx.conf`](../../nginx/nginx.conf)) to define `upstream` and `location` directives to point to your service - -Avoid publishing ports directly from your container to the host machine's network interface if at all possible. The `nginx-proxy` container handles encryption and authentication and should sit in front of any user-facing interface provided by Malcolm. - -## Adding new log fields - -As several of the sections in this document will reference adding new data source fields, we'll cover that here at the beginning. - -Although OpenSearch is a NoSQL database and as-such is "unstructured" and "schemaless," in order to add a new data source field you'll need to define that field in a few places in order for it to show up and be usable throughout Malcolm. Minimally, you'll probably want to do it in these three files - -* [`arkime/etc/config.ini`](../../arkime/etc/config.ini) - follow existing examples in the `[custom-fields]` and `[custom-views]` sections in order for [Arkime](https://arkime.com) to be aware of your new fields -* [`arkime/wise/source.zeeklogs.js`](../../arkime/wise/source.zeeklogs.js) - add new fields to the `allFields` array for Malcolm to create Arkime [value actions](https://arkime.com/settings#right-click) for your fields -* [`dashboards/templates/composable/component/__(name)__.json`](../../dashboards/templates/composable/component/) - add new fields to a new [composable index template](https://opensearch.org/docs/latest/opensearch/index-templates/#composable-index-templates) file in this directory and add its name (prefixed with `custom_`) to the `composed_of` section of [`dashboards/templates/malcolm_template.json`](../../dashboards/templates/malcolm_template.json) in order for it to be included as part of the `arkime_sessions3-*` [index template](https://opensearch.org/docs/latest/opensearch/index-templates/) used by Arkime and OpenSearch Dashboards in Malcolm - -When possible, I recommend you to use (or at least take inspiration from) the [Elastic Common Schema (ECS) Reference](https://www.elastic.co/guide/en/ecs/current/index.html) when deciding how to define new field names. - -## Zeek - -### `local.zeek` - -Some Zeek behavior can be tweaked without having to manually edit configuration files through the use of environment variables: search for `ZEEK` in the [`docker-compose.yml` parameters](../../README.md#DockerComposeYml) section of the documentation. - -Other changes to Zeek's behavior could be made by modifying [local.zeek](../../zeek/config/local.zeek) and either using a [bind mount](#Bind) or [rebuilding](#Build) the `zeek` Docker image with the modification. See the [Zeek documentation](https://docs.zeek.org/en/master/quickstart.html#local-site-customization) for more information on customizing a Zeek instance. Note that changing Zeek's behavior could result in changes to the format of the logs Zeek generates, which could break Malcolm's parsing of those logs, so exercise caution. - -### Adding a new Zeek package - -The easiest way to add a new Zeek package to Malcolm is to add the git URL of that package to the `ZKG_GITHUB_URLS` array in [zeek_install_plugins.sh](../../shared/bin/zeek_install_plugins.sh) script and then [rebuilding](#Build) the `zeek` Docker image. This will cause your package to be installed (via the [`zkg`](https://docs.zeek.org/projects/package-manager/en/stable/zkg.html) command-line tool). See [Parsing new Zeek logs](#LogstashZeek) on how to process any new `.log` files if your package generates them. - -### Zeek Intelligence Framework - -See [Zeek Intelligence Framework](../../README.md#ZeekIntel) in the Malcolm README for information on how to use Zeek's [Intelligence Framework](https://docs.zeek.org/en/master/frameworks/intel.html) with Malcolm. - -## PCAP processors - -When a PCAP is uploaded (either through Malcolm's [upload web interface](../../README.md#Upload) or just copied manually into the `./pcap/upload/` directory), the `pcap-monitor` container has a script that picks up those PCAP files and publishes to a [ZeroMQ](https://zeromq.org/) topic that can be subscribed to by any other process that wants to analyze that PCAP. In Malcolm at the time of this writing (as of the [v5.0.0 release](https://github.com/idaholab/Malcolm/releases/tag/v5.0.0)), there are two of those: the `zeek` container and the `arkime` container. In Malcolm, they actually both share the [same script](../../shared/bin/pcap_processor.py) to read from that topic and run the PCAP through Zeek and Arkime, respectively. If you're looking for an example to follow, the `zeek` container is the less complicated of the two. So, if you were looking to integrate a new PCAP processing tool into Malcolm (named `cooltool` for this example), the process would be something like: - -1. Define your service as instructed in the [Adding a new service](#NewImage) section - * Note how the existing `zeek` and `arkime` services use [bind mounts](#Bind) to access the local `./pcap` directory -1. Write a script (modelled after [the one](../../shared/bin/pcap_processor.py) `zeek` and `arkime` use, if you like) which subscribes to the PCAP topic port (`30441` as defined in [pcap_utils.py](../../shared/bin/pcap_utils.py)) and handles the PCAP files published there, each PCAP file represented by a JSON dictionary with `name`, `tags`, `size`, `type` and `mime` keys (search for `FILE_INFO_` in [pcap_utils.py](../../shared/bin/pcap_utils.py)). This script should be added to and run by your `cooltool.Dockerfile`-generated container. -1. Add whatever other logic needed to get your tool's data into Malcolm, whether by writing it directly info OpenSearch or by sending log files for parsing and enrichment by [Logstash](#Logstash) (especially see the section on [Parsing a new log data source](#LogstashNewSource)) - -While that might be a bit of hand-waving, these general steps take care of the PCAP processing piece: you shouldn't have to really edit any *existing* code to add a new PCAP processor. You're just creating a new container for the Malcolm appliance to the ZeroMQ topic and handle the PCAPs your tool receives. - -The `PCAP_PIPELINE_DEBUG` and `PCAP_PIPELINE_DEBUG_EXTRA` environment variables in the `docker-compose` files can be set to `true` to enable verbose debug logging from the output of the Docker containers involved in the PCAP processing pipeline. - -## Logstash - -### Parsing a new log data source - -Let's continue with the example of the `cooltool` service we added in the [PCAP processors](#PCAP) section above, assuming that `cooltool` generates some textual log files we want to parse and index into Malcolm. - -You'd have configured `cooltool` in your `cooltool.Dockerfile` and its section in the `docker-compose` files to write logs into a subdirectory or subdirectories in a shared folder [bind mounted](#Bind) in such a way that both the `cooltool` and `filebeat` containers can access. Referring to the `zeek` container as an example, this is how the `./zeek-logs` folder is handled; both the `filebeat` and `zeek` services have `./zeek-logs` in their `volumes:` section: - -``` -$ grep -P "^( - ./zeek-logs| [\w-]+:)" docker-compose.yml | grep -B1 "zeek-logs" - filebeat: - - ./zeek-logs:/data/zeek --- - zeek: - - ./zeek-logs/upload:/zeek/upload -… -``` - -You'll need to provide access to your `cooltool` logs in a similar fashion. - -Next, tweak [`filebeat.yml`](../../filebeat/filebeat.yml) by adding a new log input path pointing to the `cooltool` logs to send them along to the `logstash` container. This modified `filebeat.yml` will need to be reflected in the `filebeat` container via [bind mount](#Bind) or by [rebuilding](#Build) it. - -Logstash can then be easily extended to add more [`logstash/pipelines`](../../logstash/pipelines). At the time of this writing (as of the [v5.0.0 release](https://github.com/idaholab/Malcolm/releases/tag/v5.0.0)), the Logstash pipelines basically look like this: - -* input (from `filebeat`) sends logs to 1..*n* **parse pipelines** -* each **parse pipeline** does what it needs to do to parse its logs then sends them to the [**enrichment pipeline**](#LogstashEnrichments) -* the [**enrichment pipeline**](../../logstash/pipelines/enrichment) performs common lookups to the fields that have been normalized and indexes the logs into the OpenSearch data store - -So, in order to add a new **parse pipeline** for `cooltool` after tweaking [`filebeat.yml`](../../filebeat/filebeat.yml) as described above, create a `cooltool` directory under [`logstash/pipelines`](../../logstash/pipelines) which follows the same pattern as the `zeek` parse pipeline. This directory will have an input file (tiny), a filter file (possibly large), and an output file (tiny). In your filter file, be sure to set the field [`event.hash`](https://www.elastic.co/guide/en/ecs/master/ecs-event.html#field-event-hash) to a unique value to identify indexed documents in OpenSearch; the [fingerprint filter](https://www.elastic.co/guide/en/logstash/current/plugins-filters-fingerprint.html) may be useful for this. - -Finally, in your `docker-compose` files, set a new `LOGSTASH_PARSE_PIPELINE_ADDRESSES` environment variable under `logstash-variables` to `cooltool-parse,zeek-parse,suricata-parse,beats-parse` (assuming you named the pipeline address from the previous step `cooltool-parse`) so that logs sent from `filebeat` to `logstash` are forwarded to all parse pipelines. - -### Parsing new Zeek logs - -The following modifications must be made in order for Malcolm to be able to parse new Zeek log files: - -1. Add a parsing section to [`logstash/pipelines/zeek/11_zeek_logs.conf`](../../logstash/pipelines/zeek/11_zeek_logs.conf) - * Follow patterns for existing log files as an example - * For common Zeek fields like the `id` four-tuple, timestamp, etc., use the same convention used by existing Zeek logs in that file (e.g., `ts`, `uid`, `orig_h`, `orig_p`, `resp_h`, `resp_p`) - * Take care, especially when copy-pasting filter code, that the Zeek delimiter isn't modified from a tab character to a space character (see "*zeek's default delimiter is a literal tab, MAKE SURE YOUR EDITOR DOESN'T SCREW IT UP*" warnings in that file) -1. If necessary, perform log normalization in [`logstash/pipelines/zeek/12_zeek_normalize.conf`](../../logstash/pipelines/zeek/12_zeek_normalize.conf) for values like action (`event.action`), result (`event.result`), application protocol version (`network.protocol_version`), etc. -1. If necessary, define conversions for floating point or integer values in [`logstash/pipelines/zeek/11_zeek_logs.conf`](../../logstash/pipelines/zeek/13_zeek_convert.conf) -1. Identify the new fields and add them as described in [Adding new log fields](#NewFields) - -### Enrichments - -Malcolm's Logstash instance will do a lot of enrichments for you automatically: see the [enrichment pipeline](../../logstash/pipelines/enrichment), including MAC address to vendor by OUI, GeoIP, ASN, and a few others. In order to take advantage of these enrichments that are already in place, normalize new fields to use the same standardized field names Malcolm uses for things like IP addresses, MAC addresses, etc. You can add your own additional enrichments by creating new `.conf` files containing [Logstash filters](https://www.elastic.co/guide/en/logstash/7.10/filter-plugins.html) in the [enrichment pipeline](../../logstash/pipelines/enrichment) directory and using either of the techniques in the [Local modifications](#LocalMods) section to implement your changes in the `logstash` container - -### Logstash plugins - -The [logstash.Dockerfile](../../Dockerfiles/logstash.Dockerfile) installs the Logstash plugins used by Malcolm (search for `logstash-plugin install` in that file). Additional Logstash plugins could be installed by modifying this Dockerfile and [rebuilding](#Build) the `logstash` Docker image. - -## OpenSearch Dashboards - -[OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) is an open-source fork of [Kibana](https://www.elastic.co/kibana/), which is [no longer open-source software](https://github.com/idaholab/Malcolm/releases/tag/v5.0.0). - -### Adding new visualizations and dashboards - -Visualizations and dashboards can be [easily created](../../README.md#BuildDashboard) in OpenSearch Dashboards using its drag-and-drop WYSIWIG tools. Assuming you've created a new dashboard you wish to package with Malcolm, the dashboard and its visualization components can be exported using the following steps: - -1. Identify the ID of the dashboard (found in the URL: e.g., for `/dashboards/app/dashboards#/view/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` the ID would be `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) -1. Export the dashboard with that ID and save it in the `./dashboards./dashboards/` directory with the following command: - ``` - export DASHID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx && \ - docker-compose exec dashboards curl -XGET \ - "http://localhost:5601/dashboards/api/opensearch-dashboards/dashboards/export?dashboard=$DASHID" > \ - ./dashboards/dashboards/$DASHID.json - ``` -1. It's preferrable for Malcolm to dynamically create the `arkime_sessions3-*` index template rather than including it in imported dashboards, so edit the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json` that was generated, and carefully locate and remove the section with the `id` of `arkime_sessions3-*` and the `type` of `index-pattern` (including the comma preceding it): - ``` - , - { - "id": "arkime_sessions3-*", - "type": "index-pattern", - "namespaces": [ - "default" - ], - "updated_at": "2021-12-13T18:21:42.973Z", - "version": "Wzk3MSwxXQ==", - … - "references": [], - "migrationVersion": { - "index-pattern": "7.6.0" - } - } - ``` -1. Include the new dashboard either by using a [bind mount](#Bind) for the `./dashboards./dashboards/` directory or by [rebuilding](#Build) the `dashboards-helper` Docker image. Dashboards are imported the first time Malcolm starts up. - -### OpenSearch Dashboards plugins - -The [dashboards.Dockerfile](../../Dockerfiles/dashboards.Dockerfile) installs the OpenSearch Dashboards plugins used by Malcolm (search for `opensearch-dashboards-plugin install` in that file). Additional Dashboards plugins could be installed by modifying this Dockerfile and [rebuilding](#Build) the `dashboards` Docker image. - -Third-party or community plugisn developed for Kibana will not install into OpenSearch dashboards without source code modification. Depending on the plugin, this could range from very smiple to very complex. As an illustrative example, the changes that were required to port the Sankey diagram visualization plugin from Kibana to OpenSearch Dashboards compatibility can be [viewed on GitHub](https://github.com/mmguero-dev/osd_sankey_vis/compare/edacf6b...main). - -## Carved file scanners - -Similar to the [PCAP processing pipeline](#PCAP) described above, new tools can plug into Malcolm's [automatic file extraction and scanning](../../README.md#ZeekFileExtraction) to examine file transfers carved from network traffic. - -When Zeek extracts a file it observes being transfered in network traffic, the `file-monitor` container picks up those extracted files and publishes to a [ZeroMQ](https://zeromq.org/) topic that can be subscribed to by any other process that wants to analyze that extracted file. In Malcolm at the time of this writing (as of the [v5.0.0 release](https://github.com/idaholab/Malcolm/releases/tag/v5.0.0)), currently implemented file scanners include ClamAV, YARA, capa and VirusTotal, all of which are managed by the `file-monitor` container. The scripts involved in this code are: - -* [shared/bin/zeek_carve_watcher.py](../../shared/bin/zeek_carve_watcher.py) - watches the directory to which Zeek extracts files and publishes information about those files to the ZeroMQ ventilator on port 5987 -* [shared/bin/zeek_carve_scanner.py](../../shared/bin/zeek_carve_scanner.py) - subscribes to `zeek_carve_watcher.py`'s topic and performs file scanning for the ClamAV, YARA, capa and VirusTotal engines and sends "hits" to another ZeroMQ sync on port 5988 -* [shared/bin/zeek_carve_logger.py](../../shared/bin/zeek_carve_logger.py) - subscribes to `zeek_carve_scanner.py`'s topic and logs hits to a "fake" Zeek signatures.log file which is parsed and ingested by Logstash -* [shared/bin/zeek_carve_utils.py](../../shared/bin/zeek_carve_utils.py) - various variables and classes related to carved file scanning - -Additional file scanners could either be added to the `file-monitor` service, or to avoid coupling with Malcolm's code you could simply define a new service as instructed in the [Adding a new service](#NewImage) section and write your own scripts to subscribe and publish to the topics as described above. While that might be a bit of hand-waving, these general steps take care of the plumbing around extracting the file and notifying your tool, as well as handling the logging of "hits": you shouldn't have to really edit any *existing* code to add a new carved file scanner. - -The `EXTRACTED_FILE_PIPELINE_DEBUG` and `EXTRACTED_FILE_PIPELINE_DEBUG_EXTRA` environment variables in the `docker-compose` files can be set to `true` to enable verbose debug logging from the output of the Docker containers involved in the carved file processing pipeline. - -## Style - -### Python - -For Python code found in Malcolm, the author uses [Black: The uncompromising Python code formatter](https://github.com/psf/black) with the options `--line-length 120 --skip-string-normalization`. - -## Copyright - -[Malcolm](https://github.com/idaholab/Malcolm) is Copyright 2022 Battelle Energy Alliance, LLC, and is developed and released through the cooperation of the [Cybersecurity and Infrastructure Security Agency](https://www.cisa.gov/) of the [U.S. Department of Homeland Security](https://www.dhs.gov/). - -See [`License.txt`](../../License.txt) for the terms of its release. - -### Contact information of author(s): - -[malcolm@inl.gov](mailto:malcolm@inl.gov?subject=Malcolm) diff --git a/docs/cyberchef.md b/docs/cyberchef.md new file mode 100644 index 000000000..c9a6b2aeb --- /dev/null +++ b/docs/cyberchef.md @@ -0,0 +1,5 @@ +# CyberChef + +Malcolm provides an instance of [CyberChef](https://github.com/gchq/CyberChef), the "Cyber Swiss Army Knife - a web app for encryption, encoding, compression and data analysis." CyberChef is available at at [https://localhost/cyberchef.html](https://localhost/cyberchef.html) if you are connecting locally. + +Arkime's [Sessions](arkime.md#ArkimeSessions) view has built-in CyberChef integration for Arkime sessions with full PCAP payloads available: expanding a session and opening the **Packet Options** drop-down menu in its payload section will provide options for **Open src packets with CyberChef** and **Open dst packets with CyberChef**. \ No newline at end of file diff --git a/docs/dashboards.md b/docs/dashboards.md new file mode 100644 index 000000000..7a5f5fc00 --- /dev/null +++ b/docs/dashboards.md @@ -0,0 +1,99 @@ +# OpenSearch Dashboards + +* [OpenSearch Dashboards](#Dashboards) + - [Discover](#Discover) + + [Screenshots](#DiscoverGallery) + - [Visualizations and dashboards](#DashboardsVisualizations) + + [Prebuilt visualizations and dashboards](#PrebuiltVisualizations) + * [Screenshots](#PrebuiltVisualizationsGallery) + + [Building your own visualizations and dashboards](#BuildDashboard) + * [Screenshots](#NewVisualizationsGallery) + +While Arkime provides very nice visualizations, especially for network traffic, [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) (an open source general-purpose data visualization tool for OpenSearch) can be used to create custom visualizations (tables, charts, graphs, dashboards, etc.) using the same data. + +The OpenSearch Dashboards container can be accessed at [https://localhost/dashboards/](https://localhost/dashboards/) if you are connecting locally. Several preconfigured dashboards for Zeek logs are included in Malcolm's OpenSearch Dashboards configuration. + +OpenSearch Dashboards has several components for data searching and visualization: + +## Discover + +The **Discover** view enables you to view events on a record-by-record basis (similar to a *session* record in Arkime or an individual line from a Zeek log). See the official [Kibana User Guide](https://www.elastic.co/guide/en/kibana/7.10/index.html) (OpenSearch Dashboards is an open-source fork of Kibana, which is no longer open-source software) for information on using the Discover view: + +* [Discover](https://www.elastic.co/guide/en/kibana/7.10/discover.html) +* [Searching Your Data](https://www.elastic.co/guide/en/kibana/7.10/search.html) + +### Screenshots + +![Discover view](./images/screenshots/dashboards_discover.png) + +![Viewing the details of a session in Discover](./images/screenshots/dashboards_discover_table.png) + +![Filtering by tags to display only sessions with public IP addresses](./images/screenshots/dashboards_add_filter.png) + +![Changing the fields displayed in Discover](./images/screenshots/dashboards_fields_list.png) + +![Opening a previously-saved search](./images/screenshots/dashboards_open_search.png) + +## Visualizations and dashboards + +### Prebuilt visualizations and dashboards + +Malcolm comes with dozens of prebuilt visualizations and dashboards for the network traffic represented by each of the Zeek log types. Click **Dashboard** to see a list of these dashboards. As is the case with all OpenSearch Dashboards visualizations, all of the charts, graphs, maps, and tables are interactive and can be clicked on to narrow or expand the scope of the data you are investigating. Similarly, click **Visualize** to explore the prebuilt visualizations used to build the dashboards. + +Many of Malcolm's prebuilt visualizations for Zeek logs were originally inspired by the excellent [Kibana Dashboards](https://github.com/Security-Onion-Solutions/securityonion-elastic/tree/master/kibana/dashboards) that are part of [Security Onion](https://securityonion.net/). + +#### Screenshots + +![The Security Overview highlights security-related network events](./images/screenshots/dashboards_security_overview.png) + +![The ICS/IoT Security Overview dashboard displays information about ICS and IoT network traffic](./images/screenshots/dashboards_ics_iot_security_overview.png) + +![The Connections dashboard displays information about the "top talkers" across all types of sessions](./images/screenshots/dashboards_connections.png) + +![The HTTP dashboard displays important details about HTTP traffic](./images/screenshots/dashboards_http.png) + +![There are several Connections visualizations using locations from GeoIP lookups](./images/screenshots/dashboards_latlon_map.png) + +![OpenSearch Dashboards includes both coordinate and region map types](./images/screenshots/dashboards_region_map.png) + +![The Suricata Alerts dashboard highlights traffic which matched Suricata signatures](./images/screenshots/dashboards_suricata_alerts.png) + +![The Zeek Notices dashboard highlights things which Zeek determine are potentially bad](./images/screenshots/dashboards_notices.png) + +![The Zeek Signatures dashboard displays signature hits, such as antivirus hits on files extracted from network traffic](./images/screenshots/dashboards_signatures.png) + +![The Software dashboard displays the type, name, and version of software seen communicating on the network](./images/screenshots/dashboards_software.png) + +![The PE (portable executables) dashboard displays information about executable files transferred over the network](./images/screenshots/dashboards_portable_executables.png) + +![The SMTP dashboard highlights details about SMTP traffic](./images/screenshots/dashboards_smtp.png) + +![The SSL dashboard displays information about SSL versions, certificates, and TLS JA3 fingerprints](./images/screenshots/dashboards_ssl.png) + +![The files dashboard displays metrics about the files transferred over the network](./images/screenshots/dashboards_files_source.png) + +![This dashboard provides insight into DNP3 (Distributed Network Protocol), a protocol used commonly in electric and water utilities](./images/screenshots/dashboards_dnp3.png) + +![Modbus is a standard protocol found in many industrial control systems (ICS)](./images/screenshots/dashboards_modbus.png) + +![BACnet is a communications protocol for Building Automation and Control (BAC) networks](./images/screenshots/dashboards_bacnet.png) + +![EtherCAT is an Ethernet-based fieldbus system](./images/screenshots/dashboards_ecat.png) + +![EtherNet/IP is an industrial network protocol that adapts the Common Industrial Protocol to standard Ethernet](./images/screenshots/dashboards_ethernetip.png) + +![PROFINET is an industry technical standard for data communication over Industrial Ethernet](./images/screenshots/dashboards_profinet.png) + +![S7comm is a Siemens proprietary protocol that runs between programmable logic controllers (PLCs) of the Siemens family](./images/screenshots/dashboards_s7comm.png) + +### Building your own visualizations and dashboards + +See the official [Kibana User Guide](https://www.elastic.co/guide/en/kibana/7.10/index.html) and [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) (OpenSearch Dashboards is an open-source fork of Kibana, which is no longer open-source software) documentation for information on creating your own visualizations and dashboards: + +* [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/index/) +* [Kibana Dashboards](https://www.elastic.co/guide/en/kibana/7.10/dashboard.html) +* [TimeLine](https://www.elastic.co/guide/en/kibana/7.12/timelion.html) + +#### Screenshots + +![OpenSearch dashboards boasts many types of visualizations for displaying your data](./images/screenshots/dashboards_new_visualization.png) \ No newline at end of file diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 000000000..bf0a46e8c --- /dev/null +++ b/docs/development.md @@ -0,0 +1,162 @@ +# Development + +* [Development](#Development) + - [Building from source](#Build) + - [Pre-Packaged installation files](#Packager) + +Checking out the [Malcolm source code]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}) results in the following subdirectories in your `malcolm/` working copy: + +* `api` - code and configuration for the `api` container which provides a REST API to query Malcolm +* `arkime` - code and configuration for the `arkime` container which processes PCAP files using `capture` and which serves the Viewer application +* `arkime-logs` - an initially empty directory to which the `arkime` container will write some debug log files +* `arkime-raw` - an initially empty directory to which the `arkime` container will write captured PCAP files; as Arkime as employed by Malcolm is currently used for processing previously-captured PCAP files, this directory is currently unused +* `Dockerfiles` - a directory containing build instructions for Malcolm's docker images +* `docs` - a directory containing instructions and documentation +* `opensearch` - an initially empty directory where the OpenSearch database instance will reside +* `opensearch-backup` - an initially empty directory for storing OpenSearch [index snapshots](index-management.md#IndexManagement) +* `filebeat` - code and configuration for the `filebeat` container which ingests Zeek logs and forwards them to the `logstash` container +* `file-monitor` - code and configuration for the `file-monitor` container which can scan files extracted by Zeek +* `file-upload` - code and configuration for the `upload` container which serves a web browser-based upload form for uploading PCAP files and Zeek logs, and which serves an SFTP share as an alternate method for upload +* `freq-server` - code and configuration for the `freq` container used for calculating entropy of strings +* `htadmin` - configuration for the `htadmin` user account management container +* `dashboards` - code and configuration for the `dashboards` container for creating additional ad-hoc visualizations and dashboards beyond that which is provided by Arkime Viewer +* `logstash` - code and configuration for the `logstash` container which parses Zeek logs and forwards them to the `opensearch` container +* `malcolm-iso` - code and configuration for building an [installer ISO](malcolm-iso.md#ISO) for a minimal Debian-based Linux installation for running Malcolm +* `name-map-ui` - code and configuration for the `name-map-ui` container which provides the [host and subnet name mapping](host-and-subnet-mapping.md#HostAndSubnetNaming) interface +* `netbox` - code and configuration for the `netbox`, `netbox-postgres`, `netbox-redis` and `netbox-redis-cache` containers which provide asset management capabilities +* `nginx` - configuration for the `nginx` reverse proxy container +* `pcap` - an initially empty directory for PCAP files to be uploaded, processed, and stored +* `pcap-capture` - code and configuration for the `pcap-capture` container which can capture network traffic +* `pcap-monitor` - code and configuration for the `pcap-monitor` container which watches for new or uploaded PCAP files notifies the other services to process them +* `scripts` - control scripts for starting, stopping, restarting, etc. Malcolm +* `sensor-iso` - code and configuration for building a [Hedgehog Linux](live-analysis.md#Hedgehog) ISO +* `shared` - miscellaneous code used by various Malcolm components +* `suricata` - code and configuration for the `suricata` container which handles PCAP processing using Suricata +* `suricata-logs` - an initially empty directory for Suricata logs to be uploaded, processed, and stored +* `zeek` - code and configuration for the `zeek` container which handles PCAP processing using Zeek +* `zeek-logs` - an initially empty directory for Zeek logs to be uploaded, processed, and stored +* `_includes` and `_layouts` - templates for the HTML version of the documentation + +and the following files of special note: + +* `auth.env` - the script `./scripts/auth_setup` prompts the user for the administrator credentials used by the Malcolm appliance, and `auth.env` is the environment file where those values are stored +* `cidr-map.txt` - specify custom IP address to network segment mapping +* `host-map.txt` - specify custom IP and/or MAC address to host mapping +* `net-map.json` - an alternative to `cidr-map.txt` and `host-map.txt`, mapping hosts and network segments to their names in a JSON-formatted file +* `docker-compose.yml` - the configuration file used by `docker-compose` to build, start, and stop an instance of the Malcolm appliance +* `docker-compose-standalone.yml` - similar to `docker-compose.yml`, only used for the ["packaged"](#Packager) installation of Malcolm + +## Building from source + +Building the Malcolm docker images from scratch requires internet access to pull source files for its components. Once internet access is available, execute the following command to build all of the Docker images used by the Malcolm appliance: + +``` +$ ./scripts/build.sh +``` + +Then, go take a walk or something since it will be a while. When you're done, you can run `docker images` and see you have fresh images for: + +* `malcolmnetsec/api` (based on `python:3-slim`) +* `malcolmnetsec/arkime` (based on `debian:11-slim`) +* `malcolmnetsec/dashboards-helper` (based on `alpine:3.16`) +* `malcolmnetsec/dashboards` (based on `opensearchproject/opensearch-dashboards`) +* `malcolmnetsec/file-monitor` (based on `debian:11-slim`) +* `malcolmnetsec/file-upload` (based on `debian:11-slim`) +* `malcolmnetsec/filebeat-oss` (based on `docker.elastic.co/beats/filebeat-oss`) +* `malcolmnetsec/freq` (based on `debian:11-slim`) +* `malcolmnetsec/htadmin` (based on `debian:11-slim`) +* `malcolmnetsec/logstash-oss` (based on `opensearchproject/logstash-oss-with-opensearch-output-plugin`) +* `malcolmnetsec/name-map-ui` (based on `alpine:3.16`) +* `malcolmnetsec/netbox` (based on `netboxcommunity/netbox:latest`) +* `malcolmnetsec/nginx-proxy` (based on `alpine:3.16`) +* `malcolmnetsec/opensearch` (based on `opensearchproject/opensearch`) +* `malcolmnetsec/pcap-capture` (based on `debian:11-slim`) +* `malcolmnetsec/pcap-monitor` (based on `debian:11-slim`) +* `malcolmnetsec/postgresql` (based on `postgres:14-alpine`) +* `malcolmnetsec/redis` (based on `redis:7-alpine`) +* `malcolmnetsec/suricata` (based on `debian:11-slim`) +* `malcolmnetsec/zeek` (based on `debian:11-slim`) + +Alternately, if you have forked Malcolm on GitHub, [workflow files]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/) are provided which contain instructions for GitHub to build the docker images and [sensor](live-analysis.md#Hedgehog) and [Malcolm](malcolm-iso.md#ISO) installer ISOs. The resulting images are named according to the pattern `ghcr.io/owner/malcolmnetsec/image:branch` (e.g., if you've forked Malcolm with the github user `romeogdetlevjr`, the `arkime` container built for the `main` would be named `ghcr.io/romeogdetlevjr/malcolmnetsec/arkime:main`). To run your local instance of Malcolm using these images instead of the official ones, you'll need to edit your `docker-compose.yml` file(s) and replace the `image:` tags according to this new pattern, or use the bash helper script `./shared/bin/github_image_helper.sh` to pull and re-tag the images. + +# Pre-Packaged installation files + +## Creating pre-packaged installation files + +`scripts/malcolm_appliance_packager.sh` can be run to package up the configuration files (and, if necessary, the Docker images) which can be copied to a network share or USB drive for distribution to non-networked machines. For example: + +``` +$ ./scripts/malcolm_appliance_packager.sh +You must set a username and password for Malcolm, and self-signed X.509 certificates will be generated + +Store administrator username/password for local Malcolm access? (Y/n): y + +Administrator username: analyst +analyst password: +analyst password (again): + +(Re)generate self-signed certificates for HTTPS access (Y/n): y + +(Re)generate self-signed certificates for a remote log forwarder (Y/n): y + +Store username/password for primary remote OpenSearch instance? (y/N): n + +Store username/password for secondary remote OpenSearch instance? (y/N): n + +Store username/password for email alert sender account? (y/N): n + +(Re)generate internal passwords for NetBox (Y/n): y + +Packaged Malcolm to "/home/user/tmp/malcolm_20190513_101117_f0d052c.tar.gz" + +Do you need to package docker images also [y/N]? y +This might take a few minutes... + +Packaged Malcolm docker images to "/home/user/tmp/malcolm_20190513_101117_f0d052c_images.tar.gz" + + +To install Malcolm: + 1. Run install.py + 2. Follow the prompts + +To start, stop, restart, etc. Malcolm: + Use the control scripts in the "scripts/" directory: + - start (start Malcolm) + - stop (stop Malcolm) + - restart (restart Malcolm) + - logs (monitor Malcolm logs) + - wipe (stop Malcolm and clear its database) + - auth_setup (change authentication-related settings) + +A minute or so after starting Malcolm, the following services will be accessible: + - Arkime: https://localhost/ + - OpenSearch Dashboards: https://localhost/dashboards/ + - PCAP upload (web): https://localhost/upload/ + - PCAP upload (sftp): sftp://USERNAME@127.0.0.1:8022/files/ + - Host and subnet name mapping editor: https://localhost/name-map-ui/ + - NetBox: https://localhost/netbox/ + - Account management: https://localhost:488/ + - Documentation: https://localhost/readme/ +``` + +The above example will result in the following artifacts for distribution as explained in the script's output: + +``` +$ ls -lh +total 2.0G +-rwxr-xr-x 1 user user 61k May 13 11:32 install.py +-rw-r--r-- 1 user user 2.0G May 13 11:37 malcolm_20190513_101117_f0d052c_images.tar.gz +-rw-r--r-- 1 user user 683 May 13 11:37 malcolm_20190513_101117_f0d052c.README.txt +-rw-r--r-- 1 user user 183k May 13 11:32 malcolm_20190513_101117_f0d052c.tar.gz +``` + +## Installing from pre-packaged installation files + +If you have obtained pre-packaged installation files to install Malcolm on a non-networked machine via an internal network share or on a USB key, you likely have the following files: + +* `malcolm_YYYYMMDD_HHNNSS_xxxxxxx.README.txt` - This readme file contains a minimal set up instructions for extracting the contents of the other tarballs and running the Malcolm appliance. +* `malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` - This tarball contains the configuration files and directory configuration used by an instance of Malcolm. It can be extracted via `tar -xf malcolm_YYYYMMDD_HHNNSS_xxxxxxx.tar.gz` upon which a directory will be created (named similarly to the tarball) containing the directories and configuration files. Alternatively, `install.py` can accept this filename as an argument and handle its extraction and initial configuration for you. +* `malcolm_YYYYMMDD_HHNNSS_xxxxxxx_images.tar.gz` - This tarball contains the Docker images used by Malcolm. It can be imported manually via `docker load -i malcolm_YYYYMMDD_HHNNSS_xxxxxxx_images.tar.gz` +* `install.py` - This install script can load the Docker images and extract Malcolm configuration files from the aforementioned tarballs and do some initial configuration for you. + +Run `install.py malcolm_XXXXXXXX_XXXXXX_XXXXXXX.tar.gz` and follow the prompts. If you do not already have Docker and Docker Compose installed, the `install.py` script will help you install them. \ No newline at end of file diff --git a/docs/download.md b/docs/download.md new file mode 100644 index 000000000..614706b85 --- /dev/null +++ b/docs/download.md @@ -0,0 +1,39 @@ +# Downloads + +## Malcolm + +### Docker images + +Malcolm operates as a cluster of Docker containers, isolated sandboxes which each serve a dedicated function of the system. Its Docker images can be pulled from [Docker Hub](https://hub.docker.com/u/malcolmnetsec) or built from source by following the instructions in the [Quick Start](quickstart.md#QuickStart) section of the documentation. + +### Installer ISO + +Malcolm's Docker-based deployment model makes Malcolm able to run on a variety of platforms. However, in some circumstances (for example, as a long-running appliance as part of a security operations center, or inside of a virtual machine) it may be desirable to install Malcolm as a dedicated standalone installation. + +Malcolm can be [packaged](malcolm-iso.md#ISOBuild) into an [installer ISO](malcolm-iso.md#ISO) based on the current [stable release](https://wiki.debian.org/DebianStable) of [Debian](https://www.debian.org/). This [customized Debian installation](https://wiki.debian.org/DebianLive) is preconfigured with the bare minimum software needed to run Malcolm. + +While official downloads of the Malcolm installer ISO are not provided, an **unofficial build** of the ISO installer for the [latest stable release]({{ site.github.repository_url }}/releases/latest) is available for download here. + +| ISO | SHA256 | +|---|---| +| [malcolm-6.4.0.iso](/iso/malcolm-6.4.0.iso) (4.5GiB) | [`xxxxxxxx`](/iso/malcolm-6.4.0.iso.sha256.txt) | + +## Hedgehog Linux + +### Installer ISO + +[Instructions are provided](hedgehog-iso-build.md#HedgehogISOBuild) to generate the Hedgehog Linux ISO from source. While official downloads of the Hedgehog Linux ISO are not provided, an **unofficial build** of the ISO installer for the latest stable release is available for download here. + +| ISO | SHA256 | +|---|---| +| [hedgehog-6.4.0.iso](/iso/hedgehog-6.4.0.iso) (2.2GiB) | [`xxxxxxxx`](/iso/hedgehog-6.4.0.iso.sha256.txt) | + +## Warning + +Please check any files you may have downloaded from the links on this page against the SHA256 sums provided to verify the integrity of the downloads. + +Read carefully the installation documentation for [Malcolm](malcolm-iso.md#ISOInstallation) and/or [Hedgehog Linux](hedgehog-installation.md#HedgehogInstallation). The ISO media boot on systems that support EFI-mode booting. The installer is designed to require as little user input as possible. For this reason, there are NO user prompts and confirmations about partitioning and reformatting hard disks for use by the operating system. The installer assumes that all non-removable storage media (eg., SSD, HDD, NVMe, etc.) are available for use and ⛔🆘😭💀 ***will partition and format them without warning*** 💀😭🆘⛔. + +## Disclaimer + +The terms of [Malcolm's license]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/License.txt) also apply to these unofficial builds of the Malcolm and Hedgehog Linux installer ISOs: neither the organizations funding Malcolm's development, its developers nor the maintainer of this site makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness or usefulness of any data, apparatus or process disclosed therein. diff --git a/docs/file-scanning.md b/docs/file-scanning.md new file mode 100644 index 000000000..6d6396a9e --- /dev/null +++ b/docs/file-scanning.md @@ -0,0 +1,28 @@ +# Automatic file extraction and scanning + +Malcolm can leverage Zeek's knowledge of network protocols to automatically detect file transfers and extract those files from PCAPs as Zeek processes them. This behavior can be enabled globally by modifying the `ZEEK_EXTRACTOR_MODE` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml), or on a per-upload basis for PCAP files uploaded via the [browser-based upload form](upload.md#Upload) when **Analyze with Zeek** is selected. + +To specify which files should be extracted, the following values are acceptable in `ZEEK_EXTRACTOR_MODE`: + +* `none`: no file extraction +* `interesting`: extraction of files with mime types of common attack vectors +* `mapped`: extraction of files with recognized mime types +* `known`: extraction of files for which any mime type can be determined +* `all`: extract all files + +Extracted files can be examined through any of the following methods: + +* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, specify the `VTOT_API2_KEY` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) +* scanning files with [**ClamAV**](https://www.clamav.net/); to enable this method, set the `EXTRACTED_FILE_ENABLE_CLAMAV` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) to `true` +* scanning files with [**Yara**](https://github.com/VirusTotal/yara); to enable this method, set the `EXTRACTED_FILE_ENABLE_YARA` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) to `true` +* scanning PE (portable executable) files with [**Capa**](https://github.com/fireeye/capa); to enable this method, set the `EXTRACTED_FILE_ENABLE_CAPA` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) to `true` + +Files which are flagged via any of these methods will be logged as Zeek `signatures.log` entries, and can be viewed in the **Signatures** dashboard in OpenSearch Dashboards. + +The `EXTRACTED_FILE_PRESERVATION` [environment variable in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) determines the behavior for preservation of Zeek-extracted files: + +* `quarantined`: preserve only flagged files in `./zeek-logs/extract_files/quarantine` +* `all`: preserve flagged files in `./zeek-logs/extract_files/quarantine` and all other extracted files in `./zeek-logs/extract_files/preserved` +* `none`: preserve no extracted files + +The `EXTRACTED_FILE_HTTP_SERVER_…` [environment variables in `docker-compose.yml`](malcolm-config.md#DockerComposeYml) configure access to the Zeek-extracted files path through the means of a simple HTTPS directory server. Beware that Zeek-extracted files may contain malware. As such, the files may be optionally encrypted upon download. \ No newline at end of file diff --git a/docs/hardening.md b/docs/hardening.md new file mode 100644 index 000000000..71fa0996b --- /dev/null +++ b/docs/hardening.md @@ -0,0 +1,62 @@ +# Hardening + +* [Hardening](#Hardening) + - [Compliance Exceptions](#ComplianceExceptions) + +The Malcolm aggregator base operating system uses the [harbian-audit](https://github.com/hardenedlinux/harbian-audit) benchmarks which target the following guidelines for establishing a secure configuration posture: + +* [CIS Debian Linux 9/10 Benchmark](https://www.cisecurity.org/cis-benchmarks/cis-benchmarks-faq/) +* [DISA STIG (Security Technical Implementation Guides) for RHEL 7](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/) v2r5 Ubuntu v1r2 [adapted](https://github.com/hardenedlinux/STIG-OS-mirror/blob/master/redhat-STIG-DOCs/U_Red_Hat_Enterprise_Linux_7_V2R5_STIG.zip) for a Debian operating system +* Additional recommendations from [cisecurity.org](https://www.cisecurity.org/) + +## Compliance Exceptions + +[Currently](https://github.com/hardenedlinux/harbian-audit/tree/master/bin/hardening) there are 274 checks to determine compliance with the [harbian-audit](https://github.com/hardenedlinux/harbian-audit) benchmark. + +The Malcolm aggregator base operating system claims exceptions from the recommendations in this benchmark in the following categories: + +**1.1 Install Updates, Patches and Additional Security Software** - When the the Malcolm aggregator appliance software is built, all of the latest applicable security patches and updates are included in it. How future updates are to be handled is still in design. + +**1.3 Enable verify the signature of local packages** - As the base distribution is not using embedded signatures, `debsig-verify` would reject all packages (see comment in `/etc/dpkg/dpkg.cfg`). Enabling it after installation would disallow any future updates. + +**2.14 Add nodev option to /run/shm Partition**, **2.15 Add nosuid Option to /run/shm Partition**, **2.16 Add noexec Option to /run/shm Partition** - The Malcolm aggregator base operating system does not mount `/run/shm` as a separate partition, so these recommendations do not apply. + +**2.19 Disable Mounting of freevxfs Filesystems**, **2.20 Disable Mounting of jffs2 Filesystems**, **2.21 Disable Mounting of hfs Filesystems**, **2.22 Disable Mounting of hfsplus Filesystems**, **2.23 Disable Mounting of squashfs Filesystems**, **2.24 Disable Mounting of udf Filesystems** - The Malcolm aggregator base operating system is not compiling a custom Linux kernel, so these filesystems are inherently supported as they are part Debian Linux's default kernel. + +**3.3 Set Boot Loader Password** - As maximizing availability is a system requirement, Malcolm should restart automatically without user intervention to ensured uninterrupted service. A boot loader password is not enabled. + +**4.8 Disable USB Devices** - The ability to ingest data (such as PCAP files) from a mounted USB mass storage device is a requirement of the system. + +**6.1 Ensure the X Window system is not installed**, **6.2 Ensure Avahi Server is not enabled**, **6.3 Ensure print server is not enabled** - An X Windows session is provided for displaying dashboards. The library packages `libavahi-common-data`, `libavahi-common3`, and `libcups2` are dependencies of some of the X components used by the Malcolm aggregator base operating system, but the `avahi` and `cups` services themselves are disabled. + +**6.17 Ensure virus scan Server is enabled**, **6.18 Ensure virus scan Server update is enabled** - As this is a network traffic analysis appliance rather than an end-user device, regular user files will not be created. A virus scan program would impact device performance and would be unnecessary. + +**7.1.1 Disable IP Forwarding**, **7.2.4 Log Suspicious Packets**, **7.2.7 Enable RFC-recommended Source Route Validation**, **7.4.1 Install TCP Wrappers** - As Malcolm may operate as a network traffic capture appliance sniffing packets on a network interface configured in promiscuous mode, these recommendations do not apply. + +**8.1.1.2 Disable System on Audit Log Full**, **8.1.1.3 Keep All Auditing Information**, **8.1.1.5 Ensure set remote_server for audit service**, **8.1.1.6 Ensure enable_krb5 set to yes for remote audit service**, **8.1.1.7 Ensure set action for audit storage volume is fulled**, **8.1.1.8 Ensure set action for network failure on remote audit service**, **8.1.1.9 Set space left for auditd service**, a few other audit-related items under section **8.1**, **8.2.4 Configure rsyslog to Send Logs to a Remote Log Host** - As maximizing availability is a system requirement, audit processing failures will be logged on the device rather than halting the system. `auditd` is set up to syslog when its local storage capacity is reached. + +**8.4.2 Implement Periodic Execution of File Integrity** - This functionality is not configured by default, but it can be configured post-install by the end user. + +Password-related recommendations under **9.2** and **10.1** - The library package `libpam-pwquality` is used in favor of `libpam-cracklib` which is what the [compliance scripts](https://github.com/hardenedlinux/harbian-audit/tree/master/bin/hardening) are looking for. Also, as an appliance running Malcolm is intended to be used as an appliance rather than a general user-facing software platform, some exceptions to password enforcement policies are claimed. + +**9.3.13 Limit Access via SSH** - The Malcolm aggregator base operating system does not create multiple regular user accounts: only `root` and an aggregator service account are used. SSH access for `root` is disabled. SSH login with a password is also disallowed: only key-based authentication is accepted. The service account accepts no keys by default. As such, the `AllowUsers`, `AllowGroups`, `DenyUsers`, and `DenyGroups` values in `sshd_config` do not apply. + +**9.4 Restrict Access to the su Command** - The Malcolm aggregator base operating system does not create multiple regular user accounts: only `root` and an aggregator service account are used. + +**10.1.6 Remove nopasswd option from the sudoers configuration** - A very limited set of operations (a single script used to run the AIDE integrity check as a non-root user) has the NOPASSWD option set to allow it to be run in the background without user intervention. + +**10.1.10 Set maxlogins for all accounts** and **10.5 Set Timeout on ttys** - The Malcolm aggregator base operating system does not create multiple regular user accounts: only `root` and an aggregator service account are used. + +**12.10 Find SUID System Executables**, **12.11 Find SGID System Executables** - The few files found by [these](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/12.10_find_suid_files.sh) [scripts](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/12.11_find_sgid_files.sh) are valid exceptions required by the Malcolm aggregator base operating system's core requirements. + +**14.1 Defense for NAT Slipstreaming** - As Malcolm may operate as a network traffic capture appliance sniffing packets on a network interface configured in promiscuous mode, this recommendation does not apply. + +Please review the notes for these additional guidelines. While not claiming an exception, the Malcolm aggregator base operating system may implement them in a manner different than is described by the [CIS Debian Linux 9/10 Benchmark](https://www.cisecurity.org/cis-benchmarks/cis-benchmarks-faq/) or the [hardenedlinux/harbian-audit](https://github.com/hardenedlinux/harbian-audit) audit scripts. + +**4.1 Restrict Core Dumps** - The Malcolm aggregator base operating system disables core dumps using a configuration file for `ulimit` named `/etc/security/limits.d/limits.conf`. The [audit script](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/4.1_restrict_core_dumps.sh) checking for this does not check the `limits.d` subdirectory, which is why this is incorrectly flagged as noncompliant. + +**5.4 Ensure ctrl-alt-del is disabled** - The Malcolm aggregator base operating system disables the `ctrl+alt+delete` key sequence by executing `systemctl disable ctrl-alt-del.target` during installation and the command `systemctl mask ctrl-alt-del.target` at boot time. + +**7.4.4 Create /etc/hosts.deny**, **7.7.1 Ensure Firewall is active**, **7.7.4.1 Ensure default deny firewall policy**, **7.7.4.2 Ensure loopback traffic is configured**, **7.7.4.3 Ensure default deny firewall policy**, **7.7.4.4 Ensure outbound and established connections are configured** - The Malcolm aggregator base operating system **is** configured with an appropriately locked-down software firewall (managed by "Uncomplicated Firewall" `ufw`). However, the methods outlined in the CIS benchmark recommendations do not account for this configuration. + +**8.6 Verifies integrity all packages** - The [script](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/8.7_verify_integrity_packages.sh) which verifies package integrity only "fails" because of missing (status `??5??????` displayed by the utility) language ("locale") files, which are removed as part of the Malcolm aggregator base operating system's trimming-down process. All non-locale-related system files pass intergrity checks. diff --git a/docs/hedgehog-boot.md b/docs/hedgehog-boot.md new file mode 100644 index 000000000..a8bfeee64 --- /dev/null +++ b/docs/hedgehog-boot.md @@ -0,0 +1,17 @@ +# Boot + +Each time the sensor boots, a grub boot menu will be shown briefly, after which the sensor will proceed to load. + +## Kiosk mode + +![Kiosk mode sensor menu: resource statistics](./images/hedgehog/images/kiosk_mode_sensor_menu.png) + +The sensor automatically logs in as the sensor user account and runs in **kiosk mode**, which is intended to show an at-a-glance view of the its resource utilization. Clicking the **☰** icon in allows you to switch between the resource statistics view and the services view. + +![Kiosk mode sensor menu: services](./images/hedgehog/images/kiosk_mode_services_menu.png) + +The kiosk's services screen (designed with large clickable labels for small portable touch screens) can be used to start and stop essential services, get a status report of the currently running services, and clean all captured data from the sensor. + +!["Clean Sensor" confirmation prompt before deleting sensor data](./images/hedgehog/images/kiosk_mode_wipe_prompt.png) + +!["Sensor Status" report from the kiosk services menu](./images/hedgehog/images/kiosk_mode_status.png) \ No newline at end of file diff --git a/docs/hedgehog-config-root.md b/docs/hedgehog-config-root.md new file mode 100644 index 000000000..327463d6e --- /dev/null +++ b/docs/hedgehog-config-root.md @@ -0,0 +1,47 @@ +# Interfaces, hostname, and time synchronization + +## Hostname + +The first step of sensor configuration is to configure the network interfaces and sensor hostname. Clicking the **Configure Interfaces and Hostname** toolbar icon (or, if you are at a command line prompt, running `configure-interfaces`) will prompt you for the root password you created during installation, after which the configuration welcome screen is shown. Select **Continue** to proceed. + +You may next select whether to configure the network interfaces, hostname, or time synchronization. + +![Selection to configure network interfaces, hostname, or time synchronization](./images/hedgehog/images/root_config_mode.png) + +Selecting **Hostname**, you will be presented with a summary of the current sensor identification information, after which you may specify a new sensor hostname. This name will be used to tag all events forwarded from this sensor in the events' **host.name** field. + +![Specifying a new sensor hostname](./images/hedgehog/images/hostname_setting.png) + +## Interfaces + +Returning to the configuration mode selection, choose **Interface**. You will be prompted if you would like help identifying network interfaces. If you select **Yes**, you will be prompted to select a network interface, after which that interface's link LED will blink for 10 seconds to help you in its identification. This network interface identification aid will continue to prompt you to identify further network interfaces until you select **No**. + +You will be presented with a list of interfaces to configure as the sensor management interface. This is the interface the sensor itself will use to communicate with the network in order to, for example, forward captured logs to an aggregate server. In order to do so, the management interface must be assigned an IP address. This is generally **not** the interface used for capturing data. Select the interface to which you wish to assign an IP address. The interfaces are listed by name and MAC address and the associated link speed is also displayed if it can be determined. For interfaces without a connected network cable, generally a `-1` will be displayed instead of the interface speed. + +![Management interface selection](./images/hedgehog/images/select_iface.png) + +Depending on the configuration of your network, you may now specify how the management interface will be assigned an IP address. In order to communicate with an event aggregator over the management interface, either **static** or **dhcp** must be selected. + +![Interface address source](./images/hedgehog/images/iface_mode.png) + +If you select static, you will be prompted to enter the IP address, netmask, and gateway to assign to the management interface. + +![Static IP configuration](./images/hedgehog/images/iface_static.png) + +In either case, upon selecting **OK** the network interface will be brought down, configured, and brought back up, and the result of the operation will be displayed. You may choose **Quit** upon returning to the configuration tool's welcome screen. + +## Time synchronization + +Returning to the configuration mode selection, choose **Time Sync**. Here you can configure the sensor to keep its time synchronized with either an NTP server (using the NTP protocol) or a local [Malcolm]({{ site.github.repository_url }}) aggregator or another HTTP/HTTPS server. On the next dialog, choose the time synchronization method you wish to configure. + +![Time synchronization method](./images/hedgehog/images/time_sync_mode.png) + +If **htpdate** is selected, you will be prompted to enter the IP address or hostname and port of an HTTP/HTTPS server (for a Malcolm instance, port `9200` may be used) and the time synchronization check frequency in minutes. A test connection will be made to determine if the time can be retrieved from the server. + +![*htpdate* configuration](./images/hedgehog/images/htpdate_setup.png) + +If *ntpdate* is selected, you will be prompted to enter the IP address or hostname of the NTP server. + +![NTP configuration](./images/hedgehog/images/ntp_host.png) + +Upon configuring time synchronization, a "Time synchronization configured successfully!" message will be displayed, after which you will be returned to the welcome screen. \ No newline at end of file diff --git a/docs/hedgehog-config-user.md b/docs/hedgehog-config-user.md new file mode 100644 index 000000000..4ec5f02f5 --- /dev/null +++ b/docs/hedgehog-config-user.md @@ -0,0 +1,189 @@ +# Capture, forwarding, and autostart services + +Clicking the **Configure Capture and Forwarding** toolbar icon (or, if you are at a command prompt, running `configure-capture`) will launch the configuration tool for capture and forwarding. The root password is not required as it was for the interface and hostname configuration, as sensor services are run under the non-privileged sensor account. Select **Continue** to proceed. You may select from a list of configuration options. + +![Select configuration mode](./images/hedgehog/images/capture_config_main.png) + +## Capture + +Choose **Configure Capture** to configure parameters related to traffic capture and local analysis. You will be prompted if you would like help identifying network interfaces. If you select **Yes**, you will be prompted to select a network interface, after which that interface's link LED will blink for 10 seconds to help you in its identification. This network interface identification aid will continue to prompt you to identify further network interfaces until you select **No**. + +You will be presented with a list of network interfaces and prompted to select one or more capture interfaces. An interface used to capture traffic is generally a different interface than the one selected previously as the management interface, and each capture interface should be connected to a network tap or span port for traffic monitoring. Capture interfaces are usually not assigned an IP address as they are only used to passively “listen” to the traffic on the wire. The interfaces are listed by name and MAC address and the associated link speed is also displayed if it can be determined. For interfaces without a connected network cable, generally a `-1` will be displayed instead of the interface speed. + +![Select capture interfaces](./images/hedgehog/images/capture_iface_select.png) + +Upon choosing the capture interfaces and selecting OK, you may optionally provide a capture filter. This filter will be used to limit what traffic the PCAP service ([`tcpdump`](https://www.tcpdump.org/)) and the traffic analysis services ([`zeek`](https://www.zeek.org/) and [`suricata`](https://suricata.io/)) will see. Capture filters are specified using [Berkeley Packet Filter (BPF)](http://biot.com/capstats/bpf.html) syntax. Clicking **OK** will attempt to validate the capture filter, if specified, and will present a warning if the filter is invalid. + +![Specify capture filters](./images/hedgehog/images/capture_filter.png) + +Next you must specify the paths where captured PCAP files and logs will be stored locally on the sensor. If the installation worked as expected, these paths should be prepopulated to reflect paths on the volumes formatted at install time for the purpose storing these artifacts. Usually these paths will exist on separate storage volumes. Enabling the PCAP and log pruning autostart services (see the section on autostart services below) will enable monitoring of these paths to ensure that their contents do not consume more than 90% of their respective volumes' space. Choose **OK** to continue. + +![Specify capture paths](./images/hedgehog/images/capture_paths.png) + +### Automatic file extraction and scanning + +Hedgehog Linux can leverage Zeek's knowledge of network protocols to automatically detect file transfers and extract those files from network traffic as Zeek sees them. + +To specify which files should be extracted, specify the Zeek file carving mode: + +![Zeek file carving mode](./images/hedgehog/images/zeek_file_carve_mode.png) + +If you're not sure what to choose, either of **mapped (except common plain text files)** (if you want to carve and scan almost all files) or **interesting** (if you only want to carve and scan files with [mime types of common attack vectors]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/sensor-iso/interface/sensor_ctl/zeek/extractor_override.interesting.zeek)) is probably a good choice. + +Next, specify which carved files to preserve (saved on the sensor under `/capture/bro/capture/extract_files/quarantine` by default). In order to not consume all of the sensor's available storage space, the oldest preserved files will be pruned along with the oldest Zeek logs as described below with **AUTOSTART_PRUNE_ZEEK** in the [autostart services](#HedgehogConfigAutostart) section. + +You'll be prompted to specify which engine(s) to use to analyze extracted files. Extracted files can be examined through any of three methods: + +![File scanners](./images/hedgehog/images/zeek_file_carve_scanners.png) + +* scanning files with [**ClamAV**](https://www.clamav.net/); to enable this method, select **ZEEK_FILE_SCAN_CLAMAV** when specifying scanners for Zeek-carved files +* submitting file hashes to [**VirusTotal**](https://www.virustotal.com/en/#search); to enable this method, select **ZEEK_FILE_SCAN_VTOT** when specifying scanners for Zeek-carved files, then manually edit `/opt/sensor/sensor_ctl/control_vars.conf` and specify your [VirusTotal API key](https://developers.virustotal.com/reference) in `VTOT_API2_KEY` +* scanning files with [**Yara**](https://github.com/VirusTotal/yara); to enable this method, select **ZEEK_FILE_SCAN_YARA** when specifying scanners for Zeek-carved files +* scanning portable executable (PE) files with [**Capa**](https://github.com/fireeye/capa); to enable this method, select **ZEEK_FILE_SCAN_CAPA** when specifying scanners for Zeek-carved files + +Files which are flagged as potentially malicious will be logged as Zeek `signatures.log` entries, and can be viewed in the **Signatures** dashboard in [OpenSearch Dashboards]({{ site.github.repository_url }}#DashboardsVisualizations) when forwarded to Malcolm. + +![File quarantine](./images/hedgehog/images/file_quarantine.png) + +Finally, you will be presented with the list of configuration variables that will be used for capture, including the values which you have configured up to this point in this section. Upon choosing **OK** these values will be written back out to the sensor configuration file located at `/opt/sensor/sensor_ctl/control_vars.conf`. It is not recommended that you edit this file manually. After confirming these values, you will be presented with a confirmation that these settings have been written to the configuration file, and you will be returned to the welcome screen. + +## Forwarding + +Select **Configure Forwarding** to set up forwarding logs and statistics from the sensor to an aggregator server, such as [Malcolm]({{ site.github.repository_url }}). + +![Configure forwarders](./images/hedgehog/images/forwarder_config.png) + +There are five forwarder services used on the sensor, each for forwarding a different type of log or sensor metric. + +## capture: Arkime session forwarding + +[capture](https://github.com/arkime/arkime/tree/master/capture) is not only used to capture PCAP files, but also the parse raw traffic into sessions and forward this session metadata to an [OpenSearch](https://opensearch.org/) database so that it can be viewed in [Arkime viewer](https://arkime.com/), whether standalone or as part of a [Malcolm]({{ site.github.repository_url }}) instance. If you're using Hedgehog Linux with Malcolm, please read [Correlating Zeek logs and Arkime sessions]({{ site.github.repository_url }}#ZeekArkimeFlowCorrelation) in the Malcolm documentation for more information. + +First, select the OpenSearch connection transport protocol, either **HTTPS** or **HTTP**. If the metrics are being forwarded to Malcolm, select **HTTPS** to encrypt messages from the sensor to the aggregator using TLS v1.2 using ECDHE-RSA-AES128-GCM-SHA256. If **HTTPS** is chosen, you must choose whether to enable SSL certificate verification. If you are using a self-signed certificate (such as the one automatically created during [Malcolm's configuration]({{ site.github.repository_url }}#configure-authentication)), choose **None**. + +![OpenSearch connection protocol](./images/hedgehog/images/opensearch_connection_protocol.png) ![OpenSearch SSL verification](./images/hedgehog/images/opensearch_ssl_verification.png) + +Next, enter the **OpenSearch host** IP address (ie., the IP address of the aggregator) and port. These metrics are written to an OpenSearch database using a RESTful API, usually using port 9200. Depending on your network configuration, you may need to open this port in your firewall to allow this connection from the sensor to the aggregator. + +![OpenSearch host and port](./images/hedgehog/images/arkime-capture-ip-port.png) + +You will be asked to enter authentication credentials for the sensor's connections to the aggregator's OpenSearch API. After you've entered the username and the password, the sensor will attempt a test connection to OpenSearch using the connection information provided. + +![OpenSearch username](./images/hedgehog/images/opensearch_username.png) ![OpenSearch password](./images/hedgehog/images/opensearch_password.png) ![Successful OpenSearch connection](./images/hedgehog/images/opensearch_connection_success.png) + +Finally, you will be shown a dialog for a list of IP addresses used to populate an access control list (ACL) for hosts allowed to connect back to the sensor for retrieving session payloads from its PCAP files for display in Arkime viewer. The list will be prepopulated with the IP address entered a few screens prior to this one. + +![PCAP retrieval ACL](./images/hedgehog/images/malcolm_arkime_reachback_acl.png) + +Finally, you'll be given the opportunity to review the all of the Arkime `capture` options you've specified. Selecting **OK** will cause the parameters to be saved and you will be returned to the configuration tool's welcome screen. + +![capture settings confirmation](./images/hedgehog/images/arkime_confirm.png) + +## filebeat: Zeek and Suricata log forwarding + +[Filebeat](https://www.elastic.co/products/beats/filebeat) is used to forward [Zeek](https://www.zeek.org/) and [Suricata](https://suricata.io/) logs to a remote [Logstash](https://www.elastic.co/products/logstash) instance for further enrichment prior to insertion into an [OpenSearch](https://opensearch.org/) database. + +To configure filebeat, first provide the log path (the same path previously configured for log file generation). + +![Configure filebeat for log forwarding](./images/hedgehog/images/filebeat_log_path.png) + +You must also provide the IP address of the Logstash instance to which the logs are to be forwarded, and the port on which Logstash is listening. These logs are forwarded using the Beats protocol, generally over port 5044. Depending on your network configuration, you may need to open this port in your firewall to allow this connection from the sensor to the aggregator. + +![Configure filebeat for log forwrading](./images/hedgehog/images/filebeat_ip_port.png) + +Next you are asked whether the connection used for log forwarding should be done **unencrypted** or over **SSL**. Unencrypted communication requires less processing overhead and is simpler to configure, but the contents of the logs may be visible to anyone who is able to intercept that traffic. + +![Filebeat SSL certificate verification](./images/hedgehog/images/filebeat_ssl.png) + +If **SSL** is chosen, you must choose whether to enable [SSL certificate verification](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-ssl-logstash.html). If you are using a self-signed certificate (such as the one automatically created during [Malcolm's configuration]({{ site.github.repository_url }}#configure-authentication), choose **None**. + +![Unencrypted vs. SSL encryption for log forwarding](./images/hedgehog/images/filebeat_ssl_verify.png) + +The last step for SSL-encrypted log forwarding is to specify the SSL certificate authority, certificate, and key files. These files must match those used by the Logstash instance receiving the logs on the aggregator. If Malcolm's `auth_setup` script was used to generate these files they would be found in the `filebeat/certs/` subdirectory of the Malcolm installation and must be manually copied to the sensor (stored under `/opt/sensor/sensor_ctl/logstash-client-certificates` or in any other path accessible to the sensor account). Specify the location of the certificate authorities file (eg., `ca.crt`), the certificate file (eg., `client.crt`), and the key file (eg., `client.key`). + +![SSL certificate files](./images/hedgehog/images/filebeat_certs.png) + +The Logstash instance receiving the events must be similarly configured with matching SSL certificate and key files. Under Malcolm, the `BEATS_SSL` variable must be set to `true` in Malcolm's `docker-compose.yml` file and the SSL files must exist in the `logstash/certs/` subdirectory of the Malcolm installation. + +Once you have specified all of the filebeat parameters, you will be presented with a summary of the settings related to the forwarding of these logs. Selecting **OK** will cause the parameters to be written to filebeat's configuration keystore under `/opt/sensor/sensor_ctl/logstash-client-certificates` and you will be returned to the configuration tool's welcome screen. + +![Confirm filebeat settings](./images/hedgehog/images/filebeat_confirm.png) + +## miscbeat: System metrics forwarding + +The sensor uses [Fluent Bit](https://fluentbit.io/) to gather miscellaneous system resource metrics (CPU, network I/O, disk I/O, memory utilization, temperature, etc.) and the [Beats](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) protocol to forward these metrics to a remote [Logstash](https://www.elastic.co/products/logstash) instance for further enrichment prior to insertion into an [OpenSearch](https://opensearch.org/) database. Metrics categories can be enabled/disabled as described in the [autostart services](#HedgehogConfigAutostart) section of this document. + +This forwarder's configuration is almost identical to that of [filebeat](#Hedgehogfilebeat) in the previous section. Select `miscbeat` from the forwarding configuration mode options and follow the same steps outlined above to set up this forwarder. + +## Autostart services + +Once the forwarders have been configured, the final step is to **Configure Autostart Services**. Choose this option from the configuration mode menu after the welcome screen of the sensor configuration tool. + +Despite configuring capture and/or forwarder services as described in previous sections, only services enabled in the autostart configuration will run when the sensor starts up. The available autostart processes are as follows (recommended services are in **bold text**): + +* **AUTOSTART_ARKIME** - [capture](#Hedgehogarkime-capture) PCAP engine for traffic capture, as well as traffic parsing and metadata insertion into OpenSearch for viewing in [Arkime](https://arkime.com/). If you are using Hedgehog Linux along with [Malcolm]({{ site.github.repository_url }}) or another Arkime installation, this is probably the packet capture engine you want to use. +* **AUTOSTART_CLAMAV_UPDATES** - Virus database update service for ClamAV (requires sensor to be connected to the internet) +* **AUTOSTART_FILEBEAT** - [filebeat](#Hedgehogfilebeat) Zeek and Suricata log forwarder +* **AUTOSTART_FLUENTBIT_AIDE** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/exec) [AIDE](https://aide.github.io/) file system integrity checks +* **AUTOSTART_FLUENTBIT_AUDITLOG** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/tail) [auditd](https://man7.org/linux/man-pages/man8/auditd.8.html) logs +* *AUTOSTART_FLUENTBIT_KMSG* - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/kernel-logs) the Linux kernel log buffer (these are generally reflected in syslog as well, which may make this agent redundant) +* **AUTOSTART_FLUENTBIT_METRICS** - [Fluent Bit](https://fluentbit.io/) agent for collecting [various](https://docs.fluentbit.io/manual/pipeline/inputs) system resource and performance metrics +* **AUTOSTART_FLUENTBIT_SYSLOG** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/syslog) Linux syslog messages +* **AUTOSTART_FLUENTBIT_THERMAL** - [Fluent Bit](https://fluentbit.io/) agent [monitoring](https://docs.fluentbit.io/manual/pipeline/inputs/thermal) system temperatures +* **AUTOSTART_MISCBEAT** - [filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-tcp.html) forwarder which sends system metrics collected by [Fluent Bit](https://fluentbit.io/) to a remote Logstash instance (e.g., [Malcolm]({{ site.github.repository_url }})'s) +* *AUTOSTART_NETSNIFF* - [netsniff-ng](http://netsniff-ng.org/) PCAP engine for saving packet capture (PCAP) files +* **AUTOSTART_PRUNE_PCAP** - storage space monitor to ensure that PCAP files do not consume more than 90% of the total size of the storage volume to which PCAP files are written +* **AUTOSTART_PRUNE_ZEEK** - storage space monitor to ensure that Zeek logs do not consume more than 90% of the total size of the storage volume to which Zeek logs are written +* **AUTOSTART_SURICATA** - [Suricata](https://suricata.io/) traffic analysis engine +* **AUTOSTART_SURICATA_UPDATES** - Rule update service for Suricata (requires sensor to be connected to the internet) +* *AUTOSTART_TCPDUMP* - [tcpdump](https://www.tcpdump.org/) PCAP engine for saving packet capture (PCAP) files +* **AUTOSTART_ZEEK** - [Zeek](https://www.zeek.org/) traffic analysis engine + +Note that only one packet capture engine ([capture](https://arkime.com/), [netsniff-ng](http://netsniff-ng.org/), or [tcpdump](https://www.tcpdump.org/)) can be used. + +![Autostart services](./images/hedgehog/images/autostarts.png) + +Once you have selected the autostart services, you will be prompted to confirm your selections. Doing so will cause these values to be written back out to the `/opt/sensor/sensor_ctl/control_vars.conf` configuration file. + +![Autostart services confirmation](./images/hedgehog/images/autostarts_confirm.png) + +After you have completed configuring the sensor it is recommended that you reboot the sensor to ensure all new settings take effect. If rebooting is not an option, you may click the **Restart Sensor Services** menu icon in the top menu bar, or open a terminal and run: + +``` +/opt/sensor/sensor_ctl/shutdown && sleep 10 && /opt/sensor/sensor_ctl/supervisor.sh +``` + +This will cause the sensor services controller to stop, wait a few seconds, and restart. You can check the status of the sensor's processes by choosing **Sensor Status** from the sensor's kiosk mode, clicking the **Sensor Service Status** toolbar icon, or running `/opt/sensor/sensor_ctl/status` from the command line: + +``` +$ /opt/sensor/sensor_ctl/status +arkime:arkime-capture RUNNING pid 6455, uptime 0:03:17 +arkime:arkime-viewer RUNNING pid 6456, uptime 0:03:17 +beats:filebeat RUNNING pid 6457, uptime 0:03:17 +beats:miscbeat RUNNING pid 6458, uptime 0:03:17 +clamav:clamav-service RUNNING pid 6459, uptime 0:03:17 +clamav:clamav-updates RUNNING pid 6461, uptime 0:03:17 +fluentbit-auditlog RUNNING pid 6463, uptime 0:03:17 +fluentbit-kmsg STOPPED Not started +fluentbit-metrics:cpu RUNNING pid 6466, uptime 0:03:17 +fluentbit-metrics:df RUNNING pid 6471, uptime 0:03:17 +fluentbit-metrics:disk RUNNING pid 6468, uptime 0:03:17 +fluentbit-metrics:mem RUNNING pid 6472, uptime 0:03:17 +fluentbit-metrics:mem_p RUNNING pid 6473, uptime 0:03:17 +fluentbit-metrics:netif RUNNING pid 6474, uptime 0:03:17 +fluentbit-syslog RUNNING pid 6478, uptime 0:03:17 +fluentbit-thermal RUNNING pid 6480, uptime 0:03:17 +netsniff:netsniff-enp1s0 STOPPED Not started +prune:prune-pcap RUNNING pid 6484, uptime 0:03:17 +prune:prune-zeek RUNNING pid 6486, uptime 0:03:17 +supercronic RUNNING pid 6490, uptime 0:03:17 +suricata RUNNING pid 6501, uptime 0:03:17 +tcpdump:tcpdump-enp1s0 STOPPED Not started +zeek:capa RUNNING pid 6553, uptime 0:03:17 +zeek:clamav RUNNING pid 6512, uptime 0:03:17 +zeek:logger RUNNING pid 6554, uptime 0:03:17 +zeek:virustotal STOPPED Not started +zeek:watcher RUNNING pid 6510, uptime 0:03:17 +zeek:yara RUNNING pid 6548, uptime 0:03:17 +zeek:zeekctl RUNNING pid 6502, uptime 0:03:17 +``` \ No newline at end of file diff --git a/docs/hedgehog-config-zeek-intel.md b/docs/hedgehog-config-zeek-intel.md new file mode 100644 index 000000000..cef702d53 --- /dev/null +++ b/docs/hedgehog-config-zeek-intel.md @@ -0,0 +1,7 @@ +# Zeek Intelligence Framework + +To quote Zeek's [Intelligence Framework](https://docs.zeek.org/en/master/frameworks/intel.html) documentation, "The goals of Zeek’s Intelligence Framework are to consume intelligence data, make it available for matching, and provide infrastructure to improve performance and memory utilization. Data in the Intelligence Framework is an atomic piece of intelligence such as an IP address or an e-mail address. This atomic data will be packed with metadata such as a freeform source field, a freeform descriptive field, and a URL which might lead to more information about the specific item." Zeek [intelligence](https://docs.zeek.org/en/master/scripts/base/frameworks/intel/main.zeek.html) [indicator types](https://docs.zeek.org/en/master/scripts/base/frameworks/intel/main.zeek.html#type-Intel::Type) include IP addresses, URLs, file names, hashes, email addresses, and more. + +Hedgehog Linux doesn't come bundled with intelligence files from any particular feed, but they can be easily included into your local instance. Before Zeek starts, Hedgehog Linux configures it such that intelligence files will be automatically included in its local policy. Subdirectories under `/opt/sensor/sensor_ctl/zeek/intel` which contain their own `__load__.zeek` file will be `@load`-ed as-is, while subdirectories containing "loose" intelligence files will be [loaded](https://docs.zeek.org/en/master/frameworks/intel.html#loading-intelligence) automatically with a `redef Intel::read_files` directive. + +Note that Hedgehog Linux does not manage updates for these intelligence files. You should use the update mechanism suggested by your feeds' maintainers to keep them up to date. Adding and deleting intelligence files under this directory will take effect upon restarting Zeek. \ No newline at end of file diff --git a/docs/hedgehog-config.md b/docs/hedgehog-config.md new file mode 100644 index 000000000..9fcb2fb4e --- /dev/null +++ b/docs/hedgehog-config.md @@ -0,0 +1,17 @@ +# Configuration + +Kiosk mode can be exited by connecting an external USB keyboard and pressing **Alt+F4**, upon which the *sensor* user's desktop is shown. + +![Sensor login session desktop](./images/hedgehog/images/desktop.png) + +Several icons are available in the top menu bar: + +* **Terminal** - opens a command prompt in a terminal emulator +* **Browser** - opens a web browser +* **Kiosk** – returns the sensor to kiosk mode +* **README** – displays this document +* **Sensor status** – displays a list with the status of each sensor service +* **Configure capture and forwarding** – opens a dialog for configuring the sensor's capture and forwarding services, as well as specifying which services should autostart upon boot +* **Configure interfaces and hostname** – opens a dialog for configuring the sensor's network interfaces and setting the sensor's hostname +* **Restart sensor services** - stops and restarts all of the [autostart services](hedgehog-config-user.md#HedgehogConfigAutostart) + diff --git a/docs/hedgehog-hardening.md b/docs/hedgehog-hardening.md new file mode 100644 index 000000000..9db45a9b9 --- /dev/null +++ b/docs/hedgehog-hardening.md @@ -0,0 +1,59 @@ +# Appendix D - Hardening + +Hedgehog Linux uses the [harbian-audit](https://github.com/hardenedlinux/harbian-audit) benchmarks which target the following guidelines for establishing a secure configuration posture: + +* [CIS Debian Linux 9/10 Benchmark](https://www.cisecurity.org/cis-benchmarks/cis-benchmarks-faq/) +* [DISA STIG (Security Technical Implementation Guides) for RHEL 7](https://www.stigviewer.com/stig/red_hat_enterprise_linux_7/) v2r5 Ubuntu v1r2 [adapted](https://github.com/hardenedlinux/STIG-OS-mirror/blob/master/redhat-STIG-DOCs/U_Red_Hat_Enterprise_Linux_7_V2R5_STIG.zip) for a Debian operating system +* Additional recommendations from [cisecurity.org](https://www.cisecurity.org/) + +## Compliance Exceptions + +[Currently](https://github.com/hardenedlinux/harbian-audit/tree/master/bin/hardening) there are 274 checks to determine compliance with the [harbian-audit](https://github.com/hardenedlinux/harbian-audit) benchmark. + +Hedgehog Linux claims exceptions from the recommendations in this benchmark in the following categories: + +**1.1 Install Updates, Patches and Additional Security Software** - When the the Malcolm aggregator appliance software is built, all of the latest applicable security patches and updates are included in it. How future updates are to be handled is still in design. + +**1.3 Enable verify the signature of local packages** - As the base distribution is not using embedded signatures, `debsig-verify` would reject all packages (see comment in `/etc/dpkg/dpkg.cfg`). Enabling it after installation would disallow any future updates. + +**2.14 Add nodev option to /run/shm Partition**, **2.15 Add nosuid Option to /run/shm Partition**, **2.16 Add noexec Option to /run/shm Partition** - Hedgehog Linux does not mount `/run/shm` as a separate partition, so these recommendations do not apply. + +**2.19 Disable Mounting of freevxfs Filesystems**, **2.20 Disable Mounting of jffs2 Filesystems**, **2.21 Disable Mounting of hfs Filesystems**, **2.22 Disable Mounting of hfsplus Filesystems**, **2.23 Disable Mounting of squashfs Filesystems**, **2.24 Disable Mounting of udf Filesystems** - Hedgehog Linux is not compiling a custom Linux kernel, so these filesystems are inherently supported as they are part Debian Linux's default kernel. + +**3.3 Set Boot Loader Password** - As maximizing availability is a system requirement, Malcolm should restart automatically without user intervention to ensured uninterrupted service. A boot loader password is not enabled. + +**4.8 Disable USB Devices** - The ability to ingest data (such as PCAP files) from a mounted USB mass storage device is a requirement of the system. + +**6.1 Ensure the X Window system is not installed**, **6.2 Ensure Avahi Server is not enabled**, **6.3 Ensure print server is not enabled** - An X Windows session is provided for displaying dashboards. The library packages `libavahi-common-data`, `libavahi-common3`, and `libcups2` are dependencies of some of the X components used by Hedgehog Linux, but the `avahi` and `cups` services themselves are disabled. + +**6.17 Ensure virus scan Server is enabled**, **6.18 Ensure virus scan Server update is enabled** - As this is a network traffic analysis appliance rather than an end-user device, regular user files will not be created. A virus scan program would impact device performance and would be unnecessary. + +**7.1.1 Disable IP Forwarding**, **7.2.4 Log Suspicious Packets**, **7.2.7 Enable RFC-recommended Source Route Validation**, **7.4.1 Install TCP Wrappers** - As Malcolm may operate as a network traffic capture appliance sniffing packets on a network interface configured in promiscuous mode, these recommendations do not apply. + +**8.1.1.2 Disable System on Audit Log Full**, **8.1.1.3 Keep All Auditing Information**, **8.1.1.5 Ensure set remote_server for audit service**, **8.1.1.6 Ensure enable_krb5 set to yes for remote audit service**, **8.1.1.7 Ensure set action for audit storage volume is fulled**, **8.1.1.8 Ensure set action for network failure on remote audit service**, **8.1.1.9 Set space left for auditd service**, a few other audit-related items under section **8.1**, **8.2.4 Configure rsyslog to Send Logs to a Remote Log Host** - As maximizing availability is a system requirement, audit processing failures will be logged on the device rather than halting the system. `auditd` is set up to syslog when its local storage capacity is reached. + +**8.4.2 Implement Periodic Execution of File Integrity** - This functionality is not configured by default, but it can be configured post-install by the end user. + +Password-related recommendations under **9.2** and **10.1** - The library package `libpam-pwquality` is used in favor of `libpam-cracklib` which is what the [compliance scripts](https://github.com/hardenedlinux/harbian-audit/tree/master/bin/hardening) are looking for. Also, as an appliance running Malcolm is intended to be used as an appliance rather than a general user-facing software platform, some exceptions to password enforcement policies are claimed. + +**9.3.13 Limit Access via SSH** - Hedgehog Linux does not create multiple regular user accounts: only `root` and a sensor service account are used. SSH access for `root` is disabled. SSH login with a password is also disallowed: only key-based authentication is accepted. The service account accepts no keys by default. As such, the `AllowUsers`, `AllowGroups`, `DenyUsers`, and `DenyGroups` values in `sshd_config` do not apply. + +**9.4 Restrict Access to the su Command** - Hedgehog Linux does not create multiple regular user accounts: only `root` and a sensor service account are used. + +**10.1.6 Remove nopasswd option from the sudoers configuration** - A very limited set of operations (a single script used to run the AIDE integrity check as a non-root user) has the NOPASSWD option set to allow it to be run in the background without user intervention. + +**10.1.10 Set maxlogins for all accounts** and **10.5 Set Timeout on ttys** - Hedgehog Linux does not create multiple regular user accounts: only `root` and a sensor service account are used. + +**12.10 Find SUID System Executables**, **12.11 Find SGID System Executables** - The few files found by [these](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/12.10_find_suid_files.sh) [scripts](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/12.11_find_sgid_files.sh) are valid exceptions required by Hedgehog Linux's core requirements. + +**14.1 Defense for NAT Slipstreaming** - As Malcolm may operate as a network traffic capture appliance sniffing packets on a network interface configured in promiscuous mode, this recommendation does not apply. + +Please review the notes for these additional guidelines. While not claiming an exception, Hedgehog Linux may implement them in a manner different than is described by the [CIS Debian Linux 9/10 Benchmark](https://www.cisecurity.org/cis-benchmarks/cis-benchmarks-faq/) or the [hardenedlinux/harbian-audit](https://github.com/hardenedlinux/harbian-audit) audit scripts. + +**4.1 Restrict Core Dumps** - Hedgehog Linux disables core dumps using a configuration file for `ulimit` named `/etc/security/limits.d/limits.conf`. The [audit script](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/4.1_restrict_core_dumps.sh) checking for this does not check the `limits.d` subdirectory, which is why this is incorrectly flagged as noncompliant. + +**5.4 Ensure ctrl-alt-del is disabled** - Hedgehog Linux disables the `ctrl+alt+delete` key sequence by executing `systemctl disable ctrl-alt-del.target` during installation and the command `systemctl mask ctrl-alt-del.target` at boot time. + +**7.4.4 Create /etc/hosts.deny**, **7.7.1 Ensure Firewall is active**, **7.7.4.1 Ensure default deny firewall policy**, **7.7.4.2 Ensure loopback traffic is configured**, **7.7.4.3 Ensure default deny firewall policy**, **7.7.4.4 Ensure outbound and established connections are configured** - Hedgehog Linux **is** configured with an appropriately locked-down software firewall (managed by "Uncomplicated Firewall" `ufw`). However, the methods outlined in the CIS benchmark recommendations do not account for this configuration. + +**8.6 Verifies integrity all packages** - The [script](https://github.com/hardenedlinux/harbian-audit/blob/master/bin/hardening/8.7_verify_integrity_packages.sh) which verifies package integrity only "fails" because of missing (status `??5??????` displayed by the utility) language ("locale") files, which are removed as part of Hedgehog Linux's trimming-down process. All non-locale-related system files pass intergrity checks. \ No newline at end of file diff --git a/docs/hedgehog-installation.md b/docs/hedgehog-installation.md new file mode 100644 index 000000000..135b954dc --- /dev/null +++ b/docs/hedgehog-installation.md @@ -0,0 +1,41 @@ +# Sensor installation + +## Image boot options + +The Hedgehog Linux installation image, when provided on an optical disc, USB thumb drive, or other removable medium, can be used to install or reinstall the sensor software. + +![Sensor installation image boot menu](./images/hedgehog/images/boot_options.png) + +The boot menu of the sensor installer image provides several options: + +* **Live system** and **Live system (fully in RAM)** may also be used to run the sensor in a "live USB" mode without installing any software or making any persistent configuration changes on the sensor hardware. +* **Install Hedgehog Linux** and **Install Hedgehog Linux (encrypted)** are used to [install the sensor](#HedgehogInstaller) onto the current system. Both selections install the same operating system and sensor software, the only difference being that the **encrypted** option encrypts the hard disks with a password (provided in a subsequent step during installation) that must be provided each time the sensor boots. There is some CPU overhead involved in an encrypted installation, so it is recommended that encrypted installations only be used for mobile installations (eg., on a sensor that may be shipped or carried for an incident response) and that the unencrypted option be used for fixed sensors in secure environments. +* **Install Hedgehog Linux (advanced configuration)** allows you to configure installation fully using all of the [Debian installer](https://www.debian.org/releases/stable/amd64/) settings and should only be selected for advanced users who know what they're doing. +* **Rescue system** is included for debugging and/or system recovery and should not be needed in most cases. + +## Installer + +The sensor installer is designed to require as little user input as possible. For this reason, there are NO user prompts and confirmations about partitioning and reformatting hard disks for use by the sensor. The installer assumes that all non-removable storage media (eg., SSD, HDD, NVMe, etc.) are available for use and ⛔🆘😭💀 ***will partition and format them without warning*** 💀😭🆘⛔. + +The installer will ask for a few pieces of information prior to installing the sensor operating system: + +* **Root password** – a password for the privileged root account which is rarely needed (only during the configuration of the sensors network interfaces and setting the sensor host name) +* **User password** – a password for the non-privileged sensor account under which the various sensor capture and forwarding services run +* **Encryption password** (optional) – if the encrypted installation option was selected at boot time, the encryption password must be entered every time the sensor boots + +Each of these passwords must be entered twice to ensure they were entered correctly. + +![Example of the installer's password prompt](./images/hedgehog/images/users_and_passwords.png) + +After the passwords have been entered, the installer will proceed to format the system drive and install Hedgehog Linux. + +![Installer progress](./images/hedgehog/images/installer_progress.png) + +At the end of the installation process, you will be prompted with a few self-explanatory yes/no questions: + +* **Disable IPv6?** +* **Automatically login to the GUI session?** +* **Should the GUI session be locked due to inactivity?** +* **Display the [Standard Mandatory DoD Notice and Consent Banner](https://www.stigviewer.com/stig/application_security_and_development/2018-12-24/finding/V-69349)?** *(only applies when installed on U.S. government information systems)* + +Following these prompts, the installer will reboot and Hedgehog Linux will boot. \ No newline at end of file diff --git a/docs/hedgehog-iso-build.md b/docs/hedgehog-iso-build.md new file mode 100644 index 000000000..a2f00b820 --- /dev/null +++ b/docs/hedgehog-iso-build.md @@ -0,0 +1,36 @@ +# Appendix A - Generating the ISO + +Official downloads of the Hedgehog Linux installer ISO are not provided: however, it can be built easily on an internet-connected Linux host with Vagrant: + +* [Vagrant](https://www.vagrantup.com/) + - [`vagrant-reload`](https://github.com/aidanns/vagrant-reload) plugin + - [`vagrant-sshfs`](https://github.com/dustymabe/vagrant-sshfs) plugin + - [`bento/debian-11`](https://app.vagrantup.com/bento/boxes/debian-11) Vagrant box + +The build should work with either the [VirtualBox](https://www.virtualbox.org/) provider or the [libvirt](https://libvirt.org/) provider: + +* [VirtualBox](https://www.virtualbox.org/) [provider](https://www.vagrantup.com/docs/providers/virtualbox) + - [`vagrant-vbguest`](https://github.com/dotless-de/vagrant-vbguest) plugin +* [libvirt](https://libvirt.org/) + - [`vagrant-libvirt`](https://github.com/vagrant-libvirt/vagrant-libvirt) provider plugin + - [`vagrant-mutate`](https://github.com/sciurus/vagrant-mutate) plugin to convert [`bento/debian-11`](https://app.vagrantup.com/bento/boxes/debian-11) Vagrant box to `libvirt` format + +To perform a clean build the Hedgehog Linux installer ISO, navigate to your local [Malcolm]({{ site.github.repository_url }}/) working copy and run: + +``` +$ ./sensor-iso/build_via_vagrant.sh -f +… +Starting build machine... +Bringing machine 'default' up with 'virtualbox' provider... +… +``` + +Building the ISO may take 90 minutes or more depending on your system. As the build finishes, you will see the following message indicating success: + +``` +… +Finished, created "/sensor-build/hedgehog-6.4.0.iso" +… +``` + +Alternately, if you have forked Malcolm on GitHub, [workflow files]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/.github/workflows/) are provided which contain instructions for GitHub to build the docker images and Hedgehog and [Malcolm]({{ site.github.repository_url }}) installer ISOs, specifically [`sensor-iso-build-docker-wrap-push-ghcr.yml`]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/.github/workflows/sensor-iso-build-docker-wrap-push-ghcr.yml) for the Hedgehog ISO. The resulting ISO file is wrapped in a Docker image that provides an HTTP server from which the ISO may be downloaded. \ No newline at end of file diff --git a/docs/hedgehog-ssh.md b/docs/hedgehog-ssh.md new file mode 100644 index 000000000..4a5f515d4 --- /dev/null +++ b/docs/hedgehog-ssh.md @@ -0,0 +1,19 @@ +# Appendix B - Configuring SSH access + +SSH access to the sensor's non-privileged sensor account is only available using secure key-based authentication which can be enabled by adding a public SSH key to the **/home/sensor/.ssh/authorized_keys** file as illustrated below: + +``` +sensor@sensor:~$ mkdir -p ~/.ssh + +sensor@sensor:~$ ssh analyst@172.16.10.48 "cat ~/.ssh/id_rsa.pub" >> ~/.ssh/authorized_keys +The authenticity of host '172.16.10.48 (172.16.10.48)' can't be established. +ECDSA key fingerprint is SHA256:... +Are you sure you want to continue connecting (yes/no)? yes +Warning: Permanently added '172.16.10.48' (ECDSA) to the list of known hosts. +analyst@172.16.10.48's password: + +sensor@sensor:~$ cat ~/.ssh/authorized_keys +ssh-rsa AAA...kff analyst@SOC +``` + +SSH access should only be configured when necessary. \ No newline at end of file diff --git a/docs/hedgehog-troubleshooting.md b/docs/hedgehog-troubleshooting.md new file mode 100644 index 000000000..736f50d40 --- /dev/null +++ b/docs/hedgehog-troubleshooting.md @@ -0,0 +1,9 @@ +# Appendix C - Troubleshooting + +Should the sensor not function as expected, first try rebooting the device. If the behavior continues, here are a few things that may help you diagnose the problem (items which may require Linux command line use are marked with **†**) + +* Stop / start services – Using the sensor's kiosk mode, attempt a **Services Stop** followed by a **Services Start**, then check **Sensor Status** to see which service(s) may not be running correctly. +* Sensor configuration file – See `/opt/sensor/sensor_ctl/control_vars.conf` for sensor service settings. It is not recommended to manually edit this file unless you are sure of what you are doing. +* Sensor control scripts – There are scripts under ``/opt/sensor/sensor_ctl/`` to control sensor services (eg., `shutdown`, `start`, `status`, `stop`, etc.) +* Sensor debug logs – Log files under `/opt/sensor/sensor_ctl/log/` may contain clues to processes that are not working correctly. If you can determine which service is failing, you can attempt to reconfigure it using the instructions in the Configure Capture and Forwarding section of this document. +* `sensorwatch` script – Running `sensorwatch` on the command line will display the most recently modified PCAP and Zeek log files in their respective directories, how much storage space they are consuming, and the amount of used/free space on the volumes containing those files. \ No newline at end of file diff --git a/docs/hedgehog-upgrade.md b/docs/hedgehog-upgrade.md new file mode 100644 index 000000000..512b9af33 --- /dev/null +++ b/docs/hedgehog-upgrade.md @@ -0,0 +1,330 @@ +# Appendix E - Upgrades + +At this time there is not an "official" upgrade procedure to get from one release of Hedgehog Linux to the next. Upgrading the underlying operating system packages is generally straightforward, but not all of the Hedgehog Linux components are packaged into .deb archives yet as they should be, so for now it's a manual (and kind of nasty) process to Frankenstein an upgrade into existance. The author of this project intends to remedy this at some future point when time and resources allow. + +If possible, it would save you **a lot** of trouble to just [re-ISO](hedgehog-installation.md#HedgehogInstallation) your Hedgehog installation and start fresh, backing up the files (in `/opt/sensor/sensor_ctl`) first and reconfiguring or restoring them as needed afterwards. + +However, if reinstalling the system is not an option, here is the basic process for doing a manual upgrade of Hedgehog Linux. It should be understood that this process is very likely to break your system, and there is **no** guarantee of any kind that any of this will work, or that these instructions are even complete or any support whatsoever regarding them. Really, it will be **much** easier if you re-ISO your installation. But for the brave among you, here you go. ⛔🆘😭💀 + +## Prerequisites + +* A good understanding of the Linux command line +* An existing installation of Hedgehog Linux **with internet access** +* A copy of the Hedgehog Linux [ISO](hedgehog-iso-build.md#HedgehogISOBuild) for the version approximating the one you're upgrading to (i.e., the latest version), **and** + - Either a separate VM with that ISO installed **OR** + - A separate Linux workstation where you can manually mount that ISO to pull stuff off of it + +## Upgrade + +1. Obtain a root shell + - `su -` + +2. Temporarily set the umask value to Debian default instead of the more restrictive Hedgehog Linux default. This will allow updates to be applied with the right permissions. + - `umask 0022` + +3. Create backups of some files + - `cp /etc/apt/sources.list /etc/apt/sources.list.bak` + +4. Set up alternate package sources, if needed + - In an offline/airgapped scenario, you could use [apt-mirror](https://apt-mirror.github.io) to mirror Debian repos and [bandersnatch](https://github.com/pypa/bandersnatch/) to mirror PyPI sources, or [combine them](https://github.com/mmguero/espejo) with Docker. If you were to do this, you'd probably want to make the following changes (and **revert them after the upgrade**): + + create `/etc/apt/apt.conf.d/80ssl-exceptions` to ignore self-signed certificate warnings from using your apt-mirror +``` +Acquire::https { + Verify-Peer "false"; + Verify-Host "false"; +} +``` + + + modify `/etc/apt/source.list` to point to your apt-mirror: + +``` +deb https://XXXXXX:443/debian buster main contrib non-free +deb https://XXXXXX:443/debian-security buster/updates main contrib non-free +deb https://XXXXXX:443/debian buster-updates main contrib non-free +deb https://XXXXXX:443/debian buster-backports main contrib non-free +``` + +5. Update underlying system packages with `apt-get` + - `apt-get update && apt-get dist-upgrade` + +6. If there were [new system deb packages added]({{ site.github.repository_url }}/tree/{{ site.github.build_revision }}/sensor-iso/config/package-lists) to this release of Hedgehog Linux (you might have to [manually compare]({{ site.github.repository_url }}/commits/main/sensor-iso/config/package-lists) on GitHub), install them. If you're not sure, of course, you could just install everything, like this (although you may have to tweak some version numbers or something if the base distribution of your Hedgehog branch is different than `main`; in this example I'm not jumping between Debian releases, just upgrading within a release): +``` +$ for LIST in apps desktopmanager net system; do curl -L -J -O {{ site.github.repository_url }}/main/sensor-iso/config/package-lists/$LIST.list.chroot; done +... +$ apt-get install $(cat *.list.chroot) +``` + +7. Update underlying python packages with `python3 -m pip` + * `apt-get install -y build-essential git-core pkg-config python3-dev` + * `python3 -m pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -r -n1 python3 -m pip install -U` + - if this fails for some reason, you may need to reinstall pip first with `python3 -m pip install --force -U pip` + - some *very* old builds of Hedgehog Linux had separate Python 3.5 and 3.7 installations: in this case, you'd need to do this for both `python3 -m pip` and `python3.7 -m pip` (or whatever `python3.x` you have) + * If there were [new python packages](https://raw.githubusercontent.com/{{ site.github.repository_nwo }}/master/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot) added to this release of Hedgehog Linux (you might have to [manually compare]({{ site.github.repository_url }}/blame/main/sensor-iso/config/hooks/normal/0169-pip-installs.hook.chroot) on GitHub), install them. If you are using a PyPI mirror, replace `XXXXXX` here with your mirror's IP. The `colorama` package is used here as an example, your package list might vary. + - `python3 -m pip install --no-compile --no-cache-dir --force-reinstall --upgrade --index-url=https://XXXXXX:443/pypi/simple --trusted-host=XXXXXX:443 colorama` + +8. Okay, **now** things start to get a little bit ugly. You're going to need access to the ISO of the release of Hedgehog Linux you're upgrading to, as we're going to grab some packages off of it. On another Linux system, [build it](hedgehog-iso-build.md#HedgehogISOBuild). + +9. Use a disk image mounter to mount the ISO, **or** if you want to just install the ISO in a VM and grab the files we need off of it, that's fine too. But I'll go through the example as if I've mounted the ISO. + +10. Navigate to the `/live/` directory, and mount the `filesystem.squashfs` file + - `sudo mount filesystem.squashfs /media/squash -t squashfs -o loop` + - **OR** + - `squashfuse filesystem.squashfs /home/user/media/squash` + +11. Very recent builds of Hedgehog Linux keep some build artifacts in `/opt/hedgehog_install_artifacts/`. You're going to want to grab those files and throw them in a temporary directory on the system you're upgrading, via SSH or whatever means you devise. +``` +root@hedgehog:/tmp# scp -r user@otherbox:/media/squash/opt/hedgehog_install_artifacts/ ./ +user@otherbox's password: +filebeat-tweaked-7.6.2-amd64.deb 100% 13MB 65.9MB/s 00:00 +arkime_2.2.3-1_amd64.deb 100% 113MB 32.2MB/s 00:03 +netsniff-ng_0.6.6-1_amd64.deb 100% 330KB 52.1MB/s 00:00 +zeek_3.0.20-1_amd64.deb 100% 26MB 63.1MB/s 00:00 +``` + +12. Blow away the old `zeek` package, we're going to start clean with that one particularly. The others should be fine to upgrade in place. +``` +root@hedgehog:/opt# apt-get --purge remove zeek +Reading package lists... Done +Building dependency tree +Reading state information... Done +The following packages will be REMOVED: + zeek* +0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded. +After this operation, 160 MB disk space will be freed. +Do you want to continue? [Y/n] y +(Reading database ... 118490 files and directories currently installed.) +Removing zeek (3.0.20-1) ... +dpkg: warning: while removing zeek, directory '/opt/zeek/spool' not empty so not removed +dpkg: warning: while removing zeek, directory '/opt/zeek/share/zeek/site' not empty so not removed +dpkg: warning: while removing zeek, directory '/opt/zeek/lib' not empty so not removed +dpkg: warning: while removing zeek, directory '/opt/zeek/bin' not empty so not removed +root@hedgehog:/opt# rm -rf /opt/zeek* +``` + +13. Install the new .deb files. You're going to have some warnings, but that's okay. +``` +root@hedgehog:/tmp# dpkg -i hedgehog_install_artifacts/*.deb +(Reading database ... 118149 files and directories currently installed.) +Preparing to unpack .../filebeat-tweaked-7.6.2-amd64.deb ... +Unpacking filebeat (7.6.2) over (6.8.4) ... +dpkg: warning: unable to delete old directory '/usr/share/filebeat/kibana/6/dashboard': Directory not empty +dpkg: warning: unable to delete old directory '/usr/share/filebeat/kibana/6': Directory not empty +Preparing to unpack .../arkime_2.2.3-1_amd64.deb ... +Unpacking arkime (2.2.3-1) over (2.0.1-1) ... +Preparing to unpack .../netsniff-ng_0.6.6-1_amd64.deb ... +Unpacking netsniff-ng (0.6.6-1) over (0.6.6-1) ... +Preparing to unpack .../zeek_3.0.20-1_amd64.deb ... +Unpacking zeek (3.0.20-1) over (3.0.0-1) ... +Setting up filebeat (7.6.2) ... +Installing new version of [...] +[...] +Setting up arkime (2.2.3-1) ... +READ /opt/arkime/README.txt and RUN /opt/arkime/bin/Configure +Setting up netsniff-ng (0.6.6-1) ... +Setting up zeek (3.0.20-1) ... +Processing triggers for systemd (232-25+deb9u12) ... +Processing triggers for man-db (2.7.6.1-2) ... +``` + +14. Fix anything that might need fixing as far as the deb package requirements go + - `apt-get -f install` + +15. We just installed a Zeek .deb, but the third-part plugins packages and local config weren't part of that package. So we're going to `rsync` those from the other box where we have the ISO and `filesystem.squashfs` mounted as well: +``` +root@hedgehog:/tmp# rsync -a user@otherbox:/media/squash/opt/zeek/ /opt/zeek +user@otherbox's password: + +root@hedgehog:/tmp# ls -l /opt/zeek/share/zeek/site/ +total 52 +lrwxrwxrwx 1 root root 13 May 6 21:52 bzar -> packages/bzar +lrwxrwxrwx 1 root root 22 May 6 21:50 cve-2020-0601 -> packages/cve-2020-0601 +-rw-r--r-- 1 root root 2031 Apr 30 16:02 extractor.zeek +-rw-r--r-- 1 root root 39134 May 1 14:20 extractor_params.zeek +lrwxrwxrwx 1 root root 14 May 6 21:52 hassh -> packages/hassh +lrwxrwxrwx 1 root root 12 May 6 21:52 ja3 -> packages/ja3 +-rw-rw-r-- 1 root root 2005 May 6 21:54 local.zeek +drwxr-xr-x 13 root root 4096 May 6 21:52 packages +lrwxrwxrwx 1 root root 27 May 6 21:52 zeek-EternalSafety -> packages/zeek-EternalSafety +lrwxrwxrwx 1 root root 26 May 6 21:52 zeek-community-id -> packages/zeek-community-id +lrwxrwxrwx 1 root root 27 May 6 21:51 zeek-plugin-bacnet -> packages/zeek-plugin-bacnet +lrwxrwxrwx 1 root root 25 May 6 21:51 zeek-plugin-enip -> packages/zeek-plugin-enip +lrwxrwxrwx 1 root root 29 May 6 21:51 zeek-plugin-profinet -> packages/zeek-plugin-profinet +lrwxrwxrwx 1 root root 27 May 6 21:52 zeek-plugin-s7comm -> packages/zeek-plugin-s7comm +lrwxrwxrwx 1 root root 24 May 6 21:52 zeek-plugin-tds -> packages/zeek-plugin-tds +``` + +16. The `zeekctl` component of zeek doesn't like being run by an unprivileged user unless the whole directory is owned by that user. As Hedgehog Linux runs everything it can as an unprivileged user, we're going to reset zeek to a "clean" state after each reboot. Zeek's config files will get regenerated when Zeek itself is started. So, now make a complete backup of `/opt/zeek` as it's going to have its ownership changed during runtime: +``` +root@hedgehog:/tmp# rsync -a /opt/zeek/ /opt/zeek.orig + +root@hedgehog:/tmp# chown -R sensor:sensor /opt/zeek/* + +root@hedgehog:/tmp# chown -R root:root /opt/zeek.orig/* + +root@hedgehog:/tmp# ls -l /opt/ | grep zeek +drwxr-xr-x 8 root root 4096 May 8 15:48 zeek +drwxr-xr-x 8 root root 4096 May 8 15:48 zeek.orig +``` + +17. Grab other new scripts and stuff from our mount of the ISO using `rsync`: +``` +root@hedgehog:/tmp# rsync -a user@otherbox:/media/squash/usr/local/bin/ /usr/local/bin +user@otherbox's password: + +root@hedgehog:/tmp# ls -l /usr/local/bin/ | tail +lrwxrwxrwx 1 root root 18 May 8 14:34 zeek -> /opt/zeek/bin/zeek +-rwxr-xr-x 1 root staff 10349 Oct 29 2019 zeek_carve_logger.py +-rwxr-xr-x 1 root staff 10467 Oct 29 2019 zeek_carve_scanner.py +-rw-r--r-- 1 root staff 25756 Oct 29 2019 zeek_carve_utils.py +-rwxr-xr-x 1 root staff 8787 Oct 29 2019 zeek_carve_watcher.py +-rwxr-xr-x 1 root staff 4883 May 4 17:39 zeek_install_plugins.sh + +root@hedgehog:/tmp# rsync -a user@otherbox:/media/squash/opt/yara-rules/ /opt/yara-rules +user@otherbox's password: + +root@hedgehog:/tmp# rsync -a user@otherbox:/media/squash/opt/capa-rules/ /opt/capa-rules +user@otherbox's password: + +root@hedgehog:/tmp# ls -l /opt/ | grep '\-rules' +drwxr-xr-x 8 root root 4096 May 8 15:48 capa-rules +drwxr-xr-x 8 root root 24576 May 8 15:48 yara-rules + +root@hedgehog:/tmp# for BEAT in filebeat; do rsync -a user@otherbox:/media/squash/usr/share/$BEAT/kibana/ /usr/share/$BEAT/kibana; done +user@otherbox's password: +user@otherbox's password: + +root@hedgehog:/tmp# rsync -avP --delete user@otherbox:/media/squash/etc/audit/rules.d/ /etc/audit/rules.d/ +user@otherbox's password: + +root@hedgehog:/tmp# rsync -avP --delete user@otherbox:/media/squash/etc/sudoers.d/ /etc/sudoers.d/ +user@otherbox's password: + +root@hedgehog:/tmp# chmod 400 /etc/sudoers.d/* +``` + +18. Set capabilities and symlinks for network capture programs to be used by the unprivileged user: + +commands: + +``` +chown root:netdev /usr/sbin/netsniff-ng && \ + setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_IPC_LOCK+eip CAP_SYS_ADMIN+eip' /usr/sbin/netsniff-ng +chown root:netdev /opt/zeek/bin/zeek && \ + setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_IPC_LOCK+eip' /opt/zeek/bin/zeek +chown root:netdev /sbin/ethtool && \ + setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /sbin/ethtool +chown root:netdev /opt/zeek/bin/capstats && \ + setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /opt/zeek/bin/capstats +chown root:netdev /usr/bin/tcpdump && \ + setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /usr/bin/tcpdump +chown root:netdev /opt/arkime/bin/capture && \ + setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_IPC_LOCK+eip' /opt/arkime/bin/capture + +ln -s -f /opt/zeek/bin/zeek /usr/local/bin/ +ln -s -f /usr/sbin/netsniff-ng /usr/local/bin/ +ln -s -f /usr/bin/tcpdump /usr/local/bin/ +ln -s -f /opt/arkime/bin/capture /usr/local/bin/ +ln -s -f /opt/arkime/bin/npm /usr/local/bin +ln -s -f /opt/arkime/bin/node /usr/local/bin +ln -s -f /opt/arkime/bin/npx /usr/local/bin +``` + +example: + +``` +root@hedgehog:/tmp# chown root:netdev /usr/sbin/netsniff-ng && \ +> setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_IPC_LOCK+eip CAP_SYS_ADMIN+eip' /usr/sbin/netsniff-ng +root@hedgehog:/tmp# chown root:netdev /opt/zeek/bin/zeek && \ +> setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_IPC_LOCK+eip' /opt/zeek/bin/zeek +root@hedgehog:/tmp# chown root:netdev /sbin/ethtool && \ +> setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /sbin/ethtool +root@hedgehog:/tmp# chown root:netdev /opt/zeek/bin/capstats && \ +> setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /opt/zeek/bin/capstats +root@hedgehog:/tmp# chown root:netdev /usr/bin/tcpdump && \ +> setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /usr/bin/tcpdump +root@hedgehog:/tmp# chown root:netdev /opt/arkime/bin/capture && \ +> setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip CAP_IPC_LOCK+eip' /opt/arkime/bin/capture +root@hedgehog:/tmp# ln -s -f /opt/zeek/bin/zeek /usr/local/bin/ +root@hedgehog:/tmp# ln -s -f /usr/sbin/netsniff-ng /usr/local/bin/ +root@hedgehog:/tmp# ln -s -f /usr/bin/tcpdump /usr/local/bin/ +root@hedgehog:/tmp# ln -s -f /opt/arkime/bin/capture /usr/local/bin/ +root@hedgehog:/tmp# ln -s -f /opt/arkime/bin/npm /usr/local/bin +root@hedgehog:/tmp# ln -s -f /opt/arkime/bin/node /usr/local/bin +root@hedgehog:/tmp# ln -s -f /opt/arkime/bin/npx /usr/local/bin +``` + +19. Back up unprivileged user sensor-specific config and scripts: + - `mv /opt/sensor/ /opt/sensor_upgrade_backup_$(date +%Y-%m-%d)` + +20. Grab unprivileged user sensor-specific config and scripts from our mount of the ISO using `rsync` and change its ownership to the unprivileged user: +``` +root@hedgehog:/tmp# rsync -av user@otherbox:/media/squash/opt/sensor /opt/ +user@otherbox's password: +receiving incremental file list +created directory ./opt +sensor/ +[...] + +sent 1,244 bytes received 1,646,409 bytes 470,758.00 bytes/sec +total size is 1,641,629 speedup is 1.00 + +root@hedgehog:/tmp# chown -R sensor:sensor /opt/sensor* + +root@hedgehog:/tmp# ls -l /opt/ | grep sensor +drwxr-xr-x 4 sensor sensor 4096 May 6 22:00 sensor +drwxr-x--- 4 sensor sensor 4096 May 8 14:33 sensor_upgrade_backup_2020-05-08 +``` + +21. Leave the root shell and `cd` to `/opt` +``` +root@hedgehog:~# exit +logout + +sensor@hedgehog:~$ whoami +sensor + +sensor@hedgehog:~$ cd /opt +``` + +22. Compare the old and new `control_vars.conf` files +``` +sensor@hedgehog:opt$ diff sensor_upgrade_backup_2020-05-08/sensor_ctl/control_vars.conf sensor/sensor_ctl/control_vars.conf +1,2c1,2 +< export CAPTURE_INTERFACE=enp0s3 +< export CAPTURE_FILTER="not port 5044 and not port 5601 and not port 8005 and not port 9200 and not port 9600" +--- +> export CAPTURE_INTERFACE=xxxx +> export CAPTURE_FILTER="" +4c4 +[...] +``` + +Examine the differences. If there aren't any new `export` variables, then you're probably safe to just replace the default version of `control_vars.conf` with the backed-up version: + +``` +sensor@hedgehog:opt$ cp sensor_upgrade_backup_2020-05-08/sensor_ctl/control_vars.conf sensor/sensor_ctl/control_vars.conf +cp: overwrite 'sensor/sensor_ctl/control_vars.conf'? y +``` + +If there are major differences or new variables, continue on to the next step, in a minute you'll need to run `capture-config` to configure from scratch anyway. + +23. Restore certificates/keystores for forwarders from the backup `sensor_ctl` path to the new one +``` +sensor@hedgehog:opt$ for BEAT in filebeat miscbeat; do cp /opt/sensor_upgrade_backup_2020-05-08/sensor_ctl/$BEAT/data/* /opt/sensor/sensor_ctl/$BEAT/data/; done + +sensor@hedgehog:opt$ cp /opt/sensor_upgrade_backup_2020-05-07/sensor_ctl/filebeat/{ca.crt,client.crt,client.key} /opt/sensor/sensor_ctl/logstash-client-certificates/ +``` + +24. Despite what we just did, you may consider running `capture-config` to re-configure [capture, forwarding, and autostart services](hedgehog-config-user.md#HedgehogConfigUser) from scratch anyway. You can use the backed-up version of `control_vars.conf` to refer back to as a basis for things you might want to restore (e.g., `CAPTURE_INTERFACE`, `CAPTURE_FILTER`, `PCAP_PATH`, `ZEEK_LOG_PATH`, your autostart settings, etc.). + +25. Once you feel confident you've completed all of these steps, issue a reboot on the Hedgehog + +## Post-upgrade + +Once the Hedgehog has come back up, check to make sure everything is working: + +* `/opt/sensor/sensor_ctl/status` should return `RUNNING` for the things you set to autorun (no `FATAL` errors) +* `sensorwatch` should show current writes to Zeek log files and PCAP files (depending on your configuration) +* `tail -f /opt/sensor/sensor_ctl/log/*` should show no egregious errors +* `zeek --version`, `zeek -N local` and `capture --version` ought to run and print out version information as expected +* if you are forwarding to a [Malcolm]({{ site.github.repository_url }}) aggregator, you should start seeing data momentarily \ No newline at end of file diff --git a/docs/hedgehog.md b/docs/hedgehog.md new file mode 100644 index 000000000..e0493a3e9 --- /dev/null +++ b/docs/hedgehog.md @@ -0,0 +1,41 @@ +# Hedgehog Linux + +**Network Traffic Capture Appliance** + +![](./images/hedgehog/logo/hedgehog-color-w-text.png) + +Hedgehog Linux is a Debian-based operating system built to + +* monitor network interfaces +* capture packets to PCAP files +* detect file transfers in network traffic and extract and scan those files for threats +* generate and forward Zeek logs, Arkime sessions and other information to [Malcolm]({{ site.github.repository_url }}) + +![sensor-iso-build-docker-wrap-push-ghcr]({{ site.github.repository_url }}/workflows/sensor-iso-build-docker-wrap-push-ghcr/badge.svg) + + +* [Sensor installation](hedgehog-installation.md#HedgehogInstallation) + - [Image boot options](hedgehog-installation.md#HedgehogBootOptions) + - [Installer](hedgehog-installation.md#HedgehogInstaller) +* [Boot](hedgehog-boot.md#HedgehogBoot) + - [Kiosk mode](hedgehog-boot.md#HedgehogKioskMode) +* [Configuration](hedgehog-config.md#HedgehogConfiguration) + - [Interfaces, hostname, and time synchronization](hedgehog-config-root.md#HedgehogConfigRoot) + + [Hostname](hedgehog-config-root.md#HedgehogConfigHostname) + + [Interfaces](hedgehog-config-root.md#HedgehogConfigIface) + + [Time synchronization](hedgehog-config-root.md#HedgehogConfigTime) + - [Capture, forwarding, and autostart services](hedgehog-config-user.md#HedgehogConfigUser) + + [Capture](hedgehog-config-user.md#HedgehogConfigCapture) + * [Automatic file extraction and scanning](hedgehog-config-user.md#HedgehogZeekFileExtraction) + + [Forwarding](hedgehog-config-user.md#HedgehogConfigForwarding) + * [arkime-capture](hedgehog-config-user.md#Hedgehogarkime-capture): Arkime session forwarding + * [filebeat](hedgehog-config-user.md#Hedgehogfilebeat): Zeek and Suricata log forwarding + * [miscbeat](hedgehog-config-user.md#Hedgehogmiscbeat): System metrics forwarding + + [Autostart services](hedgehog-config-user.md#HedgehogConfigAutostart) ++ [Zeek Intelligence Framework](hedgehog-config-zeek-intel.md#HedgehogZeekIntel) +* [Appendix A - Generating the ISO](hedgehog-iso-build.md#HedgehogISOBuild) +* [Appendix B - Configuring SSH access](hedgehog-ssh.md#HedgehogConfigSSH) +* [Appendix C - Troubleshooting](hedgehog-troubleshooting.md#HedgehogTroubleshooting) +* [Appendix D - Hardening](hedgehog-hardening.md#HedgehogHardening) + - [Compliance exceptions](hedgehog-hardening.md#HedgehogComplianceExceptions) +* [Appendix E - Upgrades](hedgehog-upgrade.md#HedgehogUpgradePlan) diff --git a/docs/host-and-subnet-mapping.md b/docs/host-and-subnet-mapping.md new file mode 100644 index 000000000..c220a1622 --- /dev/null +++ b/docs/host-and-subnet-mapping.md @@ -0,0 +1,94 @@ +# Automatic host and subnet name assignment + +* [Automatic host and subnet name assignment](host-and-subnet-mapping.md#HostAndSubnetNaming) + - [IP/MAC address to hostname mapping via `host-map.txt`](host-and-subnet-mapping.md#HostNaming) + - [CIDR subnet to network segment name mapping via `cidr-map.txt`](host-and-subnet-mapping.md#SegmentNaming) + - [Defining hostname and CIDR subnet names interface](host-and-subnet-mapping.md#NameMapUI) + - [Applying mapping changes](host-and-subnet-mapping.md#ApplyMapping) + +## IP/MAC address to hostname mapping via `host-map.txt` + +The `host-map.txt` file in the Malcolm installation directory can be used to define names for network hosts based on IP and/or MAC addresses in Zeek logs. The default empty configuration looks like this: +``` +# IP or MAC address to host name map: +# address|host name|required tag +# +# where: +# address: comma-separated list of IPv4, IPv6, or MAC addresses +# e.g., 172.16.10.41, 02:42:45:dc:a2:96, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 +# +# host name: host name to be assigned when event address(es) match +# +# required tag (optional): only check match and apply host name if the event +# contains this tag +# +``` +Each non-comment line (not beginning with a `#`), defines an address-to-name mapping for a network host. For example: +``` +127.0.0.1,127.0.1.1,::1|localhost| +192.168.10.10|office-laptop.intranet.lan| +06:46:0b:a6:16:bf|serial-host.intranet.lan|testbed +``` +Each line consists of three `|`-separated fields: address(es), hostname, and, optionally, a tag which, if specified, must belong to a log for the matching to occur. + +As Zeek logs are processed into Malcolm's OpenSearch instance, the log's source and destination IP and MAC address fields (`source.ip`, `destination.ip`, `source.mac`, and `destination.mac`, respectively) are compared against the lists of addresses in `host-map.txt`. When a match is found, a new field is added to the log: `source.hostname` or `destination.hostname`, depending on whether the matching address belongs to the originating or responding host. If the third field (the "required tag" field) is specified, a log must also contain that value in its `tags` field in addition to matching the IP or MAC address specified in order for the corresponding `_hostname` field to be added. + +`source.hostname` and `destination.hostname` may each contain multiple values. For example, if both a host's source IP address and source MAC address were matched by two different lines, `source.hostname` would contain the hostname values from both matching lines. + +## CIDR subnet to network segment name mapping via `cidr-map.txt` + +The `cidr-map.txt` file in the Malcolm installation directory can be used to define names for network segments based on IP addresses in Zeek logs. The default empty configuration looks like this: +``` +# CIDR to network segment format: +# IP(s)|segment name|required tag +# +# where: +# IP(s): comma-separated list of CIDR-formatted network IP addresses +# e.g., 10.0.0.0/8, 169.254.0.0/16, 172.16.10.41 +# +# segment name: segment name to be assigned when event IP address(es) match +# +# required tag (optional): only check match and apply segment name if the event +# contains this tag +# +``` +Each non-comment line (not beginning with a `#`), defines an subnet-to-name mapping for a network host. For example: +``` +192.168.50.0/24,192.168.40.0/24,10.0.0.0/8|corporate| +192.168.100.0/24|control| +192.168.200.0/24|dmz| +172.16.0.0/12|virtualized|testbed +``` +Each line consists of three `|`-separated fields: CIDR-formatted subnet IP range(s), subnet name, and, optionally, a tag which, if specified, must belong to a log for the matching to occur. + +As Zeek logs are processed into Malcolm's OpenSearch instance, the log's source and destination IP address fields (`source.ip` and `destination.ip`, respectively) are compared against the lists of addresses in `cidr-map.txt`. When a match is found, a new field is added to the log: `source.segment` or `destination.segment`, depending on whether the matching address belongs to the originating or responding host. If the third field (the "required tag" field) is specified, a log must also contain that value in its `tags` field in addition to its IP address falling within the subnet specified in order for the corresponding `_segment` field to be added. + +`source.segment` and `destination.segment` may each contain multiple values. For example, if `cidr-map.txt` specifies multiple overlapping subnets on different lines, `source.segment` would contain the hostname values from both matching lines if `source.ip` belonged to both subnets. + +If both `source.segment` and `destination.segment` are added to a log, and if they contain different values, the tag `cross_segment` will be added to the log's `tags` field for convenient identification of cross-segment traffic. This traffic could be easily visualized using Arkime's **Connections** graph, by setting the **Src:** value to **Originating Network Segment** and the **Dst:** value to **Responding Network Segment**: + +![Cross-segment traffic in Connections](./images/screenshots/arkime_connections_segments.png) + +## Defining hostname and CIDR subnet names interface + +As an alternative to manually editing `cidr-map.txt` and `host-map.txt`, a **Host and Subnet Name Mapping** editor is available at [https://localhost/name-map-ui/](https://localhost/name-map-ui/) if you are connecting locally. Upon loading, the editor is populated from `cidr-map.txt`, `host-map.txt` and `net-map.json`. + +This editor provides the following controls: + +* 🔎 **Search mappings** - narrow the list of visible items using a search filter +* **Type**, **Address**, **Name** and **Tag** *(column headings)* - sort the list of items by clicking a column header +* 📝 *(per item)* - modify the selected item +* 🚫 *(per item)* - remove the selected item +* 🖳 **host** / 🖧 **segment**, **Address**, **Name**, **Tag (optional)** and 💾 - save the item with these values (either adding a new item or updating the item being modified) +* 📥 **Import** - clear the list and replace it with the contents of an uploaded `net-map.json` file +* 📤 **Export** - format and download the list as a `net-map.json` file +* 💾 **Save Mappings** - format and store `net-map.json` in the Malcolm directory (replacing the existing `net-map.json` file) +* 🔁 **Restart Logstash** - restart log ingestion, parsing and enrichment + +![Host and Subnet Name Mapping Editor](./images/screenshots/malcolm_name_map_ui.png) + +## Applying mapping changes + +When changes are made to either `cidr-map.txt`, `host-map.txt` or `net-map.json`, Malcolm's Logstash container must be restarted. The easiest way to do this is to restart malcolm via `restart` (see [Stopping and restarting Malcolm](running.md#StopAndRestart)) or by clicking the 🔁 **Restart Logstash** button in the [name mapping interface](#NameMapUI) interface. + +Restarting Logstash may take several minutes, after which log ingestion will be resumed. \ No newline at end of file diff --git a/docs/host-config-linux.md b/docs/host-config-linux.md new file mode 100644 index 000000000..db5c65db7 --- /dev/null +++ b/docs/host-config-linux.md @@ -0,0 +1,91 @@ +# Linux host system configuration + +## Installing Docker + +Docker installation instructions vary slightly by distribution. Please follow the links below to docker.com to find the instructions specific to your distribution: + +* [Ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/) +* [Debian](https://docs.docker.com/install/linux/docker-ce/debian/) +* [Fedora](https://docs.docker.com/install/linux/docker-ce/fedora/) +* [CentOS](https://docs.docker.com/install/linux/docker-ce/centos/) +* [Binaries](https://docs.docker.com/install/linux/docker-ce/binaries/) + +After installing Docker, because Malcolm should be run as a non-root user, add your user to the `docker` group with something like: +``` +$ sudo usermod -aG docker yourusername +``` + +Following this, either reboot or log out then log back in. + +Docker starts automatically on DEB-based distributions. On RPM-based distributions, you need to start it manually or enable it using the appropriate `systemctl` or `service` command(s). + +You can test docker by running `docker info`, or (assuming you have internet access), `docker run --rm hello-world`. + +## Installing docker-compose + +Please follow [this link](https://docs.docker.com/compose/install/) on docker.com for instructions on installing docker-compose. + +## Operating system configuration + +The host system (ie., the one running Docker) will need to be configured for the [best possible OpenSearch performance](https://www.elastic.co/guide/en/elasticsearch/reference/master/system-config.html). Here are a few suggestions for Linux hosts (these may vary from distribution to distribution): + +* Append the following lines to `/etc/sysctl.conf`: + +``` +# the maximum number of open file handles +fs.file-max=2097152 + +# increase maximums for inotify watches +fs.inotify.max_user_watches=131072 +fs.inotify.max_queued_events=131072 +fs.inotify.max_user_instances=512 + +# the maximum number of memory map areas a process may have +vm.max_map_count=262144 + +# decrease "swappiness" (swapping out runtime memory vs. dropping pages) +vm.swappiness=1 + +# the maximum number of incoming connections +net.core.somaxconn=65535 + +# the % of system memory fillable with "dirty" pages before flushing +vm.dirty_background_ratio=40 + +# maximum % of dirty system memory before committing everything +vm.dirty_ratio=80 +``` + +* Depending on your distribution, create **either** the file `/etc/security/limits.d/limits.conf` containing: + +``` +# the maximum number of open file handles +* soft nofile 65535 +* hard nofile 65535 +# do not limit the size of memory that can be locked +* soft memlock unlimited +* hard memlock unlimited +``` + +**OR** the file `/etc/systemd/system.conf.d/limits.conf` containing: + +``` +[Manager] +# the maximum number of open file handles +DefaultLimitNOFILE=65535:65535 +# do not limit the size of memory that can be locked +DefaultLimitMEMLOCK=infinity +``` + +* Change the readahead value for the disk where the OpenSearch data will be stored. There are a few ways to do this. For example, you could add this line to `/etc/rc.local` (replacing `/dev/sda` with your disk block descriptor): + +``` +# change disk read-adhead value (# of blocks) +blockdev --setra 512 /dev/sda +``` + +* Change the I/O scheduler to `deadline` or `noop`. Again, this can be done in a variety of ways. The simplest is to add `elevator=deadline` to the arguments in `GRUB_CMDLINE_LINUX` in `/etc/default/grub`, then running `sudo update-grub2` + +* If you are planning on using very large data sets, consider formatting the drive containing the `opensearch` volume as XFS. + +After making all of these changes, do a reboot for good measure! \ No newline at end of file diff --git a/docs/host-config-macos.md b/docs/host-config-macos.md new file mode 100644 index 000000000..74e277887 --- /dev/null +++ b/docs/host-config-macos.md @@ -0,0 +1,36 @@ +# macOS host system configuration + +## Automatic installation using `install.py` + +The `install.py` script will attempt to guide you through the installation of Docker and Docker Compose if they are not present. If that works for you, you can skip ahead to **Configure docker daemon option** in this section. + +## Install Homebrew + +The easiest way to install and maintain docker on Mac is using the [Homebrew cask](https://brew.sh). Execute the following in a terminal. + +``` +$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" +$ brew install cask +$ brew tap homebrew/cask-versions +``` + +## Install docker-edge + +``` +$ brew cask install docker-edge +``` +This will install the latest version of docker and docker-compose. It can be upgraded later using `brew` as well: +``` +$ brew cask upgrade --no-quarantine docker-edge +``` +You can now run docker from the Applications folder. + +## Configure docker daemon option + +Some changes should be made for performance ([this link](http://markshust.com/2018/01/30/performance-tuning-docker-mac) gives a good succinct overview). + +* **Resource allocation** - For a good experience, you likely need at least a quad-core MacBook Pro with 16GB RAM and an SSD. I have run Malcolm on an older 2013 MacBook Pro with 8GB of RAM, but the more the better. Go in your system tray and select **Docker** → **Preferences** → **Advanced**. Set the resources available to docker to at least 4 CPUs and 8GB of RAM (>= 16GB is preferable). + +* **Volume mount performance** - You can speed up performance of volume mounts by removing unused paths from **Docker** → **Preferences** → **File Sharing**. For example, if you're only going to be mounting volumes under your home directory, you could share `/Users` but remove other paths. + +After making these changes, right click on the Docker 🐋 icon in the system tray and select **Restart**. \ No newline at end of file diff --git a/docs/host-config-windows.md b/docs/host-config-windows.md new file mode 100644 index 000000000..a12716b33 --- /dev/null +++ b/docs/host-config-windows.md @@ -0,0 +1,16 @@ +# Windows host system configuration + +## Installing and configuring Docker Desktop for Windows + +Installing and configuring [Docker to run under Windows](https://docs.docker.com/desktop/windows/wsl/) must be done manually, rather than through the `install.py` script as is done for Linux and macOS. + +1. Be running Windows 10, version 1903 or higher +1. Prepare your system and [install WSL](https://docs.microsoft.com/en-us/windows/wsl/install) and a Linux distribution by running `wsl --install -d Debian` in PowerShell as Administrator (these instructions are tested with Debian, but may work with other distributions) +1. Install Docker Desktop for Windows either by downloading the installer from the [official Docker site](https://hub.docker.com/editions/community/docker-ce-desktop-windows) or installing it through [chocolatey](https://chocolatey.org/packages/docker-desktop). +1. Follow the [Docker Desktop WSL 2 backend](https://docs.docker.com/desktop/windows/wsl/) instructions to finish configuration and review best practices +1. Reboot +1. Open the WSL distribution's terminal and run run `docker info` to make sure Docker is running + +## Finish Malcolm's configuration + +Once Docker is installed, configured and running as described in the previous section, run [`./scripts/install.py --configure`](malcolm-config.md#ConfigAndTuning) to finish configuration of the local Malcolm installation. Malcolm will be controlled and run from within your WSL distribution's terminal environment. \ No newline at end of file diff --git a/docs/host-config.md b/docs/host-config.md new file mode 100644 index 000000000..f92375141 --- /dev/null +++ b/docs/host-config.md @@ -0,0 +1,5 @@ +# Platform-specific Configuration + +* [Linux host system configuration](host-config-linux.md#HostSystemConfigLinux) +* [macOS host system configuration](host-config-macos.md#HostSystemConfigMac) +* [Windows host system configuration](host-config-windows.md#HostSystemConfigWindows) \ No newline at end of file diff --git a/docs/ics-best-guess.md b/docs/ics-best-guess.md new file mode 100644 index 000000000..08f5bb0c9 --- /dev/null +++ b/docs/ics-best-guess.md @@ -0,0 +1,11 @@ +# "Best Guess" Fingerprinting for ICS Protocols + +There are many ICS (industrial control systems) protocols. While Malcolm's collection of [protocol parsers](protocols.md#Protocols) includes a number of them, many, particularly those that are proprietary or less common, are unlikely to be supported with a full parser in the foreseeable future. + +In an effort to help identify more ICS traffic, Malcolm can use "best guess" method based on transport protocol (e.g., TCP or UDP) and port(s) to categorize potential traffic communicating over some ICS protocols without full parser support. This feature involves a [mapping table]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/zeek/config/guess_ics_map.txt) and a [Zeek script]({{ site.github.repository_url }}/blob/{{ site.github.build_revision }}/zeek/config/guess.zeek) to look up the transport protocol and destination and/or source port to make a best guess at whether a connection belongs to one of those protocols. These potential ICS communications are categorized by vendor where possible. + +Naturally, these lookups could produce false positives, so these connections are displayed in their own dashboard (the **Best Guess** dashboard found under the **ICS** section of Malcolm's [OpenSearch Dashboards](dashboards.md#DashboardsVisualizations) navigation pane). Values such as IP addresses, ports, or UID can be used to [pivot to other dashboards](arkime.md#ZeekArkimeFlowCorrelation) to investigate further. + +![](./images/screenshots/dashboards_bestguess.png) + +This feature is disabled by default, but it can be enabled by clearing (setting to `''`) the value of the `ZEEK_DISABLE_BEST_GUESS_ICS` environment variable in [`docker-compose.yml`](malcolm-config.md#DockerComposeYml). \ No newline at end of file diff --git a/sensor-iso/docs/images/arkime-capture-ip-port.png b/docs/images/hedgehog/images/arkime-capture-ip-port.png similarity index 100% rename from sensor-iso/docs/images/arkime-capture-ip-port.png rename to docs/images/hedgehog/images/arkime-capture-ip-port.png diff --git a/sensor-iso/docs/images/arkime_confirm.png b/docs/images/hedgehog/images/arkime_confirm.png similarity index 100% rename from sensor-iso/docs/images/arkime_confirm.png rename to docs/images/hedgehog/images/arkime_confirm.png diff --git a/sensor-iso/docs/images/autostarts.png b/docs/images/hedgehog/images/autostarts.png similarity index 100% rename from sensor-iso/docs/images/autostarts.png rename to docs/images/hedgehog/images/autostarts.png diff --git a/sensor-iso/docs/images/autostarts_confirm.png b/docs/images/hedgehog/images/autostarts_confirm.png similarity index 100% rename from sensor-iso/docs/images/autostarts_confirm.png rename to docs/images/hedgehog/images/autostarts_confirm.png diff --git a/sensor-iso/docs/images/boot_options.png b/docs/images/hedgehog/images/boot_options.png similarity index 100% rename from sensor-iso/docs/images/boot_options.png rename to docs/images/hedgehog/images/boot_options.png diff --git a/sensor-iso/docs/images/capture_config_main.png b/docs/images/hedgehog/images/capture_config_main.png similarity index 100% rename from sensor-iso/docs/images/capture_config_main.png rename to docs/images/hedgehog/images/capture_config_main.png diff --git a/sensor-iso/docs/images/capture_filter.png b/docs/images/hedgehog/images/capture_filter.png similarity index 100% rename from sensor-iso/docs/images/capture_filter.png rename to docs/images/hedgehog/images/capture_filter.png diff --git a/sensor-iso/docs/images/capture_iface_select.png b/docs/images/hedgehog/images/capture_iface_select.png similarity index 100% rename from sensor-iso/docs/images/capture_iface_select.png rename to docs/images/hedgehog/images/capture_iface_select.png diff --git a/sensor-iso/docs/images/capture_paths.png b/docs/images/hedgehog/images/capture_paths.png similarity index 100% rename from sensor-iso/docs/images/capture_paths.png rename to docs/images/hedgehog/images/capture_paths.png diff --git a/sensor-iso/docs/images/desktop.png b/docs/images/hedgehog/images/desktop.png similarity index 100% rename from sensor-iso/docs/images/desktop.png rename to docs/images/hedgehog/images/desktop.png diff --git a/sensor-iso/docs/images/file_quarantine.png b/docs/images/hedgehog/images/file_quarantine.png similarity index 100% rename from sensor-iso/docs/images/file_quarantine.png rename to docs/images/hedgehog/images/file_quarantine.png diff --git a/sensor-iso/docs/images/filebeat_certs.png b/docs/images/hedgehog/images/filebeat_certs.png similarity index 100% rename from sensor-iso/docs/images/filebeat_certs.png rename to docs/images/hedgehog/images/filebeat_certs.png diff --git a/sensor-iso/docs/images/filebeat_confirm.png b/docs/images/hedgehog/images/filebeat_confirm.png similarity index 100% rename from sensor-iso/docs/images/filebeat_confirm.png rename to docs/images/hedgehog/images/filebeat_confirm.png diff --git a/sensor-iso/docs/images/filebeat_ip_port.png b/docs/images/hedgehog/images/filebeat_ip_port.png similarity index 100% rename from sensor-iso/docs/images/filebeat_ip_port.png rename to docs/images/hedgehog/images/filebeat_ip_port.png diff --git a/sensor-iso/docs/images/filebeat_log_path.png b/docs/images/hedgehog/images/filebeat_log_path.png similarity index 100% rename from sensor-iso/docs/images/filebeat_log_path.png rename to docs/images/hedgehog/images/filebeat_log_path.png diff --git a/sensor-iso/docs/images/filebeat_ssl.png b/docs/images/hedgehog/images/filebeat_ssl.png similarity index 100% rename from sensor-iso/docs/images/filebeat_ssl.png rename to docs/images/hedgehog/images/filebeat_ssl.png diff --git a/sensor-iso/docs/images/filebeat_ssl_verify.png b/docs/images/hedgehog/images/filebeat_ssl_verify.png similarity index 100% rename from sensor-iso/docs/images/filebeat_ssl_verify.png rename to docs/images/hedgehog/images/filebeat_ssl_verify.png diff --git a/sensor-iso/docs/images/forwarder_config.png b/docs/images/hedgehog/images/forwarder_config.png similarity index 100% rename from sensor-iso/docs/images/forwarder_config.png rename to docs/images/hedgehog/images/forwarder_config.png diff --git a/sensor-iso/docs/images/hedgehog-color-w-text.png b/docs/images/hedgehog/images/hedgehog-color-w-text.png similarity index 100% rename from sensor-iso/docs/images/hedgehog-color-w-text.png rename to docs/images/hedgehog/images/hedgehog-color-w-text.png diff --git a/sensor-iso/docs/images/hostname_setting.png b/docs/images/hedgehog/images/hostname_setting.png similarity index 100% rename from sensor-iso/docs/images/hostname_setting.png rename to docs/images/hedgehog/images/hostname_setting.png diff --git a/sensor-iso/docs/images/htpdate_freq.png b/docs/images/hedgehog/images/htpdate_freq.png similarity index 100% rename from sensor-iso/docs/images/htpdate_freq.png rename to docs/images/hedgehog/images/htpdate_freq.png diff --git a/sensor-iso/docs/images/htpdate_host.png b/docs/images/hedgehog/images/htpdate_host.png similarity index 100% rename from sensor-iso/docs/images/htpdate_host.png rename to docs/images/hedgehog/images/htpdate_host.png diff --git a/sensor-iso/docs/images/htpdate_setup.png b/docs/images/hedgehog/images/htpdate_setup.png similarity index 100% rename from sensor-iso/docs/images/htpdate_setup.png rename to docs/images/hedgehog/images/htpdate_setup.png diff --git a/sensor-iso/docs/images/htpdate_test.png b/docs/images/hedgehog/images/htpdate_test.png similarity index 100% rename from sensor-iso/docs/images/htpdate_test.png rename to docs/images/hedgehog/images/htpdate_test.png diff --git a/sensor-iso/docs/images/iface_mode.png b/docs/images/hedgehog/images/iface_mode.png similarity index 100% rename from sensor-iso/docs/images/iface_mode.png rename to docs/images/hedgehog/images/iface_mode.png diff --git a/sensor-iso/docs/images/iface_static.png b/docs/images/hedgehog/images/iface_static.png similarity index 100% rename from sensor-iso/docs/images/iface_static.png rename to docs/images/hedgehog/images/iface_static.png diff --git a/sensor-iso/docs/images/installer_progress.png b/docs/images/hedgehog/images/installer_progress.png similarity index 100% rename from sensor-iso/docs/images/installer_progress.png rename to docs/images/hedgehog/images/installer_progress.png diff --git a/sensor-iso/docs/images/kiosk_mode_sensor_menu.png b/docs/images/hedgehog/images/kiosk_mode_sensor_menu.png similarity index 100% rename from sensor-iso/docs/images/kiosk_mode_sensor_menu.png rename to docs/images/hedgehog/images/kiosk_mode_sensor_menu.png diff --git a/sensor-iso/docs/images/kiosk_mode_services_menu.png b/docs/images/hedgehog/images/kiosk_mode_services_menu.png similarity index 100% rename from sensor-iso/docs/images/kiosk_mode_services_menu.png rename to docs/images/hedgehog/images/kiosk_mode_services_menu.png diff --git a/sensor-iso/docs/images/kiosk_mode_status.png b/docs/images/hedgehog/images/kiosk_mode_status.png similarity index 100% rename from sensor-iso/docs/images/kiosk_mode_status.png rename to docs/images/hedgehog/images/kiosk_mode_status.png diff --git a/sensor-iso/docs/images/kiosk_mode_wipe_prompt.png b/docs/images/hedgehog/images/kiosk_mode_wipe_prompt.png similarity index 100% rename from sensor-iso/docs/images/kiosk_mode_wipe_prompt.png rename to docs/images/hedgehog/images/kiosk_mode_wipe_prompt.png diff --git a/sensor-iso/docs/images/malcolm_arkime_reachback_acl.png b/docs/images/hedgehog/images/malcolm_arkime_reachback_acl.png similarity index 100% rename from sensor-iso/docs/images/malcolm_arkime_reachback_acl.png rename to docs/images/hedgehog/images/malcolm_arkime_reachback_acl.png diff --git a/sensor-iso/docs/images/ntp_host.png b/docs/images/hedgehog/images/ntp_host.png similarity index 100% rename from sensor-iso/docs/images/ntp_host.png rename to docs/images/hedgehog/images/ntp_host.png diff --git a/sensor-iso/docs/images/opensearch_connection_protocol.png b/docs/images/hedgehog/images/opensearch_connection_protocol.png similarity index 100% rename from sensor-iso/docs/images/opensearch_connection_protocol.png rename to docs/images/hedgehog/images/opensearch_connection_protocol.png diff --git a/sensor-iso/docs/images/opensearch_connection_success.png b/docs/images/hedgehog/images/opensearch_connection_success.png similarity index 100% rename from sensor-iso/docs/images/opensearch_connection_success.png rename to docs/images/hedgehog/images/opensearch_connection_success.png diff --git a/sensor-iso/docs/images/opensearch_password.png b/docs/images/hedgehog/images/opensearch_password.png similarity index 100% rename from sensor-iso/docs/images/opensearch_password.png rename to docs/images/hedgehog/images/opensearch_password.png diff --git a/sensor-iso/docs/images/opensearch_ssl_verification.png b/docs/images/hedgehog/images/opensearch_ssl_verification.png similarity index 100% rename from sensor-iso/docs/images/opensearch_ssl_verification.png rename to docs/images/hedgehog/images/opensearch_ssl_verification.png diff --git a/sensor-iso/docs/images/opensearch_username.png b/docs/images/hedgehog/images/opensearch_username.png similarity index 100% rename from sensor-iso/docs/images/opensearch_username.png rename to docs/images/hedgehog/images/opensearch_username.png diff --git a/sensor-iso/docs/images/root_config_mode.png b/docs/images/hedgehog/images/root_config_mode.png similarity index 100% rename from sensor-iso/docs/images/root_config_mode.png rename to docs/images/hedgehog/images/root_config_mode.png diff --git a/sensor-iso/docs/images/select_iface.png b/docs/images/hedgehog/images/select_iface.png similarity index 100% rename from sensor-iso/docs/images/select_iface.png rename to docs/images/hedgehog/images/select_iface.png diff --git a/sensor-iso/docs/images/time_sync_mode.png b/docs/images/hedgehog/images/time_sync_mode.png similarity index 100% rename from sensor-iso/docs/images/time_sync_mode.png rename to docs/images/hedgehog/images/time_sync_mode.png diff --git a/sensor-iso/docs/images/time_sync_success.png b/docs/images/hedgehog/images/time_sync_success.png similarity index 100% rename from sensor-iso/docs/images/time_sync_success.png rename to docs/images/hedgehog/images/time_sync_success.png diff --git a/sensor-iso/docs/images/users_and_passwords.png b/docs/images/hedgehog/images/users_and_passwords.png similarity index 100% rename from sensor-iso/docs/images/users_and_passwords.png rename to docs/images/hedgehog/images/users_and_passwords.png diff --git a/sensor-iso/docs/images/zeek_file_carve_mode.png b/docs/images/hedgehog/images/zeek_file_carve_mode.png similarity index 100% rename from sensor-iso/docs/images/zeek_file_carve_mode.png rename to docs/images/hedgehog/images/zeek_file_carve_mode.png diff --git a/sensor-iso/docs/images/zeek_file_carve_scanners.png b/docs/images/hedgehog/images/zeek_file_carve_scanners.png similarity index 100% rename from sensor-iso/docs/images/zeek_file_carve_scanners.png rename to docs/images/hedgehog/images/zeek_file_carve_scanners.png diff --git a/sensor-iso/docs/logo/Attribution.txt b/docs/images/hedgehog/logo/Attribution.txt similarity index 100% rename from sensor-iso/docs/logo/Attribution.txt rename to docs/images/hedgehog/logo/Attribution.txt diff --git a/sensor-iso/docs/logo/favicon.ico b/docs/images/hedgehog/logo/favicon.ico similarity index 100% rename from sensor-iso/docs/logo/favicon.ico rename to docs/images/hedgehog/logo/favicon.ico diff --git a/sensor-iso/docs/logo/font/ubuntu/CONTRIBUTING.txt b/docs/images/hedgehog/logo/font/ubuntu/CONTRIBUTING.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/CONTRIBUTING.txt rename to docs/images/hedgehog/logo/font/ubuntu/CONTRIBUTING.txt diff --git a/sensor-iso/docs/logo/font/ubuntu/COPYRIGHT.txt b/docs/images/hedgehog/logo/font/ubuntu/COPYRIGHT.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/COPYRIGHT.txt rename to docs/images/hedgehog/logo/font/ubuntu/COPYRIGHT.txt diff --git a/sensor-iso/docs/logo/font/ubuntu/DESCRIPTION.en_us.html b/docs/images/hedgehog/logo/font/ubuntu/DESCRIPTION.en_us.html similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/DESCRIPTION.en_us.html rename to docs/images/hedgehog/logo/font/ubuntu/DESCRIPTION.en_us.html diff --git a/sensor-iso/docs/logo/font/ubuntu/FONTLOG.txt b/docs/images/hedgehog/logo/font/ubuntu/FONTLOG.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/FONTLOG.txt rename to docs/images/hedgehog/logo/font/ubuntu/FONTLOG.txt diff --git a/sensor-iso/docs/logo/font/ubuntu/LICENCE-FAQ.txt b/docs/images/hedgehog/logo/font/ubuntu/LICENCE-FAQ.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/LICENCE-FAQ.txt rename to docs/images/hedgehog/logo/font/ubuntu/LICENCE-FAQ.txt diff --git a/sensor-iso/docs/logo/font/ubuntu/LICENCE.txt b/docs/images/hedgehog/logo/font/ubuntu/LICENCE.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/LICENCE.txt rename to docs/images/hedgehog/logo/font/ubuntu/LICENCE.txt diff --git a/sensor-iso/docs/logo/font/ubuntu/METADATA.pb b/docs/images/hedgehog/logo/font/ubuntu/METADATA.pb similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/METADATA.pb rename to docs/images/hedgehog/logo/font/ubuntu/METADATA.pb diff --git a/sensor-iso/docs/logo/font/ubuntu/README.txt b/docs/images/hedgehog/logo/font/ubuntu/README.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/README.txt rename to docs/images/hedgehog/logo/font/ubuntu/README.txt diff --git a/sensor-iso/docs/logo/font/ubuntu/TRADEMARKS.txt b/docs/images/hedgehog/logo/font/ubuntu/TRADEMARKS.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/TRADEMARKS.txt rename to docs/images/hedgehog/logo/font/ubuntu/TRADEMARKS.txt diff --git a/sensor-iso/docs/logo/font/ubuntu/UFL.txt b/docs/images/hedgehog/logo/font/ubuntu/UFL.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/UFL.txt rename to docs/images/hedgehog/logo/font/ubuntu/UFL.txt diff --git a/sensor-iso/docs/logo/font/ubuntu/Ubuntu-Bold.ttf b/docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Bold.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/Ubuntu-Bold.ttf rename to docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Bold.ttf diff --git a/sensor-iso/docs/logo/font/ubuntu/Ubuntu-BoldItalic.ttf b/docs/images/hedgehog/logo/font/ubuntu/Ubuntu-BoldItalic.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/Ubuntu-BoldItalic.ttf rename to docs/images/hedgehog/logo/font/ubuntu/Ubuntu-BoldItalic.ttf diff --git a/sensor-iso/docs/logo/font/ubuntu/Ubuntu-Italic.ttf b/docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Italic.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/Ubuntu-Italic.ttf rename to docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Italic.ttf diff --git a/sensor-iso/docs/logo/font/ubuntu/Ubuntu-Light.ttf b/docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Light.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/Ubuntu-Light.ttf rename to docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Light.ttf diff --git a/sensor-iso/docs/logo/font/ubuntu/Ubuntu-LightItalic.ttf b/docs/images/hedgehog/logo/font/ubuntu/Ubuntu-LightItalic.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/Ubuntu-LightItalic.ttf rename to docs/images/hedgehog/logo/font/ubuntu/Ubuntu-LightItalic.ttf diff --git a/sensor-iso/docs/logo/font/ubuntu/Ubuntu-Medium.ttf b/docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Medium.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/Ubuntu-Medium.ttf rename to docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Medium.ttf diff --git a/sensor-iso/docs/logo/font/ubuntu/Ubuntu-MediumItalic.ttf b/docs/images/hedgehog/logo/font/ubuntu/Ubuntu-MediumItalic.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/Ubuntu-MediumItalic.ttf rename to docs/images/hedgehog/logo/font/ubuntu/Ubuntu-MediumItalic.ttf diff --git a/sensor-iso/docs/logo/font/ubuntu/Ubuntu-Regular.ttf b/docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Regular.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntu/Ubuntu-Regular.ttf rename to docs/images/hedgehog/logo/font/ubuntu/Ubuntu-Regular.ttf diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/CONTRIBUTING.txt b/docs/images/hedgehog/logo/font/ubuntucondensed/CONTRIBUTING.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/CONTRIBUTING.txt rename to docs/images/hedgehog/logo/font/ubuntucondensed/CONTRIBUTING.txt diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/COPYRIGHT.txt b/docs/images/hedgehog/logo/font/ubuntucondensed/COPYRIGHT.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/COPYRIGHT.txt rename to docs/images/hedgehog/logo/font/ubuntucondensed/COPYRIGHT.txt diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/DESCRIPTION.en_us.html b/docs/images/hedgehog/logo/font/ubuntucondensed/DESCRIPTION.en_us.html similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/DESCRIPTION.en_us.html rename to docs/images/hedgehog/logo/font/ubuntucondensed/DESCRIPTION.en_us.html diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/FONTLOG.txt b/docs/images/hedgehog/logo/font/ubuntucondensed/FONTLOG.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/FONTLOG.txt rename to docs/images/hedgehog/logo/font/ubuntucondensed/FONTLOG.txt diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/LICENCE-FAQ.txt b/docs/images/hedgehog/logo/font/ubuntucondensed/LICENCE-FAQ.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/LICENCE-FAQ.txt rename to docs/images/hedgehog/logo/font/ubuntucondensed/LICENCE-FAQ.txt diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/LICENCE.txt b/docs/images/hedgehog/logo/font/ubuntucondensed/LICENCE.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/LICENCE.txt rename to docs/images/hedgehog/logo/font/ubuntucondensed/LICENCE.txt diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/METADATA.pb b/docs/images/hedgehog/logo/font/ubuntucondensed/METADATA.pb similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/METADATA.pb rename to docs/images/hedgehog/logo/font/ubuntucondensed/METADATA.pb diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/README.txt b/docs/images/hedgehog/logo/font/ubuntucondensed/README.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/README.txt rename to docs/images/hedgehog/logo/font/ubuntucondensed/README.txt diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/TRADEMARKS.txt b/docs/images/hedgehog/logo/font/ubuntucondensed/TRADEMARKS.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/TRADEMARKS.txt rename to docs/images/hedgehog/logo/font/ubuntucondensed/TRADEMARKS.txt diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/UFL.txt b/docs/images/hedgehog/logo/font/ubuntucondensed/UFL.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/UFL.txt rename to docs/images/hedgehog/logo/font/ubuntucondensed/UFL.txt diff --git a/sensor-iso/docs/logo/font/ubuntucondensed/UbuntuCondensed-Regular.ttf b/docs/images/hedgehog/logo/font/ubuntucondensed/UbuntuCondensed-Regular.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntucondensed/UbuntuCondensed-Regular.ttf rename to docs/images/hedgehog/logo/font/ubuntucondensed/UbuntuCondensed-Regular.ttf diff --git a/sensor-iso/docs/logo/font/ubuntumono/CONTRIBUTING.txt b/docs/images/hedgehog/logo/font/ubuntumono/CONTRIBUTING.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/CONTRIBUTING.txt rename to docs/images/hedgehog/logo/font/ubuntumono/CONTRIBUTING.txt diff --git a/sensor-iso/docs/logo/font/ubuntumono/COPYRIGHT.txt b/docs/images/hedgehog/logo/font/ubuntumono/COPYRIGHT.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/COPYRIGHT.txt rename to docs/images/hedgehog/logo/font/ubuntumono/COPYRIGHT.txt diff --git a/sensor-iso/docs/logo/font/ubuntumono/DESCRIPTION.en_us.html b/docs/images/hedgehog/logo/font/ubuntumono/DESCRIPTION.en_us.html similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/DESCRIPTION.en_us.html rename to docs/images/hedgehog/logo/font/ubuntumono/DESCRIPTION.en_us.html diff --git a/sensor-iso/docs/logo/font/ubuntumono/FONTLOG.txt b/docs/images/hedgehog/logo/font/ubuntumono/FONTLOG.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/FONTLOG.txt rename to docs/images/hedgehog/logo/font/ubuntumono/FONTLOG.txt diff --git a/sensor-iso/docs/logo/font/ubuntumono/LICENCE-FAQ.txt b/docs/images/hedgehog/logo/font/ubuntumono/LICENCE-FAQ.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/LICENCE-FAQ.txt rename to docs/images/hedgehog/logo/font/ubuntumono/LICENCE-FAQ.txt diff --git a/sensor-iso/docs/logo/font/ubuntumono/LICENCE.txt b/docs/images/hedgehog/logo/font/ubuntumono/LICENCE.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/LICENCE.txt rename to docs/images/hedgehog/logo/font/ubuntumono/LICENCE.txt diff --git a/sensor-iso/docs/logo/font/ubuntumono/METADATA.pb b/docs/images/hedgehog/logo/font/ubuntumono/METADATA.pb similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/METADATA.pb rename to docs/images/hedgehog/logo/font/ubuntumono/METADATA.pb diff --git a/sensor-iso/docs/logo/font/ubuntumono/README.txt b/docs/images/hedgehog/logo/font/ubuntumono/README.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/README.txt rename to docs/images/hedgehog/logo/font/ubuntumono/README.txt diff --git a/sensor-iso/docs/logo/font/ubuntumono/TRADEMARKS.txt b/docs/images/hedgehog/logo/font/ubuntumono/TRADEMARKS.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/TRADEMARKS.txt rename to docs/images/hedgehog/logo/font/ubuntumono/TRADEMARKS.txt diff --git a/sensor-iso/docs/logo/font/ubuntumono/UFL.txt b/docs/images/hedgehog/logo/font/ubuntumono/UFL.txt similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/UFL.txt rename to docs/images/hedgehog/logo/font/ubuntumono/UFL.txt diff --git a/sensor-iso/docs/logo/font/ubuntumono/UbuntuMono-Bold.ttf b/docs/images/hedgehog/logo/font/ubuntumono/UbuntuMono-Bold.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/UbuntuMono-Bold.ttf rename to docs/images/hedgehog/logo/font/ubuntumono/UbuntuMono-Bold.ttf diff --git a/sensor-iso/docs/logo/font/ubuntumono/UbuntuMono-BoldItalic.ttf b/docs/images/hedgehog/logo/font/ubuntumono/UbuntuMono-BoldItalic.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/UbuntuMono-BoldItalic.ttf rename to docs/images/hedgehog/logo/font/ubuntumono/UbuntuMono-BoldItalic.ttf diff --git a/sensor-iso/docs/logo/font/ubuntumono/UbuntuMono-Italic.ttf b/docs/images/hedgehog/logo/font/ubuntumono/UbuntuMono-Italic.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/UbuntuMono-Italic.ttf rename to docs/images/hedgehog/logo/font/ubuntumono/UbuntuMono-Italic.ttf diff --git a/sensor-iso/docs/logo/font/ubuntumono/UbuntuMono-Regular.ttf b/docs/images/hedgehog/logo/font/ubuntumono/UbuntuMono-Regular.ttf similarity index 100% rename from sensor-iso/docs/logo/font/ubuntumono/UbuntuMono-Regular.ttf rename to docs/images/hedgehog/logo/font/ubuntumono/UbuntuMono-Regular.ttf diff --git a/sensor-iso/docs/logo/hedgehog-bw-large.png b/docs/images/hedgehog/logo/hedgehog-bw-large.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-bw-large.png rename to docs/images/hedgehog/logo/hedgehog-bw-large.png diff --git a/sensor-iso/docs/logo/hedgehog-bw-small.png b/docs/images/hedgehog/logo/hedgehog-bw-small.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-bw-small.png rename to docs/images/hedgehog/logo/hedgehog-bw-small.png diff --git a/sensor-iso/docs/logo/hedgehog-bw-w-text-large.png b/docs/images/hedgehog/logo/hedgehog-bw-w-text-large.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-bw-w-text-large.png rename to docs/images/hedgehog/logo/hedgehog-bw-w-text-large.png diff --git a/sensor-iso/docs/logo/hedgehog-bw-w-text-small.png b/docs/images/hedgehog/logo/hedgehog-bw-w-text-small.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-bw-w-text-small.png rename to docs/images/hedgehog/logo/hedgehog-bw-w-text-small.png diff --git a/sensor-iso/docs/logo/hedgehog-bw-w-text.ai b/docs/images/hedgehog/logo/hedgehog-bw-w-text.ai similarity index 100% rename from sensor-iso/docs/logo/hedgehog-bw-w-text.ai rename to docs/images/hedgehog/logo/hedgehog-bw-w-text.ai diff --git a/sensor-iso/docs/logo/hedgehog-bw.ai b/docs/images/hedgehog/logo/hedgehog-bw.ai similarity index 100% rename from sensor-iso/docs/logo/hedgehog-bw.ai rename to docs/images/hedgehog/logo/hedgehog-bw.ai diff --git a/sensor-iso/docs/logo/hedgehog-color-large.png b/docs/images/hedgehog/logo/hedgehog-color-large.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color-large.png rename to docs/images/hedgehog/logo/hedgehog-color-large.png diff --git a/sensor-iso/docs/logo/hedgehog-color-small.png b/docs/images/hedgehog/logo/hedgehog-color-small.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color-small.png rename to docs/images/hedgehog/logo/hedgehog-color-small.png diff --git a/sensor-iso/docs/logo/hedgehog-color-w-text-large.png b/docs/images/hedgehog/logo/hedgehog-color-w-text-large.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color-w-text-large.png rename to docs/images/hedgehog/logo/hedgehog-color-w-text-large.png diff --git a/sensor-iso/docs/logo/hedgehog-color-w-text-small.png b/docs/images/hedgehog/logo/hedgehog-color-w-text-small.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color-w-text-small.png rename to docs/images/hedgehog/logo/hedgehog-color-w-text-small.png diff --git a/sensor-iso/docs/logo/hedgehog-color-w-text.ai b/docs/images/hedgehog/logo/hedgehog-color-w-text.ai similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color-w-text.ai rename to docs/images/hedgehog/logo/hedgehog-color-w-text.ai diff --git a/sensor-iso/docs/logo/hedgehog-color-w-text.png b/docs/images/hedgehog/logo/hedgehog-color-w-text.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color-w-text.png rename to docs/images/hedgehog/logo/hedgehog-color-w-text.png diff --git a/sensor-iso/docs/logo/hedgehog-color.ai b/docs/images/hedgehog/logo/hedgehog-color.ai similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color.ai rename to docs/images/hedgehog/logo/hedgehog-color.ai diff --git a/sensor-iso/docs/logo/hedgehog-color.eps b/docs/images/hedgehog/logo/hedgehog-color.eps similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color.eps rename to docs/images/hedgehog/logo/hedgehog-color.eps diff --git a/sensor-iso/docs/logo/hedgehog-color.png b/docs/images/hedgehog/logo/hedgehog-color.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-color.png rename to docs/images/hedgehog/logo/hedgehog-color.png diff --git a/sensor-iso/docs/logo/hedgehog-wallpaper-plain.png b/docs/images/hedgehog/logo/hedgehog-wallpaper-plain.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-wallpaper-plain.png rename to docs/images/hedgehog/logo/hedgehog-wallpaper-plain.png diff --git a/sensor-iso/docs/logo/hedgehog-wallpaper.png b/docs/images/hedgehog/logo/hedgehog-wallpaper.png similarity index 100% rename from sensor-iso/docs/logo/hedgehog-wallpaper.png rename to docs/images/hedgehog/logo/hedgehog-wallpaper.png diff --git a/sensor-iso/docs/logo/hedgehog-wallpaper.xcf b/docs/images/hedgehog/logo/hedgehog-wallpaper.xcf similarity index 100% rename from sensor-iso/docs/logo/hedgehog-wallpaper.xcf rename to docs/images/hedgehog/logo/hedgehog-wallpaper.xcf diff --git a/docs/images/malcolm_components.png b/docs/images/malcolm_components.png new file mode 100644 index 0000000000000000000000000000000000000000..ec43f8aae4abb7440ea45acb03457ffdc2a07d9f GIT binary patch literal 353486 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

nJw+Zl3m##qoX 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≫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 z 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�&<&iSuLb1o%t)nH-kEfOCYj4K3kkjFnOPQ5(+x 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ÐT{qGTvXANnH&2XyerBAzes>Z~@!ND9WFY9*3 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 R{6>N=B{@~uGU?}m{|C9JfzAK` literal 0 HcmV?d00001 diff --git a/docs/images/malcolm_poster.odg b/docs/images/malcolm_poster.odg index a5f0c8f46c65d0daa73fb7268ffd8974be735eab..a3586df1792734a7c944e621f2c66bd9874e6a65 100644 GIT binary patch delta 276605 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≀>M2#1BI{n_h=V?eMo{^^~S`Y|ma1h4n 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