sx

package module
v0.0.0-...-0441cc5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 22, 2023 License: MIT Imports: 8 Imported by: 0

README

SX: a minimal, declarative API gateway

WARNING: not production ready. Use at your own risk! Need something better? Check out nginx, Caddy or Envoy

SX is an all-in-one HTTP API gateway. It's intended to be the least-intrusive and simple as possible. It does not support TLS by design: add it on top.

This doesn't mean it's not feature packed - in fact, it provides:

  • round-robin load balancing with multiple addresses (if running in Kubernetes you should just use the Service address)
  • service mapping in YAML
  • route path grouping/matching (with simple glob patterns)
  • route group authorization (Basic Auth or JWT Bearer token)
  • caching and rate limiting (WIP) (requires Redis)
    • both support key extraction from request parameters
  • Prometheus metrics (and provides a simple Grafana dashboard you can import and customize)

It's very simple to deploy, and provides standard YAML manifests for:

  • docker-compose (includes Redis; also includes pre-configured Prometheus and Grafana)
  • Kubernetes (Redis not included)

Importantly, it supports auto-reload for the Kubernetes ConfigMap. Note this can take a few seconds up to a few minutes depending on the kubelet's sync interval.

It's also stateless - which means you can simply horizontally scale it, run in on preemptible/spot nodes, whatever (you should bring your own scaling for Redis, though).

Running

From the command line: sx -l :7654 -pprof :6060 -f config.yml

In docker-compose using the provided manifest: docker-compose -f sx/docker-compose.yml up

In Kubernetes: import the kubernetes/*.yml example manifests using kustomize or your preferred tool, then customize its configuration, resources...

You can also manually deploy the official Docker image ghcr.io/trapped/sx; have a look on GitHub packages for the available tags.

Configuration

Here's an example configuration file:

redis:
  readaddresses:
    - localhost:6379
  writeaddresses:
    - localhost:6379

services:
  - name: example
    addresses:
      - localhost:8080
    routes:
      - routes:
          - name: private
            method: POST
            path: /private
            ratelimit:
              perminute: 30
            auth:
              bearer:
                publickey: '{"use":"sign","kty":"oct","kid":"005456ff-1262-4bf0-a608-8534e1fe2763","alg":"HS256","k":"L0FCL4hivd7ShePdJnzEEoqlwoOfCrkcqdbXdADNk0s523xV7C5Sr6GiRIMpvNIelEsR6ta7MZnELY4JoHrm_w"}'
          - name: home
            method: GET
            path: /*
            cache:
              ttl: 5m

The above example uses a symmetric key (oct) for simplicity - you should use something asymmetric like RSA or EC.

Routes are matched in the order they are defined.

SX prefixes service paths with /{service name}, so in the above example it would expose:

  • /example/private -> localhost:8080/private
  • /example/* -> localhost:8080/*

Caching

You can enable caching for groups or single routes by specifying at least a Time-To-Live.

The request URL is always used as cache key; you can optionally add more keys (to partition your cache).

Responses are only cached if their status code indicates an OK result (1xx, 2xx, 3xx).

Prometheus metrics and profiling

SX exposes Prometheus metrics on the address specified with -pprof (0.0.0.0:6060 by default) at /metrics.

You can also attach using go tool pprof.

It's recommended not exposing this port to the external world.

Prometheus metrics follow this format:

  • system (Go) metrics: go_*
  • route metrics: sx_route_* with labels service, route, method, path, status
  • cache metrics: sx_cache_* with labels service, route, method, path

Timings are always provided as seconds.

You can also find an example Grafana dashboard in grafana/.

A health check endpoint is also available at /healthz.

valyala/fasthttp support

SX partially supports valyala/fasthttp which can be enabled with the -fast flag. Many things don't work, though, for example:

  • request/response streaming: fasthttp currently buffers everything
  • cache/rate limiting
  • metrics

Kubernetes ConfigMap autoreload

Kubernetes allows developers to update ConfigMaps in place; it builds a symlink chain in the Pod volume mount: /config/config.yml -> [...] -> /config/..2021_12_18_11_04_31.11673452/config.yml.

Note that this only happens when you mount a ConfigMap as a directory!

To detect this, SX watches FS events for the whole /config/ directory, and checks for changes in the /config/config.yml symlink real path or target file.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrorNotFound   = Error{404, "not found"}
	ErrorForbidden  = Error{401, "forbidden"}
	ErrorBadMethod  = Error{405, "bad method"}
	ErrorBadGateway = Error{502, "bad gateway"}
)

Functions

This section is empty.

Types

type Auth

type Auth struct {
	Basic  *AuthBasic  `yaml:"basic"`
	Bearer *AuthBearer `yaml:"bearer"`
}

type AuthBasic

type AuthBasic struct {
	Username string `yaml:"username"`
	Password string `yaml:"password"`
}

func (*AuthBasic) Verify

func (ab *AuthBasic) Verify(username, password string) bool

type AuthBearer

type AuthBearer struct {
	PublicKey string `yaml:"publickey"`
	// contains filtered or unexported fields
}

func (*AuthBearer) Verify

func (ab *AuthBearer) Verify(token string) error

type Cache

type Cache struct {
	TTL  time.Duration `yaml:"ttl"`
	Keys []CacheKey    `yaml:"keys"`
}

type CacheKey

type CacheKey struct {
	Header *string `yaml:"header"`
	Query  *string `yaml:"query"`
}

type CacheKeyExtractor

type CacheKeyExtractor interface {
	ExtractHeader(name string) string
	ExtractQuery(name string) string
}

type CacheKeySet

type CacheKeySet []CacheKey

func (CacheKeySet) Extract

func (cc CacheKeySet) Extract(x CacheKeyExtractor) (ks []string)

type Error

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type GatewayConfig

type GatewayConfig struct {
	Redis    Redis      `yaml:"redis"`
	Services []*Service `yaml:"services"`
}

func (*GatewayConfig) Read

func (conf *GatewayConfig) Read(r io.Reader) error

Read reads GatewayConfig from an io.Reader strictly, returning any errors caused by invalid/missing values, required fields or extra fields.

type RateLimit

type RateLimit struct {
	PerDay    *int       `yaml:"day"`
	PerHour   *int       `yaml:"hour"`
	PerMinute *int       `yaml:"minute"`
	PerSecond *int       `yaml:"second"`
	Keys      []CacheKey `yaml:"keys"`
}

type Redis

type Redis struct {
	ReadAddresses  []string `yaml:"readaddresses"`
	WriteAddresses []string `yaml:"writeaddresses"`
}

type Route

type Route struct {
	Pattern         string
	CompiledPattern glob.Glob

	RouteGroup *RouteGroup
}

func NewRoute

func NewRoute(r *RouteGroup) (Route, error)

func (*Route) Match

func (r *Route) Match(path string) bool

type RouteGroup

type RouteGroup struct {
	ParentService *Service    `yaml:"-"`
	Parent        *RouteGroup `yaml:"-"`

	Name   string         `yaml:"name"`
	Method string         `yaml:"method"`
	Path   string         `yaml:"path"`
	Routes *[]*RouteGroup `yaml:"routes"`

	Auth      *Auth      `yaml:"auth"`
	Cache     *Cache     `yaml:"cache"`
	RateLimit *RateLimit `yaml:"ratelimit"`
}

func (*RouteGroup) AbsolutePath

func (rg *RouteGroup) AbsolutePath() string

AbsolutePath constructs the absolute path for the route or group.

func (*RouteGroup) Walk

func (rg *RouteGroup) Walk(walkFunc func(parent, group *RouteGroup) error) error

Walk calls walkFunc recursively on all defined routes until it returns error.

type Service

type Service struct {
	Name       string `yaml:"name"`
	PathPrefix string `yaml:"-"`

	Addresses []string `yaml:"addresses"`

	Routes []*RouteGroup `yaml:"routes"`
}

func (*Service) CompileRoutes

func (s *Service) CompileRoutes() (newroutes []Route, err error)

Directories

Path Synopsis
cmd
sx
pkg
jwt
redis
package redis provides a Redis cache backend implementation.
package redis provides a Redis cache backend implementation.
tricks
package tricks provides Go tricks of many kinds.
package tricks provides Go tricks of many kinds.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL