├── .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 ,