Course Navigation
← Back to Course OverviewAll Lessons
✓
Introduction and Database Fundamentals ✓
Building the Core Data Structure ✓
Concurrency and Thread Safety ✓
Append-Only Log (Write-Ahead Log) ✓
SSTables and LSM Trees ✓
Compaction and Optimization ✓
TCP Server and Protocol Design ✓
Client Library and Advanced Networking ✓
Transactions and ACID Properties ✓
Replication and High Availability ✓
Monitoring, Metrics, and Observability ✓
Performance Optimization and Tuning ✓
Configuration and Deployment 14
Security and Production Hardening 15
Final Project and Beyond Current Lesson
14 of 15
Progress 93%
Security and Production Hardening
Learning Objectives
- • Implement TLS/SSL encryption for secure communication
- • Build authentication systems with passwords and tokens
- • Design authorization and access control mechanisms
- • Implement rate limiting and DDoS protection
- • Apply security hardening best practices
- • Handle graceful shutdowns and rolling upgrades
Lesson 14.1: TLS/SSL Encryption
Generate TLS Certificates
# Generate private key
openssl genrsa -out server.key 2048
# Generate certificate signing request
openssl req -new -key server.key -out server.csr
# Self-signed certificate (for testing)
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
# For production: use CA-signed certificate TLS Server Implementation
import "crypto/tls"
// Create TLS listener
func (s *Server) StartTLS(certFile, keyFile string) error {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS13,
CipherSuites: []uint16{
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_AES_128_GCM_SHA256,
},
}
listener, err := tls.Listen("tcp", s.addr, tlsConfig)
if err != nil {
return err
}
return s.serve(listener)
}
// TLS client connection
func (c *Client) ConnectTLS(addr, certFile string) error {
cert, err := tls.LoadX509KeyPair(certFile, "")
if err != nil {
return err
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: false, // Set to true for self-signed certs
}
conn, err := tls.Dial("tcp", addr, tlsConfig)
if err != nil {
return err
}
c.conn = conn
return nil
} Lesson 14.2: Authentication
Password-based Authentication
import "golang.org/x/crypto/bcrypt"
// User represents a database user
type User struct {
Username string
PasswordHash []byte
Permissions []string
CreatedAt time.Time
LastLogin time.Time
}
// Authenticate user with password
func (s *Server) Authenticate(username, password string) (*User, error) {
// Lookup user
user, err := s.getUserByUsername(username)
if err != nil {
return nil, err
}
// Verify password hash
if err := bcrypt.CompareHashAndPassword(user.PasswordHash, []byte(password)); err != nil {
return nil, fmt.Errorf("invalid credentials")
}
// Update last login
user.LastLogin = time.Now()
s.updateUser(user)
return user, nil
}
// Hash password for storage
func hashPassword(password string) ([]byte, error) {
return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
}
// CreateUser creates a new user
func (s *Server) CreateUser(username, password string, permissions []string) error {
hash, err := hashPassword(password)
if err != nil {
return err
}
user := &User{
Username: username,
PasswordHash: hash,
Permissions: permissions,
CreatedAt: time.Now(),
}
return s.saveUser(user)
} Token-based Authentication
import "github.com/golang-jwt/jwt/v5"
// JWTClaims represents JWT token claims
type JWTClaims struct {
Username string `json:"username"`
Permissions []string `json:"permissions"`
jwt.RegisteredClaims
}
// GenerateToken creates JWT token
func (s *Server) GenerateToken(user *User) (string, error) {
claims := JWTClaims{
Username: user.Username,
Permissions: user.Permissions,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(s.jwtSecret))
}
// ValidateToken checks JWT token
func (s *Server) ValidateToken(tokenString string) (*JWTClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(s.jwtSecret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
return claims, nil
}
return nil, fmt.Errorf("invalid token")
} Lesson 14.3: Authorization
Role-Based Access Control (RBAC)
// Permission represents a specific permission
type Permission struct {
Resource string // e.g., "database", "backup", "config"
Action string // e.g., "read", "write", "delete", "admin"
}
// Role represents a user role
type Role struct {
Name string
Permissions []Permission
Description string
}
// Predefined roles
var (
AdminRole = Role{
Name: "admin",
Permissions: []Permission{
{Resource: "database", Action: "read"},
{Resource: "database", Action: "write"},
{Resource: "database", Action: "delete"},
{Resource: "backup", Action: "read"},
{Resource: "backup", Action: "write"},
{Resource: "config", Action: "read"},
{Resource: "config", Action: "write"},
},
Description: "Full access to all resources",
}
ReadOnlyRole = Role{
Name: "readonly",
Permissions: []Permission{
{Resource: "database", Action: "read"},
},
Description: "Read-only access to database",
}
BackupRole = Role{
Name: "backup",
Permissions: []Permission{
{Resource: "database", Action: "read"},
{Resource: "backup", Action: "read"},
{Resource: "backup", Action: "write"},
},
Description: "Database read and backup access",
}
)
// CheckPermission checks if user has permission
func (s *Server) CheckPermission(user *User, resource, action string) bool {
for _, roleName := range user.Roles {
role, exists := s.roles[roleName]
if !exists {
continue
}
for _, perm := range role.Permissions {
if perm.Resource == resource && perm.Action == action {
return true
}
}
}
return false
}
// AuthorizeRequest checks if request is authorized
func (s *Server) AuthorizeRequest(user *User, operation string) error {
var resource, action string
switch operation {
case "GET", "SCAN":
resource, action = "database", "read"
case "PUT", "POST":
resource, action = "database", "write"
case "DELETE":
resource, action = "database", "delete"
case "BACKUP":
resource, action = "backup", "write"
case "RESTORE":
resource, action = "backup", "read"
case "CONFIG":
resource, action = "config", "read"
default:
return fmt.Errorf("unknown operation: %s", operation)
}
if !s.CheckPermission(user, resource, action) {
return fmt.Errorf("access denied: insufficient permissions for %s on %s", action, resource)
}
return nil
} Lesson 14.4: Rate Limiting
Token Bucket Rate Limiter
import "golang.org/x/time/rate"
// RateLimiter controls request rate
type RateLimiter struct {
limiters map[string]*rate.Limiter
mu sync.RWMutex
burst int
}
// NewRateLimiter creates a new rate limiter
func NewRateLimiter() *RateLimiter {
return &RateLimiter{
limiters: make(map[string]*rate.Limiter),
burst: 10,
}
}
// AllowRequest checks if request allowed
func (rl *RateLimiter) AllowRequest(clientIP string, rps float64) bool {
rl.mu.RLock()
limiter, exists := rl.limiters[clientIP]
rl.mu.RUnlock()
if !exists {
limiter = rate.NewLimiter(rate.Limit(rps), rl.burst)
rl.mu.Lock()
rl.limiters[clientIP] = limiter
rl.mu.Unlock()
}
return limiter.Allow()
}
// WaitForToken waits for token to become available
func (rl *RateLimiter) WaitForToken(clientIP string, rps float64) error {
rl.mu.RLock()
limiter, exists := rl.limiters[clientIP]
rl.mu.RUnlock()
if !exists {
limiter = rate.NewLimiter(rate.Limit(rps), rl.burst)
rl.mu.Lock()
rl.limiters[clientIP] = limiter
rl.mu.Unlock()
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
return limiter.Wait(ctx)
} Sliding Window Rate Limiter
// SlidingWindowLimiter implements sliding window rate limiting
type SlidingWindowLimiter struct {
windows map[string]*SlidingWindow
mu sync.RWMutex
limit int
window time.Duration
}
// SlidingWindow represents a sliding window for a client
type SlidingWindow struct {
requests []time.Time
limit int
window time.Duration
mu sync.Mutex
}
// NewSlidingWindowLimiter creates a new sliding window limiter
func NewSlidingWindowLimiter(limit int, window time.Duration) *SlidingWindowLimiter {
return &SlidingWindowLimiter{
windows: make(map[string]*SlidingWindow),
limit: limit,
window: window,
}
}
// AllowRequest checks if request allowed in sliding window
func (swl *SlidingWindowLimiter) AllowRequest(clientIP string) bool {
swl.mu.RLock()
window, exists := swl.windows[clientIP]
swl.mu.RUnlock()
if !exists {
window = &SlidingWindow{
limit: swl.limit,
window: swl.window,
}
swl.mu.Lock()
swl.windows[clientIP] = window
swl.mu.Unlock()
}
return window.AllowRequest()
}
// AllowRequest checks if request allowed in this window
func (sw *SlidingWindow) AllowRequest() bool {
sw.mu.Lock()
defer sw.mu.Unlock()
now := time.Now()
cutoff := now.Add(-sw.window)
// Remove old requests
var validRequests []time.Time
for _, reqTime := range sw.requests {
if reqTime.After(cutoff) {
validRequests = append(validRequests, reqTime)
}
}
sw.requests = validRequests
// Check if under limit
if len(sw.requests) < sw.limit {
sw.requests = append(sw.requests, now)
return true
}
return false
} Lesson 14.5: Graceful Shutdown and Upgrades
Graceful Shutdown
// GracefulShutdown waits for connections to finish
func (s *Server) GracefulShutdown(timeout time.Duration) error {
s.logger.Info("starting graceful shutdown")
// Stop accepting new connections
if err := s.listener.Close(); err != nil {
s.logger.Error("error closing listener", zap.Error(err))
}
// Wait for active connections to finish
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
activeCount := s.getActiveConnectionCount()
if activeCount == 0 {
s.logger.Info("all connections closed")
return nil
}
s.logger.Info("waiting for connections to close",
zap.Int("active", activeCount),
zap.Duration("remaining", deadline.Sub(time.Now())),
)
time.Sleep(100 * time.Millisecond)
}
return fmt.Errorf("timeout waiting for connections to close")
}
// ShutdownWithSignal handles shutdown signals
func (s *Server) ShutdownWithSignal() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
s.logger.Info("received shutdown signal")
if err := s.GracefulShutdown(30 * time.Second); err != nil {
s.logger.Error("graceful shutdown failed", zap.Error(err))
os.Exit(1)
}
s.logger.Info("server shutdown complete")
os.Exit(0)
} Rolling Upgrades
// RollingUpgrade upgrades nodes one at a time
func (cluster *Cluster) RollingUpgrade(newImage string) error {
cluster.logger.Info("starting rolling upgrade", zap.String("image", newImage))
for i, node := range cluster.nodes {
cluster.logger.Info("upgrading node",
zap.Int("index", i),
zap.String("node", node.Address()),
)
// Remove node from load balancer
if err := cluster.deregisterNode(node); err != nil {
return fmt.Errorf("failed to deregister node %s: %w", node.Address(), err)
}
// Wait for in-flight requests to complete
cluster.logger.Info("waiting for in-flight requests", zap.String("node", node.Address()))
time.Sleep(10 * time.Second)
// Perform upgrade
if err := node.Upgrade(newImage); err != nil {
// Try to re-register node on failure
cluster.registerNode(node)
return fmt.Errorf("failed to upgrade node %s: %w", node.Address(), err)
}
// Wait for health check
cluster.logger.Info("waiting for node to become healthy", zap.String("node", node.Address()))
if err := node.WaitHealthy(30 * time.Second); err != nil {
// Try to re-register node on failure
cluster.registerNode(node)
return fmt.Errorf("node %s failed health check: %w", node.Address(), err)
}
// Add back to load balancer
if err := cluster.registerNode(node); err != nil {
return fmt.Errorf("failed to re-register node %s: %w", node.Address(), err)
}
cluster.logger.Info("node upgrade complete",
zap.Int("index", i),
zap.String("node", node.Address()),
)
}
cluster.logger.Info("rolling upgrade complete")
return nil
}
// CanaryUpgrade performs canary deployment
func (cluster *Cluster) CanaryUpgrade(newImage string, percentage int) error {
if percentage < 1 || percentage > 100 {
return fmt.Errorf("invalid percentage: %d", percentage)
}
canaryCount := len(cluster.nodes) * percentage / 100
if canaryCount == 0 {
canaryCount = 1
}
cluster.logger.Info("starting canary upgrade",
zap.String("image", newImage),
zap.Int("canary_count", canaryCount),
)
// Upgrade canary nodes
for i := 0; i < canaryCount; i++ {
node := cluster.nodes[i]
if err := cluster.upgradeNode(node, newImage); err != nil {
return fmt.Errorf("canary upgrade failed for node %s: %w", node.Address(), err)
}
}
// Monitor canary nodes
cluster.logger.Info("monitoring canary nodes", zap.Duration("duration", 5*time.Minute))
time.Sleep(5 * time.Minute)
// Check canary health
for i := 0; i < canaryCount; i++ {
node := cluster.nodes[i]
if !node.IsHealthy() {
return fmt.Errorf("canary node %s is unhealthy", node.Address())
}
}
cluster.logger.Info("canary upgrade successful, proceeding with full rollout")
return cluster.RollingUpgrade(newImage)
} Lab 14.1: Security Implementation
Objective
Build a comprehensive security system with TLS encryption, authentication, authorization, rate limiting, and security hardening.
Requirements
- • TLS/SSL Encryption: End-to-end encrypted communication
- • Authentication: Password and JWT token authentication
- • Authorization: Role-based access control (RBAC)
- • Rate Limiting: Token bucket and sliding window limiters
- • Security Hardening: Input validation, secure defaults
- • Audit Logging: Security event logging and monitoring
Starter Code
// TODO: Implement TLS server
func (s *Server) StartTLS(certFile, keyFile string) error {
// Load TLS certificates
// Configure TLS settings
// Start TLS listener
return nil
}
// TODO: Implement authentication
func (s *Server) Authenticate(username, password string) (*User, error) {
// Validate credentials
// Return user if valid
return nil, nil
}
// TODO: Implement authorization
func (s *Server) AuthorizeRequest(user *User, operation string) error {
// Check user permissions
// Return error if not authorized
return nil
}
// TODO: Implement rate limiting
func (s *Server) CheckRateLimit(clientIP string) bool {
// Check if request is within rate limit
// Return true if allowed
return true
}
// TODO: Implement security middleware
func (s *Server) SecurityMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Rate limiting
// Authentication
// Authorization
// Input validation
next.ServeHTTP(w, r)
})
} Test Template
func TestTLSConnection(t *testing.T) {
server := NewServer()
err := server.StartTLS("server.crt", "server.key")
assert.NoError(t, err)
// Test TLS client connection
client := NewClient()
err = client.ConnectTLS("localhost:6379", "server.crt")
assert.NoError(t, err)
}
func TestAuthentication(t *testing.T) {
server := NewServer()
// Create test user
err := server.CreateUser("testuser", "password123", []string{"read"})
assert.NoError(t, err)
// Test valid authentication
user, err := server.Authenticate("testuser", "password123")
assert.NoError(t, err)
assert.Equal(t, "testuser", user.Username)
// Test invalid authentication
_, err = server.Authenticate("testuser", "wrongpassword")
assert.Error(t, err)
}
func TestAuthorization(t *testing.T) {
server := NewServer()
// Create user with read-only permissions
user := &User{Username: "readonly", Roles: []string{"readonly"}}
// Test authorized operation
err := server.AuthorizeRequest(user, "GET")
assert.NoError(t, err)
// Test unauthorized operation
err = server.AuthorizeRequest(user, "PUT")
assert.Error(t, err)
}
func TestRateLimiting(t *testing.T) {
limiter := NewRateLimiter()
// Test within rate limit
for i := 0; i < 5; i++ {
allowed := limiter.AllowRequest("127.0.0.1", 10.0)
assert.True(t, allowed)
}
// Test rate limit exceeded
time.Sleep(100 * time.Millisecond)
allowed := limiter.AllowRequest("127.0.0.1", 0.1) // Very low rate
assert.False(t, allowed)
} Acceptance Criteria
- ✅ TLS encryption works for all connections
- ✅ Password authentication with bcrypt hashing
- ✅ JWT token authentication and validation
- ✅ Role-based access control (RBAC)
- ✅ Rate limiting prevents abuse
- ✅ Input validation prevents injection attacks
- ✅ Security audit logging implemented
- ✅ Graceful shutdown handles connections
- ✅ > 90% code coverage
- ✅ All tests pass
Summary: Week 14 Complete
By completing Week 14, you've learned and implemented:
1. TLS/SSL Encryption
- • Certificate generation and management
- • TLS server and client implementation
- • Secure cipher suite configuration
- • Certificate validation
2. Authentication
- • Password-based authentication
- • JWT token authentication
- • Password hashing with bcrypt
- • User session management
3. Authorization
- • Role-based access control (RBAC)
- • Permission-based authorization
- • Resource and action mapping
- • Access control enforcement
4. Rate Limiting
- • Token bucket rate limiting
- • Sliding window rate limiting
- • Per-client rate limiting
- • DDoS protection
Key Skills Mastered:
- ✅ Implement end-to-end encryption with TLS
- ✅ Build secure authentication systems
- ✅ Design authorization and access control
- ✅ Implement rate limiting and DDoS protection
- ✅ Apply security hardening best practices
- ✅ Handle graceful shutdowns and upgrades
Ready for the Capstone Project?
Next week we'll build a complete production-ready key-value database from scratch, incorporating all the concepts we've learned throughout the course.
Continue to Capstone Project →