Files
Maison/arti-api/NETWORK_POLICIES.md
2026-02-10 12:12:11 +01:00

12 KiB

Kubernetes Network Policies for Artifactory Services

This document provides NetworkPolicy configurations to restrict access to artifactory services, allowing only root path access externally while keeping all other endpoints internal-only.

Network Policy Strategy

Access Control Rules:

  • External Access: Only / (root/health check endpoints)
  • Internal Access: All endpoints from 192.168.100.0/24 network
  • Service Communication: Allow pod-to-pod communication within namespace

Network Policies

1. Arti-API Network Policy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: arti-api-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: arti-api
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Allow internal network access to all endpoints
  - from:
    - ipBlock:
        cidr: 192.168.100.0/24
    ports:
    - protocol: TCP
      port: 8000
  
  # Allow external access only to health/status endpoints
  # Note: This requires an Ingress controller or service mesh
  # to handle path-based routing restrictions
  - from: []  # All external sources
    ports:
    - protocol: TCP
      port: 8000
  
  # Allow communication from other services in the same namespace
  - from:
    - namespaceSelector:
        matchLabels:
          name: default
    - podSelector: {}
    ports:
    - protocol: TCP
      port: 8000
  
  egress:
  # Allow outbound traffic for API functionality
  - to: []
    ports:
    - protocol: TCP
      port: 53  # DNS
    - protocol: UDP
      port: 53  # DNS
  - to:
    - podSelector: {}  # Allow communication to other pods

2. Chart Museum Network Policy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: chartmuseum-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: chartmuseum
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Allow internal network access to all endpoints
  - from:
    - ipBlock:
        cidr: 192.168.100.0/24
    ports:
    - protocol: TCP
      port: 8080
  
  # Allow external access only to health endpoint
  - from: []
    ports:
    - protocol: TCP
      port: 8080
  
  # Allow communication from arti-api and other services
  - from:
    - podSelector:
        matchLabels:
          app: arti-api
    - namespaceSelector:
        matchLabels:
          name: default
    ports:
    - protocol: TCP
      port: 8080
  
  egress:
  # Allow outbound traffic
  - to: []
    ports:
    - protocol: TCP
      port: 53
    - protocol: UDP
      port: 53
  - to:
    - podSelector: {}

3. Docker Registry Network Policy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: docker-registry-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: docker-registry
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Allow internal network access to all endpoints
  - from:
    - ipBlock:
        cidr: 192.168.100.0/24
    ports:
    - protocol: TCP
      port: 5000
  
  # Allow external access only to health endpoint (/v2/)
  - from: []
    ports:
    - protocol: TCP
      port: 5000
  
  # Allow communication from arti-api and other services
  - from:
    - podSelector:
        matchLabels:
          app: arti-api
    - namespaceSelector:
        matchLabels:
          name: default
    ports:
    - protocol: TCP
      port: 5000
  
  egress:
  # Allow outbound traffic
  - to: []
    ports:
    - protocol: TCP
      port: 53
    - protocol: UDP
      port: 53
  - to:
    - podSelector: {}

Path-Based Access Control with Traefik v2

Since NetworkPolicy works at the network layer and cannot filter by HTTP paths, you need to combine it with Traefik IngressRoute for path-based restrictions.

Traefik v2 IngressRoute Configuration

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: arti-api-simple
  namespace: artifactory
spec:
  entryPoints:
    - web
  routes:
  # Internal network gets full access
  - match: Host(`api.artifactory.local`) && ClientIP(`192.168.100.0/24`)
    kind: Rule
    priority: 100
    services:
    - name: arti-api-service
      port: 8000
    middlewares:
    - name: internal-headers
  
  # External access - only health endpoints
  - match: Host(`api.artifactory.local`) && (Path(`/`) || Path(`/health`))
    kind: Rule
    priority: 90
    services:
    - name: arti-api-service
      port: 8000
    middlewares:
    - name: external-health-headers
  
  # Block all other external access
  - match: Host(`api.artifactory.local`)
    kind: Rule
    priority: 10
    services:
    - name: error-service
      port: 80
    middlewares:
    - name: block-external

Complete Traefik Configuration Files

Two versions are provided:

  1. traefik-simple.yaml - Simplified, easy to understand configuration
  2. traefik-ingressroute.yaml - Full-featured with TLS and advanced middlewares

Key Features:

  • Priority-based routing: Higher priority rules are evaluated first
  • ClientIP matching: Uses ClientIP() matcher to identify internal network
  • Path-based filtering: Specific paths allowed for external access
  • Custom error pages: Friendly 403 pages with helpful information
  • Middleware chaining: Headers and access control through middlewares

Deployment:

# Deploy the simplified version
kubectl apply -f traefik-simple.yaml

# Or deploy the full-featured version
kubectl apply -f traefik-ingressroute.yaml

Istio Service Mesh Configuration

If using Istio, you can implement more granular path-based access control:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: arti-api-access-control
  namespace: default
spec:
  selector:
    matchLabels:
      app: arti-api
  rules:
  # Allow internal network access to all paths
  - from:
    - source:
        ipBlocks: ["192.168.100.0/24"]
  
  # Allow external access only to health endpoints
  - to:
    - operation:
        paths: ["/", "/health"]
  
  # Deny all other external access
  - from:
    - source:
        notIpBlocks: ["192.168.100.0/24"]
    to:
    - operation:
        notPaths: ["/", "/health"]
    when:
    - key: request.headers[':path']
      notValues: ["/", "/health"]
    action: DENY

Complete Kubernetes Deployment with Network Policies

apiVersion: v1
kind: Namespace
metadata:
  name: artifactory
  labels:
    name: artifactory
---
# Arti-API Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: arti-api
  namespace: artifactory
  labels:
    app: arti-api
spec:
  replicas: 1
  selector:
    matchLabels:
      app: arti-api
  template:
    metadata:
      labels:
        app: arti-api
    spec:
      containers:
      - name: arti-api
        image: hexah/arti-api:1.0.1
        ports:
        - containerPort: 8000
        env:
        - name: PYTHONUNBUFFERED
          value: "1"
        volumeMounts:
        - name: artifactory-storage
          mountPath: /data
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
      volumes:
      - name: artifactory-storage
        persistentVolumeClaim:
          claimName: artifactory-pvc
---
# Arti-API Service
apiVersion: v1
kind: Service
metadata:
  name: arti-api-service
  namespace: artifactory
  labels:
    app: arti-api
spec:
  type: ClusterIP
  ports:
  - port: 8000
    targetPort: 8000
    protocol: TCP
  selector:
    app: arti-api
---
# Chart Museum Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chartmuseum
  namespace: artifactory
  labels:
    app: chartmuseum
spec:
  replicas: 1
  selector:
    matchLabels:
      app: chartmuseum
  template:
    metadata:
      labels:
        app: chartmuseum
    spec:
      containers:
      - name: chartmuseum
        image: chartmuseum/chartmuseum:latest
        ports:
        - containerPort: 8080
        env:
        - name: STORAGE
          value: "local"
        - name: STORAGE_LOCAL_ROOTDIR
          value: "/data/charts"
        - name: PORT
          value: "8080"
        - name: AUTH_ANONYMOUS_GET
          value: "false"
        - name: HTPASSWD_PATH
          value: "/data/htpasswd"
        - name: AUTH_REALM
          value: "Chart Museum"
        - name: ALLOW_OVERWRITE
          value: "true"
        - name: DISABLE_API
          value: "false"
        volumeMounts:
        - name: artifactory-storage
          mountPath: /data
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
      volumes:
      - name: artifactory-storage
        persistentVolumeClaim:
          claimName: artifactory-pvc
---
# Chart Museum Service
apiVersion: v1
kind: Service
metadata:
  name: chartmuseum-service
  namespace: artifactory
  labels:
    app: chartmuseum
spec:
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080
    protocol: TCP
  selector:
    app: chartmuseum
---
# Network Policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: artifactory-network-policy
  namespace: artifactory
spec:
  podSelector: {}  # Apply to all pods in namespace
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Allow internal network full access
  - from:
    - ipBlock:
        cidr: 192.168.100.0/24
  
  # Allow limited external access (health checks only)
  - from: []
    ports:
    - protocol: TCP
      port: 8000  # Arti-API
    - protocol: TCP
      port: 8080  # Chart Museum
    - protocol: TCP
      port: 5000  # Docker Registry
  
  # Allow inter-pod communication
  - from:
    - podSelector: {}
  
  egress:
  # Allow DNS
  - to: []
    ports:
    - protocol: TCP
      port: 53
    - protocol: UDP
      port: 53
  
  # Allow inter-pod communication
  - to:
    - podSelector: {}
  
  # Allow outbound internet (for package downloads, etc.)
  - to: []
    ports:
    - protocol: TCP
      port: 80
    - protocol: TCP
      port: 443
---
# PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: artifactory-pvc
  namespace: artifactory
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

Testing Network Policies

1. Test from Internal Network (192.168.100.x)

# Should work - internal access to all endpoints
curl http://arti-api-service.artifactory.svc.cluster.local:8000/users
curl http://chartmuseum-service.artifactory.svc.cluster.local:8080/api/charts

# Test from a pod in the internal network
kubectl run test-pod --rm -i --tty --image=curlimages/curl -- sh
# Inside the pod:
curl http://arti-api-service.artifactory.svc.cluster.local:8000/debian/packages

2. Test from External Network

# Should work - external access to health endpoints
curl http://your-ingress-ip/health
curl http://your-ingress-ip/

# Should be blocked - external access to management endpoints
curl http://your-ingress-ip/users  # Should return 403 or timeout
curl http://your-ingress-ip/debian/packages  # Should return 403 or timeout

3. Verify Network Policy

# Check network policies
kubectl get networkpolicies -n artifactory

# Describe policy
kubectl describe networkpolicy artifactory-network-policy -n artifactory

# Check if pods are selected by policy
kubectl get pods -n artifactory --show-labels

Key Points

  1. NetworkPolicy Limitations: NetworkPolicy works at Layer 3/4, not HTTP paths
  2. Path-Based Control: Use Ingress controllers or service mesh for HTTP path filtering
  3. Internal Network: 192.168.100.0/24 gets full access to all endpoints
  4. External Access: Limited to health check endpoints only
  5. Service Communication: Pods can communicate within the namespace
  6. DNS: Allow DNS traffic for service discovery

This configuration provides defense in depth by combining network-level and application-level access controls.