# 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 ```yaml 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 ```yaml 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 ```yaml 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 ```yaml 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: ```bash # 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: ```yaml 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 ```yaml 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) ```bash # 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 ```bash # 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 ```bash # 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.