diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 27f7af80a..dd4535c1b 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -147,10 +147,15 @@ jobs: run: scripts/ci_prepare.sh - name: run migrations to stable run: pipenv run python -m lifecycle.migrate + - name: prepare variables + id: ev + run: | + python ./scripts/gh_do_set_branch.py - name: checkout current code run: | set -x - git checkout $GITHUB_REF + git fetch + git checkout ${{ steps.ev.outputs.branchName }} pipenv sync --dev - name: migrate to latest run: pipenv run python -m lifecycle.migrate diff --git a/.github/workflows/ci-outpost.yml b/.github/workflows/ci-outpost.yml index 163981675..50c267184 100644 --- a/.github/workflows/ci-outpost.yml +++ b/.github/workflows/ci-outpost.yml @@ -44,8 +44,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1.2.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: prepare variables @@ -70,6 +68,5 @@ jobs: beryju.org/authentik/outpost-${{ matrix.type }}:gh-${{ steps.ev.outputs.branchName }}-${{ steps.ev.outputs.timestamp }} beryju.org/authentik/outpost-${{ matrix.type }}:gh-${{ steps.ev.outputs.sha }} file: ${{ matrix.type }}.Dockerfile - platforms: linux/amd64,linux/arm64 build-args: | GIT_BUILD_HASH=${{ steps.ev.outputs.sha }} diff --git a/authentik/providers/oauth2/migrations/0017_alter_oauth2provider_token_validity.py b/authentik/providers/oauth2/migrations/0017_alter_oauth2provider_token_validity.py new file mode 100644 index 000000000..5783ff7ea --- /dev/null +++ b/authentik/providers/oauth2/migrations/0017_alter_oauth2provider_token_validity.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.6 on 2021-09-08 15:12 + +from django.db import migrations, models + +import authentik.lib.utils.time + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_providers_oauth2", "0016_alter_authorizationcode_nonce"), + ] + + operations = [ + migrations.AlterField( + model_name="oauth2provider", + name="token_validity", + field=models.TextField( + default="days=30", + help_text="Tokens not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).", + validators=[authentik.lib.utils.time.timedelta_string_validator], + ), + ), + ] diff --git a/authentik/providers/oauth2/models.py b/authentik/providers/oauth2/models.py index 72f101c0d..5fc9ea470 100644 --- a/authentik/providers/oauth2/models.py +++ b/authentik/providers/oauth2/models.py @@ -182,7 +182,7 @@ class OAuth2Provider(Provider): ), ) token_validity = models.TextField( - default="minutes=10", + default="days=30", validators=[timedelta_string_validator], help_text=_( ( diff --git a/authentik/providers/oauth2/tests/test_authorize.py b/authentik/providers/oauth2/tests/test_authorize.py index 68008aeeb..4172486b5 100644 --- a/authentik/providers/oauth2/tests/test_authorize.py +++ b/authentik/providers/oauth2/tests/test_authorize.py @@ -247,7 +247,7 @@ class TestAuthorize(OAuthTestCase): "to": ( f"http://localhost#access_token={token.access_token}" f"&id_token={provider.encode(token.id_token.to_dict())}&token_type=bearer" - f"&expires_in=600&state={state}" + f"&expires_in=2592000&state={state}" ), }, ) diff --git a/authentik/providers/oauth2/tests/test_token.py b/authentik/providers/oauth2/tests/test_token.py index 97a0b220e..1ec7e4521 100644 --- a/authentik/providers/oauth2/tests/test_token.py +++ b/authentik/providers/oauth2/tests/test_token.py @@ -141,7 +141,7 @@ class TestToken(OAuthTestCase): "access_token": new_token.access_token, "refresh_token": new_token.refresh_token, "token_type": "bearer", - "expires_in": 600, + "expires_in": 2592000, "id_token": provider.encode( new_token.id_token.to_dict(), ), @@ -190,7 +190,7 @@ class TestToken(OAuthTestCase): "access_token": new_token.access_token, "refresh_token": new_token.refresh_token, "token_type": "bearer", - "expires_in": 600, + "expires_in": 2592000, "id_token": provider.encode( new_token.id_token.to_dict(), ), @@ -236,7 +236,7 @@ class TestToken(OAuthTestCase): "access_token": new_token.access_token, "refresh_token": new_token.refresh_token, "token_type": "bearer", - "expires_in": 600, + "expires_in": 2592000, "id_token": provider.encode( new_token.id_token.to_dict(), ), diff --git a/authentik/providers/proxy/api.py b/authentik/providers/proxy/api.py index f77df23ca..65039fb50 100644 --- a/authentik/providers/proxy/api.py +++ b/authentik/providers/proxy/api.py @@ -1,7 +1,7 @@ """ProxyProvider API Views""" from typing import Any -from drf_spectacular.utils import extend_schema_field, extend_schema_serializer +from drf_spectacular.utils import extend_schema_field from rest_framework.exceptions import ValidationError from rest_framework.fields import CharField, ListField, SerializerMethodField from rest_framework.serializers import ModelSerializer @@ -72,6 +72,7 @@ class ProxyProviderSerializer(ProviderSerializer): "mode", "redirect_uris", "cookie_domain", + "token_validity", ] @@ -101,7 +102,6 @@ class ProxyProviderViewSet(UsedByMixin, ModelViewSet): ordering = ["name"] -@extend_schema_serializer(deprecate_fields=["forward_auth_mode"]) class ProxyOutpostConfigSerializer(ModelSerializer): """Proxy provider serializer for outposts""" diff --git a/authentik/providers/proxy/controllers/docker.py b/authentik/providers/proxy/controllers/docker.py index cc1e3b541..9047c612d 100644 --- a/authentik/providers/proxy/controllers/docker.py +++ b/authentik/providers/proxy/controllers/docker.py @@ -13,8 +13,8 @@ class ProxyDockerController(DockerController): def __init__(self, outpost: Outpost, connection: DockerServiceConnection): super().__init__(outpost, connection) self.deployment_ports = [ - DeploymentPort(4180, "http", "tcp"), - DeploymentPort(4443, "https", "tcp"), + DeploymentPort(9000, "http", "tcp"), + DeploymentPort(9443, "https", "tcp"), ] def _get_labels(self) -> dict[str, str]: @@ -30,5 +30,5 @@ class ProxyDockerController(DockerController): labels[f"traefik.http.routers.{traefik_name}-router.tls"] = "true" labels[f"traefik.http.routers.{traefik_name}-router.service"] = f"{traefik_name}-service" labels[f"traefik.http.services.{traefik_name}-service.loadbalancer.healthcheck.path"] = "/" - labels[f"traefik.http.services.{traefik_name}-service.loadbalancer.server.port"] = "4180" + labels[f"traefik.http.services.{traefik_name}-service.loadbalancer.server.port"] = "9000" return labels diff --git a/authentik/providers/proxy/controllers/k8s/traefik.py b/authentik/providers/proxy/controllers/k8s/traefik.py index 0f850f74b..49e90545c 100644 --- a/authentik/providers/proxy/controllers/k8s/traefik.py +++ b/authentik/providers/proxy/controllers/k8s/traefik.py @@ -96,7 +96,6 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware]) def get_reference_object(self) -> TraefikMiddleware: """Get deployment object for outpost""" - port = 9000 if self.is_embedded else 4180 return TraefikMiddleware( apiVersion=f"{CRD_GROUP}/{CRD_VERSION}", kind="Middleware", @@ -107,7 +106,7 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware]) ), spec=TraefikMiddlewareSpec( forwardAuth=TraefikMiddlewareSpecForwardAuth( - address=f"http://{self.name}.{self.namespace}:{port}/akprox/auth?traefik", + address=f"http://{self.name}.{self.namespace}:9000/akprox/auth?traefik", authResponseHeaders=[ "Set-Cookie", "X-Auth-Username", diff --git a/authentik/providers/proxy/controllers/kubernetes.py b/authentik/providers/proxy/controllers/kubernetes.py index 7c728ece2..3dbcffe3f 100644 --- a/authentik/providers/proxy/controllers/kubernetes.py +++ b/authentik/providers/proxy/controllers/kubernetes.py @@ -12,8 +12,8 @@ class ProxyKubernetesController(KubernetesController): def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection): super().__init__(outpost, connection) self.deployment_ports = [ - DeploymentPort(4180, "http", "tcp"), - DeploymentPort(4443, "https", "tcp"), + DeploymentPort(9000, "http", "tcp"), + DeploymentPort(9443, "https", "tcp"), ] self.reconcilers["ingress"] = IngressReconciler self.reconcilers["traefik middleware"] = TraefikMiddlewareReconciler diff --git a/authentik/providers/proxy/models.py b/authentik/providers/proxy/models.py index 69c00c468..a8dee92ba 100644 --- a/authentik/providers/proxy/models.py +++ b/authentik/providers/proxy/models.py @@ -128,8 +128,8 @@ class ProxyProvider(OutpostModel, OAuth2Provider): def set_oauth_defaults(self): """Ensure all OAuth2-related settings are correct""" self.client_type = ClientTypes.CONFIDENTIAL - self.jwt_alg = JWTAlgorithms.RS256 - self.rsa_key = CertificateKeyPair.objects.exclude(key_data__iexact="").first() + self.jwt_alg = JWTAlgorithms.HS256 + self.rsa_key = None scopes = ScopeMapping.objects.filter( scope_name__in=[ SCOPE_OPENID, @@ -139,12 +139,7 @@ class ProxyProvider(OutpostModel, OAuth2Provider): ] ) self.property_mappings.set(scopes) - self.redirect_uris = "\n".join( - [ - _get_callback_url(self.external_host), - _get_callback_url(self.internal_host), - ] - ) + self.redirect_uris = _get_callback_url(self.external_host) def __str__(self): return f"Proxy Provider {self.name}" diff --git a/authentik/root/asgi/logger.py b/authentik/root/asgi/logger.py index 45ef1e20f..1f1568e48 100644 --- a/authentik/root/asgi/logger.py +++ b/authentik/root/asgi/logger.py @@ -64,9 +64,8 @@ class ASGILogger: return return await self.app(scope, receive, send_hooked) - def _get_ip(self, scope: Scope) -> str: + def _get_ip(self, headers: dict[bytes, bytes], scope: Scope) -> str: client_ip = None - headers = dict(scope.get("headers", [])) for header in ASGI_IP_HEADERS: if header in headers: client_ip = headers[header].decode() @@ -77,7 +76,8 @@ class ASGILogger: def log(self, scope: Scope, content_length: int, runtime: float, status_code: int, **kwargs): """Outpot access logs in a structured format""" - host = self._get_ip(scope) + headers = dict(scope.get("headers", [])) + host = self._get_ip(headers, scope) query_string = "" if scope.get("query_string", b"") != b"": query_string = f"?{scope.get('query_string').decode()}" @@ -89,5 +89,6 @@ class ASGILogger: status=status_code, size=content_length / 1000 if content_length > 0 else 0, runtime=runtime, + user_agent=headers.get(b"user-agent", b"").decode(), **kwargs, ) diff --git a/authentik/root/test_runner.py b/authentik/root/test_runner.py index 9cab2175d..dff172ea9 100644 --- a/authentik/root/test_runner.py +++ b/authentik/root/test_runner.py @@ -2,6 +2,7 @@ from django.conf import settings from authentik.lib.config import CONFIG +from tests.e2e.utils import get_docker_tag class PytestTestRunner: # pragma: no cover @@ -17,7 +18,7 @@ class PytestTestRunner: # pragma: no cover CONFIG.y_set("authentik.geoip", "tests/GeoLite2-City-Test.mmdb") CONFIG.y_set( "outposts.docker_image_base", - "beryju.org/authentik/outpost-%(type)s:gh-master", + f"beryju.org/authentik/outpost-%(type)s:{get_docker_tag()}", ) def run_tests(self, test_labels): diff --git a/cmd/proxy/server.go b/cmd/proxy/server.go index f6073d62b..97c4c1147 100644 --- a/cmd/proxy/server.go +++ b/cmd/proxy/server.go @@ -9,7 +9,7 @@ import ( "goauthentik.io/internal/common" "goauthentik.io/internal/outpost/ak" - "goauthentik.io/internal/outpost/proxy" + "goauthentik.io/internal/outpost/proxyv2" ) const helpMessage = `authentik proxy @@ -17,7 +17,10 @@ const helpMessage = `authentik proxy Required environment variables: - AUTHENTIK_HOST: URL to connect to (format "http://authentik.company") - AUTHENTIK_TOKEN: Token to authenticate with -- AUTHENTIK_INSECURE: Skip SSL Certificate verification` +- AUTHENTIK_INSECURE: Skip SSL Certificate verification + +Optionally, you can set these: +- AUTHENTIK_HOST_BROWSER: URL to use in the browser, when it differs from AUTHENTIK_HOST` func main() { log.SetLevel(log.DebugLevel) @@ -46,7 +49,7 @@ func main() { ac := ak.NewAPIController(*akURLActual, akToken) - ac.Server = proxy.NewServer(ac) + ac.Server = proxyv2.NewProxyServer(ac) err = ac.Start() if err != nil { diff --git a/cmd/server/main.go b/cmd/server/main.go index b7101839a..aad8c7f47 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -12,7 +12,7 @@ import ( "goauthentik.io/internal/constants" "goauthentik.io/internal/gounicorn" "goauthentik.io/internal/outpost/ak" - "goauthentik.io/internal/outpost/proxy" + "goauthentik.io/internal/outpost/proxyv2" "goauthentik.io/internal/web" ) @@ -99,7 +99,7 @@ func attemptProxyStart(ws *web.WebServer, u *url.URL) { } continue } - srv := proxy.NewServer(ac) + srv := proxyv2.NewProxyServer(ac) ws.ProxyServer = srv ac.Server = srv log.WithField("logger", "authentik").Debug("attempting to start outpost") diff --git a/go.mod b/go.mod index 816dd9bb8..9556e78c7 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/coreos/go-oidc v2.2.1+incompatible + github.com/garyburd/redigo v1.6.2 // indirect github.com/getsentry/sentry-go v0.11.0 github.com/go-ldap/ldap/v3 v3.4.1 github.com/go-openapi/analysis v0.20.1 // indirect @@ -14,39 +15,30 @@ require ( github.com/go-openapi/strfmt v0.20.2 github.com/go-openapi/swag v0.19.15 // indirect github.com/go-openapi/validate v0.20.2 // indirect - github.com/go-redis/redis/v7 v7.4.0 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.3.0 github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 + github.com/gorilla/securecookie v1.1.1 + github.com/gorilla/sessions v1.2.1 github.com/gorilla/websocket v1.4.2 github.com/imdario/mergo v0.3.12 - github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a - github.com/justinas/alice v1.2.0 - github.com/kr/pretty v0.2.1 // indirect - github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3 - github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc - github.com/pelletier/go-toml v1.9.1 // indirect github.com/pires/go-proxyproto v0.6.0 github.com/pkg/errors v0.9.1 github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect + github.com/prometheus/client_golang v1.11.0 github.com/recws-org/recws v1.3.1 github.com/sirupsen/logrus v1.8.1 - github.com/spf13/afero v1.6.0 // indirect - github.com/spf13/cast v1.3.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.7.1 // indirect go.mongodb.org/mongo-driver v1.5.2 // indirect golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect - golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 // indirect - golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect + golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 google.golang.org/appengine v1.6.7 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect + gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 7749ce9f4..879c8c22b 100644 --- a/go.sum +++ b/go.sum @@ -12,7 +12,6 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= @@ -22,7 +21,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -36,18 +34,13 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw= -github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= -github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb h1:w9IDEB7P1VzNcBpOG7kMpFkZp2DkyJIUt0gDx5MBhRU= github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb/go.mod h1:9XMFaCeRyW7fC9XJOWQ+NdAv8VLG7ys7l3x4ozEGLUQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -57,18 +50,12 @@ github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqR github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE= -github.com/alicebob/miniredis/v2 v2.13.0 h1:QPosMaxm+r6Qs+YcCtL2Z2a2RSdC9VfXJLpd80l8ICU= -github.com/alicebob/miniredis/v2 v2.13.0/go.mod h1:0UIBNuf97uxrWhdVBpJvPtafKyGpL2NS2pYe0tYM97k= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= @@ -80,40 +67,30 @@ github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/ github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -124,19 +101,15 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE= -github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/garyburd/redigo v1.6.2 h1:yE/pwKCrbLpLpQICzYTeZ7JsTA/C53wFTJHaEtRqniM= +github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getsentry/sentry-go v0.11.0 h1:qro8uttJGvNAMr5CLcFI9CHR0aDzXl0Vs3Pmw/oTPg8= github.com/getsentry/sentry-go v0.11.0/go.mod h1:KBQIxiZAetw62Cj8Ri964vAEWVdgfaUCn30Q3bCvANo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -150,10 +123,13 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-ldap/ldap/v3 v3.4.1 h1:fU/0xli6HY02ocbMuozHAYsaHLcnkLjvho2r5a34BUU= github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -249,9 +225,6 @@ github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9G github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts= github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= -github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= -github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= -github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -283,12 +256,11 @@ github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22 github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -310,13 +282,12 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/gomodule/redigo v1.8.1/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -327,6 +298,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -346,43 +318,23 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= @@ -394,25 +346,22 @@ github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/ github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= -github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= -github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= -github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= @@ -421,7 +370,6 @@ github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYb github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -430,12 +378,9 @@ github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -444,9 +389,6 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -457,27 +399,16 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa h1:hI1uC2A3vJFjwvBn0G0a7QBRdBUp6Y048BtLAHRTKPo= -github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa/go.mod h1:8vxFeeg++MqgCHwehSuwTlYCF0ALyDJbYJ1JsKi7v6s= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -491,6 +422,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -501,33 +433,17 @@ github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 h1:D9EvfGQvlkKaD github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484/go.mod h1:O1EljZ+oHprtxDDPHiMWVo/5dBT6PlvWX5PSwj80aBA= github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3 h1:NNis9uuNpG5h97Dvxxo53Scg02qBg+3Nfabg6zjFGu8= github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3/go.mod h1:YtrVB1/v9Td9SyjXpjYVmbdKgj9B0nPTBsdGUxy0i8U= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc h1:jf/4meI7lkRwGoiD7Ex/ns0BekEPKZ8nsB3u2oLhLGM= -github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc/go.mod h1:AtLBgw8gBVaipTvaFq25VIdy+TQQMsj5m7dEUhxplpo= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.9.1 h1:a6qW1EVNZWH9WGI6CsYdD8WAylkoXBS5yv0XHlh17Tc= -github.com/pelletier/go-toml v1.9.1/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pires/go-proxyproto v0.6.0 h1:cLJUPnuQdiNf7P/wbeOKmM1khVdaMgTFDLj8h9ZrVYk= @@ -536,81 +452,66 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac h1:jWKYCNlX4J5s8M0nHYkh7Y7c9gRVDEb3mq51j5J0F5M= github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/recws-org/recws v1.3.1 h1:vtRhYpgNPBs3iFyu/+zxBqNzLYgID7UPC5siThkvbs0= github.com/recws-org/recws v1.3.1/go.mod h1:gRH/uJLMsO7lbcecAB1Im1Zc6eKxs93ftGR0R39QeYA= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= -github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -621,10 +522,6 @@ github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBn github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/vmihailenco/msgpack/v4 v4.3.11 h1:Q47CePddpNGNhk4GCnAx9DDtASi2rasatE0cd26cZoE= -github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= @@ -633,11 +530,8 @@ github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHM github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yhat/wsutil v0.0.0-20170731153501-1d66fa95c997 h1:1+FQ4Ns+UZtUiQ4lP0sTCyKSQ0EXoiwAdHZB0Pd5t9Q= -github.com/yhat/wsutil v0.0.0-20170731153501-1d66fa95c997/go.mod h1:DIGbh/f5XMAessMV/uaIik81gkDVjUeQ9ApdaU7wRKE= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= @@ -645,11 +539,6 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -github.com/yuin/gopher-lua v0.0.0-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0= -github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= @@ -664,13 +553,8 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -681,7 +565,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -723,9 +606,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -735,14 +616,12 @@ golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -753,7 +632,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -782,15 +660,12 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -808,14 +683,11 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -827,14 +699,16 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= @@ -848,7 +722,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -875,7 +748,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -921,7 +793,6 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -959,11 +830,9 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -973,7 +842,6 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -989,6 +857,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b h1:U/Uqd1232+wrnHOvWNaxrNqn/kFnr4yu4blgPtQt0N8= +gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b/go.mod h1:fgfIZMlsafAHpspcks2Bul+MWUNw/2dyQmjC2faKjtg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -998,23 +868,15 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/internal/constants/constants.go b/internal/constants/constants.go index c879d3a7a..a05530fd2 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -14,7 +14,7 @@ func BUILD() string { } func OutpostUserAgent() string { - return fmt.Sprintf("authentik-outpost@%s (%s)", VERSION, BUILD()) + return fmt.Sprintf("authentik-outpost@%s (build=%s)", VERSION, BUILD()) } const VERSION = "2021.8.4" diff --git a/internal/outpost/ak/api.go b/internal/outpost/ak/api.go index b690e1cba..e12492c62 100644 --- a/internal/outpost/ak/api.go +++ b/internal/outpost/ak/api.go @@ -11,6 +11,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/google/uuid" "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" "github.com/recws-org/recws" "goauthentik.io/api" "goauthentik.io/internal/constants" @@ -43,11 +44,10 @@ type APIController struct { // NewAPIController initialise new API Controller instance from URL and API token func NewAPIController(akURL url.URL, token string) *APIController { config := api.NewConfiguration() - config.UserAgent = constants.OutpostUserAgent() config.Host = akURL.Host config.Scheme = akURL.Scheme config.HTTPClient = &http.Client{ - Transport: NewTracingTransport(context.TODO(), GetTLSTransport()), + Transport: NewUserAgentTransport(constants.OutpostUserAgent(), NewTracingTransport(context.TODO(), GetTLSTransport())), } config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token)) @@ -89,6 +89,13 @@ func NewAPIController(akURL url.URL, token string) *APIController { } ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset) ac.initWS(akURL, strfmt.UUID(outpost.Pk)) + + OutpostInfo.With(prometheus.Labels{ + "uuid": ac.instanceUUID.String(), + "name": outpost.Name, + "version": constants.VERSION, + "build": constants.BUILD(), + }).Set(1) return ac } @@ -111,6 +118,13 @@ func (a *APIController) StartBackgorundTasks() error { err := a.Server.Refresh() if err != nil { return errors.Wrap(err, "failed to run initial refresh") + } else { + LastUpdate.With(prometheus.Labels{ + "uuid": a.instanceUUID.String(), + "name": a.Outpost.Name, + "version": constants.VERSION, + "build": constants.BUILD(), + }).SetToCurrentTime() } go func() { a.logger.Debug("Starting WS Handler...") diff --git a/internal/outpost/ak/api_update.go b/internal/outpost/ak/api_update.go deleted file mode 100644 index d88998cab..000000000 --- a/internal/outpost/ak/api_update.go +++ /dev/null @@ -1,16 +0,0 @@ -package ak - -import ( - "context" - - "goauthentik.io/api" -) - -func (a *APIController) Update() ([]api.ProxyOutpostConfig, error) { - providers, _, err := a.Client.OutpostsApi.OutpostsProxyList(context.Background()).Execute() - if err != nil { - a.logger.WithError(err).Error("Failed to fetch providers") - return nil, err - } - return providers.Results, nil -} diff --git a/internal/outpost/ak/api_ws.go b/internal/outpost/ak/api_ws.go index d66addfec..4ba1e53b8 100644 --- a/internal/outpost/ak/api_ws.go +++ b/internal/outpost/ak/api_ws.go @@ -11,6 +11,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/gorilla/websocket" + "github.com/prometheus/client_golang/prometheus" "github.com/recws-org/recws" "goauthentik.io/internal/constants" ) @@ -74,17 +75,32 @@ func (ac *APIController) startWSHandler() { var wsMsg websocketMessage err := ac.wsConn.ReadJSON(&wsMsg) if err != nil { + ConnectionStatus.With(prometheus.Labels{ + "uuid": ac.instanceUUID.String(), + "name": ac.Outpost.Name, + }).Set(0) logger.WithError(err).Warning("ws write error, reconnecting") ac.wsConn.CloseAndReconnect() time.Sleep(time.Second * 5) continue } + ConnectionStatus.With(prometheus.Labels{ + "uuid": ac.instanceUUID.String(), + "name": ac.Outpost.Name, + }).Set(1) if wsMsg.Instruction == WebsocketInstructionTriggerUpdate { time.Sleep(ac.reloadOffset) logger.Debug("Got update trigger...") err := ac.Server.Refresh() if err != nil { logger.WithError(err).Debug("Failed to update") + } else { + LastUpdate.With(prometheus.Labels{ + "uuid": ac.instanceUUID.String(), + "name": ac.Outpost.Name, + "version": constants.VERSION, + "build": constants.BUILD(), + }).SetToCurrentTime() } } } @@ -110,6 +126,11 @@ func (ac *APIController) startWSHealth() { ac.logger.WithField("loop", "ws-health").WithError(err).Warning("ws write error, reconnecting") ac.wsConn.CloseAndReconnect() continue + } else { + ConnectionStatus.With(prometheus.Labels{ + "uuid": ac.instanceUUID.String(), + "name": ac.Outpost.Name, + }).Set(1) } } } @@ -121,6 +142,13 @@ func (ac *APIController) startIntervalUpdater() { err := ac.Server.Refresh() if err != nil { logger.WithError(err).Debug("Failed to update") + } else { + LastUpdate.With(prometheus.Labels{ + "uuid": ac.instanceUUID.String(), + "name": ac.Outpost.Name, + "version": constants.VERSION, + "build": constants.BUILD(), + }).SetToCurrentTime() } } } diff --git a/internal/outpost/ak/tracing.go b/internal/outpost/ak/http_tracing.go similarity index 70% rename from internal/outpost/ak/tracing.go rename to internal/outpost/ak/http_tracing.go index dd3bf40d6..4f267d403 100644 --- a/internal/outpost/ak/tracing.go +++ b/internal/outpost/ak/http_tracing.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/getsentry/sentry-go" + log "github.com/sirupsen/logrus" ) type tracingTransport struct { @@ -21,5 +22,10 @@ func (tt *tracingTransport) RoundTrip(r *http.Request) (*http.Response, error) { span.SetTag("url", r.URL.String()) span.SetTag("method", r.Method) defer span.Finish() - return tt.inner.RoundTrip(r.WithContext(span.Context())) + res, err := tt.inner.RoundTrip(r.WithContext(span.Context())) + log.WithFields(log.Fields{ + "url": r.URL.String(), + "method": r.Method, + }).Trace("http request") + return res, err } diff --git a/internal/outpost/ak/http_user_agent.go b/internal/outpost/ak/http_user_agent.go new file mode 100644 index 000000000..114323bef --- /dev/null +++ b/internal/outpost/ak/http_user_agent.go @@ -0,0 +1,19 @@ +package ak + +import ( + "net/http" +) + +type userAgentTransport struct { + inner http.RoundTripper + ua string +} + +func NewUserAgentTransport(ua string, inner http.RoundTripper) *userAgentTransport { + return &userAgentTransport{inner, ua} +} + +func (uat *userAgentTransport) RoundTrip(r *http.Request) (*http.Response, error) { + r.Header.Set("User-Agent", uat.ua) + return uat.inner.RoundTrip(r) +} diff --git a/internal/outpost/ak/metrics.go b/internal/outpost/ak/metrics.go new file mode 100644 index 000000000..72e699bf9 --- /dev/null +++ b/internal/outpost/ak/metrics.go @@ -0,0 +1,21 @@ +package ak + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +var ( + OutpostInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "authentik_outpost_info", + Help: "Outpost info", + }, []string{"uuid", "name", "version", "build"}) + LastUpdate = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "authentik_outpost_last_update", + Help: "Time of last update", + }, []string{"uuid", "name", "version", "build"}) + ConnectionStatus = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "authentik_outpost_connection", + Help: "Connection status", + }, []string{"uuid", "name"}) +) diff --git a/internal/outpost/flow.go b/internal/outpost/flow.go index 97dbc2c7f..591776166 100644 --- a/internal/outpost/flow.go +++ b/internal/outpost/flow.go @@ -59,10 +59,9 @@ func NewFlowExecutor(ctx context.Context, flowSlug string, refConfig *api.Config config := api.NewConfiguration() config.Host = refConfig.Host config.Scheme = refConfig.Scheme - config.UserAgent = constants.OutpostUserAgent() config.HTTPClient = &http.Client{ Jar: jar, - Transport: ak.NewTracingTransport(ctx, ak.GetTLSTransport()), + Transport: ak.NewUserAgentTransport(constants.OutpostUserAgent(), ak.NewTracingTransport(ctx, ak.GetTLSTransport())), } token := strings.Split(refConfig.DefaultHeader["Authorization"], " ")[1] config.AddDefaultHeader(HeaderAuthentikOutpostToken, token) diff --git a/internal/outpost/ldap/api.go b/internal/outpost/ldap/api.go index b42356048..eb63b2263 100644 --- a/internal/outpost/ldap/api.go +++ b/internal/outpost/ldap/api.go @@ -66,7 +66,7 @@ func (ls *LDAPServer) Refresh() error { } func (ls *LDAPServer) StartHTTPServer() error { - listen := "0.0.0.0:4180" // same port as proxy + listen := "0.0.0.0:9000" // same port as proxy m := http.NewServeMux() m.HandleFunc("/akprox/ping", func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(204) diff --git a/internal/outpost/proxy/api.go b/internal/outpost/proxy/api.go deleted file mode 100644 index f579d41c2..000000000 --- a/internal/outpost/proxy/api.go +++ /dev/null @@ -1,49 +0,0 @@ -package proxy - -import ( - "net/url" - - log "github.com/sirupsen/logrus" - "goauthentik.io/api" -) - -func (s *Server) Refresh() error { - providers, err := s.ak.Update() - if err != nil { - return err - } - if providers == nil { - s.logger.Debug("Providers have not changed, not updating") - return nil - } - bundles := s.bundleProviders(providers) - s.updateHTTPServer(bundles) - return nil -} - -func (s *Server) bundleProviders(providers []api.ProxyOutpostConfig) []*providerBundle { - bundles := make([]*providerBundle, len(providers)) - for idx, provider := range providers { - externalHost, err := url.Parse(provider.ExternalHost) - if err != nil { - log.WithError(err).Warning("Failed to parse URL, skipping provider") - } - bundles[idx] = &providerBundle{ - s: s, - Host: externalHost.Host, - log: log.WithField("logger", "authentik.outpost.proxy-bundle").WithField("provider", provider.Name), - endSessionUrl: provider.OidcConfiguration.EndSessionEndpoint, - } - bundles[idx].Build(provider) - } - return bundles -} - -func (s *Server) updateHTTPServer(bundles []*providerBundle) { - newMap := make(map[string]*providerBundle) - for _, bundle := range bundles { - newMap[bundle.Host] = bundle - } - s.logger.Debug("Swapped maps") - s.Handlers = newMap -} diff --git a/internal/outpost/proxy/api_bundle.go b/internal/outpost/proxy/api_bundle.go deleted file mode 100644 index 61c1447c4..000000000 --- a/internal/outpost/proxy/api_bundle.go +++ /dev/null @@ -1,169 +0,0 @@ -package proxy - -import ( - "crypto/tls" - "net" - "net/http" - "net/url" - "os" - "strings" - - "github.com/jinzhu/copier" - "github.com/justinas/alice" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "github.com/oauth2-proxy/oauth2-proxy/pkg/middleware" - "github.com/oauth2-proxy/oauth2-proxy/pkg/validation" - log "github.com/sirupsen/logrus" - "goauthentik.io/api" -) - -type providerBundle struct { - http.Handler - - s *Server - proxy *OAuthProxy - Host string - - endSessionUrl string - Mode *api.ProxyMode - - cert *tls.Certificate - - log *log.Entry -} - -func intToPointer(i int) *int { - return &i -} - -func (pb *providerBundle) replaceLocal(url string) string { - if strings.HasPrefix(url, "http://localhost:8000") { - authentikHost, c := pb.s.ak.Outpost.Config["authentik_host"] - if !c || authentikHost == "" { - pb.log.Warning("Outpost has localhost/blank API Connection but no authentik_host is configured.") - return url - } - f := strings.ReplaceAll(url, "http://localhost:8000", authentikHost.(string)) - return f - } - return url -} - -func (pb *providerBundle) prepareOpts(provider api.ProxyOutpostConfig) *options.Options { - // We need to save the mode in the bundle - // Since for the embedded outpost we only switch for fully proxy providers - pb.Mode = provider.Mode - - externalHost, err := url.Parse(provider.ExternalHost) - if err != nil { - log.WithError(err).Warning("Failed to parse URL, skipping provider") - return nil - } - providerOpts := &options.Options{} - err = copier.Copy(&providerOpts, getCommonOptions()) - if err != nil { - log.WithError(err).Warning("Failed to copy options, skipping provider") - return nil - } - providerOpts.ClientID = *provider.ClientId - providerOpts.ClientSecret = *provider.ClientSecret - - providerOpts.Cookie.Secret = *provider.CookieSecret - providerOpts.Cookie.Secure = externalHost.Scheme == "https" - - providerOpts.SkipOIDCDiscovery = true - providerOpts.OIDCIssuerURL = pb.replaceLocal(provider.OidcConfiguration.Issuer) - providerOpts.LoginURL = pb.replaceLocal(provider.OidcConfiguration.AuthorizationEndpoint) - providerOpts.RedeemURL = pb.replaceLocal(provider.OidcConfiguration.TokenEndpoint) - providerOpts.OIDCJwksURL = pb.replaceLocal(provider.OidcConfiguration.JwksUri) - providerOpts.ProfileURL = pb.replaceLocal(provider.OidcConfiguration.UserinfoEndpoint) - providerOpts.ValidateURL = pb.replaceLocal(provider.OidcConfiguration.UserinfoEndpoint) - providerOpts.AcrValues = "goauthentik.io/providers/oauth2/default" - - if *provider.SkipPathRegex != "" { - skipRegexes := strings.Split(*provider.SkipPathRegex, "\n") - providerOpts.SkipAuthRegex = skipRegexes - } - - if *provider.Mode == api.PROXYMODE_FORWARD_SINGLE || *provider.Mode == api.PROXYMODE_FORWARD_DOMAIN { - providerOpts.UpstreamServers = []options.Upstream{ - { - ID: "static", - Static: true, - StaticCode: intToPointer(202), - Path: "/", - }, - } - } else { - providerOpts.UpstreamServers = []options.Upstream{ - { - ID: "default", - URI: *provider.InternalHost, - Path: "/", - InsecureSkipTLSVerify: !(*provider.InternalHostSslValidation), - }, - } - } - - if provider.Certificate.Get() != nil { - kp := provider.Certificate.Get() - err := pb.s.cs.AddKeypair(*kp) - if err != nil { - pb.log.WithError(err).Warning("Failed to initially fetch certificate") - } - pb.cert = pb.s.cs.Get(*kp) - } - return providerOpts -} - -func (pb *providerBundle) Build(provider api.ProxyOutpostConfig) { - opts := pb.prepareOpts(provider) - - if *provider.Mode == api.PROXYMODE_FORWARD_DOMAIN { - opts.Cookie.Domains = []string{*provider.CookieDomain} - } - - chain := alice.New() - - if opts.ForceHTTPS { - _, httpsPort, err := net.SplitHostPort(opts.HTTPSAddress) - if err != nil { - log.Fatalf("FATAL: invalid HTTPS address %q: %v", opts.HTTPAddress, err) - } - chain = chain.Append(middleware.NewRedirectToHTTPS(httpsPort)) - } - - healthCheckPaths := []string{opts.PingPath} - healthCheckUserAgents := []string{opts.PingUserAgent} - - // To silence logging of health checks, register the health check handler before - // the logging handler - if opts.Logging.SilencePing { - chain = chain.Append(middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents), LoggingHandler) - } else { - chain = chain.Append(LoggingHandler, middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents)) - } - - err := validation.Validate(opts) - if err != nil { - log.Printf("%s", err) - os.Exit(1) - } - oauthproxy, err := NewOAuthProxy(opts, provider, pb.s.ak.Client.GetConfig().HTTPClient) - if err != nil { - log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err) - os.Exit(1) - } - - if *provider.BasicAuthEnabled { - oauthproxy.SetBasicAuth = true - oauthproxy.BasicAuthUserAttribute = *provider.BasicAuthUserAttribute - oauthproxy.BasicAuthPasswordAttribute = *provider.BasicAuthPasswordAttribute - } - - oauthproxy.endSessionEndpoint = pb.endSessionUrl - oauthproxy.ExternalHost = pb.Host - - pb.proxy = oauthproxy - pb.Handler = chain.Then(oauthproxy) -} diff --git a/internal/outpost/proxy/claims.go b/internal/outpost/proxy/claims.go deleted file mode 100644 index e96dccace..000000000 --- a/internal/outpost/proxy/claims.go +++ /dev/null @@ -1,31 +0,0 @@ -package proxy - -import ( - "encoding/base64" - "encoding/json" - "strings" -) - -type Claims struct { - Proxy struct { - UserAttributes map[string]interface{} `json:"user_attributes"` - } `json:"ak_proxy"` - Groups []string `json:"groups"` -} - -func (c *Claims) FromIDToken(idToken string) error { - // id_token is a base64 encode ID token payload - // https://developers.google.com/accounts/docs/OAuth2Login#obtainuserinfo - jwt := strings.Split(idToken, ".") - jwtData := strings.TrimSuffix(jwt[1], "=") - b, err := base64.RawURLEncoding.DecodeString(jwtData) - if err != nil { - return err - } - - err = json.Unmarshal(b, c) - if err != nil { - return err - } - return nil -} diff --git a/internal/outpost/proxy/common.go b/internal/outpost/proxy/common.go deleted file mode 100644 index 47203335e..000000000 --- a/internal/outpost/proxy/common.go +++ /dev/null @@ -1,39 +0,0 @@ -package proxy - -import ( - "fmt" - "time" - - log "github.com/sirupsen/logrus" - - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - "goauthentik.io/internal/config" -) - -func getCommonOptions() *options.Options { - commonOpts := options.NewOptions() - commonOpts.Cookie.Name = "authentik_proxy" - commonOpts.Cookie.Expire = 24 * time.Hour - commonOpts.EmailDomains = []string{"*"} - commonOpts.ProviderType = "oidc" - commonOpts.ProxyPrefix = "/akprox" - commonOpts.Logging.SilencePing = true - commonOpts.SetAuthorization = false - commonOpts.Scope = "openid email profile ak_proxy" - if config.G.Redis.Host != "" { - protocol := "redis" - if config.G.Redis.TLS { - protocol = "rediss" - } - url := fmt.Sprintf("%s://@%s:%d/%d", protocol, config.G.Redis.Host, config.G.Redis.Port, config.G.Redis.OutpostSessionDB) - log.WithField("url", url).Info("Using redis session backend") - commonOpts.Session.Redis = options.RedisStoreOptions{ - ConnectionURL: url, - Password: config.G.Redis.Password, - } - if config.G.Redis.TLSReqs != "" { - commonOpts.Session.Redis.InsecureSkipTLSVerify = true - } - } - return commonOpts -} diff --git a/internal/outpost/proxy/cookies.go b/internal/outpost/proxy/cookies.go deleted file mode 100644 index d611c7213..000000000 --- a/internal/outpost/proxy/cookies.go +++ /dev/null @@ -1,69 +0,0 @@ -package proxy - -import ( - "net" - "net/http" - "strings" - "time" - - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies" - "goauthentik.io/internal/utils/web" -) - -// MakeCSRFCookie creates a cookie for CSRF -func (p *OAuthProxy) MakeCSRFCookie(req *http.Request, value string, expiration time.Duration, now time.Time) *http.Cookie { - return p.makeCookie(req, p.CSRFCookieName, value, expiration, now) -} - -func (p *OAuthProxy) makeCookie(req *http.Request, name string, value string, expiration time.Duration, now time.Time) *http.Cookie { - cookieDomain := cookies.GetCookieDomain(req, p.CookieDomains) - - if cookieDomain != "" { - domain := web.GetHost(req) - if h, _, err := net.SplitHostPort(domain); err == nil { - domain = h - } - if !strings.HasSuffix(domain, cookieDomain) { - p.logger.Errorf("Warning: request host is %q but using configured cookie domain of %q", domain, cookieDomain) - } - } - - return &http.Cookie{ - Name: name, - Value: value, - Path: p.CookiePath, - Domain: cookieDomain, - HttpOnly: p.CookieHTTPOnly, - Secure: p.CookieSecure, - Expires: now.Add(expiration), - SameSite: cookies.ParseSameSite(p.CookieSameSite), - } -} - -// ClearCSRFCookie creates a cookie to unset the CSRF cookie stored in the user's -// session -func (p *OAuthProxy) ClearCSRFCookie(rw http.ResponseWriter, req *http.Request) { - http.SetCookie(rw, p.MakeCSRFCookie(req, "", time.Hour*-1, time.Now())) -} - -// SetCSRFCookie adds a CSRF cookie to the response -func (p *OAuthProxy) SetCSRFCookie(rw http.ResponseWriter, req *http.Request, val string) { - http.SetCookie(rw, p.MakeCSRFCookie(req, val, p.CookieExpire, time.Now())) -} - -// ClearSessionCookie creates a cookie to unset the user's authentication cookie -// stored in the user's session -func (p *OAuthProxy) ClearSessionCookie(rw http.ResponseWriter, req *http.Request) error { - return p.sessionStore.Clear(rw, req) -} - -// LoadCookiedSession reads the user's authentication details from the request -func (p *OAuthProxy) LoadCookiedSession(req *http.Request) (*sessionsapi.SessionState, error) { - return p.sessionStore.Load(req) -} - -// SaveSession creates a new session cookie value and sets this on the response -func (p *OAuthProxy) SaveSession(rw http.ResponseWriter, req *http.Request, s *sessionsapi.SessionState) error { - return p.sessionStore.Save(rw, req, s) -} diff --git a/internal/outpost/proxy/oauth.go b/internal/outpost/proxy/oauth.go deleted file mode 100644 index deb2bad6c..000000000 --- a/internal/outpost/proxy/oauth.go +++ /dev/null @@ -1,227 +0,0 @@ -package proxy - -import ( - "context" - "errors" - "fmt" - "net/http" - "net/url" - "strings" - - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" -) - -// GetRedirectURI returns the redirectURL that the upstream OAuth Provider will -// redirect clients to once authenticated -func (p *OAuthProxy) GetRedirectURI(host string) string { - // default to the request Host if not set - if p.redirectURL.Host != "" { - return p.redirectURL.String() - } - u := *p.redirectURL - if u.Scheme == "" { - if p.CookieSecure { - u.Scheme = httpsScheme - } else { - u.Scheme = httpScheme - } - } - u.Host = host - return u.String() -} - -// HTTPClient is the context key to use with golang.org/x/net/context's -// WithValue function to associate an *http.Client value with a context. -var HTTPClient ContextKey - -// ContextKey is just an empty struct. It exists so HTTPClient can be -// an immutable public variable with a unique type. It's immutable -// because nobody else can create a ContextKey, being unexported. -type ContextKey struct{} - -func (p *OAuthProxy) redeemCode(ctx context.Context, host, code string) (s *sessionsapi.SessionState, err error) { - if code == "" { - return nil, errors.New("missing code") - } - redirectURI := p.GetRedirectURI(host) - redeemCtx := context.WithValue(ctx, HTTPClient, p.client) - s, err = p.provider.Redeem(redeemCtx, redirectURI, code) - if err != nil { - return - } - - if s.Email == "" { - s.Email, err = p.provider.GetEmailAddress(ctx, s) - } - - if s.PreferredUsername == "" { - s.PreferredUsername, err = p.provider.GetPreferredUsername(ctx, s) - if err != nil && err.Error() == "not implemented" { - err = nil - } - } - - if s.User == "" { - s.User, err = p.provider.GetUserName(ctx, s) - if err != nil && err.Error() == "not implemented" { - err = nil - } - } - return -} - -// GetRedirect reads the query parameter to get the URL to redirect clients to -// once authenticated with the OAuthProxy -func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error) { - err = req.ParseForm() - if err != nil { - return - } - - redirect = req.Header.Get("X-Auth-Request-Redirect") - if req.Form.Get("rd") != "" { - redirect = req.Form.Get("rd") - } - if !p.IsValidRedirect(redirect) { - // Use RequestURI to preserve ?query - redirect = req.URL.RequestURI() - if strings.HasPrefix(redirect, p.ProxyPrefix) { - redirect = "/" - } - } - - return -} - -// IsValidRedirect checks whether the redirect URL is whitelisted -func (p *OAuthProxy) IsValidRedirect(redirect string) bool { - switch { - case redirect == "": - // The user didn't specify a redirect, should fallback to `/` - return false - case strings.HasPrefix(redirect, "/") && !strings.HasPrefix(redirect, "//") && !invalidRedirectRegex.MatchString(redirect): - return true - case strings.HasPrefix(redirect, "http://") || strings.HasPrefix(redirect, "https://"): - redirectURL, err := url.Parse(redirect) - if err != nil { - p.logger.WithField("redirect", redirect).Printf("Rejecting invalid redirect %q: scheme unsupported or missing", redirect) - return false - } - redirectHostname := redirectURL.Hostname() - - for _, domain := range p.CookieDomains { - if strings.HasSuffix(redirectHostname, domain) { - p.logger.WithField("redirect", redirect).WithField("domain", domain).Debug("allowing redirect") - return true - } - } - - p.logger.WithField("redirect", redirect).Printf("Rejecting invalid redirect %q: domain / port not in whitelist", redirect) - return false - default: - p.logger.WithField("redirect", redirect).Printf("Rejecting invalid redirect %q: not an absolute or relative URL", redirect) - return false - } -} - -// IsWhitelistedRequest is used to check if auth should be skipped for this request -func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) bool { - isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS" - return isPreflightRequestAllowed || p.IsWhitelistedPath(req.URL.Path) -} - -// IsWhitelistedPath is used to check if the request path is allowed without auth -func (p *OAuthProxy) IsWhitelistedPath(path string) bool { - for _, u := range p.compiledRegex { - if u.MatchString(path) { - return true - } - } - return false -} - -// OAuthStart starts the OAuth2 authentication flow -func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) { - prepareNoCache(rw) - nonce, err := encryption.Nonce() - if err != nil { - p.logger.Errorf("Error obtaining nonce: %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error()) - return - } - p.SetCSRFCookie(rw, req, nonce) - redirect, err := p.GetRedirect(req) - if err != nil { - p.logger.Errorf("Error obtaining redirect: %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error()) - return - } - redirectURI := p.GetRedirectURI(req.Host) - http.Redirect(rw, req, p.provider.GetLoginURL(redirectURI, fmt.Sprintf("%v:%v", nonce, redirect)), http.StatusFound) -} - -// OAuthCallback is the OAuth2 authentication flow callback that finishes the -// OAuth2 authentication flow -func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { - // finish the oauth cycle - err := req.ParseForm() - if err != nil { - p.logger.Errorf("Error while parsing OAuth2 callback: %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error()) - return - } - errorString := req.Form.Get("error") - if errorString != "" { - p.logger.Errorf("Error while parsing OAuth2 callback: %s", errorString) - p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", errorString) - return - } - - session, err := p.redeemCode(req.Context(), req.Host, req.Form.Get("code")) - if err != nil { - p.logger.Errorf("Error redeeming code during OAuth2 callback: %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Internal Error") - return - } - - s := strings.SplitN(req.Form.Get("state"), ":", 2) - if len(s) != 2 { - p.logger.Error("Error while parsing OAuth2 state: invalid length") - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Invalid State") - return - } - nonce := s[0] - redirect := s[1] - c, err := req.Cookie(p.CSRFCookieName) - if err != nil { - p.logger.WithField("user", session.Email).WithField("status", "AuthFailure").Errorf("Invalid authentication via OAuth2: unable to obtain CSRF cookie") - p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", err.Error()) - return - } - p.ClearCSRFCookie(rw, req) - if c.Value != nonce { - p.logger.WithField("is", c.Value).WithField("should", nonce).WithField("user", session.Email).WithField("status", "AuthFailure").Errorf("Invalid authentication via OAuth2: CSRF token mismatch, potential attack") - p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", "CSRF Failed") - return - } - - if !p.IsValidRedirect(redirect) { - redirect = "/" - } - - // set cookie, or deny - if p.provider.ValidateGroup(session.Email) { - p.logger.WithField("user", session.Email).WithField("status", "AuthFailure").Infof("Authenticated via OAuth2: %s", session) - err := p.SaveSession(rw, req, session) - if err != nil { - p.logger.Errorf("Error saving session state for client %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error()) - return - } - http.Redirect(rw, req, redirect, http.StatusFound) - } else { - p.logger.WithField("user", session.Email).WithField("status", "AuthFailure").Errorf("Invalid authentication via OAuth2: unauthorized") - p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", "Invalid Account") - } -} diff --git a/internal/outpost/proxy/proxy.go b/internal/outpost/proxy/proxy.go deleted file mode 100644 index 81b8b8ecd..000000000 --- a/internal/outpost/proxy/proxy.go +++ /dev/null @@ -1,507 +0,0 @@ -package proxy - -import ( - b64 "encoding/base64" - "encoding/json" - "errors" - "fmt" - "html/template" - "net/http" - "net/url" - "regexp" - "strings" - "time" - - "github.com/coreos/go-oidc" - "github.com/justinas/alice" - "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" - sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/middleware" - "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions" - "github.com/oauth2-proxy/oauth2-proxy/pkg/upstream" - "github.com/oauth2-proxy/oauth2-proxy/providers" - "goauthentik.io/api" - "goauthentik.io/internal/utils/web" - staticWeb "goauthentik.io/web" - - log "github.com/sirupsen/logrus" -) - -const ( - httpScheme = "http" - httpsScheme = "https" - - applicationJSON = "application/json" -) - -var ( - // ErrNeedsLogin means the user should be redirected to the login page - ErrNeedsLogin = errors.New("redirect to login page") - - // Used to check final redirects are not susceptible to open redirects. - // Matches //, /\ and both of these with whitespace in between (eg / / or / \). - invalidRedirectRegex = regexp.MustCompile(`[/\\](?:[\s\v]*|\.{1,2})[/\\]`) -) - -// OAuthProxy is the main authentication proxy -type OAuthProxy struct { - client *http.Client - - CookieSeed string - CookieName string - CSRFCookieName string - CookieDomains []string - CookiePath string - CookieSecure bool - CookieHTTPOnly bool - CookieExpire time.Duration - CookieRefresh time.Duration - CookieSameSite string - - RobotsPath string - SignInPath string - SignOutPath string - OAuthStartPath string - OAuthCallbackPath string - AuthOnlyPath string - UserInfoPath string - - endSessionEndpoint string - mode api.ProxyMode - BasicAuthUserAttribute string - BasicAuthPasswordAttribute string - ExternalHost string - - redirectURL *url.URL // the url to receive requests at - whitelistDomains []string - provider providers.Provider - sessionStore sessionsapi.SessionStore - ProxyPrefix string - serveMux http.Handler - SetXAuthRequest bool - SetBasicAuth bool - PassUserHeaders bool - PassAccessToken bool - SetAuthorization bool - PassAuthorization bool - PreferEmailToUser bool - skipAuthRegex []string - skipAuthPreflight bool - skipAuthStripHeaders bool - mainJwtBearerVerifier *oidc.IDTokenVerifier - extraJwtBearerVerifiers []*oidc.IDTokenVerifier - compiledRegex []*regexp.Regexp - templates *template.Template - - sessionChain alice.Chain - - logger *log.Entry -} - -// NewOAuthProxy creates a new instance of OAuthProxy from the options provided -func NewOAuthProxy(opts *options.Options, provider api.ProxyOutpostConfig, c *http.Client) (*OAuthProxy, error) { - logger := log.WithField("logger", "authentik.outpost.proxy").WithField("provider", provider.Name) - sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie) - if err != nil { - return nil, fmt.Errorf("error initialising session store: %v", err) - } - - templates := getTemplates() - proxyErrorHandler := upstream.NewProxyErrorHandler(templates.Lookup("error.html"), opts.ProxyPrefix) - upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), proxyErrorHandler) - if err != nil { - return nil, fmt.Errorf("error initialising upstream proxy: %v", err) - } - - for _, u := range opts.GetCompiledRegex() { - logger.Printf("compiled skip-auth-regex => %q", u) - } - - redirectURL := opts.GetRedirectURL() - if redirectURL.Path == "" { - redirectURL.Path = fmt.Sprintf("%s/callback", opts.ProxyPrefix) - } - - logger.WithField("auth_url", opts.GetProvider().Data().LoginURL.String()).WithField("client_id", opts.ClientID).Info("proxy instance configured") - - sessionChain := buildSessionChain(opts, sessionStore) - - return &OAuthProxy{ - client: c, - CookieName: opts.Cookie.Name, - CSRFCookieName: fmt.Sprintf("%v_%v", opts.Cookie.Name, "csrf"), - CookieSeed: opts.Cookie.Secret, - CookieDomains: opts.Cookie.Domains, - CookiePath: opts.Cookie.Path, - CookieSecure: opts.Cookie.Secure, - CookieHTTPOnly: opts.Cookie.HTTPOnly, - CookieExpire: opts.Cookie.Expire, - CookieRefresh: opts.Cookie.Refresh, - CookieSameSite: opts.Cookie.SameSite, - - mode: *provider.Mode, - RobotsPath: "/robots.txt", - SignInPath: fmt.Sprintf("%s/sign_in", opts.ProxyPrefix), - SignOutPath: fmt.Sprintf("%s/sign_out", opts.ProxyPrefix), - OAuthStartPath: fmt.Sprintf("%s/start", opts.ProxyPrefix), - OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix), - AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix), - UserInfoPath: fmt.Sprintf("%s/userinfo", opts.ProxyPrefix), - - ProxyPrefix: opts.ProxyPrefix, - provider: opts.GetProvider(), - sessionStore: sessionStore, - serveMux: upstreamProxy, - redirectURL: redirectURL, - whitelistDomains: opts.WhitelistDomains, - skipAuthRegex: opts.SkipAuthRegex, - skipAuthPreflight: opts.SkipAuthPreflight, - skipAuthStripHeaders: opts.SkipAuthStripHeaders, - mainJwtBearerVerifier: opts.GetOIDCVerifier(), - extraJwtBearerVerifiers: opts.GetJWTBearerVerifiers(), - compiledRegex: opts.GetCompiledRegex(), - SetXAuthRequest: opts.SetXAuthRequest, - SetBasicAuth: opts.SetBasicAuth, - PassUserHeaders: opts.PassUserHeaders, - PassAccessToken: opts.PassAccessToken, - SetAuthorization: opts.SetAuthorization, - PassAuthorization: opts.PassAuthorization, - PreferEmailToUser: opts.PreferEmailToUser, - templates: templates, - - sessionChain: sessionChain, - - logger: logger, - }, nil -} - -func buildSessionChain(opts *options.Options, sessionStore sessionsapi.SessionStore) alice.Chain { - chain := alice.New(middleware.NewScope()) - - chain = chain.Append(middleware.NewStoredSessionLoader(&middleware.StoredSessionLoaderOptions{ - SessionStore: sessionStore, - RefreshPeriod: opts.Cookie.Refresh, - RefreshSessionIfNeeded: opts.GetProvider().RefreshSessionIfNeeded, - ValidateSessionState: opts.GetProvider().ValidateSessionState, - })) - - return chain -} - -// RobotsTxt disallows scraping pages from the OAuthProxy -func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter) { - _, err := fmt.Fprintf(rw, "User-agent: *\nDisallow: /") - if err != nil { - p.logger.Errorf("Error writing robots.txt: %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error()) - return - } - rw.WriteHeader(http.StatusOK) -} - -// ErrorPage writes an error response -func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, message string) { - rw.WriteHeader(code) - t := struct { - Title string - Message string - ProxyPrefix string - }{ - Title: fmt.Sprintf("%d %s", code, title), - Message: message, - ProxyPrefix: p.ProxyPrefix, - } - err := p.templates.ExecuteTemplate(rw, "error.html", t) - if err != nil { - p.logger.Printf("Error rendering error.html template: %v", err) - http.Error(rw, "Internal Server Error", http.StatusInternalServerError) - } -} - -// See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=en -var noCacheHeaders = map[string]string{ - "Expires": time.Unix(0, 0).Format(time.RFC1123), - "Cache-Control": "no-cache, no-store, must-revalidate, max-age=0", - "X-Accel-Expires": "0", // https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/ -} - -// prepareNoCache prepares headers for preventing browser caching. -func prepareNoCache(w http.ResponseWriter) { - // Set NoCache headers - for k, v := range noCacheHeaders { - w.Header().Set(k, v) - } -} - -func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - if req.URL.Path != p.AuthOnlyPath && strings.HasPrefix(req.URL.Path, p.ProxyPrefix) { - prepareNoCache(rw) - } - rw.Header().Set("Server", "authentik-outpost") - - switch path := req.URL.Path; { - case path == p.RobotsPath: - p.RobotsTxt(rw) - case p.IsWhitelistedRequest(req): - p.SkipAuthProxy(rw, req) - case path == p.SignInPath: - p.OAuthStart(rw, req) - case path == p.SignOutPath: - p.SignOut(rw, req) - case path == p.OAuthStartPath: - p.OAuthStart(rw, req) - case path == p.OAuthCallbackPath: - p.OAuthCallback(rw, req) - case path == p.AuthOnlyPath: - p.AuthenticateOnly(rw, req) - case path == p.UserInfoPath: - p.UserInfo(rw, req) - case strings.HasPrefix(path, fmt.Sprintf("%s/static", p.ProxyPrefix)): - p.ServeStatic(rw, req) - default: - p.Proxy(rw, req) - } -} - -func (p *OAuthProxy) ServeStatic(rw http.ResponseWriter, req *http.Request) { - staticFs := http.FileServer(http.FS(staticWeb.StaticDist)) - http.StripPrefix(fmt.Sprintf("%s/static", p.ProxyPrefix), staticFs).ServeHTTP(rw, req) -} - -//UserInfo endpoint outputs session email and preferred username in JSON format -func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) { - - session, err := p.getAuthenticatedSession(rw, req) - if err != nil { - http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - userInfo := struct { - Email string `json:"email"` - PreferredUsername string `json:"preferredUsername,omitempty"` - }{ - Email: session.Email, - PreferredUsername: session.PreferredUsername, - } - rw.Header().Set("Content-Type", "application/json") - rw.WriteHeader(http.StatusOK) - err = json.NewEncoder(rw).Encode(userInfo) - if err != nil { - p.logger.Errorf("Error encoding user info: %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error()) - } -} - -// SignOut sends a response to clear the authentication cookie -func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { - err := p.ClearSessionCookie(rw, req) - if err != nil { - p.logger.Errorf("Error clearing session cookie: %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error()) - return - } - http.Redirect(rw, req, p.endSessionEndpoint, http.StatusFound) -} - -// AuthenticateOnly checks whether the user is currently logged in -func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) { - session, err := p.getAuthenticatedSession(rw, req) - if err != nil { - if p.mode == api.PROXYMODE_FORWARD_SINGLE || p.mode == api.PROXYMODE_FORWARD_DOMAIN { - if _, ok := req.URL.Query()["nginx"]; ok { - rw.WriteHeader(401) - return - } - if _, ok := req.URL.Query()["traefik"]; ok { - host := "" - // Optional suffix, which is appended to the URL - suffix := "" - if p.mode == api.PROXYMODE_FORWARD_SINGLE { - host = web.GetHost(req) - } else if p.mode == api.PROXYMODE_FORWARD_DOMAIN { - host = p.ExternalHost - // set the ?rd flag to the current URL we have, since we redirect - // to a (possibly) different domain, but we want to be redirected back - // to the application - v := url.Values{ - // see https://doc.traefik.io/traefik/middlewares/forwardauth/ - // X-Forwarded-Uri is only the path, so we need to build the entire URL - "rd": []string{fmt.Sprintf( - "%s://%s%s", - req.Header.Get("X-Forwarded-Proto"), - req.Header.Get("X-Forwarded-Host"), - req.Header.Get("X-Forwarded-Uri"), - )}, - } - suffix = fmt.Sprintf("?%s", v.Encode()) - } - proto := req.Header.Get("X-Forwarded-Proto") - if proto != "" { - proto = proto + ":" - } - rdFinal := fmt.Sprintf("%s//%s%s%s", proto, host, p.OAuthStartPath, suffix) - p.logger.WithField("url", rdFinal).Debug("Redirecting to login") - http.Redirect(rw, req, rdFinal, http.StatusTemporaryRedirect) - return - } - } - http.Error(rw, "unauthorized request", http.StatusUnauthorized) - return - } - // we are authenticated - p.addHeadersForProxying(rw, req, session) - if p.mode == api.PROXYMODE_FORWARD_SINGLE || p.mode == api.PROXYMODE_FORWARD_DOMAIN { - for headerKey, headers := range req.Header { - for _, value := range headers { - rw.Header().Set(headerKey, value) - } - } - } - rw.WriteHeader(http.StatusAccepted) -} - -// SkipAuthProxy proxies whitelisted requests and skips authentication -func (p *OAuthProxy) SkipAuthProxy(rw http.ResponseWriter, req *http.Request) { - if p.skipAuthStripHeaders { - p.stripAuthHeaders(req) - } - p.serveMux.ServeHTTP(rw, req) -} - -// Proxy proxies the user request if the user is authenticated else it prompts -// them to authenticate -func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) { - session, err := p.getAuthenticatedSession(rw, req) - switch err { - case nil: - // we are authenticated - p.addHeadersForProxying(rw, req, session) - p.serveMux.ServeHTTP(rw, req) - - case ErrNeedsLogin: - // we need to send the user to a login screen - if isAjax(req) { - // no point redirecting an AJAX request - p.ErrorJSON(rw, http.StatusUnauthorized) - return - } - - p.OAuthStart(rw, req) - - default: - // unknown error - p.logger.Errorf("Unexpected internal error: %v", err) - p.ErrorPage(rw, http.StatusInternalServerError, - "Internal Error", "Internal Error") - } - -} - -// getAuthenticatedSession checks whether a user is authenticated and returns a session object and nil error if so -// Returns nil, ErrNeedsLogin if user needs to login. -// Set-Cookie headers may be set on the response as a side-effect of calling this method. -func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.Request) (*sessionsapi.SessionState, error) { - var session *sessionsapi.SessionState - - getSession := p.sessionChain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - session = middleware.GetRequestScope(req).Session - })) - getSession.ServeHTTP(rw, req) - - if session == nil { - return nil, ErrNeedsLogin - } - - return session, nil -} - -// addHeadersForProxying adds the appropriate headers the request / response for proxying -func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Request, session *sessionsapi.SessionState) { - // req is the request that is forwarded to the upstream server - // rw is the response writer that goes back to the client - req.Header["X-Forwarded-User"] = []string{session.User} - if session.Email != "" { - req.Header["X-Forwarded-Email"] = []string{session.Email} - } - - if session.PreferredUsername != "" { - req.Header["X-Forwarded-Preferred-Username"] = []string{session.PreferredUsername} - req.Header["X-Auth-Username"] = []string{session.PreferredUsername} - } else { - req.Header.Del("X-Forwarded-Preferred-Username") - req.Header.Del("X-Auth-Username") - } - - claims := Claims{} - err := claims.FromIDToken(session.IDToken) - if err != nil { - log.WithError(err).Warning("Failed to parse IDToken") - } - // Set groups in header - groups := strings.Join(claims.Groups, "|") - req.Header["X-Auth-Groups"] = []string{groups} - - userAttributes := claims.Proxy.UserAttributes - // Attempt to set basic auth based on user's attributes - if p.SetBasicAuth { - var ok bool - var password string - if password, ok = userAttributes[p.BasicAuthPasswordAttribute].(string); !ok { - password = "" - } - // Check if we should use email or a custom attribute as username - var username string - if username, ok = userAttributes[p.BasicAuthUserAttribute].(string); !ok { - username = session.Email - } - authVal := b64.StdEncoding.EncodeToString([]byte(username + ":" + password)) - p.logger.WithField("username", username).Trace("setting http basic auth") - req.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)} - } - // Check if user has additional headers set that we should sent - if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]interface{}); ok { - p.logger.WithField("headers", additionalHeaders).Trace("setting additional headers") - if additionalHeaders == nil { - return - } - for key, value := range additionalHeaders { - req.Header.Set(key, toString(value)) - } - } -} - -// stripAuthHeaders removes Auth headers for whitelisted routes from skipAuthRegex -func (p *OAuthProxy) stripAuthHeaders(req *http.Request) { - if p.PassUserHeaders { - req.Header.Del("X-Forwarded-User") - req.Header.Del("X-Auth-Groups") - req.Header.Del("X-Forwarded-Email") - req.Header.Del("X-Forwarded-Preferred-Username") - } - - if p.PassAccessToken { - req.Header.Del("X-Forwarded-Access-Token") - } - - if p.PassAuthorization { - req.Header.Del("Authorization") - } -} - -// isAjax checks if a request is an ajax request -func isAjax(req *http.Request) bool { - acceptValues := req.Header.Values("Accept") - const ajaxReq = applicationJSON - for _, v := range acceptValues { - if v == ajaxReq { - return true - } - } - return false -} - -// ErrorJSON returns the error code with an application/json mime type -func (p *OAuthProxy) ErrorJSON(rw http.ResponseWriter, code int) { - rw.Header().Set("Content-Type", applicationJSON) - rw.WriteHeader(code) -} diff --git a/internal/outpost/proxy/server.go b/internal/outpost/proxy/server.go deleted file mode 100644 index fb34f0ce0..000000000 --- a/internal/outpost/proxy/server.go +++ /dev/null @@ -1,137 +0,0 @@ -package proxy - -import ( - "context" - "crypto/tls" - "errors" - "fmt" - "net" - "net/http" - "strings" - "time" - - "github.com/pires/go-proxyproto" - log "github.com/sirupsen/logrus" - "goauthentik.io/internal/crypto" - "goauthentik.io/internal/outpost/ak" - "goauthentik.io/internal/utils/web" -) - -// Server represents an HTTP server -type Server struct { - Handlers map[string]*providerBundle - Listen string - - stop chan struct{} // channel for waiting shutdown - logger *log.Entry - ak *ak.APIController - cs *ak.CryptoStore - defaultCert tls.Certificate -} - -// NewServer initialise a new HTTP Server -func NewServer(ac *ak.APIController) *Server { - defaultCert, err := crypto.GenerateSelfSignedCert() - if err != nil { - log.Warning(err) - } - return &Server{ - Handlers: make(map[string]*providerBundle), - Listen: "0.0.0.0:%d", - logger: log.WithField("logger", "authentik.outpost.proxy-http-server"), - defaultCert: defaultCert, - ak: ac, - cs: ak.NewCryptoStore(ac.Client.CryptoApi), - } -} - -// ServeHTTP constructs a net.Listener and starts handling HTTP requests -func (s *Server) ServeHTTP() { - listenAddress := fmt.Sprintf(s.Listen, 4180) - listener, err := net.Listen("tcp", listenAddress) - if err != nil { - s.logger.Fatalf("FATAL: listen (%s) failed - %s", listenAddress, err) - } - proxyListener := &proxyproto.Listener{Listener: listener} - defer proxyListener.Close() - - s.logger.Printf("listening on %s", listener.Addr()) - s.serve(proxyListener) - s.logger.Printf("closing %s", listener.Addr()) -} - -func (s *Server) TimerFlowCacheExpiry() {} - -func (s *Server) Handler(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/akprox/ping" { - w.WriteHeader(204) - return - } - host := web.GetHost(r) - handler, ok := s.Handlers[host] - if !ok { - // If we only have one handler, host name switching doesn't matter - if len(s.Handlers) == 1 { - for k := range s.Handlers { - s.Handlers[k].ServeHTTP(w, r) - return - } - } - // Get a list of all host keys we know - hostKeys := make([]string, 0, len(s.Handlers)) - for k := range s.Handlers { - hostKeys = append(hostKeys, k) - } - s.logger.WithField("host", host).WithField("known-hosts", strings.Join(hostKeys, ",")).Debug("Host header does not match any we know of") - w.WriteHeader(404) - return - } - handler.ServeHTTP(w, r) -} - -func (s *Server) serve(listener net.Listener) { - srv := &http.Server{Handler: http.HandlerFunc(s.Handler)} - - // See https://golang.org/pkg/net/http/#Server.Shutdown - idleConnsClosed := make(chan struct{}) - go func() { - <-s.stop // wait notification for stopping server - - // We received an interrupt signal, shut down. - if err := srv.Shutdown(context.Background()); err != nil { - // Error from closing listeners, or context timeout: - s.logger.Printf("HTTP server Shutdown: %v", err) - } - close(idleConnsClosed) - }() - - err := srv.Serve(listener) - if err != nil && !errors.Is(err, http.ErrServerClosed) { - s.logger.Errorf("ERROR: http.Serve() - %s", err) - } - <-idleConnsClosed -} - -// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted -// connections. It's used by ListenAndServe and ListenAndServeTLS so -// dead TCP connections (e.g. closing laptop mid-download) eventually -// go away. -type tcpKeepAliveListener struct { - *net.TCPListener -} - -func (ln tcpKeepAliveListener) Accept() (net.Conn, error) { - tc, err := ln.AcceptTCP() - if err != nil { - return nil, err - } - err = tc.SetKeepAlive(true) - if err != nil { - log.Printf("Error setting Keep-Alive: %v", err) - } - err = tc.SetKeepAlivePeriod(3 * time.Minute) - if err != nil { - log.Printf("Error setting Keep-Alive period: %v", err) - } - return tc, nil -} diff --git a/internal/outpost/proxy/server_https.go b/internal/outpost/proxy/server_https.go deleted file mode 100644 index d939bc54e..000000000 --- a/internal/outpost/proxy/server_https.go +++ /dev/null @@ -1,62 +0,0 @@ -package proxy - -import ( - "crypto/tls" - "fmt" - "net" - "sync" - - "github.com/pires/go-proxyproto" -) - -func (s *Server) getCertificates(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - handler, ok := s.Handlers[info.ServerName] - if !ok { - s.logger.WithField("server-name", info.ServerName).Debug("Handler does not exist") - return &s.defaultCert, nil - } - if handler.cert == nil { - s.logger.WithField("server-name", info.ServerName).Debug("Handler does not have a certificate") - return &s.defaultCert, nil - } - return handler.cert, nil -} - -// ServeHTTPS constructs a net.Listener and starts handling HTTPS requests -func (s *Server) ServeHTTPS() { - listenAddress := fmt.Sprintf(s.Listen, 4443) - config := &tls.Config{ - MinVersion: tls.VersionTLS12, - MaxVersion: tls.VersionTLS12, - GetCertificate: s.getCertificates, - } - - ln, err := net.Listen("tcp", listenAddress) - if err != nil { - s.logger.Fatalf("FATAL: listen (%s) failed - %s", listenAddress, err) - } - s.logger.Printf("listening on %s", ln.Addr()) - - proxyListener := &proxyproto.Listener{Listener: tcpKeepAliveListener{ln.(*net.TCPListener)}} - defer proxyListener.Close() - - tlsListener := tls.NewListener(proxyListener, config) - s.serve(tlsListener) - s.logger.Printf("closing %s", tlsListener.Addr()) -} - -func (s *Server) Start() error { - wg := sync.WaitGroup{} - wg.Add(2) - go func() { - defer wg.Done() - s.logger.Debug("Starting HTTP Server...") - s.ServeHTTP() - }() - go func() { - defer wg.Done() - s.logger.Debug("Starting HTTPs Server...") - s.ServeHTTPS() - }() - return nil -} diff --git a/internal/outpost/proxy/templates.go b/internal/outpost/proxy/templates.go deleted file mode 100644 index 607bfb577..000000000 --- a/internal/outpost/proxy/templates.go +++ /dev/null @@ -1,16 +0,0 @@ -package proxy - -import ( - "html/template" - - log "github.com/sirupsen/logrus" - "goauthentik.io/internal/outpost/proxy/templates" -) - -func getTemplates() *template.Template { - t, err := template.New("foo").Parse(templates.ErrorTemplate) - if err != nil { - log.Fatalf("failed parsing template %s", err) - } - return t -} diff --git a/internal/outpost/proxy/templates/templates.go b/internal/outpost/proxy/templates/templates.go deleted file mode 100644 index bb37f3ff9..000000000 --- a/internal/outpost/proxy/templates/templates.go +++ /dev/null @@ -1,6 +0,0 @@ -package templates - -import _ "embed" - -//go:embed error.html -var ErrorTemplate string diff --git a/internal/outpost/proxy/utils.go b/internal/outpost/proxy/utils.go deleted file mode 100644 index 4ddbd3980..000000000 --- a/internal/outpost/proxy/utils.go +++ /dev/null @@ -1,18 +0,0 @@ -package proxy - -import ( - "strconv" -) - -// toString Generic to string function, currently supports actual strings and integers -func toString(in interface{}) string { - switch v := in.(type) { - case string: - return v - case *string: - return *v - case int: - return strconv.Itoa(v) - } - return "" -} diff --git a/internal/outpost/proxyv2/application/application.go b/internal/outpost/proxyv2/application/application.go new file mode 100644 index 000000000..69f48d08d --- /dev/null +++ b/internal/outpost/proxyv2/application/application.go @@ -0,0 +1,208 @@ +package application + +import ( + "context" + "crypto/tls" + "encoding/gob" + "fmt" + "net/http" + "net/url" + "os" + "regexp" + "strings" + "time" + + "github.com/coreos/go-oidc" + "github.com/gorilla/mux" + "github.com/gorilla/sessions" + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" + "goauthentik.io/api" + "goauthentik.io/internal/outpost/ak" + "goauthentik.io/internal/outpost/proxyv2/constants" + "goauthentik.io/internal/outpost/proxyv2/hs256" + "goauthentik.io/internal/outpost/proxyv2/metrics" + "goauthentik.io/internal/utils/web" + "golang.org/x/oauth2" +) + +type Application struct { + Host string + Cert *tls.Certificate + UnauthenticatedRegex []*regexp.Regexp + + oauthConfig oauth2.Config + tokenVerifier *oidc.IDTokenVerifier + + sessions sessions.Store + proxyConfig api.ProxyOutpostConfig + httpClient *http.Client + + log *log.Entry + mux *mux.Router +} + +func akProviderToEndpoint(p api.ProxyOutpostConfig) oauth2.Endpoint { + authUrl := p.OidcConfiguration.AuthorizationEndpoint + if browserHost, found := os.LookupEnv("AUTHENTIK_HOST_BROWSER"); found { + host := os.Getenv("AUTHENTIK_HOST") + authUrl = strings.ReplaceAll(authUrl, host, browserHost) + } + return oauth2.Endpoint{ + AuthURL: authUrl, + TokenURL: p.OidcConfiguration.TokenEndpoint, + AuthStyle: oauth2.AuthStyleInParams, + } +} + +func NewApplication(p api.ProxyOutpostConfig, c *http.Client, cs *ak.CryptoStore) *Application { + gob.Register(Claims{}) + + externalHost, err := url.Parse(p.ExternalHost) + if err != nil { + log.WithError(err).Warning("Failed to parse URL, skipping provider") + } + + // Support for RS256, new proxy providers will use HS256 but old ones + // might not, and this makes testing easier + var ks oidc.KeySet + if contains(p.OidcConfiguration.IdTokenSigningAlgValuesSupported, "HS256") { + ks = hs256.NewKeySet(*p.ClientSecret) + } else { + ctx := context.WithValue(context.Background(), oauth2.HTTPClient, c) + oidc.NewRemoteKeySet(ctx, p.OidcConfiguration.JwksUri) + } + + var verifier = oidc.NewVerifier(p.OidcConfiguration.Issuer, ks, &oidc.Config{ + ClientID: *p.ClientId, + SupportedSigningAlgs: []string{"HS256"}, + }) + + // Configure an OpenID Connect aware OAuth2 client. + oauth2Config := oauth2.Config{ + ClientID: *p.ClientId, + ClientSecret: *p.ClientSecret, + RedirectURL: fmt.Sprintf("%s/akprox/callback", p.ExternalHost), + Endpoint: akProviderToEndpoint(p), + Scopes: []string{oidc.ScopeOpenID, "profile", "email", "ak_proxy"}, + } + mux := mux.NewRouter() + a := &Application{ + Host: externalHost.Host, + log: log.WithField("logger", "authentik.outpost.proxy.bundle").WithField("provider", p.Name), + oauthConfig: oauth2Config, + tokenVerifier: verifier, + sessions: GetStore(p), + proxyConfig: p, + httpClient: c, + mux: mux, + } + muxLogger := log.WithField("logger", "authentik.outpost.proxyv2.application").WithField("name", p.Name) + mux.Use(web.NewLoggingHandler(muxLogger, func(l *log.Entry, r *http.Request) *log.Entry { + s, err := a.sessions.Get(r, constants.SeesionName) + if err != nil { + return l + } + claims, ok := s.Values[constants.SessionClaims] + if claims == nil || !ok { + return l + } + c, ok := claims.(Claims) + if !ok { + return l + } + return l.WithField("request_username", c.Email) + })) + mux.Use(func(inner http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + c, _ := a.getClaims(r) + user := "" + if c != nil { + user = c.Email + } + before := time.Now() + inner.ServeHTTP(rw, r) + after := time.Since(before) + metrics.Requests.With(prometheus.Labels{ + "type": "app", + "scheme": r.URL.Scheme, + "method": r.Method, + "path": r.URL.Path, + "host": web.GetHost(r), + "user": user, + }).Observe(float64(after)) + }) + }) + + // Support /start and /sign_in for backwards compatibility + mux.HandleFunc("/akprox/start", a.handleRedirect) + mux.HandleFunc("/akprox/sign_in", a.handleRedirect) + mux.HandleFunc("/akprox/callback", a.handleCallback) + mux.HandleFunc("/akprox/sign_out", a.handleSignOut) + switch *p.Mode { + case api.PROXYMODE_PROXY: + err = a.configureProxy() + case api.PROXYMODE_FORWARD_SINGLE: + fallthrough + case api.PROXYMODE_FORWARD_DOMAIN: + err = a.configureForward() + } + if err != nil { + a.log.WithError(err).Warning("failed to configure mode") + } + + if kp := p.Certificate.Get(); kp != nil { + err := cs.AddKeypair(*kp) + if err != nil { + a.log.WithError(err).Warning("Failed to initially fetch certificate") + } + a.Cert = cs.Get(*kp) + } + + if *p.SkipPathRegex != "" { + a.UnauthenticatedRegex = make([]*regexp.Regexp, 0) + for _, regex := range strings.Split(*p.SkipPathRegex, "\n") { + re, err := regexp.Compile(regex) + if err != nil { + // TODO: maybe create event for this? + a.log.WithError(err).Warning("failed to compile regex") + } else { + a.UnauthenticatedRegex = append(a.UnauthenticatedRegex, re) + } + } + } + return a +} + +func (a *Application) IsAllowlisted(r *http.Request) bool { + for _, u := range a.UnauthenticatedRegex { + if u.MatchString(r.URL.Path) { + return true + } + } + return false +} + +func (a *Application) Mode() api.ProxyMode { + return *a.proxyConfig.Mode +} + +func (a *Application) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + a.mux.ServeHTTP(rw, r) +} + +func (a *Application) handleSignOut(rw http.ResponseWriter, r *http.Request) { + // TODO: Token revocation + s, err := a.sessions.Get(r, constants.SeesionName) + if err != nil { + http.Redirect(rw, r, a.proxyConfig.OidcConfiguration.EndSessionEndpoint, http.StatusFound) + return + } + s.Options.MaxAge = -1 + err = s.Save(r, rw) + if err != nil { + http.Redirect(rw, r, a.proxyConfig.OidcConfiguration.EndSessionEndpoint, http.StatusFound) + return + } + http.Redirect(rw, r, a.proxyConfig.OidcConfiguration.EndSessionEndpoint, http.StatusFound) +} diff --git a/internal/outpost/proxyv2/application/claims.go b/internal/outpost/proxyv2/application/claims.go new file mode 100644 index 000000000..40cd148a2 --- /dev/null +++ b/internal/outpost/proxyv2/application/claims.go @@ -0,0 +1,16 @@ +package application + +type ProxyClaims struct { + UserAttributes map[string]interface{} `json:"user_attributes"` +} + +type Claims struct { + Sub string `json:"sub"` + Exp int `json:"exp"` + Email string `json:"email"` + Verified bool `json:"email_verified"` + Proxy ProxyClaims `json:"ak_proxy"` + Name string `json:"name"` + PreferredUsername string `json:"preferred_username"` + Groups []string `json:"groups"` +} diff --git a/internal/outpost/proxyv2/application/error.go b/internal/outpost/proxyv2/application/error.go new file mode 100644 index 000000000..ba7d78d9a --- /dev/null +++ b/internal/outpost/proxyv2/application/error.go @@ -0,0 +1,29 @@ +package application + +import ( + "html/template" + "net/http" + + log "github.com/sirupsen/logrus" +) + +// NewProxyErrorHandler creates a ProxyErrorHandler using the template given. +func NewProxyErrorHandler(errorTemplate *template.Template) func(http.ResponseWriter, *http.Request, error) { + return func(rw http.ResponseWriter, req *http.Request, proxyErr error) { + log.Errorf("Error proxying to upstream server: %v", proxyErr) + rw.WriteHeader(http.StatusBadGateway) + data := struct { + Title string + Message string + ProxyPrefix string + }{ + Title: "Bad Gateway", + Message: "Error proxying to upstream server", + ProxyPrefix: "/akprox", + } + err := errorTemplate.Execute(rw, data) + if err != nil { + http.Error(rw, "Internal Server Error", http.StatusInternalServerError) + } + } +} diff --git a/internal/outpost/proxyv2/application/mode_common.go b/internal/outpost/proxyv2/application/mode_common.go new file mode 100644 index 000000000..64d244201 --- /dev/null +++ b/internal/outpost/proxyv2/application/mode_common.go @@ -0,0 +1,53 @@ +package application + +import ( + "encoding/base64" + "fmt" + "net/http" + "strings" +) + +func (a *Application) addHeaders(r *http.Request, c *Claims) { + // https://goauthentik.io/docs/providers/proxy/proxy + r.Header.Set("X-Auth-Username", c.PreferredUsername) + r.Header.Set("X-Auth-Groups", strings.Join(c.Groups, "|")) + r.Header.Set("X-Forwarded-Email", c.Email) + r.Header.Set("X-Forwarded-Preferred-Username", c.PreferredUsername) + r.Header.Set("X-Forwarded-User", c.Sub) + + userAttributes := c.Proxy.UserAttributes + // Attempt to set basic auth based on user's attributes + if *a.proxyConfig.BasicAuthEnabled { + var ok bool + var password string + if password, ok = userAttributes[*a.proxyConfig.BasicAuthPasswordAttribute].(string); !ok { + password = "" + } + // Check if we should use email or a custom attribute as username + var username string + if username, ok = userAttributes[*a.proxyConfig.BasicAuthUserAttribute].(string); !ok { + username = c.Email + } + authVal := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) + a.log.WithField("username", username).Trace("setting http basic auth") + r.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)} + } + // Check if user has additional headers set that we should sent + if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]interface{}); ok { + a.log.WithField("headers", additionalHeaders).Trace("setting additional headers") + if additionalHeaders == nil { + return + } + for key, value := range additionalHeaders { + r.Header.Set(key, toString(value)) + } + } +} + +func copyHeadersToResponse(rw http.ResponseWriter, r *http.Request) { + for headerKey, headers := range r.Header { + for _, value := range headers { + rw.Header().Set(headerKey, value) + } + } +} diff --git a/internal/outpost/proxyv2/application/mode_forward.go b/internal/outpost/proxyv2/application/mode_forward.go new file mode 100644 index 000000000..e4e514b98 --- /dev/null +++ b/internal/outpost/proxyv2/application/mode_forward.go @@ -0,0 +1,77 @@ +package application + +import ( + "fmt" + "net/http" + "net/url" + + "goauthentik.io/api" + "goauthentik.io/internal/utils/web" +) + +func (a *Application) configureForward() error { + a.mux.HandleFunc("/akprox/auth", func(rw http.ResponseWriter, r *http.Request) { + if _, ok := r.URL.Query()["traefik"]; ok { + a.forwardHandleTraefik(rw, r) + return + } + a.forwardHandleNginx(rw, r) + }) + a.mux.HandleFunc("/akprox/auth/traefik", a.forwardHandleTraefik) + a.mux.HandleFunc("/akprox/auth/nginx", a.forwardHandleNginx) + return nil +} + +func (a *Application) forwardHandleTraefik(rw http.ResponseWriter, r *http.Request) { + claims, err := a.getClaims(r) + if claims != nil && err == nil { + a.addHeaders(r, claims) + copyHeadersToResponse(rw, r) + return + } else if claims == nil && a.IsAllowlisted(r) { + a.log.Trace("path can be accessed without authentication") + return + } + host := "" + // Optional suffix, which is appended to the URL + suffix := "" + if *a.proxyConfig.Mode == api.PROXYMODE_FORWARD_SINGLE { + host = web.GetHost(r) + } else if *a.proxyConfig.Mode == api.PROXYMODE_FORWARD_DOMAIN { + host = a.proxyConfig.ExternalHost + // set the ?rd flag to the current URL we have, since we redirect + // to a (possibly) different domain, but we want to be redirected back + // to the application + v := url.Values{ + // see https://doc.traefik.io/traefik/middlewares/forwardauth/ + // X-Forwarded-Uri is only the path, so we need to build the entire URL + "rd": []string{fmt.Sprintf( + "%s://%s%s", + r.Header.Get("X-Forwarded-Proto"), + r.Header.Get("X-Forwarded-Host"), + r.Header.Get("X-Forwarded-Uri"), + )}, + } + suffix = fmt.Sprintf("?%s", v.Encode()) + } + proto := r.Header.Get("X-Forwarded-Proto") + if proto != "" { + proto = proto + ":" + } + rdFinal := fmt.Sprintf("%s//%s%s%s", proto, host, "/akprox/start", suffix) + a.log.WithField("url", rdFinal).Debug("Redirecting to login") + http.Redirect(rw, r, rdFinal, http.StatusTemporaryRedirect) +} + +func (a *Application) forwardHandleNginx(rw http.ResponseWriter, r *http.Request) { + claims, err := a.getClaims(r) + if claims != nil && err == nil { + a.addHeaders(r, claims) + copyHeadersToResponse(rw, r) + return + } else if claims == nil && a.IsAllowlisted(r) { + a.log.Trace("path can be accessed without authentication") + return + } + http.Error(rw, "unauthorized request", http.StatusUnauthorized) +} diff --git a/internal/outpost/proxyv2/application/mode_proxy.go b/internal/outpost/proxyv2/application/mode_proxy.go new file mode 100644 index 000000000..fa5f13fbc --- /dev/null +++ b/internal/outpost/proxyv2/application/mode_proxy.go @@ -0,0 +1,63 @@ +package application + +import ( + "net/http" + "net/http/httputil" + "net/url" + "time" + + "github.com/prometheus/client_golang/prometheus" + "goauthentik.io/internal/outpost/proxyv2/metrics" + "goauthentik.io/internal/outpost/proxyv2/templates" + "goauthentik.io/internal/utils/web" +) + +func (a *Application) configureProxy() error { + // Reverse proxy to the application server + u, err := url.Parse(*a.proxyConfig.InternalHost) + if err != nil { + return err + } + rp := &httputil.ReverseProxy{Director: a.proxyModifyRequest(u)} + rp.ErrorHandler = NewProxyErrorHandler(templates.GetTemplates()) + rp.ModifyResponse = a.proxyModifyResponse + a.mux.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + claims, err := a.getClaims(r) + if claims == nil && a.IsAllowlisted(r) { + a.log.Trace("path can be accessed without authentication") + } else if claims == nil && err != nil { + a.redirectToStart(rw, r) + return + } else { + a.addHeaders(r, claims) + } + before := time.Now() + rp.ServeHTTP(rw, r) + after := time.Since(before) + + user := "" + if claims != nil { + user = claims.Email + } + metrics.UpstreamTiming.With(prometheus.Labels{ + "upstream_host": u.String(), + "scheme": r.URL.Scheme, + "method": r.Method, + "path": r.URL.Path, + "host": web.GetHost(r), + "user": user, + }).Observe(float64(after)) + }) + return nil +} + +func (a *Application) proxyModifyRequest(u *url.URL) func(req *http.Request) { + return func(req *http.Request) { + req.URL.Scheme = u.Scheme + req.URL.Host = u.Host + } +} + +func (a *Application) proxyModifyResponse(res *http.Response) error { + return nil +} diff --git a/internal/outpost/proxyv2/application/oauth.go b/internal/outpost/proxyv2/application/oauth.go new file mode 100644 index 000000000..c83ad778a --- /dev/null +++ b/internal/outpost/proxyv2/application/oauth.go @@ -0,0 +1,54 @@ +package application + +import ( + "encoding/base64" + "net/http" + + "github.com/gorilla/securecookie" + "goauthentik.io/internal/outpost/proxyv2/constants" +) + +func (a *Application) handleRedirect(rw http.ResponseWriter, r *http.Request) { + state := base64.RawStdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)) + s, _ := a.sessions.Get(r, constants.SeesionName) + s.Values[constants.SessionOAuthState] = state + err := s.Save(r, rw) + if err != nil { + a.log.WithError(err).Warning("failed to save session") + } + http.Redirect(rw, r, a.oauthConfig.AuthCodeURL(state), http.StatusFound) +} + +func (a *Application) handleCallback(rw http.ResponseWriter, r *http.Request) { + s, _ := a.sessions.Get(r, constants.SeesionName) + state, ok := s.Values[constants.SessionOAuthState] + if !ok { + a.log.Warning("No state saved in session") + http.Redirect(rw, r, a.proxyConfig.ExternalHost, http.StatusFound) + return + } + claims, err := a.redeemCallback(r, state.(string)) + if err != nil { + a.log.WithError(err).Warning("failed to redeem code") + rw.WriteHeader(400) + // To prevent the user from just refreshing and cause more errors, delete + // the state from the session + delete(s.Values, constants.SessionOAuthState) + err := s.Save(r, rw) + if err != nil { + a.log.WithError(err).Warning("failed to save session") + rw.WriteHeader(400) + return + } + return + } + s.Options.MaxAge = claims.Exp / 1000 + s.Values[constants.SessionClaims] = &claims + err = s.Save(r, rw) + if err != nil { + a.log.WithError(err).Warning("failed to save session") + rw.WriteHeader(400) + return + } + http.Redirect(rw, r, a.proxyConfig.ExternalHost, http.StatusFound) +} diff --git a/internal/outpost/proxyv2/application/oauth_callback.go b/internal/outpost/proxyv2/application/oauth_callback.go new file mode 100644 index 000000000..7f2937184 --- /dev/null +++ b/internal/outpost/proxyv2/application/oauth_callback.go @@ -0,0 +1,49 @@ +package application + +import ( + "context" + "fmt" + "net/http" + + "golang.org/x/oauth2" +) + +func (a *Application) redeemCallback(r *http.Request, shouldState string) (*Claims, error) { + state := r.URL.Query().Get("state") + if state == "" || state != shouldState { + return nil, fmt.Errorf("blank/invalid state") + } + + code := r.URL.Query().Get("code") + if code == "" { + return nil, fmt.Errorf("blank code") + } + + ctx := context.WithValue(r.Context(), oauth2.HTTPClient, a.httpClient) + // Verify state and errors. + oauth2Token, err := a.oauthConfig.Exchange(ctx, code) + if err != nil { + return nil, err + } + + // Extract the ID Token from OAuth2 token. + rawIDToken, ok := oauth2Token.Extra("id_token").(string) + if !ok { + return nil, fmt.Errorf("missing id_token") + } + + a.log.WithField("id_token", rawIDToken).Trace("id_token") + + // Parse and verify ID Token payload. + idToken, err := a.tokenVerifier.Verify(ctx, rawIDToken) + if err != nil { + return nil, err + } + + // Extract custom claims + var claims *Claims + if err := idToken.Claims(&claims); err != nil { + return nil, err + } + return claims, nil +} diff --git a/internal/outpost/proxyv2/application/session.go b/internal/outpost/proxyv2/application/session.go new file mode 100644 index 000000000..2f0695f79 --- /dev/null +++ b/internal/outpost/proxyv2/application/session.go @@ -0,0 +1,28 @@ +package application + +import ( + "fmt" + "strconv" + + "github.com/gorilla/sessions" + "goauthentik.io/api" + "goauthentik.io/internal/config" + "gopkg.in/boj/redistore.v1" +) + +func GetStore(p api.ProxyOutpostConfig) sessions.Store { + var store sessions.Store + if config.G.Redis.Host != "" { + rs, err := redistore.NewRediStoreWithDB(10, "tcp", fmt.Sprintf("%s:%d", config.G.Redis.Host, config.G.Redis.Port), config.G.Redis.Password, strconv.Itoa(config.G.Redis.OutpostSessionDB)) + if err != nil { + panic(err) + } + rs.Options.Domain = *p.CookieDomain + store = rs + } else { + cs := sessions.NewCookieStore([]byte(*p.CookieSecret)) + cs.Options.Domain = *p.CookieDomain + store = cs + } + return store +} diff --git a/internal/outpost/proxyv2/application/utils.go b/internal/outpost/proxyv2/application/utils.go new file mode 100644 index 000000000..f5b493296 --- /dev/null +++ b/internal/outpost/proxyv2/application/utils.go @@ -0,0 +1,56 @@ +package application + +import ( + "fmt" + "net/http" + "strconv" + + "goauthentik.io/internal/outpost/proxyv2/constants" +) + +func (a *Application) redirectToStart(rw http.ResponseWriter, r *http.Request) { + authUrl := fmt.Sprintf("%s/akprox/start", a.proxyConfig.ExternalHost) + http.Redirect(rw, r, authUrl, http.StatusFound) +} + +// getClaims Get claims which are currently in session +// Returns an error if the session can't be loaded or the claims can't be parsed/type-cast +func (a *Application) getClaims(r *http.Request) (*Claims, error) { + s, err := a.sessions.Get(r, constants.SeesionName) + if err != nil { + // err == user has no session/session is not valid, reject + return nil, fmt.Errorf("invalid session") + } + claims, ok := s.Values[constants.SessionClaims] + if claims == nil || !ok { + // no claims saved, reject + return nil, fmt.Errorf("invalid session") + } + c, ok := claims.(Claims) + if !ok { + return nil, fmt.Errorf("invalid session") + } + return &c, nil +} + +func contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +// toString Generic to string function, currently supports actual strings and integers +func toString(in interface{}) string { + switch v := in.(type) { + case string: + return v + case *string: + return *v + case int: + return strconv.Itoa(v) + } + return "" +} diff --git a/internal/outpost/proxyv2/constants/constants.go b/internal/outpost/proxyv2/constants/constants.go new file mode 100644 index 000000000..c33c3896d --- /dev/null +++ b/internal/outpost/proxyv2/constants/constants.go @@ -0,0 +1,6 @@ +package constants + +const SeesionName = "authentik_proxy" + +const SessionOAuthState = "oauth_state" +const SessionClaims = "claims" diff --git a/internal/outpost/proxyv2/handlers.go b/internal/outpost/proxyv2/handlers.go new file mode 100644 index 000000000..4556f15c4 --- /dev/null +++ b/internal/outpost/proxyv2/handlers.go @@ -0,0 +1,48 @@ +package proxyv2 + +import ( + "net/http" + "time" + + "github.com/prometheus/client_golang/prometheus" + "goauthentik.io/internal/outpost/proxyv2/metrics" + "goauthentik.io/internal/utils/web" + staticWeb "goauthentik.io/web" +) + +func (ps *ProxyServer) HandlePing(rw http.ResponseWriter, r *http.Request) { + before := time.Now() + rw.WriteHeader(204) + after := time.Since(before) + metrics.Requests.With(prometheus.Labels{ + "type": "ping", + "method": r.Method, + "path": r.URL.Path, + "host": web.GetHost(r), + }).Observe(float64(after)) +} + +func (ps *ProxyServer) HandleStatic(rw http.ResponseWriter, r *http.Request) { + staticFs := http.FileServer(http.FS(staticWeb.StaticDist)) + before := time.Now() + http.StripPrefix("/akprox/static", staticFs).ServeHTTP(rw, r) + after := time.Since(before) + metrics.Requests.With(prometheus.Labels{ + "type": "static", + "method": r.Method, + "path": r.URL.Path, + "host": web.GetHost(r), + }).Observe(float64(after)) +} + +func (ps *ProxyServer) Handle(rw http.ResponseWriter, r *http.Request) { + host := web.GetHost(r) + a, ok := ps.apps[host] + if !ok { + ps.log.WithField("host", host).Warning("no app for hostname") + rw.WriteHeader(400) + return + } + ps.log.WithField("host", host).Trace("passing to application mux") + a.ServeHTTP(rw, r) +} diff --git a/internal/outpost/proxyv2/hs256/hs256.go b/internal/outpost/proxyv2/hs256/hs256.go new file mode 100644 index 000000000..3df8c73e8 --- /dev/null +++ b/internal/outpost/proxyv2/hs256/hs256.go @@ -0,0 +1,31 @@ +package hs256 + +import ( + "context" + "encoding/base64" + "strings" + + "github.com/golang-jwt/jwt" +) + +type KeySet struct { + m jwt.SigningMethod + secret string +} + +func NewKeySet(secret string) *KeySet { + return &KeySet{ + m: jwt.GetSigningMethod("HS256"), + secret: secret, + } +} + +func (ks *KeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) { + parts := strings.Split(jwt, ".") + err := ks.m.Verify(strings.Join(parts[0:2], "."), parts[2], []byte(ks.secret)) + if err != nil { + return nil, err + } + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + return payload, err +} diff --git a/internal/outpost/proxyv2/metrics/metrics.go b/internal/outpost/proxyv2/metrics/metrics.go new file mode 100644 index 000000000..6a8961414 --- /dev/null +++ b/internal/outpost/proxyv2/metrics/metrics.go @@ -0,0 +1,30 @@ +package metrics + +import ( + "net/http" + + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var ( + Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "authentik_outpost_proxy_requests", + Help: "The total number of configured providers", + }, []string{"scheme", "type", "method", "path", "host", "user"}) + UpstreamTiming = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "authentik_outpost_proxy_upstream_time", + Help: "A summary of the duration we wait for the upstream reply", + }, []string{"method", "scheme", "path", "host", "upstream_host", "user"}) +) + +func RunServer() { + m := mux.NewRouter() + m.Path("/metrics").Handler(promhttp.Handler()) + err := http.ListenAndServe("localhost:9300", m) + if err != nil { + panic(err) + } +} diff --git a/internal/outpost/proxyv2/proxyv2.go b/internal/outpost/proxyv2/proxyv2.go new file mode 100644 index 000000000..7be705adc --- /dev/null +++ b/internal/outpost/proxyv2/proxyv2.go @@ -0,0 +1,202 @@ +package proxyv2 + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "net" + "net/http" + "sync" + "time" + + "github.com/gorilla/mux" + "github.com/pires/go-proxyproto" + log "github.com/sirupsen/logrus" + "goauthentik.io/api" + "goauthentik.io/internal/crypto" + "goauthentik.io/internal/outpost/ak" + "goauthentik.io/internal/outpost/proxyv2/application" + "goauthentik.io/internal/outpost/proxyv2/metrics" + "goauthentik.io/internal/utils/web" +) + +type ProxyServer struct { + Listen string + PortOffset int + + defaultCert tls.Certificate + stop chan struct{} // channel for waiting shutdown + + cryptoStore *ak.CryptoStore + apps map[string]*application.Application + log *log.Entry + mux *mux.Router + akAPI *ak.APIController +} + +func NewProxyServer(ac *ak.APIController) *ProxyServer { + l := log.WithField("logger", "authentik.outpost.proxyv2") + defaultCert, err := crypto.GenerateSelfSignedCert() + if err != nil { + l.Fatal(err) + } + + rootMux := mux.NewRouter() + rootMux.Use(func(h http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + h.ServeHTTP(rw, r) + rw.Header().Set("Server", "authentik_proxy2") + }) + }) + + globalMux := rootMux.NewRoute().Subrouter() + globalMux.Use(web.NewLoggingHandler(l.WithField("logger", "authentik.outpost.proxyv2.http"), nil)) + s := &ProxyServer{ + Listen: "0.0.0.0:%d", + PortOffset: 0, + + cryptoStore: ak.NewCryptoStore(ac.Client.CryptoApi), + apps: make(map[string]*application.Application), + log: l, + mux: rootMux, + akAPI: ac, + defaultCert: defaultCert, + } + globalMux.Path("/akprox/ping").HandlerFunc(s.HandlePing) + globalMux.PathPrefix("/akprox/static").HandlerFunc(s.HandleStatic) + rootMux.PathPrefix("/").HandlerFunc(s.Handle) + return s +} + +func (ps *ProxyServer) HandleHost(host string, rw http.ResponseWriter, r *http.Request) bool { + if app, ok := ps.apps[host]; ok { + if app.Mode() == api.PROXYMODE_PROXY { + ps.log.WithField("host", host).Trace("routing to proxy outpost") + app.ServeHTTP(rw, r) + return true + } + } + return false +} + +func (ps *ProxyServer) TimerFlowCacheExpiry() {} + +func (ps *ProxyServer) getCertificates(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + app, ok := ps.apps[info.ServerName] + if !ok { + ps.log.WithField("server-name", info.ServerName).Debug("app does not exist") + return &ps.defaultCert, nil + } + if app.Cert == nil { + ps.log.WithField("server-name", info.ServerName).Debug("app does not have a certificate") + return &ps.defaultCert, nil + } + return app.Cert, nil +} + +// ServeHTTP constructs a net.Listener and starts handling HTTP requests +func (ps *ProxyServer) ServeHTTP() { + listenAddress := fmt.Sprintf(ps.Listen, 9000+ps.PortOffset) + listener, err := net.Listen("tcp", listenAddress) + if err != nil { + ps.log.Fatalf("FATAL: listen (%s) failed - %s", listenAddress, err) + } + proxyListener := &proxyproto.Listener{Listener: listener} + defer proxyListener.Close() + + ps.log.Printf("listening on %s", listener.Addr()) + ps.serve(proxyListener) + ps.log.Printf("closing %s", listener.Addr()) +} + +// ServeHTTPS constructs a net.Listener and starts handling HTTPS requests +func (ps *ProxyServer) ServeHTTPS() { + listenAddress := fmt.Sprintf(ps.Listen, 9443+ps.PortOffset) + config := &tls.Config{ + MinVersion: tls.VersionTLS12, + MaxVersion: tls.VersionTLS12, + GetCertificate: ps.getCertificates, + } + + ln, err := net.Listen("tcp", listenAddress) + if err != nil { + ps.log.Fatalf("listen (%s) failed - %s", listenAddress, err) + } + ps.log.Printf("listening on %s", ln.Addr()) + + proxyListener := &proxyproto.Listener{Listener: tcpKeepAliveListener{ln.(*net.TCPListener)}} + defer proxyListener.Close() + + tlsListener := tls.NewListener(proxyListener, config) + ps.serve(tlsListener) + ps.log.Printf("closing %s", tlsListener.Addr()) +} + +func (ps *ProxyServer) Start() error { + wg := sync.WaitGroup{} + wg.Add(3) + go func() { + defer wg.Done() + ps.log.Debug("Starting HTTP Server...") + ps.ServeHTTP() + }() + go func() { + defer wg.Done() + ps.log.Debug("Starting HTTPs Server...") + ps.ServeHTTPS() + }() + go func() { + defer wg.Done() + ps.log.Debug("Starting Metrics Server...") + metrics.RunServer() + }() + return nil +} + +func (ps *ProxyServer) serve(listener net.Listener) { + srv := &http.Server{Handler: ps.mux} + + // See https://golang.org/pkg/net/http/#Server.Shutdown + idleConnsClosed := make(chan struct{}) + go func() { + <-ps.stop // wait notification for stopping server + + // We received an interrupt signal, shut down. + if err := srv.Shutdown(context.Background()); err != nil { + // Error from closing listeners, or context timeout: + ps.log.Printf("HTTP server Shutdown: %v", err) + } + close(idleConnsClosed) + }() + + err := srv.Serve(listener) + if err != nil && !errors.Is(err, http.ErrServerClosed) { + ps.log.Errorf("ERROR: http.Serve() - %s", err) + } + <-idleConnsClosed +} + +// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted +// connections. It's used by ListenAndServe and ListenAndServeTLS so +// dead TCP connections (e.g. closing laptop mid-download) eventually +// go away. +type tcpKeepAliveListener struct { + *net.TCPListener +} + +func (ln tcpKeepAliveListener) Accept() (net.Conn, error) { + tc, err := ln.AcceptTCP() + if err != nil { + return nil, err + } + err = tc.SetKeepAlive(true) + if err != nil { + log.Printf("Error setting Keep-Alive: %v", err) + } + err = tc.SetKeepAlivePeriod(3 * time.Minute) + if err != nil { + log.Printf("Error setting Keep-Alive period: %v", err) + } + return tc, nil +} diff --git a/internal/outpost/proxyv2/refresh.go b/internal/outpost/proxyv2/refresh.go new file mode 100644 index 000000000..c81c7dd4e --- /dev/null +++ b/internal/outpost/proxyv2/refresh.go @@ -0,0 +1,33 @@ +package proxyv2 + +import ( + "context" + "fmt" + "net/http" + + "goauthentik.io/internal/constants" + "goauthentik.io/internal/outpost/ak" + "goauthentik.io/internal/outpost/proxyv2/application" +) + +func (ps *ProxyServer) Refresh() error { + providers, _, err := ps.akAPI.Client.OutpostsApi.OutpostsProxyList(context.Background()).Execute() + if err != nil { + ps.log.WithError(err).Error("Failed to fetch providers") + } + if err != nil { + return err + } + apps := make(map[string]*application.Application) + for _, provider := range providers.Results { + ua := fmt.Sprintf(" (provider=%s)", provider.Name) + hc := &http.Client{ + Transport: ak.NewUserAgentTransport(constants.OutpostUserAgent()+ua, ak.NewTracingTransport(context.TODO(), ak.GetTLSTransport())), + } + a := application.NewApplication(provider, hc, ps.cryptoStore) + apps[a.Host] = a + } + ps.apps = apps + ps.log.Debug("Swapped maps") + return nil +} diff --git a/internal/outpost/proxy/templates/error.html b/internal/outpost/proxyv2/templates/error.html similarity index 98% rename from internal/outpost/proxy/templates/error.html rename to internal/outpost/proxyv2/templates/error.html index ed4e8744d..7bc4b18e6 100644 --- a/internal/outpost/proxy/templates/error.html +++ b/internal/outpost/proxyv2/templates/error.html @@ -1,4 +1,4 @@ -{{define "error.html"}} +
@@ -62,4 +62,3 @@ -{{end}} diff --git a/internal/outpost/proxyv2/templates/templates.go b/internal/outpost/proxyv2/templates/templates.go new file mode 100644 index 000000000..39cb23cc7 --- /dev/null +++ b/internal/outpost/proxyv2/templates/templates.go @@ -0,0 +1,18 @@ +package templates + +import ( + _ "embed" + "html/template" + "log" +) + +//go:embed error.html +var ErrorTemplate string + +func GetTemplates() *template.Template { + t, err := template.New("foo").Parse(ErrorTemplate) + if err != nil { + log.Fatalf("failed parsing template %s", err) + } + return t +} diff --git a/internal/outpost/proxy/middleware.go b/internal/utils/web/middleware.go similarity index 69% rename from internal/outpost/proxy/middleware.go rename to internal/utils/web/middleware.go index 514e6d207..0c5f94f82 100644 --- a/internal/outpost/proxy/middleware.go +++ b/internal/utils/web/middleware.go @@ -1,4 +1,4 @@ -package proxy +package web import ( "bufio" @@ -9,7 +9,6 @@ import ( "time" log "github.com/sirupsen/logrus" - "goauthentik.io/internal/utils/web" ) // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status @@ -19,7 +18,6 @@ type responseLogger struct { status int size int upstream string - authInfo string } // Header returns the ResponseWriter's Header @@ -35,28 +33,12 @@ func (l *responseLogger) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err erro return nil, nil, errors.New("http.Hijacker is not available on writer") } -// ExtractGAPMetadata extracts and removes GAP headers from the ResponseWriter's -// Header -func (l *responseLogger) ExtractGAPMetadata() { - upstream := l.w.Header().Get("GAP-Upstream-Address") - if upstream != "" { - l.upstream = upstream - l.w.Header().Del("GAP-Upstream-Address") - } - authInfo := l.w.Header().Get("GAP-Auth") - if authInfo != "" { - l.authInfo = authInfo - l.w.Header().Del("GAP-Auth") - } -} - // Write writes the response using the ResponseWriter func (l *responseLogger) Write(b []byte) (int, error) { if l.status == 0 { // The status will be StatusOK if WriteHeader has not been called yet l.status = http.StatusOK } - l.ExtractGAPMetadata() size, err := l.w.Write(b) l.size += size return size, err @@ -64,7 +46,6 @@ func (l *responseLogger) Write(b []byte) (int, error) { // WriteHeader writes the status code for the Response func (l *responseLogger) WriteHeader(s int) { - l.ExtractGAPMetadata() l.w.WriteHeader(s) l.status = s } @@ -88,15 +69,26 @@ func (l *responseLogger) Flush() { // loggingHandler is the http.Handler implementation for LoggingHandler type loggingHandler struct { - handler http.Handler - logger *log.Entry + handler http.Handler + logger *log.Entry + afterHandler afterHandler } -// LoggingHandler provides an http.Handler which logs requests to the HTTP server -func LoggingHandler(h http.Handler) http.Handler { - return loggingHandler{ - handler: h, - logger: log.WithField("logger", "authentik.outpost.proxy-http-server"), +type afterHandler func(l *log.Entry, r *http.Request) *log.Entry + +// NewLoggingHandler provides an http.Handler which logs requests to the HTTP server +func NewLoggingHandler(logger *log.Entry, after afterHandler) func(h http.Handler) http.Handler { + if after == nil { + after = func(l *log.Entry, r *http.Request) *log.Entry { + return l + } + } + return func(h http.Handler) http.Handler { + return loggingHandler{ + handler: h, + logger: logger, + afterHandler: after, + } } } @@ -106,9 +98,9 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { responseLogger := &responseLogger{w: w} h.handler.ServeHTTP(responseLogger, req) duration := float64(time.Since(t)) / float64(time.Millisecond) - h.logger.WithFields(log.Fields{ + h.afterHandler(h.logger.WithFields(log.Fields{ "host": req.RemoteAddr, - "vhost": web.GetHost(req), + "vhost": GetHost(req), "request_protocol": req.Proto, "runtime": fmt.Sprintf("%0.3f", duration), "method": req.Method, @@ -116,6 +108,5 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { "status": responseLogger.Status(), "upstream": responseLogger.upstream, "request_useragent": req.UserAgent(), - "request_username": responseLogger.authInfo, - }).Info(url.RequestURI()) + }), req).Info(url.RequestURI()) } diff --git a/internal/web/web.go b/internal/web/web.go index 4a62b7d0d..2c199711b 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -11,7 +11,7 @@ import ( "github.com/pires/go-proxyproto" log "github.com/sirupsen/logrus" "goauthentik.io/internal/config" - "goauthentik.io/internal/outpost/proxy" + "goauthentik.io/internal/outpost/proxyv2" ) type WebServer struct { @@ -22,7 +22,7 @@ type WebServer struct { stop chan struct{} // channel for waiting shutdown - ProxyServer *proxy.Server + ProxyServer *proxyv2.ProxyServer m *mux.Router lh *mux.Router diff --git a/internal/web/web_proxy.go b/internal/web/web_proxy.go index 101798569..d32d4e4b3 100644 --- a/internal/web/web_proxy.go +++ b/internal/web/web_proxy.go @@ -6,7 +6,6 @@ import ( "net/http/httputil" "net/url" - "goauthentik.io/api" "goauthentik.io/internal/utils/web" ) @@ -29,7 +28,7 @@ func (ws *WebServer) configureProxy() { rp.ModifyResponse = ws.proxyModifyResponse ws.m.PathPrefix("/akprox").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { if ws.ProxyServer != nil { - ws.ProxyServer.Handler(rw, r) + ws.ProxyServer.Handle(rw, r) return } ws.proxyErrorHandler(rw, r, fmt.Errorf("proxy not running")) @@ -37,12 +36,8 @@ func (ws *WebServer) configureProxy() { ws.m.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { host := web.GetHost(r) if ws.ProxyServer != nil { - if p, ok := ws.ProxyServer.Handlers[host]; ok { - if *p.Mode == api.PROXYMODE_PROXY { - ws.log.WithField("host", host).Trace("routing to proxy outpost") - ws.ProxyServer.Handler(rw, r) - return - } + if ws.ProxyServer.HandleHost(host, rw, r) { + return } } ws.log.WithField("host", host).Trace("routing to application server") diff --git a/ldap.Dockerfile b/ldap.Dockerfile index 193bb604c..3cb70964f 100644 --- a/ldap.Dockerfile +++ b/ldap.Dockerfile @@ -31,6 +31,6 @@ ENV GIT_BUILD_HASH=$GIT_BUILD_HASH COPY --from=builder /go/ldap / -HEALTHCHECK CMD [ "wget", "--spider", "http://localhost:4180/akprox/ping" ] +HEALTHCHECK CMD [ "wget", "--spider", "http://localhost:9000/akprox/ping" ] ENTRYPOINT ["/ldap"] diff --git a/proxy.Dockerfile b/proxy.Dockerfile index b23aa26b3..2ff255698 100644 --- a/proxy.Dockerfile +++ b/proxy.Dockerfile @@ -43,6 +43,6 @@ ENV GIT_BUILD_HASH=$GIT_BUILD_HASH COPY --from=builder /go/proxy / -HEALTHCHECK CMD [ "wget", "--spider", "http://localhost:4180/akprox/ping" ] +HEALTHCHECK CMD [ "wget", "--spider", "http://localhost:9000/akprox/ping" ] ENTRYPOINT ["/proxy"] diff --git a/schema.yml b/schema.yml index eb0186b91..c4beb3afb 100644 --- a/schema.yml +++ b/schema.yml @@ -25930,6 +25930,10 @@ components: Exclusive with internal_host. cookie_domain: type: string + token_validity: + type: string + description: 'Tokens not valid on or after current time + this value (Format: + hours=1;minutes=2;seconds=3).' PatchedReputationPolicyRequest: type: object description: Reputation Policy Serializer @@ -27135,6 +27139,10 @@ components: readOnly: true cookie_domain: type: string + token_validity: + type: string + description: 'Tokens not valid on or after current time + this value (Format: + hours=1;minutes=2;seconds=3).' required: - assigned_application_name - assigned_application_slug @@ -27200,6 +27208,10 @@ components: Exclusive with internal_host. cookie_domain: type: string + token_validity: + type: string + description: 'Tokens not valid on or after current time + this value (Format: + hours=1;minutes=2;seconds=3).' required: - authorization_flow - external_host diff --git a/tests/e2e/test_provider_ldap.py b/tests/e2e/test_provider_ldap.py index 808a74d7a..70dbca587 100644 --- a/tests/e2e/test_provider_ldap.py +++ b/tests/e2e/test_provider_ldap.py @@ -15,7 +15,14 @@ from authentik.flows.models import Flow from authentik.outposts.managed import MANAGED_OUTPOST from authentik.outposts.models import Outpost, OutpostType from authentik.providers.ldap.models import LDAPProvider -from tests.e2e.utils import USER, SeleniumTestCase, apply_migration, object_manager, retry +from tests.e2e.utils import ( + USER, + SeleniumTestCase, + apply_migration, + get_docker_tag, + object_manager, + retry, +) @skipUnless(platform.startswith("linux"), "requires local docker") @@ -33,7 +40,7 @@ class TestProviderLDAP(SeleniumTestCase): """Start ldap container based on outpost created""" client: DockerClient = from_env() container = client.containers.run( - image="beryju.org/authentik/outpost-ldap:gh-master", + image=f"beryju.org/authentik/outpost-ldap:{get_docker_tag()}", detach=True, network_mode="host", auto_remove=True, diff --git a/tests/e2e/test_provider_proxy.py b/tests/e2e/test_provider_proxy.py index 0a7559840..5a9b25fde 100644 --- a/tests/e2e/test_provider_proxy.py +++ b/tests/e2e/test_provider_proxy.py @@ -16,7 +16,14 @@ from authentik.flows.models import Flow from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostConfig, OutpostType from authentik.outposts.tasks import outpost_local_connection from authentik.providers.proxy.models import ProxyProvider -from tests.e2e.utils import USER, SeleniumTestCase, apply_migration, object_manager, retry +from tests.e2e.utils import ( + USER, + SeleniumTestCase, + apply_migration, + get_docker_tag, + object_manager, + retry, +) @skipUnless(platform.startswith("linux"), "requires local docker") @@ -42,7 +49,7 @@ class TestProviderProxy(SeleniumTestCase): """Start proxy container based on outpost created""" client: DockerClient = from_env() container = client.containers.run( - image="beryju.org/authentik/outpost-proxy:gh-master", + image=f"beryju.org/authentik/outpost-proxy:{get_docker_tag()}", detach=True, network_mode="host", auto_remove=True, @@ -73,7 +80,7 @@ class TestProviderProxy(SeleniumTestCase): slug="default-provider-authorization-implicit-consent" ), internal_host="http://localhost", - external_host="http://localhost:4180", + external_host="http://localhost:9000", ) # Ensure OAuth2 Params are set proxy.set_oauth_defaults() @@ -100,7 +107,7 @@ class TestProviderProxy(SeleniumTestCase): healthcheck_retries += 1 sleep(0.5) - self.driver.get("http://localhost:4180") + self.driver.get("http://localhost:9000") self.login() sleep(1) @@ -108,7 +115,7 @@ class TestProviderProxy(SeleniumTestCase): self.assertIn("X-Forwarded-Preferred-Username: akadmin", full_body_text) self.assertIn("X-Foo: bar", full_body_text) - self.driver.get("http://localhost:4180/akprox/sign_out") + self.driver.get("http://localhost:9000/akprox/sign_out") sleep(2) full_body_text = self.driver.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text self.assertIn("You've logged out of proxy.", full_body_text) @@ -134,7 +141,7 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase): slug="default-provider-authorization-implicit-consent" ), internal_host="http://localhost", - external_host="http://localhost:4180", + external_host="http://localhost:9000", ) # Ensure OAuth2 Params are set proxy.set_oauth_defaults() diff --git a/tests/e2e/utils.py b/tests/e2e/utils.py index 51dec3e92..fa99ac5c8 100644 --- a/tests/e2e/utils.py +++ b/tests/e2e/utils.py @@ -1,5 +1,6 @@ """authentik e2e testing utilities""" import json +import os from functools import lru_cache, wraps from os import environ, makedirs from time import sleep, time @@ -36,6 +37,17 @@ def USER() -> User: # noqa return User.objects.get(username="akadmin") +def get_docker_tag() -> str: + """Get docker-tag based off of CI variables""" + env_pr_branch = "GITHUB_HEAD_REF" + default_branch = "GITHUB_REF" + branch_name = os.environ.get(default_branch, "master") + if os.environ.get(env_pr_branch, "") != "": + branch_name = os.environ[env_pr_branch] + branch_name = branch_name.replace("refs/heads/", "").replace("/", "-") + return f"gh-{branch_name}" + + class SeleniumTestCase(StaticLiveServerTestCase): """StaticLiveServerTestCase which automatically creates a Webdriver instance""" diff --git a/tests/integration/test_outpost_docker.py b/tests/integration/test_outpost_docker.py index 0244c8436..ddc6ff26b 100644 --- a/tests/integration/test_outpost_docker.py +++ b/tests/integration/test_outpost_docker.py @@ -16,6 +16,7 @@ from authentik.outposts.controllers.docker import DockerController from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType from authentik.outposts.tasks import outpost_local_connection from authentik.providers.proxy.models import ProxyProvider +from tests.e2e.utils import get_docker_tag class OutpostDockerTests(TestCase): @@ -107,5 +108,5 @@ class OutpostDockerTests(TestCase): self.assertEqual(compose["version"], "3.5") self.assertEqual( compose["services"]["authentik_proxy"]["image"], - "beryju.org/authentik/outpost-proxy:gh-master", + f"beryju.org/authentik/outpost-proxy:{get_docker_tag()}", ) diff --git a/tests/integration/test_proxy_docker.py b/tests/integration/test_proxy_docker.py index 5a608b6ee..a0c9ae6d4 100644 --- a/tests/integration/test_proxy_docker.py +++ b/tests/integration/test_proxy_docker.py @@ -16,6 +16,7 @@ from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostT from authentik.outposts.tasks import outpost_local_connection from authentik.providers.proxy.controllers.docker import DockerController from authentik.providers.proxy.models import ProxyProvider +from tests.e2e.utils import get_docker_tag class TestProxyDocker(TestCase): @@ -107,5 +108,5 @@ class TestProxyDocker(TestCase): self.assertEqual(compose["version"], "3.5") self.assertEqual( compose["services"]["authentik_proxy"]["image"], - "beryju.org/authentik/outpost-proxy:gh-master", + f"beryju.org/authentik/outpost-proxy:{get_docker_tag()}", ) diff --git a/web/src/locales/en.po b/web/src/locales/en.po index 871e691ca..9ed6f5be2 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -15,6 +15,7 @@ msgstr "" #: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts +#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/stages/user_login/UserLoginStageForm.ts msgid "(Format: hours=-1;minutes=-2;seconds=-3)." @@ -794,6 +795,10 @@ msgstr "Configure how long access codes are valid for." msgid "Configure how long refresh tokens and their id_tokens are valid for." msgstr "Configure how long refresh tokens and their id_tokens are valid for." +#: src/pages/providers/proxy/ProxyProviderForm.ts +msgid "Configure how long tokens are valid for." +msgstr "Configure how long tokens are valid for." + #: src/pages/providers/saml/SAMLProviderForm.ts msgid "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected." msgstr "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected." @@ -2053,6 +2058,10 @@ msgstr "If this is selected, the token will expire. Upon expiration, the token w msgid "If your authentik Instance is using a self-signed certificate, set this value." msgstr "If your authentik Instance is using a self-signed certificate, set this value." +#: src/pages/outposts/OutpostDeploymentModal.ts +msgid "If your authentik_host setting does not match the URL you want to login with, add this setting." +msgstr "If your authentik_host setting does not match the URL you want to login with, add this setting." + #: src/pages/users/UserListPage.ts msgid "Impersonate" msgstr "Impersonate" @@ -4472,6 +4481,7 @@ msgid "Token expiry" msgstr "Token expiry" #: src/pages/providers/oauth2/OAuth2ProviderForm.ts +#: src/pages/providers/proxy/ProxyProviderForm.ts msgid "Token validity" msgstr "Token validity" diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index 9b834f3f0..2eb7de7b5 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -15,6 +15,7 @@ msgstr "" #: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts +#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/stages/user_login/UserLoginStageForm.ts msgid "(Format: hours=-1;minutes=-2;seconds=-3)." @@ -788,6 +789,10 @@ msgstr "" msgid "Configure how long refresh tokens and their id_tokens are valid for." msgstr "" +#: src/pages/providers/proxy/ProxyProviderForm.ts +msgid "Configure how long tokens are valid for." +msgstr "" + #: src/pages/providers/saml/SAMLProviderForm.ts msgid "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected." msgstr "" @@ -2045,6 +2050,10 @@ msgstr "" msgid "If your authentik Instance is using a self-signed certificate, set this value." msgstr "" +#: src/pages/outposts/OutpostDeploymentModal.ts +msgid "If your authentik_host setting does not match the URL you want to login with, add this setting." +msgstr "" + #: src/pages/users/UserListPage.ts msgid "Impersonate" msgstr "" @@ -4457,6 +4466,7 @@ msgid "Token expiry" msgstr "" #: src/pages/providers/oauth2/OAuth2ProviderForm.ts +#: src/pages/providers/proxy/ProxyProviderForm.ts msgid "Token validity" msgstr "" diff --git a/web/src/pages/outposts/OutpostDeploymentModal.ts b/web/src/pages/outposts/OutpostDeploymentModal.ts index fc6f67160..41a53dac4 100644 --- a/web/src/pages/outposts/OutpostDeploymentModal.ts +++ b/web/src/pages/outposts/OutpostDeploymentModal.ts @@ -1,4 +1,4 @@ -import { Outpost } from "@goauthentik/api"; +import { Outpost, OutpostTypeEnum } from "@goauthentik/api"; import { customElement, html, property, TemplateResult } from "lit-element"; import { t } from "@lingui/macro"; import { ifDefined } from "lit-html/directives/if-defined"; @@ -53,6 +53,26 @@ export class OutpostDeploymentModal extends ModalButton { + ${this.outpost?.type == OutpostTypeEnum.Proxy + ? html` +