Merge pull request 'rate-limit' (#3) from rate-limit into main
Limiting the rates Introducing rate limiting seemed like a very sensible step in order to mitigate against some forms of DoS attacks. As of now, we set the rates to a fixed amount with no configuration ability. This might change later on, depending on the use cases.
This commit is contained in:
commit
a79a37d4d4
5 changed files with 63 additions and 5 deletions
2
build.py
2
build.py
|
@ -56,7 +56,7 @@ def test_schema(project):
|
||||||
"java -jar target/uberjar/c4k-forgejo-standalone.jar "
|
"java -jar target/uberjar/c4k-forgejo-standalone.jar "
|
||||||
+ "src/test/resources/forgejo-test/valid-config.yaml "
|
+ "src/test/resources/forgejo-test/valid-config.yaml "
|
||||||
+ "src/test/resources/forgejo-test/valid-auth.yaml | "
|
+ "src/test/resources/forgejo-test/valid-auth.yaml | "
|
||||||
+ "kubeconform --kubernetes-version 1.23.0 --strict --skip Certificate -",
|
+ """kubeconform --kubernetes-version 1.23.0 --strict --skip "Certificate, Middleware" -""",
|
||||||
shell=True,
|
shell=True,
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
[dda.c4k-common.postgres :as postgres]))
|
[dda.c4k-common.postgres :as postgres]))
|
||||||
|
|
||||||
(def config-defaults {:issuer "staging", :deploy-federated "false"})
|
(def config-defaults {:issuer "staging", :deploy-federated "false"})
|
||||||
|
(def rate-limit-defaults {:max-rate 10, :max-concurrent-requests 5})
|
||||||
|
|
||||||
(def config? (s/keys :req-un [::forgejo/fqdn
|
(def config? (s/keys :req-un [::forgejo/fqdn
|
||||||
::forgejo/mailer-from
|
::forgejo/mailer-from
|
||||||
|
@ -30,7 +31,7 @@
|
||||||
|
|
||||||
(def vol? (s/keys :req-un [::forgejo/volume-total-storage-size]))
|
(def vol? (s/keys :req-un [::forgejo/volume-total-storage-size]))
|
||||||
|
|
||||||
(defn k8s-objects [config auth]
|
(defn k8s-objects [config auth] ; ToDo: ADR for generate functions - vector or no vector?
|
||||||
(let [storage-class (if (contains? config :postgres-data-volume-path) :manual :local-path)]
|
(let [storage-class (if (contains? config :postgres-data-volume-path) :manual :local-path)]
|
||||||
(map yaml/to-string
|
(map yaml/to-string
|
||||||
(filter #(not (nil? %))
|
(filter #(not (nil? %))
|
||||||
|
@ -46,11 +47,12 @@
|
||||||
(postgres/generate-service)
|
(postgres/generate-service)
|
||||||
(forgejo/generate-deployment config)
|
(forgejo/generate-deployment config)
|
||||||
(forgejo/generate-service)
|
(forgejo/generate-service)
|
||||||
(forgejo/generate-service-ssh)
|
(forgejo/generate-service-ssh)
|
||||||
(forgejo/generate-data-volume config)
|
(forgejo/generate-data-volume config)
|
||||||
(forgejo/generate-appini-env config)
|
(forgejo/generate-appini-env config)
|
||||||
(forgejo/generate-secrets auth)]
|
(forgejo/generate-secrets auth)
|
||||||
(forgejo/generate-ingress-and-cert config)
|
(forgejo/generate-rate-limit-middleware rate-limit-defaults)] ; this does not have a vector as output
|
||||||
|
(forgejo/generate-rate-limit-ingress-and-cert config) ; this function has a vector as output
|
||||||
(when (contains? config :restic-repository)
|
(when (contains? config :restic-repository)
|
||||||
[(backup/generate-config config)
|
[(backup/generate-config config)
|
||||||
(backup/generate-secret auth)
|
(backup/generate-secret auth)
|
||||||
|
|
|
@ -42,6 +42,8 @@
|
||||||
(s/def ::mailer-pw pred/bash-env-string?)
|
(s/def ::mailer-pw pred/bash-env-string?)
|
||||||
(s/def ::issuer pred/letsencrypt-issuer?)
|
(s/def ::issuer pred/letsencrypt-issuer?)
|
||||||
(s/def ::volume-total-storage-size (partial pred/int-gt-n? 5))
|
(s/def ::volume-total-storage-size (partial pred/int-gt-n? 5))
|
||||||
|
(s/def ::max-rate int?)
|
||||||
|
(s/def ::max-concurrent-requests int?)
|
||||||
|
|
||||||
(def config? (s/keys :req-un [::fqdn
|
(def config? (s/keys :req-un [::fqdn
|
||||||
::mailer-from
|
::mailer-from
|
||||||
|
@ -53,6 +55,9 @@
|
||||||
::default-app-name
|
::default-app-name
|
||||||
::service-domain-whitelist]))
|
::service-domain-whitelist]))
|
||||||
|
|
||||||
|
(def rate-limit-config? (s/keys :req-un [::max-rate
|
||||||
|
::max-concurrent-requests]))
|
||||||
|
|
||||||
(def auth? (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password ::mailer-user ::mailer-pw]))
|
(def auth? (s/keys :req-un [::postgres/postgres-db-user ::postgres/postgres-db-password ::mailer-user ::mailer-pw]))
|
||||||
|
|
||||||
(def vol? (s/keys :req-un [::volume-total-storage-size]))
|
(def vol? (s/keys :req-un [::volume-total-storage-size]))
|
||||||
|
@ -119,6 +124,26 @@
|
||||||
:fqdns [fqdn]}
|
:fqdns [fqdn]}
|
||||||
config))))
|
config))))
|
||||||
|
|
||||||
|
(defn-spec generate-rate-limit-ingress-and-cert pred/map-or-seq?
|
||||||
|
[config config?]
|
||||||
|
(->
|
||||||
|
(generate-ingress-and-cert config) ; returns a vector
|
||||||
|
(#(assoc-in % ; Attention: heavily relying on the output order of ing/generate-ingress-and-cert
|
||||||
|
[1 :metadata :annotations :traefik.ingress.kubernetes.io/router.middlewares]
|
||||||
|
(str
|
||||||
|
(-> (second %) :metadata :annotations :traefik.ingress.kubernetes.io/router.middlewares)
|
||||||
|
", default-ratelimit@kubernetescrd")))))
|
||||||
|
|
||||||
|
|
||||||
|
; using :average and :burst seems sensible, :period may be interesting for fine tuning later on
|
||||||
|
(defn-spec generate-rate-limit-middleware pred/map-or-seq?
|
||||||
|
[config rate-limit-config?]
|
||||||
|
(let [{:keys [max-rate max-concurrent-requests]} config]
|
||||||
|
(->
|
||||||
|
(yaml/load-as-edn "forgejo/middleware-ratelimit.yaml")
|
||||||
|
(cm/replace-key-value :average max-rate)
|
||||||
|
(cm/replace-key-value :burst max-concurrent-requests))))
|
||||||
|
|
||||||
(defn-spec generate-data-volume pred/map-or-seq?
|
(defn-spec generate-data-volume pred/map-or-seq?
|
||||||
[config vol?]
|
[config vol?]
|
||||||
(let [{:keys [volume-total-storage-size]} config
|
(let [{:keys [volume-total-storage-size]} config
|
||||||
|
|
8
src/main/resources/forgejo/middleware-ratelimit.yaml
Normal file
8
src/main/resources/forgejo/middleware-ratelimit.yaml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: ratelimit
|
||||||
|
spec:
|
||||||
|
rateLimit: # Config options for rate limiting: https://doc.traefik.io/traefik/middlewares/http/ratelimit/
|
||||||
|
average: AVG
|
||||||
|
burst: BRS
|
|
@ -130,3 +130,26 @@
|
||||||
:storage-c2 "15Gi"}
|
:storage-c2 "15Gi"}
|
||||||
(th/map-diff (cut/generate-data-volume {:volume-total-storage-size 1})
|
(th/map-diff (cut/generate-data-volume {:volume-total-storage-size 1})
|
||||||
(cut/generate-data-volume {:volume-total-storage-size 15})))))
|
(cut/generate-data-volume {:volume-total-storage-size 15})))))
|
||||||
|
|
||||||
|
(deftest should-generate-middleware-ratelimit
|
||||||
|
(is (= {:apiVersion "traefik.containo.us/v1alpha1",
|
||||||
|
:kind "Middleware",
|
||||||
|
:metadata {:name "ratelimit"},
|
||||||
|
:spec {:rateLimit {:average 10, :burst 5}}}
|
||||||
|
(cut/generate-rate-limit-middleware {:max-rate 10, :max-concurrent-requests 5}))))
|
||||||
|
|
||||||
|
(deftest should-generate-middleware-ratelimit-ingress-and-cert
|
||||||
|
(is (= {:traefik.ingress.kubernetes.io/router.entrypoints "web, websecure",
|
||||||
|
:traefik.ingress.kubernetes.io/router.middlewares
|
||||||
|
"default-redirect-https@kubernetescrd, default-ratelimit@kubernetescrd",
|
||||||
|
:metallb.universe.tf/address-pool "public"}
|
||||||
|
(-> (second
|
||||||
|
(cut/generate-rate-limit-ingress-and-cert
|
||||||
|
{:fqdn "test.de"
|
||||||
|
:mailer-from ""
|
||||||
|
:mailer-host "m.t.de"
|
||||||
|
:mailer-port "123"
|
||||||
|
:service-noreply-address ""
|
||||||
|
:average 10
|
||||||
|
:burst 5}))
|
||||||
|
:metadata :annotations))))
|
||||||
|
|
Loading…
Reference in a new issue