diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e42bbef --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Daniel Dagfinrud + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 73a29f3..f91eb0d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You are welcome to contribute! Open an issue or create a pull request if there is some functionality missing that you would like. -## Installation via Homebrew +## Installation via Homebrew for Mac You can install `netfetch` using our Homebrew tap: @@ -20,6 +20,8 @@ brew tap deggja/netfetch https://github.com/deggja/netfetch brew install netfetch ``` +For specific Linux distros, Windows etc. Check the latest release for a downloadable binary. + ### Prerequisites Before you begin, ensure you have the following: @@ -28,11 +30,11 @@ Before you begin, ensure you have the following: - Access to a Kubernetes cluster with configured `kubectl`. - Sufficient permissions to list namespaces and network policies in the cluster. -### Basic Usage +### Basic usage The primary command provided by `netfetch` is `scan`. This command scans all non-system Kubernetes namespaces for network policies. -#### Command Structure +#### Command structure ```sh netfetch scan @@ -42,4 +44,14 @@ You can also specifiy namespaces when running netfetch. ```sh netfetch scan default -``` \ No newline at end of file +``` + +## Netfetch score + +The `netfetch` tool provides a score at the end of each scan. The score ranges from 1 to 42, with 1 being the lowest and 42 being the highest possible score. + +This score reflects the security posture of your Kubernetes namespaces based on network policies and pod coverage. If changes are made based on recommendations from the initial scan, rerunning `netfetch` will likely result in an improved score. + +## License + +[MIT License], see [LICENSE](LICENSE). \ No newline at end of file diff --git a/netfetch b/netfetch index ded155b..17118e1 100755 Binary files a/netfetch and b/netfetch differ diff --git a/pkg/k8s/scanner.go b/pkg/k8s/scanner.go index 5d8a991..5cfdb99 100644 --- a/pkg/k8s/scanner.go +++ b/pkg/k8s/scanner.go @@ -28,9 +28,10 @@ func printToBoth(writer *bufio.Writer, s string) { // ScanNetworkPolicies scans namespaces for network policies func ScanNetworkPolicies(specificNamespace string) { var output bytes.Buffer - writer := bufio.NewWriter(&output) - + var namespacesToScan []string var kubeconfig string + + writer := bufio.NewWriter(&output) if home := homedir.HomeDir(); home != "" { kubeconfig = filepath.Join(home, ".kube", "config") } @@ -47,7 +48,6 @@ func ScanNetworkPolicies(specificNamespace string) { return } - var namespacesToScan []string if specificNamespace != "" { namespacesToScan = append(namespacesToScan, specificNamespace) } else { @@ -65,7 +65,11 @@ func ScanNetworkPolicies(specificNamespace string) { missingPoliciesOrUncoveredPods := false userDeniedPolicyApplication := false + policyChangesMade := false + confirm := false + deniedNamespaces := []string{} + unprotectedPodDetails := []string{} for _, nsName := range namespacesToScan { policies, err := clientset.NetworkingV1().NetworkPolicies(nsName).List(context.TODO(), metav1.ListOptions{}) @@ -107,6 +111,13 @@ func ScanNetworkPolicies(specificNamespace string) { printToBoth(writer, errorMsg) continue } + unprotectedPodsCount := len(unprotectedPodDetails) + if unprotectedPodsCount > 0 || !hasDenyAll || !hasPolicies { + missingPoliciesOrUncoveredPods = true + } + if confirm { + policyChangesMade = true + } unprotectedPods := false var unprotectedPodDetails []string @@ -174,6 +185,14 @@ func ScanNetworkPolicies(specificNamespace string) { } } + // Calculate the final score after scanning all namespaces + finalScore := calculateScore(!missingPoliciesOrUncoveredPods, !userDeniedPolicyApplication, len(deniedNamespaces)) + fmt.Printf("\nYour Netfetch security score is: %d/42\n", finalScore) + + if policyChangesMade { + fmt.Println("\nChanges were made during this scan. It's recommended to re-run the scan for an updated score.") + } + if missingPoliciesOrUncoveredPods { if userDeniedPolicyApplication { printToBoth(writer, "\nFor the following namespaces, you should assess the need of implementing network policies:\n") @@ -234,3 +253,26 @@ func isSystemNamespace(namespace string) bool { return false } } + +// Scoring logic +func calculateScore(hasPolicies bool, hasDenyAll bool, unprotectedPodsCount int) int { + // Simple scoring logic - can be more complex based on requirements + score := 42 // Start with the highest score + + if !hasPolicies { + score -= 20 + } + + if !hasDenyAll { + score -= 15 + } + + // Deduct score based on the number of unprotected pods + score -= unprotectedPodsCount + + if score < 1 { + score = 1 // Minimum score + } + + return score +}