The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/authn/clients/proxy.go

155 lines
3.8 KiB

package clients
import (
"context"
"fmt"
"net"
"path"
"strings"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
)
const (
proxyFieldName = "Name"
proxyFieldEmail = "Email"
proxyFieldLogin = "Login"
proxyFieldRole = "Role"
proxyFieldGroups = "Groups"
)
var proxyFields = [...]string{proxyFieldName, proxyFieldEmail, proxyFieldLogin, proxyFieldRole, proxyFieldGroups}
var (
errNotAcceptedIP = errutil.NewBase(errutil.StatusUnauthorized, "auth-proxy.invalid-ip")
errEmptyProxyHeader = errutil.NewBase(errutil.StatusUnauthorized, "auth-proxy.empty-header")
errInvalidProxyHeader = errutil.NewBase(errutil.StatusInternal, "auth-proxy.invalid-proxy-header")
)
var _ authn.ContextAwareClient = new(Proxy)
func ProvideProxy(cfg *setting.Cfg, clients ...authn.ProxyClient) (*Proxy, error) {
list, err := parseAcceptList(cfg.AuthProxyWhitelist)
if err != nil {
return nil, err
}
return &Proxy{cfg, clients, list}, nil
}
type Proxy struct {
cfg *setting.Cfg
clients []authn.ProxyClient
acceptedIPs []*net.IPNet
}
func (c *Proxy) Name() string {
return authn.ClientProxy
}
func (c *Proxy) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) {
if !c.isAllowedIP(r) {
return nil, errNotAcceptedIP.Errorf("request ip is not in the configured accept list")
}
username := getProxyHeader(r, c.cfg.AuthProxyHeaderName, c.cfg.AuthProxyHeadersEncoded)
if len(username) == 0 {
return nil, errEmptyProxyHeader.Errorf("no username provided in auth proxy header")
}
additional := getAdditionalProxyHeaders(r, c.cfg)
// FIXME: add cache to prevent sync on every request
var clientErr error
for _, proxyClient := range c.clients {
var identity *authn.Identity
identity, clientErr = proxyClient.AuthenticateProxy(ctx, r, username, additional)
if identity != nil {
return identity, nil
}
}
return nil, clientErr
}
func (c *Proxy) Test(ctx context.Context, r *authn.Request) bool {
return len(getProxyHeader(r, c.cfg.AuthProxyHeaderName, c.cfg.AuthProxyHeadersEncoded)) != 0
}
func (c *Proxy) Priority() uint {
return 50
}
func (c *Proxy) isAllowedIP(r *authn.Request) bool {
if len(c.acceptedIPs) == 0 {
return true
}
host, _, err := net.SplitHostPort(r.HTTPRequest.RemoteAddr)
if err != nil {
return false
}
ip := net.ParseIP(host)
for _, v := range c.acceptedIPs {
if v.Contains(ip) {
return true
}
}
return false
}
func parseAcceptList(s string) ([]*net.IPNet, error) {
if len(strings.TrimSpace(s)) == 0 {
return nil, nil
}
addresses := strings.Split(s, ",")
list := make([]*net.IPNet, 0, len(addresses))
for _, addr := range addresses {
result, err := coerceProxyAddress(addr)
if err != nil {
return nil, err
}
list = append(list, result)
}
return list, nil
}
// coerceProxyAddress gets network of the presented CIDR notation
func coerceProxyAddress(proxyAddr string) (*net.IPNet, error) {
proxyAddr = strings.TrimSpace(proxyAddr)
if !strings.Contains(proxyAddr, "/") {
proxyAddr = path.Join(proxyAddr, "32")
}
_, network, err := net.ParseCIDR(proxyAddr)
if err != nil {
return nil, fmt.Errorf("could not parse the network: %w", err)
}
return network, nil
}
func getProxyHeader(r *authn.Request, headerName string, encoded bool) string {
if r.HTTPRequest == nil {
return ""
}
v := r.HTTPRequest.Header.Get(headerName)
if encoded {
v = util.DecodeQuotedPrintable(v)
}
return v
}
func getAdditionalProxyHeaders(r *authn.Request, cfg *setting.Cfg) map[string]string {
additional := make(map[string]string, len(proxyFields))
for _, k := range proxyFields {
if v := getProxyHeader(r, cfg.AuthProxyHeaders[k], cfg.AuthProxyHeadersEncoded); v != "" {
additional[k] = v
}
}
return additional
}