Compare commits

...

19 Commits

Author SHA1 Message Date
c8fc143efa doc: add prometheus metrics documentation
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2024-02-21 12:29:03 +01:00
f91c14e5d4 feat(admin): print default writer token to logs by default
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2024-02-21 11:09:34 +01:00
1602626e8c Merge pull request 'fix(depends): update go.mod library versions' (#15) from fix/go-lib-versions into develop
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
Reviewed-on: #15
2024-02-05 11:24:02 +01:00
e2e38841f4 fix(depends): update go.mod library versions
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
Cadoles/bouncer/pipeline/pr-develop This commit looks good
2024-02-05 11:21:27 +01:00
c23d8e3adb Merge pull request 'fix(config): supporting multiple env variables in a value.' (#11) from fix/issue-9/multiple-env-variables into develop
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
Reviewed-on: #11
2024-02-05 11:13:47 +01:00
a3f44cf123 fix(config): supporting multiple env variables in a value.
ref #9
2024-02-05 11:13:47 +01:00
5453988419 Merge pull request 'fix(dockerfile): updating base images versions.' (#14) from fix/dockerfile into develop
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
Reviewed-on: #14
2024-02-05 11:08:50 +01:00
1e392f94a7 fix(dockerfile): updating base images versions.
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
Cadoles/bouncer/pipeline/pr-develop This commit looks good
Keep things up to date and security alerts away from trivi.

Using apk package for dumb-init
2024-02-05 11:04:28 +01:00
b44ff2a68e doc: add proxy http api reference
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-08 12:19:43 -06:00
c719fdca37 feat: add prometheus + grafterm dashboard in local dev environment 2023-07-08 12:18:38 -06:00
2b91c1e167 feat(store,repository): add more integration tests
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-07 12:22:31 -06:00
cebf1daf72 chore: update github.com/lestrrat-go/jwx/v2
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-07 10:20:27 -06:00
6734cf6526 Merge pull request 'Génération de l'image Docker via le pipeline Jenkins' (#7) from ci-docker-release into develop
Some checks reported errors
Cadoles/bouncer/pipeline/head Something is wrong with the build of this commit
Reviewed-on: #7
2023-07-07 18:11:06 +02:00
368273f1ee chore(ci): release docker image
Some checks are pending
Cadoles/bouncer/pipeline/pr-develop Build started...
2023-07-07 10:10:22 -06:00
553513d647 Merge pull request 'Implémentation du layer "circuitbreaker"' (#6) from circuitbreaker into develop
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
Reviewed-on: #6
2023-07-06 16:46:17 +02:00
60487c11d6 feat: optional real-ip middleware
All checks were successful
Cadoles/bouncer/pipeline/pr-develop This commit looks good
2023-07-06 08:16:17 -06:00
e6f18e7cd8 fix(doc): typo
All checks were successful
Cadoles/bouncer/pipeline/pr-develop This commit looks good
2023-07-06 07:59:20 -06:00
a207291c04 feat: implements circuitbreaker layer 2023-07-06 07:59:20 -06:00
64b5182f8b fix(doc): bad link
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-06 07:41:53 -06:00
34 changed files with 1215 additions and 73 deletions

View File

@ -1,4 +1,4 @@
FROM golang:1.20 AS BUILD FROM reg.cadoles.com/proxy_cache/library/golang:1.21.6 AS BUILD
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y make && apt-get install -y make
@ -21,17 +21,11 @@ RUN /src/dist/bouncer_linux_amd64_v1/bouncer -c '' config dump > /src/dist/bounc
&& yq -i '.admin.auth.privateKey = "/etc/bouncer/admin-key.json"' /src/dist/bouncer_linux_amd64_v1/config.yml \ && yq -i '.admin.auth.privateKey = "/etc/bouncer/admin-key.json"' /src/dist/bouncer_linux_amd64_v1/config.yml \
&& yq -i '.redis.adresses = ["redis:6379"]' /src/dist/bouncer_linux_amd64_v1/config.yml && yq -i '.redis.adresses = ["redis:6379"]' /src/dist/bouncer_linux_amd64_v1/config.yml
FROM alpine:3.18 AS RUNTIME FROM reg.cadoles.com/proxy_cache/library/alpine:3.19.1 AS RUNTIME
ARG DUMB_INIT_VERSION=1.2.5 RUN apk add --no-cache ca-certificates dumb-init
RUN apk add --no-cache ca-certificates ENTRYPOINT ["/usr/bin/dumb-init", "--"]
RUN mkdir -p /usr/local/bin \
&& wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_x86_64 \
&& chmod +x /usr/local/bin/dumb-init
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
RUN mkdir -p /usr/local/bin /usr/share/bouncer/bin /etc/bouncer RUN mkdir -p /usr/local/bin /usr/share/bouncer/bin /etc/bouncer

27
Jenkinsfile vendored
View File

@ -29,7 +29,7 @@ pipeline {
} }
} }
stage('Release') { stage('Release binaries and packages') {
when { when {
anyOf { anyOf {
branch 'master' branch 'master'
@ -50,6 +50,31 @@ pipeline {
} }
} }
} }
stage('Build and release Docker image') {
when {
anyOf {
branch 'master'
branch 'develop'
}
}
steps {
script {
withCredentials([
usernamePassword([
credentialsId: 'kipp-credentials',
usernameVariable: 'DOCKER_REGISTRY_USERNAME',
passwordVariable: 'DOCKER_REGISTRY_PASSWORD'
])
]) {
sh """
echo '${env.DOCKER_REGISTRY_PASSWORD}' | docker login --username '${env.DOCKER_REGISTRY_USERNAME}' --password-stdin reg.cadoles.com
make docker-build docker-release
"""
}
}
}
}
} }
post { post {

View File

@ -101,6 +101,12 @@ gitea-release: tools/gitea-release/bin/gitea-release.sh goreleaser
GITEA_RELEASE_ATTACHMENTS="$$(find .gitea-release/* -type f)" \ GITEA_RELEASE_ATTACHMENTS="$$(find .gitea-release/* -type f)" \
tools/gitea-release/bin/gitea-release.sh tools/gitea-release/bin/gitea-release.sh
grafterm: tools/grafterm/bin/grafterm
tools/grafterm/bin/grafterm -c ./misc/grafterm/dashboard.json -v job=bouncer-proxy -r 5s
siege:
siege -i -c 100 -f ./misc/siege/urls.txt
tools/gitea-release/bin/gitea-release.sh: tools/gitea-release/bin/gitea-release.sh:
mkdir -p tools/gitea-release/bin mkdir -p tools/gitea-release/bin
curl --output tools/gitea-release/bin/gitea-release.sh https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/gitea/gitea-release.sh curl --output tools/gitea-release/bin/gitea-release.sh https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/gitea/gitea-release.sh
@ -110,6 +116,10 @@ tools/modd/bin/modd:
mkdir -p tools/modd/bin mkdir -p tools/modd/bin
GOBIN=$(PWD)/tools/modd/bin go install github.com/cortesi/modd/cmd/modd@latest GOBIN=$(PWD)/tools/modd/bin go install github.com/cortesi/modd/cmd/modd@latest
tools/grafterm/bin/grafterm:
mkdir -p tools/grafterm/bin
GOBIN=$(PWD)/tools/grafterm/bin go install github.com/slok/grafterm/cmd/grafterm@v0.2.0
full-version: full-version:
@echo $(FULL_VERSION) @echo $(FULL_VERSION)
@ -128,4 +138,12 @@ run-redis:
redis-shell: redis-shell:
docker exec -it \ docker exec -it \
bouncer-redis \ bouncer-redis \
redis-cli redis-cli
run-prometheus:
docker kill bouncer-prometheus || exit 0
docker run --rm -t \
--name bouncer-prometheus \
--network host \
-v $(PWD)/misc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus

View File

@ -6,18 +6,21 @@
## Exemples ## Exemples
- [(FR) - Exemple de déploiement multi-noeuds](../misc/docker-compose/README.md) - [(FR) - Exemple de déploiement multi-noeuds](../misc/docker-compose/README.md)
## Référence ## Référence
- [(FR) - Layers](./fr/references/layers/README.md) - [(FR) - Layers](./fr/references/layers/README.md)
- [Fichier de configuration](../misc/packaging/common/config.yml) - [(FR) - Métriques](./fr/references/metrics.md)
- [(FR) - Fichier de configuration](../misc/packaging/common/config.yml)
- [(FR) - API d'administration](./fr/references/admin_api.md)
## Tutoriels ## Tutoriels
### Utilisation ### Utilisation
- [(FR) - Ajouter un calque de type "file d'attente"](./fr/tutorials/add-queue-layer.md) - [(FR) - Ajouter un layer de type "file d'attente"](./fr/tutorials/add-queue-layer.md)
### Développement ### Développement
- [(FR) - Démarrer avec les sources](./fr/tutorials/getting-start-with-sources.md) - [(FR) - Démarrer avec les sources](./fr/tutorials/getting-started-with-sources.md)
- [(FR) - Créer son propre layer](./fr/tutorials/create-custom-layer.md) - [(FR) - Créer son propre layer](./fr/tutorials/create-custom-layer.md)

View File

@ -0,0 +1,182 @@
# API d'administration
## Authentification
L'ensemble des appels aux APIs HTTP du service `bouncer-admin` sont authentifiées via l'utilisation d'un jeton [JWT](https://datatracker.ietf.org/doc/html/rfc7519) signé par la clé privée du serveur.
Le jeton d'accès doit être transmis avec l'ensemble des appels aux points d'entrée via l'entête HTTP `Authorization` en respectant la forme suivante:
```
Authorization: Bearer <jwt>
```
### Génération d'un jeton d'authentification
La génération d'un jeton d'authentification s'effectue via la commande suivante:
```shell
bouncer auth create-token --subject "<subject>" --role "<role>"
```
Où:
- `"<subject>"` est une chaîne de caractère arbitraire ayant pour objectif d'identifier de manière unique l'utilisateur associé au jeton;
- `"<role>"` peut prendre une des deux valeurs `reader` ou `writer` correspondant aux droits suivants respectifs:
- droit en lecture sur l'ensemble des entités (proxy, layer);
- droit en lecture ET en écriture sur l'ensemble des entités.
## Points d'entrée
### `POST /api/v1/proxies`
Créer un nouveau proxy
#### Exemple de corps de requête
```json5
{
"name": "myproxy", // OBLIGATOIRE - Nom du proxy
"to": "https://www.cadoles.com", // OBLIGATOIRE - Site distant ciblé par le proxy
"from": ["*"] // OPTIONNEL - Liste de patrons de filtrage associés au proxy
}
```
#### Exemple de résultat
```json5
{
"data": {
"proxy": {
"name": "myproxy",
"weight": 0,
"enabled": false,
"to": "https://www.cadoles.com",
"from": ["*"],
"createdAt": "2018-12-10T13:45:00.000Z",
"updatedAt": "2018-12-10T13:45:00.000Z"
}
}
}
```
#### Source
Voir [`internal/admin/proxy_route.go#createProxy()`](../../../internal/admin/proxy_route.go#createProxy)
### `GET /api/v1/proxies/{proxyName}`
Récupérer les informations complètes sur un proxy
#### Paramètres
- `{proxyName}` - Nom du proxy
#### Exemple de résultat
```json5
{
"data": {
"proxy": {
"name": "myproxy",
"weight": 0,
"enabled": false,
"to": "https://www.cadoles.com",
"from": ["*"],
"createdAt": "2018-12-10T13:45:00.000Z",
"updatedAt": "2018-12-10T13:45:00.000Z"
}
}
}
```
#### Source
Voir [`internal/admin/proxy_route.go#getProxy()`](../../../internal/admin/proxy_route.go#getProxy)
### `PUT /api/v1/proxies/{proxyName}`
Modifier un proxy
#### Exemple de corps de requête
```json5
{
"to": "https://www.cadoles.com", // OPTIONNEL - Site distant ciblé par le proxy
"from": ["mylocalproxydomain:*"], // OPTIONNEL - Liste de patrons de filtrage associés au proxy
"weight": 100, // OPTIONNEL - Poids à associer au proxy
"enabled": true, // OPTIONNEL - Activer/désactiver le proxy
}
```
#### Exemple de résultat
```json5
{
"data": {
"proxy": {
"name": "myproxy",
"weight": 100,
"enabled": true,
"to": "https://www.cadoles.com",
"from": ["mylocalproxydomain:*"],
"createdAt": "2018-12-10T13:45:00.000Z",
"updatedAt": "2020-10-02T15:09:00.000Z"
}
}
}
```
#### Source
Voir [`internal/admin/proxy_route.go#updateProxy()`](../../../internal/admin/proxy_route.go#updateProxy)
### `GET /api/v1/proxies?names={name1,name2,...}`
Lister les proxies existants
#### Paramètres
- `{names}` - Optionnel - Liste des noms de proxy à appliquer en tant que filtre
#### Exemple de résultat
```json5
{
"data": {
"proxies": [
{
"name": "myproxy",
"weight": 0,
"enabled": false,
}
]
}
}
```
#### Source
Voir [`internal/admin/proxy_route.go#queryProxy()`](../../../internal/admin/proxy_route.go#queryProxy)
## `DELETE /api/v1/proxies/{proxyName}`
Supprimer le proxy
#### Paramètres
- `{proxyName}` - Nom du proxy
#### Exemple de résultat
```json5
{
"data": {
"proxyName": "myproxy"
}
}
```
#### Source
Voir [`internal/admin/proxy_route.go#deleteProxy()`](../../../internal/admin/proxy_route.go#deleteProxy)

View File

@ -2,4 +2,5 @@
Vous trouverez ci-dessous la liste des entités "Layer" activables sur vos entité "Proxy": Vous trouverez ci-dessous la liste des entités "Layer" activables sur vos entité "Proxy":
- [Queue](./queue.md) - File d'attente dynamique - [Queue](./queue.md) - File d'attente dynamique
- [Circuit Breaker](./circuitbreaker.md) - Coupure d'accès à un site ou une sous section de celui ci

View File

@ -0,0 +1,41 @@
# Layer "Circuit Breaker"
## Description
Ce layer permet de bloquer l'accès à un site (ou une section de celui ci) ciblé par un proxy.
## Type
`circuitbreaker`
## Options
### `authorizedCIDRs`
- **Type:** `[]string`
- **Valeur par défaut:** `[]`
- **Description:** Autoriser les adresses distantes contenues dans un des masques réseau (en notation ["CIDR"](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation) définis à contourner la restriction d'accès.
### `matchURLs`
- **Type:** `[]string`
- **Valeur par défaut:** `["*"]`
- **Description:** Limiter l'action du layer à cette liste de patrons d'URLs.
Par exemple, si vous souhaitez limiter votre restriction d'accès à l'ensemble d'une section "`/blog`" d'un site, vous pouvez déclarer la valeur `["*/blog*"]`. Les autres URLs du site ne seront pas affectées par la restriction.
### `templateBlock`
- **Type:** `string`
- **Valeur par défaut:** `"default"`
- **Description:** Bloc du template HTML pour effectuer le rendu de la page indiquant la restriction d'accès.
Voir le [fichier de configuration de référence](../../../../misc/packaging/common/config.yml), section `layers.circuitbreaker` pour voir les options permettant de personnaliser le chemin du répertoire contenant les templates.
## Schéma
Voir le [schéma JSON](../../../../internal/proxy/director/layer/circuitbreaker/layer-options.json).
## Métriques
_Aucune [métrique Prometheus](../metrics.md) n'est exportée par ce layer._

View File

@ -30,6 +30,34 @@ Ce layer permet d'ajouter un mécanisme de file d'attente dynamique au proxy ass
Par exemple, si vous souhaitez limiter votre file à l'ensemble d'une section "`/blog`" d'un site, vous pouvez déclarer la valeur `["*/blog*"]`. Les autres URLs du site ne seront pas affectées par cette file d'attente. Par exemple, si vous souhaitez limiter votre file à l'ensemble d'une section "`/blog`" d'un site, vous pouvez déclarer la valeur `["*/blog*"]`. Les autres URLs du site ne seront pas affectées par cette file d'attente.
### Schéma ## Schéma
Voir le [schéma JSON](../../../../internal/proxy/director/layer/queue/schema/layer-options.json). Voir le [schéma JSON](../../../../internal/proxy/director/layer/queue/schema/layer-options.json).
## Métriques
Les [métriques Prometheus](../metrics.md) suivantes sont exposées par ce layer.
### `bouncer_layer_queue_capacity{layer=<layerName>,proxy=<proxyName>}`
- **Type:** `gauge`
- **Description**: Capacité maximale de la queue
- **Exemple**
```
# HELP bouncer_layer_queue_capacity Bouncer's queue layer capacity
# TYPE bouncer_layer_queue_capacity gauge
bouncer_layer_queue_capacity{layer="queue",proxy="cadoles"} 2
```
### `bouncer_layer_queue_sessions{layer=<layerName>,proxy=<proxyName>}`
- **Type:** `gauge`
- **Description**: Nombre courant de sessions ouvertes
- **Exemple**
```
# HELP bouncer_layer_queue_sessions Bouncer's queue layer current sessions
# TYPE bouncer_layer_queue_sessions gauge
bouncer_layer_queue_sessions{layer="queue",proxy="cadoles"} 3
```

View File

@ -0,0 +1,29 @@
# Métriques
Bouncer expose un certain nombre de métriques Prometheus sur le serveur proxy ainsi que sur le serveur d'administration. Ces métriques sont par défaut accessibles sur `/.bouncer/metrics`.
Il est possible de configurer le point d'entrée de ces métriques ainsi que d'ajouter une authentification de type `Basic Auth` [via la configuration](../../../misc/packaging/common/config.yml) (voir les clés `admin.metrics` et `proxy.metrics`).
Outre les métriques par défaut fournies par la librairie [Prometheus](https://prometheus.io/docs/guides/go-application/#instrumenting-a-go-application-for-prometheus), les serveurs Bouncer exposent également des métriques propres.
Chaque layer associé à un proxy peut également ses propres métriques spécifiques. [Voir la page de documentation](./layers/README.md) de chaque layer pour plus d'informations.
## Métriques spécifiques
### Serveur proxy
#### `bouncer_proxy_director_proxy_requests_total{proxy=<proxyName>}`
- **Type:** `counter`
- **Description**: Nombre total de requêtes ayant transité par le proxy
- **Exemple**
```
# HELP bouncer_proxy_director_proxy_requests_total Bouncer proxy total requests
# TYPE bouncer_proxy_director_proxy_requests_total counter
bouncer_proxy_director_proxy_requests_total{proxy="cadoles"} 64
```
### Serveur d'administration
_Pas de métrique supplémentaire._

View File

@ -70,15 +70,15 @@ docker run --rm -t \
Surveiller les sources, compiler celles ci en cas de modifications et lancer les services `bouncer-proxy` et `bouncer-admin`. Surveiller les sources, compiler celles ci en cas de modifications et lancer les services `bouncer-proxy` et `bouncer-admin`.
#### `make test` ### `make test`
Exécuter les tests unitaires/d'intégration du projet. Exécuter les tests unitaires/d'intégration du projet.
#### `make build` ### `make build`
Compiler une version de développement du binaire `bouncer`. Compiler une version de développement du binaire `bouncer`.
#### `make docker-build` ### `make docker-build`
Construire une image Docker pour Bouncer. Construire une image Docker pour Bouncer.
@ -92,6 +92,13 @@ docker run \
bouncer server proxy run bouncer server proxy run
``` ```
### `make grafterm`
Afficher un tableau de bord [`grafterm`](https://github.com/slok/grafterm) branché sur l'instance Prometheus locale.
### `make siege`
Lancer une session de test [`siege`](https://github.com/JoeDog/siege) sur l'instance `bouncer-proxy` locale.
## Arborescence du projet ## Arborescence du projet
```bash ```bash

16
go.mod
View File

@ -6,6 +6,7 @@ require (
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230701194111-c6b3d482cca6 forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230701194111-c6b3d482cca6
github.com/Masterminds/sprig/v3 v3.2.3 github.com/Masterminds/sprig/v3 v3.2.3
github.com/btcsuite/btcd/btcutil v1.1.3 github.com/btcsuite/btcd/btcutil v1.1.3
github.com/drone/envsubst v1.0.3
github.com/getsentry/sentry-go v0.22.0 github.com/getsentry/sentry-go v0.22.0
github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/chi/v5 v5.0.8
github.com/jedib0t/go-pretty/v6 v6.4.6 github.com/jedib0t/go-pretty/v6 v6.4.6
@ -52,13 +53,14 @@ require (
github.com/qri-io/jsonpointer v0.1.1 // indirect github.com/qri-io/jsonpointer v0.1.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cast v1.3.1 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
@ -68,7 +70,7 @@ require (
cdr.dev/slog v1.4.2 // indirect cdr.dev/slog v1.4.2 // indirect
github.com/alecthomas/chroma v0.10.0 // indirect github.com/alecthomas/chroma v0.10.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/dlclark/regexp2 v1.9.0 // indirect github.com/dlclark/regexp2 v1.9.0 // indirect
github.com/fatih/color v1.15.0 // indirect github.com/fatih/color v1.15.0 // indirect
github.com/go-chi/cors v1.2.1 github.com/go-chi/cors v1.2.1
@ -78,11 +80,11 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/lestrrat-go/blackmagic v1.0.1 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.4 // indirect github.com/lestrrat-go/httprc v1.0.4 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.9 github.com/lestrrat-go/jwx/v2 v2.0.19
github.com/lestrrat-go/option v1.0.1 // indirect github.com/lestrrat-go/option v1.0.1 // indirect
github.com/lib/pq v1.10.0 // indirect github.com/lib/pq v1.10.0 // indirect
github.com/lithammer/shortuuid/v4 v4.0.0 github.com/lithammer/shortuuid/v4 v4.0.0
@ -94,10 +96,10 @@ require (
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
gitlab.com/wpetit/goweb v0.0.0-20230419082146-a94d9ed7202b gitlab.com/wpetit/goweb v0.0.0-20230419082146-a94d9ed7202b
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.8.0 // indirect golang.org/x/crypto v0.17.0 // indirect
golang.org/x/mod v0.9.0 // indirect golang.org/x/mod v0.9.0 // indirect
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.7.0 // indirect golang.org/x/term v0.15.0 // indirect
golang.org/x/tools v0.7.0 // indirect golang.org/x/tools v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect gopkg.in/go-playground/validator.v9 v9.29.1 // indirect

49
go.sum
View File

@ -144,8 +144,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
@ -162,6 +162,8 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -322,17 +324,16 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.0.9 h1:TRX4Q630UXxPVLvP5vGaqVJO7S+0PE6msRZUsFSBoC8= github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY=
github.com/lestrrat-go/jwx/v2 v2.0.9/go.mod h1:K68euYaR95FnL0hIQB8VvzL70vB7pSifbJUydCTPmgM= github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
@ -421,6 +422,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
@ -446,8 +449,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
@ -493,9 +495,8 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -532,7 +533,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -577,9 +577,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -608,7 +606,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -680,17 +677,14 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -701,10 +695,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -763,7 +755,6 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -9,6 +9,7 @@ import (
"forge.cadoles.com/cadoles/bouncer/internal/auth" "forge.cadoles.com/cadoles/bouncer/internal/auth"
"forge.cadoles.com/cadoles/bouncer/internal/auth/jwt" "forge.cadoles.com/cadoles/bouncer/internal/auth/jwt"
bouncerChi "forge.cadoles.com/cadoles/bouncer/internal/chi"
"forge.cadoles.com/cadoles/bouncer/internal/config" "forge.cadoles.com/cadoles/bouncer/internal/config"
"forge.cadoles.com/cadoles/bouncer/internal/jwk" "forge.cadoles.com/cadoles/bouncer/internal/jwk"
"forge.cadoles.com/cadoles/bouncer/internal/store" "forge.cadoles.com/cadoles/bouncer/internal/store"
@ -91,7 +92,11 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
router := chi.NewRouter() router := chi.NewRouter()
router.Use(middleware.Logger) if s.serverConfig.HTTP.UseRealIP {
router.Use(middleware.RealIP)
}
router.Use(middleware.RequestLogger(bouncerChi.NewLogFormatter()))
if s.serverConfig.Sentry.DSN != "" { if s.serverConfig.Sentry.DSN != "" {
logger.Info(ctx, "enabling sentry http middleware") logger.Info(ctx, "enabling sentry http middleware")

View File

@ -5,15 +5,28 @@ import (
"strings" "strings"
"forge.cadoles.com/cadoles/bouncer/internal/admin" "forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/auth/jwt"
"forge.cadoles.com/cadoles/bouncer/internal/command/common" "forge.cadoles.com/cadoles/bouncer/internal/command/common"
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
"forge.cadoles.com/cadoles/bouncer/internal/setup" "forge.cadoles.com/cadoles/bouncer/internal/setup"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"gitlab.com/wpetit/goweb/logger" "gitlab.com/wpetit/goweb/logger"
) )
const (
flagPrintDefaultToken = "print-default-token"
)
func RunCommand() *cli.Command { func RunCommand() *cli.Command {
flags := common.Flags() flags := append(
common.Flags(),
&cli.BoolFlag{
Name: flagPrintDefaultToken,
Usage: "Generate and print a default writer token in console at startup",
Value: true,
},
)
return &cli.Command{ return &cli.Command{
Name: "run", Name: "run",
@ -36,6 +49,22 @@ func RunCommand() *cli.Command {
defer flushSentry() defer flushSentry()
if printDefaultToken := ctx.Bool(flagPrintDefaultToken); printDefaultToken {
key, err := jwk.Generate(jwk.DefaultKeySize)
if err != nil {
return errors.Wrap(err, "could not generate default key")
}
token, err := jwt.GenerateToken(ctx.Context, key, string(conf.Admin.Auth.Issuer), "default-admin", jwt.Role(jwt.RoleWriter))
if err != nil {
return errors.WithStack(err)
}
logger.SetLevel(logger.LevelInfo)
logger.Info(ctx.Context, "default writer token", logger.F("token", token))
logger.SetLevel(logger.Level(conf.Logger.Level))
}
srv := admin.NewServer( srv := admin.NewServer(
admin.WithServerConfig(conf.Admin), admin.WithServerConfig(conf.Admin),
admin.WithRedisConfig(conf.Redis), admin.WithRedisConfig(conf.Redis),

View File

@ -6,11 +6,13 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/drone/envsubst"
"github.com/pkg/errors" "github.com/pkg/errors"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
var reVar = regexp.MustCompile(`^\${(\w+)}$`) // var reVar = regexp.MustCompile(`^\${(\w+)}$`)
var reVar = regexp.MustCompile(`\${(.*?)}`)
type InterpolatedString string type InterpolatedString string
@ -130,14 +132,22 @@ type InterpolatedStringSlice []string
func (iss *InterpolatedStringSlice) UnmarshalYAML(value *yaml.Node) error { func (iss *InterpolatedStringSlice) UnmarshalYAML(value *yaml.Node) error {
var data []string var data []string
var evErr error
if err := value.Decode(&data); err != nil { if err := value.Decode(&data); err != nil {
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into map", value.Value, value.Line) return errors.Wrapf(err, "could not decode value '%v' (line '%d') into map", value.Value, value.Line)
} }
for index, value := range data { for index, value := range data {
if match := reVar.FindStringSubmatch(value); len(match) > 0 { //match := reVar.FindStringSubmatch(value)
value = os.Getenv(match[1]) re := regexp.MustCompile(`\${(.*?)}`)
res := re.FindAllStringSubmatch(value, 10)
if len(res) > 0 {
value, evErr = envsubst.EvalEnv(value)
if evErr != nil {
return evErr
}
} }
data[index] = value data[index] = value

View File

@ -1,13 +1,15 @@
package config package config
type HTTPConfig struct { type HTTPConfig struct {
Host InterpolatedString `yaml:"host"` Host InterpolatedString `yaml:"host"`
Port InterpolatedInt `yaml:"port"` Port InterpolatedInt `yaml:"port"`
UseRealIP InterpolatedBool `yaml:"useRealIP"`
} }
func NewHTTPConfig(host string, port int) HTTPConfig { func NewHTTPConfig(host string, port int) HTTPConfig {
return HTTPConfig{ return HTTPConfig{
Host: InterpolatedString(host), Host: InterpolatedString(host),
Port: InterpolatedInt(port), Port: InterpolatedInt(port),
UseRealIP: true,
} }
} }

View File

@ -3,7 +3,8 @@ package config
import "time" import "time"
type LayersConfig struct { type LayersConfig struct {
Queue QueueLayerConfig `yaml:"queue"` Queue QueueLayerConfig `yaml:"queue"`
CircuitBreaker CircuitBreakerLayerConfig `yaml:"circuitbreaker"`
} }
func NewDefaultLayersConfig() LayersConfig { func NewDefaultLayersConfig() LayersConfig {
@ -12,6 +13,9 @@ func NewDefaultLayersConfig() LayersConfig {
TemplateDir: "./layers/queue/templates", TemplateDir: "./layers/queue/templates",
DefaultKeepAlive: NewInterpolatedDuration(time.Minute), DefaultKeepAlive: NewInterpolatedDuration(time.Minute),
}, },
CircuitBreaker: CircuitBreakerLayerConfig{
TemplateDir: "./layers/circuitbreaker/templates",
},
} }
} }
@ -19,3 +23,7 @@ type QueueLayerConfig struct {
TemplateDir InterpolatedString `yaml:"templateDir"` TemplateDir InterpolatedString `yaml:"templateDir"`
DefaultKeepAlive *InterpolatedDuration `yaml:"defaultKeepAlive"` DefaultKeepAlive *InterpolatedDuration `yaml:"defaultKeepAlive"`
} }
type CircuitBreakerLayerConfig struct {
TemplateDir InterpolatedString `yaml:"templateDir"`
}

View File

@ -2,5 +2,5 @@ logger:
level: 0 level: 0
format: human format: human
http: http:
host: "0.0.0.0" host: "${LISTEN_ADDR}"
port: 3000 port: 3000

View File

@ -0,0 +1,23 @@
{
"$id": "https://forge.cadoles.com/cadoles/bouncer/schemas/circuitbreaker-layer-options",
"title": "Circuit breaker layer options",
"type": "object",
"properties": {
"matchURLs": {
"type": "array",
"items": {
"type": "string"
}
},
"authorizedCIDRs": {
"type": "array",
"items": {
"type": "string"
}
},
"templateBlock": {
"type": "string"
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,151 @@
package circuitbreaker
import (
"context"
"html/template"
"net"
"net/http"
"path/filepath"
"sync"
"forge.cadoles.com/Cadoles/go-proxy"
"forge.cadoles.com/Cadoles/go-proxy/wildcard"
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/Masterminds/sprig/v3"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
const LayerType store.LayerType = "circuitbreaker"
type Layer struct {
templateDir string
loadOnce sync.Once
tmpl *template.Template
}
// LayerType implements director.MiddlewareLayer
func (l *Layer) LayerType() store.LayerType {
return LayerType
}
// Middleware implements director.MiddlewareLayer
func (l *Layer) Middleware(layer *store.Layer) proxy.Middleware {
return func(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
options, err := fromStoreOptions(layer.Options)
if err != nil {
logger.Error(ctx, "could not parse layer options", logger.E(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
matches, err := l.matchAnyAuthorizedCIDRs(ctx, r.RemoteAddr, options.AuthorizedCIDRs)
if err != nil {
logger.Error(ctx, "could not match authorized cidrs", logger.E(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
if matches {
h.ServeHTTP(w, r)
return
}
matches = wildcard.MatchAny(r.URL.String(), options.MatchURLs...)
if !matches {
h.ServeHTTP(w, r)
return
}
l.renderCircuitBreakerPage(w, r, layer, options)
}
return http.HandlerFunc(fn)
}
}
func (l *Layer) matchAnyAuthorizedCIDRs(ctx context.Context, remoteHostPort string, CIDRs []string) (bool, error) {
remoteHost, _, err := net.SplitHostPort(remoteHostPort)
if err != nil {
return false, errors.WithStack(err)
}
remoteAddr := net.ParseIP(remoteHost)
if remoteAddr == nil {
return false, errors.Errorf("remote host '%s' is not a valid ip address", remoteHost)
}
for _, rawCIDR := range CIDRs {
_, net, err := net.ParseCIDR(rawCIDR)
if err != nil {
return false, errors.WithStack(err)
}
match := net.Contains(remoteAddr)
if !match {
continue
}
return true, nil
}
logger.Debug(ctx, "comparing remote host with authorized cidrs", logger.F("remoteAddr", remoteAddr))
return false, nil
}
func (l *Layer) renderCircuitBreakerPage(w http.ResponseWriter, r *http.Request, layer *store.Layer, options *LayerOptions) {
ctx := r.Context()
pattern := filepath.Join(l.templateDir, "*.gohtml")
logger.Info(ctx, "loading circuit breaker page templates", logger.F("pattern", pattern))
tmpl, err := template.New("").Funcs(sprig.FuncMap()).ParseGlob(pattern)
if err != nil {
logger.Error(ctx, "could not load circuit breaker templates", logger.E(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
templateData := struct {
Layer *store.Layer
LayerOptions *LayerOptions
}{
Layer: layer,
LayerOptions: options,
}
w.Header().Add("Cache-Control", "no-cache")
w.WriteHeader(http.StatusOK)
if err := tmpl.ExecuteTemplate(w, options.TemplateBlock, templateData); err != nil {
logger.Error(ctx, "could not render circuit breaker page", logger.E(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
}
func New(funcs ...OptionFunc) *Layer {
opts := defaultOptions()
for _, fn := range funcs {
fn(opts)
}
return &Layer{
templateDir: opts.TemplateDir,
}
}
var _ director.MiddlewareLayer = &Layer{}

View File

@ -0,0 +1,36 @@
package circuitbreaker
import (
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
type LayerOptions struct {
MatchURLs []string `mapstructure:"matchURLs"`
AuthorizedCIDRs []string `mapstructure:"authorizedCIDRs"`
TemplateBlock string `mapstructure:"templateBlock"`
}
func fromStoreOptions(storeOptions store.LayerOptions) (*LayerOptions, error) {
layerOptions := LayerOptions{
MatchURLs: []string{"*"},
AuthorizedCIDRs: []string{},
TemplateBlock: "default",
}
config := mapstructure.DecoderConfig{
Result: &layerOptions,
}
decoder, err := mapstructure.NewDecoder(&config)
if err != nil {
return nil, err
}
if err := decoder.Decode(storeOptions); err != nil {
return nil, errors.WithStack(err)
}
return &layerOptions, nil
}

View File

@ -0,0 +1,19 @@
package circuitbreaker
type Options struct {
TemplateDir string
}
type OptionFunc func(*Options)
func defaultOptions() *Options {
return &Options{
TemplateDir: "./templates",
}
}
func WithTemplateDir(templateDir string) OptionFunc {
return func(o *Options) {
o.TemplateDir = templateDir
}
}

View File

@ -0,0 +1,8 @@
package circuitbreaker
import (
_ "embed"
)
//go:embed layer-options.json
var RawLayerOptionsSchema []byte

View File

@ -89,6 +89,10 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
s.directorLayers..., s.directorLayers...,
) )
if s.serverConfig.HTTP.UseRealIP {
router.Use(middleware.RealIP)
}
router.Use(middleware.RequestLogger(bouncerChi.NewLogFormatter())) router.Use(middleware.RequestLogger(bouncerChi.NewLogFormatter()))
if s.serverConfig.Sentry.DSN != "" { if s.serverConfig.Sentry.DSN != "" {

View File

@ -0,0 +1,21 @@
package setup
import (
"forge.cadoles.com/cadoles/bouncer/internal/config"
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/circuitbreaker"
)
func init() {
RegisterLayer(circuitbreaker.LayerType, setupCircuitBreakerLayer, circuitbreaker.RawLayerOptionsSchema)
}
func setupCircuitBreakerLayer(conf *config.Config) (director.Layer, error) {
options := []circuitbreaker.OptionFunc{
circuitbreaker.WithTemplateDir(string(conf.Layers.CircuitBreaker.TemplateDir)),
}
return circuitbreaker.New(
options...,
), nil
}

View File

@ -2,6 +2,7 @@ package testsuite
import ( import (
"context" "context"
"reflect"
"testing" "testing"
"forge.cadoles.com/cadoles/bouncer/internal/store" "forge.cadoles.com/cadoles/bouncer/internal/store"
@ -48,6 +49,187 @@ var layerRepositoryTestCases = []layerRepositoryTestCase{
return errors.Errorf("layer.UpdatedAt should not be zero value") return errors.Errorf("layer.UpdatedAt should not be zero value")
} }
return nil
},
},
{
Name: "Create then get layer",
Do: func(repo store.LayerRepository) error {
ctx := context.Background()
var proxyName store.ProxyName = "create_then_get_layer_proxy"
var layerName store.LayerName = "create_then_get_layer"
var layerType store.LayerType = "dummy"
var layerOptions store.LayerOptions = store.LayerOptions{
"foo": "bar",
"test": struct {
Items []int `json:"items"`
}{
Items: []int{1, 2, 3},
},
}
createdLayer, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions)
if err != nil {
return errors.WithStack(err)
}
foundLayer, err := repo.GetLayer(ctx, proxyName, layerName)
if err != nil {
return errors.WithStack(err)
}
if e, g := createdLayer.Name, foundLayer.Name; e != g {
return errors.Errorf("foundLayer.Name: expected '%v', got '%v'", createdLayer.Name, foundLayer.Name)
}
if e, g := createdLayer.CreatedAt, foundLayer.CreatedAt; !reflect.DeepEqual(e, g) {
return errors.Errorf("foundLayer.CreatedAt: expected '%v', got '%v'", createdLayer.CreatedAt, foundLayer.CreatedAt)
}
if e, g := createdLayer.UpdatedAt, foundLayer.UpdatedAt; !reflect.DeepEqual(e, g) {
return errors.Errorf("foundLayer.UpdatedAt: expected '%v', got '%v'", createdLayer.UpdatedAt, foundLayer.UpdatedAt)
}
if e, g := createdLayer.Enabled, foundLayer.Enabled; !reflect.DeepEqual(e, g) {
return errors.Errorf("foundLayer.Enabled: expected '%v', got '%v'", createdLayer.Enabled, foundLayer.Enabled)
}
if e, g := createdLayer.Weight, foundLayer.Weight; !reflect.DeepEqual(e, g) {
return errors.Errorf("foundLayer.Weight: expected '%v', got '%v'", createdLayer.Weight, foundLayer.Weight)
}
if e, g := createdLayer.Proxy, foundLayer.Proxy; !reflect.DeepEqual(e, g) {
return errors.Errorf("foundLayer.Proxy: expected '%v', got '%v'", createdLayer.Proxy, foundLayer.Proxy)
}
if e, g := createdLayer.Options, foundLayer.Options; !reflect.DeepEqual(e, g) {
return errors.Errorf("foundLayer.Options: expected '%v', got '%v'", createdLayer.Options, foundLayer.Options)
}
return nil
},
},
{
Name: "Create then delete layer",
Do: func(repo store.LayerRepository) error {
ctx := context.Background()
var layerName store.LayerName = "create_then_delete_layer"
var proxyName store.ProxyName = store.ProxyName(string(layerName) + "_proxy")
var layerType store.LayerType = "dummy"
var layerOptions store.LayerOptions = store.LayerOptions{
"foo": "bar",
"test": struct {
Items []int `json:"items"`
}{
Items: []int{1, 2, 3},
},
}
createdLayer, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions)
if err != nil {
return errors.WithStack(err)
}
if err := repo.DeleteLayer(ctx, createdLayer.Proxy, createdLayer.Name); err != nil {
return errors.WithStack(err)
}
foundLayer, err := repo.GetLayer(ctx, createdLayer.Proxy, createdLayer.Name)
if err == nil {
return errors.New("err should not be nil")
}
if !errors.Is(err, store.ErrNotFound) {
return errors.Errorf("err should be store.ErrNotFound, got '%+v'", err)
}
if foundLayer != nil {
return errors.Errorf("foundLayer should be nil, got '%v'", foundLayer)
}
return nil
},
},
{
Name: "Create already existing layer",
Do: func(repo store.LayerRepository) error {
ctx := context.Background()
var layerName store.LayerName = "create_already_existing_layer"
var proxyName store.ProxyName = store.ProxyName(string(layerName) + "_proxy")
var layerType store.LayerType = "dummy"
var layerOptions store.LayerOptions = store.LayerOptions{
"foo": "bar",
"test": struct {
Items []int `json:"items"`
}{
Items: []int{1, 2, 3},
},
}
_, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions)
if err != nil {
return errors.WithStack(err)
}
_, err = repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions)
if err == nil {
return errors.New("err should not be nil")
}
if !errors.Is(err, store.ErrAlreadyExist) {
return errors.Errorf("err: expected store.ErrAlreadyExists, got '%+v'", err)
}
return nil
},
},
{
Name: "Create then query layer",
Do: func(repo store.LayerRepository) error {
ctx := context.Background()
var layerName store.LayerName = "create_then_query_layer"
var proxyName store.ProxyName = store.ProxyName(string(layerName) + "_proxy")
var layerType store.LayerType = "dummy"
var layerOptions store.LayerOptions = store.LayerOptions{
"foo": "bar",
"test": struct {
Items []int `json:"items"`
}{
Items: []int{1, 2, 3},
},
}
createdLayer, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions)
if err != nil {
return errors.WithStack(err)
}
headers, err := repo.QueryLayers(ctx, createdLayer.Proxy)
if err != nil {
return errors.WithStack(err)
}
if len(headers) < 1 {
return errors.Errorf("len(headers): expected value > 1, got '%v'", len(headers))
}
found := false
for _, h := range headers {
if h.Name == createdLayer.Name {
found = true
break
}
}
if !found {
return errors.New("could not find created layer in query results")
}
return nil return nil
}, },
}, },

View File

@ -83,6 +83,14 @@ var proxyRepositoryTestCases = []proxyRepositoryTestCase{
return errors.Errorf("foundProxy.To: expected '%v', got '%v'", createdProxy.To, foundProxy.To) return errors.Errorf("foundProxy.To: expected '%v', got '%v'", createdProxy.To, foundProxy.To)
} }
if e, g := createdProxy.Enabled, foundProxy.Enabled; e != g {
return errors.Errorf("foundProxy.Enabled: expected '%v', got '%v'", createdProxy.Enabled, foundProxy.Enabled)
}
if e, g := createdProxy.Weight, foundProxy.Weight; e != g {
return errors.Errorf("foundProxy.Weight: expected '%v', got '%v'", createdProxy.Weight, foundProxy.Weight)
}
if e, g := createdProxy.CreatedAt, foundProxy.CreatedAt; e != g { if e, g := createdProxy.CreatedAt, foundProxy.CreatedAt; e != g {
return errors.Errorf("foundProxy.CreatedAt: expected '%v', got '%v'", createdProxy.CreatedAt, foundProxy.CreatedAt) return errors.Errorf("foundProxy.CreatedAt: expected '%v', got '%v'", createdProxy.CreatedAt, foundProxy.CreatedAt)
} }
@ -127,7 +135,7 @@ var proxyRepositoryTestCases = []proxyRepositoryTestCase{
}, },
}, },
{ {
Name: "Create then query", Name: "Create then query layer",
Do: func(repo store.ProxyRepository) error { Do: func(repo store.ProxyRepository) error {
ctx := context.Background() ctx := context.Background()

View File

@ -0,0 +1,73 @@
{{ define "default" }}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Accès bloqué - {{ .Layer.Name }}</title>
<style>
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body, h1, h2, h3, h4, h5, h6, p, ol, ul {
margin: 0;
padding: 0;
font-weight: normal;
}
html, body {
width: 100%;
height: 100%;
font-family: Arial, Helvetica, sans-serif;
background-color: #f7f7f7;
}
#container {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
flex-direction: column;
}
#card {
padding: 1.5em 1em;
border: 1px solid #e0e0e0;
background-color: white;
border-radius: 5px;
box-shadow: 2px 2px #cccccc1c;
color: #333333 !important;
}
.title {
margin-bottom: 1.2em;
}
p {
margin-bottom: 0.5em;
}
.footer {
font-size: 0.7em;
margin-top: 2em;
text-align: right;
}
</style>
</head>
<body>
<div id="container">
<div id="card">
<h2 class="title">Page indisponible</h2>
<p>La page à laquelle vous souhaitez accéder est actuellement indisponible.</p>
<p class="footer">Propulsé par <a href="https://forge.cadoles.com/Cadoles/bouncer">Bouncer</a>.</p>
</div>
</div>
</body>
</html>
{{ end }}

View File

@ -0,0 +1,196 @@
{
"version": "v1",
"datasources": {
"prometheus": {
"prometheus": {
"address": "http://127.0.0.1:9090"
}
}
},
"dashboard": {
"variables": {
"job": {
"constant": { "value": "bouncer-proxy" }
},
"interval": {
"interval": { "steps": 50 }
}
},
"widgets": [
{
"title": "Bouncer - Total queue sessions",
"gridPos": { "w": 20 },
"singlestat": {
"thresholds": [{ "color": "#47D038" }],
"query": {
"datasourceID": "prometheus",
"expr": "sum(bouncer_layer_queue_sessions{job=\"{{.job}}\"})"
}
}
},
{
"title": "Bouncer Traffic",
"gridPos": {
"w": 80
},
"graph": {
"queries": [
{
"datasourceID": "prometheus",
"expr": "sum(rate(bouncer_proxy_director_proxy_requests_total{job=\"{{.job}}\"}[{{.interval}}]))",
"legend": "req/s"
}
]
}
},
{
"title": "Goroutines",
"gridPos": { "w": 20 },
"singlestat": {
"thresholds": [{ "color": "#47D038" }],
"query": {
"datasourceID": "prometheus",
"expr": "sum(go_goroutines{job=\"{{.job}}\"})"
}
}
},
{
"title": "GC duration",
"gridPos": { "w": 20 },
"singlestat": {
"unit": "second",
"query": {
"datasourceID": "prometheus",
"expr": "max(go_gc_duration_seconds{job=\"{{.job}}\"})"
}
}
},
{
"title": "Stack",
"gridPos": { "w": 20 },
"singlestat": {
"unit": "bytes",
"thresholds": [{ "color": "#22F1F1" }],
"query": {
"datasourceID": "prometheus",
"expr": "sum(go_memstats_stack_inuse_bytes{job=\"{{.job}}\"})"
}
}
},
{
"title": "Heap",
"gridPos": { "w": 20 },
"singlestat": {
"unit": "bytes",
"thresholds": [{ "color": "#22F1F1" }],
"query": {
"datasourceID": "prometheus",
"expr": "sum(go_memstats_heap_inuse_bytes{job=\"{{.job}}\"})"
}
}
},
{
"title": "Alloc",
"gridPos": { "w": 20 },
"singlestat": {
"unit": "bytes",
"thresholds": [{ "color": "#22F1F1" }],
"query": {
"datasourceID": "prometheus",
"expr": "sum(go_memstats_alloc_bytes{job=\"{{.job}}\"})"
}
}
},
{
"title": "Goroutines",
"gridPos": { "w": 50 },
"graph": {
"visualization": {
"legend": { "disable": true },
"yAxis": { "unit": "", "decimals": 2 }
},
"queries": [
{
"datasourceID": "prometheus",
"expr": "sum(go_goroutines{job=\"{{.job}}\"})"
}
]
}
},
{
"title": "GC duration",
"gridPos": { "w": 50 },
"graph": {
"queries": [
{
"datasourceID": "prometheus",
"expr": "max(go_gc_duration_seconds{job=\"{{.job}}\"}) by (quantile)",
"legend": "Q{{.quantile}}"
}
],
"visualization": {
"yAxis": { "unit": "second" },
"seriesOverride": [
{ "regex": "^Q0$", "color": "#F9E2D2" },
{ "regex": "^Q0.25$", "color": "#F2C96D" },
{ "regex": "^Q0.5(0)?$", "color": "#EAB839" },
{ "regex": "^Q0.75$", "color": "#EF843C" },
{ "regex": "^Q1(.0)?$", "color": "#E24D42" }
]
}
}
},
{
"title": "Memory",
"gridPos": { "w": 50 },
"graph": {
"visualization": {
"yAxis": { "unit": "byte", "decimals": 0 }
},
"queries": [
{
"datasourceID": "prometheus",
"expr": "sum(go_memstats_stack_inuse_bytes{job=\"{{.job}}\"})",
"legend": "stack inuse"
},
{
"datasourceID": "prometheus",
"expr": "sum(go_memstats_heap_inuse_bytes{job=\"{{.job}}\"})",
"legend": "heap inuse"
},
{
"datasourceID": "prometheus",
"expr": "sum(go_memstats_alloc_bytes{job=\"{{.job}}\"})",
"legend": "alloc"
}
]
}
},
{
"title": "Memory ops rate",
"gridPos": {
"w": 50
},
"graph": {
"queries": [
{
"datasourceID": "prometheus",
"expr": "sum(rate(go_memstats_frees_total{job=\"{{.job}}\"}[{{.interval}}]))",
"legend": "frees/s"
},
{
"datasourceID": "prometheus",
"expr": "sum(rate(go_memstats_mallocs_total{job=\"{{.job}}\"}[{{.interval}}]))",
"legend": "mallocs/s"
},
{
"datasourceID": "prometheus",
"expr": "sum(rate(go_memstats_lookups_total{job=\"{{.job}}\"}[{{.interval}}]))",
"legend": "lookups/s"
}
]
}
}
]
}
}

View File

@ -7,12 +7,22 @@ ARG https_proxy=
# Install dev environment dependencies # Install dev environment dependencies
RUN export DEBIAN_FRONTEND=noninteractive &&\ RUN export DEBIAN_FRONTEND=noninteractive &&\
apt clean &&\
apt-get update -y &&\ apt-get update -y &&\
apt-get install -y --no-install-recommends curl ca-certificates build-essential wget unzip tar git jq apt-get install -y --no-install-recommends curl ca-certificates build-essential wget unzip tar git jq gnupg
# Add LetsEncrypt certificates # Add LetsEncrypt certificates
RUN curl -k https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/common/add-letsencrypt-ca.sh | bash RUN curl -k https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/common/add-letsencrypt-ca.sh | bash
RUN install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& chmod a+r /etc/apt/keyrings/docker.gpg \
&& echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt-get update \
&& apt-get install -y docker-ce-cli
ARG GO_VERSION=1.20.4 ARG GO_VERSION=1.20.4
# Install Go # Install Go

View File

@ -6,6 +6,9 @@ admin:
host: 127.0.0.1 host: 127.0.0.1
# Port d'écoute du service # Port d'écoute du service
port: 8081 port: 8081
# Utiliser les entêtes HTTP True-Client-IP, X-Real-IP ou X-Forwarded-For
# pour le calcul de l'adresse distante à l'origine des requêtes
useRealIP: true
# Configuration CORS du service # Configuration CORS du service
# Uniquement nécessaire si un frontend web # Uniquement nécessaire si un frontend web
@ -44,6 +47,10 @@ admin:
# Authentification "basic auth" sur la page # Authentification "basic auth" sur la page
# de publication # de publication
# Mettre à null pour désactiver l'authentification # Mettre à null pour désactiver l'authentification
# Les couples d'identifiants doivent être spécifiés sous la forme:
# basicAuth:
# credentials:
# <username>: <password>
basicAuth: null basicAuth: null
# Configuration de l'intégration Sentry # Configuration de l'intégration Sentry
@ -73,6 +80,9 @@ proxy:
host: 0.0.0.0 host: 0.0.0.0
# Port d'écoute du service # Port d'écoute du service
port: 8080 port: 8080
# Utiliser les entêtes HTTP True-Client-IP, X-Real-IP ou X-Forwarded-For
# pour le calcul de l'adresse distante à l'origine des requêtes
useRealIP: true
# Métriques Prometheus # Métriques Prometheus
metrics: metrics:
@ -84,8 +94,10 @@ proxy:
# de publication # de publication
# Mettre à null pour désactiver l'authentification # Mettre à null pour désactiver l'authentification
basicAuth: basicAuth:
# Les couples d'identifiants doivent être spécifiés
# sous la forme "<username>: <password>"
credentials: credentials:
prom: etheus prometheus: changeme
# Configuration du transport HTTP(S) # Configuration du transport HTTP(S)
# Voir https://pkg.go.dev/net/http#Transport # Voir https://pkg.go.dev/net/http#Transport
@ -161,4 +173,10 @@ layers:
# Répertoire contenant les templates # Répertoire contenant les templates
templateDir: "/etc/bouncer/layers/queue/templates" templateDir: "/etc/bouncer/layers/queue/templates"
# Temps de vie par défaut d'une session # Temps de vie par défaut d'une session
defaultKeepAlive: 1m defaultKeepAlive: 1m
# Configuration du layer "circuitbreaker"
circuitbreaker:
# Répertoire contenant les templates
templateDir: "/etc/bouncer/layers/circuitbreaker/templates"

View File

@ -0,0 +1,7 @@
scrape_configs:
- job_name: bouncer-proxy
metrics_path: /.bouncer/metrics
static_configs:
- targets:
- "localhost:8080"
scrape_interval: 5s

6
misc/siege/urls.txt Normal file
View File

@ -0,0 +1,6 @@
http://localhost:8080/blog/
http://localhost:8080/services/
http://localhost:8080/
http://localhost:8080/recrutement/
http://localhost:8080/faq/
http://localhost:8080/societe/histoire/

View File

@ -14,4 +14,9 @@ layers/**
{ {
daemon +sigint: make run-redis daemon +sigint: make run-redis
}
misc/prometheus/prometheus.yml
{
daemon +sigint: make run-prometheus
} }