Skip to content

Instantly share code, notes, and snippets.

@sergioloppe
Last active October 13, 2023 07:21
Show Gist options
  • Select an option

  • Save sergioloppe/20a064bb94d2f87c0a11e4c0d2986a6d to your computer and use it in GitHub Desktop.

Select an option

Save sergioloppe/20a064bb94d2f87c0a11e4c0d2986a6d to your computer and use it in GitHub Desktop.
Kubernetes Network policy generator
package main
// DeploymentLabel represents a Kubernetes namespace and a set of labels for the deployment in that namespace.
type DeploymentLabel struct {
Namespace string
Labels map[string]string // Key-value pairs for labels
}
// RulePort specifies the protocol and the port.
type RulePort struct {
Protocol string
Port int
}
// NetworkRule represents an allowed network rule between two deployments.
type NetworkRule struct {
From DeploymentLabel
To DeploymentLabel
Ports []RulePort // Allow for multiple ports per rule
}
// Rules represent the matrix of allowed network rules between deployments.
// This is where the engineer updates the data.
var Rules = []NetworkRule{
{
From: DeploymentLabel{
Namespace: "ns1",
Labels: map[string]string{
"app": "frontend",
"env": "prod",
},
},
To: DeploymentLabel{
Namespace: "ns2",
Labels: map[string]string{
"app": "backend",
"tier": "database",
},
},
Ports: []RulePort{
{Protocol: "TCP", Port: 80},
{Protocol: "TCP", Port: 443},
},
},
// ... Add more rules as needed
}
package main
import (
"fmt"
"os"
"strings"
)
// GeneratePorts creates a YAML section for the ports in NetworkPolicy.
func GeneratePorts(ports []RulePort) string {
if len(ports) == 0 {
return ""
}
var portStrs []string
for _, port := range ports {
portStrs = append(portStrs, fmt.Sprintf(" - protocol: %s\n port: %d", port.Protocol, port.Port))
}
return "ports:\n" + strings.Join(portStrs, "\n")
}
// GenerateLabels converts label map to string.
func GenerateLabels(labels map[string]string) string {
var labelStrs []string
for key, value := range labels {
labelStrs = append(labelStrs, fmt.Sprintf("%s: %s", key, value))
}
return strings.Join(labelStrs, "\n ")
}
// GenerateTrafficRuleSection creates a YAML section for either ingress or egress.
// TODO: Check the final section... not happy with the results here
func GenerateTrafficRuleSection(rule NetworkRule, direction string) string {
var selectors []string
if direction == "ingress" && len(rule.From.Labels) > 0 {
fromLabelsStr := GenerateLabels(rule.From.Labels)
selectors = append(selectors, fmt.Sprintf(`- from:
- namespaceSelector:
matchLabels:
name: %s
podSelector:
matchLabels:
%s`, rule.From.Namespace, fromLabelsStr))
}
toLabelsStr := GenerateLabels(rule.To.Labels)
selectors = append(selectors, fmt.Sprintf(`
- podSelector:
matchLabels:
%s`, toLabelsStr))
ports := GeneratePorts(rule.Ports)
// Attach ports to the selectors.
if ports != "" {
selectors[len(selectors)-1] += "\n " + ports
}
return fmt.Sprintf("\n %s:\n%s", direction, strings.Join(selectors, "\n"))
}
// CreateNetworkPolicyFile generates and writes NetworkPolicy in YAML format to the outputs directory.
func CreateNetworkPolicyFile(policyYAML, fileName string) {
outputDir := "outputs"
if _, err := os.Stat(outputDir); os.IsNotExist(err) {
os.Mkdir(outputDir, 0755) // Create the directory with read-write permissions
}
filePath := fmt.Sprintf("%s/%s", outputDir, fileName)
file, err := os.Create(filePath)
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
file.WriteString(policyYAML)
fmt.Printf("Generated policy: %s\n", filePath)
}
func GenerateSimpleLabel(labelMap map[string]string) string {
var labels []string
for k, v := range labelMap {
labels = append(labels, k+"-"+v)
}
return strings.Join(labels, "-")
}
func GenerateFileName(rule NetworkRule) string {
fromLabel := GenerateSimpleLabel(rule.From.Labels)
toLabel := GenerateSimpleLabel(rule.To.Labels)
return fmt.Sprintf("%s-to-%s-policy.yaml", fromLabel, toLabel)
}
// GenerateNetworkPolicyYAML generates the YAML string for the NetworkPolicy.
func GenerateNetworkPolicyYAML(rule NetworkRule) string {
ingress := GenerateTrafficRuleSection(rule, "ingress")
egress := GenerateTrafficRuleSection(rule, "egress")
fromLabel := GenerateSimpleLabel(rule.From.Labels)
toLabel := GenerateSimpleLabel(rule.To.Labels)
networkPolicyYAML := fmt.Sprintf(`
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: %s-to-%s-policy
namespace: %s
spec:
podSelector:
matchLabels:
%s
%s
%s
`, fromLabel, toLabel, rule.To.Namespace, toLabel, ingress, egress)
return networkPolicyYAML
}
// ProcessRules generates a NetworkPolicy YAML for all rules and saves it to a file.
func ProcessRules() {
for _, rule := range Rules {
networkPolicyYAML := GenerateNetworkPolicyYAML(rule)
fileName := GenerateFileName(rule)
CreateNetworkPolicyFile(networkPolicyYAML, fileName)
}
}
func main() {
ProcessRules()
}

How to use it

To run the code:

  1. Setup:

    • Install Go if you haven't already. You can get it from Go's official website.

    • Ensure your GOPATH is set. Typically it's ~/go, but you might want to check or configure it according to your needs.

  2. Structure your Workspace:

    myproject/
    ├── main.go             # This contains the `main()` function (from the `generate.go` content provided).
    ├── config.go           # This contains configurations and data.
    ├── go.mod              # This is Go's module file.
    ├── outputs/            # Directory where the YAML files will be stored.
    
  3. Initialize a Go Module:

    Navigate to the npgenerator directory:

    cd path/to/npgenerator

    Initialize a new Go module:

    go mod init npgenerator

    This will create a go.mod file in your directory.

  4. Write the Code:

    Populate main.go and config.go with the provided code.

  5. Run the Code:

    Ensure you're still in the myproject directory:

    go run .

    This will execute the code, and if everything is correct, you should see the generated YAML files in the outputs directory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment