├── .gitignore ├── postgrest ├── product.conf └── Dockerfile ├── grafana ├── provisioning │ ├── datasources │ │ └── all.yaml │ └── dashboards │ │ └── all.yaml ├── config │ └── grafana.ini └── dashboards │ └── apisix-grafana-dashboard.json ├── apisix └── config.yml ├── script ├── 4_monitoring.sh ├── 2_protect_ddos.sh ├── 1_setup.sh └── 3_authorize_endpoints.sh ├── prometheus └── prometheus.yml ├── postgres ├── a-schema.sql └── b-data.sql └── docker-compose.yml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | -------------------------------------------------------------------------------- /postgrest/product.conf: -------------------------------------------------------------------------------- 1 | db-uri = "postgres://authenticator:apacheapisixrocks@postgres:5432/postgres" 2 | db-schemas = "api" 3 | db-anon-role = "web" 4 | -------------------------------------------------------------------------------- /grafana/provisioning/datasources/all.yaml: -------------------------------------------------------------------------------- 1 | datasources: 2 | - access: 'proxy' 3 | editable: true 4 | is_default: true 5 | name: 'apisix' 6 | org_id: 1 7 | type: 'prometheus' 8 | url: 'http://prometheus:9090' 9 | version: 1 10 | -------------------------------------------------------------------------------- /grafana/provisioning/dashboards/all.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'default' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: false 9 | editable: false 10 | options: 11 | path: /var/lib/grafana/dashboards 12 | -------------------------------------------------------------------------------- /apisix/config.yml: -------------------------------------------------------------------------------- 1 | apisix: 2 | allow_admin: 3 | - 0.0.0.0/0 4 | etcd: 5 | host: 6 | - "http://etcd:2397" 7 | prefix: "/apisix" 8 | timeout: 30 9 | plugin_attr: 10 | prometheus: 11 | export_addr: 12 | ip: "0.0.0.0" 13 | port: 9091 14 | -------------------------------------------------------------------------------- /script/4_monitoring.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/global_rules/2 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' 3 | { 4 | "plugins": { 5 | "prometheus": {} 6 | } 7 | }' 8 | -------------------------------------------------------------------------------- /prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 5s 3 | external_labels: 4 | stack: "apisix" 5 | scrape_configs: 6 | - job_name: "prometheus" 7 | static_configs: 8 | - targets: [ "localhost:9090" ] 9 | - job_name: "apisix" 10 | metrics_path: "/apisix/prometheus/metrics" 11 | static_configs: 12 | - targets: [ "apisix:9091" ] 13 | -------------------------------------------------------------------------------- /script/2_protect_ddos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/global_rules/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' 3 | { 4 | "plugins": { 5 | "limit-count": { 6 | "count": 1, 7 | "time_window": 5, 8 | "rejected_code": 429 9 | } 10 | } 11 | }' 12 | -------------------------------------------------------------------------------- /postgres/a-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA api; 2 | 3 | CREATE TABLE api.product ( 4 | id SERIAL PRIMARY KEY, 5 | name TEXT NOT NULL, 6 | description TEXT NOT NULL, 7 | price NUMERIC NOT NULL, 8 | hero BOOLEAN NOT NULL DEFAULT FALSE 9 | ); 10 | 11 | CREATE ROLE web NOLOGIN; 12 | 13 | GRANT USAGE ON SCHEMA api to web; 14 | GRANT SELECT ON api.product TO web; 15 | 16 | CREATE ROLE authenticator NOINHERIT LOGIN PASSWORD 'apacheapisixrocks'; 17 | GRANT web TO authenticator; 18 | -------------------------------------------------------------------------------- /postgrest/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm-slim 2 | 3 | ARG POSTGREST_VERSION=v10.1.1 4 | ARG POSTGREST_FILE=postgrest-$POSTGREST_VERSION-linux-static-x64.tar.xz 5 | 6 | RUN mkdir postgrest 7 | 8 | WORKDIR postgrest 9 | 10 | ADD https://github.com/PostgREST/postgrest/releases/download/$POSTGREST_VERSION/$POSTGREST_FILE \ 11 | . 12 | 13 | RUN apt-get update && \ 14 | apt-get install -y libpq-dev xz-utils && \ 15 | tar xvf $POSTGREST_FILE && \ 16 | rm $POSTGREST_FILE 17 | -------------------------------------------------------------------------------- /script/1_setup.sh: -------------------------------------------------------------------------------- 1 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' 2 | { 3 | "type": "roundrobin", 4 | "nodes": { 5 | "postgrest:3000": 1 6 | } 7 | }' 8 | 9 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' 10 | { 11 | "uri": "/*", 12 | "upstream_id": 1 13 | }' 14 | -------------------------------------------------------------------------------- /postgres/b-data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO api.product (id, name, description, price, hero) VALUES 2 | (1, 'Stickers pack', 'A pack of rad stickers to display on your laptop or wherever you feel like. Show your love for Apache APISIX', 0.49, false), 3 | (2, 'Lapel pin', 'With this "Powered by Apache APISIX" lapel pin, support your favorite API Gateway and let everybody know about it.', 1.49, false), 4 | (3, 'Tee-Shirt', 'The classic geek product! At a conference, at home, at work, this tee-shirt will be your best friend.', 9.99, true) 5 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | apisix: 4 | image: apache/apisix:2.15.0-alpine 5 | volumes: 6 | - ./apisix/config.yml:/usr/local/apisix/conf/config.yaml:ro 7 | ports: 8 | - "9080:9080" 9 | restart: always 10 | depends_on: 11 | - etcd 12 | - postgrest 13 | etcd: 14 | image: bitnami/etcd:3.5.2 15 | environment: 16 | ETCD_ENABLE_V2: "true" 17 | ALLOW_NONE_AUTHENTICATION: "yes" 18 | ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2397" 19 | ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2397" 20 | postgrest: 21 | build: ./postgrest 22 | volumes: 23 | - ./postgrest/product.conf:/etc/product.conf:ro 24 | ports: 25 | - "3000:3000" 26 | entrypoint: ["/postgrest/postgrest"] 27 | command: ["/etc/product.conf"] 28 | depends_on: 29 | - postgres 30 | postgres: 31 | image: postgres:15-alpine 32 | environment: 33 | POSTGRES_PASSWORD: "root" 34 | volumes: 35 | - ./postgres:/docker-entrypoint-initdb.d:ro 36 | prometheus: 37 | image: prom/prometheus:v2.40.1 38 | volumes: 39 | - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml 40 | depends_on: 41 | - apisix 42 | grafana: 43 | image: grafana/grafana:8.5.15 44 | volumes: 45 | - ./grafana/provisioning:/etc/grafana/provisioning 46 | - ./grafana/dashboards:/var/lib/grafana/dashboards 47 | - ./grafana/config/grafana.ini:/etc/grafana/grafana.ini 48 | ports: 49 | - "3001:3001" 50 | depends_on: 51 | - prometheus 52 | -------------------------------------------------------------------------------- /script/3_authorize_endpoints.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' 3 | { 4 | "username": "admin", 5 | "plugins": { 6 | "key-auth": { 7 | "key": "admin" 8 | } 9 | } 10 | }' 11 | 12 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' 13 | { 14 | "username": "user", 15 | "plugins": { 16 | "key-auth": { 17 | "key": "user" 18 | } 19 | } 20 | }' 21 | 22 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE 23 | 24 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X POST -d ' 25 | { 26 | "uri": "/", 27 | "upstream_id": 1, 28 | "plugins": { 29 | "key-auth": {}, 30 | "consumer-restriction": { 31 | "whitelist": [ "admin" ] 32 | } 33 | } 34 | }' 35 | 36 | docker run --network poor-man-api_default --rm curlimages/curl:7.86.0 -v -i http://apisix:9080/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X POST -d ' 37 | { 38 | "uri": "/product", 39 | "upstream_id": 1, 40 | "plugins": { 41 | "key-auth": {}, 42 | "consumer-restriction": { 43 | "whitelist": [ "admin", "user" ] 44 | } 45 | } 46 | }' 47 | -------------------------------------------------------------------------------- /grafana/config/grafana.ini: -------------------------------------------------------------------------------- 1 | ##################### Grafana Configuration Example ##################### 2 | # 3 | # Everything has defaults so you only need to uncomment things you want to 4 | # change 5 | 6 | # possible values : production, development 7 | ;app_mode = production 8 | 9 | # instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty 10 | ;instance_name = ${HOSTNAME} 11 | 12 | #################################### Paths #################################### 13 | [paths] 14 | # Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) 15 | ;data = /var/lib/grafana 16 | 17 | # Temporary files in `data` directory older than given duration will be removed 18 | ;temp_data_lifetime = 24h 19 | 20 | # Directory where grafana can store logs 21 | ;logs = /var/log/grafana 22 | 23 | # Directory where grafana will automatically scan and look for plugins 24 | ;plugins = /var/lib/grafana/plugins 25 | 26 | # folder that contains provisioning config files that grafana will apply on startup and while running. 27 | ;provisioning = conf/provisioning 28 | 29 | #################################### Server #################################### 30 | [server] 31 | # Protocol (http, https, h2, socket) 32 | ;protocol = http 33 | 34 | # The ip address to bind to, empty will bind to all interfaces 35 | ;http_addr = 36 | 37 | # The http port to use 38 | http_port = 3001 39 | 40 | # The public facing domain name used to access grafana from a browser 41 | ;domain = localhost 42 | 43 | # Redirect to correct domain if host header does not match domain 44 | # Prevents DNS rebinding attacks 45 | ;enforce_domain = false 46 | 47 | # The full public facing url you use in browser, used for redirects and emails 48 | # If you use reverse proxy and sub path specify full url (with sub path) 49 | ;root_url = %(protocol)s://%(domain)s:%(http_port)s/ 50 | 51 | # Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. 52 | ;serve_from_sub_path = false 53 | 54 | # Log web requests 55 | ;router_logging = false 56 | 57 | # the path relative working path 58 | ;static_root_path = public 59 | 60 | # enable gzip 61 | ;enable_gzip = false 62 | 63 | # https certs & key file 64 | ;cert_file = 65 | ;cert_key = 66 | 67 | # Unix socket path 68 | ;socket = 69 | 70 | #################################### Database #################################### 71 | [database] 72 | # You can configure the database connection by specifying type, host, name, user and password 73 | # as separate properties or as on string using the url properties. 74 | 75 | # Either "mysql", "postgres" or "sqlite3", it's your choice 76 | ;type = sqlite3 77 | ;host = 127.0.0.1:3306 78 | ;name = grafana 79 | ;user = root 80 | # If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" 81 | ;password = 82 | 83 | # Use either URL or the previous fields to configure the database 84 | # Example: mysql://user:secret@host:port/database 85 | ;url = 86 | 87 | # For "postgres" only, either "disable", "require" or "verify-full" 88 | ;ssl_mode = disable 89 | 90 | ;ca_cert_path = 91 | ;client_key_path = 92 | ;client_cert_path = 93 | ;server_cert_name = 94 | 95 | # For "sqlite3" only, path relative to data_path setting 96 | ;path = grafana.db 97 | 98 | # Max idle conn setting default is 2 99 | ;max_idle_conn = 2 100 | 101 | # Max conn setting default is 0 (mean not set) 102 | ;max_open_conn = 103 | 104 | # Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) 105 | ;conn_max_lifetime = 14400 106 | 107 | # Set to true to log the sql calls and execution times. 108 | ;log_queries = 109 | 110 | # For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared) 111 | ;cache_mode = private 112 | 113 | #################################### Cache server ############################# 114 | [remote_cache] 115 | # Either "redis", "memcached" or "database" default is "database" 116 | ;type = database 117 | 118 | # cache connectionstring options 119 | # database: will use Grafana primary database. 120 | # redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. 121 | # memcache: 127.0.0.1:11211 122 | ;connstr = 123 | 124 | #################################### Data proxy ########################### 125 | [dataproxy] 126 | 127 | # This enables data proxy logging, default is false 128 | ;logging = false 129 | 130 | # How long the data proxy should wait before timing out default is 30 (seconds) 131 | ;timeout = 30 132 | 133 | # If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false. 134 | ;send_user_header = false 135 | 136 | #################################### Analytics #################################### 137 | [analytics] 138 | # Server reporting, sends usage counters to stats.grafana.org every 24 hours. 139 | # No ip addresses are being tracked, only simple counters to track 140 | # running instances, dashboard and error counts. It is very helpful to us. 141 | # Change this option to false to disable reporting. 142 | ;reporting_enabled = true 143 | 144 | # Set to false to disable all checks to https://grafana.net 145 | # for new vesions (grafana itself and plugins), check is used 146 | # in some UI views to notify that grafana or plugin update exists 147 | # This option does not cause any auto updates, nor send any information 148 | # only a GET request to http://grafana.com to get latest versions 149 | ;check_for_updates = true 150 | 151 | # Google Analytics universal tracking code, only enabled if you specify an id here 152 | ;google_analytics_ua_id = 153 | 154 | # Google Tag Manager ID, only enabled if you specify an id here 155 | ;google_tag_manager_id = 156 | 157 | #################################### Security #################################### 158 | [security] 159 | # disable creation of admin user on first start of grafana 160 | ;disable_initial_admin_creation = false 161 | 162 | # default admin user, created on startup 163 | ;admin_user = admin 164 | 165 | # default admin password, can be changed before first start of grafana, or in profile settings 166 | ;admin_password = admin 167 | 168 | # used for signing 169 | ;secret_key = SW2YcwTIb9zpOOhoPsMm 170 | 171 | # disable gravatar profile images 172 | ;disable_gravatar = false 173 | 174 | # data source proxy whitelist (ip_or_domain:port separated by spaces) 175 | ;data_source_proxy_whitelist = 176 | 177 | # disable protection against brute force login attempts 178 | ;disable_brute_force_login_protection = false 179 | 180 | # set to true if you host Grafana behind HTTPS. default is false. 181 | ;cookie_secure = false 182 | 183 | # set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" 184 | ;cookie_samesite = none 185 | 186 | # set to true if you want to allow browsers to render Grafana in a ,