Skip to main content
EVOKORE// BROWSE
>

./browse/prompts

3 NODES
🤖system prompt•7 months ago

api-design-principles

Master REST and GraphQL API design principles to build intuitive,

coding
⭐1
# API Design Principles Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers and stand the test of time. ## When to Use This Skill - Designing new REST or GraphQL APIs - Refactoring existing APIs for better usability - Establishing API design standards for your team - Reviewing API specifications before implementation - Migrating between API paradigms (REST to GraphQL, etc.) - Creating developer-friendly API documentation - Optimizing APIs for specific use cases (mobile, third-party integrations) ## Core Concepts ### 1. RESTful Design Principles **Resource-Oriented Architecture** - Resources are nouns (users, orders, products), not verbs - Use HTTP methods for actions (GET, POST, PUT, PATCH, DELETE) - URLs represent resource hierarchies - Consistent naming conventions **HTTP Methods Semantics:** - `GET`: Retrieve resources (idempotent, safe) - `POST`: Create new resources - `PUT`: Replace entire resource (idempotent) - `PATCH`: Partial resource updates - `DELETE`: Remove resources (idempotent) ### 2. GraphQL Design Principles **Schema-First Development** - Types define your domain model - Queries for reading data - Mutations for modifying data - Subscriptions for real-time updates **Query Structure:** - Clients request exactly what they need - Single endpoint, multiple operations - Strongly typed schema - Introspection built-in ### 3. API Versioning Strategies **URL Versioning:** ``` /api/v1/users /api/v2/users ``` **Header Versioning:** ``` Accept: application/vnd.api+json; version=1 ``` **Query Parameter Versioning:** ``` /api/users?version=1 ``` ## REST API Design Patterns ### Pattern 1: Resource Collection Design ```python # Good: Resource-oriented endpoints GET /api/users # List users (with pagination) POST /api/users # Create user GET /api/users/{id} # Get specific user PUT /api/users/{id} # Replace user PATCH /api/users/{id} # Update user fields DELETE /api/users/{id} # Delete user # Nested resources GET /api/users/{id}/orders # Get user's orders POST /api/users/{id}/orders # Create order for user # Bad: Action-oriented endpoints (avoid) POST /api/createUser POST /api/getUserById POST /api/deleteUser ``` ### Pattern 2: Pagination and Filtering ```python from typing import List, Optional from pydantic import BaseModel, Field class PaginationParams(BaseModel): page: int = Field(1, ge=1, description="Page number") page_size: int = Field(20, ge=1, le=100, description="Items per page") class FilterParams(BaseModel): status: Optional[str] = None created_after: Optional[str] = None search: Optional[str] = None class PaginatedResponse(BaseModel): items: List[dict] total: int page: int page_size: int pages: int @property def has_next(self) -> bool: return self.page < self.pages @property def has_prev(self) -> bool: return self.page > 1 # FastAPI endpoint example from fastapi import FastAPI, Query, Depends app = FastAPI() @app.get("/api/users", response_model=PaginatedResponse) async def list_users( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), status: Optional[str] = Query(None), search: Optional[str] = Query(None) ): # Apply filters query = build_query(status=status, search=search) # Count total total = await count_users(query) # Fetch page offset = (page - 1) * page_size users = await fetch_users(query, limit=page_size, offset=offset) return PaginatedResponse( items=users, total=total, page=page, page_size=page_size, pages=(total + page_size - 1) // page_size ) ``` ### Pattern 3: Error Handling and Status Codes ```python from fastapi import HTTPException, status from pydantic import BaseModel class ErrorResponse(BaseModel): error: str message: str details: Optional[dict] = None timestamp: str path: str class ValidationErrorDetail(BaseModel): field: str message: str value: Any # Consistent error responses STATUS_CODES = { "success": 200, "created": 201, "no_content": 204, "bad_request": 400, "unauthorized": 401, "forbidden": 403, "not_found": 404, "conflict": 409, "unprocessable": 422, "internal_error": 500 } def raise_not_found(resource: str, id: str): raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail={ "error": "NotFound", "message": f"{resource} not found", "details": {"id": id} } ) def raise_validation_error(errors: List[ValidationErrorDetail]): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail={ "error": "ValidationError", "message": "Request validation failed", "details": {"errors": [e.dict() for e in errors]} } ) # Example usage @app.get("/api/users/{user_id}") async def get_user(user_id: str): user = await fetch_user(user_id) if not user: raise_not_found("User", user_id) return user ``` ### Pattern 4: HATEOAS (Hypermedia as the Engine of Application State) ```python class UserResponse(BaseModel): id: str name: str email: str _links: dict @classmethod def from_user(cls, user: User, base_url: str): return cls( id=user.id, name=user.name, email=user.email, _links={ "self": {"href": f"{base_url}/api/users/{user.id}"}, "orders": {"href": f"{base_url}/api/users/{user.id}/orders"}, "update": { "href": f"{base_url}/api/users/{user.id}", "method": "PATCH" }, "delete": { "href": f"{base_url}/api/users/{user.id}", "method": "DELETE" } } ) ``` ## GraphQL Design Patterns ### Pattern 1: Schema Design ```graphql # schema.graphql # Clear type definitions type User { id: ID! email: String! name: String! createdAt: DateTime! # Relationships orders(first: Int = 20, after: String, status: OrderStatus): OrderConnection! profile: UserProfile } type Order { id: ID! status: OrderStatus! total: Money! items: [OrderItem!]! createdAt: DateTime! # Back-reference user: User! } # Pagination pattern (Relay-style) type OrderConnection { edges: [OrderEdge!]! pageInfo: PageInfo! totalCount: Int! } type OrderEdge { node: Order! cursor: String! } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } # Enums for type safety enum OrderStatus { PENDING CONFIRMED SHIPPED DELIVERED CANCELLED } # Custom scalars scalar DateTime scalar Money # Query root type Query { user(id: ID!): User users(first: Int = 20, after: String, search: String): UserConnection! order(id: ID!): Order } # Mutation root type Mutation { createUser(input: CreateUserInput!): CreateUserPayload! updateUser(input: UpdateUserInput!): UpdateUserPayload! deleteUser(id: ID!): DeleteUserPayload! createOrder(input: CreateOrderInput!): CreateOrderPayload! } # Input types for mutations input CreateUserInput { email: String! name: String! password: String! } # Payload types for mutations type CreateUserPayload { user: User errors: [Error!] } type Error { field: String message: String! } ``` ### Pattern 2: Resolver Design ```python from typing import Optional, List from ariadne import QueryType, MutationType, ObjectType from dataclasses import dataclass query = QueryType() mutation = MutationType() user_type = ObjectType("User") @query.field("user") async def resolve_user(obj, info, id: str) -> Optional[dict]: """Resolve single user by ID.""" return await fetch_user_by_id(id) @query.field("users") async def resolve_users( obj, info, first: int = 20, after: Optional[str] = None, search: Optional[str] = None ) -> dict: """Resolve paginated user list.""" # Decode cursor offset = decode_cursor(after) if after else 0 # Fetch users users = await fetch_users( limit=first + 1, # Fetch one extra to check hasNextPage offset=offset, search=search ) # Pagination has_next = len(users) > first if has_next: users = users[:first] edges = [ { "node": user, "cursor": encode_cursor(offset + i) } for i, user in enumerate(users) ] return { "edges": edges, "pageInfo": { "hasNextPage": has_next, "hasPreviousPage": offset > 0, "startCursor": edges[0]["cursor"] if edges else None, "endCursor": edges[-1]["cursor"] if edges else None }, "totalCount": await count_users(search=search) } @user_type.field("orders") async def resolve_user_orders(user: dict, info, first: int = 20) -> dict: """Resolve user's orders (N+1 prevention with DataLoader).""" # Use DataLoader to batch requests loader = info.context["loaders"]["orders_by_user"] orders = await loader.load(user["id"]) return paginate_orders(orders, first) @mutation.field("createUser") async def resolve_create_user(obj, info, input: dict) -> dict: """Create new user.""" try: # Validate input validate_user_input(input) # Create user user = await create_user( email=input["email"], name=input["name"], password=hash_password(input["password"]) ) return { "user": user, "errors": [] } except ValidationError as e: return { "user": None, "errors": [{"field": e.field, "message": e.message}] } ``` ### Pattern 3: DataLoader (N+1 Problem Prevention) ```python from aiodataloader import DataLoader from typing import List, Optional class UserLoader(DataLoader): """Batch load users by ID.""" async def batch_load_fn(self, user_ids: List[str]) -> List[Optional[dict]]: """Load multiple users in single query.""" users = await fetch_users_by_ids(user_ids) # Map results back to input order user_map = {user["id"]: user for user in users} return [user_map.get(user_id) for user_id in user_ids] class OrdersByUserLoader(DataLoader): """Batch load orders by user ID.""" async def batch_load_fn(self, user_ids: List[str]) -> List[List[dict]]: """Load orders for multiple users in single query.""" orders = await fetch_orders_by_user_ids(user_ids) # Group orders by user_id orders_by_user = {} for order in orders: user_id = order["user_id"] if user_id not in orders_by_user: orders_by_user[user_id] = [] orders_by_user[user_id].append(order) # Return in input order return [orders_by_user.get(user_id, []) for user_id in user_ids] # Context setup def create_context(): return { "loaders": { "user": UserLoader(), "orders_by_user": OrdersByUserLoader() } } ``` ## Best Practices ### REST APIs 1. **Consistent Naming**: Use plural nouns for collections (`/users`, not `/user`) 2. **Stateless**: Each request contains all necessary information 3. **Use HTTP Status Codes Correctly**: 2xx success, 4xx client errors, 5xx server errors 4. **Version Your API**: Plan for breaking changes from day one 5. **Pagination**: Always paginate large collections 6. **Rate Limiting**: Protect your API with rate limits 7. **Documentation**: Use OpenAPI/Swagger for interactive docs ### GraphQL APIs 1. **Schema First**: Design schema before writing resolvers 2. **Avoid N+1**: Use DataLoaders for efficient data fetching 3. **Input Validation**: Validate at schema and resolver levels 4. **Error Handling**: Return structured errors in mutation payloads 5. **Pagination**: Use cursor-based pagination (Relay spec) 6. **Deprecation**: Use `@deprecated` directive for gradual migration 7. **Monitoring**: Track query complexity and execution time ## Common Pitfalls - **Over-fetching/Under-fetching (REST)**: Fixed in GraphQL but requires DataLoaders - **Breaking Changes**: Version APIs or use deprecation strategies - **Inconsistent Error Formats**: Standardize error responses - **Missing Rate Limits**: APIs without limits are vulnerable to abuse - **Poor Documentation**: Undocumented APIs frustrate developers - **Ignoring HTTP Semantics**: POST for idempotent operations breaks expectations - **Tight Coupling**: API structure shouldn't mirror database schema ## Resources - **references/rest-best-practices.md**: Comprehensive REST API design guide - **references/graphql-schema-design.md**: GraphQL schema patterns and anti-patterns - **references/api-versioning-strategies.md**: Versioning approaches and migration paths - **assets/rest-api-template.py**: FastAPI REST API template - **assets/graphql-schema-template.graphql**: Complete GraphQL schema example - **assets/api-design-checklist.md**: Pre-implementation review checklist - **scripts/openapi-generator.py**: Generate OpenAPI specs from code
👍0
👁️0
🤖 Auto-discovered
🤖system prompt•7 months ago

openapi-spec-generation

Generate and maintain OpenAPI 3.1 specifications from code,

coding
⭐1
# OpenAPI Spec Generation Comprehensive patterns for creating, maintaining, and validating OpenAPI 3.1 specifications for RESTful APIs. ## When to Use This Skill - Creating API documentation from scratch - Generating OpenAPI specs from existing code - Designing API contracts (design-first approach) - Validating API implementations against specs - Generating client SDKs from specs - Setting up API documentation portals ## Core Concepts ### 1. OpenAPI 3.1 Structure ```yaml openapi: 3.1.0 info: title: API Title version: 1.0.0 servers: - url: https://api.example.com/v1 paths: /resources: get: ... components: schemas: ... securitySchemes: ... ``` ### 2. Design Approaches | Approach | Description | Best For | | ---------------- | ---------------------------- | ------------------- | | **Design-First** | Write spec before code | New APIs, contracts | | **Code-First** | Generate spec from code | Existing APIs | | **Hybrid** | Annotate code, generate spec | Evolving APIs | ## Templates ### Template 1: Complete API Specification ```yaml openapi: 3.1.0 info: title: User Management API description: | API for managing users and their profiles. ## Authentication All endpoints require Bearer token authentication. ## Rate Limiting - 1000 requests per minute for standard tier - 10000 requests per minute for enterprise tier version: 2.0.0 contact: name: API Support email: api-support@example.com url: https://docs.example.com license: name: MIT url: https://opensource.org/licenses/MIT servers: - url: https://api.example.com/v2 description: Production - url: https://staging-api.example.com/v2 description: Staging - url: http://localhost:3000/v2 description: Local development tags: - name: Users description: User management operations - name: Profiles description: User profile operations - name: Admin description: Administrative operations paths: /users: get: operationId: listUsers summary: List all users description: Returns a paginated list of users with optional filtering. tags: - Users parameters: - $ref: "#/components/parameters/PageParam" - $ref: "#/components/parameters/LimitParam" - name: status in: query description: Filter by user status schema: $ref: "#/components/schemas/UserStatus" - name: search in: query description: Search by name or email schema: type: string minLength: 2 maxLength: 100 responses: "200": description: Successful response content: application/json: schema: $ref: "#/components/schemas/UserListResponse" examples: default: $ref: "#/components/examples/UserListExample" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "429": $ref: "#/components/responses/RateLimited" security: - bearerAuth: [] post: operationId: createUser summary: Create a new user description: Creates a new user account and sends welcome email. tags: - Users requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateUserRequest" examples: standard: summary: Standard user value: email: user@example.com name: John Doe role: user admin: summary: Admin user value: email: admin@example.com name: Admin User role: admin responses: "201": description: User created successfully content: application/json: schema: $ref: "#/components/schemas/User" headers: Location: description: URL of created user schema: type: string format: uri "400": $ref: "#/components/responses/BadRequest" "409": description: Email already exists content: application/json: schema: $ref: "#/components/schemas/Error" security: - bearerAuth: [] /users/{userId}: parameters: - $ref: "#/components/parameters/UserIdParam" get: operationId: getUser summary: Get user by ID tags: - Users responses: "200": description: Successful response content: application/json: schema: $ref: "#/components/schemas/User" "404": $ref: "#/components/responses/NotFound" security: - bearerAuth: [] patch: operationId: updateUser summary: Update user tags: - Users requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateUserRequest" responses: "200": description: User updated content: application/json: schema: $ref: "#/components/schemas/User" "400": $ref: "#/components/responses/BadRequest" "404": $ref: "#/components/responses/NotFound" security: - bearerAuth: [] delete: operationId: deleteUser summary: Delete user tags: - Users - Admin responses: "204": description: User deleted "404": $ref: "#/components/responses/NotFound" security: - bearerAuth: [] - apiKey: [] components: schemas: User: type: object required: - id - email - name - status - createdAt properties: id: type: string format: uuid readOnly: true description: Unique user identifier email: type: string format: email description: User email address name: type: string minLength: 1 maxLength: 100 description: User display name status: $ref: "#/components/schemas/UserStatus" role: type: string enum: [user, moderator, admin] default: user avatar: type: string format: uri nullable: true metadata: type: object additionalProperties: true description: Custom metadata createdAt: type: string format: date-time readOnly: true updatedAt: type: string format: date-time readOnly: true UserStatus: type: string enum: [active, inactive, suspended, pending] description: User account status CreateUserRequest: type: object required: - email - name properties: email: type: string format: email name: type: string minLength: 1 maxLength: 100 role: type: string enum: [user, moderator, admin] default: user metadata: type: object additionalProperties: true UpdateUserRequest: type: object minProperties: 1 properties: name: type: string minLength: 1 maxLength: 100 status: $ref: "#/components/schemas/UserStatus" role: type: string enum: [user, moderator, admin] metadata: type: object additionalProperties: true UserListResponse: type: object required: - data - pagination properties: data: type: array items: $ref: "#/components/schemas/User" pagination: $ref: "#/components/schemas/Pagination" Pagination: type: object required: - page - limit - total - totalPages properties: page: type: integer minimum: 1 limit: type: integer minimum: 1 maximum: 100 total: type: integer minimum: 0 totalPages: type: integer minimum: 0 hasNext: type: boolean hasPrev: type: boolean Error: type: object required: - code - message properties: code: type: string description: Error code for programmatic handling message: type: string description: Human-readable error message details: type: array items: type: object properties: field: type: string message: type: string requestId: type: string description: Request ID for support parameters: UserIdParam: name: userId in: path required: true description: User ID schema: type: string format: uuid PageParam: name: page in: query description: Page number (1-based) schema: type: integer minimum: 1 default: 1 LimitParam: name: limit in: query description: Items per page schema: type: integer minimum: 1 maximum: 100 default: 20 responses: BadRequest: description: Invalid request content: application/json: schema: $ref: "#/components/schemas/Error" example: code: VALIDATION_ERROR message: Invalid request parameters details: - field: email message: Must be a valid email address Unauthorized: description: Authentication required content: application/json: schema: $ref: "#/components/schemas/Error" example: code: UNAUTHORIZED message: Authentication required NotFound: description: Resource not found content: application/json: schema: $ref: "#/components/schemas/Error" example: code: NOT_FOUND message: User not found RateLimited: description: Too many requests content: application/json: schema: $ref: "#/components/schemas/Error" headers: Retry-After: description: Seconds until rate limit resets schema: type: integer X-RateLimit-Limit: description: Request limit per window schema: type: integer X-RateLimit-Remaining: description: Remaining requests in window schema: type: integer examples: UserListExample: value: data: - id: "550e8400-e29b-41d4-a716-446655440000" email: "john@example.com" name: "John Doe" status: "active" role: "user" createdAt: "2024-01-15T10:30:00Z" pagination: page: 1 limit: 20 total: 1 totalPages: 1 hasNext: false hasPrev: false securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT token from /auth/login apiKey: type: apiKey in: header name: X-API-Key description: API key for service-to-service calls security: - bearerAuth: [] ``` ### Template 2: Code-First Generation (Python/FastAPI) ```python # FastAPI with automatic OpenAPI generation from fastapi import FastAPI, HTTPException, Query, Path, Depends from pydantic import BaseModel, Field, EmailStr from typing import Optional, List from datetime import datetime from uuid import UUID from enum import Enum app = FastAPI( title="User Management API", description="API for managing users and profiles", version="2.0.0", openapi_tags=[ {"name": "Users", "description": "User operations"}, {"name": "Profiles", "description": "Profile operations"}, ], servers=[ {"url": "https://api.example.com/v2", "description": "Production"}, {"url": "http://localhost:8000", "description": "Development"}, ], ) # Enums class UserStatus(str, Enum): active = "active" inactive = "inactive" suspended = "suspended" pending = "pending" class UserRole(str, Enum): user = "user" moderator = "moderator" admin = "admin" # Models class UserBase(BaseModel): email: EmailStr = Field(..., description="User email address") name: str = Field(..., min_length=1, max_length=100, description="Display name") class UserCreate(UserBase): role: UserRole = Field(default=UserRole.user) metadata: Optional[dict] = Field(default=None, description="Custom metadata") model_config = { "json_schema_extra": { "examples": [ { "email": "user@example.com", "name": "John Doe", "role": "user" } ] } } class UserUpdate(BaseModel): name: Optional[str] = Field(None, min_length=1, max_length=100) status: Optional[UserStatus] = None role: Optional[UserRole] = None metadata: Optional[dict] = None class User(UserBase): id: UUID = Field(..., description="Unique identifier") status: UserStatus role: UserRole avatar: Optional[str] = Field(None, description="Avatar URL") metadata: Optional[dict] = None created_at: datetime = Field(..., alias="createdAt") updated_at: Optional[datetime] = Field(None, alias="updatedAt") model_config = {"populate_by_name": True} class Pagination(BaseModel): page: int = Field(..., ge=1) limit: int = Field(..., ge=1, le=100) total: int = Field(..., ge=0) total_pages: int = Field(..., ge=0, alias="totalPages") has_next: bool = Field(..., alias="hasNext") has_prev: bool = Field(..., alias="hasPrev") class UserListResponse(BaseModel): data: List[User] pagination: Pagination class ErrorDetail(BaseModel): field: str message: str class ErrorResponse(BaseModel): code: str = Field(..., description="Error code") message: str = Field(..., description="Error message") details: Optional[List[ErrorDetail]] = None request_id: Optional[str] = Field(None, alias="requestId") # Endpoints @app.get( "/users", response_model=UserListResponse, tags=["Users"], summary="List all users", description="Returns a paginated list of users with optional filtering.", responses={ 400: {"model": ErrorResponse, "description": "Invalid request"}, 401: {"model": ErrorResponse, "description": "Unauthorized"}, }, ) async def list_users( page: int = Query(1, ge=1, description="Page number"), limit: int = Query(20, ge=1, le=100, description="Items per page"), status: Optional[UserStatus] = Query(None, description="Filter by status"), search: Optional[str] = Query(None, min_length=2, max_length=100), ): """ List users with pagination and filtering. - **page**: Page number (1-based) - **limit**: Number of items per page (max 100) - **status**: Filter by user status - **search**: Search by name or email """ # Implementation pass @app.post( "/users", response_model=User, status_code=201, tags=["Users"], summary="Create a new user", responses={ 400: {"model": ErrorResponse}, 409: {"model": ErrorResponse, "description": "Email already exists"}, }, ) async def create_user(user: UserCreate): """Create a new user and send welcome email.""" pass @app.get( "/users/{user_id}", response_model=User, tags=["Users"], summary="Get user by ID", responses={404: {"model": ErrorResponse}}, ) async def get_user( user_id: UUID = Path(..., description="User ID"), ): """Retrieve a specific user by their ID.""" pass @app.patch( "/users/{user_id}", response_model=User, tags=["Users"], summary="Update user", responses={ 400: {"model": ErrorResponse}, 404: {"model": ErrorResponse}, }, ) async def update_user( user_id: UUID = Path(..., description="User ID"), user: UserUpdate = ..., ): """Update user attributes.""" pass @app.delete( "/users/{user_id}", status_code=204, tags=["Users", "Admin"], summary="Delete user", responses={404: {"model": ErrorResponse}}, ) async def delete_user( user_id: UUID = Path(..., description="User ID"), ): """Permanently delete a user.""" pass # Export OpenAPI spec if __name__ == "__main__": import json print(json.dumps(app.openapi(), indent=2)) ``` ### Template 3: Code-First (TypeScript/Express with tsoa) ```typescript // tsoa generates OpenAPI from TypeScript decorators import { Controller, Get, Post, Patch, Delete, Route, Path, Query, Body, Response, SuccessResponse, Tags, Security, Example, } from "tsoa"; // Models interface User { /** Unique identifier */ id: string; /** User email address */ email: string; /** Display name */ name: string; status: UserStatus; role: UserRole; /** Avatar URL */ avatar?: string; /** Custom metadata */ metadata?: Record<string, unknown>; createdAt: Date; updatedAt?: Date; } enum UserStatus { Active = "active", Inactive = "inactive", Suspended = "suspended", Pending = "pending", } enum UserRole { User = "user", Moderator = "moderator", Admin = "admin", } interface CreateUserRequest { email: string; name: string; role?: UserRole; metadata?: Record<string, unknown>; } interface UpdateUserRequest { name?: string; status?: UserStatus; role?: UserRole; metadata?: Record<string, unknown>; } interface Pagination { page: number; limit: number; total: number; totalPages: number; hasNext: boolean; hasPrev: boolean; } interface UserListResponse { data: User[]; pagination: Pagination; } interface ErrorResponse { code: string; message: string; details?: { field: string; message: string }[]; requestId?: string; } @Route("users") @Tags("Users") export class UsersController extends Controller { /** * List all users with pagination and filtering * @param page Page number (1-based) * @param limit Items per page (max 100) * @param status Filter by user status * @param search Search by name or email */
👍0
👁️0
🤖 Auto-discovered
🤖system prompt•7 months ago

k8s-security-policies

Implement Kubernetes security policies including NetworkPolicy,

architecture
⭐1
# Kubernetes Security Policies Comprehensive guide for implementing NetworkPolicy, PodSecurityPolicy, RBAC, and Pod Security Standards in Kubernetes. ## Purpose Implement defense-in-depth security for Kubernetes clusters using network policies, pod security standards, and RBAC. ## When to Use This Skill - Implement network segmentation - Configure pod security standards - Set up RBAC for least-privilege access - Create security policies for compliance - Implement admission control - Secure multi-tenant clusters ## Pod Security Standards ### 1. Privileged (Unrestricted) ```yaml apiVersion: v1 kind: Namespace metadata: name: privileged-ns labels: pod-security.kubernetes.io/enforce: privileged pod-security.kubernetes.io/audit: privileged pod-security.kubernetes.io/warn: privileged ``` ### 2. Baseline (Minimally restrictive) ```yaml apiVersion: v1 kind: Namespace metadata: name: baseline-ns labels: pod-security.kubernetes.io/enforce: baseline pod-security.kubernetes.io/audit: baseline pod-security.kubernetes.io/warn: baseline ``` ### 3. Restricted (Most restrictive) ```yaml apiVersion: v1 kind: Namespace metadata: name: restricted-ns labels: pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/audit: restricted pod-security.kubernetes.io/warn: restricted ``` ## Network Policies ### Default Deny All ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: production spec: podSelector: {} policyTypes: - Ingress - Egress ``` ### Allow Frontend to Backend ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: production spec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080 ``` ### Allow DNS ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-dns namespace: production spec: podSelector: {} policyTypes: - Egress egress: - to: - namespaceSelector: matchLabels: name: kube-system ports: - protocol: UDP port: 53 ``` **Reference:** See `assets/network-policy-template.yaml` ## RBAC Configuration ### Role (Namespace-scoped) ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pod-reader namespace: production rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"] ``` ### ClusterRole (Cluster-wide) ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: secret-reader rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "watch", "list"] ``` ### RoleBinding ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods namespace: production subjects: - kind: User name: jane apiGroup: rbac.authorization.k8s.io - kind: ServiceAccount name: default namespace: production roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io ``` **Reference:** See `references/rbac-patterns.md` ## Pod Security Context ### Restricted Pod ```yaml apiVersion: v1 kind: Pod metadata: name: secure-pod spec: securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000 seccompProfile: type: RuntimeDefault containers: - name: app image: myapp:1.0 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL ``` ## Policy Enforcement with OPA Gatekeeper ### ConstraintTemplate ```yaml apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8srequiredlabels spec: crd: spec: names: kind: K8sRequiredLabels validation: openAPIV3Schema: type: object properties: labels: type: array items: type: string targets: - target: admission.k8s.gatekeeper.sh rego: | package k8srequiredlabels violation[{"msg": msg, "details": {"missing_labels": missing}}] { provided := {label | input.review.object.metadata.labels[label]} required := {label | label := input.parameters.labels[_]} missing := required - provided count(missing) > 0 msg := sprintf("missing required labels: %v", [missing]) } ``` ### Constraint ```yaml apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredLabels metadata: name: require-app-label spec: match: kinds: - apiGroups: ["apps"] kinds: ["Deployment"] parameters: labels: ["app", "environment"] ``` ## Service Mesh Security (Istio) ### PeerAuthentication (mTLS) ```yaml apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: default namespace: production spec: mtls: mode: STRICT ``` ### AuthorizationPolicy ```yaml apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: allow-frontend namespace: production spec: selector: matchLabels: app: backend action: ALLOW rules: - from: - source: principals: ["cluster.local/ns/production/sa/frontend"] ``` ## Best Practices 1. **Implement Pod Security Standards** at namespace level 2. **Use Network Policies** for network segmentation 3. **Apply least-privilege RBAC** for all service accounts 4. **Enable admission control** (OPA Gatekeeper/Kyverno) 5. **Run containers as non-root** 6. **Use read-only root filesystem** 7. **Drop all capabilities** unless needed 8. **Implement resource quotas** and limit ranges 9. **Enable audit logging** for security events 10. **Regular security scanning** of images ## Compliance Frameworks ### CIS Kubernetes Benchmark - Use RBAC authorization - Enable audit logging - Use Pod Security Standards - Configure network policies - Implement secrets encryption at rest - Enable node authentication ### NIST Cybersecurity Framework - Implement defense in depth - Use network segmentation - Configure security monitoring - Implement access controls - Enable logging and monitoring ## Troubleshooting **NetworkPolicy not working:** ```bash # Check if CNI supports NetworkPolicy kubectl get nodes -o wide kubectl describe networkpolicy <name> ``` **RBAC permission denied:** ```bash # Check effective permissions kubectl auth can-i list pods --as system:serviceaccount:default:my-sa kubectl auth can-i '*' '*' --as system:serviceaccount:default:my-sa ``` ## Reference Files - `assets/network-policy-template.yaml` - Network policy examples - `assets/pod-security-template.yaml` - Pod security policies - `references/rbac-patterns.md` - RBAC configuration patterns ## Related Skills - `k8s-manifest-generator` - For creating secure manifests - `gitops-workflow` - For automated policy deployment
👍0
👁️0
🤖 Auto-discovered