Skip to main content

What is a Network Policy?

A network policy in Garnet defines rules for allowing or blocking network traffic based on IP addresses and domain names. Network policies are the enforcement mechanism that translates security decisions into actual protection. Policies are hierarchical and context-aware, allowing you to apply rules at different scopes from global organization-wide rules down to specific workflows or nodes.

Policy Structure

type NetworkPolicy struct {
    ID        string              `json:"id"`
    ProjectID string              `json:"-"`
    Scope     NetworkPolicyScope  `json:"scope"`
    Config    NetworkPolicyConfig `json:"config"`
    Rules     []NetworkPolicyRule `json:"rules"`
    CreatedAt time.Time           `json:"created_at"`
    UpdatedAt time.Time           `json:"updated_at"`
    DeletedAt *time.Time          `json:"deleted_at,omitempty"`
}

Policy Scopes

Policies can be applied at different hierarchical levels:
const (
    NetworkPolicyScopeSystemGlobal NetworkPolicyScope = "system_global"
    NetworkPolicyScopeGlobal       NetworkPolicyScope = "global"
    NetworkPolicyScopeRepo         NetworkPolicyScope = "repo"
    NetworkPolicyScopeWorkflow     NetworkPolicyScope = "workflow"
    NetworkPolicyScopeCluster      NetworkPolicyScope = "cluster"
    NetworkPolicyScopeNode         NetworkPolicyScope = "node"
)

System-Wide

System Global: Applies across all projects (admin only)Project Global: Applies to all agents in a projectPriority: Lowest (can be overridden by more specific policies)

GitHub Context

Repository: Applies to all workflows in a repositoryWorkflow: Applies to specific workflowPriority: Workflow overrides Repository, which overrides Global

Kubernetes Context

Cluster: Applies to entire Kubernetes clusterNode: Applies to specific nodePriority: Node overrides Cluster, which overrides Global

Vanilla Context

Uses Project Global scopeRules apply to all vanilla agents in the projectPriority: Project-level enforcement

Scope Hierarchy

Policies merge in order of increasing specificity:
1

System Global

Base policies set by system administrators
2

Project Global

Project-wide policies override system global
3

Context-Specific

Repository/Cluster policies override project global
4

Most Specific

Workflow/Node policies override all others
When multiple policies apply to an agent, they are merged with more specific policies taking precedence. Rules from all applicable policies are combined.

Policy Configuration

Each policy has a configuration that controls enforcement behavior:
type NetworkPolicyConfig struct {
    CIDRMode      NetworkPolicyCIDRMode    `json:"cidr_mode"`
    CIDRPolicy    NetworkPolicyType        `json:"cidr_policy"`
    ResolveMode   NetworkPolicyResolveMode `json:"resolve_mode"`
    ResolvePolicy NetworkPolicyType        `json:"resolve_policy"`
}

Enforcement Modes

Controls how IP-based rules are enforced:
const (
    NetworkPolicyCIDRModeAlert   NetworkPolicyCIDRMode = "alert"   // Log only
    NetworkPolicyCIDRModeEnforce NetworkPolicyCIDRMode = "enforce" // Block
    NetworkPolicyCIDRModeBoth    NetworkPolicyCIDRMode = "both"    // Log + Block
)
  • Alert: Violations are logged but traffic is allowed
  • Enforce: Violations are blocked
  • Both: Violations are logged AND blocked
Controls DNS resolution behavior:
const (
    NetworkPolicyResolveModsBypass     NetworkPolicyResolveMode = "bypass"     // No DNS checking
    NetworkPolicyResolveModeStrict     NetworkPolicyResolveMode = "strict"     // Enforce DNS rules
    NetworkPolicyResolveModePermissive NetworkPolicyResolveMode = "permissive" // Relaxed checking
)
  • Bypass: DNS resolution not enforced
  • Strict: Domain rules strictly enforced
  • Permissive: Allows some DNS resolution flexibility
Default action for traffic not matching any rules:
const (
    NetworkPolicyTypeAllow NetworkPolicyType = "allow" // Default allow
    NetworkPolicyTypeDeny  NetworkPolicyType = "deny"  // Default deny
)
  • Allow: Traffic is allowed unless explicitly denied
  • Deny: Traffic is denied unless explicitly allowed

Simplified Mode Field

For easier configuration, policies support a simplified mode:
type NetworkPolicyMode string

const (
    NetworkPolicyModeBypass  NetworkPolicyMode = "bypass"  // No enforcement
    NetworkPolicyModeAlert   NetworkPolicyMode = "alert"   // Log violations
    NetworkPolicyModeEnforce NetworkPolicyMode = "enforce" // Block violations
    NetworkPolicyModeBoth    NetworkPolicyMode = "both"    // Log + Block
)

Policy Rules

Rules define specific allow or deny actions:
type NetworkPolicyRule struct {
    ID        string                `json:"id"`
    PolicyID  string                `json:"policy_id"`
    Type      NetworkPolicyRuleType `json:"type"`
    Value     string                `json:"value"`
    Action    NetworkPolicyType     `json:"action"`
    CreatedAt time.Time             `json:"created_at"`
    UpdatedAt time.Time             `json:"updated_at"`
}

Rule Types

CIDR Rules

Match IP addresses or ranges:
NetworkPolicyRuleTypeCIDR NetworkPolicyRuleType = "cidr"
Examples:
  • 192.168.1.0/24 - Subnet
  • 10.0.0.1/32 - Single IP
  • 2001:db8::/32 - IPv6 range

Domain Rules

Match domain names:
NetworkPolicyRuleTypeDomain NetworkPolicyRuleType = "domain"
Examples:
  • example.com
  • api.service.io
  • *.malicious.net

Rule Actions

const (
    NetworkPolicyTypeAllow NetworkPolicyType = "allow" // Explicitly allow
    NetworkPolicyTypeDeny  NetworkPolicyType = "deny"  // Explicitly deny
)
Rules override the default policy. An allow rule in a deny-by-default policy will permit that specific destination.

Creating Policies

Global Policy

import (
    "context"
    "github.com/garnet-org/api/client"
    "github.com/garnet-org/api/types"
)

func createGlobalPolicy() {
    c := client.New("your-api-key")
    
    policy := types.CreateNetworkPolicy{
        Scope: types.NetworkPolicyScopeGlobal,
        Config: types.NetworkPolicyConfig{
            CIDRMode:      types.NetworkPolicyCIDRModeEnforce,
            CIDRPolicy:    types.NetworkPolicyTypeAllow,
            ResolveMode:   types.NetworkPolicyResolveModsBypass,
            ResolvePolicy: types.NetworkPolicyTypeAllow,
        },
        Rules: []types.CreateNetworkPolicyRule{
            {
                Type:   types.NetworkPolicyRuleTypeCIDR,
                Value:  "192.168.0.0/16",
                Action: types.NetworkPolicyTypeAllow,
            },
            {
                Type:   types.NetworkPolicyRuleTypeDomain,
                Value:  "malicious.example.com",
                Action: types.NetworkPolicyTypeDeny,
            },
        },
    }
    
    result, err := c.CreateNetworkPolicy(context.Background(), policy)
    if err != nil {
        // Handle error
    }
    
    fmt.Printf("Created policy: %s\n", result.ID)
}

Repository-Scoped Policy

func createRepoPolicy(repositoryID string) {
    c := client.New("your-api-key")
    
    policy := types.CreateNetworkPolicy{
        Scope:        types.NetworkPolicyScopeRepo,
        RepositoryID: repositoryID,
        Config:       types.GetDefaultNetworkPolicyConfig(),
    }
    
    result, err := c.CreateNetworkPolicy(context.Background(), policy)
    if err != nil {
        // Handle error
    }
}

Workflow-Scoped Policy

func createWorkflowPolicy(repositoryID, workflowName string) {
    c := client.New("your-api-key")
    
    policy := types.CreateNetworkPolicy{
        Scope:        types.NetworkPolicyScopeWorkflow,
        RepositoryID: repositoryID,
        WorkflowName: workflowName,
        Config:       types.GetDefaultNetworkPolicyConfig(),
    }
    
    result, err := c.CreateNetworkPolicy(context.Background(), policy)
    if err != nil {
        // Handle error
    }
}

Kubernetes Cluster Policy

func createClusterPolicy(clusterName string) {
    c := client.New("your-api-key")
    
    policy := types.CreateNetworkPolicy{
        Scope:       types.NetworkPolicyScopeCluster,
        ClusterName: clusterName,
        Config:      types.GetDefaultNetworkPolicyConfig(),
    }
    
    result, err := c.CreateNetworkPolicy(context.Background(), policy)
    if err != nil {
        // Handle error
    }
}

Kubernetes Node Policy

func createNodePolicy(clusterName, nodeName string) {
    c := client.New("your-api-key")
    
    policy := types.CreateNetworkPolicy{
        Scope:       types.NetworkPolicyScopeNode,
        ClusterName: clusterName,
        NodeName:    nodeName,
        Config:      types.GetDefaultNetworkPolicyConfig(),
    }
    
    result, err := c.CreateNetworkPolicy(context.Background(), policy)
    if err != nil {
        // Handle error
    }
}

Managing Policy Rules

Adding Rules

func addRule(policyID string) {
    c := client.New("your-api-key")
    
    rule := types.CreateNetworkPolicyRule{
        Type:   types.NetworkPolicyRuleTypeDomain,
        Value:  "safe-api.example.com",
        Action: types.NetworkPolicyTypeAllow,
    }
    
    result, err := c.CreateNetworkPolicyRule(context.Background(), policyID, rule)
    if err != nil {
        // Handle error
    }
    
    fmt.Printf("Created rule: %s\n", result.ID)
}

Updating Rules

func updateRule(ruleID string) {
    c := client.New("your-api-key")
    
    newAction := types.NetworkPolicyTypeDeny
    update := types.UpdateNetworkPolicyRule{
        Action: &newAction,
    }
    
    result, err := c.UpdateNetworkPolicyRule(context.Background(), ruleID, update)
    if err != nil {
        // Handle error
    }
}

Deleting Rules

func deleteRule(ruleID string) {
    c := client.New("your-api-key")
    
    err := c.DeleteNetworkPolicyRule(context.Background(), ruleID)
    if err != nil {
        // Handle error
    }
}

Merged Network Policies

Agents receive a merged policy that combines all applicable policies:
type MergedNetworkPolicy struct {
    // Legacy format (for backwards compatibility)
    Config NetworkPolicyConfig `json:"config"`
    Rules  []NetworkPolicyRule `json:"rules"`
    
    // Simplified format
    Mode   NetworkPolicyMode `json:"mode"`
    Policy NetworkPolicyType `json:"policy"`
    Allow  []string          `json:"allow"`
    Deny   []string          `json:"deny"`
    Resolve []string         `json:"resolve"`
    
    // Policy references for traceability
    SystemGlobalPolicy *SystemGlobalNetworkPolicy `json:"system_global_policy,omitempty"`
    GlobalPolicy       *NetworkPolicy             `json:"global_policy,omitempty"`
    RepoPolicy         *RepoNetworkPolicy         `json:"repo_policy,omitempty"`
    WorkflowPolicy     *WorkflowNetworkPolicy     `json:"workflow_policy,omitempty"`
    ClusterPolicy      *ClusterNetworkPolicy      `json:"cluster_policy,omitempty"`
    NodePolicy         *NodeNetworkPolicy         `json:"node_policy,omitempty"`
}

Retrieving Merged Policies

For GitHub Context:
func getMergedPolicyGitHub(repositoryID, workflowName string) {
    c := client.New("your-api-key")
    
    // Get as JSON
    policy, err := c.MergedNetworkPolicyGithubJSON(
        context.Background(),
        repositoryID,
        workflowName,
    )
    if err != nil {
        // Handle error
    }
    
    // Or get as YAML
    yamlPolicy, err := c.MergedNetworkPolicyGithubYAML(
        context.Background(),
        repositoryID,
        workflowName,
    )
    if err != nil {
        // Handle error
    }
}
For Kubernetes Context:
func getMergedPolicyK8s(clusterName, nodeName string) {
    c := client.New("your-api-key")
    
    policy, err := c.MergedNetworkPolicyForK8s(
        context.Background(),
        clusterName,
        nodeName,
    )
    if err != nil {
        // Handle error
    }
    
    fmt.Printf("Mode: %s\n", policy.Mode)
    fmt.Printf("Policy: %s\n", policy.Policy)
    fmt.Printf("Allow: %v\n", policy.Allow)
    fmt.Printf("Deny: %v\n", policy.Deny)
}
The merged policy includes references to all source policies, allowing you to trace which policy contributed each rule.

Policy Merge Logic

The merge algorithm (types/network_policy.go:671-775):
  1. Start with default configuration
  2. Apply system global policy (if exists)
  3. Apply project global policy (overrides system)
  4. Apply context-specific policy (repo/cluster)
  5. Apply most-specific policy (workflow/node)
  6. Populate simplified format from merged rules
More specific policies completely override the configuration of less specific policies, but rules from all policies are combined.

Listing Policies

Retrieve policies by scope:
func listPolicies(scope types.NetworkPolicyScope) {
    c := client.New("your-api-key")
    
    policies, err := c.NetworkPolicies(context.Background(), scope)
    if err != nil {
        // Handle error
    }
    
    for _, policy := range policies {
        fmt.Printf("Policy: %s (scope: %s)\n", policy.ID, policy.Scope)
        fmt.Printf("  Rules: %d\n", len(policy.Rules))
    }
}

Updating Policies

Modify policy configuration:
func updatePolicy(policyID string) {
    c := client.New("your-api-key")
    
    newConfig := types.NetworkPolicyConfig{
        CIDRMode:      types.NetworkPolicyCIDRModeEnforce,
        CIDRPolicy:    types.NetworkPolicyTypeDeny,
        ResolveMode:   types.NetworkPolicyResolveModeStrict,
        ResolvePolicy: types.NetworkPolicyTypeDeny,
    }
    
    update := types.UpdateNetworkPolicy{
        Config: &newConfig,
    }
    
    result, err := c.UpdateNetworkPolicy(context.Background(), policyID, update)
    if err != nil {
        // Handle error
    }
    
    fmt.Printf("Updated policy: %s\n", result.ID)
}

Deleting Policies

Remove a network policy:
func deletePolicy(policyID string) {
    c := client.New("your-api-key")
    
    err := c.DeleteNetworkPolicy(context.Background(), policyID)
    if err != nil {
        // Handle error
    }
}
Deleting a policy soft-deletes it (sets DeletedAt timestamp). Agents will no longer receive rules from deleted policies.

Best Practices

  • Start with alert mode before enforcing
  • Use default-deny for high-security environments
  • Apply narrow scopes for testing
  • Document the purpose of each policy
  • Review merged policies to understand actual enforcement
  • Use CIDR rules for IP ranges
  • Use domain rules for service names
  • Prefer allow lists over deny lists when possible
  • Regularly audit and cleanup unused rules
  • Test rules before deploying broadly
  • Use global policies for organization-wide rules
  • Use repo/cluster policies for team-specific rules
  • Use workflow/node policies for specific cases
  • Document scope decisions
  • Avoid conflicting rules across scopes
  • Monitor alert mode violations before enforcing
  • Gradually transition from alert to enforce
  • Use bypass mode for troubleshooting
  • Set up alerting for policy violations
  • Review enforcement effectiveness regularly

Error Handling

Common Errors

ErrInvalidNetworkPolicyScope (types/network_policy.go:24)
ErrInvalidNetworkPolicyScope = errs.InvalidArgumentError("invalid network policy scope")
Returned when the policy scope is not one of the supported types. ErrNetworkPolicyNotFound (types/network_policy.go:67)
ErrNetworkPolicyNotFound = errs.NotFoundError("network policy not found")
Returned when querying a non-existent policy. ErrNetworkPolicyAlreadyExists (types/network_policy.go:73)
ErrNetworkPolicyAlreadyExists = errs.ConflictError("network policy already exists")
Returned when trying to create a duplicate policy for a scope. ErrInvalidNetworkPolicyRuleValue (types/network_policy.go:43)
ErrInvalidNetworkPolicyRuleValue = errs.InvalidArgumentError("invalid network policy rule value")
Returned when the rule value (CIDR or domain) is malformed.
  • Agents - Components that enforce network policies
  • Events - Detections that can trigger policy rules
  • Issues - Security concerns that lead to policy actions

Build docs developers (and LLMs) love