api: replace django sentry proxy with go proxy to prevent login issues
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
6da78b8c32
commit
0d02dbf55c
|
@ -1,19 +0,0 @@
|
|||
"""API tasks"""
|
||||
|
||||
from authentik.lib.utils.http import get_http_session
|
||||
from authentik.root.celery import CELERY_APP
|
||||
|
||||
SENTRY_SESSION = get_http_session()
|
||||
|
||||
|
||||
@CELERY_APP.task()
|
||||
def sentry_proxy(payload: str):
|
||||
"""Relay data to sentry"""
|
||||
SENTRY_SESSION.post(
|
||||
"https://sentry.beryju.org/api/8/envelope/",
|
||||
data=payload,
|
||||
headers={
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
timeout=10,
|
||||
)
|
|
@ -1,65 +0,0 @@
|
|||
"""Sentry tunnel"""
|
||||
from json import loads
|
||||
|
||||
from django.conf import settings
|
||||
from django.http.request import HttpRequest
|
||||
from django.http.response import HttpResponse
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.parsers import BaseParser
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.throttling import AnonRateThrottle
|
||||
from rest_framework.views import APIView
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.api.tasks import sentry_proxy
|
||||
from authentik.lib.config import CONFIG
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class PlainTextParser(BaseParser):
|
||||
"""Plain text parser."""
|
||||
|
||||
media_type = "text/plain"
|
||||
|
||||
def parse(self, stream, media_type=None, parser_context=None) -> str:
|
||||
"""Simply return a string representing the body of the request."""
|
||||
return stream.read()
|
||||
|
||||
|
||||
class CsrfExemptSessionAuthentication(SessionAuthentication):
|
||||
"""CSRF-exempt Session authentication"""
|
||||
|
||||
def enforce_csrf(self, request: Request):
|
||||
return # To not perform the csrf check previously happening
|
||||
|
||||
|
||||
class SentryTunnelView(APIView):
|
||||
"""Sentry tunnel, to prevent ad blockers from blocking sentry"""
|
||||
|
||||
serializer_class = None
|
||||
parser_classes = [PlainTextParser]
|
||||
throttle_classes = [AnonRateThrottle]
|
||||
permission_classes = [AllowAny]
|
||||
authentication_classes = [CsrfExemptSessionAuthentication]
|
||||
|
||||
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
"""Sentry tunnel, to prevent ad blockers from blocking sentry"""
|
||||
# Only allow usage of this endpoint when error reporting is enabled
|
||||
if not CONFIG.y_bool("error_reporting.enabled", False):
|
||||
LOGGER.debug("error reporting disabled")
|
||||
return HttpResponse(status=400)
|
||||
# Body is 2 json objects separated by \n
|
||||
full_body = request.body
|
||||
lines = full_body.splitlines()
|
||||
if len(lines) < 1:
|
||||
return HttpResponse(status=400)
|
||||
header = loads(lines[0])
|
||||
# Check that the DSN is what we expect
|
||||
dsn = header.get("dsn", "")
|
||||
if dsn != settings.SENTRY_DSN:
|
||||
LOGGER.debug("Invalid dsn", have=dsn, expected=settings.SENTRY_DSN)
|
||||
return HttpResponse(status=400)
|
||||
sentry_proxy.delay(full_body.decode())
|
||||
return HttpResponse(status=204)
|
|
@ -11,7 +11,6 @@ from authentik.admin.api.tasks import TaskViewSet
|
|||
from authentik.admin.api.version import VersionView
|
||||
from authentik.admin.api.workers import WorkerView
|
||||
from authentik.api.v3.config import ConfigView
|
||||
from authentik.api.v3.sentry import SentryTunnelView
|
||||
from authentik.api.views import APIBrowserView
|
||||
from authentik.core.api.applications import ApplicationViewSet
|
||||
from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet
|
||||
|
@ -249,7 +248,6 @@ urlpatterns = (
|
|||
FlowInspectorView.as_view(),
|
||||
name="flow-inspector",
|
||||
),
|
||||
path("sentry/", SentryTunnelView.as_view(), name="sentry"),
|
||||
path("schema/", cache_page(86400)(SpectacularAPIView.as_view()), name="schema"),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -38,7 +38,7 @@ func main() {
|
|||
|
||||
if config.G.ErrorReporting.Enabled {
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
|
||||
Dsn: config.G.ErrorReporting.DSN,
|
||||
AttachStacktrace: true,
|
||||
TracesSampleRate: 0.6,
|
||||
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
||||
|
|
|
@ -26,6 +26,7 @@ func DefaultConfig() {
|
|||
LogLevel: "info",
|
||||
ErrorReporting: ErrorReportingConfig{
|
||||
Enabled: false,
|
||||
DSN: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,4 +42,5 @@ type ErrorReportingConfig struct {
|
|||
Enabled bool `yaml:"enabled" env:"AUTHENTIK_ERROR_REPORTING__ENABLED"`
|
||||
Environment string `yaml:"environment" env:"AUTHENTIK_ERROR_REPORTING__ENVIRONMENT"`
|
||||
SendPII bool `yaml:"send_pii" env:"AUTHENTIK_ERROR_REPORTING__SEND_PII"`
|
||||
DSN string
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"goauthentik.io/internal/config"
|
||||
)
|
||||
|
||||
type SentryRequest struct {
|
||||
DSN string `json:"dsn"`
|
||||
}
|
||||
|
||||
func (ws *WebServer) APISentryProxy(rw http.ResponseWriter, r *http.Request) {
|
||||
if !config.G.ErrorReporting.Enabled {
|
||||
ws.log.Debug("error reporting disabled")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fullBody, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
ws.log.Debug("failed to read body")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
lines := strings.Split(string(fullBody), "\n")
|
||||
if len(lines) < 1 {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
sd := SentryRequest{}
|
||||
ws.log.Debug(lines[0])
|
||||
err = json.Unmarshal([]byte(lines[0]), &sd)
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to parse sentry request")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if sd.DSN != config.G.ErrorReporting.DSN {
|
||||
ws.log.WithField("have", sd.DSN).WithField("expected", config.G.ErrorReporting.DSN).Debug("invalid DSN")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
res, err := http.DefaultClient.Post("https://sentry.beryju.org/api/8/envelope/", "application/octet-stream", strings.NewReader(string(fullBody)))
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to proxy sentry")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(res.StatusCode)
|
||||
}
|
|
@ -51,10 +51,15 @@ func NewWebServer(g *gounicorn.GoUnicorn) *WebServer {
|
|||
p: g,
|
||||
}
|
||||
ws.configureStatic()
|
||||
ws.configureRoutes()
|
||||
ws.configureProxy()
|
||||
return ws
|
||||
}
|
||||
|
||||
func (ws *WebServer) configureRoutes() {
|
||||
ws.m.Path("/api/v3/sentry/").HandlerFunc(ws.APISentryProxy)
|
||||
}
|
||||
|
||||
func (ws *WebServer) Start() {
|
||||
go ws.listenPlain()
|
||||
go ws.listenTLS()
|
||||
|
|
Reference in New Issue