The ephemos library now provides an HTTP client with built-in SPIFFE certificate authentication, enabling secure HTTP communication between services using the same identity verification as gRPC connections.
- 🔒 SPIFFE Certificate Authentication: HTTP requests use the same SPIFFE certificates as gRPC connections
- 🔄 Certificate Rotation: Automatic handling of certificate lifecycle
- ⚡ Connection Pooling: Optimized HTTP transport configuration
- 🛡️ Security by Default: Secure TLS configuration with proper validation
package main
import (
"context"
"fmt"
"io"
"log"
"github.com/sufield/ephemos/pkg/ephemos"
)
func main() {
ctx := context.Background()
// Create ephemos client
client, err := ephemos.IdentityClient(ctx, "/etc/ephemos/config.yaml")
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
// Connect to a service
conn, err := client.Connect(ctx, "payment-service", "https://payment.example.com")
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
// Get HTTP client with SPIFFE authentication
httpClient := conn.HTTPClient()
// Make authenticated HTTP requests
resp, err := httpClient.Get("https://payment.example.com/api/balance")
if err != nil {
log.Fatalf("HTTP request failed: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response: %v", err)
}
fmt.Printf("Response: %s\n", body)
}Ephemos focuses on authentication - use your existing service discovery solution:
// Use your existing service discovery (Kubernetes, Consul, etc.)
address, err := kubernetesClient.GetServiceEndpoint("payment-service")
if err != nil {
log.Fatalf("Service lookup failed: %v", err)
}
// Ephemos handles authentication to the discovered service
conn, err := client.Connect(ctx, "payment-service", address)
if err != nil {
log.Fatalf("Connection failed: %v", err)
}
defer conn.Close()
// Use authenticated HTTP client
httpClient := conn.HTTPClient()
resp, err := httpClient.Get("https://" + address + "/api/balance")The HTTP client automatically:
- Uses the same SPIFFE certificates as gRPC connections
- Validates server certificates against the trust bundle
- Performs mutual TLS authentication
- Handles certificate rotation seamlessly
// The HTTP client includes secure TLS defaults:
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
InsecureSkipVerify: false, // Always validate certificates
}- Redirect Limiting: Maximum 10 redirects to prevent attacks
- Timeout Protection: Configurable timeouts for all operations
- Certificate Validation: Full certificate chain verification
- Connection Pooling: Secure connection reuse
The HTTP client includes optimized transport configuration:
transport := &http.Transport{
MaxIdleConns: 100, // Connection pool size
IdleConnTimeout: 90 * time.Second, // Connection reuse timeout
TLSHandshakeTimeout: 10 * time.Second, // TLS handshake timeout
ExpectContinueTimeout: 1 * time.Second, // 100-continue timeout
}client := &http.Client{
Timeout: 30 * time.Second, // Request timeout
Transport: transport, // Configured transport
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) >= 10 {
return fmt.Errorf("stopped after 10 redirects")
}
return nil
},
}// Look up service addresses using your service discovery
paymentAddr, _ := kubernetesClient.GetServiceEndpoint("payment-service")
orderAddr, _ := kubernetesClient.GetServiceEndpoint("order-service")
// Connect to multiple services with authentication
paymentConn, err := client.Connect(ctx, "payment-service", paymentAddr)
if err != nil {
log.Fatalf("Failed to connect to payment service: %v", err)
}
defer paymentConn.Close()
orderConn, err := client.Connect(ctx, "order-service", orderAddr)
if err != nil {
log.Fatalf("Failed to connect to order service: %v", err)
}
defer orderConn.Close()
// Each connection has its own authenticated HTTP client
paymentClient := paymentConn.HTTPClient()
orderClient := orderConn.HTTPClient()
// Make authenticated requests to different services
paymentResp, _ := paymentClient.Get("https://" + paymentAddr + "/api/balance")
orderResp, _ := orderClient.Get("https://" + orderAddr + "/api/orders")// Service discovery is handled by your infrastructure
address, err := consulClient.DiscoverService("payment-service")
if err != nil {
log.Printf("Service discovery failed: %v", err)
return
}
conn, err := client.Connect(ctx, "payment-service", address)
if err != nil {
switch {
case strings.Contains(err.Error(), "certificate"):
log.Printf("Certificate error: %v", err)
// Handle certificate issues
case strings.Contains(err.Error(), "connection"):
log.Printf("Connection error: %v", err)
// Handle connection errors
default:
log.Printf("Authentication error: %v", err)
// Handle other authentication errors
}
return
}
defer conn.Close()httpClient := conn.HTTPClient()
// Create custom request
req, err := http.NewRequestWithContext(ctx, "POST", "/api/transfer", bytes.NewReader(data))
if err != nil {
log.Fatalf("Failed to create request: %v", err)
}
// Add custom headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Request-ID", "12345")
// Execute request
resp, err := httpClient.Do(req)
if err != nil {
log.Fatalf("Request failed: %v", err)
}
defer resp.Body.Close()Before:
httpClient := &http.Client{Timeout: 30 * time.Second}
resp, err := httpClient.Get("https://payment.example.com/api/balance")After:
// Use existing service discovery
address, _ := kubernetesClient.GetServiceEndpoint("payment-service")
conn, err := ephemosClient.Connect(ctx, "payment-service", address)
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
httpClient := conn.HTTPClient() // Now with SPIFFE authentication
resp, err := httpClient.Get("https://" + address + "/api/balance")The HTTP client works seamlessly with existing HTTP frameworks and libraries:
// Works with any HTTP client library
import "github.com/go-resty/resty/v2"
httpClient := conn.HTTPClient()
restyClient := resty.NewWithClient(httpClient)
resp, err := restyClient.R().
SetHeader("Accept", "application/json").
Get("/api/data")- Reuse Connections: Create connections once and reuse them
- Proper Cleanup: Always defer
conn.Close() - Context Usage: Use proper context for cancellation
- Error Handling: Handle both connection and request errors
- Certificate Validation: Never disable certificate validation
- Timeouts: Set appropriate timeouts for your use case
- Redirect Limits: Keep redirect limits reasonable
- Service Identity: Verify service identity in responses
- Connection Pooling: Leverage built-in connection pooling
- Keep-Alive: Use persistent connections when possible
- Request Batching: Batch related requests when appropriate
- Connection Reuse: Reuse connections when making multiple requests
-
Service Connection Failures
Error: failed to connect to service payment-service at address- Verify service is running and accessible at the address
- Check network connectivity
- Ensure address format is correct (host:port)
-
Certificate Errors
Error: failed to extract TLS config for HTTP client- Verify SPIFFE certificates are valid
- Check certificate expiration
- Ensure trust bundle is current
-
Connection Timeouts
Error: context deadline exceeded- Increase timeout values
- Check network latency
- Verify service responsiveness
Enable debug logging to troubleshoot issues:
// Add debug logging
log.Printf("Connecting to service: %s", serviceName)
conn, err := client.ConnectByName(ctx, serviceName)
if err != nil {
log.Printf("Connection failed: %v", err)
return
}
defer conn.Close()
log.Printf("Connection established, creating HTTP client")
httpClient := conn.HTTPClient()| Feature | Ephemos HTTP Client | Standard HTTP | gRPC |
|---|---|---|---|
| SPIFFE Authentication | ✅ | ❌ | ✅ |
| Certificate Rotation | ✅ | ❌ | ✅ |
| REST API Support | ✅ | ✅ | ❌ |
| Binary Protocol | ❌ | ❌ | ✅ |
| Browser Compatible | ✅ | ✅ | ❌ |
| Identity-based Auth | ✅ | ❌ | ✅ |
- Load balancing support
- Circuit breaker integration
- Metrics and tracing
- Advanced connection pooling strategies
- WebSocket support with SPIFFE authentication