root: make sentry DSN configurable (#4016)
* make sentry DSN configurable Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * make proxy smarter Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix typo in config struct Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
a9111bd3fd
commit
276af8457d
|
@ -35,6 +35,7 @@ class ErrorReportingConfigSerializer(PassiveSerializer):
|
||||||
"""Config for error reporting"""
|
"""Config for error reporting"""
|
||||||
|
|
||||||
enabled = BooleanField(read_only=True)
|
enabled = BooleanField(read_only=True)
|
||||||
|
sentry_dsn = CharField(read_only=True)
|
||||||
environment = CharField(read_only=True)
|
environment = CharField(read_only=True)
|
||||||
send_pii = BooleanField(read_only=True)
|
send_pii = BooleanField(read_only=True)
|
||||||
traces_sample_rate = FloatField(read_only=True)
|
traces_sample_rate = FloatField(read_only=True)
|
||||||
|
@ -77,6 +78,7 @@ class ConfigView(APIView):
|
||||||
{
|
{
|
||||||
"error_reporting": {
|
"error_reporting": {
|
||||||
"enabled": CONFIG.y("error_reporting.enabled"),
|
"enabled": CONFIG.y("error_reporting.enabled"),
|
||||||
|
"sentry_dsn": CONFIG.y("error_reporting.sentry_dsn"),
|
||||||
"environment": CONFIG.y("error_reporting.environment"),
|
"environment": CONFIG.y("error_reporting.environment"),
|
||||||
"send_pii": CONFIG.y("error_reporting.send_pii"),
|
"send_pii": CONFIG.y("error_reporting.send_pii"),
|
||||||
"traces_sample_rate": float(CONFIG.y("error_reporting.sample_rate", 0.4)),
|
"traces_sample_rate": float(CONFIG.y("error_reporting.sample_rate", 0.4)),
|
||||||
|
|
|
@ -32,6 +32,7 @@ log_level: info
|
||||||
# Error reporting, sends stacktrace to sentry.beryju.org
|
# Error reporting, sends stacktrace to sentry.beryju.org
|
||||||
error_reporting:
|
error_reporting:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
sentry_dsn: https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8
|
||||||
environment: customer
|
environment: customer
|
||||||
send_pii: false
|
send_pii: false
|
||||||
sample_rate: 0.1
|
sample_rate: 0.1
|
||||||
|
|
|
@ -34,7 +34,6 @@ from authentik.lib.utils.http import authentik_user_agent
|
||||||
from authentik.lib.utils.reflection import class_to_path, get_env
|
from authentik.lib.utils.reflection import class_to_path, get_env
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
SENTRY_DSN = "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8"
|
|
||||||
|
|
||||||
|
|
||||||
class SentryWSMiddleware(BaseMiddleware):
|
class SentryWSMiddleware(BaseMiddleware):
|
||||||
|
@ -71,7 +70,7 @@ def sentry_init(**sentry_init_kwargs):
|
||||||
kwargs.update(**sentry_init_kwargs)
|
kwargs.update(**sentry_init_kwargs)
|
||||||
# pylint: disable=abstract-class-instantiated
|
# pylint: disable=abstract-class-instantiated
|
||||||
sentry_sdk_init(
|
sentry_sdk_init(
|
||||||
dsn=SENTRY_DSN,
|
dsn=CONFIG.y("error_reporting.sentry_dsn"),
|
||||||
integrations=[
|
integrations=[
|
||||||
DjangoIntegration(transaction_style="function_name"),
|
DjangoIntegration(transaction_style="function_name"),
|
||||||
CeleryIntegration(),
|
CeleryIntegration(),
|
||||||
|
|
|
@ -38,7 +38,7 @@ func main() {
|
||||||
|
|
||||||
if config.Get().ErrorReporting.Enabled {
|
if config.Get().ErrorReporting.Enabled {
|
||||||
err := sentry.Init(sentry.ClientOptions{
|
err := sentry.Init(sentry.ClientOptions{
|
||||||
Dsn: config.Get().ErrorReporting.DSN,
|
Dsn: config.Get().ErrorReporting.SentryDSN,
|
||||||
AttachStacktrace: true,
|
AttachStacktrace: true,
|
||||||
TracesSampler: sentryutils.SamplerFunc(config.Get().ErrorReporting.SampleRate),
|
TracesSampler: sentryutils.SamplerFunc(config.Get().ErrorReporting.SampleRate),
|
||||||
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
||||||
|
|
|
@ -40,7 +40,6 @@ func defaultConfig() *Config {
|
||||||
LogLevel: "info",
|
LogLevel: "info",
|
||||||
ErrorReporting: ErrorReportingConfig{
|
ErrorReporting: ErrorReportingConfig{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
DSN: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
|
|
||||||
SampleRate: 1,
|
SampleRate: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -63,11 +62,11 @@ func (c *Config) Setup(paths ...string) {
|
||||||
func (c *Config) LoadConfig(path string) error {
|
func (c *Config) LoadConfig(path string) error {
|
||||||
raw, err := os.ReadFile(path)
|
raw, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to load config file: %w", err)
|
return fmt.Errorf("failed to load config file: %w", err)
|
||||||
}
|
}
|
||||||
err = yaml.Unmarshal(raw, c)
|
err = yaml.Unmarshal(raw, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to parse YAML: %w", err)
|
return fmt.Errorf("failed to parse YAML: %w", err)
|
||||||
}
|
}
|
||||||
c.walkScheme(c)
|
c.walkScheme(c)
|
||||||
log.WithField("path", path).Debug("Loaded config")
|
log.WithField("path", path).Debug("Loaded config")
|
||||||
|
|
|
@ -39,9 +39,9 @@ type PathsConfig struct {
|
||||||
|
|
||||||
type ErrorReportingConfig struct {
|
type ErrorReportingConfig struct {
|
||||||
Enabled bool `yaml:"enabled" env:"AUTHENTIK_ERROR_REPORTING__ENABLED"`
|
Enabled bool `yaml:"enabled" env:"AUTHENTIK_ERROR_REPORTING__ENABLED"`
|
||||||
|
SentryDSN string `yaml:"sentry_dsn" env:"AUTHENTIK_ERROR_REPORTING__SENTRY_DSN"`
|
||||||
Environment string `yaml:"environment" env:"AUTHENTIK_ERROR_REPORTING__ENVIRONMENT"`
|
Environment string `yaml:"environment" env:"AUTHENTIK_ERROR_REPORTING__ENVIRONMENT"`
|
||||||
SendPII bool `yaml:"send_pii" env:"AUTHENTIK_ERROR_REPORTING__SEND_PII"`
|
SendPII bool `yaml:"send_pii" env:"AUTHENTIK_ERROR_REPORTING__SEND_PII"`
|
||||||
DSN string
|
|
||||||
SampleRate float64 `yaml:"sample_rate" env:"AUTHENTIK_ERROR_REPORTING__SAMPLE_RATE"`
|
SampleRate float64 `yaml:"sample_rate" env:"AUTHENTIK_ERROR_REPORTING__SAMPLE_RATE"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,12 +44,11 @@ func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if globalConfig.ErrorReporting.Enabled {
|
if globalConfig.ErrorReporting.Enabled {
|
||||||
dsn := "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8"
|
|
||||||
if !initialSetup {
|
if !initialSetup {
|
||||||
l.WithField("env", globalConfig.ErrorReporting.Environment).Debug("Error reporting enabled")
|
l.WithField("env", globalConfig.ErrorReporting.Environment).Debug("Error reporting enabled")
|
||||||
}
|
}
|
||||||
err := sentry.Init(sentry.ClientOptions{
|
err := sentry.Init(sentry.ClientOptions{
|
||||||
Dsn: dsn,
|
Dsn: globalConfig.ErrorReporting.SentryDsn,
|
||||||
Environment: globalConfig.ErrorReporting.Environment,
|
Environment: globalConfig.ErrorReporting.Environment,
|
||||||
TracesSampler: sentryutils.SamplerFunc(float64(globalConfig.ErrorReporting.TracesSampleRate)),
|
TracesSampler: sentryutils.SamplerFunc(float64(globalConfig.ErrorReporting.TracesSampleRate)),
|
||||||
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
||||||
|
|
|
@ -19,7 +19,7 @@ func TestSecret() string {
|
||||||
|
|
||||||
func MockConfig() api.Config {
|
func MockConfig() api.Config {
|
||||||
return *api.NewConfig(
|
return *api.NewConfig(
|
||||||
*api.NewErrorReportingConfig(false, "test", false, 0.0),
|
*api.NewErrorReportingConfig(false, "https://foo.bar/9", "test", false, 0.0),
|
||||||
[]api.CapabilitiesEnum{},
|
[]api.CapabilitiesEnum{},
|
||||||
100,
|
100,
|
||||||
100,
|
100,
|
||||||
|
|
|
@ -3,8 +3,11 @@ package web
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"goauthentik.io/internal/config"
|
"goauthentik.io/internal/config"
|
||||||
|
@ -14,12 +17,25 @@ type SentryRequest struct {
|
||||||
DSN string `json:"dsn"`
|
DSN string `json:"dsn"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WebServer) APISentryProxy(rw http.ResponseWriter, r *http.Request) {
|
func (ws *WebServer) APISentryProxy() http.HandlerFunc {
|
||||||
|
fallbackHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
}
|
||||||
if !config.Get().ErrorReporting.Enabled {
|
if !config.Get().ErrorReporting.Enabled {
|
||||||
ws.log.Debug("error reporting disabled")
|
ws.log.Debug("error reporting disabled")
|
||||||
rw.WriteHeader(http.StatusBadRequest)
|
return fallbackHandler
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
dsn, err := url.Parse(config.Get().ErrorReporting.SentryDSN)
|
||||||
|
if err != nil {
|
||||||
|
ws.log.WithError(err).Warning("invalid sentry DSN")
|
||||||
|
return fallbackHandler
|
||||||
|
}
|
||||||
|
projectId, err := strconv.Atoi(strings.TrimPrefix(dsn.Path, "/"))
|
||||||
|
if err != nil {
|
||||||
|
ws.log.WithError(err).Warning("failed to get sentry project id")
|
||||||
|
return fallbackHandler
|
||||||
|
}
|
||||||
|
return func(rw http.ResponseWriter, r *http.Request) {
|
||||||
fb := &bytes.Buffer{}
|
fb := &bytes.Buffer{}
|
||||||
_, err := io.Copy(fb, r.Body)
|
_, err := io.Copy(fb, r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -39,12 +55,19 @@ func (ws *WebServer) APISentryProxy(rw http.ResponseWriter, r *http.Request) {
|
||||||
rw.WriteHeader(http.StatusBadRequest)
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if sd.DSN != config.Get().ErrorReporting.DSN {
|
if sd.DSN != config.Get().ErrorReporting.SentryDSN {
|
||||||
ws.log.WithField("have", sd.DSN).WithField("expected", config.Get().ErrorReporting.DSN).Debug("invalid DSN")
|
|
||||||
rw.WriteHeader(http.StatusBadRequest)
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res, err := http.DefaultClient.Post("https://sentry.beryju.org/api/8/envelope/", "application/octet-stream", fb)
|
res, err := http.DefaultClient.Post(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"https://%s/api/%d/envelope/",
|
||||||
|
dsn.Host,
|
||||||
|
projectId,
|
||||||
|
),
|
||||||
|
"application/x-sentry-envelope",
|
||||||
|
fb,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ws.log.WithError(err).Warning("failed to proxy sentry")
|
ws.log.WithError(err).Warning("failed to proxy sentry")
|
||||||
rw.WriteHeader(http.StatusBadRequest)
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
|
@ -52,3 +75,4 @@ func (ws *WebServer) APISentryProxy(rw http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
rw.WriteHeader(res.StatusCode)
|
rw.WriteHeader(res.StatusCode)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ func NewWebServer(g *gounicorn.GoUnicorn) *WebServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WebServer) configureRoutes() {
|
func (ws *WebServer) configureRoutes() {
|
||||||
ws.m.Path("/api/v3/sentry/").HandlerFunc(ws.APISentryProxy)
|
ws.m.Path("/api/v3/sentry/").HandlerFunc(ws.APISentryProxy())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WebServer) Start() {
|
func (ws *WebServer) Start() {
|
||||||
|
|
|
@ -27162,6 +27162,9 @@ components:
|
||||||
enabled:
|
enabled:
|
||||||
type: boolean
|
type: boolean
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
sentry_dsn:
|
||||||
|
type: string
|
||||||
|
readOnly: true
|
||||||
environment:
|
environment:
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
@ -27176,6 +27179,7 @@ components:
|
||||||
- enabled
|
- enabled
|
||||||
- environment
|
- environment
|
||||||
- send_pii
|
- send_pii
|
||||||
|
- sentry_dsn
|
||||||
- traces_sample_rate
|
- traces_sample_rate
|
||||||
Event:
|
Event:
|
||||||
type: object
|
type: object
|
||||||
|
|
|
@ -14,7 +14,7 @@ export async function configureSentry(canDoPpi = false): Promise<Config> {
|
||||||
const cfg = await config();
|
const cfg = await config();
|
||||||
if (cfg.errorReporting.enabled) {
|
if (cfg.errorReporting.enabled) {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
|
dsn: cfg.errorReporting.sentryDsn,
|
||||||
ignoreErrors: [
|
ignoreErrors: [
|
||||||
/network/gi,
|
/network/gi,
|
||||||
/fetch/gi,
|
/fetch/gi,
|
||||||
|
|
|
@ -90,9 +90,20 @@ Disable the inbuilt update-checker. Defaults to `false`.
|
||||||
|
|
||||||
Error reports are sent to https://sentry.beryju.org, and are used for debugging and general feedback. Anonymous performance data is also sent.
|
Error reports are sent to https://sentry.beryju.org, and are used for debugging and general feedback. Anonymous performance data is also sent.
|
||||||
|
|
||||||
|
- `AUTHENTIK_ERROR_REPORTING__SENTRY_DSN`
|
||||||
|
|
||||||
|
Sets the DSN for the Sentry API endpoint. Defaults to `https://sentry.beryju.org`.
|
||||||
|
|
||||||
|
When error reporting is enabled, the default Sentry DSN will allow the authentik developers to receive error reports and anonymous performance data, which is used for general feedback about authentik, and in some cases, may be used for debugging purposes.
|
||||||
|
|
||||||
|
Users can create their own hosted Sentry account (or self-host Sentry) and opt to collect this data themselves.
|
||||||
|
|
||||||
- `AUTHENTIK_ERROR_REPORTING__ENVIRONMENT`
|
- `AUTHENTIK_ERROR_REPORTING__ENVIRONMENT`
|
||||||
|
|
||||||
Unique environment that is attached to your error reports, should be set to your email address for example. Defaults to `customer`.
|
The environment tag associated with all data sent to Sentry. Defaults to `customer`.
|
||||||
|
|
||||||
|
When error reporting has been enabled to aid in debugging issues, this should be set to a unique
|
||||||
|
value, such as an e-mail address.
|
||||||
|
|
||||||
- `AUTHENTIK_ERROR_REPORTING__SEND_PII`
|
- `AUTHENTIK_ERROR_REPORTING__SEND_PII`
|
||||||
|
|
||||||
|
|
Reference in New Issue