feat: transform circuitbreaker layer in authn-network layer
Some checks are pending
Cadoles/bouncer/pipeline/head Build started...
Cadoles/bouncer/pipeline/pr-develop Build started...

This commit is contained in:
2024-05-17 17:29:26 +02:00
parent 5ed194618a
commit 5a34d5917f
25 changed files with 450 additions and 338 deletions

View File

@ -0,0 +1,83 @@
package network
import (
"context"
"net"
"net/http"
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/authn"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/wpetit/goweb/logger"
)
type Authenticator struct {
}
// Authenticate implements authn.Authenticator.
func (a *Authenticator) Authenticate(w http.ResponseWriter, r *http.Request, layer *store.Layer) (*authn.User, error) {
ctx := r.Context()
options, err := fromStoreOptions(layer.Options)
if err != nil {
return nil, errors.WithStack(err)
}
matches, err := a.matchAnyAuthorizedCIDRs(ctx, r.RemoteAddr, options.AuthorizedCIDRs)
if err != nil {
return nil, errors.WithStack(err)
}
user := authn.NewUser(r.RemoteAddr, map[string]any{})
if !matches {
metricForbiddenTotal.With(prometheus.Labels{
metricLabelLayer: string(layer.Name),
metricLabelProxy: string(layer.Proxy),
}).Add(1)
return user, errors.WithStack(authn.ErrForbidden)
}
metricAuthorizedTotal.With(prometheus.Labels{
metricLabelLayer: string(layer.Name),
metricLabelProxy: string(layer.Proxy),
}).Add(1)
return user, nil
}
func (a *Authenticator) 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
}
var (
_ authn.Authenticator = &Authenticator{}
)

View File

@ -0,0 +1,14 @@
{
"type": "object",
"properties": {
"authorizedCIDRs": {
"title": "Liste des adresses réseau d'origine autorisées (au format CIDR)",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,12 @@
package network
import (
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/authn"
"forge.cadoles.com/cadoles/bouncer/internal/store"
)
const LayerType store.LayerType = "authn-network"
func NewLayer(funcs ...authn.OptionFunc) *authn.Layer {
return authn.NewLayer(LayerType, &Authenticator{}, funcs...)
}

View File

@ -0,0 +1,37 @@
package network
import (
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/authn"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
type LayerOptions struct {
authn.LayerOptions
AuthorizedCIDRs []string `mapstructure:"authorizedCIDRs"`
TemplateBlock string `mapstructure:"templateBlock"`
}
func fromStoreOptions(storeOptions store.LayerOptions) (*LayerOptions, error) {
layerOptions := LayerOptions{
LayerOptions: authn.DefaultLayerOptions(),
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,31 @@
package network
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
const (
metricNamespace = "bouncer_layer_authn_network"
metricLabelProxy = "proxy"
metricLabelLayer = "layer"
)
var (
metricAuthorizedTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "authorized_total",
Help: "Bouncer's authn-network layer total authorized accesses",
Namespace: metricNamespace,
},
[]string{metricLabelProxy, metricLabelLayer},
)
metricForbiddenTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "forbidden_total",
Help: "Bouncer's authn-network layer total forbbiden accesses",
Namespace: metricNamespace,
},
[]string{metricLabelProxy, metricLabelLayer},
)
)

View File

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